(不耐烦的人:点击【这里】可进入 DEMO)
ydiff 是我去年做的一个概念模型,用于试验如何“结构化”的对比两个源程序,由此也引发了一些对版本控制 (version control) 的思考。这里我简要介绍一下。
ydiff 的主要功能是检测代码的修改,包括删除,添加,修改,移动和分解。它跟 Unix 的 diff 程序所要解决的问题相似——对比两个程序,但是它们的方式有非常大的区别。diff 完全不理解程序语言,把程序当成字符串来比较。而 ydiff 像编译器那样对程序进行 parse,然后针对不同的语言进行结构化的比较。ydiff 比起 diff 有如下优点:
- 比较结果与空白和格式无关。所以添加空行,换行,分隔符的位置,都不会影响比较的结果。
- 不进行无关的对比。比如字符串 "10000" 和数字 10000,不会被认为是经过“修改”而得到。
- 理解程序的部分语义。比如它会先比较名字相同的函数,再考虑你是否给函数换了名字。
- 能检测代码的移动和分解。这对于寻找旧代码在新代码中的位置非常有用。
- 友好的输出界面。输出的结果是交互式的,直观的,容易理解。
ydiff 含有我自己写的多种程序语言的 parser(C++, Javascript, Python,Scheme / Emacs Lisp)。虽然这些 parser 都是相当先进的设计,但是这里我要说明的主要问题是:如果程序被直接保存为一种标准化的数据结构,那么这些 parser 就完全没有必要写了!那样的话,这种结构化的比较会非常容易的扩展到支持所有程序语言。这是目前的文本编辑和存储的方式不可能做到的。
我正在设想如何实现结构化的版本控制 (structural version control)。这个内容比较复杂,我以后再讲。有兴趣的话可以暂时参考我之前的一个 talk。
ydiff 是开源软件,主要算法使用 Scheme (Racket) 实现,比较结果的网页界面含有一点 Javascript。由于是概念模型,还没有很友好的运行界面和文档,所以现在只是把生成的一些结果拿出来示意一下。如果有兴趣看代码,可以访问我的 GitHub。
上图是 ydiff 对两个不同版本的 Python 程序生成对比结果之后的界面。
(点击【这里】可进入 DEMO)
图例:
- 白底红框:未修改的内容
- 红色:删除的内容
- 绿色:添加的内容
- 蓝色:修改过的内容(鼠标指针放上去之后显示修改的方式和相似度)
在这个网页里可以进行的基本的操作是:
- 滚动任意一边的程序,另外一边随之滚动并且对齐。
- 点击任意一个白底红框或者蓝色方框,另外一个窗口自动滚动到对应位置
另外的 DEMO:
- C++ DEMO1:D8, Google的 V8 Javascript引擎里的 debugger。这里比较的两个版本,跨域两年的时间间隔。普通的 diff 会输出非常不可读的内容,但是 ydiff 的比较结果可以一目了然的看到在两年里这个程序的变化。比如你会发现 Shell::Initialize 函数被分解成了4个函数: Shell::Initialize, Shell::CreateGlobalTemplate, Shell::RenewEvaluationContext 和 Shell::InstallUtilityScript
- C++ DEMO2:V8 中的两个处理器模拟器(MIPS 和 ARM)对比。这里对比的内容不是同一个程序的两个不同版本,而是两个不同的程序。对比结果可以清晰的显示其中的相似性,所以我觉得在检查程序侵权和作业抄袭方面可能会有用处。
- Javascript:对比 ydiff 自己的浏览器程序的两个版本。
- Emacs Lisp:对比 Taylor Campbell 的 paredit-mode.el 的两个版本。同时推荐 paredit-mode。它是一个非常不错的结构化编辑 Lisp 的 Emacs mode。
- Scheme:对比两个不的 miniKanren 实现。miniKanren 是 Dan Friedman 教授设计的,主要用于教学的逻辑式编程语言。这里比较的是原来的版本和一个我重新写过的版本。
- S-expression:对比我的 Scheme 编译器的一个优化算法生成的两个不同中间结果(IR),用于寻找编译器的 bug。
希望这些例子可以展示数据结构化存储之后可能带来的一些编程环境的变化。