Nop平台为开发自定义DSL(Domain Specific Language,领域特定语言)提供了一套系统性的基础设施。其核心机制是通过XDef元模型定义XML格式的语法结构,然后自动生成AST节点类及解析器、验证器等工具。虽然XDef本身使用XML语法,但这并不意味着Nop平台中的DSL只能以XML格式表达。根据可逆计算理论,同一信息结构可以拥有多种表达形式,并能实现它们之间的自动、可逆转换。
本文将以Nop平台中的ORM模型为例,详细介绍如何为其引入Markdown语法格式,并实现Markdown与XML/JSON之间的双向无损转换。
1. 理解核心模块:nop-kernel
Nop平台支持DSL定义与实现的核心逻辑(即可逆计算理论的实现核心)完全集中在nop-kernel模块中。了解其子模块有助于理解后续的扩展机制:
- nop-core: 实现虚拟文件系统、XML/JSON等基础格式解析器和反射机制。
- nop-xdefs: 集中存放所有内置的XDef元模型。
- nop-xlang: 实现Xpl模板语言、XScript脚本语言及Delta合并算法。
- nop-record-mapping: 实现异构对象映射模型,是支撑Markdown与模型对象双向转换的关键。
- nop-markdown: 提供简易Markdown解析器,为DSL解析提供额外封装。
- nop-kernel-cli: 提供命令行工具(如convert和gen指令),用于模型转换和代码生成。
其中,nop-kernel-cli下的demo目录包含了自定义解析器和代码生成模板的示例。
2. 注册自定义DSL加载器
在Nop平台中,每种DSL文件格式对应一个唯一的fileType。全局加载器ResourceComponentManager会根据此类型选择对应的加载器。
要为orm.xml这类文件增加Markdown格式支持,需要创建注册文件/nop/core/registry/orm.register-model.xml。
<model x:schema="/nop/schema/register-model.xdef" xmlns:x="/nop/schema/xdsl.xdef"
name="orm">
<loaders>
<!-- 为XML/JSON/YAML格式注册内置加载器 -->
<xdsl-loader fileType="orm.xml" schemaPath="/nop/schema/orm/orm.xdef"/>
<xdsl-loader fileType="orm.json" schemaPath="/nop/schema/orm/orm.xdef"/>
<xdsl-loader fileType="orm.yaml" schemaPath="/nop/schema/orm/orm.xdef"/>
<!-- 为Markdown格式注册自定义加载器 -->
<loader fileType="orm.md" mappingName="orm.Md_to_OrmModel"
class="io.nop.record_mapping.md.MarkdownDslResourceLoaderFactory"/>
</loaders>
</model>
配置说明:
xdsl-loader: 用于配置XML/JSON/YAML等内置格式的加载器。
loader: 用于定义扩展加载器,支持自定义格式。
fileType: 文件类型标识符(如 orm.md)。
mappingName: 指定用于解析的映射规则名称。
class: 加载器工厂类的完整路径,需实现IResourceObjectLoaderFactory接口。
MarkdownDslResourceLoaderFactory返回的加载器实现了资源加载、保存以及DSL节点(XNode)的加载与保存接口,从而支持完整的双向转换功能。
3. 基于RecordMapping实现Markdown解析与生成
RecordMapping是Nop平台内置的对象映射机制,用于定义两个异构Java对象间的双向映射规则。在其基础上补充md:format等扩展信息,即可实现Markdown片段与DSL模型对象间的转换。
<definitions xmlns:x="/nop/schema/xdsl.xdef" x:schema="/nop/schema/record/record-mappings.xdef"
xmlns:md="md" x:dump="true">
<!-- 定义从Markdown到ORM模型的映射规则 -->
<mapping name="Md_to_OrmModel" md:titleField="displayName">
<fields>
<field name="displayName" from="displayName"/>
<field name="x:extends" from="extends" alias="Extends">
<schema stdDomain="string"/>
</field>
<!-- 映射“实体定义”章节,其内容为列表,每个子项使用Md_to_EntityModel规则 -->
<field name="entities" from="实体定义" alias="Entities" keyProp="name"
itemMapping="Md_to_EntityModel">
</field>
</fields>
</mapping>
<!-- 定义从Markdown到实体模型的映射规则 -->
<mapping name="Md_to_EntityModel" md:titleField="name">
<fields>
<field name="name" from="对象名" alias="Object Name">
<schema stdDomain="class-name"/>
<valueExpr>
value?.$fullClassName(rootRecord['ext:entityPackageName'])
</valueExpr>
</field>
<field name="tableName" from="表名" mandatory="true" alias="Table Name">
<schema stdDomain="prop-name"/>
</field>
<!-- “字段列表”和“关联列表”在Markdown中以表格形式呈现 -->
<field name="columns" from="字段列表" keyProp="name"
alias="Column List" itemMapping="Md_to_ColumnModel" md:format="table">
</field>
<field name="relations" from="关联列表" alias="Relation List"
itemMapping="Md_to_RelationModel" md:format="table">
</field>
</fields>
</mapping>
</definitions>
关键扩展属性:
md:titleField: 指定Markdown章节标题对应的模型字段。
md:format: 指定字段的Markdown呈现格式,目前支持table(表格)和code(代码块)。
一个遵循上述规则的Markdown格式ORM模型示例:
# AI模型管理
- extends: demo.orm.md
## 5 实体定义
### 5.1 NopAiProject
- 表名: nop_ai_project
- 类名: NopAiProject
- 中文名: AI项目
- 备注: 存储AI项目基本信息
#### 5.1.1 字段列表
|编号|标签|主键|非空|字段名|属性名|显示|中文名|英文名|数据域|标准域|类型|长度|小数位数|字典|备注|缺省值|控件|根节点级别|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | seq | true | true | id | id | X | 主键 | | | | VARCHAR | 36 | | | | | | |
| 2 | | false | true | language | language | | 项目语言 | | | | VARCHAR | 4 | | ai/project_language | 项目使用的编程语言类型:JAVA, PYTHON等 | | | |
此Markdown文件可被正确解析并转换为等价的XML格式,表格内容被精准映射为<columns>节点集合。
4. 利用元编程自动生成反向映射
Nop平台的元编程能力允许我们基于正向映射自动生成反向映射。例如,定义了Md_to_OrmModel后,可以自动生成OrmModel_to_Md。
在RecordMapping配置中引入生成逻辑:
<definitions xmlns:x="/nop/schema/xdsl.xdef" x:schema="/nop/schema/record/record-mappings.xdef"
xmlns:md="md" x:dump="true">
<x:post-extends>
<c:import from="/nop/record/xlib/record-mapping-gen.xlib"/>
<record-mapping-gen:GenReverseMappings/>
</x:post-extends>
<!-- 原有的正向映射配置 Md_to_OrmModel 等 -->
</definitions>
系统执行后会自动创建反向映射。如需微调,可以直接在文件中以相同的全量格式添加差量修正,例如调整反向映射中某个字段的生成表达式,这体现了可逆计算理论的灵活性。
5. 使用CLI工具进行转换与生成
配置完成后,即可使用nop-kernel-cli.jar执行模型转换和代码生成,验证双向转换效果。
# 将XML格式的ORM模型转换为Markdown格式
java -jar nop-kernel-cli.jar convert demo.orm.xml -o=demo.orm.md
# 将Markdown格式的ORM模型转换回XML格式
java -jar nop-kernel-cli.jar convert demo.orm.md -o=demo.orm.xml
# 基于Markdown格式的ORM模型生成代码
java -jar nop-kernel-cli.jar gen demo.orm.md -t=/nop/templates/orm -o=target
通过以上步骤,我们成功为Nop平台的ORM模型扩展了Markdown格式的支持,并实现了与标准XML格式的无损双向转换。这不仅丰富了DSL的书写形式,也实践了可逆计算理论中“同一逻辑,多种表象”的核心思想,显著提升了开发效率和模型的可读性。