OxL Reference

Appendix A. Grammar

The grammar describes the syntax after lexical analysis (§1). NL is a line break that terminates a statement per §1.2. The precedence of binary operators follows table §5.2.

ebnf
File        = { Use | Item } ;
Use         = "use" Path [ "." ( "*" | "{" ImportList "}" ) | "{" ImportList "}" ] ;
ImportList  = Import { "," Import } ;
Import      = ( Ident | OpSymbol ) [ "as" Ident ] ;
Path        = Ident { "." Ident } ;

Item        = { Attr } [ Vis ] ItemBody ;
Vis         = "pub" [ "(" "api" ")" ] ;
Attr        = "@" Path [ "(" [ AttrArgs ] ")" ] ;
AttrArgs    = AttrArg { "," AttrArg } ;
AttrArg     = [ Ident ":" ] Expr ;
ItemBody    = Fn | Data | Enum | Interface | Impl | TypeAlias | Const | Static
            | Service | Actor | Pipeline | Saga | Topic | Policy | Config | Dynamic
            | Cluster | Provide | Operator | AttrDecl | Test | Fixture | Mock | Assert
            | Import_ffi ;

Fn          = [ "comptime" [ "literal" ] | "unsafe" ] "fn" Ident [ Generics ] "(" [ Params ] ")"
              [ "->" Type ] [ Effects ] [ Where ] { Contract } ( Block | "=>" Expr ) ;
Params      = Param { "," Param } ;
Param       = { Attr } [ "lazy" ] [ "..." ] Ident ":" Type [ "=" Expr ] ;
Effects     = "pure" | Effect { "," Effect } | EffectVar ;
Effect      = "io" | "net" | "db" | "time" | "rand" | Ident ;
Contract    = ( "requires" | "ensures" ) Expr ;
Generics    = "<" GParam { "," GParam } ">" ;
GParam      = Ident [ ":" Bounds ] [ "=" Type ] | "const" Ident ":" Type | "effect" Ident ;
Bounds      = Type { "+" Type } ;
Where       = "where" WClause { "," WClause } ;
WClause     = Ident ":" Bounds | Expr ;

Data        = "data" Ident [ Generics ] [ Where ] "{" { Member } "}" ;
Member      = Field | "embed" Type | "invariant" Expr ;
Field       = { Attr } [ Vis ] Ident ":" Type [ "=" Expr ] { Attr } ;
Enum        = "enum" Ident [ Generics ] "{" Variant { ( "," | NL ) Variant } "}" ;
Variant     = Ident [ "(" [ VFields ] ")" ] [ "=" IntLit ] ;
VFields     = VField { "," VField } ;
VField      = [ Ident ":" ] Type ;
Interface   = "interface" Ident [ Generics ] "{" { IMember } "}" ;
IMember     = "embed" Type | "type" Ident [ "=" Type ] | Receiver "fn" Sig [ "=>" Expr | Block ] ;
Impl        = "impl" [ Type "for" ] Type "{" { ImplMember } "}" ;
ImplMember  = { Attr } [ Vis ] ( Receiver ( "fn" | "op" ) Sig ( Block | "=>" Expr )
            | "override" "fn" Sig ( Block | "=>" Expr ) | "open" "fn" Sig ( Block | "=>" Expr )
            | "init" Sig ( Block | "=>" Expr ) | "dep" Ident ":" Type | Rpc | Const ) ;
Receiver    = [ "var" | "move" | "static" ] ;
TypeAlias   = "type" Ident [ Generics ] "=" ( [ "distinct" ] Type [ "where" Expr ] ) ;
Const       = "const" Ident [ ":" Type ] "=" Expr ;
Static      = "static" Ident ":" Type "=" Expr ;

Service     = [ "extern" ] "service" Ident "{" { { Attr } RpcSig } "}" ;
RpcSig      = "rpc" Ident "(" [ Params ] ")" "->" Type ;
Rpc         = "rpc" Ident "(" [ IdentList ] ")" ( Block | "=>" Expr ) ;
Actor       = "actor" Ident [ "sharded" "by" Ident ":" Type ] "{" { ActorMember } "}" ;
ActorMember = { Attr } ( "var" Ident ":" Type "=" Expr | "dep" Ident ":" Type
            | "msg" Ident "(" [ Params ] ")" [ "->" Type ] Block
            | "on" ( "activate" | "passivate" ) Block ) ;
