Lowent Manual←↑→

Appendix D — Grammar summary

The normative text is the specification; this appendix is a reference gathering frequently sought shapes. The complete formal grammar is in Annex A (A.10) of the specification.

Clause order of an op head#

[export] [unsafe] [extern] fn|proc <name>
  satisfies · lowdoc                       ① what it is
  vector · priority                        ② character of the whole op
  input <name> comptime …                  ③ translation-time inputs
  input <name> cap … · region …            ④ capability and region inputs
  use <name> <type> .                    ⑤ allocator drawn from
  input <name> <type> .                    ⑥ data inputs
  output <type> .                          ⑦
  effects <atoms…> [via <type>] .          ⑧
  link "<name>" · variadic · asm <machine> ⑨ joining the outside
  access · inplace <written> <read> · invalidates <input> · parallel <name> split · reduce  ⑩ how it touches memory · runs split
  requires [static|debug|assume] <cond> .  ⑪ input conditions
  ensures <cond> .                         ⑫ output promises (ret)
  errors <variant> [<cond>] .              ⑬ failure
  tests … · schedule …                     ⑭
do
  <statements>
end .

An extern op whose body is in C holds the same clauses, in the same order, inside do … end — unsafe extern proc <name> do <clause>* end .. Only clauses go in that block.

Declarations#

module <name> .
use <module> [from "<place>"] .
type <name> <type> .
newtype <name> <type> .
struct <name> do [satisfies <trait> .] [layout packed .] [mmio <address> .] <field> <type> [big|little] [rw|ro|wo] . … end .
enum <name> do <variant> [<field> <type>]… . … end .
trait <name> do <op name> <clauses…> . … end .
contract <name> do requires <cond> . … end .
actor <name> do [satisfies …] state do <field> <type> . … end [failure restart max <n> .] [mailbox bounded <n> .] <op>… end .
build profile <name> .        build tier t0|t1|t2|t3 .        build <mode> .
build option <name> bool|int|choice … default <value>
test <name> [schedule explore_interleavings [limit <n>] .] do expect <cond> . … end .

Statements#

Below, <expr>, <value> and <cond> stand for a value with its own stop — a. for a variable, add a. 1 . for a call, 7 for a literal. The . at the end of a line is the stop that closes that statement.

let <name> [use <allocator>. | keep <allocator>.] <type> <expr> [else <leaving statement>] .
var <name> <type> <expr> .
let <name> <type> <expr> else do … end .
let <name> <type> <expr> else error <name> do … end .
set <place> <expr> .                      (place = <name>. · field … . · idx … .)
if <cond> do … end [else if <cond> do … end]… [else do … end] .
while <cond> do … end .
for <name> [mut] <slice> [if <cond>] do … end .
repeat <name> <type> <count> [if <cond>] do … end .
range <name> <type> <from> <to> [step <stride>] [if <cond>] do … end .
cycle <name> <type> <first> while <cond> next <next value> [if <cond>] do … end .
guard <cond> else <leaving statement> .
match <value> do case <pattern> [when <cond>] do … end . … end .
return [<expr>] .    break .    continue .    panic "<text>" .
drop <name>. .
region <name> stack|frame|arena|static|heap|mmap|disk|device do … end .
task_group [cancel_on_error] do … end .
pipe <source> do <stage> . … <terminal> . end .
spawn send <actor> <message> <value>… . .    drain <actor> .    schedule .

Patterns#

A name in a pattern takes no stop — it is a place where a name is made, not used. The body of a branch is always do … end ..

case _                      everything else
case <integer>    case <low> to <high>
case <variant> [<name>…]    case <pattern> or <pattern>
case some <name>    case none    case ok <pattern>    case error
case <name> when <cond>

Expressions#

<op> <args>… .                          prefix --- a name opens, a stop closes. No precedence
<variable>.                             a variable follows the same rule (name + stop). Literals, true, false, none take no stop
( <expr> )                              parentheses are decoration --- the expression inside closes with its own stop
expr <a> + <b> * <c> .                  infix island --- * / above + -, comparisons below, and above or
lit <type> do <field> <expr> . … end .  <enum>.<variant> <values>… .
lit array <len> <type> <value>… [_] .   lit slice <type> <value>… .
field <value> <steps>… .   idx <slice> <n> .   method <value> <steps>… <name>. <args>… .
some <value> .   ok <value> .   error <variant> .   none
try <expr> [else_none | else_error <variant>] .
comptime <expr> .   size_of <type> .   config <name> .
spawn actor <type> .   send <actor> <message> <values>… .   spawn <op> <args>… .   await <handle> .
alloc_bytes <root> capacity <n> .
payload <value> <variant> <field> .

Opening names and closers#

A form is opened by one name and closed by one closer. The closer is the full stop ., and one stop closes the innermost form that is still open. A variable is a form too — a.. Literals, true, false, none, and names in defining positions (let x, input n, type names, names in patterns) take no stop. do … end is a pair of braces, so end closes only its own do, and a form that owns a block is closed with end .. A chain joined by else is one form, with one stop at the end. A newline is not a closer but whitespace. Parentheses are decoration and cannot cross block boundaries.