Antlr树模式与重写规则匹配

时间:2011-03-08 17:31:20

标签: pattern-matching antlr antlr3

我对像a.b.c + d.e.f这样的表达式有一个简单的antlr语法:

grammar Test;
options {
    output=AST;
}
tokens {
    VARIABLE_ID;
    QUALIFIER_ID;
}

ID  : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
DOT : '.';
WS : ( ' ' | '\t' | '\r' | '\n' ) {$channel=HIDDEN;} ;

variable_id  : id=ID   -> VARIABLE_ID[$id];
qualifier_id  : id=ID   -> QUALIFIER_ID[$id];

expr_start : expr EOF;
expr : var (options {greedy=true;} : '+' expr)*;

var : variable_id (DOT qualifier_id)*;

现在我想在这个语法上定义一个模式匹配器,将a.b.c变成0.1.2,所以我定义了一个树模式匹配器,如下所示

tree grammar TestWalker;
options {
    tokenVocab=Test;
    ASTLabelType=CommonTree;
    filter=true;
    backtrack=true;
}

@members {
    TokenRewriteStream tokens;

    public void setTreeNodeStream(TreeNodeStream input) {
        super.setTreeNodeStream(input);
        tokens = (TokenRewriteStream)input.getTokenStream(); 
    }
}

topdown : var;

variable_id [int i] : id=VARIABLE_ID {
    tokens.replace($id.getToken(), "" + $i);
};

qualifier_id [int i] : id=QUALIFIER_ID {
    tokens.replace($id.getToken(), "" + $i);
};

var 
@init { int index = 0; }
: variable_id[index] 
(   DOT 
    { ++index; }
    qualifier_id[index]
)*;

然后我整理了一个小测试程序:

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;

public class Main {
    public static void main(String[] args) throws Exception {
        TestLexer lex = new TestLexer(new ANTLRInputStream(System.in));
        TokenStream tokens = new TokenRewriteStream(lex);

        TestParser parser = new TestParser(tokens);
        TestParser.expr_return expr = parser.expr();

        CommonTreeNodeStream nodes = new CommonTreeNodeStream((Tree)expr.getTree());
        nodes.setTokenStream(tokens);
        TestWalker walker = new TestWalker(nodes);
        walker.downup(expr.getTree());
        System.out.println(tokens.toString());
    }
}

当我用基本输入运行这个程序时,我看到了令人惊讶的结果:
a.b.c - > 0.b.c
a.b + d.e - > 0.b + 0.e
等等。似乎我的规则的(DOT qualifier_id)*部分从未匹配,我无法弄清楚原因。我已经尝试将我的规则添加到树模式匹配的自上而下和自下而上的部分。如果我从过滤器匹配器切换到整个树匹配器并将规则添加到'+'情况下的分支适当地工作,但是当重写只是一个更大的语法的较小片段时,这变得站不住脚。任何指针都将非常感激。

更新:使用antlr 3.3

2 个答案:

答案 0 :(得分:2)

问题的关键(由@Bart确定)是解析没有正确生成AST。语法需要构建AST(注意附加^标记)。

grammar Test;
options {
    output=AST;
}
tokens {
    VARIABLE_ID;
    QUALIFIER_ID;
}

ID  : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
DOT : '.';
WS : ( ' ' | '\t' | '\r' | '\n' ) {$channel=HIDDEN;} ;

variable_id  : id=ID   -> VARIABLE_ID[$id];
qualifier_id  : id=ID   -> QUALIFIER_ID[$id];

expr_start : expr EOF;
expr : var (options {greedy=true;} : '+'^ expr)*;

var : variable_id (DOT^ qualifier_id)*;

然后树模式匹配器需要在构建时遍历AST。请注意expr规则的结构和用于处理状态的参数。

tree grammar TestWalker;
options {
    tokenVocab=Test;
    ASTLabelType=CommonTree;
    filter=true;
    backtrack=true;
}

@members {
    TokenRewriteStream tokens;

    public void setTreeNodeStream(TreeNodeStream input) {
        super.setTreeNodeStream(input);
        tokens = (TokenRewriteStream)input.getTokenStream(); 
    }
}

topdown : expr[0];

variable_id returns [int r] : id=VARIABLE_ID {
    $r = 0;
    tokens.replace($id.getToken(), "" + $r);
};

qualifier_id [int i] returns [int r] : id=QUALIFIER_ID {
    $r = $i + 1;
    tokens.replace($id.getToken(), "" + $r);
};

expr [int i] returns [int r]
    : v=variable_id { $r = $v.r; }
    | ^(DOT e=expr[$i] q=qualifier_id[$e.r] { $r = $q.r; } )
    ;

现在输出将按预期运行:
a.b + d.e + c.d.e.f
0.1 + 0.1 + 0.1.2.3
并且生成的AST看起来是正确的。

output AST

答案 1 :(得分:1)

我不熟悉这种(相对较新的)模式树行走。我在Wiki上浏览过它,但它不在我的ANTLR参考文件中。

的东西不是100%的测试语法:当我从中生成解析器时,我得到:

java -cp antlr-3.2.jar org.antlr.Tool Test.g
warning(200): Test.g:18:46: Decision can match input such as "'+'" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input

当我想象AST你的解析器使用你的类生成时(添加一小部分):

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;

public class Main {
    public static void main(String[] args) throws Exception {
        TestLexer lex = new TestLexer(new ANTLRStringStream("a.b + d.e"));
        TokenStream tokens = new TokenRewriteStream(lex);

        TestParser parser = new TestParser(tokens);
        TestParser.expr_return expr = parser.expr();

        CommonTreeNodeStream nodes = new CommonTreeNodeStream((Tree)expr.getTree());
        nodes.setTokenStream(tokens);

        CommonTree tree = (CommonTree)expr.getTree();

        DOTTreeGenerator gen = new DOTTreeGenerator();
        StringTemplate st = gen.toDOT(tree);
        System.out.println(st);

        TestWalker walker = new TestWalker(nodes);
        walker.downup(tree);
        System.out.println(tokens.toString());
    }
}

我看到输入a.b + d.e产生AST:

enter image description here

我想你的树步行者正在遍历所述树,这并不让我感到意外,因为它只是一个单根的节点的平面列表,所以不会产生预期的结果。