我写过这个语法:
expr : multExpr ( ('+' | '-') multExpr )*;
multExpr : atom ( ('*' | '/') atom )*;
atom : INT | FLOAT | ID | '(' expr ')';
condition : cond ('or' cond)*;
cond : c1 ('and' c1)*;
c1 : ('not')? c2;
c2 : '(' condition ')' | boolean;
boolean : expr (relop expr | ²) | 'true' | 'false';
relop : '<' | '<=' | '>' | '>=' | '==' | '!=';
我已经省略了INT,FLOAT,ID的词法规则,因为很明显。
问题是c2规则,因为'(',我找不到解决方案,你能给我一个解决方案,这是不明确的吗?
答案 0 :(得分:5)
为什么不简单地做:
expr : orExpr;
orExpr : andExpr ('or' andExpr)*;
andExpr : relExpr ('and' relExpr)*;
relExpr : addExpr (relop addExpr)?;
relop : '<' | '<=' | '>' | '>=' | '==' | '!=';
addExpr : multExpr (('+' | '-') multExpr)*;
multExpr : unaryExpr (('*' | '/') unaryExpr)*;
unaryExpr : 'not'? atom;
atom : INT | FLOAT | ID | 'true' | 'false' | '(' expr ')';
一元not
的优先级通常高于您现在尝试的优先级。
这将允许像42 > true
这样的表达式,但是当你走AST /树时,可以检查这样的语义。
修改强>
输入"not(a+b >= 2 * foo/3.14159) == false"
现在将被解析为这样(忽略空格):
如果将输出设置为AST并混合使用一些树重写操作符(^
和!
):
options {
output=AST;
}
// ...
expr : orExpr;
orExpr : andExpr ('or'^ andExpr)*;
andExpr : relExpr ('and'^ relExpr)*;
relExpr : addExpr (relop^ addExpr)?;
relop : '<' | '<=' | '>' | '>=' | '==' | '!=';
addExpr : multExpr (('+' | '-')^ multExpr)*;
multExpr : unaryExpr (('*' | '/')^ unaryExpr)*;
unaryExpr : 'not'^ atom | atom;
atom : INT | FLOAT | ID | 'true' | 'false' | '('! expr ')'!;
你会得到:
答案 1 :(得分:2)
你的问题源于这样一个事实:'('可能是c2
的第一个替代品或atom
的最后一个替代品的开头。例如,给定输入像{{1第一个打开的paren是((x+y) > (a+b))
的开头,但是第二个是c2
的开头。[编辑:并且解析器没有指示在某个任意点之前要走哪条路稍后 - 例如,它不能知道第一个打开的paren是atom
的开头,直到遇到c2
。例如,如果那是>
,然后两个开放的parens将开始*
s。]
处理它的一种可能方法是统一算术和布尔表达式的规则,因此只有一个规则atom
,而'(' expression ')
可能是算术或布尔值。然而,这通常会产生相当松散的类型的副作用,在算术和布尔表达式之间进行相对自由的转换(至少在解析器级别 - 您可以在语义中严格按照您的意愿强制执行类型)。
编辑:例如,在Pascal中,规则就是这样运行的(简化了一点点):
expression
答案 2 :(得分:0)
您无法将c1定义为以下内容吗?
('not')? (('(' condition ')') | boolean)
答案 3 :(得分:0)
解决这个问题的一种方法是将它分成两组词法分析器规则并按顺序应用于输入(一个用于数学,另一个用于布尔值)。