原创,转载请注明出处: @author zfzheng http://blog.csdn.net/redvalley
MiniScript脚本语言
起因:应老大需要,编写一种脚本语言。之所以不用groovy/javascript...等,因为我们需要其它特殊的功能,自己开发一个语言,则可最大限度地进行“定制”。自告奋勇编写(貌似其它人不在行这个 :D),也练练手。
要求功能
- 表达式赋值/计算
- 控制语句if/for/while/switch
- 变量可从java执行环境传入
- 支持对象方法调用
- 与java交互
实现步骤
- 定义文法
- 编写词法/语法分析器(javacc)
- JTB产生语法树
- 使用访问者模式执行脚本
完成,测试
做个简单的UI (测试传入变量则需要调用代码中编写)界面:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicEUK5CdwlmcjNXaulWbvwVelxGbhZHZlJ3LcRXZu9lbkN3Yfd2bsJ2Xw9CXzV2Zh1WavwFdl5mLuR2cj5yZvxmYtA3Lc9CX6MHc0RHaiojIsJye.jpg)
测试脚本执行java代码功能:
TODO
- 变量范围控制
- 控制台环境可调试
- 支持函数
恐怕没这么多精力来继续了,目前够用就好。附上测试例子及javacc的jj文件
public void exec(String script,Map varMap){
try {
ScriptResult res = MiniScriptEngine.exec(script, varMap);
res.dumpVar();
} catch (Exception e) {
e.printStackTrace();
}
}
public String hello(String hello){
System.out.println( " Call method hello: " + hello);
return " [ " + hello + " ] " ;
}
public void testAssign(){
Map map = new HashMap();
map.put( " a " , new Integer( 100 ));
String s = " a/=12; b='bb'; b+=a; System.out.println('OUT:a='+(++a)); " ;
exec(s,map);
}
public void testIf(){
Map map = new HashMap();
map.put( " a " , new Integer( 1 ));
String s = " if(a>10){ System.out.println(a+'<10'); } else { System.out.println(a+'>=10'); } " ;
s += " a=20; " + s;
exec(s,map);
}
public void testFunction(){
Map map = new HashMap();
map.put( " a " , " Hello,world! " );
map.put( " s " , new MiniScriptSample());
String s = " s.hello(a); " ;
exec(s,map);
}
public void testFunction2(){
Map map = new HashMap();
map.put( " a " , " Hello,world! " );
map.put( " s " , new MiniScriptSample());
String s = " b=s.hello(a); System.err.println(b.substring(1,6)); " ;
exec(s,map);
}
public void testLoop(){
Map map = new HashMap();
map.put( " a " , new Integer( 12 ));
String s = " while(a-->0) System.out.println('############'.substring(0,a)); " ;
exec(s,map);
s = " for(a=0;a<12;a++){ " +
" for(b=0;b<2;b++){ " +
" System.out.print('------------'.substring(0,b)); " +
" } " +
" System.out.println('############'.substring(0,a)); " +
" } " ;
exec(s,map);
}
public static void main(String[] args) {
MiniScriptSample ss = new MiniScriptSample();
ss.testLoop();
}
options {
JDK_VERSION = " 1.4 " ;
JAVA_UNICODE_ESCAPE = true ;
STATIC = false ;
// DEBUG_LOOKAHEAD=true;
// DEBUG_PARSER=true;
// DEBUG_TOKEN_MANAGER=true;
}
PARSER_BEGIN(MiniScriptParser)
package com.xxx.miniscript.parser;
import com.xxx.miniscript.parser.syntaxtree. * ;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
public class MiniScriptParser {
public MiniScript parse() throws Exception{
return MiniScript();
}
public MiniScriptParser(String s) throws Exception{
this ( new ByteArrayInputStream(s.getBytes()));
}
}
PARSER_END(MiniScriptParser)
SKIP :
{
" "
| " "
| " "
| " "
| " "
}
SPECIAL_TOKEN :
{
< #SINGLE_LINE_COMMENT: " // " ( ~ [ " " , " " ]) * ( " " | " " | " " ) >
| < #FORMAL_COMMENT: " /** " ( ~ [ " * " ]) * " * " ( " * " | ( ~ [ " * " , " / " ] ( ~ [ " * " ]) * " * " )) * " / " >
| < #MULTI_LINE_COMMENT: " /* " ( ~ [ " * " ]) * " * " ( " * " | ( ~ [ " * " , " / " ] ( ~ [ " * " ]) * " * " )) * " / " >
|< COMMENT: ( < SINGLE_LINE_COMMENT > ) | ( < FORMAL_COMMENT > ) | ( < MULTI_LINE_COMMENT > ) >
}
TOKEN :
{
< TRUE: " true " >
| < FALSE: " false " >
| < IF: " if " >
| < ELSE: " else " >
| < FOR: " for " >
| < WHILE: " while " >
| < DO: " do " >
| < SWITCH: " switch " >
| < CASE: " case " >
| < DEFAULT_TOKEN: " default " >
| < BREAK: " break " >
| < CONTINUE: " continue " >
| < EXIT: " exit " >
| < NULL: " null " >
}
TOKEN :
{
< LPAREN: " ( " >
| < RPAREN: " ) " >
| < LBRACE: " { " >
| < RBRACE: " } " >
| < LBRACKET: " [ " >
| < RBRACKET: " ] " >
| < SEMICOLON: " ; " >
| < COMMA: " , " >
| < DOT: " . " >
}
TOKEN :
{
< ASSIGN: " = " >
| < GT: " > " >
| < LT: " < " >
| < BANG: " ! " >
| < TILDE: " ~ " >
| < HOOK: " ? " >
| < COLON: " : " >
| < EQ: " == " >
| < LE: " <= " >
| < GE: " >= " >
| < NE: " != " >
| < SC_OR: " || " >
| < SC_AND: " && " >
| < INCR: " ++ " >
| < DECR: " -- " >
| < PLUS: " + " >
| < MINUS: " - " >
| < STAR: " * " >
| < SLASH: " / " >
| < BIT_AND: " & " >
| < BIT_OR: " | " >
| < XOR: " ^ " >
| < REM: " % " >
| < LSHIFT: " << " >
| < RSIGNEDSHIFT: " >> " >
| < RUNSIGNEDSHIFT: " >>> " >
| < PLUSASSIGN: " += " >
| < MINUSASSIGN: " -= " >
| < STARASSIGN: " *= " >
| < SLASHASSIGN: " /= " >
| < ANDASSIGN: " &= " >
| < ORASSIGN: " |= " >
| < XORASSIGN: " ^= " >
| < REMASSIGN: " %= " >
| < LSHIFTASSIGN: " <<= " >
| < RSIGNEDSHIFTASSIGN: " >>= " >
| < RUNSIGNEDSHIFTASSIGN: " >>>= " >
}
TOKEN :
{
< #LETTER: [ " $ " , " A " - " Z " , " _ " , " a " - " z " , " À " - " Ö " , " Ø " - " ö " , " ø " - " ÿ " , " Ā " - " " , " " - " " , " ㌀ " - " ㍿ " , " 㐀 " - " 㴭 " , " 一 " - " 鿿 " , " 豈 " - " " ] >
// <#LETTER: ["A"-"Z", "_", "a"-"z", "À"-"Ö", "Ø"-"ö", "ø"-"ÿ", "Ā"-"", ""-"", "㌀"-"㍿", "㐀"-"㴭", "一"-"鿿", "豈"-""]>
| < #DIGIT: [ " 0 " - " 9 " , " ٠ " - " ٩ " , " ۰ " - " ۹ " , " ० " - " ९ " , " ০ " - " ৯ " , " ੦ " - " ੯ " , " ૦ " - " ૯ " , " ୦ " - " ୯ " , " ௧ " - " ௯ " , " ౦ " - " ౯ " , " ೦ " - " ೯ " , " ൦ " - " ൯ " , " ๐ " - " ๙ " , " ໐ " - " ໙ " , " ၀ " - " ၉ " ] >
}
TOKEN :
{
< IDENTIFIER: < LETTER > ( < LETTER > | < DIGIT > ) * >
}
TOKEN :
{
< INTEGER_LITERAL: < DECIMAL_LITERAL > ([ " l " , " L " ]) ? | < HEX_LITERAL > ([ " l " , " L " ]) ? | < OCTAL_LITERAL > ([ " l " , " L " ]) ?>
| < #DECIMAL_LITERAL: [ " 1 " - " 9 " ] ([ " 0 " - " 9 " ]) *>
| < #HEX_LITERAL: " 0 " [ " x " , " X " ] ([ " 0 " - " 9 " , " a " - " f " , " A " - " F " ]) +>
| < #OCTAL_LITERAL: " 0 " ([ " 0 " - " 7 " ]) *>
| < FLOATING_POINT_LITERAL: ([ " 0 " - " 9 " ]) + " . " ([ " 0 " - " 9 " ]) * ( < EXPONENT > ) ? ([ " f " , " F " , " d " , " D " ]) ? | " . " ([ " 0 " - " 9 " ]) + ( < EXPONENT > ) ? ([ " f " , " F " , " d " , " D " ]) ? | ([ " 0 " - " 9 " ]) + < EXPONENT > ([ " f " , " F " , " d " , " D " ]) ? | ([ " 0 " - " 9 " ]) + ( < EXPONENT > ) ? [ " f " , " F " , " d " , " D " ] >
| < #EXPONENT: [ " e " , " E " ] ([ " + " , " - " ]) ? ([ " 0 " - " 9 " ]) +>
| < CHARACTER_LITERAL: " ' " (( ~ [ " ' " , " / " , " " , " " ]) | ( " / " ([ " n " , " t " , " b " , " r " , " f " , " / " , " ' " , " " " ] | [ " 0 " - " 7 " ] ([ " 0 " - " 7 " ]) ? | [ " 0 " - " 3 " ] [ " 0 " - " 7 " ] [ " 0 " - " 7 " ]))) " ' " >
| < STRING_LITERAL1: " " " (( ~ [ " " " , " / " , " " , " " ]) | ( " / " ([ " n " , " t " , " b " , " r " , " f " , " / " , " ' " , " " " ] | [ " 0 " - " 7 " ] ([ " 0 " - " 7 " ]) ? | [ " 0 " - " 3 " ] [ " 0 " - " 7 " ] [ " 0 " - " 7 " ]))) * " " " >
| < STRING_LITERAL2: " ' " (( ~ [ " ' " , " / " , " " , " " ]) | ( " / " ([ " n " , " t " , " b " , " r " , " f " , " / " , " ' " , " " " ] | [ " 0 " - " 7 " ] ([ " 0 " - " 7 " ]) ? | [ " 0 " - " 3 " ] [ " 0 " - " 7 " ] [ " 0 " - " 7 " ]))) * " ' " >
}
void MiniScript():{}
{
(Statement()) *
}
void Statement() : {}
{
Block()
|
EmptyStatement()
|
IfStatement()
|
ForStatement()
|
WhileStatement1()
|
WhileStatement2()
|
SwitchStatement()
|
BreakStatement()
|
ContinueStatement()
|
ExitStatement()
|
StatementExpression()
}
void Block() : {}
{
" { "
(Statement()) *
" } "
}
void EmptyStatement() : {}
{
" ; "
}
void IfStatement() : {}
{
" if " " ( " Expression() " ) "
Statement()(EmptyStatement()) ?
(
" else " Statement()(EmptyStatement()) ?
) ?
}
void ForStatement():{}
{
" for " " ( " (Expression()) ? EmptyStatement() Expression() EmptyStatement() (Expression()) ? " ) "
Statement()(EmptyStatement()) ?
}
void WhileStatement1() : {}
{
" while " " ( " Expression() " ) "
Statement()(EmptyStatement()) ?
}
void WhileStatement2() : {}
{
" do "
Statement()(EmptyStatement()) ?
" while " " ( " Expression() " ) " EmptyStatement()
}
void SwitchStatement():{}
{
" switch " " ( " Expression() " ) "
" { "
(
SwitchLabel()
(Statement()(EmptyStatement()) ? ) *
) *
" } "
}
void SwitchLabel():{}
{
( " case " Expression() " : " )
|
( " default " " : " )
}
void BreakStatement() : {}
{
" break " EmptyStatement()
}
void ContinueStatement() : {}
{
" continue " EmptyStatement()
}
void ExitStatement() : {}
{
" exit " EmptyStatement()
}
void StatementExpression() : {}
{
PreIncrementExpression()
|
PreDecrementExpression()
|
LOOKAHEAD(PrimaryExpression() AssignmentOperator())
Assignment()
|
PostfixExpression()
}
void Assignment():{}{
PrimaryExpression()
AssignmentOperator()
Expression()
}
void PreIncrementExpression() :{}
{
" ++ " PrimaryExpression()
}
void PreDecrementExpression() :{}
{
" -- " PrimaryExpression()
}
void PrimaryExpression() :{}
{
PrimaryPrefix()
(
PrimarySuffix()
) *
}
void PrimaryPrefix():{}
{
Literal()
|
PrimaryVariable()
// |
// PrimarySystemObject()
|
( " ( " Expression() " ) " )
}
void PrimaryVariable():{}{
// "$"<IDENTIFIER>
< IDENTIFIER >
}
// void PrimarySystemObject():{}{
// "#"<IDENTIFIER>
// }
void PrimarySuffix():{}{
( " . " < IDENTIFIER > )
|
Arguments()
}
Literal Literal() :{}
{
IntegerLiteral()
| FloatLiteral()
| CharacterLiteral()
| StringLiteral()
| BooleanLiteral()
| NullLiteral()
}
void IntegerLiteral():{}
{
< INTEGER_LITERAL >
}
void FloatLiteral():{}
{
< FLOATING_POINT_LITERAL >
}
void CharacterLiteral():{}
{
< CHARACTER_LITERAL >
}
void StringLiteral():{}
{
< STRING_LITERAL1 > |< STRING_LITERAL2 >
}
void BooleanLiteral():{}{
" true " | " false "
}
void NullLiteral():{}{
" null "
}
void Arguments() :{}
{
LOOKAHEAD( " ( "" ) " )( " ( "" ) " )
| ( " ( " ArgumentList() " ) " )
}
void ArgumentList() :{}
{
(
Expression()( " , " Expression()) *
)
}
void AssignmentOperator() :{}
{
" = " | " += " | " -= " | " *= " | " /= " | " %= " | " &= " | " |= " | " ^= " | " >>= " | " <<= " | " >>>= "
}
void PostfixExpression() :{}
{
PrimaryExpression() ( " ++ " | " -- " ) ?
}
void Expression() : {}
{
(
LOOKAHEAD(PrimaryExpression() AssignmentOperator())
Assignment()
|
ConditionalExpression()
)
}
void ConditionalExpression() :{}
{
ConditionalOrExpression()
(
" ? "
Expression()
" : "
ConditionalExpression()
) ?
}
void ConditionalOrExpression() :{}
{
ConditionalAndExpression()
(
" || "
ConditionalAndExpression()
) *
}
void ConditionalAndExpression() :{}
{
InclusiveOrExpression()
(
" && "
InclusiveOrExpression()
) *
}
void InclusiveOrExpression() :{}
{
ExclusiveOrExpression()
(
" | "
ExclusiveOrExpression()
) *
}
void ExclusiveOrExpression() :{}
{
AndExpression()
(
" ^ "
AndExpression()
) *
}
void AndExpression() :{}
{
EqualityExpression()
(
" & "
EqualityExpression()
) *
}
void EqualityExpression() :{}
{
RelationalExpression()
(
( " == " | " != " )
RelationalExpression()
) *
}
void RelationalExpression() :{}
{
ShiftExpression()
(
( " > " | " < " | " >= " | " <= " )
ShiftExpression()
) *
}
void ShiftExpression() :{}
{
AdditiveExpression()
(
( " >> " | " << " | " >>> " )
AdditiveExpression()
) *
}
void AdditiveExpression() :{}
{
MultiplicativeExpression()(
( " + " | " - " )
MultiplicativeExpression()
) *
}
void MultiplicativeExpression() :{}
{
UnaryExpression()
(
( " * " | " / " | " % " )
UnaryExpression()
) *
}
void UnaryExpression() :{}
{
(( " + " | " - " )UnaryExpression())
|
PreIncrementExpression()
|
PreDecrementExpression()
|
UnaryExpressionNotPlusMinus()
}
void UnaryExpressionNotPlusMinus():{}
{
(( " ~ " | " ! " )UnaryExpression())
|
PostfixExpression()
}
编写过程小记:
- switch case处理起来比较麻烦
- 写一个语言,除了脑力也是一种体力活动,基本的赋值、运算语句处理好了,加一些语法还是比较轻松一点。
- 前后大概花了4个工作日,现在感觉很累很累,不过很高兴基本写好了,也很好用。
编写MiniScript脚本语言-1