This is an automated email from the ASF dual-hosted git repository.
kenhuuu pushed a commit to branch stringify-params
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/stringify-params by this push:
new 882e9f0f5a translations + feature tests
882e9f0f5a is described below
commit 882e9f0f5a8a973835b3c4033c6f7d30e6fa48ba
Author: Ken Hu <[email protected]>
AuthorDate: Mon Apr 20 20:37:06 2026 -0700
translations + feature tests
---
.../translator/AnonymizedTranslatorVisitor.java | 17 +
.../translator/DotNetTranslateVisitor.java | 28 ++
.../language/translator/GoTranslateVisitor.java | 28 ++
.../translator/GroovyTranslateVisitor.java | 25 ++
.../language/translator/JavaTranslateVisitor.java | 26 ++
.../translator/JavascriptTranslateVisitor.java | 18 +
.../translator/PythonTranslateVisitor.java | 33 ++
.../language/translator/TranslateVisitor.java | 18 +
.../language/translator/GremlinTranslatorTest.java | 27 ++
.../gremlin/process/traversal/GremlinLangTest.java | 17 +-
.../Gherkin/CommonSteps.cs | 18 +
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 42 ++-
.../Process/Traversal/GremlinLangTests.cs | 79 +----
gremlin-go/build/generate.groovy | 10 +-
gremlin-go/driver/cucumber/cucumberSteps_test.go | 52 ++-
gremlin-go/driver/cucumber/gremlin.go | 22 ++
gremlin-go/driver/gremlinlang_test.go | 50 +--
.../translator/AnonymizedTranslateVisitor.ts | 12 +
.../language/translator/DotNetTranslateVisitor.ts | 21 ++
.../lib/language/translator/GoTranslateVisitor.ts | 67 ++++
.../language/translator/GroovyTranslateVisitor.ts | 21 ++
.../language/translator/JavaTranslateVisitor.ts | 21 ++
.../translator/JavascriptTranslateVisitor.ts | 14 +
.../language/translator/PythonTranslateVisitor.ts | 69 ++++
.../lib/language/translator/TranslateVisitor.ts | 12 +
.../scripts/groovy/generate.groovy | 10 +-
.../test/cucumber/feature-steps.js | 6 +
.../gremlin-javascript/test/cucumber/gremlin.js | 22 ++
.../gremlin-javascript/test/cucumber/world.js | 8 +
.../test/unit/gremlin-lang-test.js | 13 +-
.../unit/translator/gremlin-translator-test.js | 57 +++
.../src/main/python/tests/feature/feature_steps.py | 23 +-
.../src/main/python/tests/feature/gremlin.py | 22 ++
.../python/tests/unit/process/test_gremlin_lang.py | 55 +--
.../tinkerpop/gremlin/features/StepDefinition.java | 11 +
.../gremlin/language/translator/translations.json | 387 +++++++++++++++++++++
.../gremlin/test/features/data/Binary.feature | 82 +++++
.../gremlin/test/features/data/Char.feature | 94 +++++
.../gremlin/test/features/data/Duration.feature | 154 ++++++++
39 files changed, 1472 insertions(+), 219 deletions(-)
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java
index 98c8be8ef7..23b70526bc 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java
@@ -23,6 +23,8 @@ import
org.apache.tinkerpop.gremlin.language.grammar.GremlinParser;
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.List;
@@ -187,4 +189,19 @@ public class AnonymizedTranslatorVisitor extends
TranslateVisitor {
@Override
public Void visitUuidLiteral(final GremlinParser.UuidLiteralContext ctx) {
return anonymize(ctx, String.class); }
+
+ @Override
+ public Void visitCharacterLiteral(final
GremlinParser.CharacterLiteralContext ctx) {
+ return anonymize(ctx, Character.class);
+ }
+
+ @Override
+ public Void visitDurationLiteral(final
GremlinParser.DurationLiteralContext ctx) {
+ return anonymize(ctx, Duration.class);
+ }
+
+ @Override
+ public Void visitBinaryLiteral(final GremlinParser.BinaryLiteralContext
ctx) {
+ return anonymize(ctx, ByteBuffer.class);
+ }
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
index 41fcaa4feb..f80123c4da 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
@@ -1181,6 +1181,34 @@ public class DotNetTranslateVisitor extends
AbstractTranslateVisitor {
return null;
}
+ @Override
+ public Void visitCharacterLiteral(final
GremlinParser.CharacterLiteralContext ctx) {
+ // "a"c or 'a'c -> 'a' in C#
+ final String text = ctx.getText();
+ // strip the 'c' suffix to get the quoted character
+ final String withoutSuffix = text.substring(0, text.length() - 1);
+ // C# char literals use single quotes
+ final String inner = removeFirstAndLastCharacters(withoutSuffix);
+ sb.append("'").append(inner).append("'");
+ return null;
+ }
+
+ @Override
+ public Void visitDurationLiteral(final
GremlinParser.DurationLiteralContext ctx) {
+ sb.append("XmlConvert.ToTimeSpan(");
+ sb.append(ctx.stringLiteral().getText());
+ sb.append(")");
+ return null;
+ }
+
+ @Override
+ public Void visitBinaryLiteral(final GremlinParser.BinaryLiteralContext
ctx) {
+ sb.append("Convert.FromBase64String(");
+ sb.append(ctx.stringLiteral().getText());
+ sb.append(")");
+ return null;
+ }
+
@Override
public Void visitTraversalGType(GremlinParser.TraversalGTypeContext ctx) {
final String[] split = ctx.getText().split("\\.");
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java
index 18cdfa1660..7a81b84a72 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java
@@ -28,8 +28,10 @@ import
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.Option
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.util.DatetimeHelper;
+import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.Arrays;
+import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -346,6 +348,32 @@ public class GoTranslateVisitor extends
AbstractTranslateVisitor {
return null;
}
+ @Override
+ public Void visitCharacterLiteral(final
GremlinParser.CharacterLiteralContext ctx) {
+ throw new TranslatorException("Character literals are not supported in
Go");
+ }
+
+ @Override
+ public Void visitDurationLiteral(final
GremlinParser.DurationLiteralContext ctx) {
+ final String iso8601 =
removeFirstAndLastCharacters(ctx.stringLiteral().getText());
+ final Duration d = Duration.parse(iso8601);
+ sb.append("time.Duration(").append(d.toNanos()).append(")");
+ return null;
+ }
+
+ @Override
+ public Void visitBinaryLiteral(final GremlinParser.BinaryLiteralContext
ctx) {
+ final String base64Str =
removeFirstAndLastCharacters(ctx.stringLiteral().getText());
+ final byte[] bytes = Base64.getDecoder().decode(base64Str);
+ sb.append("gremlingo.ByteBuffer{Data: []byte{");
+ for (int i = 0; i < bytes.length; i++) {
+ if (i > 0) sb.append(",");
+ sb.append(bytes[i] & 0xFF);
+ }
+ sb.append("}}");
+ return null;
+ }
+
@Override
protected String getCardinalityFunctionClass() {
return "CardinalityValue";
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java
index 33139d0919..a6e1f6050a 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java
@@ -131,6 +131,31 @@ public class GroovyTranslateVisitor extends
TranslateVisitor {
return null;
}
+ @Override
+ public Void visitCharacterLiteral(final
GremlinParser.CharacterLiteralContext ctx) {
+ final String text = ctx.getText();
+ final String withoutSuffix = text.substring(0, text.length() - 1);
+ final String inner = removeFirstAndLastCharacters(withoutSuffix);
+ sb.append("'").append(inner).append("' as char");
+ return null;
+ }
+
+ @Override
+ public Void visitDurationLiteral(final
GremlinParser.DurationLiteralContext ctx) {
+ sb.append("Duration.parse(");
+ sb.append(ctx.stringLiteral().getText());
+ sb.append(")");
+ return null;
+ }
+
+ @Override
+ public Void visitBinaryLiteral(final GremlinParser.BinaryLiteralContext
ctx) {
+ sb.append("ByteBuffer.wrap(Base64.getDecoder().decode(");
+ sb.append(ctx.stringLiteral().getText());
+ sb.append("))");
+ return null;
+ }
+
@Override
public Void visitNullLiteral(final GremlinParser.NullLiteralContext ctx) {
if (ctx.getParent() instanceof
GremlinParser.GenericMapNullableArgumentContext) {
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java
index aecf7c2015..9a0c7dbdb5 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java
@@ -248,6 +248,32 @@ public class JavaTranslateVisitor extends
AbstractTranslateVisitor {
return null;
}
+ @Override
+ public Void visitCharacterLiteral(final
GremlinParser.CharacterLiteralContext ctx) {
+ // "a"c or 'a'c -> 'a' in Java
+ final String text = ctx.getText();
+ final String withoutSuffix = text.substring(0, text.length() - 1);
+ final String inner = removeFirstAndLastCharacters(withoutSuffix);
+ sb.append("'").append(inner).append("'");
+ return null;
+ }
+
+ @Override
+ public Void visitDurationLiteral(final
GremlinParser.DurationLiteralContext ctx) {
+ sb.append("Duration.parse(");
+ sb.append(ctx.stringLiteral().getText());
+ sb.append(")");
+ return null;
+ }
+
+ @Override
+ public Void visitBinaryLiteral(final GremlinParser.BinaryLiteralContext
ctx) {
+ sb.append("ByteBuffer.wrap(Base64.getDecoder().decode(");
+ sb.append(ctx.stringLiteral().getText());
+ sb.append("))");
+ return null;
+ }
+
@Override
public Void visitGenericRangeLiteral(final
GremlinParser.GenericRangeLiteralContext ctx) {
throw new TranslatorException("Java does not support range literals");
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java
index 08f097d6f7..2a8e0ed07f 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java
@@ -230,6 +230,24 @@ public class JavascriptTranslateVisitor extends
AbstractTranslateVisitor {
return null;
}
+ @Override
+ public Void visitCharacterLiteral(final
GremlinParser.CharacterLiteralContext ctx) {
+ throw new TranslatorException("Character literals are not supported in
JavaScript");
+ }
+
+ @Override
+ public Void visitDurationLiteral(final
GremlinParser.DurationLiteralContext ctx) {
+ throw new TranslatorException("Duration literals are not supported in
JavaScript");
+ }
+
+ @Override
+ public Void visitBinaryLiteral(final GremlinParser.BinaryLiteralContext
ctx) {
+ sb.append("Buffer.from(");
+ visitStringLiteral(ctx.stringLiteral());
+ sb.append(",'base64')");
+ return null;
+ }
+
@Override
protected String getCardinalityFunctionClass() {
return "CardinalityValue";
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
index 4473a38dbf..9a9699de88 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
@@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.util.DatetimeHelper;
import java.math.BigInteger;
+import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -289,6 +290,38 @@ public class PythonTranslateVisitor extends
AbstractTranslateVisitor {
return null;
}
+ @Override
+ public Void visitCharacterLiteral(final
GremlinParser.CharacterLiteralContext ctx) {
+ final String text = ctx.getText();
+ final String withoutSuffix = text.substring(0, text.length() - 1);
+ final String inner = removeFirstAndLastCharacters(withoutSuffix);
+ sb.append("SingleChar('").append(inner).append("')");
+ return null;
+ }
+
+ @Override
+ public Void visitDurationLiteral(final
GremlinParser.DurationLiteralContext ctx) {
+ final String iso8601 =
removeFirstAndLastCharacters(ctx.stringLiteral().getText());
+ final Duration d = Duration.parse(iso8601);
+ final long totalSeconds = d.getSeconds();
+ final int nanos = d.getNano();
+ if (nanos == 0) {
+ sb.append("timedelta(seconds=").append(totalSeconds).append(")");
+ } else {
+ final long micros = nanos / 1000;
+
sb.append("timedelta(seconds=").append(totalSeconds).append(",microseconds=").append(micros).append(")");
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitBinaryLiteral(final GremlinParser.BinaryLiteralContext
ctx) {
+ sb.append("base64.b64decode(");
+ visitStringLiteral(ctx.stringLiteral());
+ sb.append(")");
+ return null;
+ }
+
@Override
protected String getCardinalityFunctionClass() {
return "CardinalityValue";
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java
index 5eccd5f400..a8d51f6b28 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java
@@ -452,6 +452,24 @@ public class TranslateVisitor extends
GremlinBaseVisitor<Void> {
return null;
}
+ @Override
+ public Void visitCharacterLiteral(final
GremlinParser.CharacterLiteralContext ctx) {
+ sb.append(ctx.getText());
+ return null;
+ }
+
+ @Override
+ public Void visitDurationLiteral(final
GremlinParser.DurationLiteralContext ctx) {
+ sb.append(ctx.getText());
+ return null;
+ }
+
+ @Override
+ public Void visitBinaryLiteral(final GremlinParser.BinaryLiteralContext
ctx) {
+ sb.append(ctx.getText());
+ return null;
+ }
+
@Override
public Void visitVariable(final GremlinParser.VariableContext ctx) {
final String var = ctx.getText();
diff --git
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java
index 525a3dd1e9..cb97ae615d 100644
---
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java
@@ -1426,6 +1426,33 @@ public class GremlinTranslatorTest {
"g.call(\"--list\").with(\"service\",
__.constant(\"tinker.search\"))",
"g.call(\"--list\").with_(\"service\",
__.constant(\"tinker.search\"))",
"g.call('--list').with_('service',
__.constant('tinker.search'))"},
+ {"g.inject(\"a\"c)",
+ null,
+ "g.inject(character0)",
+ "g.Inject<object>('a')",
+ "Character literals are not supported in Go",
+ "g.inject('a' as char)",
+ "g.inject('a')",
+ "Character literals are not supported in
JavaScript",
+ "g.inject(SingleChar('a'))"},
+ {"g.inject(Duration(\"PT2H30M\"))",
+ null,
+ "g.inject(duration0)",
+
"g.Inject<object>(XmlConvert.ToTimeSpan(\"PT2H30M\"))",
+ "g.Inject(time.Duration(9000000000000))",
+ "g.inject(Duration.parse(\"PT2H30M\"))",
+ "g.inject(Duration.parse(\"PT2H30M\"))",
+ "Duration literals are not supported in
JavaScript",
+ "g.inject(timedelta(seconds=9000))"},
+ {"g.inject(Binary(\"AQID\"))",
+ null,
+ "g.inject(bytebuffer0)",
+
"g.Inject<object>(Convert.FromBase64String(\"AQID\"))",
+ "g.Inject(gremlingo.ByteBuffer{Data:
[]byte{1,2,3}})",
+
"g.inject(ByteBuffer.wrap(Base64.getDecoder().decode(\"AQID\")))",
+
"g.inject(ByteBuffer.wrap(Base64.getDecoder().decode(\"AQID\")))",
+ "g.inject(Buffer.from(\"AQID\",'base64'))",
+ "g.inject(base64.b64decode('AQID'))"},
});
}
diff --git
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLangTest.java
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLangTest.java
index 9dd7cf3c47..48d01cfa32 100644
---
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLangTest.java
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLangTest.java
@@ -130,24 +130,13 @@ public class GremlinLangTest {
{g.inject(GValue.of("x", "x")).V(GValue.of("ids", new int[]{1,
2, 3})), "g.inject(x).V(ids)"},
{newG().inject(GValue.of(null, "test1"), GValue.of("xx2",
"test2")), "g.inject(\"test1\",xx2)"},
{newG().inject(new HashSet<>(Arrays.asList(1, 2))),
"g.inject({1,2})"},
- // Character literals
- {g.inject('a'), "g.inject(\"a\"c)"},
- {g.inject('"'), "g.inject(\"\\\"\"c)"},
+ // Character - single quote (no feature equivalent since
grammar uses double quotes in feature files)
{g.inject('\''), "g.inject(\"'\"c)"},
- {g.inject('\\'), "g.inject(\"\\\\\"c)"},
- {g.inject('\u00e9'), "g.inject(\"\\u00E9\"c)"},
- // Duration literals
- {g.inject(Duration.ofHours(2).plusMinutes(30)),
"g.inject(Duration(\"PT2H30M\"))"},
- {g.inject(Duration.ZERO), "g.inject(Duration(\"PT0S\"))"},
- {g.inject(Duration.ofSeconds(-30)),
"g.inject(Duration(\"PT-30S\"))"},
- {g.inject(Duration.ofMillis(500)),
"g.inject(Duration(\"PT0.5S\"))"},
+ // Duration - Java-specific: normalization of days to hours,
nanosecond precision
{g.inject(Duration.ofDays(1).plusHours(12)),
"g.inject(Duration(\"PT36H\"))"},
{g.inject(Duration.ofNanos(1)),
"g.inject(Duration(\"PT0.000000001S\"))"},
{g.inject(Duration.ofMillis(-500)),
"g.inject(Duration(\"PT-0.5S\"))"},
- // Binary literals
- {g.inject(ByteBuffer.wrap(new byte[]{1, 2, 3})),
"g.inject(Binary(\"AQID\"))"},
- {g.inject(ByteBuffer.wrap(new byte[]{})),
"g.inject(Binary(\"\"))"},
- {g.inject(ByteBuffer.wrap(new byte[]{0})),
"g.inject(Binary(\"AA==\"))"},
+ // Binary - byte[] type (distinct from ByteBuffer)
{g.inject(new byte[]{1, 2, 3}), "g.inject(Binary(\"AQID\"))"},
{g.inject(new byte[]{}), "g.inject(Binary(\"\"))"},
{g.inject(new byte[]{0}), "g.inject(Binary(\"AA==\"))"},
diff --git
a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
index bb3cf3e671..203317c18d 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
@@ -63,6 +63,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{@"prop\[(.+)\]", ToProperty},
{@"dt\[(.+)\]", ToDateTime},
{@"uuid\[(.+)\]", ToUuid},
+ {@"char\[(.)\]", ToChar},
+ {@"dur\[(.+)\]", ToDuration},
+ {@"bin\[(.*)\]", ToBinary},
{@"d\[(.*)\]\.([bsilfdmn])", ToNumber},
{@"D\[(.+)\]", ToDirection},
{@"M\[(.+)\]", ToMerge},
@@ -541,6 +544,21 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
return null;
}
+ private static object ToChar(string value, string graphName)
+ {
+ return value[0];
+ }
+
+ private static object ToDuration(string iso8601, string graphName)
+ {
+ return System.Xml.XmlConvert.ToTimeSpan(iso8601);
+ }
+
+ private static object ToBinary(string base64, string graphName)
+ {
+ return Convert.FromBase64String(base64);
+ }
+
private static Vertex ToVertex(string name, string graphName)
{
if
(ScenarioData.GetByGraphName(graphName).Vertices.ContainsKey(name))
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index e0c6c08853..d2ffc824b7 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -226,6 +226,11 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_valuesXintX_asNumberXGType_BIGINTX_isXtypeOfXGType_BIGINTXX_project_byXidentityX_byXmathXaddX999XXX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("int", 50), (g,p)
=>g.V().Values<object>("int").AsNumber(GType.BigInt).Is(P.TypeOf(GType.BigInt)).Project<object>("original",
"added").By(__.Identity()).By(__.Math("_ + 999"))}},
{"g_injectX777X_asNumberXGType_BIGINTX_isXtypeOfXGType_BIGINTXX_groupCount",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.Inject<object>(777).AsNumber(GType.BigInt).Is(P.TypeOf(GType.BigInt)).GroupCount<object>()}},
{"g_V_valuesXageX_isXtypeOfXGType_BIGINTXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Values<object>("age").Is(P.TypeOf(GType.BigInt))}},
+ {"g_injectXBinaryXAQIDXX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>(Convert.FromBase64String("AQID"))}},
+ {"g_injectXBinaryXemptyXX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>(Convert.FromBase64String(""))}},
+ {"g_injectXBinaryXAA_eqeqXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(Convert.FromBase64String("AA=="))}},
+ {"g_valuesXblobX_isXtypeOfXGType_BINARYXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("blob",
Convert.FromBase64String("AQID")), (g,p)
=>g.V().Values<object>("blob").Is(P.TypeOf(GType.Binary))}},
+ {"g_injectXBinaryXAQIDXX_isXeqXBinaryXAQIDXXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.Inject<object>(Convert.FromBase64String("AQID")).Is(P.Eq(Convert.FromBase64String("AQID")))}},
{"g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("int", 5), (g,p)
=>g.V().Values<object>("int").AsNumber(GType.Byte).Is(P.TypeOf(GType.Byte))}},
{"g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_mathXaddX20XX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("int", 10), (g,p)
=>g.V().Values<object>("int").AsNumber(GType.Byte).Is(P.TypeOf(GType.Byte)).Math("_
+ 20")}},
{"g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_isXltX10XX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("int", 7), (g,p)
=>g.V().Values<object>("int").AsNumber(GType.Byte).Is(P.TypeOf(GType.Byte)).Is(P.Lt(10))}},
@@ -234,6 +239,12 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_chooseXisXeqX12XX_constantXtwelveX_constantXotherXX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("int", 12), (g,p)
=>g.V().Values<object>("int").AsNumber(GType.Byte).Is(P.TypeOf(GType.Byte)).Choose<object>(__.Is(P.Eq(12)),
__.Constant<object>("twelve"), __.Constant<object>("other"))}},
{"g_injectX15X_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_groupCount", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.Inject<object>(15).AsNumber(GType.Byte).Is(P.TypeOf(GType.Byte)).GroupCount<object>()}},
{"g_V_valuesXageX_isXtypeOfXGType_BYTEXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Values<object>("age").Is(P.TypeOf(GType.Byte))}},
+ {"g_injectXaX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>('a')}},
+ {"g_injectXescaped_quoteX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>('\"')}},
+ {"g_injectXescaped_backslashX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>('\\')}},
+ {"g_injectXunicodeX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>('\u00E9')}},
+ {"g_valuesXinitialX_isXtypeOfXGType_CHARXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("initial", 'a'), (g,p)
=>g.V().Values<object>("initial").Is(P.TypeOf(GType.Char))}},
+ {"g_injectXaX_isXeqXaXX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>('a').Is(P.Eq('a'))}},
{"g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "event").Property("datetime",
DateTimeOffset.Parse("2023-08-08T00:00Z")), (g,p)
=>g.V().Values<object>("datetime").Is(P.TypeOf(GType.DateTime))}},
{"g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX_project_byXidentityX_byXdateAddXDT_dayX1XX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "event").Property("datetime",
DateTimeOffset.Parse("2023-08-08T00:00Z")), (g,p)
=>g.V().Values<object>("datetime").Is(P.TypeOf(GType.DateTime)).Project<object>("original",
"nextDay").By(__.Identity()).By(__.DateAdd(DT.Day, 1))}},
{"g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX_dateDiffXdatetimeX2023_08_10XX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "event").Property("datetime",
DateTimeOffset.Parse("2023-08-08T00:00Z")), (g,p)
=>g.V().Values<object>("datetime").Is(P.TypeOf(GType.DateTime)).DateDiff(DateTimeOffset.Parse("2023-08-08T00:00:30Z"))}},
@@ -253,6 +264,17 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_valuesXdoubleX_isXtypeOfXGType_DOUBLEXX_order_byXascX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("double", 3.2d).AddV((string)
"data").Property("double", 1.8d).AddV((string) "data").Property("double",
2.5d), (g,p)
=>g.V().Values<object>("double").Is(P.TypeOf(GType.Double)).Order().By(Order.Asc)}},
{"g_injectX5_5dX_isXtypeOfXGType_DOUBLEXX_groupCount", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.Inject<object>(5.5d).Is(P.TypeOf(GType.Double)).GroupCount<object>()}},
{"g_V_valuesXageX_isXtypeOfXGType_DOUBLEXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Values<object>("age").Is(P.TypeOf(GType.Double))}},
+ {"g_injectXDurationXPT2H30MXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(XmlConvert.ToTimeSpan("PT2H30M"))}},
+ {"g_injectXDurationXPT0SXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(XmlConvert.ToTimeSpan("PT0S"))}},
+ {"g_injectXDurationXPTneg30SXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(XmlConvert.ToTimeSpan("PT-30S"))}},
+ {"g_injectXDurationXnegPT30SXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(XmlConvert.ToTimeSpan("-PT30S"))}},
+ {"g_injectXDurationXPT0_5SXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(XmlConvert.ToTimeSpan("PT0.5S"))}},
+ {"g_injectXDurationXP1DT12HXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(XmlConvert.ToTimeSpan("P1DT12H"))}},
+ {"g_injectXDurationXP2DXX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>(XmlConvert.ToTimeSpan("P2D"))}},
+ {"g_injectXDurationXPT1H30M15SXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(XmlConvert.ToTimeSpan("PT1H30M15S"))}},
+ {"g_injectXDurationXPTneg0_5SXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(XmlConvert.ToTimeSpan("PT-0.5S"))}},
+ {"g_valuesXlengthX_isXtypeOfXGType_DURATIONXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("length",
XmlConvert.ToTimeSpan("PT2H30M")), (g,p)
=>g.V().Values<object>("length").Is(P.TypeOf(GType.Duration))}},
+ {"g_injectXDurationXPT2H30MXX_isXgtXDurationXPT1HXXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.Inject<object>(XmlConvert.ToTimeSpan("PT2H30M")).Is(P.Gt(XmlConvert.ToTimeSpan("PT1H")))}},
{"g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("float", 2.5), (g,p)
=>g.V().Values<object>("float").AsNumber(GType.Float).Is(P.TypeOf(GType.Float))}},
{"g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX_mathXmulX2XX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("float", 3.0), (g,p)
=>g.V().Values<object>("float").AsNumber(GType.Float).Is(P.TypeOf(GType.Float)).Math("_
* 2")}},
{"g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX_isXeqX1_5XX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "data").Property("float", 1.5), (g,p)
=>g.V().Values<object>("float").AsNumber(GType.Float).Is(P.TypeOf(GType.Float)).Is(P.Eq(1.5))}},
@@ -1060,7 +1082,6 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_hasLabelXpersonX_valuesXnameX_asXaX_constantXMrX_concatXselectXaX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().HasLabel("person").Values<object>("name").As("a").Constant<object>("Mr.").Concat(__.Select<object>("a"))}},
{"g_hasLabelXsoftwareX_asXaX_valuesXnameX_concatXunsesX_concatXselectXaXvaluesXlangX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().HasLabel("software").As("a").Values<object>("name").Concat("
uses ").Concat(__.Select<object>("a").Values<object>("lang"))}},
{"g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_labelX_concatXselectXaX_inV_valuesXnameXX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values<object>("name").Concat(__.Select<object>("a").Label()).Concat(__.Select<object>("a").InV().Values<object>("name"))}},
-<<<<<<< HEAD
{"g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values<object>("name").Concat(__.Select<object>("a").Label(),
__.Select<object>("a").InV().Values<object>("name"))}},
{"g_addVXconstantXprefix_X_concatXVX1X_labelX_label", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.AddV((string) "person").Property("name", "marko").Property("age",
29).As("marko").AddV((string) "person").Property("name",
"vadas").Property("age", 27).As("vadas").AddV((string)
"software").Property("name", "lop").Property("lang",
"java").As("lop").AddV((string) "person").Property("name",
"josh").Property("age", 32).As("josh").AddV( [...]
{"g_injectXnullX_conjoinX1X", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(null).Conjoin((string) "1")}},
@@ -1074,23 +1095,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_out_out_path_byXnameX_conjoinXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Out().Out().Path().By("name").Conjoin((string) "")}},
{"g_injectXa_null_bX_conjoinXxyzX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(new List<object> { "a", null, "b" }).Conjoin((string)
"xyz")}},
{"g_injectX3_threeX_conjoinX_X", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(new List<object> { 3, "three" }).Conjoin((string)
";")}},
-=======
-
{"g_VX1X_outE_asXaX_VX1X_valuesXnamesX_concatXselectXaX_label_selectXaX_inV_valuesXnameXX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V(p["vid1"]).OutE().As("a").V(p["vid1"]).Values<object>("name").Concat(__.Select<object>("a").Label(),__.Select<object>("a").InV().Values<object>("name"))}},
- {"g_addVXconstantXprefix_X_concatXVX1X_labelX_label", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.AddV("person").Property("name","marko").Property("age",29).As("marko").AddV("person").Property("name","vadas").Property("age",27).As("vadas").AddV("software").Property("name","lop").Property("lang","java").As("lop").AddV("person").Property("name","josh").Property("age",32).As("josh").AddV("software").Property("name","ripple").Proper
[...]
- {"g_injectXnullX_conjoinX1X", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(null).Conjoin("1")}},
- {"g_V_valuesXnameX_conjoinX1X", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Values<object>("name").Conjoin("1")}},
- {"g_V_valuesXnonexistantX_fold_conjoinX_X", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Values<object>("nonexistant").Fold().Conjoin(";")}},
- {"g_V_valuesXnameX_order_fold_conjoinX_X", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Values<object>("name").Order().Fold().Conjoin("_")}},
- {"g_V_valuesXageX_order_fold_conjoinX_X", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Values<object>("age").Order().Fold().Conjoin(";")}},
- {"g_V_out_path_byXvaluesXnameX_toUpperX_conjoinXMARKOX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Out().Path().By(__.Values<object>("name").ToUpper()).Conjoin("MARKO")}},
- {"g_injectXmarkoX_conjoinX_X", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject(p["xx1"]).Conjoin("-")}},
-
{"g_V_valueMapXlocationX_selectXvaluesX_unfold_orderXlocalX_conjoinX1X", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().ValueMap<object,object>("location").Select<object>(Column.Values).Unfold<object>().Order(Scope.Local).Conjoin("1")}},
- {"g_V_out_out_path_byXnameX_conjoinXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Out().Out().Path().By("name").Conjoin("")}},
- {"g_injectXa_null_bX_conjoinXxyzX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject(p["xx1"]).Conjoin("xyz")}},
- {"g_injectX3_threeX_conjoinX_X", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject(p["xx1"]).Conjoin(";")}},
- {"g_injectXnull_a_null_bX_conjoinXplusX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject(p["xx1"]).Conjoin("+")}},
- {"g_injectXnull_nullX_conjoinXplusX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject(p["xx1"]).Conjoin("+")}},
->>>>>>> 3.7-dev
+ {"g_injectXnull_a_null_bX_conjoinXplusX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(new List<object> { null, "a", null, "b"
}).Conjoin((string) "+")}},
+ {"g_injectXnull_nullX_conjoinXplusX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(new List<object> { null, null }).Conjoin((string)
"+")}},
{"g_V_connectedComponent_hasXcomponentX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().ConnectedComponent().Has("gremlin.connectedComponentVertexProgram.component")}},
{"g_V_dedup_connectedComponent_hasXcomponentX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().Dedup().ConnectedComponent().Has("gremlin.connectedComponentVertexProgram.component")}},
{"g_V_hasLabelXsoftwareX_connectedComponent_project_byXnameX_byXcomponentX",
new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V().HasLabel("software").ConnectedComponent().Project<object>("name",
"component").By("name").By("gremlin.connectedComponentVertexProgram.component")}},
diff --git
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GremlinLangTests.cs
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GremlinLangTests.cs
index 884f666ff3..47bd8b4f79 100644
---
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GremlinLangTests.cs
+++
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GremlinLangTests.cs
@@ -1042,20 +1042,7 @@ namespace Gremlin.Net.UnitTest.Process.Traversal
_g.V("1").Property(Cardinality.Set,
map).GremlinLang.GetGremlin());
}
- [Fact]
- public void g_Inject_Char_a()
- {
- Assert.Equal("g.inject(\"a\"c)",
- _g.Inject((object)'a').GremlinLang.GetGremlin());
- }
-
- [Fact]
- public void g_Inject_Char_DoubleQuote()
- {
- Assert.Equal("g.inject(\"\\\"\"c)",
- _g.Inject((object)'"').GremlinLang.GetGremlin());
- }
-
+ // Character - single quote (no feature equivalent)
[Fact]
public void g_Inject_Char_SingleQuote()
{
@@ -1063,48 +1050,7 @@ namespace Gremlin.Net.UnitTest.Process.Traversal
_g.Inject((object)'\'').GremlinLang.GetGremlin());
}
- [Fact]
- public void g_Inject_Char_Backslash()
- {
- Assert.Equal("g.inject(\"\\\\\"c)",
- _g.Inject((object)'\\').GremlinLang.GetGremlin());
- }
-
- [Fact]
- public void g_Inject_Char_Unicode()
- {
- Assert.Equal("g.inject(\"\\u00E9\"c)",
- _g.Inject((object)'\u00e9').GremlinLang.GetGremlin());
- }
-
- [Fact]
- public void g_Inject_Duration_2H30M()
- {
- Assert.Equal("g.inject(Duration(\"PT2H30M\"))",
- _g.Inject((object)new TimeSpan(2, 30,
0)).GremlinLang.GetGremlin());
- }
-
- [Fact]
- public void g_Inject_Duration_Zero()
- {
- Assert.Equal("g.inject(Duration(\"PT0S\"))",
- _g.Inject((object)TimeSpan.Zero).GremlinLang.GetGremlin());
- }
-
- [Fact]
- public void g_Inject_Duration_Negative30S()
- {
- Assert.Equal("g.inject(Duration(\"-PT30S\"))",
-
_g.Inject((object)TimeSpan.FromSeconds(-30)).GremlinLang.GetGremlin());
- }
-
- [Fact]
- public void g_Inject_Duration_FractionalSeconds()
- {
- Assert.Equal("g.inject(Duration(\"PT0.5S\"))",
-
_g.Inject((object)TimeSpan.FromMilliseconds(500)).GremlinLang.GetGremlin());
- }
-
+ // Duration - C#-specific: XmlConvert format with days, tick
precision, negative prefix
[Fact]
public void g_Inject_Duration_DaysAndHours()
{
@@ -1126,26 +1072,5 @@ namespace Gremlin.Net.UnitTest.Process.Traversal
Assert.Equal("g.inject(Duration(\"-PT0.5S\"))",
_g.Inject((object)TimeSpan.FromMilliseconds(-500)).GremlinLang.GetGremlin());
}
-
- [Fact]
- public void g_Inject_Binary()
- {
- Assert.Equal("g.inject(Binary(\"AQID\"))",
- _g.Inject((object)new byte[] { 1, 2, 3
}).GremlinLang.GetGremlin());
- }
-
- [Fact]
- public void g_Inject_Binary_Empty()
- {
- Assert.Equal("g.inject(Binary(\"\"))",
-
_g.Inject((object)Array.Empty<byte>()).GremlinLang.GetGremlin());
- }
-
- [Fact]
- public void g_Inject_Binary_Padding()
- {
- Assert.Equal("g.inject(Binary(\"AA==\"))",
- _g.Inject((object)new byte[] { 0 }).GremlinLang.GetGremlin());
- }
}
}
diff --git a/gremlin-go/build/generate.groovy b/gremlin-go/build/generate.groovy
index 0ed3937d5f..39bc209713 100644
--- a/gremlin-go/build/generate.groovy
+++ b/gremlin-go/build/generate.groovy
@@ -83,7 +83,15 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer ->
writer.write("\"")
writer.write(k)
writer.write("\": {")
- def collected = v.collect { GremlinTranslator.translate(it,
Translator.GO) }
+ def collected = v.collect {
+ try {
+ GremlinTranslator.translate(it, Translator.GO)
+ } catch (ignored) {
+ // fall back to the original gremlin-lang form for
unsupported literals;
+ // the scenario will be skipped at test runtime via tag
exclusions
+ GremlinTranslator.translate(it, Translator.LANGUAGE)
+ }
+ }
def gremlinItty = collected.iterator()
while (gremlinItty.hasNext()) {
def t = gremlinItty.next()
diff --git a/gremlin-go/driver/cucumber/cucumberSteps_test.go
b/gremlin-go/driver/cucumber/cucumberSteps_test.go
index 87d1ecdae0..ef4921936a 100644
--- a/gremlin-go/driver/cucumber/cucumberSteps_test.go
+++ b/gremlin-go/driver/cucumber/cucumberSteps_test.go
@@ -21,6 +21,7 @@ package gremlingo
import (
"context"
+ "encoding/base64"
"encoding/json"
"errors"
"fmt"
@@ -46,11 +47,15 @@ type tinkerPopGraph struct {
var parsers map[*regexp.Regexp]func(string, string) interface{}
+var iso8601DurationRe =
regexp.MustCompile(`^(-?)PT(?:(-?\d+)H)?(?:(-?\d+)M)?(?:(-?\d+(?:\.\d+)?)S)?$`)
+
func init() {
parsers = map[*regexp.Regexp]func(string, string) interface{}{
regexp.MustCompile(`^str\[(.*)]$`): func(stringVal,
graphName string) interface{} { return stringVal }, //returns the string value
as is
regexp.MustCompile(`^dt\[(.*)]$`): toDateTime,
regexp.MustCompile(`^uuid\[(.*)]$`): toUuid,
+ regexp.MustCompile(`^dur\[(.*)]$`): toDuration,
+ regexp.MustCompile(`^bin\[(.*)]$`): toBinary,
regexp.MustCompile(`^d\[(.*)]\.[bslfd]$`): toNumeric,
regexp.MustCompile(`^d\[(.*)]\.[m]$`): toBigDecimal,
regexp.MustCompile(`^d\[(.*)]\.[n]$`): toBigInt,
@@ -131,6 +136,51 @@ func toUuid(stringVal, graphName string) interface{} {
return val
}
+// Parse duration from ISO-8601 string (e.g., "PT2H30M", "PT-30S").
+func toDuration(stringVal, graphName string) interface{} {
+ d, err := parseISO8601Duration(stringVal)
+ if err != nil {
+ return nil
+ }
+ return d
+}
+
+// Parse binary from base64 string.
+func toBinary(stringVal, graphName string) interface{} {
+ data, err := base64.StdEncoding.DecodeString(stringVal)
+ if err != nil {
+ return nil
+ }
+ return &gremlingo.ByteBuffer{Data: data}
+}
+
+// parseISO8601Duration parses an ISO-8601 duration string (as produced by
Java's Duration.toString())
+// into a time.Duration. Supports formats like "PT2H30M", "PT-30S", "PT0.5S".
+func parseISO8601Duration(s string) (time.Duration, error) {
+ m := iso8601DurationRe.FindStringSubmatch(s)
+ if m == nil {
+ return 0, fmt.Errorf("invalid ISO-8601 duration: %s", s)
+ }
+ sign := time.Duration(1)
+ if m[1] == "-" {
+ sign = -1
+ }
+ var d time.Duration
+ if m[2] != "" {
+ h, _ := strconv.ParseInt(m[2], 10, 64)
+ d += time.Duration(h) * time.Hour
+ }
+ if m[3] != "" {
+ min, _ := strconv.ParseInt(m[3], 10, 64)
+ d += time.Duration(min) * time.Minute
+ }
+ if m[4] != "" {
+ sec, _ := strconv.ParseFloat(m[4], 64)
+ d += time.Duration(sec * float64(time.Second))
+ }
+ return sign * d, nil
+}
+
// Parse numeric.
func toNumeric(stringVal, graphName string) interface{} {
if strings.Contains(stringVal, ".") {
@@ -1014,7 +1064,7 @@ func TestCucumberFeatures(t *testing.T) {
TestSuiteInitializer: InitializeTestSuite,
ScenarioInitializer: InitializeScenario,
Options: &godog.Options{
- Tags: "~@GraphComputerOnly &&
~@AllowNullPropertyValues && ~@StepSubgraph && ~@StepTree && ~@StepWrite",
+ Tags: "~@GraphComputerOnly &&
~@AllowNullPropertyValues && ~@StepSubgraph && ~@StepTree && ~@StepWrite &&
~@DataChar",
Format: "pretty",
Paths:
[]string{getEnvOrDefaultString("CUCUMBER_FEATURE_FOLDER",
"../../../gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features")},
TestingT: t, // Testing instance that will run subtests.
diff --git a/gremlin-go/driver/cucumber/gremlin.go
b/gremlin-go/driver/cucumber/gremlin.go
index d42c088856..386ebb3df9 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -196,6 +196,11 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"g_V_valuesXintX_asNumberXGType_BIGINTX_isXtypeOfXGType_BIGINTXX_project_byXidentityX_byXmathXaddX999XXX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("data").Property("int", 50)}, func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("int").AsNumber(gremlingo.GType.BigInt).Is(gremlingo.P.TypeOf(gremlingo.GType.BigInt)).Project("original",
"added").By(gremlingo.T [...]
"g_injectX777X_asNumberXGType_BIGINTX_isXtypeOfXGType_BIGINTXX_groupCount":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.Inject(777).AsNumber(gremlingo.GType.BigInt).Is(gremlingo.P.TypeOf(gremlingo.GType.BigInt)).GroupCount()}},
"g_V_valuesXageX_isXtypeOfXGType_BIGINTXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("age").Is(gremlingo.P.TypeOf(gremlingo.GType.BigInt))}},
+ "g_injectXBinaryXAQIDXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(gremlingo.ByteBuffer{Data: []byte{1,2,3}})}},
+ "g_injectXBinaryXemptyXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(gremlingo.ByteBuffer{Data: []byte{}})}},
+ "g_injectXBinaryXAA_eqeqXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(gremlingo.ByteBuffer{Data: []byte{0}})}},
+ "g_valuesXblobX_isXtypeOfXGType_BINARYXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("data").Property("blob",
gremlingo.ByteBuffer{Data: []byte{1,2,3}})}, func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("blob").Is(gremlingo.P.TypeOf(gremlingo.GType.Binary))}},
+ "g_injectXBinaryXAQIDXX_isXeqXBinaryXAQIDXXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.Inject(gremlingo.ByteBuffer{Data:
[]byte{1,2,3}}).Is(gremlingo.P.Eq(gremlingo.ByteBuffer{Data:
[]byte{1,2,3}}))}},
"g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("data").Property("int", 5)}, func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("int").AsNumber(gremlingo.GType.Byte).Is(gremlingo.P.TypeOf(gremlingo.GType.Byte))}},
"g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_mathXaddX20XX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("data").Property("int", 10)}, func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("int").AsNumber(gremlingo.GType.Byte).Is(gremlingo.P.TypeOf(gremlingo.GType.Byte)).Math("_
+ 20")}},
"g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_isXltX10XX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("data").Property("int", 7)}, func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("int").AsNumber(gremlingo.GType.Byte).Is(gremlingo.P.TypeOf(gremlingo.GType.Byte)).Is(gremlingo.P.Lt(10))}},
@@ -204,6 +209,12 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_chooseXisXeqX12XX_constantXtwelveX_constantXotherXX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("data").Property("int", 12)}, func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("int").AsNumber(gremlingo.GType.Byte).Is(gremlingo.P.TypeOf(gremlingo.GType.Byte)).Choose(gremlingo.T__.Is(gremlingo.P.Eq(
[...]
"g_injectX15X_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_groupCount":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.Inject(15).AsNumber(gremlingo.GType.Byte).Is(gremlingo.P.TypeOf(gremlingo.GType.Byte)).GroupCount()}},
"g_V_valuesXageX_isXtypeOfXGType_BYTEXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("age").Is(gremlingo.P.TypeOf(gremlingo.GType.Byte))}},
+ "g_injectXaX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return g.inject("a"c)}},
+ "g_injectXescaped_quoteX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return g.inject("\""c)}},
+ "g_injectXescaped_backslashX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return g.inject("\\"c)}},
+ "g_injectXunicodeX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.inject("\u00E9"c)}},
+ "g_valuesXinitialX_isXtypeOfXGType_CHARXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.addV("data").property("initial", "a"c)},
func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("initial").Is(gremlingo.P.TypeOf(gremlingo.GType.Char))}},
+ "g_injectXaX_isXeqXaXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.inject("a"c).is(P.eq("a"c))}},
"g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("event").Property("datetime",
time.Date(2023, 8, 8, 0, 0, 0, 0, time.FixedZone("UTC+00:00", 0)))}, func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("datetime").Is(gremlingo.P.TypeOf(gremlingo.GType.DateTime))}},
"g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX_project_byXidentityX_byXdateAddXDT_dayX1XX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("event").Property("datetime",
time.Date(2023, 8, 8, 0, 0, 0, 0, time.FixedZone("UTC+00:00", 0)))}, func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("datetime").Is(gremlingo.P.TypeOf(gremlingo.GType.DateTime)).Project("orig
[...]
"g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX_dateDiffXdatetimeX2023_08_10XX":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("event").Property("datetime",
time.Date(2023, 8, 8, 0, 0, 0, 0, time.FixedZone("UTC+00:00", 0)))}, func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("datetime").Is(gremlingo.P.TypeOf(gremlingo.GType.DateTime)).DateDiff(time.Date(2023,
[...]
@@ -223,6 +234,17 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"g_V_valuesXdoubleX_isXtypeOfXGType_DOUBLEXX_order_byXascX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("data").Property("double",
3.2).AddV("data").Property("double", 1.8).AddV("data").Property("double",
2.5)}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("double").Is(gremlingo.P.TypeOf(gremlingo.GType.Double)).Order().By(gremlingo.Order.Asc)}},
"g_injectX5_5dX_isXtypeOfXGType_DOUBLEXX_groupCount": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.Inject(5.5).Is(gremlingo.P.TypeOf(gremlingo.GType.Double)).GroupCount()}},
"g_V_valuesXageX_isXtypeOfXGType_DOUBLEXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("age").Is(gremlingo.P.TypeOf(gremlingo.GType.Double))}},
+ "g_injectXDurationXPT2H30MXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(time.Duration(9000000000000))}},
+ "g_injectXDurationXPT0SXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(time.Duration(0))}},
+ "g_injectXDurationXPTneg30SXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(time.Duration(-30000000000))}},
+ "g_injectXDurationXnegPT30SXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(time.Duration(-30000000000))}},
+ "g_injectXDurationXPT0_5SXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(time.Duration(500000000))}},
+ "g_injectXDurationXP1DT12HXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(time.Duration(129600000000000))}},
+ "g_injectXDurationXP2DXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(time.Duration(172800000000000))}},
+ "g_injectXDurationXPT1H30M15SXX": {func(g *gremlingo.GraphTraversalSource,
p map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(time.Duration(5415000000000))}},
+ "g_injectXDurationXPTneg0_5SXX": {func(g *gremlingo.GraphTraversalSource,
p map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(time.Duration(-500000000))}},
+ "g_valuesXlengthX_isXtypeOfXGType_DURATIONXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("data").Property("length",
time.Duration(9000000000000))}, func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.V().Values("length").Is(gremlingo.P.TypeOf(gremlingo.GType.Duration))}},
+ "g_injectXDurationXPT2H30MXX_isXgtXDurationXPT1HXXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.Inject(time.Duration(9000000000000)).Is(gremlingo.P.Gt(time.Duration(3600000000000)))}},
"g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("data").Property("float", 2.5)},
func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("float").AsNumber(gremlingo.GType.Float).Is(gremlingo.P.TypeOf(gremlingo.GType.Float))}},
"g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX_mathXmulX2XX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("data").Property("float", 3.0)},
func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("float").AsNumber(gremlingo.GType.Float).Is(gremlingo.P.TypeOf(gremlingo.GType.Float)).Math("_
* 2")}},
"g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX_isXeqX1_5XX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.AddV("data").Property("float", 1.5)},
func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Values("float").AsNumber(gremlingo.GType.Float).Is(gremlingo.P.TypeOf(gremlingo.GType.Float)).Is(gremlingo.P.Eq(1.5))}},
diff --git a/gremlin-go/driver/gremlinlang_test.go
b/gremlin-go/driver/gremlinlang_test.go
index 9f31595113..6e5f5d98cf 100644
--- a/gremlin-go/driver/gremlinlang_test.go
+++ b/gremlin-go/driver/gremlinlang_test.go
@@ -680,79 +680,45 @@ func Test_GremlinLang(t *testing.T) {
},
equals:
"g.V().has(\"name\",\"\\\"marko\\n\\r\\t\\b\\f\\\"\")",
},
+ // Duration - Go-specific: nanosecond precision, negative
sub-second, days+hours normalization
{
- assert: func(g *GraphTraversalSource) *GraphTraversal {
- return g.Inject(2*time.Hour + 30*time.Minute)
- },
- equals: `g.inject(Duration("PT2H30M"))`,
- },
- {
- assert: func(g *GraphTraversalSource) *GraphTraversal {
- return g.Inject(time.Duration(0))
- },
- equals: `g.inject(Duration("PT0S"))`,
- },
- {
- assert: func(g *GraphTraversalSource) *GraphTraversal {
- return g.Inject(-30 * time.Second)
- },
- equals: `g.inject(Duration("PT-30S"))`,
- },
- {
- assert: func(g *GraphTraversalSource) *GraphTraversal {
- return g.Inject(500 * time.Millisecond)
- },
- equals: `g.inject(Duration("PT0.5S"))`,
- },
- {
+ name: "g_Inject_Duration_NanosecondPrecision",
assert: func(g *GraphTraversalSource) *GraphTraversal {
return g.Inject(time.Nanosecond)
},
equals: `g.inject(Duration("PT0.000000001S"))`,
},
{
+ name: "g_Inject_Duration_NegativeSubSecond",
assert: func(g *GraphTraversalSource) *GraphTraversal {
return g.Inject(-500 * time.Millisecond)
},
equals: `g.inject(Duration("PT-0.5S"))`,
},
{
+ name: "g_Inject_Duration_DaysAndHours",
assert: func(g *GraphTraversalSource) *GraphTraversal {
return g.Inject(36 * time.Hour)
},
equals: `g.inject(Duration("PT36H"))`,
},
+ // Binary - []byte type (distinct from ByteBuffer)
{
- assert: func(g *GraphTraversalSource) *GraphTraversal {
- return g.Inject(ByteBuffer{Data: []byte{1, 2,
3}})
- },
- equals: `g.inject(Binary("AQID"))`,
- },
- {
- assert: func(g *GraphTraversalSource) *GraphTraversal {
- return g.Inject(ByteBuffer{Data: []byte{}})
- },
- equals: `g.inject(Binary(""))`,
- },
- {
- assert: func(g *GraphTraversalSource) *GraphTraversal {
- return g.Inject(ByteBuffer{Data: []byte{0}})
- },
- equals: `g.inject(Binary("AA=="))`,
- },
- {
+ name: "g_Inject_ByteSlice",
assert: func(g *GraphTraversalSource) *GraphTraversal {
return g.Inject([]byte{1, 2, 3})
},
equals: `g.inject(Binary("AQID"))`,
},
{
+ name: "g_Inject_ByteSlice_Empty",
assert: func(g *GraphTraversalSource) *GraphTraversal {
return g.Inject([]byte{})
},
equals: `g.inject(Binary(""))`,
},
{
+ name: "g_Inject_ByteSlice_Padding",
assert: func(g *GraphTraversalSource) *GraphTraversal {
return g.Inject([]byte{0})
},
diff --git
a/gremlin-js/gremlin-javascript/lib/language/translator/AnonymizedTranslateVisitor.ts
b/gremlin-js/gremlin-javascript/lib/language/translator/AnonymizedTranslateVisitor.ts
index 4f90e986fa..3ff98f0288 100644
---
a/gremlin-js/gremlin-javascript/lib/language/translator/AnonymizedTranslateVisitor.ts
+++
b/gremlin-js/gremlin-javascript/lib/language/translator/AnonymizedTranslateVisitor.ts
@@ -141,6 +141,18 @@ export default class AnonymizedTranslateVisitor extends
TranslateVisitor {
this.anonymize(ctx, 'String');
}
+ visitCharacterLiteral(ctx: any): void {
+ this.anonymize(ctx, 'Character');
+ }
+
+ visitDurationLiteral(ctx: any): void {
+ this.anonymize(ctx, 'Duration');
+ }
+
+ visitBinaryLiteral(ctx: any): void {
+ this.anonymize(ctx, 'ByteBuffer');
+ }
+
// ─── Keys — output unquoted (only values are anonymized)
─────────────────
visitNakedKey(ctx: any): void {
diff --git
a/gremlin-js/gremlin-javascript/lib/language/translator/DotNetTranslateVisitor.ts
b/gremlin-js/gremlin-javascript/lib/language/translator/DotNetTranslateVisitor.ts
index 11e303144f..247f54af3f 100644
---
a/gremlin-js/gremlin-javascript/lib/language/translator/DotNetTranslateVisitor.ts
+++
b/gremlin-js/gremlin-javascript/lib/language/translator/DotNetTranslateVisitor.ts
@@ -225,6 +225,27 @@ export default class DotNetTranslateVisitor extends
TranslateVisitor {
this.sb.push(')');
}
+ visitCharacterLiteral(ctx: any): void {
+ const text: string = ctx.getText();
+ const withoutSuffix = text.substring(0, text.length - 1);
+ const inner =
TranslateVisitor.removeFirstAndLastCharacters(withoutSuffix);
+ this.sb.push("'");
+ this.sb.push(inner);
+ this.sb.push("'");
+ }
+
+ visitDurationLiteral(ctx: any): void {
+ this.sb.push('XmlConvert.ToTimeSpan(');
+ this.sb.push(ctx.stringLiteral().getText());
+ this.sb.push(')');
+ }
+
+ visitBinaryLiteral(ctx: any): void {
+ this.sb.push('Convert.FromBase64String(');
+ this.sb.push(ctx.stringLiteral().getText());
+ this.sb.push(')');
+ }
+
visitClassType(ctx: any): void {
this.sb.push('typeof(');
this.sb.push(ctx.getText());
diff --git
a/gremlin-js/gremlin-javascript/lib/language/translator/GoTranslateVisitor.ts
b/gremlin-js/gremlin-javascript/lib/language/translator/GoTranslateVisitor.ts
index a3806bca85..9bfc87c2c4 100644
---
a/gremlin-js/gremlin-javascript/lib/language/translator/GoTranslateVisitor.ts
+++
b/gremlin-js/gremlin-javascript/lib/language/translator/GoTranslateVisitor.ts
@@ -22,6 +22,7 @@
import TranslateVisitor from './TranslateVisitor.js';
import { TranslatorException } from './TranslatorException.js';
+import { Buffer } from 'buffer';
const GO_PACKAGE_NAME = 'gremlingo.';
@@ -256,6 +257,27 @@ export default class GoTranslateVisitor extends
TranslateVisitor {
this.sb.push(')');
}
+ visitCharacterLiteral(_ctx: any): void {
+ throw new TranslatorException('Character literals are not supported in
Go');
+ }
+
+ visitDurationLiteral(ctx: any): void {
+ const iso8601 =
TranslateVisitor.removeFirstAndLastCharacters(ctx.stringLiteral().getText());
+ const nanos = parseISO8601DurationToNanos(iso8601);
+ this.sb.push(`time.Duration(${nanos})`);
+ }
+
+ visitBinaryLiteral(ctx: any): void {
+ const base64Str =
TranslateVisitor.removeFirstAndLastCharacters(ctx.stringLiteral().getText());
+ const bytes = base64ToBytes(base64Str);
+ this.sb.push(GO_PACKAGE_NAME + 'ByteBuffer{Data: []byte{');
+ for (let i = 0; i < bytes.length; i++) {
+ if (i > 0) this.sb.push(',');
+ this.sb.push(String(bytes[i]));
+ }
+ this.sb.push('}}');
+ }
+
visitTraversalStrategy(ctx: any): void {
if (ctx.getChildCount() === 1) {
this.sb.push(GO_PACKAGE_NAME);
@@ -349,6 +371,51 @@ export default class GoTranslateVisitor extends
TranslateVisitor {
}
}
+/**
+ * Parses an ISO-8601 duration string into total nanoseconds,
+ * matching the Java GoTranslateVisitor behavior.
+ */
+function parseISO8601DurationToNanos(iso8601: string): number {
+ const match =
iso8601.match(/^(-?)P(?:(\d+)D)?T?(?:(-?\d+)H)?(?:(-?\d+)M)?(?:(-?\d+(?:\.\d+)?)S)?$/);
+ if (!match) {
+ throw new TranslatorException(`Invalid ISO-8601 duration: ${iso8601}`);
+ }
+ const negative = match[1] === '-';
+ const days = match[2] ? parseInt(match[2], 10) : 0;
+ const hours = match[3] ? parseInt(match[3], 10) : 0;
+ const minutes = match[4] ? parseInt(match[4], 10) : 0;
+
+ let totalNanos = (days * 86400 + hours * 3600 + minutes * 60) *
1_000_000_000;
+
+ if (match[5]) {
+ const secParts = match[5].split('.');
+ totalNanos += parseInt(secParts[0], 10) * 1_000_000_000;
+ if (secParts.length > 1) {
+ const fracStr = secParts[1].padEnd(9, '0').substring(0, 9);
+ let fracNanos = parseInt(fracStr, 10);
+ if (parseInt(secParts[0], 10) < 0 || (parseInt(secParts[0], 10)
=== 0 && match[5].startsWith('-'))) {
+ fracNanos = -fracNanos;
+ }
+ totalNanos += fracNanos;
+ }
+ }
+
+ return negative ? -totalNanos : totalNanos;
+}
+
+/**
+ * Decodes a base64 string into an array of unsigned byte values.
+ */
+function base64ToBytes(base64: string): number[] {
+ if (base64 === '') return [];
+ const buf = Buffer.from(base64, 'base64');
+ const bytes: number[] = new Array(buf.length);
+ for (let i = 0; i < buf.length; i++) {
+ bytes[i] = buf[i];
+ }
+ return bytes;
+}
+
class SymbolHelper {
private static readonly TO_GO_MAP: Record<string, string> = {
'OUT': 'Out',
diff --git
a/gremlin-js/gremlin-javascript/lib/language/translator/GroovyTranslateVisitor.ts
b/gremlin-js/gremlin-javascript/lib/language/translator/GroovyTranslateVisitor.ts
index 4058377cd2..d77946da39 100644
---
a/gremlin-js/gremlin-javascript/lib/language/translator/GroovyTranslateVisitor.ts
+++
b/gremlin-js/gremlin-javascript/lib/language/translator/GroovyTranslateVisitor.ts
@@ -114,6 +114,27 @@ export default class GroovyTranslateVisitor extends
TranslateVisitor {
this.sb.push(')');
}
+ visitCharacterLiteral(ctx: any): void {
+ const text: string = ctx.getText();
+ const withoutSuffix = text.substring(0, text.length - 1);
+ const inner =
TranslateVisitor.removeFirstAndLastCharacters(withoutSuffix);
+ this.sb.push("'");
+ this.sb.push(inner);
+ this.sb.push("' as char");
+ }
+
+ visitDurationLiteral(ctx: any): void {
+ this.sb.push('Duration.parse(');
+ this.sb.push(ctx.stringLiteral().getText());
+ this.sb.push(')');
+ }
+
+ visitBinaryLiteral(ctx: any): void {
+ this.sb.push('ByteBuffer.wrap(Base64.getDecoder().decode(');
+ this.sb.push(ctx.stringLiteral().getText());
+ this.sb.push('))');
+ }
+
visitNullLiteral(ctx: any): void {
if (ctx.parent?.constructor?.name ===
'GenericMapNullableArgumentContext') {
this.sb.push('null as Map');
diff --git
a/gremlin-js/gremlin-javascript/lib/language/translator/JavaTranslateVisitor.ts
b/gremlin-js/gremlin-javascript/lib/language/translator/JavaTranslateVisitor.ts
index b7cf670f3d..414b76b6aa 100644
---
a/gremlin-js/gremlin-javascript/lib/language/translator/JavaTranslateVisitor.ts
+++
b/gremlin-js/gremlin-javascript/lib/language/translator/JavaTranslateVisitor.ts
@@ -200,6 +200,27 @@ export default class JavaTranslateVisitor extends
TranslateVisitor {
this.sb.push(')');
}
+ visitCharacterLiteral(ctx: any): void {
+ const text: string = ctx.getText();
+ const withoutSuffix = text.substring(0, text.length - 1);
+ const inner =
TranslateVisitor.removeFirstAndLastCharacters(withoutSuffix);
+ this.sb.push("'");
+ this.sb.push(inner);
+ this.sb.push("'");
+ }
+
+ visitDurationLiteral(ctx: any): void {
+ this.sb.push('Duration.parse(');
+ this.sb.push(ctx.stringLiteral().getText());
+ this.sb.push(')');
+ }
+
+ visitBinaryLiteral(ctx: any): void {
+ this.sb.push('ByteBuffer.wrap(Base64.getDecoder().decode(');
+ this.sb.push(ctx.stringLiteral().getText());
+ this.sb.push('))');
+ }
+
visitGenericRangeLiteral(_ctx: any): void {
throw new TranslatorException('Java does not support range literals');
}
diff --git
a/gremlin-js/gremlin-javascript/lib/language/translator/JavascriptTranslateVisitor.ts
b/gremlin-js/gremlin-javascript/lib/language/translator/JavascriptTranslateVisitor.ts
index e01fb5568b..4ae5020ba1 100644
---
a/gremlin-js/gremlin-javascript/lib/language/translator/JavascriptTranslateVisitor.ts
+++
b/gremlin-js/gremlin-javascript/lib/language/translator/JavascriptTranslateVisitor.ts
@@ -185,6 +185,20 @@ export default class JavascriptTranslateVisitor extends
TranslateVisitor {
this.visitStringLiteral(ctx.stringLiteral());
this.sb.push(')');
}
+
+ visitCharacterLiteral(_ctx: any): void {
+ throw new TranslatorException('Character literals are not supported in
JavaScript');
+ }
+
+ visitDurationLiteral(_ctx: any): void {
+ throw new TranslatorException('Duration literals are not supported in
JavaScript');
+ }
+
+ visitBinaryLiteral(ctx: any): void {
+ this.sb.push("Buffer.from(");
+ this.visitStringLiteral(ctx.stringLiteral());
+ this.sb.push(",'base64')");
+ }
}
/**
diff --git
a/gremlin-js/gremlin-javascript/lib/language/translator/PythonTranslateVisitor.ts
b/gremlin-js/gremlin-javascript/lib/language/translator/PythonTranslateVisitor.ts
index e74eb549c8..f8006a3f60 100644
---
a/gremlin-js/gremlin-javascript/lib/language/translator/PythonTranslateVisitor.ts
+++
b/gremlin-js/gremlin-javascript/lib/language/translator/PythonTranslateVisitor.ts
@@ -214,6 +214,32 @@ export default class PythonTranslateVisitor extends
TranslateVisitor {
this.sb.push(')');
}
+ visitCharacterLiteral(ctx: any): void {
+ const text: string = ctx.getText();
+ const withoutSuffix = text.substring(0, text.length - 1);
+ const inner =
TranslateVisitor.removeFirstAndLastCharacters(withoutSuffix);
+ this.sb.push("SingleChar('");
+ this.sb.push(inner);
+ this.sb.push("')");
+ }
+
+ visitDurationLiteral(ctx: any): void {
+ const iso8601 =
TranslateVisitor.removeFirstAndLastCharacters(ctx.stringLiteral().getText());
+ const { totalSeconds, nanos } = parseISO8601Duration(iso8601);
+ if (nanos === 0) {
+ this.sb.push(`timedelta(seconds=${totalSeconds})`);
+ } else {
+ const micros = Math.floor(nanos / 1000);
+
this.sb.push(`timedelta(seconds=${totalSeconds},microseconds=${micros})`);
+ }
+ }
+
+ visitBinaryLiteral(ctx: any): void {
+ this.sb.push('base64.b64decode(');
+ this.visitStringLiteral(ctx.stringLiteral());
+ this.sb.push(')');
+ }
+
visitTraversalStrategy(ctx: any): void {
if (ctx.getChildCount() === 1) {
this.sb.push(ctx.getText());
@@ -319,3 +345,46 @@ function convertCamelCaseToSnakeCase(camelCase: string):
string {
}
return result;
}
+
+/**
+ * Parses an ISO-8601 duration string (as produced by Java's
Duration.toString())
+ * into total seconds and nanoseconds, matching the Java
PythonTranslateVisitor behavior.
+ */
+function parseISO8601Duration(iso8601: string): { totalSeconds: number; nanos:
number } {
+ // Use a regex that matches Java's Duration.toString() output
+ const match =
iso8601.match(/^(-?)P(?:(\d+)D)?T?(?:(-?\d+)H)?(?:(-?\d+)M)?(?:(-?\d+(?:\.\d+)?)S)?$/);
+ if (!match) {
+ throw new TranslatorException(`Invalid ISO-8601 duration: ${iso8601}`);
+ }
+ const negative = match[1] === '-';
+ const days = match[2] ? parseInt(match[2], 10) : 0;
+ const hours = match[3] ? parseInt(match[3], 10) : 0;
+ const minutes = match[4] ? parseInt(match[4], 10) : 0;
+
+ let totalSeconds = days * 86400 + hours * 3600 + minutes * 60;
+ let nanos = 0;
+
+ if (match[5]) {
+ const secParts = match[5].split('.');
+ totalSeconds += parseInt(secParts[0], 10);
+ if (secParts.length > 1) {
+ nanos = parseInt(secParts[1].padEnd(9, '0').substring(0, 9), 10);
+ if (parseInt(secParts[0], 10) < 0 || (parseInt(secParts[0], 10)
=== 0 && match[5].startsWith('-'))) {
+ nanos = -nanos;
+ }
+ }
+ }
+
+ if (negative) {
+ totalSeconds = -totalSeconds;
+ nanos = -nanos;
+ }
+
+ // Normalize so nanos is always 0..999999999 (matching Java's Duration
representation)
+ if (nanos < 0) {
+ totalSeconds -= 1;
+ nanos += 1_000_000_000;
+ }
+
+ return { totalSeconds, nanos };
+}
diff --git
a/gremlin-js/gremlin-javascript/lib/language/translator/TranslateVisitor.ts
b/gremlin-js/gremlin-javascript/lib/language/translator/TranslateVisitor.ts
index dcf2b77aa1..207a53c40a 100644
--- a/gremlin-js/gremlin-javascript/lib/language/translator/TranslateVisitor.ts
+++ b/gremlin-js/gremlin-javascript/lib/language/translator/TranslateVisitor.ts
@@ -394,6 +394,18 @@ export default class TranslateVisitor {
this.sb.push(ctx.getText());
}
+ visitCharacterLiteral(ctx: any): void {
+ this.sb.push(ctx.getText());
+ }
+
+ visitDurationLiteral(ctx: any): void {
+ this.sb.push(ctx.getText());
+ }
+
+ visitBinaryLiteral(ctx: any): void {
+ this.sb.push(ctx.getText());
+ }
+
visitStringLiteral(ctx: any): void {
const text =
TranslateVisitor.removeFirstAndLastCharacters(ctx.getText());
this.handleStringLiteralText(text);
diff --git a/gremlin-js/gremlin-javascript/scripts/groovy/generate.groovy
b/gremlin-js/gremlin-javascript/scripts/groovy/generate.groovy
index 94824d3331..bfde0e275b 100644
--- a/gremlin-js/gremlin-javascript/scripts/groovy/generate.groovy
+++ b/gremlin-js/gremlin-javascript/scripts/groovy/generate.groovy
@@ -106,7 +106,15 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer ->
writer.write(" ")
writer.write(k)
writer.write(": [")
- def collected = v.collect { GremlinTranslator.translate(it,
Translator.JAVASCRIPT) }
+ def collected = v.collect {
+ try {
+ GremlinTranslator.translate(it, Translator.JAVASCRIPT)
+ } catch (ignored) {
+ // fall back to the original gremlin-lang form for
unsupported literals;
+ // the scenario will be skipped at test runtime via tag
exclusions
+ GremlinTranslator.translate(it, Translator.LANGUAGE)
+ }
+ }
def uniqueBindings = collected.collect { it.getParameters()
}.flatten().unique()
def gremlinItty = collected.iterator()
while (gremlinItty.hasNext()) {
diff --git a/gremlin-js/gremlin-javascript/test/cucumber/feature-steps.js
b/gremlin-js/gremlin-javascript/test/cucumber/feature-steps.js
index e8ac2d1c1c..08830a407e 100644
--- a/gremlin-js/gremlin-javascript/test/cucumber/feature-steps.js
+++ b/gremlin-js/gremlin-javascript/test/cucumber/feature-steps.js
@@ -36,6 +36,7 @@ import { toLong } from '../../lib/utils.js';
import anon from '../../lib/process/anonymous-traversal.js';
const __ = statics;
import { deepMembersById } from './element-comparison.js';
+import { Buffer } from 'buffer';
import GremlinLang from "../../lib/process/gremlin-lang.js";
const parsers = [
[ 'str\\[(.*)\\]', (stringValue) => stringValue ], //returns the string
value as is
@@ -44,6 +45,7 @@ const parsers = [
[ 'dt\\[(.+)\\]', toDateTime ],
[ 'uuid\\[(.+)\\]', toUuid ],
[ 'd\\[(.*)\\]\\.[bsilfdmn]', toNumeric ],
+ [ 'bin\\[(.*)\\]', toBinary ],
[ 'v\\[(.+)\\]', toVertex ],
[ 'v\\[(.+)\\]\\.id', toVertexId ],
[ 'v\\[(.+)\\]\\.sid', toVertexIdString ],
@@ -448,6 +450,10 @@ function toMerge(value) {
return merge[value];
}
+function toBinary(value) {
+ return Buffer.from(value, 'base64');
+}
+
function splitByElement(s) {
let depth = 0;
let current = '';
diff --git a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js
b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js
index 0bb0a484a4..877d893312 100644
--- a/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js
+++ b/gremlin-js/gremlin-javascript/test/cucumber/gremlin.js
@@ -227,6 +227,11 @@ const gremlins = {
g_V_valuesXintX_asNumberXGType_BIGINTX_isXtypeOfXGType_BIGINTXX_project_byXidentityX_byXmathXaddX999XXX:
[function({g}) { return g.addV("data").property("int", 50) }, function({g}) {
return
g.V().values("int").asNumber(GType.bigInt).is(P.typeOf(GType.bigInt)).project("original",
"added").by(__.identity()).by(__.math("_ + 999")) }],
g_injectX777X_asNumberXGType_BIGINTX_isXtypeOfXGType_BIGINTXX_groupCount:
[function({g}) { return
g.inject(777).asNumber(GType.bigInt).is(P.typeOf(GType.bigInt)).groupCount()
}],
g_V_valuesXageX_isXtypeOfXGType_BIGINTXX: [function({g}) { return
g.V().values("age").is(P.typeOf(GType.bigInt)) }],
+ g_injectXBinaryXAQIDXX: [function({g}) { return
g.inject(Buffer.from("AQID",'base64')) }],
+ g_injectXBinaryXemptyXX: [function({g}) { return
g.inject(Buffer.from("",'base64')) }],
+ g_injectXBinaryXAA_eqeqXX: [function({g}) { return
g.inject(Buffer.from("AA==",'base64')) }],
+ g_valuesXblobX_isXtypeOfXGType_BINARYXX: [function({g}) { return
g.addV("data").property("blob", Buffer.from("AQID",'base64')) }, function({g})
{ return g.V().values("blob").is(P.typeOf(GType.binary)) }],
+ g_injectXBinaryXAQIDXX_isXeqXBinaryXAQIDXXX: [function({g}) { return
g.inject(Buffer.from("AQID",'base64')).is(P.eq(Buffer.from("AQID",'base64')))
}],
g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX:
[function({g}) { return g.addV("data").property("int", 5) }, function({g}) {
return g.V().values("int").asNumber(GType.byte).is(P.typeOf(GType.byte)) }],
g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_mathXaddX20XX:
[function({g}) { return g.addV("data").property("int", 10) }, function({g}) {
return
g.V().values("int").asNumber(GType.byte).is(P.typeOf(GType.byte)).math("_ +
20") }],
g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_isXltX10XX:
[function({g}) { return g.addV("data").property("int", 7) }, function({g}) {
return
g.V().values("int").asNumber(GType.byte).is(P.typeOf(GType.byte)).is(P.lt(10))
}],
@@ -235,6 +240,12 @@ const gremlins = {
g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_chooseXisXeqX12XX_constantXtwelveX_constantXotherXX:
[function({g}) { return g.addV("data").property("int", 12) }, function({g}) {
return
g.V().values("int").asNumber(GType.byte).is(P.typeOf(GType.byte)).choose(__.is(P.eq(12)),
__.constant("twelve"), __.constant("other")) }],
g_injectX15X_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_groupCount:
[function({g}) { return
g.inject(15).asNumber(GType.byte).is(P.typeOf(GType.byte)).groupCount() }],
g_V_valuesXageX_isXtypeOfXGType_BYTEXX: [function({g}) { return
g.V().values("age").is(P.typeOf(GType.byte)) }],
+ g_injectXaX: [function({g}) { return g.inject("a"c) }],
+ g_injectXescaped_quoteX: [function({g}) { return g.inject("\""c) }],
+ g_injectXescaped_backslashX: [function({g}) { return g.inject("\\"c) }],
+ g_injectXunicodeX: [function({g}) { return g.inject("\u00E9"c) }],
+ g_valuesXinitialX_isXtypeOfXGType_CHARXX: [function({g}) { return
g.addV("data").property("initial", "a"c) }, function({g}) { return
g.V().values("initial").is(P.typeOf(GType.char)) }],
+ g_injectXaX_isXeqXaXX: [function({g}) { return
g.inject("a"c).is(P.eq("a"c)) }],
g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX: [function({g}) { return
g.addV("event").property("datetime", new Date('2023-08-08T00:00Z')) },
function({g}) { return g.V().values("datetime").is(P.typeOf(GType.datetime))
}],
g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX_project_byXidentityX_byXdateAddXDT_dayX1XX:
[function({g}) { return g.addV("event").property("datetime", new
Date('2023-08-08T00:00Z')) }, function({g}) { return
g.V().values("datetime").is(P.typeOf(GType.datetime)).project("original",
"nextDay").by(__.identity()).by(__.dateAdd(DT.day, 1)) }],
g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX_dateDiffXdatetimeX2023_08_10XX:
[function({g}) { return g.addV("event").property("datetime", new
Date('2023-08-08T00:00Z')) }, function({g}) { return
g.V().values("datetime").is(P.typeOf(GType.datetime)).dateDiff(new
Date('2023-08-08T00:00:30Z')) }],
@@ -254,6 +265,17 @@ const gremlins = {
g_V_valuesXdoubleX_isXtypeOfXGType_DOUBLEXX_order_byXascX: [function({g})
{ return g.addV("data").property("double", 3.2).addV("data").property("double",
1.8).addV("data").property("double", 2.5) }, function({g}) { return
g.V().values("double").is(P.typeOf(GType.double)).order().by(Order.asc) }],
g_injectX5_5dX_isXtypeOfXGType_DOUBLEXX_groupCount: [function({g}) {
return g.inject(5.5).is(P.typeOf(GType.double)).groupCount() }],
g_V_valuesXageX_isXtypeOfXGType_DOUBLEXX: [function({g}) { return
g.V().values("age").is(P.typeOf(GType.double)) }],
+ g_injectXDurationXPT2H30MXX: [function({g}) { return
g.inject(Duration("PT2H30M")) }],
+ g_injectXDurationXPT0SXX: [function({g}) { return
g.inject(Duration("PT0S")) }],
+ g_injectXDurationXPTneg30SXX: [function({g}) { return
g.inject(Duration("PT-30S")) }],
+ g_injectXDurationXnegPT30SXX: [function({g}) { return
g.inject(Duration("-PT30S")) }],
+ g_injectXDurationXPT0_5SXX: [function({g}) { return
g.inject(Duration("PT0.5S")) }],
+ g_injectXDurationXP1DT12HXX: [function({g}) { return
g.inject(Duration("P1DT12H")) }],
+ g_injectXDurationXP2DXX: [function({g}) { return g.inject(Duration("P2D"))
}],
+ g_injectXDurationXPT1H30M15SXX: [function({g}) { return
g.inject(Duration("PT1H30M15S")) }],
+ g_injectXDurationXPTneg0_5SXX: [function({g}) { return
g.inject(Duration("PT-0.5S")) }],
+ g_valuesXlengthX_isXtypeOfXGType_DURATIONXX: [function({g}) { return
g.addV("data").property("length", Duration("PT2H30M")) }, function({g}) {
return g.V().values("length").is(P.typeOf(GType.duration)) }],
+ g_injectXDurationXPT2H30MXX_isXgtXDurationXPT1HXXX: [function({g}) {
return g.inject(Duration("PT2H30M")).is(P.gt(Duration("PT1H"))) }],
g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX: [function({g}) { return
g.addV("data").property("float", 2.5) }, function({g}) { return
g.V().values("float").asNumber(GType.float).is(P.typeOf(GType.float)) }],
g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX_mathXmulX2XX: [function({g}) {
return g.addV("data").property("float", 3.0) }, function({g}) { return
g.V().values("float").asNumber(GType.float).is(P.typeOf(GType.float)).math("_ *
2") }],
g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX_isXeqX1_5XX: [function({g}) {
return g.addV("data").property("float", 1.5) }, function({g}) { return
g.V().values("float").asNumber(GType.float).is(P.typeOf(GType.float)).is(P.eq(1.5))
}],
diff --git a/gremlin-js/gremlin-javascript/test/cucumber/world.js
b/gremlin-js/gremlin-javascript/test/cucumber/world.js
index 1e66c8e73b..563abb8ce8 100644
--- a/gremlin-js/gremlin-javascript/test/cucumber/world.js
+++ b/gremlin-js/gremlin-javascript/test/cucumber/world.js
@@ -110,6 +110,14 @@ Before({tags: "@StepWrite"}, function() {
return 'skipped'
})
+Before({tags: "@DataChar"}, function() {
+ return 'skipped'
+})
+
+Before({tags: "@DataDuration"}, function() {
+ return 'skipped'
+})
+
function getVertices(connection) {
const g = anon.traversal().withRemote(connection);
return g.V().group().by('name').by(__.tail()).next().then(it => {
diff --git a/gremlin-js/gremlin-javascript/test/unit/gremlin-lang-test.js
b/gremlin-js/gremlin-javascript/test/unit/gremlin-lang-test.js
index 59197e14c3..be3a39c460 100644
--- a/gremlin-js/gremlin-javascript/test/unit/gremlin-lang-test.js
+++ b/gremlin-js/gremlin-javascript/test/unit/gremlin-lang-test.js
@@ -415,18 +415,7 @@ describe('GremlinLang', function () {
);
});
- it('should handle Buffer', function () {
- assert.strictEqual(g.inject(Buffer.from([1, 2,
3])).getGremlinLang().getGremlin(), 'g.inject(Binary("AQID"))');
- });
-
- it('should handle empty Buffer', function () {
-
assert.strictEqual(g.inject(Buffer.from([])).getGremlinLang().getGremlin(),
'g.inject(Binary(""))');
- });
-
- it('should handle Buffer with base64 padding', function () {
-
assert.strictEqual(g.inject(Buffer.from([0])).getGremlinLang().getGremlin(),
'g.inject(Binary("AA=="))');
- });
-
+ // Binary - Uint8Array type (JS-specific, distinct from Buffer)
it('should handle Uint8Array', function () {
assert.strictEqual(g.inject(new Uint8Array([1, 2,
3])).getGremlinLang().getGremlin(), 'g.inject(Binary("AQID"))');
});
diff --git
a/gremlin-js/gremlin-javascript/test/unit/translator/gremlin-translator-test.js
b/gremlin-js/gremlin-javascript/test/unit/translator/gremlin-translator-test.js
index 02866c3b37..4fddec1343 100644
---
a/gremlin-js/gremlin-javascript/test/unit/translator/gremlin-translator-test.js
+++
b/gremlin-js/gremlin-javascript/test/unit/translator/gremlin-translator-test.js
@@ -64,6 +64,8 @@ describe('GremlinTranslator', function () {
['g.inject(NaN)', 'g.inject(Number.NaN)'],
['g.inject(Infinity)', 'g.inject(Number.POSITIVE_INFINITY)'],
['g.inject(-Infinity)', 'g.inject(Number.NEGATIVE_INFINITY)'],
+ // Binary literal
+ ['g.inject(Binary("AQID"))', 'g.inject(Buffer.from("AQID",\'base64\'))'],
// terminal steps — pass through as-is in JavaScript
['g.V().toList()', 'g.V().toList()'],
['g.V().toSet()', 'g.V().toSet()'],
@@ -134,6 +136,20 @@ describe('GremlinTranslator', function () {
TranslatorException,
);
});
+
+ it('throws TranslatorException for character literal', function () {
+ assert.throws(
+ () => GremlinTranslator.translate('g.inject("a"c)'),
+ TranslatorException,
+ );
+ });
+
+ it('throws TranslatorException for duration literal', function () {
+ assert.throws(
+ () => GremlinTranslator.translate('g.inject(Duration("PT2H30M"))'),
+ TranslatorException,
+ );
+ });
});
});
@@ -180,6 +196,12 @@ describe('PythonTranslateVisitor', function () {
['g.inject([1,2,3])', 'g.inject([1, 2, 3])'],
// Set literal (non-empty)
['g.inject({1,2,3})', 'g.inject({1, 2, 3})'],
+ // Character literal
+ ['g.inject("a"c)', "g.inject(SingleChar('a'))"],
+ // Duration literal
+ ['g.inject(Duration("PT2H30M"))', 'g.inject(timedelta(seconds=9000))'],
+ // Binary literal
+ ['g.inject(Binary("AQID"))', "g.inject(base64.b64decode('AQID'))"],
// Map literal — T.id → T.id_ in Python
['g.addV("person").property(T.id, [(T.label): "person"])',
"g.add_v('person').property(T.id_, { T.label: 'person' })"],
// terminal steps — camelCase → snake_case
@@ -259,6 +281,10 @@ describe('GoTranslateVisitor', function () {
['g.inject(NaN)', 'g.Inject(math.NaN())'],
['g.inject(Infinity)', 'g.Inject(math.Inf(1))'],
['g.inject(-Infinity)', 'g.Inject(math.Inf(-1))'],
+ // Duration literal
+ ['g.inject(Duration("PT2H30M"))',
'g.Inject(time.Duration(9000000000000))'],
+ // Binary literal
+ ['g.inject(Binary("AQID"))', 'g.Inject(gremlingo.ByteBuffer{Data:
[]byte{1,2,3}})'],
// null → nil
['g.V().has("name", null)', 'g.V().Has("name", nil)'],
// terminal steps — PascalCase
@@ -302,6 +328,13 @@ describe('GoTranslateVisitor', function () {
TranslatorException,
);
});
+
+ it('throws TranslatorException for character literal', function () {
+ assert.throws(
+ () => GremlinTranslator.translate('g.inject("a"c)', 'g', 'GO'),
+ TranslatorException,
+ );
+ });
});
});
@@ -336,6 +369,12 @@ describe('DotNetTranslateVisitor', function () {
// Collections
['g.inject([1,2,3])', 'g.Inject<object>(new List<object> { 1, 2, 3 })'],
['g.inject({1,2,3})', 'g.Inject<object>(new HashSet<object> { 1, 2, 3
})'],
+ // Character literal
+ ['g.inject("a"c)', "g.Inject<object>('a')"],
+ // Duration literal
+ ['g.inject(Duration("PT2H30M"))',
'g.Inject<object>(XmlConvert.ToTimeSpan("PT2H30M"))'],
+ // Binary literal
+ ['g.inject(Binary("AQID"))',
'g.Inject<object>(Convert.FromBase64String("AQID"))'],
// Map literal
['g.addV("person").property(T.id, [(T.label): "person"])',
'g.AddV((string) "person").Property(T.Id, new Dictionary<object, object> {{
T.Label, "person" }})'],
// values gets <object>
@@ -421,6 +460,12 @@ describe('JavaTranslateVisitor', function () {
['g.inject([1,2,3])', 'g.inject(new ArrayList<Object>() {{ add(1);
add(2); add(3); }})'],
// Set literal
['g.inject({1,2,3})', 'g.inject(new HashSet<Object>() {{ add(1); add(2);
add(3); }})'],
+ // Character literal
+ ['g.inject("a"c)', "g.inject('a')"],
+ // Duration literal
+ ['g.inject(Duration("PT2H30M"))', 'g.inject(Duration.parse("PT2H30M"))'],
+ // Binary literal
+ ['g.inject(Binary("AQID"))',
'g.inject(ByteBuffer.wrap(Base64.getDecoder().decode("AQID")))'],
// Map literal
['g.addV("person").property(T.id, [(T.label): "person"])',
'g.addV("person").property(T.id, new LinkedHashMap<Object, Object>() {{
put(T.label, "person"); }})'],
// terminal steps — pass through as-is in Java
@@ -499,6 +544,12 @@ describe('GroovyTranslateVisitor', function () {
['g.inject([1,2,3])', 'g.inject([1, 2, 3])'],
// Set literal — Groovy: [items] as Set
['g.inject({1,2,3})', 'g.inject([1, 2, 3] as Set)'],
+ // Character literal
+ ['g.inject("a"c)', "g.inject('a' as char)"],
+ // Duration literal
+ ['g.inject(Duration("PT2H30M"))', 'g.inject(Duration.parse("PT2H30M"))'],
+ // Binary literal
+ ['g.inject(Binary("AQID"))',
'g.inject(ByteBuffer.wrap(Base64.getDecoder().decode("AQID")))'],
// Map literal — Groovy preserves parens on expression keys
['g.addV("person").property(T.id, [(T.label): "person"])',
'g.addV("person").property(T.id, [(T.label):"person"])'],
// null as Map in mergeV
@@ -584,6 +635,12 @@ describe('AnonymizedTranslateVisitor', function () {
['g.inject([1,2,3])', 'g.inject(list0)'],
// set → set
['g.inject({1,2,3})', 'g.inject(set0)'],
+ // character → character
+ ['g.inject("a"c)', 'g.inject(character0)'],
+ // duration → duration
+ ['g.inject(Duration("PT2H30M"))', 'g.inject(duration0)'],
+ // binary → bytebuffer
+ ['g.inject(Binary("AQID"))', 'g.inject(bytebuffer0)'],
// map → map
['g.addV("person").property(T.id, [(T.label): "person"])',
'g.addV(string0).property(T.id, map0)'],
// enums/traversal steps pass through unchanged
diff --git a/gremlin-python/src/main/python/tests/feature/feature_steps.py
b/gremlin-python/src/main/python/tests/feature/feature_steps.py
index f82b65ab55..7ed183413c 100644
--- a/gremlin-python/src/main/python/tests/feature/feature_steps.py
+++ b/gremlin-python/src/main/python/tests/feature/feature_steps.py
@@ -18,11 +18,12 @@
#
from collections.abc import Iterable
-from datetime import datetime
+from datetime import datetime, timedelta
+import base64
import json
import re
import uuid
-from gremlin_python.statics import long, bigdecimal
+from gremlin_python.statics import long, bigdecimal, SingleChar
from gremlin_python.structure.graph import Path, Vertex, Graph, Edge,
VertexProperty, Property
from gremlin_python.process.anonymous_traversal import traversal
from gremlin_python.process.graph_traversal import __
@@ -345,6 +346,18 @@ def _split_by_element(s):
return results
+def _parse_iso8601_duration(iso_str):
+ """Parses an ISO-8601 duration string (as produced by Java's
Duration.toString()) into a timedelta."""
+ m = re.match(r'^(-?)PT(?:(-?\d+)H)?(?:(-?\d+)M)?(?:(-?\d+(?:\.\d+)?)S)?$',
iso_str)
+ if not m:
+ raise ValueError("Invalid ISO-8601 duration: " + iso_str)
+ sign = -1 if m.group(1) == '-' else 1
+ hours = int(m.group(2)) if m.group(2) else 0
+ minutes = int(m.group(3)) if m.group(3) else 0
+ seconds = float(m.group(4)) if m.group(4) else 0.0
+ return timedelta(hours=sign * hours, minutes=sign * minutes, seconds=sign
* seconds)
+
+
def _convert(val, ctx):
graph_name = ctx.graph_name
if isinstance(val, dict): # convert dictionary keys/values
@@ -366,6 +379,12 @@ def _convert(val, ctx):
elif isinstance(val, str) and re.match(r"^uuid\[.*\]$", val): # parse uuid
name = val[5:-1] # strip 'uuid[...]' or similar format
return uuid.UUID(name)
+ elif isinstance(val, str) and re.match(r"^char\[.\]$", val): # parse char
+ return SingleChar(val[5:-1])
+ elif isinstance(val, str) and re.match(r"^dur\[.*\]$", val): # parse
duration
+ return _parse_iso8601_duration(val[4:-1])
+ elif isinstance(val, str) and re.match(r"^bin\[.*\]$", val): # parse
binary
+ return base64.b64decode(val[4:-1])
elif isinstance(val, str) and re.match(r"^d\[NaN\]$", val): # parse nan
return float("nan")
elif isinstance(val, str) and re.match(r"^d\[Infinity\]$", val): # parse
+inf
diff --git a/gremlin-python/src/main/python/tests/feature/gremlin.py
b/gremlin-python/src/main/python/tests/feature/gremlin.py
index 235b5f9384..d1c1d1c70e 100644
--- a/gremlin-python/src/main/python/tests/feature/gremlin.py
+++ b/gremlin-python/src/main/python/tests/feature/gremlin.py
@@ -199,6 +199,11 @@ world.gremlins = {
'g_V_valuesXintX_asNumberXGType_BIGINTX_isXtypeOfXGType_BIGINTXX_project_byXidentityX_byXmathXaddX999XXX':
[(lambda g:g.add_v('data').property('int', 50)), (lambda
g:g.V().values('int').as_number(GType.BIGINT).is_(P.type_of(GType.BIGINT)).project('original',
'added').by(__.identity()).by(__.math('_ + 999')))],
'g_injectX777X_asNumberXGType_BIGINTX_isXtypeOfXGType_BIGINTXX_groupCount':
[(lambda
g:g.inject(777).as_number(GType.BIGINT).is_(P.type_of(GType.BIGINT)).group_count())],
'g_V_valuesXageX_isXtypeOfXGType_BIGINTXX': [(lambda
g:g.V().values('age').is_(P.type_of(GType.BIGINT)))],
+ 'g_injectXBinaryXAQIDXX': [(lambda g:g.inject(base64.b64decode('AQID')))],
+ 'g_injectXBinaryXemptyXX': [(lambda g:g.inject(base64.b64decode('')))],
+ 'g_injectXBinaryXAA_eqeqXX': [(lambda
g:g.inject(base64.b64decode('AA==')))],
+ 'g_valuesXblobX_isXtypeOfXGType_BINARYXX': [(lambda
g:g.add_v('data').property('blob', base64.b64decode('AQID'))), (lambda
g:g.V().values('blob').is_(P.type_of(GType.BINARY)))],
+ 'g_injectXBinaryXAQIDXX_isXeqXBinaryXAQIDXXX': [(lambda
g:g.inject(base64.b64decode('AQID')).is_(P.eq(base64.b64decode('AQID'))))],
'g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX': [(lambda
g:g.add_v('data').property('int', 5)), (lambda
g:g.V().values('int').as_number(GType.BYTE).is_(P.type_of(GType.BYTE)))],
'g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_mathXaddX20XX':
[(lambda g:g.add_v('data').property('int', 10)), (lambda
g:g.V().values('int').as_number(GType.BYTE).is_(P.type_of(GType.BYTE)).math('_
+ 20'))],
'g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_isXltX10XX':
[(lambda g:g.add_v('data').property('int', 7)), (lambda
g:g.V().values('int').as_number(GType.BYTE).is_(P.type_of(GType.BYTE)).is_(P.lt(10)))],
@@ -207,6 +212,12 @@ world.gremlins = {
'g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_chooseXisXeqX12XX_constantXtwelveX_constantXotherXX':
[(lambda g:g.add_v('data').property('int', 12)), (lambda
g:g.V().values('int').as_number(GType.BYTE).is_(P.type_of(GType.BYTE)).choose(__.is_(P.eq(12)),
__.constant('twelve'), __.constant('other')))],
'g_injectX15X_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX_groupCount':
[(lambda
g:g.inject(15).as_number(GType.BYTE).is_(P.type_of(GType.BYTE)).group_count())],
'g_V_valuesXageX_isXtypeOfXGType_BYTEXX': [(lambda
g:g.V().values('age').is_(P.type_of(GType.BYTE)))],
+ 'g_injectXaX': [(lambda g:g.inject(SingleChar('a')))],
+ 'g_injectXescaped_quoteX': [(lambda g:g.inject(SingleChar('\"')))],
+ 'g_injectXescaped_backslashX': [(lambda g:g.inject(SingleChar('\\')))],
+ 'g_injectXunicodeX': [(lambda g:g.inject(SingleChar('\u00E9')))],
+ 'g_valuesXinitialX_isXtypeOfXGType_CHARXX': [(lambda
g:g.add_v('data').property('initial', SingleChar('a'))), (lambda
g:g.V().values('initial').is_(P.type_of(GType.CHAR)))],
+ 'g_injectXaX_isXeqXaXX': [(lambda
g:g.inject(SingleChar('a')).is_(P.eq(SingleChar('a'))))],
'g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX': [(lambda
g:g.add_v('event').property('datetime',
datetime.datetime.fromisoformat('2023-08-08T00:00+00:00'))), (lambda
g:g.V().values('datetime').is_(P.type_of(GType.DATETIME)))],
'g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX_project_byXidentityX_byXdateAddXDT_dayX1XX':
[(lambda g:g.add_v('event').property('datetime',
datetime.datetime.fromisoformat('2023-08-08T00:00+00:00'))), (lambda
g:g.V().values('datetime').is_(P.type_of(GType.DATETIME)).project('original',
'nextDay').by(__.identity()).by(__.date_add(DT.day, 1)))],
'g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX_dateDiffXdatetimeX2023_08_10XX':
[(lambda g:g.add_v('event').property('datetime',
datetime.datetime.fromisoformat('2023-08-08T00:00+00:00'))), (lambda
g:g.V().values('datetime').is_(P.type_of(GType.DATETIME)).date_diff(datetime.datetime.fromisoformat('2023-08-08T00:00:30+00:00')))],
@@ -226,6 +237,17 @@ world.gremlins = {
'g_V_valuesXdoubleX_isXtypeOfXGType_DOUBLEXX_order_byXascX': [(lambda
g:g.add_v('data').property('double', 3.2).add_v('data').property('double',
1.8).add_v('data').property('double', 2.5)), (lambda
g:g.V().values('double').is_(P.type_of(GType.DOUBLE)).order().by(Order.asc))],
'g_injectX5_5dX_isXtypeOfXGType_DOUBLEXX_groupCount': [(lambda
g:g.inject(5.5).is_(P.type_of(GType.DOUBLE)).group_count())],
'g_V_valuesXageX_isXtypeOfXGType_DOUBLEXX': [(lambda
g:g.V().values('age').is_(P.type_of(GType.DOUBLE)))],
+ 'g_injectXDurationXPT2H30MXX': [(lambda
g:g.inject(timedelta(seconds=9000)))],
+ 'g_injectXDurationXPT0SXX': [(lambda g:g.inject(timedelta(seconds=0)))],
+ 'g_injectXDurationXPTneg30SXX': [(lambda
g:g.inject(timedelta(seconds=-30)))],
+ 'g_injectXDurationXnegPT30SXX': [(lambda
g:g.inject(timedelta(seconds=-30)))],
+ 'g_injectXDurationXPT0_5SXX': [(lambda
g:g.inject(timedelta(seconds=0,microseconds=500000)))],
+ 'g_injectXDurationXP1DT12HXX': [(lambda
g:g.inject(timedelta(seconds=129600)))],
+ 'g_injectXDurationXP2DXX': [(lambda
g:g.inject(timedelta(seconds=172800)))],
+ 'g_injectXDurationXPT1H30M15SXX': [(lambda
g:g.inject(timedelta(seconds=5415)))],
+ 'g_injectXDurationXPTneg0_5SXX': [(lambda
g:g.inject(timedelta(seconds=-1,microseconds=500000)))],
+ 'g_valuesXlengthX_isXtypeOfXGType_DURATIONXX': [(lambda
g:g.add_v('data').property('length', timedelta(seconds=9000))), (lambda
g:g.V().values('length').is_(P.type_of(GType.DURATION)))],
+ 'g_injectXDurationXPT2H30MXX_isXgtXDurationXPT1HXXX': [(lambda
g:g.inject(timedelta(seconds=9000)).is_(P.gt(timedelta(seconds=3600))))],
'g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX': [(lambda
g:g.add_v('data').property('float', 2.5)), (lambda
g:g.V().values('float').as_number(GType.FLOAT).is_(P.type_of(GType.FLOAT)))],
'g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX_mathXmulX2XX': [(lambda
g:g.add_v('data').property('float', 3.0)), (lambda
g:g.V().values('float').as_number(GType.FLOAT).is_(P.type_of(GType.FLOAT)).math('_
* 2'))],
'g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX_isXeqX1_5XX': [(lambda
g:g.add_v('data').property('float', 1.5)), (lambda
g:g.V().values('float').as_number(GType.FLOAT).is_(P.type_of(GType.FLOAT)).is_(P.eq(1.5)))],
diff --git
a/gremlin-python/src/main/python/tests/unit/process/test_gremlin_lang.py
b/gremlin-python/src/main/python/tests/unit/process/test_gremlin_lang.py
index 40f0ecd33b..4f852d8e25 100644
--- a/gremlin-python/src/main/python/tests/unit/process/test_gremlin_lang.py
+++ b/gremlin-python/src/main/python/tests/unit/process/test_gremlin_lang.py
@@ -482,66 +482,19 @@ class TestGremlinLang(object):
tests.append([g.add_v('test').property('date', datetime(2021, 2, 1, 9,
30, tzinfo=timezone(timedelta(hours=-5)))),
"g.addV('test').property('date',datetime(\"2021-02-01T09:30:00-05:00\"))"])
+ # Character - single quote (no feature equivalent)
# 122
- tests.append([g.inject(SingleChar('a')),
- "g.inject('a'c)"])
-
- # 123 - double quote char: repr gives '"', so output is '"'c
- tests.append([g.inject(SingleChar('"')),
- "g.inject('\"'c)"])
-
- # 124 - single quote char: repr gives "'" (double-quoted), so output
is "'"c
tests.append([g.inject(SingleChar("'")),
"g.inject(\"'\"c)"])
-
- # 125 - backslash char: repr gives '\\', so output is '\\'c
- tests.append([g.inject(SingleChar('\\')),
- "g.inject('\\\\'c)"])
-
- # 126 - unicode char: repr gives 'é', so output is 'é'c
- tests.append([g.inject(SingleChar('\u00e9')),
- "g.inject('\u00e9'c)"])
- # 127
- tests.append([g.inject(timedelta(hours=2, minutes=30)),
- 'g.inject(Duration("PT2H30M"))'])
-
- # 128
- tests.append([g.inject(timedelta(0)),
- 'g.inject(Duration("PT0S"))'])
-
- # 129
- tests.append([g.inject(timedelta(seconds=-30)),
- 'g.inject(Duration("PT-30S"))'])
-
- # 130
- tests.append([g.inject(timedelta(milliseconds=500)),
- 'g.inject(Duration("PT0.5S"))'])
-
- # 131
+ # Duration - Python-specific: microsecond precision, days+hours
normalization
+ # 123
tests.append([g.inject(timedelta(microseconds=1)),
'g.inject(Duration("PT0.000001S"))'])
-
- # 132
- tests.append([g.inject(timedelta(milliseconds=-500)),
- 'g.inject(Duration("PT-0.5S"))'])
-
- # 133
+ # 124
tests.append([g.inject(timedelta(days=1, hours=12)),
'g.inject(Duration("PT36H"))'])
- # 134
- tests.append([g.inject(bytes([1, 2, 3])),
- 'g.inject(Binary("AQID"))'])
-
- # 135
- tests.append([g.inject(bytes()),
- 'g.inject(Binary(""))'])
-
- # 136
- tests.append([g.inject(bytes([0])),
- 'g.inject(Binary("AA=="))'])
-
for t in range(len(tests)):
gremlin_lang = tests[t][0].gremlin_lang.get_gremlin()
assert gremlin_lang == tests[t][1]
diff --git
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
index 09987991ac..208db90d46 100644
---
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
+++
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
@@ -85,6 +85,9 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import java.nio.ByteBuffer;
+import java.time.Duration;
+import java.util.Base64;
import java.util.UUID;
import static org.apache.commons.text.StringEscapeUtils.escapeJava;
@@ -165,6 +168,10 @@ public final class StepDefinition {
add(Pair.with(Pattern.compile("dt\\[(.*)\\]"), s ->
String.format("datetime('%s')", s)));
+ add(Pair.with(Pattern.compile("char\\[(.)\\]"), s ->
String.format("\"%s\"c", s)));
+ add(Pair.with(Pattern.compile("dur\\[(.*)\\]"), s ->
String.format("Duration(\"%s\")", s)));
+ add(Pair.with(Pattern.compile("bin\\[(.*)\\]"), s ->
String.format("Binary(\"%s\")", s)));
+
add(Pair.with(Pattern.compile("v\\[(.+)\\]\\.id"), s ->
world.convertIdToScript(g.V().has("name", s).id().next(), Vertex.class)));
add(Pair.with(Pattern.compile("v\\[(.+)\\]\\.sid"), s ->
world.convertIdToScript(g.V().has("name", s).id().next(), Vertex.class)));
add(Pair.with(Pattern.compile("e\\[(.+)\\]\\.id"), s ->
world.convertIdToScript(getEdgeId(g, s), Edge.class)));
@@ -237,6 +244,10 @@ public final class StepDefinition {
add(Pair.with(Pattern.compile("dt\\[(.*)\\]"), DatetimeHelper::parse));
add(Pair.with(Pattern.compile("uuid\\[(.*)\\]"), UUID::fromString));
+ add(Pair.with(Pattern.compile("char\\[(.)\\]"), s -> s.charAt(0)));
+ add(Pair.with(Pattern.compile("dur\\[(.*)\\]"), s ->
Duration.parse(s)));
+ add(Pair.with(Pattern.compile("bin\\[(.*)\\]"), s ->
ByteBuffer.wrap(Base64.getDecoder().decode(s))));
+
add(Pair.with(Pattern.compile("v\\[(.+)\\]\\.id"), s ->
g.V().has("name", s).id().next()));
add(Pair.with(Pattern.compile("v\\[(.+)\\]\\.sid"), s ->
g.V().has("name", s).id().next().toString()));
add(Pair.with(Pattern.compile("v\\[(.+)\\]"), s ->
detachVertex(g.V().has("name", s).next())));
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json
index 35df2cff6a..ce4a3b7cd1 100644
---
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/language/translator/translations.json
@@ -3010,6 +3010,103 @@
}
]
},
+ {
+ "scenario": "g_injectXBinaryXAQIDXX",
+ "traversals": [
+ {
+ "original": "g.inject(Binary(\"AQID\"))",
+ "language": "g.inject(Binary(\"AQID\"))",
+ "canonical": "g.inject(Binary(\"AQID\"))",
+ "anonymized": "g.inject(bytebuffer0)",
+ "dotnet":
"g.Inject<object>(Convert.FromBase64String(\"AQID\"))",
+ "go": "g.Inject(gremlingo.ByteBuffer{Data: []byte{1,2,3}})",
+ "groovy":
"g.inject(ByteBuffer.wrap(Base64.getDecoder().decode(\"AQID\")))",
+ "java":
"g.inject(ByteBuffer.wrap(Base64.getDecoder().decode(\"AQID\")))",
+ "javascript": "g.inject(Buffer.from(\"AQID\",'base64'))",
+ "python": "g.inject(base64.b64decode('AQID'))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXBinaryXemptyXX",
+ "traversals": [
+ {
+ "original": "g.inject(Binary(\"\"))",
+ "language": "g.inject(Binary(\"\"))",
+ "canonical": "g.inject(Binary(\"\"))",
+ "anonymized": "g.inject(bytebuffer0)",
+ "dotnet": "g.Inject<object>(Convert.FromBase64String(\"\"))",
+ "go": "g.Inject(gremlingo.ByteBuffer{Data: []byte{}})",
+ "groovy":
"g.inject(ByteBuffer.wrap(Base64.getDecoder().decode(\"\")))",
+ "java":
"g.inject(ByteBuffer.wrap(Base64.getDecoder().decode(\"\")))",
+ "javascript": "g.inject(Buffer.from(\"\",'base64'))",
+ "python": "g.inject(base64.b64decode(''))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXBinaryXAA_eqeqXX",
+ "traversals": [
+ {
+ "original": "g.inject(Binary(\"AA==\"))",
+ "language": "g.inject(Binary(\"AA==\"))",
+ "canonical": "g.inject(Binary(\"AA==\"))",
+ "anonymized": "g.inject(bytebuffer0)",
+ "dotnet":
"g.Inject<object>(Convert.FromBase64String(\"AA==\"))",
+ "go": "g.Inject(gremlingo.ByteBuffer{Data: []byte{0}})",
+ "groovy":
"g.inject(ByteBuffer.wrap(Base64.getDecoder().decode(\"AA==\")))",
+ "java":
"g.inject(ByteBuffer.wrap(Base64.getDecoder().decode(\"AA==\")))",
+ "javascript": "g.inject(Buffer.from(\"AA==\",'base64'))",
+ "python": "g.inject(base64.b64decode('AA=='))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_valuesXblobX_isXtypeOfXGType_BINARYXX",
+ "traversals": [
+ {
+ "original": "g.addV(\"data\").property(\"blob\",
Binary(\"AQID\"))",
+ "language": "g.addV(\"data\").property(\"blob\",
Binary(\"AQID\"))",
+ "canonical": "g.addV(\"data\").property(\"blob\",
Binary(\"AQID\"))",
+ "anonymized": "g.addV(string0).property(string1, bytebuffer0)",
+ "dotnet": "g.AddV((string) \"data\").Property(\"blob\",
Convert.FromBase64String(\"AQID\"))",
+ "go": "g.AddV(\"data\").Property(\"blob\",
gremlingo.ByteBuffer{Data: []byte{1,2,3}})",
+ "groovy": "g.addV(\"data\").property(\"blob\",
ByteBuffer.wrap(Base64.getDecoder().decode(\"AQID\")))",
+ "java": "g.addV(\"data\").property(\"blob\",
ByteBuffer.wrap(Base64.getDecoder().decode(\"AQID\")))",
+ "javascript": "g.addV(\"data\").property(\"blob\",
Buffer.from(\"AQID\",'base64'))",
+ "python": "g.add_v('data').property('blob',
base64.b64decode('AQID'))"
+ },
+ {
+ "original":
"g.V().values(\"blob\").is(P.typeOf(GType.BINARY))",
+ "language":
"g.V().values(\"blob\").is(P.typeOf(GType.BINARY))",
+ "canonical":
"g.V().values(\"blob\").is(P.typeOf(GType.BINARY))",
+ "anonymized":
"g.V().values(string0).is(P.typeOf(GType.BINARY))",
+ "dotnet":
"g.V().Values<object>(\"blob\").Is(P.TypeOf(GType.Binary))",
+ "go":
"g.V().Values(\"blob\").Is(gremlingo.P.TypeOf(gremlingo.GType.Binary))",
+ "groovy": "g.V().values(\"blob\").is(P.typeOf(GType.BINARY))",
+ "java": "g.V().values(\"blob\").is(P.typeOf(GType.BINARY))",
+ "javascript":
"g.V().values(\"blob\").is(P.typeOf(GType.binary))",
+ "python": "g.V().values('blob').is_(P.type_of(GType.BINARY))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXBinaryXAQIDXX_isXeqXBinaryXAQIDXXX",
+ "traversals": [
+ {
+ "original":
"g.inject(Binary(\"AQID\")).is(P.eq(Binary(\"AQID\")))",
+ "language":
"g.inject(Binary(\"AQID\")).is(P.eq(Binary(\"AQID\")))",
+ "canonical":
"g.inject(Binary(\"AQID\")).is(P.eq(Binary(\"AQID\")))",
+ "anonymized": "g.inject(bytebuffer0).is(P.eq(bytebuffer0))",
+ "dotnet":
"g.Inject<object>(Convert.FromBase64String(\"AQID\")).Is(P.Eq(Convert.FromBase64String(\"AQID\")))",
+ "go": "g.Inject(gremlingo.ByteBuffer{Data:
[]byte{1,2,3}}).Is(gremlingo.P.Eq(gremlingo.ByteBuffer{Data: []byte{1,2,3}}))",
+ "groovy":
"g.inject(ByteBuffer.wrap(Base64.getDecoder().decode(\"AQID\"))).is(P.eq(ByteBuffer.wrap(Base64.getDecoder().decode(\"AQID\"))))",
+ "java":
"g.inject(ByteBuffer.wrap(Base64.getDecoder().decode(\"AQID\"))).is(P.eq(ByteBuffer.wrap(Base64.getDecoder().decode(\"AQID\"))))",
+ "javascript":
"g.inject(Buffer.from(\"AQID\",'base64')).is(P.eq(Buffer.from(\"AQID\",'base64')))",
+ "python":
"g.inject(base64.b64decode('AQID')).is_(P.eq(base64.b64decode('AQID')))"
+ }
+ ]
+ },
{
"scenario":
"g_V_valuesXintX_asNumberXGType_BYTEX_isXtypeOfXGType_BYTEXX",
"traversals": [
@@ -3218,6 +3315,108 @@
}
]
},
+ {
+ "scenario": "g_injectXaX",
+ "traversals": [
+ {
+ "original": "g.inject(\"a\"c)",
+ "language": "g.inject(\"a\"c)",
+ "canonical": "g.inject(\"a\"c)",
+ "anonymized": "g.inject(character0)",
+ "dotnet": "g.Inject<object>('a')",
+ "groovy": "g.inject('a' as char)",
+ "java": "g.inject('a')",
+ "python": "g.inject(SingleChar('a'))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXescaped_quoteX",
+ "traversals": [
+ {
+ "original": "g.inject(\"\\\"\"c)",
+ "language": "g.inject(\"\\\"\"c)",
+ "canonical": "g.inject(\"\\\"\"c)",
+ "anonymized": "g.inject(character0)",
+ "dotnet": "g.Inject<object>('\\\"')",
+ "groovy": "g.inject('\\\"' as char)",
+ "java": "g.inject('\\\"')",
+ "python": "g.inject(SingleChar('\\\"'))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXescaped_backslashX",
+ "traversals": [
+ {
+ "original": "g.inject(\"\\\\\"c)",
+ "language": "g.inject(\"\\\\\"c)",
+ "canonical": "g.inject(\"\\\\\"c)",
+ "anonymized": "g.inject(character0)",
+ "dotnet": "g.Inject<object>('\\\\')",
+ "groovy": "g.inject('\\\\' as char)",
+ "java": "g.inject('\\\\')",
+ "python": "g.inject(SingleChar('\\\\'))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXunicodeX",
+ "traversals": [
+ {
+ "original": "g.inject(\"\\u00E9\"c)",
+ "language": "g.inject(\"\\u00E9\"c)",
+ "canonical": "g.inject(\"\\u00E9\"c)",
+ "anonymized": "g.inject(character0)",
+ "dotnet": "g.Inject<object>('\\u00E9')",
+ "groovy": "g.inject('\\u00E9' as char)",
+ "java": "g.inject('\\u00E9')",
+ "python": "g.inject(SingleChar('\\u00E9'))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_valuesXinitialX_isXtypeOfXGType_CHARXX",
+ "traversals": [
+ {
+ "original": "g.addV(\"data\").property(\"initial\", \"a\"c)",
+ "language": "g.addV(\"data\").property(\"initial\", \"a\"c)",
+ "canonical": "g.addV(\"data\").property(\"initial\", \"a\"c)",
+ "anonymized": "g.addV(string0).property(string1, character0)",
+ "dotnet": "g.AddV((string) \"data\").Property(\"initial\",
'a')",
+ "groovy": "g.addV(\"data\").property(\"initial\", 'a' as
char)",
+ "java": "g.addV(\"data\").property(\"initial\", 'a')",
+ "python": "g.add_v('data').property('initial',
SingleChar('a'))"
+ },
+ {
+ "original":
"g.V().values(\"initial\").is(P.typeOf(GType.CHAR))",
+ "language":
"g.V().values(\"initial\").is(P.typeOf(GType.CHAR))",
+ "canonical":
"g.V().values(\"initial\").is(P.typeOf(GType.CHAR))",
+ "anonymized": "g.V().values(string0).is(P.typeOf(GType.CHAR))",
+ "dotnet":
"g.V().Values<object>(\"initial\").Is(P.TypeOf(GType.Char))",
+ "go":
"g.V().Values(\"initial\").Is(gremlingo.P.TypeOf(gremlingo.GType.Char))",
+ "groovy": "g.V().values(\"initial\").is(P.typeOf(GType.CHAR))",
+ "java": "g.V().values(\"initial\").is(P.typeOf(GType.CHAR))",
+ "javascript":
"g.V().values(\"initial\").is(P.typeOf(GType.char))",
+ "python": "g.V().values('initial').is_(P.type_of(GType.CHAR))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXaX_isXeqXaXX",
+ "traversals": [
+ {
+ "original": "g.inject(\"a\"c).is(P.eq(\"a\"c))",
+ "language": "g.inject(\"a\"c).is(P.eq(\"a\"c))",
+ "canonical": "g.inject(\"a\"c).is(P.eq(\"a\"c))",
+ "anonymized": "g.inject(character0).is(P.eq(character0))",
+ "dotnet": "g.Inject<object>('a').Is(P.Eq('a'))",
+ "groovy": "g.inject('a' as char).is(P.eq('a' as char))",
+ "java": "g.inject('a').is(P.eq('a'))",
+ "python":
"g.inject(SingleChar('a')).is_(P.eq(SingleChar('a')))"
+ }
+ ]
+ },
{
"scenario": "g_V_valuesXdatetimeX_isXtypeOfXGType_DATETIMEXX",
"traversals": [
@@ -3709,6 +3908,194 @@
}
]
},
+ {
+ "scenario": "g_injectXDurationXPT2H30MXX",
+ "traversals": [
+ {
+ "original": "g.inject(Duration(\"PT2H30M\"))",
+ "language": "g.inject(Duration(\"PT2H30M\"))",
+ "canonical": "g.inject(Duration(\"PT2H30M\"))",
+ "anonymized": "g.inject(duration0)",
+ "dotnet":
"g.Inject<object>(XmlConvert.ToTimeSpan(\"PT2H30M\"))",
+ "go": "g.Inject(time.Duration(9000000000000))",
+ "groovy": "g.inject(Duration.parse(\"PT2H30M\"))",
+ "java": "g.inject(Duration.parse(\"PT2H30M\"))",
+ "python": "g.inject(timedelta(seconds=9000))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXDurationXPT0SXX",
+ "traversals": [
+ {
+ "original": "g.inject(Duration(\"PT0S\"))",
+ "language": "g.inject(Duration(\"PT0S\"))",
+ "canonical": "g.inject(Duration(\"PT0S\"))",
+ "anonymized": "g.inject(duration0)",
+ "dotnet": "g.Inject<object>(XmlConvert.ToTimeSpan(\"PT0S\"))",
+ "go": "g.Inject(time.Duration(0))",
+ "groovy": "g.inject(Duration.parse(\"PT0S\"))",
+ "java": "g.inject(Duration.parse(\"PT0S\"))",
+ "python": "g.inject(timedelta(seconds=0))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXDurationXPTneg30SXX",
+ "traversals": [
+ {
+ "original": "g.inject(Duration(\"PT-30S\"))",
+ "language": "g.inject(Duration(\"PT-30S\"))",
+ "canonical": "g.inject(Duration(\"PT-30S\"))",
+ "anonymized": "g.inject(duration0)",
+ "dotnet":
"g.Inject<object>(XmlConvert.ToTimeSpan(\"PT-30S\"))",
+ "go": "g.Inject(time.Duration(-30000000000))",
+ "groovy": "g.inject(Duration.parse(\"PT-30S\"))",
+ "java": "g.inject(Duration.parse(\"PT-30S\"))",
+ "python": "g.inject(timedelta(seconds=-30))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXDurationXnegPT30SXX",
+ "traversals": [
+ {
+ "original": "g.inject(Duration(\"-PT30S\"))",
+ "language": "g.inject(Duration(\"-PT30S\"))",
+ "canonical": "g.inject(Duration(\"-PT30S\"))",
+ "anonymized": "g.inject(duration0)",
+ "dotnet":
"g.Inject<object>(XmlConvert.ToTimeSpan(\"-PT30S\"))",
+ "go": "g.Inject(time.Duration(-30000000000))",
+ "groovy": "g.inject(Duration.parse(\"-PT30S\"))",
+ "java": "g.inject(Duration.parse(\"-PT30S\"))",
+ "python": "g.inject(timedelta(seconds=-30))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXDurationXPT0_5SXX",
+ "traversals": [
+ {
+ "original": "g.inject(Duration(\"PT0.5S\"))",
+ "language": "g.inject(Duration(\"PT0.5S\"))",
+ "canonical": "g.inject(Duration(\"PT0.5S\"))",
+ "anonymized": "g.inject(duration0)",
+ "dotnet":
"g.Inject<object>(XmlConvert.ToTimeSpan(\"PT0.5S\"))",
+ "go": "g.Inject(time.Duration(500000000))",
+ "groovy": "g.inject(Duration.parse(\"PT0.5S\"))",
+ "java": "g.inject(Duration.parse(\"PT0.5S\"))",
+ "python": "g.inject(timedelta(seconds=0,microseconds=500000))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXDurationXP1DT12HXX",
+ "traversals": [
+ {
+ "original": "g.inject(Duration(\"P1DT12H\"))",
+ "language": "g.inject(Duration(\"P1DT12H\"))",
+ "canonical": "g.inject(Duration(\"P1DT12H\"))",
+ "anonymized": "g.inject(duration0)",
+ "dotnet":
"g.Inject<object>(XmlConvert.ToTimeSpan(\"P1DT12H\"))",
+ "go": "g.Inject(time.Duration(129600000000000))",
+ "groovy": "g.inject(Duration.parse(\"P1DT12H\"))",
+ "java": "g.inject(Duration.parse(\"P1DT12H\"))",
+ "python": "g.inject(timedelta(seconds=129600))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXDurationXP2DXX",
+ "traversals": [
+ {
+ "original": "g.inject(Duration(\"P2D\"))",
+ "language": "g.inject(Duration(\"P2D\"))",
+ "canonical": "g.inject(Duration(\"P2D\"))",
+ "anonymized": "g.inject(duration0)",
+ "dotnet": "g.Inject<object>(XmlConvert.ToTimeSpan(\"P2D\"))",
+ "go": "g.Inject(time.Duration(172800000000000))",
+ "groovy": "g.inject(Duration.parse(\"P2D\"))",
+ "java": "g.inject(Duration.parse(\"P2D\"))",
+ "python": "g.inject(timedelta(seconds=172800))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXDurationXPT1H30M15SXX",
+ "traversals": [
+ {
+ "original": "g.inject(Duration(\"PT1H30M15S\"))",
+ "language": "g.inject(Duration(\"PT1H30M15S\"))",
+ "canonical": "g.inject(Duration(\"PT1H30M15S\"))",
+ "anonymized": "g.inject(duration0)",
+ "dotnet":
"g.Inject<object>(XmlConvert.ToTimeSpan(\"PT1H30M15S\"))",
+ "go": "g.Inject(time.Duration(5415000000000))",
+ "groovy": "g.inject(Duration.parse(\"PT1H30M15S\"))",
+ "java": "g.inject(Duration.parse(\"PT1H30M15S\"))",
+ "python": "g.inject(timedelta(seconds=5415))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXDurationXPTneg0_5SXX",
+ "traversals": [
+ {
+ "original": "g.inject(Duration(\"PT-0.5S\"))",
+ "language": "g.inject(Duration(\"PT-0.5S\"))",
+ "canonical": "g.inject(Duration(\"PT-0.5S\"))",
+ "anonymized": "g.inject(duration0)",
+ "dotnet":
"g.Inject<object>(XmlConvert.ToTimeSpan(\"PT-0.5S\"))",
+ "go": "g.Inject(time.Duration(-500000000))",
+ "groovy": "g.inject(Duration.parse(\"PT-0.5S\"))",
+ "java": "g.inject(Duration.parse(\"PT-0.5S\"))",
+ "python": "g.inject(timedelta(seconds=-1,microseconds=500000))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_valuesXlengthX_isXtypeOfXGType_DURATIONXX",
+ "traversals": [
+ {
+ "original": "g.addV(\"data\").property(\"length\",
Duration(\"PT2H30M\"))",
+ "language": "g.addV(\"data\").property(\"length\",
Duration(\"PT2H30M\"))",
+ "canonical": "g.addV(\"data\").property(\"length\",
Duration(\"PT2H30M\"))",
+ "anonymized": "g.addV(string0).property(string1, duration0)",
+ "dotnet": "g.AddV((string) \"data\").Property(\"length\",
XmlConvert.ToTimeSpan(\"PT2H30M\"))",
+ "go": "g.AddV(\"data\").Property(\"length\",
time.Duration(9000000000000))",
+ "groovy": "g.addV(\"data\").property(\"length\",
Duration.parse(\"PT2H30M\"))",
+ "java": "g.addV(\"data\").property(\"length\",
Duration.parse(\"PT2H30M\"))",
+ "python": "g.add_v('data').property('length',
timedelta(seconds=9000))"
+ },
+ {
+ "original":
"g.V().values(\"length\").is(P.typeOf(GType.DURATION))",
+ "language":
"g.V().values(\"length\").is(P.typeOf(GType.DURATION))",
+ "canonical":
"g.V().values(\"length\").is(P.typeOf(GType.DURATION))",
+ "anonymized":
"g.V().values(string0).is(P.typeOf(GType.DURATION))",
+ "dotnet":
"g.V().Values<object>(\"length\").Is(P.TypeOf(GType.Duration))",
+ "go":
"g.V().Values(\"length\").Is(gremlingo.P.TypeOf(gremlingo.GType.Duration))",
+ "groovy":
"g.V().values(\"length\").is(P.typeOf(GType.DURATION))",
+ "java":
"g.V().values(\"length\").is(P.typeOf(GType.DURATION))",
+ "javascript":
"g.V().values(\"length\").is(P.typeOf(GType.duration))",
+ "python":
"g.V().values('length').is_(P.type_of(GType.DURATION))"
+ }
+ ]
+ },
+ {
+ "scenario": "g_injectXDurationXPT2H30MXX_isXgtXDurationXPT1HXXX",
+ "traversals": [
+ {
+ "original":
"g.inject(Duration(\"PT2H30M\")).is(P.gt(Duration(\"PT1H\")))",
+ "language":
"g.inject(Duration(\"PT2H30M\")).is(P.gt(Duration(\"PT1H\")))",
+ "canonical":
"g.inject(Duration(\"PT2H30M\")).is(P.gt(Duration(\"PT1H\")))",
+ "anonymized": "g.inject(duration0).is(P.gt(duration1))",
+ "dotnet":
"g.Inject<object>(XmlConvert.ToTimeSpan(\"PT2H30M\")).Is(P.Gt(XmlConvert.ToTimeSpan(\"PT1H\")))",
+ "go":
"g.Inject(time.Duration(9000000000000)).Is(gremlingo.P.Gt(time.Duration(3600000000000)))",
+ "groovy":
"g.inject(Duration.parse(\"PT2H30M\")).is(P.gt(Duration.parse(\"PT1H\")))",
+ "java":
"g.inject(Duration.parse(\"PT2H30M\")).is(P.gt(Duration.parse(\"PT1H\")))",
+ "python":
"g.inject(timedelta(seconds=9000)).is_(P.gt(timedelta(seconds=3600)))"
+ }
+ ]
+ },
{
"scenario": "g_V_valuesXfloatX_isXtypeOfXGType_FLOATXX",
"traversals": [
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/data/Binary.feature
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/data/Binary.feature
new file mode 100644
index 0000000000..c8f365c268
--- /dev/null
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/data/Binary.feature
@@ -0,0 +1,82 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+@StepClassData @DataBinary
+Feature: Data - BINARY
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXBinaryXAQIDXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Binary("AQID"))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | bin[AQID] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXBinaryXemptyXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Binary(""))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | bin[] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXBinaryXAA_eqeqXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Binary("AA=="))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | bin[AA==] |
+
+ Scenario: g_valuesXblobX_isXtypeOfXGType_BINARYXX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("data").property("blob", Binary("AQID"))
+ """
+ And the traversal of
+ """
+ g.V().values("blob").is(P.typeOf(GType.BINARY))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | bin[AQID] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXBinaryXAQIDXX_isXeqXBinaryXAQIDXXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Binary("AQID")).is(P.eq(Binary("AQID")))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | bin[AQID] |
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/data/Char.feature
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/data/Char.feature
new file mode 100644
index 0000000000..321bbe6421
--- /dev/null
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/data/Char.feature
@@ -0,0 +1,94 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+@StepClassData @DataChar
+Feature: Data - CHAR
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXaX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject("a"c)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | char[a] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXescaped_quoteX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject("\""c)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | char["] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXescaped_backslashX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject("\\"c)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | char[\] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXunicodeX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject("\u00E9"c)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | char[é] |
+
+ Scenario: g_valuesXinitialX_isXtypeOfXGType_CHARXX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("data").property("initial", "a"c)
+ """
+ And the traversal of
+ """
+ g.V().values("initial").is(P.typeOf(GType.CHAR))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | char[a] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXaX_isXeqXaXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject("a"c).is(P.eq("a"c))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | char[a] |
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/data/Duration.feature
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/data/Duration.feature
new file mode 100644
index 0000000000..c9d0a73686
--- /dev/null
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/data/Duration.feature
@@ -0,0 +1,154 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+@StepClassData @DataDuration
+Feature: Data - DURATION
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXDurationXPT2H30MXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Duration("PT2H30M"))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | dur[PT2H30M] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXDurationXPT0SXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Duration("PT0S"))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | dur[PT0S] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXDurationXPTneg30SXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Duration("PT-30S"))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | dur[PT-30S] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXDurationXnegPT30SXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Duration("-PT30S"))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | dur[PT-30S] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXDurationXPT0_5SXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Duration("PT0.5S"))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | dur[PT0.5S] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXDurationXP1DT12HXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Duration("P1DT12H"))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | dur[PT36H] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXDurationXP2DXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Duration("P2D"))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | dur[PT48H] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXDurationXPT1H30M15SXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Duration("PT1H30M15S"))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | dur[PT1H30M15S] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXDurationXPTneg0_5SXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Duration("PT-0.5S"))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | dur[PT-0.5S] |
+
+ Scenario: g_valuesXlengthX_isXtypeOfXGType_DURATIONXX
+ Given the empty graph
+ And the graph initializer of
+ """
+ g.addV("data").property("length", Duration("PT2H30M"))
+ """
+ And the traversal of
+ """
+ g.V().values("length").is(P.typeOf(GType.DURATION))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | dur[PT2H30M] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXDurationXPT2H30MXX_isXgtXDurationXPT1HXXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(Duration("PT2H30M")).is(P.gt(Duration("PT1H")))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | dur[PT2H30M] |