Skip to content

Commit 10d49fb

Browse files
committed
Implementing operators
1 parent c9ce3e7 commit 10d49fb

File tree

22 files changed

+478
-153
lines changed

22 files changed

+478
-153
lines changed

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,15 @@ expression:
1111
| '*' # fieldValues
1212
| DESCEND # allDescendantSearch
1313
| DOLLAR # contextReferece
14+
| functionStatement # functionCall
1415
| ARR_OPEN expressionList ARR_CLOSE # arrayConstructor
16+
| expression ARR_OPEN ARR_CLOSE # toArray
17+
| expression ARR_OPEN NUMBER ARR_CLOSE # arrayIndexQuery
18+
| expression ARR_OPEN expression ARR_CLOSE # arrayQuery
1519
| expression DOT functionStatement # functionFeed
16-
| functionStatement # functionCall
1720
| expression DOT expression # path
1821
| expression DOT OBJ_OPEN fieldList OBJ_CLOSE # objectMapper
1922
| expression OBJ_OPEN fieldList OBJ_CLOSE # objectConstructor
20-
| expression ARR_OPEN ARR_CLOSE # toArray
21-
| expression ARR_OPEN NUMBER ARR_CLOSE # arrayIndexQuery
22-
| expression ARR_OPEN expression ARR_CLOSE # arrayQuery
23-
| expression rangePredicate # rangeQuery
2423
| expression op=('<' | '<=' | '>' | '>=' | '!=' | '=' | 'in') expression # booleanCompare
2524
| expression op=('and' | 'or') expression # booleanExpression
2625
| expression op=('+' | '-' | '*' | '/' | '%' | '^') expression # algebraicExpression
@@ -29,6 +28,8 @@ expression:
2928
| '(' expression ')' # contextValue
3029
| '(' expression ';' (expression ';')+ ')' # blockExpression
3130
| FV_NAME VAR_ASSIGN (expression|functionDeclaration) # variableAssignment
31+
// | ARR_OPEN expression '..' expression ARR_CLOSE # arrayExpansion
32+
| expression '..' expression # arrayExpansion
3233
| FV_NAME # variableUsage
3334
| REGEX # regexValue
3435
| STRING # stringValue
@@ -43,11 +44,10 @@ parameterStatement: expression | functionDeclaration;
4344
functionDeclaration:
4445
'function' '(' FV_NAME (',' FV_NAME)* ')' '{' expression+ '}' # functionDeclarationBuilder
4546
;
46-
expressionList: expression (',' expression)*;
47+
expressionList: (expression (',' expression)*)?;
4748
fieldList: expression ':' uniqueObj expOrObject (',' expression ':' uniqueObj expOrObject)*;
4849
expOrObject: expression | object;
4950

50-
rangePredicate: ARR_OPEN ARR_OPEN NUMBER '..' NUMBER ARR_CLOSE ARR_CLOSE;
5151
BOOLEAN: 'true' | 'false';
5252
ROOT : '$$' ;
5353
DOLLAR: '$';

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22

33
import static dev.vepo.jsonata.functions.json.JsonFactory.arrayNode;
44
import static dev.vepo.jsonata.functions.json.JsonFactory.json2Value;
5+
import static java.util.Spliterators.spliteratorUnknownSize;
56

67
import java.util.ArrayList;
78
import java.util.List;
9+
import java.util.stream.Stream;
10+
import java.util.stream.StreamSupport;
11+
12+
import com.fasterxml.jackson.databind.JsonNode;
813

