Преглед изворни кода

Merge remote-tracking branch 'origin/master'

mxd пре 5 година
родитељ
комит
a9e0931ebe

+ 0 - 15
src/main/java/org/ssssssss/expression/ExpressionEngine.java

@@ -11,22 +11,7 @@ public class ExpressionEngine {
 		return ExpressionTemplate.create(expression).render(context);
 	}
 
-	public static void main(String[] args) {
 
-		ExpressionEngine engine = new ExpressionEngine();
-
-		Map<String, Object> params = new HashMap<>();
-		params.put("abc", "xxx");
-		ArrayList<Object> list = new ArrayList<>();
-		list.add("987654321");
-		list.add("");
-		params.put("arr", list);
-		Object result = engine.execute("${arr.map(e->abc.hashCode())}", params);
-		System.out.println(result);
-
-
-	}
-	
 	public Object executeWrap(String expression, Map<String, Object> variables) {
 		return execute("${" + expression + "}", variables);
 	}

+ 0 - 28
src/main/java/org/ssssssss/expression/parsing/ArrayLikeLambdaExecutor.java

@@ -1,28 +0,0 @@
-package org.ssssssss.expression.parsing;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.function.Supplier;
-
-public class ArrayLikeLambdaExecutor {
-
-
-
-    @SuppressWarnings("unchecked")
-    public static Object map(Object arrayLike, Object... arguments) {
-//        System.err.println("ArrayLikeLambdaExecutor:11 " + arrayLike);
-        List<Object> results = null;
-        Object argument = arguments[0];
-        List<Object> args = (List<Object>) argument;
-        results = new ArrayList<>(args.size());
-        for (int j = 0; j < args.size(); j++) {
-            Object result = ((Supplier) args.get(j)).get();
-            results.add(result);
-        }
-        if (arrayLike instanceof Collection) {
-            return results;
-        }
-        throw new RuntimeException("未实现");
-    }
-}

+ 104 - 0
src/main/java/org/ssssssss/expression/parsing/ArrayLikeLambdaOneArgumentExecutor.java

