From bc139247a5cd5df93de2a33530d11ee15389f259 Mon Sep 17 00:00:00 2001 From: vlalykin Date: Fri, 1 May 2020 18:13:56 +0300 Subject: [PATCH] Allow completely control the instantiation of the binary expression class (#499) Co-authored-by: Victor.Lalykin --- .../pebble/extension/core/CoreExtension.java | 45 ++++++++-------- .../pebble/operator/BinaryOperator.java | 4 +- .../pebble/operator/BinaryOperatorImpl.java | 51 +++++++++++++++++-- .../pebble/operator/BinaryOperatorType.java | 5 ++ .../pebble/parser/ExpressionParser.java | 21 ++++---- 5 files changed, 88 insertions(+), 38 deletions(-) create mode 100644 pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperatorType.java diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java b/pebble/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java index a443f5807..d69f56003 100644 --- a/pebble/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java +++ b/pebble/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java @@ -8,6 +8,10 @@ */ package com.mitchellbosecke.pebble.extension.core; +import static com.mitchellbosecke.pebble.operator.BinaryOperatorType.FILTER; +import static com.mitchellbosecke.pebble.operator.BinaryOperatorType.NORMAL; +import static com.mitchellbosecke.pebble.operator.BinaryOperatorType.TEST; + import com.mitchellbosecke.pebble.extension.AbstractExtension; import com.mitchellbosecke.pebble.extension.Filter; import com.mitchellbosecke.pebble.extension.Function; @@ -55,7 +59,6 @@ import com.mitchellbosecke.pebble.tokenParser.ParallelTokenParser; import com.mitchellbosecke.pebble.tokenParser.SetTokenParser; import com.mitchellbosecke.pebble.tokenParser.TokenParser; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -97,26 +100,26 @@ public List getUnaryOperators() { @Override public List getBinaryOperators() { List operators = new ArrayList<>(); - operators.add(new BinaryOperatorImpl("or", 10, OrExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("and", 15, AndExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("is", 20, PositiveTestExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("is not", 20, NegativeTestExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("contains", 20, ContainsExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("==", 30, EqualsExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("equals", 30, EqualsExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("!=", 30, NotEqualsExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl(">", 30, GreaterThanExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("<", 30, LessThanExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl(">=", 30, GreaterThanEqualsExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("<=", 30, LessThanEqualsExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("+", 40, AddExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("-", 40, SubtractExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("*", 60, MultiplyExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("/", 60, DivideExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("%", 60, ModulusExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("|", 100, FilterExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("~", 110, ConcatenateExpression.class, Associativity.LEFT)); - operators.add(new BinaryOperatorImpl("..", 120, RangeExpression.class, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("or", 10, OrExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("and", 15, AndExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("is", 20, PositiveTestExpression::new, TEST, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("is not", 20, NegativeTestExpression::new, TEST, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("contains", 20, ContainsExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("==", 30, EqualsExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("equals", 30, EqualsExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("!=", 30, NotEqualsExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl(">", 30, GreaterThanExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("<", 30, LessThanExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl(">=", 30, GreaterThanEqualsExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("<=", 30, LessThanEqualsExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("+", 40, AddExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("-", 40, SubtractExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("*", 60, MultiplyExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("/", 60, DivideExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("%", 60, ModulusExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("|", 100, FilterExpression::new, FILTER, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("~", 110, ConcatenateExpression::new, NORMAL, Associativity.LEFT)); + operators.add(new BinaryOperatorImpl("..", 120, RangeExpression::new, NORMAL, Associativity.LEFT)); return operators; } diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperator.java b/pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperator.java index a69d5f7b8..c9f28ad6f 100644 --- a/pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperator.java +++ b/pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperator.java @@ -16,7 +16,9 @@ public interface BinaryOperator { String getSymbol(); - Class> getNodeClass(); + BinaryExpression getInstance(); + + BinaryOperatorType getType(); Associativity getAssociativity(); diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperatorImpl.java b/pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperatorImpl.java index 15cbafc2e..114ce7315 100644 --- a/pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperatorImpl.java +++ b/pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperatorImpl.java @@ -8,7 +8,12 @@ */ package com.mitchellbosecke.pebble.operator; +import com.mitchellbosecke.pebble.error.PebbleException; import com.mitchellbosecke.pebble.node.expression.BinaryExpression; +import com.mitchellbosecke.pebble.node.expression.FilterExpression; +import com.mitchellbosecke.pebble.node.expression.NegativeTestExpression; +import com.mitchellbosecke.pebble.node.expression.PositiveTestExpression; +import java.util.function.Supplier; public class BinaryOperatorImpl implements BinaryOperator { @@ -16,16 +21,38 @@ public class BinaryOperatorImpl implements BinaryOperator { private final String symbol; - private final Class> nodeClass; + private final Supplier> nodeSupplier; + + private final BinaryOperatorType type; private final Associativity associativity; + /** + * This constuctor left for backward compatibility with custom extensions + */ public BinaryOperatorImpl(String symbol, int precedence, Class> nodeClass, Associativity associativity) { + this(symbol, precedence, () -> { + try { + return nodeClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new PebbleException(e, "Error instantiating class " + nodeClass.getName()); + } + }, getDefaultType(nodeClass), associativity); + } + + /** + * This constuctor allows you to completely control the instantiation of the expression class + */ + public BinaryOperatorImpl(String symbol, int precedence, + Supplier> nodeSupplier, + BinaryOperatorType type, + Associativity associativity) { this.symbol = symbol; this.precedence = precedence; - this.nodeClass = nodeClass; + this.nodeSupplier = nodeSupplier; + this.type = type; this.associativity = associativity; } @@ -40,12 +67,28 @@ public String getSymbol() { } @Override - public Class> getNodeClass() { - return this.nodeClass; + public BinaryExpression getInstance() { + return this.nodeSupplier.get(); + } + + @Override + public BinaryOperatorType getType() { + return this.type; } @Override public Associativity getAssociativity() { return this.associativity; } + + private static BinaryOperatorType getDefaultType(Class> nodeClass) { + if (FilterExpression.class.equals(nodeClass)) { + return BinaryOperatorType.FILTER; + } else if (PositiveTestExpression.class.equals(nodeClass) || NegativeTestExpression.class + .equals(nodeClass)) { + return BinaryOperatorType.TEST; + } else { + return BinaryOperatorType.NORMAL; + } + } } diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperatorType.java b/pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperatorType.java new file mode 100644 index 000000000..c6eef3f28 --- /dev/null +++ b/pebble/src/main/java/com/mitchellbosecke/pebble/operator/BinaryOperatorType.java @@ -0,0 +1,5 @@ +package com.mitchellbosecke.pebble.operator; + +public enum BinaryOperatorType { + NORMAL, FILTER, TEST +} diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ExpressionParser.java b/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ExpressionParser.java index 5b36cc5e4..9c2c7d359 100644 --- a/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ExpressionParser.java +++ b/pebble/src/main/java/com/mitchellbosecke/pebble/parser/ExpressionParser.java @@ -22,7 +22,6 @@ import com.mitchellbosecke.pebble.node.expression.ConcatenateExpression; import com.mitchellbosecke.pebble.node.expression.ContextVariableExpression; import com.mitchellbosecke.pebble.node.expression.Expression; -import com.mitchellbosecke.pebble.node.expression.FilterExpression; import com.mitchellbosecke.pebble.node.expression.FilterInvocationExpression; import com.mitchellbosecke.pebble.node.expression.FunctionOrMacroInvocationExpression; import com.mitchellbosecke.pebble.node.expression.GetAttributeExpression; @@ -34,13 +33,12 @@ import com.mitchellbosecke.pebble.node.expression.LiteralNullExpression; import com.mitchellbosecke.pebble.node.expression.LiteralStringExpression; import com.mitchellbosecke.pebble.node.expression.MapExpression; -import com.mitchellbosecke.pebble.node.expression.NegativeTestExpression; import com.mitchellbosecke.pebble.node.expression.ParentFunctionExpression; -import com.mitchellbosecke.pebble.node.expression.PositiveTestExpression; import com.mitchellbosecke.pebble.node.expression.TernaryExpression; import com.mitchellbosecke.pebble.node.expression.UnaryExpression; import com.mitchellbosecke.pebble.operator.Associativity; import com.mitchellbosecke.pebble.operator.BinaryOperator; +import com.mitchellbosecke.pebble.operator.BinaryOperatorType; import com.mitchellbosecke.pebble.operator.UnaryOperator; import java.math.BigDecimal; @@ -180,13 +178,12 @@ else if (token.test(Token.Type.PUNCTUATION, "{")) { // the right hand expression of the FILTER operator is handled in a // unique way - if (FilterExpression.class.equals(operator.getNodeClass())) { + if (operator.getType() == BinaryOperatorType.FILTER) { expressionRight = this.parseFilterInvocationExpression(); } // the right hand expression of TEST operators is handled in a // unique way - else if (PositiveTestExpression.class.equals(operator.getNodeClass()) - || NegativeTestExpression.class.equals(operator.getNodeClass())) { + else if (operator.getType() == BinaryOperatorType.TEST) { expressionRight = this.parseTestInvocationExpression(); } else { /* @@ -204,16 +201,16 @@ else if (PositiveTestExpression.class.equals(operator.getNodeClass()) * expression we are creating. */ BinaryExpression finalExpression; - Class> operatorNodeClass = operator.getNodeClass(); + try { - finalExpression = operatorNodeClass.newInstance(); - finalExpression.setLineNumber(this.stream.current().getLineNumber()); - } catch (InstantiationException | IllegalAccessException e) { + finalExpression = operator.getInstance(); + } catch (RuntimeException e) { throw new ParserException(e, - "Error instantiating operator node [" + operatorNodeClass.getName() + "]", + "Error instantiating operator node", token.getLineNumber(), this.stream.getFilename()); } + finalExpression.setLineNumber(this.stream.current().getLineNumber()); finalExpression.setLeft(expression); finalExpression.setRight(expressionRight); @@ -578,7 +575,7 @@ public ArgumentsNode parseArguments(boolean isMacroDefinition) { throw new ParserException(null, "Positional arguments must be declared before any named arguments.", this.stream.current() - .getLineNumber(), + .getLineNumber(), this.stream.getFilename()); } positionalArgs.add(new PositionalArgumentNode(argumentValue));