This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch steps-taking-traversal in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit fd6daf40b33f3aa4f565a0131c95e140d6c05962 Author: Yang Xia <[email protected]> AuthorDate: Fri Jun 12 00:16:20 2026 -0700 Add grammar support for nestedTraversal arguments Extend Gremlin.g4 and the traversal/predicate/spawn visitors to accept a nestedTraversal where a literal or predicate is expected in has(), hasLabel(), is(), V(), E(), property(), and the P/TextP predicate productions. Start-step V(traversal)/E(traversal) parsing is supported; mutating child traversals are rejected downstream by the verification strategy. --- .../grammar/DefaultGremlinBaseVisitor.java | 28 ++++ .../language/grammar/TraversalMethodVisitor.java | 133 ++++++++++++++++-- .../grammar/TraversalPredicateVisitor.java | 60 +++++++++ .../grammar/TraversalSourceSpawnMethodVisitor.java | 23 +++- .../language/grammar/GremlinQueryParserTest.java | 4 +- .../grammar/GremlinQueryParserTraversalTest.java | 149 +++++++++++++++++++++ gremlin-language/src/main/antlr4/Gremlin.g4 | 28 ++++ 7 files changed, 409 insertions(+), 16 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java index 760da5be7a..be87c343bf 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java @@ -455,6 +455,18 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im * {@inheritDoc} */ @Override public T visitTraversalMethod_has_T_P(final GremlinParser.TraversalMethod_has_T_PContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_has_String_Traversal(final GremlinParser.TraversalMethod_has_String_TraversalContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_has_T_Traversal(final GremlinParser.TraversalMethod_has_T_TraversalContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_has_String_String_Traversal(final GremlinParser.TraversalMethod_has_String_String_TraversalContext ctx) { notImplemented(ctx); return null; } /** * {@inheritDoc} */ @@ -475,6 +487,10 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im * {@inheritDoc} */ @Override public T visitTraversalMethod_hasLabel_P(final GremlinParser.TraversalMethod_hasLabel_PContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_hasLabel_Traversal(final GremlinParser.TraversalMethod_hasLabel_TraversalContext ctx) { notImplemented(ctx); return null; } /** * {@inheritDoc} */ @@ -703,6 +719,18 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im * {@inheritDoc} */ @Override public T visitTraversalMethod_property_Object(final GremlinParser.TraversalMethod_property_ObjectContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_property_Cardinality_Object_Traversal(final GremlinParser.TraversalMethod_property_Cardinality_Object_TraversalContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_property_Object_Traversal(final GremlinParser.TraversalMethod_property_Object_TraversalContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalMethod_property_Traversal(final GremlinParser.TraversalMethod_property_TraversalContext ctx) { notImplemented(ctx); return null; } /** * {@inheritDoc} */ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java index 537bdcbe52..518196daef 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java @@ -22,15 +22,23 @@ import org.apache.tinkerpop.gremlin.process.traversal.DT; import org.apache.tinkerpop.gremlin.process.traversal.Merge; import org.apache.tinkerpop.gremlin.process.traversal.Operator; import org.apache.tinkerpop.gremlin.process.traversal.Order; +import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.Pop; import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions; import org.apache.tinkerpop.gremlin.process.traversal.Scope; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.step.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStepPlaceholder; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; import org.apache.tinkerpop.gremlin.structure.Column; import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality; import java.util.Arrays; @@ -65,7 +73,14 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> */ @Override public GraphTraversal visitTraversalMethod_V(final GremlinParser.TraversalMethod_VContext ctx) { - return this.graphTraversal.V(antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs())); + if (ctx.nestedTraversal() != null) { + return this.graphTraversal.V((Traversal<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } + final Object[] args = antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs()); + if (args.length == 1 && args[0] instanceof Traversal) { + return this.graphTraversal.V((Traversal<?, ?>) args[0]); + } + return this.graphTraversal.V(args); } /** @@ -73,7 +88,14 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> */ @Override public GraphTraversal visitTraversalMethod_E(final GremlinParser.TraversalMethod_EContext ctx) { - return this.graphTraversal.E(antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs())); + if (ctx.nestedTraversal() != null) { + return this.graphTraversal.E((Traversal<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } + final Object[] args = antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs()); + if (args.length == 1 && args[0] instanceof Traversal) { + return this.graphTraversal.E((Traversal<?, ?>) args[0]); + } + return this.graphTraversal.E(args); } /** @@ -749,6 +771,15 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> return graphTraversal.hasLabel(antlr.traversalPredicateVisitor.visitTraversalPredicate(ctx.traversalPredicate())); } + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_hasLabel_Traversal(final GremlinParser.TraversalMethod_hasLabel_TraversalContext ctx) { + final Traversal.Admin<?, ?> traversal = (Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()); + return graphTraversal.hasLabel((Traversal<?, ?>) traversal); + } + /** * {@inheritDoc} */ @@ -820,8 +851,12 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> */ @Override public GraphTraversal visitTraversalMethod_has_String_Object(final GremlinParser.TraversalMethod_has_String_ObjectContext ctx) { - return graphTraversal.has(antlr.genericVisitor.parseString(ctx.stringNullableLiteral()), - antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument())); + final String key = antlr.genericVisitor.parseString(ctx.stringNullableLiteral()); + final Object value = antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()); + if (value instanceof Traversal) { + return graphTraversal.has(key, (Traversal<?, ?>) value); + } + return graphTraversal.has(key, value); } /** @@ -839,14 +874,19 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> @Override public GraphTraversal visitTraversalMethod_has_String_String_Object(final GremlinParser.TraversalMethod_has_String_String_ObjectContext ctx) { final Object literalOrVar = antlr.argumentVisitor.visitStringNullableArgument(ctx.stringNullableArgument()); + final String key = antlr.genericVisitor.parseString(ctx.stringNullableLiteral()); + final Object value = antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()); + if (value instanceof Traversal) { + if (GValue.valueInstanceOf(literalOrVar, String.class)) { + return graphTraversal.has(((GValue<String>) literalOrVar).get(), key, (Traversal<?, ?>) value); + } else { + return graphTraversal.has((String) literalOrVar, key, (Traversal<?, ?>) value); + } + } if (GValue.valueInstanceOf(literalOrVar, String.class)) { - return graphTraversal.has((GValue) literalOrVar, - antlr.genericVisitor.parseString(ctx.stringNullableLiteral()), - antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument())); + return graphTraversal.has((GValue) literalOrVar, key, value); } else { - return graphTraversal.has((String) literalOrVar, - antlr.genericVisitor.parseString(ctx.stringNullableLiteral()), - antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument())); + return graphTraversal.has((String) literalOrVar, key, value); } } @@ -872,8 +912,12 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> */ @Override public GraphTraversal visitTraversalMethod_has_T_Object(final GremlinParser.TraversalMethod_has_T_ObjectContext ctx) { - return graphTraversal.has(TraversalEnumParser.parseTraversalEnumFromContext(T.class, ctx.traversalT()), - antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument())); + final T accessor = TraversalEnumParser.parseTraversalEnumFromContext(T.class, ctx.traversalT()); + final Object value = antlr.argumentVisitor.visitGenericArgument(ctx.genericArgument()); + if (value instanceof Traversal) { + return graphTraversal.has(accessor, (Traversal<?, ?>) value); + } + return graphTraversal.has(accessor, value); } /** @@ -885,6 +929,41 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> antlr.traversalPredicateVisitor.visitTraversalPredicate(ctx.traversalPredicate())); } + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_has_String_Traversal(final GremlinParser.TraversalMethod_has_String_TraversalContext ctx) { + final String key = antlr.genericVisitor.parseString(ctx.stringNullableLiteral()); + final Traversal.Admin<?, ?> traversal = (Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()); + return this.graphTraversal.has(key, (Traversal<?, ?>) traversal); + } + + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_has_String_String_Traversal(final GremlinParser.TraversalMethod_has_String_String_TraversalContext ctx) { + final Object literalOrVar = antlr.argumentVisitor.visitStringNullableArgument(ctx.stringNullableArgument()); + final String key = antlr.genericVisitor.parseString(ctx.stringNullableLiteral()); + final Traversal.Admin<?, ?> traversal = (Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()); + if (GValue.valueInstanceOf(literalOrVar, String.class)) { + return this.graphTraversal.has(((GValue<String>) literalOrVar).get(), key, (Traversal<?, ?>) traversal); + } else { + return this.graphTraversal.has((String) literalOrVar, key, (Traversal<?, ?>) traversal); + } + } + + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_has_T_Traversal(final GremlinParser.TraversalMethod_has_T_TraversalContext ctx) { + final T accessor = TraversalEnumParser.parseTraversalEnumFromContext(T.class, ctx.traversalT()); + final Traversal.Admin<?, ?> traversal = (Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()); + return this.graphTraversal.has(accessor, (Traversal<?, ?>) traversal); + } + /** * {@inheritDoc} */ @@ -1432,6 +1511,36 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> return graphTraversal.property(antlr.argumentVisitor.parseMap(ctx.genericMapNullableArgument())); } + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_property_Cardinality_Object_Traversal(final GremlinParser.TraversalMethod_property_Cardinality_Object_TraversalContext ctx) { + return graphTraversal.property( + TraversalEnumParser.parseTraversalEnumFromContext(Cardinality.class, ctx.traversalCardinality()), + antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()), + antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } + + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_property_Object_Traversal(final GremlinParser.TraversalMethod_property_Object_TraversalContext ctx) { + return graphTraversal.property( + antlr.genericVisitor.visitGenericLiteral(ctx.genericLiteral()), + antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } + + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal visitTraversalMethod_property_Traversal(final GremlinParser.TraversalMethod_property_TraversalContext ctx) { + final Traversal<?, ?> traversal = (Traversal<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal()); + return this.graphTraversal.property(traversal); + } + /** * {@inheritDoc} */ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalPredicateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalPredicateVisitor.java index 19a5ab4f29..c504c2e76d 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalPredicateVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalPredicateVisitor.java @@ -22,6 +22,7 @@ import org.antlr.v4.runtime.tree.ParseTree; import org.apache.tinkerpop.gremlin.process.traversal.GType; import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.TextP; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import java.util.Collection; @@ -67,6 +68,9 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { */ @Override public P visitTraversalPredicate_eq(final GremlinParser.TraversalPredicate_eqContext ctx) { + if (ctx.nestedTraversal() != null) { + return P.eq((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return P.eq(getSingleGenericLiteralArgument(ctx)); } @@ -75,6 +79,9 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { */ @Override public P visitTraversalPredicate_neq(final GremlinParser.TraversalPredicate_neqContext ctx) { + if (ctx.nestedTraversal() != null) { + return P.neq((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return P.neq(getSingleGenericLiteralArgument(ctx)); } @@ -97,6 +104,9 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { */ @Override public P visitTraversalPredicate_lt(final GremlinParser.TraversalPredicate_ltContext ctx) { + if (ctx.nestedTraversal() != null) { + return P.lt((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return P.lt(getSingleGenericLiteralArgument(ctx)); } @@ -105,6 +115,9 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { */ @Override public P visitTraversalPredicate_lte(final GremlinParser.TraversalPredicate_lteContext ctx) { + if (ctx.nestedTraversal() != null) { + return P.lte((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return P.lte(getSingleGenericLiteralArgument(ctx)); } @@ -113,6 +126,9 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { */ @Override public P visitTraversalPredicate_gt(final GremlinParser.TraversalPredicate_gtContext ctx) { + if (ctx.nestedTraversal() != null) { + return P.gt((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return P.gt(getSingleGenericLiteralArgument(ctx)); } @@ -121,6 +137,9 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { */ @Override public P visitTraversalPredicate_gte(final GremlinParser.TraversalPredicate_gteContext ctx) { + if (ctx.nestedTraversal() != null) { + return P.gte((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return P.gte(getSingleGenericLiteralArgument(ctx)); } @@ -129,6 +148,11 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { */ @Override public P visitTraversalPredicate_inside(final GremlinParser.TraversalPredicate_insideContext ctx) { + if (!ctx.nestedTraversal().isEmpty()) { + final Traversal.Admin<?, ?> first = (Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal(0)); + final Traversal.Admin<?, ?> second = (Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal(1)); + return P.gt(first).and(P.lt(second)); + } final Object[] arguments = getDoubleGenericLiteralArgument(ctx); return P.inside(arguments[0], arguments[1]); } @@ -138,6 +162,11 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { */ @Override public P visitTraversalPredicate_outside(final GremlinParser.TraversalPredicate_outsideContext ctx) { + if (!ctx.nestedTraversal().isEmpty()) { + final Traversal.Admin<?, ?> first = (Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal(0)); + final Traversal.Admin<?, ?> second = (Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal(1)); + return P.lt(first).or(P.gt(second)); + } final Object[] arguments = getDoubleGenericLiteralArgument(ctx); return P.outside(arguments[0], arguments[1]); } @@ -147,6 +176,11 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { */ @Override public P visitTraversalPredicate_between(final GremlinParser.TraversalPredicate_betweenContext ctx) { + if (!ctx.nestedTraversal().isEmpty()) { + final Traversal.Admin<?, ?> first = (Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal(0)); + final Traversal.Admin<?, ?> second = (Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal(1)); + return P.gte(first).and(P.lt(second)); + } final Object[] arguments = getDoubleGenericLiteralArgument(ctx); return P.between(arguments[0], arguments[1]); } @@ -156,6 +190,10 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { */ @Override public P visitTraversalPredicate_within(final GremlinParser.TraversalPredicate_withinContext ctx) { + if (ctx.nestedTraversal() != null) { + return P.within((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } + // called with no args which is valid for java/groovy if (null == ctx.genericArgumentVarargs()) return P.within(); @@ -175,6 +213,10 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { */ @Override public P visitTraversalPredicate_without(final GremlinParser.TraversalPredicate_withoutContext ctx) { + if (ctx.nestedTraversal() != null) { + return P.without((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } + // called with no args which is valid for java/groovy if (null == ctx.genericArgumentVarargs()) return P.without(); @@ -199,31 +241,49 @@ public class TraversalPredicateVisitor extends DefaultGremlinBaseVisitor<P> { @Override public P visitTraversalPredicate_containing(final GremlinParser.TraversalPredicate_containingContext ctx) { + if (ctx.nestedTraversal() != null) { + return TextP.containing((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return TextP.containing((String) antlr.argumentVisitor.visitStringArgument(ctx.stringArgument())); } @Override public P visitTraversalPredicate_notContaining(final GremlinParser.TraversalPredicate_notContainingContext ctx) { + if (ctx.nestedTraversal() != null) { + return TextP.notContaining((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return TextP.notContaining((String) antlr.argumentVisitor.visitStringArgument(ctx.stringArgument())); } @Override public P visitTraversalPredicate_notEndingWith(final GremlinParser.TraversalPredicate_notEndingWithContext ctx) { + if (ctx.nestedTraversal() != null) { + return TextP.notEndingWith((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return TextP.notEndingWith((String) antlr.argumentVisitor.visitStringArgument(ctx.stringArgument())); } @Override public P visitTraversalPredicate_endingWith(final GremlinParser.TraversalPredicate_endingWithContext ctx) { + if (ctx.nestedTraversal() != null) { + return TextP.endingWith((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return TextP.endingWith((String) antlr.argumentVisitor.visitStringArgument(ctx.stringArgument())); } @Override public P visitTraversalPredicate_startingWith(final GremlinParser.TraversalPredicate_startingWithContext ctx) { + if (ctx.nestedTraversal() != null) { + return TextP.startingWith((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return TextP.startingWith((String) antlr.argumentVisitor.visitStringArgument(ctx.stringArgument())); } @Override public P visitTraversalPredicate_notStartingWith(final GremlinParser.TraversalPredicate_notStartingWithContext ctx) { + if (ctx.nestedTraversal() != null) { + return TextP.notStartingWith((Traversal.Admin<?, ?>) antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); + } return TextP.notStartingWith((String) antlr.argumentVisitor.visitStringArgument(ctx.stringArgument())); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java index a57f9cf40b..bed196c29e 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSpawnMethodVisitor.java @@ -19,9 +19,14 @@ package org.apache.tinkerpop.gremlin.language.grammar; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.step.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; + import java.util.Map; /** @@ -94,7 +99,14 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor */ @Override public GraphTraversal visitTraversalSourceSpawnMethod_E(final GremlinParser.TraversalSourceSpawnMethod_EContext ctx) { - return this.traversalSource.E(antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs())); + if (ctx.nestedTraversal() != null) { + return this.traversalSource.E((Traversal<?, ?>) anonymousVisitor.visitNestedTraversal(ctx.nestedTraversal())); + } + final Object[] args = antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs()); + if (args.length == 1 && args[0] instanceof Traversal) { + return this.traversalSource.E((Traversal<?, ?>) args[0]); + } + return this.traversalSource.E(args); } /** @@ -102,7 +114,14 @@ public class TraversalSourceSpawnMethodVisitor extends DefaultGremlinBaseVisitor */ @Override public GraphTraversal visitTraversalSourceSpawnMethod_V(final GremlinParser.TraversalSourceSpawnMethod_VContext ctx) { - return this.traversalSource.V(antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs())); + if (ctx.nestedTraversal() != null) { + return this.traversalSource.V((Traversal<?, ?>) anonymousVisitor.visitNestedTraversal(ctx.nestedTraversal())); + } + final Object[] args = antlr.argumentVisitor.parseObjectVarargs(ctx.genericArgumentVarargs()); + if (args.length == 1 && args[0] instanceof Traversal) { + return this.traversalSource.V((Traversal<?, ?>) args[0]); + } + return this.traversalSource.V(args); } /** diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java index e26a621ba0..cd16ef308c 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java @@ -96,7 +96,7 @@ public class GremlinQueryParserTest { VariableResolver.NullVariableResolver.instance()); final GraphTraversal<?, ?> t = (GraphTraversal<?, ?>) GremlinQueryParser.parse("g.V().has('name',gt(z))", gremlinAntlrToJava); - assertEquals(g.V().has("name", P.gt(null)).asAdmin().getGremlinLang(), + assertEquals(g.V().has("name", P.gt((Object) null)).asAdmin().getGremlinLang(), t.asAdmin().getGremlinLang()); } @@ -107,7 +107,7 @@ public class GremlinQueryParserTest { VariableResolver.NullVariableResolver.instance()); final GraphTraversal<?, ?> t = (GraphTraversal<?, ?>) GremlinQueryParser.parse("g.V(a,b,c).has('name',gt(z))", gremlinAntlrToJava); - assertEquals(g.V(null, null, null).has("name", P.gt(null)).asAdmin().getGremlinLang(), + assertEquals(g.V(null, null, null).has("name", P.gt((Object) null)).asAdmin().getGremlinLang(), t.asAdmin().getGremlinLang()); } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTraversalTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTraversalTest.java new file mode 100644 index 0000000000..12e0ab78ec --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTraversalTest.java @@ -0,0 +1,149 @@ +/* + * 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.grammar; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; +import org.junit.Before; +import org.junit.Test; + +import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal; +import static org.junit.Assert.assertEquals; + +/** + * Tests that the Gremlin grammar correctly parses the new traversal-accepting syntax forms + * for has(), V(), E(), property(), and predicates. + */ +public class GremlinQueryParserTraversalTest { + + private GraphTraversalSource g; + private GremlinAntlrToJava antlrToLanguage; + + @Before + public void setup() { + g = traversal().withEmbedded(EmptyGraph.instance()); + antlrToLanguage = new GremlinAntlrToJava(); + } + + private Object eval(final String query) { + final GremlinLexer lexer = new GremlinLexer(CharStreams.fromString(query)); + final GremlinParser parser = new GremlinParser(new CommonTokenStream(lexer)); + return antlrToLanguage.visit(parser.queryList()); + } + + private void compare(final Object expected, final Object actual) { + assertEquals(((DefaultGraphTraversal) expected).asAdmin().getGremlinLang(), + ((DefaultGraphTraversal) actual).asAdmin().getGremlinLang()); + } + + // ---- has(key, traversal) ---- + + @Test + public void shouldParseHasStringTraversal() { + compare(g.V().has("name", __.values("x")), + eval("g.V().has('name', __.values('x'))")); + } + + // ---- has(label, key, traversal) ---- + + @Test + public void shouldParseHasStringStringTraversal() { + compare(g.V().has("person", "name", __.values("x")), + eval("g.V().has('person', 'name', __.values('x'))")); + } + + // ---- has(T, traversal) ---- + + @Test + public void shouldParseHasTTraversal() { + compare(g.V().has(T.id, __.values("x")), + eval("g.V().has(T.id, __.values('x'))")); + } + + // ---- V(traversal) mid-traversal ---- + + @Test + public void shouldParseVTraversalMidTraversal() { + compare(g.V().out().V(__.select("ids")), + eval("g.V().out().V(__.select('ids'))")); + } + + // ---- E(traversal) mid-traversal ---- + + @Test + public void shouldParseETraversalMidTraversal() { + compare(g.V().out().E(__.select("ids")), + eval("g.V().out().E(__.select('ids'))")); + } + + // ---- property(key, traversal) ---- + + @Test + public void shouldParsePropertyKeyTraversal() { + compare(g.V().property("key", __.select("val")), + eval("g.V().property('key', __.select('val'))")); + } + + // ---- property(Cardinality, key, traversal) ---- + + @Test + public void shouldParsePropertyCardinalityKeyTraversal() { + compare(g.V().property(VertexProperty.Cardinality.single, "key", __.select("val")), + eval("g.V().property(Cardinality.single, 'key', __.select('val'))")); + } + + // ---- P.eq(traversal) via has ---- + + @Test + public void shouldParseHasWithPEqTraversal() { + compare(g.V().has("name", P.eq(__.values("x"))), + eval("g.V().has('name', P.eq(__.values('x')))")); + } + + // ---- P.gt(traversal) via has ---- + + @Test + public void shouldParseHasWithPGtTraversal() { + compare(g.V().has("age", P.gt(__.values("x"))), + eval("g.V().has('age', P.gt(__.values('x')))")); + } + + // ---- P.within(traversal) via has ---- + + @Test + public void shouldParseHasWithPWithinTraversal() { + compare(g.V().has("name", P.within(__.values("x"))), + eval("g.V().has('name', P.within(__.values('x')))")); + } + + // ---- property(traversal) - Map-producing form ---- + + @Test + public void shouldParsePropertyTraversal() { + compare(g.V().property(__.V().project("a").by("name")), + eval("g.V().property(__.V().project('a').by('name'))")); + } +} diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4 index fbe8d41404..65969c5d1d 100644 --- a/gremlin-language/src/main/antlr4/Gremlin.g4 +++ b/gremlin-language/src/main/antlr4/Gremlin.g4 @@ -123,10 +123,12 @@ traversalSourceSpawnMethod_addV traversalSourceSpawnMethod_E : K_E LPAREN genericArgumentVarargs RPAREN + | K_E LPAREN nestedTraversal RPAREN ; traversalSourceSpawnMethod_V : K_V LPAREN genericArgumentVarargs RPAREN + | K_V LPAREN nestedTraversal RPAREN ; traversalSourceSpawnMethod_inject @@ -313,10 +315,12 @@ traversalMethod traversalMethod_V : K_V LPAREN genericArgumentVarargs RPAREN + | K_V LPAREN nestedTraversal RPAREN ; traversalMethod_E : K_E LPAREN genericArgumentVarargs RPAREN + | K_E LPAREN nestedTraversal RPAREN ; traversalMethod_addE @@ -547,10 +551,13 @@ traversalMethod_has : K_HAS LPAREN stringNullableLiteral RPAREN #traversalMethod_has_String | K_HAS LPAREN stringNullableLiteral COMMA genericArgument RPAREN #traversalMethod_has_String_Object | K_HAS LPAREN stringNullableLiteral COMMA traversalPredicate RPAREN #traversalMethod_has_String_P + | K_HAS LPAREN stringNullableLiteral COMMA nestedTraversal RPAREN #traversalMethod_has_String_Traversal | K_HAS LPAREN stringNullableArgument COMMA stringNullableLiteral COMMA genericArgument RPAREN #traversalMethod_has_String_String_Object | K_HAS LPAREN stringNullableArgument COMMA stringNullableLiteral COMMA traversalPredicate RPAREN #traversalMethod_has_String_String_P + | K_HAS LPAREN stringNullableArgument COMMA stringNullableLiteral COMMA nestedTraversal RPAREN #traversalMethod_has_String_String_Traversal | K_HAS LPAREN traversalT COMMA genericArgument RPAREN #traversalMethod_has_T_Object | K_HAS LPAREN traversalT COMMA traversalPredicate RPAREN #traversalMethod_has_T_P + | K_HAS LPAREN traversalT COMMA nestedTraversal RPAREN #traversalMethod_has_T_Traversal ; traversalMethod_hasId @@ -566,6 +573,7 @@ traversalMethod_hasKey traversalMethod_hasLabel : K_HASLABEL LPAREN traversalPredicate RPAREN #traversalMethod_hasLabel_P | K_HASLABEL LPAREN stringNullableArgument (COMMA stringNullableArgumentVarargs)? RPAREN #traversalMethod_hasLabel_String_String + | K_HASLABEL LPAREN nestedTraversal RPAREN #traversalMethod_hasLabel_Traversal ; traversalMethod_hasNot @@ -767,9 +775,12 @@ traversalMethod_properties traversalMethod_property : K_PROPERTY LPAREN traversalCardinality COMMA genericLiteral COMMA genericArgument (COMMA genericArgumentVarargs)? RPAREN #traversalMethod_property_Cardinality_Object_Object_Object + | K_PROPERTY LPAREN traversalCardinality COMMA genericLiteral COMMA nestedTraversal RPAREN #traversalMethod_property_Cardinality_Object_Traversal | K_PROPERTY LPAREN traversalCardinality COMMA genericMapNullableArgument RPAREN # traversalMethod_property_Cardinality_Object | K_PROPERTY LPAREN genericLiteral COMMA genericArgument (COMMA genericArgumentVarargs)? RPAREN #traversalMethod_property_Object_Object_Object + | K_PROPERTY LPAREN genericLiteral COMMA nestedTraversal RPAREN #traversalMethod_property_Object_Traversal | K_PROPERTY LPAREN genericMapNullableArgument RPAREN # traversalMethod_property_Object + | K_PROPERTY LPAREN nestedTraversal RPAREN #traversalMethod_property_Traversal ; traversalMethod_propertyMap @@ -1187,10 +1198,12 @@ traversalBiFunction traversalPredicate_eq : (K_P DOT K_EQ | K_EQ) LPAREN genericArgument RPAREN + | (K_P DOT K_EQ | K_EQ) LPAREN nestedTraversal RPAREN ; traversalPredicate_neq : (K_P DOT K_NEQ | K_NEQ) LPAREN genericArgument RPAREN + | (K_P DOT K_NEQ | K_NEQ) LPAREN nestedTraversal RPAREN ; traversalPredicate_typeOf @@ -1200,40 +1213,49 @@ traversalPredicate_typeOf traversalPredicate_lt : (K_P DOT K_LT | K_LT) LPAREN genericArgument RPAREN + | (K_P DOT K_LT | K_LT) LPAREN nestedTraversal RPAREN ; traversalPredicate_lte : (K_P DOT K_LTE | K_LTE) LPAREN genericArgument RPAREN + | (K_P DOT K_LTE | K_LTE) LPAREN nestedTraversal RPAREN ; traversalPredicate_gt : (K_P DOT K_GT | K_GT) LPAREN genericArgument RPAREN + | (K_P DOT K_GT | K_GT) LPAREN nestedTraversal RPAREN ; traversalPredicate_gte : (K_P DOT K_GTE | K_GTE) LPAREN genericArgument RPAREN + | (K_P DOT K_GTE | K_GTE) LPAREN nestedTraversal RPAREN ; traversalPredicate_inside : (K_P DOT K_INSIDE | K_INSIDE) LPAREN genericArgument COMMA genericArgument RPAREN + | (K_P DOT K_INSIDE | K_INSIDE) LPAREN nestedTraversal COMMA nestedTraversal RPAREN ; traversalPredicate_outside : (K_P DOT K_OUTSIDE | K_OUTSIDE) LPAREN genericArgument COMMA genericArgument RPAREN + | (K_P DOT K_OUTSIDE | K_OUTSIDE) LPAREN nestedTraversal COMMA nestedTraversal RPAREN ; traversalPredicate_between : (K_P DOT K_BETWEEN | K_BETWEEN) LPAREN genericArgument COMMA genericArgument RPAREN + | (K_P DOT K_BETWEEN | K_BETWEEN) LPAREN nestedTraversal COMMA nestedTraversal RPAREN ; traversalPredicate_within : (K_P DOT K_WITHIN | K_WITHIN) LPAREN RPAREN | (K_P DOT K_WITHIN | K_WITHIN) LPAREN genericArgumentVarargs RPAREN + | (K_P DOT K_WITHIN | K_WITHIN) LPAREN nestedTraversal RPAREN ; traversalPredicate_without : (K_P DOT K_WITHOUT | K_WITHOUT) LPAREN RPAREN | (K_P DOT K_WITHOUT | K_WITHOUT) LPAREN genericArgumentVarargs RPAREN + | (K_P DOT K_WITHOUT | K_WITHOUT) LPAREN nestedTraversal RPAREN ; traversalPredicate_not @@ -1242,26 +1264,32 @@ traversalPredicate_not traversalPredicate_containing : (K_TEXTP DOT K_CONTAINING | K_CONTAINING) LPAREN stringArgument RPAREN + | (K_TEXTP DOT K_CONTAINING | K_CONTAINING) LPAREN nestedTraversal RPAREN ; traversalPredicate_notContaining : (K_TEXTP DOT K_NOTCONTAINING | K_NOTCONTAINING) LPAREN stringArgument RPAREN + | (K_TEXTP DOT K_NOTCONTAINING | K_NOTCONTAINING) LPAREN nestedTraversal RPAREN ; traversalPredicate_startingWith : (K_TEXTP DOT K_STARTINGWITH | K_STARTINGWITH) LPAREN stringArgument RPAREN + | (K_TEXTP DOT K_STARTINGWITH | K_STARTINGWITH) LPAREN nestedTraversal RPAREN ; traversalPredicate_notStartingWith : (K_TEXTP DOT K_NOTSTARTINGWITH | K_NOTSTARTINGWITH) LPAREN stringArgument RPAREN + | (K_TEXTP DOT K_NOTSTARTINGWITH | K_NOTSTARTINGWITH) LPAREN nestedTraversal RPAREN ; traversalPredicate_endingWith : (K_TEXTP DOT K_ENDINGWITH | K_ENDINGWITH) LPAREN stringArgument RPAREN + | (K_TEXTP DOT K_ENDINGWITH | K_ENDINGWITH) LPAREN nestedTraversal RPAREN ; traversalPredicate_notEndingWith : (K_TEXTP DOT K_NOTENDINGWITH | K_NOTENDINGWITH) LPAREN stringArgument RPAREN + | (K_TEXTP DOT K_NOTENDINGWITH | K_NOTENDINGWITH) LPAREN nestedTraversal RPAREN ; traversalPredicate_regex
