我们有很多编程语言。在将每种语言转换为代码之前,都要对每种语言进行解析和语法检查,以便构建抽象语法树(AST)。
我们有这个抽象的语法树,为什么不存储这个语法树而不是源代码(或在源代码旁边)呢?
通过使用AST而不是源代码。团队中的每个程序员都可以将此树序列化为他们想要的任何语言(具有适当的上下文无关语法),并在完成后解析回AST。因此,这将消除有关编码样式问题(在何处放置{和},在何处放置空格,缩进等)的争论。
这种方法的优缺点是什么?
我们有很多编程语言。在将每种语言转换为代码之前,都要对每种语言进行解析和语法检查,以便构建抽象语法树(AST)。
我们有这个抽象的语法树,为什么不存储这个语法树而不是源代码(或在源代码旁边)呢?
通过使用AST而不是源代码。团队中的每个程序员都可以将此树序列化为他们想要的任何语言(具有适当的上下文无关语法),并在完成后解析回AST。因此,这将消除有关编码样式问题(在何处放置{和},在何处放置空格,缩进等)的争论。
这种方法的优缺点是什么?
Answers:
通常,AST不包括空格,行终止符和注释。
您是正确的,在大多数情况下,这是肯定的(消除格式化圣战),在许多情况下,原始代码的格式传达了某些含义,例如在多行字符串文字和“代码段”中(空行的语句)。
尽管许多解析器可以很好地抵御缺少的语法,但是带有错误的代码通常会导致语法树非常怪异,在用户重新加载文件之前,这种语法树是很好且花哨的。是否曾经在IDE中犯了一个错误,然后突然整个文件都出现了“弯曲”?想象一下如何用另一种语言重新加载。
也许用户不会提交无法解析的代码,但是他们确实确实需要保存在本地。
正如其他人指出的那样,几乎没有两种语言具有完美的功能奇偶性。我能想到的最接近的是VB和C#,或者JavaScript和CoffeeScript,但即使到那时,VB仍具有XML文字等功能,这些功能在C#中并不完全等效,并且从JavaScript到CoffeeScript的转换可能会导致很多JavaScript文字。
个人经验:在我编写的软件应用程序中,实际上我们需要这样做,因为期望用户输入“纯英语”表达式,这些表达式会在后台转换为JS。我们考虑只存储JS版本,但发现几乎没有可接受的方法可以可靠地加载和卸载,因此我们最终总是存储用户文本和JS版本,以及一个标志,指示“ ”版本是否完美解析。
我们为什么不存储此语法树而不是源代码?团队中的每个程序员都可以将此树序列化为他们想要的任何语言,并在完成后解析回AST。
确实,这是一个合理的想法。微软在1990年代有一个研究项目几乎可以做到这一点。
我想到了几种情况。
首先是微不足道的;如您所说,您可以根据不同程序员对间距等事物的偏好,将AST渲染为不同的视图。但是,在这种情况下,存储AST会显得过大。只是给自己写一个漂亮的打印机。将文件加载到编辑器中时,请运行漂亮打印机将其设置为首选格式,然后在保存时恢复为原始格式。
第二个更有趣。如果您可以存储抽象语法树,则对代码进行差异化更改就不会变成文本,而是语法。重构代码的位置变得更加容易理解。不利的一面当然是,编写树差异算法并不完全是琐碎的事,通常必须根据每种语言来完成。文本差异适用于几乎所有语言。
第三个更像西蒙尼(Simonyi)为有意编程所设想的那样:编程语言共有的基本概念是序列化的,然后您对以不同语言呈现的那些概念有不同的看法。尽管这是一个美丽的主意,但丑陋的事实是语言的细节差异很大,以至于最低分母方法实际上是行不通的。
简而言之,这是一个不错的主意,但这是大量的额外工作,但收益却相对较小。这就是为什么几乎没有人这样做。
我有点喜欢您的一些想法,但是您大大高估了将语言翻译成语言的难易程度。如果那样简单,您甚至不需要存储AST,因为您总是可以将语言X解析为AST,然后从AST转换为语言Y。
但是,我希望编译器规范对通过某种API公开一些AST进行更多的思考。诸如面向方面的编程,重构和静态程序分析之类的事情可以通过这样的API来实现,而那些功能的实现者不必重做编译器编写者已经完成的大量工作。
奇怪的是,程序员表示一个程序的数据结构经常是一堆包含字符串的文件。
我认为最突出的要点是:
没有好处。您说过,这意味着每个人都可以使用他们的宠物语言。但是,事实并非如此 -使用语法树表示只会消除语法差异,而不会消除语义差异。它在某种程度上适用于非常相似的语言,例如VB和C#或Java和Scala。但是,甚至没有完全。
这是有问题的。您已经获得了语言自由,但是却失去了工具自由。您不再可以在文本编辑器甚至任何IDE中读取和编辑代码,而只能依靠能说AST表示的特定工具来读取和编辑代码。这里什么都没有得到。
为了说明最后一点,请看一下RealBasic,它是功能强大的BASIC方言的专有实现。有一时间,它似乎看起来像是一种语言,但它完全取决于供应商,以至于您只能在他们的IDE中查看代码,因为它是以专有的非文本格式保存的。大错
astyle
或UnniversalIndent)来执行此操作。不需要神秘的二进制格式。
我认为这个想法在理论上很有趣,但不是很实用,因为不同的编程语言支持不同的构造,其中一些在其他语言中没有等效的构造。
例如,X ++有一个'while select'语句,如果没有很多额外的代码(额外的类,额外的逻辑等),就无法用C#编写该语句。http://msdn.microsoft.com/en-us/library/aa558063.aspx
我在这里要说的是,许多语言都有语法糖,它们可以翻译成相同语言的大块代码,甚至翻译成其他语言根本不存在的元素。这是为什么AST方法不起作用的示例:
语言X的关键字K在AST中用以下4种语句翻译:S1,S2,S3和S4。AST现在被翻译成语言Y,并且程序员更改了S2。现在,转换回X会发生什么?该代码被翻译为4条语句,而不是单个关键字。
反对AST方法的最后一个争论是平台功能:将功能嵌入平台后会发生什么?就像.NET的Environment.GetEnvironmentVariable一样。您如何翻译?
有一个围绕这个想法构建的系统:JetBrains MPS。编辑器有点奇怪,或者只是有所不同,但是总的来说,这不是一个大问题。最大的问题是,好了,这是不是一个文本的多,所以你不能使用任何正常的基于文本的工具-其他编辑,grep
,sed
,合并和差异工具,等等。
实际上,有几种产品(通常称为“语言工作台”)存储AST,并在其编辑器中将AST的“投影”呈现回特定的语言。正如@ sk-logic所说,JetBrains的MPS就是这样一种系统。另一个是故意软件的故意工作台。
语言工作台的潜力似乎很高,尤其是在特定领域的语言领域,因为您可以创建特定领域的投影。例如,Intental演示了与电力相关的DSL,它以电路图的形式展示-与基于文本的编程语言描述的电路相比,对于领域专家来说,讨论和批评它更容易,更准确。
在实践中,语言工作台一直很慢,因为除DSL工作外,开发人员可能更喜欢使用熟悉的通用编程语言工作。当与文本编辑器或编程IDE进行面对面的比较时,语言工作台会产生大量的开销,并且它们的优势还不太明显。我所见过的语言工作台都没有引导到可以轻松扩展自己的IDE的地步,也就是说,如果语言工作台对提高生产力非常有用,为什么语言工作台工具却没有变得更好越来越好?
你一直在读我的想法。
几年前,当我参加编译器课程时,我发现如果您使用前缀表示法而不是通常的中缀表示法对AST进行序列化并使用括号定界整个语句,则会得到Lisp。虽然我在本科学习中了解了Scheme(一种Lisp的方言),但我从未真正获得过赞赏。通过该课程,我肯定对Lisp及其方言表示赞赏。
您提出的问题:
在图形环境中组成AST很难/很慢。毕竟,我们大多数人的打字速度都比移动鼠标快。然而,一个新出现的问题是“如何用平板电脑编写程序代码?” 与带有硬件键盘的键盘/笔记本电脑相比,在平板电脑上打字速度慢/麻烦。如果您可以通过将组件从调色板拖放到大型画布上来创建AST,那么在平板电脑上进行触摸屏设备编程就可以成为现实。
我们现有的工具中很少/没有支持此功能的。在开发日益复杂的IDE和日益智能的编辑器方面,我们数十年的发展历程不断。我们拥有所有用于重新格式化文本,比较文本,搜索文本的工具。可以在树上进行与正则表达式等效的工具在哪里?还是两棵树的区别?所有这些事情都可以通过文本轻松完成。但是他们只能比较单词。更改变量名,以使单词不同但语义相同,并且这些diff工具会遇到麻烦。开发用于在AST而不是文本上运行的此类工具,将使您更接近比较语义。那将是一件好事。
虽然将程序源代码转换为AST相对容易理解(我们有编译器和解释器,对吗?),但对AST转换为程序代码的理解却不那么了解。将两个质数相乘得到一个大的复合数相对简单,但是将一个大的复合数分解为质数则要困难得多。那就是我们解析与反编译AST的地方。这就是语言之间的差异成为问题的地方。即使在特定语言中,也有多种方法可以反编译AST。例如,遍历对象的集合并获得某种结果。使用for循环,遍历数组?那将是紧凑且快速的,但是存在局限性。使用某种迭代器 在集合上操作?该Collection可以是可变大小的,从而增加了灵活性(但有可能牺牲速度)。映射/缩小?更复杂,但隐式可并行化。这仅适用于Java,具体取决于您的偏好。
随着时间的流逝,开发工作将花费更多,我们将使用触摸屏和AST进行开发。键入将变得不必要。我认为这是从现在到现在的逻辑发展,看看我们如何使用计算机,这将解决第一。
我们已经在与树木合作。Lisp仅仅是序列化的AST。XML(和扩展的HTML)只是一个序列化的树。为了进行搜索,我们已经有几个原型:XPath和CSS(分别用于XML和HTML)。当创建允许我们创建CSS样式的选择器和修饰符的图形工具时,我们将解决部分#2。当这些选择器可以扩展为支持正则表达式时,我们将更加接近。仍在寻找比较两个XML或HTML文档的良好图形差异工具。随着人们开发这些工具,第二个问题将得以解决。人们已经在做这类事情了;他们只是不在那里。
我认为能够将这些AST反编译为编程语言文本的唯一方法是寻求目标。如果我要修改现有代码,则可以通过一种算法来实现该目标,该算法可使修改后的代码尽可能类似于起始代码(最小文本差异)。如果我是从头开始编写代码,则目标可能是最小,最紧密的代码(可能是for循环)。或者它可能是尽可能高效并行化的代码(可能是映射/归约或涉及CSP的某种东西)。因此,基于目标的设置方式,即使使用相同的语言,相同的AST也会导致代码差异很大。开发这样的系统将解决#3。这会导致计算复杂,这意味着我们可能需要某种客户端-服务器的安排,
如果您的目的是消除关于格式样式的争论,那么您可能想要的是一个编辑器,该编辑器读取源文件,将其格式化为您个人喜好的显示和编辑方式,但是保存时,将格式重新设置为团队选择的样式用途。
如果使用像Emacs这样的编辑器,这非常容易。更改整个文件的格式样式是一项三命令工作。
您还应该能够构建挂钩,以在加载时自动将文件转换为您自己的样式,并在保存时将其转换为团队样式。
很难读取和修改AST,而不是源代码。
但是,某些与编译器相关的工具的确允许使用AST。Java字节码和.NET中间代码的工作方式与AST类似。
这是一个好主意;但是每种语言的AST彼此都不相同。
我知道的唯一例外是VB.NET和C#,Microsoft认为它们是“使用不同语法的完全相同的语言”。甚至其他.NET语言(IronPython,F#等)在AST级别上也有所不同。
与JVM语言相同,它们都以相同的字节码为目标,但是语言结构不同,从而使其具有不同的语言和不同的AST。
甚至像CoffeScript和Xtend这样的“薄层”语言也共享了很多基础语言的理论(分别是JavaScript和Java)。但要介绍(或应该)保留在AST级别的更高级别的概念。
如果Xtend可以从Java AST重构,我认为它将被定义为Java到Xtend的“反编译器”,可以从现有Java代码神奇地创建更高级别的抽象,您是不是认为呢?