TINKERPOP-1919 Add Lambda support to Gremlin.Net This adds a Lambda class that can be used to construct Groovy or Python lambdas. The Lambda class implements all interfaces that mirror Javas functional interfaces like Function.
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/0cdaa3a2 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/0cdaa3a2 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/0cdaa3a2 Branch: refs/heads/TINKERPOP-1897 Commit: 0cdaa3a2114670a34999aa56e0487a2e7ef998e1 Parents: 0bf9b2f Author: Florian Hockmann <f...@florian-hockmann.de> Authored: Wed Mar 14 18:56:47 2018 +0100 Committer: Florian Hockmann <f...@florian-hockmann.de> Committed: Thu Mar 15 18:16:05 2018 +0100 ---------------------------------------------------------------------- CHANGELOG.asciidoc | 1 + docs/src/reference/gremlin-variants.asciidoc | 29 ++++++-- .../upgrade/release-3.2.x-incubating.asciidoc | 7 ++ gremlin-dotnet/glv/generate.groovy | 4 +- .../Process/Traversal/GraphTraversalSource.cs | 52 +++++++++++++- .../Gremlin.Net/Process/Traversal/ISupplier.cs | 32 +++++++++ .../Process/Traversal/IUnaryOperator.cs | 33 +++++++++ .../src/Gremlin.Net/Process/Traversal/Lambda.cs | 72 ++++++++++++++++++++ .../Structure/IO/GraphSON/GraphSONWriter.cs | 3 +- .../Structure/IO/GraphSON/LambdaSerializer.cs | 43 ++++++++++++ .../Gherkin/CommonSteps.cs | 2 +- .../Gherkin/IgnoreException.cs | 4 -- .../TraversalEvaluation/TraversalParser.cs | 2 +- .../IO/GraphSON/GraphSONWriterTests.cs | 13 ++++ 14 files changed, 282 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/CHANGELOG.asciidoc ---------------------------------------------------------------------- diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 94ee24f..8fcbe1a 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -23,6 +23,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima [[release-3-2-8]] === TinkerPop 3.2.8 (Release Date: NOT OFFICIALLY RELEASED YET) +* Added a `Lambda` class to Gremlin.Net that makes it possible to use Groovy and Python lambdas with Gremlin.Net. * Enums are now represented as classes in Gremlin.Net which allows to use them as arguments in more steps. * Bumped to Groovy 2.4.14. * Added `checkAdjacentVertices` option to `SubgraphStrategy`. http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/docs/src/reference/gremlin-variants.asciidoc ---------------------------------------------------------------------- diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc index ace8119..bf8c8b1 100644 --- a/docs/src/reference/gremlin-variants.asciidoc +++ b/docs/src/reference/gremlin-variants.asciidoc @@ -46,7 +46,7 @@ implementation of Gremlin and serves as the foundation by which all other Gremli === The Lambda Solution Supporting link:https://en.wikipedia.org/wiki/Anonymous_function[anonymous functions] across languages is difficult as -most language do not support lambda introspection and thus, code analysis. In Gremlin-Java, Java8 lambdas can be leveraged. +most languages do not support lambda introspection and thus, code analysis. In Gremlin-Java, Java8 lambdas can be leveraged. [source,java] g.V().out("knows").map(t -> t.get().value("name") + " is the friend name") <1> @@ -281,7 +281,7 @@ re-construction machine-side. === The Lambda Solution Supporting link:https://en.wikipedia.org/wiki/Anonymous_function[anonymous functions] across languages is difficult as -most language do not support lambda introspection and thus, code analysis. In Gremlin-Python, +most languages do not support lambda introspection and thus, code analysis. In Gremlin-Python, a link:https://docs.python.org/2/reference/expressions.html#lambda[Python lambda] should be represented as a zero-arg callable that returns a string representation of a lambda. The default lambda language is `gremlin-python` and can be changed via `gremlin_python.statics.default_lambda_language`. When the lambda is represented in `Bytecode` its language is encoded @@ -343,8 +343,10 @@ var g = graph.Traversal().WithRemote(new DriverRemoteConnection(new GremlinClien When a traversal from the `GraphTraversalSource` is iterated, the traversalâs `Bytecode` is sent over the wire via the registered `IRemoteConnection`. The bytecode is used to construct the equivalent traversal at the remote traversal source. -Since Gremlin.Net currently doesn't support lambda expressions, all traversals can be translated to Gremlin-Java on the remote -location (e.g. Gremlin Server). +Moreover, typically the bytecode is analyzed to determine which language the bytecode should be translated to. If the traversal +does not contain lambdas, the remote location (e.g. Gremlin Server) will typically +use Gremlin-Java. If it has lambdas written in Groovy, it will use Gremlin-Groovy (e.g. `GremlinGroovyScriptEngine`). +Likewise, if it has lambdas represented in Python, it will use Gremlin-Python (e.g. `GremlinJythonScriptEngine`). IMPORTANT: Gremlin.Netâs `ITraversal` interface supports the standard Gremlin methods such as `Next()`, `NextTraverser()`, `ToSet()`, `ToList()`, etc. Such "terminal" methods trigger the evaluation of the traversal. @@ -433,6 +435,25 @@ NOTE: Many of the TraversalStrategy classes in Gremlin.Net are proxies to the re JVM-based Gremlin traversal machine. As such, their `Apply(ITraversal)` method does nothing. However, the strategy is encoded in the Gremlin.Net bytecode and transmitted to the Gremlin traversal machine for re-construction machine-side. +=== The Lambda Solution + +Supporting link:https://en.wikipedia.org/wiki/Anonymous_function[anonymous functions] across languages is difficult as +most languages do not support lambda introspection and thus, code analysis. While Gremlin.Net doesn't support C# lambdas, it +is still able to represent lambdas in other languages. When the lambda is represented in `Bytecode` its language is encoded +such that the remote connection host can infer which translator and ultimate execution engine to use. + +[source,csharp] +---- +g.V().Out().Map<int>(Lambda.Groovy("it.get().value('name').length()")).Sum<int>().ToList(); <1> +g.V().Out().Map<int>(Lambda.Python("lambda x: len(x.get().value('name'))")).Sum<int>().ToList(); <2> +---- + +<1> `Lambda.Groovy()` can be used to create a Groovy lambda. +<2> `Lambda.Python()` can be used to create a Python lambda. + +The `Lambda` class implements interfaces like `IFunction` and `IPredicate` that mirror their Java counterparts which makes it possible +to use lambdas with Gremlin.Net for the same steps as in Gremlin-Java. + [[gremlin-javascript]] == Gremlin-JavaScript http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/docs/src/upgrade/release-3.2.x-incubating.asciidoc ---------------------------------------------------------------------- diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc b/docs/src/upgrade/release-3.2.x-incubating.asciidoc index 0848843..edc7f79 100644 --- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc +++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc @@ -42,6 +42,13 @@ by clients that might mysteriously disappear without properly closing their conn See: link:https://issues.apache.org/jira/browse/TINKERPOP-1726[TINKERPOP-1726] +==== Gremlin.Net Lambdas + +Gremlin.Net now has a `Lambda` class that can be used to construct Groovy or Java lambdas which will be evaluated on the +server. + +See: link:https://issues.apache.org/jira/browse/TINKERPOP-1854[TINKERPOP-1854], link:http://tinkerpop.apache.org/docs/3.2.8/reference/#_the_lambda_solution_3[Reference Documentation - Gremlin.Net - The Lambda Solution]. + ==== Gremlin.Net Tokens Improved The various Gremlin tokens (e.g. `T`, `Order`, `Operator`, etc.) that were implemented as Enums before in Gremlin.Net http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/gremlin-dotnet/glv/generate.groovy ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/glv/generate.groovy b/gremlin-dotnet/glv/generate.groovy index 5057ff8..10b1008 100644 --- a/gremlin-dotnet/glv/generate.groovy +++ b/gremlin-dotnet/glv/generate.groovy @@ -54,10 +54,10 @@ def toCSharpTypeMap = ["Long": "long", "TraversalStrategy[]": "ITraversalStrategy[]", "Function": "IFunction", "BiFunction": "IBiFunction", - "UnaryOperator": "object", + "UnaryOperator": "IUnaryOperator", "BinaryOperator": "IBinaryOperator", "Consumer": "IConsumer", - "Supplier": "object", + "Supplier": "ISupplier", "Comparator": "IComparator", "VertexProgram": "object"] http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs index 9c32559..7115016 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs @@ -104,7 +104,7 @@ namespace Gremlin.Net.Process.Traversal return source; } - public GraphTraversalSource WithSack(object initialValue, object splitOperator) + public GraphTraversalSource WithSack(object initialValue, IUnaryOperator splitOperator) { var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), new Bytecode(Bytecode)); @@ -112,7 +112,39 @@ namespace Gremlin.Net.Process.Traversal return source; } - public GraphTraversalSource WithSack(object initialValue, object splitOperator, IBinaryOperator mergeOperator) + public GraphTraversalSource WithSack(object initialValue, IUnaryOperator splitOperator, IBinaryOperator mergeOperator) + { + var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), + new Bytecode(Bytecode)); + source.Bytecode.AddSource("withSack", initialValue, splitOperator, mergeOperator); + return source; + } + + public GraphTraversalSource WithSack(ISupplier initialValue) + { + var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), + new Bytecode(Bytecode)); + source.Bytecode.AddSource("withSack", initialValue); + return source; + } + + public GraphTraversalSource WithSack(ISupplier initialValue, IBinaryOperator mergeOperator) + { + var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), + new Bytecode(Bytecode)); + source.Bytecode.AddSource("withSack", initialValue, mergeOperator); + return source; + } + + public GraphTraversalSource WithSack(ISupplier initialValue, IUnaryOperator splitOperator) + { + var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), + new Bytecode(Bytecode)); + source.Bytecode.AddSource("withSack", initialValue, splitOperator); + return source; + } + + public GraphTraversalSource WithSack(ISupplier initialValue, IUnaryOperator splitOperator, IBinaryOperator mergeOperator) { var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), new Bytecode(Bytecode)); @@ -136,6 +168,22 @@ namespace Gremlin.Net.Process.Traversal return source; } + public GraphTraversalSource WithSideEffect(string key, ISupplier initialValue) + { + var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), + new Bytecode(Bytecode)); + source.Bytecode.AddSource("withSideEffect", key, initialValue); + return source; + } + + public GraphTraversalSource WithSideEffect(string key, ISupplier initialValue, IBinaryOperator reducer) + { + var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), + new Bytecode(Bytecode)); + source.Bytecode.AddSource("withSideEffect", key, initialValue, reducer); + return source; + } + public GraphTraversalSource WithStrategies(params ITraversalStrategy[] traversalStrategies) { var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/ISupplier.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/ISupplier.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/ISupplier.cs new file mode 100644 index 0000000..b1dda13 --- /dev/null +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/ISupplier.cs @@ -0,0 +1,32 @@ +#region License + +/* + * 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. + */ + +#endregion + +namespace Gremlin.Net.Process.Traversal +{ + /// <summary> + /// Represents a supplier of results + /// </summary> + public interface ISupplier + { + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/IUnaryOperator.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/IUnaryOperator.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/IUnaryOperator.cs new file mode 100644 index 0000000..b57be02 --- /dev/null +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/IUnaryOperator.cs @@ -0,0 +1,33 @@ +#region License + +/* + * 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. + */ + +#endregion + +namespace Gremlin.Net.Process.Traversal +{ + /// <summary> + /// Represents an operation on a single operand that produces a result of the same type as its operand. This is a + /// specialization of Function for the case where the operand and result are of the same type. + /// </summary> + public interface IUnaryOperator : IFunction + { + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Lambda.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Lambda.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Lambda.cs new file mode 100644 index 0000000..21849ef --- /dev/null +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Lambda.cs @@ -0,0 +1,72 @@ +#region License + +/* + * 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. + */ + +#endregion + +namespace Gremlin.Net.Process.Traversal +{ + /// <summary> + /// Represents a lambda. + /// </summary> + public class Lambda : IFunction, IBiFunction, IPredicate, IUnaryOperator, IBinaryOperator, IComparator, IConsumer, + ISupplier + { + private const int DefaultArgument = -1; + + private Lambda(string expression, string language) + { + LambdaExpression = expression; + Language = language; + } + + /// <summary> + /// Gets the lambda expression. + /// </summary> + public string LambdaExpression { get; } + + /// <summary> + /// Gets the language of this lambda. + /// </summary> + public string Language { get; } + + internal object Arguments => DefaultArgument; + + /// <summary> + /// Creates a new Groovy <see cref="Lambda"/>. + /// </summary> + /// <param name="expression">The lambda expression.</param> + /// <returns>The created <see cref="Lambda"/>.</returns> + public static Lambda Groovy(string expression) + { + return new Lambda(expression, "gremlin-groovy"); + } + + /// <summary> + /// Creates a new Python <see cref="Lambda"/>. + /// </summary> + /// <param name="expression">The lambda expression.</param> + /// <returns>The created <see cref="Lambda"/>.</returns> + public static Lambda Python(string expression) + { + return new Lambda(expression, "gremlin-python"); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs index f23d80d..826d608 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs @@ -59,7 +59,8 @@ namespace Gremlin.Net.Structure.IO.GraphSON {typeof(Edge), new EdgeSerializer()}, {typeof(Property), new PropertySerializer()}, {typeof(VertexProperty), new VertexPropertySerializer()}, - {typeof(AbstractTraversalStrategy), new TraversalStrategySerializer()} + {typeof(AbstractTraversalStrategy), new TraversalStrategySerializer()}, + {typeof(Lambda), new LambdaSerializer()} }; /// <summary> http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/LambdaSerializer.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/LambdaSerializer.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/LambdaSerializer.cs new file mode 100644 index 0000000..fa739fd --- /dev/null +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/LambdaSerializer.cs @@ -0,0 +1,43 @@ +#region License + +/* + * 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. + */ + +#endregion + +using System.Collections.Generic; +using Gremlin.Net.Process.Traversal; + +namespace Gremlin.Net.Structure.IO.GraphSON +{ + internal class LambdaSerializer : IGraphSONSerializer + { + public Dictionary<string, dynamic> Dictify(dynamic objectData, GraphSONWriter writer) + { + Lambda lambda = objectData; + var valueDict = new Dictionary<string, dynamic> + { + {"script", lambda.LambdaExpression}, + {"language", lambda.Language}, + {"arguments", lambda.Arguments} + }; + return GraphSONUtil.ToTypedValue(nameof(Lambda), valueDict); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs index 0abc247..dd96474 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs @@ -233,7 +233,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin private static object ToLambda(string stringLambda, string graphName) { - throw new IgnoreException(IgnoreReason.LambdaNotSupported); + return Lambda.Groovy(stringLambda); } private static object ToNumber(string stringNumber, string graphName) http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs index fd226bf..9aa5213 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs @@ -40,9 +40,6 @@ namespace Gremlin.Net.IntegrationTest.Gherkin string reasonSuffix = null; switch (reason) { - case IgnoreReason.LambdaNotSupported: - reasonSuffix = " because lambdas are not supported in Gremlin.NET (TINKERPOP-1854)"; - break; case IgnoreReason.PWithinWrapsArgumentsInArray: reasonSuffix = " because P.Within() arguments are incorrectly wrapped in an array (TINKERPOP-1920)"; break; @@ -56,7 +53,6 @@ namespace Gremlin.Net.IntegrationTest.Gherkin public enum IgnoreReason { - LambdaNotSupported, PWithinWrapsArgumentsInArray, PNotDeserializationProblem } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs index 11145da..7e1486c 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs @@ -398,7 +398,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { return ParseNumber(text, ref i); } - if (text.Substring(i, 3).StartsWith("__.")) + if (text.Length >= i + 3 && text.Substring(i, 3).StartsWith("__.")) { var startIndex = i; var tokens = ParseTokens(text, ref i); http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0cdaa3a2/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs index 3e2d307..54dc8f3 100644 --- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs @@ -333,6 +333,19 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphSON const string expected = "{\"@type\":\"g:SubgraphStrategy\",\"@value\":{}}"; Assert.Equal(expected, graphSon); } + + [Fact] + public void ShouldSerializeLambda() + { + var writer = CreateStandardGraphSONWriter(); + var lambda = Lambda.Groovy("{ it.get() }"); + + var graphSon = writer.WriteObject(lambda); + + const string expected = + "{\"@type\":\"g:Lambda\",\"@value\":{\"script\":\"{ it.get() }\",\"language\":\"gremlin-groovy\",\"arguments\":-1}}"; + Assert.Equal(expected, graphSon); + } } internal class TestGraphSONSerializer : IGraphSONSerializer