914
import dev.vepo.jsonata.functions.data.Data;
1015
import dev.vepo.jsonata.functions.data.GroupedData;
@@ -20,7 +25,18 @@ public Data map(Data original, Data current) {
2025
}
2126
return json2Value(new GroupedData(elements).toJson());
2227
} else {
23-
return json2Value(arrayNode(arrayBuilder.stream().map(fn -> fn.map(original, current).toJson()).toList()));
28+
return json2Value(arrayNode(arrayBuilder.stream()
29+
.map(fn -> fn.map(original, current).toJson())
30+
.flatMap(this::planify)
31+
.toList()));
32+
}
33+
}
34+
35+
private Stream<JsonNode> planify(JsonNode node) {
36+
if (node.isArray()) {
37+
return StreamSupport.stream(spliteratorUnknownSize(node.elements(), 0), false);
38+
} else {
39+
return Stream.of(node);
2440
}
2541
}
2642
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package dev.vepo.jsonata.functions;
2+
3+
import java.util.stream.IntStream;
4+
5+
import dev.vepo.jsonata.exception.JSONataException;
6+
import dev.vepo.jsonata.functions.data.Data;
7+
import dev.vepo.jsonata.functions.json.JsonFactory;
8+
9+
public record ArrayExpansion(Mapping left, Mapping right) implements Mapping {
10+
11+
@Override
12+
public Data map(Data original, Data current) {
13+
var leftValue = left.map(original, current);
14+
var rightValue = right.map(original, current);
15+
if (!leftValue.isObject() || !leftValue.toJson().isInt()) {
16+
throw new JSONataException("Left value is not a number! value=" + leftValue);
17+
}
18+
19+
if (!rightValue.isObject() || !rightValue.toJson().isInt()) {
20+
throw new JSONataException("Left value is not a number! value=" + rightValue);
21+
}
22+
23+
return JsonFactory.arrayValue(IntStream.rangeClosed(leftValue.toJson()
24+
.intValue(),
25+
rightValue.toJson()
26+
.intValue())
27+
.toArray());
28+
}
29+
30+
}

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

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

3-
import java.util.ArrayList;
3+
import java.util.List;
4+
import java.util.stream.Collectors;
5+
import java.util.stream.IntStream;
46

