Antlr是一个可以帮助我们构建定制化DSL的非常好用的工具,无论是构建一个什么样的DSL,我们通常需要定制词法以及语法规则,这两件事情Antlr都可以帮助我们解决。

词法(Lexer)

词法(Lexer)分析是对语言进行Tokenize的过程,例如一句话是由多个子句组成,每个子句有一个个的单词组成,解析单词的过程就可以理解为是词法解析的过程。
在Antlr中,词法解析规则按照约定使用大写的名称进行标记,例如:

CLASS_PATH
    : [0-9a-zA-Z_]+(.[0-9a-zA-Z_]+)*'.'[a-zA-Z]+[0-9a-zA-Z_]*
    ;

表示匹配java中的CLASS_PATH,那么在进行词法解析的过程中,程序会把符合规则的字符当作一个整体来处理

语法(Parser)

语法(Parser)分析是对词法分析所产生的Token序列进行结构识别的过程,例如在 a + b 背后是一个加法,并且由ab+ 三个Token解析而来。
在Antlr中,语法规则的名称按照约定使用小写字母开头,例如:

paramDef
    : CLASS_PATH PARAM_VALUE
    | PRIMITIVE_TYPE PARAM_VALUE
    ;

表示例如int a这样的参数定义的两种组成方式:class_path类名 + 参数值 或者 原始类型 + 参数值

词法和语法的界限

当然,上面的paramDef也可以使用词法规则(大写)表示,这个可以视自己的情况而定,根据处理的方便程度和可读性进行选择即可。
定义好规则之后,Antlr程序(或者生成的Lexer/Parser)会帮助我们将输入的业务代码解析成一颗语法树(AST),形成语法树之后我们就可以根据业务定义的语法规则在遍历树的过程中完成程序所需要执行的逻辑。

优先级

Antlr默认按照词法/语法的定义的顺序定义优先级。
另外,语法规则在词法规则之上。

实现思路

实现一个Java Bean表达式的解析器有时候很有用,因为不需要硬编码就能进行逻辑表达,现在我们可能需要考察如下表达式:

beanName.methodName(int 1, com.a.b.ParamType true)

表示调用beanNamemethodName方法,有两个参数,类型分别是intcom.a.b.ParamType,值分别是1true
思路如下:

  • 词法解析包括:
    • beanName.methodName
    • 两个括号
    • 参数类型:包括原始类型以及class_path两种类型
    • 参数值:boolean、string、int等类型
  • 语法解析包括:
    • beanName.methodName() -- 没有参数的case
    • beanName.methodName(<参数类型,参数值>) -- 有一个或者多个参数的case