如何合并两个AST?

时间:2012-10-15 08:49:55

标签: java parsing antlr abstract-syntax-tree lexer

我正在尝试实现一个合并某些源代码的不同版本的工具。给定相同源代码的两个版本,想法是解析它们,生成相应的抽象源树(AST),最后将它们合并到单个输出源中,保持语法一致性 - 词法分析器和解析器是问题{{ 3}}

我知道有ParserRuleReturnScope课有帮助......但getStop()getStart()始终返回null: - (

这是一个片段,说明了我如何修改我的perser来打印规则:

parser grammar CodeTableParser;

options {
    tokenVocab = CodeTableLexer;
    backtrack = true;
    output = AST;
}

@header {
    package ch.bsource.ice.parsers;
}

@members {
    private void log(ParserRuleReturnScope rule) {
        System.out.println("Rule: " + rule.getClass().getName());
        System.out.println("    getStart(): " + rule.getStart());
        System.out.println("    getStop(): " + rule.getStop());
        System.out.println("    getTree(): " + rule.getTree());
    }
}

parse
    : codeTabHeader codeTable endCodeTable eof { log(retval); }
    ;

codeTabHeader
    : comment CodeTabHeader^ { log(retval); }
    ;

...

2 个答案:

答案 0 :(得分:1)

假设您拥有 AST(通常难以获得,解析真实语言通常比看起来更难),首先必须确定它们的共同点,并构建一个收集该信息的映射。这并不像看起来那么容易;你算上一块已经移动的代码块,但它是否与“普通”一样是完全相同的子树?除了标识符的一致重命名之外,两个相同的子树怎么样?更改评论怎么样? (大多数AST会丢失评论;大多数程序员会认为这是一个非常糟糕的主意。)

您可以构建“最长公共子串”算法的变体来比较树。我在我构建的工具中使用过它。

最后,在合并树之后,现在需要重新生成文本,理想情况下保留原始代码的大部分布局。 (当你改变他们如此喜爱的布局时,程序员讨厌)。因此,您的AST需要捕获位置信息,并且您的再生必须在可能的地方兑现。

答案 1 :(得分:0)

在解析器代码中对log(retval)的调用看起来会在规则结束时发生,但事实并非如此。您需要将调用移至@after块。

我更改log以发出消息以及范围信息,并将调用添加到我自己的语法中,如下所示:

script    
    @init {log("@init", retval);}
    @after {log("@after", retval);}
    : statement* EOF  {log("after last rule reference", retval);} 
        -> ^(STMTS statement*) 
    ;

解析测试输入产生以下输出:

Logging from @init
    getStart(): [@0,0:4='Print',<10>,1:0]
    getStop(): null
    getTree(): null
Logging from after last rule reference
    getStart(): [@0,0:4='Print',<10>,1:0]
    getStop(): null
    getTree(): null
Logging from @after
    getStart(): [@0,0:4='Print',<10>,1:0]
    getStop(): [@4,15:15='<EOF>',<-1>,1:15]
    getTree(): STMTS

after块中的调用同时填充了stoptree字段。

我不能说这是否会对你的合并工具有所帮助,但我认为这至少会让你解决半填充范围对象的问题。