Skip to content

Commit 619e1f7

Browse files
committed
TINKERPOP-3047 Keywords are allowed as Map keys in grammar.
Major refactoring to the grammar itself, translators. Removed a few gherkin tests that tested lambdas which weren't really necessary given they will be reserved for embedded cases. Changed infinity/nan to be able to parse to float. Removed duplicate test from SeedStrategy.feature - see coin tests. Removed gremlinValue but left some test infrastructure for future.
1 parent bd9be2c commit 619e1f7

42 files changed

Lines changed: 2240 additions & 1529 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ This release also includes changes from <<release-3-7-XXX, 3.7.XXX>>.
3434
* Removed the deprecated `withGraph()` option from `AnonymousTraversalSource`.
3535
* Modified the `split()` step to split a string into a list of its characters if the given separator is an empty string.
3636
* Changed `GremlinLangScriptEngine` via `GenericLiteralVisitor` to treat floating-point literals as `Double` by default instead of `BigDecimal` for better alignment with common programming language conventions.
37+
* Modified the Gremlin grammar to to treat `Infinity` and `NaN` as floating-points.
3738
* Added `withoutStrategies()` syntax to the Gremlin ANTLR grammar.
3839
* Modified the Gremlin ANTLR grammar to more dynamically interact with any strategies registered globally to the `TraversalStrategies` cache sets.
3940
* Made `new` keyword optional in the Gremlin grammar.
41+
* Allowed keywords to be used as `Map` keys when defined using the shorthand without quotes.
4042
* Added integer overflow checks.
4143
* Added missing strategies to the `TraversalStrategies` global cache as well as `CoreImports` in `gremlin-groovy`.
4244
* Added missing strategies to `strategies.py` in `gremlin-python`.

docs/src/upgrade/release-3.8.x.asciidoc

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ these were deserialized into arrays.
7979
8080
==== Gremlin Grammar Changes
8181
82-
A number of changes have been introduce to the Gremlin grammar to help make it be more consistent and easier to use.
82+
A number of changes have been introduced to the Gremlin grammar to help make it be more consistent and easier to use.
8383
8484
*`new` keyword is now optional*
8585
@@ -98,16 +98,79 @@ In a future version, it is likely that the `new` keyword will be removed entirel
9898
*Supports withoutStrategies()*
9999
100100
The `withoutStrategies()` configuration step is now supported syntax for the grammar. While this option is not commonly
101-
used it is still a part of the Gremlin language and there are times where it is helpful to have this fine grained
101+
used it is still a part of the Gremlin language and there are times where it is helpful to have this fine-grained
102102
control over how a traversal works.
103103
104104
[source,groovy]
105105
----
106106
g.V().withoutStrategies(CountStrategy)
107107
----
108108
109+
*`Map` keys restrictions*
110+
111+
Earlier versions of the grammar allowed a wide range of values for the keys. In many cases, these didn't really make
112+
sense for Gremlin and were just inherited from the Groovy language since Gremlin tends to follow that language in many
113+
ways. That said, Gremlin did take some liberties with that syntax and introduced its own shorthand for some cases. Those
114+
shorthands created unfortunate situations where certain words were being prevented as being able to be used as keys
115+
which could lead to confusion.
116+
117+
A `Map` is still defined in the same way it always has been, where the following two lines produce an equivalent `Map`:
118+
119+
[source,groovy]
120+
----
121+
[label: 100]
122+
["label": 100]
123+
----
124+
125+
Note that when quotes are not use to denote a string, Gremlin will assume that the intention is to shorthand a string
126+
key and not reference a Gremlin keyword. To reference an allowable keyword as the key, either wrap it with parenthesis
127+
or use its longhand form as shown in the following examples which all produce the same `Map`:
128+
129+
[source,groovy]
130+
----
131+
[T.id: 100]
132+
[(T.id): 100]
133+
[(id): 100]
134+
----
135+
136+
Note that the first example is a Gremlin convenience that is not compatible in Groovy. This does produce a syntax error
137+
in Groovy's case. When upgrading to 3.8.0, it will be important to evaluate any code that is using scripts with `Map`
138+
keys that match keywords that are not wrapped in parentheses. On upgrade they will begin to be treated as `String` keys
139+
rather than their `Enum` value. This is particularly relevant for `property(Map)`, `mergeV` and `mergeE` which use a
140+
'Map` for their arguments and commonly require that `T` and `Direction` be used as keys.
141+
142+
The following examples show some `Map` usage from older versions that will work without need for changes in 3.8.0:
143+
144+
[source,groovy]
145+
----
146+
// the long forms are used and each are wrapped in parenthesis
147+
g.mergeE([(T.label):'Sibling',created:'2022-02-07',(Direction.from):1,(Direction.to):2])
148+
149+
// the short forms are used and each are wrapped in parenthesis
150+
g.mergeE([(label):'Sibling',created:'2022-02-07',(Direction.from):1,(Direction.to):2])
151+
152+
// the long forms are used and for Gremlin this is a syntax convenience to spare typing
153+
// the parenthesis
154+
g.mergeE([T.label:'Sibling',created:'2022-02-07',Direction.from:1,Direction.to:2])
155+
156+
// while the following line mixes qualified enums with T and uses shorthand for Direction
157+
// with from and to all of the enums are wrapped in parenthesis
158+
g.mergeE([(T.label):'Sibling',created:'2022-02-07',(from):1,(to):2])
159+
----
160+
161+
In this next example, the `Map` keys are defined in a way that changes will be necessary in 3.8.0:
162+
163+
[source,groovy]
164+
----
165+
// none of the keys below are qualified with their enum long form nor are they wrapped in
166+
// parenthesis and as a result will be treated as String key values in 3.8.0 unless a
167+
// change is made
168+
g.mergeE([label:'Sibling',created:'2022-02-07',from:1,to:2])
169+
----
170+
109171
See: link:https://issues.apache.org/jira/browse/TINKERPOP-2862[TINKERPOP-2862],
110-
link:https://issues.apache.org/jira/browse/TINKERPOP-3046[TINKERPOP-3046]
172+
link:https://issues.apache.org/jira/browse/TINKERPOP-3046[TINKERPOP-3046],
173+
link:https://issues.apache.org/jira/browse/TINKERPOP-3047[TINKERPOP-3047]
111174
112175
==== SeedStrategy Construction
113176

gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitor.java

Lines changed: 12 additions & 211 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,13 @@
1818
*/
1919
package org.apache.tinkerpop.gremlin.language.grammar;
2020

21-
import org.apache.tinkerpop.gremlin.process.traversal.DT;
22-
import org.apache.tinkerpop.gremlin.process.traversal.Merge;
23-
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
24-
import org.apache.tinkerpop.gremlin.process.traversal.Order;
25-
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
26-
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
27-
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
28-
import org.apache.tinkerpop.gremlin.structure.Column;
29-
import org.apache.tinkerpop.gremlin.structure.Direction;
30-
import org.apache.tinkerpop.gremlin.structure.T;
3121
import org.apache.tinkerpop.gremlin.structure.Vertex;
32-
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
3322
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
3423

3524
import java.lang.reflect.Array;
36-
import java.util.Comparator;
3725
import java.util.Date;
3826
import java.util.Map;
39-
import java.util.function.Function;
27+
import java.util.Objects;
4028

4129
public class ArgumentVisitor extends DefaultGremlinBaseVisitor<Object> {
4230

@@ -91,90 +79,13 @@ public Object parseObject(final GremlinParser.GenericLiteralArgumentContext ctx)
9179
return visitGenericLiteralArgument(ctx);
9280
}
9381

94-
/**
95-
* Wrapper for visit function for {@link Direction} types.
96-
*/
97-
public Direction parseDirection(final GremlinParser.TraversalDirectionArgumentContext ctx) {
98-
return (Direction) visitTraversalDirectionArgument(ctx);
99-
}
100-
10182
/**
10283
* Wrapper for visit function for {@link Vertex} types.
10384
*/
10485
public Vertex parseVertex(final GremlinParser.StructureVertexArgumentContext ctx) {
10586
return (Vertex) visitStructureVertexArgument(ctx);
10687
}
10788

