教程

从第一个程序到自举

安装、变量、控制流、并发、网络、调试、交叉编译与原生后端。所有示例都由真编译器验证。

说明 · 示例可直接用 编译 运行;完整语义见 语言规格。官网下载为纯 zhlang 自举原生版。

1. 安装与第一个程序

zhlang 的编译器是单个可执行文件 —— 纯 zhlang 自举的原生编译器:直接生成机器码,不需要任何外部工具链、包管理器或运行时依赖。到下载页取对应平台的编译器,解压后给它执行权限:

chmod +x 编译
./编译 --version

写第一个程序,存成 你好.zh(源文件是 UTF-8 文本):

打印("你好,zhlang!")

编译并运行:

./编译 你好.zh -o 你好
./你好
你好,zhlang!

入口:顶层语句与 main

没有 main 时,入口模块的顶层语句就是程序。也可以只写一个无参 main(或中文名 主函数):入口模块没有顶层可执行语句时,初始化完成后自动调用它, 整数返回值作为进程退出码(非整数或无返回按 0):

函数 main()
    打印("从 main 启动")
    返回 7
从 main 启动

运行后 echo $? 得到 7。若入口模块还有顶层可执行语句,则照常执行顶层语句, main 不会被自动调用(仍可手动调用)。main 与 主函数 不能同时定义。

约定:本教程的代码块都用真编译器逐段验证过,主要示例对应站点 示例/ 目录里的真文件 —— 贴出的「输出」就是实际跑出来的结果。Windows 用户把 ./编译 换成 编译.exe 即可,中文文件名全链路可用。

2. 变量、常量与类型标注

变量 声明可变的绑定,常量 声明不可变的绑定 —— 改常量、对常量取地址都是编译错,但容器内容仍可变。类型是动态的,也可以可选地写类型标注:内置类型位(整数/浮点/字符串/布尔/通道/数组/字典/套接字/函数/指针)或结构体名,声明与每次赋值都做运行时校验。(让 是旧关键字,已改名为 变量/常量 —— 撞见会给出指引。)字符串拼接用 +,模板串用反引号 `…${表达式}…`:

变量 年 = 2026
常量 温度 = 36.6
常量 名字 = "小明"
常量 是学生 = 真
常量 名单 = ["甲", "乙"]
常量 分数 = {"语文": 92, "数学": 88}

打印(`我是${名字},${年} 年,${温度} 度`)
打印("名单:", 名单, "数学:", 分数["数学"])
打印("拼接:", "你好," + 名字)
如果 是学生
    打印("是学生")
年 = 年 + 1
打印("明年:", 年)
我是小明,2026 年,36.6 度
名单: ["甲", "乙"] 数学: 88
拼接: 你好,小明
是学生
明年: 2027

常量只冻结绑定,装进容器的内容照改不误;类型标注是约束而非注释 —— 写错的类型会在运行时被拦下(可被 尝试/捕捉 接住,报错带行号):

/* 常量:绑定不可变、容器内容仍可变;类型标注:声明与每次赋值运行时校验 */
常量 圆周率 = 3.1416
常量 名单 = ["甲", "乙"]
名单[0] = "丙"                  /* 合法:改的是内容,不是绑定 */
变量 命中数 整数 = 0
命中数 += 2
结构体 点
    x
    y
常量 原点 点 = 点(0, 0)

打印(圆周率, 名单, 命中数, 原点.x + 原点.y)

尝试
    命中数 = "不是整数"          /* 类型标注不符 → 运行时错误 */
捕捉 错误
    打印("拦下:", 错误)
3.1416 ["丙", "乙"] 2 0
拦下: 类型标注不符:声明为 整数,得到的是 字符串

真值规则与 Python 相同:空串、空数组、空字典、0、假 为假,其余为真(带原型链的字典恒为真)。打印 会按「转字符串」规则输出所有类型:

打印("整数:", 42, "浮点:", 3.5, "布尔:", 真)
打印("数组:", [1, [2, 3]], "字典:", {"a": 1})

整数是 64 位、浮点是双精度;还支持位运算 & | ^ ~ << >>、0x/0b/0o 进制字面量与数字分隔符 _:

打印(0xFF, 0b1010, 1_000_000, (5 & 3) | (1 << 4))

3. 控制流

