# 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]]