This is an automated email from the ASF dual-hosted git repository.
Cole-Greer pushed a commit to branch 3.7-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/3.7-dev by this push:
new 50de6da025 TINKERPOP-3151 Allow float literals without a leading digit
(#3558)
50de6da025 is described below
commit 50de6da0258b1a47681100f099eeec91702aad6d
Author: kirill-stepanishin <[email protected]>
AuthorDate: Thu Jul 23 17:25:08 2026 -0700
TINKERPOP-3151 Allow float literals without a leading digit (#3558)
Assisted-by: Claude Code:claude-opus-4-8
---
CHANGELOG.asciidoc | 1 +
.../language/grammar/GenericLiteralVisitor.java | 2 +-
.../grammar/GeneralLiteralVisitorTest.java | 11 +++-
.../Gherkin/GherkinTestRunner.cs | 3 +-
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 5 ++
.../Gherkin/IgnoreException.cs | 8 ++-
gremlin-go/driver/cucumber/gremlin.go | 5 ++
.../gremlin-javascript/test/cucumber/gremlin.js | 5 ++
gremlin-language/src/main/antlr4/Gremlin.g4 | 6 ++-
.../src/main/python/tests/feature/gremlin.py | 5 ++
.../test/features/sideEffect/Inject.feature | 60 ++++++++++++++++++++++
11 files changed, 105 insertions(+), 6 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 8abe3b199a..37f9e7e4c5 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -33,6 +33,7 @@
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Added `WithComputer()` to `GraphTraversalSource` in `gremlin-go`, providing
OLAP configuration parity with other language variants.
* Added `FailResponseException` to `gremlin-driver` which is thrown `fail()`
step is triggered on the server making it more consistent with embedded
behavior.
* Fixed `hasId()` to only unroll a collection when it is supplied as the
single argument, aligning its behavior with `g.V()`/`g.E()`.
+* Allowed float literals without a leading digit (e.g. `.5`, `.5f`, `.5d`) in
the Gremlin grammar to better match Groovy.
* Fixed conjoin has incorrect null handling.
* Removed the Mono dependency from the `gremlin-dotnet` build/release process,
using `dotnet pack`/`dotnet nuget push` instead of `mono nuget.exe`.
* Expanded `gremlin-python` CI matrix to test against Python 3.9, 3.10, 3.11,
3.12, and 3.13.
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 a4d5a8b641..24f4fffb6c 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
@@ -569,7 +569,7 @@ public class GenericLiteralVisitor extends
DefaultGremlinBaseVisitor<Object> {
@Override
public Object visitGenericLiteralRange(final
GremlinParser.GenericLiteralRangeContext ctx) {
final int childIndexOfParameterStart = 0;
- final int childIndexOfParameterEnd = 3;
+ final int childIndexOfParameterEnd = 2;
final ParseTree startContext =
ctx.getChild(childIndexOfParameterStart);
final ParseTree endContext = ctx.getChild(childIndexOfParameterEnd);
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 e39d9df55b..fe013cb3be 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
@@ -424,7 +424,16 @@ public class GeneralLiteralVisitorTest {
{"1.0E+12d", "1.0E12", "java.lang.Double"},
{"-0.1E-12D", "-0.1E-12", "java.lang.Double"},
{"1E12d", "1E12", "java.lang.Double"},
- {"1D", "1", "java.lang.Double"}
+ {"1D", "1", "java.lang.Double"},
+
+ // float literals without a leading digit (e.g. .5)
+ {".5", ".5", "java.math.BigDecimal"},
+ {".5m", ".5", "java.math.BigDecimal"},
+ {".5f", ".5", "java.lang.Float"},
+ {".5d", ".5", "java.lang.Double"},
+ {"-.5f", "-.5", "java.lang.Float"},
+ {".5E2", ".5E2", "java.math.BigDecimal"},
+ {".5E-2d", ".5E-2", "java.lang.Double"}
});
}
diff --git
a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
index 5462260cff..1d5199e377 100644
---
a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
+++
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
@@ -49,7 +49,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{
// Add here the name of scenarios to ignore and the reason,
e.g.:
{"g_withStrategiesXProductiveByStrategyX_V_group_byXageX",
IgnoreReason.NullKeysInMapNotSupported},
-
{"g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX",
IgnoreReason.NullKeysInMapNotSupported}
+
{"g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX",
IgnoreReason.NullKeysInMapNotSupported},
+ {"g_injectXpoint5fX",
IgnoreReason.FloatLiteralTypeNotPreserved}
};
private static class Keywords
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index ba2f9ab357..73378300f4 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -1539,6 +1539,11 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"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(1,3).Inject(100,300)}},
{"g_injectXbigintBoundaryValuesX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject(p["xx1"],p["xx2"],p["xx3"])}},
+ {"g_injectXpoint5X", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(0.5)}},
+ {"g_injectXpoint5fX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(0.5)}},
+ {"g_injectXpoint5dX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(0.5)}},
+ {"g_injectX1to5X", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(new List<object>
{1, 2, 3, 4, 5})}},
+ {"g_injectXaTocX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(new List<object>
{"a", "b", "c"})}},
{"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("~tinkerpop.io.reader","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-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs
index 3eb59d4f57..feafc86bf3 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/IgnoreException.cs
@@ -60,6 +60,12 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
/// <summary>
/// The GLV suite does not test against a graph that has null property
values enabled.
/// </summary>
- NullPropertyValuesNotSupportedOnTestGraph
+ NullPropertyValuesNotSupportedOnTestGraph,
+
+ /// <summary>
+ /// The translator does not preserve the numeric type of a float
literal, so a value such as
+ /// <c>.5f</c> is emitted as a bare <c>0.5</c> and read back as a
<c>double</c> rather than a <c>float</c>.
+ /// </summary>
+ FloatLiteralTypeNotPreserved
}
}
\ No newline at end of file
diff --git a/gremlin-go/driver/cucumber/gremlin.go
b/gremlin-go/driver/cucumber/gremlin.go
index 23cb7dfeba..d1de80cda4 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -1510,6 +1510,11 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"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_injectXbigintBoundaryValuesX": {func(g *gremlingo.GraphTraversalSource,
p map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(p["xx1"],
p["xx2"], p["xx3"])}},
+ "g_injectXpoint5X": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(0.5)}},
+ "g_injectXpoint5fX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(0.5)}},
+ "g_injectXpoint5dX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return g.Inject(0.5)}},
+ "g_injectX1to5X": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject([]interface{}{1, 2, 3, 4, 5})}},
+ "g_injectXaTocX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject([]interface{}{"a", "b", "c"})}},
"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("~tinkerpop.io.reader",
"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/test/cucumber/gremlin.js
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index 46f7b0d1a2..a83296fcd6 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
@@ -1530,6 +1530,11 @@ const gremlins = {
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_injectXbigintBoundaryValuesX: [function({g, xx1, xx3, xx2}) { return
g.inject(xx1,xx2,xx3) }],
+ g_injectXpoint5X: [function({g}) { return g.inject(0.5) }],
+ g_injectXpoint5fX: [function({g}) { return g.inject(0.5) }],
+ g_injectXpoint5dX: [function({g}) { return g.inject(0.5) }],
+ g_injectX1to5X: [function({g}) { return g.inject([1, 2, 3, 4, 5]) }],
+ g_injectXaTocX: [function({g}) { return g.inject(["a", "b", "c"]) }],
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_("~tinkerpop.io.reader","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-language/src/main/antlr4/Gremlin.g4
b/gremlin-language/src/main/antlr4/Gremlin.g4
index 0b7a224790..d19e1a95fc 100644
--- a/gremlin-language/src/main/antlr4/Gremlin.g4
+++ b/gremlin-language/src/main/antlr4/Gremlin.g4
@@ -1626,8 +1626,8 @@ genericLiteralExpr
;
genericLiteralRange
- : integerLiteral DOT DOT integerLiteral
- | stringLiteral DOT DOT stringLiteral
+ : integerLiteral RANGE integerLiteral
+ | stringLiteral RANGE stringLiteral
;
genericLiteralCollection
@@ -1875,6 +1875,7 @@ FloatingPointLiteral
fragment
DecimalFloatingPointLiteral
: Digits ('.' Digits ExponentPart? | ExponentPart) FloatTypeSuffix?
+ | '.' Digits ExponentPart? FloatTypeSuffix?
| Digits FloatTypeSuffix
;
@@ -2013,6 +2014,7 @@ LBRACK : '[';
RBRACK : ']';
SEMI : ';';
COMMA : ',';
+RANGE : '..';
DOT : '.';
COLON : ':';
diff --git a/gremlin-python/src/main/python/tests/feature/gremlin.py
b/gremlin-python/src/main/python/tests/feature/gremlin.py
index 93907ae78e..3580de30dd 100644
--- a/gremlin-python/src/main/python/tests/feature/gremlin.py
+++ b/gremlin-python/src/main/python/tests/feature/gremlin.py
@@ -1512,6 +1512,11 @@ world.gremlins = {
'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_injectXbigintBoundaryValuesX': [(lambda g,
xx1=None,xx3=None,xx2=None:g.inject(xx1,xx2,xx3))],
+ 'g_injectXpoint5X': [(lambda g:g.inject(float(0.5)))],
+ 'g_injectXpoint5fX': [(lambda g:g.inject(float(0.5)))],
+ 'g_injectXpoint5dX': [(lambda g:g.inject(float(0.5)))],
+ 'g_injectX1to5X': [(lambda g:g.inject([1,2,3,4,5]))],
+ 'g_injectXaTocX': [(lambda g:g.inject(['a','b','c']))],
'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_('~tinkerpop.io.reader','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 9acb57253a..148a7dea30 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
@@ -235,3 +235,63 @@ Feature: Step - inject()
| d[0].n |
| d[-129].n |
| d[-32769].n |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXpoint5X
+ Given the modern graph
+ And the traversal of
+ """
+ g.inject(.5)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[0.5].m |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXpoint5fX
+ Given the modern graph
+ And the traversal of
+ """
+ g.inject(.5f)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[0.5].f |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXpoint5dX
+ Given the modern graph
+ And the traversal of
+ """
+ g.inject(.5d)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[0.5].d |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectX1to5X
+ Given the modern graph
+ And the traversal of
+ """
+ g.inject(1..5)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | l[d[1].i,d[2].i,d[3].i,d[4].i,d[5].i] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXaTocX
+ Given the modern graph
+ And the traversal of
+ """
+ g.inject("a".."c")
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | l[a,b,c] |