From 9be65d7d60fa7e424fc765ddf872336bc92ab507 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 25 Apr 2026 17:59:39 +0000 Subject: [PATCH] haskell: lazy sieve of Eratosthenes (+mod/div/rem/quot, +2 tests, 390/390) --- lib/haskell/eval.sx | 8 +++++ lib/haskell/tests/program-sieve.sx | 48 +++++++++++++++++++++++++++++ lib/haskell/tests/programs/sieve.hs | 13 ++++++++ 3 files changed, 69 insertions(+) create mode 100644 lib/haskell/tests/program-sieve.sx create mode 100644 lib/haskell/tests/programs/sieve.hs diff --git a/lib/haskell/eval.sx b/lib/haskell/eval.sx index 4b605ca3..2322f994 100644 --- a/lib/haskell/eval.sx +++ b/lib/haskell/eval.sx @@ -480,6 +480,10 @@ ((= op "||") (hk-of-bool (or (hk-truthy? lv) (hk-truthy? rv)))) ((= op ":") (hk-mk-cons lv rv)) ((= op "++") (hk-list-append lv rv)) + ((= op "mod") (mod lv rv)) + ((= op "div") (floor (/ lv rv))) + ((= op "rem") (mod lv rv)) + ((= op "quot") (truncate (/ lv rv))) (:else (raise (str "unknown operator: " op)))))) (define @@ -670,6 +674,10 @@ plus a b = a + b (dict-set! env "&&" (hk-make-binop-builtin "&&" "&&")) (dict-set! env "||" (hk-make-binop-builtin "||" "||")) (dict-set! env "++" (hk-make-binop-builtin "++" "++")) + (dict-set! env "mod" (hk-make-binop-builtin "mod" "mod")) + (dict-set! env "div" (hk-make-binop-builtin "div" "div")) + (dict-set! env "rem" (hk-make-binop-builtin "rem" "rem")) + (dict-set! env "quot" (hk-make-binop-builtin "quot" "quot")) (hk-load-into! env hk-prelude-src) env))) diff --git a/lib/haskell/tests/program-sieve.sx b/lib/haskell/tests/program-sieve.sx new file mode 100644 index 00000000..3c2467b4 --- /dev/null +++ b/lib/haskell/tests/program-sieve.sx @@ -0,0 +1,48 @@ +;; sieve.hs — lazy sieve of Eratosthenes. +;; +;; The canonical artefact lives at lib/haskell/tests/programs/sieve.hs. +;; Mirrored here as an SX string because the default eval env has no +;; read-file. Uses filter + backtick `mod` + lazy [2..] — all of which +;; are now wired in via Phase 3 + the mod/div additions to hk-binop. + +(define + hk-as-list + (fn (xs) + (cond + ((and (list? xs) (= (first xs) "[]")) (list)) + ((and (list? xs) (= (first xs) ":")) + (cons (nth xs 1) (hk-as-list (nth xs 2)))) + (:else xs)))) + +(define + hk-prog-val + (fn + (src name) + (hk-deep-force (get (hk-eval-program (hk-core src)) name)))) + +(define + hk-sieve-source + "sieve (p:xs) = p : sieve (filter (\\x -> x `mod` p /= 0) xs) +sieve [] = [] +primes = sieve [2..] +result = take 10 primes +") + +(hk-test + "sieve.hs — first 10 primes" + (hk-as-list (hk-prog-val hk-sieve-source "result")) + (list 2 3 5 7 11 13 17 19 23 29)) + +(hk-test + "sieve.hs — 20th prime is 71" + (nth + (hk-as-list + (hk-prog-val + (str + hk-sieve-source + "result20 = take 20 primes\n") + "result20")) + 19) + 71) + +{:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail} diff --git a/lib/haskell/tests/programs/sieve.hs b/lib/haskell/tests/programs/sieve.hs new file mode 100644 index 00000000..f1ac4ef8 --- /dev/null +++ b/lib/haskell/tests/programs/sieve.hs @@ -0,0 +1,13 @@ +-- sieve.hs — lazy sieve of Eratosthenes. +-- +-- Each recursive call to `sieve` consumes one prime `p` off the front +-- of the input stream and produces an infinite stream of composites +-- filtered out via `filter`. Because cons is lazy, only as much of +-- the stream is forced as demanded by `take`. + +sieve (p:xs) = p : sieve (filter (\x -> x `mod` p /= 0) xs) +sieve [] = [] + +primes = sieve [2..] + +result = take 10 primes