原创,转载请注明出处: @author zfzheng http://blog.csdn.net/redvalley
MiniScript脚本语言
起因:应老大需要,编写一种脚本语言。之所以不用groovy/javascript...等,因为我们需要其它特殊的功能,自己开发一个语言,则可最大限度地进行“定制”。自告奋勇编写(貌似其它人不在行这个 :D),也练练手。
要求功能
- 表达式赋值/计算
- 控制语句if/for/while/switch
- 变量可从java执行环境传入
- 支持对象方法调用
- 与java交互
实现步骤
- 定义文法
- 编写词法/语法分析器(javacc)
- JTB产生语法树
- 使用访问者模式执行脚本
完成,测试
做个简单的UI (测试传入变量则需要调用代码中编写)界面:
测试脚本执行java代码功能:
TODO
恐怕没这么多精力来继续了,目前够用就好。附上测试例子及javacc的jj文件
/**
* 执行脚本
*
@param
script
*
@param
varMap
*/
public
void
exec(String script,Map varMap){
try
{
ScriptResult res
=
MiniScriptEngine.exec(script, varMap);
res.dumpVar();
}
catch
(Exception e) {
e.printStackTrace();
}
}
/**
* 测试方法
*
@param
hello
*
@return
*/
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);
}
/**
* 测试函数调用2
*
*/
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();
}
/**
* JavaCC file
*
@author
zfzheng
*/
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个工作日,现在感觉很累很累,不过很高兴基本写好了,也很好用。