Pipeline    = "pipeline" Ident "(" [ Params ] ")" "->" Type "{" PStep { "->" PStep } "}"
              [ "on_error" "(" Ident "," Ident ")" Block ] ;
PStep       = ( "respond" Expr | Expr ) [ "on" Ident ] [ "durable" | "best_effort" ] ;
Saga        = "saga" Ident "(" [ Params ] ")" "->" Type "{" SStep { "->" SStep } "}" ;
SStep       = Expr [ "undo" Lambda ] ;
Topic       = "topic" Ident ":" Type [ "sharded" "by" FieldPath ] ;
Policy      = "policy" Ident "{" { Rule } "}" ;
Rule        = ( "allow" | "deny" ) ( "*" | RTarget { "," RTarget } ) ( "if" | "unless" ) Expr ;
RTarget     = Ident [ "(" [ IdentList ] ")" ] ;
Config      = "config" Ident "{" { Field | "profile" Ident "{" { Ident "=" Expr } "}" } "}" ;
Dynamic     = "dynamic" Ident "{" { Field } "}" ;
Provide     = "provide" Type "=" Expr [ "for" "role" IdentList ] ;
Operator    = "operator" OpSymbol [ Generics ] "(" Params ")" "->" Type
              ( ( "infixl" | "infixr" | "infix" ) IntLit | "prefix" ) ( Block | "=>" Expr ) ;
AttrDecl    = "attr" Ident "{" { Field } "}" ;
Assert      = "assert" Type ":" Bounds ;

Cluster     = "cluster" Ident "{" { ClusterClause } "}" ;
ClusterClause = "seeds" Expr | "port" IntLit | "trace_sample" FloatLit | "auth" Block | "defaults" "{" { Ident Expr } "}"
            | "role" Ident "{" RoleClause { ( ";" | NL ) RoleClause } "}"
            | "assign" "by" Expr { "else" Expr } ;
RoleClause  = "serves" IdentList | "hosts" IdentList | "runs" ( "sagas" | "leader" "(" StrLit ")" )
            | "journal" | "shards" IntLit | "replication" IntLit | "replicas" Expr
            | "needs" Expr | "expose" IntLit { Expr } ;

Test        = "test" [ TestKind ] StrLit [ "with" EnvList ] [ "seeds" Expr ] Block ;
TestKind    = "unit" | "integ" | "sim" | "contract" | "ab" | "bench" | "fuzz" ;
EnvList     = Env { "," Env } ;
Env         = Expr [ "as" Ident ] ;
Fixture     = "fixture" Ident "(" [ Params ] ")" "->" Type Block ;
Mock        = "mock" Path "{" { MockArm } "}" ;
MockArm     = Ident "(" [ Patterns ] ")" [ "if" Expr ] "=>" MockSeq ;
MockSeq     = MockResp { "," "then" MockResp } ;
MockResp    = Expr [ "once" | "times" Expr | "always" ] ;

Type        = UnionType ;
UnionType   = PostType { "|" PostType } ;
PostType    = AtomType { "?" } ;
AtomType    = Path [ "<" TypeArgs ">" ] | "(" [ Type { "," Type } ] ")" | "[" Type [ ";" Expr ] "]"
            | "&" [ "var" ] Type | "*" [ "var" ] Type | ( "frozen" | "sync" | "view" | "dyn" ) Type
            | "fn" "(" [ Type { "," Type } ] ")" [ "->" Type ] [ Effects ] [ "escaping" ] | "never" ;

Block       = "{" { Stmt } [ Expr ] "}" ;
Stmt        = ( "let" | "var" ) Pattern [ ":" Type ] "=" Expr | Const | Place AssignOp Expr
            | "defer" Expr | Expr ;
