我们有很多编程语言。每种语言都会被解析并检查语法,然后再翻译成代码,因此会构建一个抽象语法树(AST)。源代码(或源代码旁边)?

通过使用AST代替源代码。团队中的每个程序员都可以将此树序列化为所需的任何语言(具有适当的上下文无关语法),并在完成后解析回AST。因此,这将消除有关编码样式问题(在何处放置{和},在何处放置空格,缩进等)的争论。 />

评论

Lisp通常写为抽象语法树。它没有赶上更多类似Algol的语言。

我不敢相信大卫是唯一提及LISP程序是抽象语法树的人。

除了其他几点:AST甚至不是最终的决定。用代码创建AST也不需要很长时间。当我在较小的VS2010项目上运行StyleCop时,它会以非常快的速度(有时是一两秒钟)在数千行代码上运行许多基于AST的规则。扩展StyleCop和编写自定义规则也相当容易。我怀疑将源代码解析为AST是一个很好理解的问题,并且是一个相对容易的问题。它首先提出了良好的语言,并进行了优化和所有困难的而不是解析的库。

解析了代码之后,为另一种语言生成代码并不是那么容易。 (您如何将Prolog的隐式统一转换为C?)。通常,您拥有的是原始程序的AST。

解析问题在技术上已广为人知,但是解析C或C ++并不是一件容易的事,因为它们是杂乱的讨厌的语言。许多编译器将C或C ++解析为AST:Clang,GCC等……它们并不是用于程序存储的,而GCC非常想成为编译器,而不是程序分析工具。我们的DMS软件再造工具包解析C和C ++的许多方言,生成AST,符号表和各种流分析工件。这种方法的最大优点是能够构建自动化变更工具。 logicaldesigns.com/Products/DMS/DMSToolkit.html

#1 楼

空格和注释

通常情况下,AST不包含空格,行终止符和注释。

有意义的格式

您是正确的,在大多数情况下这是肯定的(消除格式化格式的圣战),在许多情况下,原始代码的格式传达了某些含义,例如在多行字符串文字和“代码段”中(用空行分隔语句块)。

无法编译的代码

尽管许多解析器对丢失的语法具有很好的适应性,但是带有错误的代码通常会导致语法树非常怪异,既好又花哨直到用户重新加载文件为止。是否曾经在您的IDE中犯了一个错误,然后突然整个文件都出现了“弯曲”?想象一下如何用另一种语言重新加载该代码。

用户可能不会提交无法解析的代码,但他们确实确实需要在本地保存。完美匹配

正如其他人指出的那样,几乎没有两种语言具有完全相同的功能。我能想到的最接近的是VB和C#,或者JavaScript和CoffeeScript,但是即使那样,VB仍具有XML文字之类的功能,而这些文字在C#中并不完全等效,并且从JavaScript到CoffeeScript的转换可能会导致很多JavaScript文字。 br />
个人经验:

在我编写的软件应用程序中,实际上我们需要这样做,因为期望用户输入“普通英语”表达式,并在的背景。我们考虑仅存储JS版本,但是发现几乎没有可接受的方式可以可靠地加载和卸载,因此我们最终总是存储用户文本和JS版本,以及一个标志,指示“ “版本是否完美解析。

评论


有解析器捕获AST中的注释和布局。我们的DMS软件Rengineering工具包可以很好地做到这一点。使用非法代码确实很难。它具有精确的语言解析器。

–伊拉克·巴克斯特
2011年11月11日,下午3:25

实际上,有一个将Javascript转换为CoffeeScript的工具,因此我认为JavaScript和CoffeScript在没有Javascript文字的情况下可以相互翻译。

– Peter Olson
2011年11月11日20:40



有趣的工具,彼得,我没有意识到。

–凯文·麦考密克(Kevin McCormick)
2011年11月14日15:06

+1提供有意义的格式和有趣的个人体验。 -空白对于问题并不重要,可以保留评论。带有错误的代码无论如何都更容易修复,当然问题的“一种语言可以全部统治”这一部分是无法实现的。

– Cregox
15年3月16日在18:17

#2 楼


为什么不存储此语法树而不是源代码?团队中的每个程序员都可以将此树序列化为他们想要的任何语言,并在完成后解析回AST。


实际上,这是一个合理的想法。微软在1990年代有一个研究项目几乎可以做到这一点。

我想到了几种情况。如您所说,您可以根据不同程序员对空格等事物的偏好,将AST渲染为不同的视图。但是,在这种情况下,存储AST会显得过大。只是给自己写一个漂亮的打印机。将文件加载到编辑器中时,运行漂亮打印机将其设置为首选格式,然后在保存文件时恢复为原始格式。

