selfhost: port forrange — N_FORRANGE parser + cgen + tuple destructure
This commit is contained in:
@@ -140,31 +140,100 @@ fn parsefor(p: *parser) *node = {
|
||||
let pc: i32 = p.curcol;
|
||||
advance(p); // past `for`
|
||||
expecttok(p, tkind.TK_LPAREN, "expected '(' after for");
|
||||
let n: *node = newnode(p.a, nkind.N_FOR, pf, pl, pc);
|
||||
// Three forms (matching C parser):
|
||||
// for (cond) — only cond
|
||||
// for (init; cond; post) — full
|
||||
// for (true) — infinite (cond is nkind.N_TRUE)
|
||||
// Distinguish by counting ';'. Look at first chunk: if it's a
|
||||
// `let` stmt that's the init. Otherwise, parse expr; if next is
|
||||
// ';' it was cond. If we see two ';' total after init, post is
|
||||
// next. Simpler: peek for `let` to decide init form.
|
||||
|
||||
// Four forms (matching C parser):
|
||||
// for (cond) — only cond
|
||||
// for (init; cond; post) — C-style 3-clause
|
||||
// for (let x .. expr) — Hare-style range, single binding
|
||||
// for (let (a, b) .. expr) — range with tuple destructure
|
||||
// Range and 3-clause both lead with `let`, so we commit to consuming
|
||||
// `let` then disambiguate by looking at what follows.
|
||||
if (p.curkind == tkind.TK_LET) {
|
||||
n.lhs = parseletlocal(p); // init (consumes its own ';')
|
||||
n.cond = parseexpr(p);
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after for cond");
|
||||
advance(p); // past `let`
|
||||
|
||||
// Tuple destructure: `for (let (a, b) .. expr)`.
|
||||
if (p.curkind == tkind.TK_LPAREN) {
|
||||
advance(p);
|
||||
let names: *node = nil;
|
||||
let ntail: *node = nil;
|
||||
for (true) {
|
||||
let npf: str = p.curfile;
|
||||
let npl: i32 = p.curline;
|
||||
let npc: i32 = p.curcol;
|
||||
let e: *node = newnode(p.a, nkind.N_IDENT, npf, npl, npc);
|
||||
let nm: str;
|
||||
expectbindname(p, &nm);
|
||||
e.str = nm;
|
||||
if (names == nil) { names = e; }
|
||||
else { ntail.next = e; };
|
||||
ntail = e;
|
||||
if (!accepttok(p, tkind.TK_COMMA)) { break; };
|
||||
};
|
||||
expecttok(p, tkind.TK_RPAREN, "expected ')' in for-range names");
|
||||
expecttok(p, tkind.TK_DOTDOT, "expected '..' after for-range names");
|
||||
let rng: *node = newnode(p.a, nkind.N_FORRANGE, pf, pl, pc);
|
||||
rng.list = names;
|
||||
rng.lhs = parseexpr(p);
|
||||
expecttok(p, tkind.TK_RPAREN, "expected ')' after for");
|
||||
rng.body = parseblock(p);
|
||||
if (accepttok(p, tkind.TK_ELSE)) { rng.els = parseblock(p); };
|
||||
return rng;
|
||||
};
|
||||
|
||||
// Single binding range or C-style let-init. We need to consume
|
||||
// the IDENT/UNDER to know which: if followed by '..' it's a
|
||||
// range; otherwise build a synthetic LET for the C-style for-init
|
||||
// with the consumed name baked in.
|
||||
if (p.curkind == tkind.TK_IDENT || p.curkind == tkind.TK_UNDER) {
|
||||
let isunder: bool = (p.curkind == tkind.TK_UNDER);
|
||||
let nm: str;
|
||||
nm.ptr = nil; nm.len = 0;
|
||||
if (!isunder) { nm = p.curtext; };
|
||||
let lpf: str = p.curfile;
|
||||
let lpl: i32 = p.curline;
|
||||
let lpc: i32 = p.curcol;
|
||||
advance(p); // consume IDENT/UNDER
|
||||
|
||||
if (p.curkind == tkind.TK_DOTDOT) {
|
||||
advance(p);
|
||||
let rng: *node = newnode(p.a, nkind.N_FORRANGE, pf, pl, pc);
|
||||
rng.str = nm; // "" for `_`
|
||||
rng.lhs = parseexpr(p);
|
||||
expecttok(p, tkind.TK_RPAREN, "expected ')' after for");
|
||||
rng.body = parseblock(p);
|
||||
if (accepttok(p, tkind.TK_ELSE)) { rng.els = parseblock(p); };
|
||||
return rng;
|
||||
};
|
||||
|
||||
// Not a range — finish the let manually and continue as
|
||||
// a 3-clause for-init.
|
||||
let first: *node = newnode(p.a, nkind.N_LET, lpf, lpl, lpc);
|
||||
first.str = nm;
|
||||
if (accepttok(p, tkind.TK_COLON)) { first.lhs = parsetype(p); };
|
||||
if (accepttok(p, tkind.TK_ASSIGN)) { first.rhs = parseexpr(p); };
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after for-init let");
|
||||
let n: *node = newnode(p.a, nkind.N_FOR, pf, pl, pc);
|
||||
n.lhs = first;
|
||||
n.cond = parseexpr(p);
|
||||
expecttok(p, tkind.TK_SEMI, "expected ';' after for cond");
|
||||
n.rhs = parseexpr(p);
|
||||
expecttok(p, tkind.TK_RPAREN, "expected ')' after for");
|
||||
n.body = parseblock(p);
|
||||
if (accepttok(p, tkind.TK_ELSE)) { n.els = parseblock(p); };
|
||||
return n;
|
||||
};
|
||||
|
||||
errmsg(p, "expected name after 'let' in for");
|
||||
};
|
||||
|
||||
// for (cond) or for (cond; post)
|
||||
let n: *node = newnode(p.a, nkind.N_FOR, pf, pl, pc);
|
||||
let first: *node = parseexpr(p);
|
||||
if (accepttok(p, tkind.TK_SEMI)) {
|
||||
n.cond = first;
|
||||
n.rhs = parseexpr(p);
|
||||
} else {
|
||||
// Parse one expr. If next is ';', it's a 3-clause without init.
|
||||
let first: *node = parseexpr(p);
|
||||
if (accepttok(p, tkind.TK_SEMI)) {
|
||||
// cond ; post
|
||||
n.cond = first;
|
||||
n.rhs = parseexpr(p);
|
||||
} else {
|
||||
// just (cond)
|
||||
n.cond = first;
|
||||
};
|
||||
n.cond = first;
|
||||
};
|
||||
expecttok(p, tkind.TK_RPAREN, "expected ')' after for");
|
||||
n.body = parseblock(p);
|
||||
|
||||
Reference in New Issue
Block a user