【问题标题】:Is it common to have two semantic analysis phases within compiler construction?在编译器构造中有两个语义分析阶段是否很常见?
【发布时间】:2021-02-18 16:34:04
【问题描述】:

我一直在用AST explorer 研究各种语言的语法和 AST 节点。 使用 python,我注意到在解析过程中会发生某种形式的语义分析。例如

x = 2
x = 2

产生以下由 VariableDeclaration 节点和 ExpressionStatement 节点组成的 AST。

所以当解析第一行x = 2 时,它会检查符号表是否存在x,然后注册它并生成VariableDeclaration 节点。然后在解析第二行x = 2的时候,发现x已经定义好了,产生了一个ExpressionStatement节点。

但是,当我尝试使用以下语义不正确的代码时:

2 + "string"

它接受代码,并生成一个ExpressionStatement 节点——即使它在语义上不正确,即int + string,当我尝试使用 python 解释器执行它时会产生错误。

这表明语义分析发生了两次:一次是在解析过程中,另一次是在遍历完整的 AST 时。这个假设正确吗?如果是这样,为什么会这样?在解析过程中做整个语义分析阶段而不是拆分它不是更简单吗?

【问题讨论】:

    标签: parsing abstract-syntax-tree semantic-analysis


    【解决方案1】:

    在任何语义传递中都未检测到语句 2 + "string" 中的语义错误。这是一个运行时错误,当您尝试执行该语句时会报告。如果语句从不执行,则不报错,执行脚本可以看到

        if False:
            2 + "string"
        print("All good!")
    

    将全局变量的第一次使用作为声明解决更多的是一种优化,编译器执行多次优化传递是很常见的。

    总有一种尝试将这些多遍组合在一起的诱惑,但这是一种错误的经济:遍历 AST 的开销相对较低,而且当它只尝试做一件事时,代码会更清晰且更易于维护。将两个不相关的优化启发式交织在一起是糟糕的设计,就像将任何一组不相关的过程交织在一起一样。

    【讨论】:

    • 感谢 rici 的回复。因此,据我了解,解析器中应该进行最少的语义分析以确定最终的 AST,例如x=2 应该是 ExpressionStatement 或 VariableDeclaration 节点。在解析阶段之后,应该遍历 AST 并进行进一步的语义检查以确定诸如 2 +“字符串”之类的东西是否有效?
    • 是的,python 可能是一个不好的例子,因为它的语义检查发生在运行时。但例如 java 解析器也允许 2 + "string" 是有效的。
    • @tom:有些语言的运行时值具有类型信息(如 Python 和 JavaScript),使得运行时检查成为可能(甚至是必要的)。并且有些语言(如 C 和 C++)的类型信息是严格在编译时进行的,因此编译器必须完全推断出每个表达式的类型。并且有像 Java 这样的语言,每种语言都有一些。很难比较这些模型,因为它们的做事方式非常不同。此外,“语义信息”涵盖了很多领域。编译器是否应该在可能的情况下尝试捕捉被零除?有些会,很多不会。
    • 对我来说,变量作用域是语法,而不是语义,因为在运行时发生的任何事情都不会改变名称的作用域或绑定(除了具有动态作用域的 Common Lisp 和 Perl 等语言。Yuk .) 类型分析属于同一领域,除了带有鸭子类型的语言,因此语法/语义边界更加流畅。我不认为讨论很有成果。编译器可以找出一些东西,并且在分析可以节省运行时间的范围内,这可能值得尝试。如何完成是内部设计决策。
    • 谢谢@rici
    猜你喜欢
    • 2013-09-23
    • 1970-01-01
    • 1970-01-01
    • 2015-12-30
    • 1970-01-01
    • 2022-12-15
    • 2012-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多