5

我正在尝试实现一个工具来合并某些源代码的不同版本。给定相同源代码的两个版本,我们的想法是解析它们,生成各自的抽象源代码树 (AST),最后将它们合并为一个保持语法一致性的输出源 - 词法分析器和解析器是问题ANTLR:如何跳过多行注释

我知道有类可以帮助...ParserRuleReturnScope但总是返回 null :-(getStop()getStart()

这是一个片段,说明了我如何修改我的 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); }
    ;

...
4

2 回答 2

1

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

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

最后,在合并了树之后,现在您需要重新生成文本,最好保留原始代码的大部分布局。(当你改变他们如此喜爱的布局时,程序员讨厌你)。因此,您的 AST 需要捕获位置信息,而您的再生必须尽可能尊重这一点。

于 2012-11-11T22:16:40.657 回答
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字段。

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

于 2012-10-19T07:53:38.373 回答