Objectionary: Dictionary and Factory for EO Objects

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

自从Kernighan和Ritchie的时代,我们在库中共享二进制代码。想要在C++中使用printf()打印一些文本吗?你可以使用libc库,其中包含700多个其他函数。想要复制一个Java流吗?你可以使用Apache Commons IO,其中包含copy()和其他140多个方法和类。在我所了解的所有语言中都是如此,比如Ruby、Python、JavaScript、PHP:你需要一个对象、一个类、一个函数或一个方法,你必须将整个库添加到你的构建中。与之相比,处理单个对象会更加优雅,不是吗?

这个想法并不新鲜,也不是我自己的。我从David West的书《Object Thinking》(https://amzn.to/266oJr4)中得到了灵感,他在书中建议创建一个”Objectionary”(第306页),它是一个”字典和对象工厂的组合”,具有以下特性:

  • 每个对象都是一个独立的可执行实体;

  • 每个对象都有一个唯一的ID和一个唯一的”地址”。

  • 对象只不过是对象的集合。

  • 对象需要特定硬件的虚拟机来执行。

17年后(该书于2004年出版),我们在我们的新编程语言EO上实现了这个想法。该语言意图比Java或C++要简单得多。您可以在这里阅读它的更正式描述。

要将EO程序转化为可执行实体并发布到Objectionary,假设JVM用作目标平台(由我们的eo-maven-plugin实现的步骤标有🌵),必须经历以下强制步骤:

  • Parse🌵: .eo.xmir

解析🌵:.eo.xmir

  • 优化🌵:.xmir ➜ 更好的.xmir

  • 发现🌵:找到所有外国别名

  • Pull🌵: download foreign .eo objects

拉取🌵:下载 外部的 .eo 对象

  • 解决🌵:下载并解压缩.jar构件

  • Place🌵: 将构件的.class文件移动到target/classes/目录下。

  • Mark🌵:将在 .jar 中找到的 .eo 源代码标记为外部的

  • ↑ 如果还有一些 .eo 文件没有解析,请返回到 Parse

  • Transpile🌵: .xmir.java

转译🌵:.xmir.java

  • Assemble🌵: 与上述相同,但用于测试

  • 编译.java.class

  • 测试:运行所有单元测试

  • Unplace🌵: 移除artifact .class文件

  • Unspile🌵:删除自动生成的.java文件

  • 复制🌵:将.eo文件复制到.jar内的EO-SOURCES/目录下。

  • 部署:将 .jar 文件打包并放入 Maven 中央仓库

  • 推送: 将拉取请求发送到yegor256/objectionary

  • 合并:我们测试并合并拉取请求。

这是一个迭代的过程,一遍又一遍地循环,直到所有所需的.eo对象都被解析并且它们的原子作为.class文件出现。然后,所有.xmir文件被转译为.java,然后编译为.class二进制文件。然后,进行测试,打包,并部署到Maven Central。然后,通过拉取请求合并到Objectionarymaster分支。

算法的第一部分可以通过我们的Maven插件自动化,只需将.eo源文件放置在src/main/eo/中,并将其添加到pom.xml中:

register目标会扫描src/main/eo/目录,找到所有的.eo源文件,并在target/eo-foreigns.csv中将它们”注册”到一个特殊的CSV目录中。接下来,assemble目标将调用以下目标:parseoptimizediscoverpullresolve。当它们解析、优化、拉取等操作时,所有这些目标都使用CSV目录。

当所有目标都完成时,assemble会检查目录:是否还有.eo文件需要解析?如果有,将再次开始另一个循环,从解析开始。当所有.eo文件都被解析后,将执行transpile目标,将.xmir文件转换为.java文件,并将它们放置在target/generated-sources目录中。其余的工作由标准的maven-compiler-plugin完成。

让我们详细讨论每个步骤。

说,这是位于src/main/eo/hello.eo.eo源代码。

它将被解析为XMIR(XML中间表示):

如果你想知道这个 XML 是什么意思,请阅读此文档:其中有关于 XMIR 的部分内容。

在此步骤中,由解析器生成的XMIR会经过多个XSL转换,有时会获得额外的元素和属性。我们的示例XMIR可能会获得一个新属性@ref,将对象user的引用指向定义该对象的行。

一些XSL转换可能会检查语法或语义错误,并在发现问题时添加一个新元素 ““。因此,如果解析没有发现任何语法错误,所有其他错误将在XMIR文档中可见,例如,像这样:

顺便说一句,这并不是真正的错误,我只是编造出来的。

在这一步中,我们找出哪些对象是“外部”的。在我们的例子中,对象user不是外部的,因为它在我们面前的代码中已经定义了,而对象stdout在这里没有定义,所以它是一个外部对象。

通过查看所有的.xmir文件,我们可以轻松判断哪个对象是外部的,只需看它们的名称。一旦我们看到对org.eolang.io.stdout的引用,我们就会检查在包含所有.eo源文件的目录中是否存在org/eolang/io/stdout.eo文件。如果文件不存在,我们将把对象名称放入CSV目录,并声明其为外部对象。

在这里,我们只是试图找到Objectionary中所有外部对象的源代码.eo文件,方法是查看其GitHub存储库。例如,这是我们可以找到stdout.eo的地方。我们在那里找到它们并将其拉到本地磁盘上。

请注意,我们拉取的是源代码,而不是二进制文件或编译后的XMIR文档,而是以.eo格式的源代码。

这是拉取之后的stdout.eo可能的样子。

对象是一个原子。这意味着即使我们有它的源代码,没有特定于平台的二进制代码,它就不完整。原子是由运行时平台实现的对象,在执行EO程序的地方(也称为FFI机制)。以 +rt(运行时)开头的行解释了在哪里获取运行时代码。jvm部分是运行时的名称。

我们去Maven Central,在那里找到了org.eolang:eo-runtime:0.10.2这个工件,然后解压它(毕竟它是一个带有.class文件的压缩存档)。

顺便说一下,一个程序可以包含多个 +rt 元指令,例如:

在这里,三个运行时平台将知道如何获取stdout原子的丢失代码:EO➝Java将前往Maven Central获取JAR构件,EO➝Ruby将前往RubyGems尝试通过名称为eo-core和版本为0.5.8的宝石来获取,而EO➝Python将前往PyPi尝试找到版本为0.0.3eo-basics软件包。

接下来,我们将在解压的JAR文件中找到的所有.class文件放置在target/classes目录中。我们这样做是为了帮助Maven编译插件在类路径中找到它们。

在每个到达的JAR文件中,我们可以找到.eo源文件。它们是在构建JAR文件时在classpath中使用的程序。我们也将它们视为外部对象,并添加到CSV目录中。

当所有在目录中注册的外部对象都被下载、编译和优化完成后,我们准备开始进行源代码转换。与直接将XMIR编译为字节码不同,我们将其转换为.java,然后让Java编译器来生成字节码。

我们相信将代码转换为Java与直接编译为字节码相比有一些好处:

  • 现有编译器的优化能力被重复利用。

  • 请将以下Markdown段落从英文翻译成中文,不要翻译技术术语和专有名词:

转译器的复杂度低于编译器。

  • 输出代码的可移植性更高。

我们已经有两个EO➝Java的转译器:一个是官方版本,另一个是HSE大学开发的。我们还有一个EO➝Python的实验性转译器,由Innopolis大学的学生开发。很可能,当你阅读这篇文章时,会有更多的转译器可用。

尽管我们相信转译技术,但仍然有可能创建EO➝Bytecode、EO➝LLVM或EO➝x86编译器。欢迎你去尝试!

在这一步骤中,标准的Maven编译器插件会在target/generated-sources目录中找到自动生成的.java文件,并将其转换成.class文件。

在这里,我们删除从依赖中解压出来的所有.class文件。这是必要的,为了避免将它们打包到最终的JAR文件中。

我们进行放置和取消放置只是因为Maven编译器插件不允许我们在运行时扩展类路径。如果可能的话,我们只需从Maven中央仓库下载依赖并将它们添加到类路径中,而不需要解压、放置和取消放置。

在这里,我们将target/classes/目录中由.eo自动生成的所有.class文件删除。我们不想发布由.eo源文件生成的二进制文件。我们只想发布原始的.java文件中的原子。

在这一步中,我们将从src/main/eo/中获取所有的.eo源文件,并将它们复制到target/classes/EO-SOURCES/目录中。稍后,它们将与.class文件一起打包成一个.jar文件,然后部署到Maven Central。在复制过程中,我们将运行时版本中的0.0.0替换为当前部署版本。请查看文件stdout.eo,在其源代码仓库中:

+rt 行上的版本是 0.0.0。当源代码被复制到 JAR 文件时,此文本将被替换。

将源代码与二进制文件一起发布的动机如下所示。当 Java 编译成字节码时,原子二进制文件会与转译后的源代码并存。它们一起编译。此外,单元测试还依赖于原子源代码和自动生成/转译的源代码。我们希望 JAR 文件的未来用户知道在编译进行时我们所使用的源代码,也许让他们能够重现它,或者至少知道获取的二进制文件所处的环境。

从更实际的角度来看,我们需要将这些源代码放在 JAR 文件中,以便让 Mark 步骤了解哪些对象值得与解析的原子一起获取。

在这里,我们将target/classes/中的所有内容打包成一个JAR存档,并将其部署到Maven Central。

我建议将源代码部署到GitHub Pages,以便用户可以在网上查看。而且,当我们向Objectionary发起pull request时,这也会很有帮助。在我的一个EO库中,可以查看这个.rultor.yml脚本,它将.eo源代码正确地部署到GitHub Pages,并替换其中的0.0.0版本标记。

当部署完成并且Maven Central更新其CDN服务器时,是时候向yegor256/objectionary提交一个pull request了。对象的.eo源码放在objects/目录下,它们的单元测试放在tests/目录下。基本上,我们只需将src/main/eo/src/test/eo/复制到那里即可。但是,停一下…有一点重要的细节。在源码中,正如之前所说,我们将+rt版本设为0.0.0。在这里,当我们复制到Objectionary时,版本必须设置为实际数字。

当拉取请求到达时,存储库yegor256/objectionary中预配置的GitHub Action会将所有.eo源代码转译为所有已知平台,并运行所有单元测试。如果一切都很干净,我们会审查该拉取请求,并决定所建议的对象是否与Objectionary中已存在的其他对象相匹配。

一旦合并了拉取请求,这些对象将成为EO所有对象的集中字典的一部分。请查看此拉取请求,在该请求中,一个新的对象被提交到Objectionary,其原子已部署到Maven Central。

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-16 at 15:27

sixnines availability badge   GitHub stars