@@ -0,0 +1,104 @@
+package org.ssssssss.expression.parsing;
+
+import java.lang.reflect.Method;
+import java.util.*;
+import java.util.function.Supplier;
+
+public class ArrayLikeLambdaOneArgumentExecutor {
+
+    public static final Set<String> SUPPORT_METHOD;
+    public static final Map<String, Method> METHODS;
+
+
+    static {
+        Map<String, Method> temp = new HashMap<>();
+        Set<String> set = new HashSet<>();
+        addSupport(temp, set, "map");
+        addSupport(temp, set, "filter");
+        SUPPORT_METHOD = Collections.unmodifiableSet(set);
+        METHODS = Collections.unmodifiableMap(temp);
+    }
+
+    private static void addSupport(Map<String, Method> temp, Set<String> set, String name) {
+        set.add(name);
+        addMethod(temp, name);
+    }
+
+    private static void addMethod(Map<String, Method> initialMap, String name) {
+        try {
+            initialMap.put(name, ArrayLikeLambdaOneArgumentExecutor.class.getMethod(name, Object.class, Object[].class));
+        } catch (NoSuchMethodException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Object eachParse(Object arrayLike, Object argument, SPConsumer spConsumer) {
+        List<Object> results = null;
+        List<Object> args = (List<Object>) argument;
+        results = new ArrayList<>(args.size());
+        for (int j = 0; j < args.size(); j++) {
+            SourceAndParsed<Object, Object> result = (SourceAndParsed<Object, Object>) ((Supplier) args.get(j)).get();
+            spConsumer.accept(results, result);
+        }
+        if (arrayLike instanceof Collection) {
+            return results;
+        } else if (arrayLike.getClass().isArray()) {
+            return results.toArray();
+        } else if (arrayLike instanceof Iterator) {
+            return results;
+        } else if (arrayLike instanceof Enumeration) {
+            return results;
+        }
+        throw new RuntimeException("未实现");
+    }
+
+    @SuppressWarnings("unchecked")
+    public static Object map(Object arrayLike, Object... arguments) {
+        return eachParse(arrayLike, arguments[0], (list, sp) -> list.add(sp.getParsed()));
+    }
+
+    @SuppressWarnings("unchecked")
+    public static Object filter(Object arrayLike, Object... arguments) {
+        return eachParse(arrayLike, arguments[0], (list, sp) -> {
+            if (sp.getParsed() instanceof Boolean) {
+                if ((Boolean)sp.getParsed()) {
+                    list.add(sp.getSource());
+                }
+            } else {
+                throw new RuntimeException("lambda函数filter的结果非布尔类型");
+            }
+        });
+    }
+
+    public interface SPConsumer {
+        void accept(List<Object> list, SourceAndParsed<Object, Object> sp);
+    }
+
+    public static class SourceAndParsed<S, P> {
+        private S source;
+        private P parsed;
+
+        public SourceAndParsed(S source, P parsed) {
+            this.source = source;
+            this.parsed = parsed;
+        }
+
+        public P getParsed() {
+            return parsed;
+        }
+
+        public void setParsed(P parsed) {
+            this.parsed = parsed;
+        }
+
+        public S getSource() {
+            return source;
+        }
+
+        public void setSource(S source) {
+            this.source = source;
+        }
+    }
+}

+ 1382 - 1411
src/main/java/org/ssssssss/expression/parsing/Ast.java

@@ -16,6 +16,8 @@ import java.io.IOException;
 import java.lang.reflect.Array;
 import java.lang.reflect.InvocationTargetException;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
@@ -23,1416 +25,1385 @@ import java.util.stream.Collectors;
 /** Templates are parsed into an abstract syntax tree (AST) nodes by a Parser. This class contains all AST node types. */
 public abstract class Ast {
 
-	/** Base class for all AST nodes. A node minimally stores the {@link Span} that references its location in the
-	 * {@link Source}. **/
-	public abstract static class Node {
-		private final Span span;
-
-		public Node (Span span) {
-			this.span = span;
-		}
-
-		/** Returns the {@link Span} referencing this node's location in the {@link Source}. **/
-		public Span getSpan () {
-			return span;
-		}
-
-		@Override
-		public String toString () {
-			return span.getText();
-		}
-
-		public abstract Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException;
-	}
-
-	/** A text node represents an "un-templated" span in the source that should be emitted verbatim. **/
-	public static class Text extends Node {
-		private final String content;
-
-		public Text (Span text) {
-			super(text);
-			String unescapedValue = text.getText();
-			StringBuilder builder = new StringBuilder();
-
-			CharacterStream stream = new CharacterStream(unescapedValue);
-			while (stream.hasMore()) {
-				if (stream.match("\\{", true)) {
-					builder.append('{');
-				} else if (stream.match("\\}", true)) {
-					builder.append('}');
-				} else {
-					builder.append(stream.consume());
-				}
-			}
-			content = builder.toString();
-		}
-
-		/** Returns the UTF-8 representation of this text node. **/
-		public String getContent () {
-			return content;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			return null;
-		}
-	}
-
-	/** All expressions are subclasses of this node type. Expressions are separated into unary operations (!, -), binary operations
-	 * (+, -, *, /, etc.) and ternary operations (?:). */
-	public abstract static class Expression extends Node {
-		public Expression (Span span) {
-			super(span);
-		}
-	}
-
-	/** An unary operation node represents a logical or numerical negation. **/
-	public static class UnaryOperation extends Expression {
-
-		public static enum UnaryOperator {
-			Not, Negate, Positive;
-
-			public static UnaryOperator getOperator (Token op) {
-				if (op.getType() == TokenType.Not) {
-					return UnaryOperator.Not;
-				}
-				if (op.getType() == TokenType.Plus) {
-					return UnaryOperator.Positive;
-				}
-				if (op.getType() == TokenType.Minus) {
-					return UnaryOperator.Negate;
-				}
-				ExpressionError.error("Unknown unary operator " + op + ".", op.getSpan());
-				return null; // not reached
-			}
-		}
-
-		private final UnaryOperator operator;
-		private final Expression operand;
-
-		public UnaryOperation (Token operator, Expression operand) {
-			super(operator.getSpan());
-			this.operator = UnaryOperator.getOperator(operator);
-			this.operand = operand;
-		}
-
-		public UnaryOperator getOperator () {
-			return operator;
-		}
-
-		public Expression getOperand () {
-			return operand;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			Object operand = getOperand().evaluate(template, context);
-
-			if (getOperator() == UnaryOperator.Negate) {
-				if (operand instanceof Integer) {
-					return -(Integer)operand;
-				} else if (operand instanceof Float) {
-					return -(Float)operand;
-				} else if (operand instanceof Double) {
-					return -(Double)operand;
-				} else if (operand instanceof Byte) {
-					return -(Byte)operand;
-				} else if (operand instanceof Short) {
-					return -(Short)operand;
-				} else if (operand instanceof Long) {
-					return -(Long)operand;
-				} else {
-					ExpressionError.error("Operand of operator '" + getOperator().name() + "' must be a number, got " + operand, getSpan());
-					return null; // never reached
-				}
-			} else if (getOperator() == UnaryOperator.Not) {
-				if (!(operand instanceof Boolean)) {
-					ExpressionError.error("Operand of operator '" + getOperator().name() + "' must be a boolean", getSpan());
-				}
-				return !(Boolean)operand;
-			} else {
-				return operand;
-			}
-		}
-	}
-
-	/** A binary operation represents arithmetic operators, like addition or division, comparison operators, like less than or
-	 * equals, logical operators, like and, or an assignment. **/
-	public static class BinaryOperation extends Expression {
-
-		public static enum BinaryOperator {
-			Addition, Subtraction, Multiplication, Division, Modulo, Equal, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or, Xor, Assignment;
-
-			public static BinaryOperator getOperator (Token op) {
-				if (op.getType() == TokenType.Plus) {
-					return BinaryOperator.Addition;
-				}
-				if (op.getType() == TokenType.Minus) {
-					return BinaryOperator.Subtraction;
-				}
-				if (op.getType() == TokenType.Asterisk) {
-					return BinaryOperator.Multiplication;
-				}
-				if (op.getType() == TokenType.ForwardSlash) {
-					return BinaryOperator.Division;
-				}
-				if (op.getType() == TokenType.Percentage) {
-					return BinaryOperator.Modulo;
-				}
-				if (op.getType() == TokenType.Equal) {
-					return BinaryOperator.Equal;
-				}
-				if (op.getType() == TokenType.NotEqual) {
-					return BinaryOperator.NotEqual;
-				}
-				if (op.getType() == TokenType.Less) {
-					return BinaryOperator.Less;
-				}
-				if (op.getType() == TokenType.LessEqual) {
-					return BinaryOperator.LessEqual;
-				}
-				if (op.getType() == TokenType.Greater) {
-					return BinaryOperator.Greater;
-				}
-				if (op.getType() == TokenType.GreaterEqual) {
-					return BinaryOperator.GreaterEqual;
-				}
-				if (op.getType() == TokenType.And) {
-					return BinaryOperator.And;
-				}
-				if (op.getType() == TokenType.Or) {
-					return BinaryOperator.Or;
-				}
-				if (op.getType() == TokenType.Xor) {
-					return BinaryOperator.Xor;
-				}
-				if (op.getType() == TokenType.Assignment) {
-					return BinaryOperator.Assignment;
-				}
-				ExpressionError.error("Unknown binary operator " + op + ".", op.getSpan());
-				return null; // not reached
-			}
-		}
-
-		private final Expression leftOperand;
-		private final BinaryOperator operator;
-		private final Expression rightOperand;
-
-		public BinaryOperation (Expression leftOperand, Token operator, Expression rightOperand) {
-			super(operator.getSpan());
-			this.leftOperand = leftOperand;
-			this.operator = BinaryOperator.getOperator(operator);
-			this.rightOperand = rightOperand;
-		}
-
-		public Expression getLeftOperand () {
-			return leftOperand;
-		}
-
-		public BinaryOperator getOperator () {
-			return operator;
-		}
-
-		public Expression getRightOperand () {
-			return rightOperand;
-		}
-
-		private Object evaluateAddition (Object left, Object right) {
-			if (left instanceof String || right instanceof String) {
-				return left.toString() + right.toString();
-			}
-			if (left instanceof Double || right instanceof Double) {
-				return ((Number)left).doubleValue() + ((Number)right).doubleValue();
-			}
-			if (left instanceof Float || right instanceof Float) {
-				return ((Number)left).floatValue() + ((Number)right).floatValue();
-			}
-			if (left instanceof Long || right instanceof Long) {
-				return ((Number)left).longValue() + ((Number)right).longValue();
-			}
-			if (left instanceof Integer || right instanceof Integer) {
-				return ((Number)left).intValue() + ((Number)right).intValue();
-			}
-			if (left instanceof Short || right instanceof Short) {
-				return ((Number)left).shortValue() + ((Number)right).shortValue();
-			}
-			if (left instanceof Byte || right instanceof Byte) {
-				return ((Number)left).byteValue() + ((Number)right).byteValue();
-			}
-
-			ExpressionError.error("Operands for addition operator must be numbers or strings, got " + left + ", " + right + ".", getSpan());
-			return null; // never reached
-		}
-
-		private Object evaluateSubtraction (Object left, Object right) {
-			if (left instanceof Double || right instanceof Double) {
-				return ((Number)left).doubleValue() - ((Number)right).doubleValue();
-			} else if (left instanceof Float || right instanceof Float) {
-				return ((Number)left).floatValue() - ((Number)right).floatValue();
-			} else if (left instanceof Long || right instanceof Long) {
-				return ((Number)left).longValue() - ((Number)right).longValue();
-			} else if (left instanceof Integer || right instanceof Integer) {
-				return ((Number)left).intValue() - ((Number)right).intValue();
-			} else if (left instanceof Short || right instanceof Short) {
-				return ((Number)left).shortValue() - ((Number)right).shortValue();
-			} else if (left instanceof Byte || right instanceof Byte) {
-				return ((Number)left).byteValue() - ((Number)right).byteValue();
-			} else {
-				ExpressionError.error("Operands for subtraction operator must be numbers" + left + ", " + right + ".", getSpan());
-				return null; // never reached
-			}
-		}
-
-		private Object evaluateMultiplication (Object left, Object right) {
-			if (left instanceof Double || right instanceof Double) {
-				return ((Number)left).doubleValue() * ((Number)right).doubleValue();
-			} else if (left instanceof Float || right instanceof Float) {
-				return ((Number)left).floatValue() * ((Number)right).floatValue();
-			} else if (left instanceof Long || right instanceof Long) {
-				return ((Number)left).longValue() * ((Number)right).longValue();
-			} else if (left instanceof Integer || right instanceof Integer) {
-				return ((Number)left).intValue() * ((Number)right).intValue();
-			} else if (left instanceof Short || right instanceof Short) {
-				return ((Number)left).shortValue() * ((Number)right).shortValue();
-			} else if (left instanceof Byte || right instanceof Byte) {
-				return ((Number)left).byteValue() * ((Number)right).byteValue();
-			} else {
-				ExpressionError.error("Operands for multiplication operator must be numbers" + left + ", " + right + ".", getSpan());
-				return null; // never reached
-			}
-		}
-
-		private Object evaluateDivision (Object left, Object right) {
-			if (left instanceof Double || right instanceof Double) {
-				return ((Number)left).doubleValue() / ((Number)right).doubleValue();
-			} else if (left instanceof Float || right instanceof Float) {
-				return ((Number)left).floatValue() / ((Number)right).floatValue();
-			} else if (left instanceof Long || right instanceof Long) {
-				return ((Number)left).longValue() / ((Number)right).longValue();
-			} else if (left instanceof Integer || right instanceof Integer) {
-				return ((Number)left).intValue() / ((Number)right).intValue();
-			} else if (left instanceof Short || right instanceof Short) {
-				return ((Number)left).shortValue() / ((Number)right).shortValue();
-			} else if (left instanceof Byte || right instanceof Byte) {
-				return ((Number)left).byteValue() / ((Number)right).byteValue();
-			} else {
-				ExpressionError.error("Operands for division operator must be numbers" + left + ", " + right + ".", getSpan());
-				return null; // never reached
-			}
-		}
-
-		private Object evaluateModulo (Object left, Object right) {
-			if (left instanceof Double || right instanceof Double) {
-				return ((Number)left).doubleValue() % ((Number)right).doubleValue();
-			} else if (left instanceof Float || right instanceof Float) {
-				return ((Number)left).floatValue() % ((Number)right).floatValue();
-			} else if (left instanceof Long || right instanceof Long) {
-				return ((Number)left).longValue() % ((Number)right).longValue();
-			} else if (left instanceof Integer || right instanceof Integer) {
-				return ((Number)left).intValue() % ((Number)right).intValue();
-			} else if (left instanceof Short || right instanceof Short) {
-				return ((Number)left).shortValue() % ((Number)right).shortValue();
-			} else if (left instanceof Byte || right instanceof Byte) {
-				return ((Number)left).byteValue() % ((Number)right).byteValue();
-			} else {
-				ExpressionError.error("Operands for modulo operator must be numbers" + left + ", " + right + ".", getSpan());
-				return null; // never reached
-			}
-		}
-
-		private boolean evaluateLess (Object left, Object right) {
-			if (left instanceof Double || right instanceof Double) {
-				return ((Number)left).doubleValue() < ((Number)right).doubleValue();
-			} else if (left instanceof Float || right instanceof Float) {
-				return ((Number)left).floatValue() < ((Number)right).floatValue();
-			} else if (left instanceof Long || right instanceof Long) {
-				return ((Number)left).longValue() < ((Number)right).longValue();
-			} else if (left instanceof Integer || right instanceof Integer) {
-				return ((Number)left).intValue() < ((Number)right).intValue();
-			} else if (left instanceof Short || right instanceof Short) {
-				return ((Number)left).shortValue() < ((Number)right).shortValue();
-			} else if (left instanceof Byte || right instanceof Byte) {
-				return ((Number)left).byteValue() < ((Number)right).byteValue();
-			} else {
-				ExpressionError.error("Operands for less operator must be numbers" + left + ", " + right + ".", getSpan());
-				return false; // never reached
-			}
-		}
-
-		private Object evaluateLessEqual (Object left, Object right) {
-			if (left instanceof Double || right instanceof Double) {
-				return ((Number)left).doubleValue() <= ((Number)right).doubleValue();
-			} else if (left instanceof Float || right instanceof Float) {
-				return ((Number)left).floatValue() <= ((Number)right).floatValue();
-			} else if (left instanceof Long || right instanceof Long) {
-				return ((Number)left).longValue() <= ((Number)right).longValue();
-			} else if (left instanceof Integer || right instanceof Integer) {
-				return ((Number)left).intValue() <= ((Number)right).intValue();
-			} else if (left instanceof Short || right instanceof Short) {
-				return ((Number)left).shortValue() <= ((Number)right).shortValue();
-			} else if (left instanceof Byte || right instanceof Byte) {
-				return ((Number)left).byteValue() <= ((Number)right).byteValue();
-			} else {
-				ExpressionError.error("Operands for less/equal operator must be numbers" + left + ", " + right + ".", getSpan());
-				return null; // never reached
-			}
-		}
-
-		private Object evaluateGreater (Object left, Object right) {
-			if(left == null || right == null){
-				return false;
-			}
-			if (left instanceof Double || right instanceof Double) {
-				return ((Number)left).doubleValue() > ((Number)right).doubleValue();
-			} else if (left instanceof Float || right instanceof Float) {
-				return ((Number)left).floatValue() > ((Number)right).floatValue();
-			} else if (left instanceof Long || right instanceof Long) {
-				return ((Number)left).longValue() > ((Number)right).longValue();
-			} else if (left instanceof Integer || right instanceof Integer) {
-				return ((Number)left).intValue() > ((Number)right).intValue();
-			} else if (left instanceof Short || right instanceof Short) {
-				return ((Number)left).shortValue() > ((Number)right).shortValue();
-			} else if (left instanceof Byte || right instanceof Byte) {
-				return ((Number)left).byteValue() > ((Number)right).byteValue();
-			} else {
-				ExpressionError.error("Operands for greater operator must be numbers" + left + ", " + right + ".", getSpan());
-				return null; // never reached
-			}
-		}
-
-		private Object evaluateGreaterEqual (Object left, Object right) {
-			if (left instanceof Double || right instanceof Double) {
-				return ((Number)left).doubleValue() >= ((Number)right).doubleValue();
-			} else if (left instanceof Float || right instanceof Float) {
-				return ((Number)left).floatValue() >= ((Number)right).floatValue();
-			} else if (left instanceof Long || right instanceof Long) {
-				return ((Number)left).longValue() >= ((Number)right).longValue();
-			} else if (left instanceof Integer || right instanceof Integer) {
-				return ((Number)left).intValue() >= ((Number)right).intValue();
-			} else if (left instanceof Short || right instanceof Short) {
-				return ((Number)left).shortValue() >= ((Number)right).shortValue();
-			} else if (left instanceof Byte || right instanceof Byte) {
-				return ((Number)left).byteValue() >= ((Number)right).byteValue();
-			} else {
-				ExpressionError.error("Operands for greater/equal operator must be numbers" + left + ", " + right + ".", getSpan());
-				return null; // never reached
-			}
-		}
-
-		private Object evaluateAnd (Object left, ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			if (!(left instanceof Boolean)) {
-				ExpressionError.error("Left operand must be a boolean, got " + left + ".", getLeftOperand().getSpan());
-			}
-			if (!(Boolean)left) {
-				return false;
-			}
-			Object right = getRightOperand().evaluate(template, context);
-			if (!(right instanceof Boolean)) {
-				ExpressionError.error("Right operand must be a boolean, got " + right + ".", getRightOperand().getSpan());
-			}
-			return (Boolean)left && (Boolean)right;
-		}
-
-		private Object evaluateOr (Object left, ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			if (!(left instanceof Boolean)) {
-				ExpressionError.error("Left operand must be a boolean, got " + left + ".", getLeftOperand().getSpan());
-			}
-			if ((Boolean)left) {
-				return true;
-			}
-			Object right = getRightOperand().evaluate(template, context);
-			if (!(right instanceof Boolean)) {
-				ExpressionError.error("Right operand must be a boolean, got " + right + ".", getRightOperand().getSpan());
-			}
-			return (Boolean)left || (Boolean)right;
-		}
-
-		private Object evaluateXor (Object left, Object right) {
-			if (!(left instanceof Boolean)) {
-				ExpressionError.error("Left operand must be a boolean, got " + left + ".", getLeftOperand().getSpan());
-			}
-			if (!(right instanceof Boolean)) {
-				ExpressionError.error("Right operand must be a boolean, got " + right + ".", getRightOperand().getSpan());
-			}
-			return (Boolean)left ^ (Boolean)right;
-		}
-
-		private Object evaluateEqual (Object left, Object right) {
-			if (left != null) {
-				return left.equals(right);
-			}
-			if (right != null) {
-				return right.equals(left);
-			}
-			return true;
-		}
-
-		private Object evaluateNotEqual (Object left, Object right) {
-			return !(Boolean)evaluateEqual(left, right);
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			if (getOperator() == BinaryOperator.Assignment) {
-				if (!(getLeftOperand() instanceof VariableAccess)) {
-					ExpressionError.error("Can only assign to top-level variables in context.", getLeftOperand().getSpan());
-				}
-				Object value = getRightOperand().evaluate(template, context);
-				context.set(((VariableAccess)getLeftOperand()).getVariableName().getText(), value);
-				return null;
-			}
-
-			Object left = getLeftOperand().evaluate(template, context);
-			Object right = getOperator() == BinaryOperator.And || getOperator() == BinaryOperator.Or ? null : getRightOperand().evaluate(template, context);
-
-			switch (getOperator()) {
-			case Addition:
-				return evaluateAddition(left, right);
-			case Subtraction:
-				return evaluateSubtraction(left, right);
-			case Multiplication:
-				return evaluateMultiplication(left, right);
-			case Division:
-				return evaluateDivision(left, right);
-			case Modulo:
-				return evaluateModulo(left, right);
-			case Less:
-				return evaluateLess(left, right);
-			case LessEqual:
-				return evaluateLessEqual(left, right);
-			case Greater:
-				return evaluateGreater(left, right);
-			case GreaterEqual:
-				return evaluateGreaterEqual(left, right);
-			case Equal:
-				return evaluateEqual(left, right);
-			case NotEqual:
-				return evaluateNotEqual(left, right);
-			case And:
-				return evaluateAnd(left, template, context);
-			case Or:
-				return evaluateOr(left, template, context);
-			case Xor:
-				return evaluateXor(left, right);
-			default:
-				ExpressionError.error("Binary operator " + getOperator().name() + " not implemented", getSpan());
-				return null;
-			}
-		}
-	}
-
-	/** A ternary operation is an abbreviated if/then/else operation, and equivalent to the the ternary operator in Java. **/
-	public static class TernaryOperation extends Expression {
-		private final Expression condition;
-		private final Expression trueExpression;
-		private final Expression falseExpression;
-
-		public TernaryOperation (Expression condition, Expression trueExpression, Expression falseExpression) {
-			super(new Span(condition.getSpan(), falseExpression.getSpan()));
-			this.condition = condition;
-			this.trueExpression = trueExpression;
-			this.falseExpression = falseExpression;
-		}
-
-		public Expression getCondition () {
-			return condition;
-		}
-
-		public Expression getTrueExpression () {
-			return trueExpression;
-		}
-
-		public Expression getFalseExpression () {
-			return falseExpression;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			Object condition = getCondition().evaluate(template, context);
-			if (!(condition instanceof Boolean)) {
-				ExpressionError.error("Condition of ternary operator must be a boolean, got " + condition + ".", getSpan());
-			}
-			return ((Boolean)condition) ? getTrueExpression().evaluate(template, context) : getFalseExpression().evaluate(template, context);
-		}
-	}
-
-	/** A null literal, with the single value <code>null</code> **/
-	public static class NullLiteral extends Expression {
-		public NullLiteral (Span span) {
-			super(span);
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			return null;
-		}
-	}
-
-	/** A boolean literal, with the values <code>true</code> and <code>false</code> **/
-	public static class BooleanLiteral extends Expression {
-		private final Boolean value;
-
-		public BooleanLiteral (Span literal) {
-			super(literal);
-			this.value = Boolean.parseBoolean(literal.getText());
-		}
-
-		public Boolean getValue () {
-			return value;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			return value;
-		}
-	}
-
-	/** A double precision floating point literal. Must be marked with the <code>d</code> suffix, e.g. "1.0d". **/
-	public static class DoubleLiteral extends Expression {
-		private final Double value;
-
-		public DoubleLiteral (Span literal) {
-			super(literal);
-			this.value = Double.parseDouble(literal.getText().substring(0, literal.getText().length() - 1));
-		}
-
-		public Double getValue () {
-			return value;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			return value;
-		}
-	}
-
-	/** A single precision floating point literla. May be optionally marked with the <code>f</code> suffix, e.g. "1.0f". **/
-	public static class FloatLiteral extends Expression {
-		private final Float value;
-
-		public FloatLiteral (Span literal) {
-			super(literal);
-			String text = literal.getText();
-			if (text.charAt(text.length() - 1) == 'f') {
-				text = text.substring(0, text.length() - 1);
-			}
-			this.value = Float.parseFloat(text);
-		}
-
-		public Float getValue () {
-			return value;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			return value;
-		}
-	}
-
-	/** A byte literal. Must be marked with the <code>b</code> suffix, e.g. "123b". **/
-	public static class ByteLiteral extends Expression {
-		private final Byte value;
-
-		public ByteLiteral (Span literal) {
-			super(literal);
-			this.value = Byte.parseByte(literal.getText().substring(0, literal.getText().length() - 1));
-		}
-
-		public Byte getValue () {
-			return value;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			return value;
-		}
-	}
-
-	/** A short literal. Must be marked with the <code>s</code> suffix, e.g. "123s". **/
-	public static class ShortLiteral extends Expression {
-		private final Short value;
-
-		public ShortLiteral (Span literal) {
-			super(literal);
-			this.value = Short.parseShort(literal.getText().substring(0, literal.getText().length() - 1));
-		}
-
-		public Short getValue () {
-			return value;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			return value;
-		}
-	}
-
-	/** An integer literal. **/
-	public static class IntegerLiteral extends Expression {
-		private final Integer value;
-
-		public IntegerLiteral (Span literal) {
-			super(literal);
-			this.value = Integer.parseInt(literal.getText());
-		}
-
-		public Integer getValue () {
-			return value;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			return value;
-		}
-	}
-
-	/** A long integer literal. Must be marked with the <code>l</code> suffix, e.g. "123l". **/
-	public static class LongLiteral extends Expression {
-		private final Long value;
-
-		public LongLiteral (Span literal) {
-			super(literal);
-			this.value = Long.parseLong(literal.getText().substring(0, literal.getText().length() - 1));
-		}
-
-		public Long getValue () {
-			return value;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			return value;
-		}
-	}
-
-	/** A character literal, enclosed in single quotes. Supports escape sequences \n, \r,\t, \' and \\. **/
-	public static class CharacterLiteral extends Expression {
-		private final Character value;
-
-		public CharacterLiteral (Span literal) {
-			super(literal);
-
-			String text = literal.getText();
-			if (text.length() > 3) {
-				if (text.charAt(2) == 'n') {
-					value = '\n';
-				} else if (text.charAt(2) == 'r') {
-					value = '\r';
-				} else if (text.charAt(2) == 't') {
-					value = '\t';
-				} else if (text.charAt(2) == '\\') {
-					value = '\\';
-				} else if (text.charAt(2) == '\'') {
-					value = '\'';
-				} else {
-					ExpressionError.error("Unknown escape sequence '" + literal.getText() + "'.", literal);
-					value = 0; // never reached
-				}
-			} else {
-				this.value = literal.getText().charAt(1);
-			}
-		}
-
-		public Character getValue () {
-			return value;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			return value;
-		}
-	}
-
-	/** A string literal, enclosed in double quotes. Supports escape sequences \n, \r, \t, \" and \\. **/
-	public static class StringLiteral extends Expression {
-		private final String value;
-
-		public StringLiteral (Span literal) {
-			super(literal);
-			String text = getSpan().getText();
-			String unescapedValue = text.substring(1, text.length() - 1);
-			StringBuilder builder = new StringBuilder();
-
-			CharacterStream stream = new CharacterStream(unescapedValue);
-			while (stream.hasMore()) {
-				if (stream.match("\\\\", true)) {
-					builder.append('\\');
-				} else if (stream.match("\\n", true)) {
-					builder.append('\n');
-				} else if (stream.match("\\r", true)) {
-					builder.append('\r');
-				} else if (stream.match("\\t", true)) {
-					builder.append('\t');
-				} else if (stream.match("\\\"", true)) {
-					builder.append('"');
-				} else {
-					builder.append(stream.consume());
-				}
-			}
-			value = builder.toString();
-		}
-
-		/** Returns the literal without quotes **/
-		public String getValue () {
-			return value;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			return value;
-		}
-	}
-
-	/** Represents a top-level variable access by name. E.g. in the expression "a + 1", <code>a</code> would be encoded as a
-	 * VariableAccess node. Variables can be both read (in expressions) and written to (in assignments). Variable values are looked
-	 * up and written to a {@link ExpressionTemplateContext}. **/
-	public static class VariableAccess extends Expression {
-		public VariableAccess (Span name) {
-			super(name);
-		}
-
-		public Span getVariableName () {
-			return getSpan();
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			Object value = context.get(getSpan().getText());
-			//if (value == null) ExpressionError.error("找不到变量'" + getSpan().getText() + "'或变量值为null", getSpan());
-			return value;
-		}
-	}
-
-	/** Represents a map or array element access of the form <code>mapOrArray[keyOrIndex]</code>. Maps and arrays may only be read
-	 * from. **/
-	public static class MapOrArrayAccess extends Expression {
-		private final Expression mapOrArray;
-		private final Expression keyOrIndex;
-
-		public MapOrArrayAccess (Span span, Expression mapOrArray, Expression keyOrIndex) {
-			super(span);
-			this.mapOrArray = mapOrArray;
-			this.keyOrIndex = keyOrIndex;
-		}
-
-		/** Returns an expression that must evaluate to a map or array. **/
-		public Expression getMapOrArray () {
-			return mapOrArray;
-		}
-
-		/** Returns an expression that is used as the key or index to fetch a map or array element. **/
-		public Expression getKeyOrIndex () {
-			return keyOrIndex;
-		}
-
-		@SuppressWarnings("rawtypes")
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			Object mapOrArray = getMapOrArray().evaluate(template, context);
-			if (mapOrArray == null) {
-				return null;
-			}
-			Object keyOrIndex = getKeyOrIndex().evaluate(template, context);
-			if (keyOrIndex == null) {
-				return null;
-			}
-
-			if (mapOrArray instanceof Map) {
-				return ((Map)mapOrArray).get(keyOrIndex);
-			} else if (mapOrArray instanceof List) {
-				if (!(keyOrIndex instanceof Number)) {
-					ExpressionError.error("List index must be an integer, but was " + keyOrIndex.getClass().getSimpleName(), getKeyOrIndex().getSpan());
-				}
-				int index = ((Number)keyOrIndex).intValue();
-				return ((List)mapOrArray).get(index);
-			} else {
-				if (!(keyOrIndex instanceof Number)) {
-					ExpressionError.error("Array index must be an integer, but was " + keyOrIndex.getClass().getSimpleName(), getKeyOrIndex().getSpan());
-				}
-				int index = ((Number)keyOrIndex).intValue();
-				if (mapOrArray instanceof int[]) {
-					return ((int[])mapOrArray)[index];
-				} else if (mapOrArray instanceof float[]) {
-					return ((float[])mapOrArray)[index];
-				} else if (mapOrArray instanceof double[]) {
-					return ((double[])mapOrArray)[index];
-				} else if (mapOrArray instanceof boolean[]) {
-					return ((boolean[])mapOrArray)[index];
-				} else if (mapOrArray instanceof char[]) {
-					return ((char[])mapOrArray)[index];
-				} else if (mapOrArray instanceof short[]) {
-					return ((short[])mapOrArray)[index];
-				} else if (mapOrArray instanceof long[]) {
-					return ((long[])mapOrArray)[index];
-				} else if (mapOrArray instanceof byte[]) {
-					return ((byte[])mapOrArray)[index];
-				} else if (mapOrArray instanceof String) {
-					return Character.toString(((String)mapOrArray).charAt(index));
-				} else {
-					return ((Object[])mapOrArray)[index];
-				}
-			}
-		}
-	}
-
-	/** Represents an access of a member (field or method or entry in a map) of the form <code>object.member</code>. Members may
-	 * only be read from. **/
-	public static class MemberAccess extends Expression {
-		private final Expression object;
-		private final Span name;
-		private Object cachedMember;
-
-		public MemberAccess (Expression object, Span name) {
-			super(name);
-			this.object = object;
-			this.name = name;
-		}
-
-		/** Returns the object on which to access the member. **/
-		public Expression getObject () {
-			return object;
-		}
-
-		/** The name of the member. **/
-		public Span getName () {
-			return name;
-		}
-
-		/** Returns the cached member descriptor as returned by {@link AbstractReflection#getField(Object, String)} or
-		 * {@link AbstractReflection#getMethod(Object, String, Object...)}. See {@link #setCachedMember(Object)}. **/
-		public Object getCachedMember () {
-			return cachedMember;
-		}
-
-		/** Sets the member descriptor as returned by {@link AbstractReflection#getField(Object, String)} or
-		 * {@link AbstractReflection#getMethod(Object, String, Object...)} for faster member lookups. Called by {@link AstInterpreter} the
-		 * first time this node is evaluated. Subsequent evaluations can use the cached descriptor, avoiding a costly reflective
-		 * lookup. **/
-		public void setCachedMember (Object cachedMember) {
-			this.cachedMember = cachedMember;
-		}
-
-		@SuppressWarnings("rawtypes")
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			Object object = getObject().evaluate(template, context);
-			if (object == null) {
-				return null;
-			}
-
-			// special case for array.length
-			if (object.getClass().isArray() && "length".equals(getName().getText())) {
-				return Array.getLength(object);
-			}
-
-			// special case for map, allows to do map.key instead of map[key]
-			if (object instanceof Map) {
-				Map map = (Map)object;
-				return map.get(getName().getText());
-			}
-
-			Object field = getCachedMember();
-			if (field != null) {
-				try {
-					return AbstractReflection.getInstance().getFieldValue(object, field);
-				} catch (Throwable t) {
-					// fall through
-				}
-			}
-			String text = getName().getText();
-			field = AbstractReflection.getInstance().getField(object, text);
-			if (field == null) {
-				String methodName = null;
-				if(text.length() > 1){
-					methodName = text.substring(0,1).toUpperCase() + text.substring(1);
-				}else{
-					methodName = text.toUpperCase();
-				}
-				MemberAccess access = new MemberAccess(this.object, new Span("get" + methodName));
-				MethodCall methodCall = new MethodCall(getName(),access, Collections.emptyList());
-				try {
-					return methodCall.evaluate(template, context);
-				} catch (TemplateException e) {
-					if(ExceptionUtils.indexOfThrowable(e, InvocationTargetException.class) > -1){
-						ExpressionError.error(String.format("在%s中调用方法get%s发生异常"
-								,object.getClass()
-								,methodName), getSpan(),e);
-						return null;
-					}
-					access = new MemberAccess(this.object, new Span("is" + methodName));
-					methodCall = new MethodCall(getName(),access, Collections.emptyList());
-					try {
-						return methodCall.evaluate(template, context);
-					} catch (TemplateException e1) {
-						if(ExceptionUtils.indexOfThrowable(e1, InvocationTargetException.class) > -1){
-							ExpressionError.error(String.format("在%s中调用方法is%s发生异常"
-									,object.getClass()
-									,methodName), getSpan(),e);
-							return null;
-						}
-						ExpressionError.error(String.format("在%s中找不到属性%s或者方法get%s、方法is%s"
-								,object.getClass()
-								,getName().getText()
-								,methodName
-								,methodName), getSpan());
-					}
-				}
-			}
-			setCachedMember(field);
-			return AbstractReflection.getInstance().getFieldValue(object, field);
-		}
-	}
-
-	public static class LambdaAccess extends Expression {
-		private final Expression element;
-		private final Expression function;
-		private MemberAccess arrayLike;
-
-		public LambdaAccess (Span span, Expression element, Expression function) {
-			super(span);
-			this.element = element;
-			this.function = function;
-		}
-
-		/** Returns an expression that must evaluate to a map or array. **/
-		public Expression getElement() {
-			return element;
-		}
-
-		/** Returns an expression that is used as the key or index to fetch a map or array element. **/
-		public Expression getFunction() {
-			return function;
-		}
-		@SuppressWarnings("rawtypes")
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			if ("map".equals(arrayLike.getName().getText())) {
-				Expression object = arrayLike.getObject();
-				if (object instanceof VariableAccess) {
-					VariableAccess arrLike = (VariableAccess) object;
-					String parName = arrLike.getVariableName().getText();
-					Object arrLikeObj = context.get(parName);
-					if (arrLikeObj instanceof Collection) {
-						Collection<?> coll = (Collection<?>) arrLikeObj;
-
-						if (function instanceof MethodCall) {
-							List<Object> collect = coll.stream().map(o -> {
-								Object result = null;
-								//TODO need multiple params
-								return ((Supplier) () -> {
-									try {
-										context.push();
-										context.setOnCurrentScope(getElement().getSpan().getText(), o);
-										Object res = function.evaluate(template, context);
-										context.pop();
-										return res;
-									} catch (IOException e) {
-										e.printStackTrace();
-										throw new RuntimeException(e);
-									}
-								});
-
-							}).collect(Collectors.toList());
-							return collect;
-						} else {
-							ExpressionError.error("err : function not instanceof MethodCall.", function.getSpan());
-						}
-						return null;
-					}
-				}
-			} else {
-				ExpressionError.error("只支持 map。不支持的lambda函数: " + arrayLike.getName().getText(), arrayLike.getSpan());
-			}
-			Set<String> variables = context.getVariables();
-
-
-			Object mapOrArray = getElement().evaluate(template, context);
-			if (mapOrArray == null) {
-				return null;
-			}
-			Object keyOrIndex = getFunction().evaluate(template, context);
-			if (keyOrIndex == null) {
-				return null;
-			}
-
-			if (mapOrArray instanceof Map) {
-				return ((Map)mapOrArray).get(keyOrIndex);
-			} else if (mapOrArray instanceof List) {
-				if (!(keyOrIndex instanceof Number)) {
-					ExpressionError.error("List index must be an integer, but was " + keyOrIndex.getClass().getSimpleName(), getFunction().getSpan());
-				}
-				int index = ((Number)keyOrIndex).intValue();
-				return ((List)mapOrArray).get(index);
-			} else {
-				if (!(keyOrIndex instanceof Number)) {
-					ExpressionError.error("Array index must be an integer, but was " + keyOrIndex.getClass().getSimpleName(), getFunction().getSpan());
-				}
-				int index = ((Number)keyOrIndex).intValue();
-				if (mapOrArray instanceof int[]) {
-					return ((int[])mapOrArray)[index];
-				} else if (mapOrArray instanceof float[]) {
-					return ((float[])mapOrArray)[index];
-				} else if (mapOrArray instanceof double[]) {
-					return ((double[])mapOrArray)[index];
-				} else if (mapOrArray instanceof boolean[]) {
-					return ((boolean[])mapOrArray)[index];
-				} else if (mapOrArray instanceof char[]) {
-					return ((char[])mapOrArray)[index];
-				} else if (mapOrArray instanceof short[]) {
-					return ((short[])mapOrArray)[index];
-				} else if (mapOrArray instanceof long[]) {
-					return ((long[])mapOrArray)[index];
-				} else if (mapOrArray instanceof byte[]) {
-					return ((byte[])mapOrArray)[index];
-				} else if (mapOrArray instanceof String) {
-					return Character.toString(((String)mapOrArray).charAt(index));
-				} else {
-					return ((Object[])mapOrArray)[index];
-				}
-			}
-		}
-
-		public void setArrayLike(MemberAccess arrayLike) {
-			this.arrayLike = arrayLike;
-		}
-
-		public MemberAccess getArrayLike() {
-			return arrayLike;
-		}
-	}
-
-	/** Represents a call to a top-level function. A function may either be a {@link FunctionalInterface} stored in a
-	 * {@link ExpressionTemplateContext}, or a {@link Macro} defined in a template. */
-	public static class FunctionCall extends Expression {
-		private final Expression function;
-		private final List<Expression> arguments;
-		private Object cachedFunction;
-		private final ThreadLocal<Object[]> cachedArguments;
-
-		public FunctionCall (Span span, Expression function, List<Expression> arguments) {
-			super(span);
-			this.function = function;
-			this.arguments = arguments;
-			this.cachedArguments = new ThreadLocal<Object[]>();
-		}
-
-		/** Return the expression that must evaluate to a {@link FunctionalInterface} or a {@link Macro}. **/
-		public Expression getFunction () {
-			return function;
-		}
-
-		/** Returns the list of expressions to be passed to the function as arguments. **/
-		public List<Expression> getArguments () {
-			return arguments;
-		}
-
-		/** Returns the cached "function" descriptor as returned by {@link AbstractReflection#getMethod(Object, String, Object...)} or the
-		 * {@link Macro}. See {@link #setCachedFunction(Object)}. **/
-		public Object getCachedFunction () {
-			return cachedFunction;
-		}
-
-		/** Sets the "function" descriptor as returned by {@link AbstractReflection#getMethod(Object, String, Object...)} for faster
-		 * lookups, or the {@link Macro} to be called. Called by {@link AstInterpreter} the first time this node is evaluated.
-		 * Subsequent evaluations can use the cached descriptor, avoiding a costly reflective lookup. **/
-		public void setCachedFunction (Object cachedFunction) {
-			this.cachedFunction = cachedFunction;
-		}
-
-		/** Returns a scratch buffer to store arguments in when calling the function in {@link AstInterpreter}. Avoids generating
-		 * garbage. **/
-		public Object[] getCachedArguments () {
-			Object[] args = cachedArguments.get();
-			if (args == null) {
-				args = new Object[arguments.size()];
-				cachedArguments.set(args);
-			}
-			return args;
-		}
-
-		/** Must be invoked when this node is done evaluating so we don't leak memory **/
-		public void clearCachedArguments () {
-			Object[] args = getCachedArguments();
-			for (int i = 0; i < args.length; i++) {
-				args[i] = null;
-			}
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			try {
-				Object[] argumentValues = getCachedArguments();
-				List<Expression> arguments = getArguments();
-				for (int i = 0, n = argumentValues.length; i < n; i++) {
-					Expression expr = arguments.get(i);
-					argumentValues[i] = expr.evaluate(template, context);
-				}
-
-				// This is a special case to handle template level macros. If a call to a macro is
-				// made, evaluating the function expression will result in an exception, as the
-				// function name can't be found in the context. Instead we need to manually check
-				// if the function expression is a VariableAccess and if so, if it can be found
-				// in the context.
-				Object function = null;
-				if (getFunction() instanceof VariableAccess) {
-					VariableAccess varAccess = (VariableAccess)getFunction();
-					function = context.get(varAccess.getVariableName().getText());
-				} else {
-					function = getFunction().evaluate(template, context);
-				}
-
-				if (function != null) {
-					Object method = getCachedFunction();
-					if (method != null) {
-						try {
-							return AbstractReflection.getInstance().callMethod(function, method, argumentValues);
-						} catch (Throwable t) {
-							// fall through
-						}
-					}
-					method = AbstractReflection.getInstance().getMethod(function, null, argumentValues);
-					if (method == null) {
-						ExpressionError.error("Couldn't find function.", getSpan());
-					}
-					setCachedFunction(method);
-					try {
-						return AbstractReflection.getInstance().callMethod(function, method, argumentValues);
-					} catch (Throwable t) {
-						ExpressionError.error(t.getMessage(), getSpan(), t);
-						return null; // never reached
-					}
-				} else {
-					ExpressionError.error("Couldn't find function " + getFunction(), getSpan());
-					return null; // never reached
-				}
-			} finally {
-				clearCachedArguments();
-			}
-		}
-	}
-
-	/** Represents a call to a method of the form <code>object.method(a, b, c)</code>. **/
-	public static class MethodCall extends Expression {
-		private final MemberAccess method;
-		private final List<Expression> arguments;
-		private Object cachedMethod;
-		private final ThreadLocal<Object[]> cachedArguments;
-		private boolean cachedMethodStatic;
-
-		public MethodCall (Span span, MemberAccess method, List<Expression> arguments) {
-			super(span);
-			this.method = method;
-			this.arguments = arguments;
-			this.cachedArguments = new ThreadLocal<Object[]>();
-		}
-
-		/** Returns the object on which to call the method. **/
-		public Expression getObject () {
-			return method.getObject();
-		}
-
-		/** Returns the method to call. **/
-		public MemberAccess getMethod () {
-			return method;
-		}
-
-		/** Returns the list of expressions to be passed to the function as arguments. **/
-		public List<Expression> getArguments () {
-			return arguments;
-		}
-
-		/** Returns the cached member descriptor as returned by {@link AbstractReflection#getMethod(Object, String, Object...)}. See
-		 * {@link #setCachedMember(Object)}. **/
-		public Object getCachedMethod () {
-			return cachedMethod;
-		}
-
-		/** Sets the method descriptor as returned by {@link AbstractReflection#getMethod(Object, String, Object...)} for faster lookups.
-		 * Called by {@link AstInterpreter} the first time this node is evaluated. Subsequent evaluations can use the cached
-		 * descriptor, avoiding a costly reflective lookup. **/
-		public void setCachedMethod (Object cachedMethod) {
-			this.cachedMethod = cachedMethod;
-		}
-
-		/** Returns a scratch buffer to store arguments in when calling the function in {@link AstInterpreter}. Avoids generating
-		 * garbage. **/
-		public Object[] getCachedArguments () {
-			Object[] args = cachedArguments.get();
-			if (args == null) {
-				args = new Object[arguments.size()];
-				cachedArguments.set(args);
-			}
-			return args;
-		}
-
-		/** Must be invoked when this node is done evaluating so we don't leak memory **/
-		public void clearCachedArguments () {
-			Object[] args = getCachedArguments();
-			for (int i = 0; i < args.length; i++) {
-				args[i] = null;
-			}
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			try {
-				Object object = getObject().evaluate(template, context);
-				if (object == null) {
-					return null;
-				}
-
-				Object[] argumentValues = getCachedArguments();
-				List<Expression> arguments = getArguments();
-				for (int i = 0, n = argumentValues.length; i < n; i++) {
-					Expression expr = arguments.get(i);
-					argumentValues[i] = expr.evaluate(template, context);
-				}
-				// Otherwise try to find a corresponding method or field pointing to a lambda.
-				Object method = getCachedMethod();
-				if (method != null) {
-					try {
-						if (isCachedMethodStatic()) {
-							return AbstractReflection.getInstance().callMethod(null, method, object, argumentValues);
-						}
-						return AbstractReflection.getInstance().callMethod(object, method, argumentValues);
-					} catch (Throwable t) {
-						t.printStackTrace();
-						// fall through
-					}
-				}
-
-				method = AbstractReflection.getInstance().getMethod(object, getMethod().getName().getText(), argumentValues);
-				if (method != null) {
-					// found the method on the object, call it
-					setCachedMethod(method);
-					try {
-						return AbstractReflection.getInstance().callMethod(object, method, argumentValues);
-					} catch (Throwable t) {
-						ExpressionError.error(t.getMessage(), getSpan(), t);
-						return null; // never reached
-					}
-				}
-				method = AbstractReflection.getInstance().getExtensionMethod(object, getMethod().getName().getText(), argumentValues);
-				if(method != null){
-					try {
-						int argumentLength = argumentValues == null ? 0 : argumentValues.length;
-						Object[] parameters = new Object[argumentLength + 1];
-						if(argumentLength > 0){
-							for (int i = 0; i < argumentLength; i++) {
-								parameters[i + 1] = argumentValues[i];
-							}
-						}
-						parameters[0] = object;
-						if(object.getClass().isArray()){
-							Object[] objs = new Object[Array.getLength(object)];
-							for (int i = 0,len = objs.length; i < len; i++) {
-								Array.set(objs, i, Array.get(object, i));
-							}
-							parameters[0] = objs;
-						}
-						return AbstractReflection.getInstance().callMethod(object, method, parameters);
-					} catch (Throwable t) {
-						ExpressionError.error(t.getMessage(), getSpan(), t);
-						// fall through
-						return null;
-					}
-				}else {
-					// didn't find the method on the object, try to find a field pointing to a lambda
-					Object field = AbstractReflection.getInstance().getField(object, getMethod().getName().getText());
-					if (field == null){
-						ExpressionError.error("在'" + object.getClass() + "'中找不到方法 " + getMethod().getName().getText() + "(" + StringUtils.join(JavaReflection.getStringTypes(argumentValues),",") + ")",
-							getSpan());
-					}
-					Object function = AbstractReflection.getInstance().getFieldValue(object, field);
-					method = AbstractReflection.getInstance().getMethod(function, null, argumentValues);
-					if (method == null){
-						ExpressionError.error("在'" + object.getClass() + "'中找不到方法 " + getMethod().getName().getText() + "("+ StringUtils.join(JavaReflection.getStringTypes(argumentValues),",") +")",
-								getSpan());
-					}
-					try {
-						return AbstractReflection.getInstance().callMethod(function, method, argumentValues);
-					} catch (Throwable t) {
-						ExpressionError.error(t.getMessage(), getSpan(), t);
-						return null; // never reached
-					}
-				}
-			} finally {
-				clearCachedArguments();
-			}
-		}
-
-		public void setCachedMethodStatic(boolean cachedMethodStatic) {
-			this.cachedMethodStatic = cachedMethodStatic;
-		}
-
-		public boolean isCachedMethodStatic() {
-			return cachedMethodStatic;
-		}
-	}
-
-	/** Represents a map literal of the form <code>{ key: value, key2: value, ... }</code> which can be nested. */
-	public static class MapLiteral extends Expression {
-		private final List<Token> keys;
-		private final List<Expression> values;
-
-		public MapLiteral (Span span, List<Token> keys, List<Expression> values) {
-			super(span);
-			this.keys = keys;
-			this.values = values;
-		}
-
-		public List<Token> getKeys () {
-			return keys;
-		}
-
-		public List<Expression> getValues () {
-			return values;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			Map<String, Object> map = new HashMap<>();
-			for (int i = 0, n = keys.size(); i < n; i++) {
-				Object value = values.get(i).evaluate(template, context);
-				Token tokenKey = keys.get(i);
-				String key = tokenKey.getSpan().getText();
-				if(tokenKey.getType() == TokenType.StringLiteral){
-					key = (String) new StringLiteral(tokenKey.getSpan()).evaluate(template, context);
-				}else if(key != null && key.startsWith("$")){
-					Object objKey = context.get(key.substring(1));
-					if(objKey != null){
-						key = objKey.toString();
-					}else{
-						key = null;
-					}
-				}
-				map.put(key, value);
-			}
-			return map;
-		}
-	}
-
-	/** Represents a list literal of the form <code>[ value, value2, value3, ...]</code> which can be nested. */
-	public static class ListLiteral extends Expression {
-		public final List<Expression> values;
-
-		public ListLiteral (Span span, List<Expression> values) {
-			super(span);
-			this.values = values;
-		}
-
-		public List<Expression> getValues () {
-			return values;
-		}
-
-		@Override
-		public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
-			List<Object> list = new ArrayList<>();
-			for (int i = 0, n = values.size(); i < n; i++) {
-				list.add(values.get(i).evaluate(template, context));
-			}
-			return list;
-		}
-	}
+    /** Base class for all AST nodes. A node minimally stores the {@link Span} that references its location in the
+     * {@link Source}. **/
+    public abstract static class Node {
+        private final Span span;
+
+        public Node (Span span) {
+            this.span = span;
+        }
+
+        /** Returns the {@link Span} referencing this node's location in the {@link Source}. **/
+        public Span getSpan () {
+            return span;
+        }
+
+        @Override
+        public String toString () {
+            return span.getText();
+        }
+
+        public abstract Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException;
+    }
+
+    /** A text node represents an "un-templated" span in the source that should be emitted verbatim. **/
+    public static class Text extends Node {
+        private final String content;
+
+        public Text (Span text) {
+            super(text);
+            String unescapedValue = text.getText();
+            StringBuilder builder = new StringBuilder();
+
+            CharacterStream stream = new CharacterStream(unescapedValue);
+            while (stream.hasMore()) {
+                if (stream.match("\\{", true)) {
+                    builder.append('{');
+                } else if (stream.match("\\}", true)) {
+                    builder.append('}');
+                } else {
+                    builder.append(stream.consume());
+                }
+            }
+            content = builder.toString();
+        }
+
+        /** Returns the UTF-8 representation of this text node. **/
+        public String getContent () {
+            return content;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            return null;
+        }
+    }
+
+    /** All expressions are subclasses of this node type. Expressions are separated into unary operations (!, -), binary operations
+     * (+, -, *, /, etc.) and ternary operations (?:). */
+    public abstract static class Expression extends Node {
+        public Expression (Span span) {
+            super(span);
+        }
+    }
+
+    /** An unary operation node represents a logical or numerical negation. **/
+    public static class UnaryOperation extends Expression {
+
+        public static enum UnaryOperator {
+            Not, Negate, Positive;
+
+            public static UnaryOperator getOperator (Token op) {
+                if (op.getType() == TokenType.Not) {
+                    return UnaryOperator.Not;
+                }
+                if (op.getType() == TokenType.Plus) {
+                    return UnaryOperator.Positive;
+                }
+                if (op.getType() == TokenType.Minus) {
+                    return UnaryOperator.Negate;
+                }
+                ExpressionError.error("Unknown unary operator " + op + ".", op.getSpan());
+                return null; // not reached
+            }
+        }
+
+        private final UnaryOperator operator;
+        private final Expression operand;
+
+        public UnaryOperation (Token operator, Expression operand) {
+            super(operator.getSpan());
+            this.operator = UnaryOperator.getOperator(operator);
+            this.operand = operand;
+        }
+
+        public UnaryOperator getOperator () {
+            return operator;
+        }
+
+        public Expression getOperand () {
+            return operand;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            Object operand = getOperand().evaluate(template, context);
+
+            if (getOperator() == UnaryOperator.Negate) {
+                if (operand instanceof Integer) {
+                    return -(Integer)operand;
+                } else if (operand instanceof Float) {
+                    return -(Float)operand;
+                } else if (operand instanceof Double) {
+                    return -(Double)operand;
+                } else if (operand instanceof Byte) {
+                    return -(Byte)operand;
+                } else if (operand instanceof Short) {
+                    return -(Short)operand;
+                } else if (operand instanceof Long) {
+                    return -(Long)operand;
+                } else {
+                    ExpressionError.error("Operand of operator '" + getOperator().name() + "' must be a number, got " + operand, getSpan());
+                    return null; // never reached
+                }
+            } else if (getOperator() == UnaryOperator.Not) {
+                if (!(operand instanceof Boolean)) {
+                    ExpressionError.error("Operand of operator '" + getOperator().name() + "' must be a boolean", getSpan());
+                }
+                return !(Boolean)operand;
+            } else {
+                return operand;
+            }
+        }
+    }
+
+    /** A binary operation represents arithmetic operators, like addition or division, comparison operators, like less than or
+     * equals, logical operators, like and, or an assignment. **/
+    public static class BinaryOperation extends Expression {
+
+        public static enum BinaryOperator {
+            Addition, Subtraction, Multiplication, Division, Modulo, Equal, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or, Xor, Assignment;
+
+            public static BinaryOperator getOperator (Token op) {
+                if (op.getType() == TokenType.Plus) {
+                    return BinaryOperator.Addition;
+                }
+                if (op.getType() == TokenType.Minus) {
+                    return BinaryOperator.Subtraction;
+                }
+                if (op.getType() == TokenType.Asterisk) {
+                    return BinaryOperator.Multiplication;
+                }
+                if (op.getType() == TokenType.ForwardSlash) {
+                    return BinaryOperator.Division;
+                }
+                if (op.getType() == TokenType.Percentage) {
+                    return BinaryOperator.Modulo;
+                }
+                if (op.getType() == TokenType.Equal) {
+                    return BinaryOperator.Equal;
+                }
+                if (op.getType() == TokenType.NotEqual) {
+                    return BinaryOperator.NotEqual;
+                }
+                if (op.getType() == TokenType.Less) {
+                    return BinaryOperator.Less;
+                }
+                if (op.getType() == TokenType.LessEqual) {
+                    return BinaryOperator.LessEqual;
+                }
+                if (op.getType() == TokenType.Greater) {
+                    return BinaryOperator.Greater;
+                }
+                if (op.getType() == TokenType.GreaterEqual) {
+                    return BinaryOperator.GreaterEqual;
+                }
+                if (op.getType() == TokenType.And) {
+                    return BinaryOperator.And;
+                }
+                if (op.getType() == TokenType.Or) {
+                    return BinaryOperator.Or;
+                }
+                if (op.getType() == TokenType.Xor) {
+                    return BinaryOperator.Xor;
+                }
+                if (op.getType() == TokenType.Assignment) {
+                    return BinaryOperator.Assignment;
+                }
+                ExpressionError.error("Unknown binary operator " + op + ".", op.getSpan());
+                return null; // not reached
+            }
+        }
+
+        private final Expression leftOperand;
+        private final BinaryOperator operator;
+        private final Expression rightOperand;
+
+        public BinaryOperation (Expression leftOperand, Token operator, Expression rightOperand) {
+            super(operator.getSpan());
+            this.leftOperand = leftOperand;
+            this.operator = BinaryOperator.getOperator(operator);
+            this.rightOperand = rightOperand;
+        }
+
+        public Expression getLeftOperand () {
+            return leftOperand;
+        }
+
+        public BinaryOperator getOperator () {
+            return operator;
+        }
+
+        public Expression getRightOperand () {
+            return rightOperand;
+        }
+
+        private Object evaluateAddition (Object left, Object right) {
+            if (left instanceof String || right instanceof String) {
+                return left.toString() + right.toString();
+            }
+            if (left instanceof Double || right instanceof Double) {
+                return ((Number)left).doubleValue() + ((Number)right).doubleValue();
+            }
+            if (left instanceof Float || right instanceof Float) {
+                return ((Number)left).floatValue() + ((Number)right).floatValue();
+            }
+            if (left instanceof Long || right instanceof Long) {
+                return ((Number)left).longValue() + ((Number)right).longValue();
+            }
+            if (left instanceof Integer || right instanceof Integer) {
+                return ((Number)left).intValue() + ((Number)right).intValue();
+            }
+            if (left instanceof Short || right instanceof Short) {
+                return ((Number)left).shortValue() + ((Number)right).shortValue();
+            }
+            if (left instanceof Byte || right instanceof Byte) {
+                return ((Number)left).byteValue() + ((Number)right).byteValue();
+            }
+
+            ExpressionError.error("Operands for addition operator must be numbers or strings, got " + left + ", " + right + ".", getSpan());
+            return null; // never reached
+        }
+
+        private Object evaluateSubtraction (Object left, Object right) {
+            if (left instanceof Double || right instanceof Double) {
+                return ((Number)left).doubleValue() - ((Number)right).doubleValue();
+            } else if (left instanceof Float || right instanceof Float) {
+                return ((Number)left).floatValue() - ((Number)right).floatValue();
+            } else if (left instanceof Long || right instanceof Long) {
+                return ((Number)left).longValue() - ((Number)right).longValue();
+            } else if (left instanceof Integer || right instanceof Integer) {
+                return ((Number)left).intValue() - ((Number)right).intValue();
+            } else if (left instanceof Short || right instanceof Short) {
+                return ((Number)left).shortValue() - ((Number)right).shortValue();
+            } else if (left instanceof Byte || right instanceof Byte) {
+                return ((Number)left).byteValue() - ((Number)right).byteValue();
+            } else {
+                ExpressionError.error("Operands for subtraction operator must be numbers" + left + ", " + right + ".", getSpan());
+                return null; // never reached
+            }
+        }
+
+        private Object evaluateMultiplication (Object left, Object right) {
+            if (left instanceof Double || right instanceof Double) {
+                return ((Number)left).doubleValue() * ((Number)right).doubleValue();
+            } else if (left instanceof Float || right instanceof Float) {
+                return ((Number)left).floatValue() * ((Number)right).floatValue();
+            } else if (left instanceof Long || right instanceof Long) {
+                return ((Number)left).longValue() * ((Number)right).longValue();
+            } else if (left instanceof Integer || right instanceof Integer) {
+                return ((Number)left).intValue() * ((Number)right).intValue();
+            } else if (left instanceof Short || right instanceof Short) {
+                return ((Number)left).shortValue() * ((Number)right).shortValue();
+            } else if (left instanceof Byte || right instanceof Byte) {
+                return ((Number)left).byteValue() * ((Number)right).byteValue();
+            } else {
+                ExpressionError.error("Operands for multiplication operator must be numbers" + left + ", " + right + ".", getSpan());
+                return null; // never reached
+            }
+        }
+
+        private Object evaluateDivision (Object left, Object right) {
+            if (left instanceof Double || right instanceof Double) {
+                return ((Number)left).doubleValue() / ((Number)right).doubleValue();
+            } else if (left instanceof Float || right instanceof Float) {
+                return ((Number)left).floatValue() / ((Number)right).floatValue();
+            } else if (left instanceof Long || right instanceof Long) {
+                return ((Number)left).longValue() / ((Number)right).longValue();
+            } else if (left instanceof Integer || right instanceof Integer) {
+                return ((Number)left).intValue() / ((Number)right).intValue();
+            } else if (left instanceof Short || right instanceof Short) {
+                return ((Number)left).shortValue() / ((Number)right).shortValue();
+            } else if (left instanceof Byte || right instanceof Byte) {
+                return ((Number)left).byteValue() / ((Number)right).byteValue();
+            } else {
+                ExpressionError.error("Operands for division operator must be numbers" + left + ", " + right + ".", getSpan());
+                return null; // never reached
+            }
+        }
+
+        private Object evaluateModulo (Object left, Object right) {
+            if (left instanceof Double || right instanceof Double) {
+                return ((Number)left).doubleValue() % ((Number)right).doubleValue();
+            } else if (left instanceof Float || right instanceof Float) {
+                return ((Number)left).floatValue() % ((Number)right).floatValue();
+            } else if (left instanceof Long || right instanceof Long) {
+                return ((Number)left).longValue() % ((Number)right).longValue();
+            } else if (left instanceof Integer || right instanceof Integer) {
+                return ((Number)left).intValue() % ((Number)right).intValue();
+            } else if (left instanceof Short || right instanceof Short) {
+                return ((Number)left).shortValue() % ((Number)right).shortValue();
+            } else if (left instanceof Byte || right instanceof Byte) {
+                return ((Number)left).byteValue() % ((Number)right).byteValue();
+            } else {
+                ExpressionError.error("Operands for modulo operator must be numbers" + left + ", " + right + ".", getSpan());
+                return null; // never reached
+            }
+        }
+
+        private boolean evaluateLess (Object left, Object right) {
+            if (left instanceof Double || right instanceof Double) {
+                return ((Number)left).doubleValue() < ((Number)right).doubleValue();
+            } else if (left instanceof Float || right instanceof Float) {
+                return ((Number)left).floatValue() < ((Number)right).floatValue();
+            } else if (left instanceof Long || right instanceof Long) {
+                return ((Number)left).longValue() < ((Number)right).longValue();
+            } else if (left instanceof Integer || right instanceof Integer) {
+                return ((Number)left).intValue() < ((Number)right).intValue();
+            } else if (left instanceof Short || right instanceof Short) {
+                return ((Number)left).shortValue() < ((Number)right).shortValue();
+            } else if (left instanceof Byte || right instanceof Byte) {
+                return ((Number)left).byteValue() < ((Number)right).byteValue();
+            } else {
+                ExpressionError.error("Operands for less operator must be numbers" + left + ", " + right + ".", getSpan());
+                return false; // never reached
+            }
+        }
+
+        private Object evaluateLessEqual (Object left, Object right) {
+            if (left instanceof Double || right instanceof Double) {
+                return ((Number)left).doubleValue() <= ((Number)right).doubleValue();
+            } else if (left instanceof Float || right instanceof Float) {
+                return ((Number)left).floatValue() <= ((Number)right).floatValue();
+            } else if (left instanceof Long || right instanceof Long) {
+                return ((Number)left).longValue() <= ((Number)right).longValue();
+            } else if (left instanceof Integer || right instanceof Integer) {
+                return ((Number)left).intValue() <= ((Number)right).intValue();
+            } else if (left instanceof Short || right instanceof Short) {
+                return ((Number)left).shortValue() <= ((Number)right).shortValue();
+            } else if (left instanceof Byte || right instanceof Byte) {
+                return ((Number)left).byteValue() <= ((Number)right).byteValue();
+            } else {
+                ExpressionError.error("Operands for less/equal operator must be numbers" + left + ", " + right + ".", getSpan());
+                return null; // never reached
+            }
+        }
+
+        private Object evaluateGreater (Object left, Object right) {
+            if(left == null || right == null){
+                return false;
+            }
+            if (left instanceof Double || right instanceof Double) {
+                return ((Number)left).doubleValue() > ((Number)right).doubleValue();
+            } else if (left instanceof Float || right instanceof Float) {
+                return ((Number)left).floatValue() > ((Number)right).floatValue();
+            } else if (left instanceof Long || right instanceof Long) {
+                return ((Number)left).longValue() > ((Number)right).longValue();
+            } else if (left instanceof Integer || right instanceof Integer) {
+                return ((Number)left).intValue() > ((Number)right).intValue();
+            } else if (left instanceof Short || right instanceof Short) {
+                return ((Number)left).shortValue() > ((Number)right).shortValue();
+            } else if (left instanceof Byte || right instanceof Byte) {
+                return ((Number)left).byteValue() > ((Number)right).byteValue();
+            } else {
+                ExpressionError.error("Operands for greater operator must be numbers" + left + ", " + right + ".", getSpan());
+                return null; // never reached
+            }
+        }
+
+        private Object evaluateGreaterEqual (Object left, Object right) {
+            if (left instanceof Double || right instanceof Double) {
+                return ((Number)left).doubleValue() >= ((Number)right).doubleValue();
+            } else if (left instanceof Float || right instanceof Float) {
+                return ((Number)left).floatValue() >= ((Number)right).floatValue();
+            } else if (left instanceof Long || right instanceof Long) {
+                return ((Number)left).longValue() >= ((Number)right).longValue();
+            } else if (left instanceof Integer || right instanceof Integer) {
+                return ((Number)left).intValue() >= ((Number)right).intValue();
+            } else if (left instanceof Short || right instanceof Short) {
+                return ((Number)left).shortValue() >= ((Number)right).shortValue();
+            } else if (left instanceof Byte || right instanceof Byte) {
+                return ((Number)left).byteValue() >= ((Number)right).byteValue();
+            } else {
+                ExpressionError.error("Operands for greater/equal operator must be numbers" + left + ", " + right + ".", getSpan());
+                return null; // never reached
+            }
+        }
+
+        private Object evaluateAnd (Object left, ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            if (!(left instanceof Boolean)) {
+                ExpressionError.error("Left operand must be a boolean, got " + left + ".", getLeftOperand().getSpan());
+            }
+            if (!(Boolean)left) {
+                return false;
+            }
+            Object right = getRightOperand().evaluate(template, context);
+            if (!(right instanceof Boolean)) {
+                ExpressionError.error("Right operand must be a boolean, got " + right + ".", getRightOperand().getSpan());
+            }
+            return (Boolean)left && (Boolean)right;
+        }
+
+        private Object evaluateOr (Object left, ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            if (!(left instanceof Boolean)) {
+                ExpressionError.error("Left operand must be a boolean, got " + left + ".", getLeftOperand().getSpan());
+            }
+            if ((Boolean)left) {
+                return true;
+            }
+            Object right = getRightOperand().evaluate(template, context);
+            if (!(right instanceof Boolean)) {
+                ExpressionError.error("Right operand must be a boolean, got " + right + ".", getRightOperand().getSpan());
+            }
+            return (Boolean)left || (Boolean)right;
+        }
+
+        private Object evaluateXor (Object left, Object right) {
+            if (!(left instanceof Boolean)) {
+                ExpressionError.error("Left operand must be a boolean, got " + left + ".", getLeftOperand().getSpan());
+            }
+            if (!(right instanceof Boolean)) {
+                ExpressionError.error("Right operand must be a boolean, got " + right + ".", getRightOperand().getSpan());
+            }
+            return (Boolean)left ^ (Boolean)right;
+        }
+
+        private Object evaluateEqual (Object left, Object right) {
+            if (left != null) {
+                return left.equals(right);
+            }
+            if (right != null) {
+                return right.equals(left);
+            }
+            return true;
+        }
+
+        private Object evaluateNotEqual (Object left, Object right) {
+            return !(Boolean)evaluateEqual(left, right);
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            if (getOperator() == BinaryOperator.Assignment) {
+                if (!(getLeftOperand() instanceof VariableAccess)) {
+                    ExpressionError.error("Can only assign to top-level variables in context.", getLeftOperand().getSpan());
+                }
+                Object value = getRightOperand().evaluate(template, context);
+                context.set(((VariableAccess)getLeftOperand()).getVariableName().getText(), value);
+                return null;
+            }
+
+            Object left = getLeftOperand().evaluate(template, context);
+            Object right = getOperator() == BinaryOperator.And || getOperator() == BinaryOperator.Or ? null : getRightOperand().evaluate(template, context);
+
+            switch (getOperator()) {
+                case Addition:
+                    return evaluateAddition(left, right);
+                case Subtraction:
+                    return evaluateSubtraction(left, right);
+                case Multiplication:
+                    return evaluateMultiplication(left, right);
+                case Division:
+                    return evaluateDivision(left, right);
+                case Modulo:
+                    return evaluateModulo(left, right);
+                case Less:
+                    return evaluateLess(left, right);
+                case LessEqual:
+                    return evaluateLessEqual(left, right);
+                case Greater:
+                    return evaluateGreater(left, right);
+                case GreaterEqual:
+                    return evaluateGreaterEqual(left, right);
+                case Equal:
+                    return evaluateEqual(left, right);
+                case NotEqual:
+                    return evaluateNotEqual(left, right);
+                case And:
+                    return evaluateAnd(left, template, context);
+                case Or:
+                    return evaluateOr(left, template, context);
+                case Xor:
+                    return evaluateXor(left, right);
+                default:
+                    ExpressionError.error("Binary operator " + getOperator().name() + " not implemented", getSpan());
+                    return null;
+            }
+        }
+    }
+
+    /** A ternary operation is an abbreviated if/then/else operation, and equivalent to the the ternary operator in Java. **/
+    public static class TernaryOperation extends Expression {
+        private final Expression condition;
+        private final Expression trueExpression;
+        private final Expression falseExpression;
+
+        public TernaryOperation (Expression condition, Expression trueExpression, Expression falseExpression) {
+            super(new Span(condition.getSpan(), falseExpression.getSpan()));
+            this.condition = condition;
+            this.trueExpression = trueExpression;
+            this.falseExpression = falseExpression;
+        }
+
+        public Expression getCondition () {
+            return condition;
+        }
+
+        public Expression getTrueExpression () {
+            return trueExpression;
+        }
+
+        public Expression getFalseExpression () {
+            return falseExpression;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            Object condition = getCondition().evaluate(template, context);
+            if (!(condition instanceof Boolean)) {
+                ExpressionError.error("Condition of ternary operator must be a boolean, got " + condition + ".", getSpan());
+            }
+            return ((Boolean)condition) ? getTrueExpression().evaluate(template, context) : getFalseExpression().evaluate(template, context);
+        }
+    }
+
+    /** A null literal, with the single value <code>null</code> **/
+    public static class NullLiteral extends Expression {
+        public NullLiteral (Span span) {
+            super(span);
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            return null;
+        }
+    }
+
+    /** A boolean literal, with the values <code>true</code> and <code>false</code> **/
+    public static class BooleanLiteral extends Expression {
+        private final Boolean value;
+
+        public BooleanLiteral (Span literal) {
+            super(literal);
+            this.value = Boolean.parseBoolean(literal.getText());
+        }
+
+        public Boolean getValue () {
+            return value;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            return value;
+        }
+    }
+
+    /** A double precision floating point literal. Must be marked with the <code>d</code> suffix, e.g. "1.0d". **/
+    public static class DoubleLiteral extends Expression {
+        private final Double value;
+
+        public DoubleLiteral (Span literal) {
+            super(literal);
+            this.value = Double.parseDouble(literal.getText().substring(0, literal.getText().length() - 1));
+        }
+
+        public Double getValue () {
+            return value;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            return value;
+        }
+    }
+
+    /** A single precision floating point literla. May be optionally marked with the <code>f</code> suffix, e.g. "1.0f". **/
+    public static class FloatLiteral extends Expression {
+        private final Float value;
+
+        public FloatLiteral (Span literal) {
+            super(literal);
+            String text = literal.getText();
+            if (text.charAt(text.length() - 1) == 'f') {
+                text = text.substring(0, text.length() - 1);
+            }
+            this.value = Float.parseFloat(text);
+        }
+
+        public Float getValue () {
+            return value;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            return value;
+        }
+    }
+
+    /** A byte literal. Must be marked with the <code>b</code> suffix, e.g. "123b". **/
+    public static class ByteLiteral extends Expression {
+        private final Byte value;
+
+        public ByteLiteral (Span literal) {
+            super(literal);
+            this.value = Byte.parseByte(literal.getText().substring(0, literal.getText().length() - 1));
+        }
+
+        public Byte getValue () {
+            return value;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            return value;
+        }
+    }
+
+    /** A short literal. Must be marked with the <code>s</code> suffix, e.g. "123s". **/
+    public static class ShortLiteral extends Expression {
+        private final Short value;
+
+        public ShortLiteral (Span literal) {
+            super(literal);
+            this.value = Short.parseShort(literal.getText().substring(0, literal.getText().length() - 1));
+        }
+
+        public Short getValue () {
+            return value;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            return value;
+        }
+    }
+
+    /** An integer literal. **/
+    public static class IntegerLiteral extends Expression {
+        private final Integer value;
+
+        public IntegerLiteral (Span literal) {
+            super(literal);
+            this.value = Integer.parseInt(literal.getText());
+        }
+
+        public Integer getValue () {
+            return value;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            return value;
+        }
+    }
+
+    /** A long integer literal. Must be marked with the <code>l</code> suffix, e.g. "123l". **/
+    public static class LongLiteral extends Expression {
+        private final Long value;
+
+        public LongLiteral (Span literal) {
+            super(literal);
+            this.value = Long.parseLong(literal.getText().substring(0, literal.getText().length() - 1));
+        }
+
+        public Long getValue () {
+            return value;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            return value;
+        }
+    }
+
+    /** A character literal, enclosed in single quotes. Supports escape sequences \n, \r,\t, \' and \\. **/
+    public static class CharacterLiteral extends Expression {
+        private final Character value;
+
+        public CharacterLiteral (Span literal) {
+            super(literal);
+
+            String text = literal.getText();
+            if (text.length() > 3) {
+                if (text.charAt(2) == 'n') {
+                    value = '\n';
+                } else if (text.charAt(2) == 'r') {
+                    value = '\r';
+                } else if (text.charAt(2) == 't') {
+                    value = '\t';
+                } else if (text.charAt(2) == '\\') {
+                    value = '\\';
+                } else if (text.charAt(2) == '\'') {
+                    value = '\'';
+                } else {
+                    ExpressionError.error("Unknown escape sequence '" + literal.getText() + "'.", literal);
+                    value = 0; // never reached
+                }
+            } else {
+                this.value = literal.getText().charAt(1);
+            }
+        }
+
+        public Character getValue () {
+            return value;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            return value;
+        }
+    }
+
+    /** A string literal, enclosed in double quotes. Supports escape sequences \n, \r, \t, \" and \\. **/
+    public static class StringLiteral extends Expression {
+        private final String value;
+
+        public StringLiteral (Span literal) {
+            super(literal);
+            String text = getSpan().getText();
+            String unescapedValue = text.substring(1, text.length() - 1);
+            StringBuilder builder = new StringBuilder();
+
+            CharacterStream stream = new CharacterStream(unescapedValue);
+            while (stream.hasMore()) {
+                if (stream.match("\\\\", true)) {
+                    builder.append('\\');
+                } else if (stream.match("\\n", true)) {
+                    builder.append('\n');
+                } else if (stream.match("\\r", true)) {
+                    builder.append('\r');
+                } else if (stream.match("\\t", true)) {
+                    builder.append('\t');
+                } else if (stream.match("\\\"", true)) {
+                    builder.append('"');
+                } else {
+                    builder.append(stream.consume());
+                }
+            }
+            value = builder.toString();
+        }
+
+        /** Returns the literal without quotes **/
+        public String getValue () {
+            return value;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            return value;
+        }
+    }
+
+    /** Represents a top-level variable access by name. E.g. in the expression "a + 1", <code>a</code> would be encoded as a
+     * VariableAccess node. Variables can be both read (in expressions) and written to (in assignments). Variable values are looked
+     * up and written to a {@link ExpressionTemplateContext}. **/
+    public static class VariableAccess extends Expression {
+        public VariableAccess (Span name) {
+            super(name);
+        }
+
+        public Span getVariableName () {
+            return getSpan();
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            Object value = context.get(getSpan().getText());
+            //if (value == null) ExpressionError.error("找不到变量'" + getSpan().getText() + "'或变量值为null", getSpan());
+            return value;
+        }
+    }
+
+    /** Represents a map or array element access of the form <code>mapOrArray[keyOrIndex]</code>. Maps and arrays may only be read
+     * from. **/
+    public static class MapOrArrayAccess extends Expression {
+        private final Expression mapOrArray;
+        private final Expression keyOrIndex;
+
+        public MapOrArrayAccess (Span span, Expression mapOrArray, Expression keyOrIndex) {
+            super(span);
+            this.mapOrArray = mapOrArray;
+            this.keyOrIndex = keyOrIndex;
+        }
+
+        /** Returns an expression that must evaluate to a map or array. **/
+        public Expression getMapOrArray () {
+            return mapOrArray;
+        }
+
+        /** Returns an expression that is used as the key or index to fetch a map or array element. **/
+        public Expression getKeyOrIndex () {
+            return keyOrIndex;
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            Object mapOrArray = getMapOrArray().evaluate(template, context);
+            if (mapOrArray == null) {
+                return null;
+            }
+            Object keyOrIndex = getKeyOrIndex().evaluate(template, context);
+            if (keyOrIndex == null) {
+                return null;
+            }
+
+            if (mapOrArray instanceof Map) {
+                return ((Map)mapOrArray).get(keyOrIndex);
+            } else if (mapOrArray instanceof List) {
+                if (!(keyOrIndex instanceof Number)) {
+                    ExpressionError.error("List index must be an integer, but was " + keyOrIndex.getClass().getSimpleName(), getKeyOrIndex().getSpan());
+                }
+                int index = ((Number)keyOrIndex).intValue();
+                return ((List)mapOrArray).get(index);
+            } else {
+                if (!(keyOrIndex instanceof Number)) {
+                    ExpressionError.error("Array index must be an integer, but was " + keyOrIndex.getClass().getSimpleName(), getKeyOrIndex().getSpan());
+                }
+                int index = ((Number)keyOrIndex).intValue();
+                if (mapOrArray instanceof int[]) {
+                    return ((int[])mapOrArray)[index];
+                } else if (mapOrArray instanceof float[]) {
+                    return ((float[])mapOrArray)[index];
+                } else if (mapOrArray instanceof double[]) {
+                    return ((double[])mapOrArray)[index];
+                } else if (mapOrArray instanceof boolean[]) {
+                    return ((boolean[])mapOrArray)[index];
+                } else if (mapOrArray instanceof char[]) {
+                    return ((char[])mapOrArray)[index];
+                } else if (mapOrArray instanceof short[]) {
+                    return ((short[])mapOrArray)[index];
+                } else if (mapOrArray instanceof long[]) {
+                    return ((long[])mapOrArray)[index];
+                } else if (mapOrArray instanceof byte[]) {
+                    return ((byte[])mapOrArray)[index];
+                } else if (mapOrArray instanceof String) {
+                    return Character.toString(((String)mapOrArray).charAt(index));
+                } else {
+                    return ((Object[])mapOrArray)[index];
+                }
+            }
+        }
+    }
+
+    /** Represents an access of a member (field or method or entry in a map) of the form <code>object.member</code>. Members may
+     * only be read from. **/
+    public static class MemberAccess extends Expression {
+        private final Expression object;
+        private final Span name;
+        private Object cachedMember;
+
+        public MemberAccess (Expression object, Span name) {
+            super(name);
+            this.object = object;
+            this.name = name;
+        }
+
+        /** Returns the object on which to access the member. **/
+        public Expression getObject () {
+            return object;
+        }
+
+        /** The name of the member. **/
+        public Span getName () {
+            return name;
+        }
+
+        /** Returns the cached member descriptor as returned by {@link AbstractReflection#getField(Object, String)} or
+         * {@link AbstractReflection#getMethod(Object, String, Object...)}. See {@link #setCachedMember(Object)}. **/
+        public Object getCachedMember () {
+            return cachedMember;
+        }
+
+        /** Sets the member descriptor as returned by {@link AbstractReflection#getField(Object, String)} or
+         * {@link AbstractReflection#getMethod(Object, String, Object...)} for faster member lookups. Called by {@link AstInterpreter} the
+         * first time this node is evaluated. Subsequent evaluations can use the cached descriptor, avoiding a costly reflective
+         * lookup. **/
+        public void setCachedMember (Object cachedMember) {
+            this.cachedMember = cachedMember;
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            Object object = getObject().evaluate(template, context);
+            if (object == null) {
+                return null;
+            }
+
+            // special case for array.length
+            if (object.getClass().isArray() && "length".equals(getName().getText())) {
+                return Array.getLength(object);
+            }
+
+            // special case for map, allows to do map.key instead of map[key]
+            if (object instanceof Map) {
+                Map map = (Map)object;
+                return map.get(getName().getText());
+            }
+
+            Object field = getCachedMember();
+            if (field != null) {
+                try {
+                    return AbstractReflection.getInstance().getFieldValue(object, field);
+                } catch (Throwable t) {
+                    // fall through
+                }
+            }
+            String text = getName().getText();
+            field = AbstractReflection.getInstance().getField(object, text);
+            if (field == null) {
+                String methodName = null;
+                if(text.length() > 1){
+                    methodName = text.substring(0,1).toUpperCase() + text.substring(1);
+                }else{
+                    methodName = text.toUpperCase();
+                }
+                MemberAccess access = new MemberAccess(this.object, new Span("get" + methodName));
+                MethodCall methodCall = new MethodCall(getName(),access, Collections.emptyList());
+                try {
+                    return methodCall.evaluate(template, context);
+                } catch (TemplateException e) {
+                    if(ExceptionUtils.indexOfThrowable(e, InvocationTargetException.class) > -1){
+                        ExpressionError.error(String.format("在%s中调用方法get%s发生异常"
+                                ,object.getClass()
+                                ,methodName), getSpan(),e);
+                        return null;
+                    }
+                    access = new MemberAccess(this.object, new Span("is" + methodName));
+                    methodCall = new MethodCall(getName(),access, Collections.emptyList());
+                    try {
+                        return methodCall.evaluate(template, context);
+                    } catch (TemplateException e1) {
+                        if(ExceptionUtils.indexOfThrowable(e1, InvocationTargetException.class) > -1){
+                            ExpressionError.error(String.format("在%s中调用方法is%s发生异常"
+                                    ,object.getClass()
+                                    ,methodName), getSpan(),e);
+                            return null;
+                        }
+                        ExpressionError.error(String.format("在%s中找不到属性%s或者方法get%s、方法is%s"
+                                ,object.getClass()
+                                ,getName().getText()
+                                ,methodName
+                                ,methodName), getSpan());
+                    }
+                }
+            }
+            setCachedMember(field);
+            return AbstractReflection.getInstance().getFieldValue(object, field);
+        }
+    }
+
+    public static class LambdaAccess extends Expression {
+        private final Expression element;
+        private final Expression function;
+        private MemberAccess arrayLike;
+
+        public LambdaAccess (Span span, Expression element, Expression function) {
+            super(span);
+            this.element = element;
+            this.function = function;
+        }
+
+        /** Returns an expression that must evaluate to a map or array. **/
+        public Expression getElement() {
+            return element;
+        }
+
+        /** Returns an expression that is used as the key or index to fetch a map or array element. **/
+        public Expression getFunction() {
+            return function;
+        }
+        @SuppressWarnings("rawtypes")
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            if (ArrayLikeLambdaOneArgumentExecutor.SUPPORT_METHOD.contains(arrayLike.getName().getText())) {
+                return oneArgumentParser(template, context);
+            } else {
+                ExpressionError.error("只支持 "+ String.join(",", ArrayLikeLambdaOneArgumentExecutor.SUPPORT_METHOD) +"。不支持的lambda函数: " + arrayLike.getName().getText(), arrayLike.getSpan());
+            }
+            return null;
+        }
+
+        private <T,R> Object oneArgumentParser(ExpressionTemplate template, ExpressionTemplateContext context) {
+            Expression object = arrayLike.getObject();
+            if (object instanceof VariableAccess) {
+                VariableAccess arrLike = (VariableAccess) object;
+                String parName = arrLike.getVariableName().getText();
+                Object arrLikeObj = context.get(parName);
+                if (arrLikeObj.getClass().isArray()) {
+                    try {
+                        Integer size = (Integer) arrLikeObj.getClass().getDeclaredField("length").get(arrLikeObj);
+                        List<Object> list = new ArrayList<>(size);
+                        for (int i = 0; i < size; i++) {
+                            list.add(Array.get(arrLikeObj, i));
+                        }
+                        arrLikeObj = list;
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                } else if (arrLikeObj instanceof Iterator) {
+                    Iterator<?> it = (Iterator<?>) arrLikeObj;
+                    List<Object> list = new ArrayList<>();
+                    it.forEachRemaining(list::add);
+                    arrLikeObj = list;
+                } else if (arrLikeObj instanceof Enumeration) {
+                    Enumeration<?> en = (Enumeration<?>) arrLikeObj;
+                    arrLikeObj = Collections.list(en);
+                }
+                if (arrLikeObj instanceof Collection) {
+                    Collection<?> coll = (Collection<?>) arrLikeObj;
+                    AtomicInteger ai = new AtomicInteger();
+                    return coll.stream().map(o -> ((Supplier) () -> {
+                        try {
+                            context.push();
+                            context.setOnCurrentScope(getElement().getSpan().getText(), o);
+                            context.setOnCurrentScope("_i", ai.getAndIncrement());
+                            Object res = function.evaluate(template, context);
+                            context.pop();
+                            return new ArrayLikeLambdaOneArgumentExecutor.SourceAndParsed<>(o, res);
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                            throw new RuntimeException(e);
+                        }
+                    })).collect(Collectors.toList());
+                }
+            }
+            return null;
+        }
+
+        public void setArrayLike(MemberAccess arrayLike) {
+            this.arrayLike = arrayLike;
+        }
+
+        public MemberAccess getArrayLike() {
+            return arrayLike;
+        }
+    }
+
+    /** Represents a call to a top-level function. A function may either be a {@link FunctionalInterface} stored in a
+     * {@link ExpressionTemplateContext}, or a {@link Macro} defined in a template. */
+    public static class FunctionCall extends Expression {
+        private final Expression function;
+        private final List<Expression> arguments;
+        private Object cachedFunction;
+        private final ThreadLocal<Object[]> cachedArguments;
+
+        public FunctionCall (Span span, Expression function, List<Expression> arguments) {
+            super(span);
+            this.function = function;
+            this.arguments = arguments;
+            this.cachedArguments = new ThreadLocal<Object[]>();
+        }
+
+        /** Return the expression that must evaluate to a {@link FunctionalInterface} or a {@link Macro}. **/
+        public Expression getFunction () {
+            return function;
+        }
+
+        /** Returns the list of expressions to be passed to the function as arguments. **/
+        public List<Expression> getArguments () {
+            return arguments;
+        }
+
+        /** Returns the cached "function" descriptor as returned by {@link AbstractReflection#getMethod(Object, String, Object...)} or the
+         * {@link Macro}. See {@link #setCachedFunction(Object)}. **/
+        public Object getCachedFunction () {
+            return cachedFunction;
+        }
+
+        /** Sets the "function" descriptor as returned by {@link AbstractReflection#getMethod(Object, String, Object...)} for faster
+         * lookups, or the {@link Macro} to be called. Called by {@link AstInterpreter} the first time this node is evaluated.
+         * Subsequent evaluations can use the cached descriptor, avoiding a costly reflective lookup. **/
+        public void setCachedFunction (Object cachedFunction) {
+            this.cachedFunction = cachedFunction;
+        }
+
+        /** Returns a scratch buffer to store arguments in when calling the function in {@link AstInterpreter}. Avoids generating
+         * garbage. **/
+        public Object[] getCachedArguments () {
+            Object[] args = cachedArguments.get();
+            if (args == null) {
+                args = new Object[arguments.size()];
+                cachedArguments.set(args);
+            }
+            return args;
+        }
+
+        /** Must be invoked when this node is done evaluating so we don't leak memory **/
+        public void clearCachedArguments () {
+            Object[] args = getCachedArguments();
+            for (int i = 0; i < args.length; i++) {
+                args[i] = null;
+            }
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            try {
+                Object[] argumentValues = getCachedArguments();
+                List<Expression> arguments = getArguments();
+                for (int i = 0, n = argumentValues.length; i < n; i++) {
+                    Expression expr = arguments.get(i);
+                    argumentValues[i] = expr.evaluate(template, context);
+                }
+
+                // This is a special case to handle template level macros. If a call to a macro is
+                // made, evaluating the function expression will result in an exception, as the
+                // function name can't be found in the context. Instead we need to manually check
+                // if the function expression is a VariableAccess and if so, if it can be found
+                // in the context.
+                Object function = null;
+                if (getFunction() instanceof VariableAccess) {
+                    VariableAccess varAccess = (VariableAccess)getFunction();
+                    function = context.get(varAccess.getVariableName().getText());
+                } else {
+                    function = getFunction().evaluate(template, context);
+                }
+
+                if (function != null) {
+                    Object method = getCachedFunction();
+                    if (method != null) {
+                        try {
+                            return AbstractReflection.getInstance().callMethod(function, method, argumentValues);
+                        } catch (Throwable t) {
+                            // fall through
+                        }
+                    }
+                    method = AbstractReflection.getInstance().getMethod(function, null, argumentValues);
+                    if (method == null) {
+                        ExpressionError.error("Couldn't find function.", getSpan());
+                    }
+                    setCachedFunction(method);
+                    try {
+                        return AbstractReflection.getInstance().callMethod(function, method, argumentValues);
+                    } catch (Throwable t) {
+                        ExpressionError.error(t.getMessage(), getSpan(), t);
+                        return null; // never reached
+                    }
+                } else {
+                    ExpressionError.error("Couldn't find function " + getFunction(), getSpan());
+                    return null; // never reached
+                }
+            } finally {
+                clearCachedArguments();
+            }
+        }
+    }
+
+    /** Represents a call to a method of the form <code>object.method(a, b, c)</code>. **/
+    public static class MethodCall extends Expression {
+        private final MemberAccess method;
+        private final List<Expression> arguments;
+        private Object cachedMethod;
+        private final ThreadLocal<Object[]> cachedArguments;
+        private boolean cachedMethodStatic;
+
+        public MethodCall (Span span, MemberAccess method, List<Expression> arguments) {
+            super(span);
+            this.method = method;
+            this.arguments = arguments;
+            this.cachedArguments = new ThreadLocal<Object[]>();
+        }
+
+        /** Returns the object on which to call the method. **/
+        public Expression getObject () {
+            return method.getObject();
+        }
+
+        /** Returns the method to call. **/
+        public MemberAccess getMethod () {
+            return method;
+        }
+
+        /** Returns the list of expressions to be passed to the function as arguments. **/
+        public List<Expression> getArguments () {
+            return arguments;
+        }
+
+        /** Returns the cached member descriptor as returned by {@link AbstractReflection#getMethod(Object, String, Object...)}. See
+         * {@link #setCachedMember(Object)}. **/
+        public Object getCachedMethod () {
+            return cachedMethod;
+        }
+
+        /** Sets the method descriptor as returned by {@link AbstractReflection#getMethod(Object, String, Object...)} for faster lookups.
+         * Called by {@link AstInterpreter} the first time this node is evaluated. Subsequent evaluations can use the cached
+         * descriptor, avoiding a costly reflective lookup. **/
+        public void setCachedMethod (Object cachedMethod) {
+            this.cachedMethod = cachedMethod;
+        }
+
+        /** Returns a scratch buffer to store arguments in when calling the function in {@link AstInterpreter}. Avoids generating
+         * garbage. **/
+        public Object[] getCachedArguments () {
+            Object[] args = cachedArguments.get();
+            if (args == null) {
+                args = new Object[arguments.size()];
+                cachedArguments.set(args);
+            }
+            return args;
+        }
+
+        /** Must be invoked when this node is done evaluating so we don't leak memory **/
+        public void clearCachedArguments () {
+            Object[] args = getCachedArguments();
+            for (int i = 0; i < args.length; i++) {
+                args[i] = null;
+            }
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            try {
+                Object object = getObject().evaluate(template, context);
+                if (object == null) {
+                    return null;
+                }
+
+                Object[] argumentValues = getCachedArguments();
+                List<Expression> arguments = getArguments();
+                for (int i = 0, n = argumentValues.length; i < n; i++) {
+                    Expression expr = arguments.get(i);
+                    argumentValues[i] = expr.evaluate(template, context);
+                }
+                // Otherwise try to find a corresponding method or field pointing to a lambda.
+                Object method = getCachedMethod();
+                if (method != null) {
+                    try {
+                        if (isCachedMethodStatic()) {
+                            return AbstractReflection.getInstance().callMethod(null, method, object, argumentValues);
+                        }
+                        return AbstractReflection.getInstance().callMethod(object, method, argumentValues);
+                    } catch (Throwable t) {
+                        t.printStackTrace();
+                        // fall through
+                    }
+                }
+
+                method = AbstractReflection.getInstance().getMethod(object, getMethod().getName().getText(), argumentValues);
+                if (method != null) {
+                    // found the method on the object, call it
+                    setCachedMethod(method);
+                    try {
+                        return AbstractReflection.getInstance().callMethod(object, method, argumentValues);
+                    } catch (Throwable t) {
+                        ExpressionError.error(t.getMessage(), getSpan(), t);
+                        return null; // never reached
+                    }
+                }
+                method = AbstractReflection.getInstance().getExtensionMethod(object, getMethod().getName().getText(), argumentValues);
+                if(method != null){
+                    try {
+                        int argumentLength = argumentValues == null ? 0 : argumentValues.length;
+                        Object[] parameters = new Object[argumentLength + 1];
+                        if(argumentLength > 0){
+                            for (int i = 0; i < argumentLength; i++) {
+                                parameters[i + 1] = argumentValues[i];
+                            }
+                        }
+                        parameters[0] = object;
+                        if(object.getClass().isArray()){
+                            Object[] objs = new Object[Array.getLength(object)];
+                            for (int i = 0,len = objs.length; i < len; i++) {
+                                Array.set(objs, i, Array.get(object, i));
+                            }
+                            parameters[0] = objs;
+                        }
+                        return AbstractReflection.getInstance().callMethod(object, method, parameters);
+                    } catch (Throwable t) {
+                        ExpressionError.error(t.getMessage(), getSpan(), t);
+                        // fall through
+                        return null;
+                    }
+                }else {
+                    // didn't find the method on the object, try to find a field pointing to a lambda
+                    Object field = AbstractReflection.getInstance().getField(object, getMethod().getName().getText());
+                    if (field == null){
+                        ExpressionError.error("在'" + object.getClass() + "'中找不到方法 " + getMethod().getName().getText() + "(" + StringUtils.join(JavaReflection.getStringTypes(argumentValues),",") + ")",
+                                getSpan());
+                    }
+                    Object function = AbstractReflection.getInstance().getFieldValue(object, field);
+                    method = AbstractReflection.getInstance().getMethod(function, null, argumentValues);
+                    if (method == null){
+                        ExpressionError.error("在'" + object.getClass() + "'中找不到方法 " + getMethod().getName().getText() + "("+ StringUtils.join(JavaReflection.getStringTypes(argumentValues),",") +")",
+                                getSpan());
+                    }
+                    try {
+                        return AbstractReflection.getInstance().callMethod(function, method, argumentValues);
+                    } catch (Throwable t) {
+                        ExpressionError.error(t.getMessage(), getSpan(), t);
+                        return null; // never reached
+                    }
+                }
+            } finally {
+                clearCachedArguments();
+            }
+        }
+
+        public void setCachedMethodStatic(boolean cachedMethodStatic) {
+            this.cachedMethodStatic = cachedMethodStatic;
+        }
+
+        public boolean isCachedMethodStatic() {
+            return cachedMethodStatic;
+        }
+    }
+
+    /** Represents a map literal of the form <code>{ key: value, key2: value, ... }</code> which can be nested. */
+    public static class MapLiteral extends Expression {
+        private final List<Token> keys;
+        private final List<Expression> values;
+
+        public MapLiteral (Span span, List<Token> keys, List<Expression> values) {
+            super(span);
+            this.keys = keys;
+            this.values = values;
+        }
+
+        public List<Token> getKeys () {
+            return keys;
+        }
+
+        public List<Expression> getValues () {
+            return values;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            Map<String, Object> map = new HashMap<>();
+            for (int i = 0, n = keys.size(); i < n; i++) {
+                Object value = values.get(i).evaluate(template, context);
+                Token tokenKey = keys.get(i);
+                String key = tokenKey.getSpan().getText();
+                if(tokenKey.getType() == TokenType.StringLiteral){
+                    key = (String) new StringLiteral(tokenKey.getSpan()).evaluate(template, context);
+                }else if(key != null && key.startsWith("$")){
+                    Object objKey = context.get(key.substring(1));
+                    if(objKey != null){
+                        key = objKey.toString();
+                    }else{
+                        key = null;
+                    }
+                }
+                map.put(key, value);
+            }
+            return map;
+        }
+    }
+
+    /** Represents a list literal of the form <code>[ value, value2, value3, ...]</code> which can be nested. */
+    public static class ListLiteral extends Expression {
+        public final List<Expression> values;
+
+        public ListLiteral (Span span, List<Expression> values) {
+            super(span);
+            this.values = values;
+        }
+
+        public List<Expression> getValues () {
+            return values;
+        }
+
+        @Override
+        public Object evaluate (ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
+            List<Object> list = new ArrayList<>();
+            for (int i = 0, n = values.size(); i < n; i++) {
+                list.add(values.get(i).evaluate(template, context));
+            }
+            return list;
+        }
+    }
 
 }

