Приложение A. Грамматика
Грамматика описывает синтаксис после лексического анализа (§1). NL — перевод строки, завершающий инструкцию по §1.2. Приоритеты бинарных операторов — по таблице §5.2.
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 } ; (* приоритеты по §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 ] ] "]" ;Неоднозначности разрешаются так:
{после выражения в позиции условияif/while/for/matchначинает блок, а не литерал структуры; литерал в условии берётся в скобки.Path(...)— вызов функции, позиционный литералdata, конструкторdistinctили ссылка на шардированный актор; выбор по виду объявленияPath.|в типе — объединение, в образце — альтернатива, в выражении — побитовое ИЛИ.whereпосле сигнатуры — ограничения, после типа в псевдониме или поле — уточнение, вfor— фильтр, в запросе — условие.- В шаге цепочки
(e1, e2)— fan-out, а не кортежное значение; кортеж как значение шага пишетсяx => (e1, e2).
Связанные диагностики
Исходник: docs/spec/oxl-standard.md