Xee:基于 Rust 的现代 XPath 和 XSLT 引擎
Xee:基于 Rust 的现代 XPath 和 XSLT 引擎
在过去的两年里,我一直在用 Rust 实现一种编程语言,名为 Xee。 Xee 代表 "XML Execution Engine",它支持现代版本的 XPath 和 XSLT。它们都是编程语言,而且是的,它们都和 XML 相关。
现在请稍等。 当我谈论 XML 时,您的大脑可能会自动关闭。 我完全理解 XML 可能不是您喜欢的菜。 但我也将要谈论一个奇怪的、不同的技术世界,在这个世界里,一切都被详细地规范,并且使用 Rust 实现了一种编程语言。所以我希望,如果这些主题让您感兴趣,您仍然决定继续阅读。
如果 XML 恰好是您的菜,我认为您应该对 Xee 感到兴奋,因为我认为它可以帮助确保 XML 技术拥有更美好的未来。
这是 Xee 仓库。
其中有两个亮点:一个命令行工具 xee
可以让您执行 XPath 查询,还有一个 Rust 库 xee-xpath
可以让您从 Rust 代码中发出 XPath 查询。
Genesis
2023 年,我的杰出且慷慨的客户 Paligo 问我是否愿意用 Rust 实现现代版本的 XPath 和 XSLT。 我感到非常紧张了一个星期。 然后我告诉他们这是一个大项目。 我告诉他们我可以做到,而且我很乐意去做,但这将是 很多 的工作。
虽然我当时对这个项目的范围感到非常害怕是对的,但我 仍然 低估了当时的工作量。
但无论如何,Xee 已经走了很长一段路! 如果您愿意跟随,我将带您踏上它的旅程。
什么是 Xee?
Xee 是一种编程语言的实现。 它实现了两种核心 XML 编程语言:XPath 和 XSLT(在撰写本文时,只是部分实现)。 XPath 是一种 XML 查询语言,而 XSLT 是一种使用 XPath 作为其表达式语言的语言,它可以让您将 XML 文档转换为其他文档。 Xee 实现了这些规范的 现代 版本,而不是 1999 年发布的版本。
Xee 使用 Rust 编程语言实现了这些语言。 这不仅将现代 XML 技术带给 Rust。 Rust 是一种系统编程语言,擅长与其他编程语言集成。 因此,Xee 也可以将其功能带给其他编程语言,从 PHP 到 Python。 我已经试验过 PHP bindings。
由于 Xee 是用 Rust 编写的,因此也可以将 Xee 解释器编译为 WASM,并在浏览器中运行它。
稍后我将继续讨论 Xee 的实现方式,但首先我们将休息一下,分享一些 XML 历史。
XML 历史
让我们谈谈 XML。 XML 出现在 90 年代末,虽然现在可能难以置信,但在 2000 年代初期的一段时间里,XML 是一种很酷的技术,每个人都想使用它。 行业活动和大量计算机科学论文的发表都表明了当时的盛况。
为了说明它的规模有多大,去年我参加了 RustNL 会议,我与 两位不同的演讲者 进行了交谈,他们都提到他们过去曾从事过 XSLT 引擎 的工作。 其中一位是 Rust 核心开发人员 Niko Matsakis。
所以,那时我还是一个年轻而时髦的开发者,我也在做很酷的 XML 相关的事情。我在 XML 领域的最大成就是创建了 lxml,即 Python 的 XML 库。 我在 2004 年底开始了那个项目。 早期,Stefan Behnel 加入了该项目,此后他一直胜任地维护着它——没有他,它就不会那么成功。
虽然 XML 技术今天已经不再流行,但它仍然无处不在。 Web 浏览器使用的核心语言不是 XML,而是它的近亲 HTML。 嵌入在 HTML 中的是真正的基于 XML 的语言,例如 SVG 和 MathML。 尽管 JSON 和其他语言抢占了很大一部分市场,但 XML 仍然用于存储和传输大量数据,并且还广泛用于文档,例如 docbook 和 JATS 格式。 XML 现在是一种小众技术,但它比您想象的要大,并且不会很快消失。
在我的职业生涯中,我与 XML 的接触越来越少,尽管我仍然会定期遇到它。 无论何时我与使用 Python 的潜在客户交谈,他们已经在某个地方使用 lxml 了,这既有趣又实用。
几年前,我重新进入了 XML 世界。 这就是现在的我,一个相对罕见的鸟类,既了解 Rust 这样时髦的现代编程语言,同时又非常熟悉 XML。
XPath 和 XSLT 都是编程语言
所以 XPath 和 XSLT 都是编程语言。
XPath 是 XML 的查询语言。 给定一个 XML 文档,比如 HTML,您可以使用如下表达式查询它:/html/body//p
以获取外部 html
元素的 body
元素内的所有 p
元素。 现代的 XPath 是一种具有类型系统、变量、函数定义、条件、循环等的函数式编程语言。
XSLT 是一种 XML 的转换语言。 它使用模板和函数式方法描述如何将一种类型的 XML 文档转换为另一种类型。 例如,您可以使用它将描述文档的 docbook XML 转换为 HTML。 它建立在 XPath 之上——XPath 表达式是 XSLT 的表达式语言。 XSLT 本身 也 支持像变量、循环、条件、函数等编程结构,与 XPath 有部分重复。
XML 开源堆栈的现状
因此,如果您想使用这些编程语言,并且您使用开源堆栈,您会去哪里?
Java 世界对现代 XPath 和 XSLT 提供了良好的支持。 XPath 和 XSLT 由 Saxon 实现,Saxon 已经存在很长时间了。 Saxon 也可在 .NET 上使用。 还有通过相当复杂的 C 到 Java 桥接的 PHP 和 Python bindings,Saxon 还提供了其运行时的 JavaScript 重新实现。 除了其开源产品之外,Saxon 还拥有闭源的专业/企业版,这些版本提供了更多功能。 除了 Saxon 之外,Java 中还有开源 XQuery 实现。
但是,如果您走出 Java 世界及其外围,并且在您普通的开源堆栈或 Linux 发行版中寻找 XPath 或 XSLT 实现,您不会找到 Saxon 或这些 XQuery 数据库; 你会找到 libxml2
和 libxslt
。
libxml2
和 libxslt
是用于处理 XML 的 C 库。 这些库的混合体支持解析 XML、使用 XPath 查询 XML、使用 XSLT 转换 XML 等。 libxml2
无处不在——在您的 Linux 发行版和 MacOS 中。 人们不仅仅从 C 代码中使用它——例如,对于 Python,我构建了基于它的 lxml
。
这些库最初由 Daniel Veillard 创建。 我记得很多年前和他谈过一次。 我们来自不同的世界——他正在考虑用 C 编写快速的处理器缓存友好的代码,而我则对 Python 中易于使用的 API 感兴趣。 我对他在实现了所有这些规范印象深刻——lxml
只是在利用这项艰苦的工作。
但是 libxml2
停留在过去——它实现了 XPath,但仅实现了 XPath 1.0,类似地,libxslt
仅实现了 XSLT 1.0。 这些都是 1999 年的规范。 XPath 2 规范于 2007 年发布,而我们目前实际上使用的是 2017 年发布的 XPath 3.1。 类似地,XSLT 2.0 于 2007 年发布,而当前版本 XSLT 3.0 于 2017 年发布。
我希望 Xee 可以成为 libxml2
和 libxslt
的更现代的替代方案,并在开源世界中找到自己的位置。 为了让 XPath 和 XSLT 成为蓬勃发展的标准,它们需要多个实现,使用多种编程语言,由多个参与者实现。
就我个人而言,我觉得我已经走到了一个完整的循环——最终,在 XML 的后期,我发现自己和 Daniel Veillard 很久以前使用 libxml2
所做的事情一样了。 我发现自己正在实现相同的东西,不是用 C 语言,但仍然是用一种系统编程语言 Rust 实现。
规范文化
去年我参加了 XML 会议 XML Prague,我注意到 XML 文化的一些有趣之处。 它仍然非常注重标准。 这在 2000 年代初期的 Web 开发领域非常普遍,但我认为,尽管标准今天仍然被认为很重要,但它们在文化上的重要性有所降低。
XML 文化不同:所有东西都需要被规范化。 如果它没有在规范中,那么它就不是完全 真实的 。 这使得 XML 社区的行动比其他软件社区慢。 我有点困惑地听到 2024 年有人谈论更新 RESTXQ 规范,这是一种基于 XQuery 的 Web 框架标准,首次在 2012 年讨论,以便利用诸如哈希映射和数组之类的语言特性,这些特性最终于 2017 年添加到 XPath/XQuery 中。
这些 XML 规范深入、相互构建、非常扎实。 如果您重视经得起时间考验的坚实基础,那么 XML 世界可以为您提供支持。
实现一种编程语言
您现在可能已经对 XML 感到厌烦了,所以在回到对规范的讨论之前,我将简要地谈谈 Xee 的架构。
Xee 遵循编程语言实现中的各种熟悉的模式。 我基于优秀的 Crafting Interpreters 一书构建了其部分架构。
在 Xee 中,XPath 首先被词法分析为 tokens,然后被解析为抽象语法树 (AST)。 然后,AST 被转换为中间表示 (IR),该中间表示以更紧凑的方式表示表达式。 然后,此 IR 被编译为 bytecode——一种简单的类似汇编语言的栈机器,类似于许多编程语言(如 Python 和 Java)的基础。 然后,Xee 解释器可以执行 bytecode。
目前的翻译很简单; 虽然我已经准备好 IR 来支持诸如常量折叠之类的优化过程,但目前尚未实现。
虽然 XSLT 尚未完成,但它构建在与 XPath 引擎相同的架构上。 有一个前端将 XSLT XML 转换为 XSLT AST,然后将其转换为与用于 XPath 相同的 IR。 它使用相同的 bytecode 解释器。 因此,只有 XSLT 前端不同,其他一切都相同。 这使得实现一大堆 XSLT 功能变得很容易,因为我已经为 XPath 实现了它们。
实现编程语言很有趣!
再次谈到规范
XPath 和 XSLT 是完全规范化的编程语言。 您可以真正地从规范中实现它们。 一方面,这让生活变得轻松很多——因为规范清楚地说明了事情应该如何工作,所以目标很明确。 还有一个庞大的符合性测试套件可用。 另一方面,这意味着无休止的跑步机; 当我认为它看起来足够好时,我不能仅仅停下来,因为还有更多的规范需要实现。
XPath 3.1 比 XPath 1.0 大 很多 ; 它变成了一种成熟的编程语言,具有更大的标准库。 自 XSLT 1.0 以来,XSLT 3.0 也得到了很大的发展。 规范不断相互构建,并在新的更新中添加更多功能,直到实现它们成为一项艰巨的任务。 我有时希望我像当年的 Daniel Veillard 一样,正在实现 XPath 1.0 和 XSLT 1.0。
让我快速浏览一下各种规范,以便您了解实现它们的任务的艰巨性。
XPath 语言的语法和行为在 W3C 规范 XML Xpath Language (XPath) 3.1 中进行了说明。 这引用了另一个规范 XQuery and XPath Data Model 3.1,该规范描述了 XPath 如何查看 XML 数据——XML 数据存在哪些属性。 它还构建在另一个规范 XPath and XQuery Functions and Operators 3.1 上,该规范不仅描述了 XPath 运算符(如 +
、-
和 *
)的行为,还定义了其标准函数库。
XPath 具有类型系统,其类型由 W3C XML Schema Definition Language (XSD) 1.1: Part 1: Structures 和 W3C XML Schema Definition Language (XSD) 1.1 Part 2: Datatypes 描述。 这定义了原子类型(Xee 实现了这些类型),但也允许您定义新类型并使用来自 XML 模式的类型,而 Xee 目前尚未实现这些类型。 这些规范还描述了 XPath 如何解析和格式化原子类型的字符串,例如小数和日期的格式。
哦,还有 XPath 函数和运算符规范? 其中一些函数使用正则表达式。 该规范将 XPath 正则表达式定义为 XML 模式规范中定义的正则表达式系统的扩展。 所有这些都建立在 unicode 规范之上,但那是另一个国家的事情了。 所以我最终也实现了一个 regex engine。
然后是 XSLT。 有 XSL Transformations (XSLT) Version 3.0,它定义了 XSLT 编程语言。 它建立在之前的所有规范之上,还建立在 XSLT and XQuery Serialization 3.0 之上,该规范描述了如何序列化 XML 和各种其他事物的选项。
当然,所有这些都建立在 XML 规范本身之上,Extensive Markup Language (XML) 1.0 (Fifth Edition),并使用命名空间进行了扩展,在 Namespaces in XML 1.0 中。
然后还有一些相关的零散规范,如 XML Base 和 xml:id。 但这些都是小规范。
我曾经计算过仅 XPath 和 XSLT 规范以及最相关的 XML Schema 规范(第 2 部分)的页数,该子集的页数超过 1800 页。
我可能忘记了一些规范,因为过一段时间后,它们开始从我的耳朵里冒出来,但这应该会给您留下深刻的印象。
Xee 状态
我最自豪的是 Xee 中的 XPath 3.1 实现。 XPath 核心语言及其大部分标准库都已实现。 标准库的实现中仍然存在差距——例如,一些格式化函数特别大,但总的来说它非常完整。
有一个 XPath 3.1 符合性测试套件,在撰写本文时,在 21859 个测试中,有 20130 个测试通过。 大多数失败的测试都与缺少标准库功能的实现有关。
顺便说一句,这个测试套件在我的机器上用 13 秒运行了 20130 个测试。 计算机真快。
同时,Xee 还为 XSLT 提供了坚实的基础,重复使用了很多 XPath 基础设施。 虽然很多 XSLT 工作,但还有很多工作要做,我希望找到愿意帮助贡献的人!
贡献者招募
所以现在我将呼吁这种罕见的鸟类:有人读了所有这些,看到了所有这些 XML 规范,了解一点 Rust,喜欢实现编程语言并认为:太酷了! 我想帮忙!
- 您是否喜欢按照规范实现一些功能(无论大小)的挑战? Xee 为您提供了大量的任务。
- 您对编程语言的实现感兴趣吗? 也许做一些很酷的编程语言优化工作? 为一种已经有用户群的编程语言? Xee 具有基础。
- 您是否喜欢思考查询优化问题? 关心使用 简洁的 数据结构? (尚未集成到 Xee 本身中)。 我们有很多您应该感兴趣的东西。
- 您是否关心 XML 的未来,并希望确保在 Java 世界之外提供现代开源实现?
Xee 项目可以使用您的帮助,并且已准备就绪。 欢迎您的大大小小的贡献!