# zhlang 语言规格(规范)

版本:2.40 · 2026-11-02 · 本文档是 zhlang 语言与自举编译器实现的唯一依据。v2.40(**整数溢出默认检查**):`+` `-` `*` 与一元 `-` 的有符号 64 位溢出在运行期抛「整数溢出」(此前静默回绕);新增 `环绕加/环绕减/环绕乘` 提供显式模 2^64 语义;`INT64_MIN % -1` 不再触发 #DE 崩溃;修复 GC 只扫 64KB 全局区(v2.39 已扩到 256KB)的误回收风险。以下为原 v2.39 发布说明——官网发布**最新自举版**(纯 zhlang 原生编译器,直接生成机器码)。v2.39(**网络库增强**):**新增网络内置**——`域名解析(主机)`(IPv4 字面量/hosts/DNS 全部 A 记录,`ZHC_DNS` 支持 `IPv4:端口`)、UDP 三件套 `UDP绑定(端口[, 地址])`/`UDP发送到(套接字, 主机, 端口, 数据)`/`UDP接收自(套接字[, 最多])`(接收返回 `{"数据","地址","端口"}`)、`套接字设超时(套接字, 秒)`(Linux 非阻塞套接字由运行时按每次收发独立计时,Windows 走 SO_RCVTIMEO/SO_SNDTIMEO)、`本端地址`/`对端地址`(→ `{"地址","端口"}`)与 `关闭写端`(SHUT_WR);三端 x86_64/aarch64/PE 的 WinSock 表补齐 sendto/recvfrom/getsockname/getpeername/shutdown。**标准库**——`网络.zh` 新增 `解析网址`/`拼查询`/`表单编码`/`取网页跟随`/`网页请求跟随[最多]`/`取JSON`/`提交表单[完整]`/`提交JSON[完整]`/`请求头`/`下载`/`读全部[最多]`/`解析Cookie`/`Cookie头`;新模块 `网络增强.zh` 封装 UDP/DNS/超时/地址与 `UDP请求[超时]`;`服务.zh` 支持 multipart/form-data(`表单文件`/`表单值`/`上传文件`)、SSE(`事件流`/`发事件[名/编号]`/`发注释`)与连接接管(`请求["连接"]`/`请求["余"]`,处理函数置 `请求["接管"]=真` 后框架不再写 HTTP 响应,供 WebSocket 等协议使用)。**HTTP 客户端**原生后端补上 10 秒收发空闲超时。顺带修复 PE 两个潜伏 bug:字符串字面量缺 C 终止符导致 `GetProcAddress("recvfrom")` 失败、手写 `imul` 漏 REX.R 把分配指针乘坏(Windows 超时失效)。套件 316/0;三端网络用例输出逐字一致,自举与交叉自举见开发记录。**服务端加固(2026-11-01)**:`服务.zh` 增加读超时、最大连接 + 503 背压、`停止`/`已停止`/`活动连接` 与停机排空;静态文件支持二进制、ETag/If-None-Match、单区间 Range;访问日志按状态码/耗时着色并统一为 `[状态]` 标签;新增 `彩色` 配置与 `NO_COLOR`/`ZHLANG_COLOR` 环境变量。顺带修复原生运行时全局暂存区重叠:终端探测 termios 工作区由 `0x150`、保存区由 `0x110` 迁到 `0x210`/`0x250`,不再覆盖 GC 表指针/计数(`0x168`/`0x170`)——真实终端下调用 `是终端()` 会段错误。前端改为 `@引入` 可写在任意顶层位置、装饰器按声明处源码顺序应用。**三端一致性补丁(2026-11-01)**:aarch64 后端 `/` 补除零检查(此前 `1/0` 静默得 `inf`,x86/PE 均抛「除以零」;回归 `tests/原生/浮点除零.zh`);终端原始模式标志由 `0x1B8` 迁到 `0x208`,不再与 `0x1A0` 的 sigaction(32B)重叠;服务框架补 Windows PE(wine)三项回归(纯内存路由/静态/装饰器、multipart/SSE、真实 WinSock 503 背压与停止排空);多 worker 时钟暂存由共享全局 `0x80D0` 改为每 worker `+96`(旧实现会和 `nanosleep` 的 timespec 互相覆盖,hub 睡掉数天并卡死 GC 停世)。**标准库补强**——数学/集合/时间/随机/文件/路径工具/编码/摘要/进程新增一批 API(见 §6);`格式化时间` 补齐 `%j/%w/%z/%a/%A/%b/%B` 等;非 ASCII HTTP 头值统一 `%XX`;`执行` 真正透传 stderr,`-d` 断点回 stderr;`建目录` 递归与 `真实路径` errno 原因;随机种子混入单调时钟/栈地址。完整套件 **343/0**。v2.38(**REPL 隔离与 TLS 诊断**):**REPL 会话目录按微秒时间戳 + 随机数命名**——原生运行时的 `随机数` 只以秒级时间做种子,同一秒启动的两个 REPL 会共用 `/tmp/zhc-repl-自举-<N>` 的 `会话.zh`/`会话.bin`,符号与输出跨会话污染(实测 4/4 复现);现目录名带 `当前时间()` 的微秒部分,进程间彻底隔离。**REPL 命令在续行中生效**——`:清`/`:重置`(`:reset`/`:clear`)、`:帮助`、`:会话`、`:退出` 不再要求「不在续行中」;全角冒号 `：` 与英文别名同义,续行中 `:清` 会放弃未完成输入并清空会话,旧符号不再残留。**TLS 告警细分**——服务器告警解析为 `TLS:服务器告警 <名称>(<编号>,<级别>)`(RFC 8446 告警表,close_notify 按正常关闭处理);记录类型不符/未知内层类型带实际编号;证书错误区分自签名不受信、链无法到受信根(含叶/签发者 CN)与主机名不匹配(含期望主机与证书 CN);`服务器未选 ChaCha20/TLS 1.3` 带收到的密码套件/版本。v2.37(**原生后端诊断**):**未捕获运行时错误带行号与调用栈**——每个非运行时函数帧保存函数名、声明行与当前语句行;未捕获错误打印「运行时错误(第 N 行):消息」,随后按「调用栈(由内向外)」打印 zhlang 函数帧(最内层 64 帧,更深注明省略),每协程独立。行号与帧元数据放在机器栈帧里(`[rbp-16]` 魔数 / `[rbp-32]` 函数名 / `[rbp-48]` 声明行 / `[rbp-64]` 当前语句行),运行时函数不写元数据,错误沿 rbp 链向上取最近用户/标准库帧,因此无全局槽与并发竞争,协程切换与 `捕捉` 恢复 rbp 天然隔离。只有「最终」块原样重抛时,`rt抛出` 在发现处理者时把行号与帧快照进协程 `+416` 捕获区(1080 字节),重抛沿用最初快照;普通新抛出即使复用同一字符串字面量也重拍活动栈。被 `捕捉` 的消息改为纯文本(去掉「运行时错误:」「原生后端:」内部前缀),与 C 版语义一致;未捕获打印器全程只用原写、不分配堆内存,内存不足时也能输出。x86_64(ELF/PE)与 aarch64 三端一致。实测三端同一用例输出逐字相同;套件 307/0;自举全过(stage1==stage2、8 worker、Windows 交叉/自举);6000 项链输出正确。明细见 `开发记录/2026-10-31-v2.37-ZL17-错误行号与调用栈.md`。v2.36(**编译器修复**):**超长二元链不再栈溢出**——原生后端 `生成二元` 改为**显式帧栈后序生成**:左结合(`a+b+c…`)与右深括号(`1+(1+(…))`)的递归深度都不再等于表达式项数,求值顺序与递归版完全一致;`&&`/`||` 短路子树仍交给 `生成逻辑`。前端同时给表达式嵌套设 **3000 层上限**,超过报编译错「表达式嵌套过深(超过 3000 层,请拆分或改用中间变量)」,不再在约 1 万层时以裸「协程栈溢出」崩溃。实测单条 31,500/32,000/50,000 项 `q0() + q1() + …` 表达式均可编译:32k 143s、50k 196s(峰值 3.7GB),产物求和正确;x86/aarch64/PE 三端 6000 项链输出一致。v2.35(**编译器修复**):**恢复局部变量遮蔽模块级全局**——v2.34 把函数入口灌入作用域的全局名误当作同作用域声明,函数内 `变量`/`常量` 与全局同名时误报「变量「x」在当前作用域重复声明」(违反 §2.3 对遮蔽的规格承诺,并使教程 `网络.zh` 与 HTTP 服务用例编译失败);现 x86/aarch64 两后端判重时放行既有全局条目,同作用域内真正的重复声明仍报错。**版本字符串同步**——`--version` 由滞后的「规格 v2.32」改为 v2.35。v2.34(**语言/原生后端**):**指针/对象成员访问 `->`**——`p->字段`、`p->方法(…)`:基若为指针(`&变量`、返回指针的函数/方法)先解引用一层,基若已是对象引用(类/结构体/字典,含函数与方法返回值)则直接访问;可链式(`p->a->b`、`f()->x`),属性可读写与复合赋值;三端(ELF/PE/aarch64)一致。**平台修复(三端对齐)**——aarch64 栈守护序言把「TLS 就绪标志」当指针二次解引用、`cmp sp` 用错编码(移位寄存器形式的 31 是 XZR),导致所有 a64 程序启动即崩;通道改用独立运行时标签(9,原与指针共用 8)后 `类型(通道(…))` 返回「通道」、`打印` 输出「通道:0x…」,指针打印「指针:0x…」、函数值打印 `<函数>`;真值规则对齐 §2.2(数组按长度、字典按条目或原型为真,函数/指针/通道恒真);类型标注补齐 `指针`/`函数`/`通道`;aarch64 补复合成员赋值(`p->x += …`、`d.x += …`)。v2.32(**原生运行时/编译器**):**协作式网络与睡眠**——`休息` 恢复为**毫秒**语义并接入调度睡眠队列;Linux 套接字(监听/连接/accept 产物)改非阻塞,`接受连接`/套接字收发/HTTP 收全部/纯 zhlang TLS 读写遇 `EAGAIN` 时 `休息(1)` 让出 worker,单 worker(`ZHC_THREADS=1`)下服务端并发可用;aarch64 补 `fcntl`/`clock_gettime` 系统调用号翻译,Windows `休息` 走 `Sleep`。**调试断点等级名**——`@断点调试(信息, …)` 与 `error/warn/info/debug` 等名称等价于整数(运行时按名称显示/过滤)。**中文切片与负索引**——切片按码点取边界,字符串与数组负索引读写。**`启动`** 对函数值/内置函数给明确编译错(规格只允许命名函数)。**死锁检测**——无就绪、无睡眠且无 worker 在跑协程时报「死锁——所有协程都在等待」。**除零文案**去掉内部 `原生后端:` 前缀。**aarch64 常量压缩**——MOVZ/MOVK 只发非零块,空程序产物 514KB→403KB、宿主编译器 17.3MB→14.2MB;`发32`/`发4`/`发8` 热路径。v2.31(**原生后端**):**模块级全局变量**——入口模块顶层 `变量`/`常量` 对所有具名函数、类方法、闭包可见并共享同一存储(闭包/方法按地址捕获,读实时值);函数参数/局部变量同名时遮蔽;全局槽位于 BSS `0x9000` 起,GC 根扫描三端统一 64KB(PE 原只扫 4KB)。**函数级按需发射**——只生成入口/显式引入模块及其引用到的运行时、隐式标准库函数与类方法挂载;空程序产物 1.99MB → 385KB,stage0 编译 7.6s → 2.3s;1 万行程序 50.5s → 10.0s。**字节发射快路径**——`发字节` 实例方法直写缓冲,替换 1600+ 处静态 `发` 调用;自举 stage1 编译自身 4 分 15 秒 → 54.6 秒。v2.30(**原生运行时/编译器**):**GC 性能与停世修复(原生后端)**——回收预算改为自适应:每次回收后预算 = max(存活 × 2, 32MB)(与 §5.4 既定口径一致;此前原生实现误为固定 32MB,编译期反复全量扫描,原生编译器编译冒烟程序需 260s);页索引由 64KB 页改为 4KB 页(页内平均块数约 127 → 8,单字地址查找链缩短一个数量级);**修正 GC 代际竞态**——上一轮被迫停住的 worker 在新一轮 GC 中不再计数,收集者等不齐停稳数 → 全体 worker 挂在 futex 上的死锁(现按 GC 代际号逐代计数)。实测原生编译器编译冒烟程序 260s → 17.3s(15 倍,环境见开发记录)。**原生后端 `+` 对齐 §3**——任一侧为字符串、另一侧整数/浮点自动转串拼接(此前仅「串+串」);布尔/数组/字典与字符串混搭报「运行时错误:「+」不支持 X 与 Y」。**M:N 并发异常隔离**——异常处理链头与异常值改为**协程级**状态(此前为全局槽,
多 worker 并发 `尝试/捕捉` 会互相踩踏:最小复现修复前 16/20 错乱/崩溃,修复后
0/20);x86(ELF/PE)与 aarch64 三套后端一致。
**错误体验**——未定义变量/函数/结构体按编辑距离给出「你是不是想写「X」?」;新增 `大写`/`小写` 别名(同 `转大写`/`转小写`)。v2.29(**原生后端**):**JS 后端移除**——实验性的 `--emit-js`(zhlang→JavaScript)从维护侧删除(自举驱动分支、`JS生成.zh`、`runtime/js` 内嵌、js-test 对拍);`标准库/JS.zh`(用 zhlang 生成 JS 源码的库)不受影响;冻结的 C 版 stage-0 引导器仍保留其内嵌实现。**GC 阶段 2(保守标记-清除 + 空闲链复用)**——分配登记(阶段 1)保留,每次分配按阈值(32MB,按距上次存活的增量)触发回收:根 = 全局区 + 各协程活跃栈区间 + 协程上下文寄存器,全局/栈保守扫描(8 字节粒度,凡落在已登记块内即视为指针),协程栈整块跳过改按活跃区间扫描;未标记块进按大小分级的空闲链表(16..4096B 256 类 + 大块单链),后续分配优先复用(小块复用清零);页节点/标记栈溢出本轮不回收,表满永久停用 GC;`堆统计` 的 `字节` 改为当前存活字节;新内置 **`堆回收(heapcollect)`**(强制触发一次完整回收)与 **`原栈指针()`**(GC 根扫描用:当前栈指针,无英文别名);类字段声明支持 `变量`/`常量` 前缀(与 `属性`、裸名等价;纯 zhlang 前端,C 版 `zhc`/标准库仍用 `属性`)。见 §5.4。v2.28:**文档注释与 HTML 文档**——`/** … */` 文档注释(紧邻声明或作为模块文档),标签 `@作者/@时间/@说明/@参数/@返回/@示例/@弃用`;`tools/文档生成.zh` 生成自包含响应式 HTML(自动/桌面/移动布局、侧栏、搜索、深色模式),见 §3.5;v2.27(**原生后端**):**调试断点**——`@断点调试[(等级, 内容)]` 指令(等价函数 `调试断点(...)`;等级 错误=0/警告=1/信息=2/调试=3),`-d`/`--debug` 调试运行(编译到临时文件直接执行、不留产物;加 `-o` 则产出调试二进制)才生成代码,运行时向 stderr 打印 `断点 文件:行 函数 [等级] 内容`,并按 `ZHLANG_DEBUG`/`ZHLANG_DEBUG_LEVEL`/TTY 决定过滤与命令循环(见 §6.10);缩进允许 **Tab**(按 8 列制表位,与空格按视觉宽度对齐);v2.26(**原生后端**):新增无符号 64 位 / 64×64→128 intrinsic(`原乘128`/`原乘高64`/`原乘低64`/`原无符号右移`/`原无符号比`/`原无符号除`/`原无符号余`,含英文别名;见 §6.9),SHA-512/SHA-384 改用单 64 位字实现(逻辑右移走 intrinsic),哈希吞吐约 3–4 倍;v2.25(**原生后端**):**接口**——顶层 `接口 名[(父接口, …)]` 只写方法签名(方法行可写 `名字(参数[ 类型])`,可省略 `函数`),Go 式结构化匹配,在带接口标注的变量声明/赋值与 `是(值, 接口名)` 处运行时检查(方法名 + 参数个数;类方法按含隐式 `本` 的个数,普通函数值按原个数);接口本身无方法体,不需要 `implements`。**参数类型标注**——函数/方法与接口签名里的参数可写 `名字 类型`,`整数/浮点/字符串/布尔/数组/字典` 按标签、结构体按原型链、接口按方法集,在被调函数入口检查(错误文案与变量标注同口径);接口匹配仍只看方法名与参数个数,签名里的类型由实现方法自己把关。**类可以没有属性**(纯行为类/接口实现)。**原生网络**(三端):`连接到/套接字发送/套接字接收/关闭套接字/监听/接受连接` 与 `取网页/网页请求`(HTTP/1.1,支持 chunked);Linux(x86_64/aarch64)走纯 syscall,https 走**纯 zhlang TLS 1.3**(证书链/主机名校验,信任库 = `ZHLANG_CA_FILE`/`SSL_CERT_FILE` → 系统 CA → 内嵌 `ca-bundle.crt`;`ZHLANG_HTTPS=curl` 可强制系统 curl);Windows PE 走 WinSock,https 经内置 WinHTTP(系统 TLS/CA,无需 curl)。套接字 Linux 为文件描述符整数、Windows 为 SOCKET 句柄;Linux DNS 内置(IPv4 字面量 → /etc/hosts → ZHC_DNS → resolv.conf 多路径含 systemd/Android/$PREFIX(Termux) → 8.8.8.8 兜底),Windows 用 gethostbyname。原生运行时不支持 GC,网络资源由显式 `关闭套接字` 释放。**原生并发**(三端):协程/通道/选择在 Windows PE 也已启用(此前编译期拒绝)。**自定义 User-Agent**:`网页请求` 的头字典传 `User-Agent`(键不区分大小写)即覆盖默认 `zhlang-native/1.0`;未传时 Linux HTTP/curl 与 Windows WinHTTP 都发默认值。v2.24:C 版 `zhc` 是 stage-0 引导器,不实现 §3.5 的 C 混写。v2.24:**块语句可选冒号(Python 风格)**——`如果`/`否则如果`/`否则`/`当`/`循环`/`尝试`/`捕捉`/`最终`/`选择` 及其臂(`接收`/`发送`)的条件或参数之后可写可选冒号 `:`,与换行等价;三端(zhc/自举/原生)解析器一致。v2.23 增补:**原生编译器自举与 Go 式多端**——纯 zhlang 原生编译器(`自举/原生.zh`)可编译自身(stage0→stage1→stage2 不动点,`make selfhost`),同一二进制内建 x86_64 ELF/PE 与 aarch64 目标;`--target` 接受 Go 风格 `系统/架构`,`ZHC_TARGET`/`ZHC_GOOS`+`ZHC_GOARCH` 环境变量与 `--targets` 目标表(`自举/驱动/目标.zh`,含能力说明);Windows PE 目标补齐文件 IO/参数/环境变量/程序路径,路径与命令行全部走 UTF-16 宽字符 API(中文源码名/参数/文件名可用),PE 堆改为 bump 分配器;Windows PE 交叉自举与「Windows 宿主 → Linux ELF」已验证(见 §6.8)。v2.23:**C 混写(自举编译器)**——`@C` … `@C结束` 内嵌 C、`@C "文件.c"` 引入 C 文件、`@外部("C符号") 函数` 按 `zhv_t` ABI 调用 C;C 侧可用运行时 API,并可通过函数值 + `zh_call` 回调 zhlang(见 §3.5;C 版 `zhc` 与原生后端不支持)。**标准库目录与命名迁移**——仓库标准库目录由 `std/` 改为顶层 `标准库/`;隐式模块改名为 `字符串.zh`/`编码.zh`/`容器.zh`/`数学.zh`/`摘要.zh`(模块名随基名),可引入层新增 `网络.zh`(网络内置的命名空间包装:同名函数内部用 `httpget`/`httpreq`/`tcpconn` 等英文别名调用内置,避免同名递归;原生后端尚未移植网络)。**裸名标准库引入**——`@引入 名字 [为 别名 | 只 名字, …]` ≡ `@引入 "名字.zh" …`:先相对引入文件找同名 `.zh`,找不到再走标准库查找路径(磁盘 `标准库/` + 内嵌清单);C 版、自举版与原生后端解析器一致。**跨模块类方法挂载**——顶层 `@类方法(模块.类名)` / `@类函数(模块.类名)` 可给已加载模块中的类挂实例/静态方法(模块可写真实名或本文件别名);解析期不要求同类声明,名字解析期校验目标是类、非私有、非标准库模块;同一方法跨模块重复挂载为编译错(同模块保持后者覆盖);挂载在模块初始化阶段执行,所有结构体/类模板先于模块初始化创建。v2.22:**编译器与运行时源码全面面向对象化**(语言行为不变)——自举编译器拆为 `类 词法器`(词法)、`类 语法器`(语法/AST/转储)、`类 解析器`(名字解析)、`类 生成器`(C 代码生成)、`类 驱动`(CLI/REPL),纯辅助函数为 `@类函数` 静态方法;原生后端为 `类 原生x86后端` / `类 原生a64后端` / `类 原生驱动`,原生运行时高层函数(浮点/JSON/正则/文件 IO/高阶字符串)为 `类 原生运行时`,底层字符串/数组/字典原语保留模块级(类方法挂载自身依赖字典原语,存在循环依赖);标准库此前已 OOP。配套语言能力:`模块.类.静态方法(…)` 链式调用、`引入 … 为 别名`(允许别名等于模块自身名)、入口 `main`/`主函数` 的整数返回值作退出码、类初始化方法 `函数 初始化` 与命名构造。主协程栈提升到 256MB(虚拟预留,按需提交)以适配 OOP 解析器更深的递归。`examples/` 全部改为面向对象写法;回归微用例保持按特性编写(测试目的不是演示风格)。v2.22 增补:可引入标准库 **`标准库/同步.zh`**——纯 zhlang 通道实现的互斥锁/条件/读写锁/等待组/一次(见 §6.5);**`抛出对象` + `标准库/错误.zh`** 类型化错误(见 §3.4/§6.6;跨模块类继承仍不支持,错误类型走原型链 + 类型标签);**`最终`(finally)块**——离开尝试语句的每条路径(正常/捕捉/重抛/返回/跳出/继续)都会执行(见 §3.4;x86_64 原生后端同样支持);**Windows 控制台的 REPL 行编辑/语法着色**——运行时用 ReadConsoleInputW 取键并开启控制台 ANSI/VT 处理,旧系统或输出重定向时回退 `读入`。v2.21:**类的初始化方法(构造)**——类体内可写至多一个 `函数 初始化(参数 [= 默认], …)`:构造 `类名(实参…)` 先按字段声明填默认值/零值,再执行初始化方法,整个表达式的结果恒为 `本`;初始化方法体内 `返回` 只能不帶值(带值编译错),参数支持默认值,缺参/未知/重复参数为编译错;有初始化方法时**命名构造 `类名{键: 值}` 的键对应初始化参数**(无初始化时仍对应字段名);子类初始化方法执行前**自动先调父类初始化**(父参数按同名匹配、缺失且无默认值则编译错),子类没有自己的初始化时继承父类签名;跨模块构造改写为类所在模块的合成工厂并补参数默认值(跨模块命名构造仍不支持)。初始化方法挂载在隐藏名 `__初始化_类名` 上,不可显式 `对象.初始化(…)`;合成工厂名 `__构造_类名`(`__` 开头为内部保留名)。三端(zhc/自举/原生)一致。v2.20:**结构体声明支持花括号**——`结构体 点 { x, y }` 与缩进式声明等价(字段至少 1 个、不得重复;花括号内换行被忽略,字段以逗号或空白分隔);**命名构造** `点{x: 1, y: 2}`(键为字段名,标识符或字符串,顺序任意):缺省字段补零值(结构体)或类默认值(类),未知字段/重复字段为编译错,命名与位置构造不可混用;命名构造目前只支持本文件已声明的结构体/类(跨模块用位置构造),解析期展开为普通构造调用,三端(zhc/自举/原生)一致;位置构造 `点(1, 2)` 不变。**标准库改为面向对象实现**——`标准库/*.zh` 内的实现改为隐式模块中的私有工具类 + 静态方法,公开内置名保留薄包装(行为、错误文案、行号口径不变);结构体注册 id 改按实际发射顺序重编(按需发射跳过标准库模块时不再出现 id 空洞)。**`模块.类.静态方法(…)` 链式调用**与 `引入 … 为 别名` 同名写法(允许别名等于模块自身名)加入;链式调用在编译期绑定静态方法,三端语法一致(原生后端仍受「模块内结构体/类未移植」限制)。**可选入口 `main`/`主函数`**——入口模块没有顶层可执行语句时自动调用无参入口函数(整数返回值作退出码,非整数/无返回按 0);有顶层语句时不自动调用(三端一致;`main` 与 `主函数` 不能同时定义,自动调用时不得带参数)。v2.19:**类语法移植到 C 版与自举编译器(继承/封装/多态)**——`类 名[(父类)]` + 缩进类体:字段可写 `名字 [类型] [= 默认]`,也兼容 `属性 名字 [类型] [= 默认]`;类体内 `函数 方法(参数)` 为实例方法(隐式 `本`);类体内的 `@私有` 表示该类**仅当前文件可用**(模块外限定访问与选择性引入均报编译错)。顶层新增 **`@类方法(类名)`**(实例方法,注入 `本`)与 **`@类函数(类名)`**(静态方法,不注入 `本`,以 `类名.函数(…)` 调用);`函数 类名:方法(…)` 旧写法保留。构造调用自动补零值/默认值(无类型→假,`数值`/`整数`→0,`浮点`→0.0,`字符串`→`""`,`数组`/`字典`→空);子类字段含父类字段且可重写继承字段的默认值;类模板原型指向父类模板,方法沿链查找/覆盖,`是` 沿链判定;`对象.方法(…)` 自适应接收者(声明参数数 = 实参数+1 时自动传对象为 `本`,显式写法仍可用)。类可定义在被引入的模块文件中(类方法挂载是模块内唯一允许的顶层语句);修复自举编译器选择性引入结构体时把 `[假,假]` 误当函数值的旧 bug。C 版/自举/原生后端三端对拍一致,ELF、PE、aarch64 验证。v2.18:**原生后端类语法(实验,先于 C 版)**——`类 名[(父类)]` + `属性 名字 [类型] [= 默认]` + 类内 `函数 方法(参数)`;可选冒号;也支持顶层 `函数 类名:方法(参数)`。解析器在编译期脱糖为现有 `结构体` + `类.方法 = 本 => …`:构造调用自动补零值/默认值,类模板原型指向父类模板(方法查找沿链、可覆盖),`对象.方法(args)` 自适应接收者(声明参数数 = 实参数+1 时自动把对象作为 `本` 传入,兼容 `对象.方法(对象, …)` 旧写法),`是(x, 父类)` 沿原型链判定。x86_64 ELF 与 PE 已验证;C 版 `zhc` 与自举编译器尚未移植该类语法(bootdiff 跳过带「原生专用」标记的用例)。v2.17:**原生后端正则(实验)**——新增 `正则匹配/正则全匹配/正则查找/正则全部/正则分组/正则替换/正则分割/正则转义`(字节级回溯引擎:字面量、`.`、`^$`、字符类与 `\d\w\s`、分组、`|`、`* + ? {n} {n,} {n,m}`、懒惰量词;空匹配遵循 Python finditer 口径;替换支持 `$0..$9`/`$$`),ELF 与 PE 输出与完整版逐字节一致。同轮把跨类型 `==`/`!=` 改为直接不等(C 语义,不再报类型错);REPL 在编译/运行期间保持终端原始模式,避免 `^C` 触发 SIGINT 冲刷输入队列丢键。官网 14 个示例中 13 个可编译运行。v2.16:**原生后端文件 IO/JSON/REPL(实验)**——x86_64-linux 目标新增 `读文件/写文件/追加文件/文件存在/文件大小/删除文件/建目录`(直接 syscall)与 `编码JSON/解码JSON`(紧凑格式、转义/\u 转义、null→假、插入序),Windows 目标文件 IO 未移植(调用时明确报错,JSON 可用);`原生 --repl` 与完整版同款「整段会话重跑」:TTY 行编辑(方向键/Home/End/Delete)、上下键历史(落盘 `~/.zhlang_history`)、Tab 缩进、Ctrl-C 取消、Ctrl-D 退出、关键字/字符串/数字/注释着色,表达式自动打印,块/未闭合字面量续行,运行失败的行不计入会话。官网 14 个示例中 12 个可编译运行。v2.15:**原生后端语法扩展(实验)**——x86_64 后端补齐模块(`@引入`/`为 别名`/`只`/`@私有`)、浮点(`/` 恒为浮点、混合数值运算/比较/转换/类型标注、15 位有效数字显示)、模板字符串、结构体(声明/构造/字段读写/方法/`是`/结构体标注)、闭包与一等函数(值捕获、lambda 内改写外层变量、具名函数作值、`映射/过滤/归约/排序`)、指针(`&`/`*` 读写与相等)、容器与浮点打印、字符串内置(`分割/连接/查找/替换/去空白/转大写/转小写/开头是/结尾是/重复`)以及裸名 `标准库/` 模块查找;x86_64-linux ELF 与 x86_64-windows PE 均通过(前者本机、后者 wine),官网 14 个示例中 11 个可编译运行且输出与完整编译器逐字节一致。aarch64 后端仍为子集(模块已支持;容器/浮点/闭包/结构体未移植)。已知限制:浮点显示 15 位有效数字(完整编译器为最短往返,个别数值会少末位)、闭包按地址捕获仅在外层栈帧存活期有效、协程/正则/网络/JSON/通用文件 IO 未移植。v2.14:**原生后端 P0(实验)**——`自举/原生.zh` 用纯 zhlang 把 AST 直接编成 x86_64-linux 静态 ELF(自编机器码/ELF 写出/直接 syscall,不经过 TinyCC),覆盖整数/布尔/字符串、算术/比较/位运算/逻辑、如果/当/跳出/继续、函数与递归、打印;未覆盖特性明确报「原生后端暂不支持」;新内置 **`写字节(writebytes)`**(0..255 整数数组 → 原始二进制;ELF 等含 NUL 内容必需,`写文件` 的字符串以 NUL 结尾)。v2.13:**导入唯一写法 `@引入`**——裸 `引入` 退役(报「已改名:请写「@引入」」),`@引入 … 为/只` 与裸名标准库引入照旧;**REPL 交互升级**——TTY 下自管行编辑(方向键/Home/End/Delete、上/下历史、Tab 缩进、Ctrl-C 取消当前行、Ctrl-D 退出)、输入按语法着色(关键字/字符串/数字/注释),历史落盘 `~/.zhlang_history`(可用 `ZHC_REPL_HISTORY` 覆盖);新增四个内置支撑原语:`终端原始模式`/`读键`/`显示宽度`/`字符`(§6;Windows 控制台回退系统自带行编辑)。v2.12:**标准库迁移第二波**——`哈希SHA256`/`哈希MD5`/`编码URL`/`解码URL`/`转整数` 也改由 zhlang 实现(共 20 个内置,`标准库/摘要.zh` 加入隐式层);**标准库按需发射**——隐式标准库模块整模块粒度按需生成,未被引用的模块不进入产物(不调用迁移内置的程序,其产物与 `ZHC_NO_STDLIB=1` 逐字节相同)。v2.11:**标准库用 zhlang 编写**——《规格》§6 中的 15 个内置(字符串/编码/容器/数学)改由 **zhlang 源码模块**实现(`标准库/`,隐式装载进每个模块,非限定名直接可用;不可重定义/不能当值/内置式 arity 文案与从前完全一致),C 实现保留作 `ZHC_NO_STDLIB=1` 回退;模块装载「磁盘优先、内嵌兜底」(发行版内嵌标准库文本,`ZHC_STDLIB` 可指目录或强制内嵌);另加**用户可引入的标准库模块层**(`标准库/集合.zh`:反转/最大值/最小值,裸名 `引入 "集合.zh"` 在编译器旁 `标准库/` 查找),新内置 **`类型(type)`**(返回值的类型名:整数/浮点/字符串/布尔/通道/数组/字典/套接字/函数/指针)。实现于标准库的内置,其运行时错误**消息正文逐字不变**,行号指向标准库实现行(见 §7)。v2.10:**切片 `c[起:止]`**——半开区间(起含止不含),起/止可省略(`s[:3]`/`s[3:]`/`s[:]`)、负数从尾部数、越界钳位不报错;字符串按码点、数组按元素,其他类型运行期报错;不可作赋值目标(编译错)。**网络解析兜底**——系统解析失败时用自写最小 UDP DNS 客户端自解析(nameserver 来源 `ZHC_DNS` 环境变量 → `/etc/resolv.conf` → `$PREFIX/etc/resolv.conf`,先 A 后 AAAA;musl 静态二进制在 Android/Termux 上零配置可用;`ZH_FORCE_DNS` 强制走兜底(测试用));失败报文区分「解析失败/连接失败」。**clang/bionic 兼容**——aarch64 协程上下文改 `void*` 字段(clang 的 int-conversion 硬错误)、补 `<netinet/in.h>`/`<arpa/inet.h>`、去 `strtok`(bionic 弃用)。v2.9:**声明改名 `变量`/`常量`**(`让` 退役:可变绑定用 `变量`,不可变绑定用 `常量` —— 绑定不可变、容器内容仍可变,常量性随闭包捕获传播;旧写法报「已改名」指引);**类型标注(可选)**——声明可写内置类型位(`整数`/`浮点`/`字符串`/`布尔`/`通道`/`数组`/`字典`/`套接字`/`函数`/`指针`)或结构体名,声明与每次赋值运行时校验;**全开箱**——三目标 sysroot 以 gzip 归档内嵌进编译器发行版(磁盘 sysroot 优先,缺档时按需解压到缓存;自写 gzip/DEFLATE/ustar 解包器 runtime/zhccgz.c;构建侧 `--embed-sysroot`),新内置 `确保归档(ensurearchive)`;生成的 C 可交 clang/macOS 直接编译(`spawn.h` 缺席时 `执行捕获` 回退 fork/execvp);v2.8:**Windows 中文路径全链路**——`参数()`/`程序路径()` 改从 `GetCommandLineW` 取(CRT 的 argv 是 ANSI 码页字节,中文参数变 `??`);内置 C 后端(tcc)的产出先写 ASCII 临时名再用宽字符 `MoveFileExW` 挪到目标,`编译C` 读 .c 也走宽字符 —— 中文的源码名、产物名、`.c` 名在 Windows 上从此可用(此前「你好.exe」会写成乱码名);v2.7:**内置 `真实路径(realpath)`**(规范绝对路径,解析符号链接);**自举编译器的模块装载对齐到与 C 版逐字**(去重真按 realpath——同一文件经符号链接等不同写法只算一次;入口/引入打不开带 `strerror` 原文;目录报「内存不足」同款;`--tokens`/`--ast` 只看入口文件、不碰 `引入`),并补齐 C 版漏掉的七个 v2.6 英文别名(`compilec`/`execcap`/`isterm`/`ignoreint`/`exepath`/`inputeof`/`output`);v2.6:**进程与终端内置**(`执行捕获`/`是终端`/`忽略中断`/`程序路径`/`输入结束`/`输出`,REPL 与 CLI 全部用语言自身写成);**自举编译器的 CLI 与 C 版 zhc 逐字对齐**(`--help`/`--version`/`--targets`/`--from-c`/`--gcc`/`--repl` 等全部搬进 zhlang 侧,见 自举/README 与 tools/clidiff.sh);v2.5:**内置 C 后端**`编译C`(把 C 源编译成产物,自举编译器据此自带后端);v2.4:协程栈溢出守护在 Linux 上真正生效(守护页 mprotect + 每线程备用信号栈)、主协程栈 32 MB;v2.3:内置 `打印错误(eprint)`(同 `打印` 的拼接规则但写标准错误并立即冲刷,供诊断/日志);v2.2:**分点式抢占**(忙协程占满 worker 时不再饿死睡眠/长运算协程);v2.1:**HTTPS/TLS**(内置 HTTP 客户端支持 https:自包含 mbedTLS 客户端 + 证书链/主机名校验);v2.0:**正则表达式**(八内置,自包含回溯引擎,码点级,逐项对齐 Python re)与**原始字符串 `r"…"`**;v1.9:**按引用捕获**(被闭包捕获且被改写的变量自动装箱为共享单元)、模块注解式可见性 `@私有` 与 `引入 … 为 别名`/`引入 … 只 名字`、模块函数作值;v1.8:位运算(& | ^ ~ << >> 与复合式)、进制字面量(0x/0b/0o)与数字分隔 `_`、箭头函数语句体、BASE64 与 SHA256/MD5、结构体(声明式)、选择(select);v1.7:对象方法调用(对象.方法())、原型链(继承/读走链/写落自身)、是() 类型判断;v1.6:一等函数与箭头函数(按值捕获)、映射/过滤/归约/排序;v1.5:结构体点语法(字典糖)、服务端套接字、复合赋值;v1.4:实用标准库(文件系统/JSON/HTTP+TCP/进程执行);v1.3:标准库扩充、模块系统、异常处理;v1.2:数组与字典;v1.1:缩进块、指针、内置工具链。v2.39 补丁(2026-11-01,**本地时区/诊断/并发打印**)——`格式化时间` 改为**本地时区**(Linux 解析 `TZ` 指向的 TZif,Windows 走 `SystemTimeToTzSpecificLocalTime`,无时区数据回退 UTC);ZL-17 剩余文案:`字典键不存在:键`、`调用了非函数值(收到 类型)`、`函数「名」需要 N 个参数(实际 M 个)`;函数值对象在 `+24` 携带显示名,打印 `<函数 名>`。ZL-27 支持任意接收者 `造().方法(…)` 链式调用与 `是(对象, 模块.结构体)`。ZL-33 并发 `打印` 行级原子:先转字符串(可分配/GC),再持输出锁整行写出。ZL-24 语义/模块编译错误统一为 `文件:行:列: 错误:消息` + 源码行 + `^`,跨模块错误指向模块文件。**文本拼接**新增 `字符串构造/字符串追加/字符串取值`(200k 次追加约 0.3s,替代 `+` 的 O(n²));运行期整数溢出提供 `加溢出/减溢出/乘溢出` 与抛错的 `加检/减检/乘检`(普通 `+ - *` 仍按规格环绕);`堆用量` 与 `堆统计.字节` 排除协程栈虚拟预留,`堆统计` 新增 `栈字节`;`连接/重复/替换` 的运行时实现改为线性;常量赋值错误附带「请声明为变量」提示;新增长度别名 `串长/字符串长度`;修正并发服务器示例的 Content-Length(UTF-8 字节数);隐式标准库新增数组/字典操作 `索引/计数/去重/求和/反序/扁平/最大元素/最小元素/复制/合并/字典筛选/字典映射`。