Expr        = Chain ;
Chain       = Binary { Link } { Suffix } ;
Link        = ( "->" | "?->" | "~>" ) Step | "!>" Pattern "=>" Expr ;
Step        = Lambda | "(" Expr { "," Expr } ")" | Binary ;
Suffix      = "within" Binary | "retry" Binary [ "on" Type ] [ "backoff" Binary ] ;
Binary      = Unary { BinOp Unary } ;                 (* precedence per §5.2 *)
Unary       = { PrefixOp } Postfix ;
Postfix     = Primary { "(" [ Args ] ")" [ TrailingBlock ] | "[" Expr "]" | "." ( Ident | IntLit | "[" Expr "]" )
            | "?." Ident | "?" | "!" | "as" Type | "is" Type } ;
Primary     = Literal | Path | "it" | "self" | "(" Expr { "," Expr } ")" | ListLit | MapLit
            | StructLit | Lambda | Block | If | Match | For | While | Loop | Scope | Select | Query
            | "spawn" ( Postfix | Block ) | "await" Unary | "atomic" [ "(" Args ")" ] Block
            | "return" [ Expr ] | "fail" Expr | "yield" Expr | "respond" Expr | "break" [ Label ] [ Expr ]
            | "continue" [ Label ] | "emit" Path "(" Expr ")" | "unsafe" Block | "blocking" Block
            | "comptime" Block | "." Ident [ "(" [ Args ] ")" ] ;
Args        = Arg { "," Arg } ;
Arg         = [ Ident ":" ] Expr | "&" "var" Expr ;
TrailingBlock = Block | "{" Lambda "}" ;
StructLit   = Path "{" [ FieldInit { "," FieldInit } ] [ "," ] [ ".." Expr ] "}" ;
FieldInit   = Ident [ ":" Expr ] ;
Lambda      = [ "move" ] ( Ident | "(" [ LParams ] ")" ) "=>" Expr ;
If          = "if" ( Expr | "let" Pattern "=" Expr ) Block [ "else" ( If | Block ) ] ;
Match       = "match" Expr "{" { Pattern [ "if" Expr ] "=>" Expr ( "," | NL ) } "}" ;
For         = [ Label ":" ] [ "inline" ] "for" [ "await" ] Pattern "in" Expr [ "where" Expr ] Block ;
While       = [ Label ":" ] "while" ( Expr | "let" Pattern "=" Expr ) Block ;
Loop        = [ Label ":" ] "loop" Block ;
Scope       = "scope" Block ;
Select      = "select" "{" { SelArm } "}" ;
SelArm      = ( "recv" Pattern "from" Expr | "send" Expr "to" Expr | "after" Expr | "else" ) "=>" Expr ;
Query       = "from" Pattern "in" Expr { "where" Expr } [ "order" "by" Ord { "," Ord } ]
              [ "group" "by" Expr "into" Ident ] [ "skip" Expr ] [ "take" Expr ]
              [ "load" "(" IdentList ")" ] [ "select" Expr ] ;

Pattern     = PatAlt { "|" PatAlt } ;
PatAlt      = "_" | Literal | Literal ( ".." | "..=" ) Literal | [ "var" ] Ident [ "?" ]
            | Type [ Ident ] | [ "." ] Path "(" [ Patterns ] ")" | Path "{" [ FieldPats ] [ ".." ] "}"
            | "(" Patterns ")" | "[" [ Patterns ] [ ".." [ Ident ] ] "]" ;

Ambiguities are resolved as follows:

  1. A { after an expression in the condition position of if/while/for/match starts a block, not a struct literal; a literal in a condition is put in parentheses.
  2. Path(...) is a function call, a positional data literal, a distinct constructor or a reference to a sharded actor; the choice depends on the kind of declaration of Path.
  3. | in a type is a union, in a pattern an alternative, in an expression bitwise OR.
  4. where after a signature is constraints, after a type in an alias or field a refinement, in for a filter, in a query a condition.
  5. In a chain step (e1, e2) is a fan-out, not a tuple value; a tuple as the value of a step is written x => (e1, e2).

syntax

Source: docs/spec/oxl-standard.en.md