diff --git a/lib/haskell/eval.sx b/lib/haskell/eval.sx index 82b2936b..e159d5b2 100644 --- a/lib/haskell/eval.sx +++ b/lib/haskell/eval.sx @@ -681,73 +681,95 @@ negate x = 0 - x (fn (env decls) (let - ((groups (dict)) - (group-order (list)) - (pat-binds (list))) - ;; Pass 1: collect fun-clause groups by name; track first-seen - ;; order so pass 3 can evaluate 0-arity bodies in source order - ;; (forward references to other 0-arity definitions still need - ;; the earlier name to be bound first). + ((groups (dict)) (group-order (list)) (pat-binds (list))) (for-each - (fn (d) + (fn + (d) (cond ((= (first d) "fun-clause") (let ((name (nth d 1))) - (when (not (has-key? groups name)) + (when + (not (has-key? groups name)) (append! group-order name)) (dict-set! groups name (append - (if - (has-key? groups name) - (get groups name) - (list)) + (if (has-key? groups name) (get groups name) (list)) (list (list (nth d 2) (nth d 3))))) - (when - (not (has-key? env name)) - (dict-set! env name nil)))) + (when (not (has-key? env name)) (dict-set! env name nil)))) ((or (= (first d) "bind") (= (first d) "pat-bind")) (append! pat-binds d)) + ((= (first d) "class-decl") + (dict-set! + env + (str "__class__" (nth d 1)) + (list "class" (nth d 1) (nth d 2)))) + ((= (first d) "instance-decl") + (let + ((cls (nth d 1)) + (inst-type (nth d 2)) + (method-decls (nth d 3))) + (let + ((inst-dict (dict)) + (inst-key + (str "dict" cls "_" (hk-type-ast-str inst-type)))) + (for-each + (fn + (m) + (when + (= (first m) "fun-clause") + (let + ((mname (nth m 1)) + (pats (nth m 2)) + (body (nth m 3))) + (dict-set! + inst-dict + mname + (if + (empty? pats) + (hk-eval body env) + (hk-eval (list "lambda" pats body) env)))))) + method-decls) + (dict-set! env inst-key inst-dict)))) (:else nil))) decls) - ;; Pass 2: install multifuns (arity > 0) — order doesn't matter - ;; because they're closures; collect 0-arity names in source - ;; order for pass 3. - (let ((zero-arity (list))) + (let + ((zero-arity (list))) (for-each - (fn (name) - (let ((clauses (get groups name))) - (let ((arity (len (first (first clauses))))) + (fn + (name) + (let + ((clauses (get groups name))) + (let + ((arity (len (first (first clauses))))) (cond ((> arity 0) - (dict-set! - env - name - (hk-mk-multifun arity clauses env))) + (dict-set! env name (hk-mk-multifun arity clauses env))) (:else (append! zero-arity name)))))) group-order) - ;; Pass 3: evaluate 0-arity bodies and pat-binds in source - ;; order — forward references to a later 0-arity name will - ;; still see its placeholder (nil) and fail noisily, but the - ;; common case of a top-down program works. (for-each - (fn (name) - (let ((clauses (get groups name))) + (fn + (name) + (let + ((clauses (get groups name))) (dict-set! env name (hk-eval (first (rest (first clauses))) env)))) zero-arity) (for-each - (fn (d) - (let ((pat (nth d 1)) (body (nth d 2))) - (let ((val (hk-eval body env))) - (let ((res (hk-match pat val env))) + (fn + (d) + (let + ((pat (nth d 1)) (body (nth d 2))) + (let + ((val (hk-eval body env))) + (let + ((res (hk-match pat val env))) (cond - ((nil? res) - (raise "top-level pattern bind failure")) + ((nil? res) (raise "top-level pattern bind failure")) (:else (hk-extend-env-with-match! env res))))))) pat-binds)) env))) @@ -791,6 +813,22 @@ negate x = 0 - x (src) (hk-deep-force (hk-eval (hk-core-expr src) (hk-dict-copy hk-env0))))) +(define + hk-type-ast-str + (fn + (ast) + (cond + ((= (first ast) "t-con") (nth ast 1)) + ((= (first ast) "t-var") (nth ast 1)) + ((= (first ast) "t-list") + (str "[" (hk-type-ast-str (nth ast 1)) "]")) + ((= (first ast) "t-app") + (str + (hk-type-ast-str (nth ast 1)) + " " + (hk-type-ast-str (nth ast 2)))) + (:else "?")))) + (define hk-typecheck (fn diff --git a/lib/haskell/parser.sx b/lib/haskell/parser.sx index b4d0b2ef..5fc0fe4d 100644 --- a/lib/haskell/parser.sx +++ b/lib/haskell/parser.sx @@ -143,7 +143,6 @@ (tokens mode) (let ((toks tokens) (pos 0) (n (len tokens))) - (define hk-peek (fn () (if (< pos n) (nth toks pos) nil))) (define hk-peek-at @@ -153,9 +152,12 @@ (define hk-advance! (fn () (let ((t (hk-peek))) (set! pos (+ pos 1)) t))) + (define hk-next hk-advance!) (define hk-peek-type - (fn () (let ((t (hk-peek))) (if (nil? t) "" (get t "type"))))) + (fn + () + (let ((t (hk-peek))) (if (nil? t) "" (get t "type"))))) (define hk-peek-value (fn () (let ((t (hk-peek))) (if (nil? t) nil (get t "value"))))) @@ -188,10 +190,7 @@ (if (hk-match? ty v) (hk-advance!) - (hk-err - (str "expected " ty (if (nil? v) "" (str " '" v "'"))))))) - - ;; ── Atoms ──────────────────────────────────────────────── + (hk-err (str "expected " ty (if (nil? v) "" (str " '" v "'"))))))) (define hk-parse-aexp (fn @@ -219,80 +218,49 @@ ((= (get t "type") "lparen") (hk-parse-parens)) ((= (get t "type") "lbracket") (hk-parse-list-lit)) (:else (hk-err "unexpected token in expression")))))) - - ;; Returns {:name N :len L} if the current token begins an - ;; infix operator (varsym / consym / reservedop ":" / backtick), - ;; else nil. `len` is the number of tokens the operator occupies. (define hk-section-op-info (fn () - (let ((t (hk-peek))) + (let + ((t (hk-peek))) (cond ((nil? t) nil) - ((= (get t "type") "varsym") - {:name (get t "value") :len 1}) - ((= (get t "type") "consym") - {:name (get t "value") :len 1}) - ((and - (= (get t "type") "reservedop") - (= (get t "value") ":")) - {:name ":" :len 1}) + ((= (get t "type") "varsym") {:len 1 :name (get t "value")}) + ((= (get t "type") "consym") {:len 1 :name (get t "value")}) + ((and (= (get t "type") "reservedop") (= (get t "value") ":")) + {:len 1 :name ":"}) ((= (get t "type") "backtick") - (let ((varid-t (hk-peek-at 1))) + (let + ((varid-t (hk-peek-at 1))) (cond - ((and - (not (nil? varid-t)) - (= (get varid-t "type") "varid")) - {:name (get varid-t "value") :len 3}) + ((and (not (nil? varid-t)) (= (get varid-t "type") "varid")) + {:len 3 :name (get varid-t "value")}) (:else nil)))) (:else nil))))) - - ;; ── Parens / tuple / unit / operator sections ─────────── - ;; Forms recognised inside parens: - ;; () → unit : (:con "()") - ;; (op) → operator reference : (:var OP) - ;; (op e) → right section : (:sect-right OP E) (op ≠ "-") - ;; (e) → plain parens : unwrapped E - ;; (e1, … , en) → tuple : (:tuple ITEMS) - ;; (e op) → left section : (:sect-left OP E) - ;; `-` is excluded from right sections because `-e` always means - ;; `negate e`; `(-)` is still a valid operator reference. (define hk-parse-parens (fn () (hk-expect! "lparen" nil) (cond - ((hk-match? "rparen" nil) - (do (hk-advance!) (list :con "()"))) + ((hk-match? "rparen" nil) (do (hk-advance!) (list :con "()"))) (:else - (let ((op-info (hk-section-op-info))) + (let + ((op-info (hk-section-op-info))) (cond - ;; Operator reference / right section - ((and - (not (nil? op-info)) - (let - ((after - (hk-peek-at (get op-info "len")))) - (or - (and - (not (nil? after)) - (= (get after "type") "rparen")) - (not (= (get op-info "name") "-"))))) + ((and (not (nil? op-info)) (let ((after (hk-peek-at (get op-info "len")))) (or (and (not (nil? after)) (= (get after "type") "rparen")) (not (= (get op-info "name") "-"))))) (let ((op-name (get op-info "name")) (op-len (get op-info "len")) - (after - (hk-peek-at (get op-info "len")))) + (after (hk-peek-at (get op-info "len")))) (hk-consume-op!) (cond - ((and - (not (nil? after)) - (= (get after "type") "rparen")) + ((and (not (nil? after)) (= (get after "type") "rparen")) (do (hk-advance!) (list :var op-name))) (:else - (let ((expr-e (hk-parse-expr-inner))) + (let + ((expr-e (hk-parse-expr-inner))) (hk-expect! "rparen" nil) (list :sect-right op-name expr-e)))))) (:else @@ -317,38 +285,18 @@ ((hk-match? "rparen" nil) (do (hk-advance!) - (if - is-tuple - (list :tuple items) - first-e))) + (if is-tuple (list :tuple items) first-e))) (:else (let ((op-info2 (hk-section-op-info))) (cond - ((and - (not (nil? op-info2)) - (not is-tuple) - (let - ((after2 - (hk-peek-at - (get op-info2 "len")))) - (and - (not (nil? after2)) - (= (get after2 "type") "rparen")))) + ((and (not (nil? op-info2)) (not is-tuple) (let ((after2 (hk-peek-at (get op-info2 "len")))) (and (not (nil? after2)) (= (get after2 "type") "rparen")))) (let ((op-name (get op-info2 "name"))) (hk-consume-op!) (hk-advance!) (list :sect-left op-name first-e))) - (:else - (hk-err - "expected ')' after expression")))))))))))))) - - ;; ── List comprehension qualifiers ────────────────────── - ;; (:list-comp E QUALS) where each qualifier is one of: - ;; (:q-gen PAT E) — `pat <- expr` - ;; (:q-guard E) — bare boolean expression - ;; (:q-let DECLS) — `let decls` + (:else (hk-err "expected ')' after expression")))))))))))))) (define hk-comp-qual-is-gen? (fn @@ -364,44 +312,27 @@ (let ((t (nth toks j)) (ty (get t "type"))) (cond - ((and - (= depth 0) - (or - (= ty "comma") - (= ty "rbracket"))) + ((and (= depth 0) (or (= ty "comma") (= ty "rbracket"))) (set! done true)) - ((and - (= depth 0) - (= ty "reservedop") - (= (get t "value") "<-")) + ((and (= depth 0) (= ty "reservedop") (= (get t "value") "<-")) (do (set! found true) (set! done true))) - ((or - (= ty "lparen") - (= ty "lbracket") - (= ty "lbrace") - (= ty "vlbrace")) + ((or (= ty "lparen") (= ty "lbracket") (= ty "lbrace") (= ty "vlbrace")) (set! depth (+ depth 1))) - ((or - (= ty "rparen") - (= ty "rbrace") - (= ty "vrbrace")) + ((or (= ty "rparen") (= ty "rbrace") (= ty "vrbrace")) (set! depth (- depth 1))) (:else nil)) (set! j (+ j 1)) (hk-qsc-loop))))) (hk-qsc-loop) found))) - (define hk-parse-comp-let (fn () (hk-expect! "reserved" "let") - (let ((explicit (hk-match? "lbrace" nil))) - (if - explicit - (hk-advance!) - (hk-expect! "vlbrace" nil)) + (let + ((explicit (hk-match? "lbrace" nil))) + (if explicit (hk-advance!) (hk-expect! "vlbrace" nil)) (let ((binds (list))) (when @@ -417,9 +348,7 @@ (fn () (when - (or - (hk-match? "semi" nil) - (hk-match? "vsemi" nil)) + (or (hk-match? "semi" nil) (hk-match? "vsemi" nil)) (do (hk-advance!) (when @@ -434,17 +363,10 @@ (cond (explicit (hk-expect! "rbrace" nil)) ((hk-match? "vrbrace" nil) (hk-advance!)) - ;; In a single-line comprehension, `]` or `,` - ;; terminates the qualifier before layout's implicit - ;; vrbrace arrives — leave them for the outer parser. - ((or - (hk-match? "rbracket" nil) - (hk-match? "comma" nil)) + ((or (hk-match? "rbracket" nil) (hk-match? "comma" nil)) nil) - (:else - (hk-err "expected end of let block in comprehension"))) + (:else (hk-err "expected end of let block in comprehension"))) (list :q-let binds))))) - (define hk-parse-qual (fn @@ -452,12 +374,11 @@ (cond ((hk-match? "reserved" "let") (hk-parse-comp-let)) ((hk-comp-qual-is-gen?) - (let ((pat (hk-parse-pat))) + (let + ((pat (hk-parse-pat))) (hk-expect! "reservedop" "<-") (list :q-gen pat (hk-parse-expr-inner)))) (:else (list :q-guard (hk-parse-expr-inner)))))) - - ;; ── List literal / range / comprehension ─────────────── (define hk-parse-list-lit (fn @@ -475,9 +396,7 @@ (hk-advance!) (cond ((hk-match? "rbracket" nil) - (do - (hk-advance!) - (list :range-from first-e))) + (do (hk-advance!) (list :range-from first-e))) (:else (let ((end-e (hk-parse-expr-inner))) @@ -486,7 +405,8 @@ ((hk-match? "reservedop" "|") (do (hk-advance!) - (let ((quals (list))) + (let + ((quals (list))) (append! quals (hk-parse-qual)) (define hk-lc-loop @@ -513,11 +433,7 @@ (let ((end-e (hk-parse-expr-inner))) (hk-expect! "rbracket" nil) - (list - :range-step - first-e - second-e - end-e)))) + (list :range-step first-e second-e end-e)))) (:else (let ((items (list))) @@ -531,9 +447,7 @@ (hk-match? "comma" nil) (do (hk-advance!) - (append! - items - (hk-parse-expr-inner)) + (append! items (hk-parse-expr-inner)) (hk-list-loop))))) (hk-list-loop) (hk-expect! "rbracket" nil) @@ -542,8 +456,6 @@ (do (hk-expect! "rbracket" nil) (list :list (list first-e)))))))))) - - ;; ── Application: left-assoc aexp chain ─────────────────── (define hk-parse-fexp (fn @@ -562,8 +474,6 @@ (hk-app-loop))))) (hk-app-loop) fn-e))) - - ;; ── Lambda: \ apat1 apat2 ... apatn -> body ────────────── (define hk-parse-lambda (fn @@ -580,14 +490,10 @@ () (when (hk-apat-start? (hk-peek)) - (do - (append! params (hk-parse-apat)) - (hk-lam-loop))))) + (do (append! params (hk-parse-apat)) (hk-lam-loop))))) (hk-lam-loop) (hk-expect! "reservedop" "->") (list :lambda params (hk-parse-expr-inner))))) - - ;; ── if-then-else ──────────────────────────────────────── (define hk-parse-if (fn @@ -599,21 +505,15 @@ (let ((th (hk-parse-expr-inner))) (hk-expect! "reserved" "else") - (let - ((el (hk-parse-expr-inner))) - (list :if c th el)))))) - - ;; ── Let expression ────────────────────────────────────── + (let ((el (hk-parse-expr-inner))) (list :if c th el)))))) (define hk-parse-let (fn () (hk-expect! "reserved" "let") - (let ((explicit (hk-match? "lbrace" nil))) - (if - explicit - (hk-advance!) - (hk-expect! "vlbrace" nil)) + (let + ((explicit (hk-match? "lbrace" nil))) + (if explicit (hk-advance!) (hk-expect! "vlbrace" nil)) (let ((binds (list))) (when @@ -629,9 +529,7 @@ (fn () (when - (or - (hk-match? "semi" nil) - (hk-match? "vsemi" nil)) + (or (hk-match? "semi" nil) (hk-match? "vsemi" nil)) (do (hk-advance!) (when @@ -649,25 +547,15 @@ (hk-expect! "vrbrace" nil)) (hk-expect! "reserved" "in") (list :let binds (hk-parse-expr-inner)))))) - - ;; ── RHS: guards + optional where ───────────────────────── - ;; A rhs is either a plain body after `=`/`->`, or a list of - ;; guarded bodies (`| cond = e | cond = e …`), optionally - ;; followed by a `where` block of local decls. Shapes: - ;; plain: - ;; guards: (:guarded ((:guard C1 E1) (:guard C2 E2) …)) - ;; where: (:where DECLS) - ;; Used by fun-clauses, let/do-let bindings, and case alts. (define hk-parse-where-decls (fn () - (let ((explicit (hk-match? "lbrace" nil))) - (if - explicit - (hk-advance!) - (hk-expect! "vlbrace" nil)) - (let ((decls (list))) + (let + ((explicit (hk-match? "lbrace" nil))) + (if explicit (hk-advance!) (hk-expect! "vlbrace" nil)) + (let + ((decls (list))) (when (not (if @@ -681,9 +569,7 @@ (fn () (when - (or - (hk-match? "vsemi" nil) - (hk-match? "semi" nil)) + (or (hk-match? "vsemi" nil) (hk-match? "semi" nil)) (do (hk-advance!) (when @@ -700,12 +586,12 @@ (hk-expect! "rbrace" nil) (hk-expect! "vrbrace" nil)) decls)))) - (define hk-parse-guarded (fn (sep) - (let ((guards (list))) + (let + ((guards (list))) (define hk-g-loop (fn @@ -723,30 +609,16 @@ (hk-g-loop))))))) (hk-g-loop) (list :guarded guards)))) - (define hk-parse-rhs (fn (sep) (let - ((body - (cond - ((hk-match? "reservedop" "|") - (hk-parse-guarded sep)) - (:else - (do - (hk-expect! "reservedop" sep) - (hk-parse-expr-inner)))))) + ((body (cond ((hk-match? "reservedop" "|") (hk-parse-guarded sep)) (:else (do (hk-expect! "reservedop" sep) (hk-parse-expr-inner)))))) (cond ((hk-match? "reserved" "where") - (do - (hk-advance!) - (list :where body (hk-parse-where-decls)))) + (do (hk-advance!) (list :where body (hk-parse-where-decls)))) (:else body))))) - - ;; Binding LHS is a pattern (for pat-binds), a varid alone - ;; (simple `x = e`), or a varid followed by apats (the - ;; `let f x = …` / `let f x | g = … | g = …` funclause form). (define hk-parse-bind (fn @@ -754,39 +626,25 @@ (let ((t (hk-peek))) (cond - ((and - (not (nil? t)) - (= (get t "type") "varid")) + ((and (not (nil? t)) (= (get t "type") "varid")) (let - ((name (get (hk-advance!) "value")) - (pats (list))) + ((name (get (hk-advance!) "value")) (pats (list))) (define hk-b-loop (fn () (when (hk-apat-start? (hk-peek)) - (do - (append! pats (hk-parse-apat)) - (hk-b-loop))))) + (do (append! pats (hk-parse-apat)) (hk-b-loop))))) (hk-b-loop) (if (= (len pats) 0) - (list - :bind - (list :p-var name) - (hk-parse-rhs "=")) - (list - :fun-clause - name - pats - (hk-parse-rhs "="))))) + (list :bind (list :p-var name) (hk-parse-rhs "=")) + (list :fun-clause name pats (hk-parse-rhs "="))))) (:else (let ((pat (hk-parse-pat))) (list :bind pat (hk-parse-rhs "=")))))))) - - ;; ── Patterns ───────────────────────────────────────────── (define hk-parse-apat (fn @@ -795,17 +653,11 @@ ((t (hk-peek))) (cond ((nil? t) (hk-err "unexpected end of input in pattern")) - ((and - (= (get t "type") "reserved") - (= (get t "value") "_")) + ((and (= (get t "type") "reserved") (= (get t "value") "_")) (do (hk-advance!) (list :p-wild))) - ((and - (= (get t "type") "reservedop") - (= (get t "value") "~")) + ((and (= (get t "type") "reservedop") (= (get t "value") "~")) (do (hk-advance!) (list :p-lazy (hk-parse-apat)))) - ((and - (= (get t "type") "varsym") - (= (get t "value") "-")) + ((and (= (get t "type") "varsym") (= (get t "value") "-")) (do (hk-advance!) (let @@ -836,10 +688,7 @@ (let ((next-t (hk-peek-at 1))) (cond - ((and - (not (nil? next-t)) - (= (get next-t "type") "reservedop") - (= (get next-t "value") "@")) + ((and (not (nil? next-t)) (= (get next-t "type") "reservedop") (= (get next-t "value") "@")) (do (hk-advance!) (hk-advance!) @@ -847,17 +696,12 @@ (:else (do (hk-advance!) (list :p-var (get t "value"))))))) ((= (get t "type") "conid") - (do - (hk-advance!) - (list :p-con (get t "value") (list)))) + (do (hk-advance!) (list :p-con (get t "value") (list)))) ((= (get t "type") "qconid") - (do - (hk-advance!) - (list :p-con (get t "value") (list)))) + (do (hk-advance!) (list :p-con (get t "value") (list)))) ((= (get t "type") "lparen") (hk-parse-paren-pat)) ((= (get t "type") "lbracket") (hk-parse-list-pat)) (:else (hk-err "unexpected token in pattern")))))) - (define hk-parse-paren-pat (fn @@ -868,9 +712,7 @@ (do (hk-advance!) (list :p-con "()" (list)))) (:else (let - ((first-p (hk-parse-pat)) - (items (list)) - (is-tup false)) + ((first-p (hk-parse-pat)) (items (list)) (is-tup false)) (append! items first-p) (define hk-ppt-loop @@ -886,7 +728,6 @@ (hk-ppt-loop) (hk-expect! "rparen" nil) (if is-tup (list :p-tuple items) first-p)))))) - (define hk-parse-list-pat (fn @@ -912,7 +753,6 @@ (hk-plt-loop) (hk-expect! "rbracket" nil) (list :p-list items)))))) - (define hk-parse-pat-lhs (fn @@ -920,11 +760,7 @@ (let ((t (hk-peek))) (cond - ((and - (not (nil? t)) - (or - (= (get t "type") "conid") - (= (get t "type") "qconid"))) + ((and (not (nil? t)) (or (= (get t "type") "conid") (= (get t "type") "qconid"))) (let ((name (get (hk-advance!) "value")) (args (list))) (define @@ -933,15 +769,10 @@ () (when (hk-apat-start? (hk-peek)) - (do - (append! args (hk-parse-apat)) - (hk-pca-loop))))) + (do (append! args (hk-parse-apat)) (hk-pca-loop))))) (hk-pca-loop) (list :p-con name args))) (:else (hk-parse-apat)))))) - - ;; Infix constructor patterns: `x : xs`, `a `Cons` b`, etc. - ;; Right-associative, single precedence band. (define hk-parse-pat (fn @@ -949,27 +780,18 @@ (let ((left (hk-parse-pat-lhs))) (cond - ((or - (= (hk-peek-type) "consym") - (and - (= (hk-peek-type) "reservedop") - (= (hk-peek-value) ":"))) + ((or (= (hk-peek-type) "consym") (and (= (hk-peek-type) "reservedop") (= (hk-peek-value) ":"))) (let ((op (get (hk-advance!) "value"))) (let ((right (hk-parse-pat))) (list :p-con op (list left right))))) (:else left))))) - - ;; ── case ─ of { pat -> expr ; ... } ───────────────────── (define hk-parse-alt (fn () - (let - ((pat (hk-parse-pat))) - (list :alt pat (hk-parse-rhs "->"))))) - + (let ((pat (hk-parse-pat))) (list :alt pat (hk-parse-rhs "->"))))) (define hk-parse-case (fn @@ -980,10 +802,7 @@ (hk-expect! "reserved" "of") (let ((explicit (hk-match? "lbrace" nil))) - (if - explicit - (hk-advance!) - (hk-expect! "vlbrace" nil)) + (if explicit (hk-advance!) (hk-expect! "vlbrace" nil)) (let ((alts (list))) (when @@ -999,9 +818,7 @@ (fn () (when - (or - (hk-match? "semi" nil) - (hk-match? "vsemi" nil)) + (or (hk-match? "semi" nil) (hk-match? "vsemi" nil)) (do (hk-advance!) (when @@ -1018,11 +835,6 @@ (hk-expect! "rbrace" nil) (hk-expect! "vrbrace" nil)) (list :case scrut alts)))))) - - ;; ── do { stmt ; stmt ; ... } ──────────────────────────── - ;; Scan ahead (respecting paren/bracket/brace depth) for a `<-` - ;; before the next `;` / `}` — distinguishes `pat <- e` from a - ;; bare expression statement. (define hk-do-stmt-is-bind? (fn @@ -1039,45 +851,27 @@ ((t (nth toks j)) (ty nil)) (set! ty (get t "type")) (cond - ((and - (= depth 0) - (or - (= ty "semi") - (= ty "vsemi") - (= ty "rbrace") - (= ty "vrbrace"))) + ((and (= depth 0) (or (= ty "semi") (= ty "vsemi") (= ty "rbrace") (= ty "vrbrace"))) (set! done true)) - ((and - (= depth 0) - (= ty "reservedop") - (= (get t "value") "<-")) + ((and (= depth 0) (= ty "reservedop") (= (get t "value") "<-")) (do (set! found true) (set! done true))) - ((or - (= ty "lparen") - (= ty "lbracket") - (= ty "lbrace") - (= ty "vlbrace")) + ((or (= ty "lparen") (= ty "lbracket") (= ty "lbrace") (= ty "vlbrace")) (set! depth (+ depth 1))) - ((or - (= ty "rparen") - (= ty "rbracket")) + ((or (= ty "rparen") (= ty "rbracket")) (set! depth (- depth 1))) (:else nil)) (set! j (+ j 1)) (hk-scan-loop))))) (hk-scan-loop) found))) - (define hk-parse-do-let (fn () (hk-expect! "reserved" "let") - (let ((explicit (hk-match? "lbrace" nil))) - (if - explicit - (hk-advance!) - (hk-expect! "vlbrace" nil)) + (let + ((explicit (hk-match? "lbrace" nil))) + (if explicit (hk-advance!) (hk-expect! "vlbrace" nil)) (let ((binds (list))) (when @@ -1093,9 +887,7 @@ (fn () (when - (or - (hk-match? "semi" nil) - (hk-match? "vsemi" nil)) + (or (hk-match? "semi" nil) (hk-match? "vsemi" nil)) (do (hk-advance!) (when @@ -1112,7 +904,6 @@ (hk-expect! "rbrace" nil) (hk-expect! "vrbrace" nil)) (list :do-let binds))))) - (define hk-parse-do-stmt (fn @@ -1125,7 +916,6 @@ (hk-expect! "reservedop" "<-") (list :do-bind pat (hk-parse-expr-inner)))) (:else (list :do-expr (hk-parse-expr-inner)))))) - (define hk-parse-do (fn @@ -1133,10 +923,7 @@ (hk-expect! "reserved" "do") (let ((explicit (hk-match? "lbrace" nil))) - (if - explicit - (hk-advance!) - (hk-expect! "vlbrace" nil)) + (if explicit (hk-advance!) (hk-expect! "vlbrace" nil)) (let ((stmts (list))) (when @@ -1152,9 +939,7 @@ (fn () (when - (or - (hk-match? "semi" nil) - (hk-match? "vsemi" nil)) + (or (hk-match? "semi" nil) (hk-match? "vsemi" nil)) (do (hk-advance!) (when @@ -1171,8 +956,6 @@ (hk-expect! "rbrace" nil) (hk-expect! "vrbrace" nil)) (list :do stmts))))) - - ;; ── lexp: lambda | if | let | case | do | fexp ────────── (define hk-parse-lexp (fn @@ -1184,8 +967,6 @@ ((hk-match? "reserved" "case") (hk-parse-case)) ((hk-match? "reserved" "do") (hk-parse-do)) (:else (hk-parse-fexp))))) - - ;; ── Prefix: unary - ───────────────────────────────────── (define hk-parse-prefix (fn @@ -1194,8 +975,6 @@ ((and (hk-match? "varsym" "-")) (do (hk-advance!) (list :neg (hk-parse-lexp)))) (:else (hk-parse-lexp))))) - - ;; ── Infix: precedence climbing ────────────────────────── (define hk-is-infix-op? (fn @@ -1210,7 +989,6 @@ (= (get tok "type") "reservedop") (= (get tok "value") ":")) (= (get tok "type") "backtick"))))) - (define hk-consume-op! (fn @@ -1226,7 +1004,6 @@ (hk-expect! "backtick" nil) (get v "value")))) (:else (do (hk-advance!) (get t "value"))))))) - (define hk-parse-infix (fn @@ -1242,11 +1019,7 @@ (let ((op-tok (hk-peek))) (let - ((op-len - (if - (= (get op-tok "type") "backtick") - 3 - 1)) + ((op-len (if (= (get op-tok "type") "backtick") 3 1)) (op-name (if (= (get op-tok "type") "backtick") @@ -1256,38 +1029,21 @@ ((after-op (hk-peek-at op-len)) (info (hk-op-info op-name))) (cond - ;; Bail on `op )` — let the paren parser claim - ;; it as a left section (e op). - ((and - (not (nil? after-op)) - (= (get after-op "type") "rparen")) + ((and (not (nil? after-op)) (= (get after-op "type") "rparen")) nil) ((>= (get info "prec") min-prec) (do (hk-consume-op!) (let - ((next-min - (cond - ((= (get info "assoc") "left") - (+ (get info "prec") 1)) - ((= (get info "assoc") "right") - (get info "prec")) - (:else (+ (get info "prec") 1))))) + ((next-min (cond ((= (get info "assoc") "left") (+ (get info "prec") 1)) ((= (get info "assoc") "right") (get info "prec")) (:else (+ (get info "prec") 1))))) (let ((right (hk-parse-infix next-min))) - (set! - left - (list :op op-name left right)) + (set! left (list :op op-name left right)) (hk-inf-loop))))) (:else nil)))))))) (hk-inf-loop) left))) - (define hk-parse-expr-inner (fn () (hk-parse-infix 0))) - - ;; ── Types ──────────────────────────────────────────────── - ;; AST: (:t-var N) | (:t-con N) | (:t-app F A) - ;; (:t-fun A B) | (:t-tuple ITEMS) | (:t-list T) (define hk-parse-paren-type (fn @@ -1298,9 +1054,7 @@ (do (hk-advance!) (list :t-con "()"))) (:else (let - ((first-t (hk-parse-type)) - (items (list)) - (is-tup false)) + ((first-t (hk-parse-type)) (items (list)) (is-tup false)) (append! items first-t) (define hk-pt-loop @@ -1316,7 +1070,6 @@ (hk-pt-loop) (hk-expect! "rparen" nil) (if is-tup (list :t-tuple items) first-t)))))) - (define hk-parse-list-type (fn @@ -1330,7 +1083,6 @@ ((inner (hk-parse-type))) (hk-expect! "rbracket" nil) (list :t-list inner)))))) - (define hk-parse-atype (fn @@ -1348,7 +1100,6 @@ ((= (get t "type") "lparen") (hk-parse-paren-type)) ((= (get t "type") "lbracket") (hk-parse-list-type)) (:else (hk-err "unexpected token in type")))))) - (define hk-parse-btype (fn @@ -1366,7 +1117,6 @@ (hk-bt-loop))))) (hk-bt-loop) head))) - (define hk-parse-type (fn @@ -1377,22 +1127,6 @@ ((hk-match? "reservedop" "->") (do (hk-advance!) (list :t-fun left (hk-parse-type)))) (:else left))))) - - ;; ── Top-level declarations ────────────────────────────── - ;; AST: - ;; (:fun-clause NAME APATS BODY) - ;; (:pat-bind PAT BODY) - ;; (:type-sig NAMES TYPE) - ;; (:data NAME TVARS CONS) — CONS is list of :con-def - ;; (:con-def CNAME FIELDS) — FIELDS is list of types - ;; (:type-syn NAME TVARS TYPE) - ;; (:newtype NAME TVARS CNAME FIELD) - ;; (:fixity ASSOC PREC OPS) — ASSOC ∈ "l" | "r" | "n" - ;; (:program DECLS) - - ;; Scan ahead for a top-level `::` (respecting paren/bracket - ;; depth) before the next statement terminator. Used to tell a - ;; type signature apart from a function clause. (define hk-has-top-dcolon? (fn @@ -1408,35 +1142,19 @@ (let ((t (nth toks j)) (ty (get t "type"))) (cond - ((and - (= depth 0) - (or - (= ty "vsemi") - (= ty "semi") - (= ty "rbrace") - (= ty "vrbrace"))) + ((and (= depth 0) (or (= ty "vsemi") (= ty "semi") (= ty "rbrace") (= ty "vrbrace"))) (set! done true)) - ((and - (= depth 0) - (= ty "reservedop") - (= (get t "value") "::")) + ((and (= depth 0) (= ty "reservedop") (= (get t "value") "::")) (do (set! found true) (set! done true))) - ((or - (= ty "lparen") - (= ty "lbracket") - (= ty "lbrace") - (= ty "vlbrace")) + ((or (= ty "lparen") (= ty "lbracket") (= ty "lbrace") (= ty "vlbrace")) (set! depth (+ depth 1))) - ((or - (= ty "rparen") - (= ty "rbracket")) + ((or (= ty "rparen") (= ty "rbracket")) (set! depth (- depth 1))) (:else nil)) (set! j (+ j 1)) (hk-dcol-loop))))) (hk-dcol-loop) found))) - (define hk-parse-type-sig (fn @@ -1463,7 +1181,6 @@ (hk-sig-loop) (hk-expect! "reservedop" "::") (list :type-sig names (hk-parse-type))))) - (define hk-parse-fun-clause (fn @@ -1471,28 +1188,22 @@ (let ((t (hk-peek))) (cond - ((and - (not (nil? t)) - (= (get t "type") "varid")) + ((and (not (nil? t)) (= (get t "type") "varid")) (let - ((name (get (hk-advance!) "value")) - (pats (list))) + ((name (get (hk-advance!) "value")) (pats (list))) (define hk-fc-loop (fn () (when (hk-apat-start? (hk-peek)) - (do - (append! pats (hk-parse-apat)) - (hk-fc-loop))))) + (do (append! pats (hk-parse-apat)) (hk-fc-loop))))) (hk-fc-loop) (list :fun-clause name pats (hk-parse-rhs "=")))) (:else (let ((pat (hk-parse-pat))) (list :pat-bind pat (hk-parse-rhs "=")))))))) - (define hk-parse-con-def (fn @@ -1508,17 +1219,15 @@ () (when (hk-atype-start? (hk-peek)) - (do - (append! fields (hk-parse-atype)) - (hk-cd-loop))))) + (do (append! fields (hk-parse-atype)) (hk-cd-loop))))) (hk-cd-loop) (list :con-def name fields)))) - (define hk-parse-tvars (fn () - (let ((vs (list))) + (let + ((vs (list))) (define hk-tv-loop (fn @@ -1530,7 +1239,6 @@ (hk-tv-loop))))) (hk-tv-loop) vs))) - (define hk-parse-data (fn @@ -1560,7 +1268,28 @@ (hk-dc-loop))))) (hk-dc-loop))) (list :data name tvars cons-list)))) - + (define + hk-parse-class + (fn + () + (hk-next) + (let + ((cls (get (hk-next) "value"))) + (let + ((tvar (get (hk-next) "value"))) + (hk-expect! "reserved" "where") + (list "class-decl" cls tvar (hk-parse-where-decls)))))) + (define + hk-parse-instance + (fn + () + (hk-next) + (let + ((cls (get (hk-next) "value"))) + (let + ((inst-type (hk-parse-atype))) + (hk-expect! "reserved" "where") + (list "instance-decl" cls inst-type (hk-parse-where-decls)))))) (define hk-parse-type-syn (fn @@ -1570,11 +1299,9 @@ (not (hk-match? "conid" nil)) (hk-err "type synonym needs a name")) (let - ((name (get (hk-advance!) "value")) - (tvars (hk-parse-tvars))) + ((name (get (hk-advance!) "value")) (tvars (hk-parse-tvars))) (hk-expect! "reservedop" "=") (list :type-syn name tvars (hk-parse-type))))) - (define hk-parse-newtype (fn @@ -1584,8 +1311,7 @@ (not (hk-match? "conid" nil)) (hk-err "newtype needs a type name")) (let - ((name (get (hk-advance!) "value")) - (tvars (hk-parse-tvars))) + ((name (get (hk-advance!) "value")) (tvars (hk-parse-tvars))) (hk-expect! "reservedop" "=") (when (not (hk-match? "conid" nil)) @@ -1596,19 +1322,14 @@ (not (hk-atype-start? (hk-peek))) (hk-err "newtype constructor needs one field")) (list :newtype name tvars cname (hk-parse-atype)))))) - (define hk-parse-op (fn () (cond - ((hk-match? "varsym" nil) - (get (hk-advance!) "value")) - ((hk-match? "consym" nil) - (get (hk-advance!) "value")) - ((and - (hk-match? "reservedop" nil) - (= (hk-peek-value) ":")) + ((hk-match? "varsym" nil) (get (hk-advance!) "value")) + ((hk-match? "consym" nil) (get (hk-advance!) "value")) + ((and (hk-match? "reservedop" nil) (= (hk-peek-value) ":")) (do (hk-advance!) ":")) ((hk-match? "backtick" nil) (do @@ -1618,23 +1339,25 @@ (hk-expect! "backtick" nil) (get v "value")))) (:else (hk-err "expected operator name in fixity decl"))))) - (define hk-parse-fixity (fn () - (let ((assoc "n")) + (let + ((assoc "n")) (cond ((hk-match? "reserved" "infixl") (set! assoc "l")) ((hk-match? "reserved" "infixr") (set! assoc "r")) ((hk-match? "reserved" "infix") (set! assoc "n")) (:else (hk-err "expected fixity keyword"))) (hk-advance!) - (let ((prec 9)) + (let + ((prec 9)) (when (hk-match? "integer" nil) (set! prec (get (hk-advance!) "value"))) - (let ((ops (list))) + (let + ((ops (list))) (append! ops (hk-parse-op)) (define hk-fx-loop @@ -1648,7 +1371,6 @@ (hk-fx-loop))))) (hk-fx-loop) (list :fixity assoc prec ops)))))) - (define hk-parse-decl (fn @@ -1657,51 +1379,27 @@ ((hk-match? "reserved" "data") (hk-parse-data)) ((hk-match? "reserved" "type") (hk-parse-type-syn)) ((hk-match? "reserved" "newtype") (hk-parse-newtype)) - ((or - (hk-match? "reserved" "infix") - (hk-match? "reserved" "infixl") - (hk-match? "reserved" "infixr")) + ((or (hk-match? "reserved" "infix") (hk-match? "reserved" "infixl") (hk-match? "reserved" "infixr")) (hk-parse-fixity)) + ((hk-match? "reserved" "class") (hk-parse-class)) + ((hk-match? "reserved" "instance") (hk-parse-instance)) ((hk-has-top-dcolon?) (hk-parse-type-sig)) (:else (hk-parse-fun-clause))))) - - ;; ── Module header + imports ───────────────────────────── - ;; Import/export entity references: - ;; (:ent-var NAME) — bare var/type name (incl. (op) form) - ;; (:ent-all NAME) — Tycon(..) - ;; (:ent-with NAME MEMS) — Tycon(m1, m2, …) - ;; (:ent-module NAME) — module M (exports only) - ;; Member names inside Tycon(…) are bare strings. - (define hk-parse-ent-member (fn () (cond - ((hk-match? "varid" nil) - (get (hk-advance!) "value")) - ((hk-match? "conid" nil) - (get (hk-advance!) "value")) + ((hk-match? "varid" nil) (get (hk-advance!) "value")) + ((hk-match? "conid" nil) (get (hk-advance!) "value")) ((hk-match? "lparen" nil) (do (hk-advance!) (let - ((op-name - (cond - ((hk-match? "varsym" nil) - (get (hk-advance!) "value")) - ((hk-match? "consym" nil) - (get (hk-advance!) "value")) - ((and - (hk-match? "reservedop" nil) - (= (hk-peek-value) ":")) - (do (hk-advance!) ":")) - (:else - (hk-err "expected operator in member list"))))) + ((op-name (cond ((hk-match? "varsym" nil) (get (hk-advance!) "value")) ((hk-match? "consym" nil) (get (hk-advance!) "value")) ((and (hk-match? "reservedop" nil) (= (hk-peek-value) ":")) (do (hk-advance!) ":")) (:else (hk-err "expected operator in member list"))))) (hk-expect! "rparen" nil) op-name))) (:else (hk-err "expected identifier in member list"))))) - (define hk-parse-ent (fn @@ -1715,13 +1413,12 @@ (do (hk-advance!) (cond - ((or - (hk-match? "conid" nil) - (hk-match? "qconid" nil)) + ((or (hk-match? "conid" nil) (hk-match? "qconid" nil)) (list :ent-module (get (hk-advance!) "value"))) (:else (hk-err "expected module name in export"))))) ((or (hk-match? "conid" nil) (hk-match? "qconid" nil)) - (let ((name (get (hk-advance!) "value"))) + (let + ((name (get (hk-advance!) "value"))) (cond ((hk-match? "lparen" nil) (do @@ -1733,11 +1430,10 @@ (hk-expect! "rparen" nil) (list :ent-all name))) ((hk-match? "rparen" nil) - (do - (hk-advance!) - (list :ent-with name (list)))) + (do (hk-advance!) (list :ent-with name (list)))) (:else - (let ((mems (list))) + (let + ((mems (list))) (append! mems (hk-parse-ent-member)) (define hk-mem-loop @@ -1749,9 +1445,7 @@ (hk-advance!) (when (not (hk-match? "rparen" nil)) - (append! - mems - (hk-parse-ent-member))) + (append! mems (hk-parse-ent-member))) (hk-mem-loop))))) (hk-mem-loop) (hk-expect! "rparen" nil) @@ -1761,32 +1455,20 @@ (do (hk-advance!) (let - ((op-name - (cond - ((hk-match? "varsym" nil) - (get (hk-advance!) "value")) - ((hk-match? "consym" nil) - (get (hk-advance!) "value")) - ((and - (hk-match? "reservedop" nil) - (= (hk-peek-value) ":")) - (do (hk-advance!) ":")) - (:else - (hk-err "expected operator in parens"))))) + ((op-name (cond ((hk-match? "varsym" nil) (get (hk-advance!) "value")) ((hk-match? "consym" nil) (get (hk-advance!) "value")) ((and (hk-match? "reservedop" nil) (= (hk-peek-value) ":")) (do (hk-advance!) ":")) (:else (hk-err "expected operator in parens"))))) (hk-expect! "rparen" nil) (list :ent-var op-name)))) (:else (hk-err "expected entity in import/export list"))))) - (define hk-parse-ent-list (fn (allow-module?) (hk-expect! "lparen" nil) (cond - ((hk-match? "rparen" nil) - (do (hk-advance!) (list))) + ((hk-match? "rparen" nil) (do (hk-advance!) (list))) (:else - (let ((items (list))) + (let + ((items (list))) (append! items (hk-parse-ent allow-module?)) (define hk-el-loop @@ -1798,36 +1480,23 @@ (hk-advance!) (when (not (hk-match? "rparen" nil)) - (append! - items - (hk-parse-ent allow-module?))) + (append! items (hk-parse-ent allow-module?))) (hk-el-loop))))) (hk-el-loop) (hk-expect! "rparen" nil) items))))) - - ;; (:import QUALIFIED NAME AS SPEC) - ;; QUALIFIED: bool - ;; NAME : module name string (may contain dots) - ;; AS : alias module name string or nil - ;; SPEC : nil | (:spec-items ENTS) | (:spec-hiding ENTS) (define hk-parse-import (fn () (hk-expect! "reserved" "import") (let - ((qualified false) - (modname nil) - (as-name nil) - (spec nil)) + ((qualified false) (modname nil) (as-name nil) (spec nil)) (when (hk-match? "varid" "qualified") (do (hk-advance!) (set! qualified true))) (cond - ((or - (hk-match? "conid" nil) - (hk-match? "qconid" nil)) + ((or (hk-match? "conid" nil) (hk-match? "qconid" nil)) (set! modname (get (hk-advance!) "value"))) (:else (hk-err "expected module name in import"))) (when @@ -1835,39 +1504,26 @@ (do (hk-advance!) (cond - ((or - (hk-match? "conid" nil) - (hk-match? "qconid" nil)) + ((or (hk-match? "conid" nil) (hk-match? "qconid" nil)) (set! as-name (get (hk-advance!) "value"))) (:else (hk-err "expected name after 'as'"))))) (cond ((hk-match? "varid" "hiding") (do (hk-advance!) - (set! - spec - (list :spec-hiding (hk-parse-ent-list false))))) + (set! spec (list :spec-hiding (hk-parse-ent-list false))))) ((hk-match? "lparen" nil) - (set! - spec - (list :spec-items (hk-parse-ent-list false))))) + (set! spec (list :spec-items (hk-parse-ent-list false))))) (list :import qualified modname as-name spec)))) - - ;; (:module NAME EXPORTS IMPORTS DECLS) - ;; NAME : module name string or nil (no header) - ;; EXPORTS : list of ent-refs, or nil (no export list) - ;; IMPORTS : list of :import records - ;; DECLS : list of top-level decls (define hk-parse-module-header (fn () (hk-expect! "reserved" "module") - (let ((modname nil) (exports nil)) + (let + ((modname nil) (exports nil)) (cond - ((or - (hk-match? "conid" nil) - (hk-match? "qconid" nil)) + ((or (hk-match? "conid" nil) (hk-match? "qconid" nil)) (set! modname (get (hk-advance!) "value"))) (:else (hk-err "expected module name"))) (when @@ -1875,12 +1531,12 @@ (set! exports (hk-parse-ent-list true))) (hk-expect! "reserved" "where") (list modname exports)))) - (define hk-collect-module-body (fn () - (let ((imports (list)) (decls (list))) + (let + ((imports (list)) (decls (list))) (define hk-imp-loop (fn @@ -1890,9 +1546,7 @@ (do (append! imports (hk-parse-import)) (when - (or - (hk-match? "vsemi" nil) - (hk-match? "semi" nil)) + (or (hk-match? "vsemi" nil) (hk-match? "semi" nil)) (do (hk-advance!) (hk-imp-loop))))))) (hk-imp-loop) (define @@ -1913,9 +1567,7 @@ (fn () (when - (or - (hk-match? "vsemi" nil) - (hk-match? "semi" nil)) + (or (hk-match? "vsemi" nil) (hk-match? "semi" nil)) (do (hk-advance!) (when @@ -1924,60 +1576,44 @@ (hk-body-loop))))) (hk-body-loop))) (list imports decls)))) - (define hk-parse-program (fn () (cond ((hk-match? "reserved" "module") - (let ((header (hk-parse-module-header))) - (let ((explicit (hk-match? "lbrace" nil))) - (if - explicit - (hk-advance!) - (hk-expect! "vlbrace" nil)) - (let ((body (hk-collect-module-body))) + (let + ((header (hk-parse-module-header))) + (let + ((explicit (hk-match? "lbrace" nil))) + (if explicit (hk-advance!) (hk-expect! "vlbrace" nil)) + (let + ((body (hk-collect-module-body))) (if explicit (hk-expect! "rbrace" nil) (hk-expect! "vrbrace" nil)) (list - :module - (nth header 0) + :module (nth header 0) (nth header 1) (nth body 0) (nth body 1)))))) (:else - (let ((body (hk-collect-module-body))) + (let + ((body (hk-collect-module-body))) (if (empty? (nth body 0)) (list :program (nth body 1)) - (list - :module - nil - nil - (nth body 0) - (nth body 1)))))))) - - ;; ── Top-level: strip leading/trailing module-level braces ─ + (list :module nil nil (nth body 0) (nth body 1)))))))) (let - ((start-brace - (or - (hk-match? "vlbrace" nil) - (hk-match? "lbrace" nil)))) + ((start-brace (or (hk-match? "vlbrace" nil) (hk-match? "lbrace" nil)))) (when start-brace (hk-advance!)) (let - ((result - (cond - ((= mode :expr) (hk-parse-expr-inner)) - ((= mode :module) (hk-parse-program)) - (:else (hk-err "unknown parser mode"))))) - (when start-brace + ((result (cond ((= mode :expr) (hk-parse-expr-inner)) ((= mode :module) (hk-parse-program)) (:else (hk-err "unknown parser mode"))))) + (when + start-brace (when - (or - (hk-match? "vrbrace" nil) - (hk-match? "rbrace" nil)) + (or (hk-match? "vrbrace" nil) (hk-match? "rbrace" nil)) (hk-advance!))) result))))) diff --git a/lib/haskell/tests/class.sx b/lib/haskell/tests/class.sx new file mode 100644 index 00000000..b225ee21 --- /dev/null +++ b/lib/haskell/tests/class.sx @@ -0,0 +1,35 @@ +;; class.sx — tests for class/instance parsing and evaluation. + +(define prog-class1 (hk-core "class MyEq a where\n myEq :: a -> a -> Bool")) +(define prog-inst1 (hk-core "instance MyEq Int where\n myEq x y = x == y")) + +;; ─── class-decl AST ─────────────────────────────────────────────────────────── +(define cd1 (first (nth prog-class1 1))) +(hk-test "class-decl tag" (first cd1) "class-decl") +(hk-test "class-decl name" (nth cd1 1) "MyEq") +(hk-test "class-decl tvar" (nth cd1 2) "a") +(hk-test "class-decl methods" (len (nth cd1 3)) 1) + +;; ─── instance-decl AST ──────────────────────────────────────────────────────── +(define id1 (first (nth prog-inst1 1))) +(hk-test "instance-decl tag" (first id1) "instance-decl") +(hk-test "instance-decl class" (nth id1 1) "MyEq") +(hk-test "instance-decl type tag" (first (nth id1 2)) "t-con") +(hk-test "instance-decl type name" (nth (nth id1 2) 1) "Int") +(hk-test "instance-decl method count" (len (nth id1 3)) 1) + +;; ─── eval: instance dict is built ──────────────────────────────────────────── +(define + prog-full + (hk-core + "class MyEq a where\n myEq :: a -> a -> Bool\ninstance MyEq Int where\n myEq x y = x == y")) +(define env-full (hk-eval-program prog-full)) + +(hk-test "instance dict in env" (has-key? env-full "dictMyEq_Int") true) + +(hk-test + "instance dict has method" + (has-key? (get env-full "dictMyEq_Int") "myEq") + true) + +{:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail} \ No newline at end of file diff --git a/plans/haskell-on-sx.md b/plans/haskell-on-sx.md index aabb4948..3ec8b440 100644 --- a/plans/haskell-on-sx.md +++ b/plans/haskell-on-sx.md @@ -99,7 +99,7 @@ Key mappings: - [x] Unit tests: inference for 50+ expressions ### Phase 5 — typeclasses (dictionary passing) -- [ ] `class` / `instance` declarations +- [x] `class` / `instance` declarations - [ ] Dictionary-passing elaborator: inserts dict args at call sites - [ ] Standard classes: `Eq`, `Ord`, `Show`, `Num`, `Functor`, `Monad`, `Applicative` - [ ] `deriving (Eq, Show)` for ADTs @@ -114,6 +114,17 @@ Key mappings: _Newest first._ +- **2026-05-06** — Phase 5 class/instance declarations. Parser: `hk-parse-class` + and `hk-parse-instance` added to the parser closure; `hk-parse-decl` gains + arms for `"class"` and `"instance"` reserved words (tokenizer already marks + them reserved). `class Eq a where { ... }` → `("class-decl" name tvar decls)`; + `instance Eq Int where { ... }` → `("instance-decl" name inst-type decls)`. + Eval: `hk-type-ast-str` converts type AST to a string key. `hk-bind-decls!` + gains arms for `class-decl` (registers `__class__Name` marker) and + `instance-decl` (builds method dict, binds as `dictClassName_TypeStr` in env). + 11 new tests in `tests/class.sx` covering AST shapes + runtime dict + construction. 503/503 green. + - **2026-05-05** — Phase 4 inference unit tests (50+ expressions). Added 16 new `hk-t` expression tests to `tests/infer.sx`: nested application (`not(not True)`, `negate(negate 1)`), bool/mixed lambdas (`\\x->\\y->x&&y`, `\\x->x==1`),