缩进就是块,像 Python。条件与循环的关键字是中英双份的:如果/如果否则/否则、当(while)、循环 i 从 A 到 B(区间,含两端)、循环 x 在 数组/字典(遍历),跳出(break)与 继续(continue):

循环 i 从 1 到 15
    如果 i % 15 == 0
        打印("FizzBuzz")
    否则如果 i % 3 == 0
        打印("Fizz")
    否则如果 i % 5 == 0
        打印("Buzz")
    否则
        打印(i)

变量 n = 10
当 n > 0
    n = n - 3
打印("n 停在", n)

变量 和 = 0
循环 x 在 [3, 4, 5]
    如果 x == 4
        继续
    和 = 和 + x
打印("跳过 4 的和:", 和)
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
n 停在 -2
跳过 4 的和: 8
注意坑:循环 i 从 A 到 B 在 A > B 时会降序迭代 (空区间先想清楚),要跳过整轮就用 当;行内可以用分号连写短句(变量 a = 1; 变量 b = 2), 语句以换行分隔,不需要分号。

4. 函数、闭包与函数式

函数用 函数 定义。函数是一等值:可以赋给变量、传参、返回;箭头函数 x => x * 2 写起来更短。捕获是创建时按值快照,而当闭包改写了外层变量时,该变量自动装箱为共享单元(按引用捕获):

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

打印("5! =", 阶乘(5))

常量 双倍 = x => x * 2
常量 加 = a => b => a + b
打印("双倍 21 =", 双倍(21), ";  加(3)(4) =", 加(3)(4))

常量 nums = [1, 2, 3, 4, 5, 6]
打印("平方:", 映射(nums, n => n * n))
打印("偶数:", 过滤(nums, n => n % 2 == 0))
打印("总和:", 归约(nums, 0, (a, x) => a + x))
打印("排序:", 排序(["banana", "apple", "cherry"],
                   (a, b) => 长度(a) - 长度(b)))

变量 计数 = 0
常量 累加 = 值 =>
    计数 = 计数 + 值
累加(3)
累加(4)
打印("闭包改写外层:", 计数)
5! = 120
双倍 21 = 42 ;  加(3)(4) = 7
平方: [1, 4, 9, 16, 25, 36]
偶数: [2, 4, 6]
总和: 21
排序: ["apple", "banana", "cherry"]
闭包改写外层: 7

映射 / 过滤 / 归约 / 排序 是内置的高阶函数;排序默认升序且稳定,传比较函数时它须返回整数(负/零/正)。箭头函数体也可以是一个缩进块(=> 后换行)。

5. 容器:数组与字典

数组与字典都是引用类型(赋值不改内容、别名互见)。字典保持插入序,取 带缺省值,遍历字典得到键:

变量 a = [1, 2, 3]
追加(a, 4, 5)
打印("a:", a, "长度:", 长度(a))
打印("弹出:", 弹出(a), "现在:", a)

常量 d = {"名": "小明", "岁": 12}
d["城市"] = "杭州"
打印("取:", 取(d, "城市"), "缺键默认:", 取(d, "电话", "无"))
打印("包含:", 包含(d, "岁"), "; 删除:", 删除(d, "岁"))
循环 键名 在 键(d)
    打印(键名, "=", d[键名])
a: [1, 2, 3, 4, 5] 长度: 5
弹出: 5 现在: [1, 2, 3, 4]
取: 杭州 缺键默认: 无
包含: 真 ; 删除: 真
名 = 小明
城市 = 杭州

切片 c[起:止]:半开区间(起含、止不含),两端都可省略,负数从尾部数,越界钳位不报错;字符串按码点、数组按元素。切片是只读表达式(s[1:2] = "x" 是编译错):

常量 s = "床前明月光"
打印(s[2:4], s[:3], s[-2:], s[3:99])
打印([1, 2, 3, 4][1:3])
打印(类型(s), 类型(1), 类型(1.5), 类型([]))
明月 床前明 月光 月光
[2, 3]
字符串 整数 浮点 数组

常用内置还有:长度、子串、查找、替换、分割、连接、去空白、转大写/转小写、转整数/转浮点/转字符串、排序、键/值、编码JSON/解码JSON、类型(值的类型名)。

6. 结构体、类与对象

