Skip to content

Commit a101d4e

Browse files
olwangclaude
andcommitted
feat(selfhost): AST producer — manage/spawn/await exprs + with/task-group/select stmts → 331/578
Large batch of previously-unhandled async/resource constructs (46 corpus files use these keywords): - Prefix expressions `manage X` / `spawn X` / `await X` (value child), checked after the effect branch, before postfix (matching parse_expr order). - `with <resource> as <binding> { body }` -> `with binding=N` + resource + block (first-`as` split, mirroring parse_with_stmt). - `task_group { body }` -> `task-group` + block. - `select { <b> = <op> => { body } ... }` -> `select` + per-arm `select-arm binding=N` + operation + block (arm loop mirrors match arms). Corpus reach: 286 -> 331 / 578 byte-exact (+43 net), still 0 run-failures. Floor -> 331. New samples prefix_exprs.rss, resource_stmts.rss; 23 samples green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 941e2d3 commit a101d4e

4 files changed

Lines changed: 142 additions & 1 deletion

File tree

crates/rsscript/src/selfhost_parity.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1522,7 +1522,7 @@ const AST_SAMPLE_MIN: usize = 6;
15221522
/// Floor for `ast_parity_corpus` — the number of corpus files whose rss AST dump
15231523
/// already matches the oracle byte-for-byte. Ratchets up as the producer's
15241524
/// coverage grows; a drop signals a regression. (Full parity = files.len().)
1525-
const AST_CORPUS_PARITY_FLOOR: usize = 286;
1525+
const AST_CORPUS_PARITY_FLOOR: usize = 331;
15261526

15271527
/// Step-2 measurement gate (ignored by default): how many corpus files the rss
15281528
/// producer reproduces byte-for-byte. Not full parity yet — this ratchets a floor

