Grammar (Informal EBNF)
Informal sketch of the public surface. The authoritative grammar is
cambrian-transpiler/src/cambrian.lalrpop. X? is optional, X* / X+
repeat, X,* is a comma-separated list, … elides detail.
Program ::= TopLevel*
TopLevel ::= ImportFile | UseDecl | PureFn | TypeAlias | RecordDecl
| EnumDecl | Entity | ExternEntity | EventDecl | ErrorDecl
| LibraryDecl | UsingDecl | TestDecl | PropertyDecl
| InvariantDecl
ImportFile ::= "import" StringLit
UseDecl ::= "use" Ident
PureFn ::= "pure" "fn" Ident "(" Params ")" "->" Type "{" Block "}"
TypeAlias ::= "type" Ident "=" Type // no ";"
Const ::= "const" Ident ":" Type "=" Expr // no ";"
RecordDecl ::= "record" Ident "{" (Ident ":" Type),* "}"
EnumDecl ::= "enum" Ident "{" (Ident ("(" Type,* ")")?),* "}"
EventDecl ::= "event" Ident "(" ("indexed"? Ident ":" Type),* ")" ";"
ErrorDecl ::= "error" Ident "(" Params ")" ";"
LibraryDecl ::= "library" Ident "{" (PureFn | Const | TypeAlias)* "}"
UsingDecl ::= "using" ("{" Ident,* "}" | Ident) "for" Type ";"
Entity ::= "entity" Ident "{" EntityItem* "}"
EntityItem ::= Const | Macro | RecordDecl | EnumDecl | TypeAlias
| EventDecl | ErrorDecl | RoutesBlock | Member
Macro ::= "macro" Ident "(" Params ")" "->" Type "=" "{" Block "}"
ExternEntity ::= ("@" "solidity_import" "(" StringLit ")")?
"extern" "entity" Ident "{" ExternRoute* "}"
ExternRoute ::= ("view" | "accept")? "route" Ident "(" Params ")" ("->" Type)? ";"
Member ::= Ident ":" Type ("=" Expr)? "{" Transform* "}"
| "identity" Ident ":" Type
Transform ::= "in" Ident "(" Pattern,* ")" "=>" TransformBody
| "in" Ident "(" Pattern,* ")" "=>" (Ident ":" TransformBody)+ // per phase
TransformBody ::= Expr | "{" Block "}"
RoutesBlock ::= "routes" "{" Route* "}"
Route ::= Attr* RouteHead "=>" "[" Body "]"
RouteHead ::= Ident "(" Params ")" From? ("->" Type)? Where? // plain
| "accept" Ident "(" Params ")" From? ("->" Type)? Where?
| "view" Ident "(" Params ")" From? ("->" Type)? Where?
| "pure" Ident "(" Params ")" ("->" Type)?
| "init" Ident "(" Params ")" Where?
| "private" Ident "(" Params ")" Where?
Attr ::= "#" "[" Ident ("(" (StringLit | IntLit) ")")? "]" // e.g. #[factory_only]
From ::= "from" FromAlt ("|" FromAlt)*
FromAlt ::= Ident "(" Expr,* ")" ("with" "{" … "}")? ThrowSpec? // entity form
| Ident ThrowSpec? // member form
Where ::= "where" Cond ("&&" Cond)*
| "where" "{" Cond,* "}"
Cond ::= Expr ThrowSpec
ThrowSpec ::= ":" "throw" (IntLit | Ident | Ident "(" Expr,* ")")
Body ::= Action* | PhaseBlock*
PhaseBlock ::= Ident Where? ":" "[" Action* "]"
Action ::= "let" Pattern "=" LetValue ";"
| "var" Ident "=" Ident "(" Expr,* ")" "~>" Expr SendOpts? ";"
| "emit" Ident "(" Expr,* ")" ";"
| Ident "(" Expr,* ")" "~>" Expr SendOpts? // typed send
| "~>" Expr SendOpts? // value transfer
| "deploy" Ident ("(" Expr,* ")")? SendOpts?
| "return" "(" Expr,* ")"
| "throw" (IntLit | Ident | Ident "(" Expr,* ")")
| "call" Ident "(" Expr,* ")"
| Ident "::" Ident "(" Expr,* ")" // namespaced effect
| "if" Expr "=>" "[" Action* "]" ("else" "[" Action* "]")?
| "for" Pattern "in" Expr "=>" "[" Action* "]"
LetValue ::= Expr | "if" … | "match" … | "{" Block "}"
SendOpts ::= "with" "{" (Ident ":" Expr),* "}"
Block ::= LetStmt* BlockTail
LetStmt ::= "let" Pattern "=" (Expr | RecordLit | "if" … | "match" … | "{" Block "}") ";"
BlockTail ::= Expr | RecordLit | "{" Block "}"
| "if" Expr "{" Block "}" ("else" "{" Block "}")?
| "match" Expr "{" (MatchPat "=>" (Expr | "{" Block "}")),* "}"
| "for" Pattern "in" Expr "{" Block "}"
RecordLit ::= Ident "{" (Ident ":" Expr),* "}"
MatchPat ::= Ident "::" Ident ("(" Pattern,* ")")? | "some" "(" Pattern ")"
| "none" | "_" | IntLit | "true" | "false" | Ident
Pattern ::= Ident | "_" | "(" Pattern,* ")" | "some" "(" Pattern ")" | "none"
Expr ::= Closure | Expr ".." Expr | BinaryExpr // see Operator Precedence
Closure ::= "|" Pattern,* "|" (Expr | RecordLit | "{" Block "}" | "if" …)
Primary ::= IntLit | HexLit | BinLit | StringLit | BytesLit | "true" | "false"
| "array" "(" Expr,* ")" | "{" "}" | "(" Expr ("," Expr)+ ")"
| "^" Ident | "@" Ident "(" Expr,* ")"
| "msg::" Ident | "sys::" Ident | "trace::" Ident ("(" Ident ")")?
| "some" "(" Expr ")" | "none"
| Ident "::" Ident "::" Ident "(" Expr,* ")" // std::math::min(…)
| Ident "::" Ident ("(" Expr,* ")")? // Enum::Variant, evm::…
| "address_of" Ident "(" Expr,* ")"
| Ident "(" Expr,* ")" | Ident | "(" Expr ")"
TestDecl ::= Attr* "test" StringLit "for" Ident ("skip" "from")?
("with" "{" (Ident ":" Expr),* "}")? "{" TestStep* "}"
TestStep ::= "call" (Ident ".")? Ident "(" Expr,* ")"
| Ident "{" (Ident ":" Expr),* "}" // msg { sender: … }
| "let" Ident (":" Type)? "=" Expr
| "deploy" Ident "=" Ident "(" Expr,* ")" ("with" "{" … "}")?
| "expect" "state" "{" (FieldPath ":" Expr),* "}"
| "expect" "return" ("==")? Expr
| "expect" "return" ("." FieldPath | "[" … "]") ("==" | RelOp) Expr
| "expect" "throw" IntLit
| "expect" "emit" Ident "(" Expr,* ")"
| "expect" Expr // boolean predicate
| "assume" Expr | "bound" Ident "in" Expr (".." | "..=") Expr
PropertyDecl ::= Attr* "property" StringLit ("(" Params ")")? "for" Ident
With? Ctx? "{" (TestStep | Instance)* "}"
Instance ::= Attr* ("test" | "fuzz") StringLit? "{" Binding,* "}" With? Ctx?
Binding ::= Ident ":" Expr | Ident "in" Expr (".." | "..=") Expr
With ::= "with" "{" (Ident ":" (Expr | "*") | "*"),* "}"
Ctx ::= "ctx" "{" (("msg::" | "sys::") Ident ":" (Expr | "*")),* "}"
InvariantDecl ::= Attr* "invariant" StringLit Attr* "for" InvTarget Attr* "{" InvItem* "}"
InvTarget ::= Ident | "{" (Ident ":" Ident),* "}" // single / multi-entity
InvItem ::= "init" Ident? "{" … "}" | "with" "{" … "}" // Ident = instance
| "deploy" "{" Expr,* "}"
| "senders" "{" Expr,* "}" | Ctx
| "action" Ident ("." Ident)? "(" Params ")" "{" TestStep* "}"
| "check" Expr
| "track" "{" ("let" Ident "=" Expr ";"?)* "}"
| "derived" Ident "(" Params ")" "->" Type "{" TestStep* "return" Expr "}"
| "exclude" ("senders" | "selectors") "{" … "}"
Invariant action bodies additionally accept skip if Expr and
advanceTime(Expr) (with #[with_time]).
Comments are // … only. Literals: decimal 1_000, hex 0xff, binary
0b1010, strings "…", bytes b"…". Strings and bytes decode the escapes
\n, \r, \t, \0, \\ and \xHH; any other character, including
non-ASCII text, is taken literally. A string can’t contain a " character
(\" is not accepted).
See also Keywords and Operator Precedence.