zhlang 是一门中英混合关键字的动态类型语言:手感取 **Python 的简易**(动态类型、缩进块、模板字符串、真值规则),并发取 **Go 的轻量**(协程 + 通道),并支持 C 风格指针。源码由纯 zhlang 自举编译器直接生成原生二进制,无需任何外部工具链。

- 中文与英文关键字**完全等价**,可混用(`变量 x = 1` = `var x = 1`;`常量`/`const` 同理)。
- 文件扩展名 `.zh`,源文件为 UTF-8(允许开头 BOM)。

---

## 1. 词法

### 1.1 字符集与标识符

- 源文件必须是合法 UTF-8,否则报错。
- 标识符:以 ASCII 字母、`_` 或 CJK 表意字符(U+3400–4DBF、U+4E00–9FFF、U+F900–FAFF)开头,后接上述字符或数字。**中英文关键字与标识符按「整词最长匹配」**:先扫出完整标识符串,再查关键字表。因此:
  - `否则如果` 是关键字(elif);单独 `否则` 是关键字(else);
  - `如果x` 是一个**标识符**,不是「如果 + x」——关键字后跟标识符时必须留空格(如 `如果 x`);
  - `变量x` 同理是一个名为「变量x」的标识符。
- 整数字面量:十进制,64 位有符号(`9223372036854775807` 以内),越界报「整数太大」;另支持 `0x`/`0X`(十六)、`0b`/`0B`(二)、`0o`/`0O`(八)进制,满 64 位按二补数解读(`0xFFFFFFFFFFFFFFFF` = -1,遮罩友好);数字间可夹 `_` 作分隔(`1_000_000`,解析时忽略)。
- 浮点字面量:`1.5`、`2e5`、`1.5e-3`;**禁止** `1.` 与 `.5`(报错)。
- 字符串字面量:`"..."`,转义 `\n` `\"` `\\` `\t` `\r`;**未知转义报错**(报文会提示正则等场景改用原始字符串);字符串内可含真实换行。
- 原始字符串:`r"..."`——反斜杠一律原样保留(写正则模式的惯用形式),`\"` 只用于「不结束字面量」且反斜杠仍留在值里(与 Python 同规则:`r"引号\"内侧"` 的值是 `引号\"内侧`);未闭合报「原始字符串未闭合」。
- 模板字符串:反引号 `` ` `` 包裹,字面部分仅支持 `` \` `` 与 `\${` 两个转义(其余反斜杠按字面处理);`${表达式}` 插值,表达式内可嵌套任意表达式并跨行(其中的换行被忽略),花括号配对到与之匹配的 `}`。
- 注释:`//` 到行尾(换行符本身保留为语句分隔);`/* ... */` 块注释(内部换行**不**产生语句分隔),未闭合报错。
- 分号 `;` 等价于一个换行。

### 1.2 换行敏感规则(语句分隔)

词法先产生独立的「换行 token」(连续换行与空行折叠为一个;`;` 亦产生换行 token;EOF 前补一个),随后做**过滤遍**,删除满足以下任一条件的换行 token:

1. 前一个 token 是 `+` `-` `*` `/` `%` `<` `<=` `>` `>=` `==` `!=` `&&` `||` `!` `&`、`=`、`,`、`.`、`(`、`[`、`{`,或关键字 `与`/`或`/`非`/`从`/`到`(即行尾是「期待后续」的位置;`|`/`^`/`~`/`<<`/`>>` 与 `:` 不在其列);
2. 下一个 token 是 `)`、`]`、`}`、`否则`、`否则如果`、`从`、`到`;
3. 当前处于括号深度 > 0(小括号、方括号、花括号或模板插值内)——因此数组/字典字面量与索引可跨多行书写。

保留下来的换行即**语句分隔符**。语义后果(Go 同款取舍):

- 下一行以二元运算符开头时,**不会**自动续行:`变量 a = 1\n+2` 中 `+2` 是非法表达式语句;
- `返回\nx` 视为无返回值的 `返回`(想返回下一行的表达式,写 `返回 (` 换行 `x)` 或同一行)。

### 1.2.1 缩进块(Python 式)

块由**行首缩进**表示,不使用花括号(花括号用于**字典字面量**、结构体声明的字段表与命名构造;语句位置出现的 `{`/`}` 会得到引导性报错):

- 缩进可用**空格或制表符 Tab**:空格算 1 列,Tab 跳到下一个 **8 列**制表位(Python 同款);每层缩进的宽度不必统一,只需严格「更深层更多」。空格/Tab 混用时按视觉宽度比较,宽度相同即同一层。
- 块 = 头部行(如 `如果 条件`)行尾换行 + 缩进增加 + 语句序列 + 缩进退回;缩进与既有任何层级都不匹配 → 报「缩进与之前的层级都不匹配」;
- 块不能为空;`EOF` 自动闭合全部层级。
- **可选冒号(Python 风格)**:块头部行的条件/表达式之后可写可选冒号 `:`,与换行等价——`如果 x > 0:` ≡ `如果 x > 0`。支持位置:`如果`/`否则如果`/`否则`、`当`、`循环`(含 `在` 遍历与 `从…到…`)、`尝试`/`捕捉`/`最终`、`选择` 及其臂(`接收`/`发送`)。冒号后必须换行缩进,不支持单行块。

### 1.3 非法字符与友好提示

- 全角标点(,、`;`、`:`、`(`、`)`、`"`、`'`、`、`、`。`、`!`、`?` 等)逐项报错,如:「非法字符「,」,请使用半角 ','」。
- 语句位置的 `}`:「本语言用缩进表示块」;`.` 仅用于**模块.函数() / 对象.方法() 限定调用与字段访问**,其余位置报错。
- 其他非法字节:报「非法字符」并显示该 Unicode 码点。

### 1.4 运算符与优先级(从低到高)

| 级 | 运算符 | 备注 |
|---|---|---|
| 1 | `或` / `or` / `\|\|` | 短路 |
| 2 | `与` / `and` / `&&` | 短路 |
| 3 | `\|` | 位或(双目) |
| 4 | `^` | 位异或(双目) |
| 5 | `&` | 位与(双目;单目位置为取地址) |
| 6 | `==` `!=` | |
| 7 | `<` `<=` `>` `>=` | |
| 8 | `<<` `>>` | 移位(次数 0..63) |
| 9 | `+` `-` | `+` 含字符串/数组拼接 |
| 10 | `*` `/` `%` | `/` 为真除法 |
| 11 | `!` / `非` / `not`、一元 `-`、`~`、`&`(取地址)、`*`(解引用) | 右结合 |
| 12 | `容器[下标]`、`容器[起:止]`、`表达式.字段`、`表达式->字段`、`模块.函数(…)`、`@模块.名字`、`对象.方法(…)`、`对象->方法(…)`、`表达式(…)`(间接调用) | 后缀,可链式混用:`m[i][j]`、`p.a[0].b`、`f(1)(2)`、`列表[0](x)`、`p->a->b`;点号带调用括号=限定调用(首名为已引入模块 → 模块调用,否则按 `对象.方法(…)` ≡ `对象["方法"](…)`),不带=字段访问;`->` 用于指针/对象引用(见 §2.1) |

