Parsing 编译器构造中有两个语义分析阶段是常见的吗?

Parsing 编译器构造中有两个语义分析阶段是常见的吗?,parsing,abstract-syntax-tree,semantic-analysis,Parsing,Abstract Syntax Tree,Semantic Analysis,我一直在研究各种语言的语法和AST节点。 对于python,我注意到在解析过程中发生了某种形式的语义分析。例如 x = 2 x = 2 生成以下AST,该AST由变量声明节点和表达式声明节点组成 因此,当解析第一行x=2时,它会检查符号表中是否存在x,然后注册它并生成VariableDeclaration节点。然后,当第二行x=2被解析时,它发现x已经被定义,并生成一个ExpressionStatement节点 但是,当我尝试使用以下语义不正确的代码时: 2 + "string&q

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

x = 2
x = 2
生成以下AST,该AST由
变量声明
节点和
表达式声明
节点组成

因此,当解析第一行
x=2
时,它会检查符号表中是否存在
x
,然后注册它并生成
VariableDeclaration
节点。然后,当第二行
x=2
被解析时,它发现
x
已经被定义,并生成一个
ExpressionStatement
节点

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

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


这对我来说意味着语义分析会发生两次:一次是在解析过程中,另一次是在遍历整个AST时。这个假设正确吗?如果是,为什么会这样?在解析过程中执行整个语义分析阶段而不是将其拆分不是更简单吗?

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

如果为False:
2+“字符串”
打印(“一切都好!”)
解决第一次使用全局变量作为声明更像是一种优化,编译器通常会执行多个优化过程


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

感谢rici的响应。因此,据我所知,语法分析器中应该进行最少量的语义分析,以确定最终的AST,例如,x=2应该是ExpressionStatement还是VariableDeclaration节点。在解析阶段之后,应该遍历AST并进行进一步的语义检查,以确定2+“字符串”是否有效?是的,python可能是一个坏例子,因为它在运行时进行语义检查。但是,例如java解析器也允许2+“字符串”是有效的。@tom:有些语言的运行时值包含类型信息(如Python和JavaScript),这使得运行时检查成为可能(甚至是必要的)。有些语言(如C和C++)的类型信息是严格的编译时信息,因此编译器必须完全推断每个表达式的类型。还有像Java这样的语言,每种语言都有一点相似之处。很难比较这些模型,因为它们做的事情非常不同。此外,“语义信息”涵盖了很多领域。在可能的情况下,编译器是否应该尝试捕捉除法为零?对我来说,变量作用域是语法上的,而不是语义上的,因为在运行时发生的任何事情都不会改变名称的作用域或绑定(除了像Common Lisp和Perl这样具有dynamic scope.Yuk的语言)。类型分析在同一个一般领域,除了具有duck类型的语言,因此,语法/语义边界更具流动性。我认为讨论没有多大成果。编译器可以找出一些东西,在某种程度上,分析可以节省运行时,这可能是值得尝试的。如何做到这一点是内部设计决策。谢谢@rici