软件开发未来:CodeDOM(第一部分)






4.95/5 (21投票s)
建模编程语言的语义。
引言
本文是关于创建代码模型或“CodeDOM”的,这些模型可以模拟编程语言的语义。这是关于 codeDOM 系列的第一部分,其中将包含大量源代码,但第一篇文章是关于正在尝试什么以及为什么需要它的必要背景讨论。
当今大多数计算机语言都缺少重要的东西
软件开发人员不仅必须创建满足要求且尽可能健壮、无 bug 的应用程序,而且还必须尽最大努力构建尽可能易于维护和扩展的软件——不仅由他们自己,也由其他人。这通常涉及使用面向对象技术来创建易于理解且易于扩展的对象模型。在当今 HTML 和脚本语言的时代,这些技术并非总是被使用,但可以说,大多数资深开发人员都会同意,对于大型、复杂的应用程序来说,面向对象分析、设计和编程技术是一种“最佳实践”。
因此,一个讽刺之处在于,用于构建此类应用程序的工具很少暴露对象模型,并且通常是不可扩展的。这个问题始于最重要的工具:语言编译器。开发人员经常会发现他们使用的语言在特定需求方面功能或特性有所欠缺,但别无选择,只能在语言的限制内工作,通常要等待多年才能获得新功能。在复杂系统上工作的经验丰富的开发人员通常会有效地实现一个“领域特定语言”(DSL),以此作为简化系统中核心逻辑的自然方式。这种“语言”可能只包含一组有用的方法或类型(开发人员甚至可能没有意识到他们已经有效地创建了一个 DSL),或者它可能是一种真正的脚本语言或编译语言(无论是自制的还是第三方的)。然而,他们将无法扩展主语言以适应 DSL,这限制了这种技术的强大功能(可以考虑 C# 的 LINQ 功能中添加“内联 SQL”作为允许此类扩展可以实现什么的例子)。
更重要的是,语言编译器的封闭性给第三方工具的开发人员(例如代码分析工具、代码差异比较器等)带来了巨大的负担。供应商必须实现自己的解析和引用解析,这需要大量的时间并且极其复杂。编译器、编辑器和其他工具之间的这种重复工作会导致质量低下,因为存在不一致、错误以及性能和内存使用问题。它还为这些工具创造了巨大的市场壁垒,限制了开发人员的选择。无论开发人员有多好,好的工具都会让他们变得更好。一个好的静态分析工具可以发现任何人的代码中的问题。但是,工具质量的缺乏可能会驱使开发人员放弃使用那些本可以改进他们代码的工具。
总而言之,软件开发人员在他们创建的软件中努力提供的质量,在他们用来编写代码的核心语言工具中并不真正存在:它们没有易于扩展的对象模型(至少,没有公开可访问的)。而且,这给开发过程中所有更高级别的工具造成了严重的困难。换句话说,“鞋匠的孩子没鞋穿”。大多数开发人员可能甚至没有意识到这一点,或者不明白这对他们日常工作有多大的限制——这只是过去一直以来的方式。语言是封闭的。
为什么计算机语言通常是“封闭的”?
计算机语言几乎总是以文本形式定义的,并且几乎总是对用户扩展是封闭的。我认为这种封闭性更多是由于传统而非充分的理由。创建计算机语言的传统是创建一个定义其文本表示的“语法”。然后将该语法馈送到代码生成器,该生成器会生成极其复杂的代码,这些代码“解析”该语言并构建一个抽象语法树(AST),然后可以对其进行正确性分析并用于生成可执行代码(或伪指令,稍后会转换为真实的操作码)。对语言的任何更改都需要对语法进行更改,重新生成解析代码,并重新构建整个编译器——这种架构的一切都对非编译器团队成员的更改构成障碍。而且,即使你是该团队的一员(也许你创建了自己的 DSL),你也可能会因为不得不调试问题并深入到那些丑陋的生成解析逻辑而感到畏惧。
计算机语言是基于文本的。它们就是这样开始的,而且从未改变过(忽略一些很少见但从未真正流行起来的例外)。开发人员在学校接受的教育是就这样做的。他们中的大多数人可能从未真正仔细思考过为什么这样做的原因,更不用说可能的替代方案以及它们可能带来的巨大利益了。他们中的大多数人也可能因为一次编译器理论课(语法、编译器编译器、词法分析器、AST、语义分析、EBNF、LL(k)、LALR——哎呀!)而害怕创建自己的语言。正是计算机语言的传统文本性质与实现它们的传统方法相结合,导致了它们的封闭性。曾经,这一切也许很有道理,但在我看来,至少自从图形化 IDE 问世以来,这已经有些过时了。
是时候跳出(文本)的思维定势了。是时候吞下红色药丸了。
代码对象模型:CodeDOMs
究竟什么是“计算机语言”?为什么不以对象而不是文本来定义计算机语言?毕竟,文本语言在现代编译器处理时通常会转换为对象。文本对计算机来说是一种糟糕的格式——它是为人类而非机器服务的。使用文本是计算机语言的一个古老传统,因为它允许开发人员使用任何文本编辑器轻松编写代码。然而,几十年来,大多数开发人员使用具有本质上是图形化编辑器的 IDE(具有颜色、字体、弹出菜单、工具提示、智能感知、可折叠部分等)。他们感觉自己是在处理文本,并且他们的代码存储在文本文件中,但 IDE 远非简单的文本编辑器——它使用隐藏的对象模型在内部表示代码。
那么,为什么不迈出巨大的一步,将语言**直接设计为表示语言语义的对象模型**,而忽略文本及其所有限制呢?好吧,也许我们不能完全忽略文本,但我们至少能否让文本表示成为对象模型之下的第二顺位,而不是反过来?这实际上并非一个全新的想法——Smalltalk 为代码提供了对象模型,各种可视化编程环境也有效地这样做了。这些尝试没有获得巨大成功的事实并不意味着这个普遍想法不好——只是存在各种缺点阻碍了广泛采用。毫无疑问,潜在的益处是巨大且众多的,但成功的 W设计需要几乎零缺点。拖放式编程可能有其用途,但阻止开发人员疯狂打字、编写临时伪代码或他们今天所做的任何事情,会让大多数人非常不高兴。在编辑代码方面,用户至少必须可以选择一种与他们使用文本语言时非常相似的体验。此外,虽然将程序作为对象存储在数据库中似乎很有意义(毕竟,任何其他复杂数据通常都是这样处理的),但保留以文本形式存储以实现向后兼容的选项也很有意义。
这种思路的逻辑结论是:**我们应该开始主要将计算机语言设计为表示语言语义的对象模型,同时还提供可选的文本表示,并在这两者之间实现轻松转换。**这听起来可能与今天存在的基本情况没什么不同,但专注于使对象模型成为第一顺位的实现将产生巨大影响:所有工具都将是一致的,并且比每个人不断复制工作通过创建自己不兼容的对象模型要容易得多。而且,该语言将像任何良好的面向对象设计一样完全开放和可扩展。语言工具的数量和质量都将急剧增加。有时,视角上的相对较小的改变可以带来巨大的结果。
我们将此类代码的对象模型称为“CodeDOMs”。“DOM”代表“文档对象模型”。它并不完美,但它简洁,并且该术语之前有使用历史(尽管可能定义不完全相同,稍后将对此进行讨论)。此外,应该使用哪种语言来创建对象模型?在大多数情况下,**正是被建模的语言本身**——这乍一听可能有点奇怪,但实际上很有意义(这被称为“自举”)。如果出于某种原因,代码DOM也可以用其他语言来操作主语言。
如何在不使用文本的情况下检查和编辑代码?
CodeDOM 对象将像 IDE 中的文本一样被显示,这意味着使用实际上是图形显示,但仍然使用大量彩色字体文本。毕竟,代码对象(如类型和方法)有名称,语句使用关键字,并且代码中包含注释,这仍然是有意义的。应该始终可以获得与文本几乎完全相同的显示,但也可以选择更图形化的显示(或使用不同的“皮肤”),因为渲染的是对象而不是纯文本。例如,背景颜色和封闭线可能用于表示其他对象的子对象,与特定代码对象关联的注释等。换行到多行的子表达式可能在父表达式中垂直居中,这意味着所有文本可能不会完全对齐到屏幕上的特定行(或列)。大多数 IDE 已经内部使用专有的对象模型来显示,该模型映射到文本——这里的想法是为语言提供一个公共对象模型。这也建议 IDE 朝着更图形化的显示发展,放弃将表示的代码几乎完全显示为文本文件的行和列的想法。
实际上,一旦你开始将代码真正视为图形化显示而不是文本,许多新事物就成为可能。任何语句都可以选择性地折叠其主体。许多语法字符的重要性降低,像大括号、分号,甚至语句和方法的括号可能被选择性隐藏。注释可以以比例字体显示或隐藏。文档注释可以以所见即所得(WYSIWYG)格式显示,而不是 XML。可以使用真正的数学符号来显示某些运算符,而不是传统上用于替代它们的 ASCII 字符。你甚至可以为代码对象定制现有的 UI 控件,或创建自己的控件——例如,一个看起来像电子表格的映射表,直接将一列值映射到另一列(通过隐藏的‘switch’语句实现,甚至通过自定义代码生成)。你可能选择看到代码对象的树状表示,以使复杂表达式的求值顺序更加明显。可能性几乎是无限的——而且最重要的是——每个用户都可以根据自己的需要自定义代码的视图(不再有关于格式的担忧或争论)。你甚至可以选择将关键字和库名称映射到程序员的(人类)语言,而不是强迫每个人处理英语。
至于编辑,GUI 可能比标准 IDE 提供更多图形化编辑选项,例如使用下拉选择、拖放等。使用更图形化的显示可以比使用基于文本的显示更快、更轻松地选择正确的代码片段。然而,要“正确”地进行编辑,意味着允许用户正常键入,动态地将代码解析为代码对象,同时还能轻松处理尚未完全有效的代码片段或伪代码。这是此类事物先前尝试经常失败的领域,但理论上没有理由不能为对象树支持类似的文本式编辑。
标准化 codeDOM 语言的普通用户不一定需要学习或使用 codeDOM。他们可以像今天使用基于文本的语言一样学习该语言。他们将受益于 codeDOM 带来的更多数量和更高质量的语言工具,但他们自己不需要直接使用它。然而,很可能有一天,他们会发现自己使用 codeDOM 来创建工具、扩展语言、添加代码分析规则或生成代码。CodeDOM 还提供“反射”和“表达式树”支持(在 C# 等现代托管语言中使用)。
CodeDOM 到底能带来多少好处?
我已经讨论了不少好处,但这里是回顾加上一些额外的想法
- 工具之间更好的一致性,更少的内存使用,更好的性能。
- 更多样化的工具选择,以及更好的整体质量。
- 所有工具的定制程度大大提高,从语言本身开始。
- 对 DSL 的更好支持,并与主语言紧密集成。
- 除了文本式编辑外,还可以更图形化地显示和操作代码。
- 每个用户对代码的高度可定制显示——更容易阅读和理解代码,并最终结束关于格式风格的争论。
- 更好、更易于定制的代码分析,性能更好。
- 基于实际代码对象更改而不是文本的更好的版本控制。
- 能够将代码直接存储在数据库中而不是文本文件中,通过消除解析的需要来提高性能,并为大型代码库提供更好的管理。
- 使用强大的 SQL 查询搜索、分析和重构代码。
我确实希望大多数读者现在开始接受 codeDOM 的想法,并且你们中的许多人对此充满期待。老实说,我从 90 年代起就在思考这些问题,坦白说,我对整个行业花了这么长时间才实现如此显而易见的好处感到非常失望!作为软件开发人员,我们不仅**需要**这种东西,而且**几十年前**就需要了!这样说来,我冒着一点夸张的风险,展示一份“宣言”(虽然不使用那个被过度使用的词)。
软件开发人员的最后通牒
作为软件开发人员,我们经常被要求在极短的时间内完成艰巨的任务,而且错误很少,最终产出的是易于理解和扩展的东西,方便后来的接班人。可悲的是,我们常常远未达到这些期望。
我们承担部分责任,承认我们只是凡人。然而,请知悉,提供给我们的工具对于这项工作来说是远远不够的。要创建更好的软件,我们需要更好的工具。对面向对象技术、托管代码和敏捷方法论的支持是巨大的进步,但还不够。**我们的核心工具在许多方面都存在不足,在我们获得更好的工具之前,我们工作的质量将相应地受到影响。**
具体来说,一个现代计算机语言平台应该包括
1) 一套公开可访问且可扩展的类,用于模拟语言的语义,用语言本身实现,并且除了文本表示外,两者之间易于转换。我们需要摆脱文本的限制,以及语言工具不断地重新解析它的需要。
2) 一个公开可访问且可扩展的图形编辑器,允许直接操作代码对象,同时提供最常用的面向文本的编辑功能。我们需要更好的代码编辑和重构工具,并且我们需要能够自己定制它们。
3) 集成且易于定制的代码分析支持,可以随着时间的推移而改进,远远优于目前可用的。我们需要更好的代码分析而没有性能问题,并且我们需要能够自己定制它。
4) 集成的自动版本控制,以代码对象级别而不是文本级别确定差异,并提供出色的分支和合并能力。我们需要确切地知道发生了什么变化,而不是一个粗略的猜测,并且我们需要简单且无错误的合并。
5) 可选地将代码对象直接存储在数据库中而不是文本文件中,从而允许在非常大的代码库上进行更快、更强大、并行的搜索和分析操作。我们需要用于分析海量代码的正确工具,而不是扫描数千个文本文件。
当提供这样一个开发平台时,我们的生产力和代码质量将显着提高。我们将最终能够专注于我们试图解决的问题,而不是在工具的限制中摸索。
截至 2012 年秋季——尽管在过去的 20 年里创建了许多新的托管和脚本语言——我们实际上并没有广泛使用的计算机语言提供**任何**这些功能,更不用说**所有**了。
CodeDOM 的设计目标
我将概述一下我所定义的 codeDOM 的一些主要设计目标。我心目中的 codeDOM 是一组类,可用于创建对象树,这些对象树表示特定语言(新语言或现有语言)的代码语义(含义)。这些“代码对象”将以最易于其他代码操作的形式表示代码,从而尽可能轻松地让程序员编写分析和修改代码的代码,从而极大地促进语言工具的创建。我认为对于这样一个 codeDOM 最重要的设计目标是
- 清晰命名的类,模拟语言的实际语义。所有语句、运算符、注释等都应该有自己的类,它们应该处于逻辑层次结构中,有一个共同的基类,并且最好是用被建模的语言来实现(尽管也可以存在其他语言的实现)。
- 子对象应该有一个指向其父对象的引用。除了独立的注释外,还应该可以为特定代码对象关联注释。
- 谨慎使用内存。大型代码库将拥有数百万个对象,因此对于不必要的语法、标记或仅适用于文本的格式,不应有额外的字段或对象。
- 代码对象树的易于修改。只需更改对象的 Name 属性即可重命名一个对象。将对象分配给新父对象应自动更新其父引用。
- 支持将代码对象格式化为文本(用于导出、调试等),但以一种不显眼的方式实现,如果不明确指定,则默认为标准格式。
- 支持将现有的文本代码解析为代码对象,以及手动创建它们。
- 支持将符号引用解析为对其他代码对象的直接引用。
不可能过度强调设计目标 A 的重要性。类的名称、它们的层次结构以及它们的成员基本上以编程形式定义了语言,它们应该尽可能地与文本表示相匹配。
难道不存在类似的东西吗?
如前所述,这并非一个革命性的想法——至少,代码对象模型的基本想法不是。但是,我们应该首先将语言设计为开放对象模型,而文本表示更多地是事后考虑,这种特定视角可能有些激进。而且,在我看来,到目前为止,任何现有工具都未能真正实现上面列出的许多设计目标。然而,在 .NET 中,System.CodeDOM 和 System.Linq.Expressions 命名空间中有代码建模类,还有 Roslyn 项目。我将在我的下一篇文章中进一步讨论这些。
够了——来点代码吧!
我写代码已经 30 多年了,我厌倦了等待某个大公司终于创建一个开发环境,让我们进入代码以对象而不是文本形式存储的光明未来。忘掉飞车吧——我想要 codeDOMs!
好消息是,我实际上为此工作了很长时间,并且我准备将我的许多源代码公开,以期增加对 codeDOMs 的兴趣,也为了分享有用的代码。在本系列文章中,我将分享一个用于 C# 的 codeDOM,展示一个用 WPF 表示的 codeDOM,一种面向对象的解析技术,用于读取/写入 VS Solution 和 Project 文件的 codeDOM 类,使用 Reflection 和 Mono Cecil 从程序集中加载元数据的示例,如何在 codeDOM 中解析符号引用,从 codeDOM 计算指标和搜索 codeDOM,分析 Roslyn 等工具提供了什么,以及更多内容。
在我的下一篇文章中,我将开始创建一个基于 C# 的 codeDOM,并包含源代码。我会尽量保持它简单、干净、组织良好,但一开始我们会有大约 45,000 行代码分布在约 300 个类型中,并在本系列结束时达到数倍于此。 点击此处查看第二部分。