所以我最近问question这个问题并得到了很好的答复。但是,所描述的步骤似乎更像是创建具体语法树的步骤。
LR解析过程中的每个减少对应于内部 解析树中的节点。减少的规则是内部AST 节点,从堆栈弹出的项目对应于子节点 那个内部节点。推送到goto的项目对应于 内部节点,而由shift操作推送的那些对应于 AST的叶子(代币)。
将所有这些放在一起,您可以通过创建一个简单的方法来构建AST 每次进行减少并连接所有内容时,都会有新的内部节点 合适的。 〜克里斯·多德
我知道所采取的步骤暗示了解析树,但我不想显式构建解析树。并且每次减少生成一个节点似乎会导致整个解析树。我认为如果看到某个状态,我只构建一个节点。然而,这似乎无法正常工作。
答案 0 :(得分:1)
您不需要在每次减少上构建节点,而您构建的节点不需要包含每个减少的符号。缩小符号也不需要以与解析相同的顺序出现。
在许多情况下,AST是完整解析树的简化,与上面相对应。
简单的例子,对于表达式语法,使用类似yacc的解析器生成器:
expr: term { $$ = $1; /* see below */ }
| expr '+' term { $$ = new_sum_node($1, $3); }
term: factor { $$ = $1; /* see below */ }
| term '*' factor { $$ = new_product_node($1, $3); }
factor: '(' expr ')' { $$ = $2; /* See below */ }
| ID { $$ = new_variable_node($1); }
| NUMBER { $$ = new_literal_node($1); }
AST构建为非终端的语义值。期望函数new_*_node
返回指定类型的新分配节点。 (这里我们假设有一些机制可以从指针中推断出它是什么类型的节点。例如,可以使用子类型和虚函数。)
在Yacc(和类似的解析器生成器)中,每个符号(终端或非终端)都有一个关联的"值",并且每个生产都有一个相关的操作,在生产减少时执行。生产的动作可以分配"值"非终端的减少。该动作可以利用"值"右侧的组件,因为每个组件都是终端(其值由扫描仪设置)或已经减少的非终端。实际上,这是一个S-attributed grammar。
上述一些减少在AST中根本没有出现。特别是,单位减少(expr:term
和term:factor
)只是通过右侧的AST。类似地,括号缩减term:'(' expr ')'
只是通过expr
的AST,结果括号有效地从AST中消失。 (在所有语言中都不正确;在某些语言中,显然冗余的括号实际上会影响语义,您需要创建一个AST节点来记录事实。)
在Yacc中,如果未指定任何操作,$$ = $1
是默认的减少操作,并且由于大多数单位减少从AST中消除,因此通常会显示它们而不进行减少操作。我将它们明确地用于教学目的;在实践中,你不应该这样做。