package org.ssssssss.magicapi.expression.parsing;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.ssssssss.magicapi.expression.ExpressionError;
import org.ssssssss.magicapi.expression.ExpressionError.TemplateException;
import org.ssssssss.magicapi.expression.ExpressionTemplate;
import org.ssssssss.magicapi.expression.ExpressionTemplateContext;
import org.ssssssss.magicapi.expression.interpreter.AbstractReflection;
import org.ssssssss.magicapi.expression.interpreter.AstInterpreter;
import org.ssssssss.magicapi.expression.interpreter.JavaReflection;
import javax.xml.transform.Source;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
/** 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 null
**/
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 true
and false
**/
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 d
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 f
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 b
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 s
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 l
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", a
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 mapOrArray[keyOrIndex]
. 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 object.member
. 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 List elements = new ArrayList<>();
private final Expression function;
private MemberAccess arrayLike;
public LambdaAccess (Span span, Expression function, List elements) {
super(span);
this.elements.addAll(elements);
this.function = function;
}
public LambdaAccess (Span span, Expression function, Expression... elements) {
super(span);
this.elements.addAll(Arrays.asList(elements));
this.function = function;
}
public List getElements() {
return elements;
}
/** 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 (ArrayLikeLambdaExecutor.SUPPORT_METHOD.contains(arrayLike.getName().getText())) {
return oneArgumentParser(template, context);
} else {
ExpressionError.error("只支持 "+ String.join(",", ArrayLikeLambdaExecutor.SUPPORT_METHOD) +"。不支持的lambda函数: " + arrayLike.getName().getText(), arrayLike.getSpan());
}
return null;
}
private 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);
int size = Array.getLength(arrLikeObj);
List