我正在使用whittle来解析语法,但我遇到了经典的LALR歧义问题。我的语法如下(简化):
<comment> ::= '{' <string> '}' # string enclosed in braces
<tag> ::= '[' <name> <quoted-string> ']' # [tagname "tag value"]
<name> ::= /[A-Za-z_]+/ # subset of all printable chars
<quoted-string> ::= '"' <string> '"' # string enclosed in quotes
<string> ::= /[:print:]/ # regex for all printable chars
问题当然是<string>
。它包含所有可打印的字符,因此非常贪婪。由于它是一个LALR解析器,所以它尝试将<name>
解析为<string>
,然后一切都会中断。语法使事情变得复杂,因为它对不同的事情使用不同的字符串分隔符,这就是为什么我首先尝试制定<string>
规则。
如果可能的话,有没有一种规范的方法来规范这个语法,使其符合LALR?
这不是"经典的LALR歧义问题",不管是什么。这只是语言词汇规范中的一个错误。
我快速浏览了一下惠特尔的自述,但它与OP中的语法没有任何相似之处。因此,我假设OP中的文本是概念性的,而不是字面意义上的,而且它包含了明显不正确的
<string> ::= /[:print:]/ # regex for all printable chars
只是一个拼写错误。
最好是/[:print:]*/
,假设Ruby允许您使用[:print:]
而不是Posix标准的[[:print:]]
。
但这也不正确,因为词法分析(通常)匹配尽可能长的字符串,因此会吞噬最后的引号和后面的任何文本。
因此,quoted-string
的正确解决方案是正确地写出:
<quoted-string> ::= /"[^"]*"/
甚至
<quoted-string> :: /"([^\"]|\.)*"/
# any number of characters other than quote or escape, or escaped pairs
关于如何避开内部双引号,您可能还有其他想法;这些只是例子。在这两种情况下,您都需要对令牌进行后处理,以便(至少)去掉双引号和可能的解释转义序列。事情就是这样。
假设您的意图是注释可能包括嵌套大括号(例如{This comment {with this} ends here}
),因为嵌套大括号语法不是正则的,因此无法与正则表达式匹配,那么您的注释序列就提出了一个更困难的问题。当然,现在很少有"正则表达式"库是真正正则的,我不知道Ruby是否包含某种大括号计数扩展,比如Lua的模式语法。嵌套的大括号语法当然是上下文无关的,但要真正解析它,您需要以与程序其他部分不同的方式对外部{...}
的内容进行词法分析。
正是后一种观察结果,而不是LALR算法中的任何弱点,给你带来了痛苦,我想说,这是whittle(大多是未记录的afaics)词汇分析部分的弱点。例如,在flex生成的lexer中,使用开始条件来分离词法环境(程序/引用的字符串/支持的注释)是正常的,这样解析器就不会有歧义。
希望能有所帮助。