Skip to content

Commit a0b0835

Browse files
[CESQL] Constant folding (#392)
* Added visitor for the expressions Signed-off-by: Francesco Guardiani <[email protected]> * First constant folding draft Signed-off-by: Francesco Guardiani <[email protected]> * Constant folding for unary expressions Signed-off-by: Francesco Guardiani <[email protected]> * More testing Constant folding for exists expression Signed-off-by: Francesco Guardiani <[email protected]> * Little mistake Signed-off-by: Francesco Guardiani <[email protected]> * Added a ParserBuilder Signed-off-by: Francesco Guardiani <[email protected]>
1 parent 687e03b commit a0b0835

33 files changed

+431
-89
lines changed

sql/src/main/java/io/cloudevents/sql/ParseException.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
public class ParseException extends RuntimeException {
1111

1212
public enum ErrorKind {
13-
RECOGNITION_ERROR,
14-
PARSE_VALUE
13+
RECOGNITION,
14+
PARSE_VALUE,
15+
CONSTANT_EXPRESSION_EVALUATION,
1516
}
1617

1718
private final ErrorKind errorKind;
@@ -49,12 +50,22 @@ public static ParseException cannotParseValue(ParseTree node, Type target, Throw
4950

5051
public static ParseException recognitionError(RecognitionException e, String msg) {
5152
return new ParseException(
52-
ErrorKind.RECOGNITION_ERROR,
53+
ErrorKind.RECOGNITION,
5354
new Interval(e.getOffendingToken().getStartIndex(), e.getOffendingToken().getStopIndex()),
5455
e.getOffendingToken().getText(),
5556
"Cannot parse: " + msg,
5657
e
5758
);
5859
}
5960

61+
public static ParseException cannotEvaluateConstantExpression(EvaluationException exception) {
62+
return new ParseException(
63+
ErrorKind.CONSTANT_EXPRESSION_EVALUATION,
64+
exception.getExpressionInterval(),
65+
exception.getExpressionText(),
66+
"Cannot evaluate the constant expression: " + exception.getExpressionText(),
67+
exception
68+
);
69+
}
70+
6071
}

sql/src/main/java/io/cloudevents/sql/Parser.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.cloudevents.sql;
22

3+
import io.cloudevents.sql.impl.ParserBuilder;
34
import io.cloudevents.sql.impl.ParserImpl;
45

56
public interface Parser {
@@ -24,4 +25,18 @@ static Expression parseDefault(String inputExpression) throws ParseException {
2425
return ParserImpl.getInstance().parse(inputExpression);
2526
}
2627

28+
/**
29+
* @return the default instance of the parser
30+
*/
31+
static Parser getDefault() {
32+
return ParserImpl.getInstance();
33+
}
34+
35+
/**
36+
* @return a new {@link ParserBuilder}, to create a customized parser instance
37+
*/
38+
static ParserBuilder builder() {
39+
return new ParserBuilder();
40+
}
41+
2742
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package io.cloudevents.sql.impl;
2+
3+
import io.cloudevents.SpecVersion;
4+
import io.cloudevents.sql.EvaluationRuntime;
5+
import io.cloudevents.sql.impl.expressions.*;
6+
7+
public class ConstantFoldingExpressionVisitor implements ExpressionInternalVisitor<ExpressionInternal> {
8+
9+
@Override
10+
public ExpressionInternal visitExpressionInternal(ExpressionInternal expressionInternal) {
11+
return expressionInternal;
12+
}
13+
14+
@Override
15+
public ExpressionInternal visitBaseBinaryExpression(BaseBinaryExpression baseBinaryExpression) {
16+
ExpressionInternal left = baseBinaryExpression.getLeftOperand().visit(this);
17+
ExpressionInternal right = baseBinaryExpression.getRightOperand().visit(this);
18+
19+
if (left instanceof ValueExpression && right instanceof ValueExpression) {
20+
// I can do constant folding!
21+
return new ValueExpression(
22+
baseBinaryExpression.expressionInterval(),
23+
baseBinaryExpression.expressionText(),
24+
baseBinaryExpression.evaluate(
25+
EvaluationRuntime.getDefault(),
26+
((ValueExpression) left).getValue(),
27+
((ValueExpression) right).getValue(),
28+
FailFastExceptionThrower.getInstance()
29+
)
30+
);
31+
}
32+
33+
baseBinaryExpression.setLeftOperand(left);
34+
baseBinaryExpression.setRightOperand(right);
35+
return baseBinaryExpression;
36+
}
37+
38+
@Override
39+
public ExpressionInternal visitExistsExpression(ExistsExpression existsExpression) {
40+
if (SpecVersion.V1.getMandatoryAttributes().contains(existsExpression.getKey())) {
41+
// If the attribute is a mandatory attribute of the spec, there's no need to check it
42+
return new ValueExpression(existsExpression.expressionInterval(), existsExpression.expressionText(), true);
43+
}
44+
return existsExpression;
45+
}
46+
47+
@Override
48+
public ExpressionInternal visitBaseUnaryExpression(BaseUnaryExpression baseUnaryExpression) {
49+
ExpressionInternal inner = baseUnaryExpression.getOperand().visit(this);
50+
51+
if (inner instanceof ValueExpression) {
52+
return new ValueExpression(
53+
baseUnaryExpression.expressionInterval(),
54+
baseUnaryExpression.expressionText(),
55+
baseUnaryExpression.evaluate(EvaluationRuntime.getDefault(), ((ValueExpression) inner).getValue(), FailFastExceptionThrower.getInstance())
56+
);
57+
}
58+
59+
baseUnaryExpression.setOperand(inner);
60+
return baseUnaryExpression;
61+
}
62+
}

sql/src/main/java/io/cloudevents/sql/impl/ExceptionsStore.java renamed to sql/src/main/java/io/cloudevents/sql/impl/ExceptionStore.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
import java.util.ArrayList;
66
import java.util.List;
77

8-
class ExceptionsStore implements ExceptionThrower {
8+
class ExceptionStore implements ExceptionThrower {
99

1010
private List<EvaluationException> exceptions;
1111

12-
ExceptionsStore() {
12+
ExceptionStore() {
1313
}
1414

1515
@Override

sql/src/main/java/io/cloudevents/sql/impl/ExpressionImpl.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import io.cloudevents.sql.EvaluationRuntime;
66
import io.cloudevents.sql.Expression;
77
import io.cloudevents.sql.Result;
8+
import io.cloudevents.sql.impl.expressions.ExpressionInternal;
89

910
public class ExpressionImpl implements Expression {
1011

@@ -16,7 +17,7 @@ public ExpressionImpl(ExpressionInternal expressionInternal) {
1617

1718
@Override
1819
public Result evaluate(EvaluationRuntime evaluationRuntime, CloudEvent event) {
19-
ExceptionsStore exceptions = new ExceptionsStore();
20+
ExceptionStore exceptions = new ExceptionStore();
2021
Object value = this.expressionInternal.evaluate(evaluationRuntime, event, exceptions);
2122
return new EvaluationResult(value, exceptions.getExceptions());
2223
}
@@ -25,4 +26,8 @@ public Result evaluate(EvaluationRuntime evaluationRuntime, CloudEvent event) {
2526
public Object tryEvaluate(EvaluationRuntime evaluationRuntime, CloudEvent event) throws EvaluationException {
2627
return this.expressionInternal.evaluate(evaluationRuntime, event, FailFastExceptionThrower.getInstance());
2728
}
29+
30+
protected ExpressionInternal getExpressionInternal() {
31+
return expressionInternal;
32+
}
2833
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package io.cloudevents.sql.impl;
2+
3+
import io.cloudevents.sql.Parser;
4+
5+
public class ParserBuilder {
6+
7+
private boolean constantFolding;
8+
9+
public ParserBuilder() {
10+
this.constantFolding = true;
11+
}
12+
13+
/**
14+
* Disable constant folding when parsing.
15+
*
16+
* @return this
17+
*/
18+
public ParserBuilder disableConstantFolding() {
19+
this.constantFolding = false;
20+
return this;
21+
}
22+
23+
/**
24+
* @return the new {@link Parser}
25+
*/
26+
public Parser build() {
27+
return new ParserImpl(this.constantFolding);
28+
}
29+
30+
}

sql/src/main/java/io/cloudevents/sql/impl/ParserImpl.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package io.cloudevents.sql.impl;
22

3+
import io.cloudevents.sql.EvaluationException;
34
import io.cloudevents.sql.Expression;
45
import io.cloudevents.sql.ParseException;
56
import io.cloudevents.sql.Parser;
67
import io.cloudevents.sql.generated.CESQLParserLexer;
78
import io.cloudevents.sql.generated.CESQLParserParser;
9+
import io.cloudevents.sql.impl.expressions.ExpressionInternal;
810
import org.antlr.v4.runtime.*;
911
import org.antlr.v4.runtime.atn.ATNConfigSet;
1012
import org.antlr.v4.runtime.dfa.DFA;
@@ -17,17 +19,20 @@
1719
public class ParserImpl implements Parser {
1820

1921
private static class SingletonContainer {
20-
private final static ParserImpl INSTANCE = new ParserImpl();
22+
private final static ParserImpl INSTANCE = new ParserImpl(true);
2123
}
2224

2325
/**
24-
* @return instance of {@link ParserImpl}
26+
* @return default instance of {@link ParserImpl}
2527
*/
2628
public static Parser getInstance() {
2729
return ParserImpl.SingletonContainer.INSTANCE;
2830
}
2931

30-
public ParserImpl() {
32+
private final boolean constantFolding;
33+
34+
ParserImpl(boolean constantFolding) {
35+
this.constantFolding = constantFolding;
3136
}
3237

3338
@Override
@@ -72,6 +77,14 @@ public void reportContextSensitivity(org.antlr.v4.runtime.Parser recognizer, DFA
7277

7378
ExpressionInternal internal = new ExpressionTranslatorVisitor().visit(tree);
7479

80+
if (this.constantFolding) {
81+
try {
82+
internal = internal.visit(new ConstantFoldingExpressionVisitor());
83+
} catch (EvaluationException e) {
84+
throw ParseException.cannotEvaluateConstantExpression(e);
85+
}
86+
}
87+
7588
return new ExpressionImpl(internal);
7689
}
7790

sql/src/main/java/io/cloudevents/sql/impl/expressions/AccessAttributeExpression.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,9 @@ public Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThr
2020
return CloudEventUtils.accessContextAttribute(thrower, expressionInterval(), expressionText(), event, key);
2121
}
2222

23+
@Override
24+
public <T> T visit(ExpressionInternalVisitor<T> visitor) {
25+
return visitor.visitAccessAttributeExpression(this);
26+
}
27+
2328
}

sql/src/main/java/io/cloudevents/sql/impl/expressions/AndExpression.java

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

33
import io.cloudevents.sql.EvaluationRuntime;
44
import io.cloudevents.sql.impl.ExceptionThrower;
5-
import io.cloudevents.sql.impl.ExpressionInternal;
65
import org.antlr.v4.runtime.misc.Interval;
76

87
public class AndExpression extends BaseBinaryExpression {
@@ -12,7 +11,7 @@ public AndExpression(Interval expressionInterval, String expressionText, Express
1211
}
1312

1413
@Override
15-
Object evaluate(EvaluationRuntime runtime, Object left, Object right, ExceptionThrower exceptions) {
14+
public Object evaluate(EvaluationRuntime runtime, Object left, Object right, ExceptionThrower exceptions) {
1615
boolean x = castToBoolean(runtime, exceptions, left);
1716
if (!x) {
1817
// Short circuit

sql/src/main/java/io/cloudevents/sql/impl/expressions/BaseBinaryExpression.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,48 @@
33
import io.cloudevents.CloudEvent;
44
import io.cloudevents.sql.EvaluationRuntime;
55
import io.cloudevents.sql.impl.ExceptionThrower;
6-
import io.cloudevents.sql.impl.ExpressionInternal;
76
import org.antlr.v4.runtime.misc.Interval;
87

98
public abstract class BaseBinaryExpression extends BaseExpression {
109

11-
protected final ExpressionInternal leftOperand;
12-
protected final ExpressionInternal rightOperand;
10+
protected ExpressionInternal leftOperand;
11+
protected ExpressionInternal rightOperand;
1312

1413
protected BaseBinaryExpression(Interval expressionInterval, String expressionText, ExpressionInternal leftOperand, ExpressionInternal rightOperand) {
1514
super(expressionInterval, expressionText);
1615
this.leftOperand = leftOperand;
1716
this.rightOperand = rightOperand;
1817
}
1918

20-
abstract Object evaluate(EvaluationRuntime runtime, Object left, Object right, ExceptionThrower exceptions);
19+
public abstract Object evaluate(EvaluationRuntime runtime, Object left, Object right, ExceptionThrower exceptions);
2120

2221
@Override
2322
public Object evaluate(EvaluationRuntime runtime, CloudEvent event, ExceptionThrower thrower) {
2423
Object left = leftOperand.evaluate(runtime, event, thrower);
2524
Object right = rightOperand.evaluate(runtime, event, thrower);
2625
return evaluate(runtime, left, right, thrower);
2726
}
27+
28+
@Override
29+
public <T> T visit(ExpressionInternalVisitor<T> visitor) {
30+
return visitor.visitBaseBinaryExpression(this);
31+
}
32+
33+
public ExpressionInternal getLeftOperand() {
34+
return leftOperand;
35+
}
36+
37+
public ExpressionInternal getRightOperand() {
38+
return rightOperand;
39+
}
40+
41+
public BaseBinaryExpression setLeftOperand(ExpressionInternal leftOperand) {
42+
this.leftOperand = leftOperand;
43+
return this;
44+
}
45+
46+
public BaseBinaryExpression setRightOperand(ExpressionInternal rightOperand) {
47+
this.rightOperand = rightOperand;
48+
return this;
49+
}
2850
}

0 commit comments

Comments
 (0)