据我所知,重写规则的语法不允许像您的暂定代码段所建议的那样直接分配值。这部分是因为解析器并不真正知道应该将值添加到树/节点的哪个部分。
然而,ANTLR 生成的 AST 的一个很酷的特性是解析器不对节点的类型做任何假设。只需要实现一个 TreeAdapator,它作为新节点的工厂和树结构的导航器。因此,可以在节点中填充可能需要的任何信息,如下所述。
ANTLR 提供了一个默认的树节点实现CommonTree,并且在大多数情况下(如手头的情况)我们只需要
- 通过向 CommonTree 添加一些自定义字段来子类化 CommonTree
- 子类化 CommonTreeAdaptor 以覆盖它的 create() 方法,即它产生新节点的方式。
但也可以为一些奇怪的图结构或诸如此类的东西创建一种新颖的节点类型。对于手头的情况,以下内容就足够了(如果这不是java,请适应特定的目标语言)
import org.antlr.runtime.tree.*;
import org.antlr.runtime.Token;
public class NodeWithScope extends CommonTree {
/* Just declare the extra fields for the node */
public ArrayList symbols;
public string name;
public object whatever_else;
public NodeWithScope (Token t) {
super(t);
}
}
/* TreeAdaptor: we just need to override create method */
class NodeWithScopeAdaptor extends CommonTreeAdaptor {
public Object create(Token standardPayload) {
return new NodeWithScope(standardPayload);
}
}
然后需要稍微修改解析过程的启动方式,以便 ANTLR(或者更确切地说是 ANTLR 生成的解析器)知道使用 NodeWithScopeAdaptor 而不是 CommnTree。
(下面的步骤 4.1,其余的如果是标准的 ANTLR 测试台)
// ***** Typical ANTLR pipe rig *****
// ** 1. input stream
ANTLRInputStream input = new ANTLRInputStream(my_input_file);
// ** 2, Lexer
MyGrammarLexer lexer = new MyGrammarLexer(input);
// ** 3. token stream produced by lexer
CommonTokenStream tokens = new CommonTokenStream(lexer);
// ** 4. Parser
MyGrammarParser parser = new MyGrammarParser(tokens);
// 4.1 !!! Specify the TreeAdapter
NodeWithScopeAdaptor adaptor = new NodeWithScopeAdaptor();
parser.setTreeAdaptor(adaptor); // use my adaptor
// ** 5. Start process by invoking the root rule
r = parser.MyTopRule();
// ** 6. AST tree
NodeWithScope t = (NodeWithScope)r.getTree();
// ** 7. etc. parse the tree or do whatever is needed on it.
最后,您的语法必须使用类似于以下内容的内容进行调整
(请注意,节点 [for the current rule] 仅在 @after 部分中可用。但是它可能会引用语法级别的任何标记属性和其他上下文变量,使用通常的 $rule.attribute 表示法)
composite_instruction
scope JScope;
@init {
$JScope::symbols = new ArrayList();
$JScope::name = "level "+ $JScope.size();
}
@after {
($composite_instruction.tree).symbols = $JScope::symbols;
($composite_instruction.tree).name = $JScope::name;
($composite_instruction.tree).whatever_else
= new myFancyObject($x.Text, $y.line, whatever, blah);
}
: '{' instruction* '}' -> ^(INSTRUCTION_LIST instruction*)
;