spec: define-type special form — constructors, predicates, accessors (20 tests)
Adds sf-define-type via register-special-form! in spec/evaluator.sx.
ADT values are dicts {:_adt true :_type "T" :_ctor "C" :_fields (list ...)}.
Each define-type call registers: ctor functions with arity checking, Name?
type predicate, Ctor? constructor predicates, Ctor-field positional accessors,
and populates *adt-registry* dict with type→[ctor-names] mapping.
20/20 JS tests pass in spec/tests/test-adt.sx.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1898,6 +1898,67 @@
|
||||
:else (step-eval-call head args env kont)))))
|
||||
(step-eval-call head args env kont))))))
|
||||
|
||||
(define
|
||||
sf-define-type
|
||||
(fn
|
||||
(args env)
|
||||
(let
|
||||
((type-sym (first args)) (ctor-specs (rest args)))
|
||||
(let
|
||||
((type-name (symbol-name type-sym))
|
||||
(ctor-names
|
||||
(map (fn (spec) (symbol-name (first spec))) ctor-specs)))
|
||||
(when
|
||||
(not (env-has? env "*adt-registry*"))
|
||||
(env-bind! env "*adt-registry*" {}))
|
||||
(dict-set! (env-get env "*adt-registry*") type-name ctor-names)
|
||||
(env-bind!
|
||||
env
|
||||
(str type-name "?")
|
||||
(fn
|
||||
(v)
|
||||
(and (dict? v) (get v :_adt) (= (get v :_type) type-name))))
|
||||
(for-each
|
||||
(fn
|
||||
(spec)
|
||||
(let
|
||||
((cn (symbol-name (first spec)))
|
||||
(field-names (map (fn (f) (symbol-name f)) (rest spec)))
|
||||
(arity (len (rest spec))))
|
||||
(env-bind!
|
||||
env
|
||||
cn
|
||||
(fn
|
||||
(&rest ctor-args)
|
||||
(if
|
||||
(not (= (len ctor-args) arity))
|
||||
(error
|
||||
(str
|
||||
cn
|
||||
": expected "
|
||||
arity
|
||||
" args, got "
|
||||
(len ctor-args)))
|
||||
{:_ctor cn :_type type-name :_adt true :_fields ctor-args})))
|
||||
(env-bind!
|
||||
env
|
||||
(str cn "?")
|
||||
(fn
|
||||
(v)
|
||||
(and (dict? v) (get v :_adt) (= (get v :_ctor) cn))))
|
||||
(for-each-indexed
|
||||
(fn
|
||||
(idx field-name)
|
||||
(env-bind!
|
||||
env
|
||||
(str cn "-" field-name)
|
||||
(fn (v) (nth (get v :_fields) idx))))
|
||||
field-names)))
|
||||
ctor-specs)
|
||||
nil))))
|
||||
|
||||
(register-special-form! "define-type" sf-define-type)
|
||||
|
||||
(define
|
||||
kont-extract-provides
|
||||
(fn
|
||||
@@ -1932,6 +1993,14 @@
|
||||
subs)
|
||||
(for-each (fn (sub) (cek-call sub (list kont))) subs))))))
|
||||
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
;; Part 10: Continue Phase — Frame Dispatch
|
||||
;;
|
||||
;; When phase="continue", pop the top frame and process the value.
|
||||
;; Each frame type has its own handling: if frames check truthiness,
|
||||
;; let frames bind the value, arg frames accumulate it, etc.
|
||||
;; continue-with-call handles the final function/component dispatch.
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
(define
|
||||
fire-provide-subscribers
|
||||
(fn
|
||||
@@ -1951,18 +2020,13 @@
|
||||
subs)
|
||||
(for-each (fn (sub) (cek-call sub (list nil))) subs))))))
|
||||
|
||||
;; Final call dispatch from arg frame — all args evaluated, invoke function.
|
||||
;; Handles: lambda (bind params + TCO), component (keyword args + TCO),
|
||||
;; native fn (direct call), continuation (resume), callcc continuation (escape).
|
||||
(define
|
||||
batch-begin!
|
||||
(fn () (set! *provide-batch-depth* (+ *provide-batch-depth* 1))))
|
||||
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
;; Part 10: Continue Phase — Frame Dispatch
|
||||
;;
|
||||
;; When phase="continue", pop the top frame and process the value.
|
||||
;; Each frame type has its own handling: if frames check truthiness,
|
||||
;; let frames bind the value, arg frames accumulate it, etc.
|
||||
;; continue-with-call handles the final function/component dispatch.
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
(define
|
||||
batch-end!
|
||||
(fn
|
||||
@@ -1975,9 +2039,13 @@
|
||||
(set! *provide-batch-queue* (list))
|
||||
(for-each (fn (sub) (cek-call sub (list nil))) queue)))))
|
||||
|
||||
;; Final call dispatch from arg frame — all args evaluated, invoke function.
|
||||
;; Handles: lambda (bind params + TCO), component (keyword args + TCO),
|
||||
;; native fn (direct call), continuation (resume), callcc continuation (escape).
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
;; Part 11: Entry Points
|
||||
;;
|
||||
;; eval-expr-cek / trampoline-cek: CEK evaluation entry points.
|
||||
;; eval-expr / trampoline: top-level bindings that override the
|
||||
;; forward declarations from Part 5.
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
(define
|
||||
step-sf-bind
|
||||
(fn
|
||||
@@ -2008,13 +2076,6 @@
|
||||
(make-parameterize-frame bindings nil (list) body env)
|
||||
kont)))))))
|
||||
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
;; Part 11: Entry Points
|
||||
;;
|
||||
;; eval-expr-cek / trampoline-cek: CEK evaluation entry points.
|
||||
;; eval-expr / trampoline: top-level bindings that override the
|
||||
;; forward declarations from Part 5.
|
||||
;; ═══════════════════════════════════════════════════════════════
|
||||
(define
|
||||
syntax-rules-match
|
||||
(fn
|
||||
@@ -2186,7 +2247,10 @@
|
||||
((all-vars (syntax-rules-find-all-vars elem bindings)))
|
||||
(if
|
||||
(empty? all-vars)
|
||||
(syntax-rules-instantiate-list template (+ i 2) bindings)
|
||||
(syntax-rules-instantiate-list
|
||||
template
|
||||
(+ i 2)
|
||||
bindings)
|
||||
(let
|
||||
((count (len (get bindings (first all-vars))))
|
||||
(expanded
|
||||
@@ -2209,7 +2273,10 @@
|
||||
(syntax-rules-instantiate elem b)))
|
||||
(range count)))
|
||||
(rest-result
|
||||
(syntax-rules-instantiate-list template (+ i 2) bindings)))
|
||||
(syntax-rules-instantiate-list
|
||||
template
|
||||
(+ i 2)
|
||||
bindings)))
|
||||
(append expanded rest-result))))
|
||||
(cons
|
||||
(syntax-rules-instantiate elem bindings)
|
||||
@@ -2536,7 +2603,8 @@
|
||||
(let
|
||||
((proto-name (symbol-name (first args)))
|
||||
(raw-type-name (symbol-name (nth args 1)))
|
||||
(type-name (slice raw-type-name 1 (- (len raw-type-name) 1)))
|
||||
(type-name
|
||||
(slice raw-type-name 1 (- (len raw-type-name) 1)))
|
||||
(method-defs (rest (rest args))))
|
||||
(let
|
||||
((proto (get *protocol-registry* proto-name)))
|
||||
@@ -2678,8 +2746,12 @@
|
||||
(and
|
||||
(>= (len value) rest-idx)
|
||||
(every?
|
||||
(fn (pair) (match-pattern (first pair) (nth pair 1) env))
|
||||
(zip (slice pattern 0 rest-idx) (slice value 0 rest-idx)))
|
||||
(fn
|
||||
(pair)
|
||||
(match-pattern (first pair) (nth pair 1) env))
|
||||
(zip
|
||||
(slice pattern 0 rest-idx)
|
||||
(slice value 0 rest-idx)))
|
||||
(let
|
||||
((rest-name (nth pattern (+ rest-idx 1))))
|
||||
(env-bind! env (symbol-name rest-name) (slice value rest-idx))
|
||||
@@ -2691,7 +2763,9 @@
|
||||
(let
|
||||
((pairs (zip pattern value)))
|
||||
(every?
|
||||
(fn (pair) (match-pattern (first pair) (nth pair 1) env))
|
||||
(fn
|
||||
(pair)
|
||||
(match-pattern (first pair) (nth pair 1) env))
|
||||
pairs)))
|
||||
:else (= pattern value))))
|
||||
|
||||
@@ -3354,7 +3428,8 @@
|
||||
kont)))))
|
||||
("reduce"
|
||||
(let
|
||||
((init (nth ordered 1)) (coll (nth ordered 2)))
|
||||
((init (nth ordered 1))
|
||||
(coll (nth ordered 2)))
|
||||
(if
|
||||
(empty? coll)
|
||||
(make-cek-value init env kont)
|
||||
@@ -3658,7 +3733,10 @@
|
||||
(next-test (first next-clause)))
|
||||
(if
|
||||
(is-else-clause? next-test)
|
||||
(make-cek-state (nth next-clause 1) fenv rest-k)
|
||||
(make-cek-state
|
||||
(nth next-clause 1)
|
||||
fenv
|
||||
rest-k)
|
||||
(make-cek-state
|
||||
next-test
|
||||
fenv
|
||||
@@ -3830,7 +3908,9 @@
|
||||
(let
|
||||
((d (dict)))
|
||||
(for-each
|
||||
(fn (pair) (dict-set! d (first pair) (nth pair 1)))
|
||||
(fn
|
||||
(pair)
|
||||
(dict-set! d (first pair) (nth pair 1)))
|
||||
completed)
|
||||
(make-cek-value d fenv rest-k))
|
||||
(let
|
||||
@@ -4156,9 +4236,14 @@
|
||||
(list)
|
||||
fenv
|
||||
(list)
|
||||
(kont-push (make-wind-return-frame body-result fenv) rest-k)))))
|
||||
(kont-push
|
||||
(make-wind-return-frame body-result fenv)
|
||||
rest-k)))))
|
||||
("wind-return"
|
||||
(make-cek-value (get frame "body-result") (get frame "env") rest-k))
|
||||
(make-cek-value
|
||||
(get frame "body-result")
|
||||
(get frame "env")
|
||||
rest-k))
|
||||
("raise-eval"
|
||||
(let
|
||||
((condition value)
|
||||
@@ -4183,7 +4268,9 @@
|
||||
(kont-push
|
||||
(make-signal-return-frame fenv unwound-k)
|
||||
unwound-k)
|
||||
(kont-push (make-raise-guard-frame fenv unwound-k) unwound-k))))))
|
||||
(kont-push
|
||||
(make-raise-guard-frame fenv unwound-k)
|
||||
unwound-k))))))
|
||||
("raise-guard"
|
||||
(do
|
||||
(set! *last-error-kont* rest-k)
|
||||
@@ -4317,9 +4404,7 @@
|
||||
((arg (if (empty? args) nil (first args)))
|
||||
(captured (callcc-continuation-data f))
|
||||
(w-len (callcc-continuation-winders-len f)))
|
||||
(do
|
||||
(wind-escape-to w-len)
|
||||
(make-cek-value arg env captured)))
|
||||
(do (wind-escape-to w-len) (make-cek-value arg env captured)))
|
||||
(continuation? f)
|
||||
(let
|
||||
((arg (if (empty? args) nil (first args)))
|
||||
@@ -4364,7 +4449,9 @@
|
||||
" args, got "
|
||||
(len args))))
|
||||
(for-each
|
||||
(fn (pair) (env-bind! local (first pair) (nth pair 1)))
|
||||
(fn
|
||||
(pair)
|
||||
(env-bind! local (first pair) (nth pair 1)))
|
||||
(zip params args))
|
||||
(for-each
|
||||
(fn (p) (env-bind! local p nil))
|
||||
@@ -4419,7 +4506,11 @@
|
||||
(if
|
||||
(= match-val test-val)
|
||||
(make-cek-state body env kont)
|
||||
(sf-case-step-loop match-val (slice clauses 2) env kont))))))))
|
||||
(sf-case-step-loop
|
||||
match-val
|
||||
(slice clauses 2)
|
||||
env
|
||||
kont))))))))
|
||||
|
||||
(define
|
||||
eval-expr-cek
|
||||
|
||||
Reference in New Issue
Block a user