Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 44s
hk-bind-exceptions! in eval.sx registers throwIO, throw, evaluate, catch, try, handle, displayException. SomeException constructor pre-registered in runtime.sx (arity 1, type SomeException). throwIO and the existing error primitive both raise via SX `raise` with a uniform "hk-error: msg" string. catch/try/handle parse it back into a SomeException via hk-exception-of, which strips nested 'Unhandled exception: "..."' host wraps (CEK's host_error formatter) and the "hk-error: " prefix. catch and handle evaluate the handler outside the guard scope (build an "ok"/"exn" outcome tag inside guard, then dispatch outside) so that a re-throw from the handler propagates past this catch — matching Haskell semantics rather than infinite-looping in the same guard. 14 unit tests in tests/exceptions.sx (catch success, catch error, try Right/Left, handle, throwIO + catch/try, evaluate, nested catch, do-bind through catch, branch on try result, IORef-mutating handler). Conformance: safediv.hs (8/8) and trycatch.hs (8/8). Scoreboard now 285/285 tests, 36/36 programs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
151 lines
4.1 KiB
Plaintext
151 lines
4.1 KiB
Plaintext
;; Haskell runtime: constructor registry.
|
|
;;
|
|
;; A mutable dict keyed by constructor name (e.g. "Just", "[]") with
|
|
;; entries of shape {:arity N :type TYPE-NAME-STRING}.
|
|
;; Populated by ingesting `data` / `newtype` decls from parsed ASTs.
|
|
;; Pre-registers a small set of constructors tied to Haskell syntactic
|
|
;; forms (Bool, list, unit) — every nontrivial program depends on
|
|
;; these, and the parser/desugar pipeline emits them as (:var "True")
|
|
;; etc. without a corresponding `data` decl.
|
|
|
|
(define hk-constructors (dict))
|
|
|
|
(define
|
|
hk-register-con!
|
|
(fn (cname arity type-name) (dict-set! hk-constructors cname {:arity arity :type type-name})))
|
|
|
|
(define hk-is-con? (fn (name) (has-key? hk-constructors name)))
|
|
|
|
(define
|
|
hk-con-arity
|
|
(fn
|
|
(name)
|
|
(if
|
|
(has-key? hk-constructors name)
|
|
(get (get hk-constructors name) "arity")
|
|
nil)))
|
|
|
|
(define
|
|
hk-con-type
|
|
(fn
|
|
(name)
|
|
(if
|
|
(has-key? hk-constructors name)
|
|
(get (get hk-constructors name) "type")
|
|
nil)))
|
|
|
|
(define hk-con-names (fn () (keys hk-constructors)))
|
|
|
|
;; ── Registration from AST ────────────────────────────────────
|
|
;; (:data NAME TVARS ((:con-def CNAME FIELDS) …))
|
|
(define
|
|
hk-register-data!
|
|
(fn
|
|
(data-node)
|
|
(let
|
|
((type-name (nth data-node 1)) (cons-list (nth data-node 3)))
|
|
(for-each
|
|
(fn (cd) (hk-register-con! (nth cd 1) (len (nth cd 2)) type-name))
|
|
cons-list))))
|
|
|
|
;; (:newtype NAME TVARS CNAME FIELD)
|
|
(define
|
|
hk-register-newtype!
|
|
(fn (nt-node) (hk-register-con! (nth nt-node 3) 1 (nth nt-node 1))))
|
|
|
|
;; Walk a decls list, registering every `data` / `newtype` decl.
|
|
(define
|
|
hk-register-decls!
|
|
(fn
|
|
(decls)
|
|
(for-each
|
|
(fn
|
|
(d)
|
|
(cond
|
|
((and (list? d) (not (empty? d)) (= (first d) "data"))
|
|
(hk-register-data! d))
|
|
((and (list? d) (not (empty? d)) (= (first d) "newtype"))
|
|
(hk-register-newtype! d))
|
|
(:else nil)))
|
|
decls)))
|
|
|
|
(define
|
|
hk-register-program!
|
|
(fn
|
|
(ast)
|
|
(cond
|
|
((nil? ast) nil)
|
|
((not (list? ast)) nil)
|
|
((empty? ast) nil)
|
|
((= (first ast) "program") (hk-register-decls! (nth ast 1)))
|
|
((= (first ast) "module") (hk-register-decls! (nth ast 4)))
|
|
(:else nil))))
|
|
|
|
;; Convenience: source → AST → desugar → register.
|
|
(define hk-load-source! (fn (src) (hk-register-program! (hk-core src))))
|
|
|
|
;; ── Built-in constructors pre-registered ─────────────────────
|
|
;; Bool — used implicitly by `if`, comparison operators.
|
|
(hk-register-con! "True" 0 "Bool")
|
|
(hk-register-con! "False" 0 "Bool")
|
|
;; List — used by list literals, range syntax, and cons operator.
|
|
(hk-register-con! "[]" 0 "List")
|
|
(hk-register-con! ":" 2 "List")
|
|
;; Unit — produced by empty parens `()`.
|
|
(hk-register-con! "()" 0 "Unit")
|
|
;; Standard Prelude types — pre-registered so expression-level
|
|
;; programs can use them without a `data` decl.
|
|
(hk-register-con! "Nothing" 0 "Maybe")
|
|
(hk-register-con! "Just" 1 "Maybe")
|
|
(hk-register-con! "Left" 1 "Either")
|
|
(hk-register-con! "Right" 1 "Either")
|
|
(hk-register-con! "LT" 0 "Ordering")
|
|
(hk-register-con! "EQ" 0 "Ordering")
|
|
(hk-register-con! "GT" 0 "Ordering")
|
|
(hk-register-con! "SomeException" 1 "SomeException")
|
|
|
|
(define
|
|
hk-str?
|
|
(fn (v) (or (string? v) (and (dict? v) (has-key? v "hk-str")))))
|
|
|
|
(define
|
|
hk-str-head
|
|
(fn
|
|
(v)
|
|
(if
|
|
(string? v)
|
|
(char-code (char-at v 0))
|
|
(char-code (char-at (get v "hk-str") (get v "hk-off"))))))
|
|
|
|
(define
|
|
hk-str-tail
|
|
(fn
|
|
(v)
|
|
(let
|
|
((buf (if (string? v) v (get v "hk-str")))
|
|
(off (if (string? v) 1 (+ (get v "hk-off") 1))))
|
|
(if (>= off (string-length buf)) (list "[]") {:hk-off off :hk-str buf}))))
|
|
|
|
(define
|
|
hk-str-null?
|
|
(fn
|
|
(v)
|
|
(if
|
|
(string? v)
|
|
(= (string-length v) 0)
|
|
(>= (get v "hk-off") (string-length (get v "hk-str"))))))
|
|
|
|
(define
|
|
hk-str-to-native
|
|
(fn
|
|
(v)
|
|
(if
|
|
(string? v)
|
|
v
|
|
(let
|
|
((buf (get v "hk-str")) (off (get v "hk-off")))
|
|
(reduce
|
|
(fn (acc i) (str acc (char-at buf i)))
|
|
""
|
|
(range off (string-length buf)))))))
|