Traversal parser for tests
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/6d8ff7d7 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/6d8ff7d7 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/6d8ff7d7 Branch: refs/heads/tp32 Commit: 6d8ff7d7a4151948696a08eecdd73f334b464f7f Parents: cee6859 Author: Jorge Bay Gondra <jorgebaygon...@gmail.com> Authored: Thu Oct 26 13:06:04 2017 +0200 Committer: Jorge Bay Gondra <jorgebaygon...@gmail.com> Committed: Thu Nov 30 10:00:07 2017 +0100 ---------------------------------------------------------------------- .../Gherkin/CommonSteps.cs | 13 +- .../Gherkin/GherkinTestRunner.cs | 45 +++- .../TraversalEvaluation/ITokenParameter.cs | 30 +++ .../TraversalEvaluation/NumericParameter.cs | 68 +++++ .../StaticTraversalParameter.cs | 57 ++++ .../TraversalEvaluation/StringParameter.cs | 69 +++++ .../Gherkin/TraversalEvaluation/Token.cs | 76 ++++++ .../TraversalEvaluationTests.cs | 79 ++++++ .../TraversalEvaluation/TraversalParser.cs | 261 +++++++++++++++++++ .../TraversalPredicateParameter.cs | 60 +++++ .../TraversalTokenParameter.cs | 60 +++++ .../Gherkin/TraversalTranslations.cs | 102 -------- 12 files changed, 810 insertions(+), 110 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/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 50ea9b8..16aef9d 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs @@ -29,6 +29,7 @@ using System.Net.Http.Headers; using System.Runtime.CompilerServices; 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; @@ -67,6 +68,16 @@ namespace Gremlin.Net.IntegrationTest.Gherkin _result = enumerable.Cast<object>().ToArray(); } + [When("iterated next")] + public void IterateNext() + { + if (!(_traversal is ITraversal)) + { + throw new InvalidOperationException("Traversal should be set before iterating"); + } + _result = _traversal.Next(); + } + [Given("the traversal of")] public void TranslateTraversal(string traversalText) { @@ -74,7 +85,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin { throw new InvalidOperationException("g should be a traversal source"); } - _traversal = TraversalTranslations.GetTraversal(traversalText, _g); + _traversal = TraversalParser.GetTraversal(traversalText, _g); } [Then("the result should be (\\w+)")] http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/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 e1df3a8..7523b0b 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs @@ -24,6 +24,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; @@ -146,8 +147,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin WriteOutput($" Scenario: {resultScenario.Key.Name}"); foreach (var step in resultScenario.Key.Steps) { - Exception failure; - resultScenario.Value.TryGetValue(step, out failure); + resultScenario.Value.TryGetValue(step, out var failure); if (failure == null) { WriteOutput($" {step.Keyword} {step.Text}"); @@ -295,11 +295,42 @@ namespace Gremlin.Net.IntegrationTest.Gherkin private IEnumerable<Feature> GetFeatures() { - // TODO: go through all the .feature files - const string gherkinFile = "/Users/jorge/workspace/temp/count.feature"; - var parser = new Parser(); - var doc = parser.Parse(gherkinFile); - yield return doc.Feature; + var rootPath = GetRootPath(); + var path = Path.Combine(rootPath, "gremlin-test", "features"); + WriteOutput(path); + WriteOutput("------"); + + var files = new [] {"/Users/jorge/workspace/temp/count.feature"}; + //var files = Directory.GetFiles(path, "*.feature", SearchOption.AllDirectories); + foreach (var gherkinFile in files) + { + var parser = new Parser(); + WriteOutput("Parsing " + gherkinFile); + var doc = parser.Parse(gherkinFile); + yield return doc.Feature; + } + } + + private string GetRootPath() + { + var codeBaseUrl = new Uri(GetType().GetTypeInfo().Assembly.CodeBase); + var codeBasePath = Uri.UnescapeDataString(codeBaseUrl.AbsolutePath); + DirectoryInfo rootDir = null; + for (var dir = Directory.GetParent(Path.GetDirectoryName(codeBasePath)); + dir.Parent != null; + dir = dir.Parent) + { + if (dir.Name == "gremlin-dotnet" && dir.Parent?.Name == "tinkerpop") + { + rootDir = dir.Parent; + break; + } + } + if (rootDir == null) + { + throw new FileNotFoundException("tinkerpop root not found in path"); + } + return rootDir.FullName; } private void PrintGherkin() http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/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 new file mode 100644 index 0000000..1c940db --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/ITokenParameter.cs @@ -0,0 +1,30 @@ +#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.IntegrationTest.Gherkin.TraversalEvaluation +{ + public interface ITokenParameter + { + + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/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 new file mode 100644 index 0000000..9effc17 --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/NumericParameter.cs @@ -0,0 +1,68 @@ +#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; + +namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation +{ + public class NumericParameter<T> : ITokenParameter, IEquatable<NumericParameter<T>> where T : struct + { + public bool Equals(NumericParameter<T> other) + { + return Value.Equals(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((NumericParameter<T>) obj); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public T Value { get; } + + public NumericParameter(T value) + { + Value = value; + } + + public override string ToString() + { + return $"NumericParameter<{typeof(T).Name}>({Value})"; + } + } + + internal static class NumericParameter + { + public static NumericParameter<TType> Create<TType>(TType value) where TType : struct + { + return new NumericParameter<TType>(value); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/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 new file mode 100644 index 0000000..b054cbc --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StaticTraversalParameter.cs @@ -0,0 +1,57 @@ +#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; + +namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation +{ + internal class StaticTraversalParameter : ITokenParameter, IEquatable<StaticTraversalParameter> + { + public bool Equals(StaticTraversalParameter other) + { + return Parts.SequenceEqual(other.Parts); + } + + 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((StaticTraversalParameter) obj); + } + + public override int GetHashCode() + { + return Parts != null ? Parts.GetHashCode() : 0; + } + + public IList<Token> Parts { get; } + + public StaticTraversalParameter(IList<Token> parts) + { + Parts = parts; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/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 new file mode 100644 index 0000000..ad182b5 --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StringParameter.cs @@ -0,0 +1,69 @@ +#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; + +namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation +{ + internal class StringParameter : ITokenParameter, IEquatable<StringParameter> + { + public bool Equals(StringParameter other) + { + return string.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((StringParameter) obj); + } + + public override int GetHashCode() + { + return (Value != null ? Value.GetHashCode() : 0); + } + + public string Value { get; } + + public StringParameter(string value) + { + Value = value; + } + + public static StringParameter Parse(string text, ref int i) + { + i++; + var endIndex = text.IndexOf('"', i); + var result = new StringParameter(text.Substring(i, endIndex - i)); + i = endIndex; + return result; + } + + public override string ToString() + { + return $"{GetType().Name}({Value})"; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/Token.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/Token.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/Token.cs new file mode 100644 index 0000000..633e2b5 --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/Token.cs @@ -0,0 +1,76 @@ +#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; + +namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation +{ + internal class Token : IEquatable<Token> + { + private static readonly IList<ITokenParameter> EmptyParameters = new ITokenParameter[0]; + + public bool Equals(Token other) + { + if (!string.Equals(Name, other.Name)) + { + return false; + } + return (Parameters ?? EmptyParameters).SequenceEqual(other.Parameters ?? EmptyParameters); + } + + 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((Token) obj); + } + + public override int GetHashCode() + { + unchecked + { + return ((Name != null ? Name.GetHashCode() : 0) * 397) ^ + (Parameters != null ? Parameters.GetHashCode() : 0); + } + } + + public string Name { get; } + + public ICollection<ITokenParameter> Parameters { get; } + + public Token(string name, ICollection<ITokenParameter> parameters = null) + { + Name = name; + Parameters = parameters; + } + + public Token(string name, ITokenParameter parameter) + { + Name = name; + Parameters = new[] {parameter}; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/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 new file mode 100644 index 0000000..ea65cd2 --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs @@ -0,0 +1,79 @@ +#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.Diagnostics; +using System.Linq; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + +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_Parts() + { + var items = new[] + { + Tuple.Create("g.V().count()", new[] {new Token("count")}), + Tuple.Create("g.V().values(\"name\")", + 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().has(\"no\").count()", + new[] {new Token("has", new StringParameter("no")), new Token("count")}), + Tuple.Create("g.V().has(\"lang\", \"java\")", + new[] {new Token("has", new[] {new StringParameter("lang"), new StringParameter("java")})}), + Tuple.Create("g.V().where(__.in(\"knows\"))", + new[] {new Token("where", new[] {new StaticTraversalParameter( + new[] {new Token("__"), new Token("in", new StringParameter("knows"))})})}), + 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)) }) })}) + }; + foreach (var item in items) + { + var parts = TraversalParser.ParseParts(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); + } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/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 new file mode 100644 index 0000000..0ac97ff --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs @@ -0,0 +1,261 @@ +#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.Reflection; +using System.Text.RegularExpressions; +using Gremlin.Net.Process.Traversal; + +namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation +{ + public class TraversalParser + { + private static readonly IDictionary<string, Func<GraphTraversalSource, ITraversal>> FixedTranslations = + new Dictionary<string, Func<GraphTraversalSource, ITraversal>> + { + { "g.V().has(\"no\").count()", g => g.V().Has("no").Count() }, + { "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); + + internal static ITraversal GetTraversal(string traversalText, GraphTraversalSource g) + { + Func<GraphTraversalSource, ITraversal> traversalBuilder; + if (!FixedTranslations.TryGetValue(traversalText, out traversalBuilder)) + { + return BuildFromMethods(traversalText, g); + } + return traversalBuilder(g); + } + + private static ITraversal BuildFromMethods(string traversalText, GraphTraversalSource g) + { + var parts = traversalText.Split('.'); + if (parts[0] != "g") + { + throw BuildException(traversalText); + } + ITraversal traversal; + switch (parts[1]) + { + case "V()": + traversal = g.V(); + break; + case "E()": + traversal = g.E(); + break; + default: + throw BuildException(traversalText); + } + for (var i = 2; i < parts.Length; i++) + { + var name = GetCsharpName(parts[i], traversalText); + var method = traversal.GetType().GetMethod(name); + if (method == null) + { + throw new InvalidOperationException($"Traversal method '{parts[i]}' not found for testing"); + } + if (method.IsGenericMethod) + { + throw new InvalidOperationException( + $"Can not build traversal to test as '{name}()' method is generic"); + } + traversal = (ITraversal) method.Invoke(traversal, new object[] { new object[0]}); + } + return traversal; + } + + private static string GetCsharpName(string part, string traversalText) + { + if (!part.EndsWith("()")) + { + throw BuildException(traversalText); + } + // Transform to PascalCasing and remove the parenthesis + return char.ToUpper(part[0]) + part.Substring(1, part.Length - 3); + } + + private static Exception BuildException(string traversalText) + { + return new InvalidOperationException($"Can not build a traversal to test from '{traversalText}'"); + } + + internal static IList<Token> ParseParts(string traversalText) + { + var index = 0; + return ParseTokens(traversalText, ref index); + } + + private static IList<Token> ParseTokens(string text, ref int i) + { + var result = new List<Token>(); + var startIndex = i; + var parsing = ParsingPart.Name; + string name = null; + var parameters = new List<ITokenParameter>(); + while (i < text.Length) + { + switch (text[i]) + { + case '.': + if (name == null) + { + name = 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; + var param = ParseParameter(text, ref i); + if (param == null) + { + parsing = ParsingPart.EndParameters; + } + else + { + parameters.Add(param); + } + break; + } + case ',' when text[i+1] != ' ': + case ' ' when text[i+1] != ' ': + { + if (parsing != ParsingPart.StartParameters) + { + throw new InvalidOperationException( + "Can not parse space or comma chars outside parameters"); + } + i++; + var param = ParseParameter(text, ref i); + if (param == null) + { + parsing = ParsingPart.EndParameters; + } + else + { + parameters.Add(param); + } + break; + } + case ')' when parsing != ParsingPart.StartParameters: + // The traversal already ended + i--; + if (name != null) + { + result.Add(new Token(name, parameters)); + } + return result; + case ')': + parsing = ParsingPart.EndParameters; + break; + } + i++; + } + if (name != null) + { + result.Add(new Token(name, parameters)); + } + return result; + } + + private static ITokenParameter ParseParameter(string text, ref int i) + { + var firstChar = text[i]; + if (firstChar == ')') + { + return null; + } + if (firstChar == '"') + { + return StringParameter.Parse(text, ref i); + } + if (char.IsDigit(firstChar)) + { + return ParseNumber(text, ref i); + } + if (text.Substring(i, 3).StartsWith("__.")) + { + return new StaticTraversalParameter(ParseTokens(text, ref i)); + } + if (text.Substring(i, 2).StartsWith("T.")) + { + return new TraversalTokenParameter(ParseTokens(text, ref i)); + } + if (text.Substring(i, 2).StartsWith("P.")) + { + return new TraversalPredicateParameter(ParseTokens(text, ref i)); + } + return null; + } + + private static ITokenParameter ParseNumber(string text, ref int i) + { + var match = RegexLong.Match(text.Substring(i)); + if (match.Success) + { + i += match.Value.Length - 1; + return NumericParameter.Create(Convert.ToInt64(match.Value.Substring(0, match.Value.Length-1))); + } + match = RegexFloat.Match(text.Substring(i)); + if (match.Success) + { + i += match.Value.Length - 1; + return NumericParameter.Create(Convert.ToSingle(match.Value.Substring(0, match.Value.Length-1))); + } + match = RegexDouble.Match(text.Substring(i)); + if (match.Success) + { + i += match.Value.Length; + return NumericParameter.Create(Convert.ToSingle(match.Value)); + } + match = RegexInteger.Match(text.Substring(i)); + if (!match.Success) + { + throw new InvalidOperationException( + $"Could not parse numeric value from the beginning of {text.Substring(i)}"); + } + i += match.Value.Length; + return NumericParameter.Create(Convert.ToInt32(match.Value)); + } + + private enum ParsingPart + { + Name, + StartParameters, + EndParameters + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/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 new file mode 100644 index 0000000..d9bf061 --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalPredicateParameter.cs @@ -0,0 +1,60 @@ +#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; + +namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation +{ + /// <summary> + /// Represents a parameter for a traversal predicate (ie: P.gt()) + /// </summary> + internal class TraversalPredicateParameter : ITokenParameter, IEquatable<TraversalPredicateParameter> + { + public bool Equals(TraversalPredicateParameter other) + { + return Parts.SequenceEqual(other.Parts); + } + + 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((TraversalPredicateParameter) obj); + } + + public override int GetHashCode() + { + return Parts != null ? Parts.GetHashCode() : 0; + } + + public IList<Token> Parts { get; } + + public TraversalPredicateParameter(IList<Token> parts) + { + Parts = parts; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalTokenParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalTokenParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalTokenParameter.cs new file mode 100644 index 0000000..5a5e8c1 --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalTokenParameter.cs @@ -0,0 +1,60 @@ +#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; + +namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation +{ + /// <summary> + /// Represents a parameter for a traversal token (ie: T.label) + /// </summary> + internal class TraversalTokenParameter : ITokenParameter, IEquatable<TraversalTokenParameter> + { + public bool Equals(TraversalTokenParameter other) + { + return Parts.SequenceEqual(other.Parts); + } + + 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((TraversalTokenParameter) obj); + } + + public override int GetHashCode() + { + return Parts != null ? Parts.GetHashCode() : 0; + } + + public IList<Token> Parts { get; } + + public TraversalTokenParameter(IList<Token> parts) + { + Parts = parts; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/6d8ff7d7/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalTranslations.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalTranslations.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalTranslations.cs deleted file mode 100644 index af02b8c..0000000 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalTranslations.cs +++ /dev/null @@ -1,102 +0,0 @@ -#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.Reflection; -using Gremlin.Net.Process.Traversal; - -namespace Gremlin.Net.IntegrationTest.Gherkin -{ - internal class TraversalTranslations - { - private static readonly IDictionary<string, Func<GraphTraversalSource, ITraversal>> FixedTranslations = - new Dictionary<string, Func<GraphTraversalSource, ITraversal>> - { - { "g.V().has(\"no\").count()", g => g.V().Has("no").Count() }, - { "g.V().fold().count(Scope.local)", g => g.V().Fold<object>().Count(Scope.Local)} - }; - - internal static ITraversal GetTraversal(string traversalText, GraphTraversalSource g) - { - Func<GraphTraversalSource, ITraversal> traversalBuilder; - if (!FixedTranslations.TryGetValue(traversalText, out traversalBuilder)) - { - return BuildFromMethods(traversalText, g); - } - return traversalBuilder(g); - } - - private static ITraversal BuildFromMethods(string traversalText, GraphTraversalSource g) - { - var parts = traversalText.Split('.'); - if (parts[0] != "g") - { - throw BuildException(traversalText); - } - ITraversal traversal; - switch (parts[1]) - { - case "V()": - traversal = g.V(); - break; - case "E()": - traversal = g.E(); - break; - default: - throw BuildException(traversalText); - } - for (var i = 2; i < parts.Length; i++) - { - var name = GetCsharpName(parts[i], traversalText); - var method = traversal.GetType().GetMethod(name); - if (method == null) - { - throw new InvalidOperationException($"Traversal method '{parts[i]}' not found for testing"); - } - if (method.IsGenericMethod) - { - throw new InvalidOperationException( - $"Can not build traversal to test as '{name}()' method is generic"); - } - traversal = (ITraversal) method.Invoke(traversal, new object[] { new object[0]}); - } - return traversal; - } - - private static string GetCsharpName(string part, string traversalText) - { - if (!part.EndsWith("()")) - { - throw BuildException(traversalText); - } - // Transform to PascalCasing and remove the parenthesis - return char.ToUpper(part[0]) + part.Substring(1, part.Length - 3); - } - - private static Exception BuildException(string traversalText) - { - return new InvalidOperationException($"Can not build a traversal to test from '{traversalText}'"); - } - } -} \ No newline at end of file