【问题标题】:How to merge two ASTs?如何合并两个 AST?
【发布时间】:2012-10-15 08:49:54
【问题描述】:

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

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

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

...

【问题讨论】:

  • 我认为您需要更多信息才能合并。就像一个共同的祖先版本,或者至少是被删除、添加或修改的版本。
  • 也许稍后......但现在该工具应该只是:1)保持公共部分原样; 2) 从最新的源版本中进行修改; 3) 保留两个源版本的非通用部分。就是这样。
  • 查看我帖子的更新了解更多详情。

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


【解决方案1】:

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

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

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

【讨论】:

  • 是的...这或多或少是我所做的...无论如何,非常感谢您提供有趣且有用的 cmets :-)
  • 我对你如何实际解决合并树的问题很感兴趣——我遇到了一些麻烦。你是如何实现 LCS 来比较树的?得到 commont 部分后,你是如何将 deltas 应用到它的?
  • 我们使用后缀树。您可以在ieeexplore.ieee.org/iel5/4023959/4023960/04023995.pdf 中了解它们如何用于查找克隆(例如,“通用”)代码,从而获得匹配的常量子树。你需要在剩下的东西上建立某种统一器,这是相当大胆的。
【解决方案2】:

在您的解析器代码中对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 块中的调用同时填充了 stop 和 tree 字段。

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

【讨论】:

  • 谢谢 :-) 现在 ParserRuleReturnScope 字段已填充。关于合并源代码...好吧,我只是在研究如何实现它...无论如何,再次感谢您的大力支持!
  • @j3d 没问题,我很高兴能帮上忙。
猜你喜欢
  • 1970-01-01
  • 2020-11-18
  • 2015-11-07
  • 2022-01-07
  • 2015-06-04
  • 1970-01-01
  • 1970-01-01
  • 2022-11-08
  • 1970-01-01
相关资源
最近更新 更多