天天看点

编写MiniScript脚本语言-1

原创,转载请注明出处: @author zfzheng http://blog.csdn.net/redvalley

MiniScript脚本语言

起因:应老大需要,编写一种脚本语言。之所以不用groovy/javascript...等,因为我们需要其它特殊的功能,自己开发一个语言,则可最大限度地进行“定制”。自告奋勇编写(貌似其它人不在行这个 :D),也练练手。

要求功能

  1. 表达式赋值/计算
  2. 控制语句if/for/while/switch
  3. 变量可从java执行环境传入
  4. 支持对象方法调用
  5. 与java交互

实现步骤

  1. 定义文法
  2. 编写词法/语法分析器(javacc)
  3. JTB产生语法树
  4. 使用访问者模式执行脚本

完成,测试

做个简单的UI (测试传入变量则需要调用代码中编写)界面:

编写MiniScript脚本语言-1

测试脚本执行java代码功能:

编写MiniScript脚本语言-1

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