据我了解,大多数语言都没有上下文,但有一些例外。例如,a * b
可能代表type * pointer_declaration
或C ++中的乘法。发生哪一个取决于上下文,第一个标识符的含义。另一个例子是VHDL中的name
生产
enum_literal ::= char_literal | identifer
physical_literal ::= [num] unit_identifier
func_call ::= func_identifier [parenthized_args]
array_indexing ::= arr_name (index_expr)
name ::= func_call | physical_literal | enum_litral | array_indexing
你看到句法形式不同但是如果省略了可选参数,它们可以匹配,例如f
,它代表func_call,physical_lite,如1米,隐含可选数量1,或enum_literal。
与Scala插件设计人员交谈时,我受过教育,知道您在依赖关系发生变化时构建AST以重新评估它。如果你有AST,则无需重新解析文件。 AST也值得显示文件内容。但是,如果语法是上下文敏感的,则AST无效(假设f
是一个函数,在另一个文件中定义,但后来用户将其重新认证为枚举文字或未定义)。在这种情况下AST会发生变化每当您更改依赖项时,AST都会更改。我要求评估并让我知道如何制作它的另一个选择是构建一个模糊的AST。
据我所知,解析器组合符为PEG kind. They hide the ambiguity by returning you the first matched production,f
与函数调用匹配,因为它是我语法中的第一个替代方法。我要求一个组合器,而不是回到第一次成功,它继续下一个替代。最后,它会返回所有匹配替代品的列表。它会让我感到含糊不清。
我不知道如何向用户显示模糊文件内容树,但它不需要重新解析相关文件。我也很高兴知道现代语言设计如何解决这个问题。
一旦解析了模糊节点并返回结果的模糊性,我希望解析器收敛,因为我想继续解析超出name
并且我不希望每次解析到文件末尾歧义。像f(10)
这样的情况很复杂,它可以是一个带有单个参数的函数调用,也可以是一个带有函数调用的函数调用,它返回一个数组,后来被索引。因此,f(10)可以将名称两种方式匹配,可以直接或递归地func_call
作为arr_indexing -> name ~ (expr)
。因此,它不会像几个并行规则那样含糊不清,比如fcall | literal
。在重新聚合之前,某些分支可能比1个解析器长,例如fcall ~ (expr) | fcall
。
你会如何解决它?是否可以在PEG中添加含糊不清的组合物?
答案 0 :(得分:2)
首先,您声称“大多数语言都是无上下文的,但有一些例外”,这并非完全正确。在设计计算机语言时,我们主要尝试将其保持为无上下文,因为CFG是事实上的标准。它将减轻很多工作。然而,这并不总是可行的,并且很多 [?] 语言依赖于语义分析阶段来消除任何可能的歧义歧义。
Parser组合器通常不使用正式模型;另一方面,PEG是语法的形式主义,CFG也是如此。在过去的十年中,由于两个事实,一些人决定使用PEG而不是CFG:根据设计,PEG是明确的,并且它们可能总是在线性时间内被解析。解析器组合库可能使用PEG作为基础形式,但也可能使用CFG甚至不使用。
PEG对于设计计算机语言很有吸引力,因为我们通常不想处理歧义,这在使用CFG时很难(或甚至不可能)。并且,正因为如此,它们可能通过使用动态编程(所谓的packrat解析器)解析O(n)时间。 It's not simple to "add ambiguities to them" for a few reasons, most importantly because the language they recognize depend on the fact that the options are deterministic, which is used for example when checking for lookahead。它并不像“只选择第一选择”那么简单。例如,您可以定义PEG:
S = "a" S "a" / "aa"
仅解析N“a”的序列,其中N是2 的幂。因此它识别一个2,4,8,16,32,64等的序列,字母“a”。通过添加模糊性,如CFG所具有的,那么你会认识到任何偶数的“a”(2,4,6,8,10等),这是一种不同的语言。
要回答你的问题,
你会如何解决它?是否可以在PEG中添加含糊不清的组合物?
首先,我必须说这可能不是一个好主意。如果您希望保持AST的模糊性,您可能应该使用CFG解析器。
例如,可以为PEG生成一个解析器,类似于boolean grammars的解析器,但是我们的渐近解析时间将从O(n)增长到O(n 3 )通过保持所有替代品存活,同时保持相同的语言。而且我们实际上同时失去了关于PEG的好处。另一种方法是将packrat解析器保留在内存中,并横向其表来处理来自AST的语义。这也不是一个好主意,因为这意味着占用大量内存。
理想情况下,应该通过更改语法结构来构建一个已经包含有关可能含糊不清的信息的AST。虽然这需要手工操作,而且通常并不简单,但您不必返回阶段再次检查语法。