【问题标题】:ANTLR forward referencesANTLR 前向引用
【发布时间】:2010-11-18 10:54:05
【问题描述】:

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

有可能吗?有没有其他方法可以将当前范围存储在生成的树中?我可以在树语法中生成范围信息,但它不会改变任何东西,因为我仍然必须将它存储在某个地方以便在树上进行第二次传递。

【问题讨论】:

标签: antlr antlr3


【解决方案1】:

据我所知,重写规则的语法不允许直接像您的暂定 sn-p 建议的那样分配值。这部分是因为解析器并不真正知道应该将值添加到树/节点的哪个部分。

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

ANTLR 提供了一个默认的树节点实现,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.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*)
    ;

【讨论】:

  • 这可能是我一直在寻找的,但我决定在解析期间构建自己的树,然后用范围填充它并执行其他检查。谢谢!
猜你喜欢
  • 2010-10-16
  • 2017-01-20
  • 2020-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多