程序模型
一个 Zero 程序可以是单个 .0 文件,也可以是带有 zero.json 清单的包。
{
"package": { "name": "hello", "version": "0.1.0" },
"targets": { "cli": { "kind": "exe", "main": "src/main.0" } }
}命令行程序导出 main:
pub fn main Void world World ! check world.out.write "hello from zero\n"示例通过 World.out 输出用户内容,通过 World.err 输出诊断信息。Zero 不暴露 std.debug.print 或 std.log;保持打印能力基于能力机制,使格式化保持小巧且按需付费。
World 是由所选运行时创建的能力对象。它不是全局单例。
目标可以拒绝不可用的能力。在当前编译器中:
- 托管的
std.fs辅助工具在主机目标上被接受 - 托管的
std.fs在非主机目标上报告TAR002 std.mem.copy和std.mem.fill保持目标中立
词法基础
Zero 源码使用 UTF-8 文本。标识符区分大小写。行注释以 // 开头。
常见的字面量形式包括:
let name "zero"let marker char 'z'let count 42let ratio f64 0.5let ok true顶层 const 声明可以为确定性编译时值命名,供函数使用:
const base i32 40 const answer i32 + base 2 pub fn main Void world World ! if == answer 42 check world.out.write "const ok\n"字面量算术、对先前常量的引用以及受支持的 meta 表达式由有界编译时求值器求值。它们会降低为普通的产物常量。
公共常量必须编写显式类型注解,以便图 JSON 和文档暴露稳定的 API 形状。
V1 编译时求值器是确定性的且沙盒化的。
zero check --json 和 zero graph --json 包含一个 compileTime 对象,其中有:
- 缓存键输入和限制
- 沙盒策略
- 支持的事实
- 静态值支持
- 类型化构建器限制
- 反射保留策略
求值器当前支持:
- 字面量算术、比较和布尔逻辑
- 目标事实,如
target.pointerWidth、target.abi和target.hasCapability("fs") - 类型化反射事实,如
fieldCount(Point)、fieldType(Point, "x")、enumCaseCount(Mode)、hasEnumCase(Mode, "tiny")、choiceCaseCount(Event)和hasChoiceCase(Event, "tick")
文件系统、网络、环境变量和进程效果被拒绝。不支持或循环的编译时表达式报告 MET001。
类型别名为现有类型提供编译时拼写:
pub alias ByteCount usize alias BytePair Pair<u8,u8>别名不会创建运行时包装类型、布局标识或转换代码。循环别名会被拒绝,图 JSON 会报告别名名称、目标和可见性。
当前编译器有意保持编译时执行的小规模:
- 有界步数
- 有界递归深度
- 仅编译时类型化反射
- 默认不保留发布元数据
- 无原始令牌字符串构建器
函数
函数使用 fn 声明。导出函数使用 pub fn。
fn answer i32 ret + 40 2 pub fn main Void world World ! let value answer() check world.out.write "done\n"签名以返回类型开头,然后是参数名/类型对。可失败函数包含 ! 或显式的 ![...]。
当前编译器支持狭窄的静态泛型切片。泛型函数使用显式类型参数,仅在调用时作为具体特化发出:
fn identity<T: Type> T value T ret value let a i32 identity<i32> 41let b u8 identity 7_u8基于参数的推断是调用局部的。如果同一个泛型参数被多个参数使用,所有推断的具体类型必须匹配。
公共签名仍然需要显式编写参数和返回类型。编译器不会从函数体推断导出的 API 形状。
泛型声明还可以携带静态值参数。静态值在特化时已知,可以出现在固定数组长度或直接类型特化中:
type FixedVec<T: Type, static N: usize> len usize items [N]T fn first<T: Type, static N: usize> T vec ref<FixedVec<T,N>> ret vec.items[0]调用点传递显式字面量、枚举成员或顶层确定性 const 值:
first<u8, 4>(&vec)Gate<enabled, Mode.fast>编译器支持整数、Bool 和枚举静态值。它发出具体布局,如 z_FixedVec_u8_4_。
静态值诊断:
| 代码 | 含义 |
|---|---|
STC001 | 不支持的静态参数类型。 |
STC002 | 在需要编译时值的地方使用了运行时值。 |
STC003 | 静态参数与期望值不匹配。 |
静态值支持不会添加运行时注册表、反射表、虚函数表或隐藏分配。
在泛型类型内声明的方法通过 Self 继承该类型的类型参数和静态参数。
调用可以使用命名空间风格或接收者风格。两者都从具体接收者进行特化:
type FixedVec<T: Type, static N: usize> len usize 0 items [N]T fn init Self items [N]T ret FixedVec . items items fn push Void self mutref<Self> value T ![Full] if == self.len N raise Full set self.items[self.len] value set self.len + self.len 1 mut vec FixedVec<u8,4> FixedVec.init ([0, 0, 0, 0])check FixedVec.push (&mut vec) 10check vec.push 20字段默认值允许类型字面量在带注解的泛型类型提供 T 和 N 时省略 len 等字段。
方法降低保持直接:
FixedVec.init<u8,4>(...)特化为z_FixedVec_u8_4_initvec.push(20)将&mut vec作为显式第一个参数传递- 接收者调用发出直接函数,如
z_FixedVec_u8_4_push
没有方法注册表、虚函数表、反射、隐藏分配或动态分派。
方法诊断:
| 代码 | 含义 |
|---|---|
SHM001 | 泛型类型方法调用无法绑定 Self、T 或 N。 |
SHM002 | 显式方法参数与接收者类型不一致。 |
RCV001 | 未知或非接收者方法。 |
RCV002 | 在需要可变接收者的地方使用了临时或不可变接收者。 |
静态接口约束泛型函数而不引入运行时分派:
interface Readable<T: Type> fn read i32 self ref<T> fn readValue<T: Readable<T>> i32 value ref<T> ret T.read value具体类型参数必须是具有匹配静态方法的类型。Readable<T> 在特化时检查,在直接发出之前擦除。
诸如 readValue<Counter>(&counter) 的调用会降低为直接的具体调用,如 z_Counter_read(...)。缺少方法或签名不匹配会报告 IFC001 到 IFC005。
绑定与可变性
使用 let 创建不可变绑定:
let message "hello\n"使用 mut 创建有意重新赋值的绑定:
mut index 0set index + index 1可变绑定还支持通过嵌套左值链进行字段赋值和固定数组元素赋值:
type Point x i32 y i32 mut point Point . x 1 y 2set point.x 3 mut bytes [4]u8 [65, 66, 67, 68]set bytes[1] 90检查器拒绝向不可变绑定赋值。索引赋值目前限于:
- 以
mut左值为根的固定数组 - 显式
MutSpan<T>可写视图
只读 Span<T> 和 String 的索引变更不属于当前公共接口。
类型
Zero 是静态类型的。原生编译器当前实现了 i8、i16、i32、i64、u8、u16、u32、u64、usize 和 isize 的检查整数宽度。
整数字面量支持十进制、0x 十六进制、0b 二进制、0o 八进制、_ 分隔符以及可选后缀如 _u8 或 _usize。字面量按上下文类型化并进行范围检查:let byte u8 255 有效,而 let byte u8 256 会被拒绝。
非字面量整数值不会隐式窄化、宽化或改变符号性。使用 value as Type 进行显式整数到整数的转换。
let count u32 0x12c_u32let byte u8 count as u8当前的 as 形式有意保持显式。它支持原始整数、浮点数和字节大小的 char。
它不会转换字符串、布尔值、内存视图、形状、选择或指针。
f32 和 f64 是原始浮点类型。浮点字面量使用 digits "." digits 格式,带有可选指数,如 1.0、0.5 和 1.0e-3。
无类型浮点字面量默认为 f64。f32 字面量需要预期的 f32 上下文。
浮点数与整数不同。算术和比较需要匹配的浮点宽度。
char 是用于 ASCII/解析器/编解码器风格值的独立字节大小原始类型。字符字面量使用单引号并解码为一个字节:
'a''\n''\'''\\''\x41'
char 不是 String 或整数类型。它不会隐式转换为 u8 或从 u8 隐式转换,也不被整数算术接受。
f16、Unicode 标量字面量和字符数组不属于当前公共接口。当函数不返回有用值时使用 Void。
可选值使用 Maybe<T>。仅在期望类型为 Maybe<T> 的地方使用 null;无类型 null 会被拒绝。
面向内存的 API 使用 Span<T>、MutSpan<T>、ref<T>、mutref<T> 和 Alloc 等类型。托管文件切片还暴露 Fs、File 和 owned<File> 用于显式资源所有权。
原生编译器当前验证这些形式,并为 Span<T>、MutSpan<T>、Maybe<T> 和小型托管文件结构体发出可运行的布局。
原生编译器支持固定数组、跨度和面向字节的字符串的单元素索引和半开范围切片。
索引表达式和切片边界必须是整数。这些位置的整数字面量作为 usize 检查:
let bytes [4]u8 [65, 66, 67, 68]let scratch [16]u8 [0_u8; 16]let first u8 bytes[0]let tail Span<u8> bytes[1..4]let view Span<u8> std.mem.span "ABCD"let second u8 view[1]let pair Span<u8> view[1..3]let suffix Span<u8> view[1..]let prefix Span<u8> view[..3]let all Span<u8> view[..] let values [4]i32 [10, 20, 30, 40]let numbers Span<i32> valueslet third i32 numbers[2]let middle Span<i32> values[1..3] mut writableValues [3]i32 [1, 2, 3]let writable MutSpan<i32> writableValuesset writable[1] 20 let text String "zero"let byte u8 text[1]let bytes Span<u8> text[1..]当前索引行为:
| 来源 | 结果 |
|---|---|
[N]T、Span<T>、MutSpan<T> | T |
String | u8 |
切片形式为 start..end、start..、..end 和 ..。它们为数组/跨度返回 Span<T> 视图,为字符串返回 Span<u8> 视图。切片是半开的:包含起始,排除结束。省略的起始默认为 0;省略的结束默认为基础长度。
赋值可以针对:
- 可变局部绑定
- 以可变局部变量为根的类型字段
- 这些左值链中的固定数组索引
MutSpan<T>元素- 索引的
mutref<MutSpan<T>>路径
索引、切片、固定数组索引赋值和 MutSpan<T> 索引赋值的边界在运行时检查。失败时打印 zero bounds check failed 并中止。当需要可恢复的 Maybe<T> 结果时,使用 std.mem.get(value, index)。
字符串索引和切片是面向字节的,不是 Unicode 标量操作。std.mem.len 接受固定数组、Span<T> 和 MutSpan<T>。std.mem.eqlBytes 比较相同元素的跨度视图。
原生编译器尚不支持:
- 只读
Span<T>或String的索引变更 - 切片赋值
- 通过调用或临时变量赋值
- 特定配置的边界检查消除
控制流
使用 if / else 进行分支:
if == value 42 check world.out.write "math works\n"else check world.out.write "math broke\n"条件必须是 Bool;整数和指针不会强制转换为真值或假值。
使用 while 进行循环:
while keepGoing check world.out.write "loop\n"使用范围 for 循环遍历整数范围。结束边界是排他的:
for index in 0..4 if == index 2 continue check world.out.write "tick\n"使用 break 退出最近的循环,使用 continue 跳到下一次迭代。
使用 ret 带值退出函数。
效果与错误
Zero 保持效果操作可见。
pub fn main Void world World ! check world.out.write "hello\n"check 调用可失败操作并传播失败。使用 check 的函数声明 ! 或 ![...]。
用户定义的错误是命名符号。函数可以声明开放 ! 标记,或显式错误集:
fn validate i32 ok Bool ![InvalidInput] if == ok false raise InvalidInput ret 42 fn run Void ![InvalidInput] check validate true原生编译器验证显式错误流:
raise ErrorName只能出现在抛出错误的函数中。- 带有
![...]的函数只能抛出列出的错误。 - 调用可失败的用户函数需要
check。 - 带有显式错误集的调用者必须包含每个被检查的被调用者错误。
let value check fallible_call()适用于用户可失败调用、Maybe<T>和命名错误的std.fs辅助工具。let value rescue (expr) err fallback适用于相同的简单情况,并降低为直接分支。
Zero 不使用语言级别的异常。
对于当前的原生辅助切片,对 Maybe<T> 的 check 降低为直接分支。如果值不存在,函数返回其默认失败值。
不会创建异常对象、展开或隐藏的全局错误状态。 用户定义的可失败函数仅在使用显式错误流时降低为小型生成的状态/结果结构体。
类型
使用 type 定义命名记录:
type Point x i32 y i32 let point Point . x 40 y 2let total + point.x point.y类型字面量命名其字段。字段访问使用点语法。
类型字段可以声明默认值:
type Pair left u8 1 right u8 let pair Pair Pair . right 2只有带默认值的字段可以省略。默认值根据声明的字段类型进行类型检查,并在每个类型字面量位置降低为普通的 C 初始化器。
当构造具有显式注解类型时,支持泛型类型:
type Pair<T: Type, U: Type> left T right U let pair Pair<i32,u8> Pair . left 42 right 7_u8let value i32 pair.left泛型类型布局在发出之前单态化。当前编译器支持:
- 多个类型参数
- 整数静态值参数
- 字段默认值
- 返回实例化类型如
Pair<T, U>的泛型函数 - 具有命名空间和接收者风格调用的泛型类型方法
更广泛的静态值类型和默认泛型参数不属于当前公共接口。
类型可以定义小型静态方法,通过命名空间风格查找调用:
type Counter value i32 fn add i32 self ref<Self> amount i32 ret + self.value amount let counter Counter Counter . value 40let answer Counter.add (&counter) 2这是直接静态降低为具体函数,如 z_Counter_add。没有动态分派、虚函数表或方法注册表。
接收者风格调用保留给第一个参数为 self: ref<Self> 或 self: mutref<Self> 的类型方法。
枚举、选择与匹配
使用 enum 定义固定名称集合:
enum Status ready failed使用 choice 定义替代项,包括带载荷的替代项:
choice Result ok i32 err String使用选择名称构造载荷变体:
let result Result Result.ok 42穷尽匹配选择:
match result ok value if == value 42 check world.out.write "choice ok\n" err message check world.out.write "choice err\n"当匹配有意分组剩余情况时,使用 ._ 作为回退分支:
match mode fast check world.out.write "fast\n" _ check world.out.write "other\n"回退分支不能绑定载荷。当需要载荷值时,在命名选择情况后放置载荷名称。
延迟执行
defer 在当前作用域结束时调度清理:
pub fn main Void world World ! defer cleanup() check world.out.write "work\n"当前原生编译器支持在词法作用域退出时的简单 defer,包括通过 ret、break 和 continue 的退出。
当 T 定义了规范的非抛出类型方法时,活跃的 owned<T> 局部变量也会在词法退出时被清理:
type Handle marker MutSpan<u8> fn drop Void self mutref<Self> set self.marker[0] 1编译器按声明逆序发出直接的 Handle_drop(&value) 调用。
如果一个 owned 局部变量被移动到另一个 owned 绑定、owned 参数或 owned 返回中,旧绑定不会被释放。直接的用户调用如 value.drop() 仍然被拒绝;使用类型方法进行自动清理,或在需要手动控制时使用单独的显式清理函数。
owned<File> 在当前托管 std.fs 切片中是编译器已知的。它降低为底层文件句柄,并在词法退出时(包括提前 ret)确定性地关闭。
这不使用注册表、引用计数或进程全局清理列表。显式的 std.fs.close(&mut file) 是允许的,并且与自动清理路径是幂等的。
借用
使用 &value 创建共享 ref<T>,使用 &mut value 创建可变 mutref<T>:
type Point x i32 y i32 fn read_x i32 point ref<Point> ret point.x fn write_x Void point mutref<Point> value i32 set point.x value&mut 需要可变左值根,通过 ref<T> 的赋值会被拒绝。
当前原生检查器跟踪词法借用冲突以及存储在值内部、从调用返回或通过控制流合并的引用。它拒绝在有可达引用活跃时赋值,拒绝返回对局部绑定的引用,拒绝将被调用者局部引用存储到调用者拥有的 mutref 存储中。BOR001 JSON 包含一个 borrowTrace.activeBorrows 数组用于词法冲突,以便代理可以识别活跃绑定并移动冲突操作或使用内部块缩小借用范围。借用降低为直接地址表达式;没有借用注册表或运行时别名元数据。
导入与标准库
使用 use 导入模块:
use std.codecuse std.parse当前原生辅助工具包括:
std.mem:无分配内存辅助工具、跨度构造、字节相等、显式分配器、固定容量Vec、空映射/集合元数据和ByteBufstd.codec:字节和校验和辅助工具,如readU32、encodedVarintLen和crc32std.parse:扫描器辅助工具,如数字和标识符谓词std.time:持续时间辅助工具,如ms、seconds、add和asMsFloorstd.args:CLI 辅助工具len()和get(index) -> Maybe<String>std.path:固定缓冲区路径辅助工具basename(path) -> String和join(buffer, left, right) -> Maybe<String>std.fs:托管路径辅助工具、显式Fs句柄、owned 文件句柄、可失败读/写,以及由显式分配器和大小限制支持的readAll辅助工具
当前 std.fs 辅助工具是托管 CLI API。它们使用:
- 路径字符串或显式
Fs能力 - 调用者拥有的固定缓冲区
Maybe<T>和Bool结果- 命名错误变体(当示例需要恢复时)
owned<File>清理
readAll 和 readAllOrRaise 使用显式分配器和大小限制;两者都不会访问进程堆。非主机目标检查使用 TAR002 拒绝此托管切片。使用 std.mem 辅助工具和包本地模块进行目标中立构建。
更丰富的文件模式、权限和平台特定的路径规范化不属于当前公共接口。
包
包使用 zero.json:
{
"package": { "name": "systems-package", "version": "0.1.0", "license": "MIT" },
"targets": { "cli": { "kind": "exe", "main": "src/main.0" } },
"deps": {},
"profiles": {
"dev": { "inherits": "dev" },
"release-small": { "inherits": "release-small" }
}
}通过传递目录来检查包:
zero check examples/systems-package包本地导入是显式的:
use helpers解析为src/helpers.0use config.parser解析为src/config/parser.0或src/config/parser/mod.0
构建解析是声明式的,不执行依赖代码。未知导入、直接导入循环、错误清单和重复的公共导出在解析组合包源之前被报告。
zero graph --json <package> 列出模块名称、源路径、带源范围的导入边、带源范围的已解析 useImports、公共/私有符号计数、目标元数据、函数效果、所需能力以及所选目标是否提供托管文件系统支持。
当前编译器中没有已发布的包注册表或语义版本求解器。
本地路径依赖从 zero.json 解析。精确版本化的注册表引用作为元数据记录,不进行远程获取。解析器在 .zero/package-locks/ 下写入确定性依赖指纹文件。
C 互操作
使用 extern c 和 extern type 处理 C 边界:
extern c "config.h" as config extern type CConfig enabled bool limit i32互操作声明应使布局和 ABI 期望显式化。
工具链
常用原生命令:
zero check examples/hello.0
zero build examples/hello.0 --out .zero/out/hello
zero build --emit exe examples/add.0 --out .zero/out/add
zero build --emit exe --target linux-musl-x64 examples/add.0 --out .zero/out/add-linux-musl
zero graph --json examples/systems-package
zero size --json examples/point.0
zero targets可执行目标以支持的产物系列命名:
darwin-arm64darwin-x64linux-arm64linux-musl-arm64linux-musl-x64win32-arm64.exewin32-x64.exe
支持的非主机可执行构建使用直接发射器。