结构体 声明一组字段;两种写法等价:结构体 点 后缩进列字段,或 结构体 点 { x, y }。构造可以按位置传值,也可以命名构造(键是字段名,顺序任意, 缺省字段补 假):

结构体 点 { x, y }
常量 p = 点{x: 3, y: 4}       // 命名构造
常量 q = 点(1, 2)             // 位置构造
打印(p.x, p.y, q.x + q.y)
3 4 3

字段用点访问(点访问会校验字段名,写错报编译错/运行时错)。方法就是挂在结构体模板上的函数, 第一个参数约定叫 本:

结构体 点
    x
    y

变量 p = 点(3, 4)
打印("p =", p.x, p.y)
p.x = 10
打印("改后 p.x + p.y =", p.x + p.y)
点.长度 = 本 => 平方根(本.x * 本.x + 本.y * 本.y)
打印("到原点距离 =", p.长度(p))
打印("是 点 的实例:", 是(p, 点))
p = 3 4
改后 p.x + p.y = 14
到原点距离 = 10.770329614269007
是 点 的实例: 真

对象模型是原型链:继承(原型, 添加字典) 造新对象,读走链、写落自身、枚举只看自己;是(对象, 原型) 做身份判断。任何字典都能当对象用 —— 继承() 只是给它挂上一条隐藏只读的原型指针。

类、初始化与继承

类 把字段、方法和构造收在一起。类体内可以写字段 名字 [类型] [= 默认]、 实例方法 函数 方法(参数)(隐式 本),以及初始化方法 函数 初始化(参数 [= 默认], …)(至多一个):构造时先按字段声明填默认值,再执行初始化, 整个表达式的结果恒为对象本身:

类 人类:
    名字 字符串
    年龄 整数 = 18
    函数 初始化(名字, 年龄 = 18)
        本.名字 = 名字
        本.年龄 = 年龄
    函数 问候(前缀)
        打印(前缀 + 本.名字)

类 学生(人类):
    学校 字符串 = "?"
    函数 初始化(名字, 学校, 年龄 = 20)
        本.学校 = 学校
    函数 问候(前缀)                 // 覆盖父类方法(多态)
        打印("学生", 前缀 + 本.名字)

常量 a = 人类("张三", 30)           // 位置构造 → 调 初始化
常量 b = 人类{名字: "李四"}         // 命名构造:键对应初始化参数
常量 c = 学生("小明", "一中")       // 子类初始化前自动先调父类初始化
a.问候("你好,")
b.问候("你好,")
c.问候("你好,")
打印(a.年龄, b.年龄, c.学校, 是(c, 人类))
你好,张三
你好,李四
学生 你好,小明
30 18 一中 真

要点:

静态方法与跨模块链式调用

写在类体里的 函数 是实例方法(自动注入 本)。实例方法也可以写在顶层, 用 @类方法(类名) 标注;静态方法用 @类函数(类名) 标注(不注入 本, 以 类名.函数(…) 调用,参数个数须严格相等):

类 计数器
    属性 就绪 布尔 = 真

@类方法(计数器)          // 实例方法(注入 本)
函数 加一(n)
    返回 n + 1

@类函数(计数器)          // 静态方法(无 本)
函数 版本()
    返回 "2.23"

常量 c = 计数器()
打印(c.加一(41), 计数器.版本())
42 2.23

其他模块里用别名引入后,可以一路点到类的静态方法 —— 模块.类.静态方法(…) (静态调用在编译期绑定):

// 计数器.zh 里有上面的 类 计数器
// 引入必须写在所有语句之前;同一模块可多次引入不同名字
@引入 "计数器.zh" 为 计数器库
@引入 "计数器.zh" 只 计数器
打印(计数器库.计数器.版本())
打印(计数器.版本())

@类函数 必须写在类体外(顶层),类体内只允许字段、实例方法与 @私有。 函数值是命名函数/箭头函数,静态方法本身不能直接当值;需要作值时可包一层箭头函数。

7. 模块

一个文件就是一个模块:顶层 @引入 "路径.zh",用 模块.函数() 限定调用。模块文件名(去掉 .zh)就是模块名。想要短名字用 为 别名,想把个别函数带进来用 只:

// 工具.zh
@私有
函数 内部换算(度)
    返回 度 * 9 / 5 + 32

函数 摄氏转华氏(度)
    返回 内部换算(度)