复合赋值:`+=` `-=` `*=` `/=` `%=` `&=` `|=` `^=` `<<=` `>>=`(目标子表达式只求值一次)。
二元运算符一律左结合。`=>`(箭头函数)不出现在上表:它只能在表达式开头(primary 位置)出现,函数体向右贪婪延伸到最近的括号/逗号边界,或在 `=>` 后以**换行+缩进**给出语句块体(见 §2.5)。

---

## 2. 类型与值(动态)

运行时值共十种:

| 类型 | 取值 | 字面量示例 |
|---|---|---|
| 整数 | int64 | `42` |
| 浮点 | IEEE754 双精度 | `3.14` |
| 字符串 | 不可变字节序列(UTF-8) | `"你好"` `` `共${n}个` `` |
| 布尔 | 真 / 假 | `真` `false` |
| 通道 | 运行时对象 | `通道()` 的返回值 |
| 指针 | 某个变量槽的地址 | `&x` 的返回值 |
| 数组 | 可变长有序序列(引用) | `[1, "二", [3]]` `[]` |
| 字典 | 键值表(引用,保插入序) | `{"名": "张三", 1: 真}` `{}` |
| 套接字 | TCP 连接(运行时对象,按身份比较,GC 自动关闭) | `连接到(…)` 的返回值 |
| 函数 | 一等函数值(命名函数或箭头函数,按身份比较) | `双倍`、`x => x * 2` |

### 2.1 运算语义

- `+`:两侧均为数值 → 加法(int/float 混算升为 float);**任一侧为字符串 → 拼接,另一侧自动转字符串**;两侧均为数组 → 拼接为**新数组**;数组与字符串/数值混搭、含布尔 → 运行时错误。
- `-` `*`:仅数值(布尔/字符串报错)。
- **整数溢出(v2.40 起默认检查)**:`+` `-` `*` 与一元 `-` 的整数结果超出 int64 范围时,运行期抛「整数溢出」,不再静默回绕。需要显式模 2^64 环绕语义时用 `标准库/数学.zh` 的 `环绕加`/`环绕减`/`环绕乘`。`%` 的 `INT64_MIN % -1` 按数学值返回 0(不崩溃)。
- `/`:真除法,**总是返回浮点**(`5 / 2 == 2.5`);除数为 0(整数或浮点 0)→ 运行时错误「除以零」。
- `%`:取模,**要求两侧均为整数**;模 0 → 运行时错误「取模为零」。
- `&` `|` `^`(位与/位或/位异或)、`~`(按位取反)、`<<` `>>`(左移/算术右移,符号扩展):**要求整数**(否则运行时错误),移位次数须在 0..63;复合赋值 `&= |= ^= <<= >>=` 同名支持。优先级(高→低):移位 > 比较 > 相等 > `&` > `^` > `|`(**`&` 在双目位置是位与,单目位置仍是取地址**;`|` 为位或,逻辑或用 `或` 或 `||`)。
- 比较 `<` `<=` `>` `>=`:数值互比(混算按数值比较);字符串与字符串按码点序(即 UTF-8 字节序);其他混搭 → 运行时错误。
- `==` `!=`:同型比较(字符串按内容;通道/套接字按身份;int/float 按数值互比;**数组/字典按深度比较**——逐元素/逐键值递归,每层先做同一对象短路,循环引用因此终止,嵌套超 64 层视为不等;**原型链不参与比较**,继承对象只比自身条目);**不同类型 `==` 恒为假**(`!=` 恒为真),不报错。
- 逻辑运算对待操作数按真值规则,返回布尔值:`与`/`或`/`非`。
- **指针**:`&变量` 取该变量槽的地址(只能对变量);`*p` 读取所指值;`*p = 值` 通过指针写入。指针 `==`/`!=` 按地址比较;其他运算不支持。指针可存入变量、传参、放入通道。**箭头成员访问**:`p->字段` ≡ 基若为指针先解引用一层、基若已是对象引用则直接使用,再取字段;`p->方法(实参…)` 同理(接收者为解引用后的对象);属性可赋值与复合赋值;可链式(`p->a->b`、`f()->x`),基可以是变量、函数/方法返回值或链式结果。**与 C 相同的危险**:指向已返回函数的局部变量是悬垂指针(未定义行为);GC 不追踪指针的目标生命周期(指针指向栈上的变量槽,本身不受回收影响)。

### 2.2 真值规则(Python 式)

| 类型 | 为真条件 |
|---|---|
| 布尔 | 即自身 |
| 整数 / 浮点 | 非零(NaN 为假) |
| 字符串 | 非空 |
| 数组 / 字典 | 非空(带原型的字典恒为真) |
| 通道 / 指针 / 套接字 / 函数 | 恒真 |

### 2.3 转字符串(拼接、打印、转字符串 共用)

- 整数:`%lld` 十进制;浮点:最短往返表示(如 `2.5`、`1e+20`);
- 布尔:`真` / `假`;通道:`「通道:0x…」`;指针:`「指针:0x…」`;套接字:`「套接字:N」`;函数:`<函数 名>`(名 = 函数名或「匿名(第 N 行)」;原生后端函数值对象在 `+24` 保存编译期静态名字指针);字符串:原样。原生后端套接字即文件描述符整数,按整数打印(见 §6 网络内置说明)。
- 数组/字典:递归显示为 `[1, "二", [3]]`、`{"名": "张三"}`——**嵌套在容器内的字符串带引号**(顶层仍为裸文本);嵌套超过 8 层显示 `[...]` / `{...}`(循环引用安全)。

### 2.4 容器:数组与字典

**数组** —— 有序、可变长、元素为任意值:

- 字面量 `[e1, e2, …]`(元素间可跨行,见 §1.2);空数组 `[]`;
- 读 `a[i]` / 写 `a[i] = 值`:下标须为整数;**负数从尾部数**(`a[-1]` 为末元素);越界 → 运行时错误「下标越界:N(长度 L)」;**不自动扩容**;
- `追加(a, v1, v2, …)` 末尾追加(1 至 7 个值),返回数组本身;`弹出(a)` 移除并返回末元素(空数组 → 运行时错误);
- `a + b` 拼接为新数组,a、b 原样不变;
- **引用语义**:`变量 b = a` 后 a、b 是同一个数组——对任一方的追加/索引赋值另一方可见;传参、放入通道同理。无拷贝操作(需要副本时用 `+ []` 或遍历构造)。

**字典** —— 键值表,**保持插入顺序**:

- 字面量 `{k1: v1, k2: v2, …}`;空字典 `{}`;**键**限整数/浮点/字符串/布尔(其他类型 → 运行时错误「字典键不能是…」;`-0.0` 与 `0.0` 视为同键);
- 读 `d[k]`:键不存在 → 运行时错误「字典键不存在:…」;安全读取用 `取(d, k[, 默认])`;写 `d[k] = 值`:不存在即插入、存在即覆盖;`删除(d, k)` 移除键,返回 真/假(是否原本存在);删除不改变其余键的相对顺序;
- `键(d)` / `值(d)` 返回按键序的数组;`包含(d, k)` 查键(对数组则按值查)。

**索引的统一规则**:`c[k]` 对数组(整数下标)、字典(键)、字符串(**按码点取单字符子串**,只读——赋值 → 运行时错误)有效;其他类型 → 运行时错误「[]索引需要数组、字典或字符串」。

**切片 `c[起:止]`**:半开区间(起含、止不含);两端均可省略(`s[:3]`、`s[3:]`、`s[:]` 取全);负数从尾部数(与下标一致);越界**钳位**不报错,`起 ≥ 止` 得空;字符串按**码点**、数组按**元素**;其他类型 → 运行时错误「「[起:止]」切片需要字符串或数组」;端点非整数 → 运行时错误「切片端点需要整数」;切片**不可作赋值目标**(编译错)。数组切片产生新数组(元素按值引用语义,与数组赋值别名同款)。

**字段访问(结构体糖)**:`表达式.标识符` ≡ `表达式["字段名"]`(仅字典;字段名须为合法标识符,不能是关键字)——`p.名字` 读、`p.年龄 = 值` 写,与索引完全互通(遍历/深相等/键值照用);写字段不存在即新建键(动态语义)。对非字典容器/值 → 按统一索引规则报运行时错误;`模块.函数` 不带括号 = 函数值(见 §3.3);模块名下没有该函数/结构体 → 编译错。

**对象.方法(…)**:与字段访问同源的语法糖,`对象.方法(实参…)` ≡ `对象["方法"](实参…)`。resolve 期区分:点号前的名字是**已引入模块** → 模块调用(模块名优先);否则必须是变量(对象)→ 方法调用。被调值不是函数 → 运行时错误「调用了非函数值…」;参数个数在运行时检查(同其他间接调用)。

**原型链(继承)**:`继承(原型[, 添加字段字典])` 生成携带隐藏原型指针的新字典(参数非字典 → 运行时错误):

- **读走链**:`d[k]`/`d.字段`/`取`/`包含` 在自身缺键时沿原型链向上找;链穷仍缺 → 按各自既有规则(索引报错 / `取` 返回默认 / `包含` 为假);
- **写落自身**:`d[k] = 值` 永远写自身条目——自然覆盖原型中的同名项(影子语义),原型不受影响;
- **枚举只看自己**:`长度`/`键`/`值`/`循环 在` 遍历/`删除`/JSON 编码/`==` 深相等只作用于自身条目;
- 带原型的字典**恒为真**(即使自身无字段);打印形如 `{…} (原型 …)`;
- 原型指针隐藏且只读(无赋值语法),链因此不可能成环;`继承(原型)` 省略第二参等价于添加空字典。

**类型判断**:`是(对象, 原型)` —— 对象**自身或原型链上任一节点**与原型**身份相同**(指针相等,不按结构)→ 真;第一参非字典 → 假(类型探测);第二参非字典 → 运行时错误。`是(狗, 狗)` 为真(自包含)。

**结构体(声明式)**:`结构体 名` + 缩进块每行一个字段标识符,或花括号形式 `结构体 名 { 字段, … }`(两种等价;至少 1 个字段,不得重复;顶层声明,函数/模块文件均可)。花括号内换行被词法层忽略,字段以逗号或空白分隔。

- **构造**:`名(值1, 值2, …)` —— 实参个数须等于字段数(编译期检查);字段按声明序填入,实例是携带字段校验标记的原型链字典(proto = 模板);
- **初始化方法(构造)**:类体内 `函数 初始化(参数 [= 默认], …)` 至多一个。构造 `类名(实参…)` 时先按字段声明填默认值/零值,再调用初始化方法,表达式结果恒为对象本身;初始化体内 `返回` 只能不帶值(带值编译错),参数默认值参与调用点补参,缺参/未知/重复参数为编译错;有初始化方法时命名构造的键对应初始化参数。子类初始化方法执行前自动调用父类初始化(参数按同名匹配,父类必填参数缺失则编译错);子类没有自己的初始化时继承父类签名。初始化方法挂载在隐藏名 `__初始化_类名` 上,不能显式 `对象.初始化(…)`;构造经合成的 `__构造_类名` 工厂(跨模块构造同样可用,跨模块命名构造除外);
- **命名构造**:`名{字段: 值, …}` —— 键为字段名(标识符或字符串字面量),顺序任意,不必写全;缺省字段补零值(`假`;类按字段声明补默认值/零值)。未知字段、重复字段为编译错;命名与位置构造不可混用。命名构造只支持**本文件已声明**的结构体/类(跨模块请用位置构造);解析期展开为普通构造调用,后续语义与位置构造完全一致;
- **字段校验**:点写只能写声明过的字段,否则运行时错误「结构体「点」没有字段「zzz」」;点读缺字段同款专用错误;`实例["字段"]` 索引访问不经校验(原始字典语义,面向反射/克隆类代码);
- **模板是值**:结构体名可作值/取模板;模板本身不受字段校验(可挂方法:`点.移动 = 本 => …`);
- **方法调用**:`p.移动(p)`(显式传自身)或 `p.移动()`(声明参数数 = 实参数+1 时自动把 `p` 作为第一个参数传入);`是(p, 点)` 为真;`继承(点, {字段…})` 得到校验延续的子对象;
- **模块**:`模块.结构体(值…)` 构造、`是(p, 模块.结构体)` 判断均可用;
- 打印/枚举/深相等与字典一致(只看自身条目)。

**类(脱糖为结构体,继承/封装/多态)**:`类 名[(父类)]` + 缩进类体;类体可直接写字段 `名字 [类型] [= 默认]`(前缀可写 `变量`/`常量`/`属性`,三者目前等价:类字段脱糖为原型字典槽,不做只读强制),也可写实例方法 `函数 方法(参数)`(签名等价于 `本 => 方法体`,参数自动加上 `本`)。类体内的 `@私有` 表示整个类仅当前文件可用(与声明上一行的 `@私有` 同义)。

- **方法定义**:类体内 `函数 方法(参数)` 为实例方法;顶层 `@类方法(类名)` + `函数 方法(参数)` 等价;顶层 `@类函数(类名)` + `函数 函数名(参数)` 为**静态方法**(不注入 `本`,以 `类名.函数(实参…)` 调用,参数个数须严格相等)。
- **继承**:父类须先声明;子类字段 = 父类字段(按序)+ 自身字段;子类重写同名继承字段只改默认值、不改字段位置;类模板的原型指向父类模板,方法沿原型链查找,子类同名方法覆盖父类(多态);`是(实例, 父类)` 沿链为真。
- **构造**:`类名(实参…)`;缺省实参按字段默认值/零值自动补齐(无类型→假,`数值`/`整数`→0,`浮点`→0.0,`字符串`→`""`,`数组`/`字典`→空);字段仍可用点写/索引写。
- **调用**:实例方法可写 `对象.方法(实参…)`(声明参数数 = 实参数+1 时自动传 `本`)或 `对象.方法(对象, 实参…)`;静态方法只写 `类名.函数(实参…)`(也可选择性引入类后写 `类名.函数(…)`)。
- **跨模块方法挂载(v2.23)**:顶层 `@类方法(模块.类名)` / `@类函数(模块.类名)` 可给已加载模块中的类挂实例/静态方法(模块写真实名或本文件 `引入 … 为` 的别名);解析期不要求类别在本文件声明,名字解析期校验目标是 `类`、非 `@私有`、非标准库模块;同一方法跨模块重复挂载为编译错,同一模块内重复保持后者覆盖。挂载语句在模块初始化阶段执行,所有类模板先于模块初始化创建。
- **模块**:类可写在被引入的模块文件中(模块文件里允许的顶层语句仅限类方法挂载);`模块.类(…)` 构造与 **`模块.类.静态方法(…)` 链式调用**均支持(别名同样可用;链式只用于静态方法,实例方法仍写 `实例.方法(…)`);也支持对模块做 `只 类名` 选择性引入后以 `类名.函数(…)` 调用。`引入 … 为 别名` 允许别名与该模块自身同名(冗余但无害,便于显式命名空间)。`@私有` 类不能被其他文件限定访问或选择性引入。

**遍历**:`循环 x 在 容器` —— 数组遍历**元素**、字典遍历**键**、字符串遍历**码点**(单字符);循环变量在循环块作用域内;容器只求值一次;循环中修改容器是否影响本轮遍历不做保证(实现按长度快照遍历)。

### 2.5 一等函数与箭头函数

函数是值:可存入变量/容器、作参数与返回值、经任意表达式调用(`f(3)`、`加(3)(4)`、`策略["加"](x, y)`、`列表[0](x)`)。

- **函数名作值**:`常量 f = 双倍`(不带调用括号即函数值);`模块.函数` 与 `@模块.函数` 同样作值(可绑定/传参/回调)。**内置函数不能作值**(编译错,提示包一层箭头函数)。
- **箭头函数**:`x => 表达式`、`(x, y) => 表达式`、`() => 表达式`(体为单个表达式,贪婪延伸到最近的括号/逗号边界);或 `x =>` 后**换行+缩进**给出语句块体——显式 `返回` 返回值,无则落回假(与具名函数一致),体内可用全部语句(变量/常量/循环/尝试…)。参数上限 8 个。
- **捕获**:箭头函数引用外层局部变量时默认按值绑定(创建那一刻的值);但**被改写的捕获变量自动装箱为共享单元**——该变量在闭包内或外层任一位置被赋值(含复合赋值)时,它存放在堆上单元里,外层与**所有**闭包读写同一单元:闭包里的改写外层可见,外层改写闭包也可见(按引用捕获;判定在编译期按「捕获 ∧ 被赋值」静态做出,与执行路径无关)。捕获容器(数组/字典)时拷贝的始终是引用——函数值与外层共享同一容器。循环中每次创建仍绑定当次的值:被改写的循环变量(区间/遍历)**每轮换一个新单元**。捕获上限 8 个(与参数合计 ≤ 8,超出 → 编译错)。
- **调用**:间接调用在**运行时**检查——被调值不是函数 → 「调用了非函数值(收到 类型)」;参数个数不符 → 「函数「名」需要 N 个参数(实际 M 个)」(直接调用命名函数仍在编译期检查)。实参求值顺序不做保证。
- **相等与显示**:`==`/`!=` 按身份比较(同一函数值相等;两次创建的箭头函数即使文本相同也不等);转字符串为 `<函数 名>`。
- **启动** 仅支持命名函数(不能 `启动 f(…)` 其中 f 为函数值);箭头函数值可作为普通参数传入被启动的函数。

---

## 3. 语句与程序结构

### 3.1 BNF 文法

```bnf
(* v2.23 修订:按自举版实现补齐裸名引入、跨模块类方法挂载、
   选择、切片、位运算与类体;模块文件的顶层限制见 §3.3。 *)

program   ::= { import | decl | stmt }
import    ::= "@" ("引入"|"import") (STRING | IDENT)
              [ "为" IDENT | "只" IDENT { "," IDENT } ]
              (* 必须写在其他顶层内容之前;裸名 ≡ "名字.zh";见 §3.3 *)

decl      ::= [ annotation ] ( funcdef | structdecl | classdecl )
annotation::= "@" ("私有"|"private")
            | "@" ("类方法"|"类函数") "(" [ IDENT "." ] IDENT ")"
              (* 一条声明至多一个注解;类方法/类函数只能修饰紧随其后的
                 顶层 函数,且不得与「函数 类名:方法」旧写法连用;见 §3.2 *)

funcdef   ::= ("函数"|"func") IDENT [ ":" IDENT ] "(" [ params ] ")" [ ":" ] block
              (* 旧写法「函数 类名:方法(…)」;普通函数参数无默认值,最多 8 个 *)
params    ::= IDENT { "," IDENT }

structdecl::= ("结构体"|"struct") IDENT
              ( structfields | "{" IDENT [ "," ] { IDENT [ "," ] } "}" )
structfields ::= 换行 缩进↑ { IDENT } 缩进↓                  (* 至少 1 个字段 *)
              (* 花括号体内字段以逗号或空白分隔,允许尾逗号;见 §2.4 *)

classdecl ::= "类" IDENT [ "(" IDENT ")" ] [ ":" ] classbody     (* 仅中文;class 未支持 *)
classbody ::= 换行 缩进↑ { classmember } 缩进↓               (* 至少 1 个字段 *)
classmember ::= "@" "私有"                                   (* 整个类仅本文件可用;
                                                              规范写法是类声明上一行 @私有;
                                                              类体内不接受英文 private *)
              | [ "属性" | "变量" | "常量" ] IDENT [ IDENT | "函数"|"func" ] [ "=" expr ]  (* 字段;第二个 IDENT 是类型标注 *)
              | ("函数"|"func") "初始化" "(" [ initparams ] ")" [ ":" ] block
              | ("函数"|"func") IDENT "(" [ params ] ")" [ ":" ] block
initparams::= IDENT [ "=" expr ] { "," IDENT [ "=" expr ] }  (* 仅初始化方法支持默认值 *)

stmt      ::= "@" ("断点调试"|"调试断点"|"breakpoint") [ "(" [ args ] ")" ]
              (* 调试指令;仅原生后端 --debug/-d 构建生效,见 §6.10 *)
            | ("变量"|"var"|"常量"|"const") IDENT [ IDENT | "函数"|"func" ] "=" expr
            | assign_target ( "=" | AUGOP ) expr
            | "*" expr "=" expr                               (* 指针写入;仅普通 = *)
            | ("启动"|"go") [ "@" ] [ IDENT "." ] IDENT "(" [ args ] ")"
              (* 带 @ 时必须写 模块.;只能启动自定义函数(含 模块.函数),
                 不能启动内置函数、函数值或类方法;见 §2.5 *)
            | ("如果"|"if") expr block
                { ("否则如果"|"elif") expr block }
                [ ("否则"|"else") block ]
            | ("循环"|"for") IDENT ("从"|"from"|"=") expr ("到"|"to") expr block
            | ("循环"|"for") IDENT ("在"|"in") expr block
            | ("当"|"while") expr block
            | ("尝试"|"try") block [ ("捕捉"|"catch") IDENT block ]
                  [ ("最终"|"finally") block ]                (* 捕捉/最终至少一个 *)
            | ("返回"|"return") [ expr ]
            | ("跳出"|"break") | ("继续"|"continue")
            | selectstmt
            | expr                                            (* 仅函数调用可作表达式语句 *)

selectstmt::= ("选择"|"select") 换行 缩进↑ { selectarm } 缩进↓
selectarm ::= "接收" expr [ "为" IDENT ] block
            | "发送" expr "," expr block
            | ("否则"|"else") block                          (* 必须最后;1..8 个臂 *)

assign_target ::= IDENT { "." IDENT | "[" expr "]" }
                  (* 变量/索引/字段写入,可复合赋值;切片不可作赋值目标;
                     指针写入是单独的「* expr = expr」形式,不支持复合赋值 *)
AUGOP     ::= "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>="

block     ::= 换行 缩进↑ { stmt } 缩进↓
args      ::= expr { "," expr }
namedarg  ::= (IDENT | STRING) ":" expr

expr      ::= or
or        ::= and { ("或"|"or"|"||") and }
and       ::= bitor { ("与"|"and"|"&&") bitor }
bitor     ::= bitxor { "|" bitxor }
bitxor    ::= bitand { "^" bitand }
bitand    ::= eq { "&" eq }
eq        ::= rel { ("=="|"!=") rel }
rel       ::= shift { ("<"|"<="|">"|">=") shift }
shift     ::= add { ("<<"|">>") add }
add       ::= mul { ("+"|"-") mul }
mul       ::= unary { ("*"|"/"|"%") unary }
unary     ::= ("!"|"非"|"not") unary | "-" unary | "~" unary
            | "&" IDENT | "*" unary | postfix   (* & 只能对变量取地址 *)

postfix   ::= primary { "[" expr "]"                        (* 索引 *)
                     | "[" [ expr ] ":" [ expr ] "]"        (* 切片:至少一个冒号 *)
                     | "." IDENT                           (* 字段访问;点号后不接 ( *)
                     | "->" IDENT [ "(" [ args ] ")" ]     (* 指针/对象成员;方法可带调用 *)
                     | "(" [ args ] ")" }                  (* 间接调用 *)
primary   ::= INT | FLOAT | STRING | TEMPLATE
            | ("真"|"true") | ("假"|"false")
            | "[" [ args ] "]"                              (* 数组字面量 *)
            | "{" [ expr ":" expr { "," expr ":" expr } ] "}"  (* 字典字面量 *)
            | "(" expr ")"
            | IDENT
            | IDENT "(" [ args ] ")"                        (* 调用 *)
            | IDENT "{" [ namedarg { "," namedarg } ] "}"   (* 命名构造 *)
            | [ "@" ] IDENT "." IDENT "(" [ args ] ")"      (* 模块.函数 / 对象.方法 / @模块.函数 *)
            | [ "@" ] IDENT "." IDENT "." IDENT "(" [ args ] ")"  (* 模块.类.静态方法 *)
            | [ "@" ] IDENT "." IDENT                       (* @模块.名字 / 模块.类 值 *)
            | IDENT "=>" arrowbody                          (* 单参箭头函数 *)
            | "(" [ IDENT { "," IDENT } ] ")" "=>" arrowbody  (* 零参/多参箭头函数 *)

arrowbody ::= expr | block
```


