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/12/23 10:51:15 UTC

[tinkerpop] branch vars updated: wip - need to restructure grammar after this to take vars more generally

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

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


The following commit(s) were added to refs/heads/vars by this push:
     new f5f3dc6  wip - need to restructure grammar after this to take vars more generally
f5f3dc6 is described below

commit f5f3dc69c735421ae9c45fe3882af03832904ce0
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Thu Dec 23 05:49:52 2021 -0500

    wip - need to restructure grammar after this to take vars more generally
    
    needs to cover things like enums and Traversal - the current model isnt working too well for that.
---
 .../gremlin/jsr223/GremlinLangScriptEngine.java    |  2 +-
 .../language/grammar/GenericLiteralVisitor.java    | 51 +++++++++++++++++++---
 .../language/grammar/GremlinAntlrToJava.java       |  8 ++++
 .../jsr223/GremlinLangScriptEngineTest.java        | 46 ++++++++++++++++++-
 4 files changed, 98 insertions(+), 9 deletions(-)

diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java
index 6c771bd..f13004c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java
@@ -104,7 +104,7 @@ public class GremlinLangScriptEngine extends AbstractScriptEngine implements Gre
         if (!(o instanceof GraphTraversalSource))
             throw new IllegalArgumentException("g is of type " + o.getClass().getSimpleName() + " and is not an instance of TraversalSource");
 
