You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2024/06/21 19:06:24 UTC

(tinkerpop) branch TINKERPOP-3023 created (now d84251d2d3)

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

spmallette pushed a change to branch TINKERPOP-3023
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


      at d84251d2d3 TINKERPOP-3023 Addeed Set syntax in grammar

This branch includes the following new commits:

     new d84251d2d3 TINKERPOP-3023 Addeed Set syntax in grammar

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(tinkerpop) 01/01: TINKERPOP-3023 Addeed Set syntax in grammar

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit d84251d2d32ec9e7db1dea1a56d8b73c862aa50b
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Fri Jun 21 15:05:07 2024 -0400

    TINKERPOP-3023 Addeed Set syntax in grammar
    
    Updated translators and serializers as needed. For the latter, particular attention was needed for Javascript where Set was being serialized as array essentially.
---
 CHANGELOG.asciidoc                                 |  4 +-
 .../grammar/DefaultGremlinBaseVisitor.java         |  7 ++-
 .../language/grammar/GenericLiteralVisitor.java    | 11 +++++
 .../translator/AnonymizedTranslatorVisitor.java    |  6 +++
 .../translator/DotNetTranslateVisitor.java         | 13 +++++
 .../language/translator/GoTranslateVisitor.java    | 14 ++++++
 .../language/translator/JavaTranslateVisitor.java  | 15 ++++++
 .../translator/JavascriptTranslateVisitor.java     | 13 +++++
 .../translator/PythonTranslateVisitor.java         | 13 +++++
 .../language/translator/TranslateVisitor.java      |  5 ++
 .../grammar/GeneralLiteralVisitorTest.java         | 55 +++++++++++++++++++---
 .../language/translator/GremlinTranslatorTest.java |  9 ++++
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |  4 +-
 gremlin-go/driver/cucumber/gremlin.go              |  4 +-
 .../structure/io/binary/internals/AnySerializer.js |  1 +
 .../structure/io/binary/internals/SetSerializer.js | 29 +++++++++++-
 .../lib/structure/io/type-serializers.ts           | 11 +++++
 .../gremlin-javascript/test/cucumber/gremlin.js    |  4 +-
 .../graphbinary/ArraySerializerTestTemplate.js     |  6 +--
 .../unit/graphbinary/SetSerializerTestTemplate.js  | 31 ++++++++++--
 gremlin-language/src/main/antlr4/Gremlin.g4        |  6 +++
 gremlin-python/src/main/python/radish/gremlin.py   |  4 +-
 .../test/features/sideEffect/Inject.feature        | 28 ++++++++++-
 23 files changed, 269 insertions(+), 24 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 6a1a0b49b1..32b7e7dfd0 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -33,6 +33,8 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Added `with()` on `AnonymousTraversalSource` to cover both embedded and remote creation options.
 * Renamed the traversal discarding `none()` step to `discard()`.
 * Added new list filtering step `none()`.
+* Added support for `Set` in GraphSON and GraphBinary serialization for `gremlin-javascript`, where it previously just converted to array.
+* Added `Set` syntax in `gremlin-language`.
 * Removed the deprecated `withGraph()` option from `AnonymousTraversalSource`.
 * Removed the `gremlin-archetype` module in favor of newer sample applications in each GLV's `examples` folder.
 * Bumped to `commons-collection4`.
@@ -41,7 +43,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Modified the Gremlin ANTLR grammar to more dynamically interact with any strategies registered globally to the `TraversalStrategies` cache sets.
 * Made `new` keyword optional in the Gremlin grammar.
 * Added TypeScript & ECMAScript module support.
-* Improved graph structures type definitions in TypeScript
+* Improved graph structures type definitions in TypeScript.
 
 == TinkerPop 3.7.0 (Gremfir Master of the Pan Flute)
 
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
index 6682c15bd5..fb5c3d24d4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
@@ -1728,6 +1728,9 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im
 	/**
 	 * {@inheritDoc}
 	 */
