多语法支持
有两个主要的应用场景
- 同一种语言,不同版本语法解析起来不太一样,例如SQL,SQL有不同的版本
- 同一种语法内,根据上下文的情况,同样的词法表达的语义不一样
多版本语法示例
这个示例里面有个关键的parser,定义了java5,而地下的enumDecl里面会判断是否是java5版本,假如不是,则会把enum部分给剪掉,在不同版本的解析中,ANTLR不会抛错,它只是不会执行被剪掉的那部分
EnumParser enumParser = new EnumParser(tokens);
enumParser.java5 = true;
grammar Enum;
@parser::members {public static boolean java5;}
prog: ( stat
| enumDecl
)+
;
stat: id '=' expr ';' {System.out.println($id.text+"="+$expr.text);} ;
expr
: id
| INT
;
enumDecl
: {java5}? 'enum' name=id '{' id (',' id)* '}'
{System.out.println("enum "+$name.text);}
;
id : ID
| {!java5}? 'enum'
;
ID : [a-zA-Z]+ ;
INT : [0-9]+ ;
WS : [ \t\r\n]+ -> skip ;
版本强制校验
上一种做法即使语法不兼容,ANTLR只是剪切掉后续的执行步骤,而不会报任何错误,这种做法,假如语法版本不兼容,在语法解析过程就会报错
留意一下lexer和param的区别,lexer设置后是不能改的,pram的则是可以修改的
grammar Enum2;
@lexer::members {public static boolean java5 = false;}
prog: ( stat
| enumDecl
)+
;
stat: ID '=' expr ';' {System.out.println($ID.text+"="+$expr.text);} ;
expr: ID
| INT
;
// No predicate needed here because 'enum' token undefined if !java5
enumDecl
: 'enum' name=ID '{' ID (',' ID)* '}'
{System.out.println("enum "+$name.text);}
;
ENUM: 'enum' {java5}? ; // must be before ID
ID : [a-zA-Z]+ ;
INT : [0-9]+ ;
WS : [ \t\r\n]+ -> skip ;
歧义语法处理
一般语法中出现这种情况,都是出现了bug。。当然也有其他情况的啦,以函数和类型转换为例,函数的调用是T(x),类型转换也可以写成T(x),处理的思路和版本号的思路类似
grammar PredCppStat;
@parser::header {
import java.util.*;
}
@parser::members {
Set<String> types = new HashSet<String>() {{add("T");}};
boolean istype() { return types.contains(getCurrentToken().getText()); }
}
stat: decl ';' {System.out.println("decl "+$decl.text);}
| expr ';' {System.out.println("expr "+$expr.text);}
;
decl: ID ID // E.g., "Point p"
| {istype()}? ID '(' ID ')' // E.g., "Point (p)", same as ID ID
;
expr: INT // integer literal
| ID // identifier
| {!istype()}? ID '(' expr ')' // function call
;
ID : [a-zA-Z]+ ;
INT : [0-9]+ ;
WS : [ \t\n\r]+ -> skip ;