-        final GremlinAntlrToJava antlr = new GremlinAntlrToJava((GraphTraversalSource) o);
+        final GremlinAntlrToJava antlr = new GremlinAntlrToJava((GraphTraversalSource) o, context.getBindings(ScriptContext.ENGINE_SCOPE));
 
         try {
             return GremlinQueryParser.parse(script, antlr);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
index f7fe412..45518dd 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
@@ -34,6 +34,7 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 
 /**
  * Visitor class to handle generic literal. All visitor methods return type is Object. It maybe used as a singleton
@@ -61,10 +62,6 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
      * Parse a string literal context and return the string literal.
      */
     public String getStringLiteral(final GremlinParser.StringLiteralContext stringLiteral, final GremlinAntlrToJava antlr) {
-        if (antlr != null && stringLiteral.variable() != null) {
-            final String varName = stringLiteral.variable().getText();
-            return tryBindings(antlr, varName);
-        }
         return (String) visitStringLiteral(stringLiteral);
     }
 
@@ -265,6 +262,10 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
      */
     @Override
     public Object visitGenericLiteralMap(final GremlinParser.GenericLiteralMapContext ctx) {
+        if (isPossibleVariable(antlr, ctx.variable())) {
+            return tryBindings(antlr, ctx.variable());
+        }
+
         final HashMap<Object, Object> literalMap = new HashMap<>();
         int childIndex = 1;
         while (childIndex < ctx.getChildCount() && ctx.getChildCount() > 3) {
@@ -303,6 +304,10 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
      */
     @Override
     public Object visitIntegerLiteral(final GremlinParser.IntegerLiteralContext ctx) {
+        if (isPossibleVariable(antlr, ctx.variable())) {
+            return tryBindings(antlr, ctx.variable());
+        }
+
         String integerLiteral = ctx.getText().toLowerCase().replace("_", "");
         // handle suffixes for specific types
         final int lastCharIndex = integerLiteral.length() - 1;
@@ -369,6 +374,10 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
      */
     @Override
     public Object visitFloatLiteral(final GremlinParser.FloatLiteralContext ctx) {
+        if (isPossibleVariable(antlr, ctx.variable())) {
+            return tryBindings(antlr, ctx.variable());
+        }
+
         final String floatLiteral = ctx.getText().toLowerCase();
 
         // check suffix
@@ -393,6 +402,10 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
      */
     @Override
     public Object visitBooleanLiteral(final GremlinParser.BooleanLiteralContext ctx) {
+        if (isPossibleVariable(antlr, ctx.variable())) {
+            return tryBindings(antlr, ctx.variable());
+        }
+
         return Boolean.valueOf(ctx.getText());
     }
 
@@ -401,6 +414,10 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
      */
     @Override
     public Object visitDateLiteral(final GremlinParser.DateLiteralContext ctx) {
+        if (isPossibleVariable(antlr, ctx.variable())) {
+            return tryBindings(antlr, ctx.variable());
+        }
+
         return DatetimeHelper.parse((String) visitStringLiteral(ctx.stringLiteral()));
     }
 
@@ -409,6 +426,10 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
      */
     @Override
     public Object visitStringLiteral(final GremlinParser.StringLiteralContext ctx) {
+        if (isPossibleVariable(antlr, ctx.variable())) {
+            return tryBindings(antlr, ctx.variable());
+        }
+
         // Using Java string unescaping because it coincides with the Groovy rules:
         // https://docs.oracle.com/javase/tutorial/java/data/characters.html
         // http://groovy-lang.org/syntax.html#_escaping_special_characters
@@ -512,10 +533,26 @@ public class GenericLiteralVisitor extends GremlinBaseVisitor<Object> {
         return result;
     }
 
-    private static <T> T tryBindings(final GremlinAntlrToJava antlr, final String varName) {
-        if (!antlr.bindings.containsKey(varName)) {
+    /**
+     * Determines if the currently parsed context is a variable and if it is potentially resolvable.
+     */
+    private static boolean isPossibleVariable(final GremlinAntlrToJava antlr, final GremlinParser.VariableContext variable) {
+        return antlr != null && variable != null;
+    }
+
+    /**
+     * Tries to extract a value for the variable from the bindings supplied to the parser.
+     * @param antlr must not be null
+     * @param variableCtx must not be null
+     */
+    private static <T> T tryBindings(final GremlinAntlrToJava antlr,
+                                     final GremlinParser.VariableContext variableCtx) {
+        Objects.requireNonNull(antlr);
+        Objects.requireNonNull(variableCtx);
+
+        final String varName = variableCtx.getText();
+        if (!antlr.bindings.containsKey(varName))
             throw new UnboundIdentifierException(varName);
-        }
 
         return (T) antlr.bindings.get(varName);
     }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinAntlrToJava.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinAntlrToJava.java
index cd9473a..364687b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinAntlrToJava.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinAntlrToJava.java
@@ -114,6 +114,14 @@ public class GremlinAntlrToJava extends GremlinBaseVisitor<Object> {
     }
 
     /**
+     * Constructs a new instance that is bound to the specified {@link GraphTraversalSource} and thus spawns the
+     * {@link Traversal} from this "g" rather than from a fresh one constructed from the {@link Graph} instance.
+     */
+    public GremlinAntlrToJava(final GraphTraversalSource g, final Map<String, Object> bindings) {
+        this(GraphTraversalSourceVisitor.TRAVERSAL_ROOT, g.getGraph(), __::start, g, bindings);
+    }
+
+    /**
      * Constructs a new instance that is bound to the specified {@link Graph} instance with an override to using
      * {@link __} for constructing anonymous {@link Traversal} instances. Assumes that "g" is the name of the
      * {@link GraphTraversalSource}.
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineTest.java
index aa4d714..9503b01 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineTest.java
@@ -20,11 +20,21 @@ package org.apache.tinkerpop.gremlin.jsr223;
 
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.structure.T;
 import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
 import org.junit.Test;
 
 import javax.script.Bindings;
+import javax.script.ScriptContext;
 import javax.script.ScriptException;
+import javax.script.SimpleBindings;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
@@ -36,7 +46,9 @@ public class GremlinLangScriptEngineTest {
     private final static GraphTraversalSource g = EmptyGraph.instance().traversal();
 
     static {
-        scriptEngine.put("g", g);
+        final Bindings globalBindings = new SimpleBindings();
+        globalBindings.put("g", g);
+        scriptEngine.setBindings(globalBindings, ScriptContext.GLOBAL_SCOPE);
     }
 
     @Test
@@ -47,6 +59,38 @@ public class GremlinLangScriptEngineTest {
     }
 
     @Test
+    public void shouldEvalGremlinScriptWithVariables() throws ScriptException {
+        final Bindings b = new SimpleBindings();
+        b.put("int0", 0);
+        b.put("dec0", 0.0d);
+        b.put("string0", "knows");
+        b.put("string1", "created");
+        b.put("bool0", true);
+        b.put("t0", T.id);
+
+        final Date d = new Date();
+        b.put("date0", d);
+
+        final List<String> l = new ArrayList<>();
+        l.add("yes");
+        l.add("no");
+        b.put("list0", l);
+
+        final Map<String,Object> m = new HashMap<>();
+        m.put("x", 1);
+        m.put("y", 2);
+        b.put("map0", m);
+
+        final Object result = scriptEngine.eval("g.inject(bool0, date0, map0, int0, dec0, list0).V().out(string0).in(string1).project('tid')", b);
+        assertThat(result, instanceOf(Traversal.Admin.class));
+        assertEquals(g.inject(true, d, m, 0, 0.0d, l).
+                       V().
+                       out("knows").in("created").
+                       project("tid").asAdmin().getBytecode(),
+                     ((Traversal.Admin) result).getBytecode());
+    }
+
+    @Test
     public void shouldEvalGremlinBytecode() throws ScriptException {
         final Object result = scriptEngine.eval(g.V().asAdmin().getBytecode(), "g");
         assertThat(result, instanceOf(Traversal.Admin.class));