ANTLR前向参考

时间:2010-11-17 23:32:51

标签: antlr antlr3

我需要为具有前向引用的语言创建语法。我认为实现这一目标的最简单方法是在生成的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})

甚至可能吗?有没有其他方法可以在生成的树中存储当前作用域?我可以在树语法中生成范围信息,但它不会改变任何东西,因为我仍然需要将它存储在树的第二遍的某处。

1 个答案:

答案 0 :(得分:2)

据我所知,重写规则的语法不允许直接分配值作为您的暂定代码段建议。这部分是由于解析器不会真正知道应该将值添加到树/节点的哪个部分。

然而,ANTLR生成的AST的一个很酷的特性是解析器不会对节点的类型做出任何假设。只需要实现一个TreeAdapator,它可以作为新节点的工厂和树结构的导航器。因此,可以填充节点中可能需要的任何信息,如下所述。

ANTLR提供默认的树节点实现, CommonTree ,在大多数情况下(如在当前的情况下),我们只需要

  • 通过向其添加一些自定义字段来为CommonTree创建子类
  • 将CommonTreeAdaptor子类化为覆盖其create()方法,即它生成新节点的方式。

但是也可以创建一种新颖的节点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*)
    ;