豫构包系统
本版供包格式检查。配置是普通 。豫 文件中的静态数据,只调用 parser,不执行配置代码。
完整包描述
日志。包。豫 所在目录就是日志包的根目录:
「名称」者『日志』也。
「所有者」者『豫言』也。
「版本」者『0.1.0』也。
「简介」者「典」【
「汉语」者『日志:分级记录运行信息,写到终端或文件。』也,
「文言」者『事有始终,录之以备考;分其轻重,书于屏与籍。』也,
「赋曰」者『夫事过无痕,录之乃存:轻者记于屏,重者书于籍;分级而列,按时而陈。他日检点,一一可寻。』也,
】也。
「说明」者「典」【
「汉语」者『包说明.汉语.md』也,
「文言」者『包说明.文言.md』也,
】也。
「类型」者「典」【
「种类」者『可执行文件』也,
「入口」者『入口』也,
「文件名」者『日志』也,
】也。
「依赖」者「列」【
『豫章』,
「典」【「所有者」者『他人』也,「包」者『终端』也,】,
】也。
名称、所有者、版本、简介、说明、类型为必填项;依赖、平台、许可、包含在、包含包按需要声明(平台见“平台”,许可见“许可”,包含在与包含包见“层级包”)。未知字段及重复字段报错。名称必须与包文件名一致,不允许改名后保留旧声明。
「简介」写成典,键为汉语、文言、赋曰,三键都须非空,讲整个包:汉语给通称与高层功能,文言讲缘起,赋曰扣住这个包做什么、怎么做(三段分工照提案 00029 第九节的篇首注释)。赋曰是一篇赋,主体写成对句,字数随包自有源码(不含子包)的行数定:不足三百行写二十八至五十六字,三百至二千九百九十九行写五十六至一百一十二字,三千行以上写一百一十二至一百六十八字。发现与检查显示汉语段。旧的字符串写法暂时也认。
所有者与版本
- 所有者是个人或组织的命名空间;官方所有者为“豫言”。不再设置作者字段。
- 包身份是结构化的“所有者+名称”,不能拼接为带保留字的字符串。所有者声明不能证明发布者身份;远程身份认证尚未实现。
- 版本当前接受三个无前导零的十进制整数,如
0.1.0。仓库本轮统一采用此初始包版本,它与编译器显示的版本不是同一字段。 - 此版没有远程下载、版本范围求解或锁文件。同一身份出现两份候选即报错,即使版本不同;绝不自动选较新版本。
发现和依赖
解析目标包的依赖图后,豫构将项目内包描述文件的跳转信息写入 .豫构缓存/豫构语言服务/<包文件相对路径去掉。豫>.语言服务.json,沿用普通源码的语言服务 JSON 格式。入口字符串(或路径段列表中的每一段)指向包内源文件;依赖字符串及依赖典中的包名、所有者指向解析所得的本地包描述文件,外部包目录中的依赖使用其绝对路径。层级包的「包含在」值指向父包的描述文件,「包含包」的每一项指向对应子包的描述文件,因此从父包或子包的描述文件都能跳到另一端;依赖闭包含库类型子包,所以为父包运行豫构时,其子包的描述文件也会写出跳转记录。重新运行豫构会更新这些记录,编辑器使用既有的通用跳转定义功能读取。
从启动时的 pwd 递归收集包文件,再解析候选包元数据形成身份索引。本地目录不决定所有者;两个不同所有者的同名包可共存。元数据损坏的候选包会报错,不再承诺无关包完全不解析。每目录仅允许一个包文件。
外部包集合由 --外部包目录 指定,第一层必须为所有者名目录;包配置的所有者必须与之相同。外部集合若位于项目内,在本地扫描时排除它。扫描跳过符号链接、缓存、Git 及 *_v0 目录。
依赖简写 『豫章』 只选声明包同一所有者的标准库;跨所有者使用含“所有者”“包”的典,不尝试其他所有者。命令行选择目标包时,省略 --所有者 仅在短名唯一时成立。
模块导入仍只写短包名,例如 寻观「日志」之「输出」之书。。可见范围为自身与直接依赖,短名冲突即报错。循环、重复和缺失依赖均报错。
库和程序
类型是带数据枚举,使用典表达。库写作 「类型」者「典」【「种类」者『库』也,「默认导入」者「列」【】也,】也。,必须写「默认导入」(见“默认导入”),不能携带入口或文件名;可执行文件分支必须同时提供入口与文件名,不能写「默认导入」。内部解码为不同构造器,不保存独立布尔开关和无意义的可选字段。顶层旧的库、程序、入口字段均报错。
可执行文件名是不带后缀的程序名,只占一个路径段,如 格式化(旧包写的 日志 这类以 yy 开头的名字照样可用);没写 --输出 时豫构按本机系统补后缀,如 macOS 上产出 格式化。林檎。豫行。入口是包内模块名或非空路径段列,不含扩展名;命令行 --输出 可以覆盖产物路径。普通依赖只能指向库,可执行文件包不可被依赖。
可执行文件分支可以另写「入口函数」,指定入口文件里的入口函数名,如 「入口函数」者『主』也;不写时默认「入口」(提案 00006)。豫构构建包自己的入口时把它传给编译器(--入口函数 名),命令行入口段或显式文件构建的入口用默认名。
可执行文件分支还可以写「界面」,区分图形软件与仅终端软件:「界面」者『图形』也 表示程序要显示面,在桌面上开成窗口;「界面」者『终端』也 表示程序只用终端与标准输入输出,在壳里用「行」运行;不写时为终端。包仓库只给有显示面的目标编图形软件,软件目录、市肆(软件市集)与桌面启动台按它把软件分成两类(提案 00046)。
默认构建库会编译其普通模块,排除测试、脚本、数据文件(名。数据。豫 随导入它的模块编译与检查)与子包,不链接程序,也不接受可执行输出路径。类型检查库遵循同一模块范围。显式文件或模块命令仍可构建测试、示例,不改变包的声明类型。
需要共享实现的程序拆成两个独立包。例如共享实现位于 库/译场/,类型为库;应用/豫言编译器/ 是依赖核心库的可执行文件包,原有自举入口路径不变。配置使用对象、字符串和数组,未新增任何序列头;种类也接受静态符号,但示例统一采用字符串以便映射 JSON。
所有模块路径都从包根解析,同包导入也必须带包名。单段导入(只写包名)按该包的“默认导入”展开,默认导入是空列表时报错,不自动合并源码。目录嵌套的包构成边界,不能通过外层包的模块路径访问其内部;但登记为子包者(见“层级包”)可经父包路径 父之子 引用。
默认导入
库的「类型」里必须写「默认导入」:条目的列表,决定只写包名的导入(寻观「X」之书。,或止于子包的路径)展开成哪些模块。
「类型」者「典」【
「种类」者『库』也,
「默认导入」者「列」【『语言核心/内建类型』,『数据结构/多态列』,『控制/惰性求值』】也,
】也。
- 条目是用“/”分段的包内模块路径(不含扩展名),或登记的子包名;子包优先于同名模块,同“层级包”的规则。条目是子包时,递归展开该子包自己的默认导入;写成
子包/模块则是该子包里的一个模块。条目不能重复,不能含“;”,每段都是合法名称。 - 「默认导入」是库的必填字段,值是列表,可以是空列表
【】:空列表表示这个包不能只写包名导入,此时只写包名的导入报“没有默认导入,请写明模块”。可执行文件包不能写这个字段。 - 展开按条目顺序:
寻观「X」之书。对每个叶子模块依次寻观(后者优先),并把X绑成这些叶子模块共同的命名空间,X之名逐个叶子模块查找;寻「X」之书。只绑命名空间;观本身不加载文件,只打开已经导入的叶子模块的名字。条目自己的模块命名空间不额外绑定,要用就显式寻。 - 展开成多个模块时,各模块不许导出同名定义,导入处报“默认导入的条目导出了同名定义「名」”。
- 条目是否存在、是否越过子包边界,到展开时才检查,报错和普通模块导入一致。底层文件按底层后缀、着色文件按着色后缀逐条目查找。
- 构建时
包记录带着这个列表;包上下文写第五版,见“层级包”。
设计理由与迁移见默认导入设计。
编译模式
编译模式由文件名决定,详见 底层模式:文件名带「。底层」的是底层文件(无垃圾回收,用于执行器、驱动等系统代码),即底层源 名。底层。豫、底层测试 名。底层。测试。豫、底层接口 名。底层接口。豫 与底层内建声明 名。底层内建。豫;其余文件是高级文件(有垃圾回收)。包描述写“编译模式”字段会报错。
底层文件的导入只找上述三种底层模块,高级文件导入底层模块时报错,所以底层代码只依赖底层库等含底层文件的包。同一个包里可以同时放两种文件,包的层级与模式无关。包入口与 豫构 文件 指定的文件两种都可以:编译器按入口文件名选择管线。底层可执行包的产物是 Wasm 模块。包上下文第三、四版的“底层”行现恒为空,格式保留给稳定编译器读取。
平台
可选字段「平台」声明包能用于哪些平台,以及在各平台上需要的外部依赖。写法与「依赖」相同:值是列,每项是平台名字符串(简写),或是含「名称」的典。例如只能在诺驿上运行、需要两个诺驿库的包:
「平台」者「列」【「典」【「名称」者『诺驿』也,「诺驿库」者「列」【『@kmamal/sdl』,『@kmamal/gpu』】也】】也。
典的「名称」必填;「诺驿库」与「系统库」可选,都是字符串列。诺驿就是 Node.js,诺驿库就是 npm 包;「系统库」写动态装载器认的文件名。例如按操作系统声明图形处理器的系统库:
「平台」者
「列」
【「典」【「名称」者『诺驿之林纳』也,「系统库」者「列」【『libcuda.so.1』】也】
,「典」【「名称」者『诺驿之轩窗』也,「系统库」者「列」【『nvcuda.dll』】也】
,「典」【「名称」者『诺驿之林檎』也,「系统库」者「列」【『Metal.framework』】也】
】
也。
平台树是固定的,子平台名是带“之”的整串:
林檎
林纳
轩窗
豫言系统
诺驿
诺驿之林檎
诺驿之林纳
诺驿之轩窗
- 不写「平台」时,包可用于所有平台,没有外部依赖。写了就只能用于列出的平台及其子平台;写『诺驿』即涵盖诺驿之林檎、诺驿之林纳、诺驿之轩窗。
- 父子平台都写了时,在子平台上两项的外部依赖取并集。
- 「诺驿库」只许用于诺驿及其子平台。名称按 npm 规则检查:只许小写字母、数字与
- . _ ~,可带一个@作用域/前缀(至多一个/,有/时须以@开头),长度不超过 214。 - 「系统库」只许用于确定了操作系统的平台:林檎、林纳、轩窗、诺驿之林檎、诺驿之林纳、诺驿之轩窗。名称只许 ASCII 字母、数字与
_ + - .,不许含路径分隔符。 - 豫言系统暂不收外部依赖,只能简写为
『豫言系统』。 - 平台名不在树中、典里有未知字段、同名平台重复、「平台」写成空列,都报错。报错文字带包描述文件路径、字段与值,例如
包配置:…/火星。包。豫:「平台」含未知平台名:火星(可写的平台:…)。 - 发现包时就解析并校验「平台」;构建暂时只解析与校验,不安装外部依赖。解析结果存在包配置的数据里:库
牌记的「包诸平台」按书写顺序列出各项,「包平台依」求包在某平台上的外部依赖(诺驿库名列与系统库名列),包不能用于该平台时得「可无」。 - 已删除旧的「原生」字段(C 源文件、包含目录与系统依赖);包描述写「原生」时报未知字段。
- 待办事项:诺驿库的版本范围、可选外部依赖、按指令集再分平台、浏览器与云工入平台树、依赖闭包的平台交集检查。
许可
可选字段「许可」写包的许可证,值是 SPDX 许可表达式字符串:
「许可」者『MIT』也。
「许可」者『AGPL-3.0-only』也。
「许可」者『MIT OR Apache-2.0』也。
- 值须非空、首尾不带空格,只许 ASCII 字母、数字、
. - + : ( )与空格;不合时报错,报错文字带包描述文件名与字段,例如包配置:火星。包。豫:「许可」只许 ASCII 字母、数字……。豫构只查字符,SPDX 标识符是否有效由用它的地方去查。 - 发现包时就解析并校验;库
牌记的「包许可」读出这个值,没写时得空串。 - 社区包仓库(
yuyan-lang/yuyan-community-packages)要求每个包都写「许可」,并只收它的许可白名单(见该仓库的说明);语言仓复制进社区仓库的包一律标AGPL-3.0-only。 - 这是新字段:旧的稳定豫构把它当未知字段报错。本仓自己的包描述要等 CI 一轮自举通过、连续版本换上支持它的原生工具链包之后才能写(CI 起步取连续版本里最新的包,见
AGENTS.md「包与接口」);社区包仓库钉自己的工具链,钉到支持它的包即可使用。 - 待办事项:本仓的包描述补写「许可」,再把它改为必填。
层级包
两个可选字段可把若干包成组:父包登记直接子包,子包声明自己的父包。以安全外壳的库为例:
「:安全外壳。包。豫(父包):」
「包含包」者「列」【『安全外壳密码』,『安全外壳协议』】也。 「:直接子包;顺序有意义:」
「:安全外壳协议。包。豫(子包):」
「包含在」者『安全外壳』也。 「:父包;不写即为树根:」
- 值都是同所有者的包名,每个包至多一个父包。层级是逻辑树,与磁盘目录嵌套无关,目录嵌套却未登记的包仍只是边界。包身份仍是(所有者,名称),全局唯一,不因层级改名。
- 严格两向一致:父包的「包含包」必须恰好等于声明「包含在」该父包的那些包。缺一向、多一向、父或子不存在、自含、重复、成环都报错(错误信息含
包含在的父包不存在、父包未登记子包、登记的子包不存在、子包未声明包含在、包含关系成环、包不能包含自身、包不能包含在自身之中、包含包重复)。 - 依赖父包 E,等于同时依赖 E 与 E 的全部库类型后代(子、孙……),也可只依赖某个子包。闭包顺序是先按声明顺序访问「依赖」,再按「包含包」的顺序访问库类型子包,最后是包自身;「包含包」的书写顺序应保持稳定。推论:子包不能依赖祖先(成环,报“包依赖成环”,环中含包含关系时并提示
子包不能依赖其祖先);可执行文件类型的子包不进闭包、不可导入,只登记归类。 - 导入语法不变。设当前包为 X,路径为
s₁之s₂之…之sₙ:s₁ 须是 X 自身或 X 直接依赖的包名,得到包 Y;自 s₂ 起,若某段是 Y 登记的库类型子包,就把 Y 换成该子包并继续,否则其余各段是 Y 的包内模块路径;没有余段时按 Y 的默认导入展开,规则同单段导入。例如只依赖『密壳』的应用写寻观「密壳」之「密壳约」之「通道」之书。。只依赖 E 时,E 的子包 A 不能作首段;寻以路径末段命名空间,所以寻观「E」之「A」之书绑定的仍是A。 - 子包优先于模块:父包自己的模块路径若以子包名开头(
<子包名>。豫或<子包名>/…),会遮蔽该子包,豫构报子包与父包模块重名。 - 豫构输出“豫构包上下文五”:在第四版基础上,每条记录在“父”与依赖之间再加“默认导入”一列(条目用“;”连接,空串表示没有默认导入)。第四版在第三版基础上,模式行恒存在,每条记录在文件与依赖之间加“父”一列。旧编译器不识别第四、五版。
- 第二至四版没有“默认导入”一列,读取时从各包的包描述文件里取该字段。
./豫构。林檎。豫行 检查 包名对有包含关系的包多打印一行<包标识> 包含在:<父包名或(根)>;包含:<子包名、…>(没有子包时省略“;包含:…”),无包含关系的包不多打印。./豫构。林檎。豫行 发现 --树按层级打印全部包:没有「包含在」的包是根,按包描述文件路径排列;子包按「包含包」的顺序每层缩进两格。每行是包名、相对项目根的目录(可执行文件另注“可执行文件”)与简介,末行汇总包数与树数。不带--树时仍逐行平铺列出。本仓库的层级安排见仓库包构建“包层级”一节。
设计理由、种子升级步骤与试点见豫构层级包设计。
写法
全仓源码只有一种写法:雅言(提案 00026、00027,见雅言文体)。包描述没有写法字段;包上下文一律写第五版,读到旧日带“文体”一列的第六版时略去这一列。
说明文档
简介用于发现与检查展示;正文存放在包内两个 Markdown 文件中,路径在“说明”中登记,文件名分别以 .汉语.md 和 .文言.md 结尾。包检查验证文件存在、归属本包且不通过符号链接或子包越界。
说明应包含用途、包身份、库接口或程序用法及维护提示。正文不参与编译,也不执行 Markdown 内的命令。本版不实现通用包分发归档;以后归档必须纳入已登记说明,不能声称现在已有该打包功能。
命令与升级
./豫构。林檎。豫行 发现
./豫构。林檎。豫行 发现 --树
./豫构。林檎。豫行 检查 豫构 --所有者 豫言
./豫构。林檎。豫行 构建 豫构
./豫构。林檎。豫行 类型检查 豫言编译器
./豫构。林檎。豫行 构建 豫构 -j 8 -- --do-not-load-cache
./豫构。林檎。豫行 -h
编译器参数放在 -- 后。监视面板区分待依赖、待槽位与运行中任务。
本版包字段与旧稳定豫构不兼容;升级须安装支持本格式的双种子,不能拿旧资产解析新包文件。当前检查版本不自动上传或替换远端发布,正式推广前必须更新稳定种子资产及其固定摘要。