ww: hare-feature batch (_, const, [_]T, ..., size/offset, assert, for-else)
This commit is contained in:
@@ -33,7 +33,7 @@ fn scanlocals(c: *cgen, n: *node) i32 = {
|
||||
// of the caller. Same-name re-declarations share the first
|
||||
// slot (see scanseenmark / localadd).
|
||||
if (!scanseenmark(c, n.str)) {
|
||||
let sz: i32 = slotsize(c, n.lhs);
|
||||
let sz: i32 = letslotsize(c, n);
|
||||
if (sz < 8) { sz = 8; };
|
||||
if ((sz & 7) != 0) { sz = (sz + 7) & ~7; };
|
||||
total += sz;
|
||||
|
||||
@@ -736,6 +736,19 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
|
||||
fn cgassign(c: *cgen, n: *node) void = {
|
||||
let lhs: *node = n.lhs;
|
||||
// Discard lvalue `_ = expr;` — evaluate rhs for side effects,
|
||||
// write nothing. Detected by lhs being an N_IDENT with empty str
|
||||
// (planted by parseprimary on the TK_UNDER token).
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
if (lhs.str.len == 0) {
|
||||
if (n.op == TK_ASSIGN) {
|
||||
cgexpr(c, n.rhs);
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// `*p = v` — deref-assign. Element width comes from the
|
||||
// pointer's declared type. Mirrors C cgen: eval rhs (AX,
|
||||
// and BX if str), push, eval pointer, pop value, store.
|
||||
|
||||
@@ -126,7 +126,7 @@ fn cgexprstmt(c: *cgen, n: *node) void = {
|
||||
|
||||
fn cglet(c: *cgen, n: *node) void = {
|
||||
let nm: str = n.str;
|
||||
let sz: i32 = slotsize(c, n.lhs);
|
||||
let sz: i32 = letslotsize(c, n);
|
||||
let off: i32 = localadd(c, nm, sz, n.lhs);
|
||||
if (n.rhs != nil) {
|
||||
let rhs: *node = n.rhs;
|
||||
@@ -187,10 +187,84 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
// Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T).
|
||||
// Walk elements in declaration order, store each at off + i*esz
|
||||
// using the right width for the element type. Trailing `...`
|
||||
// after the last value (an N_FIELD with str=="...") fills the
|
||||
// remaining slots up to the declared length with that value.
|
||||
if (rhs.kind == N_ARRLIT) {
|
||||
let elemn: *node = n.lhs.lhs;
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == N_TNAME) {
|
||||
let ps: i32 = primsize(elemn.str);
|
||||
if (ps > 0) { esz = ps; };
|
||||
};
|
||||
};
|
||||
let mop: str = "MOVQ";
|
||||
if (esz == 1) { mop = "MOVB"; }
|
||||
else { if (esz == 4) { mop = "MOVL"; }; };
|
||||
let idx: i32 = 0;
|
||||
let repeat: bool = false;
|
||||
let e: *node = rhs.list;
|
||||
for (e != nil) {
|
||||
if (e.kind == N_FIELD) {
|
||||
if (streq(e.str, "...")) {
|
||||
repeat = true;
|
||||
e = nil;
|
||||
} else {
|
||||
cgexpr(c, e);
|
||||
emitline("\t");
|
||||
emitline(mop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
} else {
|
||||
cgexpr(c, e);
|
||||
emitline("\t");
|
||||
emitline(mop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
// AX still holds the last stored value; fill remaining
|
||||
// slots up to the declared length with it.
|
||||
if (repeat) {
|
||||
let total: i32 = idx;
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == N_TARRAY) {
|
||||
if (n.lhs.rhs != nil) {
|
||||
if (n.lhs.rhs.kind == N_INTLIT) {
|
||||
total = n.lhs.rhs.uval: i32;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
for (idx < total) {
|
||||
emitline("\t");
|
||||
emitline(mop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((off + idx * esz): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
};
|
||||
};
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
};
|
||||
// Struct literal init: `let p: point = point{x=..., y=...};`.
|
||||
// For each field in the lit, evaluate its value and store at
|
||||
// the field's offset within the slot. Field-name → offset
|
||||
// from the struct registry.
|
||||
// from the struct registry. When the literal carries
|
||||
// op == TK_ELLIPSIS (autofill marker from the parser), the
|
||||
// entire slot is zero-filled first so unmentioned fields
|
||||
// read as 0.
|
||||
if (rhs.kind == N_STRUCTLIT) {
|
||||
let trefn: *node = rhs.lhs;
|
||||
let sname: str;
|
||||
@@ -201,6 +275,29 @@ fn cglet(c: *cgen, n: *node) void = {
|
||||
};
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si != nil) {
|
||||
if (rhs.op == TK_ELLIPSIS) {
|
||||
let total: i32 = si.totsize;
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= total) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
zi += 8;
|
||||
};
|
||||
for (zi + 4 <= total) {
|
||||
emitline("\tMOVL\tAX, ");
|
||||
emitoff((off + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
zi += 4;
|
||||
};
|
||||
for (zi < total) {
|
||||
emitline("\tMOVB\tAX, ");
|
||||
emitoff((off + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
zi += 1;
|
||||
};
|
||||
};
|
||||
let fieldnode: *node = rhs.list;
|
||||
for (fieldnode != nil) {
|
||||
if (fieldnode.kind == N_FIELD) {
|
||||
@@ -292,6 +389,11 @@ fn cgfor(c: *cgen, n: *node) void = {
|
||||
// label when there's no post-expression.
|
||||
let topl: str = mklabel(c, "loop");
|
||||
let endl: str = mklabel(c, "endloop");
|
||||
// `else` runs at natural cond-false exit; break skips it. When
|
||||
// present, branch the cond-fail edge to a separate natural_exit
|
||||
// label so the else body sits between it and the break target.
|
||||
let naturall: str = endl;
|
||||
if (n.els != nil) { naturall = mklabel(c, "elseloop"); };
|
||||
|
||||
if (n.lhs != nil) { cgstmt(c, n.lhs); };
|
||||
|
||||
@@ -299,7 +401,7 @@ fn cgfor(c: *cgen, n: *node) void = {
|
||||
if (n.cond != nil) {
|
||||
cgexpr(c, n.cond);
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
emitline("\tJE\t"); emitline(endl); emitline("\n");
|
||||
emitline("\tJE\t"); emitline(naturall); emitline("\n");
|
||||
};
|
||||
|
||||
c.loopendbuf[c.looptop] = endl;
|
||||
@@ -312,6 +414,10 @@ fn cgfor(c: *cgen, n: *node) void = {
|
||||
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
emitline("\tJMP\t"); emitline(topl); emitline("\n");
|
||||
if (n.els != nil) {
|
||||
emitlabel(naturall);
|
||||
cgstmt(c, n.els);
|
||||
};
|
||||
emitlabel(endl);
|
||||
c.lastwasreturn = 0;
|
||||
return;
|
||||
|
||||
@@ -619,6 +619,52 @@ fn primsize(name: str) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// letslotsize — slot size for a `let` binding. Like slotsize, but
|
||||
// detects `[_]T = arrlit;` (the type-AST has rhs == nil as the
|
||||
// length-inferred sentinel) and computes count × element-size from
|
||||
// the initialiser. Used by both scanlocals (prologue sizing) and
|
||||
// cglet (slot alloc) so they agree on the frame layout.
|
||||
export fn letslotsize(c: *cgen, n: *node) i32 = {
|
||||
// `[_]T = arrlit;` — inferred-length array. slotsize would
|
||||
// return elem_size * 1 (treating missing length as 1); intercept
|
||||
// and compute the real count first.
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == N_TARRAY) {
|
||||
if (n.lhs.rhs == nil) {
|
||||
if (n.rhs != nil) {
|
||||
if (n.rhs.kind == N_ARRLIT) {
|
||||
let elemn: *node = n.lhs.lhs;
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == N_TNAME) {
|
||||
let ps: i32 = primsize(elemn.str);
|
||||
if (ps > 0) { esz = ps; };
|
||||
};
|
||||
};
|
||||
let cnt: i32 = 0;
|
||||
let e: *node = n.rhs.list;
|
||||
for (e != nil) {
|
||||
let adv: bool = true;
|
||||
if (e.kind == N_FIELD) {
|
||||
if (streq(e.str, "...")) {
|
||||
e = nil;
|
||||
adv = false;
|
||||
};
|
||||
};
|
||||
if (adv) {
|
||||
cnt += 1;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
return esz * cnt;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return slotsize(c, n.lhs);
|
||||
};
|
||||
|
||||
fn slotsize(c: *cgen, typn: *node) i32 = {
|
||||
if (typn == nil) { return 8; };
|
||||
let k: i32 = typn.kind;
|
||||
|
||||
Reference in New Issue
Block a user