[ 
https://issues.apache.org/jira/browse/TINKERPOP-2452?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17282466#comment-17282466
 ] 

ASF GitHub Bot commented on TINKERPOP-2452:
-------------------------------------------

FlorianHockmann commented on a change in pull request #1387:
URL: https://github.com/apache/tinkerpop/pull/1387#discussion_r573571605



##########
File path: docs/src/dev/developer/for-committers.asciidoc
##########
@@ -251,7 +251,7 @@ The basic syntax of a Gherkin test is as follows:
 ----
 Scenario: g_VX1X_unionXrepeatXoutX_timesX2X__outX_name
   Given the modern graph
-  And using the parameter v1Id defined as "v[marko].id"
+  And using the parameter vId1 defined as "v[marko].id"

Review comment:
       Just a nit, but shouldn't this be `vid1` with a lower case _i_ as 
described below in line 366? (Same below in lines 320 and 323)

##########
File path: docs/src/upgrade/release-3.5.x.asciidoc
##########
@@ -121,6 +121,29 @@ 
System.out.println(javascriptTranslator.translate(t).getScript());
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-2451[TINKERPOP-2451]
 
+==== DotNetTranslator
+
+Introduced a `DotNetTranslator` for Java, which is in `gremlin-core` with the 
other language translators. It
+generates a C# representation of Gremlin from bytecode.
+
+[source,java]
+----
+// gremlin-core module
+import 
org.apache.tinkerpop.gremlin.process.traversal.translator.DotNetTranslator;
+
+GraphTraversalSource g = ...;
+Traversal<Vertex,Integer> t = g.V().has("person","name","marko").
+                                where(in("knows")).
+                                values("age").
+                                map(Lambda.function("it.get() + 1"));
+
+Translator.ScriptTranslator dotnetTranslator = DotNetTranslator.of("g");
+System.out.println(dotnetTranslator.translate(t).getScript());
+// OUTPUT: 
g.V().Has("person","name","marko").Where(__.In("knows")).Values<object>("age").Map<object>(()
 => "it.get() + 1")

Review comment:
       ```c#
   Map<object>(() => "it.get() + 1")
   ```
   
   Should be:
   
   ```c#
   Map<object>(Lambda.Groovy("it.get() + 1"))
   ```
   
   Your solution is a valid C# lambda but we unfortunately don't support that 
right now in Gremlin.Net. Lambdas can currently only provided as strings via 
`Lambda.Groovy()` or `Lambda.Python()`.
   Although I think that it would be nice to support real C# lambdas like the 
translator currently produces.