selfhost/astdump.rss

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,22 @@ fn emit_expr(toks: read List<Tok>, start: read Int, end: read Int, d: read Int)
317317
emit_expr(toks: read toks, start: read (s + 1), end: read e, d: read (d + 1))
318318
return Unit
319319
}
320+
// Prefix expression forms `manage X` / `spawn X` / `await X` (value child).
321+
if is_kw_text(toks: read toks, i: read s, word: read "manage") {
322+
emit(depth: read d, s: read "manage")
323+
emit_expr(toks: read toks, start: read (s + 1), end: read e, d: read (d + 1))
324+
return Unit
325+
}
326+
if is_kw_text(toks: read toks, i: read s, word: read "spawn") {
327+
emit(depth: read d, s: read "spawn")
328+
emit_expr(toks: read toks, start: read (s + 1), end: read e, d: read (d + 1))
329+
return Unit
330+
}
331+
if is_kw_text(toks: read toks, i: read s, word: read "await") {
332+
emit(depth: read d, s: read "await")
333+
emit_expr(toks: read toks, start: read (s + 1), end: read e, d: read (d + 1))
334+
return Unit
335+
}
320336
emit_postfix(toks: read toks, start: read s, end: read e, d: read d)
321337
return Unit
322338
}
@@ -1090,6 +1106,18 @@ fn emit_stmt(toks: read List<Tok>, start: read Int, limit: read Int, d: read Int
10901106
if is_kw_text(toks: read toks, i: read start, word: read "await") && is_kw_text(toks: read toks, i: read (start + 1), word: read "for") {
10911107
return emit_for(toks: read toks, forPos: read (start + 1), limit: read limit, d: read d, isAsync: read true)
10921108
}
1109+
// with <resource> as <binding> { body }
1110+
if is_kw_text(toks: read toks, i: read start, word: read "with") {
1111+
return emit_with(toks: read toks, start: read start, limit: read limit, d: read d)
1112+
}
1113+
// task_group { body }
1114+
if is_kw_text(toks: read toks, i: read start, word: read "task_group") {
1115+
return emit_task_group(toks: read toks, start: read start, limit: read limit, d: read d)
1116+
}
1117+
// select { arms }
1118+
if is_kw_text(toks: read toks, i: read start, word: read "select") {
1119+
return emit_select(toks: read toks, start: read start, limit: read limit, d: read d)
1120+
}
10931121
// break / continue
10941122
if is_kw_text(toks: read toks, i: read start, word: read "break") {
10951123
emit(depth: read d, s: read "break")
@@ -1204,6 +1232,98 @@ fn emit_for(toks: read List<Tok>, forPos: read Int, limit: read Int, d: read Int
12041232
return close + 1
12051233
}
12061234

1235+
// with <resource> as <binding> { body }. Mirrors parse_with_stmt: the FIRST `as`
1236+
// (not depth-aware) splits resource from binding; `as` must be immediately before
1237+
// the body brace.
1238+
fn emit_with(toks: read List<Tok>, start: read Int, limit: read Int, d: read Int) -> Int {
1239+
let mut asIdx = -1
1240+
let mut i = start + 1
1241+
while i < limit && asIdx < 0 {
1242+
if is_kw_text(toks: read toks, i: read i, word: read "as") { asIdx = i }
1243+
i = i + 1
1244+
}
1245+
if asIdx < 0 {
1246+
emit(depth: read d, s: read "malformed-with")
1247+
return stmt_end(toks: read toks, start: read start, limit: read limit)
1248+
}
1249+
let open = find_block_open(toks: read toks, start: read (asIdx + 1), limit: read limit)
1250+
if open < 0 {
1251+
emit(depth: read d, s: read "malformed-with")
1252+
return stmt_end(toks: read toks, start: read start, limit: read limit)
1253+
}
1254+
let close = find_matching(toks: read toks, open: read open, openSym: read SYM_LBRACE, closeSym: read SYM_RBRACE)
1255+
let binding = tk_text(toks: read toks, i: read (asIdx + 1))
1256+
emit(depth: read d, s: read String.concat(left: read "with binding=", right: read binding))
1257+
emit(depth: read (d + 1), s: read "resource")
1258+
emit_expr(toks: read toks, start: read (start + 1), end: read asIdx, d: read (d + 2))
1259+
emit_block(toks: read toks, open: read open, close: read close, d: read (d + 1))
1260+
return close + 1
1261+
}
1262+
1263+
// task_group { body }
1264+
fn emit_task_group(toks: read List<Tok>, start: read Int, limit: read Int, d: read Int) -> Int {
1265+
let open = start + 1
1266+
if at_symbol(toks: read toks, i: read open, code: read SYM_LBRACE) == false {
1267+
emit(depth: read d, s: read "unknown-stmt")
1268+
return stmt_end(toks: read toks, start: read start, limit: read limit)
1269+
}
1270+
let close = find_matching(toks: read toks, open: read open, openSym: read SYM_LBRACE, closeSym: read SYM_RBRACE)
1271+
emit(depth: read d, s: read "task-group")
1272+
emit_block(toks: read toks, open: read open, close: read close, d: read (d + 1))
1273+
return close + 1
1274+
}
1275+
1276+
// select { <binding> = <operation> => { body } ... }
1277+
fn emit_select(toks: read List<Tok>, start: read Int, limit: read Int, d: read Int) -> Int {
1278+
let open = start + 1
1279+
if at_symbol(toks: read toks, i: read open, code: read SYM_LBRACE) == false {
1280+
emit(depth: read d, s: read "unknown-stmt")
1281+
return stmt_end(toks: read toks, start: read start, limit: read limit)
1282+
}
1283+
let close = find_matching(toks: read toks, open: read open, openSym: read SYM_LBRACE, closeSym: read SYM_RBRACE)
1284+
emit(depth: read d, s: read "select")
1285+
emit_select_arms(toks: read toks, start: read (open + 1), end: read close, d: read (d + 1))
1286+
return close + 1
1287+
}
1288+
1289+
fn emit_select_arms(toks: read List<Tok>, start: read Int, end: read Int, d: read Int) -> Unit {
1290+
let mut i = start
1291+
while i < end {
1292+
if at_symbol(toks: read toks, i: read i, code: read SYM_SEMI) {
1293+
i = i + 1
1294+
} else {
1295+
let lineEnd = next_line_or_block_end(toks: read toks, start: read i, end: read end)
1296+
let arrow = find_top_sym(toks: read toks, start: read i, end: read lineEnd, code: read SYM_FATARROW)
1297+
if arrow < 0 {
1298+
if lineEnd > i { i = lineEnd } else { i = i + 1 }
1299+
} else {
1300+
let eq = find_top_sym(toks: read toks, start: read i, end: read arrow, code: read SYM_EQ)
1301+
if eq < 0 {
1302+
i = arrow + 1
1303+
} else {
1304+
let mut binding = "_"
1305+
if is_ident_tok(toks: read toks, i: read i) { binding = tk_text(toks: read toks, i: read i) }
1306+
emit(depth: read d, s: read String.concat(left: read "select-arm binding=", right: read binding))
1307+
emit(depth: read (d + 1), s: read "operation")
1308+
emit_expr(toks: read toks, start: read (eq + 1), end: read arrow, d: read (d + 2))
1309+
let bodyStart = arrow + 1
1310+
if at_symbol(toks: read toks, i: read bodyStart, code: read SYM_LBRACE) {
1311+
let bclose = find_matching(toks: read toks, open: read bodyStart, openSym: read SYM_LBRACE, closeSym: read SYM_RBRACE)
1312+
emit_block(toks: read toks, open: read bodyStart, close: read bclose, d: read (d + 1))
1313+
i = bclose + 1
1314+
} else {
1315+
let bEnd = next_line_or_block_end(toks: read toks, start: read bodyStart, end: read end)
1316+
emit(depth: read (d + 1), s: read "block")
1317+
emit_stmt(toks: read toks, start: read bodyStart, limit: read bEnd, d: read (d + 2))
1318+
if bEnd > i { i = bEnd } else { i = i + 1 }
1319+
}
1320+
}
1321+
}
1322+
}
1323+
}
1324+
return Unit
1325+
}
1326+
12071327
fn emit_loop(toks: read List<Tok>, start: read Int, limit: read Int, d: read Int) -> Int {
12081328
let isWhile = is_kw_text(toks: read toks, i: read start, word: read "while")
12091329
let openb = find_block_open(toks: read toks, start: read (start + 1), limit: read limit)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn work(pool: read Pool) -> Int {
2+
let r = manage pool.acquire()
3+
let h = spawn compute()
4+
let v = await h
5+
return 0
6+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fn run(pool: read Pool) -> Int {
2+
with pool.acquire() as r {
3+
return 0
4+
}
5+
task_group {
6+
return 1
7+
}
8+
}
9+
10+
fn pick(a: read Chan, b: read Chan) -> Int {
11+
select {
12+
x = await a => { return x }
13+
y = await b => { return y }
14+
}
15+
}

0 commit comments

Comments
 (0)