### 3.2 语义细则

- **顶层语句即 `main`**:全部顶层语句构成主协程体;顶层 `返回 N`(N 为整数)设置进程退出码;自然结束退出码 0;非整数返回值 → 运行时错误「退出码需为整数」。
- **可选入口函数 `main`/`主函数`**:入口模块可定义一个无参函数 `main` 或 `主函数`(不能同时定义,否则编译错「入口函数「main」与「主函数」只能定义其一」)。若入口模块**没有顶层可执行语句**(导入、函数/结构体/类声明、顶层 `变量`/`常量` 声明与类方法挂载不算),程序初始化完成后自动调用该函数;若存在顶层可执行语句,则照常执行顶层语句,该函数**不自动调用**(仍可手动调用)。将被自动调用而函数带参数 → 编译错「入口函数「main」不能有参数」;入口函数返回整数时该值作进程退出码(非整数或无返回值按 0)。被引入模块中的同名函数不受此规则影响(普通函数)。
- **作用域**:块级作用域,内层可遮蔽外层;同一作用域重复声明(变量/常量)报错;赋值给未声明变量报错(提示改用 `变量 x = …` / `常量 x = …`)。函数参数在函数体最外层作用域。**模块级全局变量(v2.31,原生后端)**:入口模块顶层声明的 `变量`/`常量` 对所有具名函数、类方法与闭包可见,并共享同一存储——读到的是当前值,闭包/类方法按地址捕获而非创建时快照;函数参数或局部变量同名时遮蔽全局。全局声明先扫描、后解析函数体,因此函数可以在声明语句之前引用全局(调用发生在初始化之后即可)。被引入模块只能包含函数/结构体/类与类方法挂载,不能声明模块级变量。
- **变量与常量**:`变量 x = 值` 声明可变绑定;`常量 x = 值` 声明不可变绑定 —— 声明后对名字本身再赋值(含复合赋值)或 `&x` 取地址 → 编译错(「常量「x」不能再赋值」/「不能对常量「x」取地址」)。**绑定不可变 ≠ 内容不可变**:`常量 名单 = [1]` 之后 `名单[0] = 9`、`名单.字段 = 值` 均合法。常量性随闭包捕获传播:闭包体内对捕获常量的赋值同样编译错。
- **类型标注(可选)**:声明可写一个类型位 —— 十个内置:`整数`/`浮点`/`字符串`/`布尔`/`通道`/`数组`/`字典`/`套接字`/`函数`/`指针`;或结构体名(`变量 p 点 = 点(3, 4)`,按原型链判实例)。校验在运行时:声明与每次赋值(含复合赋值)后检查,不符 → 运行时错误「类型标注不符:声明为 整数,得到的是 字符串」(带行号)。`整数` 与 `浮点` 不互通(标注即约束);不写类型 = 完全动态、零开销。
- **接口(v2.25,原生后端)**:顶层 `接口 名字[(父接口, …)]` 声明纯方法签名,如 `接口 读: / 读(路径 字符串)`;方法行可省略 `函数`,`@私有` 写在接口声明上一行或接口体内表示仅本文件可用,父接口须先声明且方法集合并(同名子方法覆盖)。接口没有实现体,也不需要在类上写 `implements`——**任何对象**(字典,方法沿原型链查找)只要含有全部同名方法就满足接口(Go 式结构化):方法参数个数须等于签名个数,或等于签名个数 + 1(类方法带隐式 `本`)。检查发生在 `变量 x 接口名 = 值` / 赋值与 `是(值, 接口名)` 处,不满足 → 运行时错误「类型标注不符:要求接口「X」…」或「…未实现接口「X」的方法「M」」。接口签名里的参数类型不参与匹配,由实现函数自己的参数标注在调用入口检查。接口方法按接口整体可见性处理(接口方法默认公开,不做逐方法可见性);跨模块用 `@引入 … 只 接口名` 引入,类型位只接受单个标识符(不支持 `模块.接口` 限定标注)。
- **参数类型标注(v2.25,原生后端)**:函数、类方法、接口方法的参数可写 `名字 类型`(类型名同变量标注:内置类型、结构体名或接口名);被调函数入口处检查,不符 → 运行时错误「类型标注不符:声明为 X,得到的是 Y」。参数类型只影响调用入口,函数值对象的元数据仍只有参数个数。
- **函数**:先扫描全部函数签名再检查函数体,因此递归、互递归、先使用后定义均合法。参数按值传递(`zhv_t` 装箱值)。函数与内置函数重名、函数重名 → 编译错。调用时参数个数不符 → 编译错(间接调用经函数值的,在运行时检查,见 §2.5)。
- **启动**:只能启动自定义函数,立即返回、并发执行(见 §5)。
- **循环**:`循环 i 从 A 到 B {}` —— A、B 在进入循环时求值一次,须为整数(否则运行时错误「循环边界需要整数」);A ≤ B 时步长 +1、A > B 时步长 −1,**两端均含**(A=1,B=3 → 1,2,3);循环变量 `i` 在循环块作用域内,遮蔽外层同名。`循环 x 在 容器 {}` 见 §2.4。
- **跳出/继续**只能在循环体内使用,否则编译错;`继续` 语义为「先步进,再进入下一轮」(计数循环先自增、遍历循环先前进——不会跳过步进)。
- 空语句(连续分隔符)允许。
- **函数自然结束**(未执行到 返回)相当于 `返回 假`;顶层无值的 `返回` 等价于退出码 0。

### 3.3 模块系统(@引入)

- **引入语句**:顶层 `@引入 "路径.zh"`(英文 `@import`;路径为字符串字面量)或**裸名标准库引入** `@引入 名字`(名字为合法标识符,不带引号与 `.zh`,等价于 `@引入 "名字.zh"`),**必须写在文件顶部、其他顶层内容之前**;可出现于入口文件与模块文件。**裸 `引入` 已退役**:写出即编译错`「引入」已改名:请写「@引入」`。
- **路径解析**:相对**引入文件所在目录**(绝对路径直接使用);文件不存在 → 编译错。裸名引入先按相对路径找同名 `.zh`;找不到再走标准库查找路径(见下),仍找不到报「无法打开」。
- **去重**:按真实路径(realpath)去重——重复引入、菱形引入(两个模块引入同一个第三方)幂等;**互相引入合法**(模块顶层只有声明与类方法挂载;全部结构体/类模板先创建,挂载在模块初始化阶段执行)。
- **模块名** = 文件名去掉 `.zh` 的基名,须为合法标识符(否则编译错);不同文件同名模块 → 编译错。
- **模块文件的顶层内容**只能 `@引入`、函数/结构体/类声明、类方法挂载(`@类方法`/`@类函数` 修饰的顶层函数);其余可执行顶层语句仅入口文件允许(入口顶层构成 `main`)。
- **可见性(默认公开)**:声明上一行写注解 `@私有`(`@private`)——被注解的函数/结构体/类仅本模块可见(类也可在类体内写 `@私有`,整个类生效);模块外限定访问(`库.内部()`/`@库.内部()`)与选择性引入都会报编译错;未知注解报错。
- **`@` 限定简写**:`@模块.函数(…)` ≡ `模块.函数(…)`,`@模块.结构体`/`@模块.类` 作模板值;`启动 @模块.函数(…)` 同义。**模块名/别名不存在 → 编译错**(不像 `x.y()` 那样回退为对象方法)。
- **别名**:`@引入 "路径.zh" 为 短名`——本文件内用短名限定调用;别名不得与任何已加载模块同名、同文件内不得重复。
- **选择性引入**:`@引入 "路径.zh" 只 名字, 名字…`——列出的函数/结构体/类在本文件内**非限定**可用(模块限定访问仍可);名字须存在且公开,不得与本模块声明重名;`为` 与 `只` 互斥。
- **调用**:`模块.函数(…)` 限定调用(`.` 仅此用途;`启动 模块.函数(…)` 同样支持);非限定调用解析**内置函数、本模块函数与选择性引入的名字**——其余模块函数必须写限定名。
- **函数作值**:`模块.函数` 与普通函数名一样可作一等值(绑定/传参/回调);`模块.结构体` 作模板值。
- **传递可见**:经引入(含传递引入)加载的全部模块都可用限定名访问。
- **隐式标准库(v2.11)**:编译器隐式装载一批 **zhlang 源模块**(`标准库/字符串.zh`、`编码.zh`、`容器.zh`、`数学.zh`、`摘要.zh`),其**公开函数在全部模块中非限定可用**,语义与原内置完全等价:名字**保留**(不可重定义、不能当值、`启动` 拒绝、参数个数按内置式文案报错);§6 中实现于标准库的条目即由它们承接。标准库模块名不可被用户模块占用:撞名 → 编译错`模块名「X」与内建标准库模块同名(该名字保留)`(把 `标准库/*.zh` 自身当入口编译时跳过该项,供对拍/自测)。
- **标准库按需发射**:隐式标准库模块**整模块粒度按需生成**——只有被引用的模块(非标准库模块直接引用,或沿已发射的标准库模块传递引用)才进入产物;未被引用的模块不生成任何函数/结构体。函数编号与用户代码段不受影响,产物行为不变(不调用任何迁移内置的程序,其产物与 `ZHC_NO_STDLIB=1` 逐字节相同)。
- **标准库模块查找路径(v2.11)**:标准库模块按相对路径装载(`标准库/名字.zh`),磁盘查找次序:`ZHC_STDLIB` 环境变量(目录;`内嵌`/`0` = 强制内嵌)→ `<编译器目录>/标准库/…` → `<编译器目录>/../share/zhlang/标准库/…` → `/usr/local/share/zhlang/标准库/…` → **编译器内嵌文本**(发行版把标准库文本内嵌,缺目录也能编译)。用户写**裸文件名**引入(如 `@引入 "集合.zh"`,不含 `/`)且相对本文件打不开时,自动落到同一条查找路径(再退内嵌);仍找不到报原来的「无法打开」编译错。
- **标准库模块可以相互 `@引入`(2026-10-30)**:磁盘态按引入文件所在目录解析(如 `标准库/数据库.zh` 里的 `@引入 "同步.zh"` → `标准库/同步.zh`);内嵌态(干净房)按模块名从内嵌清单解析,因此发布件拷到没有 `标准库/` 的目录也能装载整条依赖链。`tools/mkstd.sh` 允许标准库文件含 `@引入`(仍禁止顶层可执行语句)。
- **网络命名空间(v2.23)**:可引入模块 `标准库/网络.zh` 提供 `网络.取网页`、`网络.网页请求`、`网络.连接到`、`网络.套接字发送`、`网络.套接字接收`、`网络.关闭套接字`、`网络.监听`、`网络.接受连接` 等同名包装(完整参数变体见模块内注释);包装体用英文别名调用内置。完整编译器/自举编译器可用;**原生后端三端也已实现**:Linux 纯 syscall(套接字为 fd 整数,DNS 内置,HTTPS 经 curl),Windows WinSock(套接字为 SOCKET 句柄,DNS 用 gethostbyname,HTTPS 经 WinHTTP)。**v2.39 扩展**:`网络.zh` 增加网址工具(`解析网址`/`拼查询`)、HTTP 客户端便利函数(`取网页跟随`/`网页请求跟随[最多]`/`取JSON`/`提交表单[完整]`/`提交JSON[完整]`/`请求头`/`下载`)、TCP 读取(`读全部[最多]`)与 Cookie 文本工具(`解析Cookie`/`Cookie头`)。新可引入模块 `标准库/网络增强.zh` 封装 `域名解析`、UDP 三件套、`套接字设超时`、`本端地址`/`对端地址`、`关闭写端` 及 `UDP请求[超时]`;这些内置只由原生后端实现,C 引导器/自举 `编译主` 不提供。
- **服务框架网络扩展(v2.39)**:`标准库/服务.zh` 请求字典带 `连接`(原始套接字)与 `余`(请求解析后剩余缓冲);处理函数置 `请求["接管"] = 真` 后框架不再写 HTTP 响应、直接结束该连接(供 WebSocket 等自行握手)。新增 multipart/form-data 解析(`表单文件` → `{"字段", "文件"}`,`表单值`/`上传文件` 便捷取值)与 SSE(`事件流(渲染器)`,配合 `发事件`/`发事件名`/`发事件编号`/`发注释`)。
- 编译仍为**单二进制**:用户模块的函数全部生成,隐式标准库模块按需发射(见上);`--emit-c` 输出自包含单文件 C。

### 3.4 异常处理(尝试/捕捉)

- **语法**:`尝试 块 [捕捉 错误变量 块] [最终 块]`;`捕捉` 与 `最终` **至少有一个**(不能只有尝试);错误变量在捕捉块作用域内,遮蔽外层同名。
- **可捕获的错误**:全部**运行时错误**——越界、除零、键不存在、类型错误、文件读失败、向已关闭通道发送、循环边界非整数……;`抛出(值)` 主动抛出(非字符串自动转字符串,英文别名 `raise`)。
- **`抛出对象(值)`**(英文 `raiseobj`)——主动抛出并**保留原值**;捕捉变量得到该值本身(例如字典/类实例),不做字符串转换。运行时错误与普通 `抛出` 仍产生字符串。
- **捕捉变量**:普通错误/`抛出` 得到错误消息字符串(纯消息,不含「运行时错误(第 N 行):」前缀);`抛出对象` 得到原值。
- **未捕获**:打印 `运行时错误(第 N 行):消息`(stderr)并退出码 1——与没有尝试时完全一致。
- **不可捕获的致命错误**(直接终止,不经尝试):内存不足、协程栈溢出、死锁检测、退出码非整数。
- **`最终` 块**:**离开尝试语句的每条路径都会执行最终块**——正常完成、异常被捕捉、异常未被捕捉(先执行最终块再按原行号重抛)、`返回`、`跳出`、`继续`。执行顺序是「尝试体/捕捉体 → 最终块」。最终块内的 `返回/抛出/跳出/继续` 会覆盖原先的退出路径。最终块不能访问捕捉变量(捕捉作用域在它之外)。
- **跨函数抛出** ✓;**嵌套尝试** ✓(内层先接;捕捉体内再 `抛出` 传播到外层)。
- **协程语义**(与 Go 的 panic 相同):每个协程有自己的尝试帧链;某协程内未捕获的抛出终止**整个进程**——主协程的尝试接不到其他协程的抛出。
- **后端**:`最终` 在自举编译器与纯 zhlang 原生后端(x86_64 ELF/PE、aarch64 ELF)均可用(先弹处理者帧再执行最终块,最终块里的退出不会留下悬空帧;aarch64 自 v2.23 跨自举验证起支持)。
- `返回`/`跳出`/`继续` 穿出尝试块时,尝试帧自动回退(弹帧)。
- 实现:跳转帧串在协程上;**自管 `zh_setjmp`/`zh_longjmp`**(裸函数汇编,与协程切换同款)——不用 CRT 的 setjmp/longjmp:真 Windows 上 msvcrt 的 `longjmp` 会走 SEH 栈展开(RtlUnwindEx),而协程栈自管、tcc 产物无 `.pdata`,展开会把进程打成 0xC00000FD/0xC0000028;含尝试的函数其局部变量生成 `volatile`(跳转跨越赋值点后按下标访问的语义要求,与 setjmp 同规)。

### 3.5 文档注释与 HTML 文档(v2.28 增补)

- **文档注释**是 `/** … */` 块注释;`/* … */` 与 `//` 仍是普通注释,不进入文档。
- **位置**:紧邻声明(函数/类/结构体/接口/类方法),中间只允许空行、`//` 注释与
  `@私有`/`@类方法`/`@类函数` 注解。文件**首个**注释块(其后跟空行或非声明内容)
  视为**模块文档**;紧跟声明的注释块归属该声明。
- **标签**(中英等价):`@作者`/`@author`、`@时间`/`@date`、
  `@说明`/`@description`、`@参数`/`@param 名 说明`、`@返回`/`@return`、
  `@示例`/`@example`、`@弃用`/`@deprecated`。未加标签的行归入说明;
  `@示例` 之后到下一个标签之间的行原样作为代码块。
- **生成器**:`tools/文档生成.zh`(纯 zhlang,依赖标准库 `网页`):

  ```sh
  build/原生 tools/文档生成.zh 源1.zh [源2.zh …] -o 文档.html \
      [--布局 自动|桌面|移动] [--含私有] [--仅文档]
  ```

  输出**自包含单文件** HTML(内联 CSS/JS、viewport、深色模式、侧栏导航、符号搜索,
  锚点 `#sN`,`sN` 按文件/声明顺序全局编号)。布局:`自动` 窄屏折叠为单列,
  `桌面` 固定双栏,`移动` 强制单列。默认跳过 `@私有` 声明与私有类的成员;
  `--含私有` 包含它们,`--仅文档` 只输出有文档注释的符号。
- 文档注释对编译器是普通注释,三端编译器均可编译含文档注释的源码;生成器本身
  需要原生后端运行。生成器按行扫描:签名取声明首行,结构体字段与接口方法暂不
  单独列卡片。

### 3.6 装饰器(原生后端,v2.34 增补)

仅**原生后端**(x86_64/aarch64/PE)支持;冻结的 C 版引导器与 `自举/` 源码不可使用。

- **声明装饰器**:`@装饰器` 修饰紧随其后的顶层函数声明。该函数第一个参数是「被装饰的函数值」,必须返回一个函数值:
  ```zh
  @装饰器
  函数 计时(f)
      返回 (x) =>
          常量 起 = 当前时间()
          常量 结果 = f(x)
          打印("耗时", 当前时间() - 起)
          返回 结果
  ```
  可与 `@私有` 叠加;装饰器名不得与内置 `@` 指令重名(如 `私有`/`引入`/`类方法`)。
- **使用**:`@名字`、`@模块.名字`、`@名字(实参)` 紧邻顶层函数声明;可栈式,表达式自上而下求值、应用自下而上(`f = 甲(乙(原f))`,只求值一次)。
- **应用时机**:模块初始化(结构体/类模板与类方法挂载之后、入口代码之前)。每个被装饰函数有隐藏函数值槽位;对公开名的直接调用、取函数值与递归都走槽位(即装饰后的函数)。
- **限制**:不能装饰类方法/类/箭头函数;装饰器不改变「最多 8 个位置参数、无变参」约束;装饰器返回值不是函数 → 运行时错误 `装饰器「名」没有返回函数`;未知名字 → 编译错 `未定义的装饰器「名」`;普通函数直接放在 `@` 位置 → 编译错(缺少 `@装饰器` 标记)。

## 4. 关键字全表(中英 → 同一 token)

| 中文 | 英文 | 用途 | 中文 | 英文 | 用途 |
|---|---|---|---|---|---|
| 变量 | var | 声明(可变) | 常量 | const | 声明(不可变) |
| 函数 | func | 定义函数 | 从 | from | 循环下界(英文可用 `=`) |
| 结构体 | struct | 结构体声明 | 返回 | return | 返回 |
| 到 | to | 循环上界 | 在 | in | 容器遍历 |
| 如果 | if | 分支 | 当 | while | 条件循环 |
| 否则如果 | elif | 多分支 | 跳出 | break | 跳出循环 |
| 否则 | else | 否则 | 继续 | continue | 下一轮 |
| 真 | true | 布尔真 | 启动 | go | 启动协程 |
| 假 | false | 布尔假 | 与 | and | 逻辑与(`&&`) |
| 引入 | import | 引入模块(仅 `@引入`) | 尝试 | try | 异常处理 |
| 选择 | select | 多路通道等待 | 捕捉 | catch | 捕捉异常 |
| 最终 | finally | 必然执行的清理块 | 或 | or | 逻辑或(`\|\|`) |
| 循环 | for | 计数循环 | 非 | not | 逻辑非(`!`) |

以下按上下文识别的**软关键字**词法上仍是标识符,不占关键字 token:`类`(类声明)、`类方法`/`类函数`(方法挂载注解)、`属性`(类字段前缀)、`初始化`(构造方法名)、`接收`/`发送`(选择臂)、`为`(模块别名/选择绑定)、`只`(选择性引入)——以上均无英文别名。另有 `私有` 也是软关键字,英文别名 `private` 只在顶层注解位置可用(类体内只认 `@私有`)。

`@` 不是关键字而是**注解/限定前缀**:`@私有`(声明上一行,仅本模块可见)、`@引入`(唯一导入写法;裸 `引入` 已退役)、`@模块.名字`(≡ `模块.名字`,含 `启动 @模块.函数(…)`)。

---

## 5. 并发模型(协程 + 通道)

### 5.1 协程

- `启动 f(参数)`:创建协程立即返回;参数值在启动时拷贝传入。
- **主协程返回即进程退出**(退出码取主协程返回值),不等其他协程——与 Go 相同。
- 实现:M:N 调度(C 运行时与原生运行时都是:每 worker 一个 OS 线程、全局就绪队列、
  协程可跨 worker 迁移;分点式抢占是 C 运行时的实现,原生运行时在分配点/循环回边
  做 GC 安全点,不做节拍式抢占)。每个协程独立 mmap 栈(4MB;并行代码生成的递归帧所需);
  worker 线程数 = CPU 核数(2..8,`ZHC_THREADS` 可覆盖)。
