Context parameters support
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/69a710c8 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/69a710c8 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/69a710c8 Branch: refs/heads/tp32 Commit: 69a710c813f4347365d8938df8f4b619ef531538 Parents: ee33016 Author: Jorge Bay Gondra <jorgebaygon...@gmail.com> Authored: Tue Oct 31 17:33:03 2017 +0100 Committer: Jorge Bay Gondra <jorgebaygon...@gmail.com> Committed: Thu Nov 30 10:00:08 2017 +0100 ---------------------------------------------------------------------- .../Process/Traversal/Instruction.cs | 4 +- .../Gherkin/CommonSteps.cs | 113 ++++++++++----- .../Gherkin/GherkinTestRunner.cs | 32 +---- .../Gherkin/ScenarioData.cs | 59 ++++++++ .../ContextBasedParameter.cs | 84 +++++++++++ .../TraversalEvaluation/ITokenParameter.cs | 3 +- .../TraversalEvaluation/NumericParameter.cs | 8 +- .../StaticTraversalParameter.cs | 4 +- .../TraversalEvaluation/StringParameter.cs | 3 +- .../TraversalEnumParameter.cs | 2 +- .../TraversalEvaluationTests.cs | 46 +++--- .../TraversalEvaluation/TraversalParser.cs | 143 +++++++++---------- .../TraversalPredicateParameter.cs | 5 +- 13 files changed, 330 insertions(+), 176 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Instruction.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Instruction.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Instruction.cs index 195b7bf..a9163be 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Instruction.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Instruction.cs @@ -28,6 +28,8 @@ namespace Gremlin.Net.Process.Traversal /// </summary> public class Instruction { + private static readonly object[] EmptyArgs = new object[0]; + /// <summary> /// Initializes a new instance of the <see cref="Instruction" /> class. /// </summary> @@ -36,7 +38,7 @@ namespace Gremlin.Net.Process.Traversal public Instruction(string operatorName, params dynamic[] arguments) { OperatorName = operatorName; - Arguments = arguments; + Arguments = arguments ?? EmptyArgs; } /// <summary> http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/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 16aef9d..235f1ba 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs @@ -25,15 +25,12 @@ using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Net.Http.Headers; -using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; using Gherkin.Ast; using Gremlin.Net.IntegrationTest.Gherkin.Attributes; using Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation; using Gremlin.Net.Process.Traversal; using Gremlin.Net.Structure; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Xunit; namespace Gremlin.Net.IntegrationTest.Gherkin @@ -41,14 +38,26 @@ namespace Gremlin.Net.IntegrationTest.Gherkin internal class GeneralDefinitions : StepDefinition { private GraphTraversalSource _g; + private readonly IDictionary<string, object> _parameters = new Dictionary<string, object>(); private dynamic _traversal; private object[] _result; - private static readonly IDictionary<string, Func<GraphTraversalSource, ITraversal>> FixedTranslations = - new Dictionary<string, Func<GraphTraversalSource, ITraversal>> + private static readonly IDictionary<Regex, Func<string, object>> Parsers = + new Dictionary<string, Func<string, object>> { - { "g.V().has(\"no\").count()", g => g.V().Has("no").Count() } - }; + {@"d\[(\d+)\]", x => Convert.ToInt64(x)}, + {@"d\[(\d+(?:\.\d+)?)\]", x => Convert.ToDouble(x)}, + {@"v\[(.+)\]", ToVertex}, + {@"v\[(.+)\]\.id", x => ToVertex(x).Id}, + {@"v\[(.+)\]\.sid", x => ToVertex(x).Id.ToString()}, + {@"e\[(.+)\]", ToEdge}, + {@"e\[(.+)\].id", s => ToEdge(s).Id}, + {@"e\[(.+)\].sid", s => ToEdge(s).Id.ToString()}, + {@"p\[(.+)\]", ToPath}, + {@"l\[(.+)\]", ToList}, + {@"s\[(.+)\]", ToSet}, + {@"m\[(.+)\]", ToMap}, + }.ToDictionary(kv => new Regex("^" + kv.Key + "$", RegexOptions.Compiled), kv => kv.Value); [Given("the modern graph")] public void ChooseModernGraph() @@ -57,6 +66,22 @@ namespace Gremlin.Net.IntegrationTest.Gherkin _g = new Graph().Traversal().WithRemote(connection); } + [Given("using the parameter (\\w+) defined as \"(.*)\"")] + public void UsingParameter(string name, string value) + { + _parameters.Add(name, ParseValue(value)); + } + + [Given("the traversal of")] + public void TranslateTraversal(string traversalText) + { + if (_g == null) + { + throw new InvalidOperationException("g should be a traversal source"); + } + _traversal = TraversalParser.GetTraversal(traversalText, _g, _parameters); + } + [When("iterated to list")] public void IterateToList() { @@ -78,16 +103,6 @@ namespace Gremlin.Net.IntegrationTest.Gherkin _result = _traversal.Next(); } - [Given("the traversal of")] - public void TranslateTraversal(string traversalText) - { - if (_g == null) - { - throw new InvalidOperationException("g should be a traversal source"); - } - _traversal = TraversalParser.GetTraversal(traversalText, _g); - } - [Then("the result should be (\\w+)")] public void AssertResult(string characterizedAs, DataTable table) { @@ -104,9 +119,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin { var row = rows[i]; var cells = row.Cells.ToArray(); - var typeName = cells[0].Value; - var expectedValue = ConvertExpectedToType(typeName, cells[1].Value); - var resultItem = ConvertResultItem(typeName, _result[i]); + var expectedValue = ParseValue(cells[0].Value); + var resultItem = ConvertResultItem(null, _result[i]); Assert.Equal(expectedValue, resultItem); } break; @@ -116,10 +130,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin foreach (var row in rows) { var cells = row.Cells.ToArray(); - var typeName = cells[0].Value; - var expectedValue = ConvertExpectedToType(typeName, cells[1].Value); + var expectedValue = ParseValue(cells[0].Value); // Convert all the values in the result to the type - var convertedResult = _result.Select(item => ConvertResultItem(typeName, item)); + var convertedResult = _result.Select(item => ConvertResultItem(null, item)); Assert.Contains(expectedValue, convertedResult); } break; @@ -149,19 +162,51 @@ namespace Gremlin.Net.IntegrationTest.Gherkin return result; } - private object ConvertExpectedToType(string typeName, string stringValue) + private static IDictionary ToMap(string arg) + { + throw new NotImplementedException(); + } + + private static ICollection ToSet(string arg) + { + throw new NotImplementedException(); + } + + private static IList ToList(string arg) + { + throw new NotImplementedException(); + } + + private static Vertex ToVertex(string name) { - switch (typeName) + return ScenarioData.Instance.ModernVertices[name]; + } + + private static Edge ToEdge(string s) + { + throw new NotImplementedException(); + } + + private static Path ToPath(string arg) + { + throw new NotImplementedException(); + } + + private static object ParseValue(string stringValue) + { + Func<string, object> parser = null; + string extractedValue = null; + foreach (var kv in Parsers) { - case "numeric": - return Convert.ToInt64(stringValue); - case "string": - return stringValue; - case "map": - IDictionary<string, JToken> jsonObject = JObject.Parse(stringValue); - return jsonObject.ToDictionary(item => item.Key, item => item.Value.ToString()); + var match = kv.Key.Match(stringValue); + if (match.Success) + { + parser = kv.Value; + extractedValue = match.Groups[1].Value; + break; + } } - throw new NotSupportedException($"Data table result with subtype of {typeName} not supported"); + return parser != null ? parser(extractedValue) : stringValue; } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs index 7523b0b..d850d08 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs @@ -307,7 +307,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin var parser = new Parser(); WriteOutput("Parsing " + gherkinFile); var doc = parser.Parse(gherkinFile); - yield return doc.Feature; + yield return doc.Feature; } } @@ -332,35 +332,5 @@ namespace Gremlin.Net.IntegrationTest.Gherkin } return rootDir.FullName; } - - private void PrintGherkin() - { - var gherkinFile = "/Users/jorge/workspace/temp/count.feature"; - var parser = new Parser(); - GherkinDocument doc = parser.Parse(gherkinFile); - foreach (var scenario in doc.Feature.Children) - { - WriteOutput("--------"); - WriteOutput("Scenario: " + scenario.Name); - foreach (var step in scenario.Steps) - { - WriteOutput(" Step"); - WriteOutput(" Keyword: " + step.Keyword); - WriteOutput(" Text: " + step.Text); - WriteOutput(" Argument: " + step.Argument); - if (step.Argument is DocString) - { - WriteOutput(" " + ((DocString)step.Argument).Content); - } - if (step.Argument is DataTable) - { - foreach (var row in ((DataTable)step.Argument).Rows) - { - WriteOutput(" Row: " + string.Join(", ", row.Cells.Select(x => x.Value))); - } - } - } - } - } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/ScenarioData.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/ScenarioData.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/ScenarioData.cs new file mode 100644 index 0000000..c45ed12 --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/ScenarioData.cs @@ -0,0 +1,59 @@ +#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; +using System.Collections.Generic; +using System.Linq; +using Gremlin.Net.IntegrationTest.Process.Traversal.DriverRemoteConnection; +using Gremlin.Net.Process.Traversal; +using Gremlin.Net.Structure; + +namespace Gremlin.Net.IntegrationTest.Gherkin +{ + public class ScenarioData + { + private static readonly Lazy<ScenarioData> Lazy = new Lazy<ScenarioData>(Load); + + public static ScenarioData Instance => Lazy.Value; + + public IDictionary<string, Vertex> ModernVertices { get; } + + public IDictionary<string, Edge> ModernEdges { get; } + + private ScenarioData(IDictionary<string, Vertex> modernVertices, IDictionary<string, Edge> modernEdges) + { + ModernVertices = modernVertices; + ModernEdges = modernEdges; + } + + private static ScenarioData Load() + { + var connectionFactory = new RemoteConnectionFactory(); + var g = new Graph().Traversal().WithRemote(connectionFactory.CreateRemoteConnection()); + //TODO: Remove workaround once Group() is fixed TINKERPOP-1752 + var vertices = g.V().ToList().ToDictionary(v => g.V(v.Id).Values<string>("name").Next(), v => v); + connectionFactory.Dispose(); + return new ScenarioData(vertices, null); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/ContextBasedParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/ContextBasedParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/ContextBasedParameter.cs new file mode 100644 index 0000000..4f837d4 --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/ContextBasedParameter.cs @@ -0,0 +1,84 @@ +#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; +using System.Collections.Generic; + +namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation +{ + public class ContextBasedParameter : ITokenParameter, IEquatable<ContextBasedParameter> + { + public bool Equals(ContextBasedParameter other) + { + return string.Equals(_name, other._name) && Equals(_value, other._value); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((ContextBasedParameter) obj); + } + + public override int GetHashCode() + { + unchecked + { + return ((_name != null ? _name.GetHashCode() : 0) * 397) ^ (_value != null ? _value.GetHashCode() : 0); + } + } + + private readonly string _name; + private object _value; + + public ContextBasedParameter(string name) + { + _name = name; + } + + private void SetValue(IDictionary<string, object> parameterValues) + { + if (parameterValues == null || !parameterValues.TryGetValue(_name, out var value)) + { + throw new InvalidOperationException($"Parameter \"{_name}\" was not provided"); + } + _value = value; + } + + public object GetValue(IDictionary<string, object> contextParameterValues) + { + SetValue(contextParameterValues); + return _value; + } + + public Type GetParameterType() + { + if (_value == null) + { + throw new NullReferenceException($"Value for parameter \"{_name}\" was not set"); + } + return _value.GetType(); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/ITokenParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/ITokenParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/ITokenParameter.cs index 5c8197f..9b06e80 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/ITokenParameter.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/ITokenParameter.cs @@ -22,6 +22,7 @@ #endregion using System; +using System.Collections.Generic; namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { @@ -30,7 +31,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation /// <summary> /// Gets the value of the parameter /// </summary> - object GetValue(); + object GetValue(IDictionary<string, object> contextParameterValues); /// <summary> /// Gets the type of the parameter http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/NumericParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/NumericParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/NumericParameter.cs index 378680c..7428cc2 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/NumericParameter.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/NumericParameter.cs @@ -22,6 +22,7 @@ #endregion using System; +using System.Collections.Generic; namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { @@ -57,7 +58,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation return $"NumericParameter<{typeof(T).Name}>({Value})"; } - public object GetValue() + public object GetValue(IDictionary<string, object> contextParameterValues) { return Value; } @@ -74,5 +75,10 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { return new NumericParameter<TType>(value); } + + public static NumericParameter<long> CreateLong(string value) + { + return NumericParameter.Create(Convert.ToInt64(value.Substring(0, value.Length - 1))); + } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StaticTraversalParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StaticTraversalParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StaticTraversalParameter.cs index dca691b..dd38dca 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StaticTraversalParameter.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StaticTraversalParameter.cs @@ -50,9 +50,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation return Tokens != null ? Tokens.GetHashCode() : 0; } - public object GetValue() + public object GetValue(IDictionary<string, object> contextParameterValues) { - return TraversalParser.GetTraversalFromTokens(Tokens, null, _traversalText); + return TraversalParser.GetTraversalFromTokens(Tokens, null, contextParameterValues, _traversalText); } public Type GetParameterType() http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StringParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StringParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StringParameter.cs index 82f6cd9..430c2d2 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StringParameter.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StringParameter.cs @@ -22,6 +22,7 @@ #endregion using System; +using System.Collections.Generic; namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { @@ -66,7 +67,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation return $"{GetType().Name}({Value})"; } - public object GetValue() + public object GetValue(IDictionary<string, object> contextParameterValues) { return Value; } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEnumParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEnumParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEnumParameter.cs index 663928a..c99fc95 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEnumParameter.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEnumParameter.cs @@ -85,7 +85,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation return _text.GetHashCode(); } - public object GetValue() + public object GetValue(IDictionary<string, object> contextParameterValues) { return _value; } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs index 9cbceb9..be5f444 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs @@ -31,13 +31,6 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { public class TraversalEvaluationTests { - private readonly ITestOutputHelper _output; - - public TraversalEvaluationTests(ITestOutputHelper output) - { - _output = output; - } - [Fact] public void Traversal_Parser_Should_Parse_Into_Tokens() { @@ -48,6 +41,12 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation new[] {new Token("values", new StringParameter("name"))}), Tuple.Create("g.V().constant(123l)", new[] {new Token("constant", new[] {NumericParameter.Create(123L)})}), + Tuple.Create("g.V().constant(123)", + new[] {new Token("constant", new[] {NumericParameter.Create(123)})}), + Tuple.Create("g.V().constant(123.1)", + new[] {new Token("constant", new[] {NumericParameter.Create(123.1)})}), + Tuple.Create("g.V().constant(123.1f)", + new[] {new Token("constant", new[] {NumericParameter.Create(123.1f)})}), Tuple.Create("g.V().has(\"no\").count()", new[] {new Token("has", new StringParameter("no")), new Token("count")}), Tuple.Create("g.V().has(\"lang\", \"java\")", @@ -55,7 +54,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation Tuple.Create("g.V().where(__.in(\"knows\"))", new[] {new Token("where", new[] {new StaticTraversalParameter( new[] {new Token("__"), new Token("in", new StringParameter("knows"))}, "__.in(\"knows\")")})}), - Tuple.Create("g.V().has(\"age\", P.gt(27)", + Tuple.Create("g.V().has(\"age\",P.gt(27))", new[] {new Token("has", new ITokenParameter[] { new StringParameter("age"), new TraversalPredicateParameter( new[] { new Token("P"), new Token("gt", NumericParameter.Create(27)) }) })}), @@ -65,14 +64,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation foreach (var item in items) { var parts = TraversalParser.ParseTraversal(item.Item1); - _output.WriteLine("Parsing " + item.Item1); - if (parts[parts.Count-1].Parameters != null) - { - _output.WriteLine("{0}", parts[parts.Count-1].Parameters.Count); - _output.WriteLine("Values: " + - string.Join(", ", parts[parts.Count - 1].Parameters.Select(p => p.ToString()))); - } - Assert.Equal(new[] {new Token("g"), new Token("V")}.Concat(item.Item2), parts); + Assert.Equal(item.Item2, parts.Skip(2)); } } @@ -81,21 +73,21 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { var traversalTexts = new [] { - "g.V().count()", - //"g.V().constant(123L)", Can be parsed using the new type-safe API - "g.V().has(\"no\").count()", - "g.V().values(\"age\")", - "g.V().valueMap(\"name\", \"age\")", - "g.V().where(__.in(\"created\").count().is(1)).values(\"name\")", - "g.V().count(Scope.local)", - "g.V().values(\"age\").is(P.lte(30))" + Tuple.Create("g.V().count()", 2), +// //"g.V().constant(123L)", Can be parsed using the new type-safe API + Tuple.Create("g.V().has(\"no\").count()", 3), + Tuple.Create("g.V().values(\"age\")", 2), + Tuple.Create("g.V().valueMap(\"name\", \"age\")", 2), + Tuple.Create("g.V().where(__.in(\"created\").count().is(1)).values(\"name\")", 3), + Tuple.Create("g.V().count(Scope.local)", 2), + Tuple.Create("g.V().values(\"age\").is(P.lte(30))", 3) }; var g = new Graph().Traversal(); - foreach (var text in traversalTexts) + foreach (var tuple in traversalTexts) { - var traversal = TraversalParser.GetTraversal(text, g); + var traversal = TraversalParser.GetTraversal(tuple.Item1, g, null); Assert.NotNull(traversal); - Assert.True(traversal.Bytecode.StepInstructions.Count > 0); + Assert.Equal(tuple.Item2, traversal.Bytecode.StepInstructions.Count); } } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/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 9880a64..4cf7b4a 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs @@ -36,71 +36,60 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { { "g.V().fold().count(Scope.local)", g => g.V().Fold<object>().Count(Scope.Local)} }; - - private static readonly Regex RegexInteger = new Regex(@"\d+", RegexOptions.Compiled); - private static readonly Regex RegexDouble = new Regex(@"\d+\.\d+", RegexOptions.Compiled); - private static readonly Regex RegexFloat = - new Regex(@"\d+\.\d+f", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private static readonly Regex RegexLong = new Regex(@"\d+l", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + private static readonly Regex RegexNumeric = + new Regex(@"\d+(\.\d+)?(?:l|f)?", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex RegexEnum = new Regex(@"\w+\.\w+", RegexOptions.Compiled); - internal static ITraversal GetTraversal(string traversalText, GraphTraversalSource g) + private static readonly Regex RegexParam = new Regex(@"\w+", RegexOptions.Compiled); + + internal static ITraversal GetTraversal(string traversalText, GraphTraversalSource g, + IDictionary<string, object> contextParameterValues) { if (!FixedTranslations.TryGetValue(traversalText, out var traversalBuilder)) { var tokens = ParseTraversal(traversalText); - return GetTraversalFromTokens(tokens, g, traversalText); + return GetTraversalFromTokens(tokens, g, contextParameterValues, traversalText); } return traversalBuilder(g); } internal static ITraversal GetTraversalFromTokens(IList<Token> tokens, GraphTraversalSource g, - string traversalText) + IDictionary<string, object> contextParameterValues, + string traversalText) { - ITraversal traversal; - Type traversalType; - var initialIndex = 2; + object instance; + Type instanceType; if (tokens[0].Name == "g") { - switch (tokens[1].Name) - { - case "V": - //TODO: support V() parameters - traversal = g.V(); - break; - case "E": - traversal = g.E(); - break; - default: - throw BuildException(traversalText); - } - traversalType = traversal.GetType(); + instance = g; + instanceType = g.GetType(); } else if (tokens[0].Name == "__") { - traversal = null; - traversalType = typeof(__); - initialIndex = 1; + instance = null; + instanceType = typeof(__); } else { throw BuildException(traversalText); } - for (var i = initialIndex; i < tokens.Count; i++) + for (var i = 1; i < tokens.Count; i++) { var token = tokens[i]; var name = GetCsharpName(token.Name); - var method = traversalType.GetMethod(name); + var method = instanceType.GetMethod(name); if (method == null) { throw new InvalidOperationException($"Traversal method '{tokens[i]}' not found for testing"); } - var parameterValues = BuildParameters(method, token, out var genericParameters); + var parameterValues = BuildParameters(method, token, contextParameterValues, out var genericParameters); method = BuildGenericMethod(method, genericParameters, parameterValues); - traversal = (ITraversal) method.Invoke(traversal, parameterValues); - traversalType = traversal.GetType(); + instance = method.Invoke(instance, parameterValues); + instanceType = instance.GetType(); } - return traversal; + return (ITraversal) instance; } private static MethodInfo BuildGenericMethod(MethodInfo method, IDictionary<string, Type> genericParameters, @@ -133,6 +122,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation } private static object[] BuildParameters(MethodInfo method, Token token, + IDictionary<string, object> contextParameterValues, out IDictionary<string, Type> genericParameterTypes) { var paramsInfo = method.GetParameters(); @@ -145,7 +135,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation if (token.Parameters.Count > i) { var tokenParameter = token.Parameters[i]; - value = tokenParameter.GetValue(); + value = tokenParameter.GetValue(contextParameterValues); if (info.ParameterType.IsGenericParameter) { // We've provided a value for parameter of a generic type, we can infer the @@ -191,41 +181,40 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation var result = new List<Token>(); var startIndex = i; var parsing = ParsingPart.Name; - string name = null; var parameters = new List<ITokenParameter>(); + string name = null; while (i < text.Length) { switch (text[i]) { case '.': - if (name == null) + if (parsing == ParsingPart.Name) { - name = text.Substring(startIndex, i - startIndex); + // The previous token was an object property, not a method + result.Add(new Token(text.Substring(startIndex, i - startIndex))); } startIndex = i + 1; - result.Add(new Token(name, parameters)); - name = null; parameters = new List<ITokenParameter>(); parsing = ParsingPart.Name; break; case '(': { name = text.Substring(startIndex, i - startIndex); - i++; parsing = ParsingPart.StartParameters; + // Start parsing from the next index + i++; var param = ParseParameter(text, ref i); if (param == null) { - parsing = ParsingPart.EndParameters; - } - else - { - parameters.Add(param); + // The next character was a ')', empty params + // Evaluate the current position + continue; } + parameters.Add(param); break; } case ',' when text[i+1] != ' ': - case ' ' when text[i+1] != ' ': + case ' ' when text[i+1] != ' ' && text[i+1] != ')': { if (parsing != ParsingPart.StartParameters) { @@ -236,31 +225,33 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation var param = ParseParameter(text, ref i); if (param == null) { - parsing = ParsingPart.EndParameters; - } - else - { - parameters.Add(param); + // The next character was a ')', empty params + // Evaluate the current position + continue; } + parameters.Add(param); break; } case ')' when parsing != ParsingPart.StartParameters: - // The traversal already ended - i--; - if (name != null) + // The current nested object already ended + if (parsing == ParsingPart.Name) { - result.Add(new Token(name, parameters)); + // The previous token was an object property, not a method and finished + result.Add(new Token(text.Substring(startIndex, i - startIndex))); } + i--; return result; case ')': parsing = ParsingPart.EndParameters; + result.Add(new Token(name, parameters)); break; } i++; } - if (name != null) + if (parsing == ParsingPart.Name) { - result.Add(new Token(name, parameters)); + // The previous token was an object property, not a method and finished + result.Add(new Token(text.Substring(startIndex, i - startIndex))); } return result; } @@ -297,38 +288,40 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation } if (RegexEnum.IsMatch(parameterText)) { + i += parameterText.Length - 1; return new TraversalEnumParameter(parameterText); } + if (RegexParam.IsMatch(parameterText)) + { + i += parameterText.Length - 1; + return new ContextBasedParameter(parameterText); + } throw new NotSupportedException($"Parameter {parameterText} not supported"); } private static ITokenParameter ParseNumber(string text, ref int i) { - var match = RegexLong.Match(text.Substring(i)); - if (match.Success) + var match = RegexNumeric.Match(text, i); + if (!match.Success) { - i += match.Value.Length - 1; - return NumericParameter.Create(Convert.ToInt64(match.Value.Substring(0, match.Value.Length-1))); + throw new InvalidOperationException( + $"Could not parse numeric value from the beginning of {text.Substring(i)}"); } - match = RegexFloat.Match(text.Substring(i)); - if (match.Success) + var numericText = match.Value.ToUpper(); + i += match.Value.Length - 1; + if (numericText.EndsWith("L")) { - i += match.Value.Length - 1; - return NumericParameter.Create(Convert.ToSingle(match.Value.Substring(0, match.Value.Length-1))); + return NumericParameter.Create(Convert.ToInt64(match.Value.Substring(0, match.Value.Length - 1))); } - match = RegexDouble.Match(text.Substring(i)); - if (match.Success) + if (numericText.EndsWith("F")) { - i += match.Value.Length; - return NumericParameter.Create(Convert.ToSingle(match.Value)); + return NumericParameter.Create(Convert.ToSingle(match.Value.Substring(0, match.Value.Length-1))); } - match = RegexInteger.Match(text.Substring(i)); - if (!match.Success) + if (match.Groups[1].Value != "") { - throw new InvalidOperationException( - $"Could not parse numeric value from the beginning of {text.Substring(i)}"); + // Captured text with the decimal separator + return NumericParameter.Create(Convert.ToDouble(match.Value)); } - i += match.Value.Length; return NumericParameter.Create(Convert.ToInt32(match.Value)); } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/69a710c8/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalPredicateParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalPredicateParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalPredicateParameter.cs index 0d0dc49..83060c7 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalPredicateParameter.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalPredicateParameter.cs @@ -52,7 +52,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation return Tokens != null ? Tokens.GetHashCode() : 0; } - public object GetValue() + public object GetValue(IDictionary<string, object> contextParameterValues) { var type = typeof(P); object instance = null; @@ -65,7 +65,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { throw new InvalidOperationException($"Predicate (P) method '{token}' not found for testing"); } - instance = method.Invoke(instance, new object[] {token.Parameters.Select(p => p.GetValue()).ToArray()}); + instance = method.Invoke(instance, + new object[] {token.Parameters.Select(p => p.GetValue(contextParameterValues)).ToArray()}); } return instance; }