Skip to content

Commit e8dbdb4

Browse files
Aiur grammar: statement sequencing t; rest (#475)
Add `syntax:0 aiur_trm "; " (aiur_trm)?` — `f(x); rest` elaborates to `let _ = f(x); rest` (wildcard let, value discarded); trailing `f(x);` closes the block with `()`. Unit-returning calls sit in statement position without the `let _ =` boilerplate. The sequencing rule is the grammar's only prec-0 rule; positions where an expression can end right before a `;` are bumped to prec 1 so it can't leak into expression operands: `let` right-hand sides, `+`/`-` right operands, `return`, array elements/replicate. Without the binop guard, `let x = a - 1; rest` reparses as `a - (1; rest)` — caught only at `ix codegen` scope-check (`unboundGlobal`), not by `lake build`, since elaboration builds Source.Term without checking. All other argument positions are bounded by closing tokens and can't precede a `;`. Since prec-51 positions accept every leading form, default rule prec ≥ 51 and the :1 annotations exclude only the sequencing rule. Rewrite all 113 `let _ = call(...);` sites in Ix/IxVM and the 3 `let _ = store(...)` sites in Tests/Aiur to the new form, and drop the 4 redundant explicit `()` continuations after trailing `assert_eq!` (an omitted continuation already elaborates to unit). Generated kernel is byte-identical after `lake exe ix codegen` (identical IR by construction — no FFT shift), and `lake test -- aiur-cross` passes with the new syntax exercised.
1 parent c66b28f commit e8dbdb4

11 files changed

Lines changed: 143 additions & 128 deletions

File tree

Ix/Aiur/Meta.lean

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,21 @@ syntax ("." noWs)? ident :
135135
syntax num : aiur_trm
136136
syntax "(" ")" : aiur_trm
137137
syntax "(" aiur_trm (", " aiur_trm)* ")" : aiur_trm
138-
syntax "[" aiur_trm (", " aiur_trm)* "]" : aiur_trm
139-
syntax "[" aiur_trm "; " num "]" : aiur_trm
140-
syntax "return " aiur_trm : aiur_trm
141-
syntax "let " aiur_pattern (":" aiur_typ)? " = " aiur_trm "; " aiur_trm : aiur_trm
142-
syntax "let " aiur_pattern (":" aiur_typ)? " = " aiur_trm ";" : aiur_trm
143-
syntax "let " aiur_pattern (":" aiur_typ)? " = " aiur_trm : aiur_trm
138+
-- Array elements and `return` operands parse at prec 1: it keeps the
139+
-- statement-sequencing rule (prec 0, below `let`) out of expression
140+
-- positions and keeps `[t; n]` replicate vs `[t]` list unambiguous.
141+
syntax "[" aiur_trm:1 (", " aiur_trm:1)* "]" : aiur_trm
142+
syntax "[" aiur_trm:1 "; " num "]" : aiur_trm
143+
syntax "return " aiur_trm:1 : aiur_trm
144+
-- `let` right-hand sides parse at prec 1 so the statement-sequencing rule
145+
-- (prec 0, below) can't swallow the `let`'s own `;` and continuation.
146+
syntax "let " aiur_pattern (":" aiur_typ)? " = " aiur_trm:1 "; " aiur_trm : aiur_trm
147+
syntax "let " aiur_pattern (":" aiur_typ)? " = " aiur_trm:1 ";" : aiur_trm
148+
syntax "let " aiur_pattern (":" aiur_typ)? " = " aiur_trm:1 : aiur_trm
149+
-- Statement sequencing: `t; rest` discards `t`'s value (elaborates to
150+
-- `let _ = t; rest`); trailing `t;` closes the block with `()`. Lets
151+
-- unit-returning calls sit in statement position without `let _ =`.
152+
syntax:0 aiur_trm "; " (aiur_trm)? : aiur_trm
144153
syntax "match " aiur_trm " { "
145154
(aiur_pattern " => " aiur_trm ", ")+ " }" : aiur_trm
146155
syntax ("." noWs)? ident "(" ")" : aiur_trm
@@ -156,8 +165,11 @@ syntax "#" noWs ident "‹" aiur_typ (", " aiur_typ)* "›" "(" aiur_trm
156165
syntax ident "" aiur_typ (", " aiur_typ)* "" "." noWs ident "(" ")" : aiur_trm
157166
syntax ident "" aiur_typ (", " aiur_typ)* "" "." noWs ident
158167
"(" aiur_trm (", " aiur_trm)* ")" : aiur_trm
159-
syntax:50 aiur_trm " + " aiur_trm : aiur_trm
160-
syntax:50 aiur_trm " - " aiur_trm : aiur_trm
168+
-- Right operands parse at prec 1 (not 0) so the statement-sequencing rule
169+
-- can't swallow `<binop-expr>; rest` from inside the right operand. All
170+
-- other rules have prec ≥ 50, so associativity/precedence are unchanged.
171+
syntax:50 aiur_trm " + " aiur_trm:1 : aiur_trm
172+
syntax:50 aiur_trm " - " aiur_trm:1 : aiur_trm
161173
syntax aiur_trm " * " aiur_trm:51 : aiur_trm
162174
syntax "eq_zero" "(" aiur_trm ")" : aiur_trm
163175
syntax "proj" "(" aiur_trm ", " num ")" : aiur_trm
@@ -240,6 +252,9 @@ partial def elabTrm : ElabStxCat `aiur_trm
240252
| some ty => do
241253
let t ← mkAppM ``Source.Term.ann #[← elabTyp ty, ← elabTrm t]
242254
mkAppM ``Source.Term.let #[← elabPattern p, t, mkConst ``Source.Term.unit]
255+
| `(aiur_trm| $t:aiur_trm; $[$t':aiur_trm]?) => do
256+
mkAppM ``Source.Term.let
257+
#[mkConst ``Pattern.wildcard, ← elabTrm t, ← elabRet t']
243258
| `(aiur_trm| match $t:aiur_trm {$[$ps:aiur_pattern => $ts:aiur_trm,]*}) => do
244259
let mut prods := Array.mkEmpty (ps.size + 1)
245260
for (p, t) in ps.zip ts do
@@ -570,6 +585,10 @@ where
570585
let body ← if v.getId = old then pure body
571586
else replaceToken old new body
572587
`(aiur_trm| fold($i .. $j, $init, |$acc, @$v| $body))
588+
| `(aiur_trm| $t:aiur_trm; $[$t':aiur_trm]?) => do
589+
let t ← replaceToken old new t
590+
let t' ← t'.mapM $ replaceToken old new
591+
`(aiur_trm| $t; $[$t']?)
573592
| stx => pure stx
574593

575594
declare_syntax_cat aiur_constructor

Ix/IxVM/Blake3.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def blake3 := ⟦
2121
let key = [num_hashes_pred];
2222
let (idx, len) = io_get_info(0, key);
2323
let byte_stream = #read_byte_stream(0, idx, len);
24-
let _ = blake3(byte_stream);
24+
blake3(byte_stream);
2525
match num_hashes_pred {
2626
0 => 0,
2727
_ => blake3_bench(num_hashes_pred),
@@ -60,7 +60,6 @@ def blake3 := ⟦
6060
h[6][0], h[6][1], h[6][2], h[6][3],
6161
h[7][0], h[7][1], h[7][2], h[7][3]],
6262
expected);
63-
()
6463
}
6564

6665
-- Hash `bytes` and intern the digest into the Store. Returned pointer is

Ix/IxVM/Ingress.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def ingress := ⟦
4949
let raw = load(addr);
5050
let (idx, len) = io_get_info(2, raw);
5151
let bytes = #read_byte_stream(2, idx, len);
52-
let _ = verify_bytes_against(bytes, raw);
52+
verify_bytes_against(bytes, raw);
5353
let (constant, rest) = get_constant(bytes);
5454
assert_eq!(load(rest), ListNode.Nil);
5555
constant
@@ -61,7 +61,7 @@ def ingress := ⟦
6161
let raw = load(addr);
6262
let (idx, len) = io_get_info(5, raw);
6363
let bytes = #read_byte_stream(5, idx, len);
64-
let _ = verify_bytes_against(bytes, raw);
64+
verify_bytes_against(bytes, raw);
6565
bytes
6666
}
6767

Ix/IxVM/Kernel/CanonicalCheck.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ def canonicalCheck := ⟦
766766
check_canonical_block_sort_walk(rest, ba, list_snoc(cur_members, pos),
767767
pos + 1, top),
768768
0 =>
769-
let _ = validate_block_if_nonzero(cur_ba, cur_members, top);
769+
validate_block_if_nonzero(cur_ba, cur_members, top);
770770
let new_members = init_block_members(ba, pos);
771771
check_canonical_block_sort_walk(rest, ba, new_members, pos + 1, top),
772772
},