57
import dev.vepo.jsonata.functions.data.Data;
68
import dev.vepo.jsonata.functions.data.GroupedData;
@@ -10,16 +12,28 @@ public record ArrayQuery(Mapping mapFunction, Mapping filterFunction) implements
1012
@Override
1113
public Data map(Data original, Data current) {
1214
var mapped = mapFunction.map(original, current);
13-
if (mapped.isArray() || mapped.isList()) {
14-
var filteredData = new ArrayList<Data>();
15-
for (int i = 0; i < mapped.length(); ++i) {
16-
var currData = mapped.at(i);
17-
var currResult = filterFunction.map(original, currData).toJson();
18-
if (currResult.isBoolean() && currResult.asBoolean()) {
19-
filteredData.add(mapped.at(i));
20-
}
15+
if (filterFunction instanceof ArrayExpansion || filterFunction instanceof ArrayConstructor) {
16+
List<Integer> indexes = filterFunction.map(original, current)
17+
.stream()
18+
.map(Data::toJson)
19+
.map(node -> node.asInt())
20+
.collect(Collectors.toList());
21+
if (!(mapped.isArray() || mapped.isList()) && indexes.contains(0)) {
22+
return mapped;
2123
}
22-
return new GroupedData(filteredData);
24+
return new GroupedData(indexes.stream()
25+
.map(i -> i >= 0 ? i : (mapped.length() + i))
26+
.filter(i -> i < mapped.length())
27+
.sorted()
28+
.map(mapped::at)
29+
.toList());
30+
} else if (mapped.isArray() || mapped.isList()) {
31+
return new GroupedData(IntStream.range(0, mapped.length())
32+
.mapToObj(mapped::at)
33+
.filter(currData -> {
34+
var currResult = filterFunction.map(original, currData).toJson();
35+
return currResult.isBoolean() && currResult.asBoolean();
36+
}).toList());
2337
} else {
2438
return Mapping.empty();
2539
}

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

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

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ public interface Mapping {
1010
public static Data empty() {
1111
return new EmptyData();
1212
}
13+
14+
default Mapping andThen(Mapping after) {
15+
return (original, current) -> after.map(original, map(original, current));
16+
}
1317
}
Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,39 @@
11
package dev.vepo.jsonata.functions;
22

3+
import static java.util.Spliterators.spliteratorUnknownSize;
4+
5+
import java.util.function.Predicate;
6+
import java.util.stream.Stream;
7+
import java.util.stream.StreamSupport;
8+
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
12+
import com.fasterxml.jackson.databind.JsonNode;
13+
314
import dev.vepo.jsonata.functions.data.Data;
15+
import dev.vepo.jsonata.functions.data.GroupedData;
416

517
public record MappingJoin(Mapping first, Mapping second) implements Mapping {
18+
private static final Logger logger = LoggerFactory.getLogger(MappingJoin.class);
619

720
@Override
821
public Data map(Data original, Data current) {
9-
return second.map(original, first.map(original, current));
22+
logger.atInfo().log("MappingJoin: first={} second={} current={}", first, second, current);
23+
var value = first.map(original, current);
24+
Data result;
25+
if ((value.isArray() || value.isList()) && !(second instanceof ArrayConstructor)) {
26+
// && !(second instanceof ObjectBuilder)
27+
// && !(second instanceof ObjectMapper)) {
28+
result = new GroupedData(value.stream()
29+
.map(v -> second.map(original, v))
30+
.flatMap(Data::stream)
31+
.filter(Predicate.not(Data::isEmpty))
32+
.toList());
33+
} else {
34+
result = second.map(original, value);
35+
}
36+
logger.atInfo().log("MappingJoin: result={}", value);
37+
return result;
1038
}
11-
1239
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import dev.vepo.jsonata.functions.data.Data;
44

5-
public class Wildcard implements Mapping {
5+
public record Wildcard() implements Mapping {
66

77
@Override
88
public Data map(Data original, Data current) {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package dev.vepo.jsonata.functions.builtin;
2+
3+
import java.math.BigDecimal;
4+
import java.math.RoundingMode;
5+
import java.util.List;
6+
import java.util.stream.IntStream;
7+
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
11+
import com.fasterxml.jackson.databind.JsonNode;
12+
13+
import dev.vepo.jsonata.functions.AggregateMapping;
14+
import dev.vepo.jsonata.functions.DeclaredFunction;
15+
import dev.vepo.jsonata.functions.Mapping;
16+
import dev.vepo.jsonata.functions.data.Data;
17+
import dev.vepo.jsonata.functions.json.JsonFactory;
18+
19+
public record Average(List<Mapping> providers,
20+
List<DeclaredFunction> declaredFunctions)
21+
implements AggregateMapping {
22+
23+
private static final Logger logger = LoggerFactory.getLogger(Sum.class);
24+
25+
public Average {
26+
if (providers.size() != 1) {
27+
throw new IllegalArgumentException("$average function must have 1 argument");
28+
}
29+
}
30+
31+
@Override
32+
public Data operation(Data avgValues) {
33+
logger.atDebug().log("Executing max {}", avgValues);
34+
if ((avgValues.isArray() || avgValues.isList()) && avgValues.length() > 0) {
35+
return JsonFactory.numberValue(IntStream.range(0, avgValues.length())
36+
.mapToObj(avgValues::at)
37+
.map(Data::toJson)
38+
.map(JsonNode::decimalValue)
39+
.reduce(BigDecimal.ZERO, BigDecimal::add)
40+
.divide(BigDecimal.valueOf(avgValues.length()),12, RoundingMode.HALF_DOWN));
41+
} else if (!avgValues.isEmpty() && avgValues.toJson().isNumber()) {
42+
return avgValues;
43+
} else {
44+
return Mapping.empty();
45+
}
46+
}
47+
48+
@Override
49+
public Mapping extractor() {
50+
return providers.get(0);
51+
}
52+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package dev.vepo.jsonata.functions.builtin;
2+
3+
import java.util.List;
4+
5+
import dev.vepo.jsonata.functions.DeclaredFunction;
6+
import dev.vepo.jsonata.functions.Mapping;
7+
import dev.vepo.jsonata.functions.data.Data;
8+
import dev.vepo.jsonata.functions.json.JsonFactory;
9+
10+
public record Count(List<Mapping> providers,
11+
List<DeclaredFunction> declaredFunctions)
12+
implements Mapping {
13+
public Count {
14+
if (providers.size() != 1) {
15+
throw new IllegalArgumentException("$count function must have 1 parameter!");
16+
}
17+
}
18+
19+
@Override
20+
public Data map(Data original, Data current) {
21+
var data = providers.get(0).map(original, current);
22+
if (data.isArray() || data.isList()) {
23+
return JsonFactory.numberValue(data.length());
24+
} else if (!data.isEmpty()) {
25+
return JsonFactory.numberValue(1);
26+
} else {
27+
return JsonFactory.numberValue(0);
28+
}
29+
}
30+
31+
}

0 commit comments

Comments
 (0)