第二个更加有趣。如果您可以存储抽象语法树,则对代码进行差异化更改就不会变成文本,而是语法。重构代码的位置变得更加容易理解。不利的一面当然是,编写树差异算法并不完全是琐碎的事,通常必须根据每种语言来完成。文本差异适用于几乎所有语言。第三种方法更像西蒙尼(Simonyi)针对有意编程的设想:编程语言共有的基本概念是序列化的,然后您对这些概念有不同的看法用不同的语言呈现。尽管这是一个漂亮的主意,但丑陋的事实是语言在细节上有很大的不同,以至于最低分母的方法实际上是行不通的。相对而言,这是一笔巨大的额外工作。这就是为什么几乎没有人这样做。

评论


实际上,您可以以独立于语言的方式进行tree-diff。您确实需要特定于语言的解析器来构建树。请参阅我们的Smart Differencer工具系列,该工具可以比较多种语言的AST。它们都使用相同的基础差异引擎。 logicaldesigns.com/Products/SmartDifferencer

–伊拉克·巴克斯特
2011年11月12日在3:13



我希望有一天能在Visual Studio中看到我的样式漂亮的加载时保存团队样式的漂亮打印...希望多年了...还没有运气...

–罗马·斯塔科夫
2012年12月12日20:13

#3 楼

您可能会争辩说这正是.NET中的字节码。 Infact redgate的反射器程序确实将字节代码转换回了多种.NET编程语言。

但是,仍然存在问题。语法是特定于语言的,因此您可以用一种语言表示的事物最多,而其他语言则不能表示。这是在.NET中发生的,其中C ++是唯一可以访问所有7个访问级别的.NET语言。每种语言都将开始拥有自己的一组关联库。在C和Java中都不可能反映出通用的语法,因为它们以完全不同的方式解决模拟问题时,它们反映了相同的指令执行。

评论


是否曾尝试反编译F#生成的MSIL?

– SK-logic
2011年11月11日下午13:02

#4 楼

我认为最突出的要点是:


没有好处。您说过,这意味着每个人都可以使用他们的宠物语言。但是,事实并非如此-使用语法树表示只会消除语法差异,而不会消除语义差异。它在某种程度上适用于非常相似的语言-例如VB和C#,或Java和Scala。但甚至还没有完全解决。

这是有问题的。您已经获得了语言自由,但是却失去了工具自由。您不再可以在文本编辑器甚至任何IDE中读取和编辑代码,而只能依靠能说AST表示的特定工具来读取和编辑代码。这里没有任何收获。

为说明这一点,请看一下RealBasic,它是功能强大的BASIC方言的专有实现。有一时间,它看起来似乎可以使用这种语言,但是它完全取决于供应商,以至于您只能在他们的IDE中查看代码,因为它以专有的非文本格式保存。大错误。



评论


潜在的好处是,它可以结束无休止的辩论,例如“制表符与空格”,“ unix与Windows括号/缩进”,“ m_前缀是否在成员前面”,因为它们可以变成简单的IDE选项。

–尼基
2011年11月11日14:08

@nikie True,但是您已经可以使用重新格式化工具(例如astyle或UnniversalIndent)执行此操作。不需要神秘的二进制格式。

–康拉德·鲁道夫(Konrad Rudolph)
2011年11月11日14:35

真正的好处是拥有差异/补丁工具的潜力,该工具可以使您更好地了解真正的变化。但这似乎意味着需要一个全新的工具链来进行版本控制,这是一个严重的限制。

– Peter Taylor
11年11月11日在18:03

如果您认为“没有好处”,那么您就没有看到Intentional Software的Domain Workbench。

– Craig Stuntz
11年11月11日在19:18

简而言之,可以将相同的逻辑投影到不同的表示形式中,而不是全部基于文本,从而使非程序员可以访问规则。例如,像精算师这样的领域专家可以编写保险申请中的精算部分。像DSL一样,但不限于该表示形式。但是,这与问题非常相关。有一个很好的演示。

– Craig Stuntz
2011年11月13日,下午3:14

#5 楼

我有点喜欢您的想法,但是您大大高估了将语言翻译成语言的难易程度。如果那样简单的话,您甚至不需要存储AST,因为您总是可以将语言X解析为AST,然后从AST转换为语言Y。想过更多关于通过某种API公开一些AST的想法。诸如面向方面的编程,重构和静态程序分析之类的事情可以通过这样的API来实现,而那些功能的实现者不必重做编译器作者已经实现的大量工作。