+ 57 - 59
src/main/java/org/ssssssss/expression/parsing/Parser.java

@@ -3,6 +3,7 @@ package org.ssssssss.expression.parsing;
 
 import org.ssssssss.expression.ExpressionError;
 import org.ssssssss.expression.ExpressionTemplate;
+import org.ssssssss.expression.parsing.Ast.*;
 
 import javax.xml.transform.Source;
 import java.util.ArrayList;
@@ -14,8 +15,8 @@ import java.util.List;
 public class Parser {
 
 	/** Parses a {@link Source} into a {@link ExpressionTemplate}. **/
-	public static List<Ast.Node> parse (String source) {
-		List<Ast.Node> nodes = new ArrayList<Ast.Node>();
+	public static List<Node> parse (String source) {
+		List<Node> nodes = new ArrayList<Ast.Node>();
 		TokenStream stream = new TokenStream(new Tokenizer().tokenize(source));
 		while (stream.hasMore()) {
 			nodes.add(parseStatement(stream));
@@ -25,11 +26,11 @@ public class Parser {
 
 	/** Parse a statement, which may either be a text block, if statement, for statement, while statement, macro definition,
 	 * include statement or an expression. **/
-	private static Ast.Node parseStatement (TokenStream tokens) {
-		Ast.Node result = null;
+	private static Node parseStatement (TokenStream tokens) {
+		Node result = null;
 
 		if (tokens.match(TokenType.TextBlock, false)) {
-			result = new Ast.Text(tokens.consume().getSpan());
+			result = new Text(tokens.consume().getSpan());
 		} else {
 			result = parseExpression(tokens);
 		}
@@ -43,17 +44,17 @@ public class Parser {
 	}
 
 
-	private static Ast.Expression parseExpression (TokenStream stream) {
+	private static Expression parseExpression (TokenStream stream) {
 		return parseTernaryOperator(stream);
 	}
 
-	private static Ast.Expression parseTernaryOperator (TokenStream stream) {
-		Ast.Expression condition = parseBinaryOperator(stream, 0);
+	private static Expression parseTernaryOperator (TokenStream stream) {
+		Expression condition = parseBinaryOperator(stream, 0);
 		if (stream.match(TokenType.Questionmark, true)) {
-			Ast.Expression trueExpression = parseTernaryOperator(stream);
+			Expression trueExpression = parseTernaryOperator(stream);
 			stream.expect(TokenType.Colon);
-			Ast.Expression falseExpression = parseTernaryOperator(stream);
-			return new Ast.TernaryOperation(condition, trueExpression, falseExpression);
+			Expression falseExpression = parseTernaryOperator(stream);
+			return new TernaryOperation(condition, trueExpression, falseExpression);
 		} else {
 			return condition;
 		}
@@ -64,15 +65,15 @@ public class Parser {
 		new TokenType[] {TokenType.Less, TokenType.LessEqual, TokenType.Greater, TokenType.GreaterEqual}, new TokenType[] {TokenType.Plus, TokenType.Minus},
 		new TokenType[] {TokenType.ForwardSlash, TokenType.Asterisk, TokenType.Percentage}};
 
-	private static Ast.Expression parseBinaryOperator (TokenStream stream, int level) {
+	private static Expression parseBinaryOperator (TokenStream stream, int level) {
 		int nextLevel = level + 1;
-		Ast.Expression left = nextLevel == binaryOperatorPrecedence.length ? parseUnaryOperator(stream) : parseBinaryOperator(stream, nextLevel);
+		Expression left = nextLevel == binaryOperatorPrecedence.length ? parseUnaryOperator(stream) : parseBinaryOperator(stream, nextLevel);
 
 		TokenType[] operators = binaryOperatorPrecedence[level];
 		while (stream.hasMore() && stream.match(false, operators)) {
 			Token operator = stream.consume();
-			Ast.Expression right = nextLevel == binaryOperatorPrecedence.length ? parseUnaryOperator(stream) : parseBinaryOperator(stream, nextLevel);
-			left = new Ast.BinaryOperation(left, operator, right);
+			Expression right = nextLevel == binaryOperatorPrecedence.length ? parseUnaryOperator(stream) : parseBinaryOperator(stream, nextLevel);
+			left = new BinaryOperation(left, operator, right);
 		}
 
 		return left;
@@ -80,12 +81,12 @@ public class Parser {
 
 	private static final TokenType[] unaryOperators = new TokenType[] {TokenType.Not, TokenType.Plus, TokenType.Minus};
 
-	private static Ast.Expression parseUnaryOperator (TokenStream stream) {
+	private static Expression parseUnaryOperator (TokenStream stream) {
 		if (stream.match(false, unaryOperators)) {
-			return new Ast.UnaryOperation(stream.consume(), parseUnaryOperator(stream));
+			return new UnaryOperation(stream.consume(), parseUnaryOperator(stream));
 		} else {
 			if (stream.match(TokenType.LeftParantheses, true)) {
-				Ast.Expression expression = parseExpression(stream);
+				Expression expression = parseExpression(stream);
 				stream.expect(TokenType.RightParantheses);
 				return expression;
 			} else {
@@ -94,7 +95,7 @@ public class Parser {
 		}
 	}
 
-	private static Ast.Expression parseAccessOrCallOrLiteral (TokenStream stream) {
+	private static Expression parseAccessOrCallOrLiteral (TokenStream stream) {
 		if (stream.match(TokenType.Identifier, false)) {
 			return parseAccessOrCall(stream,TokenType.Identifier);
 		} else if (stream.match(TokenType.LeftCurly, false)) {
@@ -110,36 +111,36 @@ public class Parser {
 				stream.prev();
 			}
 			
-			return new Ast.StringLiteral(stream.expect(TokenType.StringLiteral).getSpan());
+			return new StringLiteral(stream.expect(TokenType.StringLiteral).getSpan());
 		} else if (stream.match(TokenType.BooleanLiteral, false)) {
-			return new Ast.BooleanLiteral(stream.expect(TokenType.BooleanLiteral).getSpan());
+			return new BooleanLiteral(stream.expect(TokenType.BooleanLiteral).getSpan());
 		} else if (stream.match(TokenType.DoubleLiteral, false)) {
-			return new Ast.DoubleLiteral(stream.expect(TokenType.DoubleLiteral).getSpan());
+			return new DoubleLiteral(stream.expect(TokenType.DoubleLiteral).getSpan());
 		} else if (stream.match(TokenType.FloatLiteral, false)) {
-			return new Ast.FloatLiteral(stream.expect(TokenType.FloatLiteral).getSpan());
+			return new FloatLiteral(stream.expect(TokenType.FloatLiteral).getSpan());
 		} else if (stream.match(TokenType.ByteLiteral, false)) {
-			return new Ast.ByteLiteral(stream.expect(TokenType.ByteLiteral).getSpan());
+			return new ByteLiteral(stream.expect(TokenType.ByteLiteral).getSpan());
 		} else if (stream.match(TokenType.ShortLiteral, false)) {
-			return new Ast.ShortLiteral(stream.expect(TokenType.ShortLiteral).getSpan());
+			return new ShortLiteral(stream.expect(TokenType.ShortLiteral).getSpan());
 		} else if (stream.match(TokenType.IntegerLiteral, false)) {
-			return new Ast.IntegerLiteral(stream.expect(TokenType.IntegerLiteral).getSpan());
+			return new IntegerLiteral(stream.expect(TokenType.IntegerLiteral).getSpan());
 		} else if (stream.match(TokenType.LongLiteral, false)) {
-			return new Ast.LongLiteral(stream.expect(TokenType.LongLiteral).getSpan());
+			return new LongLiteral(stream.expect(TokenType.LongLiteral).getSpan());
 		} else if (stream.match(TokenType.CharacterLiteral, false)) {
-			return new Ast.CharacterLiteral(stream.expect(TokenType.CharacterLiteral).getSpan());
+			return new CharacterLiteral(stream.expect(TokenType.CharacterLiteral).getSpan());
 		} else if (stream.match(TokenType.NullLiteral, false)) {
-			return new Ast.NullLiteral(stream.expect(TokenType.NullLiteral).getSpan());
+			return new NullLiteral(stream.expect(TokenType.NullLiteral).getSpan());
 		} else {
 			ExpressionError.error("Expected a variable, field, map, array, function or method call, or literal.", stream);
 			return null; // not reached
 		}
 	}
 
-	private static Ast.Expression parseMapLiteral (TokenStream stream) {
+	private static Expression parseMapLiteral (TokenStream stream) {
 		Span openCurly = stream.expect(TokenType.LeftCurly).getSpan();
 
 		List<Token> keys = new ArrayList<>();
-		List<Ast.Expression> values = new ArrayList<>();
+		List<Expression> values = new ArrayList<>();
 		while (stream.hasMore() && !stream.match("}", false)) {
 			if(stream.match(TokenType.StringLiteral, false)){
 				keys.add(stream.expect(TokenType.StringLiteral));
@@ -154,13 +155,13 @@ public class Parser {
 			}
 		}
 		Span closeCurly = stream.expect("}").getSpan();
-		return new Ast.MapLiteral(new Span(openCurly, closeCurly), keys, values);
+		return new MapLiteral(new Span(openCurly, closeCurly), keys, values);
 	}
 
-	private static Ast.Expression parseListLiteral (TokenStream stream) {
+	private static Expression parseListLiteral (TokenStream stream) {
 		Span openBracket = stream.expect(TokenType.LeftBracket).getSpan();
 
-		List<Ast.Expression> values = new ArrayList<>();
+		List<Expression> values = new ArrayList<>();
 		while (stream.hasMore() && !stream.match(TokenType.RightBracket, false)) {
 			values.add(parseExpression(stream));
 			if (!stream.match(TokenType.RightBracket, false)) {
@@ -169,37 +170,34 @@ public class Parser {
 		}
 
 		Span closeBracket = stream.expect(TokenType.RightBracket).getSpan();
-		return new Ast.ListLiteral(new Span(openBracket, closeBracket), values);
+		return new ListLiteral(new Span(openBracket, closeBracket), values);
 	}
 
-	private static Ast.Expression parseAccessOrCall (TokenStream stream, TokenType tokenType) {
+	private static Expression parseAccessOrCall (TokenStream stream,TokenType tokenType) {
 		//Span identifier = stream.expect(TokenType.Identifier);
 		//Expression result = new VariableAccess(identifier);
 		Span identifier = stream.expect(tokenType).getSpan();
-		Ast.Expression result = tokenType == TokenType.StringLiteral ? new Ast.StringLiteral(identifier) :new Ast.VariableAccess(identifier);
+		Expression result = tokenType == TokenType.StringLiteral ? new StringLiteral(identifier) :new VariableAccess(identifier);
 
 		while (stream.hasMore() && stream.match(false, TokenType.LeftParantheses, TokenType.LeftBracket, TokenType.Period, TokenType.Lambda)) {
 
 			// function or method call
 			if (stream.match(TokenType.LeftParantheses, false)) {
-				List<Ast.Expression> arguments = parseArguments(stream);
+				List<Expression> arguments = parseArguments(stream);
 				Span closingSpan = stream.expect(TokenType.RightParantheses).getSpan();
-				if (result instanceof Ast.VariableAccess || result instanceof Ast.MapOrArrayAccess) {
-					result = new Ast.FunctionCall(new Span(result.getSpan(), closingSpan), result, arguments);
-				} else if (result instanceof Ast.MemberAccess) {
-					for (Ast.Expression expression : arguments) {
-						if (expression instanceof Ast.LambdaAccess) {
-							Ast.LambdaAccess lambdaAccess = (Ast.LambdaAccess) expression;
-							lambdaAccess.setArrayLike((Ast.MemberAccess) result);
+				if (result instanceof VariableAccess || result instanceof MapOrArrayAccess) {
+					result = new FunctionCall(new Span(result.getSpan(), closingSpan), result, arguments);
+				} else if (result instanceof MemberAccess) {
+					for (Expression expression : arguments) {
+						if (expression instanceof LambdaAccess) {
+							LambdaAccess lambdaAccess = (LambdaAccess) expression;
+							lambdaAccess.setArrayLike((MemberAccess) result);
 						}
 					}
-					Ast.MethodCall methodCall = new Ast.MethodCall(new Span(result.getSpan(), closingSpan), (Ast.MemberAccess) result, arguments);
-					if ("map".equals(((Ast.MemberAccess) result).getName().getText())) {
-						try {
-							methodCall.setCachedMethod(ArrayLikeLambdaExecutor.class.getMethod("map", Object.class, Object[].class));
-						} catch (NoSuchMethodException e) {
-							e.printStackTrace();
-						}
+					MethodCall methodCall = new MethodCall(new Span(result.getSpan(), closingSpan), (MemberAccess) result, arguments);
+					String name = ((MemberAccess) result).getName().getText();
+					if (ArrayLikeLambdaOneArgumentExecutor.SUPPORT_METHOD.contains(name)) {
+						methodCall.setCachedMethod(ArrayLikeLambdaOneArgumentExecutor.METHODS.get(name));
 						methodCall.setCachedMethodStatic(true);
 					}
 					result = methodCall;
@@ -210,21 +208,21 @@ public class Parser {
 
 			// map or array access
 			else if (stream.match(TokenType.LeftBracket, true)) {
-				Ast.Expression keyOrIndex = parseExpression(stream);
+				Expression keyOrIndex = parseExpression(stream);
 				Span closingSpan = stream.expect(TokenType.RightBracket).getSpan();
-				result = new Ast.MapOrArrayAccess(new Span(result.getSpan(), closingSpan), result, keyOrIndex);
+				result = new MapOrArrayAccess(new Span(result.getSpan(), closingSpan), result, keyOrIndex);
 			}
 
 			// field or method access
 			else if (stream.match(TokenType.Period, true)) {
 				identifier = stream.expect(TokenType.Identifier).getSpan();
-				result = new Ast.MemberAccess(result, identifier);
+				result = new MemberAccess(result, identifier);
 			}
 
 			else if (stream.match(TokenType.Lambda, true)) {
-				Ast.Expression key = parseExpression(stream);
+				Expression key = parseExpression(stream);
 //				Span closingSpan = stream.expect(TokenType.RightParantheses).getSpan();
-				result = new Ast.LambdaAccess(new Span(result.getSpan(), key.getSpan()), result, key);
+				result = new LambdaAccess(new Span(result.getSpan(), key.getSpan()), result, key);
 			}
 		}
 
@@ -232,9 +230,9 @@ public class Parser {
 	}
 
 	/** Does not consume the closing parentheses. **/
-	private static List<Ast.Expression> parseArguments (TokenStream stream) {
+	private static List<Expression> parseArguments (TokenStream stream) {
 		stream.expect(TokenType.LeftParantheses);
-		List<Ast.Expression> arguments = new ArrayList<Ast.Expression>();
+		List<Expression> arguments = new ArrayList<Expression>();
 		while (stream.hasMore() && !stream.match(TokenType.RightParantheses, false)) {
 			arguments.add(parseExpression(stream));
 			if (!stream.match(TokenType.RightParantheses, false)) {