ww: add Hare-style is/as postfix ops on tagged unions
`e is T` returns bool (variant tag == T's index); `e as T` unwraps to T or exit(1) on mismatch. Postfix, same precedence as `:` cast. TK_IS / N_TYPETEST / N_TYPEASSERT appended at the tail of their enums so every prior numeric value stays unchanged — the 990_selfhost wwdump-diff stays byte-clean. Cgen mirrors the match-case slot-based load (tag at +0, value at +8/+16), so an N_IDENT tagged-union local works just like a match scrutinee. Selfhost cgen inlines the slot resolution because the wwstage cgen drops sign bits on `*i32` output parameters in this position. Renames `errors.is` -> `errors.equal` (the only naming collision; the existing comment already noted it shared shape with strings.equal/bytes.equal).
This commit is contained in:
@@ -545,7 +545,12 @@ def TK_LARROW: i32 = 79;
|
||||
def TK_ARROW: i32 = 80;
|
||||
def TK_FATARROW: i32 = 81;
|
||||
|
||||
def TK_LAST: i32 = 82;
|
||||
// Appended at the tail (not grouped with the keyword block) so every
|
||||
// pre-existing TK_* value stays unchanged — the 990_selfhost test
|
||||
// diffs wwdump output against the C side, byte for byte.
|
||||
def TK_IS: i32 = 82;
|
||||
|
||||
def TK_LAST: i32 = 83;
|
||||
|
||||
// ---- Pos / Tok --------------------------------------------------------
|
||||
//
|
||||
@@ -604,6 +609,7 @@ export fn kwlookup(p: *u8, n: i32) i32 = {
|
||||
if (streqn(p, "fn", n)) { return TK_FN; };
|
||||
if (streqn(p, "for", n)) { return TK_FOR; };
|
||||
if (streqn(p, "if", n)) { return TK_IF; };
|
||||
if (streqn(p, "is", n)) { return TK_IS; };
|
||||
if (streqn(p, "let", n)) { return TK_LET; };
|
||||
if (streqn(p, "match", n)) { return TK_MATCH; };
|
||||
if (streqn(p, "nil", n)) { return TK_NIL; };
|
||||
@@ -655,6 +661,7 @@ export fn tokname(k: i32) str = {
|
||||
if (k == TK_TRUE) { return "true"; };
|
||||
if (k == TK_FALSE) { return "false"; };
|
||||
if (k == TK_AS) { return "as"; };
|
||||
if (k == TK_IS) { return "is"; };
|
||||
if (k == TK_STATIC) { return "static"; };
|
||||
if (k == TK_MATCH) { return "match"; };
|
||||
if (k == TK_CONST) { return "const"; };
|
||||
@@ -1716,7 +1723,12 @@ def N_TRYUNW: i32 = 57;
|
||||
def N_MLET: i32 = 58;
|
||||
def N_MASSIGN: i32 = 59;
|
||||
|
||||
def N_LAST: i32 = 60;
|
||||
// Appended at the tail to keep all prior N_* values stable. The
|
||||
// 990_selfhost test diffs astprint against the C side byte-for-byte.
|
||||
def N_TYPETEST: i32 = 60;
|
||||
def N_TYPEASSERT: i32 = 61;
|
||||
|
||||
def N_LAST: i32 = 62;
|
||||
|
||||
// ---- Node -------------------------------------------------------------
|
||||
|
||||
@@ -1815,6 +1827,8 @@ fn nkname(k: i32) str = {
|
||||
if (k == N_TRYUNW) { return "tryunw"; };
|
||||
if (k == N_MLET) { return "mlet"; };
|
||||
if (k == N_MASSIGN) { return "massign"; };
|
||||
if (k == N_TYPETEST) { return "typetest"; };
|
||||
if (k == N_TYPEASSERT) { return "typeassert"; };
|
||||
if (k == N_LAST) { return "last"; };
|
||||
return "?";
|
||||
};
|
||||
@@ -2276,6 +2290,26 @@ fn parsepostfix(p: *parser, lhs: *node) *node = {
|
||||
cur = n;
|
||||
continue;
|
||||
};
|
||||
// Hare-style postfix:
|
||||
// `e as T` — assert lhs is variant T (abort otherwise) → T
|
||||
// `e is T` — bool: does lhs currently hold variant T?
|
||||
// Same precedence level as the `:` cast.
|
||||
if (p.curkind == TK_AS) {
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_TYPEASSERT, pf, pl, pc);
|
||||
n.lhs = cur;
|
||||
n.rhs = parsetype(p);
|
||||
cur = n;
|
||||
continue;
|
||||
};
|
||||
if (p.curkind == TK_IS) {
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_TYPETEST, pf, pl, pc);
|
||||
n.lhs = cur;
|
||||
n.rhs = parsetype(p);
|
||||
cur = n;
|
||||
continue;
|
||||
};
|
||||
break;
|
||||
};
|
||||
return cur;
|
||||
@@ -4863,6 +4897,115 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
if (k == N_CALL) { cgcall(c, n); return; };
|
||||
|
||||
if (k == N_ASSIGN) { cgassign(c, n); return; };
|
||||
|
||||
if (k == N_TYPETEST) { cgtypetest(c, n); return; };
|
||||
if (k == N_TYPEASSERT) { cgtypeassert(c, n); return; };
|
||||
};
|
||||
|
||||
// cgtagvariantidx — find the 0-based variant index of `vt` inside the
|
||||
// tagged-union type expression `tagged`. -1 if `tagged` isn't an
|
||||
// N_TTAGGED or no variant matches. Mirrors the lookup that cgmatch
|
||||
// does inline; pulled out so `is` / `as` can reuse it.
|
||||
fn cgtagvariantidx(tagged: *node, vt: *node) i32 = {
|
||||
if (tagged == nil) { return -1; };
|
||||
if (vt == nil) { return -1; };
|
||||
if (tagged.kind != N_TTAGGED) { return -1; };
|
||||
let want: str;
|
||||
want.ptr = nil; want.len = 0;
|
||||
if (vt.kind == N_TNAME) { want = vt.str; };
|
||||
if (want.len == 0) { return -1; };
|
||||
let v: *node = tagged.list;
|
||||
let idx: i32 = 0;
|
||||
for (v != nil) {
|
||||
if (v.kind == N_TNAME) {
|
||||
if (streq(v.str, want)) { return idx; };
|
||||
};
|
||||
v = v.next;
|
||||
idx += 1;
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn cgtypetest(c: *cgen, n: *node) void = {
|
||||
// `e is T` — load the lhs's tag, compare against T's variant
|
||||
// index, set AX = (tag == idx). Result type is bool.
|
||||
//
|
||||
// Slot resolution is inlined (rather than factored into a helper
|
||||
// with output parameters): wwstage cgen has a trap with i32
|
||||
// stored via *i32 in this context — direct assignment of the
|
||||
// local works, indirection through &scrutoff drops sign bits.
|
||||
let lhs: *node = n.lhs;
|
||||
let scrutoff: i32 = 0;
|
||||
let scrutt: *node = nil;
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
let lc: *local = localfindnode(c, lhs.str);
|
||||
if (lc != nil) {
|
||||
scrutoff = lc.off;
|
||||
scrutt = resolvetype(c, lc.tnode);
|
||||
};
|
||||
};
|
||||
};
|
||||
let want: i32 = cgtagvariantidx(scrutt, n.rhs);
|
||||
if (want < 0) { want = 0; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scrutoff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
let nel: str = mklabel(c, "is_ne");
|
||||
let dnl: str = mklabel(c, "is_done");
|
||||
emitline("\tCMPQ\t$");
|
||||
emitint(want: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(nel);
|
||||
emitline("\n\tMOVQ\t$1, AX\n\tJMP\t");
|
||||
emitline(dnl);
|
||||
emitline("\n");
|
||||
emitlabel(nel);
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
emitlabel(dnl);
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgtypeassert(c: *cgen, n: *node) void = {
|
||||
// `e as T` — load tag, abort (exit 1) if tag != T's variant
|
||||
// index, otherwise unwrap to T's ABI: scalar/ptr → AX, 16B
|
||||
// str → (AX, BX). Mirrors cgmatch's slot-based value load.
|
||||
// Slot resolution inlined; see cgtypetest comment.
|
||||
let lhs: *node = n.lhs;
|
||||
let scrutoff: i32 = 0;
|
||||
let scrutt: *node = nil;
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
let lc: *local = localfindnode(c, lhs.str);
|
||||
if (lc != nil) {
|
||||
scrutoff = lc.off;
|
||||
scrutt = resolvetype(c, lc.tnode);
|
||||
};
|
||||
};
|
||||
};
|
||||
let want: i32 = cgtagvariantidx(scrutt, n.rhs);
|
||||
if (want < 0) { want = 0; };
|
||||
let okl: str = mklabel(c, "asrt_ok");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scrutoff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tCMPQ\t$");
|
||||
emitint(want: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tJE\t");
|
||||
emitline(okl);
|
||||
emitline("\n\tMOVQ\t$1, DI\n\tMOVQ\t$60, AX\n\tSYSCALL\n");
|
||||
emitlabel(okl);
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scrutoff + 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
if (isstrtype(c, n.rhs)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scrutoff + 16): i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgstrlit(c: *cgen, n: *node) void = {
|
||||
|
||||
@@ -79,6 +79,115 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
if (k == N_CALL) { cgcall(c, n); return; };
|
||||
|
||||
if (k == N_ASSIGN) { cgassign(c, n); return; };
|
||||
|
||||
if (k == N_TYPETEST) { cgtypetest(c, n); return; };
|
||||
if (k == N_TYPEASSERT) { cgtypeassert(c, n); return; };
|
||||
};
|
||||
|
||||
// cgtagvariantidx — find the 0-based variant index of `vt` inside the
|
||||
// tagged-union type expression `tagged`. -1 if `tagged` isn't an
|
||||
// N_TTAGGED or no variant matches. Mirrors the lookup that cgmatch
|
||||
// does inline; pulled out so `is` / `as` can reuse it.
|
||||
fn cgtagvariantidx(tagged: *node, vt: *node) i32 = {
|
||||
if (tagged == nil) { return -1; };
|
||||
if (vt == nil) { return -1; };
|
||||
if (tagged.kind != N_TTAGGED) { return -1; };
|
||||
let want: str;
|
||||
want.ptr = nil; want.len = 0;
|
||||
if (vt.kind == N_TNAME) { want = vt.str; };
|
||||
if (want.len == 0) { return -1; };
|
||||
let v: *node = tagged.list;
|
||||
let idx: i32 = 0;
|
||||
for (v != nil) {
|
||||
if (v.kind == N_TNAME) {
|
||||
if (streq(v.str, want)) { return idx; };
|
||||
};
|
||||
v = v.next;
|
||||
idx += 1;
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn cgtypetest(c: *cgen, n: *node) void = {
|
||||
// `e is T` — load the lhs's tag, compare against T's variant
|
||||
// index, set AX = (tag == idx). Result type is bool.
|
||||
//
|
||||
// Slot resolution is inlined (rather than factored into a helper
|
||||
// with output parameters): wwstage cgen has a trap with i32
|
||||
// stored via *i32 in this context — direct assignment of the
|
||||
// local works, indirection through &scrutoff drops sign bits.
|
||||
let lhs: *node = n.lhs;
|
||||
let scrutoff: i32 = 0;
|
||||
let scrutt: *node = nil;
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
let lc: *local = localfindnode(c, lhs.str);
|
||||
if (lc != nil) {
|
||||
scrutoff = lc.off;
|
||||
scrutt = resolvetype(c, lc.tnode);
|
||||
};
|
||||
};
|
||||
};
|
||||
let want: i32 = cgtagvariantidx(scrutt, n.rhs);
|
||||
if (want < 0) { want = 0; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scrutoff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
let nel: str = mklabel(c, "is_ne");
|
||||
let dnl: str = mklabel(c, "is_done");
|
||||
emitline("\tCMPQ\t$");
|
||||
emitint(want: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(nel);
|
||||
emitline("\n\tMOVQ\t$1, AX\n\tJMP\t");
|
||||
emitline(dnl);
|
||||
emitline("\n");
|
||||
emitlabel(nel);
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
emitlabel(dnl);
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgtypeassert(c: *cgen, n: *node) void = {
|
||||
// `e as T` — load tag, abort (exit 1) if tag != T's variant
|
||||
// index, otherwise unwrap to T's ABI: scalar/ptr → AX, 16B
|
||||
// str → (AX, BX). Mirrors cgmatch's slot-based value load.
|
||||
// Slot resolution inlined; see cgtypetest comment.
|
||||
let lhs: *node = n.lhs;
|
||||
let scrutoff: i32 = 0;
|
||||
let scrutt: *node = nil;
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
let lc: *local = localfindnode(c, lhs.str);
|
||||
if (lc != nil) {
|
||||
scrutoff = lc.off;
|
||||
scrutt = resolvetype(c, lc.tnode);
|
||||
};
|
||||
};
|
||||
};
|
||||
let want: i32 = cgtagvariantidx(scrutt, n.rhs);
|
||||
if (want < 0) { want = 0; };
|
||||
let okl: str = mklabel(c, "asrt_ok");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scrutoff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tCMPQ\t$");
|
||||
emitint(want: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tJE\t");
|
||||
emitline(okl);
|
||||
emitline("\n\tMOVQ\t$1, DI\n\tMOVQ\t$60, AX\n\tSYSCALL\n");
|
||||
emitlabel(okl);
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scrutoff + 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
if (isstrtype(c, n.rhs)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scrutoff + 16): i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgstrlit(c: *cgen, n: *node) void = {
|
||||
|
||||
@@ -545,7 +545,12 @@ def TK_LARROW: i32 = 79;
|
||||
def TK_ARROW: i32 = 80;
|
||||
def TK_FATARROW: i32 = 81;
|
||||
|
||||
def TK_LAST: i32 = 82;
|
||||
// Appended at the tail (not grouped with the keyword block) so every
|
||||
// pre-existing TK_* value stays unchanged — the 990_selfhost test
|
||||
// diffs wwdump output against the C side, byte for byte.
|
||||
def TK_IS: i32 = 82;
|
||||
|
||||
def TK_LAST: i32 = 83;
|
||||
|
||||
// ---- Pos / Tok --------------------------------------------------------
|
||||
//
|
||||
@@ -604,6 +609,7 @@ export fn kwlookup(p: *u8, n: i32) i32 = {
|
||||
if (streqn(p, "fn", n)) { return TK_FN; };
|
||||
if (streqn(p, "for", n)) { return TK_FOR; };
|
||||
if (streqn(p, "if", n)) { return TK_IF; };
|
||||
if (streqn(p, "is", n)) { return TK_IS; };
|
||||
if (streqn(p, "let", n)) { return TK_LET; };
|
||||
if (streqn(p, "match", n)) { return TK_MATCH; };
|
||||
if (streqn(p, "nil", n)) { return TK_NIL; };
|
||||
@@ -655,6 +661,7 @@ export fn tokname(k: i32) str = {
|
||||
if (k == TK_TRUE) { return "true"; };
|
||||
if (k == TK_FALSE) { return "false"; };
|
||||
if (k == TK_AS) { return "as"; };
|
||||
if (k == TK_IS) { return "is"; };
|
||||
if (k == TK_STATIC) { return "static"; };
|
||||
if (k == TK_MATCH) { return "match"; };
|
||||
if (k == TK_CONST) { return "const"; };
|
||||
@@ -1716,7 +1723,12 @@ def N_TRYUNW: i32 = 57;
|
||||
def N_MLET: i32 = 58;
|
||||
def N_MASSIGN: i32 = 59;
|
||||
|
||||
def N_LAST: i32 = 60;
|
||||
// Appended at the tail to keep all prior N_* values stable. The
|
||||
// 990_selfhost test diffs astprint against the C side byte-for-byte.
|
||||
def N_TYPETEST: i32 = 60;
|
||||
def N_TYPEASSERT: i32 = 61;
|
||||
|
||||
def N_LAST: i32 = 62;
|
||||
|
||||
// ---- Node -------------------------------------------------------------
|
||||
|
||||
@@ -1815,6 +1827,8 @@ fn nkname(k: i32) str = {
|
||||
if (k == N_TRYUNW) { return "tryunw"; };
|
||||
if (k == N_MLET) { return "mlet"; };
|
||||
if (k == N_MASSIGN) { return "massign"; };
|
||||
if (k == N_TYPETEST) { return "typetest"; };
|
||||
if (k == N_TYPEASSERT) { return "typeassert"; };
|
||||
if (k == N_LAST) { return "last"; };
|
||||
return "?";
|
||||
};
|
||||
@@ -2276,6 +2290,26 @@ fn parsepostfix(p: *parser, lhs: *node) *node = {
|
||||
cur = n;
|
||||
continue;
|
||||
};
|
||||
// Hare-style postfix:
|
||||
// `e as T` — assert lhs is variant T (abort otherwise) → T
|
||||
// `e is T` — bool: does lhs currently hold variant T?
|
||||
// Same precedence level as the `:` cast.
|
||||
if (p.curkind == TK_AS) {
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_TYPEASSERT, pf, pl, pc);
|
||||
n.lhs = cur;
|
||||
n.rhs = parsetype(p);
|
||||
cur = n;
|
||||
continue;
|
||||
};
|
||||
if (p.curkind == TK_IS) {
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, N_TYPETEST, pf, pl, pc);
|
||||
n.lhs = cur;
|
||||
n.rhs = parsetype(p);
|
||||
cur = n;
|
||||
continue;
|
||||
};
|
||||
break;
|
||||
};
|
||||
return cur;
|
||||
@@ -4863,6 +4897,115 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
if (k == N_CALL) { cgcall(c, n); return; };
|
||||
|
||||
if (k == N_ASSIGN) { cgassign(c, n); return; };
|
||||
|
||||
if (k == N_TYPETEST) { cgtypetest(c, n); return; };
|
||||
if (k == N_TYPEASSERT) { cgtypeassert(c, n); return; };
|
||||
};
|
||||
|
||||
// cgtagvariantidx — find the 0-based variant index of `vt` inside the
|
||||
// tagged-union type expression `tagged`. -1 if `tagged` isn't an
|
||||
// N_TTAGGED or no variant matches. Mirrors the lookup that cgmatch
|
||||
// does inline; pulled out so `is` / `as` can reuse it.
|
||||
fn cgtagvariantidx(tagged: *node, vt: *node) i32 = {
|
||||
if (tagged == nil) { return -1; };
|
||||
if (vt == nil) { return -1; };
|
||||
if (tagged.kind != N_TTAGGED) { return -1; };
|
||||
let want: str;
|
||||
want.ptr = nil; want.len = 0;
|
||||
if (vt.kind == N_TNAME) { want = vt.str; };
|
||||
if (want.len == 0) { return -1; };
|
||||
let v: *node = tagged.list;
|
||||
let idx: i32 = 0;
|
||||
for (v != nil) {
|
||||
if (v.kind == N_TNAME) {
|
||||
if (streq(v.str, want)) { return idx; };
|
||||
};
|
||||
v = v.next;
|
||||
idx += 1;
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn cgtypetest(c: *cgen, n: *node) void = {
|
||||
// `e is T` — load the lhs's tag, compare against T's variant
|
||||
// index, set AX = (tag == idx). Result type is bool.
|
||||
//
|
||||
// Slot resolution is inlined (rather than factored into a helper
|
||||
// with output parameters): wwstage cgen has a trap with i32
|
||||
// stored via *i32 in this context — direct assignment of the
|
||||
// local works, indirection through &scrutoff drops sign bits.
|
||||
let lhs: *node = n.lhs;
|
||||
let scrutoff: i32 = 0;
|
||||
let scrutt: *node = nil;
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
let lc: *local = localfindnode(c, lhs.str);
|
||||
if (lc != nil) {
|
||||
scrutoff = lc.off;
|
||||
scrutt = resolvetype(c, lc.tnode);
|
||||
};
|
||||
};
|
||||
};
|
||||
let want: i32 = cgtagvariantidx(scrutt, n.rhs);
|
||||
if (want < 0) { want = 0; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scrutoff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
let nel: str = mklabel(c, "is_ne");
|
||||
let dnl: str = mklabel(c, "is_done");
|
||||
emitline("\tCMPQ\t$");
|
||||
emitint(want: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(nel);
|
||||
emitline("\n\tMOVQ\t$1, AX\n\tJMP\t");
|
||||
emitline(dnl);
|
||||
emitline("\n");
|
||||
emitlabel(nel);
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
emitlabel(dnl);
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgtypeassert(c: *cgen, n: *node) void = {
|
||||
// `e as T` — load tag, abort (exit 1) if tag != T's variant
|
||||
// index, otherwise unwrap to T's ABI: scalar/ptr → AX, 16B
|
||||
// str → (AX, BX). Mirrors cgmatch's slot-based value load.
|
||||
// Slot resolution inlined; see cgtypetest comment.
|
||||
let lhs: *node = n.lhs;
|
||||
let scrutoff: i32 = 0;
|
||||
let scrutt: *node = nil;
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
let lc: *local = localfindnode(c, lhs.str);
|
||||
if (lc != nil) {
|
||||
scrutoff = lc.off;
|
||||
scrutt = resolvetype(c, lc.tnode);
|
||||
};
|
||||
};
|
||||
};
|
||||
let want: i32 = cgtagvariantidx(scrutt, n.rhs);
|
||||
if (want < 0) { want = 0; };
|
||||
let okl: str = mklabel(c, "asrt_ok");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scrutoff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tCMPQ\t$");
|
||||
emitint(want: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tJE\t");
|
||||
emitline(okl);
|
||||
emitline("\n\tMOVQ\t$1, DI\n\tMOVQ\t$60, AX\n\tSYSCALL\n");
|
||||
emitlabel(okl);
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scrutoff + 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
if (isstrtype(c, n.rhs)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scrutoff + 16): i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgstrlit(c: *cgen, n: *node) void = {
|
||||
|
||||
Reference in New Issue
Block a user