XLang:可逆计算的编译期语言族
可逆计算公式中有一个关键操作:变换。DSL 定义完后,需要经过结构变换才能得到最终的可运行产物。这个变换由 XLang 完成。
XLang 不是一门语言,而是一整套面向 DSL 变换的语言族,包含 XScript、Xpl、XTransform 三个核心成员。它们共享同一个类型系统和运行时,各自负责不同的变换任务。
为什么需要一门新语言
通用的编程语言(Java、Python)操作 XML/JSON 时,通常需要经过“解析 → 对象 → 遍历 → 修改 → 序列化”的冗长流程。对于 DSL 的编译期变换来说,这个流程太重了。
XLang 的设计目标是在 DSL 的结构层次上直接操作,而不是把 DSL 转成内存对象再处理。这类似于 SQL 直接在表上操作,而不需要把表转成对象再遍历。
用 XLang 表达“删除实体中的 password 字段”:
for (entity in /orm/entities) {
entity.removeColumn("password")
}
对比 Java 实现同样的逻辑:
- 解析 XML → 20 行
- 遍历实体 → 5 行
- 查找并删除字段 → 5 行
- 序列化回 XML → 5 行
- 总共约 35 行
XLang 直接在内建的 XNode 结构上操作,没有解析和序列化开销。
XScript:脚本语言
XScript 是一门类似 JavaScript 的动态脚本语言,语法上兼容 JavaScript 的基础表达式,但针对树结构操作做了专门扩展。
let entity = xpl.loadXml("/nop/auth/orm/app.orm.xml");
let columns = entity.$children("columns").$children("column");
for (let col in columns) {
if (col.$attr("name") == "password") {
col.$remove();
}
}
xpl.saveXml("/nop/auth/orm/app.orm.xml", entity);
XScript 中的 $ 前缀方法($children、$attr、$remove)是操作树结构的专用 API,直接在 XML 节点上执行,不需要经过 Java 对象映射。
使用场景:
- 编译期的模型变换和修正
- 动态生成 DSL 片段
- 条件化处理(根据环境变量选择不同的模型)
Xpl:模板语言
Xpl 是类似 FreeMarker 的模板语言,但它的输出是结构化数据(XML 节点),而非字符串。这避免了字符串拼接导致的 XML 格式错误。
<c:unit xpl:outputMode="xml"> <entity name="MyEntity" table="my_entity"> <columns> <c:for var="col" items="${extFields}"> <column name="${col.name}" code="${col.type}"> <comment text="${col.desc}" /> </column> </c:for> </columns> </entity> </c:unit>
Xpl 在服务端渲染时直接输出 XNode 树,不会产生无效的 XML 字符串。这意味着它可以安全地与 x-extends 合并引擎配合——合并引擎在结构树层面操作,不需要解析字符串。
使用场景:
- 代码生成器的模板文件
- 报表模板
- 前端页面描述
XTransform:树结构变换语言
XTransform 是专门为“一棵树变成另一棵树”设计的声明式变换语言。你描述“输出结构”,框架自动推导出从输入到输出的映射关系。
<xt:transform xpl:outputMode="xml"> <!-- 输入:一个扁平列表 --> <xt:source from="/app/entities" /> <!-- 输出:按模块分组后的树 --> <modules> <c:for var="entity" items="${entities}"> <c:if test="${entity.module != lastModule}"> <module name="${entity.module}"> <entities> <entity name="${entity.name}" /> </entities> </module> </c:if> </c:for> </modules> </xt:transform>
XTransform 是可逆计算中“Generator”的具体实现载体——它定义了如何将一种 DSL 结构变换为另一种 DSL 结构。
使用场景:
- ORM 模型 → GraphQL Schema
- 数据模型 → 前端表单描述
- API 模型 → gRPC Proto 定义
共享基础设施
三个语言共享同一套基础设施:
- 类型系统:统一的数据类型,与 XDef 类型系统一致
- 函数库:内置 200+ 标准函数,涵盖字符串、日期、集合、XML 操作
- 调试器:nop-idea-plugin 支持断点调试所有 XLang 语言
- IDE 支持:语法高亮、代码补全、文件跳转
与通用语言的对比
| 对比维度 | Java/Python | XLang |
|---|---|---|
| 操作对象 | 内存对象 | 结构树(XNode) |
| 解析序列化 | 每次都要 | 不需要 |
| 类型系统 | 静态/动态 | 面向 DSL 的类型 |
| IDE 支持 | 成熟 | 基于 XDef 自动生成 |
| 适用范围 | 通用 | DSL 变换 |
XLang 的设计哲学是:在 DSL 的结构层次上思考,而不是在通用语言的抽象层次上思考。这正是面向语言编程(LOP)在工具层面的体现。