##########
File path: 
gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslatorTest.java
##########
@@ -0,0 +1,214 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Order;
+import org.apache.tinkerpop.gremlin.process.traversal.Pop;
+import org.apache.tinkerpop.gremlin.process.traversal.Scope;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
+import org.apache.tinkerpop.gremlin.structure.Column;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.UUID;
+
+import static 
org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal;
+import static org.apache.tinkerpop.gremlin.process.traversal.Order.asc;
+import static 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.hasLabel;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class DotNetTranslatorTest {
+    private static final GraphTraversalSource g = 
traversal().withEmbedded(EmptyGraph.instance());
+    private static final Translator.ScriptTranslator translator = 
DotNetTranslator.of("g");
+
+    @Test
+    public void shouldTranslateStrategies() throws Exception {
+        assertEquals("g.WithStrategies(new ReadOnlyStrategy()," +
+                        "new SubgraphStrategy(configuration: new 
Dictionary<string,dynamic> 
{{\"checkAdjacentVertices\",false},{\"vertices\",__.HasLabel(\"person\")}})).V().Has(\"name\")",
+                
translator.translate(g.withStrategies(ReadOnlyStrategy.instance(),
+                        
SubgraphStrategy.build().checkAdjacentVertices(false).vertices(hasLabel("person")).create()).
+                        V().has("name").asAdmin().getBytecode()).getScript());
+    }
+
+    @Test
+    public void shouldTranslateMaps() {
+        final String script = translator.translate(g.V().id().is(new 
LinkedHashMap<Object,Object>() {{
+            put(3, "32");
+            put(Arrays.asList(1, 2, 3.1d), 4);
+        }}).asAdmin().getBytecode()).getScript();
+        assertEquals("g.V().Id().Is(new Dictionary<object,object> 
{{3,\"32\"},{new List<object> {1, 2, 3.1},4}})", script);
+    }
+
+    @Test
+    public void shouldTranslateValues() {
+        final String script = 
translator.translate(g.V().values("name").asAdmin().getBytecode()).getScript();
+        assertEquals("g.V().Values<object>(\"name\")", script);
+    }
+
+    @Test
+    public void shouldTranslateValue() {
+        final String script = 
translator.translate(g.V().properties().order().by(T.value, 
asc).value().asAdmin().getBytecode()).getScript();
+        
assertEquals("g.V().Properties<object>().Order().By(T.Value,Order.Asc).Value<object>()",
 script);
+    }
+
+    @Test
+    public void shouldTranslateInject() {
+        final String script = 
translator.translate(g.inject(10,20,null,20,10,10).asAdmin().getBytecode()).getScript();
+        assertEquals("g.Inject<object>(10,20,null,20,10,10)", script);
+    }
+
+    @Test
+    public void shouldTranslateGroup() {
+        final String script = 
translator.translate(g.V().group("x").group().by("name").asAdmin().getBytecode()).getScript();
+        assertEquals("g.V().Group(\"x\").Group<object,object>().By(\"name\")", 
script);
+    }
+
+    @Test
+    public void shouldTranslateGroupCount() {
+        final String script = 
translator.translate(g.V().groupCount("x").groupCount().by("name").asAdmin().getBytecode()).getScript();
+        
assertEquals("g.V().GroupCount(\"x\").GroupCount<object>().By(\"name\")", 
script);
+    }
+
+    @Test
+    public void shouldTranslateDate() {
+        final Calendar c = Calendar.getInstance();
+        c.set(1975, Calendar.SEPTEMBER, 7);
+        final Date d = c.getTime();
+        
assertTranslation(String.format("DateTimeOffset.FromUnixTimeMillisecond(%s)", 
d.getTime()), d);

Review comment:
       Again _FromUnixTimeMillisecond**s**_

##########
File path: 
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
##########
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.commons.configuration2.ConfigurationConverter;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Script;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Converts bytecode to a C# string of Gremlin.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DotNetTranslator implements Translator.ScriptTranslator {
+
+    private final String traversalSource;
+    private final TypeTranslator typeTranslator;
+
+    private static final List<String> methodsWithArgsNotNeedingGeneric = 
Arrays.asList(GraphTraversal.Symbols.group,
+            GraphTraversal.Symbols.groupCount, GraphTraversal.Symbols.sack);
+
+    private DotNetTranslator(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
+    }
+
+    /**
+     * Creates the translator with a {@code false} argument to {@code 
withParameters} using
+     * {@link #of(String, boolean)}.
+     */
+    public static DotNetTranslator of(final String traversalSource) {
+        return of(traversalSource, false);
+    }
+
+    /**
+     * Creates the translator with the {@link DefaultTypeTranslator} passing 
the {@code withParameters} option to it
+     * which will handle type translation in a fashion that should typically 
increase cache hits and reduce
+     * compilation times if enabled at the sacrifice to rewriting of the 
script that could reduce readability.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
boolean withParameters) {
+        return of(traversalSource, new DefaultTypeTranslator(withParameters));
+    }
+
+    /**
+     * Creates the translator with a custom {@link TypeTranslator} instance.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        return new DotNetTranslator(traversalSource, typeTranslator);
+    }
+
+    @Override
+    public Script translate(final Bytecode bytecode) {
+        return typeTranslator.apply(traversalSource, bytecode);
+    }
+
+    @Override
+    public String getTargetLanguage() {
+        return "gremlin-dotnet";
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.translatorString(this);
+    }
+
+    @Override
+    public String getTraversalSource() {
+        return this.traversalSource;
+    }
+
+    /**
+     * Performs standard type translation for the TinkerPop types to C#.
+     */
+    public static class DefaultTypeTranslator extends AbstractTypeTranslator {
+
+        public DefaultTypeTranslator(final boolean withParameters) {
+            super(withParameters);
+        }
+
+        @Override
+        protected String getNullSyntax() {
+            return "null";
+        }
+
+        @Override
+        protected String getSyntax(final String o) {
+            return "\"" + StringEscapeUtils.escapeJava(o) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final Boolean o) {
+            return o.toString();
+        }
+
+        @Override
+        protected String getSyntax(final Date o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final Timestamp o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final UUID o) {
+            return "new Guid(\"" + o.toString() + "\")";
+        }
+
+        @Override
+        protected String getSyntax(final Lambda o) {
+            return "() => \"" + 
StringEscapeUtils.escapeEcmaScript(o.getLambdaScript().trim()) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final SackFunctions.Barrier o) {
+            return "Barrier." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final VertexProperty.Cardinality o) {
+            return "Cardinality." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final TraversalOptionParent.Pick o) {
+            return "Pick." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final Number o) {
+            return o.toString();
+        }
+
+        @Override
+        protected Script produceScript(final Set<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new HashSet<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final List<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new List<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Map<?, ?> o) {
+            script.append("new Dictionary<object,object> {");
+            produceKeyValuesForMap(o);
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Class<?> o) {
+            return script.append(o.getCanonicalName());
+        }
+
+        @Override
+        protected Script produceScript(final Enum<?> o) {
+            final String e = o instanceof Direction || o instanceof T ?
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1).toLowerCase() :
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1);
+            return script.append(o.getDeclaringClass().getSimpleName() + "." + 
e);
+        }
+
+        @Override
+        protected Script produceScript(final Vertex o) {
+            script.append("new Vertex(");
+            convertToScript(o.id());
+            script.append(",");
+            convertToScript(o.label());
+            return script.append(")");
+        }
+
+        @Override
+        protected Script produceScript(final Edge o) {
+            script.append("new Edge(");
+            convertToScript(o.id());
+            script.append(", new Vertex(");
+            convertToScript(o.outVertex().id());
+            script.append(",");
+            convertToScript(o.outVertex().label());
+            script.append("),");
+            convertToScript(o.label());
+            script.append(", new Vertex(");
+            convertToScript(o.inVertex().id());
+            script.append(",");
+            convertToScript(o.inVertex().label());
+            return script.append("))");
+        }
+
+        @Override
+        protected Script produceScript(final VertexProperty<?> o) {
+            script.append("new Property(");
+            convertToScript(o.id());
+            script.append(",");
+            convertToScript(o.label());
+            script.append(",");
+            convertToScript(o.value());
+            script.append(",");
+            return script.append("null)");
+        }
+
+        @Override
+        protected Script produceScript(final TraversalStrategyProxy<?> o) {
+            if (o.getConfiguration().isEmpty()) {
+                return script.append("new " + 
o.getStrategyClass().getSimpleName() + "()");
+            } else {
+                script.append("new " + o.getStrategyClass().getSimpleName() + 
"(configuration: ");
+                script.append("new Dictionary<string,dynamic> {");
+                
produceKeyValuesForMap(ConfigurationConverter.getMap(o.getConfiguration()));
+                script.append("}");
+
+                return script.append(")");
+            }
+        }
+
+        private Script produceKeyValuesForMap(final Map<?,?> m) {
+            final Iterator<? extends Map.Entry<?, ?>> itty = 
m.entrySet().iterator();
+            while (itty.hasNext()) {
+                final Map.Entry<?,?> entry = itty.next();
+                script.append("{");
+                convertToScript(entry.getKey());
+                script.append(",");

Review comment:
       (nitpick) appending `", "` looks nicer in C# and adheres to common C# 
style guidelines

##########
File path: 
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
##########
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.commons.configuration2.ConfigurationConverter;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Script;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Converts bytecode to a C# string of Gremlin.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DotNetTranslator implements Translator.ScriptTranslator {
+
+    private final String traversalSource;
+    private final TypeTranslator typeTranslator;
+
+    private static final List<String> methodsWithArgsNotNeedingGeneric = 
Arrays.asList(GraphTraversal.Symbols.group,
+            GraphTraversal.Symbols.groupCount, GraphTraversal.Symbols.sack);
+
+    private DotNetTranslator(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
+    }
+
+    /**
+     * Creates the translator with a {@code false} argument to {@code 
withParameters} using
+     * {@link #of(String, boolean)}.
+     */
+    public static DotNetTranslator of(final String traversalSource) {
+        return of(traversalSource, false);
+    }
+
+    /**
+     * Creates the translator with the {@link DefaultTypeTranslator} passing 
the {@code withParameters} option to it
+     * which will handle type translation in a fashion that should typically 
increase cache hits and reduce
+     * compilation times if enabled at the sacrifice to rewriting of the 
script that could reduce readability.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
boolean withParameters) {
+        return of(traversalSource, new DefaultTypeTranslator(withParameters));
+    }
+
+    /**
+     * Creates the translator with a custom {@link TypeTranslator} instance.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        return new DotNetTranslator(traversalSource, typeTranslator);
+    }
+
+    @Override
+    public Script translate(final Bytecode bytecode) {
+        return typeTranslator.apply(traversalSource, bytecode);
+    }
+
+    @Override
+    public String getTargetLanguage() {
+        return "gremlin-dotnet";
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.translatorString(this);
+    }
+
+    @Override
+    public String getTraversalSource() {
+        return this.traversalSource;
+    }
+
+    /**
+     * Performs standard type translation for the TinkerPop types to C#.
+     */
+    public static class DefaultTypeTranslator extends AbstractTypeTranslator {
+
+        public DefaultTypeTranslator(final boolean withParameters) {
+            super(withParameters);
+        }
+
+        @Override
+        protected String getNullSyntax() {
+            return "null";
+        }
+
+        @Override
+        protected String getSyntax(final String o) {
+            return "\"" + StringEscapeUtils.escapeJava(o) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final Boolean o) {
+            return o.toString();
+        }
+
+        @Override
+        protected String getSyntax(final Date o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final Timestamp o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final UUID o) {
+            return "new Guid(\"" + o.toString() + "\")";
+        }
+
+        @Override
+        protected String getSyntax(final Lambda o) {
+            return "() => \"" + 
StringEscapeUtils.escapeEcmaScript(o.getLambdaScript().trim()) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final SackFunctions.Barrier o) {
+            return "Barrier." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final VertexProperty.Cardinality o) {
+            return "Cardinality." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final TraversalOptionParent.Pick o) {
+            return "Pick." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final Number o) {
+            return o.toString();
+        }
+
+        @Override
+        protected Script produceScript(final Set<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new HashSet<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final List<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new List<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Map<?, ?> o) {
+            script.append("new Dictionary<object,object> {");
+            produceKeyValuesForMap(o);
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Class<?> o) {
+            return script.append(o.getCanonicalName());
+        }
+
+        @Override
+        protected Script produceScript(final Enum<?> o) {
+            final String e = o instanceof Direction || o instanceof T ?
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1).toLowerCase() :
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1);
+            return script.append(o.getDeclaringClass().getSimpleName() + "." + 
e);
+        }
+
+        @Override
+        protected Script produceScript(final Vertex o) {
+            script.append("new Vertex(");
+            convertToScript(o.id());
+            script.append(",");
+            convertToScript(o.label());
+            return script.append(")");
+        }
+
+        @Override
+        protected Script produceScript(final Edge o) {
+            script.append("new Edge(");
+            convertToScript(o.id());
+            script.append(", new Vertex(");
+            convertToScript(o.outVertex().id());
+            script.append(",");
+            convertToScript(o.outVertex().label());
+            script.append("),");
+            convertToScript(o.label());
+            script.append(", new Vertex(");
+            convertToScript(o.inVertex().id());
+            script.append(",");
+            convertToScript(o.inVertex().label());
+            return script.append("))");
+        }
+
+        @Override
+        protected Script produceScript(final VertexProperty<?> o) {
+            script.append("new Property(");
+            convertToScript(o.id());
+            script.append(",");
+            convertToScript(o.label());
+            script.append(",");
+            convertToScript(o.value());
+            script.append(",");
+            return script.append("null)");
+        }
+
+        @Override
+        protected Script produceScript(final TraversalStrategyProxy<?> o) {
+            if (o.getConfiguration().isEmpty()) {
+                return script.append("new " + 
o.getStrategyClass().getSimpleName() + "()");
+            } else {
+                script.append("new " + o.getStrategyClass().getSimpleName() + 
"(configuration: ");
+                script.append("new Dictionary<string,dynamic> {");
+                
produceKeyValuesForMap(ConfigurationConverter.getMap(o.getConfiguration()));
+                script.append("}");
+
+                return script.append(")");
+            }
+        }
+
+        private Script produceKeyValuesForMap(final Map<?,?> m) {
+            final Iterator<? extends Map.Entry<?, ?>> itty = 
m.entrySet().iterator();
+            while (itty.hasNext()) {
+                final Map.Entry<?,?> entry = itty.next();
+                script.append("{");
+                convertToScript(entry.getKey());
+                script.append(",");
+                convertToScript(entry.getValue());
+                script.append("}");
+                if (itty.hasNext())
+                    script.append(",");
+            }
+            return script;
+        }
+
+        @Override
+        protected Script produceScript(final String traversalSource, final 
Bytecode o) {
+            script.append(traversalSource);
+            for (final Bytecode.Instruction instruction : o.getInstructions()) 
{
+                final String methodName = instruction.getOperator();
+                // perhaps too many if/then conditions for specifying 
generics. doesnt' seem like there is a clear
+                // way to refactor this more nicely though.
+                if (0 == instruction.getArguments().length) {
+                    if (methodName.equals(GraphTraversal.Symbols.fold) && 
o.getSourceInstructions().size() + o.getStepInstructions().size() > 1)
+                        
script.append(".").append(resolveSymbol(methodName).replace("<object>", 
"")).append("()");
+                    else
+                        
script.append(".").append(resolveSymbol(methodName)).append("()");
+                } else {
+                    if (methodsWithArgsNotNeedingGeneric.contains(methodName) 
||
+                            (methodName.equals(GraphTraversal.Symbols.inject) 
&& Arrays.stream(instruction.getArguments()).noneMatch(Objects::isNull)))
+                        
script.append(".").append(resolveSymbol(methodName).replace("<object>", 
"").replace("<object,object>", "")).append("(");
+                    else
+                        
script.append(".").append(resolveSymbol(methodName)).append("(");
+
+                    // have to special case withSack() because UnaryOperator 
and BinaryOperator signatures
+                    // make it impossible for the interpreter to figure out 
which function to call. specifically we need
+                    // to discern between:
+                    //     withSack(A initialValue, UnaryOperator<A> 
splitOperator)
+                    //     withSack(A initialValue, BinaryOperator<A> 
splitOperator)
+                    // and:
+                    //     withSack(Supplier<A> initialValue, UnaryOperator<A> 
mergeOperator)
+                    //     withSack(Supplier<A> initialValue, 
BinaryOperator<A> mergeOperator)
+                    if (methodName.equals(TraversalSource.Symbols.withSack) &&
+                            instruction.getArguments().length == 2 && 
instruction.getArguments()[1] instanceof Lambda) {
+                        final String castFirstArgTo = 
instruction.getArguments()[0] instanceof Lambda ? "ISupplier" : "";
+                        final Lambda secondArg = (Lambda) 
instruction.getArguments()[1];
+                        final String castSecondArgTo = 
secondArg.getLambdaArguments() == 1 ? "IUnaryOperator" : "IBinaryOperator";
+                        if (!castFirstArgTo.isEmpty())
+                            script.append(String.format("(%s) ", 
castFirstArgTo));
+                        convertToScript(instruction.getArguments()[0]);
+                        script.append(", (").append(castSecondArgTo).append(") 
");
+                        convertToScript(instruction.getArguments()[1]);
+                        script.append(",");
+                    } else {
+                        for (final Object object : instruction.getArguments()) 
{
+                            // overloads might have trouble with null. add 
more as we find them i guess
+                            if (null == object && 
methodName.equals(GraphTraversal.Symbols.addV))
+                                script.append("(string) ");
+                            convertToScript(object);
+                            script.append(",");
+                        }
+                    }
+                    script.setCharAtEnd(')');
+                }
+            }
+            return script;
+        }
+
+        @Override
+        protected Script produceScript(final P<?> p) {
+            if (p instanceof TextP) {
+                
script.append("TextP.").append(SymbolHelper.toCSharp(p.getBiPredicate().toString())).append("(");
+                convertToScript(p.getValue());
+            } else if (p instanceof ConnectiveP) {
+                final List<P<?>> list = ((ConnectiveP) p).getPredicates();
+                for (int i = 0; i < list.size(); i++) {
+                    produceScript(list.get(i));
+                    if (i < list.size() - 1) {
+                        script.append(p instanceof OrP ? ".Or(" : ".And(");

Review comment:
       The bracket from `Or(` / `And(` isn't closed anywhere if I'm not 
mistaken. The output at least has a a missing bracket when I combine multiple 
_Ands_:
   
   ```groovy
   gremlin> translator.translate(g.V().has('age', 
gt(1).and(gt(2)).and(gt(3)))).getScript()
   ==>g.V().Has("age",P.Gt(1).And(P.Gt(2).And(P.Gt(3)))
   ```

##########
File path: docs/src/dev/developer/for-committers.asciidoc
##########
@@ -356,6 +356,19 @@ edges, maps, and any other available type.
 include the `.id` suffix which would indicate getting the vertex identifier or 
the `.sid` suffix which gets a string
 representation of the edge identifier.
 
+In addition, parameter names should adhere to a common form as they hold some 
meaning to certain language variant
+implementations:
+
+* General variables of no particular type should use `xx1`, `xx2` and `xx3`.
+* A `Vertex` variable should be prefixed with "v" and be followed by the `id`, 
therefore, `v1` would signify a `Vertex`
+with the `id` of "1".
+* An `Edge` variable follows the pattern of vertices but with a "e" prefix.
+* The "id" of a `Vertex` or `Edge` is prefixed with "vid"`" or "eid" 
respectively followed by the `id`, thus, `vid1`
+would be "1" and refer to the `Vertex` with that `id`.
+* `Function` variables should use 'l1' and `l2`.

Review comment:
       _'l1'_ -> _\`l1\`_ (backticks)

##########
File path: 
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
##########
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.commons.configuration2.ConfigurationConverter;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Script;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Converts bytecode to a C# string of Gremlin.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DotNetTranslator implements Translator.ScriptTranslator {
+
+    private final String traversalSource;
+    private final TypeTranslator typeTranslator;
+
+    private static final List<String> methodsWithArgsNotNeedingGeneric = 
Arrays.asList(GraphTraversal.Symbols.group,
+            GraphTraversal.Symbols.groupCount, GraphTraversal.Symbols.sack);
+
+    private DotNetTranslator(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
+    }
+
+    /**
+     * Creates the translator with a {@code false} argument to {@code 
withParameters} using
+     * {@link #of(String, boolean)}.
+     */
+    public static DotNetTranslator of(final String traversalSource) {
+        return of(traversalSource, false);
+    }
+
+    /**
+     * Creates the translator with the {@link DefaultTypeTranslator} passing 
the {@code withParameters} option to it
+     * which will handle type translation in a fashion that should typically 
increase cache hits and reduce
+     * compilation times if enabled at the sacrifice to rewriting of the 
script that could reduce readability.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
boolean withParameters) {
+        return of(traversalSource, new DefaultTypeTranslator(withParameters));
+    }
+
+    /**
+     * Creates the translator with a custom {@link TypeTranslator} instance.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        return new DotNetTranslator(traversalSource, typeTranslator);
+    }
+
+    @Override
+    public Script translate(final Bytecode bytecode) {
+        return typeTranslator.apply(traversalSource, bytecode);
+    }
+
+    @Override
+    public String getTargetLanguage() {
+        return "gremlin-dotnet";
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.translatorString(this);
+    }
+
+    @Override
+    public String getTraversalSource() {
+        return this.traversalSource;
+    }
+
+    /**
+     * Performs standard type translation for the TinkerPop types to C#.
+     */
+    public static class DefaultTypeTranslator extends AbstractTypeTranslator {
+
+        public DefaultTypeTranslator(final boolean withParameters) {
+            super(withParameters);
+        }
+
+        @Override
+        protected String getNullSyntax() {
+            return "null";
+        }
+
+        @Override
+        protected String getSyntax(final String o) {
+            return "\"" + StringEscapeUtils.escapeJava(o) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final Boolean o) {
+            return o.toString();
+        }
+
+        @Override
+        protected String getSyntax(final Date o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final Timestamp o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";

Review comment:
       same here

##########
File path: 
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
##########
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.commons.configuration2.ConfigurationConverter;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Script;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Converts bytecode to a C# string of Gremlin.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DotNetTranslator implements Translator.ScriptTranslator {
+
+    private final String traversalSource;
+    private final TypeTranslator typeTranslator;
+
+    private static final List<String> methodsWithArgsNotNeedingGeneric = 
Arrays.asList(GraphTraversal.Symbols.group,
+            GraphTraversal.Symbols.groupCount, GraphTraversal.Symbols.sack);
+
+    private DotNetTranslator(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
+    }
+
+    /**
+     * Creates the translator with a {@code false} argument to {@code 
withParameters} using
+     * {@link #of(String, boolean)}.
+     */
+    public static DotNetTranslator of(final String traversalSource) {
+        return of(traversalSource, false);
+    }
+
+    /**
+     * Creates the translator with the {@link DefaultTypeTranslator} passing 
the {@code withParameters} option to it
+     * which will handle type translation in a fashion that should typically 
increase cache hits and reduce
+     * compilation times if enabled at the sacrifice to rewriting of the 
script that could reduce readability.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
boolean withParameters) {
+        return of(traversalSource, new DefaultTypeTranslator(withParameters));
+    }
+
+    /**
+     * Creates the translator with a custom {@link TypeTranslator} instance.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        return new DotNetTranslator(traversalSource, typeTranslator);
+    }
+
+    @Override
+    public Script translate(final Bytecode bytecode) {
+        return typeTranslator.apply(traversalSource, bytecode);
+    }
+
+    @Override
+    public String getTargetLanguage() {
+        return "gremlin-dotnet";
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.translatorString(this);
+    }
+
+    @Override
+    public String getTraversalSource() {
+        return this.traversalSource;
+    }
+
+    /**
+     * Performs standard type translation for the TinkerPop types to C#.
+     */
+    public static class DefaultTypeTranslator extends AbstractTypeTranslator {
+
+        public DefaultTypeTranslator(final boolean withParameters) {
+            super(withParameters);
+        }
+
+        @Override
+        protected String getNullSyntax() {
+            return "null";
+        }
+
+        @Override
+        protected String getSyntax(final String o) {
+            return "\"" + StringEscapeUtils.escapeJava(o) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final Boolean o) {
+            return o.toString();
+        }
+
+        @Override
+        protected String getSyntax(final Date o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final Timestamp o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final UUID o) {
+            return "new Guid(\"" + o.toString() + "\")";
+        }
+
+        @Override
+        protected String getSyntax(final Lambda o) {
+            return "() => \"" + 
StringEscapeUtils.escapeEcmaScript(o.getLambdaScript().trim()) + "\"";

Review comment:
       I guess this needs to be changed to:
   
   ```c#
   return "Lambda.Groovy(\"" + 
StringEscapeUtils.escapeEcmaScript(o.getLambdaScript().trim()) + "\")";
   ```
   
   If we also want to support Python lambdas here, then it would probably best 
to check the language here and then use `Lambda.Python` instead if it's Python.

##########
File path: 
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
##########
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.commons.configuration2.ConfigurationConverter;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Script;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Converts bytecode to a C# string of Gremlin.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DotNetTranslator implements Translator.ScriptTranslator {
+
+    private final String traversalSource;
+    private final TypeTranslator typeTranslator;
+
+    private static final List<String> methodsWithArgsNotNeedingGeneric = 
Arrays.asList(GraphTraversal.Symbols.group,
+            GraphTraversal.Symbols.groupCount, GraphTraversal.Symbols.sack);
+
+    private DotNetTranslator(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
+    }
+
+    /**
+     * Creates the translator with a {@code false} argument to {@code 
withParameters} using
+     * {@link #of(String, boolean)}.
+     */
+    public static DotNetTranslator of(final String traversalSource) {
+        return of(traversalSource, false);
+    }
+
+    /**
+     * Creates the translator with the {@link DefaultTypeTranslator} passing 
the {@code withParameters} option to it
+     * which will handle type translation in a fashion that should typically 
increase cache hits and reduce
+     * compilation times if enabled at the sacrifice to rewriting of the 
script that could reduce readability.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
boolean withParameters) {
+        return of(traversalSource, new DefaultTypeTranslator(withParameters));
+    }
+
+    /**
+     * Creates the translator with a custom {@link TypeTranslator} instance.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        return new DotNetTranslator(traversalSource, typeTranslator);
+    }
+
+    @Override
+    public Script translate(final Bytecode bytecode) {
+        return typeTranslator.apply(traversalSource, bytecode);
+    }
+
+    @Override
+    public String getTargetLanguage() {
+        return "gremlin-dotnet";
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.translatorString(this);
+    }
+
+    @Override
+    public String getTraversalSource() {
+        return this.traversalSource;
+    }
+
+    /**
+     * Performs standard type translation for the TinkerPop types to C#.
+     */
+    public static class DefaultTypeTranslator extends AbstractTypeTranslator {
+
+        public DefaultTypeTranslator(final boolean withParameters) {
+            super(withParameters);
+        }
+
+        @Override
+        protected String getNullSyntax() {
+            return "null";
+        }
+
+        @Override
+        protected String getSyntax(final String o) {
+            return "\"" + StringEscapeUtils.escapeJava(o) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final Boolean o) {
+            return o.toString();
+        }
+
+        @Override
+        protected String getSyntax(final Date o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";

Review comment:
       _FromUnixTimeMillisecond_ -> _FromUnixTimeMillisecond**s**_ 

##########
File path: 
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
##########
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.commons.configuration2.ConfigurationConverter;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Script;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Converts bytecode to a C# string of Gremlin.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DotNetTranslator implements Translator.ScriptTranslator {
+
+    private final String traversalSource;
+    private final TypeTranslator typeTranslator;
+
+    private static final List<String> methodsWithArgsNotNeedingGeneric = 
Arrays.asList(GraphTraversal.Symbols.group,
+            GraphTraversal.Symbols.groupCount, GraphTraversal.Symbols.sack);
+
+    private DotNetTranslator(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
+    }
+
+    /**
+     * Creates the translator with a {@code false} argument to {@code 
withParameters} using
+     * {@link #of(String, boolean)}.
+     */
+    public static DotNetTranslator of(final String traversalSource) {
+        return of(traversalSource, false);
+    }
+
+    /**
+     * Creates the translator with the {@link DefaultTypeTranslator} passing 
the {@code withParameters} option to it
+     * which will handle type translation in a fashion that should typically 
increase cache hits and reduce
+     * compilation times if enabled at the sacrifice to rewriting of the 
script that could reduce readability.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
boolean withParameters) {
+        return of(traversalSource, new DefaultTypeTranslator(withParameters));
+    }
+
+    /**
+     * Creates the translator with a custom {@link TypeTranslator} instance.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        return new DotNetTranslator(traversalSource, typeTranslator);
+    }
+
+    @Override
+    public Script translate(final Bytecode bytecode) {
+        return typeTranslator.apply(traversalSource, bytecode);
+    }
+
+    @Override
+    public String getTargetLanguage() {
+        return "gremlin-dotnet";
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.translatorString(this);
+    }
+
+    @Override
+    public String getTraversalSource() {
+        return this.traversalSource;
+    }
+
+    /**
+     * Performs standard type translation for the TinkerPop types to C#.
+     */
+    public static class DefaultTypeTranslator extends AbstractTypeTranslator {
+
+        public DefaultTypeTranslator(final boolean withParameters) {
+            super(withParameters);
+        }
+
+        @Override
+        protected String getNullSyntax() {
+            return "null";
+        }
+
+        @Override
+        protected String getSyntax(final String o) {
+            return "\"" + StringEscapeUtils.escapeJava(o) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final Boolean o) {
+            return o.toString();
+        }
+
+        @Override
+        protected String getSyntax(final Date o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final Timestamp o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final UUID o) {
+            return "new Guid(\"" + o.toString() + "\")";
+        }
+
+        @Override
+        protected String getSyntax(final Lambda o) {
+            return "() => \"" + 
StringEscapeUtils.escapeEcmaScript(o.getLambdaScript().trim()) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final SackFunctions.Barrier o) {
+            return "Barrier." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final VertexProperty.Cardinality o) {
+            return "Cardinality." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final TraversalOptionParent.Pick o) {
+            return "Pick." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final Number o) {
+            return o.toString();
+        }
+
+        @Override
+        protected Script produceScript(final Set<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new HashSet<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final List<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new List<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Map<?, ?> o) {
+            script.append("new Dictionary<object,object> {");
+            produceKeyValuesForMap(o);
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Class<?> o) {
+            return script.append(o.getCanonicalName());
+        }
+
+        @Override
+        protected Script produceScript(final Enum<?> o) {
+            final String e = o instanceof Direction || o instanceof T ?
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1).toLowerCase() :
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1);
+            return script.append(o.getDeclaringClass().getSimpleName() + "." + 
e);
+        }
+
+        @Override
+        protected Script produceScript(final Vertex o) {
+            script.append("new Vertex(");
+            convertToScript(o.id());
+            script.append(",");
+            convertToScript(o.label());
+            return script.append(")");
+        }
+
+        @Override
+        protected Script produceScript(final Edge o) {
+            script.append("new Edge(");
+            convertToScript(o.id());
+            script.append(", new Vertex(");
+            convertToScript(o.outVertex().id());
+            script.append(",");
+            convertToScript(o.outVertex().label());
+            script.append("),");
+            convertToScript(o.label());
+            script.append(", new Vertex(");
+            convertToScript(o.inVertex().id());
+            script.append(",");
+            convertToScript(o.inVertex().label());
+            return script.append("))");
+        }
+
+        @Override
+        protected Script produceScript(final VertexProperty<?> o) {
+            script.append("new Property(");
+            convertToScript(o.id());
+            script.append(",");
+            convertToScript(o.label());
+            script.append(",");
+            convertToScript(o.value());
+            script.append(",");
+            return script.append("null)");
+        }
+
+        @Override
+        protected Script produceScript(final TraversalStrategyProxy<?> o) {
+            if (o.getConfiguration().isEmpty()) {
+                return script.append("new " + 
o.getStrategyClass().getSimpleName() + "()");
+            } else {
+                script.append("new " + o.getStrategyClass().getSimpleName() + 
"(configuration: ");
+                script.append("new Dictionary<string,dynamic> {");
+                
produceKeyValuesForMap(ConfigurationConverter.getMap(o.getConfiguration()));
+                script.append("}");
+
+                return script.append(")");
+            }
+        }
+
+        private Script produceKeyValuesForMap(final Map<?,?> m) {
+            final Iterator<? extends Map.Entry<?, ?>> itty = 
m.entrySet().iterator();
+            while (itty.hasNext()) {
+                final Map.Entry<?,?> entry = itty.next();
+                script.append("{");
+                convertToScript(entry.getKey());
+                script.append(",");
+                convertToScript(entry.getValue());
+                script.append("}");
+                if (itty.hasNext())
+                    script.append(",");

Review comment:
       (nitpick) Again, an extra space after the comma would make the result 
look better.

##########
File path: 
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
##########
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.commons.configuration2.ConfigurationConverter;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Script;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Converts bytecode to a C# string of Gremlin.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DotNetTranslator implements Translator.ScriptTranslator {
+
+    private final String traversalSource;
+    private final TypeTranslator typeTranslator;
+
+    private static final List<String> methodsWithArgsNotNeedingGeneric = 
Arrays.asList(GraphTraversal.Symbols.group,
+            GraphTraversal.Symbols.groupCount, GraphTraversal.Symbols.sack);
+
+    private DotNetTranslator(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
+    }
+
+    /**
+     * Creates the translator with a {@code false} argument to {@code 
withParameters} using
+     * {@link #of(String, boolean)}.
+     */
+    public static DotNetTranslator of(final String traversalSource) {
+        return of(traversalSource, false);
+    }
+
+    /**
+     * Creates the translator with the {@link DefaultTypeTranslator} passing 
the {@code withParameters} option to it
+     * which will handle type translation in a fashion that should typically 
increase cache hits and reduce
+     * compilation times if enabled at the sacrifice to rewriting of the 
script that could reduce readability.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
boolean withParameters) {
+        return of(traversalSource, new DefaultTypeTranslator(withParameters));
+    }
+
+    /**
+     * Creates the translator with a custom {@link TypeTranslator} instance.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        return new DotNetTranslator(traversalSource, typeTranslator);
+    }
+
+    @Override
+    public Script translate(final Bytecode bytecode) {
+        return typeTranslator.apply(traversalSource, bytecode);
+    }
+
+    @Override
+    public String getTargetLanguage() {
+        return "gremlin-dotnet";
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.translatorString(this);
+    }
+
+    @Override
+    public String getTraversalSource() {
+        return this.traversalSource;
+    }
+
+    /**
+     * Performs standard type translation for the TinkerPop types to C#.
+     */
+    public static class DefaultTypeTranslator extends AbstractTypeTranslator {
+
+        public DefaultTypeTranslator(final boolean withParameters) {
+            super(withParameters);
+        }
+
+        @Override
+        protected String getNullSyntax() {
+            return "null";
+        }
+
+        @Override
+        protected String getSyntax(final String o) {
+            return "\"" + StringEscapeUtils.escapeJava(o) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final Boolean o) {
+            return o.toString();
+        }
+
+        @Override
+        protected String getSyntax(final Date o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final Timestamp o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final UUID o) {
+            return "new Guid(\"" + o.toString() + "\")";
+        }
+
+        @Override
+        protected String getSyntax(final Lambda o) {
+            return "() => \"" + 
StringEscapeUtils.escapeEcmaScript(o.getLambdaScript().trim()) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final SackFunctions.Barrier o) {
+            return "Barrier." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final VertexProperty.Cardinality o) {
+            return "Cardinality." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final TraversalOptionParent.Pick o) {
+            return "Pick." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final Number o) {
+            return o.toString();
+        }
+
+        @Override
+        protected Script produceScript(final Set<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new HashSet<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final List<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new List<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Map<?, ?> o) {
+            script.append("new Dictionary<object,object> {");
+            produceKeyValuesForMap(o);
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Class<?> o) {
+            return script.append(o.getCanonicalName());
+        }
+
+        @Override
+        protected Script produceScript(final Enum<?> o) {
+            final String e = o instanceof Direction || o instanceof T ?

Review comment:
       Not that important, but was the special case necessary for `T`? Java `T` 
values seem to follow camel casing and C# values pascal casing so this should 
be the default case, or not?

##########
File path: 
gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
##########
@@ -41,8 +41,11 @@ public class GherkinTestRunner
             new Dictionary<string, IgnoreReason>
             {
                 // Add here the name of scenarios to ignore and the reason, 
e.g.:
-                { "g_V_group_byXageX", IgnoreReason.NullKeysInMapNotSupported }
-                //{ "g_V_properties_propertiesXstartTimeX_drop", 
IgnoreReason.NoReason },
+                { "g_V_group_byXageX", IgnoreReason.NullKeysInMapNotSupported 
},
+                { "g_withSackX0X_V_outE_sackXsumX_byXweightX_inV_sack_sum", 
IgnoreReason.NoReason },

Review comment:
       Maybe add an `IgnoreReason` that explains that the assertions don't work 
here? You could also add a link to the ticket as a comment to make this clear.

##########
File path: 
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
##########
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.commons.configuration2.ConfigurationConverter;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Script;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Converts bytecode to a C# string of Gremlin.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DotNetTranslator implements Translator.ScriptTranslator {
+
+    private final String traversalSource;
+    private final TypeTranslator typeTranslator;
+
+    private static final List<String> methodsWithArgsNotNeedingGeneric = 
Arrays.asList(GraphTraversal.Symbols.group,
+            GraphTraversal.Symbols.groupCount, GraphTraversal.Symbols.sack);
+
+    private DotNetTranslator(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
+    }
+
+    /**
+     * Creates the translator with a {@code false} argument to {@code 
withParameters} using
+     * {@link #of(String, boolean)}.
+     */
+    public static DotNetTranslator of(final String traversalSource) {
+        return of(traversalSource, false);
+    }
+
+    /**
+     * Creates the translator with the {@link DefaultTypeTranslator} passing 
the {@code withParameters} option to it
+     * which will handle type translation in a fashion that should typically 
increase cache hits and reduce
+     * compilation times if enabled at the sacrifice to rewriting of the 
script that could reduce readability.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
boolean withParameters) {
+        return of(traversalSource, new DefaultTypeTranslator(withParameters));
+    }
+
+    /**
+     * Creates the translator with a custom {@link TypeTranslator} instance.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        return new DotNetTranslator(traversalSource, typeTranslator);
+    }
+
+    @Override
+    public Script translate(final Bytecode bytecode) {
+        return typeTranslator.apply(traversalSource, bytecode);
+    }
+
+    @Override
+    public String getTargetLanguage() {
+        return "gremlin-dotnet";
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.translatorString(this);
+    }
+
+    @Override
+    public String getTraversalSource() {
+        return this.traversalSource;
+    }
+
+    /**
+     * Performs standard type translation for the TinkerPop types to C#.
+     */
+    public static class DefaultTypeTranslator extends AbstractTypeTranslator {
+
+        public DefaultTypeTranslator(final boolean withParameters) {
+            super(withParameters);
+        }
+
+        @Override
+        protected String getNullSyntax() {
+            return "null";
+        }
+
+        @Override
+        protected String getSyntax(final String o) {
+            return "\"" + StringEscapeUtils.escapeJava(o) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final Boolean o) {
+            return o.toString();
+        }
+
+        @Override
+        protected String getSyntax(final Date o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final Timestamp o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final UUID o) {
+            return "new Guid(\"" + o.toString() + "\")";
+        }
+
+        @Override
+        protected String getSyntax(final Lambda o) {
+            return "() => \"" + 
StringEscapeUtils.escapeEcmaScript(o.getLambdaScript().trim()) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final SackFunctions.Barrier o) {
+            return "Barrier." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final VertexProperty.Cardinality o) {
+            return "Cardinality." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final TraversalOptionParent.Pick o) {
+            return "Pick." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final Number o) {
+            return o.toString();
+        }
+
+        @Override
+        protected Script produceScript(final Set<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new HashSet<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final List<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new List<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Map<?, ?> o) {
+            script.append("new Dictionary<object,object> {");
+            produceKeyValuesForMap(o);
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Class<?> o) {
+            return script.append(o.getCanonicalName());
+        }
+
+        @Override
+        protected Script produceScript(final Enum<?> o) {
+            final String e = o instanceof Direction || o instanceof T ?
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1).toLowerCase() :
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1);
+            return script.append(o.getDeclaringClass().getSimpleName() + "." + 
e);
+        }
+
+        @Override
+        protected Script produceScript(final Vertex o) {
+            script.append("new Vertex(");
+            convertToScript(o.id());
+            script.append(",");

Review comment:
       Again just a nit, but I think basically all occurrences of 
`script.append(",");` could be replaced by `script.append(", ");`.

##########
File path: 
gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
##########
@@ -112,13 +112,16 @@ public void TranslateTraversal(string traversalText)
             {
                 throw new InvalidOperationException("g should be a traversal 
source");
             }
-            _traversal = TraversalParser.GetTraversal(traversalText, _g, 
_parameters);

Review comment:
       Can't we now remove the `TraversalParser` logic completely?

##########
File path: 
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
##########
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.commons.configuration2.ConfigurationConverter;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Script;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Converts bytecode to a C# string of Gremlin.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DotNetTranslator implements Translator.ScriptTranslator {
+
+    private final String traversalSource;
+    private final TypeTranslator typeTranslator;
+
+    private static final List<String> methodsWithArgsNotNeedingGeneric = 
Arrays.asList(GraphTraversal.Symbols.group,
+            GraphTraversal.Symbols.groupCount, GraphTraversal.Symbols.sack);
+
+    private DotNetTranslator(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
+    }
+
+    /**
+     * Creates the translator with a {@code false} argument to {@code 
withParameters} using
+     * {@link #of(String, boolean)}.
+     */
+    public static DotNetTranslator of(final String traversalSource) {
+        return of(traversalSource, false);
+    }
+
+    /**
+     * Creates the translator with the {@link DefaultTypeTranslator} passing 
the {@code withParameters} option to it
+     * which will handle type translation in a fashion that should typically 
increase cache hits and reduce
+     * compilation times if enabled at the sacrifice to rewriting of the 
script that could reduce readability.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
boolean withParameters) {
+        return of(traversalSource, new DefaultTypeTranslator(withParameters));
+    }
+
+    /**
+     * Creates the translator with a custom {@link TypeTranslator} instance.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        return new DotNetTranslator(traversalSource, typeTranslator);
+    }
+
+    @Override
+    public Script translate(final Bytecode bytecode) {
+        return typeTranslator.apply(traversalSource, bytecode);
+    }
+
+    @Override
+    public String getTargetLanguage() {
+        return "gremlin-dotnet";
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.translatorString(this);
+    }
+
+    @Override
+    public String getTraversalSource() {
+        return this.traversalSource;
+    }
+
+    /**
+     * Performs standard type translation for the TinkerPop types to C#.
+     */
+    public static class DefaultTypeTranslator extends AbstractTypeTranslator {
+
+        public DefaultTypeTranslator(final boolean withParameters) {
+            super(withParameters);
+        }
+
+        @Override
+        protected String getNullSyntax() {
+            return "null";
+        }
+
+        @Override
+        protected String getSyntax(final String o) {
+            return "\"" + StringEscapeUtils.escapeJava(o) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final Boolean o) {
+            return o.toString();
+        }
+
+        @Override
+        protected String getSyntax(final Date o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final Timestamp o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final UUID o) {
+            return "new Guid(\"" + o.toString() + "\")";
+        }
+
+        @Override
+        protected String getSyntax(final Lambda o) {
+            return "() => \"" + 
StringEscapeUtils.escapeEcmaScript(o.getLambdaScript().trim()) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final SackFunctions.Barrier o) {
+            return "Barrier." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final VertexProperty.Cardinality o) {
+            return "Cardinality." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final TraversalOptionParent.Pick o) {
+            return "Pick." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final Number o) {
+            return o.toString();
+        }
+
+        @Override
+        protected Script produceScript(final Set<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new HashSet<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final List<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();

Review comment:
       (nitpick) cast to List is redundant

##########
File path: gremlin-dotnet/build/generate.groovy
##########
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph
+import 
org.apache.tinkerpop.gremlin.process.traversal.translator.DotNetTranslator
+import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine
+import 
org.apache.tinkerpop.gremlin.groovy.jsr223.ast.VarAsBindingASTTransformation
+import 
org.apache.tinkerpop.gremlin.groovy.jsr223.ast.RepeatASTTransformationCustomizer
+import org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyCustomizer
+import org.codehaus.groovy.control.customizers.CompilationCustomizer
+import org.apache.tinkerpop.gremlin.features.FeatureReader
+
+import javax.script.SimpleBindings
+
+import static 
org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal
+
+// file is overwritten on each generation
+radishGremlinFile = new 
File("${projectBaseDir}/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/Gremlin.cs")
+
+// assumes globally unique scenario names for keys with list of Gremlin 
traversals as they appear
+gremlins = FeatureReader.parse("${projectBaseDir}")
+
+gremlinGroovyScriptEngine = new GremlinGroovyScriptEngine(new 
GroovyCustomizer() {
+    public CompilationCustomizer create() {
+        return new RepeatASTTransformationCustomizer(new 
VarAsBindingASTTransformation())
+    }
+})
+translator = DotNetTranslator.of('g')
+g = traversal().withGraph(EmptyGraph.instance())
+bindings = new SimpleBindings()
+bindings.put('g', g)
+
+radishGremlinFile.withWriter('UTF-8') { Writer writer ->
+    writer.writeLine('#region License\n' +
+            '\n' +
+            '/*\n' +
+            ' * Licensed to the Apache Software Foundation (ASF) under one\n' +
+            ' * or more contributor license agreements.  See the NOTICE 
file\n' +
+            ' * distributed with this work for additional information\n' +
+            ' * regarding copyright ownership.  The ASF licenses this file\n' +
+            ' * to you under the Apache License, Version 2.0 (the\n' +
+            ' * "License"); you may not use this file except in compliance\n' +
+            ' * with the License.  You may obtain a copy of the License at\n' +
+            ' *\n' +
+            ' *     http://www.apache.org/licenses/LICENSE-2.0\n' +
+            ' *\n' +
+            ' * Unless required by applicable law or agreed to in writing,\n' +
+            ' * software distributed under the License is distributed on an\n' 
+
+            ' * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n' +
+            ' * KIND, either express or implied.  See the License for the\n' +
+            ' * specific language governing permissions and limitations\n' +
+            ' * under the License.\n' +
+            ' */\n' +
+            '\n' +
+            '#endregion\n')
+
+    
writer.writeLine("\n\n//********************************************************************************")
+    writer.writeLine("//* Do NOT edit this file directly - generated by 
build/generate.groovy")
+    
writer.writeLine("//********************************************************************************\n\n")
+
+    writer.writeLine('using System;\n' +
+                     'using System.Collections;\n' +
+                     'using System.Collections.Generic;\n' +
+                     'using System.Linq;\n' +
+                     'using Gremlin.Net.Structure;\n' +
+                     'using Gremlin.Net.Process.Traversal;\n' +
+                     'using 
Gremlin.Net.Process.Traversal.Strategy.Decoration;\n')
+    writer.writeLine('namespace 
Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation\n' +
+            '{\n' +
+            '    public class Gremlin\n' +
+            '    {\n' +
+            '        public static readonly IDictionary<string, 
List<Func<GraphTraversalSource, IDictionary<string, object>,ITraversal>>> 
FixedTranslations = \n' +
+            '            new Dictionary<string, 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>>\n' +
+            '            {')
+
+    gremlins.each { k,v ->
+        writer.write("               {\"")
+        writer.write(k)
+        writer.write("\", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {")
+        def collected = v.collect{
+            def t = gremlinGroovyScriptEngine.eval(it, bindings)
+            [t, t.bytecode.bindings.keySet()]
+        }
+
+        def gremlinItty = collected.iterator()
+        while (gremlinItty.hasNext()) {
+            def t = gremlinItty.next()[0]
+            writer.write("(g,p) =>")
+            writer.write(translator.translate(t.bytecode).script.
+                    replace("xx1", "p[\"xx1\"]").
+                    replace("xx2", "p[\"xx2\"]").
+                    replace("xx3", "p[\"xx3\"]").
+                    replace("v1", "(Vertex) p[\"v1\"]").
+                    replace("v2", "(Vertex) p[\"v2\"]").
+                    replace("v3", "(Vertex) p[\"v3\"]").
+                    replace("v4", "(Vertex) p[\"v4\"]").
+                    replace("v5", "(Vertex) p[\"v5\"]").
+                    replace("v6", "(Vertex) p[\"v6\"]").
+                    replace("vid1", "p[\"vid1\"]").
+                    replace("vid2", "p[\"vid2\"]").
+                    replace("vid3", "p[\"vid3\"]").
+                    replace("vid4", "p[\"vid4\"]").
+                    replace("vid5", "p[\"vid5\"]").
+                    replace("vid6", "p[\"vid6\"]").
+                    replace("e7", "p[\"e7\"]").
+                    replace("e10", "p[\"e10\"]").
+                    replace("e11", "p[\"e11\"]").
+                    replace("eid7", "p[\"eid7\"]").
+                    replace("eid10", "p[\"eid10\"]").
+                    replace("eid11", "p[\"eid11\"]").
+                    replace("l1", "(IFunction) p[\"l1\"]").
+                    replace("l2", "(IFunction) p[\"l2\"]").
+                    replace("pred1", "(IPredicate) p[\"pred1\"]").
+                    replace("c1", "(IComparator) p[\"c1\"]").
+                    replace("c2", "(IComparator) p[\"c2\"]"))
+            if (gremlinItty.hasNext())
+                writer.write(', ')
+            else
+                writer.write("}")
+        }
+        writer.writeLine('}, ')
+    }
+    writer.writeLine('            };\n')
+
+    writer.writeLine(
+            '        public static ITraversal UseTraversal(string 
scenarioName, GraphTraversalSource g, IDictionary<string, object> 
parameters)\n' +

Review comment:
       Pretty cool that we can now use the translator to run the Gherkin 
scenarios :)

##########
File path: 
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
##########
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.commons.configuration2.ConfigurationConverter;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Script;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Converts bytecode to a C# string of Gremlin.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DotNetTranslator implements Translator.ScriptTranslator {
+
+    private final String traversalSource;
+    private final TypeTranslator typeTranslator;
+
+    private static final List<String> methodsWithArgsNotNeedingGeneric = 
Arrays.asList(GraphTraversal.Symbols.group,
+            GraphTraversal.Symbols.groupCount, GraphTraversal.Symbols.sack);
+
+    private DotNetTranslator(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
+    }
+
+    /**
+     * Creates the translator with a {@code false} argument to {@code 
withParameters} using
+     * {@link #of(String, boolean)}.
+     */
+    public static DotNetTranslator of(final String traversalSource) {
+        return of(traversalSource, false);
+    }
+
+    /**
+     * Creates the translator with the {@link DefaultTypeTranslator} passing 
the {@code withParameters} option to it
+     * which will handle type translation in a fashion that should typically 
increase cache hits and reduce
+     * compilation times if enabled at the sacrifice to rewriting of the 
script that could reduce readability.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
boolean withParameters) {
+        return of(traversalSource, new DefaultTypeTranslator(withParameters));
+    }
+
+    /**
+     * Creates the translator with a custom {@link TypeTranslator} instance.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        return new DotNetTranslator(traversalSource, typeTranslator);
+    }
+
+    @Override
+    public Script translate(final Bytecode bytecode) {
+        return typeTranslator.apply(traversalSource, bytecode);
+    }
+
+    @Override
+    public String getTargetLanguage() {
+        return "gremlin-dotnet";
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.translatorString(this);
+    }
+
+    @Override
+    public String getTraversalSource() {
+        return this.traversalSource;
+    }
+
+    /**
+     * Performs standard type translation for the TinkerPop types to C#.
+     */
+    public static class DefaultTypeTranslator extends AbstractTypeTranslator {
+
+        public DefaultTypeTranslator(final boolean withParameters) {
+            super(withParameters);
+        }
+
+        @Override
+        protected String getNullSyntax() {
+            return "null";
+        }
+
+        @Override
+        protected String getSyntax(final String o) {
+            return "\"" + StringEscapeUtils.escapeJava(o) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final Boolean o) {
+            return o.toString();
+        }
+
+        @Override
+        protected String getSyntax(final Date o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final Timestamp o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final UUID o) {
+            return "new Guid(\"" + o.toString() + "\")";
+        }
+
+        @Override
+        protected String getSyntax(final Lambda o) {
+            return "() => \"" + 
StringEscapeUtils.escapeEcmaScript(o.getLambdaScript().trim()) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final SackFunctions.Barrier o) {
+            return "Barrier." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final VertexProperty.Cardinality o) {
+            return "Cardinality." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final TraversalOptionParent.Pick o) {
+            return "Pick." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final Number o) {
+            return o.toString();
+        }
+
+        @Override
+        protected Script produceScript(final Set<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new HashSet<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final List<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new List<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Map<?, ?> o) {
+            script.append("new Dictionary<object,object> {");
+            produceKeyValuesForMap(o);
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Class<?> o) {
+            return script.append(o.getCanonicalName());
+        }
+
+        @Override
+        protected Script produceScript(final Enum<?> o) {
+            final String e = o instanceof Direction || o instanceof T ?
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1).toLowerCase() :
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1);
+            return script.append(o.getDeclaringClass().getSimpleName() + "." + 
e);
+        }
+
+        @Override
+        protected Script produceScript(final Vertex o) {
+            script.append("new Vertex(");
+            convertToScript(o.id());
+            script.append(",");
+            convertToScript(o.label());
+            return script.append(")");
+        }
+
+        @Override
+        protected Script produceScript(final Edge o) {
+            script.append("new Edge(");
+            convertToScript(o.id());
+            script.append(", new Vertex(");
+            convertToScript(o.outVertex().id());
+            script.append(",");
+            convertToScript(o.outVertex().label());
+            script.append("),");
+            convertToScript(o.label());
+            script.append(", new Vertex(");
+            convertToScript(o.inVertex().id());
+            script.append(",");
+            convertToScript(o.inVertex().label());
+            return script.append("))");
+        }
+
+        @Override
+        protected Script produceScript(final VertexProperty<?> o) {
+            script.append("new Property(");
+            convertToScript(o.id());
+            script.append(",");
+            convertToScript(o.label());
+            script.append(",");
+            convertToScript(o.value());
+            script.append(",");
+            return script.append("null)");
+        }
+
+        @Override
+        protected Script produceScript(final TraversalStrategyProxy<?> o) {
+            if (o.getConfiguration().isEmpty()) {
+                return script.append("new " + 
o.getStrategyClass().getSimpleName() + "()");
+            } else {
+                script.append("new " + o.getStrategyClass().getSimpleName() + 
"(configuration: ");

Review comment:
       `configuration: ` doesn't work for most strategies in C# as they don't 
have a `configuration` parameter in their constructor. `VertexProgramStrategy` 
seems to be the only strategy where this works.
   In the test, you're using `SubgraphStrategy` but that also doesn't have such 
a constructor.
   
   We could solve this for example by using the public `Configuration` property 
of the `AbstractTraversalStrategy` class which is the base class for all 
strategies in C#:
   
   ```c#
   new SubgraphStrategy {Configuration = {{"test", 1}}};
   ```
   
   The implementation looks then like this:
   
   ```c#
   script.append("new " + o.getStrategyClass().getSimpleName() + " 
{Configuration = ");
   script.append("{");
   produceKeyValuesForMap(ConfigurationConverter.getMap(o.getConfiguration()));
   script.append("}");
   
   return script.append("}");
   ```
   
   This works as long as we have a parameterless constructor for all strategies 
in C# which we seem to have generated by default. It's just something that we 
should keep in mind. (We could also enforce this with a test in Gremlin.Net 
using reflection.)

##########
File path: 
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/DotNetTranslator.java
##########
@@ -0,0 +1,434 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.tinkerpop.gremlin.process.traversal.translator;
+
+import org.apache.commons.configuration2.ConfigurationConverter;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Script;
+import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
+import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Converts bytecode to a C# string of Gremlin.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DotNetTranslator implements Translator.ScriptTranslator {
+
+    private final String traversalSource;
+    private final TypeTranslator typeTranslator;
+
+    private static final List<String> methodsWithArgsNotNeedingGeneric = 
Arrays.asList(GraphTraversal.Symbols.group,
+            GraphTraversal.Symbols.groupCount, GraphTraversal.Symbols.sack);
+
+    private DotNetTranslator(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
+    }
+
+    /**
+     * Creates the translator with a {@code false} argument to {@code 
withParameters} using
+     * {@link #of(String, boolean)}.
+     */
+    public static DotNetTranslator of(final String traversalSource) {
+        return of(traversalSource, false);
+    }
+
+    /**
+     * Creates the translator with the {@link DefaultTypeTranslator} passing 
the {@code withParameters} option to it
+     * which will handle type translation in a fashion that should typically 
increase cache hits and reduce
+     * compilation times if enabled at the sacrifice to rewriting of the 
script that could reduce readability.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
boolean withParameters) {
+        return of(traversalSource, new DefaultTypeTranslator(withParameters));
+    }
+
+    /**
+     * Creates the translator with a custom {@link TypeTranslator} instance.
+     */
+    public static DotNetTranslator of(final String traversalSource, final 
TypeTranslator typeTranslator) {
+        return new DotNetTranslator(traversalSource, typeTranslator);
+    }
+
+    @Override
+    public Script translate(final Bytecode bytecode) {
+        return typeTranslator.apply(traversalSource, bytecode);
+    }
+
+    @Override
+    public String getTargetLanguage() {
+        return "gremlin-dotnet";
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.translatorString(this);
+    }
+
+    @Override
+    public String getTraversalSource() {
+        return this.traversalSource;
+    }
+
+    /**
+     * Performs standard type translation for the TinkerPop types to C#.
+     */
+    public static class DefaultTypeTranslator extends AbstractTypeTranslator {
+
+        public DefaultTypeTranslator(final boolean withParameters) {
+            super(withParameters);
+        }
+
+        @Override
+        protected String getNullSyntax() {
+            return "null";
+        }
+
+        @Override
+        protected String getSyntax(final String o) {
+            return "\"" + StringEscapeUtils.escapeJava(o) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final Boolean o) {
+            return o.toString();
+        }
+
+        @Override
+        protected String getSyntax(final Date o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final Timestamp o) {
+            return "DateTimeOffset.FromUnixTimeMillisecond(" + o.getTime() + 
")";
+        }
+
+        @Override
+        protected String getSyntax(final UUID o) {
+            return "new Guid(\"" + o.toString() + "\")";
+        }
+
+        @Override
+        protected String getSyntax(final Lambda o) {
+            return "() => \"" + 
StringEscapeUtils.escapeEcmaScript(o.getLambdaScript().trim()) + "\"";
+        }
+
+        @Override
+        protected String getSyntax(final SackFunctions.Barrier o) {
+            return "Barrier." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final VertexProperty.Cardinality o) {
+            return "Cardinality." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final TraversalOptionParent.Pick o) {
+            return "Pick." + SymbolHelper.toCSharp(o.toString());
+        }
+
+        @Override
+        protected String getSyntax(final Number o) {
+            return o.toString();
+        }
+
+        @Override
+        protected Script produceScript(final Set<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new HashSet<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final List<?> o) {
+            final Iterator<?> iterator = ((List<?>) o).iterator();
+            script.append("new List<object> {");
+
+            while (iterator.hasNext()) {
+                final Object nextItem = iterator.next();
+                convertToScript(nextItem);
+                if (iterator.hasNext())
+                    script.append(",").append(" ");
+            }
+
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Map<?, ?> o) {
+            script.append("new Dictionary<object,object> {");
+            produceKeyValuesForMap(o);
+            return script.append("}");
+        }
+
+        @Override
+        protected Script produceScript(final Class<?> o) {
+            return script.append(o.getCanonicalName());
+        }
+
+        @Override
+        protected Script produceScript(final Enum<?> o) {
+            final String e = o instanceof Direction || o instanceof T ?
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1).toLowerCase() :
+                    o.name().substring(0,1).toUpperCase() + 
o.name().substring(1);
+            return script.append(o.getDeclaringClass().getSimpleName() + "." + 
e);
+        }
+
+        @Override
+        protected Script produceScript(final Vertex o) {
+            script.append("new Vertex(");
+            convertToScript(o.id());
+            script.append(",");
+            convertToScript(o.label());
+            return script.append(")");
+        }
+
+        @Override
+        protected Script produceScript(final Edge o) {
+            script.append("new Edge(");
+            convertToScript(o.id());
+            script.append(", new Vertex(");
+            convertToScript(o.outVertex().id());
+            script.append(",");
+            convertToScript(o.outVertex().label());
+            script.append("),");
+            convertToScript(o.label());
+            script.append(", new Vertex(");
+            convertToScript(o.inVertex().id());
+            script.append(",");
+            convertToScript(o.inVertex().label());
+            return script.append("))");
+        }
+
+        @Override
+        protected Script produceScript(final VertexProperty<?> o) {
+            script.append("new Property(");

Review comment:
       Shouldn't this be `"new VertexProperty("`?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]


> DotNetTranslator for Java
> -------------------------
>
>                 Key: TINKERPOP-2452
>                 URL: https://issues.apache.org/jira/browse/TINKERPOP-2452
>             Project: TinkerPop
>          Issue Type: Improvement
>          Components: translator
>    Affects Versions: 3.4.8
>            Reporter: Stephen Mallette
>            Priority: Major
>
> Add a {{DotNetpTranslator}} to Java to translate Gremlin into C#. This one 
> may be tricky given the need to often specify generics in C#. Of course, even 
> if the implementation got someone most of the way to a translation and they 
> had to specify the generics themselves it would be considerably less work 
> than what they are in store for today.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to