我正在编写一种简单的编程语言。它具有以下语法:
program: declaration+;
declaration: varDeclaration
| statement
;
varDeclaration: 'var' IDENTIFIER ('=' expression)?';';
statement: exprStmt
| assertStmt
| printStmt
| block
;
exprStmt: expression';';
assertStmt: 'assert' expression';';
printStmt: 'print' expression';';
block: '{' declaration* '}';
//expression without left recursion
/*
expression: assignment
;
assignment: IDENTIFIER '=' assignment
| equality;
equality: comparison (op=('==' | '!=') comparison)*;
comparison: addition (op=('>' | '>=' | '<' | '<=') addition)* ;
addition: multiplication (op=('-' | '+') multiplication)* ;
multiplication: unary (op=( '/' | '*' ) unary )* ;
unary: op=( '!' | '-' ) unary
| primary
;
*/
//expression with left recursion
expression: IDENTIFIER '=' expression
| expression op=('==' | '!=') expression
| expression op=('>' | '>=' | '<' | '<=') expression
| expression op=('-' | '+') expression
| expression op=( '/' | '*' ) expression
| op=( '!' | '-' ) expression
| primary
;
primary: intLiteral
| booleanLiteral
| stringLiteral
| identifier
| group
;
intLiteral: NUMBER;
booleanLiteral: value=('True' | 'False');
stringLiteral: STRING;
identifier: IDENTIFIER;
group: '(' expression ')';
TRUE: 'True';
FALSE: 'False';
NUMBER: [0-9]+ ;
STRING: '"' ~('\n'|'"')* '"' ;
IDENTIFIER : [a-zA-Z]+ ;
此左递归语法很有用,因为它可确保解析树中的每个节点最多具有2个子代。例如,
var a = 1 + 2 + 3
将变成两个嵌套的加法表达式,而不是一个带有三个孩子的加法表达式。该行为很有用,因为它使编写解释器变得容易,因为我可以(高度简化)进行操作:
public Object visitAddition(AdditionContext ctx) {
return visit(ctx.addition(0)) + visit(ctx.addition(1));
}
而不是遍历所有子节点。
但是,这种左递归语法有一个缺陷,那就是它接受无效的语句。 例如:
var a = 3;
var b = 4;
a = b == b = a;
在此语法下有效,即使预期行为为
b == b
首先被解析,因为==
的优先级高于分配(=
)。b == b
,所以表达式变得不连贯。解析失败。相反,发生了以下不良行为:将最后一行解析为(a = b)==(b = a)。
如何防止左递归解析不连贯的语句,例如a = b == b = a
?
非左递归语法识别出此输入正确,并引发了解析错误,这是所需的行为。