我需要为具有前向引用的语言创建语法。我认为实现这一目标的最简单方法是在生成的AST上进行多次传递,但我需要一种在树中存储符号信息的方法。
现在我的解析器正确生成AST并计算变量和函数定义的范围。问题是,我不知道如何将范围信息保存到树中。
我的语法碎片:
composite_instruction
scope JScope;
@init {
$JScope::symbols = new ArrayList();
$JScope::name = "level "+ $JScope.size();
}
@after {
System.out.println("code block scope " +$JScope::name + " = " + $JScope::symbols);
}
: '{' instruction* '}' -> ^(INSTRUCTION_LIST instruction*)
;
我想将当前范围的引用放入树中,例如:
: '{' instruction* '}' -> ^(INSTRUCTION_LIST instruction* {$JScope::symbols})
甚至可能吗?有没有其他方法可以在生成的树中存储当前作用域?我可以在树语法中生成范围信息,但它不会改变任何东西,因为我仍然需要将它存储在树的第二遍的某处。
答案 0 :(得分:2)
据我所知,重写规则的语法不允许直接分配值作为您的暂定代码段建议。这部分是由于解析器不会真正知道应该将值添加到树/节点的哪个部分。
然而,ANTLR生成的AST的一个很酷的特性是解析器不会对节点的类型做出任何假设。只需要实现一个TreeAdapator,它可以作为新节点的工厂和树结构的导航器。因此,可以填充节点中可能需要的任何信息,如下所述。
ANTLR提供默认的树节点实现, CommonTree ,在大多数情况下(如在当前的情况下),我们只需要
但是也可以创建一种新颖的节点altogher类型,用于某些奇怪的图形结构或诸如此类的东西。对于手头的情况,以下内容应该足够(如果不是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.
最后你的语法必须适应类似于后面的内容 (请注意,[当前规则]的节点仅在@after部分中可用。但是它可以使用通常的$ rule.atrribute表示法从语法级别引用任何令牌属性和其他上下文变量)
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*)
;