boot2

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

commit 077025f552fbed9b0cc283b260e136b56f001347
parent c6a82a3a2bc1166329ee461d9c9d152ee5709ea6
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 29 Apr 2026 23:22:56 -0700

cc: fix for-loop continue to execute the step expression

The previous for-loop lowering used cg-loop which targets a single
back-edge label. A `continue` inside `for (init; cond; step) body`
jumped directly to the condition check, skipping the step expression.

Re-lower for-loops explicitly:
  jump test; top: step; test: cond; if-false break; body; jump top

continue now targets ::TAG_top (step), break targets ::TAG_end.
The step token list is collected before the body is parsed and replayed
via parse-saved-expr-stmt after the top label is emitted.

Refactor collect-til-rparen into collect-til-top-punct (parameterised
on delimiter and error message) so the for-cond tokens can also be
collected without special-casing semicolons. Bracket pairs
([]) are now tracked alongside parens when scanning for the delimiter.

Regression: tests/cc/133-for-continue.c.

Diffstat:
Mcc/cc.scm | 77+++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Atests/cc/133-for-continue.c | 12++++++++++++
Atests/cc/133-for-continue.expected-exit | 1+
3 files changed, 60 insertions(+), 30 deletions(-)

diff --git a/cc/cc.scm b/cc/cc.scm @@ -6056,49 +6056,66 @@ ((stmt-starts-decl? ps) (parse-local-decl ps)) (else (parse-expr ps) (cg-pop (ps-cg ps)) (expect-punct ps 'semi))) - (cg-loop (ps-cg ps) - (lambda () - (cond ((at-punct? ps 'semi) - (cg-push-imm (ps-cg ps) %t-i32 1)) - (else (parse-expr ps) (rval! ps))) - (expect-punct ps 'semi)) - (lambda (tag) - (let ((stk (collect-til-rparen ps))) - (expect-punct ps 'rparen) - (push-loop-ctx! ps 'for tag #t) - (parse-stmt ps) - (pop-loop-ctx! ps) - (cond - ((null? stk) #t) - (else - ;; Step expr is parsed AFTER the body. Swap the - ;; parser's iter for a list-iter wrapping the saved - ;; tokens (followed by EOF so parse-expr stops); - ;; restore on exit. - (let ((sv (ps-iter ps))) - (ps-iter-set! ps - (make-list-iter - (append stk (list (make-tok 'EOF #f #f))))) - (parse-expr ps) (cg-pop (ps-cg ps)) - (ps-iter-set! ps sv))))))) + (let* ((cg (ps-cg ps)) + (cond-toks (cond + ((at-punct? ps 'semi) '()) + (else (collect-til-top-punct ps 'semi "EOF in for-cond")))) + (_ (expect-punct ps 'semi)) + (step-toks (collect-til-rparen ps)) + (_ (expect-punct ps 'rparen)) + (tag (%cg-fresh-loop-tag cg))) + ;; A C `continue` in a for-loop must run the step expression before + ;; retesting the condition. Arrange the loop as: + ;; jump test; top: step; test: condition; body; jump top + (%cg-emit-many cg (list "%b(&::" tag "_test)\n" + "::" tag "_top\n")) + (parse-saved-expr-stmt ps step-toks) + (%cg-emit-many cg (list "::" tag "_test\n")) + (cond + ((null? cond-toks) (cg-push-imm cg %t-i32 1)) + (else (parse-saved-expr ps cond-toks) (rval! ps))) + (let ((c (cg-pop cg))) + (%cg-load-opnd-into cg c 't0) + (%cg-emit-many cg (list "%if_eqz(t0, { %break(" tag ") })\n"))) + (push-loop-ctx! ps 'for tag #t) + (parse-stmt ps) + (pop-loop-ctx! ps) + (%cg-emit-many cg (list "%b(&::" tag "_top)\n" + "::" tag "_end\n"))) (scope-leave! ps) #t) -(define (collect-til-rparen ps) +(define (parse-saved-expr ps toks) + (let ((sv (ps-iter ps))) + (ps-iter-set! ps (make-list-iter (append toks (list (make-tok 'EOF #f #f))))) + (parse-expr ps) + (ps-iter-set! ps sv))) + +(define (parse-saved-expr-stmt ps toks) + (cond + ((null? toks) #t) + (else (parse-saved-expr ps toks) (cg-pop (ps-cg ps))))) + +(define (collect-til-top-punct ps punct err) (let loop ((acc '()) (d 0)) (let ((t (peek ps))) (cond ((eq? (tok-kind t) 'EOF) - (die (tok-loc t) "EOF in for-step")) + (die (tok-loc t) err)) ((and (zero? d) (eq? (tok-kind t) 'PUNCT) - (eq? (tok-value t) 'rparen)) (reverse acc)) + (eq? (tok-value t) punct)) (reverse acc)) (else (let ((nt (advance ps))) (loop (cons nt acc) (cond ((not (eq? (tok-kind nt) 'PUNCT)) d) - ((eq? (tok-value nt) 'lparen) (+ d 1)) - ((eq? (tok-value nt) 'rparen) (- d 1)) + ((or (eq? (tok-value nt) 'lparen) + (eq? (tok-value nt) 'lbrack)) (+ d 1)) + ((or (eq? (tok-value nt) 'rparen) + (eq? (tok-value nt) 'rbrack)) (- d 1)) (else d))))))))) +(define (collect-til-rparen ps) + (collect-til-top-punct ps 'rparen "EOF in for-step")) + (define (parse-switch-stmt ps) (expect-kw ps 'switch) (expect-punct ps 'lparen) (parse-expr ps) (rval! ps) diff --git a/tests/cc/133-for-continue.c b/tests/cc/133-for-continue.c @@ -0,0 +1,12 @@ +int main(void) { + int i; + int sum = 0; + + for (i = 0; i < 5; i = i + 1) { + if (i < 3) + continue; + sum = sum + i; + } + + return sum == 7 ? 0 : 1; +} diff --git a/tests/cc/133-for-continue.expected-exit b/tests/cc/133-for-continue.expected-exit @@ -0,0 +1 @@ +0