Enjoy 3.2 release ^_^
This commit is contained in:
588
src/main/java/com/jfinal/template/expr/ExprParser.java
Normal file
588
src/main/java/com/jfinal/template/expr/ExprParser.java
Normal file
@@ -0,0 +1,588 @@
|
||||
/**
|
||||
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.jfinal.template.expr;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import com.jfinal.template.EngineConfig;
|
||||
import com.jfinal.template.expr.Sym;
|
||||
import com.jfinal.template.expr.ast.Arith;
|
||||
import com.jfinal.template.expr.ast.Array;
|
||||
import com.jfinal.template.expr.ast.Assign;
|
||||
import com.jfinal.template.expr.ast.Compare;
|
||||
import com.jfinal.template.expr.ast.Const;
|
||||
import com.jfinal.template.expr.ast.Expr;
|
||||
import com.jfinal.template.expr.ast.ExprList;
|
||||
import com.jfinal.template.expr.ast.Field;
|
||||
import com.jfinal.template.expr.ast.ForCtrl;
|
||||
import com.jfinal.template.expr.ast.Id;
|
||||
import com.jfinal.template.expr.ast.IncDec;
|
||||
import com.jfinal.template.expr.ast.Index;
|
||||
import com.jfinal.template.expr.ast.Logic;
|
||||
import com.jfinal.template.expr.ast.Map;
|
||||
import com.jfinal.template.expr.ast.Method;
|
||||
import com.jfinal.template.expr.ast.NullSafe;
|
||||
import com.jfinal.template.expr.ast.RangeArray;
|
||||
import com.jfinal.template.expr.ast.SharedMethod;
|
||||
import com.jfinal.template.expr.ast.StaticField;
|
||||
import com.jfinal.template.expr.ast.StaticMethod;
|
||||
import com.jfinal.template.expr.ast.Ternary;
|
||||
import com.jfinal.template.expr.ast.Unary;
|
||||
import com.jfinal.template.stat.Location;
|
||||
import com.jfinal.template.stat.ParaToken;
|
||||
import com.jfinal.template.stat.ParseException;
|
||||
|
||||
/**
|
||||
* ExprParser
|
||||
*/
|
||||
public class ExprParser {
|
||||
|
||||
static final Tok EOF = new Tok(Sym.EOF, -1);
|
||||
|
||||
Tok peek = null;
|
||||
int forward = 0;
|
||||
List<Tok> tokenList;
|
||||
Location location;
|
||||
|
||||
ParaToken paraToken;
|
||||
EngineConfig engineConfig;
|
||||
|
||||
public ExprParser(ParaToken paraToken, EngineConfig engineConfig, String fileName) {
|
||||
this.paraToken = paraToken;
|
||||
this.engineConfig = engineConfig;
|
||||
this.location = new Location(fileName, paraToken.getRow());
|
||||
}
|
||||
|
||||
void initPeek() {
|
||||
peek = tokenList.get(forward);
|
||||
}
|
||||
|
||||
Tok peek() {
|
||||
return peek;
|
||||
}
|
||||
|
||||
Tok move() {
|
||||
peek = tokenList.get(++forward);
|
||||
return peek;
|
||||
}
|
||||
void resetForward(int position) {
|
||||
forward = position;
|
||||
peek = tokenList.get(forward);
|
||||
}
|
||||
|
||||
Tok match(Sym sym) {
|
||||
Tok current = peek();
|
||||
if (current.sym == sym) {
|
||||
move();
|
||||
return current;
|
||||
}
|
||||
throw new ParseException("Expression error: can not match the symbol \"" + sym.value() + "\"", location);
|
||||
}
|
||||
|
||||
public ExprList parseExprList() {
|
||||
return (ExprList)parse(true);
|
||||
}
|
||||
|
||||
public ForCtrl parseForCtrl() {
|
||||
Expr forCtrl = parse(false);
|
||||
if (forCtrl instanceof ForCtrl) {
|
||||
return (ForCtrl)forCtrl;
|
||||
} else {
|
||||
throw new ParseException("The expression of #for directive is error", location);
|
||||
}
|
||||
}
|
||||
|
||||
Expr parse(boolean isExprList) {
|
||||
tokenList = new ExprLexer(paraToken, location).scan();
|
||||
if (tokenList.size() == 0) {
|
||||
return ExprList.NULL_EXPR_LIST;
|
||||
}
|
||||
tokenList.add(EOF);
|
||||
initPeek();
|
||||
Expr expr = isExprList ? exprList() : forCtrl();
|
||||
if (peek() != EOF) {
|
||||
throw new ParseException("Expression error: can not match \"" + peek().value() + "\"", location);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* exprList : expr (',' expr)*
|
||||
*/
|
||||
Expr exprList() {
|
||||
List<Expr> exprList = new ArrayList<Expr>();
|
||||
while (true) {
|
||||
Expr stat = expr();
|
||||
if (stat != null) {
|
||||
exprList.add(stat);
|
||||
if (peek().sym == Sym.COMMA) {
|
||||
move();
|
||||
if (peek() == EOF) {
|
||||
throw new ParseException("Expression error: can not match the char of comma ','", location);
|
||||
}
|
||||
continue ;
|
||||
}
|
||||
}
|
||||
break ;
|
||||
}
|
||||
return new ExprList(exprList);
|
||||
}
|
||||
|
||||
Expr expr() {
|
||||
return assign();
|
||||
}
|
||||
|
||||
/**
|
||||
* assign : <assoc=right> ID ( '[' expr ']' )? '=' expr
|
||||
*/
|
||||
Expr assign() {
|
||||
Tok idTok = peek();
|
||||
if (idTok.sym != Sym.ID) {
|
||||
return ternary();
|
||||
}
|
||||
|
||||
int begin = forward;
|
||||
// ID = expr
|
||||
if (move().sym == Sym.ASSIGN) {
|
||||
move();
|
||||
return new Assign(idTok.value(), expr(), location);
|
||||
}
|
||||
|
||||
// array、map 赋值:ID [ expr ] = expr
|
||||
if (peek().sym == Sym.LBRACK) {
|
||||
move();
|
||||
Expr index = expr();
|
||||
match(Sym.RBRACK);
|
||||
if (peek().sym == Sym.ASSIGN) {
|
||||
move();
|
||||
return new Assign(idTok.value(), index, expr(), location); // 右结合无限连
|
||||
}
|
||||
}
|
||||
|
||||
resetForward(begin);
|
||||
return ternary();
|
||||
}
|
||||
|
||||
/**
|
||||
* ternary : expr '?' expr ':' expr
|
||||
*/
|
||||
Expr ternary() {
|
||||
Expr cond = or();
|
||||
if (peek().sym == Sym.QUESTION) {
|
||||
move();
|
||||
Expr exprOne = expr();
|
||||
match(Sym.COLON);
|
||||
return new Ternary(cond, exprOne, expr(), location);
|
||||
}
|
||||
return cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* or : expr '||' expr
|
||||
*/
|
||||
Expr or() {
|
||||
Expr expr = and();
|
||||
for (Tok tok=peek(); tok.sym==Sym.OR; tok=peek()) {
|
||||
move();
|
||||
expr = new Logic(Sym.OR, expr, and(), location);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* and : expr '&&' expr
|
||||
*/
|
||||
Expr and() {
|
||||
Expr expr = equalNotEqual();
|
||||
for (Tok tok=peek(); tok.sym==Sym.AND; tok=peek()) {
|
||||
move();
|
||||
expr = new Logic(Sym.AND, expr, equalNotEqual(), location);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* equalNotEqual : expr ('==' | '!=') expr
|
||||
*/
|
||||
Expr equalNotEqual() {
|
||||
Expr expr = greaterLess();
|
||||
for (Tok tok=peek(); tok.sym==Sym.EQUAL || tok.sym==Sym.NOTEQUAL; tok=peek()) {
|
||||
move();
|
||||
expr = new Compare(tok.sym, expr, greaterLess(), location);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* compare expr ('<=' | '>=' | '>' | '<') expr
|
||||
* 不支持无限连: > >= < <=
|
||||
*/
|
||||
Expr greaterLess() {
|
||||
Expr expr = addSub();
|
||||
Tok tok = peek();
|
||||
if (tok.sym == Sym.LT || tok.sym == Sym.LE || tok.sym == Sym.GT || tok.sym == Sym.GE) {
|
||||
move();
|
||||
return new Compare(tok.sym, expr, addSub(), location);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* addSub : expr ('+'|'-') expr
|
||||
*/
|
||||
Expr addSub() {
|
||||
Expr expr = mulDivMod();
|
||||
for (Tok tok=peek(); tok.sym==Sym.ADD || tok.sym==Sym.SUB; tok=peek()) {
|
||||
move();
|
||||
expr = new Arith(tok.sym, expr, mulDivMod(), location);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* mulDivMod : expr ('*'|'/'|'%') expr
|
||||
*/
|
||||
Expr mulDivMod() {
|
||||
Expr expr = nullSafe();
|
||||
for (Tok tok=peek(); tok.sym==Sym.MUL || tok.sym==Sym.DIV || tok.sym==Sym.MOD; tok=peek()) {
|
||||
move();
|
||||
expr = new Arith(tok.sym, expr, nullSafe(), location);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* nullSafe : expr '??' expr
|
||||
*/
|
||||
Expr nullSafe() {
|
||||
Expr expr = unary();
|
||||
for (Tok tok=peek(); tok.sym==Sym.NULL_SAFE; tok=peek()) {
|
||||
move();
|
||||
expr = new NullSafe(expr, unary(), location);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* unary : ('!' | '+' | '-'| '++' | '--') expr
|
||||
*/
|
||||
Expr unary() {
|
||||
Tok tok = peek();
|
||||
switch (tok.sym) {
|
||||
case NOT:
|
||||
move();
|
||||
return new Logic(tok.sym, unary(), location);
|
||||
case ADD:
|
||||
case SUB:
|
||||
move();
|
||||
return new Unary(tok.sym, unary(), location);
|
||||
case INC:
|
||||
case DEC:
|
||||
move();
|
||||
return new IncDec(tok.sym, false, incDec(), location);
|
||||
default:
|
||||
return incDec();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* incDec : expr ('++' | '--')
|
||||
*/
|
||||
Expr incDec() {
|
||||
Expr expr = staticMember();
|
||||
Tok tok = peek();
|
||||
if (tok.sym == Sym.INC || tok.sym == Sym.DEC) {
|
||||
move();
|
||||
return new IncDec(tok.sym, true, expr, location);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* staticMember
|
||||
* : ID_list '::' ID
|
||||
* | ID_list '::' ID '(' exprList? ')'
|
||||
*/
|
||||
Expr staticMember() {
|
||||
if (peek().sym != Sym.ID) {
|
||||
return sharedMethod();
|
||||
}
|
||||
|
||||
int begin = forward;
|
||||
while (move().sym == Sym.DOT && move().sym == Sym.ID) {
|
||||
;
|
||||
}
|
||||
// ID.ID.ID::
|
||||
if (peek().sym != Sym.STATIC || tokenList.get(forward - 1).sym != Sym.ID) {
|
||||
resetForward(begin);
|
||||
return sharedMethod();
|
||||
}
|
||||
|
||||
String clazz = getClazz(begin);
|
||||
match(Sym.STATIC);
|
||||
String memberName = match(Sym.ID).value();
|
||||
|
||||
// com.jfinal.kit.Str::isBlank(str)
|
||||
if (peek().sym == Sym.LPAREN) {
|
||||
move();
|
||||
if (peek().sym == Sym.RPAREN) {
|
||||
move();
|
||||
return new StaticMethod(clazz, memberName, location);
|
||||
}
|
||||
|
||||
ExprList exprList = (ExprList)exprList();
|
||||
match(Sym.RPAREN);
|
||||
return new StaticMethod(clazz, memberName, exprList, location);
|
||||
}
|
||||
|
||||
// com.jfinal.core.Const::JFINAL_VERSION
|
||||
return new StaticField(clazz, memberName, location);
|
||||
}
|
||||
|
||||
String getClazz(int begin) {
|
||||
StringBuilder clazz = new StringBuilder();
|
||||
for (int i=begin; i<forward; i++) {
|
||||
clazz.append(tokenList.get(i).value());
|
||||
}
|
||||
return clazz.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* sharedMethod : ID '(' exprList? ')'
|
||||
*/
|
||||
Expr sharedMethod() {
|
||||
Tok tok = peek();
|
||||
if (tok.sym != Sym.ID) {
|
||||
return indexMethodField(null);
|
||||
}
|
||||
if (move().sym != Sym.LPAREN) {
|
||||
resetForward(forward - 1);
|
||||
return indexMethodField(null);
|
||||
}
|
||||
|
||||
move();
|
||||
if (peek().sym == Sym.RPAREN) {
|
||||
SharedMethod sharedMethod = new SharedMethod(engineConfig.getSharedMethodKit(), tok.value(), ExprList.NULL_EXPR_LIST, location);
|
||||
move();
|
||||
return indexMethodField(sharedMethod);
|
||||
}
|
||||
|
||||
ExprList exprList = (ExprList)exprList();
|
||||
SharedMethod sharedMethod = new SharedMethod(engineConfig.getSharedMethodKit(), tok.value(), exprList, location);
|
||||
match(Sym.RPAREN);
|
||||
return indexMethodField(sharedMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* index : expr '[' expr ']'
|
||||
* method : expr '.' ID '(' exprList? ')'
|
||||
* field : expr '.' ID
|
||||
*/
|
||||
Expr indexMethodField(Expr expr) {
|
||||
if (expr == null) {
|
||||
expr = map();
|
||||
}
|
||||
|
||||
// Expr expr = map();
|
||||
while (true) {
|
||||
Tok tok = peek();
|
||||
// expr [ expr ]
|
||||
if (tok.sym == Sym.LBRACK) {
|
||||
move();
|
||||
Expr index = expr();
|
||||
match(Sym.RBRACK);
|
||||
expr = new Index(expr, index, location);
|
||||
continue;
|
||||
}
|
||||
if (tok.sym != Sym.DOT) {
|
||||
return expr;
|
||||
}
|
||||
if ((tok = move()).sym != Sym.ID) {
|
||||
resetForward(forward - 1);
|
||||
return expr;
|
||||
}
|
||||
|
||||
move();
|
||||
if (peek().sym != Sym.LPAREN) {
|
||||
expr = new Field(expr, tok.value(), location);
|
||||
continue;
|
||||
}
|
||||
|
||||
move();
|
||||
// expr '.' ID '(' ')'
|
||||
if (peek().sym == Sym.RPAREN) {
|
||||
move();
|
||||
expr = new Method(expr, tok.value(), location);
|
||||
continue;
|
||||
}
|
||||
|
||||
// expr '.' ID '(' exprList ')'
|
||||
ExprList exprList = (ExprList)exprList();
|
||||
match(Sym.RPAREN);
|
||||
expr = new Method(expr, tok.value(), exprList, location);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* map : '{' (mapEntry ( , mapEntry ) * ) ? '}'
|
||||
* mapEntry : (ID | STR) ':' expr
|
||||
*/
|
||||
Expr map() {
|
||||
if (peek().sym != Sym.LBRACE) {
|
||||
return array();
|
||||
}
|
||||
|
||||
LinkedHashMap<Object, Expr> mapEntry = new LinkedHashMap<Object, Expr>();
|
||||
Map map = new Map(mapEntry);
|
||||
move();
|
||||
if (peek().sym == Sym.RBRACE) {
|
||||
move();
|
||||
return map;
|
||||
}
|
||||
|
||||
buildMapEntry(mapEntry);
|
||||
while (peek().sym == Sym.COMMA) {
|
||||
move();
|
||||
buildMapEntry(mapEntry);
|
||||
}
|
||||
match(Sym.RBRACE);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* mapEntry : (ID | STR) ':' expr
|
||||
*/
|
||||
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 ;
|
||||
}
|
||||
throw new ParseException("Expression error: the value of map key must be identifier or String", location);
|
||||
}
|
||||
|
||||
/**
|
||||
* array : '[' exprList ? | range ? ']'
|
||||
* exprList : expr (',' expr)*
|
||||
* range : expr .. expr
|
||||
*/
|
||||
Expr array() {
|
||||
if (peek().sym != Sym.LBRACK) {
|
||||
return atom();
|
||||
}
|
||||
|
||||
move();
|
||||
if (peek().sym == Sym.RBRACK) {
|
||||
move();
|
||||
return new Array(ExprList.NULL_EXPR_ARRAY, location);
|
||||
}
|
||||
ExprList exprList = (ExprList)exprList();
|
||||
if (exprList.length() == 1 && peek().sym == Sym.RANGE) {
|
||||
move();
|
||||
Expr end = expr();
|
||||
match(Sym.RBRACK);
|
||||
return new RangeArray(exprList.getExprArray()[0], end, location);
|
||||
}
|
||||
|
||||
match(Sym.RBRACK);
|
||||
return new Array(exprList.getExprArray(), location);
|
||||
}
|
||||
|
||||
/**
|
||||
* atom : '(' expr ')' | ID | STR | 'true' | 'false' | 'null'
|
||||
* | INT | LONG | FLOAT | DOUBLE
|
||||
*/
|
||||
Expr atom() {
|
||||
Tok tok = peek();
|
||||
switch (tok.sym) {
|
||||
case LPAREN:
|
||||
move();
|
||||
Expr expr = expr();
|
||||
match(Sym.RPAREN);
|
||||
return expr;
|
||||
case ID:
|
||||
move();
|
||||
return new Id(tok.value());
|
||||
case STR:
|
||||
case INT:
|
||||
case LONG:
|
||||
case FLOAT:
|
||||
case DOUBLE:
|
||||
move();
|
||||
return new Const(tok.sym, tok.value());
|
||||
case TRUE:
|
||||
move();
|
||||
return Const.TRUE;
|
||||
case FALSE:
|
||||
move();
|
||||
return Const.FALSE;
|
||||
case NULL:
|
||||
move();
|
||||
return Const.NULL;
|
||||
case COMMA:
|
||||
case SEMICOLON:
|
||||
case QUESTION: // support "c ?? ? a : b"
|
||||
case AND: case OR: case EQUAL: case NOTEQUAL: // support "a.b ?? && expr"
|
||||
case RPAREN: // support "(a.b ??)"
|
||||
case RBRACK: // support "[start .. end ??]"
|
||||
case RBRACE: // support "{key : value ??}"
|
||||
case RANGE: // support "[start ?? .. end]"
|
||||
case COLON: // support "c ? a ?? : b"
|
||||
case EOF:
|
||||
return null;
|
||||
default :
|
||||
throw new ParseException("Expression error: can not match the symbol \"" + tok.value() + "\"", location);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* forControl : ID : expr | exprList? ';' expr? ';' exprList?
|
||||
*/
|
||||
Expr forCtrl() {
|
||||
ExprList exprList = (ExprList)exprList();
|
||||
if (peek().sym == Sym.SEMICOLON) {
|
||||
move();
|
||||
Expr cond = expr();
|
||||
match(Sym.SEMICOLON);
|
||||
Expr update = exprList();
|
||||
return new ForCtrl(exprList, cond, update, location);
|
||||
}
|
||||
|
||||
if (exprList.length() == 1) {
|
||||
Expr expr = exprList.getExprArray()[0];
|
||||
if (expr instanceof Id) {
|
||||
match(Sym.COLON);
|
||||
return new ForCtrl(((Id)expr), expr(), location);
|
||||
}
|
||||
}
|
||||
throw new ParseException("The expression of #for directive is error", location);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user