奇怪的是,程序员用来表示一个程序的数据结构经常是一堆包含字符串的文件。

评论


您是否一直在关注Microsoft的“ Roslyn”项目的开发,以将VBc和C#编译器作为API开放?有一个预览版本。

– Carson63000
2011年11月11日下午4:24

#6 楼

我认为,如果同时存储文本和AST,那么您实际上并没有添加任何有用的东西,因为文本已经以一种语言存在,并且AST可以从文本中快速重建。 />另一方面,如果仅存储AST,则会丢失无法恢复的注释之类的内容。

评论


并且是否使注释成为语法树的一部分(注释节点可以是任何子项)?

–棘轮怪胎
2011-11-10 23:28



我们的工具正是这样做的。在此主题中查看我的其他评论。

–伊拉克·巴克斯特
2011年11月11日,下午3:27

#7 楼

有一个围绕这个想法的系统:JetBrains MPS。编辑器有点奇怪,或者只是有所不同,但是总的来说这不是一个大问题。最大的问题是,它不再是文本了,因此您不能使用任何常规的基于文本的工具-其他编辑器,grepsed,merge和diff工具等。

评论


...但是您确实可以使用许多编辑器功能。考虑扩大这个答案,这是一项非常有趣的技术,值得更多地详细说明不将源代码存储为文本的优点。例如。正如我在制表符与空格中回答此问题的方式一样。

–史蒂文·杰里斯(Steven Jeuris)
2011年11月11日17:24



AST可以以人类可读的格式保存,而不是以二进制格式保存。您现在可以使用linux工具替换例如以参数可序列化对象作为参数的代码中的所有方法吗?很难写,但是AST使它非常容易。

– IAdapter
2011年11月11日在22:58

人们不断犯这个错误。与仅使用原始文本相比,AST使其更容易。但是对于任何有趣的事情,您需要大量的附加信息:控制和数据流,符号表,范围分析等。AST可以帮助您,但仅是真正需要的一小部分。

–伊拉克·巴克斯特
2011年11月12日下午4:35

@Ira Baxter,当然,使用AST更容易。但是,将其集成到现有基础架构中要困难得多。

– SK-logic
2011年11月12日13:09

#8 楼

实际上,有几种产品(通常称为“语言工作台”)存储AST,并在其编辑器中将AST的“投影”呈现回特定的语言。正如@ sk-logic所说,JetBrains的MPS就是这样一种系统。另一个是Intental Software的Intental Workbench。

语言工作台的潜力似乎很高,尤其是在特定领域的语言领域,因为您可以创建特定于领域的投影。例如,Intentional演示了与电力相关的DSL,它以电路图的形式展示-与基于文本的编程语言描述的电路相比,对于领域专家来说,讨论和批评它更容易,更准确。

实际上,语言工作台的普及速度很慢,因为除DSL工作外,开发人员可能更喜欢使用熟悉的通用编程语言工作。当与文本编辑器或编程IDE进行面对面的比较时,语言工作台会产生大量的开销,并且它们的优势还不太明显。我所见过的语言工作台都没有引导到可以轻松扩展自己的IDE的地步,也就是说,如果语言工作台对提高生产力非常有用,为什么语言工作台工具却没有变得更好越来越好?

评论


“语言工作台”不必一定基于存储原始AST。它们也可以是面向纯文本语法的,例如,请参见meta-alternative.net/pfront.pdf(并且该目录实际上扩展了Visual Studio和Emacs编辑器,并在其之上实现了任何eDSL)。

– SK-logic
2011年11月12日13:13

那是一篇有趣的论文。它使我想起了(雄心勃勃,根本没有实现)一个名为SugarJ的工具,该工具于几周前在SPLASH / OOPSLA上发布:uni-marburg.de/fb12/ps/research/sugarj

–拉里·奥布莱恩(Larry OBrien)
2011年11月13日在1:10

有趣的是,我也会尝试那个。

– SK-logic
2011年11月13日,9:50

#9 楼

我相信这个想法在理论上很有趣,但不是很实用,因为不同的编程语言支持不同的结构,有些结构没有其他语言的等效语言。

例如,X ++有一个'while select'语句,如果没有很多额外的代码(额外的类,额外的逻辑等),就无法用C#编写该语句。 http://msdn.microsoft.com/zh-cn/library/aa558063.aspx

我在这里说的是,许多语言都有语法糖,这些糖可以翻译成相同的大块代码语言甚至其他元素根本不存在的元素。这是AST方法不起作用的示例:

语言X在AST中用4个语句S1,S2,S3和S4转换了关键字K。 AST现在被翻译成语言Y,并且程序员更改了S2。现在,转换回X会发生什么?该代码被翻译为4条语句,而不是单个关键字...

反对AST方法的最后一个参数是平台功能:将功能嵌入平台后会发生什么?就像.NET的Environment.GetEnvironmentVariable一样。您如何翻译?

#10 楼

您一直在读我的想法。

几年前,当我参加编译器课程时,我发现如果您学习AST并使用前缀表示法而不是通常的中缀表示法对其进行序列化,并使用括号定界整个语句,您将获得Lisp。虽然我在本科学习中了解了Scheme(一种Lisp的方言),但我从未真正获得过赞赏。通过该课程,我肯定对Lisp及其方言表示赞赏。图形环境中的AST。毕竟,我们大多数人的打字速度都比移动鼠标快。然而,一个新出现的问题是“如何用平板电脑编写程序代码?”与配备硬件键盘的键盘/笔记本电脑相比,在平板电脑上打字速度慢/麻烦。如果您可以通过将组件从调色板拖放到大型画布上的画布上来创建AST,那么在平板电脑上进行触摸屏设备编程就可以成为现实。
我们现有的工具很少/没有一个支持此功能。在创建日益复杂的IDE和日益智能的编辑器方面,我们有数十年的发展历程。我们拥有所有用于重新格式化文本,比较文本,搜索文本的工具。可以在树上进行与正则表达式等效的工具在哪里?还是两棵树的区别?所有这些事情都可以通过文本轻松完成。但是他们只能比较单词。更改变量名称,以使单词不同但语义相同,并且这些diff工具会遇到麻烦。开发用于在AST而不是文本上运行的此类工具,将使您更接近比较语义。那将是一件好事。
虽然将程序源代码转换为AST相对容易理解(我们有编译器和解释器,对吗?),但对AST转换为程序代码的理解却不那么了解。将两个质数相乘得到一个大的复合数是相对简单的,但是将一个大的复合数分解为质数则要困难得多。这就是解析和反编译AST的地方。这就是语言之间的差异成为问题的地方。即使在一种特定的语言中,也有多种方法可以反编译AST。例如,遍历对象集合并获得某种结果。使用for循环,遍历数组?那将是紧凑且快速的,但是存在局限性。使用某种在集合上运行的迭代器?该Collection可以是可变大小的,从而增加了灵活性(但有可能牺牲速度)。映射/缩小?更复杂,但隐式可并行化。而且这仅适用于Java,具体取决于您的喜好。

随着时间的推移,开发工作将花费更多,我们将使用触摸屏和AST进行开发。键入将变得不必要。我认为这是从现在到现在的逻辑发展,看看我们如何使用计算机,这将解决#1。

我们已经在使用树木。 Lisp只是序列化的AST。 XML(和扩展的HTML)只是一个序列化的树。为了进行搜索,我们已经有几个原型:XPath和CSS(分别用于XML和HTML)。当创建允许我们创建CSS样式的选择器和修饰符的图形工具时,我们将解决部分#2。当这些选择器可以扩展为支持正则表达式时,我们将更加接近。仍在寻找比较两个XML或HTML文档的良好图形差异工具。随着人们开发这些工具,将可以解决第二个问题。人们已经在做这类事情了。他们还不在那里。

我认为能够将这些AST反编译为编程语言文本的唯一方法是寻求目标。如果我要修改现有代码,则可以通过一种算法来实现该目标,该算法可使修改后的代码与起始代码尽可能相似(最小文本差异)。如果我是从头开始编写代码,则目标可能是最小,最严格的代码(可能是for循环)。或者它可能是尽可能高效并行化的代码(可能是映射/归约或涉及CSP的某种东西)。因此,基于目标的设置方式,即使使用相同的语言,相同的AST也可能导致明显不同的代码。开发这样的系统将解决#3。这将导致计算复杂,这意味着我们可能需要某种客户端-服务器布置,从而使您的手持式平板电脑可以将大量繁重的工作分担给某些基于云的服务器。

#11 楼

另外两个与抽象语法树编码有关的有趣项目是Tree-Sitter,它现在是Atom文本编辑器和Github的一部分。它将您的代码实时解析为AST。这使您可以做一些非常有趣的事情,例如它的创建者称为“扩展选择”的部分,您可以在其中继续单击一个单词,由于它了解AST的结构,因此它将突出显示与AST级别更高有关的文本。 。:



