我正在编写一个解析器来解析类似C的语法。
首先,它现在可以解析代码:
a = 1;
b = 2;
现在我想在行尾添加分号。
最初的YACC规则是:
stmt: expr ';' { ... }
由我自己编写的词法分析器处理新行(代码被简化):
rule(/\r\n|\r|\n/) { increase_lineno(); return :PASS }
指令:此处的PASS相当于在LEX中不返回任何内容,它会删除当前匹配的文本并跳到下一个规则,就像通常使用空格一样。
因此,我不能简单地将我的YACC规则更改为:
stmt: expr end_of_stmt { ... }
;
end_of_stmt: ';'
| '\n'
;
所以我选择相应地通过解析器动态地改变词法分析器的状态。
像这样:
stmt: expr { state = :STATEMENT_END } ';' { ... }
添加一个可以将新行与新状态匹配的词法规则:
rule(/\r\n|\r|\n/, :STATEMENT_END) { increase_lineno(); state = nil; return ';' }
这意味着词法分析器处于以下状态:STATEMENT_END状态。它将像往常一样首先增加行号,然后将状态设置为初始状态,然后假装自己是分号。
奇怪的是它实际上并不适用于以下代码:
a = 1
b = 2
我调试它并得到它实际上并没有得到';'在数字1之后扫描换行符时所期望的,并且状态指定的规则并未真正执行。
设置新状态的代码在扫描新行并且没有返回任何内容后执行,这意味着,这些工作按以下顺序完成:
a
,=
和1
b
{ state = :STATEMENT_END }
)b
此处这就是我的期望:
a
,=
和1
expr
,因此请缩减为stmt
;
内省后我发现可能因为YACC使用LALR(1)而引起,此解析器将首先读取一个令牌。当它扫描到那里时,状态尚未设置,因此无法获得正确的令牌。
我的问题是:如何使其按预期工作?我对此一无所知。
感谢。
答案 0 :(得分:6)
首先要认识到,拥有这样的可选行终止符会使您的语言含糊不清,因此您首先需要决定解决歧义的方式。在这种情况下,主要歧义来自可能是中缀或前缀的运算符。例如:
a = b
-c;
您是希望将上述内容视为单个expr语句,还是将第一个分号排除在两个单独的语句之后?使用类似C语言的函数调用语法会出现类似的潜在歧义:
a = b
(c);
如果您希望将这些语句解析为两个语句,则可以使用您尝试过的方法;你只需要提前设置一个令牌。这很棘手,因为如果你有未闭括号,你不想设置状态,所以你最终需要一个额外的状态var来记录paren嵌套深度,并且只设置insert-semi-before-newline状态,当它是0
如果你想将上述情况作为一个陈述来解决,事情变得棘手,因为你实际上需要更多的预测来决定换行何时应该结束一个陈述 - 至少你需要在换行符之后查看令牌(以及任何评论或其他被忽略的东西)。在这种情况下,你可以让词法分析器做额外的前瞻。如果你使用flex(你显然不是吗?),我建议使用/
运算符(直接查找),或者推迟返回分号,直到匹配下一个标记的词法分析器。< / p>
一般来说,在进行这种令牌状态记录时,我觉得最简单的方法是尽可能完全在词法分析器中完成,所以你不需要担心额外的前瞻标记(有时候并不总是)由解析器。在这种特定情况下,一个简单的方法是让词法分析器记录括号((
为+1,)
为-1),并返回最后一个标记。然后,在换行规则中,如果paren级别为0且最后一个标记可以结束表达式(ID或常量或)
或仅后缀运算符),则返回额外的;
< / p>
另一种方法是让词法分析器返回NEWLINE
作为自己的标记。然后,您将更改解析器以接受stmt: expr NEWLINE
以及语法中大多数其他令牌之间的可选换行符。这会将歧义直接暴露给解析器(现在它不是LALR(1)),因此您需要通过使用yacc的运算符优先级规则(棘手且容易出错)或使用类似bison的%glr-parser
选项或btyacc的回溯能力直接处理歧义。
答案 1 :(得分:3)
你所尝试的当然是可能的。
事实上,Ruby正是这样做的,它有一个yacc解析器。换行软终止语句,分号是可选的,并且语句会在“如果需要”的情况下自动在多行上继续。解析器和词法分析器之间的通信可能是必要的,是的,遗留的yacc是LALR(1)。
我不确切知道Ruby是如何做到的。我的猜测一直是它实际上没有通信(很多),而是词法分析器识别显然没有完成的构造,并且默默地将换行视为空格直到parens和括号平衡。当线条以二元运算符或逗号结尾时也必须注意,并且也会使用这些换行符。
只是一个猜测,但我相信这种技术会起作用。如果您想确切了解Ruby is open source...是如何做到的,请Matz。