- **分点式抢占(v2.2)**:编译器在**循环回边**插入分点,运行库在长循环(排序/映射/过滤/归约)里也插分点;
  分点按节拍(每 256 次)做一次调度节拍——**唤醒到期睡眠者**,并在**有其他协程在等 worker(就绪队列非空)时让出**,
  由当前 worker 把本协程重新入队(轮转到队尾)。因此忙协程占满全部 worker 时,睡眠协程与长运算协程仍能推进;
  「一个协程、就绪队列为空」的常见形态只做一次加锁检查,不产生额外切换。
  **持运行库锁时绝不让出**(打印的输出锁等),避免让出把别的 worker 锁死。留白:不含信号式异步抢占
  ——单个分点之间不被打断(例如一次超大分配、一次超长正则回溯)。
- 协程结束后栈不再使用(C 运行时随即释放;原生后端当前保留栈块不复用——只是虚拟预留,`堆统计` 的 `栈字节` 可见,`堆用量` 不计入);函数序言检查协程栈限(协程结构 `+408`,= 栈基 + 256KB 余量)
  → 越限时报不可捕获的运行时错误「协程栈溢出」;三端同款,不依赖守护页/信号。
  (协程栈 4MB;**主协程 256MB**——只是虚拟预留、
  按需提交,普通程序只碰几页;留这么大是为了「用 zhlang 写编译器/解析器」这类深递归程序)。

### 5.2 通道

- `通道()` 无缓冲;`通道(n)` 容量 n(n 为非负整数)。缓冲满/空分别阻塞发送/接收。
- `发送(ch, v)` 阻塞发送,返回 v 本身;向已关闭通道发送 → 运行时错误「向已关闭的通道发送」。
- `接收(ch)` 阻塞接收;**通道已关闭且已空时返回 `假`**(注意:这与合法值 假 无法区分——需要区分时用关闭方先发哨兵值的约定,已知设计取舍)。
- `关闭(ch)`:幂等;缓冲中未取走的值仍可继续 `接收` 直到取空。
- 通道值(含缓冲中的值)可跨协程共享;发送/接收/关闭本身有内部锁保证线程安全。

### 5.3 选择(多路通道等待)

```
选择
    接收 ch1 为 v
        …使用 v…
    接收 ch2
        …丢弃收到的值…
    发送 ch3, 值
        …
    否则
        …都未就绪时的分支(可省)…
```

