syntax: align for-header grammar to cstage (Go's three forms)
The bbdd8bed residue, ruled by the Go derivation (ww's for IS the Go
for; the cstage grammar is the language definition and already the
Go-shaped one):
- bare `for { }` accepted (align UP to parse.c:1023, incl. for-else);
- 3-clause cond and post each omissible — `for (init;; post)`,
`for (init; cond)`, `for (init;)` (align UP, parse.c:1092-1097);
- 2-clause `for (cond; post)` REJECTED (align DOWN): the form exists
in neither Go nor cstage (parse.c:1101 eats a stray ';' then
requires ')'), so the old wwstage accept was a parser bug. The
reject wording mirrors cstage's failure mode per stage.
forhdr_{brace,emptymid,partial} run fixtures pin the accepted forms
byte-identically; forhdr_twoclause_reject pins both reject fragments.
Corpus pin 1745/346/21/209/1169/3490.
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
package wwfixture;
|
package wwfixture;
|
||||||
|
|
||||||
def protocolversion: i32 = 1;
|
def protocolversion: i32 = 1;
|
||||||
def corpuscount: i32 = 1741;
|
def corpuscount: i32 = 1745;
|
||||||
def errorcount: i32 = 345;
|
def errorcount: i32 = 346;
|
||||||
def compilecount: i32 = 21;
|
def compilecount: i32 = 21;
|
||||||
def runcount: i32 = 209;
|
def runcount: i32 = 209;
|
||||||
def runexitcount: i32 = 1166;
|
def runexitcount: i32 = 1169;
|
||||||
def nativecount: i32 = 3482;
|
def nativecount: i32 = 3490;
|
||||||
def corpushash: str = "bad5b37d555f3f4d742f7a6fcfa857ff46315757233819a19eb9bc1da2800ce7";
|
def corpushash: str = "472974a00552c6b3f5db1fc6b0c1ed128d7554e134880196c833f11f99370f21";
|
||||||
|
|
||||||
type directive = enum i32 {
|
type directive = enum i32 {
|
||||||
ERROR = 0,
|
ERROR = 0,
|
||||||
|
|||||||
@@ -139,12 +139,24 @@ fn parsefor(p: *parser) *node = {
|
|||||||
let pl = p.curline;
|
let pl = p.curline;
|
||||||
let pc = p.curcol;
|
let pc = p.curcol;
|
||||||
advance(p); // past `for`
|
advance(p); // past `for`
|
||||||
|
|
||||||
|
// Go's bare `for { }` — no header at all (cmd/wcc/parse.c:1023;
|
||||||
|
// Go's for has exactly three forms and this is the empty one).
|
||||||
|
// Keeps the for-else arm exactly as the cstage brace form does.
|
||||||
|
if (p.curkind == tkind.TK_LBRACE) {
|
||||||
|
let n = newnode(nkind.N_FOR, pf, pl, pc);
|
||||||
|
n.body = parseblock(p);
|
||||||
|
if (accepttok(p, tkind.TK_ELSE)) { n.els = parseblock(p); };
|
||||||
|
return n;
|
||||||
|
};
|
||||||
expecttok(p, tkind.TK_LPAREN, "expected '(' after for");
|
expecttok(p, tkind.TK_LPAREN, "expected '(' after for");
|
||||||
|
|
||||||
// Five forms (matching C parser):
|
// Six forms (matching C parser):
|
||||||
|
// for { } — bare Go form, no header
|
||||||
// for () — empty cond, unconditional loop
|
// for () — empty cond, unconditional loop
|
||||||
// for (cond) — only cond
|
// for (cond) — only cond
|
||||||
// for (init; cond; post) — C-style 3-clause
|
// for (init; cond; post) — C-style 3-clause (cond and
|
||||||
|
// post each omissible, parse.c:1092)
|
||||||
// for (let x .. expr) — Hare-style range, single binding
|
// for (let x .. expr) — Hare-style range, single binding
|
||||||
// for (let (a, b) .. expr) — range with tuple destructure
|
// for (let (a, b) .. expr) — range with tuple destructure
|
||||||
|
|
||||||
@@ -225,9 +237,15 @@ fn parsefor(p: *parser) *node = {
|
|||||||
expecttok(p, tkind.TK_SEMI, "expected ';' after for-init let");
|
expecttok(p, tkind.TK_SEMI, "expected ';' after for-init let");
|
||||||
let n = newnode(nkind.N_FOR, pf, pl, pc);
|
let n = newnode(nkind.N_FOR, pf, pl, pc);
|
||||||
n.lhs = first;
|
n.lhs = first;
|
||||||
n.cond = parseexpr(p);
|
// cond and post are each omissible (`for (init;; post)`,
|
||||||
expecttok(p, tkind.TK_SEMI, "expected ';' after for cond");
|
// `for (init; cond)`, `for (init;)`) — mirror cstage
|
||||||
n.rhs = parseexpr(p);
|
// parse.c:1092-1097 exactly.
|
||||||
|
if (p.curkind != tkind.TK_SEMI && p.curkind != tkind.TK_RPAREN) {
|
||||||
|
n.cond = parseexpr(p);
|
||||||
|
};
|
||||||
|
if (accepttok(p, tkind.TK_SEMI)) {
|
||||||
|
n.rhs = parseexpr(p);
|
||||||
|
};
|
||||||
expecttok(p, tkind.TK_RPAREN, "expected ')' after for");
|
expecttok(p, tkind.TK_RPAREN, "expected ')' after for");
|
||||||
n.body = parseblock(p);
|
n.body = parseblock(p);
|
||||||
if (accepttok(p, tkind.TK_ELSE)) { n.els = parseblock(p); };
|
if (accepttok(p, tkind.TK_ELSE)) { n.els = parseblock(p); };
|
||||||
@@ -237,15 +255,14 @@ fn parsefor(p: *parser) *node = {
|
|||||||
errmsg(p, "expected name after 'let' in for");
|
errmsg(p, "expected name after 'let' in for");
|
||||||
};
|
};
|
||||||
|
|
||||||
// for (cond) or for (cond; post)
|
// for (cond) — a 2-clause `for (cond; post)` is NOT a form: it
|
||||||
|
// exists in neither Go's grammar nor the cstage one (parse.c:1101
|
||||||
|
// consumes a stray ';' then requires ')'), so the old wwstage
|
||||||
|
// accept was a parser bug, not a feature. Mirror cstage: eat the
|
||||||
|
// stray ';' and demand ')' — a post expression then fails loud.
|
||||||
let n = newnode(nkind.N_FOR, pf, pl, pc);
|
let n = newnode(nkind.N_FOR, pf, pl, pc);
|
||||||
let first = parseexpr(p);
|
n.cond = parseexpr(p);
|
||||||
if (accepttok(p, tkind.TK_SEMI)) {
|
if (accepttok(p, tkind.TK_SEMI)) { };
|
||||||
n.cond = first;
|
|
||||||
n.rhs = parseexpr(p);
|
|
||||||
} else {
|
|
||||||
n.cond = first;
|
|
||||||
};
|
|
||||||
expecttok(p, tkind.TK_RPAREN, "expected ')' after for");
|
expecttok(p, tkind.TK_RPAREN, "expected ')' after for");
|
||||||
n.body = parseblock(p);
|
n.body = parseblock(p);
|
||||||
// Optional `else { ... }` — runs at normal cond-false exit; skipped
|
// Optional `else { ... }` — runs at normal cond-false exit; skipped
|
||||||
|
|||||||
10
test/wcc/data/forhdr_brace/case.ww
Normal file
10
test/wcc/data/forhdr_brace/case.ww
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
//ww:run-exit 5
|
||||||
|
package main;
|
||||||
|
export fn main() i32 = {
|
||||||
|
let i: i32 = 0;
|
||||||
|
for {
|
||||||
|
i += 1;
|
||||||
|
if (i >= 5) { break; };
|
||||||
|
};
|
||||||
|
return i;
|
||||||
|
};
|
||||||
10
test/wcc/data/forhdr_emptymid/case.ww
Normal file
10
test/wcc/data/forhdr_emptymid/case.ww
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
//ww:run-exit 10
|
||||||
|
package main;
|
||||||
|
export fn main() i32 = {
|
||||||
|
let s: i32 = 0;
|
||||||
|
for (let i: i32 = 0;; i += 1) {
|
||||||
|
if (i >= 4) { break; };
|
||||||
|
s += i;
|
||||||
|
};
|
||||||
|
return s + 4;
|
||||||
|
};
|
||||||
20
test/wcc/data/forhdr_partial/case.ww
Normal file
20
test/wcc/data/forhdr_partial/case.ww
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//ww:run-exit 12
|
||||||
|
package main;
|
||||||
|
export fn main() i32 = {
|
||||||
|
let a: i32 = 0;
|
||||||
|
for (let i: i32 = 0; i < 5) {
|
||||||
|
i += 1;
|
||||||
|
a = i;
|
||||||
|
};
|
||||||
|
let b: i32 = 0;
|
||||||
|
for (let j: i32 = 0;) {
|
||||||
|
j += 1;
|
||||||
|
if (j >= 3) { break; };
|
||||||
|
};
|
||||||
|
b = 3;
|
||||||
|
let k: i32 = 0;
|
||||||
|
for (k < 4;) {
|
||||||
|
k += 1;
|
||||||
|
};
|
||||||
|
return a + b + k;
|
||||||
|
};
|
||||||
12
test/wcc/data/forhdr_twoclause_reject/case.ww
Normal file
12
test/wcc/data/forhdr_twoclause_reject/case.ww
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
//ww:error c "expected ), got IDENT" ww "expected ')' after for"
|
||||||
|
// 2-clause `for (cond; post)` is a form in neither Go nor the cstage
|
||||||
|
// grammar — the old wwstage accept was a parser bug (aligned DOWN,
|
||||||
|
// rule 10 / the Go-derivation ruling). Both parsers consume the stray
|
||||||
|
// ';' and then demand ')'.
|
||||||
|
package main;
|
||||||
|
export fn main() i32 = {
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i < 5; i += 1) {
|
||||||
|
};
|
||||||
|
return i;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user