对于一门计算机语言的语法,有4种基本的抽象语言模式:
序列:即一列元素选择:在多种可选方案中作出选择词法符号依赖:一个词法符号需要和某处的另外一个词法符号配对,例如左右括号匹配。嵌套结构:一种自相似的语言结构接下来就介绍一下Antlr如何表达这些模式
使用 | 符号作为或者来表达编程语言中的选择模式。 例1 : field : INT | STRING 例2 :
stmt : node_stmt | edge_stmt | attr_stmt | id '=' id ;使用一个序列来指明所有配对的符号,通常这些符号会把其他元素分组或者包裹起来。 例1: vector : '[' INT+ ']' ; 例2: expr : expr '(' exprList? ')'; 例3: object : '{' pair (',' pair)* '}';
嵌套的词组是一种自相似的语言结构,即子词组遵循相同的结构。 例1:
stat: 'while' '(' expr ')' stat | '{' stat* '}' ... ;以上语句中 stat 是一个循环结构,可以是一个语句或者由花括号包裹的一组语句。
对于 1+23 这样的输入文本而言,有 1 + (23) 和 (1+2) * 3 两种理解。 ANTLR通过优先选择位置靠前的备选分支来解决歧义问题,隐式地允许我们指定运算符优先级。
结合性的处理 默认情况下,ANTLR将运算符从左向右进行结合。但是有一些运算符,例如指数运算符是从右向左结合的,例如 2^3^4正确解释应该是 2^(3^4)。ANTLR允许我们手工指定结合性,如下: expr : expr '^'<assoc=right> expr | INT ; 左递归 左递归规则指的是在某个备选分支的最左侧以直接或者间接方式调用了自身。ANTLR4 已经能够处理直接左递归,但是无法处理间接左递归。间接左递归如下,对于ANTLR来说不可行: expr : expo | ... ; expo : expr '^'<assoc=right> expr ;ANTLR允许词法规则和语法规则在同一个语法文件中,但是这是语言识别过程中的两个不同阶段,我们必须告诉ANTLR每条规则对应的阶段。方式: 词法规则以大写字母开头,语法规则以小写字母开头。
常见词法符号的词法规则:
匹配标识符 一个基本标识符就是一个由大小写字母组成的字符序列,规则如下: ID : ('A'..'Z'|'a'..'z')+; ANTLR也支持正则表达式中的类似规则,如下: ID : [A-Za-z]+; 类似ID的规则有时候会和其他词法规则或者字符串常量值产生冲突,比如’enum’。 ANTLR解决这种冲突的方法是: 首先ANTLR会从词法规则中筛选出所有的字符串常量,并将它们和词法规则放在一起。所以 ‘enum’ 这样的字符串常量会被隐式地定义为词法规则,然后放置在语法规则之后,显示定义的词法规则之前。 而ANTLR词法分析器解决歧义问题的方法是优先使用位置靠前的词法规则,所以像’enum’这样的词比显示定义的词法具有更高优先级。 另外,ANTLR会自动将词法规则放置在语法规则之后。 enumDef : 'enum' '{' ... '}'; ... ID : [a-zA-Z]+; 匹配数字 整数比较容易匹配,但是浮点数稍微复杂一点。 浮点数的格式可以是 xxx.xxx 也可以是 .xxx,规则可以如下: FLOAT : DIGIT+'.'DIGIT* | '.'DIGIT+ ; fragment DIGIT : [0-9] ;将一条规则声明为fragment是说明改规则本身不是一个词法符号,只会被其他的词法规则使用,即不能在文法规则中引用。
匹配字符串常量 .*? 是非贪婪模式(匹配数量最少的字符,即获取一些字符直到发现匹配后续子规则的字符为止),.*是贪婪模式,会消费掉一切匹配的字符,所以会把 " 消费掉。 ESC片段是为了让字符串中允许出现转义双引号的语法和转义字符的语法。由于ANTLR语法本身需要对转义字符\进行转义,因此需要\来表示单个反斜杠字符。 STRING: '"' (ESC|.)*? '"'; fragment ESC : '\\"' | '\\\\' ; 匹配注释和空白字符 当词法分析器匹配到注释和空白字符的时候,我们并不需要进行语法分析,所以通常需要将它们丢弃。定义需要被丢弃的词法符号需要用skip指令通知词法分析器将它们丢弃。案例如下: LINE_COMMENT : '//' .*? '\r'?'\n' -> skip ; COMMENT : '/*' .*? '*/' -> skip ;大多编程语言把空白字符当成词法符号间的分隔符,并可以将它们忽略,丢弃空白符的ANTLR语法如下: WS : (' '|'\t'|'\r'|'\n')+ -> skip ; 或者 WS : [ \t\r\n]+ -> skip;
以上内容主要对ANTLR常见的一些语法进行了说明,后续会对更细节的语法进行说明
