# 语法参考
这是 Deft 的形式化表层语法。它按照解释器看待语言的顺序组织:词法元素、语句、表达式、模式与类型。叙述性说明见语法与风格;特殊形式见元编程。
## 词法元素
### 空白与注释
空白用于分隔词法单元。`#` 开始单行注释,持续到行尾。`#|` 开启块注释,持续到匹配的 `|#`,可跨多行,且可嵌套。
# This is a comment echo "hi" # trailing comment #| Block comments nest: #| inner |# and span lines |#
### 标识符
标识符由字母、数字、`-`、`_`、`?`、`!`、`=` 以及 `<`、`>` 组成,且不能以数字开头。约定:
- 变量与函数使用 `kebab-case`(`my-fn`、`user-name`)
- 类型名使用 `PascalCase`(`%User`、`%Point`)
- 谓词使用 `X?` 后缀(`empty?`、`is-admin?` —— 见语法与风格)
- 有副作用的函数或 setter 宏使用 `!` 后缀(`reset!`、`swap!`)
### 数字
十进制整数与浮点数:`42`、`3.14`、`-7`、`1e10`。整数支持十六进制 `0x`、八进制 `0o` 和二进制 `0b` 前缀。
### 字符串
| 形式 | 用途 |
|---|---|
| `"..."` | 双引号,支持插值 |
| `'...'` | 单引号,不支持插值(字面量) |
| `` `...` `` | 反引号,支持插值(多行) |
支持插值的字符串中的插值形式:
| 形式 | 展开为 |
|---|---|
| `$name` | 变量值 |
| `$name~field` | 变量字段链 |
| `$name(args)` | 后缀调用 |
| `$(expr)` | 中缀表达式(裸标识符也可作为变量引用) |
| `$[func args]` | 前缀调用 |
| `\$` | 字面量 `$` |
字面量 `[`、`]`、`{`、`}`、`(`、`)` 无需转义即可安全使用。
### 关键字
`:name` 关键字是轻量的驻留值,用作映射键、类枚举标签和协议方法标记。它们支持 `kebab-case` 和命名空间形式(`:ns/name`)。
### 布尔值与 nil
`true`、`false`、`nil`。
### 运算符
| 类别 | 运算符 |
|---|---|
| 算术 | `+ - * / mod` |
| 比较 | `== != < > <= >=` |
| 布尔 | `and or` |
| 一元 | `- !`(前缀,紧绑定) |
| 位运算 | `& \| ^ << >>` |
| 范围 | `..` |
| 展开 | `..$expr`(前缀调用、`@{...}` 字面量) |
### 展开
`..$expr` 将列表的元素拼接进前缀调用的参数,或拼进 `@{...}` 向量字面量:
def xs @{1 2 3} [f ..$xs] # passes 1, 2, 3 as three args @{0 ..$xs 4} # => @{0 1 2 3 4}
在列表模式中,`..$name` 收集所匹配列表的尾部(见模式)。
## 语句
脚本是语句的序列。语句由换行或分号分隔。块的值是其最后一条语句的值。
### 定义语句
```ebnf
definition ::= "def" name [type-hint] value
| "def" name "{" function-body+ "}" # function (multi-arity)
| "def" name "=" value # explicit default form
| "defcoro" name "{" coroutine-body "}"
| "defmacro" name "{" macro-body "}"
| "deftype" name "{" field-spec-map "}"
| "defprotocol" name "{" method-specs "}"
| "defimpl" type protocol "{" methods "}"
| "defmethod" type method-name "{" method-body "}"
| "defsuite" string "{" test-cases "}"
| "deftest" string "{" body "}"
| "defbench" string "{" body "}"
````
### 控制流语句
```ebnf
control-flow ::= "if" cond block ("else" block)?
| "if" "let" pattern "=" expr block ("else" block)?
| "match" expr "{" match-arms "}"
| "for" name ["idx"] "in" expr block
| "while" cond block
| "try" block "catch" name block
| "defer" block
| "return" expr
| "break" | "continue"
| "throw" value
````
### 编译期形式
这些方括号形式由编译器在编译期处理,而非运行时:
```ebnf
compile-form ::= "[" "macro/compile-when" expr expr "]" # cond evaluated at compile time
````
`[macro/compile-when cond body]` 在编译期通过 VM 对 `cond` 求值。若为真值,`body` 就地编译;若为假值,`body` 被丢弃(发出 `push_null`)。用于日志宏中的零开销级别过滤及类似的编译期配置。见宏。
### 模块语句
```ebnf
module-stmt ::= "import" string "as" name
| "from" string "import" import-list
| "implements" name
| "in-ns" string
````
### 装饰器
在定义体内部,`@key value` 行用于附加元数据:
```ebnf
decorator ::= "@" name value
````
## 表达式
### 调用形式
```ebnf
call ::= prefix-call | postfix-call | bare-call | infix-expr
prefix-call ::= "[" name expr* "]" # expr* may include ".." expr spread args
postfix-call ::= primary "(" expr* ")"
bare-call ::= name expr* # only at statement position
infix-expr ::= "(" expr operator expr ")"
````
#### 关键规则
裸圆括号 `(...)` 只对中缀表达式分组——绝不表示函数调用。`(trim $x)` 是语法错误。
### 管道
```ebnf
pipeline ::= expr ("|>" step)+
````
左侧的值作为每一步的隐式最后一个参数传入。
### 字面量
```ebnf
literal ::= number
| string
| keyword
| "true" | "false" | "nil"
| list-literal
| map-literal
| set-literal
| tagged-map-literal
list-literal ::= "@{" expr* "}" # expr* may include ".." expr spreads
map-literal ::= "%{" (key expr)* "}"
set-literal ::= "^{" expr* "}"
tagged-map-literal ::= "%" name "{" (key expr)* "}"
````
### 字段访问
```ebnf
field-access ::= primary ("~" name)+
````
`$m~field~subfield` 支持链式访问。使用 `~?field` 进行可选访问:字段缺失时返回 `nil`(而非抛出异常)。
### 索引/切片访问
```ebnf
index-access ::= primary "(" range-expr ")"
range-expr ::= number ".." number | ".." number
| number ".."
````
`"hello"(0..3)` 从 0 切片到 3(不含 3)。`xs(0..)` 从 0 切片到末尾。
### 闭包
```ebnf
closure ::= "{" ["|" params "|"] body "}"
params ::= param (","? param)*
param ::= (name | "@" name) [type-hint] ["=" default]
````
形如 `@name` 的 `param` 是剩余参数收集器:它把所有剩余的调用参数收集到一个列表中。`@` 与 `@{...}` 向量字面量相呼应——它必须是最后一个参数。`@` 之后的名称按原样绑定(`@rest`、`@items`、`@args` 均可)。
不带 `|params|` 的闭包将其单个参数绑定到 `$it`。
## 模式
用于 `match` 和 `defmethod` 的参数解构。
```ebnf
pattern ::= "_"
| literal
| "$" name # binding
| "%" name "{" field-patterns "}" # tagged map (type pattern)
| "%{" field-patterns "}" # map pattern
| "@{" list-patterns "}" # list pattern
| number ".." number # range
| "(" pattern ("|" pattern)+ ")" # or-pattern
| "not" pattern # negative
| pattern "when" infix-expr # guard
| "#\" regex "\"" # regex
````
字段模式匹配键并绑定值:
```ebnf
field-pattern ::= keyword pattern
````
列表模式通过 `| $rest` 或 `..$rest` 支持尾部收集器:
```ebnf
list-pattern ::= pattern | pattern ("|" | "..") "$" name
````
## 类型标注
```ebnf
type-hint ::= "^" type-name
type-name ::= name # "string", "number", or a deftype name
````
用于参数和带类型的 `set`。见函数与类型与协议。
## 类型定义
`deftype` 的字段规格使用每个字段一个选项映射:
deftype User %{ :name %{:type :string :required true} :age %{:type :number :default 0} :roles %{:type :set :default ^{:user}} }
字段规格选项:`:type`(必需)、`:required`、`:default`、`:description`。
## 优先级(中缀)
在 `(...)` 表达式内部,从高到低:
1. 函数应用、字段访问、索引
2. `* / mod`
3. `+ -`
4. 位运算 `& | ^ << >>`
4. 比较 `== != < > <= >=`
5. `and or`
6. `..`(范围)
使用嵌套括号覆盖优先级。
## 编译器保留的关键字
这些名称不能用作变量名:
```
def defn defcoro deftask defmacro
deftype defprotocol defimpl defmethod
defsuite deftest defbench
if else match for while try catch defer return break continue throw
import from implements in-ns
true false nil
````
注意:`defgen`、`defwait`、`defgroup`、`defasync`、`chan`、`<-`、`select`(通道形式)、`receive` 和 `send` 出现在一些较旧的文档中,但不属于当前语言。支持的原语见并发。
## 完整示例
#!/usr/bin/env deft # # A small script demonstrating most of the surface syntax. deftype User %{ :name %{:type :string :required true} :age %{:type :number :default 0} } defprotocol Greeter %{ greet {} } defimpl User Greeter { greet {|| @doc "Return a greeting for this user." if ($self~age >= 18) { "Hello, $self~name." } else { "Hi, $self~name!" } } } def main {|argv^list| set users @{} for arg in $argv(rest) { $users << %User{ :name $arg :age 30 } } $users |> map { $it~name } |> sort |> each { echo $it } | [main [os/args]]