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 2021/03/16 19:07:48 UTC

[tinkerpop] branch TINKERPOP-2533 updated (7fe49d5 -> 9a85554)

This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a change to branch TINKERPOP-2533
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git.


 discard 7fe49d5  TINKERPOP-2533 Added gremlin-grammar module.
     add 3b01560  Bump to Netty 4.1.59 CTR
     add 80139a1  Merge branch '3.4-dev'
     new 9a85554  TINKERPOP-2533 Added gremlin-grammar module.

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (7fe49d5)
            \
             N -- N -- N   refs/heads/TINKERPOP-2533 (9a85554)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.asciidoc                     | 1 +
 gremlin-console/src/main/static/NOTICE | 2 +-
 gremlin-server/src/main/static/NOTICE  | 2 +-
 pom.xml                                | 2 +-
 4 files changed, 4 insertions(+), 3 deletions(-)


[tinkerpop] 01/01: TINKERPOP-2533 Added gremlin-grammar module.

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch TINKERPOP-2533
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 9a855542eda5ac41b59cc55a4bf168b84639fe53
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Tue Mar 16 15:00:09 2021 -0400

    TINKERPOP-2533 Added gremlin-grammar module.
    
    This module is currently standalone, meaning it depends on no other TinkerPop modules nor does any module depend on it. It represents an initial body of work which has reasonable coverage of Gremlin and a expansive and flexible test suite.
---
 CHANGELOG.asciidoc                                 |    1 +
 docs/src/dev/developer/contributing.asciidoc       |    8 +-
 .../dev/developer/development-environment.asciidoc |   17 +
 docs/src/dev/developer/for-committers.asciidoc     |   12 +
 docs/src/recipes/anti-patterns.asciidoc            |    4 +-
 docs/src/recipes/looping.asciidoc                  |   12 +-
 docs/src/recipes/recommendation.asciidoc           |    2 +-
 docs/src/recipes/traversal-induced-values.asciidoc |   10 +-
 docs/src/reference/the-traversal.asciidoc          |   28 +-
 docs/src/upgrade/release-3.5.x.asciidoc            |   21 +
 gremlin-grammar/pom.xml                            |   90 ++
 gremlin-grammar/src/main/antlr4/Gremlin.g4         | 1545 ++++++++++++++++++++
 .../gremlin/grammar/AbstractGrammarTest.java       |   85 ++
 .../gremlin/grammar/BasicGrammarTest.java          |   31 +
 .../gremlin/grammar/DocumentationReader.java       |  122 ++
 .../tinkerpop/gremlin/grammar}/FeatureReader.java  |   47 +-
 .../gremlin/grammar/GremlinErrorListener.java      |   33 +
 .../gremlin/grammar/NegativeGrammarTest.java       |   60 +
 .../gremlin/grammar/ReferenceGrammarTest.java      |   74 +
 .../src/test/resources/incorrect-traversals.txt    |   22 +
 .../tinkerpop/gremlin/features/FeatureReader.java  |    3 +-
 neo4j-gremlin/pom.xml                              |    2 +-
 pom.xml                                            |    5 +
 .../tinkergraph/structure/TinkerGraphPlayTest.java |    5 +
 24 files changed, 2192 insertions(+), 47 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index c1f7041..f7642b9 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -25,6 +25,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 
 This release also includes changes from <<release-3-4-3, 3.4.3>>.
 
+* Added `gremlin-grammar` module.
 * Allowed the possibility for the propagation of `null` as a `Traverser` in Gremlin.
 * Exposed websocket connection status in JavaScript driver.
 * Fixed a bug where spark-gremlin was not re-attaching properties when using `dedup()`.
diff --git a/docs/src/dev/developer/contributing.asciidoc b/docs/src/dev/developer/contributing.asciidoc
index fc720d5..3087124 100644
--- a/docs/src/dev/developer/contributing.asciidoc
+++ b/docs/src/dev/developer/contributing.asciidoc
@@ -75,10 +75,10 @@ old content.
 
 For both types of documentation, changes can be submitted via pull request. For project documentation, TinkerPop has
 a robust documentation system that is based on link:http://asciidoc.org/[asciidoc]. The content can be found in the
-link:https://github.com/apache/tinkerpop/tree/master/docs/src[docs/src]. Recall that this documentation is version
-specific, so consider the appropriate branch on which to submit the pull request so that the documentation is reflective
-of the version it is tied to. To view generated documentation locally, read more about environment configurations in
-the <<documentation-environment,Documentation Environment>> section.
+link:https://github.com/apache/tinkerpop/tree/master/docs/src[docs/src]. Documentation is version specific, so consider
+the appropriate branch on which to submit the pull request so that the documentation is reflective of the version it is
+tied to. To view generated documentation locally, read more about environment configurations in the
+<<documentation-environment,Documentation Environment>> and <<documentation, Contributor Documentation>> sections.
 
 For web site changes, the process is largely the same except that the documentation system is HTML based instead of
 Asciidoc. The content can be found in the source control tree at link:https://github.com/apache/tinkerpop/tree/master/docs/site[docs/site].
diff --git a/docs/src/dev/developer/development-environment.asciidoc b/docs/src/dev/developer/development-environment.asciidoc
index b2fea52..c4860e6 100644
--- a/docs/src/dev/developer/development-environment.asciidoc
+++ b/docs/src/dev/developer/development-environment.asciidoc
@@ -467,3 +467,20 @@ Intellij should be able to detect the processor automatically on build.
 `target/generated-test-sources` should be marked as "Generated Sources Root". If they are not setup that way by
 Intellij by default then simply right-click on them use the "Mark Directory with" option to make the appropriate
 selections.
+
+The `gremlin-grammar` module requires ANTLR processing. While this processing is configured to execute with Maven, it
+can also be setup to generate parser files within Intellij itself on command:
+
+. Install the ANTLR4 Grammar Plugin for Intellij
+. Right-click on the `Gremlin.g4` file and "Configure ANTLR"
+. Set "Output directory where all output is generated" to `target/generated-sources/antlr4`
+. Set "Grammar file encoding" to `utf-8`
+. Set "Package/namespace for the generated code" to `org.apache.tinkerpop.gremlin.grammar`
+. Set "Language" to `Java`
+. Set "Case transformation in the Preview window" should be "Leave as-is"
+. The "generate parse tree listener" should be unchecked and the "generate parse tree visitor" should be checked.
+
+With these settings it should be possible to right-click `Gremlin.g4` and "Generate ANTLR Recognizer" which will place
+the generated code in where specified at `target/generated-sources/antlr4`. Be sure to right-click the `antlr4`
+directory and "Mark directory as" "Generated Sources Root" which should allow Intellij to recognize it.
+
diff --git a/docs/src/dev/developer/for-committers.asciidoc b/docs/src/dev/developer/for-committers.asciidoc
index 1cd9580..ac751cc 100644
--- a/docs/src/dev/developer/for-committers.asciidoc
+++ b/docs/src/dev/developer/for-committers.asciidoc
@@ -717,6 +717,18 @@ the existing `<execution>` entries, paying special attention to the pathing of t
 `<outputDirectory>` and `<imagesdir>`.  Note that the `<outputDirectory>` represents where the book will exist when
 uploaded to the server and should preserve the directory structure in git as referenced in `<sourceDirectory>`.
 
+Adding Gremlin code examples to any of the link:https://github.com/apache/tinkerpop/tree/master/docs/src/recipes[docs/src/recipes]
+or to link:https://github.com/apache/tinkerpop/tree/master/docs/src/reference/the-traversal.asciidoc[docs/src/reference/the-traversal.asciidoc]
+also has the effect of improving testing of the Gremlin language. All Gremlin found in code sections that are marked
+as `[gremlin-groovy]` are tested in two ways:
+
+1. When `mvn clean install` is executed all such Gremlin are passed through the grammar parser to ensure validity.
+As the grammar parser is not a Groovy parser, the test framework attempts to filter away or ignore things it can't
+possibly parse. Ideally, examples should be written in such a way as to be parsed by the grammar, but in cases where it
+cannot be as such, the test suite simply needs to be modified to suitably ignore the example.
+2. When the documentation is built, the code snippets are actually executed and errors will result in a failure to
+build the documentation.
+
 Please see the <<building-testing,Building and Testing>> section for more information on how to generate the
 documentation.
 
diff --git a/docs/src/recipes/anti-patterns.asciidoc b/docs/src/recipes/anti-patterns.asciidoc
index 5567be4..1341f33 100644
--- a/docs/src/recipes/anti-patterns.asciidoc
+++ b/docs/src/recipes/anti-patterns.asciidoc
@@ -68,7 +68,7 @@ the traversal and process them from there.
 
 [gremlin-groovy]
 ----
-g.withSideEffect("relations", relations).
+g.withSideEffect("rels", relations).
   inject(persons).sideEffect(
     unfold().
     addV("person").
@@ -78,7 +78,7 @@ g.withSideEffect("relations", relations).
     group("m").
       by(id).
       by(unfold())).
-  select("relations").unfold().as("r").
+  select("rels").unfold().as("r").
   addE("knows").
     from(select("m").select(select("r").select("from"))).
     to(select("m").select(select("r").select("to"))).
diff --git a/docs/src/recipes/looping.asciidoc b/docs/src/recipes/looping.asciidoc
index 164bfd9..3ae4ecc 100644
--- a/docs/src/recipes/looping.asciidoc
+++ b/docs/src/recipes/looping.asciidoc
@@ -82,8 +82,8 @@ If the desired output is to get each vertex and its associated depth this can be
 ----
 g.withSack(1).V('A').
   repeat(both().simplePath().
-    sack(assign).by(loops())
-  ).emit().
+         sack(assign).by(loops())).
+    emit().
   project('vertex', 'depth').
     by().
     by(sack())
@@ -95,9 +95,11 @@ this can be accomplished with this traversal.
 [gremlin-groovy,existing]
 ----
 g.V('A').
-  repeat(both().simplePath().group('x').
-    by(loops())
-  ).emit().times(3).
+  repeat(both().simplePath().
+         group('x').
+           by(loops())).
+    emit().
+    times(3).
   cap('x')
 ----
 
diff --git a/docs/src/recipes/recommendation.asciidoc b/docs/src/recipes/recommendation.asciidoc
index aab20a4..f02c4e9 100644
--- a/docs/src/recipes/recommendation.asciidoc
+++ b/docs/src/recipes/recommendation.asciidoc
@@ -33,7 +33,7 @@ g.addV("person").property("name","alice").
   addV("person").property("name","bob").
   addV("person").property("name","jon").
   addV("person").property("name","jack").
-  addV("person").property("name","jill")iterate()
+  addV("person").property("name","jill").iterate()
 (1..10).each {
   g.addV("product").property("name","product #${it}").iterate()
 }; []
diff --git a/docs/src/recipes/traversal-induced-values.asciidoc b/docs/src/recipes/traversal-induced-values.asciidoc
index f3592b8..6e421dd 100644
--- a/docs/src/recipes/traversal-induced-values.asciidoc
+++ b/docs/src/recipes/traversal-induced-values.asciidoc
@@ -33,8 +33,8 @@ obvious to any programmer - use a variable:
 
 [gremlin-groovy,modern]
 ----
