You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2024/03/22 17:39:33 UTC
(tinkerpop) 02/08: groovy
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch TINKERPOP-3028
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 23bcd9600015af0071991d6e7dcc151b1afe6224
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Wed Jan 3 08:32:02 2024 -0500
groovy
---
.../translator/GroovyTranslateVisitor.java | 116 +++++++++++++++++++
.../language/translator/JavaTranslateVisitor.java | 2 +
.../translator/JavascriptTranslateVisitor.java | 10 ++
.../translator/PythonTranslateVisitor.java | 1 +
.../language/translator/TranslateVisitor.java | 5 +-
.../gremlin/language/translator/Translator.java | 5 +
.../language/translator/GremlinTranslatorTest.java | 128 +++++++++++++++++++--
.../tinkerpop/gremlin/features/StepDefinition.java | 8 +-
8 files changed, 262 insertions(+), 13 deletions(-)
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
new file mode 100644
index 0000000000..44e7d2d56f
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GroovyTranslateVisitor.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+package org.apache.tinkerpop.gremlin.language.translator;
+
+import org.apache.tinkerpop.gremlin.language.grammar.GremlinParser;
+
+/**
+ * Converts a Gremlin traversal string into a Groovy source code representation of that traversal with an aim at
+ * sacrificing some formatting for the ability to compile correctly. The translations may require use of TinkerPop's
+ * sugar syntax and therefore requires use of the {@code GremlinLoader} in the gremlin-groovy module unless you are
+ * specifically certain that your translations will not result in the use of that syntax. If in doubt, prefer the
+ * {@link JavaTranslateVisitor} instead.
+ * <ul>
+ * <li>Normalize numeric suffixes to lower case</li>
+ * <li>If floats are not suffixed they will translate as BigDecimal</li>
+ * <li>Makes anonymous traversals explicit with double underscore</li>
+ * <li>Makes enums explicit with their proper name</li>
+ * </ul>
+ */
+public class GroovyTranslateVisitor extends TranslateVisitor {
+ public GroovyTranslateVisitor() {
+ this("g");
+ }
+
+ public GroovyTranslateVisitor(final String graphTraversalSourceName) {
+ super(graphTraversalSourceName);
+ }
+
+ @Override
+ public Void visitStructureVertex(final GremlinParser.StructureVertexContext ctx) {
+ sb.append("new DetachedVertex(");
+ visit(ctx.getChild(3)); // id
+ sb.append(", ");
+ visit(ctx.getChild(5)); // label
+ sb.append(")");
+ return null;
+ }
+
+ @Override
+ public Void visitIntegerLiteral(final GremlinParser.IntegerLiteralContext ctx) {
+ final String integerLiteral = ctx.getText().toLowerCase();
+
+ // check suffix
+ final int lastCharIndex = integerLiteral.length() - 1;
+ final char lastCharacter = integerLiteral.charAt(lastCharIndex);
+ switch (lastCharacter) {
+ case 'b':
+ // parse B/b as byte
+ sb.append("new Byte(");
+ sb.append(integerLiteral, 0, lastCharIndex);
+ sb.append(")");
+ break;
+ case 's':
+ // parse S/s as short
+ sb.append("new Short(");
+ sb.append(integerLiteral, 0, lastCharIndex);
+ sb.append(")");
+ break;
+ case 'i':
+ case 'l':
+ // parse I/i and L/l as Integer and Long respectively
+ sb.append(integerLiteral, 0, lastCharIndex).append(lastCharacter);
+ break;
+ case 'n':
+ // parse N/n as BigInteger which for groovy is "g" shorthand
+ sb.append(integerLiteral, 0, lastCharIndex).append("g");
+ break;
+ default:
+ // everything else just goes as specified
+ sb.append(integerLiteral);
+ break;
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitFloatLiteral(final GremlinParser.FloatLiteralContext ctx) {
+ final String floatLiteral = ctx.getText().toLowerCase();
+
+ // check suffix
+ final int lastCharIndex = floatLiteral.length() - 1;
+ final char lastCharacter = floatLiteral.charAt(lastCharIndex);
+ switch (lastCharacter) {
+ case 'f':
+ case 'd':
+ // parse F/f as Float and D/d suffix as Double
+ sb.append(floatLiteral, 0, lastCharIndex).append(lastCharacter);
+ break;
+ case 'm':
+ // parse N/n as BigDecimal which for groovy is "g" shorthand
+ sb.append(floatLiteral, 0, lastCharIndex).append("g");
+ break;
+ default:
+ // everything else just goes as specified
+ sb.append(floatLiteral);
+ break;
+ }
+ return null;
+ }
+}
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 f176e85943..9f0d43065e 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
@@ -31,6 +31,8 @@ import java.util.List;
* sacrificing some formatting for the ability to compile correctly.
* <ul>
* <li>Range syntax has no direct support</li>
+ * <li>Normalizes whitespace</li>
+ * <li>Normalize numeric suffixes to lower case</li>
* <li>If floats are not suffixed they will translate as BigDecimal</li>
* <li>Makes anonymous traversals explicit with double underscore</li>
* <li>Makes enums explicit with their proper name</li>
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 f6d03478a0..b0f0852dd2 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
@@ -30,6 +30,16 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+/**
+ * Converts a Gremlin traversal string into a Javascript source code representation of that traversal with an aim at
+ * sacrificing some formatting for the ability to compile correctly.
+ * <ul>
+ * <li>Range syntax has no direct support</li>
+ * <li>Normalizes whitespace</li>
+ * <li>Makes anonymous traversals explicit with double underscore</li>
+ * <li>Makes enums explicit with their proper name</li>
+ * </ul>
+ */
public class JavascriptTranslateVisitor extends AbstractTranslateVisitor {
public JavascriptTranslateVisitor() {
this("g");
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 76a40010c6..4a99dade0c 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
@@ -35,6 +35,7 @@ import java.util.Map;
* sacrificing some formatting for the ability to compile correctly.
* <ul>
* <li>Range syntax has no direct support</li>
+ * <li>Normalizes whitespace</li>
* <li>If floats are not suffixed they will translate as BigDecimal</li>
* <li>Makes anonymous traversals explicit with double underscore</li>
* <li>Makes enums explicit with their proper name</li>
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 924011c734..94a4bb7632 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
@@ -44,6 +44,7 @@ import java.util.Set;
* A Gremlin to Gremlin translator. Makes no changes to input except:
* <ul>
* <li>Normalizes whitespace</li>
+ * <li>Normalize numeric suffixes to lower case</li>
* <li>Makes anonymous traversals explicit with double underscore</li>
* <li>Makes enums explicit with their proper name</li>
* </ul>
@@ -2269,13 +2270,13 @@ public class TranslateVisitor extends AbstractParseTreeVisitor<Void> implements
@Override
public Void visitIntegerLiteral(final GremlinParser.IntegerLiteralContext ctx) {
- sb.append(ctx.getText());
+ sb.append(ctx.getText().toLowerCase());
return null;
}
@Override
public Void visitFloatLiteral(final GremlinParser.FloatLiteralContext ctx) {
- sb.append(ctx.getText());
+ sb.append(ctx.getText().toLowerCase());
return null;
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/Translator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/Translator.java
index 169105c2bf..9a1788b9f5 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/Translator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/Translator.java
@@ -35,6 +35,11 @@ public enum Translator {
*/
ANONYMIZED("Anonymized", AnonymizedTranslatorVisitor::new),
+ /**
+ * Translates to gremlin-groovy.
+ */
+ GROOVY("Groovy", GroovyTranslateVisitor::new),
+
/**
* Translates to gremlin-java.
*/
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 ce95ec4c9d..a9f954d414 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
@@ -80,6 +80,7 @@ public class GremlinTranslatorTest {
private final String query;
private final String expectedForLang;
private final String expectedForAnonymized;
+ private final String expectedForGroovy;
private final String expectedForJava;
private final String expectedForJavascript;
private final String expectedForPython;
@@ -90,6 +91,7 @@ public class GremlinTranslatorTest {
* <ol>
* <li>Language</li>
* <li>Anonymized</li>
+ * <li>Groovy</li>
* <li>Java</li>
* <li>Javascript</li>
* <li>Python</li>
@@ -104,256 +106,306 @@ public class GremlinTranslatorTest {
null,
null,
null,
+ null,
null},
{"g.with(\"x\")",
null,
"g.with(string0)",
null,
+ null,
"g.with_(\"x\")",
"g.with_('x')"},
{"g.with(\"x\n\\\"yz\")",
null,
"g.with(string0)",
null,
+ null,
"g.with_(\"x\n\\\"yz\")",
"g.with_('x\n\\\"yz')"},
{"g.with('x', 'xyz')",
null,
"g.with(string0, string1)",
+ null,
"g.with(\"x\", \"xyz\")",
"g.with_(\"x\", \"xyz\")",
"g.with_('x', 'xyz')"},
{"g.with('x','xyz')",
"g.with('x', 'xyz')",
"g.with(string0, string1)",
+ "g.with('x', 'xyz')",
"g.with(\"x\", \"xyz\")",
"g.with_(\"x\", \"xyz\")",
"g.with_('x', 'xyz')"},
{"g.with('x', '')",
null,
"g.with(string0, string1)",
+ null,
"g.with(\"x\", \"\")",
"g.with_(\"x\", \"\")",
"g.with_('x', '')"},
+ {"g.with('x', ' ')",
+ null,
+ "g.with(string0, string1)",
+ null,
+ "g.with(\"x\", \" \")",
+ "g.with_(\"x\", \" \")",
+ "g.with_('x', ' ')"},
{"g.with('x', 'x')",
null,
"g.with(string0, string0)",
+ null,
"g.with(\"x\", \"x\")",
"g.with_(\"x\", \"x\")",
"g.with_('x', 'x')"},
{"g.with('x', null)",
null,
"g.with(string0, object0)",
+ null,
"g.with(\"x\", null)",
"g.with_(\"x\", null)",
"g.with_('x', None)"},
{"g.with('x', NaN)",
null,
"g.with(string0, number0)",
+ null,
"g.with(\"x\", Double.NaN)",
"g.with_(\"x\", Number.NaN)",
"g.with_('x', float('nan'))"},
{"g.with('x', Infinity)",
null,
"g.with(string0, number0)",
+ null,
"g.with(\"x\", Double.POSITIVE_INFINITY)",
"g.with_(\"x\", Number.POSITIVE_INFINITY)",
"g.with_('x', float('inf'))"},
{"g.with('x', -Infinity)",
null,
"g.with(string0, number0)",
+ null,
"g.with(\"x\", Double.NEGATIVE_INFINITY)",
"g.with_(\"x\", Number.NEGATIVE_INFINITY)",
"g.with_('x', float('-inf'))"},
{"g.with('x', 1.0)",
null,
"g.with(string0, number0)",
+ null,
"g.with(\"x\", 1.0)",
"g.with_(\"x\", 1.0)",
"g.with_('x', 1.0)",},
{"g.with('x', 1.0D)",
- null,
+ "g.with('x', 1.0d)",
"g.with(string0, double0)",
+ "g.with('x', 1.0d)",
"g.with(\"x\", 1.0d)",
"g.with_(\"x\", 1.0)",
"g.with_('x', 1.0)"},
{"g.with('x', 1.0d)",
null,
"g.with(string0, double0)",
+ null,
"g.with(\"x\", 1.0d)",
"g.with_(\"x\", 1.0)",
"g.with_('x', 1.0)"},
{"g.with('x', -1.0d)",
null,
"g.with(string0, double0)",
+ null,
"g.with(\"x\", -1.0d)",
"g.with_(\"x\", -1.0)",
"g.with_('x', -1.0)"},
{"g.with('x', 1.0F)",
- null,
+ "g.with('x', 1.0f)",
"g.with(string0, float0)",
+ "g.with('x', 1.0f)",
"g.with(\"x\", 1.0f)",
"g.with_(\"x\", 1.0)",
"g.with_('x', 1.0)"},
{"g.with('x', 1.0f)",
null,
"g.with(string0, float0)",
+ null,
"g.with(\"x\", 1.0f)",
"g.with_(\"x\", 1.0)",
"g.with_('x', 1.0)"},
{"g.with('x', -1.0F)",
- null,
+ "g.with('x', -1.0f)",
"g.with(string0, float0)",
+ "g.with('x', -1.0f)",
"g.with(\"x\", -1.0f)",
"g.with_(\"x\", -1.0)",
"g.with_('x', -1.0)"},
{"g.with('x', 1.0m)",
null,
"g.with(string0, bigdecimal0)",
+ "g.with('x', 1.0g)",
"g.with(\"x\", new BigDecimal(\"1.0\"))",
"g.with_(\"x\", 1.0)",
"g.with_('x', 1.0)"},
{"g.with('x', -1.0m)",
null,
"g.with(string0, bigdecimal0)",
+ "g.with('x', -1.0g)",
"g.with(\"x\", new BigDecimal(\"-1.0\"))",
"g.with_(\"x\", -1.0)",
"g.with_('x', -1.0)"},
{"g.with('x', -1.0M)",
- null,
+ "g.with('x', -1.0m)",
"g.with(string0, bigdecimal0)",
+ "g.with('x', -1.0g)",
"g.with(\"x\", new BigDecimal(\"-1.0\"))",
"g.with_(\"x\", -1.0)",
"g.with_('x', -1.0)"},
{"g.with('x', 1b)",
null,
"g.with(string0, byte0)",
+ "g.with('x', new Byte(1))",
"g.with(\"x\", new Byte(1))",
"g.with_(\"x\", 1)",
"g.with_('x', 1)"},
{"g.with('x', 1B)",
- null,
+ "g.with('x', 1b)",
"g.with(string0, byte0)",
+ "g.with('x', new Byte(1))",
"g.with(\"x\", new Byte(1))",
"g.with_(\"x\", 1)",
"g.with_('x', 1)"},
{"g.with('x', -1b)",
null,
"g.with(string0, byte0)",
+ "g.with('x', new Byte(-1))",
"g.with(\"x\", new Byte(-1))",
"g.with_(\"x\", -1)",
"g.with_('x', -1)"},
{"g.with('x', 1s)",
null,
"g.with(string0, short0)",
+ "g.with('x', new Short(1))",
"g.with(\"x\", new Short(1))",
"g.with_(\"x\", 1)",
"g.with_('x', 1)"},
{"g.with('x', -1s)",
null,
"g.with(string0, short0)",
+ "g.with('x', new Short(-1))",
"g.with(\"x\", new Short(-1))",
"g.with_(\"x\", -1)",
"g.with_('x', -1)"},
{"g.with('x', 1S)",
- null,
+ "g.with('x', 1s)",
"g.with(string0, short0)",
+ "g.with('x', new Short(1))",
"g.with(\"x\", new Short(1))",
"g.with_(\"x\", 1)",
"g.with_('x', 1)"},
{"g.with('x', 1i)",
null,
"g.with(string0, integer0)",
+ null,
"g.with(\"x\", 1)",
"g.with_(\"x\", 1)",
"g.with_('x', 1)"},
{"g.with('x', 1I)",
- null,
+ "g.with('x', 1i)",
"g.with(string0, integer0)",
+ "g.with('x', 1i)",
"g.with(\"x\", 1)",
"g.with_(\"x\", 1)",
"g.with_('x', 1)"},
{"g.with('x', -1i)",
null,
"g.with(string0, integer0)",
+ null,
"g.with(\"x\", -1)",
"g.with_(\"x\", -1)",
"g.with_('x', -1)"},
{"g.with('x', 1l)",
null,
"g.with(string0, long0)",
+ null,
"g.with(\"x\", 1l)",
"g.with_(\"x\", 1)",
"g.with_('x', long(1))"},
{"g.with('x', 1L)",
- null,
+ "g.with('x', 1l)",
"g.with(string0, long0)",
+ "g.with('x', 1l)",
"g.with(\"x\", 1l)",
"g.with_(\"x\", 1)",
"g.with_('x', long(1))"},
{"g.with('x', -1l)",
null,
"g.with(string0, long0)",
+ null,
"g.with(\"x\", -1l)",
"g.with_(\"x\", -1)",
"g.with_('x', long(-1))"},
{"g.with('x', 1n)",
null,
"g.with(string0, biginteger0)",
+ "g.with('x', 1g)",
"g.with(\"x\", new BigInteger(\"1\"))",
"g.with_(\"x\", 1)",
"g.with_('x', 1)"},
{"g.with('x', 1N)",
- null,
+ "g.with('x', 1n)",
"g.with(string0, biginteger0)",
+ "g.with('x', 1g)",
"g.with(\"x\", new BigInteger(\"1\"))",
"g.with_(\"x\", 1)",
"g.with_('x', 1)"},
{"g.with('x', -1n)",
null,
"g.with(string0, biginteger0)",
+ "g.with('x', -1g)",
"g.with(\"x\", new BigInteger(\"-1\"))",
"g.with_(\"x\", -1)",
"g.with_('x', -1)"},
{"g.with('x', datetime('2023-08-02T00:00:00Z'))",
null,
"g.with(string0, date0)",
+ null,
"g.with(\"x\", new Date(1690934400000))",
"g.with_(\"x\", new Date(1690934400000))",
"g.with_('x', datetime.datetime.utcfromtimestamp(1690934400000 / 1000.0))"},
{"g.with('x', [x: 1])",
"g.with('x', [x:1])",
"g.with(string0, map0)",
+ "g.with('x', [x:1])",
"g.with(\"x\", new LinkedHashMap<Object, Object>() {{ put(\"x\", 1); }})",
"g.with_(\"x\", new Map([[\"x\", 1]]))",
"g.with_('x', { 'x': 1 })"},
{"g.with('x', [x:1, new:2])",
null,
"g.with(string0, map0)",
+ null,
"g.with(\"x\", new LinkedHashMap<Object, Object>() {{ put(\"x\", 1); put(\"new\", 2); }})",
"g.with_(\"x\", new Map([[\"x\", 1], [\"new\", 2]]))",
"g.with_('x', { 'x': 1, 'new': 2 })"},
{"g.with('x', [\"x\":1])",
null,
"g.with(string0, map0)",
+ null,
"g.with(\"x\", new LinkedHashMap<Object, Object>() {{ put(\"x\", 1); }})",
"g.with_(\"x\", new Map([[\"x\", 1]]))",
"g.with_('x', { 'x': 1 })"},
{"g.with('x', [1:'x'])",
null,
"g.with(string0, map0)",
+ null,
"g.with(\"x\", new LinkedHashMap<Object, Object>() {{ put(1, \"x\"); }})",
"g.with_(\"x\", new Map([[1, \"x\"]]))",
"g.with_('x', { 1: 'x' })"},
{"g.with('x', [1, 'x'])",
null,
"g.with(string0, list0)",
+ null,
"g.with(\"x\", new ArrayList<Object>() {{ add(1); add(\"x\"); }})",
"g.with_(\"x\", [1, \"x\"])",
"g.with_('x', [1, 'x'])"},
{"g.with('x', 0..5)",
null,
"g.with(string0, number0..number1)",
+ "g.with('x', 0..5)",
"Java does not support range literals",
"Javascript does not support range literals",
"Python does not support range literals"},
@@ -362,26 +414,31 @@ public class GremlinTranslatorTest {
"g.withBulk(boolean0)",
null,
null,
+ null,
"g.with_bulk(False)"},
{"g.withBulk(true)",
null,
"g.withBulk(boolean0)",
null,
null,
+ null,
"g.with_bulk(True)"},
{"g.withBulk( true )",
"g.withBulk(true)",
"g.withBulk(boolean0)",
"g.withBulk(true)",
"g.withBulk(true)",
+ "g.withBulk(true)",
"g.with_bulk(True)"},
{"g.withBulk(x)",
null,
null,
null,
null,
+ null,
"g.with_bulk(x)"},
{"g.withStrategies(ReadOnlyStrategy)",
+ null,
null,
null,
"g.withStrategies(ReadOnlyStrategy.instance())",
@@ -390,18 +447,21 @@ public class GremlinTranslatorTest {
{"g.withStrategies(new SeedStrategy(seed:10000))",
null,
"g.withStrategies(new SeedStrategy(seed:number0))",
+ null,
"g.withStrategies(SeedStrategy.build().seed(10000).create())",
"g.withStrategies(new SeedStrategy({seed: 10000}))",
"g.with_strategies(SeedStrategy(seed=10000))"},
{"g.withStrategies(new PartitionStrategy(includeMetaProperties: true, partitionKey:'x'))",
"g.withStrategies(new PartitionStrategy(includeMetaProperties:true, partitionKey:'x'))",
"g.withStrategies(new PartitionStrategy(includeMetaProperties:boolean0, partitionKey:string0))",
+ "g.withStrategies(new PartitionStrategy(includeMetaProperties:true, partitionKey:'x'))",
"g.withStrategies(PartitionStrategy.build().includeMetaProperties(true).partitionKey(\"x\").create())",
"g.withStrategies(new PartitionStrategy({includeMetaProperties: true, partitionKey: \"x\"}))",
"g.with_strategies(PartitionStrategy(include_meta_properties=True, partition_key='x'))"},
{"g.withStrategies(new SubgraphStrategy(vertices:__.has('name', 'vadas'), edges: has('weight', gt(0.5))))",
"g.withStrategies(new SubgraphStrategy(vertices:__.has('name', 'vadas'), edges:__.has('weight', P.gt(0.5))))",
"g.withStrategies(new SubgraphStrategy(vertices:__.has(string0, string1), edges:__.has(string2, P.gt(number0))))",
+ "g.withStrategies(new SubgraphStrategy(vertices:__.has('name', 'vadas'), edges:__.has('weight', P.gt(0.5))))",
"g.withStrategies(SubgraphStrategy.build().vertices(__.has(\"name\", \"vadas\")).edges(__.has(\"weight\", P.gt(0.5))).create())",
"g.withStrategies(new SubgraphStrategy({vertices: __.has(\"name\", \"vadas\"), edges: __.has(\"weight\", P.gt(0.5))}))",
"g.with_strategies(SubgraphStrategy(vertices=__.has('name', 'vadas'), edges=__.has('weight', P.gt(0.5))))"},
@@ -411,12 +471,14 @@ public class GremlinTranslatorTest {
" __.has(\"weight\", 1.0).hasLabel(\"created\")))).E()",
"g.withStrategies(new SubgraphStrategy(checkAdjacentVertices:false, vertices:__.has(\"name\", P.within(\"josh\", \"lop\", \"ripple\")), edges:__.or(__.has(\"weight\", 0.4).hasLabel(\"created\"), __.has(\"weight\", 1.0).hasLabel(\"created\")))).E()",
"g.withStrategies(new SubgraphStrategy(checkAdjacentVertices:boolean0, vertices:__.has(string0, P.within(string1, string2, string3)), edges:__.or(__.has(string4, number0).hasLabel(string5), __.has(string4, number1).hasLabel(string5)))).E()",
+ "g.withStrategies(new SubgraphStrategy(checkAdjacentVertices:false, vertices:__.has(\"name\", P.within(\"josh\", \"lop\", \"ripple\")), edges:__.or(__.has(\"weight\", 0.4).hasLabel(\"created\"), __.has(\"weight\", 1.0).hasLabel(\"created\")))).E()",
"g.withStrategies(SubgraphStrategy.build().checkAdjacentVertices(false).vertices(__.has(\"name\", P.within(\"josh\", \"lop\", \"ripple\"))).edges(__.or(__.has(\"weight\", 0.4).hasLabel(\"created\"), __.has(\"weight\", 1.0).hasLabel(\"created\"))).create()).E()",
"g.withStrategies(new SubgraphStrategy({checkAdjacentVertices: false, vertices: __.has(\"name\", P.within(\"josh\", \"lop\", \"ripple\")), edges: __.or(__.has(\"weight\", 0.4).hasLabel(\"created\"), __.has(\"weight\", 1.0).hasLabel(\"created\"))})).E()",
"g.with_strategies(SubgraphStrategy(check_adjacent_vertices=False, vertices=__.has('name', P.within('josh', 'lop', 'ripple')), edges=__.or_(__.has('weight', 0.4).has_label('created'), __.has('weight', 1.0).has_label('created')))).E()"},
{"g.inject(0..5)",
null,
"g.inject(number0..number1)",
+ "g.inject(0..5)",
"Java does not support range literals",
"Javascript does not support range literals",
"Python does not support range literals"},
@@ -425,28 +487,33 @@ public class GremlinTranslatorTest {
"g.inject(number0).asDate()",
null,
null,
+ null,
"g.inject(long(1694017707000)).as_date()"},
{"g.V().hasLabel(null)",
null,
"g.V().hasLabel(string0)",
null,
null,
+ null,
"g.V().has_label(None)",},
{"g.V().hasLabel('person')",
null,
"g.V().hasLabel(string0)",
+ "g.V().hasLabel('person')",
"g.V().hasLabel(\"person\")",
"g.V().hasLabel(\"person\")",
"g.V().has_label('person')"},
{"g.V().hasLabel('person', 'software', 'class')",
null,
"g.V().hasLabel(string0, string1, string2)",
+ "g.V().hasLabel('person', 'software', 'class')",
"g.V().hasLabel(\"person\", \"software\", \"class\")",
"g.V().hasLabel(\"person\", \"software\", \"class\")",
"g.V().has_label('person', 'software', 'class')"},
{"g.V().hasLabel(null, 'software', 'class')",
null,
"g.V().hasLabel(string0, string1, string2)",
+ "g.V().hasLabel(null, 'software', 'class')",
"g.V().hasLabel(null, \"software\", \"class\")",
"g.V().hasLabel(null, \"software\", \"class\")",
"g.V().has_label(None, 'software', 'class')"},
@@ -455,106 +522,124 @@ public class GremlinTranslatorTest {
null,
null,
null,
+ null,
null},
{"g.V().map(out().count())",
"g.V().map(__.out().count())",
"g.V().map(__.out().count())",
"g.V().map(__.out().count())",
"g.V().map(__.out().count())",
+ "g.V().map(__.out().count())",
"g.V().map(__.out().count())"},
{"g.V().fold().count(Scope.local)",
null,
null,
null,
null,
+ null,
null},
{"g.V().fold().count(Scope.local)",
null,
null,
null,
null,
+ null,
null},
{"g.V().has(T.id, 1)",
null,
"g.V().has(T.id, number0)",
null,
null,
+ null,
"g.V().has(T.id_, 1)"},
{"g.V().has(id, 1)",
"g.V().has(T.id, 1)",
"g.V().has(T.id, number0)",
"g.V().has(T.id, 1)",
"g.V().has(T.id, 1)",
+ "g.V().has(T.id, 1)",
"g.V().has(T.id_, 1)"},
{"g.V().has(\"name\", P.within(\"josh\",\"stephen\"))",
"g.V().has(\"name\", P.within(\"josh\", \"stephen\"))",
"g.V().has(string0, P.within(string1, string2))",
"g.V().has(\"name\", P.within(\"josh\", \"stephen\"))",
"g.V().has(\"name\", P.within(\"josh\", \"stephen\"))",
+ "g.V().has(\"name\", P.within(\"josh\", \"stephen\"))",
"g.V().has('name', P.within('josh', 'stephen'))"},
{"g.V().has(\"name\", P.eq(\"josh\"))",
null,
"g.V().has(string0, P.eq(string1))",
null,
null,
+ null,
"g.V().has('name', P.eq('josh'))"},
{"g.V().has(\"name\", P.eq(\"josh\").negate())",
null,
"g.V().has(string0, P.eq(string1).negate())",
null,
null,
+ null,
"g.V().has('name', P.eq('josh').negate())"},
{"g.V().has(\"name\", P.within())",
null,
"g.V().has(string0, P.within())",
null,
null,
+ null,
"g.V().has('name', P.within())"},
{"g.V().has(\"name\", P.within(\"josh\",\"stephen\").or(eq(\"vadas\")))",
"g.V().has(\"name\", P.within(\"josh\", \"stephen\").or(P.eq(\"vadas\")))",
"g.V().has(string0, P.within(string1, string2).or(P.eq(string3)))",
"g.V().has(\"name\", P.within(\"josh\", \"stephen\").or(P.eq(\"vadas\")))",
"g.V().has(\"name\", P.within(\"josh\", \"stephen\").or(P.eq(\"vadas\")))",
+ "g.V().has(\"name\", P.within(\"josh\", \"stephen\").or(P.eq(\"vadas\")))",
"g.V().has('name', P.within('josh', 'stephen').or_(P.eq('vadas')))"},
{"g.V().has(\"name\", within(\"josh\", \"stephen\"))",
"g.V().has(\"name\", P.within(\"josh\", \"stephen\"))",
"g.V().has(string0, P.within(string1, string2))",
"g.V().has(\"name\", P.within(\"josh\", \"stephen\"))",
"g.V().has(\"name\", P.within(\"josh\", \"stephen\"))",
+ "g.V().has(\"name\", P.within(\"josh\", \"stephen\"))",
"g.V().has('name', P.within('josh', 'stephen'))"},
{"g.V().has(\"name\", TextP.containing(\"j\").negate())",
null,
"g.V().has(string0, TextP.containing(string1).negate())",
null,
null,
+ null,
"g.V().has('name', TextP.containing('j').negate())"},
{"g.V().hasLabel(\"person\").has(\"age\", P.not(P.lte(10).and(P.not(P.between(11, 20)))).and(P.lt(29).or(P.eq(35)))).values(\"name\")",
null,
"g.V().hasLabel(string0).has(string1, P.not(P.lte(number0).and(P.not(P.between(number1, number2)))).and(P.lt(number3).or(P.eq(number4)))).values(string2)",
null,
null,
+ null,
"g.V().has_label('person').has('age', P.not_(P.lte(10).and_(P.not_(P.between(11, 20)))).and_(P.lt(29).or_(P.eq(35)))).values('name')"},
{"g.V().has(\"name\", containing(\"j\"))",
"g.V().has(\"name\", TextP.containing(\"j\"))",
"g.V().has(string0, TextP.containing(string1))",
"g.V().has(\"name\", TextP.containing(\"j\"))",
"g.V().has(\"name\", TextP.containing(\"j\"))",
+ "g.V().has(\"name\", TextP.containing(\"j\"))",
"g.V().has('name', TextP.containing('j'))"},
{"g.V().property(set, \"name\", \"stephen\")",
"g.V().property(Cardinality.set, \"name\", \"stephen\")",
"g.V().property(Cardinality.set, string0, string1)",
"g.V().property(Cardinality.set, \"name\", \"stephen\")",
"g.V().property(Cardinality.set, \"name\", \"stephen\")",
+ "g.V().property(Cardinality.set, \"name\", \"stephen\")",
"g.V().property(Cardinality.set_, 'name', 'stephen')"},
{"g.V().property(Cardinality.set, \"name\", \"stephen\")",
null,
"g.V().property(Cardinality.set, string0, string1)",
null,
null,
+ null,
"g.V().property(Cardinality.set_, 'name', 'stephen')"},
{"g.V().has('name', 'foo').property([\"name\":Cardinality.set(\"bar\"), \"age\":43])",
null,
"g.V().has(string0, string1).property(map0)",
+ null,
"g.V().has(\"name\", \"foo\").property(new LinkedHashMap<Object, Object>() {{ put(\"name\", Cardinality.set(\"bar\")); put(\"age\", 43); }})",
"g.V().has(\"name\", \"foo\").property(new Map([[\"name\", CardinalityValue.set(\"bar\")], [\"age\", 43]]))",
"g.V().has('name', 'foo').property({ 'name': CardinalityValue.set_('bar'), 'age': 43 })"},
@@ -562,6 +647,7 @@ public class GremlinTranslatorTest {
null,
"g.V(new Vertex(number0, string0)).limit(number0)",
"g.V(new DetachedVertex(1, \"person\")).limit(1)",
+ "g.V(new DetachedVertex(1, \"person\")).limit(1)",
"g.V(new Vertex(1, \"person\")).limit(1)",
"g.V(Vertex(1, 'person')).limit(1)",},
{"g.V().both().properties().dedup().hasKey(\"age\").value()",
@@ -569,11 +655,13 @@ public class GremlinTranslatorTest {
"g.V().both().properties().dedup().hasKey(string0).value()",
null,
null,
+ null,
"g.V().both().properties().dedup().has_key('age').value()",},
{"g.V().connectedComponent().with(ConnectedComponent.propertyName, \"component\")",
"g.V().connectedComponent().with(ConnectedComponent.propertyName, \"component\")",
"g.V().connectedComponent().with(ConnectedComponent.propertyName, string0)",
"g.V().connectedComponent().with(ConnectedComponent.propertyName, \"component\")",
+ "g.V().connectedComponent().with(ConnectedComponent.propertyName, \"component\")",
"g.V().connectedComponent().with_(ConnectedComponent.propertyName, \"component\")",
"g.V().connected_component().with_(ConnectedComponent.property_name, 'component')"},
{"g.withSideEffect(\"c\", xx2).withSideEffect(\"m\", xx3).mergeE(xx1).option(Merge.onCreate, __.select(\"c\")).option(Merge.onMatch, __.select(\"m\"))",
@@ -581,23 +669,27 @@ public class GremlinTranslatorTest {
"g.withSideEffect(string0, xx2).withSideEffect(string1, xx3).mergeE(map0).option(Merge.onCreate, __.select(string0)).option(Merge.onMatch, __.select(string1))",
null,
null,
+ null,
"g.with_side_effect('c', xx2).with_side_effect('m', xx3).merge_e(xx1).option(Merge.on_create, __.select('c')).option(Merge.on_match, __.select('m'))"},
{"g.V(1, 2, 3)",
null,
"g.V(number0, number1, number2)",
null,
null,
+ null,
null},
{"g.V().limit(1)",
null,
"g.V().limit(number0)",
null,
null,
+ null,
null},
{"g.V().limit(1L)",
- null,
+ "g.V().limit(1l)",
"g.V().limit(long0)",
"g.V().limit(1l)",
+ "g.V().limit(1l)",
"g.V().limit(1)",
"g.V().limit(long(1))"},
{"g.V().limit(x)",
@@ -605,36 +697,42 @@ public class GremlinTranslatorTest {
null,
null,
null,
+ null,
null},
{"g.V().toList()",
null,
null,
null,
null,
+ null,
"g.V().to_list()"},
{"g.V().iterate()",
null,
null,
null,
null,
+ null,
null},
{"g.tx().commit()",
null,
null,
null,
null,
+ null,
null},
});
}
public TranslationTest(final String query, final String expectedForLang,
final String expectedForAnonymized,
+ final String expectedForGroovy,
final String expectedForJava,
final String expectedForJavascript,
final String expectedForPython) {
this.query = query;
this.expectedForLang = expectedForLang != null ? expectedForLang : query;
this.expectedForAnonymized = expectedForAnonymized != null ? expectedForAnonymized : query;
+ this.expectedForGroovy = expectedForGroovy != null ? expectedForGroovy : query;
this.expectedForJava = expectedForJava != null ? expectedForJava : query;
this.expectedForJavascript = expectedForJavascript != null ? expectedForJavascript : query;
this.expectedForPython = expectedForPython != null ? expectedForPython : query;
@@ -652,6 +750,16 @@ public class GremlinTranslatorTest {
assertEquals(expectedForAnonymized, translatedQuery);
}
+ @Test
+ public void shouldTranslateForGroovy() {
+ try {
+ final String translatedQuery = GremlinTranslator.translate(query, "g", Translator.GROOVY).getTranslated();
+ assertEquals(expectedForGroovy, translatedQuery);
+ } catch (TranslatorException e) {
+ assertThat(e.getMessage(), startsWith(expectedForGroovy));
+ }
+ }
+
@Test
public void shouldTranslateForJava() {
try {
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 38303b2738..c081426992 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
@@ -32,6 +32,8 @@ import org.antlr.v4.runtime.CommonTokenStream;
import org.apache.tinkerpop.gremlin.language.grammar.GremlinAntlrToJava;
import org.apache.tinkerpop.gremlin.language.grammar.GremlinLexer;
import org.apache.tinkerpop.gremlin.language.grammar.GremlinParser;
+import org.apache.tinkerpop.gremlin.language.translator.GremlinTranslator;
+import org.apache.tinkerpop.gremlin.language.translator.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Merge;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -443,7 +445,11 @@ public final class StepDefinition {
}
private Traversal parseGremlin(final String script) {
- final GremlinLexer lexer = new GremlinLexer(CharStreams.fromString(script));
+ // tests the normalizer by running the script from the feature file first
+ final String normalizedGremlin = GremlinTranslator.translate(script, Translator.LANGUAGE).getTranslated();
+
+ // parse the Gremlin to a Traversal
+ final GremlinLexer lexer = new GremlinLexer(CharStreams.fromString(normalizedGremlin));
final GremlinParser parser = new GremlinParser(new CommonTokenStream(lexer));
final GremlinParser.QueryContext ctx = parser.query();
return (Traversal) new GremlinAntlrToJava(g).visitQuery(ctx);