boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

commit 311fbbd7bf644c421acabce8905880cd1b367362
parent 3ad49004bf38631d89ed95d4db57c03c360d40c4
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri,  1 May 2026 14:55:48 -0700

cc: deduplicate and unify constant-expression evaluator

- Delete %find-field (5347); its two callers now use %cg-find-field
- Move %ctype-ptr?, %ctype-pointee, %ctype-unsigned?, %ctype-fp? from
  cg section to data section where they belong alongside ctype accessors
- Inline %ctype-size (trivial alias for ctype-size) at all callsites
- Unify the two constant-expression grammar implementations: delete the
  15-function %pp-cx-* block; add %pp-make-const-ps IO adapter that
  wraps a preprocessor token list as a minimal pstate (empty scope,
  ps-cg=#f) so pp-eval-cexpr can delegate directly to parse-const-int;
  guard %const-sizeof-expr against the ps-cg=#f case
- Refactor parse-const-* binary levels: add %const-binl (generic
  left-associative level loop), %const-arith-op, %const-div-op,
  %const-cmp-op; collapse 8 hand-rolled binary level functions into
  single-line %const-binl calls

Net: -163 lines, one shared grammar instead of two

Diffstat:
Mcc/cc.scm | 303++++++++++++++++++++++---------------------------------------------------------
1 file changed, 82 insertions(+), 221 deletions(-)

diff --git a/cc/cc.scm b/cc/cc.scm @@ -2431,27 +2431,35 @@ ;; --- pp-eval-cexpr: #if expression evaluator --- ;; Steps: resolve `defined NAME`, macro-expand the rest, treat any -;; remaining IDENT as 0, parse with recursive descent. +;; remaining IDENT as 0, then delegate to parse-const-int via a minimal +;; pstate (empty scope, no cg). sizeof(type) works as an extension; +;; sizeof(expr) dies with a clear message. ;; ;; Arena boundary (test 093 A→B→C pattern). Everything inside the -;; call-with-heap-rewind thunk is scratch: `s1`/`s2`/`s3` (each a fresh -;; token list, where `s2` runs the full macro-expansion engine), plus -;; the recursive parser's (val . rest) cons cell at every level. The -;; result is a fixnum, so no pre-allocated out cell is needed — `val` -;; survives the rewind by virtue of being an immediate. The error path -;; goes through `die` (which sys-exits), so no rewind there. +;; call-with-heap-rewind thunk is scratch: `s1`/`s2`/`s3` plus the +;; parse-const-* (value . ctype) cells at every level. parse-const-int +;; returns the integer via `car`, which is a fixnum immediate and +;; survives the rewind. The error path goes through `die` (sys-exits), +;; so no rewind there. +(define (%pp-make-const-ps toks) + (%pstate (make-list-iter toks) + (%world (list '()) (list '()) '() '()) + '() #f #f)) + (define (pp-eval-cexpr toks macros) (call-with-heap-rewind (lambda () (let* ((state (%pp-state macros '() #f 0 #f '() '())) (s1 (%pp-resolve-defined toks state)) (s2 (%pp-expand-line s1 state)) - (s3 (%pp-idents-as-zero s2))) - (let-values (((val rest) (%pp-cx-expr s3))) - (cond - ((null? rest) val) - (else (die (tok-loc (car rest)) "#if: garbage at end of expression" - (tok-kind (car rest)))))))))) + (s3 (%pp-idents-as-zero s2)) + (ps (%pp-make-const-ps s3)) + (val (parse-const-int ps)) + (t (peek ps))) + (cond + ((eq? (tok-kind t) 'EOF) val) + (else (die (tok-loc t) "#if: garbage at end of expression" + (tok-kind t)))))))) (define (%pp-expand-line toks state) (let ((out (make-buf-list))) @@ -2493,101 +2501,6 @@ (else t))) toks)) -;; --- recursive-descent #if expression parser --- -;; Each %pp-cx-* returns (values value rest). -(define (%pp-cx-expr toks) (%pp-cx-cond toks)) - -(define (%pp-cx-cond toks) - (let-values (((v rest) (%pp-cx-lor toks))) - (cond - ((and (not (null? rest)) (%pp-punct? (car rest) 'qmark)) - (let-values (((vt after) (%pp-cx-expr (cdr rest)))) - (cond - ((or (null? after) (not (%pp-punct? (car after) 'colon))) - (die (if (null? after) #f (tok-loc (car after))) "?: missing ':'")) - (else - (let-values (((vf rest3) (%pp-cx-cond (cdr after)))) - (values (if (not (= v 0)) vt vf) rest3)))))) - (else (values v rest))))) - -(define (%pp-cx-binl next ops toks) - (let-values (((v rest) (next toks))) - (let loop ((v v) (rest rest)) - (cond - ((null? rest) (values v rest)) - (else - (let* ((tt (car rest)) - (hit (and (eq? (tok-kind tt) 'PUNCT) - (alist-ref/eq (tok-value tt) ops)))) - (cond - ((not hit) (values v rest)) - (else - (let-values (((v2 rest2) (next (cdr rest)))) - (loop (hit v v2) rest2)))))))))) - -(define (%pp-cx-lor toks) - (%pp-cx-binl %pp-cx-land - (list (cons 'lor (lambda (a b) (if (or (not (= a 0)) (not (= b 0))) 1 0)))) - toks)) -(define (%pp-cx-land toks) - (%pp-cx-binl %pp-cx-bor - (list (cons 'land (lambda (a b) (if (and (not (= a 0)) (not (= b 0))) 1 0)))) - toks)) -(define (%pp-cx-bor toks) (%pp-cx-binl %pp-cx-bxor (list (cons 'bar bit-or)) toks)) -(define (%pp-cx-bxor toks) (%pp-cx-binl %pp-cx-band (list (cons 'caret bit-xor)) toks)) -(define (%pp-cx-band toks) (%pp-cx-binl %pp-cx-eq (list (cons 'amp bit-and)) toks)) -(define (%pp-cx-eq toks) - (%pp-cx-binl %pp-cx-rel - (list (cons 'eq2 (lambda (a b) (if (= a b) 1 0))) - (cons 'ne (lambda (a b) (if (= a b) 0 1)))) - toks)) -(define (%pp-cx-rel toks) - (%pp-cx-binl %pp-cx-shift - (list (cons 'lt (lambda (a b) (if (< a b) 1 0))) - (cons 'le (lambda (a b) (if (<= a b) 1 0))) - (cons 'gt (lambda (a b) (if (> a b) 1 0))) - (cons 'ge (lambda (a b) (if (>= a b) 1 0)))) - toks)) -(define (%pp-cx-shift toks) - (%pp-cx-binl %pp-cx-add - (list (cons 'shl (lambda (a b) (arithmetic-shift a b))) - (cons 'shr (lambda (a b) (arithmetic-shift a (- 0 b))))) - toks)) -(define (%pp-cx-add toks) - (%pp-cx-binl %pp-cx-mul (list (cons 'plus +) (cons 'minus -)) toks)) -(define (%pp-cx-mul toks) - (%pp-cx-binl %pp-cx-unary - (list (cons 'star *) (cons 'slash quotient) (cons 'pct remainder)) - toks)) - -(define (%pp-cx-unary toks) - (cond - ((null? toks) (die #f "#if: unexpected end of expression")) - ((%pp-punct? (car toks) 'plus) (%pp-cx-unary (cdr toks))) - ((%pp-punct? (car toks) 'minus) - (let-values (((v r) (%pp-cx-unary (cdr toks)))) - (values (- 0 v) r))) - ((%pp-punct? (car toks) 'bang) - (let-values (((v r) (%pp-cx-unary (cdr toks)))) - (values (if (= v 0) 1 0) r))) - ((%pp-punct? (car toks) 'tilde) - (let-values (((v r) (%pp-cx-unary (cdr toks)))) - (values (bit-not v) r))) - (else (%pp-cx-primary toks)))) - -(define (%pp-cx-primary toks) - (cond - ((null? toks) (die #f "#if: expected expression")) - ((%pp-int? (car toks)) (values (tok-value (car toks)) (cdr toks))) - ((eq? (tok-kind (car toks)) 'CHAR) - (values (tok-value (car toks)) (cdr toks))) - ((%pp-punct? (car toks) 'lparen) - (let-values (((v r) (%pp-cx-expr (cdr toks)))) - (cond - ((or (null? r) (not (%pp-punct? (car r) 'rparen))) - (die (if (null? r) #f (tok-loc (car r))) "#if: missing ')'")) - (else (values v (cdr r)))))) - (else (die (tok-loc (car toks)) "#if: unexpected token" (tok-kind (car toks)))))) ;; cc/cg.scm — codegen state and emission API. ;; Conversion split: parse owns promotion etc; cg owns sign extension, ;; signed/unsigned dispatch, pointer scaling. @@ -4571,93 +4484,60 @@ (cond ((%const-bool? c) t) (else e)))) (else c)))) +;; Generic left-associative binary level. +;; ops: alist of punct-sym → (vp vp → vp). +(define (%const-binl ps next ops) + (let lp ((a (next ps))) + (let* ((t (peek ps)) + (hit (and (eq? (tok-kind t) 'PUNCT) + (alist-ref/eq (tok-value t) ops)))) + (cond ((not hit) a) + (else (advance ps) (lp (hit a (next ps)))))))) + +;; Arithmetic combiner: promote both, arith-conv, apply fn, truncate. +(define (%const-arith-op fn a b) + (let-values (((av bv rt) (%const-arith-conv (%const-promote a) (%const-promote b)))) + (cons (%const-trunc (fn av bv) rt) rt))) + +;; Like %const-arith-op but rejects a zero divisor. +(define (%const-div-op fn a b) + (let-values (((av bv rt) (%const-arith-conv (%const-promote a) (%const-promote b)))) + (cond ((= bv 0) (die #f "const-expr: divide by zero"))) + (cons (%const-trunc (fn av bv) rt) rt))) + +;; Comparison combiner: result is always (0-or-1 . %t-i32). +(define (%const-cmp-op fn a b) + (let-values (((av bv _rt) (%const-arith-conv (%const-promote a) (%const-promote b)))) + (cons (if (fn av bv) 1 0) %t-i32))) + (define (parse-const-lor ps) - (let lp ((a (parse-const-land ps))) - (cond - ((at-punct? ps 'lor) - (advance ps) - (let ((b (parse-const-land ps))) - (lp (cons (if (or (%const-bool? a) (%const-bool? b)) 1 0) - %t-i32)))) - (else a)))) + (%const-binl ps parse-const-land + (list (cons 'lor (lambda (a b) + (cons (if (or (%const-bool? a) (%const-bool? b)) 1 0) %t-i32)))))) (define (parse-const-land ps) - (let lp ((a (parse-const-bor ps))) - (cond - ((at-punct? ps 'land) - (advance ps) - (let ((b (parse-const-bor ps))) - (lp (cons (if (and (%const-bool? a) (%const-bool? b)) 1 0) - %t-i32)))) - (else a)))) + (%const-binl ps parse-const-bor + (list (cons 'land (lambda (a b) + (cons (if (and (%const-bool? a) (%const-bool? b)) 1 0) %t-i32)))))) (define (parse-const-bor ps) - (let lp ((a (parse-const-bxor ps))) - (cond - ((at-punct? ps 'bar) - (advance ps) - (let ((b (parse-const-bxor ps))) - (let-values (((av bv rt) (%const-arith-conv (%const-promote a) - (%const-promote b)))) - (lp (cons (bit-or av bv) rt))))) - (else a)))) - + (%const-binl ps parse-const-bxor (list (cons 'bar (lambda (a b) (%const-arith-op bit-or a b)))))) (define (parse-const-bxor ps) - (let lp ((a (parse-const-band ps))) - (cond - ((at-punct? ps 'caret) - (advance ps) - (let ((b (parse-const-band ps))) - (let-values (((av bv rt) (%const-arith-conv (%const-promote a) - (%const-promote b)))) - (lp (cons (bit-xor av bv) rt))))) - (else a)))) - + (%const-binl ps parse-const-band (list (cons 'caret (lambda (a b) (%const-arith-op bit-xor a b)))))) (define (parse-const-band ps) - (let lp ((a (parse-const-eq ps))) - (cond - ((at-punct? ps 'amp) - (advance ps) - (let ((b (parse-const-eq ps))) - (let-values (((av bv rt) (%const-arith-conv (%const-promote a) - (%const-promote b)))) - (lp (cons (bit-and av bv) rt))))) - (else a)))) + (%const-binl ps parse-const-eq (list (cons 'amp (lambda (a b) (%const-arith-op bit-and a b)))))) (define (parse-const-eq ps) - (let lp ((a (parse-const-rel ps))) - (let ((t (peek ps))) - (cond - ((and (eq? (tok-kind t) 'PUNCT) - (or (eq? (tok-value t) 'eq2) (eq? (tok-value t) 'ne))) - (let ((op (tok-value (advance ps))) - (b (parse-const-rel ps))) - (let-values (((av bv _rt) (%const-arith-conv (%const-promote a) - (%const-promote b)))) - (lp (cons - (cond ((eq? op 'eq2) (if (= av bv) 1 0)) - (else (if (= av bv) 0 1))) - %t-i32))))) - (else a))))) + (%const-binl ps parse-const-rel + (list (cons 'eq2 (lambda (a b) (%const-cmp-op = a b))) + (cons 'ne (lambda (a b) (%const-cmp-op (lambda (x y) (not (= x y))) a b)))))) (define (parse-const-rel ps) - (let lp ((a (parse-const-shift ps))) - (let ((t (peek ps))) - (cond - ((and (eq? (tok-kind t) 'PUNCT) - (let ((v (tok-value t))) - (or (eq? v 'lt) (eq? v 'le) (eq? v 'gt) (eq? v 'ge)))) - (let ((op (tok-value (advance ps))) - (b (parse-const-shift ps))) - (let-values (((av bv _rt) (%const-arith-conv (%const-promote a) - (%const-promote b)))) - (lp (cons - (cond ((eq? op 'lt) (if (< av bv) 1 0)) - ((eq? op 'le) (if (<= av bv) 1 0)) - ((eq? op 'gt) (if (> av bv) 1 0)) - (else (if (>= av bv) 1 0))) - %t-i32))))) - (else a))))) + (%const-binl ps parse-const-shift + (list (cons 'lt (lambda (a b) (%const-cmp-op < a b))) + (cons 'le (lambda (a b) (%const-cmp-op <= a b))) + (cons 'gt (lambda (a b) (%const-cmp-op > a b))) + (cons 'ge (lambda (a b) (%const-cmp-op >= a b)))))) (define (parse-const-shift ps) ;; Shift result type is the (promoted) lhs type — rhs is just a count. @@ -4679,38 +4559,15 @@ (else a))))) (define (parse-const-add ps) - (let lp ((a (parse-const-mul ps))) - (let ((t (peek ps))) - (cond - ((and (eq? (tok-kind t) 'PUNCT) - (or (eq? (tok-value t) 'plus) (eq? (tok-value t) 'minus))) - (let ((op (tok-value (advance ps))) - (b (parse-const-mul ps))) - (let-values (((av bv rt) (%const-arith-conv (%const-promote a) - (%const-promote b)))) - (let ((raw (cond ((eq? op 'plus) (+ av bv)) (else (- av bv))))) - (lp (cons (%const-trunc raw rt) rt)))))) - (else a))))) + (%const-binl ps parse-const-mul + (list (cons 'plus (lambda (a b) (%const-arith-op + a b))) + (cons 'minus (lambda (a b) (%const-arith-op - a b)))))) (define (parse-const-mul ps) - (let lp ((a (parse-const-cast ps))) - (let ((t (peek ps))) - (cond - ((and (eq? (tok-kind t) 'PUNCT) - (let ((v (tok-value t))) - (or (eq? v 'star) (eq? v 'slash) (eq? v 'pct)))) - (let ((op (tok-value (advance ps))) - (b (parse-const-cast ps))) - (let-values (((av bv rt) (%const-arith-conv (%const-promote a) - (%const-promote b)))) - (cond - ((and (or (eq? op 'slash) (eq? op 'pct)) (= bv 0)) - (die (tok-loc t) "const-expr: divide by zero"))) - (let ((raw (cond ((eq? op 'star) (* av bv)) - ((eq? op 'slash) (quotient av bv)) - (else (remainder av bv))))) - (lp (cons (%const-trunc raw rt) rt)))))) - (else a))))) + (%const-binl ps parse-const-cast + (list (cons 'star (lambda (a b) (%const-arith-op * a b))) + (cons 'slash (lambda (a b) (%const-div-op quotient a b))) + (cons 'pct (lambda (a b) (%const-div-op remainder a b)))))) (define (parse-const-cast ps) ;; (typename) operand — distinguished from ( expr ) by paren-is-group?. @@ -4940,13 +4797,17 @@ ;; discarded. Returns the operand's byte size as a non-negative int. ;; If `paren?`, consumes the closing `)` after parsing. (define (%const-sizeof-expr ps paren?) - (let ((tag (cg-snapshot (ps-cg ps)))) - (cond (paren? (parse-expr ps) (expect-punct ps 'rparen)) - (else (parse-unary ps))) - (let* ((tp (cg-top (ps-cg ps))) - (sz (max (ctype-size (opnd-type tp)) 0))) - (cg-rewind (ps-cg ps) tag) - sz))) + (cond + ((not (ps-cg ps)) + (die #f "#if: sizeof of expression not valid in preprocessor context")) + (else + (let ((tag (cg-snapshot (ps-cg ps)))) + (cond (paren? (parse-expr ps) (expect-punct ps 'rparen)) + (else (parse-unary ps))) + (let* ((tp (cg-top (ps-cg ps))) + (sz (max (ctype-size (opnd-type tp)) 0))) + (cg-rewind (ps-cg ps) tag) + sz))))) ;; Convenience: returns the integer value alone (callers that don't ;; need the type half of parse-const-expr's (value . ctype) result).