From c02ffcf3163df8575e11e34473a32e95dd41f5e5 Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 1 May 2026 21:41:11 +0000 Subject: [PATCH] phase 22 Haskell: runtime.sx + 143 tests lib/haskell/runtime.sx (113 forms): numeric type class helpers (hk-div/mod/rem/quot floor semantics), rational numbers (dict-based, GCD-normalised), hk-force for lazy promises, Data.Char (hk-ord/chr, inline ASCII predicates, digit-to-int), Data.Set wrappers, Data.List (take/drop/zip/nub/foldl/foldr/scanl/etc), Maybe/Either ADTs, tuple helpers (hk-pair/fst/snd/curry/uncurry), string helpers (words/lines/ is-prefix-of/is-infix-of/etc), hk-show. test.sh updated to pre-load runtime.sx alongside tokenizer.sx. 143/143 runtime tests + 5/5 parse tests = 148/148 total. --- lib/haskell/runtime.sx | 507 +++++++++++++++++++++++++++++++++++ lib/haskell/test.sh | 2 + lib/haskell/tests/runtime.sx | 451 +++++++++++++++++++++++++++++++ 3 files changed, 960 insertions(+) create mode 100644 lib/haskell/runtime.sx create mode 100644 lib/haskell/tests/runtime.sx diff --git a/lib/haskell/runtime.sx b/lib/haskell/runtime.sx new file mode 100644 index 00000000..0d4aca8e --- /dev/null +++ b/lib/haskell/runtime.sx @@ -0,0 +1,507 @@ +;; lib/haskell/runtime.sx — Haskell-on-SX runtime layer +;; +;; Covers the Haskell primitives now reachable via SX spec: +;; 1. Numeric type class helpers (Num / Integral / Fractional) +;; 2. Rational numbers (dict-based: {:_rational true :num n :den d}) +;; 3. Lazy evaluation — hk-force for promises created by delay +;; 4. Char utilities (Data.Char) +;; 5. Data.Set wrappers +;; 6. Data.List utilities +;; 7. Maybe / Either ADTs +;; 8. Tuples (lists, since list->vector unreliable in sx_server) +;; 9. String helpers (words/lines/isPrefixOf/etc.) +;; 10. Show helper + +;; =========================================================================== +;; 1. Numeric type class helpers +;; =========================================================================== + +(define hk-is-integer? integer?) +(define hk-is-float? float?) +(define hk-is-num? number?) + +;; fromIntegral — coerce integer to Float +(define (hk-to-float x) (exact->inexact x)) + +;; truncate / round toward zero +(define hk-to-integer truncate) +(define hk-from-integer (fn (n) n)) + +;; Haskell div: floor division (rounds toward -inf) +(define + (hk-div a b) + (let + ((q (quotient a b)) (r (remainder a b))) + (if + (and + (not (= r 0)) + (or + (and (< a 0) (> b 0)) + (and (> a 0) (< b 0)))) + (- q 1) + q))) + +;; Haskell mod: result has same sign as divisor +(define hk-mod modulo) + +;; Haskell rem: result has same sign as dividend +(define hk-rem remainder) + +;; Haskell quot: truncation division +(define hk-quot quotient) + +;; divMod and quotRem return pairs (lists) +(define (hk-div-mod a b) (list (hk-div a b) (hk-mod a b))) +(define (hk-quot-rem a b) (list (hk-quot a b) (hk-rem a b))) + +(define (hk-abs x) (if (< x 0) (- 0 x) x)) +(define + (hk-signum x) + (cond + ((> x 0) 1) + ((< x 0) -1) + (else 0))) + +(define hk-gcd gcd) +(define hk-lcm lcm) + +(define (hk-even? n) (= (modulo n 2) 0)) +(define (hk-odd? n) (not (= (modulo n 2) 0))) + +;; =========================================================================== +;; 2. Rational numbers (dict implementation — no built-in rational in sx_server) +;; =========================================================================== + +(define + (hk-make-rational n d) + (let + ((g (gcd (hk-abs n) (hk-abs d)))) + (if (< d 0) {:num (quotient (- 0 n) g) :den (quotient (- 0 d) g) :_rational true} {:num (quotient n g) :den (quotient d g) :_rational true}))) + +(define + (hk-rational? x) + (and (dict? x) (not (= (get x :_rational) nil)))) +(define (hk-numerator r) (get r :num)) +(define (hk-denominator r) (get r :den)) + +(define + (hk-rational-add r1 r2) + (hk-make-rational + (+ + (* (hk-numerator r1) (hk-denominator r2)) + (* (hk-numerator r2) (hk-denominator r1))) + (* (hk-denominator r1) (hk-denominator r2)))) + +(define + (hk-rational-sub r1 r2) + (hk-make-rational + (- + (* (hk-numerator r1) (hk-denominator r2)) + (* (hk-numerator r2) (hk-denominator r1))) + (* (hk-denominator r1) (hk-denominator r2)))) + +(define + (hk-rational-mul r1 r2) + (hk-make-rational + (* (hk-numerator r1) (hk-numerator r2)) + (* (hk-denominator r1) (hk-denominator r2)))) + +(define + (hk-rational-div r1 r2) + (hk-make-rational + (* (hk-numerator r1) (hk-denominator r2)) + (* (hk-denominator r1) (hk-numerator r2)))) + +(define + (hk-rational-to-float r) + (exact->inexact (/ (hk-numerator r) (hk-denominator r)))) + +(define (hk-show-rational r) (str (hk-numerator r) "%" (hk-denominator r))) + +;; =========================================================================== +;; 3. Lazy evaluation — promises (created via SX delay) +;; =========================================================================== + +(define + (hk-force p) + (if + (and (dict? p) (not (= (get p :_promise) nil))) + (if (get p :forced) (get p :value) ((get p :thunk))) + p)) + +;; =========================================================================== +;; 4. Char utilities (Data.Char) +;; =========================================================================== + +(define hk-ord char->integer) +(define hk-chr integer->char) + +;; Inline ASCII predicates — char-alphabetic?/char-numeric? unreliable in sx_server +(define + (hk-is-alpha? c) + (let + ((n (char->integer c))) + (or + (and (>= n 65) (<= n 90)) + (and (>= n 97) (<= n 122))))) + +(define + (hk-is-digit? c) + (let ((n (char->integer c))) (and (>= n 48) (<= n 57)))) + +(define + (hk-is-alnum? c) + (let + ((n (char->integer c))) + (or + (and (>= n 48) (<= n 57)) + (and (>= n 65) (<= n 90)) + (and (>= n 97) (<= n 122))))) + +(define + (hk-is-upper? c) + (let ((n (char->integer c))) (and (>= n 65) (<= n 90)))) + +(define + (hk-is-lower? c) + (let ((n (char->integer c))) (and (>= n 97) (<= n 122)))) + +(define + (hk-is-space? c) + (let + ((n (char->integer c))) + (or + (= n 32) + (= n 9) + (= n 10) + (= n 13) + (= n 12) + (= n 11)))) + +(define hk-to-upper char-upcase) +(define hk-to-lower char-downcase) + +;; digitToInt: '0'-'9' → 0-9, 'a'-'f'/'A'-'F' → 10-15 +(define + (hk-digit-to-int c) + (let + ((n (char->integer c))) + (cond + ((and (>= n 48) (<= n 57)) (- n 48)) + ((and (>= n 65) (<= n 70)) (- n 55)) + ((and (>= n 97) (<= n 102)) (- n 87)) + (else (error (str "hk-digit-to-int: not a hex digit: " c)))))) + +;; intToDigit: 0-15 → char +(define + (hk-int-to-digit n) + (cond + ((and (>= n 0) (<= n 9)) + (integer->char (+ n 48))) + ((and (>= n 10) (<= n 15)) + (integer->char (+ n 87))) + (else (error (str "hk-int-to-digit: out of range: " n))))) + +;; =========================================================================== +;; 5. Data.Set wrappers +;; =========================================================================== + +(define (hk-set-empty) (make-set)) +(define hk-set? set?) +(define hk-set-member? set-member?) + +(define (hk-set-insert x s) (begin (set-add! s x) s)) + +(define (hk-set-delete x s) (begin (set-remove! s x) s)) + +(define hk-set-union set-union) +(define hk-set-intersection set-intersection) +(define hk-set-difference set-difference) +(define hk-set-from-list list->set) +(define hk-set-to-list set->list) +(define (hk-set-null? s) (= (len (set->list s)) 0)) +(define (hk-set-size s) (len (set->list s))) + +(define (hk-set-singleton x) (let ((s (make-set))) (set-add! s x) s)) + +;; =========================================================================== +;; 6. Data.List utilities +;; =========================================================================== + +(define hk-head first) +(define hk-tail rest) +(define (hk-null? lst) (= (len lst) 0)) +(define hk-length len) + +(define + (hk-take n lst) + (if + (or (= n 0) (= (len lst) 0)) + (list) + (cons (first lst) (hk-take (- n 1) (rest lst))))) + +(define + (hk-drop n lst) + (if + (or (= n 0) (= (len lst) 0)) + lst + (hk-drop (- n 1) (rest lst)))) + +(define + (hk-take-while pred lst) + (if + (or (= (len lst) 0) (not (pred (first lst)))) + (list) + (cons (first lst) (hk-take-while pred (rest lst))))) + +(define + (hk-drop-while pred lst) + (if + (or (= (len lst) 0) (not (pred (first lst)))) + lst + (hk-drop-while pred (rest lst)))) + +(define + (hk-zip a b) + (if + (or (= (len a) 0) (= (len b) 0)) + (list) + (cons (list (first a) (first b)) (hk-zip (rest a) (rest b))))) + +(define + (hk-zip-with f a b) + (if + (or (= (len a) 0) (= (len b) 0)) + (list) + (cons (f (first a) (first b)) (hk-zip-with f (rest a) (rest b))))) + +(define + (hk-unzip pairs) + (list + (map (fn (p) (first p)) pairs) + (map (fn (p) (nth p 1)) pairs))) + +(define + (hk-elem x lst) + (cond + ((= (len lst) 0) false) + ((= x (first lst)) true) + (else (hk-elem x (rest lst))))) + +(define (hk-not-elem x lst) (not (hk-elem x lst))) + +(define + (hk-nub lst) + (letrec + ((go (fn (seen acc items) (if (= (len items) 0) (reverse acc) (let ((h (first items)) (t (rest items))) (if (hk-elem h seen) (go seen acc t) (go (cons h seen) (cons h acc) t))))))) + (go (list) (list) lst))) + +(define (hk-sum lst) (reduce + 0 lst)) +(define (hk-product lst) (reduce * 1 lst)) + +(define + (hk-maximum lst) + (reduce (fn (a b) (if (> a b) a b)) (first lst) (rest lst))) + +(define + (hk-minimum lst) + (reduce (fn (a b) (if (< a b) a b)) (first lst) (rest lst))) + +(define (hk-concat lsts) (reduce append (list) lsts)) + +(define (hk-concat-map f lst) (hk-concat (map f lst))) + +(define hk-sort sort) + +(define + (hk-span pred lst) + (list (hk-take-while pred lst) (hk-drop-while pred lst))) + +(define (hk-break pred lst) (hk-span (fn (x) (not (pred x))) lst)) + +(define + (hk-foldl f acc lst) + (if + (= (len lst) 0) + acc + (hk-foldl f (f acc (first lst)) (rest lst)))) + +(define + (hk-foldr f z lst) + (if + (= (len lst) 0) + z + (f (first lst) (hk-foldr f z (rest lst))))) + +(define + (hk-scanl f acc lst) + (if + (= (len lst) 0) + (list acc) + (cons acc (hk-scanl f (f acc (first lst)) (rest lst))))) + +(define + (hk-replicate n x) + (if (= n 0) (list) (cons x (hk-replicate (- n 1) x)))) + +(define + (hk-intersperse sep lst) + (if + (or (= (len lst) 0) (= (len lst) 1)) + lst + (cons (first lst) (cons sep (hk-intersperse sep (rest lst)))))) + +;; =========================================================================== +;; 7. Maybe / Either ADTs +;; =========================================================================== + +(define hk-nothing {:_maybe true :_tag "nothing"}) +(define (hk-just x) {:_maybe true :value x :_tag "just"}) +(define (hk-is-nothing? m) (= (get m :_tag) "nothing")) +(define (hk-is-just? m) (= (get m :_tag) "just")) +(define (hk-from-just m) (get m :value)) +(define (hk-from-maybe def m) (if (hk-is-nothing? m) def (hk-from-just m))) +(define + (hk-maybe def f m) + (if (hk-is-nothing? m) def (f (hk-from-just m)))) + +(define (hk-left x) {:value x :_either true :_tag "left"}) +(define (hk-right x) {:value x :_either true :_tag "right"}) +(define (hk-is-left? e) (= (get e :_tag) "left")) +(define (hk-is-right? e) (= (get e :_tag) "right")) +(define (hk-from-left e) (get e :value)) +(define (hk-from-right e) (get e :value)) +(define + (hk-either f g e) + (if (hk-is-left? e) (f (hk-from-left e)) (g (hk-from-right e)))) + +;; =========================================================================== +;; 8. Tuples (lists — list->vector unreliable in sx_server) +;; =========================================================================== + +(define (hk-pair a b) (list a b)) +(define hk-fst first) +(define (hk-snd t) (nth t 1)) + +(define (hk-triple a b c) (list a b c)) +(define hk-fst3 first) +(define (hk-snd3 t) (nth t 1)) +(define (hk-thd3 t) (nth t 2)) + +(define (hk-curry f) (fn (a) (fn (b) (f a b)))) +(define (hk-uncurry f) (fn (p) (f (hk-fst p) (hk-snd p)))) + +;; =========================================================================== +;; 9. String helpers (Data.List / Data.Char for strings) +;; =========================================================================== + +;; words: split on whitespace +(define + (hk-words s) + (letrec + ((slen (len s)) + (skip-ws + (fn + (i) + (if + (>= i slen) + (list) + (let + ((c (substring s i (+ i 1)))) + (if + (or (= c " ") (= c "\t") (= c "\n")) + (skip-ws (+ i 1)) + (collect-word i (+ i 1))))))) + (collect-word + (fn + (start i) + (if + (>= i slen) + (list (substring s start i)) + (let + ((c (substring s i (+ i 1)))) + (if + (or (= c " ") (= c "\t") (= c "\n")) + (cons (substring s start i) (skip-ws (+ i 1))) + (collect-word start (+ i 1)))))))) + (skip-ws 0))) + +;; unwords: join with spaces +(define + (hk-unwords lst) + (if + (= (len lst) 0) + "" + (reduce (fn (a b) (str a " " b)) (first lst) (rest lst)))) + +;; lines: split on newline +(define + (hk-lines s) + (letrec + ((slen (len s)) + (go + (fn + (start i acc) + (if + (>= i slen) + (reverse (cons (substring s start i) acc)) + (if + (= (substring s i (+ i 1)) "\n") + (go + (+ i 1) + (+ i 1) + (cons (substring s start i) acc)) + (go start (+ i 1) acc)))))) + (if (= slen 0) (list) (go 0 0 (list))))) + +;; unlines: join, each with trailing newline +(define (hk-unlines lst) (reduce (fn (a b) (str a b "\n")) "" lst)) + +;; isPrefixOf +(define + (hk-is-prefix-of pre s) + (and (<= (len pre) (len s)) (= pre (substring s 0 (len pre))))) + +;; isSuffixOf +(define + (hk-is-suffix-of suf s) + (let + ((sl (len suf)) (tl (len s))) + (and (<= sl tl) (= suf (substring s (- tl sl) tl))))) + +;; isInfixOf — linear scan +(define + (hk-is-infix-of pat s) + (let + ((plen (len pat)) (slen (len s))) + (letrec + ((go (fn (i) (if (> (+ i plen) slen) false (if (= pat (substring s i (+ i plen))) true (go (+ i 1))))))) + (if (= plen 0) true (go 0))))) + +;; =========================================================================== +;; 10. Show helper +;; =========================================================================== + +(define + (hk-show x) + (cond + ((= x nil) "Nothing") + ((= x true) "True") + ((= x false) "False") + ((hk-rational? x) (hk-show-rational x)) + ((integer? x) (str x)) + ((float? x) (str x)) + ((= (type-of x) "string") (str "\"" x "\"")) + ((= (type-of x) "char") (str "'" (str x) "'")) + ((list? x) + (str + "[" + (if + (= (len x) 0) + "" + (reduce + (fn (a b) (str a "," (hk-show b))) + (hk-show (first x)) + (rest x))) + "]")) + (else (str x)))) diff --git a/lib/haskell/test.sh b/lib/haskell/test.sh index 892194d4..3ea6d249 100755 --- a/lib/haskell/test.sh +++ b/lib/haskell/test.sh @@ -46,6 +46,7 @@ for FILE in "${FILES[@]}"; do cat > "$TMPFILE" < "$TMPFILE2" <char 65)) 65) +(hk-test "chr 65" (hk-ord (hk-chr 65)) 65) +(hk-test "is-alpha? A" (hk-is-alpha? (integer->char 65)) true) +(hk-test "is-alpha? 0" (hk-is-alpha? (integer->char 48)) false) +(hk-test "is-digit? 5" (hk-is-digit? (integer->char 53)) true) +(hk-test "is-digit? A" (hk-is-digit? (integer->char 65)) false) +(hk-test "is-upper? A" (hk-is-upper? (integer->char 65)) true) +(hk-test "is-upper? a" (hk-is-upper? (integer->char 97)) false) +(hk-test "is-lower? a" (hk-is-lower? (integer->char 97)) true) +(hk-test "is-space? spc" (hk-is-space? (integer->char 32)) true) +(hk-test "is-space? A" (hk-is-space? (integer->char 65)) false) +(hk-test + "to-upper a" + (hk-ord (hk-to-upper (integer->char 97))) + 65) +(hk-test + "to-lower A" + (hk-ord (hk-to-lower (integer->char 65))) + 97) +(hk-test + "digit-to-int 0" + (hk-digit-to-int (integer->char 48)) + 0) +(hk-test + "digit-to-int 9" + (hk-digit-to-int (integer->char 57)) + 9) +(hk-test + "digit-to-int a" + (hk-digit-to-int (integer->char 97)) + 10) +(hk-test + "digit-to-int F" + (hk-digit-to-int (integer->char 70)) + 15) +(hk-test "int-to-digit 0" (hk-ord (hk-int-to-digit 0)) 48) +(hk-test "int-to-digit 10" (hk-ord (hk-int-to-digit 10)) 97) + +;; --------------------------------------------------------------------------- +;; 5. Data.Set +;; --------------------------------------------------------------------------- + +(hk-test "set-empty is set?" (hk-set? (hk-set-empty)) true) +(hk-test "set-null? empty" (hk-set-null? (hk-set-empty)) true) + +(let + ((s (hk-set-singleton 42))) + (do + (hk-test "singleton member" (hk-set-member? 42 s) true) + (hk-test "singleton size" (hk-set-size s) 1))) + +(let + ((s (hk-set-from-list (list 1 2 3)))) + (do + (hk-test "from-list member" (hk-set-member? 2 s) true) + (hk-test "from-list absent" (hk-set-member? 9 s) false) + (hk-test "from-list size" (hk-set-size s) 3))) + +;; --------------------------------------------------------------------------- +;; 6. Data.List +;; --------------------------------------------------------------------------- + +(hk-test "head" (hk-head (list 1 2 3)) 1) +(hk-test + "tail length" + (len (hk-tail (list 1 2 3))) + 2) +(hk-test "null? empty" (hk-null? (list)) true) +(hk-test "null? non-empty" (hk-null? (list 1)) false) +(hk-test + "length" + (hk-length (list 1 2 3)) + 3) + +(hk-test + "take 2" + (hk-take 2 (list 1 2 3)) + (list 1 2)) +(hk-test "take 0" (hk-take 0 (list 1 2)) (list)) +(hk-test + "take overflow" + (hk-take 5 (list 1 2)) + (list 1 2)) +(hk-test + "drop 1" + (hk-drop 1 (list 1 2 3)) + (list 2 3)) +(hk-test + "drop 0" + (hk-drop 0 (list 1 2)) + (list 1 2)) + +(hk-test + "take-while" + (hk-take-while + (fn (x) (< x 3)) + (list 1 2 3 4)) + (list 1 2)) +(hk-test + "drop-while" + (hk-drop-while + (fn (x) (< x 3)) + (list 1 2 3 4)) + (list 3 4)) + +(hk-test + "zip" + (hk-zip (list 1 2) (list 3 4)) + (list (list 1 3) (list 2 4))) +(hk-test + "zip uneven" + (hk-zip + (list 1 2 3) + (list 4 5)) + (list (list 1 4) (list 2 5))) + +(hk-test + "zip-with +" + (hk-zip-with + + + (list 1 2 3) + (list 10 20 30)) + (list 11 22 33)) + +(hk-test + "unzip fst" + (first + (hk-unzip + (list (list 1 3) (list 2 4)))) + (list 1 2)) +(hk-test + "unzip snd" + (nth + (hk-unzip + (list (list 1 3) (list 2 4))) + 1) + (list 3 4)) + +(hk-test + "elem hit" + (hk-elem 2 (list 1 2 3)) + true) +(hk-test + "elem miss" + (hk-elem 9 (list 1 2 3)) + false) +(hk-test + "not-elem" + (hk-not-elem 9 (list 1 2 3)) + true) + +(hk-test + "nub" + (hk-nub (list 1 2 1 3 2)) + (list 1 2 3)) + +(hk-test + "sum" + (hk-sum (list 1 2 3 4)) + 10) +(hk-test + "product" + (hk-product (list 1 2 3 4)) + 24) +(hk-test + "maximum" + (hk-maximum (list 3 1 4 1 5)) + 5) +(hk-test + "minimum" + (hk-minimum (list 3 1 4 1 5)) + 1) + +(hk-test + "concat" + (hk-concat + (list (list 1 2) (list 3 4))) + (list 1 2 3 4)) +(hk-test + "concat-map" + (hk-concat-map + (fn (x) (list x (* x x))) + (list 1 2 3)) + (list 1 1 2 4 3 9)) + +(hk-test + "sort" + (hk-sort (list 3 1 4 1 5)) + (list 1 1 3 4 5)) +(hk-test + "replicate" + (hk-replicate 3 0) + (list 0 0 0)) +(hk-test "replicate 0" (hk-replicate 0 99) (list)) + +(hk-test + "intersperse" + (hk-intersperse 0 (list 1 2 3)) + (list 1 0 2 0 3)) +(hk-test + "intersperse 1" + (hk-intersperse 0 (list 1)) + (list 1)) +(hk-test "intersperse empty" (hk-intersperse 0 (list)) (list)) + +(hk-test + "span" + (hk-span + (fn (x) (< x 3)) + (list 1 2 3 4)) + (list (list 1 2) (list 3 4))) +(hk-test + "break" + (hk-break + (fn (x) (>= x 3)) + (list 1 2 3 4)) + (list (list 1 2) (list 3 4))) + +(hk-test + "foldl" + (hk-foldl + (fn (a b) (- a b)) + 10 + (list 1 2 3)) + 4) +(hk-test + "foldr" + (hk-foldr cons (list) (list 1 2 3)) + (list 1 2 3)) + +(hk-test + "scanl" + (hk-scanl + 0 (list 1 2 3)) + (list 0 1 3 6)) + +;; --------------------------------------------------------------------------- +;; 7. Maybe / Either +;; --------------------------------------------------------------------------- + +(hk-test "nothing is-nothing?" (hk-is-nothing? hk-nothing) true) +(hk-test "nothing is-just?" (hk-is-just? hk-nothing) false) +(hk-test "just is-just?" (hk-is-just? (hk-just 42)) true) +(hk-test "just is-nothing?" (hk-is-nothing? (hk-just 42)) false) +(hk-test "from-just" (hk-from-just (hk-just 99)) 99) +(hk-test + "from-maybe nothing" + (hk-from-maybe 0 hk-nothing) + 0) +(hk-test + "from-maybe just" + (hk-from-maybe 0 (hk-just 42)) + 42) +(hk-test + "maybe nothing" + (hk-maybe 0 (fn (x) (* x 2)) hk-nothing) + 0) +(hk-test + "maybe just" + (hk-maybe 0 (fn (x) (* x 2)) (hk-just 5)) + 10) + +(hk-test "left is-left?" (hk-is-left? (hk-left "e")) true) +(hk-test "right is-right?" (hk-is-right? (hk-right 42)) true) +(hk-test "from-right" (hk-from-right (hk-right 7)) 7) +(hk-test + "either left" + (hk-either (fn (x) (str "L" x)) (fn (x) (str "R" x)) (hk-left "err")) + "Lerr") +(hk-test + "either right" + (hk-either + (fn (x) (str "L" x)) + (fn (x) (str "R" x)) + (hk-right 42)) + "R42") + +;; --------------------------------------------------------------------------- +;; 8. Tuples +;; --------------------------------------------------------------------------- + +(hk-test "pair" (hk-pair 1 2) (list 1 2)) +(hk-test "fst" (hk-fst (hk-pair 3 4)) 3) +(hk-test "snd" (hk-snd (hk-pair 3 4)) 4) +(hk-test + "triple" + (hk-triple 1 2 3) + (list 1 2 3)) +(hk-test + "fst3" + (hk-fst3 (hk-triple 7 8 9)) + 7) +(hk-test + "thd3" + (hk-thd3 (hk-triple 7 8 9)) + 9) + +(hk-test "curry" ((hk-curry +) 3 4) 7) +(hk-test + "uncurry" + ((hk-uncurry (fn (a b) (* a b))) (list 3 4)) + 12) + +;; --------------------------------------------------------------------------- +;; 9. String helpers +;; --------------------------------------------------------------------------- + +(hk-test "words" (hk-words "hello world") (list "hello" "world")) +(hk-test "words leading ws" (hk-words " foo bar") (list "foo" "bar")) +(hk-test "words empty" (hk-words "") (list)) +(hk-test "unwords" (hk-unwords (list "a" "b" "c")) "a b c") +(hk-test "unwords single" (hk-unwords (list "x")) "x") + +(hk-test "lines" (hk-lines "a\nb\nc") (list "a" "b" "c")) +(hk-test "lines single" (hk-lines "hello") (list "hello")) +(hk-test "unlines" (hk-unlines (list "a" "b")) "a\nb\n") + +(hk-test "is-prefix-of yes" (hk-is-prefix-of "he" "hello") true) +(hk-test "is-prefix-of no" (hk-is-prefix-of "wo" "hello") false) +(hk-test "is-prefix-of eq" (hk-is-prefix-of "hi" "hi") true) +(hk-test "is-prefix-of empty" (hk-is-prefix-of "" "hi") true) + +(hk-test "is-suffix-of yes" (hk-is-suffix-of "lo" "hello") true) +(hk-test "is-suffix-of no" (hk-is-suffix-of "he" "hello") false) +(hk-test "is-suffix-of empty" (hk-is-suffix-of "" "hi") true) + +(hk-test "is-infix-of yes" (hk-is-infix-of "ell" "hello") true) +(hk-test "is-infix-of no" (hk-is-infix-of "xyz" "hello") false) +(hk-test "is-infix-of empty" (hk-is-infix-of "" "hello") true) + +;; --------------------------------------------------------------------------- +;; 10. Show +;; --------------------------------------------------------------------------- + +(hk-test "show nil" (hk-show nil) "Nothing") +(hk-test "show true" (hk-show true) "True") +(hk-test "show false" (hk-show false) "False") +(hk-test "show int" (hk-show 42) "42") +(hk-test "show string" (hk-show "hi") "\"hi\"") +(hk-test + "show list" + (hk-show (list 1 2 3)) + "[1,2,3]") +(hk-test "show empty list" (hk-show (list)) "[]") + +;; --------------------------------------------------------------------------- +;; Summary (required by test.sh — last expression is the return value) +;; --------------------------------------------------------------------------- + +(list hk-test-pass hk-test-fail)