Skip to content

Commit 1ea635f

Browse files
committed
Using different identifier for fields and variables
1 parent 573b173 commit 1ea635f

File tree

9 files changed

+153
-80
lines changed

9 files changed

+153
-80
lines changed

src/main/antlr4/dev/vepo/jsonata/functions/generated/JSONataGrammar.g4

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ object: OBJ_OPEN fieldList OBJ_CLOSE #
88
expression:
99
functionStatement # functionCall
1010
| ROOT # rootPath
11-
| DOLLAR IDENTIFIER # variableUsage
1211
| IDENTIFIER # identifier
12+
| FV_NAME # variableUsage
1313
| '*' # fieldValues
1414
| DESCEND # allDescendantSearch
1515
| DOLLAR # contextReferece
@@ -28,18 +28,18 @@ expression:
2828
| expression '&' expression # concatValues
2929
| '(' expression ')' # contextValue
3030
| '(' expression ';' (expression ';')+ ')' # blockExpression
31-
| IDENTIFIER VAR_ASSIGN (expression|functionDeclaration) # variableAssignment
31+
| FV_NAME VAR_ASSIGN (expression|functionDeclaration) # variableAssignment
3232
| STRING # stringValue
3333
| NUMBER # numberValue
3434
| FLOAT # floatValue
3535
| EXP_NUMBER # expNumberValue
3636
| BOOLEAN # booleanValue
3737
;
3838

39-
functionStatement: IDENTIFIER '(' parameterStatement (',' parameterStatement)* ')' ;
39+
functionStatement: FV_NAME '(' parameterStatement (',' parameterStatement)* ')' ;
4040
parameterStatement: expression | functionDeclaration;
4141
functionDeclaration:
42-
'function' '(' IDENTIFIER (',' IDENTIFIER)* ')' '{' expression+ '}' # functionDeclarationBuilder
42+
'function' '(' FV_NAME (',' FV_NAME)* ')' '{' expression+ '}' # functionDeclarationBuilder
4343
;
4444
expressionList: expression (',' expression)*;
4545
fieldList: expression ':' uniqueObj expOrObject (',' expression ':' uniqueObj expOrObject)*;
@@ -57,7 +57,9 @@ OBJ_CLOSE: '}';
5757
VAR_ASSIGN : ':=' ;
5858
uniqueObj: (DOLLAR DOT)?;
5959

