Can this seeming ambiguity be parsed in a LALR(1) parser (PLY)?
我在ply(python-lexx-yacc)中有一个大型的ish语法,用于解析过程中有一些特殊挑战的语言。语言允许两种调用的前导语法看起来几乎相同,直到调用非终端结束。这为减少/减少冲突提供了很多机会,因为沿途令牌的语义不同,但可以用相同的终端令牌构建。我已经提取了下面语法的简单前后版本,我将对此做一点解释。
最初,表达式是一种典型的"分层语法",将调用和文本等转换为主表达式,然后转换为一元表达式,再转换为二元表达式。问题是,有两个参数的
我的方法是做一种表达,不能仅仅是一个
你可能已经猜到了,这把一切都搞砸了。对表达式链的修改也对
所以,我的问题是:有没有一种技术可以让人清楚地表达,如何允许相同的令牌代表不同的语义(即expr和id),这些语义仍然可以用lalr(1)样的ply进行解析?除此之外,还有什么有用的帮助解决问题的黑客吗?这能消除歧义吗?
1 2 3 | terminals: '+' '^' ',' '>' '(' ')' '/' ':' 'id' 'literal' (i.e. punctuation (besides '->' and '|', initial-lower-case words) non-terminals: initial-Upper-case words |
原始语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | S'-> S S -> Call_expr | Iter_expr Expr -> BinOp_expr BinOp_expr -> Unary_expr BinOp_expr -> BinOp_expr '+' BinOp_expr Unary_expr -> Primary_expr | '^' BinOp_expr Primary_expr -> Name_expr | Call_expr | Iter_expr | Literal_expr Name_expr -> Id Args -> Expr | Args ',' Expr Call_expr -> Primary_expr '>' Id '(' ')' | Primary_expr '>' Id '(' Args ')' Iter_expr -> Primary_expr '>' Id '(' Id '/' Expr ')' | Primary_expr '>' Id '(' Id ':' Id '/' Expr ')' | Primary_expr '>' Id '(' Id ',' Id ':' Id '/' Expr ')' Literal_expr -> literal Id -> id |
号
我试图消除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | S'-> S S -> Call_expr | Iter_expr Expr -> BinOp_expr_nn | BinOp_expr BinOp_expr -> BinOp_expr_nn | Unary_expr BinOp_expr_nn -> Unary_expr_nn | BinOp_expr '+' BinOp_expr Unary_expr -> Primary_expr | Unary_expr_nn Unary_expr_nn -> Primary_expr_nn | '^' BinOp_expr Primary_expr -> Primary_expr_nn | Name_expr Primary_expr_nn -> Call_expr | Iter_expr | Literal_expr Name_expr -> Id Args -> Expr | Args ',' Expr Call_expr -> Primary_expr '>' Id '(' ')' | Primary_expr '>' Id '(' Expr ')' | Primary_expr '>' Id '(' Id , Args ')' | Primary_expr '>' Id '(' BinOp_expr_nn , Args ')' Iter_expr -> Primary_expr '>' Id '(' Id '/' Expr ')' | Primary_expr '>' Id '(' Id ':' Id '/' Expr ')' | Primary_expr '>' Id '(' Id ',' Id ':' Id '/' Expr ')' Literal_expr -> literal Id -> id |
尽管你的文章有标题,但你的语法并不含糊。它不是lr(1),因为您提到的原因:输入好的。
1 | A ( B , |
可以是
这种冲突最多可以用两个附加的先行令牌来解决,因为只有在、后面紧跟
因为语法是明确的,所以可以使用GLR解析器进行解析,而不会出现任何问题(也不会进行任何修改)。PLY也不生产GLR解析器,但是Bison可以。这对您可能没什么用处,但我想我应该提一下,以防您没有被锁定在Python的使用中。好的。2。使用允许一些无效输入的语法,并通过语义分析丢弃它们
这几乎肯定是最简单的解决方案,我通常会推荐它。如果您将
1 2 | Iter_expr : id '(' id '/' Expr ')' | id '(' Args ':' id '/' Expr ')' |
号
然后它仍然会识别每个有效输入(因为
当然,
弥补lookahead信息不足的一种方法是在lexer中收集它,方法是将必要的lookahead收集到缓冲区中,并且仅在其语法类别已知时输出lexem。在这种情况下,lexer可以查找序列
1 2 3 | Iter_expr : id '(' id '/' Expr ')' | id '(' id ':' id '/' Expr ')' | id '(' iter_id ',' id ':' id '/' Expr ')' |
虽然在这种特殊情况下这会很好地工作,但它的维护性不是很强。特别是,它依赖于能够定义一个简单而明确的模式,该模式可以在lexer中实现。因为这种模式是对语法的简化,所以很有可能将来的一些句法添加会创建一个同样符合相同模式的语法。(这被称为词汇"hack"是有原因的。)好的。4。找到一个LALR(1)语法
如前所述,这种语法是
因为机械生成的语法相当大,所以这个过程不是很有吸引力,我不知道算法的实现。相反,最常见的方法是尝试只重写语法的一部分,就像在最初的问题中所做的那样。当然,这是可以做到的,但最终的结果并不是完全直观的。好的。
不过,这并不难。例如,这里是您的语法的一个稍微简化的版本,删除了多余的单元生成,并在运算符优先级中修复了几个(可能不正确,因为我不知道您要寻找什么语义)。好的。
名称以
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | Start : Call | Iter Expr : ID | ExprN ExprN : UnaryN | Expr '+' Unary Unary : ID | UnaryN UnaryN : ChainN | '^' Chain Chain : ID | ChainN ChainN : PrimaryN | Chain '>' CallIter PrimaryN: LITERAL | Call | Iter | '(' Expr ')' Call : ID '(' ')' | ID '(' ID ')' | ID '(' ID ',' ID ')' | ID '(' Args ')' Iter : ID '(' ID '/' Expr ')' | ID '(' ID ':' ID '/' Expr ')' | ID '(' ID ',' ID ':' ID '/' Expr ')' Args : ExprN ExprList | ID ',' ExprN ExprList | ID ',' ID ',' Expr ExprList ExprList: | ExprList ',' Expr |
号
我并没有像我想的那样对它进行测试,但我认为它产生了正确的语言。好的。好啊。