commit f462972eb40f81e1508ef135a24a8692309ca16f parent 60d266bb6aed2ed8c7ba77dc3fbd9a2413438df0 Author: Ryan Sepassi <rsepassi@gmail.com> Date: Sun, 26 Apr 2026 22:46:36 -0700 cc/parse: compound-assign + ++/-- + sizeof + ternary + && + || + comma (§B, §C, §H, §K.1) Wires the new cg primitives through parse-binary-rhs / parse-unary / parse-postfix-rest: §B.1–B.4 — Compound assignment and pre-/post-inc/dec Compound assigns now `cg-dup` the lhs lval, `cg-load` to read the current value, evaluate rhs, then arith-conv + binop + cg-assign. Pre-`++`/`--` follows the same shape with an imm 1 rhs. Post-`++` / `--` switches to the new `cg-postinc` / `cg-postdec` primitive, which captures the old rval BEFORE the store and pushes it as the expression's value (the previous take-addr/push-deref/load chain consumed the lval and was returning the new value). §B.5 — *p++ walking a span Falls out of §B.2 + cg-binop's pointer-scaling branch. §C.1–C.2 — sizeof e returns the type's actual size Both `sizeof e` and `sizeof(e)` paths now peek the cg-top opnd's ctype-size, pop, and push imm u64 of that size. The type-form (`sizeof(int)` etc.) was already correct. §H.1 — Ternary `?:` Switches `qmark` from `cg-ifelse` to `cg-ifelse-merge`, so each arm pushes one rval which the primitive merges into a single result slot. §H.2/H.3 — `&&` and `||` Same merge primitive; the rhs side gets `cg-cast bool` then `cg-cast i32` so the merge slot carries i32 ∈ {0,1}, matching the short-circuit constant in the other arm. §K.1 — Comma operator Adds `(comma . (1 . 2))` to %binop-bp at the lowest precedence (left-assoc, below assign's 4/3 so parse-call-args' min-bp 4 still excludes it). Handler `cg-pop`s the lhs's rval and evaluates the rhs; comma's value is the rhs's value. Locks the surface into cc-parse fixtures 21–31. Diffstat:
23 files changed, 139 insertions(+), 23 deletions(-)
diff --git a/cc/parse.scm b/cc/parse.scm @@ -683,6 +683,7 @@ (define %binop-bp (list + (cons 'comma (cons 1 2)) (cons 'assign (cons 4 3)) (cons 'plus-eq (cons 4 3)) (cons 'minus-eq (cons 4 3)) (cons 'star-eq (cons 4 3)) (cons 'slash-eq (cons 4 3)) (cons 'pct-eq (cons 4 3)) @@ -739,13 +740,17 @@ (let ((op (tok-value t)) (rb (cdr bp))) (advance ps) (cond + ((eq? op 'comma) + ;; lhs has been parsed; discard it and evaluate rhs. + ;; Result of the comma expr is the rhs's rval. + (cg-pop (ps-cg ps)) + (parse-expr-bp ps rb) (rval! ps)) ((eq? op 'assign) (parse-expr-bp ps rb) (rval! ps) (cg-assign (ps-cg ps))) ((compound-op op) (let ((b (compound-op op))) - (cg-take-addr (ps-cg ps)) - (cg-push-deref (ps-cg ps)) + (cg-dup (ps-cg ps)) (cg-load (ps-cg ps)) (parse-expr-bp ps rb) (rval! ps) (cg-arith-conv (ps-cg ps)) @@ -753,7 +758,7 @@ (cg-assign (ps-cg ps)))) ((eq? op 'qmark) (rval! ps) - (cg-ifelse (ps-cg ps) + (cg-ifelse-merge (ps-cg ps) (lambda () (parse-expr-bp ps 0) (rval! ps)) (lambda () @@ -761,18 +766,25 @@ (parse-expr-bp ps rb) (rval! ps)))) ((eq? op 'land) (rval! ps) - (cg-ifelse (ps-cg ps) + ;; Both branches must push i32 0/1. Right side is + ;; coerced via `cg-cast bool` so the merge slot + ;; carries i32 (per §H.2). + (cg-ifelse-merge (ps-cg ps) (lambda () - (parse-expr-bp ps rb) (rval! ps)) + (parse-expr-bp ps rb) (rval! ps) + (cg-cast (ps-cg ps) %t-bool) + (cg-cast (ps-cg ps) %t-i32)) (lambda () (cg-push-imm (ps-cg ps) %t-i32 0)))) ((eq? op 'lor) (rval! ps) - (cg-ifelse (ps-cg ps) + (cg-ifelse-merge (ps-cg ps) (lambda () (cg-push-imm (ps-cg ps) %t-i32 1)) (lambda () - (parse-expr-bp ps rb) (rval! ps)))) + (parse-expr-bp ps rb) (rval! ps) + (cg-cast (ps-cg ps) %t-bool) + (cg-cast (ps-cg ps) %t-i32)))) (else (rval! ps) (cg-promote (ps-cg ps)) (parse-expr-bp ps rb) (rval! ps) @@ -808,12 +820,12 @@ (advance ps) (parse-unary ps) (rval! ps) (cg-unop (ps-cg ps) 'lnot)) ((eq? v 'inc) (advance ps) (parse-unary ps) - (cg-take-addr (ps-cg ps)) (cg-push-deref (ps-cg ps)) + (cg-dup (ps-cg ps)) (cg-load (ps-cg ps)) (cg-push-imm (ps-cg ps) %t-i32 1) (cg-binop (ps-cg ps) 'add) (cg-assign (ps-cg ps))) ((eq? v 'dec) (advance ps) (parse-unary ps) - (cg-take-addr (ps-cg ps)) (cg-push-deref (ps-cg ps)) + (cg-dup (ps-cg ps)) (cg-load (ps-cg ps)) (cg-push-imm (ps-cg ps) %t-i32 1) (cg-binop (ps-cg ps) 'sub) (cg-assign (ps-cg ps))) @@ -834,10 +846,15 @@ (max (ctype-size ty) 0)))) (else (parse-expr ps) (expect-punct ps 'rparen) - (cg-pop (ps-cg ps)) - (cg-push-imm (ps-cg ps) %t-u64 8)))) - (else (parse-unary ps) (cg-pop (ps-cg ps)) - (cg-push-imm (ps-cg ps) %t-u64 8)))) + (let* ((tp (cg-top (ps-cg ps))) + (sz (max (ctype-size (opnd-type tp)) 0))) + (cg-pop (ps-cg ps)) + (cg-push-imm (ps-cg ps) %t-u64 sz))))) + (else (parse-unary ps) + (let* ((tp (cg-top (ps-cg ps))) + (sz (max (ctype-size (opnd-type tp)) 0))) + (cg-pop (ps-cg ps)) + (cg-push-imm (ps-cg ps) %t-u64 sz))))) (else (parse-postfix ps))))) (define (token-is-decl? ps) @@ -920,18 +937,10 @@ (cg-push-deref (ps-cg ps)) (lp)) ((eq? v 'inc) (advance ps) - (cg-take-addr (ps-cg ps)) (cg-push-deref (ps-cg ps)) - (cg-load (ps-cg ps)) - (cg-push-imm (ps-cg ps) %t-i32 1) - (cg-binop (ps-cg ps) 'add) - (cg-assign (ps-cg ps)) (lp)) + (cg-postinc (ps-cg ps)) (lp)) ((eq? v 'dec) (advance ps) - (cg-take-addr (ps-cg ps)) (cg-push-deref (ps-cg ps)) - (cg-load (ps-cg ps)) - (cg-push-imm (ps-cg ps) %t-i32 1) - (cg-binop (ps-cg ps) 'sub) - (cg-assign (ps-cg ps)) (lp)) + (cg-postdec (ps-cg ps)) (lp)) (else #t)))))))) (define (parse-call-args ps) diff --git a/tests/cc-parse/21-preinc.c b/tests/cc-parse/21-preinc.c @@ -0,0 +1,6 @@ +// tests/cc-parse/21-preinc.c — pre-increment on a simple lval (§B.1). +int main(void) { + int x = 5; + ++x; + return x; +} diff --git a/tests/cc-parse/21-preinc.expected-exit b/tests/cc-parse/21-preinc.expected-exit @@ -0,0 +1 @@ +6 diff --git a/tests/cc-parse/22-postinc.c b/tests/cc-parse/22-postinc.c @@ -0,0 +1,6 @@ +// tests/cc-parse/22-postinc.c — post-increment returns OLD value (§B.2). +int main(void) { + int x = 5; + int y = x++; + return x * 10 + y; +} diff --git a/tests/cc-parse/22-postinc.expected-exit b/tests/cc-parse/22-postinc.expected-exit @@ -0,0 +1 @@ +65 diff --git a/tests/cc-parse/23-cmpd-simple.c b/tests/cc-parse/23-cmpd-simple.c @@ -0,0 +1,16 @@ +// tests/cc-parse/23-cmpd-simple.c — compound assignment on simple lval (§B.3). +// Exercises one op per family: +=, -=, *=, /=, %=, <<=, >>=, &=, ^=, |=. +int main(void) { + int a = 7; a += 3; // 10 + int b = 7; b -= 3; // 4 + int c = 7; c *= 3; // 21 + int d = 12; d /= 3; // 4 + int e = 7; e %= 3; // 1 + int f = 1; f <<= 4; // 16 + int g = 32; g >>= 1; // 16 + int h = 0xF; h &= 0xA; // 10 + int i = 0xF; i ^= 0xA; // 5 + int j = 0; j |= 7; // 7 + // sum = 10 + 4 + 21 + 4 + 1 + 16 + 16 + 10 + 5 + 7 = 94 + return a + b + c + d + e + f + g + h + i + j; +} diff --git a/tests/cc-parse/23-cmpd-simple.expected-exit b/tests/cc-parse/23-cmpd-simple.expected-exit @@ -0,0 +1 @@ +94 diff --git a/tests/cc-parse/24-cmpd-ptr.c b/tests/cc-parse/24-cmpd-ptr.c @@ -0,0 +1,7 @@ +// tests/cc-parse/24-cmpd-ptr.c — compound assignment through pointer (§B.4). +int main(void) { + int x = 7; + int *p = &x; + *p += 3; + return x; +} diff --git a/tests/cc-parse/24-cmpd-ptr.expected-exit b/tests/cc-parse/24-cmpd-ptr.expected-exit @@ -0,0 +1 @@ +10 diff --git a/tests/cc-parse/25-deref-postinc.c b/tests/cc-parse/25-deref-postinc.c @@ -0,0 +1,17 @@ +// tests/cc-parse/25-deref-postinc.c — *p++ walking a span (§B.5). +// +// We can't use a[i] (§D.5, owned elsewhere) and we can't span +// `int` locals because cg's per-statement spill slots interleave +// between adjacent local declarations. Instead use a multi-byte int +// holding our three values in its low bytes plus an unsigned-char +// pointer to walk the bytes — pointer scaling is by 1, no scaling +// quirk to dodge. +int main(void) { + int packed = (4 << 16) | (2 << 8) | 1; // bytes: 1, 2, 4 (LE) + unsigned char *p = (unsigned char *)&packed; + int s = 0; + s += *p++; + s += *p++; + s += *p++; + return s; +} diff --git a/tests/cc-parse/25-deref-postinc.expected-exit b/tests/cc-parse/25-deref-postinc.expected-exit @@ -0,0 +1 @@ +7 diff --git a/tests/cc-parse/26-sizeof-expr.c b/tests/cc-parse/26-sizeof-expr.c @@ -0,0 +1,5 @@ +// tests/cc-parse/26-sizeof-expr.c — sizeof e returns actual size (§C.1). +int main(void) { + int x; + return sizeof x; +} diff --git a/tests/cc-parse/26-sizeof-expr.expected-exit b/tests/cc-parse/26-sizeof-expr.expected-exit @@ -0,0 +1 @@ +4 diff --git a/tests/cc-parse/27-sizeof-types.c b/tests/cc-parse/27-sizeof-types.c @@ -0,0 +1,14 @@ +// tests/cc-parse/27-sizeof-types.c — sizeof over struct, array, ptr, +// char, plus the named integer types (§C.2). Sums to a known total. +struct S { int a; int b; }; +int main(void) { + int sum = 0; + sum += sizeof(char); // 1 + sum += sizeof(short); // 2 + sum += sizeof(int); // 4 + sum += sizeof(long); // 8 + sum += sizeof(int *); // 8 + sum += sizeof(int[5]); // 20 + sum += sizeof(struct S); // 8 + return sum; // 1+2+4+8+8+20+8 = 51 +} diff --git a/tests/cc-parse/27-sizeof-types.expected-exit b/tests/cc-parse/27-sizeof-types.expected-exit @@ -0,0 +1 @@ +51 diff --git a/tests/cc-parse/28-ternary.c b/tests/cc-parse/28-ternary.c @@ -0,0 +1,6 @@ +// tests/cc-parse/28-ternary.c — ternary leaves exactly one rval (§H.1). +int main(void) { + int c = 1; + int x = c ? 7 : 9; + return x; +} diff --git a/tests/cc-parse/28-ternary.expected-exit b/tests/cc-parse/28-ternary.expected-exit @@ -0,0 +1 @@ +7 diff --git a/tests/cc-parse/29-land.c b/tests/cc-parse/29-land.c @@ -0,0 +1,6 @@ +// tests/cc-parse/29-land.c — `&&` leaves exactly one i32 rval (§H.2). +int main(void) { + int a = 5; + int b = 0; + return (a && b) ? 100 : 42; +} diff --git a/tests/cc-parse/29-land.expected-exit b/tests/cc-parse/29-land.expected-exit @@ -0,0 +1 @@ +42 diff --git a/tests/cc-parse/30-lor.c b/tests/cc-parse/30-lor.c @@ -0,0 +1,6 @@ +// tests/cc-parse/30-lor.c — `||` leaves exactly one i32 rval (§H.3). +int main(void) { + int a = 0; + int b = 5; + return (a || b) ? 11 : 33; +} diff --git a/tests/cc-parse/30-lor.expected-exit b/tests/cc-parse/30-lor.expected-exit @@ -0,0 +1 @@ +11 diff --git a/tests/cc-parse/31-comma.c b/tests/cc-parse/31-comma.c @@ -0,0 +1,7 @@ +// tests/cc-parse/31-comma.c — comma operator (§K.1). +int main(void) { + int a; + int b; + (a = 1, b = 2); + return a + b * 10; // 1 + 20 = 21 +} diff --git a/tests/cc-parse/31-comma.expected-exit b/tests/cc-parse/31-comma.expected-exit @@ -0,0 +1 @@ +21