如何在antlr3的树语法中捕获令牌列表?

时间:2011-02-12 13:36:20

标签: antlr antlr3

我拿了一个虚拟语言,例如: 它只接受一个或多个'!'。 它的词法和规则是:

grammar Ns;

options {
  output=AST;
  ASTLabelType=CommonTree;
}
tokens {
  NOTS;
}

@header { 
  package test;
}
@lexer::header {
  package test;
}

ns : NOT+ EOF -> ^(NOTS NOT+);

NOT : '!';

好的,正如您所看到的,这代表了一种接受'!'的语言。要么 '!!!'或'!!!!!'......

我定义了一些有意义的类来构建AST:

public class Not {
    public static final Not SINGLETON = new Not();

    private Not() {
    }
}



public class Ns {
    private List<Not> nots;

    public Ns(String nots) {
        this.nots = new ArrayList<Not>();
        for (int i = 0; i < nots.length(); i++) {
            this.nots.add(Not.SINGLETON);
        }
    }

    public String toString() {
        String ret = "";
        for (int i = 0; i < this.nots.size(); i++) {
            ret += "!";
        }
        return ret;
    }
}

这是树语法:

tree grammar NsTreeWalker;

options {
  output = AST;
  tokenVocab = Ns;
  ASTLabelType = CommonTree;
}
@header { 
  package test;
}
ns returns [Ns ret] : ^(NOTS n=NOT+) {$ret = new Ns($n.text);};

以及包含一些示例数据的主类代码来测试生成的类:

public class Test {

    public static void main(String[] args) throws Exception {
        ANTLRInputStream input = new ANTLRInputStream(new ByteArrayInputStream("!!!".getBytes("utf-8")));
        NsLexer lexer = new NsLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        NsParser parser = new NsParser(tokens);
        CommonTree root = (CommonTree) parser.ns().getTree();
        NsTreeWalker walker = new NsTreeWalker(new CommonTreeNodeStream(root));
        try {
            NsTreeWalker.ns_return r = walker.ns();
            System.out.println(r.ret);
        } catch (RecognitionException e) {
            e.printStackTrace();
        }
    }
}

但打印的最终输出是'!',而不是期待的'!!!'。 这主要是因为这行代码:

ns returns [Ns ret] : ^(NOTS n=NOT+) {$ret = new Ns($n.text);};

上面的$ n只捕获了一个'!',我不知道如何捕获'!'的所有三个标记,换句话说,一个'!'列表$ n。 有人可以帮忙吗?谢谢!

1 个答案:

答案 0 :(得分:4)

只有一个!被打印的事实是因为你的规则:

ns returns [Ns ret] 
  :  ^(NOTS n=NOT+) {$ret = new Ns($n.text);}
  ;

或多或少被翻译为:

Token n = null
LOOP
  n = match NOT_token
END
return new Ns(n.text)

因此,n.text将始终只是一个!

您需要做的是在列表中收集这些NOT令牌。在ANTLR中,您可以使用+=运算符而不是“单一标记”运算符=创建标记列表。因此,请将ns规则更改为:

ns returns [Ns ret] 
  :  ^(NOTS n+=NOT+) {$ret = new Ns($n);}
  ;

被翻译为:

List n = null
LOOP
  n.add(match NOT_token)
END
return new Ns(n)

请务必更改Ns课程的构造函数,以取代List

public Ns(List nots) {
    this.nots = new ArrayList<Not>();
    for (Object o : nots) {
        this.nots.add(Not.SINGLETON);
    }
}

之后,您的测试类的输出将是:

!!!
祝你好运!