您的问题谈到了构建 AST 节点,但您的解释正文显然谈到了符号表。这些想法不一样!AST 代表程序的结构。符号表表示关于哪些名称在哪里可见以及它们具有哪些类型的推断。
按照您的符号表焦点,您在“进入”块时推送当前范围并在“退出”时弹出它的概念在概念上是正确的,因为它抽象地实现了每个块的新范围。
我不认为你可以让 YACC 做你所说的,因为我不确定你可以在语法规则的任何点附加语义动作。我相信您只能将操作附加到整个规则,并且该操作仅在规则被识别(“减少”)时才会运行。所以如果你真的想这样做,你会想改变语法来创造插入语义动作的机会。您可以通过重写规则来做到这一点(按照您的风格,我认为这实际上不是有效的 YACC 语法):
compound_statement : block_start statement_list block_end ;
block_start = '{' pushScope() ;
block_end = '}' popScope();
我添加了动作来对称地阻止开始和结束,但你可以多一点,嗯,吝啬(咧嘴笑):
compound_statement : block_start statement_list '}' popScope() ;
block_start = '{' pushScope() ;
这里真正的秘密是在您进入区块后通过在原始规则中添加子规则来创建缩减/语义操作执行机会。我经常使用空规则来做到这一点:
compound_statement : '{' compound_statement_sub_rule block_start statement_list '}' popScope() ;
compound_statement_sub_rule = pushScope() ;
已经展示了如何做到这一点,我认为你根本不想这样做。您正在做的是将语义与解析过程纠缠不清。如果你走这条路,你会发现自己用复杂的操作来装饰语法的其余部分,以创建/查找标识符。通常最好使用语义动作来简单地构建语法树,然后在解析完成后,遍历语法树来实现符号表构建/标识符查找。
我会去办公时间问你能想到的尽可能多的问题,不管你是否认为他们是愚蠢的。它会得到丰厚的回报。