# Grammar Reference This is the formal surface syntax of Deft. It's organised in the order an interpreter sees the language: lexical elements, statements, expressions, patterns, and types. For narrative coverage see [Syntax and Style](syntax-and-style); for special forms see [Metaprogramming](metaprogramming). ## Lexical Elements ### Whitespace and comments Whitespace separates tokens. `#` starts a single-line comment that runs to end of line. `#|` opens a block comment that runs to the matching `|#`, spans multiple lines, and nests.
 # This is a comment
echo "hi" # trailing comment
#| Block comments nest: #| inner |# and span lines |# 
### Identifiers Identifiers are made of letters, digits, `-`, `_`, `?`, `!`, `=`, and `<`, `>`. They must not start with a digit. Conventions: - `kebab-case` for variables and functions (`my-fn`, `user-name`) - `PascalCase` for type names (`%User`, `%Point`) - `X?` suffix for predicates (`empty?`, `is-admin?` — see [Syntax and Style](syntax-and-style)) - `!` suffix for side-effecting functions or setter macros (`reset!`, `swap!`) ### Numbers Decimal integers and floats: `42`, `3.14`, `-7`, `1e10`. Hex `0x`, octal `0o`, and binary `0b` prefixes are supported for integers. ### Strings | Form | Purpose | |---|---| | `"..."` | Double-quoted, supports interpolation | | `'...'` | Single-quoted, **no** interpolation (literal) | | `` `...` `` | Backtick, supports interpolation (multiline) | Interpolation forms inside interpolation-supporting strings: | Form | Expands to | |---|---| | `$name` | Variable value | | `$name~field` | Variable field chain | | `$name(args)` | Postfix call | | `$(expr)` | Infix expression (bare identifier also works as var ref) | | `$[func args]` | Prefix call | | `\$` | Literal `$` | Literal `[`, `]`, `{`, `}`, `(`, `)` are safe without escaping. ### Keywords `:name` keywords are lightweight interned values used as map keys, enum-like tags, and protocol method markers. They support `kebab-case` and namespaced (`:ns/name`) forms. ### Booleans and nil `true`, `false`, `nil`. ### Operators | Class | Operators | |---|---| | Arithmetic | `+ - * / mod` | | Comparison | `== != < > <= >=` | | Boolean | `and or` | | Unary | `- !` (prefix, tight binding) | | Bitwise | `& \| ^ << >>` | | Range | `..` | | Spread | `..$expr` (prefix calls, `@{...}` literals) | ### Spread `..$expr` splices a list's elements into a prefix call's arguments or into a `@{...}` vector literal:
 def xs @{1 2 3}
[f ..$xs]        # passes 1, 2, 3 as three args
@{0 ..$xs 4}     # => @{0 1 2 3 4} 
In list patterns `..$name` collects the tail of the matched list (see [Patterns](#patterns)). ## Statements A script is a sequence of statements. Statements are separated by newlines or semicolons. The value of a block is the value of its last statement. ### Definition statements ```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 "}" ``` ### Control-flow statements ```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 ``` ### Compile-time forms These bracket forms are handled by the compiler during compilation, not at runtime: ```ebnf compile-form ::= "[" "compile-when" expr expr "]" # cond evaluated at compile time ``` `[compile-when cond body]` evaluates `cond` through the VM at compile time. If truthy, `body` is compiled in place; if falsy, `body` is discarded (emits `push_null`). Used for zero-cost level filtering in logging macros and similar compile-time configuration. See [Macros](macros#compile-time-conditionals-compile-when). ### Module statements ```ebnf module-stmt ::= "import" string "as" name | "from" string "import" import-list | "implements" name | "in-ns" string ``` ### Decorators Inside a definition body, `@key value` lines attach metadata: ```ebnf decorator ::= "@" name value ``` ## Expressions ### Call forms ```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 ")" ``` #### Critical rule Bare parentheses `(...)` group **infix expressions only** — never function calls. `(trim $x)` is a syntax error. ### Pipelines ```ebnf pipeline ::= expr ("|>" step)+ ``` The left side's value is passed as the implicit final argument of each step. ### Literals ```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)* "}" ``` ### Field access ```ebnf field-access ::= primary ("~" name)+ ``` `$m~field~subfield` chains. Use `~?field` for optional access that returns `nil` if the field is missing (rather than throwing). ### Index/slice access ```ebnf index-access ::= primary "(" range-expr ")" range-expr ::= number ".." number | ".." number | number ".." ``` `"hello"(0..3)` slices from 0 to 3 exclusive. `xs(0..)` slices from 0 to end. ### Closures ```ebnf closure ::= "{" ["|" params "|"] body "}" params ::= param (","? param)* param ::= (name | "@" name) [type-hint] ["=" default] ``` A `param` of the form `@name` is a rest-collector: it gathers any remaining call args into a list. The `@` mirrors the `@{...}` vector literal — it must be the last parameter. The name after `@` is bound verbatim (`@rest`, `@items`, `@args` are all fine). A closure with no `|params|` binds its single argument to `$it`. ## Patterns Used in `match` and `defmethod` argument destructuring. ```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 ``` Field patterns match keys and bind values: ```ebnf field-pattern ::= keyword pattern ``` List patterns support a tail collector via `| $rest` or `..$rest`: ```ebnf list-pattern ::= pattern | pattern ("|" | "..") "$" name ``` ## Type Hints ```ebnf type-hint ::= "^" type-name type-name ::= name # "string", "number", or a deftype name ``` Used on parameters and on typed `set`. See [Functions](functions) and [Types and Protocols](types-and-protocols). ## Type Definitions `deftype` field specs use a map of options per field:
 deftype User %{
    :name  %{:type :string  :required true}
    :age   %{:type :number  :default 0}
    :roles %{:type :set     :default ^{:user}}
} 
Field spec options: `:type` (required), `:required`, `:default`, `:description`. ## Precedence (Infix) Highest to lowest, inside `(...)` expressions: 1. Function application, field access, indexing 2. `* / mod` 3. `+ -` 4. Bitwise `& | ^ << >>` 4. Comparison `== != < > <= >=` 5. `and or` 6. `..` (range) Use nested parentheses to override. ## Keywords Reserved By The Compiler These names cannot be reused as variable names: ``` 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 ``` Note: `defgen`, `defwait`, `defgroup`, `defasync`, `chan`, `<-`, `select` (channel form), `receive`, and `send` appear in some older documentation but are **not** part of the current language. See [Concurrency](concurrency) for the supported primitives. ## Complete Example
 #!/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]]