Map 定义初始化表达式,添加支持 key 可以为 int、long、float、double、true、false、null 常量
This commit is contained in:
parent
bd1949fc56
commit
e0abc218b1
@ -22,6 +22,7 @@ import java.util.Map;
|
||||
import com.jfinal.kit.HashKit;
|
||||
import com.jfinal.kit.StrKit;
|
||||
import com.jfinal.template.expr.ast.MethodKit;
|
||||
import com.jfinal.template.source.ClassPathSourceFactory;
|
||||
import com.jfinal.template.source.ISource;
|
||||
import com.jfinal.template.source.ISourceFactory;
|
||||
import com.jfinal.template.source.StringSource;
|
||||
@ -426,6 +427,13 @@ public class Engine {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置为 ClassPathSourceFactory 的快捷方法
|
||||
*/
|
||||
public Engine setToClassPathSourceFactory() {
|
||||
return setSourceFactory(new ClassPathSourceFactory());
|
||||
}
|
||||
|
||||
public ISourceFactory getSourceFactory() {
|
||||
return sourceFactory;
|
||||
}
|
||||
|
@ -292,7 +292,7 @@ public class ExprParser {
|
||||
case ADD:
|
||||
case SUB:
|
||||
move();
|
||||
return new Unary(tok.sym, unary(), location);
|
||||
return new Unary(tok.sym, unary(), location).toConstIfPossible();
|
||||
case INC:
|
||||
case DEC:
|
||||
move();
|
||||
@ -468,21 +468,26 @@ public class ExprParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* mapEntry : (ID | STR) ':' expr
|
||||
* mapEntry : (ID | STR | INT | LONG | FLOAT | DOUBLE | TRUE | FALSE | NULL) ':' expr
|
||||
* 设计目标为 map 定义与初始化,所以 ID 仅当成 STR 不进行求值
|
||||
*/
|
||||
void buildMapEntry(LinkedHashMap<Object, Expr> map) {
|
||||
Tok tok = peek();
|
||||
if (tok.sym == Sym.ID || tok.sym == Sym.STR) {
|
||||
move();
|
||||
match(Sym.COLON);
|
||||
Expr value = expr();
|
||||
if (value == null) {
|
||||
throw new ParseException("Expression error: the value on the right side of map entry can not be blank", location);
|
||||
}
|
||||
map.put(tok.value(), value);
|
||||
return ;
|
||||
Expr keyExpr = expr();
|
||||
Object key;
|
||||
if (keyExpr instanceof Id) {
|
||||
key = ((Id)keyExpr).getId();
|
||||
} else if (keyExpr instanceof Const) {
|
||||
key = ((Const)keyExpr).getValue();
|
||||
} else {
|
||||
throw new ParseException("Expression error: the value of map key must be identifier, String, Boolean, null or Number", location);
|
||||
}
|
||||
throw new ParseException("Expression error: the value of map key must be identifier or String", location);
|
||||
|
||||
match(Sym.COLON);
|
||||
Expr value = expr();
|
||||
if (value == null) {
|
||||
throw new ParseException("Expression error: the value on the right side of map entry can not be blank", location);
|
||||
}
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -528,12 +533,14 @@ public class ExprParser {
|
||||
move();
|
||||
return new Id(tok.value());
|
||||
case STR:
|
||||
move();
|
||||
return new Const(tok.sym, tok.value());
|
||||
case INT:
|
||||
case LONG:
|
||||
case FLOAT:
|
||||
case DOUBLE:
|
||||
move();
|
||||
return new Const(tok.sym, tok.value());
|
||||
return new Const(tok.sym, ((NumTok)tok).getNumberValue());
|
||||
case TRUE:
|
||||
move();
|
||||
return Const.TRUE;
|
||||
|
@ -43,7 +43,7 @@ import com.jfinal.template.stat.ParseException;
|
||||
*/
|
||||
public class NumTok extends Tok {
|
||||
|
||||
private Object value;
|
||||
private Number value;
|
||||
|
||||
NumTok(Sym sym, String s, int radix, boolean isScientificNotation, Location location) {
|
||||
super(sym, location.getRow());
|
||||
@ -101,3 +101,8 @@ public class NumTok extends Tok {
|
||||
return sym.value() + " : " + value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -20,54 +20,25 @@ import com.jfinal.template.expr.Sym;
|
||||
import com.jfinal.template.stat.Scope;
|
||||
|
||||
/**
|
||||
* STR INT LONG FLOAT DOUBLE true false null
|
||||
* STR INT LONG FLOAT DOUBLE TRUE FALSE NULL
|
||||
*/
|
||||
public class Const extends Expr {
|
||||
|
||||
public static final Const TRUE = new Const(Boolean.TRUE, Sym.TRUE);
|
||||
public static final Const FALSE = new Const(Boolean.FALSE, Sym.FALSE);
|
||||
public static final Const NULL = new Const(null, Sym.NULL);
|
||||
public static final Const TRUE = new Const(Sym.TRUE, Boolean.TRUE);
|
||||
public static final Const FALSE = new Const(Sym.FALSE, Boolean.FALSE);
|
||||
public static final Const NULL = new Const(Sym.NULL, null);
|
||||
|
||||
private Sym type;
|
||||
private Object value;
|
||||
private final Sym type;
|
||||
private final Object value;
|
||||
|
||||
/**
|
||||
* 构造 TRUE FALSE NULL 常量,无需对 value 进行转换
|
||||
* INT LONG FLOAT DOUBLE 常量已在 NumTok 中转换成了确切的类型,无需再次转换
|
||||
*/
|
||||
private Const(Object value, Sym type) {
|
||||
public Const(Sym type, Object value) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Const(Sym type, String value) {
|
||||
this.type = type;
|
||||
this.value = typeConvert(type, value);
|
||||
}
|
||||
|
||||
private Object typeConvert(Sym type, String value) {
|
||||
switch (type) {
|
||||
case STR:
|
||||
return value;
|
||||
case INT:
|
||||
return Integer.parseInt(value);
|
||||
case LONG:
|
||||
return Long.parseLong(value);
|
||||
case FLOAT:
|
||||
return Float.parseFloat(value);
|
||||
case DOUBLE:
|
||||
return Double.parseDouble(value);
|
||||
/*
|
||||
case TRUE:
|
||||
case FALSE:
|
||||
return Boolean.parseBoolean(value);
|
||||
case NULL:
|
||||
return null;
|
||||
*/
|
||||
default:
|
||||
throw new RuntimeException("never happend");
|
||||
}
|
||||
}
|
||||
|
||||
public Object eval(Scope scope) {
|
||||
return value;
|
||||
}
|
||||
@ -108,6 +79,10 @@ public class Const extends Expr {
|
||||
return type == Sym.DOUBLE;
|
||||
}
|
||||
|
||||
public boolean isNumber() {
|
||||
return value instanceof Number;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
@ -136,6 +111,10 @@ public class Const extends Expr {
|
||||
return (Double)value;
|
||||
}
|
||||
|
||||
public Number getNumber() {
|
||||
return (Number)value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value != null ? value.toString() : "null";
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ public class Index extends Expr {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object eval(Scope scope) {
|
||||
Object array = expr.eval(scope);
|
||||
if (array == null) {
|
||||
Object target = expr.eval(scope);
|
||||
if (target == null) {
|
||||
if (scope.getCtrl().isNullSafe()) {
|
||||
return null;
|
||||
}
|
||||
@ -56,25 +56,30 @@ public class Index extends Expr {
|
||||
if (scope.getCtrl().isNullSafe()) {
|
||||
return null;
|
||||
}
|
||||
throw new TemplateException("The index of list/array and the key of map can not be null", location);
|
||||
}
|
||||
|
||||
if (array instanceof List) {
|
||||
if (idx instanceof Integer) {
|
||||
return ((List<?>)array).get((Integer)idx);
|
||||
if (target instanceof java.util.Map) {
|
||||
// Map 的 key 可以是 null,不能抛异常
|
||||
} else {
|
||||
throw new TemplateException("The index of list and array can not be null", location);
|
||||
}
|
||||
throw new TemplateException("The index of list can only be integer", location);
|
||||
}
|
||||
|
||||
if (array instanceof java.util.Map) {
|
||||
return ((java.util.Map)array).get(idx);
|
||||
}
|
||||
|
||||
if (array.getClass().isArray()) {
|
||||
if (target instanceof List) {
|
||||
if (idx instanceof Integer) {
|
||||
return java.lang.reflect.Array.get(array, (Integer)idx);
|
||||
return ((List<?>)target).get((Integer)idx);
|
||||
}
|
||||
throw new TemplateException("The index of array can only be integer", location);
|
||||
throw new TemplateException("The index of list must be integer", location);
|
||||
}
|
||||
|
||||
if (target instanceof java.util.Map) {
|
||||
return ((java.util.Map)target).get(idx);
|
||||
}
|
||||
|
||||
if (target.getClass().isArray()) {
|
||||
if (idx instanceof Integer) {
|
||||
return java.lang.reflect.Array.get(target, (Integer)idx);
|
||||
}
|
||||
throw new TemplateException("The index of array must be integer", location);
|
||||
}
|
||||
|
||||
throw new TemplateException("Only the list array and map is supported by index access", location);
|
||||
|
@ -83,6 +83,51 @@ public class Unary extends Expr {
|
||||
throw new TemplateException("Unsupported operator: " + op.value(), location);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果可能的话,将 Unary 表达式转化成 Const 表达式,类似于 ExprParser.buildMapEntry() 需要这种转化来简化实现
|
||||
* 除了可简化程序外,还起到一定的性能优化作用
|
||||
*
|
||||
* Number : +123 -456 +3.14 -0.12
|
||||
* Boolean : !true !false
|
||||
*
|
||||
* 特别注意:
|
||||
* Boolean 的支持并不需要,!true、!false 已在 ExprParser 中被 Logic 表达式接管,在此仅为逻辑上的完备性而添加
|
||||
*/
|
||||
public Expr toConstIfPossible() {
|
||||
if (expr instanceof Const && (op == Sym.SUB || op == Sym.ADD || op == Sym.NOT)) {
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
|
||||
Expr ret = this;
|
||||
Const c = (Const)expr;
|
||||
if (op == Sym.SUB) {
|
||||
if (c.isInt()) {
|
||||
ret = new Const(Sym.INT, -c.getInt());
|
||||
} else if (c.isLong()) {
|
||||
ret = new Const(Sym.LONG, -c.getLong());
|
||||
} else if (c.isFloat()) {
|
||||
ret = new Const(Sym.FLOAT, -c.getFloat());
|
||||
} else if (c.isDouble()) {
|
||||
ret = new Const(Sym.DOUBLE, -c.getDouble());
|
||||
}
|
||||
} else if (op == Sym.ADD) {
|
||||
if (c.isNumber()) {
|
||||
ret = c;
|
||||
}
|
||||
} else if (op == Sym.NOT) {
|
||||
if (c.isBoolean()) {
|
||||
ret = c.isTrue() ? Const.FALSE : Const.TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return op.toString() + expr.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user