还有Unison编程语言,它不将源代码存储在文本文件中。取而代之的是,它解析文本文件,删除所有详细信息,以便为您提供代码的高度抽象版本,然后对其进行哈希处理。然后通过使用哈希而不是文件名来访问该“代码”。这带来了许多有趣的好处,例如不再需要构建,不存在依赖冲突,并且您可以轻松更改变量/函数/等的名称而无需更改代码库。

这两个项目都使您可以在抽象级别进行编码,该层次离原始文本稍远一些,更接近AST。

#12 楼

如果您打算消除关于格式样式的争论,那么您可能想要的是一个编辑器,该编辑器读取源文件,将其格式化为您个人喜好的显示和编辑方式,但是在保存时,将格式重新设置为团队选择的样式用途。

如果使用像Emacs这样的编辑器,这非常容易。更改整个文件的格式样式是一项三命令工作。

您还应该能够构建钩子,以在加载时自动将文件转换为自己的样式,并在保存时将其转换为团队样式。

评论


然后,您仍然需要进行语义差异和合并(即再次是AST级)。

– SK-logic
2011年11月11日15:41

不,编辑器会重新格式化为团队样式来存储源-因此,您需要将一种源类型与同一类型进行比较。

–古斯塔夫·贝特拉姆(Gustav Bertram)
2011年11月11日15:44

好的一点是,单个归一化表示可以解决所有问题

– SK-logic
2011年11月11日15:49

不,它仅解决了区分两个文件以进行标识的问题。如果要查看文件之间的差异,则理想情况下需要了解结构的内容。我爱我的emacs,但它不了解结构。

–伊拉克·巴克斯特
2011年11月12日下午4:37

Emacs很棒,但是我从不使用它来区分。为了在签入之前比较我的源代码树,我总是使用meld。它实际上了解SVN和git。在Windows上,我可能将WinMerge与乌龟结合使用。

–古斯塔夫·贝特拉姆(Gustav Bertram)
2011年11月12日在8:33

#13 楼

很难读取和修改AST而不是源代码。

但是,一些与编译器相关的工具确实允许使用AST。 Java字节码和.NET中间代码的工作方式与AST类似。

评论


用机械工具可靠地修改AST比用文本可靠地修改。您可以通过模式定向的更改来执行此操作。参见语义设计.com /产品/DMS/ProgramTransformation.html

–伊拉克·巴克斯特
2011年11月11日,下午3:32

现在告诉LISPers ...

–hugomg
11年11月11日在11:48

@艾拉·巴克斯特我知道,Im实际上是在直接与AST一起使用的自定义视觉工具上工作,但是,有时开发人员必须使用文本而不是视觉。某些AST也以较短的编程语言表示为文本。

–umlcat
2011年11月11日18:57

@umlcat,您能告诉我更多有关AST视觉工具的工作吗?

–丹尼尔·阿尔布沙特(Daniel Albuschat)
13-10-21在18:14

@Daniel Albuschat我正在研究一个宠物编程语言项目。解析器难以实现,因此暂时跳过它,并制作了一个工具,用于显示AST(带有树视图控件的表单)并直接添加表达式。并且可以做相反的事情,从AST生成代码。

–umlcat
13-10-22在3:33

#14 楼

这是一个好主意;但是每种语言的AST彼此都不相同。

我知道的唯一例外是VB.NET和C#,Microsoft认为它们是“语法完全相同的相同语言”。甚至其他.NET语言(IronPython,F#等)在AST级别上也不同。语言和各种AST。但要介绍在AST级别保留(或应该保留)的更高级别的概念。

如果可以从Java AST重构Xtend,我认为它将被定义为Java-to- Xtend'uncompiler'可以从现有Java代码神奇地创建更高级别的抽象,您不觉得吗?

评论


作为一个非常熟悉C#和VB编译器的人,我可以告诉您,它们当然是相似的,但是有很多重要的细节完全不同,以致于将它们视为“具有不同语法的相同语言”是不切实际的。我们考虑过为罗斯林项目这样做;建立一个可以以相同的功能编译两种语言的编译器-经过大量辩论后,决定使用两种编译器来处理两种语言。

–埃里克·利珀特
2011年11月11日17:47



@EricLippert:太可惜了。并不是说我曾经计划学习任何一种语言,但这听起来确实是个不错的例外。我认为htat留下了像lisp一样的Dylan和algol一样的Dylan作为唯一的“具有不同语法的相同语言”示例。

–哈维尔
11年11月11日在18:42