Toka 语法内部笔记
August 26, 2026 · View on GitHub
This file preserves earlier working notes only. It is not a normative source for compiler or AI-assisted development: historical rows may describe removed syntax. For the current language contract, see
docs/syntax.mdanddocs/syntax_zh.md.
Toka 语法说明 按表格罗列
| 代码示例 | 说明 | 要点/铁律 |
|---|---|---|
| 基础结构 | ||
fn main() -> i32 { ... } | 主函数 | 必须返回 i32,通常返回 0 |
import std/io::* | 模块导入 | 无分号,使用双冒号 :: 分隔。同目录循环导入放行:为了支持大型文件模块化拆分,Toka 允许**同一物理目录下(不含子目录)**的 .tk 文件之间相互 import 循环依赖。跨目录的循环依赖依然会被严格拦截,以维护宏观架构的单向清晰。 |
// comment | 注释 | 仅支持单行注释 |
/* comment */ | 注释 | 多行注释 |
| 变量与可变性 | ||
const MAX: i32 = 100 | 全局常量 | 必须在文件顶层或模块顶层声明,并且必须显式指定类型,运行时不可变。完美支持静态数组等复杂类型的编译期静态化存放(如 const K: [u32; 64] = [...])。 |
auto x = 1 | 变量声明 | 默认为不可变,自动类型推断 |
auto x# = 1 | 可变变量 | 变量名后加 # 表示可变 (Mutable) |
x = 2 | 赋值 | 仅具备可变性 (#) 的变量可被重新赋值 |
len = 0 / x = x + 1 | 标识符后缀与 H/P 权限 | # 在声明中定义 payload 写入或 handle 重绑定权限;带帽调用/模式视图中的 # 只请求已经声明的 H/P 权限,绝不扩大它。日常 payload 读写仍使用裸名(如 len、x);不能把调用处或模式处的 # 当作给变量、参数或字段补授权的写法。 |
auto x: i64 = 10 或 auto x = 10:i64 | 类型推导与隐式拓宽 | 定义时跟类型,或字面量后跟类型约束。 新特性:支持无损整数隐式拓宽。当源数据位宽绝对安全时(如小整数给大空间),无需后缀强制转换(如 auto n: u64 = 0 合法,不再强制要求 0 as u64)。 |
| 指针变量 | ||
auto *p | 只读原始指针 (Raw) | 必须带 *,无所有权,无生命周期管理 |
auto ^p | 只读独占指针 (Unique) | ^ 表示独占所有权,出作用域自动释放 |
auto ~p | 只读共享指针 (Shared) | ~ 表示引用计数共享,自动管理生命周期。【重要升级】引用计数操作默认原子化(默认多线程安全的 @arc 语义),其中递增(Inc/Retain)采用 Monotonic 内存序,递减(Dec/Release)采用 AcquireRelease 内存序,建立 Happens-Before 物理闸门确保并发生命周期绝对安全。 |
auto &p | 只读借用指针 (引用 Ref) | & 表示借用,不转移所有权 |
auto *p# | 可变原始指针 | *原始指针, #字段本身可变 |
auto ^p# | 可变独占指针 | ^独占指针, #字段本身可变 |
auto ~p# | 不存在 禁止 | 需携带并发安全锁 |
auto &p# | 可变引用 | &引用, #字段本身可变 |
auto &#p | 可重绑定 | #置于指针符号右侧语义为指针本身可重绑定,对所有指针形态都有效 |
| 仅考虑指针的种类和对实体的读写权限,以上组合就是全部,该可变性完全独立于指针本身的可重绑性 | 即指针对实体的读写只有2 中,只读或读写,不存在自动继承的情况 | |
| 缺席与物理零地址 | ||
null | raw pointer 的物理零地址字面值 | 只能流入 nul *T,不能流入 *T、^T、~T 或 &T |
miss | `T | miss` 的操作未命中分支 |
Option<T>::None | 显式零或一个存储状态 | 不等于 miss,也不会被隐式 flatten |
auto nul *x: i32 = null | raw 地址可能为零 | nul 是 raw-pointer 专属物理属性 |
T? / none | 已永久删除 | 分别报告 E0484 / E0486 |
nul ^T / nul ~T | 已永久删除 | 报 E0485;Safe owning handle 必须指向有效对象 |
| nullable assertion operator | 已永久删除 | 报 E0487;raw pointer 使用 .unwrap(),结果类型使用 match/方法 |
if *ptr == null 或 guard *ptr | 检查 nul *T 是否为零 | 不跨越 Option、Result 或 `T |
| 未完成编辑 | ||
todo | 类型待办 | 只能作为表达式使用的保留关键字;它消耗已经确定的需求,但不是变量、值、place、能力或资源。任何可达待办都使检查/构建失败,且不产生编译产物。 |
auto value: i32 = todo | 有完整需求的待办 | 报 E04603,并可由 toka todo-goals --json --check-only file.tk 输出 i32 需求;该 JSON 不是普通 Allow 证据。 |
auto value = todo | 推断不足 | 报 E04604;待办不参与类型推断、候选选择或泛型实参推断。 |
^todo / todo# / cede todo | 不支持的待办上下文 | 报 E04605;待办不能制造 H/P 权限、路径来源或资源转移,也不能传给 cede 参数。 |
| 函数与方法 | ||
fn add(a: i32, b: i32) -> i32 | 函数定义 | 参数需显式类型,-> 后跟返回类型,函数传参行为为原地捕获(隐式借用传递,不需要额外添加&符号),返回为值传递,返回引用需要显式添加&符号,如 -> &i32 表示返回一个 i32 的引用,返回其他指针形态同理 |
fn read(x: T) / fn mutate(x#: T) | 默认 payload 参数 | 参数层级准则:普通参数默认绑定 payload/Soul 视图。Toka 的传参逻辑语义已经是原地捕获,因此若函数只读取或修改实体内容,应写裸参数或 # payload 参数,如 x: T、x#: T,不要为了表达“借用传参”额外戴帽。 |
fn inspect(^p: T) / fn pass_raw(*p: T) | 纯 handle 传递参数 | 纯传递也是契约:当函数确实需要 handle 视角(如临时观察、比较、转发给另一个非接管参数),可以在参数名侧佩戴 &、*、^、~ 等帽子。纯传递不是复制、也不是转移;函数不能因此接管该 handle。若函数体既没有使用该参数的 handle 本体,也没有继续把它传给另一个帽子参数,就说明签名本不需要帽子,应改写为 payload 参数。 |
fn reset(^#p: T) / fn advance(*#p: T) | 重绑定参数 | 重绑定准则:函数若需要改变调用方 handle 变量本身(让它指向别处、置空、替换资源等),参数必须绑定 handle 本体,并且必须带 # 表示该 handle 可重绑定。 |
fn take(cede ^p: T) / fn forward(cede *p: T) | cede 契约参数 | cede 是执行契约,不只是权限标记:签名声明 cede 参数表示调用者已把该 handle/resource 交给函数,函数体必须在所有正常完成路径上继续显式 cede 它(传给下游 cede 参数、存入接管位置、返回 cede、或交给释放/关闭类终点)。若函数体没有完成 cede 任务,说明签名不需要 cede 或函数体漏写转交,编译器应报错。 |
fn foo(a: i32, b: i32 = 0) | 默认参数定义铁律 | 对列坍缩准则:与 C++ 一致,如果要为某个参数赋予默认值,则必须保障其右侧的所有参数都必须含有默认值。允许全部参数都带有默认值签名。(由于纯位置传参的特性,不允许中间隔断) |
foo(1, ..) | 默认参数调用铁律 | 显式兜底凭证:如果你在调用函数时,打算省略尾部参数并接纳它们的默认值,必须且只能在参数的最末尾显式给出一个 ..(一场调用仅允许一个,且必在句尾)。即使是 #file 这类魔法宏的注入,也受到不可逾越的 .. 管辖。杜绝隐式参数带来的重构黑洞! |
fn foo() -> &res: i32 | 命名返回值 | 在返回类型前书写可选的指针帽、标识符和冒号,专用于向后侧的 effects: 域开放该资源的路由锚点 |
effects: return <- a | 效应块(基础路由) | 置于函数签名后与 { 函数体之间。用于显式声明内存与生命周期的约束有向图,return <- a 表示返回值存活受参数 a 约束 |
effects: &res <- a | 效应块(命名路由) | 配合命名返回值,使用 &res 作为对象指向 a,可精确消除复杂结构的返回所有权歧义 |
impl MyShape { fn method(self) } | 方法定义 | impl 块定义类型方法 |
fn mut_method(obj#) | 可变方法 | obj# 声明参数可变,调用时必须显式标注:o#.m() |
obj.method() | 方法调用 | 点号调用 |
fn foo() -> nul *#Node# | may-zero raw handle 与两层写权限组合 | ? payload 属性已删除;最复杂的有效组合只保留 raw nul、handle # 与 payload # |
| 闭包 (Closure) | ||
auto f = { x, y => x + y } | 闭包 (常规) | 使用 {} 作为边界,=> 分隔参数与闭包体。默认隐式借用外部环境变量 |
auto f = { .a + 1 } | 隐式参数 (短语) | 省略参数与 =>,使用 .a, .b 作为匿名参数(类似 Swift 的 $0),绝佳的流式 API 伴侣 |
auto f = { => println("hi") } | 零参数闭包 | 铁律:无参且无隐式参数(没有 .a)的闭包,必须写出 =>,以避免与普通代码块混淆 |
auto f = { [cede a] x => a+x } | 显式捕获: 让渡所有权 | 捕获列表 [cede ...] 写在 { 内首位。cede 放弃外层对 a 的所有权,移交闭包,解决并发逃逸 |
auto f = { [copy ~rc] x => ...} | 显式捕获: 按值拷贝 | copy 针对外部变量制作副本送入闭包(如触发 ~ 共享指针的安全引用计数 RC+1) |
auto f = { [cede a] => a + .a } | 混合形态 | 若仅声明捕获而不声明实参,必须跟随 =>。然后在闭包体中照常使用隐式参数 .a |
| 数据结构 (Shape) | ||
shape Point(x: i32, y: i32) | 结构体 (Struct) | 使用圆括号,字段需命名 |
shape VecI32(nul *buf#: [i32], id: i32) | 成员属性 | nul 仅表示 raw 地址 may-zero;# 表示 payload 可写 |
auto vec#: VecI32 | 成员的可变性继承父实例的可变性 | 此时vec本身是可变的,所以*buf就是可重绑定的,id是可变的,依据指针变量一节的规定可以推断,可变性继承链在指针的实体上被阻断 |
auto p = Point(x=1, y=2) | 列表初始化 | 【强制铁律】实例化结构体(Shape)时必须使用具名参数 (Named Parameters) 进行赋值。即使只有一个字段也不例外。 |
auto p = Point(1, 2) | 【已废除】实例化结构体按位置顺序初始化已彻底废除,强行拦截抛出 E042A。 | |
auto p = Point(x=1, ..) | 具名默认值省略 | 铁律:可在具名初始化列表的**任意位置(但最多只能包含一个)**使用 .. 显式省略并接纳其余所有未指定字段的默认值。如果被 .. 省略的任何一个字段在 Shape 定义中没有提供默认值,编译器将精准抛出 E042C 编译错误。 |
auto p = Point(1, .., 3) | .. 不用非得放在末尾,可以放在开头、中间或末尾(一个初始化列表最多只能包含一个 ..)。编译器会自动推演两端的参数数量,并用 Shape 中定义的默认值填充中间被省略的字段。如果被 .. 省略的任何一个字段没有提供默认值,同样会精准抛出 E042C 编译错误。【已废除】由于位置初始化已废除,该语法已随之失效。 | |
| 不支持 base 状态重组 | 禁止 base 克隆 | 铁律:Toka 彻底不支持任何 base 变量克隆或状态重组语法。所有未指定的字段一律仅能通过 .. 安全地回退到 Shape 声明的默认值。不支持任何具名与位置的混用。 |
auto p2 = Person(p1) p2 = p1 | 同构拷贝与赋值语义 (Isomorphic Copy & Assign) | 全局一致的拷贝铁律:无论是对目标执行同构构造,还是直接使用 = 纯实体赋值。若该结构本身或内部实现带有 @Encap/clone(),均会隐式拦截并执行完全独立的深拷贝 (Deep Copy);若无,则退化为极速的比特拷贝 (Bitwise)。此时新旧变量物理彻底切断独立,两边的存续与释放互不干扰。 |
auto ^p3 = new Person(cede p1) x3 = cede x1 | 终极物理夺舍 (Move) | 全局一致的移动铁律:在构造入参或 = 赋值时强加 cede 关键字,即触发最高级的强制夺舍语义。编译器将立刻拦截深克隆,原封不动且零成本地过户内存句柄,并当场全局吊销(报废)原主 p1 的可用合法性(其后对 p1 的任何访问会触发 E0438 报错)。 |
auto p = Person(..) vs Person() | 全量缺省防线 (Strict Defaults) | 高安全工程铁律:() 的空括号调用仅且只能用于实例化真正自带 0 个字段的标记空结构体!如果结构体中具有任意一个字段(即便是全都有默认值),你想偷懒全量继承的话,必须强行带上声明符 ..(即写成 Person(..) )。这一语法屏障彻底杜绝了后期扩充属性和添加默认值所造成的静默“重构黑洞”! |
shape Pair(i32, i32)已彻底废除 | 匿名具名记录 (Anonymous Record) | .0, .1 访问【已废除】Toka 已彻底废除元组。现推荐使用轻量且可推导的匿名键值对记录 (x = 10, y = 20),通过 .x,.y 访问以彻底平替元组。 |
shape Res(Ok(T) | Err) | 枚举 (Enum) | | 分隔变体,支持关联数据 (Tagged Enum payload) |
shape IntOrFloat(as i32 | as f32)已彻底废除 | 裸联合 (Bare Union) | as 关键字【已废除】为了 100% Zero UB,Toka 已彻底废除并剥离裸 Union (Bare Union)! |
alias ID = i32 | 类型别名 | 为现有类型起别名 |
type ID = i32 | 类型克隆 | 以已有类型为原型声明新类型 |
| 封装与可见性控制 (Visibility) | ||
无 @Encap 的 Shape 声明 | shape Box(v: i32) | 若没有任何关联的 impl @Encap,其所有成员默认全部 public (为了基础数据聚合的方便性)。 |
@Encap 控制块接管 | impl Box@Encap { ... } | 一旦为其声明该块,结构体成员默认 private;仅能通过下列精确 pub 授权开放。 |
pub field1, field2 | 全局公开 | 向全域开放明确列出的字段;可用逗号批量列出。 |
pub(crate) field1 | 已移除 | 不存在包级或路径级授权。 |
pub(os::driver) field | 已移除 | 不存在模块路径授权。 |
pub * ! secret, id | 已移除 | 不存在 wildcard 或反向排除授权。 |
| 同物理文件上帝视角 | 不存在 | 同一物理文件不绕过 @Encap 可见性检查。 |
| 连续多元素排列类型 | ||
[T; N] | 静态数组 (Array) | 编译时固定大小的连续数组,他是一个泛型类型,使用时 T 和 N 必须具体 |
[T] | 动态数组 (Slice/Vec) | 一等动态数组实体,作为胖指针 (Fat Pointer) 携带内建运行时长度跨越内存。除了静态展开,平时他不能独立存在,必须搭配指针形态 如独占数组 ^p: [i32] 或引用切片 &p: [i32] 等组合使用。 |
auto arr: [i32; 3] = [1, 2, 3] | 静态数组实例化 | 使用方括号。若为 auto arr#,则 arr[i] 可被修改。动态 [T] 同理 |
auto ^darr: [i32] = new []i32(1, 2, 3) | 动态序列实例化 (自动推导长度) | 内存形态为 ^{i32*, i64}。当资源离开作用域时,自动依据 i64 长度精确触发迭代析构所有受托管的实体元素。 |
auto ^darr: [i32] = new [3]i32(0) | 动态序列实例化 (指定尺寸及广播初值) | 使用广播值快速填充内存序列。原始指针申请时对应语法为 unsafe alloc [3]i32(...)。 |
auto ^darr = new [3]Uninit<Data> | 未初始化内存的安全申请及免析构 | 指定序列泛型为内建的 Uninit<T> 形态时,在堆内存分配期间将会合法跳过默认的初值广播以及退出作用域的边界析构闭环。 |
| 原始指针操作(本节中不戴*的变量均为解引用后的实体) | ||
*val | 取地址 (Hatting) | 获取 LValue 的 Handle (通常得到 *,^,~ 或 &) |
auto *buf = unsafe alloc [N]T | 堆内存分配 | 在堆上分配 N 个 T 类型对象,返回 *[T] |
unsafe free [live]p | 原始数组析构并释放 | live 是从下标 0 开始、仍需析构的连续活元素数,不是分配容量。连续活前缀容器用 free[len];元素已全部 cede 到别处,或已按非连续布局逐个析构后,用 free[0] 只释放存储。 |
p[i] | 数组下标访问 | 像数组一样访问第 i 个元素,类型为 T |
*p[i] | 指针下标偏移 | 整体表达式返回一个指针,指向第 i 个元素的内存地址,位置与 *p + i*sizeof(T) 等价, 偏移后的类型仍然为 *T,可以理解为*(p[i]) |
auto *r = *p + i | 指针下标偏移 | 整体表达式返回一个指针,指向第 i 个元素的内存地址,位置与 *p + i*sizeof(T) 等价, 偏移后的类型仍然为 *[T]可以理解为只是位置偏移了,他还是代表一组元素的指针 |
p = val | 内存重写 | 这不适用于*[T]类型,因为*[T]是动态数组,其长度在编译时未知,无法确定需要memcpy多少内存 |
*p = *q | 指针重绑定 | 修改指针变量本身的指向(限指针本身可重绑定,如 &#p) |
| 控制流 | ||
if cond { ... } else { ... } | 条件分支 | 条件无需括号 |
loop { ... } | 无条件无限循环 | 大一统三态 Repetition:最纯净的循环形态,退出循环必须且只能显式使用 break 语句。不支持传统 for(;;) 或者是 C 风格的 for。 |
loop cond { ... } | 条件循环 | 彻底超度 While:Toka 1.0 的条件循环语法,当 cond 为真时执行循环体,条件无需括号。使用它来完全平替传统的 while 循环。如果误用 while 会触发 E0107 拦截指导。 |
for auto x in iter { ... } | 协议迭代循环 | 迭代绑定铁律:支持实现了迭代器协议的对象。迭代绑定必须且只能携带 auto 关键字声明修饰符(可变迭代为 for auto x#,借用为 for auto &x),绝对不支持 let 关键字(E0107),防止隐式名称重名或变量隐藏。 |
auto label: void = loop { ... } | Labeled Loop 声明与长跳转 | 绝对 Void 坍缩与特化放行:Toka 1.0 的 loop 绝对隐式坍缩为 void。如果在中端检测到它被用作表达式带值返回(即 ExpectedType != "void" 且 isReceiver 为真),会触发类型收紧拦截(E0406)。但为了支持多层嵌套循环的 Labeled Loop 与长跳转,特设 auto label: void = loop { ... } 这一合法写法:将变量名作为 loop 的 label,内部通过 break to label 穿透长跳。中端对此 isReceiver 且期待类型为 void 的情况进行了特化放行,后端 LLVM 实现零 cost 内存分配消除(消除 alloca),捍卫绝对零成本抽象。 |
match val { ... } | 模式匹配 | 强制穷尽检查 (Exhaustive),如果有遗漏需用 _ => 通配符 |
auto val = res! | 错误传递 (解包) | 后缀感叹号 ! 仅用于 Result 或 Option 结构。遇到 Err/None 时会自动提早 return 并清理存活局部值;成功则移出有效负载。不同错误类型必须有一步显式 @ErrorInto<Target> 转换。该操作消费原容器。 |
auto Variant(x) = 或者 match 分支 | 解构的复制语义(纯数据) | 铁律:解构和 match 匹配分支的绑定默认具备复制 (Copy) 语义。这仅对纯数据(如 i32)合法且安全。 |
auto Variant(&x) = 或者 match 分支 | 强制资源对象仅允许借用解构 | 铁律 (防深拷贝/内存泄漏机制):对于带有生命周期托管(Drop / @Encap)的资源(如 string),编译器会直接封杀阻止其裸解构(auto Ok(msg))。你必须显式戴上借用帽 &(如 auto Ok(&msg))以引用的形态读写原物理内存!这样彻底杜绝了因隐式海量深拷贝暴雪或非法浅拷贝引发的双重释放崩溃。 |
auto Variant | 无参解构 | 对于无负荷状态(如 None),直接写名字即可 |
auto Point(val = .y, ..) = p | 具名解构声明 | 铁律:Toka 支持在 auto 解构声明中使用具名绑定。【Struct 具名硬性铁律】自 v0.9.7-04 起,结构体(Struct)的解构声明已彻底废除位置形式,必须 100% 具名!支持极致安全的“同名省略”简写语法:如 auto Point(x, y) = p 完美等效于 auto Point(x = .x, y = .y) = p。 任何对结构体的位置解构均被编译器严格拦截并报错。未列出的字段若无默认值,必须显式以 .. 省略,否则抛出 E042C。对非结构体(如 Tagged Enum)进行具名解构抛出 E0421。严格形态学对齐 (E0448):若被解构的字段带有指针/借用修饰符(如 &s、^p),则键名与值绑定均必须显式佩戴对应的修饰符帽子(如 &s_val = .&s),否则抛出 E0448。 |
match p { Point(val = .y, ..) => ... } | 具名模式匹配 | 铁律:与具名解构一致,在 match 分支中匹配 Shape 具名结构。结构体模式匹配已彻底废除位置形式,必须 100% 具名(或同名省略简写)。支持使用 .. 缺省忽略未提及的字段。同样受严格形态学校验(E0448)制约,带有形态修饰的成员必须在模式匹配时也显式戴帽。 |
.. 和 _ 的大一统缺省 | 缺省与忽略语法大一统 | 铁律:列表初始化、解构声明和 match 模式匹配中的 .. 和 _ 达成大一统,均表示缺省/忽略之意。其中 .. 表示“省略其余未指定的字段”,而 _ 用于单字段的通配与忽略(例如在 Enum 位置解构中忽略特定位置 auto Option::Some(_),或者在 Struct 具名解构中将特定字段忽略 auto Point(_ = .x, ..) = p)。 |
| 特殊机制 | ||
unsafe { ... } | 不安全块 | 解引用原始指针、调用外部 C 函数必须在此块内 |
impl Type@Encap { ... } | 封装/析构 | 定义私有字段和唯一的 drop 析构函数。复制必须由普通 impl Type { pub fn clone(...) -> Type { ... } } 中的显式方法提供;未定义该方法即不可复制。 |
return x | 函数返回 | 返回本地变量时自动移动 (Move),不发生拷贝析构 |
| 成员访问 | ||
obj.field | 成员 Soul 访问 | 访问结构体字段的实体视图或调用方法。若字段本身是指针形态,则这里表达的是指针指向的 Soul 视图,不是字段 Handle 身份。 |
tuple.0已彻底废除 | 匿名记录访问 | 【已废除】元组已废除。 现用于访问轻量匿名具名记录的字段值。 |
arr[i] | 数组索引 | 访问数组或切片元素 |
p.field | 指针成员访问 | 推荐:摘帽后直接点号访问 (自动解引用) |
obj.*field | 读指针成员 | 读取结构体中的指针字段本身 (Handle) |
obj.field | 读指针指向的灵魂数据 | 读取结构体中的指针指向的数据字段本身 (Soul)。对 morphic 字段而言,这是显式请求 Soul 视图;无约束通用容器通常不应这样访问,因为元素可能是指针,也可能是实体。 |
obj.'field | 读 morphic 成员身份 | 对声明为 'field: T 的形态泛型成员,obj.'field 表示保留抽象形态身份访问:在泛型内部它仍是 'T,不能假定它有帽或无帽,也不能自然解引用为 Soul。通用容器内部搬运、返回、cede 元素时应使用这种访问,避免强制 Soul collapse。只有在外部写出具体实例化参数(如 Box<^Node> 或 Box<Node>)后,编译器才可由 <> 参数显化真实帽子或无帽。 |
| 泛型 (Generics) | ||
shape Box<T> | 刚性泛型参数 (Rigid) | T 泛指实体类型(Soul)。不可传递任何指针类型(如 ^i32 会导致 E0604 错误)。 |
shape Box<'T> | 形态泛型参数 (Morphic) | 'T 带有单引号前缀,表示该泛型允许并保留指针形态(如 ^、~、&)。可以理解为 'T 是一组泛型参数表达 (', T):' 是形态保留开关,T 是类型参数本体。标准库容器和通道机制(如 Vec<'T>)均基于此实现以安全承载指针。 |
fn foo<'T>() | 泛型函数声明 | 函数同样支持 'T 形态泛型推导,可与普通刚性泛型互用。 |
fn id<'T>('x: T) -> 'T { return 'x } | 形态泛型值绑定 | 绑定位置铁律:当形态泛型用于声明一个值、参数或字段时,单引号必须写在绑定名左侧,类型侧只写裸 T。也就是说参数写 'x: T,局部变量写 auto 'x: T,Shape 成员写 'data: T。普通变量后续也引用带引号的绑定名,如 return 'x。Shape 成员在容器内部保留抽象形态身份时写 obj.'data。 |
Vec<'T> / Option<'T> / -> 'T / as *'T / sizeof('T) | 纯类型位置 | 纯类型位置仍然写 'T:泛型实参、返回类型、cast 目标、sizeof 等没有绑定名的地方,'T 仍是合法且必要的形态类型表达。不要把这条规则误用到参数、字段、局部变量等绑定位置。 |
| 文本与字节双轨物理体系 | ||
auto s = s"Hello" | 只读文本视图 (str) | Txt & Byte 双轨意图对齐:str (16字节) 为包含 raw *char 与 len 的 UTF-8 视图。1. str.byte_count() -> usize:获取底层物理字节数 ();2. str.count() -> usize:获取逻辑字符数 ( 遍历);3. str.slice(start: i32, end: i32) -> str:安全逻辑字符切片;4. str.at(idx: usize) -> Option<Char32>:获取逻辑字符 ();5. str.as_bytes() -> bytes:0开销降级为二进制物理字节视图;6. str.chars() -> Cursor:获得正向码点游标。 |
auto b = bytes(...) | 只读字节视图 (bytes) | 物理二进制意图度量:bytes (16字节) 用于承载底层原始字节流。1. bytes.size() -> usize:物理尺寸 ();2. bytes.slice_bytes(start: usize, end: usize) -> bytes:物理切片;3. bytes.at(idx: usize) -> Option<byte>:获取物理字节;4. bytes.try_to_str() -> Result<str, UnicodeError>:0拷贝严格文本晋升;5. bytes.to_str_lossy() -> string:堆分配容错文本晋升。 |
auto s# = string::from("...") | 拥有型可变文本 (string) | 唯一可变文本容器:string (24字节) 在堆上分配,是唯一的动态可变文本容器。1. push_str(s: str) / push_char(c: Char32) / push_ascii(c: byte):文本与码点追加;2. pop_codepoint() -> Option<Char32>:栈解耦零别名安全退格;3. as_str() / c_str() / capacity():视图获取与物理暴露。 |
iter#.next() / iter#.rewind(k) | 消灭反向字符迭代 (Cursor) | 物理文本一向性宪法:UTF-8 是单向图,Toka 彻底禁止 任何形式的隐式反向字符迭代(废除 reverse_chars())。如果需要回溯,必须使用 Cursor 游标并在必要时使用 rewind(k) 显式回拨游标,将真实物理开销显式交还开发者。 |
常见误区与铁律 (AI 必读)
| 错误写法 (Wrong) | 正确写法 (Right) | 原因/铁律 |
|---|---|---|
let x = 1 / var x = 1 / for let x in iter | auto x = 1 / for auto x in iter | 坚决无 let 钢印:Toka 全局绝对没有任何 let (或 var) 关键字(包括 for 循环和变量声明),一律强制使用 auto! |
while cond { ... } | loop cond { ... } | 超度 While:Toka 1.0 已将 while 语法连根拔起并彻底超度,大一统为 loop。如果误用 while 编译器会温情拦截并报错指导(E0107)。 |
for x in iter { ... } | for auto x in iter { ... } | 迭代绑定铁律:for 循环的迭代项绑定必须且仅能佩戴 auto 关键字(可变迭代则为 for auto x#,引用为 for auto &x),绝对不支持裸标识符迭代声明,防止变量隐式覆盖的混乱。 |
auto val = loop { ... break 42 } | loop { ... } (通过外部可变变量传递状态) | 控制流绝对 Void 坍缩:为严防控制流类型断裂,Toka 1.0 的 loop 纯净化为绝对 Void 坍缩,break 不允许带值(取消 or 从句),不作为带值表达式。若需返回状态,请在外部声明 auto res# = init_val 并在循环中重写。 |
func name() | fn name() | 铁律:函数关键字是 fn。 |
fn() { ... }[cede a] { ... } | { => ... } { [cede a] => ... } | 铁律:Toka 闭包只使用内联花括号 {},已废弃 fn 匿名前缀和外置捕获;且遇见零参数闭包必须使用 { => ... } 防歧义。便利性:闭包边界和泛型边界后现在支持隐式分号结尾,允许流式换行,不会再报 E0101 缺失分号。 |
shape Point { x: i32 } | shape Point(x: i32) | 铁律:Shape 定义必须使用圆括号 ()。 |
auto p = Point{x=1} | auto p = Point(x=1) | 铁律:初始化也必须使用圆括号 ()。 |
println("Hello") | import std/io::printlnprintln("Hello") | println 不是内置关键字,必须从标准库导入。 |
println("x=%d, y=%s", 1, s) | println("x={}, y={}", 1, s) | 铁律:Toka 格式化输出 (包括 println、字符串插值等) 严格使用前沿的 {} 占位语法,绝对不要使用 C 语言的 %d、%s 标志。零拷贝与格式化新特性: 1. 对于 string 和 str,println 已实装真正的零拷贝特化 (Zero-Copy)。2. 支持全新的精细格式化语法 (Mini Formatting Language),语法形式为 {:指令}。数字支持补零(如 {:04})、十六进制(如 {:x})、浮点精度(如 {:.2},注意无需冗余类型符 f)。3. 此机制通过标准库的 @ToFormat 契约(to_string_fmt(self, fmt: str) -> string)实现,支持自定义 Shape 自行解析任何控制符(如 {:json})。 |
auto s: str = "abc" | auto s = s"abc" | 视角字串铁律:普通 "[文本]" 推导为 *char。若需要携带长度的胖形态 str,务必使用 s"..." 语法。新特性:当函数/方法明确期望接收 str 时,你可以直接传入 string 变量(编译器会自动且零成本地插入 as_str() 降级为视图投影,也就是 Auto-Deref),彻底摆脱手动 .as_str() 的繁琐体验。 |
str1 + str2 | auto s = string::from(s1); s.push_str(s2) | 禁止隐式堆分配铁律:Toka 不支持 使用 + 拼接字符串。因为 + 会导致隐式的堆内存分配,这违背了 Toka 的核心系统级哲学。请显式使用 string 的 push_str() (完美兼容传入 str 或 string) 或 println/print 进行格式化。 |
auto p = User("Alice", 1) | auto p = User(name = "Alice", id = 1) | 【强制铁律】实例化结构体(Shape)时必须使用具名参数 (Named Parameters) 进行赋值。按位置顺序初始化已彻底废除并强行拦截抛出 E042A。 |
x.modify() (如果定义需要 self#) | x#.modify() | 修改自身的方法必须在调用者上加 #,哪怕 x 本身是可变的。 |
*p (想解引用) | p | 铁律 (Hat Principle):Toka 中裸名 p 即为灵魂 (Soul),自动解引用。*p 是戴帽操作,得到指针本身 (Handle)。与 C++ 相反。 |
*p = val (想写内存) | p = val | 铁律:写内存也是操作 Soul,直接用 p。*p 是操作 Handle (重绑定)。 |
p = q (想重绑定) | *p = *q | 铁律:重绑定必须操作双方的 Handle (戴帽),如果不戴帽就是把 q 的值写给 p 指向的内存。注:此处的 * 为泛指帽子,实际可以是 *,^,~,& |
^p->field | p.field | abolished: 箭头操作符已废除,直接用点号访问成员。 |
main() {} | main() -> i32 { return 0 } | main 必须返回 i32,通常显式返回 0 (某些上下文可隐式,但测试中推荐显式)。 |
shape B<T> 用 B<^i32> 实例化 | shape B<'T> | 铁律:若需传给泛型一个 指针类型(带帽子),必须将泛型参数声明为 'T,普通的 T 是绝对禁止接受指针形态参数的。 |
fn id<'T>(x: 'T) -> 'T | fn id<'T>('x: T) -> 'T | 形态泛型绑定铁律:参数、字段、局部变量等“带名字的绑定位置”必须把量子帽写在绑定名侧。类型侧的 : 'T 旧写法已被编译器拒绝并提示改为 'x: T。 |
shape Box<'T>('data: 'T) | shape Box<'T>('data: T) | Shape 成员同理:'data 表示该字段绑定保留形态,T 表示字段的泛型实体本体。不要写成 'data: 'T,否则重复表达同一个形态开关。容器实现中若要保持元素的抽象形态身份,应访问 box.'data;box.data 是请求 Soul 视图,不是通用搬运接口。真实帽子或无帽只能在外部 Box<...> 实例化后确定。 |
auto x: 'T = v | auto 'x: T = v | 局部变量同理:绑定名负责承载形态豁免,类型名保持裸泛型参数。后续使用该变量时也写 'x,如 return 'x 或 cede 'x。 |
^arr[i] 或 arr.^at(i) | arr[i] 或 arr.at(i) | 铁律: arr[i] 严格等价于 arr.at(i)。函数/方法调用和下标访问自带返回签名(包含帽子形态),绝对禁止在调用表达式上戴帽。若返回值为指针,交接时带名字的左值接收方必须显式戴帽(如 auto ^p = arr[i] 或 ^p = foo()),除非左值也是免写帽的签名表达式且形态一致(如 arr_A[i] = arr_B[i])。 |
~m.a / *p.field | m.a (读实体)m.~a (取指针Handle) | 铁律 (链式访问单帽置尾原则):点号 . 的左侧必须是灵魂 (Soul,也就是实体结构),绝不能是戴帽子的 Handle形态。因此,在点号链式访问中,所有中间环节必须是裸名。如果你试图操作字段的指针本身,帽子只能紧挨在最后一环的成员名之前 (如 a.b.~c)。向中间环节戴帽(如 ~m.a / a.~b.c)是严重错误。 |
extern fn libc_free(*p: void)unsafe { *p = null } | extern fn libc_free(nul *p: void)auto nul *p = null:nul *void | raw pointer 零地址铁律:*T 表示非零 raw 地址,nul *T 才表示物理上可能为零。即使在 unsafe 中,也不再允许用 null 构造、赋值、返回或比较普通 *T;从 nul *T 取得 *T 必须显式 .unwrap()。FFI 声明必须诚实标注 may-zero 参数与返回值。 |
impl Foo@Encap { fn drop(self#) {} } | impl Foo { pub fn clone(self) -> Foo { ... } }(仅在需要显式复制时) | 铁律:@Encap 仅定义封装与析构边界,必须有唯一的 drop hook。资源默认不能复制;如需复制,在普通 impl 中提供显式 clone 方法。不存在 clone = delete 或隐式复制规则。 |
shape U(as string | as i32) (裸联合体包含受控资源)shape U(as i32 | as f32) (声明/使用裸联合体) | @Encap 析构使用 Tagged Enum 或 等宽占位类型 + core/mem::bit_cast | Untagged Union 内直接存放包含 @Encap/drop 生命周期管理的类型(如 string、Vec)。因运行时无从知晓当前变体,放任自动生成 drop 会导致严重的段错误。【已废除】Toka 已彻底废除裸 Union (Bare Union),全面杜绝类型双关未定义行为(UB)。 C-FFI 交互含有 union 的结构体(如 epoll_event)时,最佳实践是声明等宽 of 无符号整数或字节数组作为占位符,在系统边界通过 bit_cast 重新解释。🚨 C-FFI ABI 调用约定(Calling Convention)致命陷阱:与 C 语言交互包含 Union 的结构体时,请务必通过指针(Pointer/Reference)传递,严禁跨越 FFI 边界按值(Pass-by-Value)传递等宽占位符! |
auto x = cede map.inner | auto x = cede map (整体交出) | 铁律 (Partial Move 局部夺舍禁令):绝对禁止使用 cede 强行剥夺带有生命周期托管的宿主对象(如包含 @Encap 的复杂容器)内部属性!强行拆散闭环会导致宿主进入不可恢复的报废污染状态,破坏安全析构链。 |
auto Point(val = .x, y) / Point(val = .x, y => ...) | auto Point(val = .x, another_val = .y) 或者 Point(1, 2) | 铁律 (E0105: 混合解构禁令):严禁在列表初始化、解构声明和模式匹配中混用具名与位置参数/字段!要么全部使用位置方式,要么全部使用具名方式,不能参杂。(注:自 v0.9.7-04 起,结构体的实例化、解构声明和模式匹配已彻底废除位置形式,必须 100% 具名,不再存在混合的可能性;本条仅对支持位置形式的 Enum 模式匹配/解构与未来的位置类型有效,严禁在 Enum 中混用位置与具名(Enum 不支持具名,混用会抛出 E0105/E0421))。 |
auto Point(val = .y) (缺省部分字段且无 ..) | auto Point(val = .y, ..) | 铁律 (E042C: 完备性缺失):具名解构与模式匹配时,未列出的且没有默认值的字段必须通过 .. 进行显式省略。若没有 .. 且有未列出的非默认值字段,则会精准抛出 E042C。 |
auto Tuple(val = .x, ..)已彻底废除 | auto Tuple(val, _)元组 (Tuple) 与位置解构 | E0421。【已废除】Toka 已彻底从编译器中剥离元组语法。 所有聚合数据必须具名,且 Struct 不再支持位置解构,必须使用具名解构。对非结构体(如 Tagged Enum)进行具名解构将抛出 E0421。 |
auto RefPair(s_val = .s)(解构带有指针形态 of 字段 &s) | auto RefPair(&s_val = .&s) | 铁律 (E0448: 指针形态错配拦截):为了捍卫“帽子法则”底线,严防隐式解引用越权,当解构声明或 match 模式匹配含有 &、^、~、* 等形态修饰的成员时,解构绑定的键名与值绑定均必须显式且一致地戴帽!若将 &s 剥去帽子写成 s,编译器会抛出 E0448 错误进行拦截。 |
| 公共 API 安全红线 (Safety Redline) | ||
pub fn foo(*p: char) pub shape S(*ptr: i32) | fn unsafe_foo(*p: char) shape UnsafeS(*ptr: i32) | 安全红线铁律 (E0480/E0481/E0482):公共 API 签名(参数/返回值)与公共 Shape 字段严禁直接暴露裸指针(如 *char / *T)或不安全类型。违规者将在编译期被高压电网强杀阻断。唯一豁免场景是:函数名以 unsafe_、raw_ 或 __ 开头;结构体名以 Unsafe 或 Raw 开头;或定义位于物理的 lib/、prelude、tests/pass/、build.tk 中(标准库与测试自愈豁免)。 |
待办事项 / 核心架构坏味道记录 (TODO)
| 待办主题 | 坏味道说明 | 拟重构方案 |
|---|---|---|
| 1. 编译器与标准库强耦合解耦 (Smell #3) [已解决] | println / print 等低级 LLVM IR 组装过程曾硬性绑定标准库特定名称 of ABI 函数(如 String_from、String_push_str 等)。 | 已重构解决:新版 print / println 采用零开销展开生成(Unrolled Codegen)技术。对于所有基本类型(i32, f64, bool, char 等)及 str,直接编译生成 toka_print_xxx / printf / putchar 调用,与 std/string 彻底解耦且无任何堆分配开销。仅对实现 to_string/to_string_fmt 的自定义 Struct 保留轻量 ABI 调用以提取和析构临时 string。 |
| 2. 基本类型“捕获语义”物理 ABI 澄清 (Smell #5) | 物理层(Codegen)针对非可变基本类型(如 i32)优化为了普通 LLVM Value 传值拷贝,这与 Toka 宣称的“一切皆捕获(隐式引用传递)”的逻辑语义在物理层不一致,存在 FFI 灰色地带。 | 保持现有高效的传值优化,但在文档中明确区分 逻辑语义(捕获) 与 物理 ABI 优化(基本类型传值、聚合类型引用),并在 FFI 规范文档中进行严谨的物理层澄清。 |