Notist
On this page
  1. Code 函数系统
  2. 类型与阶段
  3. 签名与实例
  4. Where 约束
  5. 表达式与检查
  6. 内容字面量与边界
  7. 对 Item 与 Shape 的契约
  8. 错误冒泡与恢复
  9. 实现边界

Code 函数系统

Code 使用不可变值与静态类型,通过函数计算数据、生成内容。资源读取由宿主提供。语法见 Code Syntax,数据流见 文档管线。

类型与阶段

类型

表示与用途

Unit、Bool、Int、Float、String、Path、Array<E>、Dict

普通数据;参数、局部计算与 Item 字段

InlineContent、BlockContent

对应类别的有序 Item 森林

Type

规范化类型身份的编译期值;内建 Type : Type

Type 仅可作为 <...> 中参数的类型。类型值可参与类型注解、编译期传参、比较与分支;不得进入普通参数、children、返回值或数据容器。签名中的类型只引用具体类型、Type 参数及 Array 类型构造器,不执行任意用户类型函数。

静态类型与值的可用性分别记录。编译期参数为已知值;函数体检查中的普通参数与 children 仅有类型,即使某次调用传入字面量,也不会据此选择类型分支。不可变 let 绑定继承其表达式的可用性。

普通运算在操作数已知时计算;否则保留经过检查的运算。普通值函数调用默认保留到执行期。comptime expr 和编译期实参可执行已检查的普通值函数,要求所需输入完全已知;不能构造编译期内容或读取资源。read_text 的结果依赖宿主输入,不标记为编译期已知;间接调用也受同一能力限制。

签名与实例

fn foo<x: T>(arg: E)[children: C] -> O {
    expression
}

位置

实例化后的要求

<x: T>

x 为编译期值,T 可为 Type 或普通数据类型

(arg: E)

E 为普通数据类型

[children: C]

唯一内容槽,C 为 InlineContent 或 BlockContent

-> O

唯一确定的数据或内容类型

普通参数列表、编译期参数列表和 children 槽均可省略。花括号提供 Code 实现,分号声明签名,原生实现由宿主绑定。参数名在同一签名内唯一,普通参数可提供字面量默认值;? 声明可选参数,在 Code 函数体内以 Option 接收,见 Option 类型。

函数实现来源只有 Code 与 Rust native,与返回数据或 Content 正交。所有函数共用签名、参数绑定、编译期实例化、调用入口和结果检查,最后才分派到 Code 函数体或 Rust 实现。text、embed 与 readtext 都是 Rust-native 函数;readtext 返回 String,不建立 Item 边界。读取能力及宿主协议见 Path 与文本读取。

输入类别 C 与输出类别 O 独立。函数体按具体实例的 O 检查,不从函数体或返回上下文推导、合并或扩大返回类型。

编译期参数依次通过以下信息确定:

1. 显式编译期实参。2. 普通实参及 children 的静态类型,按参数位置与 Array 结构匹配推导。3. 声明默认值。

多个来源必须一致;缺失或冲突产生诊断。推导不读取普通参数值,不从期望输出倒推,也不对用户函数求逆。不存在于输入类型中的编译期参数必须显式提供或具有默认值。

函数按具体实例检查,实例由函数身份与编译期实参决定。默认实例化深度和求值调用深度上限为 32,实例数上限为 4096;每个内容求值边界及每次编译期求值允许 100000 步,数据结果上限为 16 MiB,内容节点上限为 100000。Rust 宿主可通过 Limits 调整。编译产物与检查过程见 Code 管线。

Where 约束

返回类型之后、函数体或分号之前可声明一个 where 表达式:

fn reference<T: Type>(target: T) -> InlineContent
where T == Path || T == String;

fn bounded<n: Int = 2>() -> InlineContent
where n > 0 && n < 8 { [有效范围] }

where 在显式实参、输入类型推导和默认值确定全部编译期参数之后求值,必须产生已知的 Bool;false 或类型错误拒绝该实例,不检查或执行其函数体。Code 与 native 声明使用相同规则。

