子语言设计
豫言用一套语法写出多种子语言:高级豫言、脚本、测试、宿主接口与应用接口、内建声明、着色语言、底层豫言、包描述,以及写成普通库的嵌入式子语言(如界面布局语言)。本篇写这些子语言共同遵守的设计原则,以及它们在一个程序里怎样配合。各后缀的逐条规则见文件后缀,着色语言的细则见着色语言,底层豫言的细则见 应用/豫言编译器/文档/底层模式.汉语.md。
各条以“则”编号。
则一:一种语法,一个前端
全部子语言都用豫言的标准语法书写,由同一个语法分析器读入,经同一套名称解析与类型检查(见词法与语法、类型与模块)。子语言用已有的语法形式表达自己的结构:定义 「名」者…也、签名 「名」乃…也、序列 「头」【项,项】、典 「典」【「键」者值也,…】。
因此格式化、高亮与语言服务(补全、跳转定义)对每种子语言都适用;人与 AI 学会豫言,就能读写全部子语言。
则二:后缀定种类
文件属于哪种子语言,由 。豫 前的种类段决定,如 入口。豫、基准。脚本。豫、逐元。着色器。豫、入口。底层。豫、标准库。包。豫。种类段决定四件事:文件里能写什么、无体声明由谁提供、编成哪种代码、导入时能找到哪些模块。
种类是文件的属性:一个包里可以同时放高级文件、底层文件与着色源,包的层级与种类无关。程序按入口文件的种类选择编译管线:入口是底层文件时,整个程序走底层管线;其余程序走高级管线,编成 WasmGC。
则三:子语言是豫言的子集
各子语言沿用豫言的全部语法与类型检查,各自的限制落在后端:后端接受本子语言能表达的写法,其余写法在编译期报错,报错写明原因。
- 底层豫言写整数、爻、小数、单精度、长整数、向量、一阶函数、递归与尾调用、可变格,直接读写线性内存;闭包、构造器、字符串值与异常在编译期报错。底层程序因此始终没有垃圾回收,可以放进内核与驱动。
- 着色语言写着色内建提供的标量、向量、矩阵与运算;着色目标没有的类型组合在翻译时报错。
- 宿主接口、应用接口与内建声明只写类型与
乃签名。 - 包描述只写字段的静态数据,豫构只解析它;把包描述当作模块导入时报错。
- 数据文件只写导入、签名与
「名」者 值 也。;值是字面量、「列」、组值与构造写法,构造写法的头与单写的名字是值构造器(立)。记录的字段可以取列,列里的元素可以是记录或组值,列还可以是列之列。函数、运算与其他调用在类型检查时报错,报错带文件、行、列并写明原因。
则四:一套类型系统
类型检查在共用的前端完成,然后才交给各子语言的后端,所以类型规则在每种子语言里相同:
- 参数标签在类型检查后擦除,高级文件、底层文件与接口签名都能用(见标签函数类型)。
- 依值类型照同一套规则检查,隐式值参数随类型擦除(见依值类型);着色语言用整数索引写标量宽度,如「定长小数」于「三二」是 f32。
- 接口签名用豫言类型书写,Wasm 层的签名是它向下的投影:宿主接口投影成 WasmGC 的带类型导入,底层接口投影成整数、长整数、单精度、小数与向量的数值签名。
则五:导入有方向
编译器按导入方的种类查找模块(见文件后缀缀二),子语言由此分层:
| 导入方 | 能导入 |
|---|---|
| 高级文件 | 高级模块、宿主接口、应用接口、内建声明、着色源、数据文件 |
| 着色文件 | 着色源、着色内建声明 |
| 底层文件 | 底层模块、底层接口、底层内建声明 |
测试只能被测试与脚本导入;导入脚本或包描述时报错;数据文件只供导入,当程序入口时报错。分层带来三个结果:垃圾回收只出现在高级程序里,底层代码可以进内核与驱动;着色源只依赖着色内建,整个译成图形处理器代码;高级代码用到底层代码与着色源时,都经则六所说的接缝。
则六:层与层经无体声明相接
子语言之间的接缝是无体声明:声明方只写类型与 乃 签名,实现由另一方提供,后缀写明提供者。
| 后缀 | 实现的提供者 | 编成 |
|---|---|---|
。接口。豫 |
宿主,或构建时绑定的适配 | 带类型的 Wasm 导入,模块名是所在包的短名 |
。应用接口。豫 |
应用或库 | 导出 |
。内建。豫 |
编译器 | 语言原语,签名按原语表核对 |
。底层接口。豫 |
宿主、平台后端或操作系统 | Wasm 导入,模块名是接口文件名 |
。底层内建。豫 |
编译器 | 就地生成的一条 Wasm 指令 |
。着色器。接口。豫 |
着色目标 | WGSL、MSL 的运算符与内建函数,PTX 指令 |
高级程序与底层程序在宿主接口处相接。高级程序调用 。接口。豫 里声明的函数;在原生与裸机上,这些函数由构建时绑定的适配与执行器实现,执行器用底层豫言写成,再经豫言操作系统底层接口(豫寓底榫卯/)或内核服务进入操作系统。以打印一行为例:应用调用标准库的打印,落到宿主接口的带类型导入;执行器里的宿主原语把它转成底层接口的「写」;平台后端发出系统调用 write。
着色源与高级代码在构建时相接。高级文件导入着色源,得到编译器在构建时生成的模块(虚拟路径 名。着色生成。豫),模块里是 WGSL、MSL、PTX 文字与各入口名的字符串常量;高级代码把这些字符串交给图形处理器编译、派发。
则七:统一的执行边界
产生代码的子语言都编到豫汇:高级文件编成豫汇(与 WasmGC 一一对应),底层文件编成底层豫汇(与不带垃圾回收的 Wasm 一一对应),两层共用一套豫汇模型与二进制编码器。原生、裸机、诺驿、浏览器、云工各后端并列,都从豫汇出发:编译器可以把豫汇输出成标准 Wasm,原生机器码由裸机后端(豫言操作系统/裸机/库/冶铸)从底层豫汇生成。豫汇模块可以存成汇编树码(名。树码),树码同时是编译器各阶段中间产物的编码(提案 00001)。
着色语言的目标是图形处理器:着色源在构建时译成 WGSL、MSL 与 PTX 文字,以字符串常量进入高级程序,随高级程序一起编成豫汇。
则八:配置、命令与数据也用豫言写
描述配置、命令、界面与数据时,直接用豫言:
- 包描述
。包。豫是写成豫言的静态数据,名称、版本、类型、依赖都是豫言的值。 - 数据文件
。数据。豫(提案 00028)是写成豫言的静态数据:顶层只放「名」者 值 也。,记录用构造写法「头」【「字段」者 值 也,…】。高级文件导入数据文件,得到带类型的顶层数据,字段按记录的签名检查;运行时下载或读入的数据文件由库「数据文件」只解析不编译地读写。工具与 CI 之间交换的清单、软件目录与工具链钉都写成数据文件。 - 壳的命令语言就是豫言(提案 00003):壳里输入的每一句是一句豫言,命令是调用库函数的句子,参数用「于」引出并带标签,如
「设分辨率」于「宽」曰「1920」于「高」曰「1080」;同样的句子写进。脚本。豫就是脚本。脚本运行与编译执行共用前端与代码生成,接受的程序相同,求值轨迹与输出也相同。 - 界面布局语言是普通的豫言库(
库/门面):元素函数对应 HTML,类型为「样式」的值对应 CSS;界面树可以译成 HTML 与 CSS,也可以交给自绘后端排版、绘制。 - 测试写成豫言源文件(
。测试。豫、黄金样例名其编号。豫),测试数据写成豫言的值。
这些子语言得到与普通代码相同的类型检查、格式化与语言服务。
例:张量内核里的配合
包「算阵」(库/算阵)一个包里用到四种子语言:
- 包描述
张量内核。包。豫写出包名、版本与依赖(「豫章」「点染」)。 - 图形处理器内核写成着色源,如
逐元。着色器。豫、矩阵乘。着色器。豫,导入「点染」的着色内建声明着色内建。着色器。接口。豫。 - 高级文件
内核表。豫用寻「算阵」之「逐元」之书。导入着色源,取(「逐元」之「着色源」)(WGSL)与(「逐元」之「金属源」)(MSL)造出内核描述,后端据此用统一的方式调用任一内核。 - 测试写在
测试/下的。测试。豫里,由全树测试发现、运行。
中央处理器上的同类内核写成底层豫言,放在包「中枢算阵」(库/底层库/中枢算阵)。
设计组件与源上下文
设计组件使用普通豫言函数。编译器在用户单态函数返回设计场景节点或文档块时,将实际参数值、函数定义与展开结果记录为组件实例。普通函数调用与构造调用共用函数体包装。整数、小数、字符串、爻、小数信号和字符串信号可以保存并恢复,反编译保留普通函数定义和调用。设计源的文档作品同时保存完整规范源码上下文,反编译恢复文档构造并重新分页。泛型、闭包参数与同名跨模块组件列为后续版本事项。
后续版本
以下两项已有提案,照本篇的原则设计:
- 文本子语言(提案 00010):序列的头可以是用户函数(自定义列宏),结构化文档用现有的序列写法书写,配套新库「文本」。
- 证明层(提案 00016):每个实现文件可以配一个
。证明。豫证明文件,写精化类型与一般依值类型;精化擦除把证明层擦成不带证明的程序,擦除结果须与实现层一致。