3 antlr 常见语言模式

Antlr 常见的语言模式:

1)序列模式
ANTLR使用序列模式

rule : ID ':' alternative ('|' alternative)* ';';

描述:匹配这样的结构: '规则名: ' 后面跟着至少一个备选分支,然后是若干条以 '|' 符号分隔的备选分支,最后一个';'

序列模式匹配Java结构:

stats : (stat ';')*

描述:匹配零个或多个以';' 终止的语句

匹配以逗号分隔的多个表达式

exprList : expr (',' expr)* ;


2)选择模式(多个备选分支)


我们使用 | 符号作为“或者” 来表达编程语言中的选择模式,在ANTLR的规则中,它用于分隔多个可选的语法结构-称作备选分支

允许字段中出现整数或者字符串

field : INT | STRING ;

3)词法符号依赖模式

vector : '[' INT+ ']'; //[1], [1 2], [1 2 3], ...

描述:我们使用一个序列来指明所有配对的符号,通常这些符号会把其他元素分组或者包裹起来。

4)嵌套模式

嵌套的词组是一种自相似的语言结构,即它的子词组也遵循相同的结构。表达式是一种典型的自相似语言结构,它包含多个嵌套的、以运算符分隔的子表达式。在语法中,我们使用递归规则来表达这种这种自相似的语言结构。所以,如果一条规则定义中的伪代码引用了它自身,我们就需要一条递归规则(自引用规则)。

stat : 'while' '(' expr ')' stat //匹配WHILE语句
     | '{' stat* '}'
    ...
    ;

描述:一个while表达式由一个关键字while开始,后面是一个括号中的条件表达式,再后面就是一条语句。我们也可以把多个语句放入花括号中,当做一个“代码块语句”使用。其中,while 中stat是一个循环结构,它可以是一个语句或者花括号包裹的一组语句。因为stat规则在前两个备选分支中引用了自身,我们称它为直接递归(directly recursive)的。

如果我们将它的第二个备选分支抽取出来,stat规则和block规则就会互为间接递归(indirectly recursive)的。

stat: 'while' '(' expr ')' stat  //匹配WHILE语句
    | block  //匹配一个语句组成的代码块
   ...       //其他种类的语句
    ;
block: '{' stat* '}' ;   //匹配花括号中若干条语句组成的代码块

 常见的计算机语言模式

模式名

描述

序列模式

它是一个有限长度或着任意长度的序列,序列中元素可以是词法符号或者子规则。序列模式的例子包括变量申明(类型后面紧跟着标识符)和整数序列,下面是范例实现:

x y ... z     //x 后面跟着 y, ..., z

'[' INT ']'  //Matlab 的整数向量

带终止符的序列模式

它是一个任意长的、可能为空的序列,该序列由一个词法符号分隔开通常是分号或者换行符,其中的元素可以是词法符号或者子规则。这样的例子包括类C语言的语句集合和一些用换行符来分隔的数据格式。下面是范例实现:

(statement ';')*  //Java 的语句集合

(row '\n')*         //多行数据

带分隔符的序列模式

它是一个任意长的、可能为空的序列,该序列由一个词法符号分隔开,通常是分号或者换行符,其中的元素可以是词法符号或者子规则。这样的例子包括函数定义中的参数表、函数调用时传递的参数表、某些语句之间有分隔符却无终止符的编程语言,以及目录名。下面是范例实现:

expr (' , ' expr)* //函数调用时传递的参数

( expr (' , ' expr)*)? //函数调用时传递的参数是可选的

'/'?name ('/' name) *  //简化的目录名

stat ('.' stat)*  //若干个SmallTalk 语句

选择模式

它是一组备选分支的集合。这样的例子包括不同种类的类型、语句、表达式或者

XML标签。下面是范例实现:

type : 'int' | 'float';

stat : ifstat | whilestat | 'return' expr ';' ;

expr : '(' expr ')' | INT | ID;

tag : '<' Name attibute* '>' | '<' '/' Name '>' ;

词法符号依赖

一个词法符号需要和一个或者多个后续词法符号匹配。这样的例子包括配对的圆括号、花括号、方括号和尖括号。下面是范例实现:

'(' expr ')'  //嵌套表达式

ID '[' expr ']'  //数组索引表达式

'{' stat* '}'      //花括号包裹的若干个语句

'<' ID (',' ID)* '>'  //泛型声明

嵌套模式

它是一种自相似的语言结构。这样的例子包括表达式、java的内部类、嵌套代码块以及嵌套的Python函数定义。下面是范例实现:

expr : '(' expr ')' | ID;

classDef: 'class' ID '{' (classDef | method| field) '}' ;

Antlr 处理优先级、左递归和结合性

大多数语言规范使用了一种特殊的递归方式,称为左递归(left recursion)。自顶向下的语法和语法分析器的经典形式无法处理左递归。

例如:

对于 1 + 2 * 3 这样的输入而言,上述规则能用两种方式解释它

Antlr 通过优先选择位置靠前的备选分支来解决歧义问题,这隐式地允许我们指定运算符优先级。

例如:expr 规则中,乘法规则在加法规则之前,所以ANTLR在解决 1+2*3的歧义问题时会优先处理乘法。

 

grammar expr;


/**起始规则,语法分析的起点**/
prog: stat+;


stat: expr NEWLINE
    | NEWLINE
    ;

expr : expr '*' expr  //乘法规则在加法规则之前
     | expr '+' expr
     | INT
     ;

INT: [0-9]+;
MUL : '*';
DIV : '/';
ADD : '+';
SUB : '-';
NEWLINE: '\r'? '\n';

语法树:

默认情况下,ANTLR按照我们通常对*和+的理解,将运算符从左向右的进行结合。尽管如此,一些运算符

例如指数运算符是从右向左结合的,所以我们需要在这样的运算符上使用assoc选项手工指定结合性。

这样输入的2^3^4就能被正确解释为2^(3^4):

grammar indexexpr;
proc: expr+;

expr: <assoc=right> expr '^' expr //^运算符是右结合的
     | INT
     ;
INT: [0-9]+;
EXPONENTIATION : '^';
NEWLINE: '\r'? '\n';

语法树:

若要将上述三种运算符组合成为同一条规则,我们就必须把^放在最前面,因为它的优先级比*和+都要高。

grammar symbolicintegration;

proc: expr*;
expr: <assoc=right> expr '^' expr //^运算符是右结合
    | expr '*' expr //匹配由'*' 运算符连接的子表达式
    | expr '+' expr //匹配 '+' 运算符连接的子表达式
    | INT           //匹配简单的整数因子
    ;

INT: [0-9]+;

注意:在ANTLR 4.2 之后,<assoc=right>需要被放到备选分支的最左侧,否则会受到警告。

左递归规则是这样的一种规则:在某个备选分支的最左侧以直接或间接方式调用了自身。上面的例子中的expr规则是直接左递归的,因为除INT之外的所有备选分支都以expr规则本身开头(它同时也是右递归(right recursive)的,因为它的某些备选分支在最右侧引用了expr)。

虽然ANTLR 已经能够处理直接左递归,但是它还无法处理间接左递归。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独行客-编码爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值