forked from nulang-org/nulang
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgrammar.ebnf
More file actions
179 lines (152 loc) · 7.98 KB
/
Copy pathgrammar.ebnf
File metadata and controls
179 lines (152 loc) · 7.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# Nulang Grammar (EBNF)
> **Authoritative grammar for the Frozen (Core) and Stable tiers** of the
> Nulang language, per RFC 0002 (Frozen Core) and `GOVERNANCE.md` §2. This
> file is a Frozen-tier artifact: the Core productions below may not change
> without a new RFC and a major-version bump. The Stable productions may
> change only under the deprecation contract.
>
> The hand-rolled recursive-descent parser in `src/parser.rs` is the reference
> implementation of this grammar. The conformance corpus in
> `conformance/grammar/` (positive + negative examples) validates the parser
> against this grammar; see `conformance/README.md` for the check harness.
>
> Experimental surface (actors' `@backend`, `workflow`, `agent`, FFI `extern`,
> Python interop, WIT) is **out of scope** for this grammar and is not part of
> the frozen or stable language.
>
> EBNF conventions: `{ X }` = zero or more; `[ X ]` = optional; `X | Y` =
> alternation; `"tok"` = literal terminal; `(* comment *)`. Newlines and
> semicolons are equivalent statement separators (`SEP`).
## 1. Lexical conventions
```
IDENT = lowercase ( lowercase | uppercase | digit | "_" )*
UPPER_IDENT = uppercase ( lowercase | uppercase | digit | "_" )*
INT_LIT = digit { digit } | "0x" hex_digit { hex_digit }
FLOAT_LIT = digit { digit } "." digit { digit } [ ("e"|"E") ["+"|"-"] digit { digit } ]
STRING_LIT = '"' { char_escape | printable_char - '"' } '"'
CHAR_LIT = "'" ( char_escape | printable_char - "'" ) "'"
BOOL_LIT = "true" | "false"
```
Reserved keywords (not usable as identifiers): `fn`, `let`, `if`, `else`,
`match`, `return`, `type`, `alias`, `enum`, `struct`, `effect`, `handle`,
`perform`, `actor`, `behavior`, `spawn`, `send`, `receive`, `val`, `iso`,
`trn`, `box`, `tag`, `lineariso`, `true`, `false`, `nil`, `unit`, `pub`,
`extern`, `where`, `priv`, `loop`, `node`, `monitor`, `link`, `exit`,
`await`, `subworkflow`, `case`, `do`.
## 2. Core productions (Frozen — RFC 0002)
A Core program is a sequence of declarations. Core excludes actors, all
effects except `IO.print`/`IO.read`, and all capabilities except `val`.
```
program = { decl SEP } ;
fn_decl = [ "pub" ] "fn" IDENT "(" [ param_list ] ")" [ "->" type ] block ;
decl = fn_decl
| type_decl (* enum / struct / variant *)
| alias_decl
| const_decl ;
fn_decl = [ "pub" ] "fn" IDENT "(" [ param_list ] ")" [ ":" type ] block ;
param_list = param { "," param } ;
param = IDENT [ ":" type ] ;
type_decl = [ "pub" ] "type" UPPER_IDENT [ type_params ]
( variant_body | record_body ) ;
type_params = "[" IDENT { "," IDENT } "]" ;
variant_body = "=" variant { "|" variant } ;
variant = UPPER_IDENT [ "(" type { "," type } ")" ] ;
record_body = "{" { field SEP } "}" ;
field = IDENT ":" type ;
alias_decl = [ "pub" ] "alias" UPPER_IDENT [ type_params ] "=" type ;
const_decl = [ "pub" ] "const" IDENT [ ":" type ] "=" expr ;
(* Expressions — Pratt precedence; listed lowest to highest binding *)
expr = assign_expr ;
assign_expr = or_expr [ "=" assign_expr ] ; (* only in `let`/`const` *)
or_expr = and_expr { "||" and_expr } ;
and_expr = eq_expr { "&&" eq_expr } ;
eq_expr = cmp_expr { ( "==" | "!=" ) cmp_expr } ;
cmp_expr = add_expr { ( "<" | ">" | "<=" | ">=" ) add_expr } ;
add_expr = mul_expr { ( "+" | "-" ) mul_expr } ;
mul_expr = unary_expr { ( "*" | "/" | "%" ) unary_expr } ;
unary_expr = ( "-" | "!" ) unary_expr | primary ;
primary = INT_LIT | FLOAT_LIT | STRING_LIT | CHAR_LIT | BOOL_LIT
| "nil" | "unit"
| IDENT
| "(" expr { "," expr } ")" (* paren or tuple *)
| block
| if_expr
| match_expr
| lambda
| record_literal
| call_expr ;
call_expr = primary "(" [ expr { "," expr } ")" ] ; (* desugared in Pratt *)
record_literal = "{" { field_init SEP } "}" ;
field_init = IDENT [ ":" expr ] ; (* `x` desugars to `x: x` *)
if_expr = "if" expr block [ "else" ( if_expr | block ) ] ;
match_expr = "match" expr "{" { match_arm SEP } "}" ;
match_arm = [ "case" ] pattern "=>" expr ;
pattern = "_" | INT_LIT | STRING_LIT | BOOL_LIT
| IDENT | UPPER_IDENT [ "(" pattern { "," pattern } ")" ]
lambda = "fn" "(" [ param_list ] ")" [ "->" type ] block ;
block = "{" { stmt SEP } [ expr ] "}" ;
stmt = let_stmt | return_stmt | expr ;
let_stmt = "let" IDENT [ ":" type ] "=" expr ;
return_stmt = "return" [ expr ] ;
(* Types *)
type = arrow_type ;
arrow_type = atomic_type { "->" atomic_type } ;
atomic_type = UPPER_IDENT [ type_args ] (* named: Int, Bool, String, Vec<T>, ... *)
| "(" [ type { "," type } ] ")" ;
type_args = "[" type { "," type } "]" ;
SEP = newline | ";" ;
```
## 3. Stable extensions (Stable tier — change requires RFC + deprecation)
These productions extend the Core grammar. They are Stable: breaking changes
require an RFC and a deprecation cycle.
```
(* Effects (beyond IO) *)
decl += effect_decl ;
effect_op = IDENT ":" [ "(" [ type { "," type } ] ")" ] "->" type ;
perform_expr = "perform" UPPER_IDENT "." IDENT "(" [ expr_list ] ")" ;
primary += perform_expr ;
primary += handle_expr ;
handle_expr = "handle" expr "{" { handle_arm SEP } "}" ;
handle_arm = [ "|" ] UPPER_IDENT "." IDENT "(" [ param_list ] ")" [ "resume" ] "=>" expr ;
cap = "val" | "iso" | "trn" | "box" | "tag" | "lineariso" ;
param += IDENT ":" type "@" cap ; (* capability-annotated param *)
let_stmt = "let" IDENT ":" type "@" cap "=" expr ;
(* Actors — the universal abstraction (SPEC2 Ch. 8) *)
decl += actor_decl ;
actor_decl = [ "pub" ] "actor" IDENT "{" { actor_member SEP } "}" ;
actor_member = state_field | behavior | fn_decl ;
state_field = "state" IDENT [ ":" type ] [ "@" state_model ] [ "=" expr ] ;
state_model = "local" | "durable" | "event_sourced" | "crdt" ;
behavior = "behavior" IDENT "(" [ param_list ] ")" block ;
(* Actor expressions *)
spawn_expr = "spawn" [ cap ] IDENT [ "{" [ init_fields ] "}" ]
[ "link" | "monitor" ] ; (* parser desugar *)
send_expr = "send" expr "to" expr ;
receive_expr = "receive" "{" { receive_arm SEP } "}"
[ "after" expr "=>" block ] ;
send_expr = "send" expr IDENT [ "(" [ expr { "," expr } ] ")" ] ;
primary += spawn_expr | send_expr | receive_expr ;
receive_arm = [ "|" ] IDENT "(" [ param_list ] ")" "=>" expr ;
(* Pipe operator *)
primary = primary "|>" IDENT [ "." IDENT ] ; (* left-associative, desugars to call *)
```
## 4. Out of scope (Experimental tier)
The following surface is Experimental and deliberately **not** specified here.
It may change without RFC and is not part of the frozen or stable language:
- `workflow` and `agent` declarations (AI orchestration).
- `@backend(native|wasm)` actor annotation.
- `@tool(description: "...")` function annotation (LLM tool exposure).
- `extern "lib" { ... }` FFI blocks.
- `perform Python.call(...)` and the `python` feature surface.
- `@persistent` and the durable-execution syntax beyond the `@durable`
state model (which is Stable).
- WASM/WIT integration, the `nula` package manager CLI grammar.
When an Experimental feature stabilizes, its productions are added to §3
under an RFC and removed from this list.
## 5. Parser conformance
The reference parser (`src/parser.rs`) must accept every string in the language
of §2 ∪ §3 and reject every string outside it (modulo Experimental §4). The
`conformance/grammar/` corpus contains positive cases (must parse) and negative
cases (must reject); `conformance/run.rs` executes them against the current
parser. A drift between this grammar and the parser is a bug in whichever was
not most recently updated under an RFC.