表达式只能依赖本函数 <> 参数、字面量和内建类型名;允许由这些值计算的局部绑定,不能引用普通参数、children、模块函数或导入。所有分支都检查引用范围,不因短路或已知条件绕过。条件不读取资源。嵌套调用分别检查具体实例,不推导约束之间的包含关系,也不允许仅按 where 重载同名函数。替换目标实例同样必须满足其自身约束。

表达式与检查

Code 支持数据与内容字面量、名称引用、模块成员访问、函数调用、不可变 let、标量运算、if、match、for 和 comptime。模块顶层通过 import 加载依赖,见 本地模块。

分支条件

检查规则

编译期已知

只检查选中分支;match 必须匹配

运行期值

检查所有分支,正常结果类型必须一致;match 使用末尾 _ 覆盖剩余值

match 按普通 == 的类型与数值规则选择分支,编译期与运行期一致;实例缓存键仅用于区分编译期实参身份。

类型比较使用普通表达式检查,结果为 Bool;按规范化类型身份计算,且在实例检查中消去。函数可以在同一函数体中按具体类型选择不同实现,无需声明多个重载。

fn decorate<C: Type>[children: C] -> C {
    match C {
        InlineContent => strong()[#children],
        BlockContent => callout()[ #children ],
        _ => compile_error("decorate requires content"),
    }
}

compile_error 是编译器内建调用,消息必须为已知 String。检查器遇到它即记录诊断、使当前检查位置失败;内容插值边界隔离失败,其余位置的失败使当前实例不可执行。它不返回零值、Never 或错误内容,也不能作为运行期断言。运行期分支需要检查的每个分支同样遵守这条规则。

Array 可以省略元素类型,此时不能静态收窄为 Array<E>;已知数组按全部元素递归检查,空数组可按具体数组类型检查。编译期实参、默认值与普通实参遵守同一规则。

静态类型与值域检查分别执行。已知数据立即检查原生值域约束;待执行的实参在取得值后检查。所有影响 Shape 的信息必须在结构定稿前已知;heading 的 level 因此属于编译期参数。

内容字面量与边界

字面量的 Inline / Block 类别由 flank 判定,值域规则见 Types & Literals。类别决定片段结构:InlineContent 的根序列只含行内片段;BlockContent 按硬分隔划分内容片段,全 Inline 片段生成段落,单个 Block 表达式保留其森林。

内容值本身是森林,构造、绑定或返回值不额外建立字面量容器。Code 字面量内的 #name 求值得到内容后直接引入结果森林;有非空属性时才保留透明 Group,将整个结果放入 children,空结果也保留该属性目标。空注解与无注解等价。内部段落和 section 保持不变;数据必须通过 text 等内容函数显式转换。独立 Markup 的插值只允许内容函数调用,两种入口共用语法解析,在检查阶段区分合法位置。

插值前的行内或块注解落在这次挂载的分组上,标记整个内容贡献,不只标记第一个子节点,也不复制给所有子节点。内容内部原有注解保持原位置。同一个值可以多次挂载,各次分组的属性互不影响;空内容也有独立的属性落点。

fn marked() -> InlineContent {
    let part = [#strong[A]#emph[B]];
    [@(id: "part")#part]
}

Code 内容调用按属性决定是否保留 Group;原生内容调用生成实际元素,属性直接附于元素。显式 node 保留声明的节点边界。for 使用相同的挂载规则。

同一片段内不允许混合 Inline / Block 或并列多个 Block 表达式;空行或显式分段分隔片段,单换行不分隔。片段规则在解析调用的具体类型后检查,与函数体展开无关。

构造、绑定或传递内容值只保存森林与不透明调用;实际挂载呈现时才执行内容实现。局部绑定复用其计算结果,未使用的内容及未被返回的输入 children 不展开。

内容位置保存普通 Call 表达式,实参与 children 都是该调用的子表达式;调用对外类别取 O,输入契约取 C。求值后原生构造得到带具体字段与 children 的内容元素;Code 调用得到其结果森林,不默认保留原调用外壳。数据函数调用不直接产生内容节点。Code 直接内容调用与字面量插值共用调用绑定、输入类别和公开结构角色校验。

内容字面量先按所选 Markup 前端解析,再在实例检查时完成名称解析、类型检查与结构整理。处理流程见 Code 管线。

Markup 降糖生成的调用使用规范内置函数身份,不受模块同名函数影响;显式调用按词法 Context 解析。

列表、表格与行使用公开 item、row、cell 角色检查结构序列;BlockContent 本身不证明这些角色。node 具有内建结构透明契约;用户函数不能靠私有实现取得角色;同签名映射也必须保留 node 的结构透明契约。结构槽的方括号承载结构序列,检查不依赖其书写留白。

已检查的局部内容、for 模板及运行期分支保留内部结构证据,经 let/块尾值传递;Rows、Cells、Items 只能与相同角色或同类别空内容连接。空数组也检查模板。普通函数签名只承诺 InlineContent/BlockContent,不从私有函数体推断公开角色。证据由正常 Resolve/Shape/Materialize 产生,求值后的结构槽另作内核校验,不依赖 Renderer 或 after_evaluate 插入节点。

对 Item 与 Shape 的契约

阶段

内容树表示

消费者

求值前 Node 树

Item 内容或 Expr 计算;Call 的 children 是按 C 检查的内容表达式

默认查看、查询、索引、诊断与语言服务

求值后 Node 树

只有 Item:元素、透明 Group 或 Error

--eval 查看、渲染与导出

两阶段共用 Node,Item 是其兼容类型别名。Node 保存内容/表达式、属性、类别和源码位置;具体内容载荷为 ContentItem。原调用与实参保留在求值前的 Call 中;结果原生元素只保留具体实现的参数,不另存 target 或 evaluation。来源包含定义文件及调用展开链,不能把定义范围当作调用文档中的范围。

Analysis 保存求值前的树与 CheckedProgram。Evaluate 按保存的词法环境执行内容表达式,组合出最终内容树。内容可重复挂载,每次独立执行;后续同名绑定不影响已保存的内容。所有结果仍须满足内容类别与 row/cell/item 等结构约束。

Replace 扩展 在 Evaluate 中按同签名函数映射选择执行目标。求值前 Call 保持源码身份;结果元素记录实际构造身份,Code 实现的结果按通常规则引入,不额外保存 source/target 对。

错误冒泡与恢复

失败沿值与调用依赖向外冒泡,在最近的内容计算边界恢复为普通 Error 内容。错误节点保留类别、注解与来源,其身份为错误内容而非原函数调用;其余独立插值继续求值和呈现。已恢复的错误随森林挂载与转发,不再次冒泡。函数内部某个内容插值失败不会抹掉父函数的整个结果;只有父调用自身的计算失败才继续向外传播。

for 是受检的整体组装:任一迭代的计算或挂载失败会丢弃该循环已经组装的前缀,保留来源诊断,并将循环挂载恢复为错误;循环外的独立内容按内容边界处理。迭代次数与循环工作额度跨嵌套调用、重复挂载累计,不随普通内容调用的步数重置。

诊断的产生阶段与恢复状态分别记录。recovered 表示失败已被内容边界容纳,包含函数体解析、lowering、检查或求值错误;预览据此保留可呈现的结果。Error 节点直接按错误内容呈现,包括列表、表格和透明结构轴;呈现不依赖执行状态。

签名或环境无法安装属于加载失败。签名有效但函数体或某个实例失败,不移除无关定义;失败实现不执行。Code 检查、编译期计算和 Evaluate 的限额耗尽采用同一内容边界恢复规则。内容边界隔离执行步数,失败或超限不会耗尽独立兄弟插值的预算。调用自身的失败与依赖实现的诊断分别记录,已恢复或未使用的内容不会使成功的数据依赖失效。

实现边界

Registry 保存公开签名及 Code/Native 实现来源;Context 组合 Registry、CodeProgram 和 ExtensionPlan,不保存宿主文件对象或资源缓存。Checker 的可变会话状态保存当前模块、编译期实参、参数与局部绑定及 CheckedProgram;具体实例的 TypedExpr 保存在 InstanceRecord.body 中。文档入口将 CheckedProgram 随 Item 树保存在 Analysis,Code 文件检查入口仅导出声明、诊断与来源源码组成的 CodeAnalysis。Evaluate 可以为映射目标补查实例;需要资源时仅查询显式的 EvaluationInputs,文件或网络访问仍由宿主负责。

可选值模型见 Option 类型。