108-
/**
109-
* Wrapper for visit function for {@link Order} types.
110-
*/
111-
public Order parseOrder(final GremlinParser.TraversalOrderArgumentContext ctx) {
112-
return (Order) visitTraversalOrderArgument(ctx);
113-
}
114-
115-
/**
116-
* Wrapper for visit function for {@link Scope} types.
117-
*/
118-
public Scope parseScope(final GremlinParser.TraversalScopeArgumentContext ctx) {
119-
return (Scope) visitTraversalScopeArgument(ctx);
120-
}
121-
122-
/**
123-
* Wrapper for visit function for {@link T} types.
124-
*/
125-
public T parseT(final GremlinParser.TraversalTokenArgumentContext ctx) {
126-
return (T) visitTraversalTokenArgument(ctx);
127-
}
128-
129-
/**
130-
* Wrapper for visit function for {@link VertexProperty.Cardinality} types.
131-
*/
132-
public VertexProperty.Cardinality parseCardinality(final GremlinParser.TraversalCardinalityArgumentContext ctx) {
133-
return (VertexProperty.Cardinality) visitTraversalCardinalityArgument(ctx);
134-
}
135-
136-
/**
137-
* Wrapper for visit function for {@link Merge} types.
138-
*/
139-
public Merge parseMerge(final GremlinParser.TraversalMergeArgumentContext ctx) {
140-
return (Merge) visitTraversalMergeArgument(ctx);
141-
}
142-
143-
/**
144-
* Wrapper for visit function for {@link Pop} types.
145-
*/
146-
public Pop parsePop(final GremlinParser.TraversalPopArgumentContext ctx) {
147-
return (Pop) visitTraversalPopArgument(ctx);
148-
}
149-
150-
/**
151-
* Wrapper for visit function for {@link DT} types.
152-
*/
153-
public DT parseDT(final GremlinParser.TraversalDTArgumentContext ctx) {
154-
return (DT) visitTraversalDTArgument(ctx);
155-
}
156-
157-
/**
158-
* Wrapper for visit function for {@link Pop} types.
159-
*/
160-
public Column parseColumn(final GremlinParser.TraversalColumnArgumentContext ctx) {
161-
return (Column) visitTraversalColumnArgument(ctx);
162-
}
163-
164-
/**
165-
* Wrapper for visit function for {@code Function} types like {@link T} and {@link Column}.
166-
*/
167-
public Function parseFunction(final GremlinParser.TraversalFunctionArgumentContext ctx) {
168-
return (Function) visitTraversalFunctionArgument(ctx);
169-
}
170-
171-
/**
172-
* Wrapper for visit function for {@code Comparator} types like {@link Order}.
173-
*/
174-
public Comparator parseComparator(final GremlinParser.TraversalComparatorArgumentContext ctx) {
175-
return (Comparator) visitTraversalComparatorArgument(ctx);
176-
}
177-
17889
/**
17990
* Wrapper for visit function for {@code Map} types.
18091
*/
@@ -293,15 +204,6 @@ public Object visitGenericLiteralListArgument(final GremlinParser.GenericLiteral
293204
}
294205
}
295206