-marko = g.V().has('name','marko').next()
-g.V(marko).out('knows').has('age', gt(marko.value('age'))).values('name')
+vMarko = g.V().has('name','marko').next()
+g.V(vMarko).out('knows').has('age', gt(marko.value('age'))).values('name')
 ----
 
 The downside to this approach is that it takes two separate traversals to answer the question. Ideally, there should
@@ -137,9 +137,9 @@ g.addV('tank').property('name', 'a').property('amount', 100.0).as('a').
   addV('tank').property('name', 'c').property('amount', 0.0).as('c').
   addE('drain').property('factor', 0.5).from('a').to('b').
   addE('drain').property('factor', 0.1).from('b').to('c').iterate()
-a = g.V().has('name','a').next()
-g.withSack(a.value('amount')).
-  V(a).repeat(outE('drain').sack(mult).by('factor').
+vA = g.V().has('name','a').next()
+g.withSack(vA.value('amount')).
+  V(vA).repeat(outE('drain').sack(mult).by('factor').
               inV().property('amount', sack())).
        until(__.outE('drain').count().is(0)).iterate()
 g.V().elementMap()
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 8a96b51..fb57dce 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -240,10 +240,10 @@ g.V().match(
       addE('friendlyCollaborator').from('a').to('b').
         property(id,23).property('project',select('c').values('name')) <5>
 g.E(23).valueMap()
-marko = g.V().has('name','marko').next()
-peter = g.V().has('name','peter').next()
-g.V(marko).addE('knows').to(peter) <6>
-g.addE('knows').from(marko).to(peter) <7>
+vMarko = g.V().has('name','marko').next()
+vPeter = g.V().has('name','peter').next()
+g.V(vMarko).addE('knows').to(vPeter) <6>
+g.addE('knows').from(vMarko).to(vPeter) <7>
 ----
 
 <1> Add a co-developer edge with a year-property between marko and his collaborators.
@@ -2723,15 +2723,17 @@ the traverser never splits and continues down a single path in the graph.
 
 [gremlin-groovy,modern]
 ----
-g.V(1).repeat(local(
-         bothE().sample(1).by('weight').otherV()
-       )).times(5)
-g.V(1).repeat(local(
-         bothE().sample(1).by('weight').otherV()
-       )).times(5).path()
-g.V(1).repeat(local(
-         bothE().sample(1).by('weight').otherV()
-       )).times(10).path()
+g.V(1).
+  repeat(local(bothE().sample(1).by('weight').otherV())).
+    times(5)
+g.V(1).
+  repeat(local(bothE().sample(1).by('weight').otherV())).
+    times(5).
+  path()
+g.V(1).
+  repeat(local(bothE().sample(1).by('weight').otherV())).
+    times(10).
+  path()
 ----
 
 As a clarification, note that in the above example `local()` is not strictly required as it only does the random walk
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index 79793ab..527061b 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -937,6 +937,27 @@ determine when it is considered acceptable to retry an operation.
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-2517[TINKERPOP-2517]
 
+===== gremlin-grammar
+
+The new `gremlin-grammar` module contains an ANTLR4 grammar for the Gremlin language along with the generated parser
+code. The grammar is still under development but covers most of the Gremlin language, with the idea that it will
+eventually drive the ongoing design of the language, as opposed to driving it from Java.
+
+The grammar is currently tested against the Gremlin traversals in the entire Gherkin test suite, as well as a major
+portion of the Gremlin used for examples in the Reference Documentation. The grammar has the following limitations:
+
+* It does not support lambdas or Groovy syntax
+* The following steps are not yet fully supported:
+** `withComputer()`
+** `io()`
+** `withoutStrategies()`
+** `program()`
+** `connectedComponent()`
+** `fill()` terminator step
+* `Vertex` and `Edge` instance definitions
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2533[https://issues.apache.org/jira/browse/TINKERPOP-2533]
+
 ==== Graph Driver Providers
 
 ===== TraversalOpProcessor Side-effects
diff --git a/gremlin-grammar/pom.xml b/gremlin-grammar/pom.xml
new file mode 100644
index 0000000..8fd3108
--- /dev/null
+++ b/gremlin-grammar/pom.xml
@@ -0,0 +1,90 @@
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.tinkerpop</groupId>
+        <artifactId>tinkerpop</artifactId>
+        <version>3.5.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>gremlin-grammar</artifactId>
+    <name>Apache TinkerPop :: Gremlin Grammar</name>
+    <dependencies>
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr4</artifactId>
+            <version>${antlr4.version}</version>
+        </dependency>
+        <!-- TESTING -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-text</artifactId>
+            <version>${commons.text.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <properties>
+        <antlr4.version>4.9.1</antlr4.version>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.antlr</groupId>
+                <artifactId>antlr4-maven-plugin</artifactId>
+                <version>${antlr4.version}</version>
+                <executions>
+                    <execution>
+                        <id>antlr</id>
+                        <goals>
+                            <goal>antlr4</goal>
+                        </goals>
+                        <configuration>
+                            <arguments>
+                                <argument>-package</argument>
+                                <argument>org.apache.tinkerpop.gremlin.grammar</argument>
+                                <argument>-encoding</argument>
+                                <argument>utf-8</argument>
+                            </arguments>
+                            <visitor>true</visitor>
+                            <outputDirectory>${project.build.directory}/generated-sources/antlr4/org/apache/tinkerpop/gremlin/grammar/</outputDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/gremlin-grammar/src/main/antlr4/Gremlin.g4 b/gremlin-grammar/src/main/antlr4/Gremlin.g4
new file mode 100644
index 0000000..0c1569e
--- /dev/null
+++ b/gremlin-grammar/src/main/antlr4/Gremlin.g4
@@ -0,0 +1,1545 @@
+/*
+ * 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.
+ */
+grammar Gremlin;
+
+/*********************************************
+    PARSER RULES
+**********************************************/
+
+queryList
+    : query (SEMI? query)* SEMI? EOF
+    ;
+
+query
+    : traversalSource
+    | traversalSource DOT transactionPart
+    | rootTraversal
+    | rootTraversal DOT traversalTerminalMethod
+    | query DOT 'toString' LPAREN RPAREN
+    | emptyQuery
+    ;
+
+emptyQuery
+    : EmptyStringLiteral
+    ;
+
+traversalSource
+    : TRAVERSAL_ROOT
+    | TRAVERSAL_ROOT DOT traversalSourceSelfMethod
+    | traversalSource DOT traversalSourceSelfMethod
+    ;
+
+transactionPart
+    : 'tx' LPAREN RPAREN DOT 'commit' LPAREN RPAREN
+    | 'tx' LPAREN RPAREN DOT 'rollback' LPAREN RPAREN
+    | 'tx' LPAREN RPAREN
+    ;
+
+rootTraversal
+    : traversalSource DOT traversalSourceSpawnMethod
+    | traversalSource DOT traversalSourceSpawnMethod DOT chainedTraversal
+    | traversalSource DOT traversalSourceSpawnMethod DOT chainedParentOfGraphTraversal
+    ;
+
+traversalSourceSelfMethod
+    : traversalSourceSelfMethod_withBulk
+    | traversalSourceSelfMethod_withPath
+    | traversalSourceSelfMethod_withSack
+    | traversalSourceSelfMethod_withSideEffect
+    | traversalSourceSelfMethod_withStrategies
+    | traversalSourceSelfMethod_with
+    ;
+
+traversalSourceSelfMethod_withBulk
+    : 'withBulk' LPAREN booleanLiteral RPAREN
+    ;
+
+traversalSourceSelfMethod_withPath
+    : 'withPath' LPAREN RPAREN
+    ;
+
+traversalSourceSelfMethod_withSack
+    : 'withSack' LPAREN genericLiteral RPAREN
+    | 'withSack' LPAREN genericLiteral COMMA traversalOperator RPAREN
+    ;
+
+traversalSourceSelfMethod_withSideEffect
+    : 'withSideEffect' LPAREN stringLiteral COMMA genericLiteral RPAREN
+    ;
+
+traversalSourceSelfMethod_withStrategies
+    : 'withStrategies' LPAREN traversalStrategy (COMMA traversalStrategyList)? RPAREN
+    ;
+
+traversalSourceSelfMethod_with
+    : 'with' LPAREN stringLiteral RPAREN
+    | 'with' LPAREN stringLiteral COMMA genericLiteral RPAREN
+    ;
+
+traversalSourceSpawnMethod
+	: traversalSourceSpawnMethod_addE
+	| traversalSourceSpawnMethod_addV
+	| traversalSourceSpawnMethod_E
+	| traversalSourceSpawnMethod_V
+	| traversalSourceSpawnMethod_inject
+    | traversalSourceSpawnMethod_io
+    ;
+
+traversalSourceSpawnMethod_addE
+	: 'addE' LPAREN stringLiteral RPAREN
+	| 'addE' LPAREN nestedTraversal RPAREN
+	;
+
+traversalSourceSpawnMethod_addV
+	: 'addV' LPAREN RPAREN
+	| 'addV' LPAREN stringLiteral RPAREN
+	| 'addV' LPAREN nullLiteral RPAREN          // null means use the default vertex label
+	| 'addV' LPAREN nestedTraversal RPAREN
+	;
+
+traversalSourceSpawnMethod_E
+	: 'E' LPAREN genericLiteralList RPAREN
+	;
+
+traversalSourceSpawnMethod_V
+	: 'V' LPAREN genericLiteralList RPAREN
+	;
+
+traversalSourceSpawnMethod_inject
+    : 'inject' LPAREN genericLiteralList RPAREN
+    ;
+
+traversalSourceSpawnMethod_io
+    : 'io' LPAREN stringLiteral RPAREN
+    ;
+
+chainedTraversal
+    : traversalMethod
+    | chainedTraversal DOT traversalMethod
+    | chainedTraversal DOT chainedParentOfGraphTraversal
+    ;
+
+chainedParentOfGraphTraversal
+    : traversalSelfMethod
+    | chainedParentOfGraphTraversal DOT traversalSelfMethod
+    ;
+
+nestedTraversal
+    : rootTraversal
+    | chainedTraversal
+    | ANON_TRAVERSAL_ROOT DOT chainedTraversal
+    ;
+
+terminatedTraversal
+    : rootTraversal DOT traversalTerminalMethod
+    ;
+
+/*********************************************
+    GENERATED GRAMMAR - DO NOT CHANGE
+**********************************************/
+
+traversalMethod
+	: traversalMethod_V
+	| traversalMethod_addE
+	| traversalMethod_addV
+	| traversalMethod_aggregate
+	| traversalMethod_and
+	| traversalMethod_as
+	| traversalMethod_barrier
+	| traversalMethod_both
+	| traversalMethod_bothE
+	| traversalMethod_bothV
+	| traversalMethod_branch
+	| traversalMethod_by
+	| traversalMethod_cap
+	| traversalMethod_choose
+	| traversalMethod_coalesce
+	| traversalMethod_coin
+	| traversalMethod_connectedComponent
+	| traversalMethod_constant
+	| traversalMethod_count
+	| traversalMethod_cyclicPath
+	| traversalMethod_dedup
+	| traversalMethod_drop
+	| traversalMethod_elementMap
+	| traversalMethod_emit
+	| traversalMethod_filter
+	| traversalMethod_flatMap
+	| traversalMethod_fold
+	| traversalMethod_from
+	| traversalMethod_group
+	| traversalMethod_groupCount
+	| traversalMethod_has
+	| traversalMethod_hasId
+	| traversalMethod_hasKey
+	| traversalMethod_hasLabel
+	| traversalMethod_hasNot
+	| traversalMethod_hasValue
+	| traversalMethod_id
+	| traversalMethod_identity
+	| traversalMethod_in
+	| traversalMethod_inE
+	| traversalMethod_inV
+	| traversalMethod_index
+	| traversalMethod_inject
+	| traversalMethod_is
+	| traversalMethod_key
+	| traversalMethod_label
+	| traversalMethod_limit
+	| traversalMethod_local
+	| traversalMethod_loops
+	| traversalMethod_map
+	| traversalMethod_match
+	| traversalMethod_math
+	| traversalMethod_max
+	| traversalMethod_mean
+	| traversalMethod_min
+	| traversalMethod_not
+	| traversalMethod_option
+	| traversalMethod_optional
+	| traversalMethod_or
+	| traversalMethod_order
+	| traversalMethod_otherV
+	| traversalMethod_out
+	| traversalMethod_outE
+	| traversalMethod_outV
+	| traversalMethod_pageRank
+	| traversalMethod_path
+	| traversalMethod_peerPressure
+	| traversalMethod_profile
+	| traversalMethod_project
+	| traversalMethod_properties
+	| traversalMethod_property
+	| traversalMethod_propertyMap
+	| traversalMethod_range
+	| traversalMethod_read
+	| traversalMethod_repeat
+	| traversalMethod_sack
+	| traversalMethod_sample
+	| traversalMethod_select
+	| traversalMethod_shortestPath
+	| traversalMethod_sideEffect
+	| traversalMethod_simplePath
+	| traversalMethod_skip
+	| traversalMethod_store
+	| traversalMethod_subgraph
+	| traversalMethod_sum
+	| traversalMethod_tail
+	| traversalMethod_timeLimit
+	| traversalMethod_times
+	| traversalMethod_to
+	| traversalMethod_toE
+	| traversalMethod_toV
+	| traversalMethod_tree
+	| traversalMethod_unfold
+	| traversalMethod_union
+	| traversalMethod_until
+	| traversalMethod_value
+	| traversalMethod_valueMap
+	| traversalMethod_values
+	| traversalMethod_where
+	| traversalMethod_with
+	| traversalMethod_write
+	;
+traversalMethod_V
+	: 'V' LPAREN genericLiteralList RPAREN
+	;
+
+traversalMethod_addE
+	: 'addE' LPAREN stringLiteral RPAREN #traversalMethod_addE_String
+	| 'addE' LPAREN nestedTraversal RPAREN #traversalMethod_addE_Traversal
+	;
+
+traversalMethod_addV
+	: 'addV' LPAREN RPAREN #traversalMethod_addV_Empty
+	| 'addV' LPAREN stringLiteral RPAREN #traversalMethod_addV_String
+	| 'addV' LPAREN nullLiteral RPAREN #traversalMethod_addV_String        // null means use the default vertex label
+	| 'addV' LPAREN nestedTraversal RPAREN #traversalMethod_addV_Traversal
+	;
+
+traversalMethod_aggregate
+	: 'aggregate' LPAREN traversalScope COMMA stringLiteral RPAREN #traversalMethod_aggregate_Scope_String
+	| 'aggregate' LPAREN stringLiteral RPAREN #traversalMethod_aggregate_String
+	;
+
+traversalMethod_and
+	: 'and' LPAREN nestedTraversalList RPAREN
+	;
+
+traversalMethod_as
+	: 'as' LPAREN stringLiteral (COMMA stringLiteralList)? RPAREN
+	;
+
+traversalMethod_barrier
+	: 'barrier' LPAREN traversalSackMethod RPAREN #traversalMethod_barrier_Consumer
+	| 'barrier' LPAREN RPAREN #traversalMethod_barrier_Empty
+	| 'barrier' LPAREN integerLiteral RPAREN #traversalMethod_barrier_int
+	;
+
+traversalMethod_both
+	: 'both' LPAREN stringLiteralList RPAREN
+	;
+
+traversalMethod_bothE
+	: 'bothE' LPAREN stringLiteralList RPAREN
+	;
+
+traversalMethod_bothV
+	: 'bothV' LPAREN RPAREN
+	;
+
+traversalMethod_branch
+	: 'branch' LPAREN nestedTraversal RPAREN
+	;
+
+traversalMethod_by
+	: 'by' LPAREN traversalComparator RPAREN #traversalMethod_by_Comparator
+	| 'by' LPAREN RPAREN #traversalMethod_by_Empty
+	| 'by' LPAREN traversalFunction RPAREN #traversalMethod_by_Function
+	| 'by' LPAREN traversalFunction COMMA traversalComparator RPAREN #traversalMethod_by_Function_Comparator
+	| 'by' LPAREN traversalOrder RPAREN #traversalMethod_by_Order
+	| 'by' LPAREN stringLiteral RPAREN #traversalMethod_by_String
+	| 'by' LPAREN stringLiteral COMMA traversalComparator RPAREN #traversalMethod_by_String_Comparator
+	| 'by' LPAREN traversalToken RPAREN #traversalMethod_by_T
+	| 'by' LPAREN nestedTraversal RPAREN #traversalMethod_by_Traversal
+	| 'by' LPAREN nestedTraversal COMMA traversalComparator RPAREN #traversalMethod_by_Traversal_Comparator
+	;
+
+traversalMethod_cap
+	: 'cap' LPAREN stringLiteral (COMMA stringLiteralList)? RPAREN
+	;
+
+traversalMethod_choose
+	: 'choose' LPAREN traversalFunction RPAREN #traversalMethod_choose_Function
+	| 'choose' LPAREN traversalPredicate COMMA nestedTraversal RPAREN #traversalMethod_choose_Predicate_Traversal
+	| 'choose' LPAREN traversalPredicate COMMA nestedTraversal COMMA nestedTraversal RPAREN #traversalMethod_choose_Predicate_Traversal_Traversal
+	| 'choose' LPAREN nestedTraversal RPAREN #traversalMethod_choose_Traversal
+	| 'choose' LPAREN nestedTraversal COMMA nestedTraversal RPAREN #traversalMethod_choose_Traversal_Traversal
+	| 'choose' LPAREN nestedTraversal COMMA nestedTraversal COMMA nestedTraversal RPAREN #traversalMethod_choose_Traversal_Traversal_Traversal
+	;
+
+traversalMethod_coalesce
+	: 'coalesce' LPAREN nestedTraversalList RPAREN
+	;
+
+traversalMethod_coin
+	: 'coin' LPAREN floatLiteral RPAREN
+	;
+
+traversalMethod_connectedComponent
+	: 'connectedComponent' LPAREN RPAREN
+	;
+
+traversalMethod_constant
+	: 'constant' LPAREN genericLiteral RPAREN
+	;
+
+traversalMethod_count
+	: 'count' LPAREN RPAREN #traversalMethod_count_Empty
+	| 'count' LPAREN traversalScope RPAREN #traversalMethod_count_Scope
+	;
+
+traversalMethod_cyclicPath
+	: 'cyclicPath' LPAREN RPAREN
+	;
+
+traversalMethod_dedup
+	: 'dedup' LPAREN traversalScope (COMMA stringLiteralList)? RPAREN #traversalMethod_dedup_Scope_String
+	| 'dedup' LPAREN stringLiteralList RPAREN #traversalMethod_dedup_String
+	;
+
+traversalMethod_drop
+	: 'drop' LPAREN RPAREN
+	;
+
+traversalMethod_elementMap
+	: 'elementMap' LPAREN stringLiteralList RPAREN
+	;
+
+traversalMethod_emit
+	: 'emit' LPAREN RPAREN #traversalMethod_emit_Empty
+	| 'emit' LPAREN traversalPredicate RPAREN #traversalMethod_emit_Predicate
+	| 'emit' LPAREN nestedTraversal RPAREN #traversalMethod_emit_Traversal
+	;
+
+traversalMethod_filter
+	: 'filter' LPAREN traversalPredicate RPAREN #traversalMethod_filter_Predicate
+	| 'filter' LPAREN nestedTraversal RPAREN #traversalMethod_filter_Traversal
+	;
+
+traversalMethod_flatMap
+	: 'flatMap' LPAREN nestedTraversal RPAREN
+	;
+
+traversalMethod_fold
+	: 'fold' LPAREN RPAREN #traversalMethod_fold_Empty
+	| 'fold' LPAREN genericLiteral COMMA traversalBiFunction RPAREN #traversalMethod_fold_Object_BiFunction
+	;
+
+traversalMethod_from
+	: 'from' LPAREN stringLiteral RPAREN #traversalMethod_from_String
+	| 'from' LPAREN nestedTraversal RPAREN #traversalMethod_from_Traversal
+	;
+
+traversalMethod_group
+	: 'group' LPAREN RPAREN #traversalMethod_group_Empty
+	| 'group' LPAREN stringLiteral RPAREN #traversalMethod_group_String
+	;
+
+traversalMethod_groupCount
+	: 'groupCount' LPAREN RPAREN #traversalMethod_groupCount_Empty
+	| 'groupCount' LPAREN stringLiteral RPAREN #traversalMethod_groupCount_String
+	;
+
+traversalMethod_has
+	: 'has' LPAREN stringLiteral RPAREN #traversalMethod_has_String
+	| 'has' LPAREN stringLiteral COMMA genericLiteral RPAREN #traversalMethod_has_String_Object
+	| 'has' LPAREN stringLiteral COMMA traversalPredicate RPAREN #traversalMethod_has_String_P
+	| 'has' LPAREN stringLiteral COMMA stringLiteral COMMA genericLiteral RPAREN #traversalMethod_has_String_String_Object
+	| 'has' LPAREN stringLiteral COMMA stringLiteral COMMA traversalPredicate RPAREN #traversalMethod_has_String_String_P
+	| 'has' LPAREN stringLiteral COMMA nestedTraversal RPAREN #traversalMethod_has_String_Traversal
+	| 'has' LPAREN traversalToken COMMA genericLiteral RPAREN #traversalMethod_has_T_Object
+	| 'has' LPAREN traversalToken COMMA traversalPredicate RPAREN #traversalMethod_has_T_P
+	| 'has' LPAREN traversalToken COMMA nestedTraversal RPAREN #traversalMethod_has_T_Traversal
+	;
+
+traversalMethod_hasId
+	: 'hasId' LPAREN genericLiteral (COMMA genericLiteralList)? RPAREN #traversalMethod_hasId_Object_Object
+	| 'hasId' LPAREN traversalPredicate RPAREN #traversalMethod_hasId_P
+	;
+
+traversalMethod_hasKey
+	: 'hasKey' LPAREN traversalPredicate RPAREN #traversalMethod_hasKey_P
+	| 'hasKey' LPAREN stringLiteral (COMMA stringLiteralList)? RPAREN #traversalMethod_hasKey_String_String
+	;
+
+traversalMethod_hasLabel
+	: 'hasLabel' LPAREN traversalPredicate RPAREN #traversalMethod_hasLabel_P
+	| 'hasLabel' LPAREN stringLiteral (COMMA stringLiteralList)? RPAREN #traversalMethod_hasLabel_String_String
+	;
+
+traversalMethod_hasNot
+	: 'hasNot' LPAREN stringLiteral RPAREN
+	;
+
+traversalMethod_hasValue
+	: 'hasValue' LPAREN genericLiteral (COMMA genericLiteralList)? RPAREN #traversalMethod_hasValue_Object_Object
+	| 'hasValue' LPAREN traversalPredicate RPAREN #traversalMethod_hasValue_P
+	;
+
+traversalMethod_id
+	: 'id' LPAREN RPAREN
+	;
+
+traversalMethod_identity
+	: 'identity' LPAREN RPAREN
+	;
+
+traversalMethod_in
+	: 'in' LPAREN stringLiteralList RPAREN
+	;
+
+traversalMethod_inE
+	: 'inE' LPAREN stringLiteralList RPAREN
+	;
+
+traversalMethod_inV
+	: 'inV' LPAREN RPAREN
+	;
+
+traversalMethod_index
+	: 'index' LPAREN RPAREN
+	;
+
+traversalMethod_inject
+	: 'inject' LPAREN genericLiteralList RPAREN
+	;
+
+traversalMethod_is
+	: 'is' LPAREN genericLiteral RPAREN #traversalMethod_is_Object
+	| 'is' LPAREN traversalPredicate RPAREN #traversalMethod_is_P
+	;
+
+traversalMethod_key
+	: 'key' LPAREN RPAREN
+	;
+
+traversalMethod_label
+	: 'label' LPAREN RPAREN
+	;
+
+traversalMethod_limit
+	: 'limit' LPAREN traversalScope COMMA integerLiteral RPAREN #traversalMethod_limit_Scope_long
+	| 'limit' LPAREN integerLiteral RPAREN #traversalMethod_limit_long
+	;
+
+traversalMethod_local
+	: 'local' LPAREN nestedTraversal RPAREN
+	;
+
+traversalMethod_loops
+	: 'loops' LPAREN RPAREN #traversalMethod_loops_Empty
+	| 'loops' LPAREN stringLiteral RPAREN #traversalMethod_loops_String
+	;
+
+traversalMethod_map
+	: 'map' LPAREN nestedTraversal RPAREN
+	;
+
+traversalMethod_match
+	: 'match' LPAREN nestedTraversalList RPAREN
+	;
+
+traversalMethod_math
+	: 'math' LPAREN stringLiteral RPAREN
+	;
+
+traversalMethod_max
+	: 'max' LPAREN RPAREN #traversalMethod_max_Empty
+	| 'max' LPAREN traversalScope RPAREN #traversalMethod_max_Scope
+	;
+
+traversalMethod_mean
+	: 'mean' LPAREN RPAREN #traversalMethod_mean_Empty
+	| 'mean' LPAREN traversalScope RPAREN #traversalMethod_mean_Scope
+	;
+
+traversalMethod_min
+	: 'min' LPAREN RPAREN #traversalMethod_min_Empty
+	| 'min' LPAREN traversalScope RPAREN #traversalMethod_min_Scope
+	;
+
+traversalMethod_not
+	: 'not' LPAREN nestedTraversal RPAREN
+	;
+
+traversalMethod_option
+	: 'option' LPAREN traversalPredicate COMMA nestedTraversal RPAREN #traversalMethod_option_Predicate_Traversal
+	| 'option' LPAREN genericLiteral COMMA nestedTraversal RPAREN #traversalMethod_option_Object_Traversal
+	| 'option' LPAREN nestedTraversal RPAREN #traversalMethod_option_Traversal
+	;
+
+traversalMethod_optional
+	: 'optional' LPAREN nestedTraversal RPAREN
+	;
+
+traversalMethod_or
+	: 'or' LPAREN nestedTraversalList RPAREN
+	;
+
+traversalMethod_order
+	: 'order' LPAREN RPAREN #traversalMethod_order_Empty
+	| 'order' LPAREN traversalScope RPAREN #traversalMethod_order_Scope
+	;
+
+traversalMethod_otherV
+	: 'otherV' LPAREN RPAREN
+	;
+
+traversalMethod_out
+	: 'out' LPAREN stringLiteralList RPAREN
+	;
+
+traversalMethod_outE
+	: 'outE' LPAREN stringLiteralList RPAREN
+	;
+
+traversalMethod_outV
+	: 'outV' LPAREN RPAREN
+	;
+
+traversalMethod_pageRank
+	: 'pageRank' LPAREN RPAREN #traversalMethod_pageRank_Empty
+	| 'pageRank' LPAREN floatLiteral RPAREN #traversalMethod_pageRank_double
+	;
+
+traversalMethod_path
+	: 'path' LPAREN RPAREN
+	;
+
+traversalMethod_peerPressure
+	: 'peerPressure' LPAREN RPAREN
+	;
+
+traversalMethod_profile
+	: 'profile' LPAREN RPAREN #traversalMethod_profile_Empty
+	| 'profile' LPAREN stringLiteral RPAREN #traversalMethod_profile_String
+	;
+
+traversalMethod_project
+	: 'project' LPAREN stringLiteral (COMMA stringLiteralList)? RPAREN
+	;
+
+traversalMethod_properties
+	: 'properties' LPAREN stringLiteralList RPAREN
+	;
+
+traversalMethod_property
+	: 'property' LPAREN traversalCardinality COMMA genericLiteral COMMA genericLiteral (COMMA genericLiteralList)? RPAREN #traversalMethod_property_Cardinality_Object_Object_Object
+	| 'property' LPAREN genericLiteral COMMA genericLiteral (COMMA genericLiteralList)? RPAREN #traversalMethod_property_Object_Object_Object
+	;
+
+traversalMethod_propertyMap
+	: 'propertyMap' LPAREN stringLiteralList RPAREN
+	;
+
+traversalMethod_range
+	: 'range' LPAREN traversalScope COMMA integerLiteral COMMA integerLiteral RPAREN #traversalMethod_range_Scope_long_long
+	| 'range' LPAREN integerLiteral COMMA integerLiteral RPAREN #traversalMethod_range_long_long
+	;
+
+traversalMethod_read
+	: 'read' LPAREN RPAREN
+	;
+
+traversalMethod_repeat
+	: 'repeat' LPAREN stringLiteral COMMA nestedTraversal RPAREN #traversalMethod_repeat_String_Traversal
+	| 'repeat' LPAREN nestedTraversal RPAREN #traversalMethod_repeat_Traversal
+	;
+
+traversalMethod_sack
+	: 'sack' LPAREN traversalBiFunction RPAREN #traversalMethod_sack_BiFunction
+	| 'sack' LPAREN RPAREN #traversalMethod_sack_Empty
+	;
+
+traversalMethod_sample
+	: 'sample' LPAREN traversalScope COMMA integerLiteral RPAREN #traversalMethod_sample_Scope_int
+	| 'sample' LPAREN integerLiteral RPAREN #traversalMethod_sample_int
+	;
+
+traversalMethod_select
+	: 'select' LPAREN traversalColumn RPAREN #traversalMethod_select_Column
+	| 'select' LPAREN traversalPop COMMA stringLiteral RPAREN #traversalMethod_select_Pop_String
+	| 'select' LPAREN traversalPop COMMA stringLiteral COMMA stringLiteral (COMMA stringLiteralList)? RPAREN #traversalMethod_select_Pop_String_String_String
+	| 'select' LPAREN traversalPop COMMA nestedTraversal RPAREN #traversalMethod_select_Pop_Traversal
+	| 'select' LPAREN stringLiteral RPAREN #traversalMethod_select_String
+	| 'select' LPAREN stringLiteral COMMA stringLiteral (COMMA stringLiteralList)? RPAREN #traversalMethod_select_String_String_String
+	| 'select' LPAREN nestedTraversal RPAREN #traversalMethod_select_Traversal
+	;
+
+traversalMethod_shortestPath
+	: 'shortestPath' LPAREN RPAREN
+	;
+
+traversalMethod_sideEffect
+	: 'sideEffect' LPAREN nestedTraversal RPAREN
+	;
+
+traversalMethod_simplePath
+	: 'simplePath' LPAREN RPAREN
+	;
+
+traversalMethod_skip
+	: 'skip' LPAREN traversalScope COMMA integerLiteral RPAREN #traversalMethod_skip_Scope_long
+	| 'skip' LPAREN integerLiteral RPAREN #traversalMethod_skip_long
+	;
+
+traversalMethod_store
+	: 'store' LPAREN stringLiteral RPAREN
+	;
+
+traversalMethod_subgraph
+	: 'subgraph' LPAREN stringLiteral RPAREN
+	;
+
+traversalMethod_sum
+	: 'sum' LPAREN RPAREN #traversalMethod_sum_Empty
+	| 'sum' LPAREN traversalScope RPAREN #traversalMethod_sum_Scope
+	;
+
+traversalMethod_tail
+	: 'tail' LPAREN RPAREN #traversalMethod_tail_Empty
+	| 'tail' LPAREN traversalScope RPAREN #traversalMethod_tail_Scope
+	| 'tail' LPAREN traversalScope COMMA integerLiteral RPAREN #traversalMethod_tail_Scope_long
+	| 'tail' LPAREN integerLiteral RPAREN #traversalMethod_tail_long
+	;
+
+traversalMethod_timeLimit
+	: 'timeLimit' LPAREN integerLiteral RPAREN
+	;
+
+traversalMethod_times
+	: 'times' LPAREN integerLiteral RPAREN
+	;
+
+traversalMethod_to
+	: 'to' LPAREN traversalDirection (COMMA stringLiteralList)? RPAREN #traversalMethod_to_Direction_String
+	| 'to' LPAREN stringLiteral RPAREN #traversalMethod_to_String
+	| 'to' LPAREN nestedTraversal RPAREN #traversalMethod_to_Traversal
+	;
+
+traversalMethod_toE
+	: 'toE' LPAREN traversalDirection (COMMA stringLiteralList)? RPAREN
+	;
+
+traversalMethod_toV
+	: 'toV' LPAREN traversalDirection RPAREN
+	;
+
+traversalMethod_tree
+	: 'tree' LPAREN RPAREN #traversalMethod_tree_Empty
+	| 'tree' LPAREN stringLiteral RPAREN #traversalMethod_tree_String
+	;
+
+traversalMethod_unfold
+	: 'unfold' LPAREN RPAREN
+	;
+
+traversalMethod_union
+	: 'union' LPAREN nestedTraversalList RPAREN
+	;
+
+traversalMethod_until
+	: 'until' LPAREN traversalPredicate RPAREN #traversalMethod_until_Predicate
+	| 'until' LPAREN nestedTraversal RPAREN #traversalMethod_until_Traversal
+	;
+
+traversalMethod_value
+	: 'value' LPAREN RPAREN
+	;
+
+traversalMethod_valueMap
+	: 'valueMap' LPAREN stringLiteralList RPAREN #traversalMethod_valueMap_String
+	| 'valueMap' LPAREN booleanLiteral (COMMA stringLiteralList)? RPAREN #traversalMethod_valueMap_boolean_String
+	;
+
+traversalMethod_values
+	: 'values' LPAREN stringLiteralList RPAREN
+	;
+
+traversalMethod_where
+	: 'where' LPAREN traversalPredicate RPAREN #traversalMethod_where_P
+	| 'where' LPAREN stringLiteral COMMA traversalPredicate RPAREN #traversalMethod_where_String_P
+	| 'where' LPAREN nestedTraversal RPAREN #traversalMethod_where_Traversal
+	;
+
+traversalMethod_with
+	: 'with' LPAREN stringLiteral RPAREN #traversalMethod_with_String
+	| 'with' LPAREN stringLiteral COMMA genericLiteral RPAREN #traversalMethod_with_String_Object
+	;
+
+traversalMethod_write
+	: 'write' LPAREN RPAREN
+	;
+
+
+
+
+/*********************************************
+    ARGUMENT AND TERMINAL RULES
+**********************************************/
+
+traversalStrategy
+//  : 'ConnectiveStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'ElementIdStrategy' - not supported as the configuration takes a lambda
+//  | 'EventStrategy' - not supported as there is no way to send events back to the client
+//  | 'HaltedTraverserStrategy' - not supported as it is not typically relevant to OLTP
+//  | 'OptionsStrategy' - not supported as it's internal to with()
+    : 'new' 'PartitionStrategy' LPAREN traversalStrategyArgs_PartitionStrategy? (COMMA traversalStrategyArgs_PartitionStrategy)* RPAREN
+//  | 'RequirementStrategy' - not supported as it's internally relevant only
+//  | 'SackStrategy' - not supported directly as it's internal to withSack()
+//  | 'SideEffectStrategy' - not supported directly as it's internal to withSideEffect()
+    | 'new' 'SubgraphStrategy' LPAREN traversalStrategyArgs_SubgraphStrategy? (COMMA traversalStrategyArgs_SubgraphStrategy)* RPAREN
+//  | 'MatchAlgorithmStrategy' - not supported directly as it's internal to match()
+//  | 'ProfileStrategy' - not supported directly as it's internal to profile()
+//  | 'ReferenceElementStrategy' - not supported directly as users really can't/shouldn't change this in our context of a remote Gremlin provider
+//  | 'AdjacentToIncidentStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'CountStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'EarlyLimitStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'FilterRankingStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'IdentityRemovalStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'IncidentToAdjacentStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'InlineFilterStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'LazyBarrierStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'MatchPredicateStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'OrderLimitStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'PathProcessorStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'PathRetractionStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'RepeatUnrollStrategy' - not supported as it is a default strategy and we don't allow removal at this time
+//  | 'ComputerVerificationStrategy' - not supported since it's GraphComputer related
+    | 'new' 'EdgeLabelVerificationStrategy' LPAREN traversalStrategyArgs_EdgeLabelVerificationStrategy? (COMMA traversalStrategyArgs_EdgeLabelVerificationStrategy)* RPAREN
+//  | 'LambdaRestrictionStrategy' - not supported as we don't support lambdas in any situation
+    | 'ReadOnlyStrategy'
+    | 'new' 'ReservedKeysVerificationStrategy' LPAREN traversalStrategyArgs_ReservedKeysVerificationStrategy? (COMMA traversalStrategyArgs_ReservedKeysVerificationStrategy)* RPAREN
+//  | 'StandardVerificationStrategy' - not supported since this is an interal strategy
+    ;
+
+traversalStrategyArgs_PartitionStrategy
+    : 'includeMetaProperties' COLON booleanLiteral
+    | 'writePartition' COLON stringLiteral
+    | 'partitionKey' COLON stringLiteral
+    | 'readPartitions' COLON stringLiteralList
+    ;
+
+traversalStrategyArgs_SubgraphStrategy
+    : 'vertices' COLON nestedTraversal
+    | 'edges' COLON nestedTraversal
+    | 'vertexProperties' COLON nestedTraversal
+    | 'checkAdjacentVertices' COLON booleanLiteral
+    ;
+
+traversalStrategyArgs_EdgeLabelVerificationStrategy
+    : 'throwException' COLON booleanLiteral
+    | 'logWarning' COLON booleanLiteral
+    ;
+
+traversalStrategyArgs_ReservedKeysVerificationStrategy
+    : 'keys' COLON stringLiteralList
+    | 'throwException' COLON booleanLiteral
+    | 'logWarning' COLON booleanLiteral
+    ;
+
+traversalScope
+    : 'local' | 'Scope.local'
+    | 'global' | 'Scope.global'
+    ;
+
+traversalToken
+    : 'id' | 'T.id'
+    | 'label' | 'T.label'
+    | 'key' | 'T.key'
+    | 'value' | 'T.value'
+    ;
+
+traversalOrder
+    : 'incr' | 'Order.incr'
+    | 'decr' | 'Order.decr'
+    | 'asc'  | 'Order.asc'
+    | 'desc' | 'Order.desc'
+    | 'shuffle' | 'Order.shuffle'
+    ;
+
+traversalDirection
+    : 'IN' | 'Direction.IN'
+    | 'OUT' | 'Direction.OUT'
+    | 'BOTH' | 'Direction.BOTH'
+    ;
+
+traversalCardinality
+    : 'single' | 'Cardinality.single'
+    | 'set' | 'Cardinality.set'
+    | 'list' | 'Cardinality.list'
+    ;
+
+traversalColumn
+    : 'keys' | 'Column.keys'
+    | 'values' | 'Column.values'
+    ;
+
+traversalPop
+    : 'first' | 'Pop.first'
+    | 'last' | 'Pop.last'
+    | 'all' | 'Pop.all'
+    | 'mixed' | 'Pop.mixed'
+    ;
+
+traversalOperator
+    : 'addAll' | 'Operator.addAll'
+    | 'and' | 'Operator.and'
+    | 'assign' | 'Operator.assign'
+    | 'div' | 'Operator.div'
+    | 'max' | 'Operator.max'
+    | 'min' | 'Operator.min'
+    | 'minus' | 'Operator.minus'
+    | 'mult' | 'Operator.mult'
+    | 'or' | 'Operator.or'
+    | 'sum' | 'Operator.sum'
+    | 'sumLong' | 'Operator.sumLong'
+    ;
+
+traversalOptionParent
+    : 'any' | 'Pick.any'
+    | 'none' | 'Pick.none'
+    ;
+
+traversalPredicate
+    : traversalPredicate_eq
+    | traversalPredicate_neq
+    | traversalPredicate_lt
+    | traversalPredicate_lte
+    | traversalPredicate_gt
+    | traversalPredicate_gte
+    | traversalPredicate_inside
+    | traversalPredicate_outside
+    | traversalPredicate_between
+    | traversalPredicate_within
+    | traversalPredicate_without
+    | traversalPredicate_not
+    | traversalPredicate_startingWith
+    | traversalPredicate_notStartingWith
+    | traversalPredicate_endingWith
+    | traversalPredicate_notEndingWith
+    | traversalPredicate_containing
+    | traversalPredicate_notContaining
+    | traversalPredicate DOT 'and' LPAREN traversalPredicate RPAREN
+    | traversalPredicate DOT 'or' LPAREN traversalPredicate RPAREN
+    | traversalPredicate DOT 'negate' LPAREN RPAREN
+    ;
+
+traversalTerminalMethod
+    : traversalTerminalMethod_explain
+    | traversalTerminalMethod_iterate
+    | traversalTerminalMethod_hasNext
+    | traversalTerminalMethod_tryNext
+    | traversalTerminalMethod_next
+    | traversalTerminalMethod_toList
+    | traversalTerminalMethod_toSet
+    | traversalTerminalMethod_toBulkSet
+    ;
+
+traversalSackMethod
+    : 'normSack' | 'Barrier.normSack'
+    ;
+
+traversalSelfMethod
+    : traversalSelfMethod_none
+    ;
+
+// Additional special rules that are derived from above
+// These are used to restrict broad method signatures that accept lambdas
+// to a smaller set.
+traversalComparator
+    : traversalOrder
+    ;
+
+traversalFunction
+    : traversalToken
+    | traversalColumn
+    ;
+
+traversalBiFunction
+    : traversalOperator
+    ;
+
+traversalPredicate_eq
+    : ('P.eq' | 'eq') LPAREN genericLiteral RPAREN
+    ;
+
+traversalPredicate_neq
+    : ('P.neq' | 'neq') LPAREN genericLiteral RPAREN
+    ;
+
+traversalPredicate_lt
+    : ('P.lt' | 'lt') LPAREN genericLiteral RPAREN
+    ;
+
+traversalPredicate_lte
+    : ('P.lte' | 'lte') LPAREN genericLiteral RPAREN
+    ;
+
+traversalPredicate_gt
+    : ('P.gt' | 'gt') LPAREN genericLiteral RPAREN
+    ;
+
+traversalPredicate_gte
+    : ('P.gte' | 'gte') LPAREN genericLiteral RPAREN
+    ;
+
+traversalPredicate_inside
+    : ('P.inside' | 'inside') LPAREN genericLiteral COMMA genericLiteral RPAREN
+    ;
+
+traversalPredicate_outside
+    : ('P.outside' | 'outside') LPAREN genericLiteral COMMA genericLiteral RPAREN
+    ;
+
+traversalPredicate_between
+    : ('P.between' | 'between') LPAREN genericLiteral COMMA genericLiteral RPAREN
+    ;
+
+traversalPredicate_within
+    : ('P.within' | 'within') LPAREN genericLiteralList RPAREN
+    ;
+
+traversalPredicate_without
+    : ('P.without' | 'without') LPAREN genericLiteralList RPAREN
+    ;
+
+traversalPredicate_not
+    : ('P.not' | 'not') LPAREN traversalPredicate RPAREN
+    ;
+
+traversalPredicate_containing
+    : ('TextP.containing' | 'containing') LPAREN stringLiteral RPAREN
+    ;
+
+traversalPredicate_notContaining
+    : ('TextP.notContaining' | 'notContaining') LPAREN stringLiteral RPAREN
+    ;
+
+traversalPredicate_startingWith
+    : ('TextP.startingWith' | 'startingWith') LPAREN stringLiteral RPAREN
+    ;
+
+traversalPredicate_notStartingWith
+    : ('TextP.notStartingWith' | 'notStartingWith') LPAREN stringLiteral RPAREN
+    ;
+
+traversalPredicate_endingWith
+    : ('TextP.endingWith' | 'endingWith') LPAREN stringLiteral RPAREN
+    ;
+
+traversalPredicate_notEndingWith
+    : ('TextP.notEndingWith' | 'notEndingWith') LPAREN stringLiteral RPAREN
+    ;
+
+traversalTerminalMethod_explain
+    : 'explain' LPAREN RPAREN
+    ;
+
+traversalTerminalMethod_hasNext
+    : 'hasNext' LPAREN RPAREN
+    ;
+
+traversalTerminalMethod_iterate
+    : 'iterate' LPAREN RPAREN
+    ;
+
+traversalTerminalMethod_tryNext
+    : 'tryNext' LPAREN RPAREN
+    ;
+
+traversalTerminalMethod_next
+    : 'next' LPAREN RPAREN
+    | 'next' LPAREN integerLiteral RPAREN
+    ;
+
+traversalTerminalMethod_toList
+    : 'toList' LPAREN RPAREN
+    ;
+
+traversalTerminalMethod_toSet
+    : 'toSet' LPAREN RPAREN
+    ;
+
+traversalTerminalMethod_toBulkSet
+    : 'toBulkSet' LPAREN RPAREN
+    ;
+
+traversalSelfMethod_none
+    : 'none' LPAREN RPAREN
+    ;
+
+// Gremlin specific lexer rules
+
+gremlinStringConstants
+    : withOptionsStringConstants
+    | shortestPathStringConstants
+    | pageRankStringConstants
+    | peerPressureStringConstants
+    ;
+
+pageRankStringConstants
+    : gremlinStringConstants_pageRankStringConstants_edges
+    | gremlinStringConstants_pageRankStringConstants_times
+    | gremlinStringConstants_pageRankStringConstants_propertyName
+    ;
+
+peerPressureStringConstants
+    : gremlinStringConstants_peerPressureStringConstants_edges
+    | gremlinStringConstants_peerPressureStringConstants_times
+    | gremlinStringConstants_peerPressureStringConstants_propertyName
+    ;
+
+shortestPathStringConstants
+    : gremlinStringConstants_shortestPathStringConstants_target
+    | gremlinStringConstants_shortestPathStringConstants_edges
+    | gremlinStringConstants_shortestPathStringConstants_distance
+    | gremlinStringConstants_shortestPathStringConstants_maxDistance
+    | gremlinStringConstants_shortestPathStringConstants_includeEdges
+    ;
+
+withOptionsStringConstants
+    : gremlinStringConstants_withOptionsStringConstants_tokens
+    | gremlinStringConstants_withOptionsStringConstants_none
+    | gremlinStringConstants_withOptionsStringConstants_ids
+    | gremlinStringConstants_withOptionsStringConstants_labels
+    | gremlinStringConstants_withOptionsStringConstants_keys
+    | gremlinStringConstants_withOptionsStringConstants_values
+    | gremlinStringConstants_withOptionsStringConstants_all
+    | gremlinStringConstants_withOptionsStringConstants_indexer
+    | gremlinStringConstants_withOptionsStringConstants_list
+    | gremlinStringConstants_withOptionsStringConstants_map
+    ;
+
+gremlinStringConstants_pageRankStringConstants_edges
+    : pageRankStringConstant DOT 'edges'
+    ;
+
+gremlinStringConstants_pageRankStringConstants_times
+    : pageRankStringConstant DOT 'times'
+    ;
+
+gremlinStringConstants_pageRankStringConstants_propertyName
+    : pageRankStringConstant DOT 'propertyName'
+    ;
+
+gremlinStringConstants_peerPressureStringConstants_edges
+    : peerPressureStringConstant DOT 'edges'
+    ;
+
+gremlinStringConstants_peerPressureStringConstants_times
+    : peerPressureStringConstant DOT 'times'
+    ;
+
+gremlinStringConstants_peerPressureStringConstants_propertyName
+    : peerPressureStringConstant DOT 'propertyName'
+    ;
+
+gremlinStringConstants_shortestPathStringConstants_target
+    : shortestPathStringConstant DOT 'target'
+    ;
+
+gremlinStringConstants_shortestPathStringConstants_edges
+    : shortestPathStringConstant DOT 'edges'
+    ;
+
+gremlinStringConstants_shortestPathStringConstants_distance
+    : shortestPathStringConstant DOT 'distance'
+    ;
+
+gremlinStringConstants_shortestPathStringConstants_maxDistance
+    : shortestPathStringConstant DOT 'maxDistance'
+    ;
+
+gremlinStringConstants_shortestPathStringConstants_includeEdges
+    : shortestPathStringConstant DOT 'includeEdges'
+    ;
+
+gremlinStringConstants_withOptionsStringConstants_tokens
+    : withOptionsStringConstant DOT 'tokens'
+    ;
+
+gremlinStringConstants_withOptionsStringConstants_none
+    : withOptionsStringConstant DOT 'none'
+    ;
+
+gremlinStringConstants_withOptionsStringConstants_ids
+    : withOptionsStringConstant DOT 'ids'
+    ;
+
+gremlinStringConstants_withOptionsStringConstants_labels
+    : withOptionsStringConstant DOT 'labels'
+    ;
+
+gremlinStringConstants_withOptionsStringConstants_keys
+    : withOptionsStringConstant DOT 'keys'
+    ;
+
+gremlinStringConstants_withOptionsStringConstants_values
+    : withOptionsStringConstant DOT 'values'
+    ;
+
+gremlinStringConstants_withOptionsStringConstants_all
+    : withOptionsStringConstant DOT 'all'
+    ;
+
+gremlinStringConstants_withOptionsStringConstants_indexer
+    : withOptionsStringConstant DOT 'indexer'
+    ;
+
+gremlinStringConstants_withOptionsStringConstants_list
+    : withOptionsStringConstant DOT 'list'
+    ;
+
+gremlinStringConstants_withOptionsStringConstants_map
+    : withOptionsStringConstant DOT 'map'
+    ;
+
+pageRankStringConstant
+    : 'PageRank'
+    ;
+
+peerPressureStringConstant
+    : 'PeerPressure'
+    ;
+
+shortestPathStringConstant
+    : 'ShortestPath'
+    ;
+
+withOptionsStringConstant
+    : 'WithOptions'
+    ;
+
+traversalStrategyList
+    : traversalStrategyExpr?
+    ;
+
+traversalStrategyExpr
+    : traversalStrategy (COMMA traversalStrategy)*
+    ;
+
+nestedTraversalList
+    : nestedTraversalExpr?
+    ;
+
+nestedTraversalExpr
+    : nestedTraversal (COMMA nestedTraversal)*
+    ;
+
+genericLiteralList
+    : genericLiteralExpr?
+    ;
+
+genericLiteralExpr
+    : genericLiteral (COMMA genericLiteral)*
+    ;
+
+genericLiteralRange
+    : integerLiteral DOT DOT integerLiteral
+    | stringLiteral DOT DOT stringLiteral
+    ;
+
+genericLiteralCollection
+    : LBRACK (genericLiteral (COMMA genericLiteral)*)? RBRACK
+    ;
+
+stringLiteralList
+    : stringLiteralExpr?
+    | LBRACK stringLiteralExpr? RBRACK
+    ;
+
+stringLiteralExpr
+    : stringLiteral (COMMA stringLiteral)*
+    ;
+
+genericLiteral
+	: integerLiteral
+	| floatLiteral
+	| booleanLiteral
+	| stringLiteral
+	| dateLiteral
+	| nullLiteral
+	// Allow the generic literal to match specific gremlin tokens also
+	| traversalToken
+	| traversalCardinality
+	| traversalDirection
+	| traversalOptionParent
+	| genericLiteralCollection
+	| genericLiteralRange
+	| nestedTraversal
+	| terminatedTraversal
+	| genericLiteralMap
+	;
+
+genericLiteralMap
+  : LBRACK (genericLiteral)? COLON (genericLiteral)? (COMMA (genericLiteral)? COLON (genericLiteral)?)* RBRACK
+  ;
+
+integerLiteral
+    : IntegerLiteral
+    ;
+
+floatLiteral
+    : FloatingPointLiteral
+    ;
+
+booleanLiteral
+    : BooleanLiteral
+    ;
+
+stringLiteral
+    : NonEmptyStringLiteral
+    | EmptyStringLiteral
+    | gremlinStringConstants
+    ;
+
+dateLiteral
+    : 'datetime' LPAREN stringLiteral RPAREN
+    ;
+
+nullLiteral
+    : NullLiteral
+    ;
+
+/*********************************************
+    LEXER RULES
+**********************************************/
+
+// Lexer rules
+// These rules are extracted from Java ANTLRv4 Grammar.
+// Source: https://github.com/antlr/grammars-v4/blob/master/java8/Java8.g4
+
+// ยง3.9 Keywords
+
+NEW : 'new';
+
+// Integer Literals
+
+IntegerLiteral
+	:	Sign? DecimalIntegerLiteral
+	|	Sign? HexIntegerLiteral
+	|	Sign? OctalIntegerLiteral
+	;
+
+fragment
+DecimalIntegerLiteral
+	:	DecimalNumeral IntegerTypeSuffix?
+	;
+
+fragment
+HexIntegerLiteral
+	:	HexNumeral IntegerTypeSuffix?
+	;
+
+fragment
+OctalIntegerLiteral
+	:	OctalNumeral IntegerTypeSuffix?
+	;
+
+fragment
+IntegerTypeSuffix
+	:	[lL]
+	;
+
+fragment
+DecimalNumeral
+	:	'0'
+	|	NonZeroDigit (Digits? | Underscores Digits)
+	;
+
+fragment
+Digits
+	:	Digit (DigitsAndUnderscores? Digit)?
+	;
+
+fragment
+Digit
+	:	'0'
+	|	NonZeroDigit
+	;
+
+fragment
+NonZeroDigit
+	:	[1-9]
+	;
+
+fragment
+DigitsAndUnderscores
+	:	DigitOrUnderscore+
+	;
+
+fragment
+DigitOrUnderscore
+	:	Digit
+	|	'_'
+	;
+
+fragment
+Underscores
+	:	'_'+
+	;
+
+fragment
+HexNumeral
+	:	'0' [xX] HexDigits
+	;
+
+fragment
+HexDigits
+	:	HexDigit (HexDigitsAndUnderscores? HexDigit)?
+	;
+
+fragment
+HexDigit
+	:	[0-9a-fA-F]
+	;
+
+fragment
+HexDigitsAndUnderscores
+	:	HexDigitOrUnderscore+
+	;
+
+fragment
+HexDigitOrUnderscore
+	:	HexDigit
+	|	'_'
+	;
+
+fragment
+OctalNumeral
+	:	'0' Underscores? OctalDigits
+	;
+
+fragment
+OctalDigits
+	:	OctalDigit (OctalDigitsAndUnderscores? OctalDigit)?
+	;
+
+fragment
+OctalDigit
+	:	[0-7]
+	;
+
+fragment
+OctalDigitsAndUnderscores
+	:	OctalDigitOrUnderscore+
+	;
+
+fragment
+OctalDigitOrUnderscore
+	:	OctalDigit
+	|	'_'
+	;
+
+// Floating-Point Literals
+
+FloatingPointLiteral
+	:	Sign? DecimalFloatingPointLiteral
+	;
+
+fragment
+DecimalFloatingPointLiteral
+    :   Digits ('.' Digits ExponentPart? | ExponentPart) FloatTypeSuffix?
+	|	Digits FloatTypeSuffix
+	;
+
+fragment
+ExponentPart
+	:	ExponentIndicator SignedInteger
+	;
+
+fragment
+ExponentIndicator
+	:	[eE]
+	;
+
+fragment
+SignedInteger
+	:	Sign? Digits
+	;
+
+fragment
+Sign
+	:	[+-]
+	;
+
+fragment
+FloatTypeSuffix
+	:	[fFdD]
+	;
+
+// Boolean Literals
+
+BooleanLiteral
+	:	'true'
+	|	'false'
+	;
+
+// Null Literal
+
+NullLiteral
+	:	'null'
+	;
+
+// String Literals
+
+// String literal is customized since Java only allows double quoted strings where Groovy supports single quoted
+// literals also. A side effect of this is ANTLR will not be able to parse single character string literals with
+// single quoted so we instead remove char literal altogether and only have string literal in lexer tokens.
+NonEmptyStringLiteral
+	:   '"' DoubleQuotedStringCharacters '"'
+	|   '\'' SingleQuotedStringCharacters '\''
+	;
+
+// We define NonEmptyStringLiteral and EmptyStringLiteral separately so that we can unambiguously handle empty queries
+EmptyStringLiteral
+	:   '""'
+	|   '\'\''
+	;
+
+fragment
+DoubleQuotedStringCharacters
+	:	DoubleQuotedStringCharacter+
+	;
+
+fragment
+DoubleQuotedStringCharacter
+	:	~('"' | '\\')
+	|   JoinLineEscape
+	|	EscapeSequence
+	;
+
+fragment
+SingleQuotedStringCharacters
+	:	SingleQuotedStringCharacter+
+	;
+
+fragment
+SingleQuotedStringCharacter
+	:	~('\'' | '\\')
+	|   JoinLineEscape
+	|	EscapeSequence
+	;
+
+// Escape Sequences for Character and String Literals
+fragment JoinLineEscape
+    : '\\' '\r'? '\n'
+    ;
+
+fragment
+EscapeSequence
+	:	'\\' [btnfr"'\\]
+	|	OctalEscape
+    |   UnicodeEscape // This is not in the spec but prevents having to preprocess the input
+	;
+
+fragment
+OctalEscape
+	:	'\\' OctalDigit
+	|	'\\' OctalDigit OctalDigit
+	|	'\\' ZeroToThree OctalDigit OctalDigit
+	;
+
+fragment
+ZeroToThree
+	:	[0-3]
+	;
+
+// This is not in the spec but prevents having to preprocess the input
+fragment
+UnicodeEscape
+    :   '\\' 'u'+  HexDigit HexDigit HexDigit HexDigit
+    ;
+
+// Separators
+
+LPAREN : '(';
+RPAREN : ')';
+LBRACE : '{';
+RBRACE : '}';
+LBRACK : '[';
+RBRACK : ']';
+SEMI : ';';
+COMMA : ',';
+DOT : '.';
+COLON : ':';
+
+TRAVERSAL_ROOT:     'g';
+ANON_TRAVERSAL_ROOT:     '__';
+
+// Trim whitespace and comments if present
+
+WS  :  [ \t\r\n\u000C]+ -> skip
+    ;
+
+LINE_COMMENT
+    :   '//' ~[\r\n]* -> skip
+    ;
diff --git a/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/AbstractGrammarTest.java b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/AbstractGrammarTest.java
new file mode 100644
index 0000000..5679e27
--- /dev/null
+++ b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/AbstractGrammarTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.grammar;
+
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.atn.PredictionMode;
+import org.antlr.v4.runtime.misc.ParseCancellationException;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+
+import static org.junit.Assert.fail;
+
+public class AbstractGrammarTest {
+
+    protected void parse(final String query) {
+        parse(query, false);
+    }
+
+    protected void parse(final String query, final boolean expectFailures) {
+        try {
+            final CharStream in = CharStreams.fromString(query);
+            final GremlinLexer lexer = new GremlinLexer(in);
+            lexer.removeErrorListeners();
+            lexer.addErrorListener(new GremlinErrorListener());
+
+            final CommonTokenStream tokens = new CommonTokenStream(lexer);
+
+            // Setup error handler on parser
+            final GremlinParser parser = new GremlinParser(tokens);
+            parser.setErrorHandler(new BailErrorStrategy());
+            parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
+
+            // Visit top level query
+            try {
+                final GremlinVisitor<GremlinParser.QueryListContext> visitor = new GremlinBaseVisitor<>();
+                visitor.visit(parser.queryList());
+            } catch (Exception ex) {
+                // Retry parsing the query again with using LL prediction mode.  LL parsing mode is more powerful
+                // so retrying the parsing would help parsing the rare edge cases.
+                tokens.seek(0); // rewind input stream
+                lexer.reset();
+                parser.reset();
+                parser.getInterpreter().setPredictionMode(PredictionMode.LL);
+
+                final GremlinVisitor<GremlinParser.QueryListContext> visitor = new GremlinBaseVisitor<>();
+                visitor.visit(parser.queryList());
+            }
+
+            // If we are expecting failures, put assert check here
+            if (expectFailures) {
+                fail("Query parsing should have failed for " + query);
+            }
+
+        } catch(Exception e) {
+            if (!expectFailures) {
+                final Throwable t = ExceptionUtils.getRootCause(e);
+                if (t instanceof LexerNoViableAltException) {
+                    fail(String.format("Error parsing query: %s - [%s] - failed at index: %s",
+                            query, t.toString(), ((LexerNoViableAltException) t).getStartIndex()));
+                } else if (t instanceof NoViableAltException) {
+                    fail(String.format("Error parsing query: %s - [%s]", query, ((NoViableAltException) t).getStartToken().toString()));
+                } else {
+                    fail(String.format("Error parsing query: %s - [%s]", query, t.getMessage()));
+                }
+            }
+        }
+    }
+
+}
+
diff --git a/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/BasicGrammarTest.java b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/BasicGrammarTest.java
new file mode 100644
index 0000000..8ac8263
--- /dev/null
+++ b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/BasicGrammarTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.grammar;
+
+import org.junit.Test;
+
+/**
+ * Basic smoke testing of the parser.
+ */
+public class BasicGrammarTest extends AbstractGrammarTest {
+    @Test
+    public void shouldParseV() {
+        parse("g.V()");
+    }
+}
diff --git a/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/DocumentationReader.java b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/DocumentationReader.java
new file mode 100644
index 0000000..af9aa15
--- /dev/null
+++ b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/DocumentationReader.java
@@ -0,0 +1,122 @@
+/*
+ * 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.grammar;
+
+import org.apache.commons.text.StringEscapeUtils;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * Reads asciidoc documentation for Gremlin snippets to help build the testing corpus.
+ */
+public class DocumentationReader {
+    private static final Pattern sourceCodePattern = Pattern.compile("\\[gremlin-groovy.*\\].*");
+    private static final Pattern startGremlinPattern = Pattern.compile("^g\\..*");
+
+    /**
+     * Some Gremlin in documentation is just not going to parse.
+     */
+    private static final Set<String> throwAway = new HashSet<>(Arrays.asList("g.inject(g.withComputer().V().shortestPath().with(ShortestPath.distance, 'weight').with(ShortestPath.includeEdges, true).with(ShortestPath.maxDistance, 1).toList().toArray()).map(unfold().values('name','weight').fold())"));
+
+    public static Set<String> parse(final String projectRoot) throws IOException {
+        final Set<String> gremlins = new LinkedHashSet<>();
+        Files.find(Paths.get(projectRoot, "docs", "src"),
+                Integer.MAX_VALUE,
+                (filePath, fileAttr) -> fileAttr.isRegularFile() && (filePath.toString().endsWith("traversal.asciidoc") || filePath.toString().contains("recipes"))).
+                sorted().
+                forEach(f -> {
+                    String currentGremlin = "";
+                    int openSnippet = 0;
+
+                    try {
+                        final List<String> lines = Files.readAllLines(f, StandardCharsets.UTF_8);
+                        for (String line : lines) {
+                            // trim and remove callouts
+                            String cleanLine = line.replaceAll("<\\d>", "").trim();
+
+                            // remove line comments
+                            int pos = cleanLine.indexOf("//");
+                            if (pos > 0) cleanLine = cleanLine.substring(0, pos).trim();
+
+                            if (sourceCodePattern.matcher(cleanLine).matches() || (openSnippet > 0 && cleanLine.equals("----"))) {
+                                if (openSnippet > 1) {
+                                    openSnippet = 0;
+                                } else {
+                                    openSnippet++;
+                                }
+                            } else if (openSnippet > 0 && (startGremlinPattern.matcher(cleanLine).matches() || isTerminated(currentGremlin))) {
+                                // new line of Gremlin that starts with g.
+                                currentGremlin += cleanLine;
+
+                                // line doesn't continue if there is no period or open paren
+                                if (!isTerminated(currentGremlin)) {
+                                    final String gremlinToAdd = replaceVariables(currentGremlin.trim());
+                                    if (!throwAway.contains(gremlinToAdd))
+                                        gremlins.add(gremlinToAdd);
+                                    currentGremlin = "";
+                                }
+                            }
+                        }
+                    } catch (IOException ioe) {
+                        throw new RuntimeException(ioe);
+                    }
+                });
+
+        return gremlins;
+    }
+
+    private static boolean isTerminated(final String gremlin) {
+        return gremlin.endsWith(".") || gremlin.endsWith("(") || gremlin.endsWith(",");
+    }
+
+    /**
+     * Variables can't be parsed by the grammar so they must be replaced with something concrete.
+     */
+    private static String replaceVariables(final String gremlin) {
+        // convert lambda vars to closures and they will be ignored by the test suite
+        return gremlin.replace("relations", "\"relations\"").
+                replace("places.size()", "6").
+                replace("places", "\"places\"").
+                replace("ids", "\"ids\"").
+                replace("vRexsterJob1", "\"rj1\"").
+                replace("vBlueprintsJob1", "\"bj1\"").
+                replace("weightFilter.clone()", "{it}").
+                replace("weightFilter", "{it}").
+                replace("vBob", "\"bob\"").
+                replace("vMarko", "\"marko\"").
+                replace("vPeter", "\"peter\"").
+                replace("vStephen", "\"stephen\"").
+                replace("input.head()", "\"head\"").
+                replace("input.tail().size()", "6").
+                replace("input.tail()", "\"tail\"").
+                replace("System.out.&println", "{it}").
+                replace("persons", "\"persons\"").
+                replace("marko.value('age')", "11").
+                replace("seedStrategy", "ReadOnlyStrategy").
+                replace(".getClass()", "").
+                replace("result.toArray()", "4").
+                replace("vA.value('amount')", "0.0").
+                replace("vA", "\"vA\"");
+    }
+}
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/FeatureReader.java b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/FeatureReader.java
similarity index 61%
copy from gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/FeatureReader.java
copy to gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/FeatureReader.java
index 945b836..4b7f085 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/FeatureReader.java
+++ b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/FeatureReader.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.tinkerpop.gremlin.features;
+package org.apache.tinkerpop.gremlin.grammar;
 
 import org.apache.commons.text.StringEscapeUtils;
 
@@ -24,19 +24,17 @@ import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Map;
+import java.util.Set;
 
 /**
- * Reads the feature files and extracts Gremlin to a {@code Map} structure that can be used to then generate
+ * Reads the feature files and extracts Gremlin to help build the parsing test corpus.
  */
 public class FeatureReader {
 
-    public static Map<String, List<String>> parse(final String projectRoot) throws IOException {
-        final Map<String, List<String>> gremlins = new LinkedHashMap<>();
+    public static Set<String> parse(final String projectRoot) throws IOException {
+        final Set<String> gremlins = new LinkedHashSet<>();
         Files.find(Paths.get(projectRoot, "gremlin-test", "features"),
                    Integer.MAX_VALUE,
                 (filePath, fileAttr) -> fileAttr.isRegularFile() && filePath.toString().endsWith(".feature")).
@@ -45,23 +43,21 @@ public class FeatureReader {
                     String currentGremlin = "";
                     boolean openTriples = false;
                     boolean skipIgnored = false;
-                    String scenarioName = "";
 
                     try {
                         final List<String> lines = Files.readAllLines(f, StandardCharsets.UTF_8);
                         for (String line : lines) {
                             String cleanLine = line.trim();
-                            if (cleanLine.startsWith("Scenario:")) {
-                                scenarioName = cleanLine.split(":")[1].trim();
-                                skipIgnored = false;
-                            } else if (cleanLine.startsWith("Then nothing should happen because")) {
+                            if (cleanLine.startsWith("Then nothing should happen because")) {
                                 skipIgnored = true;
                             } else if (cleanLine.startsWith("And the graph should return")) {
-                                gremlins.computeIfAbsent(scenarioName, k -> new ArrayList<>()).add(StringEscapeUtils.unescapeJava(cleanLine.substring(cleanLine.indexOf("\"") + 1, cleanLine.lastIndexOf("\""))));
+                                gremlins.add(replaceVariables(
+                                        StringEscapeUtils.unescapeJava(
+                                                cleanLine.substring(cleanLine.indexOf("\"") + 1, cleanLine.lastIndexOf("\"")))));
                             } else if (cleanLine.startsWith("\"\"\"")) {
                                 openTriples = !openTriples;
                                 if (!skipIgnored && !openTriples) {
-                                    gremlins.computeIfAbsent(scenarioName, k -> new ArrayList<>()).add(currentGremlin);
+                                    gremlins.add(replaceVariables(currentGremlin));
                                     currentGremlin = "";
                                 }
                             } else if (openTriples && !skipIgnored) {
@@ -75,4 +71,25 @@ public class FeatureReader {
 
         return gremlins;
     }
+
+    /**
+     * Variables can't be parsed by the grammar so they must be replaced with something concrete.
+     */
+    private static String replaceVariables(final String gremlin) {
+        return gremlin.replace("xx1", "\"1\"").
+                replace("xx2", "\"2\"").
+                replace("xx3", "\"3\"").
+                replace("vid1", "1").
+                replace("vid2", "2").
+                replace("vid3", "3").
+                replace("vid4", "4").
+                replace("vid5", "5").
+                replace("vid6", "6").
+                replace("eid7", "7").
+                replace("eid8", "8").
+                replace("eid9", "9").
+                replace("eid10", "10").
+                replace("eid11", "11").
+                replace("eid12", "12");
+    }
 }
diff --git a/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/GremlinErrorListener.java b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/GremlinErrorListener.java
new file mode 100644
index 0000000..57be2cb
--- /dev/null
+++ b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/GremlinErrorListener.java
@@ -0,0 +1,33 @@
+/*
+ * 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.grammar;
+
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.antlr.v4.runtime.misc.ParseCancellationException;
+
+public class GremlinErrorListener extends BaseErrorListener {
+
+    @Override
+    public void syntaxError(final Recognizer<?, ?> recognizer, final Object offendingSymbol, final int line,
+                            final int charPositionInLine, final String msg, final RecognitionException e) {
+        throw new ParseCancellationException(e);
+    }
+}
diff --git a/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/NegativeGrammarTest.java b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/NegativeGrammarTest.java
new file mode 100644
index 0000000..ddf56ef
--- /dev/null
+++ b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/NegativeGrammarTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.grammar;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class NegativeGrammarTest extends AbstractGrammarTest {
+
+    @Parameterized.Parameters
+    public static Iterable<String> queries() throws IOException {
+        final InputStream stream = ReferenceGrammarTest.class.getClassLoader()
+                .getResourceAsStream("incorrect-traversals.txt");
+
+        final List<String> queries = new ArrayList<>();
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                queries.add(line);
+            }
+        }
+
+        return queries;
+    }
+
+    @Parameterized.Parameter
+    public String query;
+
+    @Test
+    public void test_parse() {
+        parse(query, true);
+    }
+
+
+}
diff --git a/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/ReferenceGrammarTest.java b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/ReferenceGrammarTest.java
new file mode 100644
index 0000000..e70fa44
--- /dev/null
+++ b/gremlin-grammar/src/test/java/org/apache/tinkerpop/gremlin/grammar/ReferenceGrammarTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.grammar;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assume.assumeThat;
+
+/**
+ * Builds a corpus of Gremlin statements from Recipes, "The Traversal" in Reference Documentation and the Gherkin
+ * test suite and passes them all through the grammar parser.
+ */
+@RunWith(Parameterized.class)
+public class ReferenceGrammarTest extends AbstractGrammarTest {
+
+    private static final Pattern vertexPattern = Pattern.compile(".*v\\d.*");
+    private static final Pattern edgePattern = Pattern.compile(".*e\\d.*");
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Iterable<String> queries() throws IOException {
+        final Set<String> gremlins = new LinkedHashSet<>(DocumentationReader.parse("../"));
+        gremlins.addAll(FeatureReader.parse("../"));
+        return gremlins;
+    }
+
+    @Parameterized.Parameter
+    public String query;
+
+    @Test
+    public void test_parse() {
+        assumeThat("Lambdas are not supported", query.contains("l1"), is(false));
+        assumeThat("Lambdas are not supported", query.contains("l2"), is(false));
+        assumeThat("Lambdas are not supported", query.contains("pred1"), is(false));
+        assumeThat("Lambdas are not supported", query.contains("c1"), is(false));
+        assumeThat("Lambdas are not supported", query.contains("c2"), is(false));
+        assumeThat("Lambdas are not supported", query.contains("Lambda.function("), is(false));
+        // start of a closure
+        assumeThat("Lambdas are not supported", query.contains("{"), is(false));
+        assumeThat("withComputer() step is not supported", query.startsWith("g.withComputer("), is(false));
+        assumeThat("io() step is not supported", query.startsWith("g.io("), is(false));
+        assumeThat("Vertex instances are not supported", vertexPattern.matcher(query).matches(), is(false));
+        assumeThat("Edge instances are not supported", edgePattern.matcher(query).matches(), is(false));
+        assumeThat("fill() terminator is not supported", query.contains("fill("), is(false));
+        assumeThat("connectedComponent() is not fully supported - needs tokens", query.contains("connectedComponent("), is(false));
+        assumeThat("withoutStrategies() is not supported", query.contains("withoutStrategies("), is(false));
+        assumeThat("program() is not supported", query.contains("program("), is(false));
+
+        parse(query);
+    }
+}
diff --git a/gremlin-grammar/src/test/resources/incorrect-traversals.txt b/gremlin-grammar/src/test/resources/incorrect-traversals.txt
new file mode 100644
index 0000000..4aa769b
--- /dev/null
+++ b/gremlin-grammar/src/test/resources/incorrect-traversals.txt
@@ -0,0 +1,22 @@
+g.addVertex()
+g.V();---
+g.VVV()
+g.tx().error()
+;;
+
+something something error
+h.V()
+V()
+addV()
+tx().commit()
+g.next()
+g.hasNext()
+g.V().repeat(g).times(10)
+g.V().repeat(g.V().next()).times(10)
+' '
+"  "
+g.V().has("person","name", startingWith(1234)).id()
+g.V('3').oute().limit(1)
+g.V('3').dedupe()
+g.V().order().bye('code').limit(1)
+g.V().horder().by(id)
\ No newline at end of file
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/FeatureReader.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/FeatureReader.java
index 945b836..763cb0e 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/FeatureReader.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/FeatureReader.java
@@ -31,7 +31,8 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * Reads the feature files and extracts Gremlin to a {@code Map} structure that can be used to then generate
+ * Reads the feature files and extracts Gremlin to a {@code Map} structure of the test name as the key with a
+ * {@code List} of Gremlin strings for the value.
  */
 public class FeatureReader {
 
diff --git a/neo4j-gremlin/pom.xml b/neo4j-gremlin/pom.xml
index 0accdea..7973577 100644
--- a/neo4j-gremlin/pom.xml
+++ b/neo4j-gremlin/pom.xml
@@ -172,7 +172,7 @@ limitations under the License.
                 <dependency>
                     <groupId>org.apache.commons</groupId>
                     <artifactId>commons-text</artifactId>
-                    <version>1.8</version>
+                    <version>${commons.text.version}</version>
                     <scope>test</scope>
                 </dependency>
                 <dependency>
diff --git a/pom.xml b/pom.xml
index fc4c822..a84c918 100644
--- a/pom.xml
+++ b/pom.xml
@@ -122,6 +122,7 @@ limitations under the License.
         </contributor>
     </contributors>
     <modules>
+        <module>gremlin-grammar</module>
         <module>gremlin-shaded</module>
         <module>gremlin-core</module>
         <module>gremlin-test</module>
@@ -149,6 +150,7 @@ limitations under the License.
         <commons.configuration.version>2.6</commons.configuration.version>
         <commons.lang.version>2.6</commons.lang.version>
         <commons.lang3.version>3.11</commons.lang3.version>
+        <commons.text.version>1.9</commons.text.version>
         <groovy.version>2.5.14</groovy.version>
         <hadoop.version>2.7.7</hadoop.version>
         <java.tuples.version>1.2</java.tuples.version>
@@ -421,6 +423,9 @@ limitations under the License.
                         <exclude>**/.pytest_cache/**</exclude>
                         <exclude>**/venv/**</exclude>
                         <exclude>**/docfx/**</exclude>
+                        <!-- exclude gremlin-grammar as it is generated code and the license plugin will auto-update
+                             the generated files -->
+                        <exclude>**/src/main/java/org/apache/tinkerpop/gremlin/grammar/**</exclude>
                     </excludes>
                     <licenses>
                         <license implementation="org.apache.rat.analysis.license.ApacheSoftwareLicense20"/>
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
index 13f308c..3489fcf 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java
@@ -276,5 +276,10 @@ public class TinkerGraphPlayTest {
         Object o1 = g.V().map(__.V(1));
         System.out.println(g.V().as("a").both().as("b").dedup("a", "b").by(T.label).select("a", "b").explain());
         System.out.println(g.V().as("a").both().as("b").dedup("a", "b").by(T.label).select("a", "b").toList());
+
+        Traversal<?,?> t =
+                g.V("3").
+                        union(__.repeat(out().simplePath()).times(2).count(),
+                                __.repeat(in().simplePath()).times(2).count());
     }
 }