// 主.zh
@引入 "工具.zh"
打印("30 度 =", 工具.摄氏转华氏(30), "华氏")
30 度 = 86 华氏

细节:引入要写在文件顶部;@私有(@private)只影响模块外可见性;重复引入、互相引入、菱形依赖都按真实路径去重,幂等;模块只能放函数/结构体/引入,顶层语句只在入口文件(构成 main)。

标准库模块:编译器自带一层用 zhlang 写的标准库(标准库/)。可引入的模块用裸名即可,不用写路径与扩展名(@引入 集合 ≡ @引入 "集合.zh"):

@引入 集合 只 反转, 最大值
常量 名单 = [3, 1, 2]
反转(名单)                  // 原地反转
打印(名单, 最大值(1, 9))
[2, 1, 3] 9

可引入层还有 文件、JSON、正则、进程、终端、时间、随机、路径工具,以及 同步、错误、网络;函数清单见 API 参考。

另有一批内置函数本身就是 zhlang 源码实现(分割/连接/转大写/映射/绝对值/哈希SHA256/编码URL 等 20 个),隐式可用、行为与规格逐字一致 —— 想读源码直接看仓库的 标准库/;只有被引用到的标准库模块才会进入产物(按需发射)。

8. 错误处理

所有运行时错误(越界、除零、缺键、读文件失败……)都可以用 尝试 / 捕捉 接住;捕捉变量就是一段消息字符串。主动报错用 抛出:

尝试
    变量 a = [1, 2]
    打印(a[99])
捕捉 e
    打印("捕获:", e)

函数 检查年龄(岁)
    如果 岁 < 0
        抛出("年龄不能是负数:" + 转字符串(岁))
    返回 岁

尝试
    检查年龄(-1)
捕捉 e
    打印("捕获:", e)
打印("继续往下走:", 检查年龄(18))
捕获: 下标越界:99(长度 2)
捕获: 年龄不能是负数:-1
继续往下走: 18

未捕获的错误会打印 运行时错误(第 N 行):消息 并以退出码 1 结束;协程里未捕获的错误按 Go 语义终止整个进程。

8.1 类型化错误

普通 抛出 与全部运行时错误仍然是消息字符串;需要错误类型时, 用内置 抛出对象 保留原值,配合标准库 标准库/错误.zh 的基类与工厂:

@引入 "错误.zh" 只 新建错误类型, 是错误, 错误类型

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

8.2 最终块(清理)

尝试 可以只写 最终(不要捕捉),也可以两者都写。 离开尝试语句的每条路径都会执行最终块——正常完成、异常被捕捉、异常未被捕捉 (先执行最终块,再按原行号重抛)、返回、跳出、继续:

函数 读取(模式)
    尝试
        打印("打开")
        如果 模式 == 1
            抛出("失败")
        返回 "内容"
    最终
        打印("关闭")

函数 main()
    打印(读取(0))
    尝试
        打印(读取(1))
    捕捉 e
        打印("捕获", e)
    最终
        打印("最终")
打开
关闭
内容
打开
关闭
捕获 失败
最终

9. 并发:协程、通道与锁

启动 函数(参数) 开一个协程(几 KB 栈,可以开上万个);通道() 传值,通道(n) 是缓冲通道。接收 从关闭且空的通道读到 假,这是约定的「没有更多」:

函数 生产者(ch, 件数)
    循环 i 从 1 到 件数
        发送(ch, i * 100)
    关闭(ch)

常量 ch = 通道()
启动 生产者(ch, 3)
当 真
    常量 v = 接收(ch)
    如果 v == 假
        跳出
    打印("收到", v)

常量 完成 = 通道()
循环 名 在 ["甲", "乙", "丙"]
    启动 干活(名, 完成)

函数 干活(名, 完成)
    休息(10)
    发送(完成, 名 + " 完工")

循环 i 从 1 到 3
    打印(接收(完成))
收到 100
收到 200
收到 300
丙 完工
乙 完工
甲 完工

多路等待用 选择:每个臂是「接收 通道 为 变量」「发送 通道, 值」或兜底的「否则」;多个臂同时就绪时随机取一个(与 Go 相同)。协程由运行时调度到多核上跑;阻塞式 I/O(网络收发、接受连接、休息)会让出 worker,不会卡住其他协程。纯计算忙协程不抢占(分点式抢占只在 C 版运行时;自举原生版靠多 worker 并行,忙协程占满全部 worker 时其他协程要等它结束)。