Ix/IxVM/Kernel/Check.lean

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@ def check := ⟦
9393
KLevelNode.Zero => (),
9494
KLevelNode.Succ(inner) => validate_univ_params_seen(inner, bound),
9595
KLevelNode.Max(a, b) =>
96-
let _ = validate_univ_params_seen(a, bound);
96+
validate_univ_params_seen(a, bound);
9797
validate_univ_params_seen(b, bound),
9898
KLevelNode.IMax(a, b) =>
99-
let _ = validate_univ_params_seen(a, bound);
99+
validate_univ_params_seen(a, bound);
100100
validate_univ_params_seen(b, bound),
101101
KLevelNode.Param(i) =>
102102
assert_eq!(u32_less_than(i, bound), 1);
@@ -108,7 +108,7 @@ def check := ⟦
108108
match load(lvls) {
109109
ListNode.Nil => (),
110110
ListNode.Cons(u, rest) =>
111-
let _ = validate_univ_params_seen(u, bound);
111+
validate_univ_params_seen(u, bound);
112112
validate_univ_params_list(rest, bound),
113113
}
114114
}
@@ -129,17 +129,17 @@ def check := ⟦
129129
assert_eq!(list_length(lvls), expected);
130130
validate_univ_params_list(lvls, bound),
131131
KExprNode.App(f, a) =>
132-
let _ = validate_expr_well_scoped(f, depth, bound, top);
132+
validate_expr_well_scoped(f, depth, bound, top);
133133
validate_expr_well_scoped(a, depth, bound, top),
134134
KExprNode.Lam(t, b) =>
135-
let _ = validate_expr_well_scoped(t, depth, bound, top);
135+
validate_expr_well_scoped(t, depth, bound, top);
136136
validate_expr_well_scoped(b, depth + 1, bound, top),
137137
KExprNode.Forall(t, b) =>
138-
let _ = validate_expr_well_scoped(t, depth, bound, top);
138+
validate_expr_well_scoped(t, depth, bound, top);
139139
validate_expr_well_scoped(b, depth + 1, bound, top),
140140
KExprNode.Let(t, v, b) =>
141-
let _ = validate_expr_well_scoped(t, depth, bound, top);
142-
let _ = validate_expr_well_scoped(v, depth, bound, top);
141+
validate_expr_well_scoped(t, depth, bound, top);
142+
validate_expr_well_scoped(v, depth, bound, top);
143143
validate_expr_well_scoped(b, depth + 1, bound, top),
144144
KExprNode.Lit(_) => (),
145145
KExprNode.Proj(_, _, val) =>
@@ -152,7 +152,7 @@ def check := ⟦
152152
fn validate_const_well_scoped(ci: KConstantInfo, top: List‹&KConstantInfo›) {
153153
let bound = const_num_lvls(ci);
154154
let ty = const_type_of(ci);
155-
let _ = validate_expr_well_scoped(ty, 0, bound, top);
155+
validate_expr_well_scoped(ty, 0, bound, top);
156156
match ci {
157157
KConstantInfo.Defn(_, _, val, _, _) =>
158158
validate_expr_well_scoped(val, 0, bound, top),
@@ -173,7 +173,7 @@ def check := ⟦
173173
ListNode.Cons(rule, rest) =>
174174
match rule {
175175
KRecRule.Mk(_, _, rhs) =>
176-
let _ = validate_expr_well_scoped(rhs, 0, bound, top);
176+
validate_expr_well_scoped(rhs, 0, bound, top);
177177
validate_recr_rules(rest, bound, top),
178178
},
179179
}
@@ -201,59 +201,59 @@ def check := ⟦
201201
-- check_const: dispatch per KConstantInfo variant.
202202
-- ============================================================================
203203
fn check_const(ci: KConstantInfo, pos: G, top: List‹&KConstantInfo›, addrs: List‹Addr›) {
204-
let _ = validate_const_well_scoped(ci, top);
204+
validate_const_well_scoped(ci, top);
205205
let u = is_unsafe_ci(ci);
206206
match ci {
207207
KConstantInfo.Axiom(_, ty, _) =>
208-
let _ = k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
209-
let _ = assert_safety(u, ty, top);
208+
k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
209+
assert_safety(u, ty, top);
210210
(),
211211

212212
KConstantInfo.Defn(_, ty, val, _, _) =>
213-
let _ = k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
214-
let _ = assert_safety(u, ty, top);
215-
let _ = assert_safety(u, val, top);
216-
let _ = k_check(val, ty, store(ListNode.Nil), top, addrs);
213+
k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
214+
assert_safety(u, ty, top);
215+
assert_safety(u, val, top);
216+
k_check(val, ty, store(ListNode.Nil), top, addrs);
217217
(),
218218

219219
KConstantInfo.Thm(_, ty, val) =>
220220
-- Mirror: src/ix/kernel/check.rs:135. Theorem type must be Sort 0.
221221
let lvl = k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
222222
assert_eq!(level_equal(lvl, store(KLevelNode.Zero)), 1);
223-
let _ = assert_safety(u, ty, top);
224-
let _ = assert_safety(u, val, top);
225-
let _ = k_check(val, ty, store(ListNode.Nil), top, addrs);
223+
assert_safety(u, ty, top);
224+
assert_safety(u, val, top);
225+
k_check(val, ty, store(ListNode.Nil), top, addrs);
226226
(),
227227

228228
KConstantInfo.Opaque(_, ty, val, is_unsafe) =>
229-
let _ = k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
230-
let _ = assert_safety(u, ty, top);
231-
let _ = assert_safety(u, val, top);
229+
k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
230+
assert_safety(u, ty, top);
231+
assert_safety(u, val, top);
232232
match is_unsafe {
233233
1 => (),
234234
0 =>
235-
let _ = k_check(val, ty, store(ListNode.Nil), top, addrs);
235+
k_check(val, ty, store(ListNode.Nil), top, addrs);
236236
(),
237237
},
238238

239239
KConstantInfo.Quot(num_lvls, ty, kind) =>
240-
let _ = k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
241-
let _ = assert_safety(u, ty, top);
240+
k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
241+
assert_safety(u, ty, top);
242242
-- Mirror: src/ix/kernel/check.rs:606-675 fn check_quot.
243243
-- Validate kind ↔ address consistency, universe-param count per
244244
-- variant, and forall-binder count.
245245
let self_addr = list_lookup(addrs, pos);
246-
let _ = check_quot(self_addr, kind, num_lvls, ty, top, addrs);
246+
check_quot(self_addr, kind, num_lvls, ty, top, addrs);
247247
(),
248248

249249
KConstantInfo.Induct(_, ty, n_params, n_indices, _,
250250
_, block_addr) =>
251-
let _ = k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
252-
let _ = assert_safety(u, ty, top);
253-
let _ = check_block_peer_param_agreement(pos, ty, n_params, n_indices,
251+
k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
252+
assert_safety(u, ty, top);
253+
check_block_peer_param_agreement(pos, ty, n_params, n_indices,
254254
block_addr, top, addrs);
255255
let block_idxs = derive_block_member_idxs(pos, top);
256-
let _ = validate_block_auxes(block_idxs, top);
256+
validate_block_auxes(block_idxs, top);
257257
-- The former H1 declared-vs-computed is_rec check is gone with the
258258
-- Ixon recr flag: there is no declared value to verify. is_rec is
259259
-- computed on demand (`computed_is_rec_ind`) wherever it matters
@@ -264,29 +264,29 @@ def check := ⟦
264264
-- Ctor cross-ref + return-type + field-universe + strict-positivity
265265
-- (positivity walks mutual + nested via derive_block_member_idxs).
266266
KConstantInfo.Ctor(_, ty, induct_idx, _, num_params, num_fields, _) =>
267-
let _ = k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
268-
let _ = assert_safety(u, ty, top);
269-
let _ = check_ctor_against_inductive_member(pos, ci, top);
267+
k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
268+
assert_safety(u, ty, top);
269+
check_ctor_against_inductive_member(pos, ci, top);
270270
let ind_ci = load(list_lookup(top, induct_idx));
271271
match ind_ci {
272272
KConstantInfo.Induct(ind_num_lvls, ind_ty, ind_n_params, ind_n_indices, _, _, _) =>
273273
assert_eq!(num_params, ind_n_params);
274274
-- A1 defense-in-depth: ctor's leading param domains must match
275275
-- parent inductive's. Mirror src/ix/kernel/inductive.rs:283,393.
276-
let _ = check_param_agreement(ind_ty, ty, ind_n_params, top, addrs);
277-
let _ = check_ctor_return_type(ty, num_params, ind_n_indices, num_fields,
276+
check_param_agreement(ind_ty, ty, ind_n_params, top, addrs);
277+
check_ctor_return_type(ty, num_params, ind_n_indices, num_fields,
278278
induct_idx, ind_num_lvls);
279279
let ind_level = get_result_sort_level(ind_ty, ind_n_params + ind_n_indices);
280-
let _ = check_field_universes(ty, num_params, ind_level,
280+
check_field_universes(ty, num_params, ind_level,
281281
store(ListNode.Nil), top, addrs);
282-
let _ = check_positivity(ty, num_params, induct_idx, store(ListNode.Nil), top, addrs);
282+
check_positivity(ty, num_params, induct_idx, store(ListNode.Nil), top, addrs);
283283
(),
284284
},
285285

286286
KConstantInfo.Rec(_, ty, _, _, _, _, _, _, _, _) =>
287-
let _ = k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
288-
let _ = assert_safety(u, ty, top);
289-
let _ = check_recursor_member(pos, ci, top, addrs);
287+
k_ensure_sort(ty, store(ListNode.Nil), top, addrs);
288+
assert_safety(u, ty, top);
289+
check_recursor_member(pos, ci, top, addrs);
290290
(),
291291
}
292292
}
@@ -310,7 +310,7 @@ def check := ⟦
310310
assert_eq!(address_eq(self_addr, quot_lift_addr()), 1);
311311
-- Mirror: src/ix/kernel/check.rs:651-653. Lift requires Eq type
312312
-- to be properly formed (Quot.lift uses Eq in its reduction rule).
313-
let _ = check_eq_type(top, addrs);
313+
check_eq_type(top, addrs);
314314
(2, 6),
315315
QuotKind.Ind =>
316316
assert_eq!(address_eq(self_addr, quot_ind_addr()), 1);
@@ -337,7 +337,7 @@ def check := ⟦
337337
}
338338

339339
fn check_all(consts: List‹&KConstantInfo›, top: List‹&KConstantInfo›, addrs: List‹Addr›) {
340-
let _ = check_canonical_block_sort(top);
340+
check_canonical_block_sort(top);
341341
check_all_iter(consts, top, addrs, 0)
342342
}
343343

@@ -346,7 +346,7 @@ def check := ⟦
346346
match load(consts) {
347347
ListNode.Nil => (),
348348
ListNode.Cons(&ci, rest) =>
349-
let _ = check_const(ci, pos, top, addrs);
349+
check_const(ci, pos, top, addrs);
350350
check_all_iter(rest, top, addrs, pos + 1),
351351
}
352352
}

0 commit comments

Comments
 (0)