Crate 架构
workspace 围绕共享内容模型组织:前端产生 Expr 森林,管线生成 Item 树,输出后端消费 Item,宿主装配资源与环境。本文描述当前实现的职责与依赖边界;Markup 的处理阶段和目标契约见 Markup 管线。
职责与依赖
箭头由使用者指向依赖。图中展示 workspace 内部的直接依赖;第三方库见各 crate 的 Cargo.toml。
notist-core:提供 Expr、RExpr、Item、Value、CheckedProgram、InstanceRecord、已检查表达式与待求值信息、函数定义、Registry、前端接口、DocumentExtension 阶段契约与诊断等共享语义模型,以及共同校验和单文档索引,不依赖其他 workspace crate。notist-syntax:提供原生 Markup 与 Code 的词法、解析和 AST,共享 Rowan 基础,并定义内容字面量的 ContentParser 委托接口;不生成 IR,不依赖语义模型或具体前端。notist-pipeline:依赖 core、syntax 与 eval,提供 Context(Registry、CodeProgram 与 ExtensionPlan)、静态 import lowering、共用 lowering、ContentLowering 接口、resolve / shape / materialize、Code lowering、具体实例检查及 Evaluate 编排。frontend.markup 组合原生解析与正文 lowering,frontend.markdown 使用 rushdown 解析并 lower Markdown;两者复用 Code 片段入口和共同转换。外部前端通过公开接口注册,pipeline 不静态依赖外部实现。notist-eval:依赖 core,解释已检查的 Item / TypedExpr,负责内容绑定、挂载、原生与 Data 调用、编译期求值、逻辑预算及局部实例缓存。EvaluationContext 提供定义、执行映射和缺失实例的检查回调,由 pipeline 的 Context 实现;eval 不依赖检查器、语法、文件系统或输出后端。Pipeline 保留来源合并、文档扩展调度及 evaluate 入口。notist-html:在 workspace 内仅依赖 core,接收 Item 与渲染配置,生成 HTML 并处理组件绑定。notist:依赖 core、syntax、pipeline 与 html,从 Markup / Code 的源码声明加载模块图,负责输出配置,将 Context 与配置、模块源码信息组合成 Environment,并提供 Vault、VaultIndex、查询、调试及内存 / 本地 / Worker 宿主接口,并通过 command_extension 将 stdio 外部命令适配为文档扩展。notist-ssg:依赖 notist 与 html,负责站点编译、路由、链接检查、模板、资源、Sass 和完整构建产物。Book 通过内置 SiteExtension 提供 SUMMARY 与导航数据,通过独立的内置主题呈现。default 与 book 主题的模板均存放在 themes 目录。Builder 为每次构建创建扩展实例,在 beforecompile 修改源码集合,在 beforetemplate 根据最终 Pages 修改模板 data;命令扩展以同一进程处理两个 JSON Lines 事件。完整接口见 SSG 站点扩展。notist-cli:依赖 notist、html 与 ssg,提供终端命令、构建、共享的发布与预览宿主及 LSP 入口。notist-grammar:位于 modules/grammar,独立解析文法记法并生成 SVG 铁路图,不依赖其他 workspace crate。
接口与实现边界
语法模块
notist-syntax 按语法域组织:
src/
markup/ 原生 Markup AST、lexer、parser、MarkupContentParser
code/ Code AST、lexer、parser 与语法片段入口
tree.rs 共同的 SyntaxKind、Lang、SyntaxNode、SyntaxToken
parse.rs 共同的 Parse 与 Diagnostic,从 crate 根导出
lexed.rs 源码与 token 范围存储
content.rs 内容字面量的解析接口与不透明前端语法产物
markup.parsedocument 与 code.parsemodule 分别产生 Document 和 Module 根。两套类型化 AST 共用 SyntaxKind 与 Rowan 节点类型,解析时分别构造树。tree.rs 定义共同表示,不保存一棵全局树;AST 访问器是 CST 的视图,不复制底层树。
Markup 将 Code 调用头和注解片段接入自身 CST。Code 的 ContentLiteral 保留括号与不透明正文,具体前端的内容树另存于 Parse.contents。树的关联与坐标规则见 文档前端。语法层不依赖语义模型或管线。
管线内置前端
notist-pipeline 将前端实现与共用转换按职责组织:
src/
context.rs Registry、CodeProgram 与 ExtensionPlan
extensions/ 文档扩展调度、阶段输出校验与执行映射校验
definitions.rs 单文件 CodeModule 与共享模块集合的 CodeProgram 视图
modules.rs BindingSpec、纯导入绑定、ModuleScope 与 LinkedModule
code/check.rs Code Expr 到 TypedExpr 的具体实例检查
code/content.rs 共享内容后端:IR₁ 到 Item、children 契约与注解
materialize.rs 挂载节点注解、生成 Item 森林与来源信息
frontend.rs 前端注册与选择
frontend/markup/ 原生文档与内容 lowering、NotistFrontend
frontend/markdown/ Markdown 解析、扩展语法与 lowering、MarkdownFrontend
lowering/ 共用注解、调用头、数据字面量转换与内容 lowering 接口
原生语法定义留在 syntax;Markdown 的 rushdown 适配与两种前端的 IR 生成由 pipeline 提供。Code 声明转换也复用 lowering 中的数据字面量转换。
模型与算法
Expr、Node、Value、定义模型、Registry、Frontend Trait 与诊断从 notist-core 的对应模块导入。core.evaluation 定义 CheckedProgram、InstanceRecord、TypedExpr 与 Invocation;NodeContent 区分内容与表达式,RExpr.expression 保存 Shape 前的已检查计算,不另设 pending 模型。这些类型只描述 IR,检查算法位于 pipeline,执行算法位于 eval。执行内核的内容值由共享代码与词法环境构成,挂载时直接生成最终 Node,不复制改写捕获表达式,也不往公共 IR 导出执行状态。Expr.Content 保存 Markup IR₁、注解 side list 和 body flavor,不依赖前端语法树。syntax 提供 Markup 与 Code 的解析和 AST 视图;pipeline 在第一阶段完成 Parse / Lower,第二阶段在具体实例环境中将 IR₁ 经共享内容后端转换为 Node 森林。
crate 根的 lib.rs 声明自身模块并公开入口;调用方按职责所在 crate 引用模块与算法。Context 从 notist-pipeline 导出;顶层 notist 公开供宿主使用的 Context、Pipeline、NotistFrontend、MarkdownFrontend、Frontend、syntax 模块和共享模型,同时组织查询、调试输出、资源与后端处理。
前端接口、默认实现与注册规则见 文档前端。
纯处理入口
Pipeline.analyze / inspect 是完整分析入口,宿主可拆成 lower 与 analyzelowered,以便在两者之间装配 import 作用域。后端显式接收 Context。前端的 Parse / Lower 只接收源码与语法选项,输出 import 声明列表、Expr 森林与并列的节点注解;Context 从 Resolve 开始提供函数定义,注解由 Materialize 在 Shape 之后挂载。Pipeline.compilemodule 接收内容方言、模块身份、来源路径与 .notc 源码,阶段一将 Markup 字面量 Lower 为 IR₁ 并嵌入 CodeModule 的 Expr 树;阶段二实例检查向共享 Markup 内容管线提供 IR₁ 与词法环境,生成 Item 森林。该管线从 Resolve 开始,不包含前端 Parse / Lower。Pipeline.analyzecontentliteral 接收内容前缀与 Context,直接返回 Item 森林、flavor 和诊断,并保留解析产物。Context 的 ExtensionPlan 在检查前、检查后与求值后运行文档 hooks,并在 Evaluate 中选择执行目标;replace 保留原始调用身份。renderer 接收 Item 与渲染配置。
CodeModule 共享保存单文件声明、按 DefId 索引的函数体与源码,只包含未解析 import 声明。宿主解析路径并加载依赖后,纯绑定器按 BindingSpec 和 ModuleExports 生成 ModuleScope,其公开表保存 ModuleId/DefId 和名称拥有快照。LinkedModule 组合两层产物,CodeProgram 按 ModuleId 共享持有模块和作用域;Registry 的数字索引共享同一份 DefinitionModule,名字索引只用于解析边界。检查、求值与导航通过当前 Context 定位定义和函数体,不从绑定结果中取得依赖实现。单文件编译按源码与编译上下文进行 comemo 复用,导入绑定按 BindingSpec 和跟踪的导出查询复用;这两种计算缓存不替代宿主输入加载。身份、修订和输出边界见 定义身份。
实例检查借用 Context 中的 CodeBody,将具体签名、已检查函数体和诊断保存为 CheckedProgram 的不可变记录。Context 可以复用成功的检查计划,求值结果由每次执行独立生成。
Evaluate 由 pipeline 调用 notist-eval,根据参数与词法环境执行已检查函数体,生成独立输出树。编译期求值复用同一引擎,缺失实例经 EvaluationContext 请求检查;来源合并与 after_evaluate 在 pipeline 完成。
求值器按内容边界处理错误,并累计步骤、节点与循环预算。延迟内容在挂载时执行。
这些入口在传入数据上完成解析、校验、转换和呈现。文件读取、import 图加载、配置解析与组件资源取得由 Resources / Environment / Vault 组织,规则见 本地模块、内容函数与输出绑定 与 文档扩展。
宿主与发布
Resources 表达宿主的资源输入视图。Environment 在 Parse / Lower 之后沿源码 import 图构建 Context,分别解析各条输出规则的局部 import 表,共享按规范路径加载的模块。它保留配置路径与模块源码,通过 context() 只读提供 Context;Vault 按规范路径与文档源码快照缓存 Arc<Environment>。DocumentHandle 绑定 Vault 配置身份、规范路径、共享源码和 Environment,供 SSG 后续求值与 HTML 绑定直接使用;句柄拒绝跨 Vault 或配置使用,求值时另核对 Analysis 的源码快照身份,纯 Analysis 不携带宿主环境。
Vault 在输入批次内复用模块装配与配置解析,逐文档建立词法作用域并校验扩展。模块文本与分析来源由 Sources 共享保存;PreparedInputs 将宿主准备的输入传给内存 / Worker 环境。
core 的 EvaluationInputs、InputRead 与 InputError 定义同步的窄输入接口;eval 的 read_text 原生实现只查询该接口并检查 UTF-8。顶层 notist 的 ResourceSession 负责读取授权、有界后端和批次内固定的字节/错误缓存;Vault、Book 和 SSG 共用会话,输入更新通过新批次接收。PreparedInputs 区分字节、确认失败与未准备资源,浏览器按 NeedResources 准备并重试,详见 资源输入与求值。
notist-ssg 接收 Vault 与 SiteConfig,负责站点构建及 Book 等扩展的调度;notist-cli 提供名为 notist 的可执行文件,负责命令行、终端输出、本地预览与发布操作。LSP 的 stdio 传输和编辑器坐标转换由 CLI 的 lsp feature 提供,默认启用;库保留分析管线、查询与共享 IR 数据。
notist.publication 提供资源复制、组件注册与发布 URL 计划,供书籍构建和单文档 HTML 导出共同使用。已知生成路径可先预留、再一次填入字节;预留与普通资源共用文件/目录冲突检查,未填槽位不能形成最终产物。
SSG 校验页面路由,建立文档索引,按序执行文档扩展与求值,再通过模板生成页面。独立的模板任务可以并行执行,结果按页面顺序合并。
CLI 的 publication 模块负责输出目录校验、暂存、替换与过期文件清理。构建失败时保留已有输出,产物完全相同时跳过替换。
HTML 接口见 HTML Rendering,书籍配置与构建见 Notist Book,Vault 内容边界与索引规则见 文档管线。