ANTLR - 树结构中的孙子节点

时间:2012-12-11 02:10:18

标签: antlr grammar declarative

我试图写一个声明性语法,其中声明和其他语句的顺序并不重要。但是,对于解析,我希望语法以有序的方式输出树。我们假设语言包含声明(decl)和赋值(assign)。一个例子可能是:

decl x
assign y 2
assign x 1
decl y

我想让一个树所代表的程序包含一个子树中的所有声明,以及另一个子树中的所有赋值。对于上面的示例,例如:

(PROGRAM
    (DECLARATIONS x y)
    (ASSIGNMENTS
        (y 2)
        (x 1)))

我可以在树构造期间执行此重新排列,还是应该编写树语法?

2 个答案:

答案 0 :(得分:3)

我认为答案比其他答案更简单:

token { DECLS; ASSIGNS; }

prog: (d+=decl | a+=assign)* EOF -> ^(DECLS $d*) ^(ASSIGNS $a*) ;

...

当然,您可以根据自己的喜好调整适用的规则。

但是,你确定需要这样做吗?为什么不在解析器中构建DECL指令的符号表,然后只构建一个ASSIGNs的AST,你可以在树中查看。

吉姆

答案 1 :(得分:2)

  

我可以在树构造期间执行此重新排列,还是应该编写树语法?

可以做任何一种,但我建议在令牌解析期间对节点进行分组。

我对编写组节点的任何树重写语法都不满意,因为这些语法必须重新发现每个可分组节点所在的位置 - 因此需要进行分组。令牌解析器在常规处理期间触及所有数据,并且树语法最终在树中为这些节点行走,就像令牌解析器已经将其输入用于令牌一样。如果 用于分组,我认为树解析器不值得麻烦。

无论如何,在解析器中管理分组归结为在生成它们之后保存declassign个节点,然后在它们的分组级别发生时再次将它们推出。这是一个简单的例子。

Declarative.g

grammar Declarative;

options { 
    output = AST;
}

tokens { 
    PROGRAM; DECLARATIONS; ASSIGNMENTS;
} 

@parser::header { 
    import java.util.ArrayList;
}

@members {
    private ArrayList<Object> assigns = new ArrayList<Object>(); 
    private ArrayList<Object> decls = new ArrayList<Object>();

    private Object createTree(int ttype,  ArrayList<Object> children) {
        Object tree = adaptor.create(ttype, tokenNames[ttype]);
        for (Object child : children){
            adaptor.addChild(tree, child);
        }

        return tree; 
    }
}

compilationUnit     : statement* EOF -> ^(PROGRAM {createTree(DECLARATIONS, decls)} {createTree(ASSIGNMENTS, assigns)}); 

statement           : decl {decls.add($decl.tree);}
                    | assign {assigns.add($assign.tree);}
                    ;

decl                : DECL^ ID;
assign              : ASSIGN^ ID INT;

DECL    : 'decl';
ASSIGN  : 'assign';
ID      : ('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z')*;
INT     : ('0'..'9')+;                  
WS      : (' '|'\t'|'\f'|'\n'|'\r'){skip();};

每个decl节点都由statement列表中的decls规则保存,同样适用于每个assign节点。

方法createTree使用解析器的TreeAdaptor来构建组节点并填充它们。

CommonTree tree = (CommonTree) adaptor.create(ttype, tokenNames[ttype]);
for (Object child : children){
    adaptor.addChild(tree, child);
}

return tree; 

compilationUnit的制作是^(PROGRAM {createTree(DECLARATIONS, decls)} {createTree(ASSIGNMENTS, assigns)}),它将分组节点添加到PROGRAM。方法createTree用于一次构建分组节点及其子节点。

可能有一种棘手的方法让ANTLR为你拉出所有这些,但这是有效的并且是相当不言自明的。

所以给出了这个输入......

decl x
assign y 2
assign x 1
decl y

...为上面的语法生成的令牌解析器生成此树作为输出:

(PROGRAM 
    (DECLARATIONS 
        (decl x) 
        (decl y)) 
    (ASSIGNMENTS 
        (assign y 2) 
        (assign x 1)))