选择
    接收 ch1 为 v
        打印("ch1:", v)
    接收 ch2 为 w
        打印("ch2:", w)
    否则
        休息(1)

9.1 共享内存:互斥锁与读写锁

通道适合转移所有权;如果确实要共享可变容器,标准库提供 标准库/同步.zh(纯 zhlang 用通道实现,自举原生版可用,需要手动引入)。语言本身没有内置锁语法; Go 的 sync.Mutex 同样走标准库路线。互斥锁让「读改写」成为原子临界区:

@引入 "同步.zh" 只 互斥锁, 等待组

函数 累加(锁, 计数, wg)
    循环 i 从 1 到 1000
        锁.加锁()
        计数[0] = 计数[0] + 1
        锁.解锁()
    wg.完成()

函数 main()
    常量 锁 = 互斥锁()
    常量 计数 = [0]
    常量 wg = 等待组()
    wg.添加(2)
    启动 累加(锁, 计数, wg)
    启动 累加(锁, 计数, wg)
    wg.等待()
    打印(计数[0])
2000

要点:

失败路径也要释放锁;例如把捕获到的局部数组交给闭包,再走作用域锁:

函数 加一(锁, 计数)
    常量 增加 = () =>
        计数[0] = 计数[0] + 1
    锁.加锁执行(增加)          // f 抛错也会解锁

10. 文件与 JSON

写文件("名单.json", 编码JSON({"名字": ["甲", "乙"], "人数": 2}))
常量 数据 = 解码JSON(读文件("名单.json"))
打印("人数:", 数据["人数"], "第一个:", 数据["名字"][0])
打印("文件存在:", 文件存在("名单.json"))
删除文件("名单.json")
人数: 2 第一个: 甲
文件存在: 真

文件族内置:读文件 / 写文件 / 追加文件 / 删除文件 / 文件存在 / 文件大小 / 列目录 / 建目录 / 真实路径(realpath,解析符号链接);JSON 是 编码JSON / 解码JSON(对象 ↔ 字典、数组 ↔ 数组,null 解码为 假)。

11. 网络与 https

HTTP 客户端一行就能用,https 内置(自包含 TLS,带证书链与主机名校验;证书不符必报错,不静默放行):

常量 r = 网页请求("https://example.com")          // 也可 GET/POST/自定义头
打印("状态:", r["状态"], "内容长度:", 长度(r["体"]))
打印(取网页("https://example.com")[0:0])          // 取网页 直接返回正文

TCP 是「监听 / 接受连接 / 连接到 / 套接字发送 / 套接字接收 / 关闭套接字」。下面这条程序一条进程里既当服务端又当客户端(示例 网络.zh):

函数 回音服务(端口)
    常量 服务 = 监听(端口)
    常量 c = 接受连接(服务)
    常量 请求 = 套接字接收(c, 1024)
    套接字发送(c, "回音:" + 请求)
    关闭套接字(c)

启动 回音服务(18930)
休息(80)
常量 c = 连接到("127.0.0.1", 18930)
套接字发送(c, "你好")
打印(套接字接收(c, 1024))
关闭套接字(c)
回音:你好

12. 正则表达式

八个正则内置(匹配 / 全匹配 / 查找 / 全部 / 分组 / 替换 / 分割 / 转义),语义对齐 Python re:码点级、支持 i s m 选项、替换串里用 $1…$9。写模式用原始字符串 r"…" 免二次转义:

常量 文本 = "订单 A1024 与 B2048 已发货"
打印("全部编号:", 正则全部(文本, r"[A-Z]\d{4}"))
打印("替换:", 正则替换(文本, r"\d+", "****"))

常量 m = 正则分组("2026-10-16", r"(\d{4})-(\d{2})-(\d{2})")
打印(`年=${m[1]} 月=${m[2]} 日=${m[3]}`)
打印("分割:", 正则分割("甲, 乙;丙", r"[,;]\s*"))
全部编号: ["A1024", "B2048"]
替换: 订单 A**** 与 B**** 已发货
年=2026 月=10 日=16
分割: ["甲", "乙", "丙"]

13. 指针(进阶)