- 臂形态:`接收 通道 [为 变量]`(变量在臂块作用域)、`发送 通道, 值`、`否则`(**必须是最后一个臂**,可与任意臂共存);至少 1 个臂,至多 8 个。
- 语义:各臂按下标顺序检查**就绪性**,只执行其中一个;**多个臂同时就绪时随机执行其一**(Go 语义——已关闭的接收臂恒就绪,若固定取源码序首个会饿死其他臂;任一臂内可 `跳出/继续` 外层循环、`返回`、`尝试`);
  - 接收臂就绪:无缓冲有发送方/缓冲非空/**通道已关闭**(得 `假`);
  - 发送臂就绪:缓冲有空位/无缓冲有接收方;指向已关闭通道发送 → 运行时错误;
- 无臂就绪且无 `否则` → 挂起等待,任一通道有动静即被唤醒重查(与普通收发同款);
- 无臂就绪且有 `否则` → **立即执行否则臂**(非阻塞语义);
- 全协程都阻塞在等待时同样触发死锁检测报错;
- 各臂的通道与值表达式**先全部求值一次**再进入等待。

### 5.4 内存管理(自动垃圾回收)

- **堆**:字符串、通道、数组与字典为堆对象,由**标记-清除 GC** 自动回收:
  - 触发:堆用量超过预算(初始 32MB;每次回收后预算 = max(存活 × 2, 32MB));
  - 停止世界:安全点设在——每次堆分配、循环回边(编译器插入)、通道操作、休息;所有 worker 停稳后回收;
  - 根集合:**各协程的活跃栈帧**(保守扫描:自挂起点到栈顶——栈自顶向下生长,挂起点即活跃区最低端)+ 协程挂起时的 ucontext 保存寄存器 + GC 发起线程停世时的被调用者保存寄存器冲刷(x19–x30/d8–d15 或 rbx…r15)+ 调度器持有的通道等(精确);
  - 通道缓冲、数组元素、字典键值内的值按图精确扫描;**循环引用可回收**;
  - 保守扫描的已知局限:栈上恰好等于堆地址的整数值会被误保留一轮(安全方向的偏差);循环内反复 `+` 拼接字符串的时间复杂度仍是 O(n²)——GC 回收内存但不改变算法时间;文本累积请用 `字符串构造()/字符串追加()/字符串取值()`(总拷贝 O(n)),`连接/重复/替换` 已改为线性实现。
- **栈**:协程栈随协程生灭;主协程亦同。指针指向栈上的变量槽,不参与 GC 追踪(见 §2.1 的悬垂警告)。
- **自举原生版(官网下载)GC 阶段 2(保守标记-清除 + 空闲复用)**:每次分配
  仍登记到独立分配表(地址/大小/类别;表本身一次 192MB 匿名映射),并按阈值
  在分配点触发回收:
  - 根 = 全局区 + 各协程活跃栈区间(当前协程取 min(保存的 rsp, `原栈指针()`))
    + 协程上下文寄存器 + 全局/栈**保守扫描**(每个 8 字节字凡落在已登记块内
    即视为指针);协程栈整块置特殊类别,标记传播跳过、改按活跃区间扫描;
  - 未标记块回收进**按大小分级的空闲链表**(16..4096B 共 256 类,大块单链),
    后续分配优先复用(小块复用清零,保持「新分配为零」语义),堆字节随之回落;
  - 触发:距上次回收的分配增量超过预算(初始 32MB;每次回收后预算 =
    max(存活 × 2, 32MB),与本节上文一致);页索引按 4KB 页建立(页内平均块数
    约 8;64KB 页实测 ~127,单字查找链过长);页节点/标记栈溢出时本轮不回收
    (并翻倍预算退避),表满则永久停用 GC——失败方向都是「不回收」,不会错回收;
  - 保守扫描的已知局限与阶段 1 相同:栈上恰好等于堆地址的整数会被多保留一轮。
- 观测:`堆用量()` 返回当前堆字节用量(整数);原生版 `堆统计()` 返回
  `{块数, 字节, 容量, 表基, GC次数, GC中止, GC存活, GC块数, GC页节点}`
  (字节为**当前存活**登记字节;GC* 为诊断计数),
  `堆整理()` 压缩地址为 0 的无效登记项并返回是否已建立分配表;
  `堆回收()` 强制触发一次完整回收(调试/测试入口)。

---

## 6. 标准库(内置函数,不可重定义;均提供英文别名)

| 签名(英文别名) | 说明 |
|---|---|
| `打印(print)` | 逐个按转字符串规则输出,空格分隔,末尾换行;至少 1 个至多 8 个参数;**返回 假**(因此可作表达式) |
| `打印错误(eprint)` | 同 `打印`,但写标准错误并立即冲刷(诊断/日志用;可作表达式,返回 假) |
| `长度(len)` | 字符串长度(**按码点计**);数组元素数;字典键数 |
| `子串(substr)` | 按码点取子串;`长` 可超出末尾(截断);`起`/`长` 为负或 `起` 越界 → 运行时错误「子串越界」 |
| `查找(find)` | 首次出现的码点下标,找不到返回 -1 |
| `替换(replace)` | 全部替换,返回新串 |
| `转整数(toint)` | 字符串(允许首尾空白)/浮点(截断)/整数/布尔 → 整数;无法解析 → 运行时错误「无法将「…」转换为整数」 |
| `转浮点(tofloat)` | 同上 → 浮点 |
| `转字符串(tostr)` | → 字符串(同 §2.3) |
| `类型(type)` | 值的内置类型名:`整数`/`浮点`/`字符串`/`布尔`/`通道`/`数组`/`字典`/`套接字`/`函数`/`指针`(标准库的 zhlang 实现用它做类型守卫;与 `是()` 的原型链判定不同,后者测的是「是不是某结构体的实例」) |
| `读入(readln)` | 从标准输入读一行(不含换行);EOF 返回空串 |
| `读文件(readfile)` | 整个文件读为字符串;失败 → 运行时错误「无法读取文件「…」:原因」 |
| `写文件(writefile)` | 覆盖写;返回写入字节数;失败 → 运行时错误 |
| `写字节(writebytes)` | `写字节(路径, 数组)` 把元素为 0..255 的整数数组按原始字节写入;返回写入字节数(字符串以 NUL 结尾,ELF 等二进制必须用它) |
| `当前时间(now)` | Unix 时间戳(浮点秒) |
| `通道(chan)` | 创建通道(见 §5.2) |
| `发送(send)` / `接收(recv)` / `关闭(close)` | 见 §5.2 |
| `休息(sleep)` | 当前协程睡眠指定**毫秒**数(须非负整数,否则运行时错误);返回 真。原生后端在调度已启动时把协程挂到睡眠队列并让出 worker,不占住线程 |
| `协程数(cocount)` | 存活协程数(含主协程) |
| `堆用量(heapuse)` | GC 堆当前字节数(**不含**协程栈的虚拟预留;原生后端取最近一次回收后的存活登记字节 − 栈字节) |
| `堆统计(heapstats)` | 原生后端 GC:返回 `{块数, 字节, 容量, 表基, 栈字节, GC次数, GC中止, GC存活, GC块数, GC页节点}`(字节为当前存活登记字节且不含栈;栈字节为协程栈预留之和);未建立分配表时全为 0 |
| `堆整理(heapcompact)` | 原生后端 GC:压缩地址为 0 的无效登记项,返回是否已建立分配表(不回收) |
| `堆回收(heapcollect)` | 原生后端 GC 阶段 2:强制触发一次完整回收(标记-清除 + 空闲链复用),返回 真 |
| `追加(push)` | `追加(a, v1, …)` 数组末尾追加 1–7 个值,返回数组本身(就地修改) |
| `弹出(pop)` | 移除并返回数组末元素;空数组 → 运行时错误 |
| `取(get)` | `取(d, k[, 默认])` 字典安全读取;缺键返回 默认(省略时 假) |
| `删除(del)` | `删除(d, k)` 移除键,返回 真/假(是否原本存在);不影响其余键序 |
| `包含(contains)` | 数组按值查、字典按键查(沿原型链),返回布尔 |
| `继承(inherit)` | `继承(原型[, 添加])` → 带原型指针的新字典(隐藏只读;读走链、写落自身、枚举只看自己,见 §3 原型链) |
| `是(is)` | `是(对象, 原型)` → 对象自身或原型链上有该原型(身份比对);第一参非字典为假 |
| `键(keys)` / `值(values)` | 字典的键数组 / 值数组(按插入序) |
| `映射(map)` | `映射(数组, f)` → 新数组(`f(元素)` 逐个);数组与 f 类型错 → 运行时错误 |
| `过滤(filter)` | `过滤(数组, f)` → 保留 `f(元素)` 为真的元素组成新数组 |
| `归约(reduce)` | `归约(数组, 初值, f)` → 左折叠,`f(累积, 元素)` |
| `索引(数组, 值)` / `计数(数组, 值)` | 首次出现的下标(找不到 -1)/出现次数;按 `==` 的深度相等(标准库实现) |
| `去重(数组)` | 保持首次出现顺序的新数组;数值按 == 语义(`1` 与 `1.0` 同值),数组/字典按深度相等 |
| `求和(数组)` | 数值和(整数保整数、混浮点升浮点;空数组为 0;含非数值 → 运行时错误) |
| `反序(数组)` | 倒序的新数组(原数组不变;原地反转用 `集合.zh` 的 `反转`) |
| `扁平(数组)` | 只展开一层;非数组元素原样保留 |
| `最大元素(数组)` / `最小元素(数组)` | 非空数组的极值(元素须可比较) |
| `复制(容器)` | 数组/字典的浅拷贝(内层容器仍共享) |
| `合并(甲, 乙)` | 新字典:甲浅拷贝后乙覆盖;甲已有键保持位置、乙新键按乙序追加 |
| `字典筛选(字典, f)` / `字典映射(字典, f)` | `f(键, 值)` 为真保留 / 返回新值;保持键序 |
| `排序(sort)` | `排序(数组)` 默认升序(整数/浮点互通、字符串字典序;混类型 → 运行时错误);`排序(数组, f)` 用 `f(a, b)` 比较,**f 须返回整数**(负/零/正,返回其他类型 → 运行时错误);**稳定排序**,就地修改并返回数组本身 |
| `平方根(sqrt)` `正弦(sin)` `余弦(cos)` `正切(tan)` `对数(log)` `指数(exp)` | 数学函数(浮点入浮点出;负数开方/取对返回 NaN 不报错;参数须为数值) |
| `幂(pow)` | `幂(底, 指数)` → 浮点 |
| `夹取` `符号` `最大公约数` `最小公倍数` | 数值夹取 / 符号(-1,0,1) / 欧几里得 GCD / LCM(任一为 0 返回 0) |
| `阶乘` `幂模` `是否质数` `质数列表` | 阶乘(溢出抛错)、快速幂取模(模 ≤ 3037000499)、质数判定、≤1e7 的质数表 |
| `转进制` `解析进制` | 2..36 进制互转;2 的幂精确到 64 位,其余基受 2^53 限制 |
| `均值` `中位数` `方差` `标准差` `舍入到` | 数值数组统计(空/非数值抛错)与按小数位四舍五入(半数远离零) |
| `绝对值(abs)` | 整数保整数,浮点为浮点 |
| `加溢出(a, b)` / `减溢出(a, b)` / `乘溢出(a, b)` | 64 位整数 + / - / * 是否会溢出 → 布尔(标准库实现;普通 `+ - *` 仍是规格的 64 位环绕) |
| `加检(a, b)` / `减检(a, b)` / `乘检(a, b)` | 返回运算结果;溢出时抛运行时错误「整数加法/减法/乘法溢出」(标准库实现) |
| `向下取整(floor)` `向上取整(ceil)` `四舍五入(round)` | 返回整数;四舍五入为「远离零」(2.5→3,-2.5→-3) |
| `随机数(rand)` | `随机数(n)` → [0, n) 整数;须正整数;种子自动初始化 |
| `分割(split)` | `分割(s, 分隔串)` → 字符串数组;分隔串不能为空(空串报运行时错误) |
| `连接(join)` | `连接(数组, 连接串)` → 字符串(元素按打印的顶层规则转字符串) |
| `字符串构造()` / `字符串追加(缓冲, 片段)` / `字符串取值(缓冲)` | 文本拼接缓冲(标准库实现):追加 O(1) 摊还、取值一次性连接;替代 `s = s + x` 的 O(n²) 累积 |
| `字符串长度(s)` / `串长(s)` | `长度(s)` 的别名(按码点计;标准库实现) |
| `转大写(upper)` / `转小写(lower)` | 仅 ASCII 字母;其余字节原样 |
| `去空白(trim)` | 去两端空白(ASCII 空白 + 全角空格 U+3000) |
| `开头是(startswith)` / `结尾是(endswith)` | 前缀/后缀判断 → 布尔;空前缀/后缀恒真 |
| `重复(repeat)` | `重复(s, n)`;n 为负 → 运行时错误 |
| `反转文本` `左填充` `右填充` `去前缀` `去后缀` `分行` `字节长度` `出现次数` `是空白` | 文本工具:按码点反转;填充到宽度;剥离前后缀;按行切分(去 CR);UTF-8 字节数;不重叠出现次数;纯空白判定 |
| `格式化时间(fmttime)` | `格式化时间([时间戳[, 格式]])`,strftime 格式,本地时区;默认当前时间与 `%Y-%m-%d %H:%M:%S`。支持 `%Y %y %m %d %e %H %M %S %F %T %R %j %w %u %a %A %b %B %z %s %n %t %%`(英文星期/月名,与 C locale 的 strftime 一致) |
| `参数(args)` | 命令行参数数组(不含程序名)。*平台注*:Windows 下取自 `GetCommandLineW`(UTF-8)——CRT 的 `argv` 是按 ANSI 码页解释的,中文参数会变 `??` |
| `抛出(raise)` | 主动抛出异常(见 §3.4);非字符串自动转字符串 |
| `抛出对象(raiseobj)` | 主动抛出并保留原值;捕捉变量得到对象(见 §3.4/§6.6);未捕获时优先显示值的「消息」字段 |
| `文件存在(exists)` / `文件大小(filesize)` | stat;`文件大小` 对不存在的文件 → 运行时错误 |
| `真实路径(realpath)` | 规范绝对路径(POSIX `realpath(3)`:解析符号链接;Windows 绝对化 + 存在性检查);路径不存在/无法解析 → 运行时错误「无法解析路径「…」:原因」。编译器用它做模块去重(§3.3) |
| `确保归档(ensurearchive)` | 把编译器内嵌的某目标 sysroot 归档解压就绪,返回其缓存目录(无内嵌 → 空串)。自举编译器的 sysroot 预检用;普通程序恒得空串 |
| `删除文件(remove)` | unlink,返回是否成功 |
| `列目录(listdir)` | 目录项名数组(不含 `.`/`..`);非目录 → 运行时错误 |
| `建目录(mkdir)` | **递归**创建(0755);已存在返回 真 |
| `追加文件(appendfile)` | 追加写,返回字节数 |
| `解码JSON(jsondec)` | JSON → 值:**null 解码为 假**(取舍);`\uXXXX`(含代理对)转 UTF-8;语法错/尾随内容/嵌套>128 → 运行时错误(可捕获) |
| `编码JSON(jsonenc)` | 值 → JSON 字符串:字典键须为字符串;字符串转义(`\"` `\\` `\n`、控制字符 `\u00XX`),UTF-8 透传;整数/浮点(最短往返)/真/假/数组/字典;嵌套>128(含循环引用)→ 运行时错误 |
| `编码URL(urlenc)` / `解码URL(urldec)` | 百分号编码(保留 `-_.~` 与字母数字);解码时 `+`→空格 |
| `编码BASE64(base64enc)` / `解码BASE64(base64dec)` | 标准字母表带 `=` 填充;解码宽松容忍填充与换行,非法字符 → 运行时错误 |
| `编码十六进制(hexenc)` / `解码十六进制(hexdec)` | UTF-8 字节 ↔ 小写十六进制;解码要求偶数长度,非法字符 → 运行时错误 |
| `哈希SHA256(sha256)` / `哈希MD5(md5)` | 输入的十六进制摘要(小写);MD5 仅供校验/去重等非安全用途 |
| `哈希HMAC(hmacsha256)` | HMAC-SHA256(键, 消息),返回小写十六进制;键超过 64 字节先取 SHA256(RFC 2104) |
| `哈希文件(sha256file)` / `哈希文件MD5(md5file)` | 文件内容摘要(SHA256 / MD5,二进制安全) |
| `取网页(httpget)` | HTTP/HTTPS GET → 响应体;DNS/连接/超时/状态≥400 → 运行时错误(含状态码);**支持 https**(见 §6.2) |
| `网页请求(httpreq)` | `网页请求(url[, 方法[, 头字典[, 体]]])` → `{"状态": 整数, "体": 字符串, "头": 字典(键小写)}`;连接超时 5s,收发超时 10s;支持 chunked 响应;https 同上 |
| `连接到(tcpconn)` | `连接到(主机, 端口[, 超时秒=5])` → **套接字**(新值类型:按身份相等、恒真、显示为「套接字:N」;GC 自动关闭) |
| `套接字发送(tcpsend)` | 发送字符串(阻塞直到全部发出),返回字节数 |
| `套接字接收(tcprecv)` | `套接字接收(s[, 最多字节=4096])` 阻塞收一次 → 字符串;对端关闭且无数据 → 空串 |
| `关闭套接字(tcpclose)` | 关闭(幂等);未关闭的套接字由 GC 终结器关闭 |
| `监听(listen)` | `监听(端口[, 待发队列=128])` → **服务套接字**(SO_REUSEADDR,绑 0.0.0.0);端口占用 → 运行时错误 |
| `接受连接(accept)` | 阻塞等待并返回客户端套接字(同 ZH_SOCK);范式:`当 真 / 常量 c = 接受连接(srv) / 启动 处理(c)`——每连接一协程 |
| `域名解析(resolvehost)` | `域名解析(主机)` → IPv4 地址数组:IPv4 字面量原样、`/etc/hosts`、DNS 全部 A 记录(原生运行时);`ZHC_DNS` 可写 `IPv4` 或 `IPv4:端口`;Windows 走 `gethostbyname`(单个) |
| `UDP绑定(udpbind)` | `UDP绑定(端口[, 绑定地址])` → UDP 套接字(SO_REUSEADDR;地址省略为 0.0.0.0);端口占用/地址非法 → 运行时错误 |
| `UDP发送到(udpsendto)` | `UDP发送到(套接字, 主机, 端口, 数据)` → 发送字节数;主机按 `域名解析` 规则解析 |
| `UDP接收自(udprecvfrom)` | `UDP接收自(套接字[, 最多字节=65535])` 阻塞收一个数据报 → `{"数据": 字符串, "地址": 字符串, "端口": 整数}` |
| `套接字设超时(socktimeout)` | `套接字设超时(套接字, 秒)` → 真;每次收发/接受独立计时(0 清除);Linux 非阻塞套接字由运行时计时,Windows 同时设 SO_RCVTIMEO/SO_SNDTIMEO;超时抛「套接字接收超时」等可捕获错误 |
| `本端地址(sockname)` / `对端地址(peername)` | 套接字本地/对端地址 → `{"地址": 字符串, "端口": 整数}`(getsockname/getpeername) |
| `关闭写端(sockhalfclose)` | 半关闭写方向(SHUT_WR);读方向仍可用 |
| `执行(shell)` | `/bin/sh -c` 执行,捕获 stdout → `{"输出": 字符串, "退出码": 整数}`;stderr 原样透传 |
| `执行捕获(execcap)` | `执行捕获(程序, 参数数组)` **不经 shell** 直接执行:argv 原样传,stdout+stderr 合流 → `{"输出": 字符串, "退出码": 整数}`;程序起不来 → 运行时错误 |
| `是终端(isterm)` | 标准输入是不是终端(isatty(0)) → 真/假 |
| `忽略中断(ignoreint)` | `忽略中断(真/假)` 置 SIGINT 忽略开关(REPL 在 Ctrl-C 下存活);返回 假 |
| `程序路径(exepath)` | 本程序路径(argv[0]):CLI 靠它找自身目录下的 sysroot/后端归档 |
| `输入结束(inputeof)` | 标准输入是否已到末尾(`读入` 在 EOF 与空行都返回 "",靠它区分) |
| `输出(output)` | `输出(s)` 逐字写 stdout、**不补换行**、立即冲刷(提示符/前缀裁剪用);返回 假 |
| `终端原始模式(terminalraw)` | `终端原始模式(真/假)` 进入/退出行编辑模式(POSIX:清 ICANON/ECHO/ISIG/IEXTEN,保留 ICRNL/OPOST;Windows:清 ENABLE_LINE_INPUT/ENABLE_ECHO_INPUT/ENABLE_PROCESSED_INPUT,并对控制台输出句柄开启 ANSI/VT 处理);成功返回 真;非终端返回 **假**(调用方回退 `读入`);Windows 控制台不支持 VT(旧系统)或输出被重定向时也返回 假 |
| `读键(readkey)` | 读一个键:返回码点(0..0x10FFFF);特殊键返回 `0x110001`–`0x110007`(依次为上/下/左/右/Home/End/Delete);EOF 返回 -1。仅在 `终端原始模式(真)` 成功后使用(块读) |
| `显示宽度(displaywidth)` | 字符串的终端显示列宽:组合字符 0、CJK/全角/emoji 2,其余 1;非法 UTF-8 字节按 1 计。参数须为字符串,否则运行时错误 |
| `字符(chr)` | `字符(码点)` → 单字符字符串;码点越界(含代理区)→ 运行时错误 |
| `环境变量(getenv)` | `环境变量(名[, 缺省])`;未设置返回 缺省(省略时 假) |
| `正则匹配(regexmatch)` | `正则匹配(文本, 模式[, 选项])` → 真/假(是否含匹配,即「查找得到」) |
| `正则全匹配(regexfullmatch)` | `正则全匹配(文本, 模式[, 选项])` → 真/假(整串须恰好匹配) |
| `正则查找(regexfind)` | 首个匹配子串;无匹配 → 假 |
| `正则全部(regexfindall)` | 所有(不重叠)匹配子串 → 数组;无匹配 → 空数组 |
| `正则分组(regexgroups)` | 首个匹配的分组 → 数组(下标 0 = 整匹配,其后按左括号序);未参与的分组为 假;无匹配 → 假 |
| `正则替换(regexreplace)` | `正则替换(文本, 模式, 替换串[, 选项])`;全部替换;替换串中 `$0`–`$9` 引用分组、`$$` 为字面 `$` |
| `正则分割(regexsplit)` | 按匹配切开 → 数组(不含分组;与 Python 的 split 收组不同) |
| `正则转义(regexescape)` | 转义元字符(与 Python 3.7+ `re.escape` 同集),用于把字面串安全嵌进模式 |

别名与中文名指向同一函数,可混用。调用非内置且未定义的函数、参数个数不符 → **编译错**(非运行时错)。

**实现形态(v2.12)**:表中 `分割`/`连接`/`开头是`/`结尾是`/`重复`/`去空白`/`转大写`/`转小写`/`转整数`/`编码BASE64`/`解码BASE64`/`正则转义`/`编码URL`/`解码URL`/`哈希SHA256`/`哈希MD5`/`映射`/`过滤`/`归约`/`绝对值` 二十条(以及 v2.39 新增的 `字符串构造`/`字符串追加`/`字符串取值`/`字符串长度`/`串长` 与 `加溢出`/`减溢出`/`乘溢出`/`加检`/`减检`/`乘检`、`索引`/`计数`/`去重`/`求和`/`反序`/`扁平`/`最大元素`/`最小元素`/`复制`/`合并`/`字典筛选`/`字典映射`)由 **zhlang 源码**实现(仓库 `标准库/{字符串,编码,容器,数学,摘要}.zh`,隐式装载,见 §3.3;**按需发射**——未被引用的模块不进入产物);**行为与本规格一致**(函数名、参数、错误文案逐字不变,保持原语义)。差别仅在**运行时错误的行号**指向标准库实现行(见 §7)。另有用户可引入的标准库模块 `标准库/集合.zh`(`反转`(原地反转)、`最大值(a, b)`、`最小值(a, b)`,等值返回 a;裸名 `@引入 "集合.zh"` 即可,见 §3.3)与 `标准库/同步.zh`(互斥锁/条件/读写锁/等待组/一次,见 §6.5)、`标准库/错误.zh`(类型化错误基类/工厂/文本提取,见 §6.6)。

### 6.1 正则(v2.0)

**语法**:字面字符、`.`(任意码点,不含 `\n`)、`^` `$`(串首/串尾;非多行时 `$` 也匹配结尾换行前)、`\A` `\Z`(严格串首/串尾)、
字符类 `[abc]` `[^abc]` `[a-z]` `[一-龥]`(`]` 作首字符为字面)、预定义类 `\d \D \w \W \s \S`、
词界 `\b` `\B`、分组 `(...)` 与 `(?:...)`、或 `|`、
量词 `*` `+` `?` `{n}` `{n,}` `{n,m}`(均可后缀 `?` 表示懒匹配)、转义 `\n \t \r \f \v \0 \uXXXX \xNN` 与标点字面。

**选项**(第 3 参,缺省空串;`i` 忽略大小写(ASCII、全角字母、Latin-1)、`s` 让 `.` 匹配换行、`m` 让 `^` `$` 按行):
`正则匹配(文本, 模式, "is")`。

**语义**(与 Python `re` 逐项对齐):最左匹配、备选取先(左优先)、量词贪婪(懒匹配后缀 `?`);
空匹配的处理同 Python 扫描器——某位置匹配为空时,**同位置再试一次非空匹配**,再前进一个码点
(故 `正则全部("aaaa", "a*?")` 得 9 个结果:`"", "a", "", "a", …`)。

**未支持**:反向引用(`\1`)、命名分组、环视 `(?=…)`、条件分组、POSIX 字符类 `[[:alpha:]]`、`\p{…}` —— 用到即报「不支持/未知转义」错。
**Unicode 口径是近似**:`\w` 对非 ASCII 默认算词字符,但 CJK 标点(U+3000–303F)、通用标点(U+2000–2BFF)、全角标点、表情符号除外(与 Python 对 CJK 文本大体一致);
`\d` 认 ASCII 与全角数字;`\s` 认 ASCII 空白与常见 Unicode 空白(含 U+3000)。

**限定与错误**:模式是运行时值,模式错误与超限都走**运行时错误**(带行号):
`正则模式错误:…`(如「分组未闭合(缺少 ))」「不支持反向引用(\1)」「字符类范围颠倒」);
回溯有**步数上限**(约 2 亿步)——病态模式(如 `(a+)+b` 长串)报「回溯步数超限」而不是挂死;
含「或/重复」的**分组重复**在长输入上有**约 100 轮**的嵌套上限(报「回溯嵌套过深」),
而确定性体(如 `(?:ab)*`、`(\d\d,)*`)与前缀单字符重复(`a*`、`\d+`)走迭代实现,**不受轮数限制**。

### 6.2 网络与 TLS(v2.1)

`取网页` / `网页请求` / `连接到` / `监听` / `接受连接` 是内置网络栈;其中 **原生后端**的 https
走**纯 zhlang TLS 1.3**(`标准库/TLS.zh`);冻结的 C 版/自举编译器走自包含 mbedTLS 3.6.2
(代码 vendored 在 `third_party/mbedtls/`,见其 ZHLANG-VENDOR.md)。

- **协议**:TLS 1.3 与 1.2(客户端;C 版/自举的 mbedTLS 路径)。**原生后端的纯 zhlang TLS 仅实现 TLS 1.3**(TLS_CHACHA20_POLY1305_SHA256 + x25519);只支持 TLS 1.2、或不提供 ChaCha20 密码套件的服务器(部分 badssl 用例、百度等)无法连接,可用 `ZHLANG_HTTPS=curl` 回退系统 curl。SNI 送出;**只做客户端**,不提供 DTLS/服务端/客户端证书/会话票据持久化。
- **撤销检查**:mbedTLS 路径(C 版/自举)不做 OCSP/CRL;原生后端的纯 zhlang TLS 支持 **CRL**
  (本地 PEM 文件 + CDP 抓取,吊销命中硬失败)与 **OCSP 钉接**(只验证服务器在 CertificateEntry
  中钉接的响应,不主动请求响应器)。
- **校验**:`VERIFY_REQUIRED`——证书链必须链到受信任 CA,且主机名必须与证书 SAN(无 SAN 时回退 CN)匹配;
  任一不符即**运行时错误**(`TLS 握手失败(主机名:TLSv1.3):X509 - Certificate verification failed…`),
  绝不静默放行。
- **CA 来源**(按序):环境变量 `ZHLANG_CA_FILE` / `SSL_CERT_FILE` → 系统包
  (`/etc/ssl/certs/ca-certificates.crt`、`/etc/pki/tls/certs/ca-bundle.crt`、
  `/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem`、`/etc/ssl/cert.pem` 等) →
  **内嵌**的 CA 包(`third_party/ca-bundle.crt`,三个目标都随产物携带,最小容器/真机开箱可用)。
  全部落空 → 报错并提示安装 `ca-certificates` 或设置 `ZHLANG_CA_FILE`/`SSL_CERT_FILE`。
  系统包优先于内嵌包,系统更新 CA 后立即生效。
- **超时**:连接 5s;收发沿用套接字读超时 10s(TLS 层把读超时映射为错误而不是空转)。
- **收尾**:对端直接关连接而未发 close_notify(精简实现常见)按正常结束处理;正文以连接关闭定界,
  不做 Content-Length 截断检测(需要严格截断防护的场景请自行核对响应长度)。
- **排查开关**:`ZHLANG_TLS_DEBUG=2`(1~4 递增)打印 mbedTLS 报文级日志到 stderr。
- **原生后端(纯 zhlang TLS,M4)**:x86_64/aarch64 Linux 的 https 由 `标准库/TLS.zh` 完成 TLS 1.3 握手、证书链/主机名校验与传输;**不依赖 curl、不链接 C 库**。信任库顺序:`ZHLANG_CA_FILE` → `SSL_CERT_FILE` → 系统 CA 文件 → 编译期内嵌 `third_party/ca-bundle.crt`(`ZHLANG_CA_EMBEDDED=1` 可强制内嵌)。校验失败**必定报错,不静默回退**;套接字在原生后端就是 fd/SOCKET 整数,`类型(监听(...))` 返回 `整数`,打印按整数输出;`套接字` 类型标注按整数承载检查(见 §2.3);`ZHLANG_HTTPS=curl` 可显式改用系统 `curl -sS -i`。Windows PE 仍用 WinHTTP(系统 TLS/CA)。TLS 代码与内嵌 CA 只在程序用到 `取网页`/`网页请求` 时发射。
- **体积账**:TLS 代码**只在程序用到 `取网页`/`网页请求` 时才链进产物**(编译期门控 `ZHLANG_HTTP_TLS`),
  用到时约 +0.6~0.9MB;不用 HTTP 的程序体积不变。交叉目标从 `sysroot/<目标>/lib/libzhtls-<目标>.a`
  取归档(缺档则 https 回退为「暂不支持」错误,明文 http 不受影响)。


### 6.4 进程与终端(v2.6)

这六个内置供 **CLI/REPL 用语言自身实现**而设(自举编译器 `编译主.zh` 的
`--repl` 通路)。都可用英文别名调用。

- `执行捕获(程序, 参数数组)` —— 与 `执行` 的**关键差别是不经 shell**:
  参数按数组原样进 argv(空格、引号、通配符都不会被再解释一遍),退出码
  也不会被 shell 覆盖;`stdout` 与 `stderr` **合流**捕获(顺序即真实写出顺序),
  返回 `{"输出": 字符串, "退出码": 整数}`(被信号结束记 1)。
  程序无法启动(不存在/无执行权限)→ **可捕获的运行时错误**
  「无法执行「…」:<原因>(程序不存在或无法启动)」。
  参数数组只能放字符串,否则运行时错误。
  *平台注*:Windows 的 argv 经 ANSI 码页,非 ANSI 字符到不了子进程(与 shell 无关的既有约束)。
- `是终端()` —— `isatty(0)`。REPL 据此决定要不要印提示符(`>>> ` / `... `);
  管道/重定向里为假,REPL 输出因此可稳定对拍。
- `忽略中断(真/假)` —— 置 `SIGINT` 为 `SIG_IGN`/`SIG_DFL`。REPL 开头调
  `忽略中断(真)`:子进程收到 Ctrl-C 不会把 REPL 一起带走。
- `程序路径()` —— `argv[0]`。CLI 靠它定位**自身可执行文件所在目录**,
  进而找 `<程序目录>/sysroot/<三元组>` 里的交叉归档(自举编译器的关键依赖)。
- `输入结束()` —— `feof(stdin)`。`读入` 在 EOF 与「用户敲了空行」都返回 `""`,
  REPL 靠这个区分 Ctrl-D 与回车续行。
- `输出(s)` —— 逐字写 `stdout`、不补换行、立即冲刷。`打印` 会补换行并加空格分隔,
  `输出` 不会 —— REPL 的提示符与「只显示新增输出」的前缀裁剪要的正是前者。

### 6.5 同步原语(标准库/同步.zh)

`标准库/同步.zh` 是可引入的标准库模块(**不是内置、不隐式注入**),纯 zhlang 用通道实现,
为共享内存并发提供 Go `sync` 风格的锁。裸名引入(见 §3.3):

```
@引入 "同步.zh" 只 互斥锁, 读写锁, 等待组, 一次
@引入 "同步.zh" 为 同步
```

- **互斥锁**:`初始化()`、`加锁()`(阻塞)、`解锁()`(未持有时抛可捕获错误;不校验解锁者身份)、
  `尝试加锁()` → 真/假、`加锁执行(f)`(作用域锁:f 正常返回或抛错都会解锁,返回 f 的结果)。
- **条件**:`初始化(互斥锁)`、`等待()`、`通知()` → 真/假、`通知全部()` → 被唤醒数。
  与 Go `sync.Cond` 同规:`等待/通知/通知全部` 必须在**关联的互斥锁持有期间**调用;
  等待者进入 FIFO 队列后释放锁,被唤醒后重新加锁。
- **读写锁**:`读锁()/读解锁()`、`写锁()/写解锁()`、`尝试读锁()/尝试写锁()`、
  `读锁执行(f)/写锁执行(f)`。读者共享、写者独占,**写者优先**(有写者在等时新读者等待,
  避免写者饿死);非重入。读/写解锁在计数不符时抛错。
- **等待组**:`添加(n)`、`完成()`(=`添加(-1)`)、`等待()`。计数减到 0 时广播唤醒全部等待者;
  `添加` 须在 `等待` 之前完成(Go `sync.WaitGroup` 同规),归零后可复用;负计数抛错。
- **一次**:`执行一次(f)`。首个调用者执行 f 并返回 真,其余调用者等待 f 结束(或抛错)后返回 假;
  f 的返回值不向外传。f 抛错也算「已执行」——错误只抛给执行者,等待者不重跑;递归调用自身会死锁
  (与 Go `sync.Once` 相同)。

限制与约定:

- 全部阻塞都是通道阻塞,由调度器挂起协程,不占死 worker;实现只依赖 `通道`/`选择`,不新增运行时。
- 互斥锁/读写锁**不是重入锁**,也不校验持有者身份(与 Go `sync` 一致)。
- zhlang 没有 `finally`;临界区可能抛错时用 `加锁执行/读锁执行/写锁执行`,手写加解锁需覆盖所有退出路径。
- 通道只保护队列本身,不保护负载;数组/字典内部仍无锁,并发读写共享容器必须用这里的锁或通道所有权。
- 同步模块基于协程/通道,自举原生版已支持(x86_64/aarch64 实测,Windows PE 经 wine 验证)。

### 6.6 类型化错误(标准库/错误.zh)

`标准库/错误.zh` 是可引入模块(不是内置),提供共享的错误基类、工厂与判别工具,
配合 §3.4 的 `抛出对象` 使用。

- **`错误基类`**:字段 `消息 字符串`、`代码 整数`、`原因`(默认 假);方法 `文本()` 返回消息。
  它作为原型链的基类:`继承(错误基类, {...})` 或工厂函数都能创建带该原型的错误对象。
- **工厂**:`新建错误(消息)`、`新建错误码(消息, 代码)`、`新建错误类型(类型, 消息, 代码)`。
  返回带 `错误基类` 原型与 `消息`/`代码`/`原因` 字段的字典;`新建错误类型` 额外写 `类型` 标签。
- **工具**:`错误文本(e)`(字符串原样;字典优先取 `消息`;其余转字符串)、
  `是错误(e)`(字典且沿原型链是 `错误基类`)、`错误类型(e)`(是错误时返回 `类型` 字段,缺省空串)。

```
@引入 "错误.zh" 只 新建错误类型, 是错误, 错误类型
尝试
    抛出对象(新建错误类型("参数错误", "年龄必须为正", 400))
捕捉 e
    如果 是错误(e)
        打印(错误类型(e), e.消息, e.代码)
```

捕捉变量得到**对象**(字典),可 `是(e, 错误基类)`、读字段;普通 `抛出` 与运行时错误仍是字符串。
未捕获的 `抛出对象` 优先打印对象的 `消息` 字段(含原型链),没有该字段时按显示规则打印整个值。

**限制**:语言目前只支持**同一模块内**的 `类 子类(父类)` 继承;跨模块类继承不支持。
因此本模块用**原型链 + `类型` 字段**做错误分类。若需要真正的类继承,可在同一文件内自行
声明错误基类与子类(类语法、`是`、初始化继承都可用),再用 `抛出对象` 保留实例。

原生实验后端支持 `抛出对象` 与字典/原型错误的捕捉;未捕获对象只打印「未捕获的异常」,
不打印消息字段。

### 6.7 可引入命名空间模块(v2.23 增补)

以下模块都不是内置、不隐式注入,源码在 `标准库/`,用裸名引入(见 §3.3)后限定调用:

| 模块 | 主要 API |
|---|---|
| `文件` | `读文件` `写文件` `追加文件` `写字节` `读字节` `复制文件` `写行` `追加行` `存在` `大小` `删除` `建目录` `确保目录` `列目录` `递归列目录` `真实路径` `是目录` `目录存在` `是文件` `读行` `临时目录` `临时文件` `临时文件夹` |
| `数据库` | `打开` `内存`;库对象:`建表` `表` `有表` `表名` `删表` `保存` `事务` `是否已改`;表对象:`插入` `按` `找` `查询` `第一` `全部` `计数` `更新` `更新条件` `删除` `删除条件` `建索引` `索引列表`(见 §6.12) |
| `JSON` | `编码` `解码` `美化` `美化缩进` `美化文本` `读取` `读取或` `尝试解码` `写入` `写入美化` `是JSON` |
| `正则` | `匹配` `全匹配` `查找` `全部` `分组` `替换` `分割` `转义`;带选项的 API 以 `…选项` 结尾(如 `匹配选项`) |
| `进程` | `执行` `执行捕获` `运行` `程序路径` `参数` `环境变量` `环境变量或` `成功` `输出` `退出码` `检查` `运行输出` `命令输出` `运行成功` |
| `终端` | `是终端` `读入` `输出` `提示` `读键` `显示宽度` `字符` `忽略中断` `原始模式` `输入结束` |
| `时间` | `现在` `格式化` `默认格式` `格式化当前` `日期` `时间` `日期时间` `毫秒` `现在毫秒` `经过` `计时` `睡眠` `分解` `是否闰年` `月天数` `增加秒` `增加天` `当天零点` `星期名` `格式化ISO` `解析日期` |
| `随机` | `随机整数` `随机范围` `随机浮点` `随机布尔` `随机选择` `洗牌` `抽样` `随机字节` `随机字符串` `随机密码` `随机正态` `按权重选择` |
| `路径工具` | `是绝对` `拼接` `目录名` `父目录` `文件名` `扩展名` `去扩展名` `去尾分隔` `规范化` `拆解` `家目录` |
| `网页` | `转义HTML` `文本` `原样` `片段` `元素` `空元素` `逐块输出` `动态` `响应` `响应码` `流式响应` `流式响应码` `响应体` `按需` `动态加载` `动态加载文本` `加载脚本` `加载脚本文本`;一次性/兼容:`渲染` `渲染美化` `区块` `段落` `标题` `链接` `图片` `列表` `有序列表` `列表项` `表格` `按钮` `输入` `换行` `水平线` `表单` `样式块` `样式链接` `脚本` `脚本链接` `脚本模块` `页面` `页面带语言` `页面带样式` `页面带资源` `页面完整` `写出` `写出美化` |
| `样式` | `像素` `百分比` `em` `rem` `秒` `毫秒` `类选择器` `标识选择器` `标签选择器` `后代` `子代` `同时` `规则` `媒体` `关键帧` `导入` `原样` `根变量` `样式表` `写出` |
| `JS` | `原样` `标识` `字面` `字符串` `数` `布尔` `数组` `对象` `二元` `三元` `一元` `调用` `成员` `索引` `箭头` `模板` `常量声明` `变量声明` `赋值` `表达式语句` `返回语句` `返回空` `如果语句` `如果单` `当语句` `逐语句` `循环语句` `函数声明` `尝试语句` `尝试捕捉` `尝试最终` `跳出语句` `继续语句` `块` `注释` `导入` `导入默认` `导出` `导出默认` `程序` `渲染` `渲染紧凑` `写出` `写出紧凑` |
| `服务` | `新应用` `配置` `取值` `设值` `使用` `分组` `异常处理` `未找到` `日志` `彩色` `路由` `装饰` `任何` `GET` `POST` `PUT` `DELETE` `PATCH` `OPTIONS` `HEAD` `路由列表` `打印路由` `静态` `测试请求` `测试请求体` `测试请求头` `测试请求头体` `响应` `文本` `HTML` `动态HTML` `动态HTML码` `流式HTML` `流式HTML码` `响应体` `JSON` `状态` `加头` `追加头` `重定向` `重定向到` `空` `错误` `文件响应` `取参数` `查询` `头` `Cookie` `设Cookie` `删Cookie` `表单` `表单文件` `表单值` `上传文件` `取JSON` `事件流` `发事件` `发事件名` `发事件编号` `发注释` `运行` `运行套接字` `运行开发` `停止` `已停止` `活动连接` `允许跨域` |
| `集合`(扩展) | 原有 `反转` `最大值` `最小值`;新增 `索引` `计数` `去重` `求和` `平均` `并集` `交集` `差集` `对称差集` `分组` `计数表` `分块` `拉链` `枚举` `展平` `任一满足` `全部满足` |
| `错误`(扩展) | 原有工厂/判定;新增 `错误码` `错误原因` `新建错误带原因` `包装错误` |

`网页` 是 HTML 构建/渲染库:节点是普通值(`文本`/`原样`/`片段`/`元素`),文本与属性值
默认转义,只有 `原样` 输出未转义 HTML。**核心输出方式是动态的**:`逐块输出(节点, 写块)`
按文档顺序把片段逐块交给写块,不构造完整字符串;`动态(节点)` 返回渲染器函数;
`响应(节点)`/`响应码(节点, 状态)` 直接得到可交给 `服务` 的动态响应(HTTP/1.1 分块传输)。
`渲染`/`渲染美化` 与 `写出`/`写出美化` 保留为一次性收集/静态落盘的兼容接口。

```zh
@引入 网页
@引入 服务
常量 页 = 网页.页面("你好", [
    网页.区块({"class": "卡片"}, [
        网页.标题(1, "你好,zhlang"),
        网页.段落("安全转义: <b> & \"引号\""),
        网页.链接("https://example.com/", "去看看"),
    ]),
])
网页.写出("index.html", 页)              // 直接输出 HTML 到文件
服务.GET(应用, "/", (请求) => 网页.响应(页))  // 动态输出

// 动态加载:先出占位节点,浏览器端加载脚本拉取片段并替换
常量 板 = 网页.片段([
    网页.标题(1, "慢慢来"),
    网页.动态加载("/片段/统计"),
    网页.加载脚本(),
])
```

`JS` 用 zhlang 构造 JavaScript 源码:节点是普通值;字符串借 JSON 规则转义,标识符
做形状与 JS 保留字校验;二元/一元表达式自动加括号避免优先级陷阱;`渲染` 产出 2 空格
缩进的多行代码,`渲染紧凑` 产出一行内联代码,`写出`/`写出紧凑` 直接落盘;`原样` 是
唯一不转义的入口(调用方保证可信)。本库只**生成 JS 文本**,不把 zhlang 编译成 JS,
可配合 `网页.脚本(JS.渲染(脚本))` 内联到页面。

```zh
@引入 JS

常量 脚本 = JS.程序([
    JS.常量声明("x", 1),
    JS.函数声明("add", ["a", "b"], [
        JS.返回语句(JS.二元("+", JS.标识("a"), JS.标识("b")))
    ]),
    JS.表达式语句(JS.调用(JS.成员(JS.标识("console"), "log"), [
        JS.调用(JS.标识("add"), [JS.标识("x"), 2])
    ]))
])
JS.写出("app.js", 脚本)
```

产出:

```js
const x = 1;
function add(a, b) {
  return (a + b);
}
console.log(add(x, 2));
```

`服务` 是 Flask/Gin 风格的 HTTP 服务框架:路由支持静态段、`:名字`(单段捕获)与
`*名字`(剩余路径);`使用` 组合中间件(`(请求, 下一个) => 响应`,不调用下一个即短路),
`分组` 带前缀注册并拥有独立中间件;请求侧提供查询/头/Cookie/表单/JSON/路由参数,
响应侧提供文本/HTML/JSON/状态码/加头/重定向/文件/空;自动处理 404、405(带 Allow)、
OPTIONS 与 HEAD;`静态` 映射目录(拒绝 `..` 穿越),支持二进制、`ETag`/`If-None-Match`
与单区间 Range(206/416)。每连接一个协程,支持 HTTP/1.1 保持连接、Content-Length、
分块请求体与 100-continue、请求头/体大小上限;配置 `读超时`(默认 30s)给每个连接设
收发超时,`最大连接`(默认 1024,0=不限)超限回 503 并立即关闭(背压),`停止(应用)`
置停止标记、关闭监听并在 `停机超时`(默认 10s)内排空在途连接。上传与推送:
multipart/form-data(`表单文件`/`表单值`/`上传文件`)、SSE(`事件流`/`发事件`/
`发事件名`/`发事件编号`/`发注释`);处理函数置 `请求["接管"]=真` 后框架不再写
HTTP 响应(WebSocket 等自写协议)。访问日志写 stdout:`服务:GET / [200] 0ms 2B
对端 UA=…`,状态码按类别着色(2xx 绿/3xx 青/4xx 黄/5xx 红),耗时 ≥300ms 黄、≥1s 红;
配置 `彩色` 取 真/假/"自动"(默认;仅交互终端),`NO_COLOR=1` 关闭、`ZHLANG_COLOR=1`
强制。装饰器 `@服务.装饰(应用, 方法, 路径)` 供原生后端使用,C 引导器改走三参调用。
**响应体可以是字符串,也可以是 `(写块) => …` 的动态渲染器**:`动态HTML`/`动态HTML码`
默认在发送前收集动态体并给 Content-Length(对大页面更稳);`流式HTML`/`流式HTML码`
显式选择 `Transfer-Encoding: chunked` 边渲染边发送(HTTP/1.0/HEAD 下仍自动收集)。
`响应体(响应)` 用于测试时收集动态体。`测试请求`/`测试请求体` 不启动服务器,
直接跑一次分派,便于单元测试。**头编码**:头值中的非 ASCII/控制字节按 UTF-8
逐字节 `%XX` 编码(响应与 `网页请求` 请求头同口径),线上头保持 ASCII,读取端
可用 `解码URL` 还原;请求头名必须是 ASCII 令牌(§6.2)。

```zh
@引入 服务

常量 应用 = 服务.新应用()
服务.GET(应用, "/", (请求) => 服务.文本("你好,zhlang"))
服务.GET(应用, "/api/用户/:id", (请求) =>
    服务.JSON({"id": 服务.取参数(请求, "id")}))
服务.运行(应用, 8080)
```

限制:无 TLS 服务端与 HTTP/2;WebSocket 需用连接接管(`请求["接管"]`)自写帧。
本模块使用原生后端的套接字超时/地址原语,须用自举原生编译器(`build/原生`)编译;
已冻结的 C 引导器不能编译本模块。动态响应体仍按字符串采集,静态文件可发二进制。

`样式` 用 zhlang 写 CSS:声明键可写中文(如 `颜色`/`字号`/`内边距`,映射到 CSS)或标准
CSS 名(`--自定义变量` 原样通过);数组值按空格连接(`内边距: [像素(12), 像素(16)]`),
`假` 值跳过(条件样式)。规则/媒体/关键帧/根变量组合成样式表;与网页库配合用
`网页.页面带样式(标题, 体, css)` 内联,或 `网页.样式链接("样式.css")` 外链。

```zh
@引入 样式
@引入 网页

常量 css = 样式.样式表([
    样式.规则(样式.类选择器("卡片"), {
        "背景色": "#fff",
        "圆角": 样式.像素(8),
        "内边距": [样式.像素(12), 样式.像素(16)],
    }),
    样式.媒体("(max-width: 600px)", [
        样式.规则(样式.类选择器("卡片"), {"内边距": 样式.像素(8)}),
    ]),
])
网页.写出("样式.css", css)
网页.写出("index.html", 网页.页面带样式("标题", [网页.段落("你好")], css))
```

原生内容可以自由混入(内容由调用方保证可信):HTML 用 `网页.原样(html)`,CSS 用
`网页.样式块(css)` / `网页.样式链接(url)` 或 `样式.原样(css)` 混进样式表,JS 用
`网页.脚本(js)` / `网页.脚本链接(url)` / `网页.脚本模块(url)`;`网页.页面带资源(标题,
体, css, js)` 一次带上原样 CSS 与 JS。

约定:

- 模块内不 `@引入` 其他模块,只依赖内置;同名包装(如 `文件.写文件`)内部用英文内置别名
  或不同的中文内置名调用,避免递归。
- 普通函数没有默认参数,因此可选参数一律拆成独立变体(如 `正则.匹配选项`,
  `进程.环境变量或`)。
- **官网自举原生版以原生后端为准**:原生 x86_64/aarch64 Linux 可用全部可引入模块
  (含 `同步`,纯 zhlang 通道实现),包括 `文件`/`JSON`/`正则`(含 `i`/`s`/`m` 选项)/
  `进程`/`终端`/`时间`/`随机`/`路径工具`/`集合`/`错误`/`网页`/`网络`
  (TCP/HTTP/HTTPS;Linux DNS 内置/HTTPS 纯 zhlang TLS,PE 用 gethostbyname + WinHTTP);
  未覆盖的内置会明确报错。原生 Windows PE 目标自 v2.23 增补起支持文件 IO
  (宽字符路径;目录存在性用 GetFileAttributesW 判定)与目录列举
  (FindFirstFileW/FindNextFileW/FindClose);`进程`(`执行`/`执行捕获`)也已覆盖
  (CreatePipe + CreateProcessW,命令行 MSVCRT 引用规则,输出按 ACP 解码)。

---

### 6.8 原生编译器:自举与 Go 式多端(v2.23 增补)

纯 zhlang 原生编译器(`自举/原生.zh`)自本版起可以**编出自身**:

- **自举不动点**:stage0(仓库 stage-0 引导器)→ stage1(stage0 编译编译器源码)
  → stage2(stage1 再编译自身);`cmp stage1 stage2` 逐字节相同(`make selfhost`)。
- **Go 式多目标**:一个原生编译器二进制内建 x86_64 ELF/PE 与 aarch64 后端。
  `--target` 可用三元组或 Go 风格 `系统/架构`(`linux/amd64`、`windows/amd64`、
  `linux/arm64`);选择优先级:`--target` > `ZHC_TARGET` >
  `ZHC_GOOS`+`ZHC_GOARCH` > 宿主。`--targets` 列出目标、产物后缀与能力表。
  目标表的唯一来源是 `自举/驱动/目标.zh`,驱动不再散落按目标名判断。
- **Windows PE 目标**:`读文件/写文件/追加文件/文件存在/文件大小/删除文件/建目录`
  已实现(CreateFileW/ReadFile/WriteFile/SetFilePointer/DeleteFileW/
  CreateDirectoryW/GetFileAttributesW);路径、命令行、环境变量全部走 UTF-16
  W 版 API,通过 MultiByteToWideChar/WideCharToMultiByte 与 UTF-8 互转,
  中文源码名/参数/文件名在 Windows 上可用;`参数()`/`程序路径()` 从
  GetCommandLineW 解析;PE 堆改为 64MB/256MB 的 bump 分配器(旧实现每次分配
  调用一次 VirtualAlloc,无法支撑自举)。
- **Windows 交叉自举**:Linux 上的 stage0 可直接产出 PE 编译器;该 PE 编译器
  在 wine/Windows 上能再编译自身,也能交叉输出 Linux ELF(同一编译器多目标)。
- **aarch64 交叉自举**:a64 后端已与 x86_64 后端同覆盖面(容器/字典/异常/
  闭包/结构体/类/浮点/模块/文件 IO/JSON/正则/进程/终端),`tests/原生` 全部语料
  在 qemu 下与 x86_64 产物逐字一致。`make selfhost-a64` 由 x86_64 宿主产出
  arm64 编译器,该编译器在 qemu 下继续产出 arm64 程序;
  `ZHC_SELFHOST_A64_FULL=1` 时再让它编译自身并做逐字节不动点比对
  (v2.23 已在 qemu 下验证 stage1 与 stage2 逐字节相同)。
- **原生网络与接口(v2.25)**:x86_64-linux 原生后端新增纯 syscall 网络栈
  (`连接到/套接字发送/套接字接收/关闭套接字/监听/接受连接` 与
  `取网页/网页请求`,HTTP/1.1、chunked、DNS;HTTPS 纯 zhlang TLS(Linux,aarch64 同样)/WinHTTP(PE))
  与 Go 式结构化 `接口`、参数类型标注(见 §3.2);套接字是文件描述符整数,
  PE 网络已接入(WinSock + WinHTTP),PE 目录列举与进程 IO(CreateProcess)亦已接入。网络/TLS 的完整版实现仍更全。
- **仍未打通**:PE 目标上的 REPL 行编辑/终端路径未在真机充分验证;
  原生网络未移植(网络在编译期报错)。

### 6.9 原生后端:无符号 64 位 / 64×64→128 intrinsic

原生后端(x86_64 与 aarch64)提供一组 intrinsic。值仍是整数(tag=1),64 位载荷按
二补码解释;**负数视为 2^64+v**,没有独立的“无符号”类型,`类型()` 仍返回“整数”。

| intrinsic | 语义 |
| --- | --- |
| `原乘128(a, b)` | 无符号 64×64→128,返回 `[高64, 低64]` 两元素数组 |
| `原乘高64(a, b)` | 无符号乘积的高 64 位(`umulh` / `mul` 的 rdx) |
| `原乘低64(a, b)` | 乘积的低 64 位 |
| `原无符号右移(a, n)` | 逻辑右移(`lsr` / `shr`;n 取低 6 位) |
| `原无符号比(a, b)` | 无符号比较:小于 -1、等于 0、大于 1 |
| `原无符号除(a, b)` | 无符号商;b=0 抛运行时错误“除以零” |
| `原无符号余(a, b)` | 无符号余数;b=0 抛运行时错误“除以零” |

英文别名:`umul128` / `umulhi` / `umullo` / `ushr64` / `ucmp64` / `udiv64` / `umod64`。
参数必须是整数(否则运行时类型错误)。普通 `+ - * & | ^ <<` 的 64 位环绕语义不变;
普通 `>>` 仍是算术右移,需要逻辑右移时用 `原无符号右移`。这些 intrinsic 为原生后端专用。

```zh
常量 组 = 原乘128(-1, -1)          // (2^64-1)^2
打印(组[0] == -2)                  // 高 64 位 0xFFFFFFFFFFFFFFFE
打印(组[1] == 1)                   // 低 64 位 1
打印(原无符号右移(-1, 1))          // 9223372036854775807
```

### 6.10 调试断点(原生后端,v2.27 增补)

- **指令**:`@断点调试[(等级, 内容)]`,可写在顶层或函数/块内;也接受
  `@调试断点`、`@breakpoint`。等价函数调用:`调试断点([等级, 内容])`、
  `断点(...)`、`breakpoint(...)`(可作为表达式使用)。省略等级时只写内容;
  `@断点调试()` 无内容。
- **等级**:标准等级如下(越小越重要),名称与整数等价,打印在 `[ ]` 里:

  | 名称 | 数值 | 用途 |
  | --- | --- | --- |
  | `错误` / `error` | 0 | 必须立刻看 |
  | `警告` / `warning` / `warn` | 1 | 可疑状态 |
  | `信息` / `info` | 2 | 一般流程 |
  | `调试` / `debug` | 3 | 细节/高频 |

  环境变量 `ZHLANG_DEBUG_LEVEL` 写数值或名称作为上限,等级大于上限的断点
  **不输出也不暂停**;其它非标准值不参与过滤,始终显示。
- **开关**:只有 `-d`/`--debug` 才生成断点代码;不加时整条指令剥掉,参数表达式
  不求值。`原生 源.zh -d` 是**调试运行**——编译到临时文件后直接执行(用后删除,
  不留下二进制;交互终端下 stdout/stderr 直连终端,断点提示实时可见),退出码即
  被调试程序退出码;`原生 源.zh -o 产物 -d` 则产出带断点的调试二进制。
  **非终端**(管道/重定向)下只捕获被调试程序的 stdout 并回放到标准输出,
  stderr(断点/诊断)继承父进程的 stderr,不再与程序输出合流。
  交叉目标不能直接运行,必须写 `-o`。
- **输出**:运行时向 stderr 打印一行 `断点 <文件>:<行> <函数> [<等级>] <内容>`;
  顶层语句的函数名为 `(顶层)`。文件是传给编译器的模块路径。交互终端下等级标签按
  等级着色(错误红/警告黄/信息青/调试灰);stderr 不是终端时输出纯文本。
- **暂停策略**:stdin 不是终端时打印后继续(便于 CI/重定向);
  `ZHLANG_DEBUG=0` 完全关闭;`ZHLANG_DEBUG=1` 强制进入命令循环(管道调试)。
- **命令**:交互提示符为 `调试 <文件>:<行>>`,暂停位置始终可见;
  `c`/`continue`/`继续` 继续运行;`q`/`quit`/`exit`/`退出` 终止程序;
  `b`/`bt`/`栈` 再次显示当前位置(调用栈回溯尚未支持);`?`/`h`/`help`/`帮助`。

```zh
函数 处理(n)
    @断点调试(1, "n=" + 转字符串(n))
    返回 n + 1

打印(处理(1))
```

```
$ ./原生 例.zh -d
断点 例.zh:2 处理 [1] n=1
调试 例.zh:2> c
2
```

### 6.11 网页动态输出与动态加载(v2.28 增补)

`网页` 的主输出路径不再返回整段 HTML 字符串,而是**逐块输出**:

| API | 说明 |
| --- | --- |
| `逐块输出(节点, 写块)` | 按文档顺序调用 `写块(块)`,返回块数;不拼接完整字符串 |
| `动态(节点)` | 返回 `(写块) => 逐块输出(节点, 写块)` 渲染器函数 |
| `响应(节点)` / `响应码(节点, 状态)` | 返回 `{状态, 头, 体:渲染器}`,可直接交给 `服务`;发送前收集并给 Content-Length |
| `流式响应(节点)` / `流式响应码(节点, 状态)` | 同上,但显式要求 HTTP/1.1 分块传输,边渲染边发送 |
| `响应体(响应)` | 逐块收集动态响应体并返回字符串(测试/调试) |
| `渲染(节点)` | 一次性收集的兼容接口,等价于把块连接起来 |
| `写出(路径, 节点)` | 动态输出到文件(每 64 KiB 追加一批),仍不构造完整字符串 |

动态加载有两层:

- **服务端按需**:`按需(加载函数)` 节点的加载函数在输出到该位置时才调用,返回要渲染的
  节点;节点数组/子节点里也可以直接放函数值 `() => 节点`。
- **浏览器端动态**:`动态加载(地址)`/`动态加载文本(地址, 占位)` 输出带 `data-src` 的
  占位 `div`;页面加入 `加载脚本()`(或单独引用 `加载脚本文本()`)后,脚本在
  `DOMContentLoaded` 时 fetch 地址并用返回的 HTML 片段替换内容。片段端点可直接返回
  `网页.响应(片段)`。加载成功加类 `已加载`,失败加 `加载失败` 并显示错误文本。

直接输出 HTML 仍然完整保留:`渲染`/`渲染美化` 出字符串,`写出`/`写出美化` 落盘;
静态站点生成与动态服务端渲染共用同一棵节点树。

### 6.12 数据库(标准库/数据库.zh)

`标准库/数据库.zh` 是可引入模块(不是内置),纯 zhlang 实现的嵌入式表/文档数据库:
记录是字典,表是记录数组,JSON 文件持久化,可选主键、字段索引与事务回滚。

```zh
@引入 数据库
常量 库 = 数据库.打开("应用.db")
常量 用户 = 库.建表("用户", "编号")
用户.插入({"编号": 1, "名字": "张三", "年龄": 30})
用户.建索引("名字")
打印(用户.按(1)["名字"])               // 主键查
打印(用户.找("名字", "张三"))           // 字段查(参数化)
打印(用户.查询(r => r["年龄"] > 26))    // 条件函数查
用户.更新(1, {"年龄": 31})
用户.删除(1)
库.保存()
```

| 对象 | API |
| --- | --- |
| 模块 | `打开(路径)`、`内存()`、`远程服务(库, 端口, 令牌)`、`远程连接(地址, 端口, 令牌)`、`网关连接(地址, 端口, 令牌)` |
| 库 | `建表(名, 主键)`(主键传空串表示无主键)、`表(名)`、`有表(名)`、`表名()`、`删表(名)`、`保存()`、`事务(f)`、`是否已改()` |
| 表 | `插入(记录)`、`按(键值)`、`找(字段, 值)`、`查询(条件)`、`第一(条件)`、`全部()`、`计数()`、`更新(键值, 变更)`、`更新条件(条件, 变更)`、`删除(键值)`、`删除条件(条件)`、`建索引(字段)`、`索引列表()` |
| 网关库 | `执行(语句, 参数)`、`查询(语句, 参数)`、`调用(命令, 参数)`、`关闭()` |

语义与约束:

- **记录**:只允许 JSON 数据(字典/数组/整数/浮点/字符串/布尔,字典键必须是字符串)。
  插入/更新时深拷贝并校验,外部改不动库内状态。
- **主键**:只接受 整数/字符串,插入与更新时保证唯一;`按` 用主键命中原行或返回 `假`。
- **读取返回副本**:`按`/`找`/`查询`/`第一`/`全部` 都返回深拷贝,修改必须走 `更新`/`删除`,
  避免绕过主键与索引约束。
- **索引**:`建索引(字段)` 只加速 字符串/布尔 的等值查询;数值与容器值走线性扫描,
  保证跨 整数/浮点 的 `==` 语义正确。
- **持久化**:`保存()` 先写 `路径.bak` 再写主文件;`打开` 时主文件损坏则回退 `.bak`。
  文件内容是 `{"格式":1,"表":{…}}` 的 JSON。JSON 不区分整数/浮点:整数值浮点(如 4.0)
  重新读回会变成整数 4。
- **事务**:`事务(f)` 在任何抛出时把全部表恢复到进入前状态并重抛;不支持嵌套;
  事务进行中调用 `保存()` 会抛错(不自动落盘)。
- **并发**:本地库对象不是协程安全的;跨协程共享请用 `同步.互斥锁` 保护。
- **远程**:`远程服务(库, 端口, 令牌)` 启动令牌认证服务,`远程连接(地址, 端口, 令牌)`
  返回远程库代理(方法:`建表` `删表` `有表` `表名` `保存` `关闭`;表代理:
  `插入` `按` `找` `全部` `计数` `更新` `删除`)。协议是换行分隔 JSON、值参数化,
  不接收 SQL 文本;条件函数与事务不能跨进程。内置 `监听` 绑 0.0.0.0,必须使用
  非空令牌;协议为明文 TCP(无服务端 TLS),公网须走反向代理/隧道。服务端串行化
  请求;服务器程序应使用 ≥2 个 worker(默认按 CPU 数)。
- **外部数据库网关**:纯 zhlang 且原生后端无 C 混写,不能直接加载 JDBC/ODBC/
  原生驱动。`网关连接` 把「SQL 语句 + 参数数组」发给网关进程,由网关用目标数据库
  的驱动绑定执行;参考网关 `tools/数据库网关.py` 用 Python 标准库 sqlite3,
  PostgreSQL/MySQL 按同一协议扩展(关闭多语句、引号外 `?` 安全转换)。
  **值永远走参数绑定,禁止把用户数据拼进 SQL**;zhlang 侧不解析 SQL。

```zh
// 外部数据库:网关连接(参考网关为 Python sqlite3;PG/MySQL 同协议扩展)
@引入 数据库
常量 网 = 数据库.网关连接("127.0.0.1", 18932, "令牌")
网.执行("CREATE TABLE 用户 (编号 INTEGER PRIMARY KEY, 名字 TEXT)", [])
网.执行("INSERT INTO 用户 (编号, 名字) VALUES (?, ?)", [1, "张三"])
常量 行 = 网.查询("SELECT 编号, 名字 FROM 用户 WHERE 编号 = ?", [1])
打印(行[0]["名字"])            // 张三
常量 载荷 = "Robert'); DROP TABLE 用户;--"
网.执行("INSERT INTO 用户 (编号, 名字) VALUES (?, ?)", [2, 载荷])  // 只是数据
网.关闭()
```

**注入安全**:本地/远程表引擎没有 SQL 解析器,传入的字符串永远是数据;
网关客户端只发「语句 + 参数数组」,值由网关侧绑定。因此不存在可被绕过的转义层。
回归用例把 `Robert'); DROP TABLE 用户;--`、`" OR "1"="1` 等载荷作为参数存取,
并验证表结构不变。SQL 文本必须由开发者编写占位符,用户数据只进参数数组。

### 6.13 原生后端内部原语(不保证兼容)

下列名称由原生后端运行时/编译器内部使用,**普通用户代码可调用但不承诺兼容性**:误用可读写任意进程内存、破坏运行时状态或直接崩溃进程。

`原分配` `原读8` `原读64` `原写8` `原写64` `原拷贝` `原系统` `原标签` `原载荷` `原错误` `原线程取` `原原子交换64` `原原子写64` `原原子加64`(以及 `原生_` 前缀的运行时内部函数)。

标准库与规格示例不应使用这些名称;后续版本可能改名、限制参数或禁止用户代码调用。需要文件/网络/进程/内存等能力时使用 §6 的公开内置。

### 6.14 标准库扩展 API(v2.39)

本节是 §6.7 模块表与隐式标准库新增函数的完整签名/返回值/错误口径。隐式模块
(字符串/编码/容器/数学/摘要)的函数全局可用;可引入模块用 `模块.函数(…)`。

#### 数学(隐式,`标准库/数学.zh`)

| 签名 | 返回值 / 错误 |
|---|---|
| `夹取(v, 下, 上)` | 把数值 `v` 夹到 `[下, 上]`;非数值、`下 > 上` → 错误 |
| `符号(v)` | `-1` / `0` / `1`;NaN 按 0;非数值 → 错误 |
| `最大公约数(a, b)` | 非负 GCD(欧几里得,负数取绝对值);非整数 → 错误 |
| `最小公倍数(a, b)` | LCM;任一为 0 → 0;结果按 64 位环绕 |
| `阶乘(n)` | `n!`;`n < 0` → 错误;溢出 → 错误(逐步 `乘检`) |
| `幂模(底, 指数, 模)` | `底^指数 mod 模`;模 ∈ 1..3037000499,指数 ≥ 0,否则错误 |
| `是否质数(n)` | 布尔;`n < 2` 为假;非整数 → 错误 |
| `质数列表(n)` | ≤ `n` 的质数数组(`n < 2` 空);`n > 10000000` → 错误 |
| `转进制(n, 基)` | 2..36 进制文本(小写,负数带 `-`);2 的幂用移位精确到 64 位,其余基限制 `|n| ≤ 2^53-1` |
| `解析进制(s, 基)` | 文本 → 整数;允许首尾空白与 `+/-`;非法数字/越界溢出 → 错误 |
| `均值(数组)` | 算术平均(浮点);非空数值数组,否则错误 |
| `中位数(数组)` | 排序后中间值;偶数取两中值平均 |
| `方差(数组)` / `标准差(数组)` | 总体方差/标准差(除以 n);空/非数值 → 错误 |
| `舍入到(v, 位)` | 四舍五入到小数位(负数位 = 十位/百位);半数远离零 |

#### 字符串(隐式,`标准库/字符串.zh`)

| 签名 | 返回值 / 错误 |
|---|---|
| `反转文本(s)` | 按码点反转的新串 |
| `左填充(s, 宽, 填)` / `右填充(s, 宽, 填)` | 补到 `宽` 个码点;`宽 ≤ 长度(s)` 原样;`填` 通常为单字符 |
| `去前缀(s, 前缀)` / `去后缀(s, 后缀)` | 匹配则去掉,否则原样返回 |
| `分行(s)` | 按 `\n` 切分并去掉行尾 `\r`;末尾换行不产生空行;空串 → `[]` |
| `字节长度(s)` | UTF-8 字节数(与按码点的 `长度` 区分) |
| `出现次数(s, 子串)` | 不重叠出现次数;空子串 → 0 |
| `是空白(s)` | 去 ASCII 空白与全角空格后是否为空 |

#### 集合(可引入,`标准库/集合.zh`)

| 签名 | 返回值 / 错误 |
|---|---|
| `对称差集(a, b)` | 只出现在一侧的元素(保序去重) |
| `分组(数组, f)` | `{键: 元素数组}`;键 = `类型(f(x)) + ":" + 转字符串(f(x))`,保持首次出现顺序 |
| `计数表(数组)` | `{键: 次数}`,键规则同 `分组` |
| `分块(数组, 大小)` | 每 `大小` 个元素一组(末组可短);大小须正整数 |
| `拉链(a, b)` | `[[a0,b0], …]`,长度取较短数组 |
| `枚举(数组)` | `[[下标, 元素], …]`,下标从 0 起 |

#### 时间(可引入,`标准库/时间.zh`)

| 签名 | 返回值 / 错误 |
|---|---|
| `分解(时间戳)` | `{年,月,日,时,分,秒,星期,年内第几天,时区}`;星期 0=周日;按本地时区 |
| `是否闰年(年)` / `月天数(年, 月)` | 公历闰年判断 / 该月天数(月 1..12) |
| `增加秒(时间戳, 秒)` / `增加天(时间戳, 天)` | 绝对时间加减(按 86400 秒/天,不做 DST 钟点对齐) |
| `当天零点(时间戳)` | 本地当天 00:00:00 的时间戳(近似,无 DST 跳变) |
| `星期名(时间戳)` | `"星期" + 日/一/…/六` |
| `格式化ISO(时间戳)` | `YYYY-MM-DDTHH:MM:SS±HHMM`;无时区数据时省略偏移 |
| `解析日期(文本)` | → **UTC 整数秒**。接受 `YYYY-MM-DD`、`YYYY-MM-DD HH:MM[:SS]`、`YYYY-MM-DDTHH:MM[:SS]`;尾部 `Z` 或 `±HH:MM`/`±HHMM` 换算到 UTC;非法日期/时间 → 错误 |

#### 随机(可引入,`标准库/随机.zh`)

| 签名 | 返回值 / 错误 |
|---|---|
| `随机浮点()` | `[0.0, 1.0)`,52 位尾数(两次 26 位抽取) |
| `随机字节(n)` | 长度 `n` 的 0..255 数组;`n < 0` → 错误 |
| `随机字符串(n)` | 长度 `n` 的 A-Za-z0-9 字符串 |
| `随机密码(n)` | 字母/数字/符号混合;`n ≥ 3` 时三类各至少一 |
| `随机正态()` | 标准正态 N(0,1)(Box–Muller) |
| `按权重选择(权重)` | 按非负权重返回下标;权重和须为正 |

随机种子由运行时自动初始化,**混入秒级时间、单调毫秒与栈地址低 10 位**,同一秒
启动的多个进程不再产生相同序列(仍为确定性伪随机,不用于密码学)。

#### 文件 / 路径工具 / 编码 / 摘要 / JSON / 进程

| 模块/签名 | 返回值 / 错误 |
|---|---|
| `文件.读字节(路径)` | 整个文件 → 0..255 数组(二进制安全,含 NUL) |
| `文件.复制文件(源, 目标)` | 二进制复制,返回写入字节数 |
| `文件.写行(路径, 行们)` / `文件.追加行(路径, 行们)` | 每行补 `\n`;返回写入字节数 |
| `文件.临时目录()` | `TMPDIR` → `TEMP` → `TMP` → Windows 当前目录 / POSIX `/tmp`(去尾分隔) |
| `文件.临时文件(前缀)` / `文件.临时文件夹(前缀)` | 创建并返回路径(秒+随机数命名) |
| `文件.递归列目录(路径)` | 相对路径数组;目录项以 `/` 结尾;深度上限 32(防符号链接环) |
| `路径工具.去尾分隔(路径)` | 去掉多余尾部 `/`、`\`(根路径保留) |
| `路径工具.规范化(路径)` | 折叠重复分隔符/`.`/`..`;保留绝对根与盘符;分隔符风格跟随输入 |
| `路径工具.拆解(路径)` | `{目录, 文件, 扩展}` |
| `路径工具.家目录()` | `HOME` → `USERPROFILE`;都没有 → `""` |
| `编码十六进制(s)` / `解码十六进制(s)` | UTF-8 字节 ↔ 小写十六进制;解码须偶数长度,非法字符 → 错误 |
| `哈希HMAC(键, 消息)` | HMAC-SHA256 小写十六进制;键 > 64 字节先取 SHA256(RFC 2104) |
| `哈希文件(路径)` / `哈希文件MD5(路径)` | 文件内容摘要(二进制安全) |
| `JSON.尝试解码(文本, 缺省)` | 失败返回缺省,不抛错 |
| `JSON.读取或(路径, 缺省)` | 文件不存在/语法错 → 缺省 |
| `JSON.写入美化(路径, 值)` | 两空格缩进写入,返回字节数 |
| `进程.检查(结果)` | 退出码非 0 抛 `命令失败(退出码 N):输出`,否则原样返回结果 |
| `进程.运行输出(程序, 参数)` / `进程.命令输出(命令)` | 返回 stdout,失败抛错 |
| `进程.运行成功(程序, 参数)` | 布尔,不抛错 |

**HTTP 头编码**:服务端响应头与 `网页请求` 请求头中的非 ASCII/控制字节按 UTF-8
逐字节 `%XX` 编码,ASCII(含已有 `%`)原样;线上头保持 ASCII。请求头名必须是
ASCII 令牌。读取端用 `解码URL` 还原。

```zh
@引入 时间
@引入 摘要
@引入 集合

常量 d = 时间.分解(1700000000)
打印(d["年"], d["月"], d["日"], d["星期"])       // 2023 11 15 3
打印(时间.解析日期("2023-11-15T06:13:20+08:00")) // 1700000000
打印(摘要.哈希HMAC("key", "x"))                  // 4fc3b7ea…
打印(集合.分块([1,2,3,4,5], 2))                  // [[1, 2], [3, 4], [5]]
```

### 6.15 包管理与测试(v2.40)

**`原生 get <包名>[@版本] [--仓库 网址] [--目录 项目根]`**(自举发行版编译器;stage-0 `zhc` 不带):
从静态仓库下载并安装包;默认仓库 `ZHLANG_仓库` 环境变量或
`https://pingsec.top/repo`。仓库协议:

```
<仓库>/索引.json
<仓库>/包/<名>/<版本>/<文件>.tar.b64
```

`索引.json`(UTF-8):`{"格式":1,"包":{"名":{"最新":"1.0.0","说明":"…",
"版本":{"1.0.0":{"文件":"包/名/1.0.0/名-1.0.0.tar.b64","sha256":"…","编码":"base64",
"依赖":{"其他包":"1.0.0"},"需要编译器":"2.40"}}}}}`。`sha256` 是 **base64 文本**的
摘要(stage-0 C 运行时的哈希/字节数组按 C 串处理,二进制串会在 NUL 截断;base64
传输让 C 引导编译器与原生编译器行为一致)。`依赖` 递归安装;`""`/`最新`/`^`/`~`
按仓库最新版本处理(v2.40 不做 semver 区间求值)。安装布局:
`<项目根>/包/<名>/…`(ustar 内相对路径原样展开;拒绝绝对路径、`..`、反斜杠、
盘符与符号链接以外的条目),并合并写 `zh包.json`(依赖)与 `zh包锁.json`
(精确版本 + sha256)。

**测试框架**:`标准库/测试.zh`(可引入)提供 `测试套件(名)`、`测试(套, 名, 体)`、
`运行(套, 用例表)`、`断言/断言真/断言假/断言相等/断言不等/断言近似`、
`报告(套)`(返回失败数)。`体` 可为无参函数或函数数组;断言失败抛可捕获错误,
由 `测试` 收集并继续。注意:块体箭头函数不能直接写在调用括号里(括号内换行被
忽略),多断言用 `[() => 断言…, () => 断言…]` 或先赋给变量。

**环绕运算**:`数学.环绕加/环绕减/环绕乘` 提供显式模 2^64 运算,不触发「整数溢出」。

```zh
@引入 "测试.zh" 只 测试套件, 测试, 断言相等, 报告
变量 t = 测试套件("示例")
测试(t, "相等", [() => 断言相等(1 + 1, 2, "1+1")])
如果 报告(t) != 0
    抛出("测试失败")
```

## 7. 错误信息约定

**编译错**(退出码 1):`文件:行:列: 错误:消息` + 源码行 + `^` 指示(列按码点,指示按显示宽度对齐)。示例:

```
例子.zh:3:9: 错误:未定义的变量「名字」
    打印("你好," + 名字)
            ^
```

原生后端对语义/模块错误也按同一格式渲染(列优先取消息中第一个「…」名字在该行的位置,否则取首个非空白;跨模块错误指向定义该函数的模块文件,并回显该文件对应的源码行)。

**运行时错误**(生成的程序,stderr,退出码 1):`运行时错误(第 N 行):消息`。示例:

```
运行时错误(第 12 行):「+」不支持 布尔 与 字符串
```

未被 `捕捉` 接住的抛出与此格式完全一致(见 §3.4);普通抛出/运行时错误的捕捉消息是纯文本(无该前缀);`抛出对象` 的捕捉值是原对象,未捕获时优先打印其「消息」字段。

**调用栈**(v2.23 增补):未捕获错误在错误行之后打印**由内向外**的 zhlang 调用栈,每帧是
`函数名(定义于第 N 行)`(生成函数在入口压帧、返回前弹帧;行号是函数声明行)。每协程独立,
最多保留最内层 64 帧(更深时注明省略层数);被 `捕捉` 的错误不打印,捕捉后栈深度回到 `尝试`
入口,后续错误不会带出已展开的旧帧。示例:

```
运行时错误(第 3 行):下标越界:5(长度 1)
调用栈(由内向外):
  内(定义于第 1 行)
  外(定义于第 4 行)
```

**行号与实现位置(v2.11)**:实现于标准库(§6 zhlang 实现)的内置,错误**消息正文保持不变**,行号指向标准库实现行(`标准库/*.zh` 内的行),不是调用处。其余情况行号一律指向调用处。

**gcc 错误**:透传 gcc 退出码与 stderr,并保留生成的 .c 文件供排查。

---

## 8. 实现形态

- `编译 源.zh [-o 输出] [--target 目标] [-d|--debug]`:纯 zhlang 自举编译器,AST 直接生成机器码并写出 ELF/PE,**不依赖任何外部工具链**;省略 `-o` 时产物放在源文件旁。
- **三端目标内置**:`x86_64-linux-gnu` / `aarch64-linux-gnu` / `x86_64-windows-gnu`;`--target` 接受三元组、短名或 Go 风格 `系统/架构`,环境变量 `ZHC_TARGET`(或 `ZHC_GOOS`+`ZHC_GOARCH`)提供默认值,`--targets` 列出目标与能力表。
- 生成代码特征:值模型为「类型标签 + 载荷」;字符串/数组/字典/闭包由内嵌的 zhlang 运行时提供;循环回边与函数调用插入协作调度安全点;产物为静态单文件。
- 编译器源码全部是 zhlang,由自己编译自己(stage1 == stage2 逐字节相同)。

---

### 8.1 交互式会话(`编译 --repl`)

语言本身无解释器(编译单元 = 整个程序),会话因此实现为**整段重跑**:每段可编译输入都触发一次
「累计源码 → 临时程序 → 运行」,只显示比上一轮新增的 stdout(前缀裁剪);编译错不计入会话,
运行失败(运行时错误/崩溃/非零退出)同样不计入,以保证后续轮次的输出基线能推进。
**副作用(写文件、网络请求、打印)会随每轮重放**——这是该设计的固有代价,已在会话内 `:帮助` 说明。
会话命令 `:帮助`、`:会话`、`:清`(`:重置`/`:reset`/`:clear` 同义)、`:退出` 在续行中同样生效;
续行中 `:清` 会放弃未完成输入。全角冒号 `：` 与英文别名同义。会话临时目录按微秒时间戳 + 随机数命名,
同一秒启动的多个会话互不共用 `会话.zh`/`会话.bin`。

## 9. 完整示例

### 9.1 你好世界

```zh
打印("你好,zhlang!")
```

### 9.2 阶乘(递归)

```zh
函数 阶乘(n)
    如果 n <= 1
        返回 1
    返回 n * 阶乘(n - 1)

常量 汇报 = `5 的阶乘是 ${阶乘(5)}`
打印(汇报)
```

### 9.3 九九乘法表

```zh
循环 i 从 1 到 9
    循环 j 从 1 到 i
        打印(i, "*", j, "=", i * j)
```

### 9.4 模板字符串与字符串库

```zh
常量 名字 = "zhlang"
常量 版本 = 1.0
打印(`语言:${名字},版本:${版本},明年:${版本 + 1}`)
打印(长度("你好世界"))          // 4
打印(子串("你好世界", 1, 2))     // 好世
打印(查找("banana", "na"))       // 2
打印(替换("a-b-c", "-", "+"))    // a+b+c
```

### 9.5 猜数字(交互)

```zh
常量 答案 = 42
当 真
    打印("猜一个数:")
    常量 猜 = 转整数(读入())
    如果 猜 == 答案
        打印("猜对了!")
        跳出
    如果 猜 < 答案
        打印("小了")
    否则
        打印("大了")
```

### 9.6 生产者-消费者(并发)

```zh
函数 生产者(ch)
    循环 i 从 1 到 5
        发送(ch, i)
    关闭(ch)

函数 消费者(ch)
    当 真
        常量 v = 接收(ch)
        如果 v == 假
            跳出
        打印("收到", v)

常量 ch = 通道()
启动 生产者(ch)
消费者(ch)
打印("完成")
```

(多生产者共用一个通道时不要各自 `关闭`——向已关闭通道发送是运行时错误;确需结束信号时由唯一的关闭方发哨兵值。)

### 9.7 一万协程压测(并发 + GC)

```zh
函数 工人(id, ch)
    发送(ch, id * 2)

常量 ch = 通道(10000)
常量 开始 = 当前时间()
循环 i 从 1 到 10000
    启动 工人(i, ch)
变量 收 = 0
循环 i 从 1 到 10000
    常量 v = 接收(ch)
    收 = 收 + v
打印("合计", 收, "耗时", 转字符串(当前时间() - 开始) + " 秒")
打印("堆用量", 堆用量())
```

### 9.8 数组与字典(v1.2)

```zh
// 数组:排序与遍历
常量 分数 = [88, 92, 79, 95]
分数[2] = 80                    // 索引赋值
追加(分数, 90)
打印(分数, 分数[-1])            // [88, 92, 80, 95, 90] 90

// 字典:统计字频(保插入序)
常量 文本 = "床前明月光疑是地上霜举头望明月低头思故乡"
常量 频次 = {}
循环 字 在 文本                 // 字符串遍历得码点
    如果 包含(频次, 字)
        频次[字] = 频次[字] + 1
    否则
        频次[字] = 1
打印(频次)                     // {"床": 1, "前": 1, …, "明": 2, …}

// 遍历字典得键;键() 亦然
循环 k 在 频次
    如果 频次[k] > 1
        打印(`「${k}」出现 ${频次[k]} 次`)
```

### 9.9 一等函数与箭头函数(v1.6)

```
函数 双倍(x)
    返回 x * 2

常量 nums = [1, 2, 3, 4, 5]

打印(映射(nums, n => n * n))            // [1, 4, 9, 16, 25]
打印(过滤(nums, n => n % 2 == 1))        // [1, 3, 5]
打印(归约(nums, 0, (a, x) => a + x))     // 15

常量 f = 双倍                              // 函数名作值
打印(f(21))                              // 42

常量 加 = a => b => a + b                  // 柯里化:箭头函数返回箭头函数
打印(加(3)(4))                           // 7

常量 基数 = 100
常量 含基数 = x => 基数 + x                // 捕获(此后不赋值):按值绑定
打印(含基数(1))                          // 101

变量 计数 = 0
常量 加一 = () =>                          // 捕获且被改写 → 共享单元
    计数 = 计数 + 1
    返回 计数
常量 读计数 = () => 计数                   // 同一变量,同一单元
打印(加一(), 加一(), 读计数())           // 1 2 2
计数 = 100
打印(读计数())                           // 100(外层改写闭包可见)
```

### 9.10 正则(v2.0)

```zh
// 原始字符串:模式里的反斜杠原样书写
常量 行 = "2026-10-15 09:13:02 ERROR 登录失败 user=alice ip=10.0.0.7"

打印(正则分组(行, r"^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}) (\w+)"))
// ["2026-10-15 09:13:02 ERROR", "2026", "10", "15", "09", "13", "02", "ERROR"]

打印(正则查找(行, r"user=(\w+)"), 正则分组(行, r"user=(\w+)")[1])   // user=alice alice
打印(正则全部(行, r"\d+"))                                        // ["2026", "10", "15", "09", "13", "02", "10", "0", "0", "7"]
打印(正则替换(行, r"(\d+)\.(\d+)\.(\d+)\.(\d+)", "$1.$2.*.*"))    // …ip=10.0.*.*
打印(正则分割("a, b;c", r"[,;]\s*"))                              // ["a", "b", "c"]

打印(正则匹配("HELLO", "hello", "i"))                             // 真(选项 i 忽略大小写)
打印(正则匹配("中文测试", r"[一-龥]+"))                            // 真
打印(正则匹配("abc", "(未闭合"))                                   // 运行时错误:正则模式错误:分组未闭合(缺少 ))
```

### 9.11 HTTPS(v2.1)

```zh
// 内置 TLS 客户端:证书链 + 主机名校验;CA 用系统包(或 ZHLANG_CA_FILE 指定)
常量 r = 网页请求("https://example.com/", "GET")
打印(r["状态"])                          // 200
打印(长度(r["体"]) > 0)                  // 真
打印(正则匹配(r["头"]["content-type"], "text/html"))   // 真

// 校验失败一定报错,不会静默放行:
// 取网页("https://127.0.0.1:8443/")     // CA 不匹配 → 运行时错误:TLS 握手失败(…):X509 - Certificate verification failed…
```

> **原生后端(M4)**:x86_64/aarch64 Linux 上同一 API 由 `标准库/TLS.zh` 的纯 zhlang
> TLS 1.3 实现(TLS_CHACHA20_POLY1305_SHA256 + x25519,证书链/主机名/SAN/有效期校验),
> 信任库见 §6.2 的原生后端说明;编译期按程序是否用到 `取网页`/`网页请求` 决定是否
> 发射 TLS 代码与内嵌 CA(不用 HTTP 的程序体积不变,PE 目标也不发射)。
> `ZHLANG_HTTPS=curl` 显式回退系统 curl;证书校验失败不会自动回退。
