表达式与语句
本文档收敛 Dujie 的字面量、控制流、运算、推导边界、语句模型和语言级错误行为。
总体原则
- Dujie 采用表达式优先的风格
if和 block 都可以作为表达式- 赋值、
break、continue属于语句,不参与普通表达式求值 unit是正式类型,用于表示“无值”
字面量
基础字面量
intfloatboolstringrune
当前公开整数类型只有 int。
string
- 普通字符串使用
"..."表示 - 支持转义:
\\、\"、\n、\r、\t、\u{...} string是文本类型,不是字节数组
rune
rune使用'a'形式- 支持转义:
\\、\'、\n、\r、\t、\u{...} - 转义展开后必须恰好表示一个字符,否则是词法错误
插值字符串
- 插值字符串使用反引号:
`hello ${name}` - 插值片段写作
${expr} - 整体结果类型是
string - 插值允许任意类型
插值使用的是展示语义,不是序列化语义:
- 基础类型按自然文本形式展示
none在插值上下文里等价于空字符串some(v)等价于插入v的展示结果list / map / struct / widget / iter / func使用统一摘要展示,不展开结构
容器与聚合字面量
list
- 语法:
[]、[1, 2, 3] - 元素必须同类型
- 不做
int/float混合推导 T可在上下文需要时提升到opt<T>
例如:
[1, 2, 3]
["a", none]
map
- 语法:
{ "a": 1, "b": 2 } - key 必须同类型
- value 必须同类型
mapkey 当前只允许:intboolrunestring
第一阶段不引入联合类型。
struct 构造
- 语法:
Person { name: "snow", age: 18 } - 这是具名类型构造,不是匿名对象字面量
- 所有字段必须显式提供
- 未知字段、重复字段、缺字段都是编译错误
struct
struct 是名义类型,不是结构类型。
规则:
- 只支持具名字段
- 字段声明写作
name: Type - 不支持 tuple struct
- 不支持按数字索引访问
- 字段顺序不构成类型语义
- 第一阶段不支持字段默认值
类型兼容:
- 两个
struct即使字段完全一致,只要名字不同,就是不同类型 - 不支持
struct和map之间自动互转
字段类型:
- 可包含基础类型
- 可包含
list / map / opt - 可包含其他
struct - 可包含
widget - 第一阶段不建议
iter<T>作为字段类型
递归:
- 允许通过
opt<T>、list<T>、map<K, T>间接递归 - 不允许字段直接按值引用自身类型
多行格式与尾逗号
- 单行
list / map / struct可带尾逗号,也可不带 - 多行
list / map / struct必须带尾逗号
类型推导边界
let x = expr只在expr类型唯一时允许推导[]、{}、none不能独立推导类型if两个分支类型必须一致- 空容器和
none需要依赖上下文或显式标注
例如:
let a = 1
let b: list<int> = []
let c: opt<string> = none
opt<T>
opt<T> 的语言级构造和判定:
nonesome(x)x is nonex is some(v)
规则:
opt<T>不支持比较运算- 参数类型为
opt<T>时允许省略 T可自动提升为some(T)
any
any 是边界类型,不是万能类型。
当前语义:
T -> any允许any -> T不做隐式恢复- 第一阶段使用
is做显式收窄
支持的最小 is 形式:
x is T
x is T(v)
当前稳定支持的收窄目标首先是基础类型:
intfloatboolrunestring
表达式
匿名函数表达式
匿名函数是表达式。
形式与普通函数相同,只是没有名字:
func(x: int) -> string {
`#${x}`
}
当前边界:
- 允许捕获外部变量
- 主要用于目标参数类型已知的回调位置
- 不作为完整一等函数值系统单独展开
if 表达式
if是表达式if和else都必须存在- 条件必须是
bool - 两个分支结果类型必须一致
block 表达式
- block 的结果类型由尾表达式决定
- 如果没有尾表达式,则结果类型为
unit
例如:
let x = {
let a = 1;
a + 1
}
语句
let / var
let:不可变绑定var:可变绑定
绑定可变性与更新规则:
let绑定不能重新赋值let绑定也不能做原地更新var绑定允许重新赋值和原地更新
赋值
=是语句,不是表达式- 左侧必须是可写左值
- 当前合法左值:
- 变量名
x xs[i]m[k]p.name
- 变量名
容器更新
list 支持:
xs[i]xs[i] = vpush(v)pop()remove(i)clear()len()
map 支持:
m[k]m[k] = vremove(k)clear()contains(k)len()
统一规则:
- 更新操作只能作用在
var绑定的可写左值上 - 临时值不能更新
- 会修改接收者的方法只能对可写目标调用
运行时行为:
list[i]越界时paniclist[i] = v越界时paniclist.pop()对空列表调用时paniclist.remove(i)越界时panicmap[k]缺 key 时panicmap[k] = v缺 key 时直接插入map.remove(k)缺 key 时panic
return
- 返回类型必须与函数声明一致
unit函数可写return;- 非
unit函数不能写裸return;
break / continue
- 都是语句,不是表达式
- 只允许出现在
for循环体中
for in
形式:
for x in expr {
...
}
当前可迭代类型:
list<T>iter<T>
当前不支持:
for (i, x) in xsfor (k, v) in map- 字符串迭代
- 解构绑定
panic
panic 是内建能力,用于主动中止执行。
当前规则:
- 形式:
panic(message: string) - 参数只接受
string - 它不会正常返回
- 在类型检查上,允许出现在任意需要值的位置
运算
算术
+ - * / %只对数值类型开放- 不支持
int/float混合运算 int op int -> intfloat op float -> float%只支持intint / int -> intfloat / float -> float- 一元负号支持
int和float
比较
支持 == / !=:
intfloatboolrunestring
支持 < <= > >=:
intfloatrunestring
不支持比较:
opt<T>structlistmapiterwidgetany
逻辑
!只接受bool&&和||只接受bool&&/||使用短路语义
显式转换
转换语法采用 Type(expr)。
第一阶段只支持:
float(int_expr)int(float_expr)
其中 float -> int 按向零截断处理。
成员访问、下标与调用
成员访问 x.y
- 只对
struct和语言内建对象开放 struct字段不存在时,编译错误- 第一阶段不支持
map的点访问
下标访问 x[y]
list<T>[int] -> Tmap<K, V>[K] -> V
运行时行为:
list越界时panicmap缺 key 时panic
调用 f(...)
- 调用结果类型由函数声明返回类型决定
- 参数匹配规则见《函数与调用》
iter<T>
iter<T> 是遍历用的中间类型,不是常规稳定容器。
允许:
- 作为函数参数或返回值
- 用于
for in - 通过
collect()显式收集为list<T>
当前核心操作:
mapfiltercollect
运算符优先级
从高到低:
后缀:x.y、x[y]、f(...)
一元:-x、!x
乘法:* / %
加法:+ -
比较:< <= > >=
相等:== !=
逻辑与:&&
逻辑或:||
if 表达式
约束:
- 不支持链式比较
- 赋值不进入普通表达式优先级体系
- 括号可显式改变优先级
语言级错误模型
编译错误
能在编译期确定的问题直接报错,例如:
- 类型不匹配
- 未知变量、字段、导入符号
- 调用参数错误
- 对
let做更新 - 非法左值赋值
break / continue出现在循环外- 非法字面量和非法转义
运行时 panic
依赖运行时值的问题进入 panic,例如:
map[k]缺 keylist[i]越界- 空列表
pop - 用户显式调用
panic("...")
总原则:
- 能在编译期确定的问题,不留到运行时
- 只有依赖运行时具体值的问题,才进入
panic