&x 取地址、*p 读写目标。指针与 C 一样危险:悬垂即未定义行为;GC(阶段 2,保守标记-清除)不追踪指针目标的生命周期——指向栈上变量槽的指针,栈帧返回后照样悬垂,拿不准就别用。

函数 加倍(p)
    *p = *p * 2

变量 x = 21
变量 p = &x
加倍(p)
打印("加倍后:", x)
*p = *p + 1
打印("自增后:", *p, x, "; p 指向 x:", p == &x)
加倍后: 42
自增后: 43 43 ; p 指向 x: 真

拿到指针后可以用 -> 读写字段、调用方法:基是指针时先解引用一层;基已经是对象引用(类/结构体/字典,包括函数与方法的返回值)时直接访问。可链式,ELF / PE / aarch64 三端一致。

类 计数器
    属性 n = 0
    函数 初始化(起 = 0)
        本.n = 起
    函数 加(k)
        本.n = 本.n + k
    函数 自身() -> 计数器
        返回 本

变量 c = 计数器(40)
变量 p = &c
p->加(2)
p->n = 100
打印(p->n)              // 100
打印(c.自身()->n)       // 100

14. 交叉编译与 REPL

同一条命令就能出别的平台的原生程序。官网下载的最新自举版内建 x86_64 / aarch64 / Windows PE 后端:编译器源码全部是 zhlang,自己编译自己,产物直接生成机器码,不需要任何外部交叉工具链:

./编译 你好.zh -o 你好                                  # 本机
./编译 你好.zh -o 你好-arm --target aarch64-linux-gnu   # 静态 ELF,arm64 Linux
./编译 你好.zh -o 你好.exe --target x86_64-windows-gnu  # PE64,Windows x64
./编译 你好.zh -d          # 调试运行:临时编译并直接执行,不留二进制

REPL 在终端下支持行编辑与语法着色:方向键/Home/End/Delete、上下键翻历史、Tab 缩进、Ctrl-C 取消当前行、Ctrl-D 退出,关键字/字符串/数字/注释实时着色;历史保存在 ~/.zhlang_history(ZHC_REPL_HISTORY 可改路径)。

调试断点:源码里写 @断点调试(等级, 内容),用 -d 运行会在命中处停下,提示符带 文件:行;命令 c/继续、q/退出、b/栈、?。等级 错误=0、警告=1、信息=2、调试=3,ZHLANG_DEBUG_LEVEL 可过滤。

15. 深入:自举与原生后端

zhlang 的编译器用 zhlang 重写并以自己编译自己(仓库 自举/:词法、语法、名字解析、x86_64/aarch64/PE 代码生成、ELF/PE 组装、CLI 与 REPL)。make selfhost 要求 stage1 与 stage2 逐字节相同;产物直接是机器码,不依赖外部工具链。

原生运行时(字符串/数组/字典/闭包/协程/通道/网络/TLS)由 自举/原生运行时.zh 与用户程序一起编译;编译器与本机程序都不依赖外部工具链。

为什么有意思:一门语言能编译自己,意味着它的编译器可以用这门语言来改 —— 自举/ 下的编译器源码本身,就是 zhlang 代码。

15.1 能力边界

官网下载的纯 zhlang 自举版GC 已实现阶段 2(保守标记-清除 + 空闲链复用:分配按 32MB 增量阈值触发回收,堆回收/堆统计/堆整理 可观测;保守扫描只多保留、不会错回收)。Windows PE 的目录列举与进程 IO 均已支持。

已实现并在三端验证:协程 / 通道 / 选择、TCP / HTTP / HTTPS(Linux 上纯 zhlang TLS 1.3,含 P-521、名称约束、CRL 吊销与 OCSP 钉接;Windows 走系统 TLS/WinHTTP,撤销检查由系统负责)、接口、闭包、结构体与类、@断点调试 与 -d 调试运行、文档注释与 HTML 文档生成。

16. 下一步

完整的函数签名、别名与错误语义见 API 参考;语言规格见 《zhlang 语言规格》。仓库 examples/ 的 15 个示例 已全部改用面向对象写法(类 / 初始化 / 静态方法),可直接对照阅读。

编译器自身的 zhlang 源码(v2.22 起)也全部面向对象化:词法器、语法器、名字解析器、 代码生成器、编译器驱动、两个原生后端与原生运行时都是类;详见 源码仓库 的 自举/README.md。