ww: hare-feature batch (_, const, [_]T, ..., size/offset, assert, for-else)
This commit is contained in:
119
cmd/wcc/parse.c
119
cmd/wcc/parse.c
@@ -81,6 +81,19 @@ expectident(Parser *p)
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Like expectident but also accepts a bare `_` discard marker. The
|
||||
* returned string is the empty string "" so the checker skips
|
||||
* scope_define. Callers that care can detect this with `s[0] == '\0'`. */
|
||||
static const char *
|
||||
expectbindname(Parser *p)
|
||||
{
|
||||
if (p->cur.kind == TK_UNDER) {
|
||||
advance(p);
|
||||
return "";
|
||||
}
|
||||
return expectident(p);
|
||||
}
|
||||
|
||||
static Node *parseexpr(Parser *p);
|
||||
static Node *parseunary(Parser *p);
|
||||
static Node *parsetype(Parser *p);
|
||||
@@ -108,11 +121,13 @@ parseparams(Parser *p)
|
||||
break;
|
||||
}
|
||||
Node *n = newnode(p->a, N_PARAM, pp);
|
||||
/* IDENT ':' type OR type-only (fn-type params).
|
||||
* Disambiguate: if current is IDENT and next is ':' it's named.
|
||||
* Otherwise treat as anonymous (type-only). */
|
||||
if (p->cur.kind == TK_IDENT && peek(p).kind == TK_COLON) {
|
||||
n->str = expectident(p);
|
||||
/* IDENT ':' type OR `_' ':' type OR type-only.
|
||||
* Disambiguate: if current is IDENT or '_' and next is ':',
|
||||
* it's a named param. Otherwise treat as anonymous. */
|
||||
int named = (p->cur.kind == TK_IDENT || p->cur.kind == TK_UNDER)
|
||||
&& peek(p).kind == TK_COLON;
|
||||
if (named) {
|
||||
n->str = expectbindname(p);
|
||||
expect(p, TK_COLON);
|
||||
n->lhs = parsetype(p);
|
||||
} else {
|
||||
@@ -150,7 +165,11 @@ parsetype(Parser *p)
|
||||
return n;
|
||||
}
|
||||
Node *n = newnode(p->a, N_TARRAY, pp);
|
||||
n->rhs = parseexpr(p);
|
||||
/* `[_]T` — length inferred from the initialiser. Marked by
|
||||
* leaving n->rhs == NULL; check.c fills in the length from
|
||||
* the array literal's element count. */
|
||||
if (!accept(p, TK_UNDER))
|
||||
n->rhs = parseexpr(p);
|
||||
expect(p, TK_RBRACK);
|
||||
n->lhs = parsetype(p);
|
||||
return n;
|
||||
@@ -332,6 +351,14 @@ parsestructlit(Parser *p, Node *typeref)
|
||||
Node *head = NULL, *tail = NULL;
|
||||
while (p->cur.kind != TK_RBRACE && p->cur.kind != TK_EOF) {
|
||||
Pos fp = p->cur.pos;
|
||||
/* Trailing `...` after the last comma (or as the only entry)
|
||||
* means "zero-init all unmentioned fields". Marked on the
|
||||
* literal node via op = TK_ELLIPSIS; cgen consumes it. */
|
||||
if (p->cur.kind == TK_ELLIPSIS) {
|
||||
advance(p);
|
||||
n->op = TK_ELLIPSIS;
|
||||
break;
|
||||
}
|
||||
const char *name = expectident(p);
|
||||
expect(p, TK_ASSIGN);
|
||||
Node *val = parseexpr(p);
|
||||
@@ -416,6 +443,16 @@ parseprimary(Parser *p)
|
||||
case TK_TRUE: advance(p); return newnode(p->a, N_TRUE, pp);
|
||||
case TK_FALSE: advance(p); return newnode(p->a, N_FALSE, pp);
|
||||
case TK_NIL: advance(p); return newnode(p->a, N_NIL, pp);
|
||||
case TK_UNDER: {
|
||||
/* Bare `_` — valid only as a discard lvalue. We yield an N_IDENT
|
||||
* with empty str; the checker rejects it outside assignment
|
||||
* lvalue positions. */
|
||||
advance(p);
|
||||
Node *n = newnode(p->a, N_IDENT, pp);
|
||||
n->str = "";
|
||||
n->strlen = 0;
|
||||
return n;
|
||||
}
|
||||
case TK_LPAREN: {
|
||||
advance(p);
|
||||
Node *e = parseexpr(p);
|
||||
@@ -535,7 +572,16 @@ parsepostfix(Parser *p, Node *lhs)
|
||||
advance(p);
|
||||
Node *n = newnode(p->a, N_CALL, pp);
|
||||
n->lhs = lhs;
|
||||
n->list = parsearglist(p, TK_RPAREN);
|
||||
/* size(T)/align(T): the single arg is a type expression,
|
||||
* not a regular expression — types like []u8 cannot parse
|
||||
* as expressions. Special-case at the parser. */
|
||||
if (lhs->kind == N_IDENT && lhs->str &&
|
||||
(strcmp(lhs->str, "size") == 0 ||
|
||||
strcmp(lhs->str, "align") == 0)) {
|
||||
n->list = parsetype(p);
|
||||
} else {
|
||||
n->list = parsearglist(p, TK_RPAREN);
|
||||
}
|
||||
expect(p, TK_RPAREN);
|
||||
lhs = n;
|
||||
break;
|
||||
@@ -715,7 +761,13 @@ static Node *
|
||||
parselet(Parser *p, int top)
|
||||
{
|
||||
Pos pp = p->cur.pos;
|
||||
expect(p, TK_LET);
|
||||
int is_const = 0;
|
||||
if (p->cur.kind == TK_CONST) {
|
||||
is_const = 1;
|
||||
advance(p);
|
||||
} else {
|
||||
expect(p, TK_LET);
|
||||
}
|
||||
|
||||
/* Hare-style tuple destructure: `let (a, b) = expr;` */
|
||||
if (p->cur.kind == TK_LPAREN) {
|
||||
@@ -725,7 +777,7 @@ parselet(Parser *p, int top)
|
||||
for (;;) {
|
||||
Pos lpp = p->cur.pos;
|
||||
Node *l = newnode(p->a, N_LET, lpp);
|
||||
l->str = expectident(p);
|
||||
l->str = expectbindname(p);
|
||||
if (accept(p, TK_COLON)) l->lhs = parsetype(p);
|
||||
if (head == NULL) head = l;
|
||||
else tail->next = l;
|
||||
@@ -737,6 +789,10 @@ parselet(Parser *p, int top)
|
||||
m->rhs = parseexpr(p);
|
||||
expect(p, TK_SEMI);
|
||||
m->list = head;
|
||||
if (is_const) {
|
||||
m->op = TK_CONST;
|
||||
for (Node *l = head; l; l = l->next) l->op = TK_CONST;
|
||||
}
|
||||
(void)top;
|
||||
return m;
|
||||
}
|
||||
@@ -744,7 +800,7 @@ parselet(Parser *p, int top)
|
||||
/* parse first binding */
|
||||
Pos lp = p->cur.pos;
|
||||
Node *first = newnode(p->a, N_LET, lp);
|
||||
first->str = expectident(p);
|
||||
first->str = expectbindname(p);
|
||||
if (accept(p, TK_COLON))
|
||||
first->lhs = parsetype(p);
|
||||
|
||||
@@ -755,7 +811,7 @@ parselet(Parser *p, int top)
|
||||
while (accept(p, TK_COMMA)) {
|
||||
Pos lpp = p->cur.pos;
|
||||
Node *l = newnode(p->a, N_LET, lpp);
|
||||
l->str = expectident(p);
|
||||
l->str = expectbindname(p);
|
||||
if (accept(p, TK_COLON))
|
||||
l->lhs = parsetype(p);
|
||||
tail->next = l;
|
||||
@@ -765,6 +821,10 @@ parselet(Parser *p, int top)
|
||||
m->rhs = parseexpr(p);
|
||||
expect(p, TK_SEMI);
|
||||
m->list = head;
|
||||
if (is_const) {
|
||||
m->op = TK_CONST;
|
||||
for (Node *l = head; l; l = l->next) l->op = TK_CONST;
|
||||
}
|
||||
(void)top;
|
||||
return m;
|
||||
}
|
||||
@@ -772,6 +832,7 @@ parselet(Parser *p, int top)
|
||||
if (accept(p, TK_ASSIGN))
|
||||
first->rhs = parseexpr(p);
|
||||
expect(p, TK_SEMI);
|
||||
if (is_const) first->op = TK_CONST;
|
||||
(void)top;
|
||||
return first;
|
||||
}
|
||||
@@ -795,6 +856,17 @@ parseif(Parser *p)
|
||||
return n;
|
||||
}
|
||||
|
||||
static Node *
|
||||
parse_for_else(Parser *p, Node *n)
|
||||
{
|
||||
/* `for (cond) { body } else { else_body }` — the else block runs
|
||||
* when the loop exits normally (cond → false) and is skipped by
|
||||
* `break`. Hare-style "did the loop find anything" idiom. */
|
||||
if (accept(p, TK_ELSE))
|
||||
n->els = parseblock(p);
|
||||
return n;
|
||||
}
|
||||
|
||||
static Node *
|
||||
parsefor(Parser *p)
|
||||
{
|
||||
@@ -803,7 +875,7 @@ parsefor(Parser *p)
|
||||
Node *n = newnode(p->a, N_FOR, pp);
|
||||
if (p->cur.kind == TK_LBRACE) {
|
||||
n->body = parseblock(p);
|
||||
return n;
|
||||
return parse_for_else(p, n);
|
||||
}
|
||||
expect(p, TK_LPAREN);
|
||||
if (p->cur.kind == TK_RPAREN) {
|
||||
@@ -826,7 +898,7 @@ parsefor(Parser *p)
|
||||
for (;;) {
|
||||
Pos np = p->cur.pos;
|
||||
Node *e = newnode(p->a, N_IDENT, np);
|
||||
e->str = expectident(p);
|
||||
e->str = expectbindname(p);
|
||||
if (names == NULL) names = e;
|
||||
else tail->next = e;
|
||||
tail = e;
|
||||
@@ -840,29 +912,30 @@ parsefor(Parser *p)
|
||||
rng->lhs = parseexpr(p);
|
||||
expect(p, TK_RPAREN);
|
||||
rng->body = parseblock(p);
|
||||
return rng;
|
||||
return parse_for_else(p, rng);
|
||||
}
|
||||
if (p->cur.kind == TK_IDENT) {
|
||||
if (p->cur.kind == TK_IDENT || p->cur.kind == TK_UNDER) {
|
||||
Pos ip = p->cur.pos;
|
||||
const char *nm = p->cur.text;
|
||||
int isunder = p->cur.kind == TK_UNDER;
|
||||
Tok la = peek(p);
|
||||
if (la.kind == TK_DOTDOT) {
|
||||
advance(p); /* consume IDENT */
|
||||
advance(p); /* consume IDENT/UNDER */
|
||||
advance(p); /* consume DOTDOT */
|
||||
Node *rng = newnode(p->a, N_FORRANGE, pp);
|
||||
rng->str = nm;
|
||||
rng->str = isunder ? "" : nm;
|
||||
rng->lhs = parseexpr(p);
|
||||
expect(p, TK_RPAREN);
|
||||
rng->body = parseblock(p);
|
||||
(void)ip;
|
||||
return rng;
|
||||
return parse_for_else(p, rng);
|
||||
}
|
||||
}
|
||||
/* Not a range. Build a synthetic LET stmt manually
|
||||
* since we already consumed `let`. */
|
||||
Pos lp = p->cur.pos;
|
||||
Node *first = newnode(p->a, N_LET, lp);
|
||||
first->str = expectident(p);
|
||||
first->str = expectbindname(p);
|
||||
if (accept(p, TK_COLON))
|
||||
first->lhs = parsetype(p);
|
||||
if (accept(p, TK_ASSIGN))
|
||||
@@ -885,7 +958,7 @@ parsefor(Parser *p)
|
||||
}
|
||||
expect(p, TK_RPAREN);
|
||||
n->body = parseblock(p);
|
||||
return n;
|
||||
return parse_for_else(p, n);
|
||||
}
|
||||
|
||||
static Node *
|
||||
@@ -953,7 +1026,8 @@ parsestmt(Parser *p)
|
||||
expect(p, TK_SEMI);
|
||||
return b;
|
||||
}
|
||||
case TK_LET: return parselet(p, 0);
|
||||
case TK_LET:
|
||||
case TK_CONST: return parselet(p, 0);
|
||||
case TK_IF: {
|
||||
Node *n = parseif(p);
|
||||
expect(p, TK_SEMI);
|
||||
@@ -1166,7 +1240,8 @@ parsefile(Parser *p)
|
||||
case TK_DEF: d = parsedef(p, exp); break;
|
||||
case TK_TYPE: d = parsetypedecl(p, exp); break;
|
||||
case TK_FN: d = parsefn(p, exp, attrs); break;
|
||||
case TK_LET: d = parselet(p, 1); d->export = exp; break;
|
||||
case TK_LET:
|
||||
case TK_CONST: d = parselet(p, 1); d->export = exp; break;
|
||||
default:
|
||||
errorf(p->cur.pos, "expected top-level decl, got %s",
|
||||
tokname(p->cur.kind));
|
||||
|
||||
Reference in New Issue
Block a user