本文介绍使用ANTLR工具(http://www.antlr.org/)实现基本的脚本语言。
一、 脚本的语言需求包含一下几点,具备 max、min和条件表达式。
例如计算: if(max(var1,10) = 10) ? max(100,var2) : min(100, max(var1, var2))
二、编写grammar文件
grammar Expr;
compute:
compute_expr
;
compute_expr:
max_expr
|min_expr
|if_else_expr
|identifier
|INTEGER
;
if_else_expr:
(INTEGER | identifier | max_expr | min_expr | OPEN_PAR_SYMBOL compute_expr CLOSE_PAR_SYMBOL) EQUAL_SYMBOL compute_expr
PARAM_MARKER compute_expr COLON_SYMBOL compute_expr
;
max_expr:
MAX_SYMBOL OPEN_PAR_SYMBOL compute_expr COMMA_SYMBOL compute_expr COMMA_SYMBOL CLOSE_PAR_SYMBOL
;
min_expr:
MIN_SYMBOL OPEN_PAR_SYMBOL compute_expr COMMA_SYMBOL compute_expr CLOSE_PAR_SYMBOL
;
identifier:
IDENTIFIER
| keyword
;
keyword:
MAX_SYMBOL
| MIN_SYMBOL
;
//Lexer rule
MAX_SYMBOL: 'MAX'|'max';
MIN_SYMBOL: 'MIN'|'min';
EQUAL_SYMBOL: '=';
PARAM_MARKER: '?';
COLON_SYMBOL: ':';
COMMA_SYMBOL: ',';
OPEN_PAR_SYMBOL: '(';
CLOSE_PAR_SYMBOL: ')';
IDENTIFIER: ('A'..'Z' | 'a'..'z' | '_' ) ('A'..'Z' | 'a'..'z' | '_' | '0'..'9')*;
INTEGER: ('+'|'-')? ('0'..'9')+;
WHITESPACE: ( ' ' | '\t' | '\f' | '\r'| '\n') -> skip;
三、借助工具创建java源代码
java -jar antlr-4.5.3-complete.jar Expr.g4 -o expr -listener -visitor -encoding UTF-8
-o java源代码输出路径
-listener 生成listener接口源代码
-visitor 生成visitor接口源代码
四、编写计算树和visitor
计算树如图,计算表达式的值只需要从根节点if开始,遍历各个实现语义的node。最终求出表达式的值
基本思路是:
max, min: 函数,取左子树的值和右子树的值进行比较,并返回。
if : 计算第一棵树和第二棵树的值,进行比较,然后根据比较结构,返回第三棵树的值或者第四棵树的值
变量: 实现一个获取变量的结构,在计算时将变量的值传进去。
五、编写JAVA main函数,调用生成的代码
//main函数:
ANTLRInputStream cs = new ANTLRInputStream(define);
ExprLexer lexer = new ExprLexer("if(max(var1,10) = 10) ? max(100,var2) : min(100, max(var1, var2))");
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
ExprParser parser = new ExprParser(tokenStream);
ParseTree tree = parser.query();
ExprVisitor<TreeNode> visitor = new ExprFuncVisitor<TreeNode>();
TreeNode node = visitor.visit(tree);