HS: break/continue/until — loop control flow via guard/raise

Parser:
- Add break, continue, exit/halt as parsed commands
- Handle bottom-tested repeat: repeat <body> until <cond>
- Handle bottom-tested repeat: repeat <body> while <cond>

Compiler:
- break → (raise "hs-break"), continue → (raise "hs-continue")
- repeat-until/repeat-while → hs-repeat-until/hs-repeat-while
- for loops use hs-for-each (break/continue aware) instead of for-each

Runtime:
- hs-repeat-times, hs-repeat-forever, hs-repeat-while: wrap body in
  guard to catch hs-break (exit loop) and hs-continue (next iteration)
- Add hs-repeat-until: bottom-tested do-until loop with guard
- Add hs-for-each: break/continue aware iteration over lists

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-20 17:58:58 +00:00
parent 79b3fa3f26
commit f200418d91
3 changed files with 112 additions and 26 deletions

View File

@@ -320,21 +320,76 @@
(n thunk)
(define
do-repeat
(fn (i) (when (< i n) (do (thunk) (do-repeat (+ i 1))))))
(fn
(i)
(when
(< i n)
(let
((signal (guard (e (true (str e))) (do (thunk) nil))))
(cond
((= signal "hs-break") nil)
((= signal "hs-continue") (do-repeat (+ i 1)))
(true (do-repeat (+ i 1))))))))
(do-repeat 0)))
(define
hs-repeat-forever
(fn
(thunk)
(define do-forever (fn () (thunk) (do-forever)))
(define
do-forever
(fn
()
(let
((signal (guard (e (true (str e))) (do (thunk) nil))))
(cond
((= signal "hs-break") nil)
((= signal "hs-continue") (do-forever))
(true (do-forever))))))
(do-forever)))
(define
hs-repeat-while
(fn
(cond-fn thunk)
(when (cond-fn) (thunk) (hs-repeat-while cond-fn thunk))))
(when
(cond-fn)
(let
((signal (guard (e (true (str e))) (do (thunk) nil))))
(cond
((= signal "hs-break") nil)
((= signal "hs-continue") (hs-repeat-while cond-fn thunk))
(true (hs-repeat-while cond-fn thunk)))))))
(define
hs-repeat-until
(fn
(cond-fn thunk)
(let
((signal (guard (e (true (str e))) (do (thunk) nil))))
(cond
((= signal "hs-break") nil)
((= signal "hs-continue")
(if (cond-fn) nil (hs-repeat-until cond-fn thunk)))
(true (if (cond-fn) nil (hs-repeat-until cond-fn thunk)))))))
(define
hs-for-each
(fn
(fn-body collection)
(define
do-loop
(fn
(items)
(when
(not (empty? items))
(let
((signal (guard (e (true (str e))) (do (fn-body (first items)) nil))))
(cond
((= signal "hs-break") nil)
((= signal "hs-continue") (do-loop (rest items)))
(true (do-loop (rest items))))))))
(when (list? collection) (do-loop collection))))
(define
hs-fetch
@@ -342,6 +397,10 @@
(url format)
(perform (list "io-fetch" url (if format format "text")))))
(define
hs-coerce
(fn
@@ -440,11 +499,8 @@
((list? b) (cons a b))
((or (string? a) (string? b)) (str a b))
(true (+ a b)))))
;; ── Sandbox/test runtime additions ──────────────────────────────
;; Property access — dot notation and .length
(define
hs-make
(fn
@@ -455,14 +511,15 @@
((= type-name "Set") (list))
((= type-name "Map") (dict))
(true (dict)))))
;; DOM query stub — sandbox returns empty list
(define hs-install (fn (behavior-fn) (behavior-fn me)))
;; ── Sandbox/test runtime additions ──────────────────────────────
;; Property access — dot notation and .length
;; Method dispatch — obj.method(args)
(define
hs-measure
(fn (target) (perform (list (quote io-measure) target))))
;; DOM query stub — sandbox returns empty list
;; ── 0.9.90 features ─────────────────────────────────────────────
;; beep! — debug logging, returns value unchanged
(define
hs-transition
(fn
@@ -475,7 +532,7 @@
(str prop " " (/ duration 1000) "s")))
(dom-set-style target prop value)
(when duration (hs-settle target))))
;; Method dispatch — obj.method(args)
;; Property-based is — check obj.key truthiness
(define
hs-transition-from
(fn
@@ -489,9 +546,7 @@
(str prop " " (/ duration 1000) "s")))
(dom-set-style target prop (str to-val))
(when duration (hs-settle target))))
;; ── 0.9.90 features ─────────────────────────────────────────────
;; beep! — debug logging, returns value unchanged
;; Array slicing (inclusive both ends)
(define
hs-type-check
(fn
@@ -511,31 +566,31 @@
(= (host-typeof value) "element")
(= (host-typeof value) "text")))
(true (= (host-typeof value) (downcase type-name)))))))
;; Property-based is — check obj.key truthiness
;; Collection: sorted by
(define
hs-type-check-strict
(fn
(value type-name)
(if (nil? value) false (hs-type-check value type-name))))
;; Array slicing (inclusive both ends)
;; Collection: sorted by descending
(define
hs-strict-eq
(fn (a b) (and (= (type-of a) (type-of b)) (= a b))))
;; Collection: sorted by
;; Collection: split by
(define
hs-eq-ignore-case
(fn (a b) (= (downcase (str a)) (downcase (str b)))))
;; Collection: sorted by descending
;; Collection: joined by
(define
hs-starts-with-ic?
(fn (str prefix) (starts-with? (downcase str) (downcase prefix))))
;; Collection: split by
(define
hs-contains-ignore-case?
(fn
(haystack needle)
(contains? (downcase (str haystack)) (downcase (str needle)))))
;; Collection: joined by
(define
hs-falsy?
(fn