ww: hare-feature batch (_, const, [_]T, ..., size/offset, assert, for-else)
This commit is contained in:
@@ -40,11 +40,15 @@ fn parselet(p: *parser, exported: i32) *node = {
|
||||
let pf: str = p.curfile;
|
||||
let pl: i32 = p.curline;
|
||||
let pc: i32 = p.curcol;
|
||||
advance(p); // past `let`
|
||||
// Accept `let` or `const`. Const-bound bindings are marked via
|
||||
// n.op = TK_CONST so the checker can reject reassignment.
|
||||
let is_const: i32 = 0;
|
||||
if (p.curkind == TK_CONST) { is_const = 1; };
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_LET, pf, pl, pc);
|
||||
n.module = p.l.module;
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
expectbindname(p, &id);
|
||||
n.str = id;
|
||||
if (accepttok(p, TK_COLON)) {
|
||||
n.lhs = parsetype(p);
|
||||
@@ -54,6 +58,7 @@ fn parselet(p: *parser, exported: i32) *node = {
|
||||
};
|
||||
expecttok(p, TK_SEMI, "expected ';' after let");
|
||||
n.exported = exported;
|
||||
if (is_const != 0) { n.op = TK_CONST; };
|
||||
return n;
|
||||
};
|
||||
|
||||
@@ -89,10 +94,10 @@ fn parseparams(p: *parser) *node = {
|
||||
let pl: i32 = p.curline;
|
||||
let pc: i32 = p.curcol;
|
||||
let n: *node = newnode(p.a, N_PARAM, pf, pl, pc);
|
||||
// Param form: IDENT ':' type. Anonymous-type-only params (used
|
||||
// in fn type expressions) aren't yet wired here.
|
||||
// Param form: (IDENT|'_') ':' type. Anonymous-type-only params
|
||||
// (used in fn type expressions) aren't yet wired here.
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
expectbindname(p, &id);
|
||||
n.str = id;
|
||||
expecttok(p, TK_COLON, "expected ':' in parameter");
|
||||
n.lhs = parsetype(p);
|
||||
|
||||
@@ -4,6 +4,18 @@ use os;
|
||||
use mem;
|
||||
use tok;
|
||||
|
||||
// streq_local — str-to-str compare. Inlined here to avoid a cross-
|
||||
// module `use sym;` for one call site.
|
||||
fn streq_local(a: str, b: str) bool = {
|
||||
if (a.len != b.len) { return false; };
|
||||
let i: i32 = 0;
|
||||
for (i < a.len) {
|
||||
if (a[i] != b[i]) { return false; };
|
||||
i += 1;
|
||||
};
|
||||
return true;
|
||||
};
|
||||
|
||||
fn parseprimary(p: *parser) *node = {
|
||||
let pf: str = p.curfile;
|
||||
let pl: i32 = p.curline;
|
||||
@@ -40,6 +52,43 @@ fn parseprimary(p: *parser) *node = {
|
||||
advance(p);
|
||||
return newnode(p.a, N_NIL, pf, pl, pc);
|
||||
};
|
||||
if (p.curkind == TK_UNDER) {
|
||||
// Bare `_` — valid only as a discard lvalue. Emit an N_IDENT
|
||||
// with empty str; the checker rejects it outside lvalue
|
||||
// positions.
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_IDENT, pf, pl, pc);
|
||||
let empty: str;
|
||||
n.str = empty;
|
||||
return n;
|
||||
};
|
||||
if (p.curkind == TK_LBRACK) {
|
||||
// Array literal `[a, b, c]` or `[v, w...]` (repeat suffix).
|
||||
// The repeat marker is an N_FIELD node with str = "..."
|
||||
// appended to the element list so cgen can detect it.
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_ARRLIT, pf, pl, pc);
|
||||
let head: *node = nil;
|
||||
let tail: *node = nil;
|
||||
for (p.curkind != TK_RBRACK) {
|
||||
if (p.curkind == TK_EOF) { break; };
|
||||
let e: *node = parseexpr(p);
|
||||
if (head == nil) { head = e; tail = e; }
|
||||
else { tail.next = e; tail = e; };
|
||||
if (accepttok(p, TK_ELLIPSIS)) {
|
||||
let rep: *node = newnode(p.a, N_FIELD,
|
||||
p.curfile, p.curline, p.curcol);
|
||||
rep.str = "...";
|
||||
tail.next = rep;
|
||||
tail = rep;
|
||||
break;
|
||||
};
|
||||
if (!accepttok(p, TK_COMMA)) { break; };
|
||||
};
|
||||
expecttok(p, TK_RBRACK, "expected ']' after array literal");
|
||||
n.list = head;
|
||||
return n;
|
||||
};
|
||||
if (p.curkind == TK_LPAREN) {
|
||||
advance(p);
|
||||
let e: *node = parseexpr(p);
|
||||
@@ -79,6 +128,13 @@ fn parseprimary(p: *parser) *node = {
|
||||
let tail: *node = nil;
|
||||
for (p.curkind != TK_RBRACE) {
|
||||
if (p.curkind == TK_EOF) { break; };
|
||||
// Trailing `...` autofill marker. Stash on s.op so
|
||||
// cgen can zero-fill the slot before per-field stores.
|
||||
if (p.curkind == TK_ELLIPSIS) {
|
||||
advance(p);
|
||||
s.op = TK_ELLIPSIS;
|
||||
break;
|
||||
};
|
||||
let fpf: str = p.curfile;
|
||||
let fpl: i32 = p.curline;
|
||||
let fpc: i32 = p.curcol;
|
||||
@@ -164,9 +220,20 @@ fn parsepostfix(p: *parser, lhs: *node) *node = {
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_CALL, pf, pl, pc);
|
||||
n.lhs = cur;
|
||||
let arghead: *node = nil;
|
||||
parsearglist(p, TK_RPAREN, &arghead);
|
||||
n.list = arghead;
|
||||
// size(T)/align(T): the single arg is a type expression,
|
||||
// not a regular expression. Special-case at the parser.
|
||||
let is_typeop: i32 = 0;
|
||||
if (cur.kind == N_IDENT) {
|
||||
if (streq_local(cur.str, "size")) { is_typeop = 1; };
|
||||
if (streq_local(cur.str, "align")) { is_typeop = 1; };
|
||||
};
|
||||
if (is_typeop != 0) {
|
||||
n.list = parsetype(p);
|
||||
} else {
|
||||
let arghead: *node = nil;
|
||||
parsearglist(p, TK_RPAREN, &arghead);
|
||||
n.list = arghead;
|
||||
};
|
||||
expecttok(p, TK_RPAREN, "expected ')' after args");
|
||||
cur = n;
|
||||
continue;
|
||||
|
||||
@@ -85,6 +85,19 @@ fn expectident(p: *parser, into: *str) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
// expectbindname — like expectident but also accepts a bare `_`
|
||||
// discard marker. On `_`, returns "" so the checker skips
|
||||
// scope_define for the binding.
|
||||
fn expectbindname(p: *parser, into: *str) bool = {
|
||||
if (p.curkind == TK_UNDER) {
|
||||
let empty: str;
|
||||
*into = empty;
|
||||
advance(p);
|
||||
return true;
|
||||
};
|
||||
return expectident(p, into);
|
||||
};
|
||||
|
||||
// ---- type expressions ------------------------------------------------
|
||||
//
|
||||
// Currently: TNAME (single ident, no dotted path yet) and TPTR (`*T`).
|
||||
@@ -112,7 +125,14 @@ fn parsetype(p: *parser) *node = {
|
||||
return n;
|
||||
};
|
||||
let n: *node = newnode(p.a, N_TARRAY, pf, pl, pc);
|
||||
n.rhs = parseexpr(p);
|
||||
// `[_]T` — length inferred from initialiser. n.rhs stays nil
|
||||
// as the sentinel; the cgen path for N_LET fills it from the
|
||||
// array literal's element count.
|
||||
if (p.curkind == TK_UNDER) {
|
||||
advance(p);
|
||||
} else {
|
||||
n.rhs = parseexpr(p);
|
||||
};
|
||||
expecttok(p, TK_RBRACK, "expected ']' in array type");
|
||||
n.lhs = parsetype(p);
|
||||
return n;
|
||||
@@ -274,6 +294,8 @@ export fn parsefile(p: *parser) *node = {
|
||||
d = parsetypedecl(p, exported);
|
||||
} else { if (p.curkind == TK_LET) {
|
||||
d = parselet(p, exported);
|
||||
} else { if (p.curkind == TK_CONST) {
|
||||
d = parselet(p, exported);
|
||||
} else { if (p.curkind == TK_FN) {
|
||||
d = parsefn(p, exported, attrs);
|
||||
} else {
|
||||
@@ -300,7 +322,7 @@ export fn parsefile(p: *parser) *node = {
|
||||
advance(p);
|
||||
};
|
||||
if (p.curkind == TK_SEMI) { advance(p); };
|
||||
};};};};};
|
||||
};};};};};};
|
||||
|
||||
if (d != nil) {
|
||||
if (head == nil) {
|
||||
|
||||
@@ -8,10 +8,13 @@ fn parseletlocal(p: *parser) *node = {
|
||||
let pf: str = p.curfile;
|
||||
let pl: i32 = p.curline;
|
||||
let pc: i32 = p.curcol;
|
||||
advance(p); // past `let`
|
||||
// `let` or `const`. Const-bound locals are marked via n.op = TK_CONST.
|
||||
let is_const: i32 = 0;
|
||||
if (p.curkind == TK_CONST) { is_const = 1; };
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_LET, pf, pl, pc);
|
||||
let id: str;
|
||||
expectident(p, &id);
|
||||
expectbindname(p, &id);
|
||||
n.str = id;
|
||||
if (accepttok(p, TK_COLON)) {
|
||||
n.lhs = parsetype(p);
|
||||
@@ -20,6 +23,7 @@ fn parseletlocal(p: *parser) *node = {
|
||||
n.rhs = parseexpr(p);
|
||||
};
|
||||
expecttok(p, TK_SEMI, "expected ';' after let");
|
||||
if (is_const != 0) { n.op = TK_CONST; };
|
||||
return n;
|
||||
};
|
||||
|
||||
@@ -98,6 +102,11 @@ fn parsefor(p: *parser) *node = {
|
||||
};
|
||||
expecttok(p, TK_RPAREN, "expected ')' after for");
|
||||
n.body = parseblock(p);
|
||||
// Optional `else { ... }` — runs at normal cond-false exit; skipped
|
||||
// by break. Hare's "did the loop find it?" idiom.
|
||||
if (accepttok(p, TK_ELSE)) {
|
||||
n.els = parseblock(p);
|
||||
};
|
||||
return n;
|
||||
};
|
||||
|
||||
@@ -116,6 +125,7 @@ fn parsestmt(p: *parser) *node = {
|
||||
return b;
|
||||
};
|
||||
if (p.curkind == TK_LET) { return parseletlocal(p); };
|
||||
if (p.curkind == TK_CONST) { return parseletlocal(p); };
|
||||
if (p.curkind == TK_IF) {
|
||||
let n: *node = parseif(p);
|
||||
expecttok(p, TK_SEMI, "expected ';' after if");
|
||||
|
||||
Reference in New Issue
Block a user