# 语法参考 这是 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]]