-	@Override
-	public T visitTraversalBarrier(final GremlinParser.TraversalBarrierContext ctx) { notImplemented(ctx); return null; }
+	@Override public T visitTraversalBarrier(final GremlinParser.TraversalBarrierContext ctx) { notImplemented(ctx); return null; }
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override public T visitGenericLiteralSet(final GremlinParser.GenericLiteralSetContext ctx) { return null; }
 }
\ No newline at end of file
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
index c6db0ec1d4..2e0cbbe751 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
@@ -33,10 +33,12 @@ import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Visitor class to handle generic literal. All visitor methods return type is Object. It maybe used as a singleton
@@ -296,6 +298,15 @@ public class GenericLiteralVisitor extends DefaultGremlinBaseVisitor<Object> {
         }
     }
 
+    @Override
+    public Object visitGenericLiteralSet(final GremlinParser.GenericLiteralSetContext ctx) {
+        final Set<Object> result = new HashSet<>(ctx.getChildCount() / 2);
+        for (GremlinParser.GenericLiteralContext ic : ctx.genericLiteral()) {
+            result.add(antlr.genericVisitor.visitGenericLiteral(ic));
+        }
+        return result;
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java
index bb5823d133..225baacbba 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java
@@ -27,6 +27,7 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * A translator that anonymizes Gremlin so that arguments that might contain sensitive information are removed.
@@ -75,6 +76,11 @@ public class AnonymizedTranslatorVisitor extends TranslateVisitor {
         return null;
     }
 
+    @Override
+    public Void visitGenericLiteralSet(final GremlinParser.GenericLiteralSetContext ctx) {
+        return anonymize(ctx, Set.class);
+    }
+
     @Override
     public Void visitGenericLiteralCollection(final GremlinParser.GenericLiteralCollectionContext ctx) {
         return anonymize(ctx, List.class);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
index bb6ee7f20d..2701819624 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
@@ -175,6 +175,19 @@ public class DotNetTranslateVisitor extends AbstractTranslateVisitor {
         return null;
     }
 
+    @Override
+    public Void visitGenericLiteralSet(final GremlinParser.GenericLiteralSetContext ctx) {
+        sb.append("new HashSet<object> { ");
+        for (int i = 0; i < ctx.genericLiteral().size(); i++) {
+            final GremlinParser.GenericLiteralContext genericLiteralContext = ctx.genericLiteral(i);
+            visit(genericLiteralContext);
+            if (i < ctx.genericLiteral().size() - 1)
+                sb.append(", ");
+        }
+        sb.append(" }");
+        return null;
+    }
+
     @Override
     public Void visitGenericLiteralCollection(final GremlinParser.GenericLiteralCollectionContext ctx) {
         sb.append("new List<object> { ");
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java
index db8ec034ca..10eec9f3e0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java
@@ -98,6 +98,20 @@ public class GoTranslateVisitor extends AbstractTranslateVisitor {
         throw new TranslatorException("Go does not support range literals");
     }
 
+    @Override
+    public Void visitGenericLiteralSet(final GremlinParser.GenericLiteralSetContext ctx) {
+        sb.append(GO_PACKAGE_NAME);
+        sb.append("NewSimpleSet(");
+        for (int i = 0; i < ctx.genericLiteral().size(); i++) {
+            final GremlinParser.GenericLiteralContext genericLiteralContext = ctx.genericLiteral(i);
+            visit(genericLiteralContext);
+            if (i < ctx.genericLiteral().size() - 1)
+                sb.append(", ");
+        }
+        sb.append(")");
+        return null;
+    }
+
     @Override
     public Void visitGenericLiteralCollection(final GremlinParser.GenericLiteralCollectionContext ctx) {
         sb.append("[]interface{}{");
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java
index c8a522cc29..9eb0e945e5 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavaTranslateVisitor.java
@@ -240,6 +240,21 @@ public class JavaTranslateVisitor extends AbstractTranslateVisitor {
         throw new TranslatorException("Java does not support range literals");
     }
 
+    @Override
+    public Void visitGenericLiteralSet(final GremlinParser.GenericLiteralSetContext ctx) {
+        sb.append("new HashSet<Object>() {{ ");
+        for (int i = 0; i < ctx.genericLiteral().size(); i++) {
+            final GremlinParser.GenericLiteralContext genericLiteralContext = ctx.genericLiteral(i);
+            sb.append("add(");
+            visit(genericLiteralContext);
+            sb.append(");");
+            if (i < ctx.genericLiteral().size() - 1)
+                sb.append(" ");
+        }
+        sb.append(" }}");
+        return null;
+    }
+
     @Override
     public Void visitGenericLiteralCollection(final GremlinParser.GenericLiteralCollectionContext ctx) {
         sb.append("new ArrayList<Object>() {{ ");
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java
index b340f26b6e..01d094fb68 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java
@@ -192,6 +192,19 @@ public class JavascriptTranslateVisitor extends AbstractTranslateVisitor {
         throw new TranslatorException("Javascript does not support range literals");
     }
 
+    @Override
+    public Void visitGenericLiteralSet(final GremlinParser.GenericLiteralSetContext ctx) {
+        sb.append("new Set([");
+        for (int i = 0; i < ctx.genericLiteral().size(); i++) {
+            final GremlinParser.GenericLiteralContext genericLiteralContext = ctx.genericLiteral(i);
+            visit(genericLiteralContext);
+            if (i < ctx.genericLiteral().size() - 1)
+                sb.append(", ");
+        }
+        sb.append("])");
+        return null;
+    }
+
     @Override
     public Void visitGenericLiteralCollection(final GremlinParser.GenericLiteralCollectionContext ctx) {
         sb.append("[");
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
index bfaac4fcb2..092c41fe5e 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
@@ -265,6 +265,19 @@ public class PythonTranslateVisitor extends AbstractTranslateVisitor {
         throw new TranslatorException("Python does not support range literals");
     }
 
+    @Override
+    public Void visitGenericLiteralSet(final GremlinParser.GenericLiteralSetContext ctx) {
+        sb.append("{");
+        for (int i = 0; i < ctx.genericLiteral().size(); i++) {
+            final GremlinParser.GenericLiteralContext genericLiteralContext = ctx.genericLiteral(i);
+            visit(genericLiteralContext);
+            if (i < ctx.genericLiteral().size() - 1)
+                sb.append(", ");
+        }
+        sb.append("}");
+        return null;
+    }
+
     @Override
     public Void visitGenericLiteralCollection(final GremlinParser.GenericLiteralCollectionContext ctx) {
         sb.append("[");
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java
index 7ac440dbc0..00a8523b45 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java
@@ -2204,6 +2204,11 @@ public class TranslateVisitor extends AbstractParseTreeVisitor<Void> implements
         return visitChildren(ctx);
     }
 
+    @Override
+    public Void visitGenericLiteralSet(GremlinParser.GenericLiteralSetContext ctx) {
+        return visitChildren(ctx);
+    }
+
     @Override
     public Void visitStringLiteralVarargs(final GremlinParser.StringLiteralVarargsContext ctx) {
         return visitChildren(ctx);
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java
index 6f53552d7f..81f2c1e100 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GeneralLiteralVisitorTest.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.language.grammar;
 import org.antlr.v4.runtime.CharStreams;
 import org.antlr.v4.runtime.CommonTokenStream;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.experimental.runners.Enclosed;
@@ -37,6 +38,7 @@ import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
@@ -572,7 +574,7 @@ public class GeneralLiteralVisitorTest {
 
         @Test
         public void shouldParseGenericLiteralCollection() {
-            final GremlinLexer lexer = new GremlinLexer(CharStreams.fromString("['world', 165, [12L, 0xA, 14.5, \"hello\"]]"));
+            final GremlinLexer lexer = new GremlinLexer(CharStreams.fromString("['world', 165, 165, [12L, 0xA, 14.5, \"hello\"]]"));
             final GremlinParser parser = new GremlinParser(new CommonTokenStream(lexer));
             final GremlinParser.GenericLiteralContext ctx = parser.genericLiteral();
             final Object genericLiteral = new GenericLiteralVisitor(new GremlinAntlrToJava()).visitGenericLiteral(ctx);
@@ -582,19 +584,20 @@ public class GeneralLiteralVisitorTest {
 
             // verify total number of elements
             List<Object> genericLiterals = (List<Object>) genericLiteral;
-            assertEquals(genericLiterals.size(), 3);
+            assertEquals(genericLiterals.size(), 4);
 
             // verify first element
             assertEquals("world", genericLiterals.get(0));
 
-            // verify second element
+            // verify second/third element
             assertEquals(165, genericLiterals.get(1));
+            assertEquals(165, genericLiterals.get(2));
 
-            // verify 3rd element
-            assertThat(genericLiterals.get(2), instanceOf(List.class));
+            // verify 4th element
+            assertThat(genericLiterals.get(3), instanceOf(List.class));
 
             // verify total number of elements
-            genericLiterals = (List<Object>) genericLiterals.get(2);
+            genericLiterals = (List<Object>) genericLiterals.get(3);
             assertEquals(genericLiterals.size(), 4);
 
             // verify first element
@@ -629,6 +632,46 @@ public class GeneralLiteralVisitorTest {
         }
     }
 
+    public static class ValidSetLiteralTest {
+
+        @Test
+        public void shouldParseGenericLiteralSet() {
+            final GremlinLexer lexer = new GremlinLexer(CharStreams.fromString("{'world', 165, 14.5}"));
+            final GremlinParser parser = new GremlinParser(new CommonTokenStream(lexer));
+            final GremlinParser.GenericLiteralContext ctx = parser.genericLiteral();
+            final Object genericLiteral = new GenericLiteralVisitor(new GremlinAntlrToJava()).visitGenericLiteral(ctx);
+
+            // verify type is Object[]
+            assertThat(genericLiteral, instanceOf(Set.class));
+
+            // verify total number of elements
+            Set<Object> genericLiterals = (Set<Object>) genericLiteral;
+            assertEquals(genericLiterals.size(), 3);
+
+            assertThat(genericLiterals.contains("world"), Matchers.is(true));
+            assertThat(genericLiterals.contains(165), Matchers.is(true));
+            assertThat(genericLiterals.contains(new BigDecimal(14.5)), Matchers.is(true));
+        }
+
+        /**
+         * Generic literal collection test
+         */
+        @Test
+        public void shouldParseEmptyGenericLiteralCollection() {
+            final GremlinLexer lexer = new GremlinLexer(CharStreams.fromString("{}"));
+            final GremlinParser parser = new GremlinParser(new CommonTokenStream(lexer));
+            final GremlinParser.GenericLiteralContext ctx = parser.genericLiteral();
+            final Object genericLiteral = new GenericLiteralVisitor(new GremlinAntlrToJava()).visitGenericLiteral(ctx);
+
+            // verify type is Object[]
+            assertThat(genericLiteral, instanceOf(Set.class));
+
+            // verify total number of elements
+            final Set<Object> genericLiterals = (Set<Object>) genericLiteral;
+            Assert.assertTrue(genericLiterals.isEmpty());
+        }
+    }
+
     public static class NullNaNInfTest {
 
         @Test
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java
index 193e939b4c..558df4dc13 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java
@@ -511,6 +511,15 @@ public class GremlinTranslatorTest {
                             "g.with(\"x\", new LinkedHashMap<Object, Object>() {{ put(1, \"x\"); }})",
                             "g.with_(\"x\", new Map([[1, \"x\"]]))",
                             "g.with_('x', { 1: 'x' })"},
+                    {"g.with('x', {1, 'x'})",
+                            null,
+                            "g.with(string0, set0)",
+                            "g.With(\"x\", new HashSet<object> { 1, \"x\" })",
+                            "g.With(\"x\", gremlingo.NewSimpleSet(1, \"x\"))",
+                            null,
+                            "g.with(\"x\", new HashSet<Object>() {{ add(1); add(\"x\"); }})",
+                            "g.with_(\"x\", new Set([1, \"x\"]))",
+                            "g.with_('x', {1, 'x'})"},
                     {"g.with('x', [1, 'x'])",
                             null,
                             "g.with(string0, list0)",
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index a4faf4faee..0f368621cc 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -1491,7 +1491,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                {"g_VX1X_valuesXageX_injectXnullX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["xx1"]).Values<object>("age").Inject(null)}}, 
                {"g_VX1X_valuesXageX_inject", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["xx1"]).Values<object>("age").Inject()}}, 
                {"g_injectXnull_1_3_nullX_asXaX_selectXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null, 1, 3, null).As("a").Select<object>("a")}}, 
-               {"g_injectX1_3lX_injectX100_300X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(1, 3).Inject(100, 300)}}, 
+               {"g_injectX1_3X_injectX100_300X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(1, 3).Inject(100, 300)}}, 
+               {"g_injectX1_3_100_300X_list", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { 1, 3, 100, 300 })}}, 
+               {"g_injectX1_3_100_300X_set", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new HashSet<object> { 1, 3, 100, 300 })}}, 
                {"g_io_readXkryoX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Io<object>("data/tinkerpop-modern.kryo").Read(), (g,p) =>g.V(), (g,p) =>g.E()}}, 
                {"g_io_read_withXreader_gryoX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Io<object>("data/tinkerpop-modern.kryo").With(IO.Reader, IO.Gryo).Read(), (g,p) =>g.V(), (g,p) =>g.E()}}, 
                {"g_io_readXgraphsonX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Io<object>("data/tinkerpop-modern.json").Read(), (g,p) =>g.V(), (g,p) =>g.E()}}, 
diff --git a/gremlin-go/driver/cucumber/gremlin.go b/gremlin-go/driver/cucumber/gremlin.go
index dee395e352..5a425332e0 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -1462,7 +1462,9 @@ var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p map[
     "g_VX1X_valuesXageX_injectXnullX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["xx1"]).Values("age").Inject(nil)}}, 
     "g_VX1X_valuesXageX_inject": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V(p["xx1"]).Values("age").Inject()}}, 
     "g_injectXnull_1_3_nullX_asXaX_selectXaX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(nil, 1, 3, nil).As("a").Select("a")}}, 
-    "g_injectX1_3lX_injectX100_300X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(1, 3).Inject(100, 300)}}, 
+    "g_injectX1_3X_injectX100_300X": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(1, 3).Inject(100, 300)}}, 
+    "g_injectX1_3_100_300X_list": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject([]interface{}{1, 3, 100, 300})}}, 
+    "g_injectX1_3_100_300X_set": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(gremlingo.NewSimpleSet(1, 3, 100, 300))}}, 
     "g_io_readXkryoX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Io("data/tinkerpop-modern.kryo").Read()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E()}}, 
     "g_io_read_withXreader_gryoX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Io("data/tinkerpop-modern.kryo").With(gremlingo.IO.Reader, gremlingo.IO.Gryo).Read()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E()}}, 
     "g_io_readXgraphsonX": {func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.Io("data/tinkerpop-modern.json").Read()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.V()}, func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return g.E()}}, 
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/AnySerializer.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/AnySerializer.js
index d506651e58..3443ea9df1 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/AnySerializer.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/AnySerializer.js
@@ -39,6 +39,7 @@ export default class AnySerializer {
       ioc.pSerializer,
       ioc.traverserSerializer,
       ioc.enumSerializer,
+      ioc.setSerializer,
       ioc.listSerializer,
       ioc.uuidSerializer,
       ioc.edgeSerializer,
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/SetSerializer.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/SetSerializer.js
index 5a31fc928c..fa5ddecaf0 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/SetSerializer.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/binary/internals/SetSerializer.js
@@ -34,7 +34,34 @@ export default class SetSerializer {
   }
 
   serialize(item, fullyQualifiedFormat = true) {
-    throw new Error('serialize() method not implemented for SetSerializer');
+    if (item === undefined || item === null) {
+      if (fullyQualifiedFormat) {
+        return Buffer.from([this.ID, 0x01]);
+      }
+      return Buffer.from([0x00, 0x00, 0x00, 0x00]); // {length} = 0
+    }
+
+    const bufs = [];
+    if (fullyQualifiedFormat) {
+      bufs.push(Buffer.from([this.ID, 0x00]));
+    }
+
+    // {length}
+    let length = item.size;
+    if (length < 0) {
+      length = 0;
+    }
+    if (length > this.ioc.intSerializer.INT32_MAX) {
+      throw new Error(`Set length=${length} is greater than supported max_length=${this.ioc.intSerializer.INT32_MAX}.`);
+    }
+    bufs.push(this.ioc.intSerializer.serialize(length, false));
+
+    // {item_0}...{item_n}
+    for (const i of item) {
+      bufs.push(this.ioc.anySerializer.serialize(i));
+    }
+
+    return Buffer.concat(bufs);
   }
 
   deserialize(buffer, fullyQualifiedFormat = true) {
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.ts b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.ts
index 0d7aba22eb..f483e59376 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.ts
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.ts
@@ -481,4 +481,15 @@ export class SetSerializer extends ArraySerializer {
   deserialize(obj: SerializedValue): any {
     return new Set(super.deserialize(obj));
   }
+
+  serialize(item: unknown[]) {
+    return {
+      [typeKey]: this.typeKey,
+      [valueKey]: [...item].map((x) => this.writer.adaptObject(x)),
+    };
+  }
+
+  canBeUsedFor(value: unknown) {
+    return value instanceof Set;
+  }
 }
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index 2767fa0e51..e1ba79d305 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -1483,7 +1483,9 @@ const gremlins = {
     g_VX1X_valuesXageX_injectXnullX: [function({g, xx1}) { return g.V(xx1).values("age").inject(null) }], 
     g_VX1X_valuesXageX_inject: [function({g, xx1}) { return g.V(xx1).values("age").inject() }], 
     g_injectXnull_1_3_nullX_asXaX_selectXaX: [function({g}) { return g.inject(null, 1, 3, null).as("a").select("a") }], 
-    g_injectX1_3lX_injectX100_300X: [function({g}) { return g.inject(1, 3).inject(100, 300) }], 
+    g_injectX1_3X_injectX100_300X: [function({g}) { return g.inject(1, 3).inject(100, 300) }], 
+    g_injectX1_3_100_300X_list: [function({g}) { return g.inject([1, 3, 100, 300]) }], 
+    g_injectX1_3_100_300X_set: [function({g}) { return g.inject(new Set([1, 3, 100, 300])) }], 
     g_io_readXkryoX: [function({g}) { return g.io("data/tinkerpop-modern.kryo").read() }, function({g}) { return g.V() }, function({g}) { return g.E() }], 
     g_io_read_withXreader_gryoX: [function({g}) { return g.io("data/tinkerpop-modern.kryo").with_(IO.reader, IO.gryo).read() }, function({g}) { return g.V() }, function({g}) { return g.E() }], 
     g_io_readXgraphsonX: [function({g}) { return g.io("data/tinkerpop-modern.json").read() }, function({g}) { return g.V() }, function({g}) { return g.E() }], 
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/ArraySerializerTestTemplate.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/ArraySerializerTestTemplate.js
index f8a3577ca7..a300412fb6 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/ArraySerializerTestTemplate.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/ArraySerializerTestTemplate.js
@@ -43,9 +43,9 @@ describe(`GraphBinary.${name}Serializer`, () => {
     { v:null,                               fq:1, b:[ID,0x01] },
     { v:null,                               fq:0, b:[0x00,0x00,0x00,0x00], av:[] },
 
-    { v:[],                                       b:[0x00,0x00,0x00,0x00] },
-    { v:[2147483647],                             b:[0x00,0x00,0x00,0x01, 0x01,0x00,0x7F,0xFF,0xFF,0xFF] },
-    { v:[-1,'A'],                                 b:[0x00,0x00,0x00,0x02, 0x01,0x00,0xFF,0xFF,0xFF,0xFF, 0x03,0x00,0x00,0x00,0x00,0x01,0x41] },
+    { v:[],                                 b:[0x00,0x00,0x00,0x00] },
+    { v:[2147483647],                       b:[0x00,0x00,0x00,0x01, 0x01,0x00,0x7F,0xFF,0xFF,0xFF] },
+    { v:[-1,'A'],                           b:[0x00,0x00,0x00,0x02, 0x01,0x00,0xFF,0xFF,0xFF,0xFF, 0x03,0x00,0x00,0x00,0x00,0x01,0x41] },
 
     { des:1, err:/buffer is missing/,       fq:1, b:undefined },
     { des:1, err:/buffer is missing/,       fq:0, b:undefined },
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/SetSerializerTestTemplate.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/SetSerializerTestTemplate.js
index 781a759eac..f151ae952d 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/SetSerializerTestTemplate.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphbinary/SetSerializerTestTemplate.js
@@ -21,9 +21,9 @@
  * @author Igor Ostapenko
  */
 
-import { des_title, cbuf_title } from './utils.js';
+import {des_title, cbuf_title, ser_title} from './utils.js';
 import assert from 'assert';
-import { serializers } from '../../../lib/structure/io/binary/GraphBinary.js';
+import {intSerializer, serializers} from '../../../lib/structure/io/binary/GraphBinary.js';
 import { Traverser } from '../../../lib/process/traversal.js';
 
 const { from, concat } = Buffer;
@@ -43,9 +43,9 @@ describe(`GraphBinary.${name}Serializer`, () => {
     { v:null,                               fq:1, b:[ID,0x01] },
     { v:null,                               fq:0, b:[0x00,0x00,0x00,0x00], av:new Set() },
 
-    { v:new Set(),                              b:[0x00,0x00,0x00,0x00] },
-    { v:new Set([2147483647]),                    b:[0x00,0x00,0x00,0x01, 0x01,0x00,0x7F,0xFF,0xFF,0xFF] },
-    { v:new Set([-1,'A']),                        b:[0x00,0x00,0x00,0x02, 0x01,0x00,0xFF,0xFF,0xFF,0xFF, 0x03,0x00,0x00,0x00,0x00,0x01,0x41] },
+    { v:new Set(),                          b:[0x00,0x00,0x00,0x00] },
+    { v:new Set([2147483647]),       b:[0x00,0x00,0x00,0x01, 0x01,0x00,0x7F,0xFF,0xFF,0xFF] },
+    { v:new Set([-1,'A']),           b:[0x00,0x00,0x00,0x02, 0x01,0x00,0xFF,0xFF,0xFF,0xFF, 0x03,0x00,0x00,0x00,0x00,0x01,0x41] },
 
     { des:1, err:/buffer is missing/,       fq:1, b:undefined },
     { des:1, err:/buffer is missing/,       fq:0, b:undefined },
@@ -72,6 +72,27 @@ describe(`GraphBinary.${name}Serializer`, () => {
     { des:1, err:/{length} is less than zero/,    b:[0x80,0x00,0x00,0x00] },
   ];
 
+  describe('#serialize', () => {
+    cases
+        .filter(({des}) => !des)
+        .forEach(({ v, fq, b }, i) => it(ser_title({i,v}), () => {
+            b = from(b);
+
+            // when fq is under control
+            if (fq !== undefined) {
+                assert.deepEqual( serializer.serialize(v, fq), b );
+                return;
+            }
+
+            // generic case
+            assert.deepEqual( serializer.serialize(v, true),  concat([type_code, value_flag, b]) );
+            assert.deepEqual( serializer.serialize(v, false), concat([                       b]) );
+        }));
+
+    it.skip('should not error if set length is INT32_MAX');
+
+  });
+
   describe('#deserialize', () =>
     cases.forEach(({ v, fq, b, av, err }, i) => it(des_title({i,b}), () => {
       if (Array.isArray(b))
diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4
index defd98a588..57bfcca445 100644
--- a/gremlin-language/src/main/antlr4/Gremlin.g4
+++ b/gremlin-language/src/main/antlr4/Gremlin.g4
@@ -1604,6 +1604,10 @@ genericLiteralRange
     | stringLiteral DOT DOT stringLiteral
     ;
 
+genericLiteralSet
+    : LBRACE (genericLiteral (COMMA genericLiteral)*)? RBRACE
+    ;
+
 genericLiteralCollection
     : LBRACK (genericLiteral (COMMA genericLiteral)*)? RBRACK
     ;
@@ -1637,6 +1641,7 @@ genericLiteral
     | traversalPick
     | traversalDT
     | structureVertex
+    | genericLiteralSet
     | genericLiteralCollection
     | genericLiteralRange
     | nestedTraversal
@@ -1655,6 +1660,7 @@ mapEntry
     | (LPAREN numericLiteral RPAREN | numericLiteral) COLON genericLiteral
     | (LPAREN traversalToken RPAREN | traversalToken) COLON genericLiteral
     | (LPAREN traversalDirection RPAREN | traversalDirection) COLON genericLiteral
+    | (LPAREN genericLiteralSet RPAREN | genericLiteralSet) COLON genericLiteral
     | (LPAREN genericLiteralCollection RPAREN | genericLiteralCollection) COLON genericLiteral
     | (LPAREN genericLiteralMap RPAREN | genericLiteralMap) COLON genericLiteral
     | keyword COLON genericLiteral
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index 58d94de046..cb1c98084c 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -1465,7 +1465,9 @@ world.gremlins = {
     'g_VX1X_valuesXageX_injectXnullX': [(lambda g, xx1=None:g.V(xx1).values('age').inject(None))], 
     'g_VX1X_valuesXageX_inject': [(lambda g, xx1=None:g.V(xx1).values('age').inject())], 
     'g_injectXnull_1_3_nullX_asXaX_selectXaX': [(lambda g:g.inject(None, 1, 3, None).as_('a').select('a'))], 
-    'g_injectX1_3lX_injectX100_300X': [(lambda g:g.inject(1, 3).inject(100, 300))], 
+    'g_injectX1_3X_injectX100_300X': [(lambda g:g.inject(1, 3).inject(100, 300))], 
+    'g_injectX1_3_100_300X_list': [(lambda g:g.inject([1, 3, 100, 300]))], 
+    'g_injectX1_3_100_300X_set': [(lambda g:g.inject({1, 3, 100, 300}))], 
     'g_io_readXkryoX': [(lambda g:g.io('data/tinkerpop-modern.kryo').read()), (lambda g:g.V()), (lambda g:g.E())], 
     'g_io_read_withXreader_gryoX': [(lambda g:g.io('data/tinkerpop-modern.kryo').with_(IO.reader, IO.gryo).read()), (lambda g:g.V()), (lambda g:g.E())], 
     'g_io_readXgraphsonX': [(lambda g:g.io('data/tinkerpop-modern.json').read()), (lambda g:g.V()), (lambda g:g.E())], 
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Inject.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Inject.feature
index e19e6d87f2..2aed3b3926 100644
--- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Inject.feature
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Inject.feature
@@ -205,7 +205,7 @@ Feature: Step - inject()
       | null |
 
   @GraphComputerVerificationInjectionNotSupported
-  Scenario: g_injectX1_3lX_injectX100_300X
+  Scenario: g_injectX1_3X_injectX100_300X
     Given the modern graph
     And the traversal of
       """
@@ -217,4 +217,28 @@ Feature: Step - inject()
       | d[100].i |
       | d[300].i |
       | d[1].i |
-      | d[3].i |
\ No newline at end of file
+      | d[3].i |
+
+  @GraphComputerVerificationInjectionNotSupported
+  Scenario: g_injectX1_3_100_300X_list
+    Given the modern graph
+    And the traversal of
+      """
+      g.inject([1, 3, 100, 300])
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | l[d[1].i,d[3].i,d[100].i,d[300].i] |
+
+  @GraphComputerVerificationInjectionNotSupported
+  Scenario: g_injectX1_3_100_300X_set
+    Given the modern graph
+    And the traversal of
+      """
+      g.inject({1, 3, 100, 300})
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | s[d[100].i,d[300].i,d[1].i,d[3].i] |
\ No newline at end of file