60-
IDENTIFIER: [\p{L}_$] [\p{L}0-9_$]*
60+
FV_NAME: DOLLAR IDENTIFIER;
61+
62+
IDENTIFIER: [\p{L}_] [\p{L}0-9_$]*
6163
| BACK_QUOTE ~[`]* BACK_QUOTE;
6264
fragment BACK_QUOTE : '`';
6365

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package dev.vepo.jsonata.functions;
2+
3+
import static dev.vepo.jsonata.functions.json.JsonFactory.objectBuilder;
4+
5+
import java.util.ArrayList;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.Optional;
10+
import java.util.Queue;
11+
import java.util.function.Predicate;
12+
13+
import dev.vepo.jsonata.functions.data.Data;
14+
15+
public class BlockContext {
16+
private final Map<String, JSONataFunction> variables;
17+
private final Map<String, DeclaredFunction> functions;
18+
private final List<BlockContext> parentContexts;
19+
20+
public BlockContext(Queue<BlockContext> parentContexts) {
21+
this.variables = new HashMap<>();
22+
this.functions = new HashMap<>();
23+
this.parentContexts = new ArrayList<>(parentContexts);
24+
}
25+
26+
public void defineVariable(String identifier, JSONataFunction variableExpression) {
27+
variables.put(identifier, variableExpression);
28+
}
29+
30+
public void defineFunction(String identifier, DeclaredFunction fn) {
31+
functions.put(identifier, fn);
32+
}
33+
34+
public Optional<DeclaredFunction> function(String identifier) {
35+
return Optional.ofNullable(functions.get(identifier)).or(() -> findFunctionOnParent(identifier));
36+
}
37+
38+
public Optional<JSONataFunction> variable(String identifier) {
39+
return Optional.ofNullable(variables.get(identifier)).or(() -> findVariableOnParent(identifier));
40+
}
41+
42+
public Data variables(Data original, Data current) {
43+
var builder = objectBuilder();
44+
variables.forEach((key, definition) -> builder.set(key, definition.map(original, current)));
45+
parentContexts.forEach(context -> context.variables.forEach((key, definition) -> {
46+
if (!builder.hasValue(key)) {
47+
builder.set(key, definition.map(original, current));
48+
}
49+
}));
50+
return builder.build();
51+
}
52+
53+
private Optional<DeclaredFunction> findFunctionOnParent(String identifier) {
54+
return parentContexts.stream()
55+
.map(c -> c.function(identifier))
56+
.filter(Predicate.not(Optional::isEmpty))
57+
.map(Optional::get)
58+
.findFirst();
59+
}
60+
61+
private Optional<JSONataFunction> findVariableOnParent(String identifier) {
62+
return parentContexts.stream()
63+
.map(c -> c.variable(identifier))
64+
.filter(Predicate.not(Optional::isEmpty))
65+
.map(Optional::get)
66+
.findFirst();
67+
}
68+
69+
}

src/main/java/dev/vepo/jsonata/functions/BockContext.java

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/main/java/dev/vepo/jsonata/functions/BuiltInSortJSONataFunction.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dev.vepo.jsonata.functions;
22

3-
import java.util.Comparator;
43
import java.util.List;
54
import java.util.Optional;
65
import java.util.stream.IntStream;
@@ -10,21 +9,28 @@
109
import dev.vepo.jsonata.functions.data.GroupedData;
1110

1211
public record BuiltInSortJSONataFunction(List<JSONataFunction> providers, Optional<DeclaredFunction> function,
13-
Comparator<Data> comparator)
12+
SortComparator comparator)
1413
implements JSONataFunction {
1514
public BuiltInSortJSONataFunction {
1615
if (providers.size() != 1) {
1716
throw new IllegalArgumentException("Sort function must have 1 argument");
1817
}
1918
}
2019

20+
@FunctionalInterface
21+
public interface SortComparator {
22+
int compare(Data original, Data current, Data left, Data right);
23+
}
24+
2125
public BuiltInSortJSONataFunction(List<JSONataFunction> providers, Optional<DeclaredFunction> function) {
2226
this(providers, function, buildComparator(function));
2327
}
2428

25-
private static Comparator<Data> buildComparator(DeclaredFunction fn) {
26-
return (left, right) -> {
27-
var result = fn.accept(left, right);
29+
private static SortComparator buildComparator(DeclaredFunction fn) {
30+
return (original, current, left, right) -> {
31+
fn.context().defineVariable(fn.parameterNames().get(0), (o, c) -> left);
32+
fn.context().defineVariable(fn.parameterNames().get(1), (o, c) -> right);
33+
var result = fn.accept(original, current, fn.context());
2834
if (result.toJson().isInt()) {
2935
return result.toJson().asInt();
3036
} else if (result.toJson().isBoolean()) {
@@ -35,7 +41,7 @@ private static Comparator<Data> buildComparator(DeclaredFunction fn) {
3541
};
3642
}
3743

38-
private static int defaultComparator(Data left, Data right) {
44+
private static int defaultComparator(Data original, Data current, Data left, Data right) {
3945
if (left.toJson().isInt()) {
4046
return Integer.compare(left.toJson().asInt(), right.toJson().asInt());
4147
} else if (left.toJson().isTextual()) {
@@ -45,7 +51,7 @@ private static int defaultComparator(Data left, Data right) {
4551
}
4652
}
4753

48-
private static Comparator<Data> buildComparator(Optional<DeclaredFunction> fn) {
54+
private static SortComparator buildComparator(Optional<DeclaredFunction> fn) {
4955
return fn.map(BuiltInSortJSONataFunction::buildComparator)
5056
.orElse(BuiltInSortJSONataFunction::defaultComparator);
5157
}
@@ -56,7 +62,7 @@ public Data map(Data original, Data current) {
5662
if (sortValue.isArray()) {
5763
return new GroupedData(IntStream.range(0, sortValue.length())
5864
.mapToObj(sortValue::at)
59-
.sorted(comparator)
65+
.sorted((left, right) -> comparator.compare(original, current, left, right))
6066
.toList());
6167
} else {
6268
return sortValue;
Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
package dev.vepo.jsonata.functions;
22

3+
import static dev.vepo.jsonata.functions.json.JsonFactory.objectBuilder;
4+
35
import java.util.List;
4-
import java.util.stream.IntStream;
56

67
import dev.vepo.jsonata.functions.data.Data;
7-
import dev.vepo.jsonata.functions.json.JsonFactory;
88

9-
public record DeclaredFunction(List<String> parameterNames, JSONataFunction functions) {
10-
Data accept(Data... args) {
11-
var input = JsonFactory.objectBuilder();
12-
IntStream.range(0, parameterNames.size())
13-
.forEach(i -> input.set(parameterNames.get(i), args[i]));
14-
var inputData = input.build();
9+
public record DeclaredFunction(List<String> parameterNames, BlockContext context, JSONataFunction functions) {
1510

16-
return functions.map(inputData, inputData);
11+
Data accept(Data original, Data current, BlockContext context) {
12+
var builder = objectBuilder();
13+
builder.fill(current);
14+
builder.fill(context.variables(original, current));
15+
return functions.map(original, builder.build());
1716
}
1817
}
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
package dev.vepo.jsonata.functions;
22

33
import java.util.List;
4+
import java.util.stream.IntStream;
45

56
import dev.vepo.jsonata.functions.data.Data;
67

78
public record UserDefinedFunctionJSONataFunction(List<JSONataFunction> valueProviders, DeclaredFunction fn) implements JSONataFunction {
89

910
@Override
1011
public Data map(Data original, Data current) {
11-
return fn.accept(valueProviders.stream()
12-
.map(provider -> provider.map(original, current))
13-
.toArray(Data[]::new));
12+
IntStream.range(0, fn.parameterNames().size())
13+
.forEach(i -> fn.context()
14+
.defineVariable(fn.parameterNames().get(i), valueProviders.get(i)));
15+
return fn.accept(original, current, fn.context());
1416
}
1517

1618
}

src/main/java/dev/vepo/jsonata/functions/json/JsonFactory.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public static Stream<Data> planify(JsonNode value) {
5454
public static Data numberValue(Integer value) {
5555
return new ObjectData(mapper.getNodeFactory().numberNode(value));
5656
}
57+
5758
public static Data numberValue(BigDecimal value) {
5859
return new ObjectData(mapper.getNodeFactory().numberNode(value));
5960
}
@@ -84,7 +85,8 @@ public static ArrayNode arrayNode(List<JsonNode> elements) {
8485
return array;
8586
}
8687

87-
private JsonFactory() {}
88+
private JsonFactory() {
89+
}
8890

8991
public static record ObjectBuilder(ObjectNode root, boolean groupRecordsInArray) {
9092

@@ -159,6 +161,16 @@ public void add(String field, Data value) {
159161
root.set(field, arr);
160162
}
161163
}
164+
165+
public void fill(Data current) {
166+
current.toJson()
167+
.fields()
168+
.forEachRemaining(entry -> root.set(entry.getKey(), entry.getValue()));
169+
}
170+
171+
public boolean hasValue(String key) {
172+
return root.has(key);
173+
}
162174
}
163175

164176
public static ObjectBuilder objectBuilder() {

0 commit comments

Comments
 (0)