296-
@Override
297-
public Object visitTraversalDirectionArgument(final GremlinParser.TraversalDirectionArgumentContext ctx) {
298-
if (ctx.traversalDirection() != null) {
299-
return TraversalEnumParser.parseTraversalDirectionFromContext(ctx.traversalDirection());
300-
} else {
301-
return visitVariable(ctx.variable());
302-
}
303-
}
304-
305207
@Override
306208
public Object visitStructureVertexArgument(final GremlinParser.StructureVertexArgumentContext ctx) {
307209
if (ctx.structureVertex() != null) {
@@ -311,112 +213,6 @@ public Object visitStructureVertexArgument(final GremlinParser.StructureVertexAr
311213
}
312214
}
313215

314-
@Override
315-
public Object visitTraversalOrderArgument(final GremlinParser.TraversalOrderArgumentContext ctx) {
316-
if (ctx.traversalOrder() != null) {
317-
return TraversalEnumParser.parseTraversalEnumFromContext(Order.class, ctx.traversalOrder());
318-
} else {
319-
return visitVariable(ctx.variable());
320-
}
321-
}
322-
323-
@Override
324-
public Object visitTraversalScopeArgument(final GremlinParser.TraversalScopeArgumentContext ctx) {
325-
if (ctx.traversalScope() != null) {
326-
return TraversalEnumParser.parseTraversalEnumFromContext(Scope.class, ctx.traversalScope());
327-
} else {
328-
return visitVariable(ctx.variable());
329-
}
330-
}
331-
332-
@Override
333-
public Object visitTraversalTokenArgument(final GremlinParser.TraversalTokenArgumentContext ctx) {
334-
if (ctx.traversalToken() != null) {
335-
return TraversalEnumParser.parseTraversalEnumFromContext(T.class, ctx.traversalToken());
336-
} else {
337-
return visitVariable(ctx.variable());
338-
}
339-
}
340-
341-
@Override
342-
public Object visitTraversalCardinalityArgument(final GremlinParser.TraversalCardinalityArgumentContext ctx) {
343-
if (ctx.traversalCardinality() != null) {
344-
return TraversalEnumParser.parseTraversalEnumFromContext(VertexProperty.Cardinality.class, ctx.traversalCardinality());
345-
} else {
346-
return visitVariable(ctx.variable());
347-
}
348-
}
349-
350-
@Override
351-
public Object visitTraversalMergeArgument(final GremlinParser.TraversalMergeArgumentContext ctx) {
352-
if (ctx.traversalMerge() != null) {
353-
return TraversalEnumParser.parseTraversalEnumFromContext(Merge.class, ctx.traversalMerge());
354-
} else {
355-
return visitVariable(ctx.variable());
356-
}
357-
}
358-
359-
@Override
360-
public Object visitTraversalPopArgument(final GremlinParser.TraversalPopArgumentContext ctx) {
361-
if (ctx.traversalPop() != null) {
362-
return TraversalEnumParser.parseTraversalEnumFromContext(Pop.class, ctx.traversalPop());
363-
} else {
364-
return visitVariable(ctx.variable());
365-
}
366-
}
367-
368-
@Override
369-
public Object visitTraversalDTArgument(final GremlinParser.TraversalDTArgumentContext ctx) {
370-
if (ctx.traversalDT() != null) {
371-
return TraversalEnumParser.parseTraversalEnumFromContext(DT.class, ctx.traversalDT());
372-
} else {
373-
return visitVariable(ctx.variable());
374-
}
375-
}
376-
377-
@Override
378-
public Object visitTraversalColumnArgument(final GremlinParser.TraversalColumnArgumentContext ctx) {
379-
if (ctx.traversalColumn() != null) {
380-
return TraversalEnumParser.parseTraversalEnumFromContext(Column.class, ctx.traversalColumn());
381-
} else {
382-
return visitVariable(ctx.variable());
383-
}
384-
}
385-
386-
@Override
387-
public Object visitTraversalComparatorArgument(final GremlinParser.TraversalComparatorArgumentContext ctx) {
388-
if (ctx.traversalComparator() != null) {
389-
return TraversalEnumParser.parseTraversalEnumFromContext(Order.class, ctx.traversalComparator().traversalOrder());
390-
} else {
391-
return visitVariable(ctx.variable());
392-
}
393-
}
394-
395-
@Override
396-
public Object visitTraversalFunctionArgument(final GremlinParser.TraversalFunctionArgumentContext ctx) {
397-
if (ctx.traversalFunction() != null) {
398-
final GremlinParser.TraversalFunctionContext tfc = ctx.traversalFunction();
399-
if (tfc.traversalToken() != null) {
400-
return TraversalEnumParser.parseTraversalEnumFromContext(T.class, tfc.traversalToken());
401-
} else if (tfc.traversalColumn() != null)
402-
return TraversalEnumParser.parseTraversalEnumFromContext(Column.class, tfc.traversalColumn());
403-
else {
404-
throw new GremlinParserException("Unrecognized enum for traversal function");
405-
}
406-
} else {
407-
return visitVariable(ctx.variable());
408-
}
409-
}
410-
411-
@Override
412-
public Object visitTraversalBiFunctionArgument(final GremlinParser.TraversalBiFunctionArgumentContext ctx) {
413-
if (ctx.traversalBiFunction() != null) {
414-
return TraversalEnumParser.parseTraversalEnumFromContext(Operator.class, ctx.traversalBiFunction().traversalOperator());
415-
} else {
416-
return visitVariable(ctx.variable());
417-
}
418-
}
419-
420216
@Override
421217
public Object visitGenericLiteralMapArgument(final GremlinParser.GenericLiteralMapArgumentContext ctx) {
422218
if (ctx.genericLiteralMap() != null) {
@@ -437,13 +233,18 @@ public Object visitGenericLiteralMapNullableArgument(final GremlinParser.Generic
437233
}
438234
}
439235

440-
@Override
441-
public Object visitTraversalSackMethodArgument(final GremlinParser.TraversalSackMethodArgumentContext ctx) {
442-
if (ctx.traversalSackMethod() != null) {
443-
return TraversalEnumParser.parseTraversalEnumFromContext(SackFunctions.Barrier.class, ctx.traversalSackMethod());
444-
} else {
445-
return visitVariable(ctx.variable());
236+
/**
237+
* Parse a string literal varargs, and return a string array
238+
*/
239+
public String[] parseStringVarargs(final GremlinParser.StringLiteralVarargsArgumentContext varargsArgumentContext) {
240+
if (varargsArgumentContext == null || varargsArgumentContext.stringNullableArgument() == null) {
241+
return new String[0];
446242
}
243+
return varargsArgumentContext.stringNullableArgument()
244+
.stream()
245+
.filter(Objects::nonNull)
246+
.map(antlr.argumentVisitor::parseString)
247+
.toArray(String[]::new);
447248
}
448249

449250
@Override

0 commit comments

Comments
 (0)