上下文
最近,我提出了一个问题,我无法在我正在编写的解析器中自己解决。
这个解析器是我正在构建的编译器中的一个组件,问题是编程语言解析所必需的表达式解析。
我的解析器使用递归下降来解析表达式。
问题所在
我使用普通的正则语言解析规则解析表达式,消除了所有规则中的左递归,但是有一个语法“歧义”是我的解析器无法处理的,它涉及泛型。
comparison → addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ;
是用于解析表达式中的比较节点的规则。
另一方面,我决定以这种方式解析泛型表达式:
generic → primary ( "<" arguments ">" ) ;
哪里
arguments → expression ( "," expression )* ;
现在,由于泛型表达式具有更高的优先级,因为它们是语言结构而不是数学表达式,这会导致泛型解析器在不应该解析表达式的情况下尝试解析表达式。
例如,在a<2
中,它将"a“解析为标识符类型的主要元素,然后立即找到泛型类型的语法,然后解析该语法,然后失败,因为它找不到结束标记。
这种情况的解决办法是什么?特别是在像C++这样的语言中,泛型也可以有表达式,如果我没有弄错的话,arr<1<2>
可能是合法的语法。
这是一种特殊的边缘情况,还是需要修改我不知道的语法定义?
谢谢
发布于 2018-04-28 15:39:03
例如,在a<2中,它会将"a“解析为标识符类型的一个主要元素,然后自动找到泛型类型的语法,然后解析它,然后失败,因为它找不到结束标记。
这个特殊的情况可以通过回溯或无限制的前瞻来解决。正如您所说的,解析器在将其解释为泛型时最终会失败,因此,当这种情况发生时,您可以返回并将其解析为关系运算符。展望变体是在看到<
时向前看,以检查<
后面是否有逗号分隔的类型名称和>
,并且只有在这种情况下才进入泛型规则。
然而,如果这两种解释在语法上都是有效的(这意味着语法实际上是模棱两可的),那么这种方法就不再有效。这方面的一个例子是x<y>z
,它既可以是类型为x<y>
的变量z
的声明,也可以是两个比较。这个例子有点不成问题,因为后者的意思几乎不是预期的意思,所以总是将其解释为前者是可以的(例如,在C#中就是这样)。
现在,如果我们允许表达,它会变得更复杂。对于x<y>z
来说,可以很容易地说,这永远不应该被解释为两个比较,因为将比较的结果与其他东西进行比较是没有意义的(在许多语言中,在Booleans上使用关系运算符都是一个类型错误)。但是,对于像a<b<c>()
这样的函数,有两种解释可能都是有效的:要么a
是用泛型参数b<c
调用的泛型函数,要么是带有泛型参数c
的泛型函数( a
与调用该函数的结果相比较)。在这一点上,仅靠句法规则就不可能解决这种模糊性:
为了支持这一点,您需要检查给定的primary
是否引用泛型函数,并在此基础上做出不同的解析决策,或者让解析器在出现歧义的情况下生成多棵树,然后在稍后阶段选择正确的树。前一个选项意味着解析器需要跟踪当前定义的泛型函数(以及作用域中的泛型函数),然后只有当给定的primary
是其中一个函数的名称时,才进入泛型规则。请注意,如果允许在使用函数后定义函数,则这将变得更加复杂。
因此,总之,将表达式支持为泛型参数需要在解析时跟踪哪些函数的作用域,并使用这些信息来做出解析决策(意味着解析器对上下文敏感)或生成多个可能的AST。没有表达式,您可以保持上下文的自由和明确,但需要回溯或任意向前看(这意味着它是LL(*)
)。
由于这两种语言都不理想,一些语言改变了使用显式类型参数调用泛型函数的语法,使其成为LL(1)。例如:
obj.<T>foo()
而不是obj.foo<T>()
。::
:foo::<T>()
而不是foo<T>()
。foo[T]()
而不是foo<T>()
。https://stackoverflow.com/questions/50077588
复制相似问题