我正在尝试实现一个合并某些源代码的不同版本的工具。给定相同源代码的两个版本,想法是解析它们,生成相应的抽象源树(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); }
;
...
答案 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
块中的调用同时填充了stop
和tree
字段。
我不能说这是否会对你的合并工具有所帮助,但我认为这至少会让你解决半填充范围对象的问题。