;; Go end-to-end tests — complete programs exercising lex+parse+ ;; types+eval+sched+stdlib together. Each test runs a multi-line Go ;; program and inspects the final env. (define go-e2e-test-count 0) (define go-e2e-test-pass 0) (define go-e2e-test-fails (list)) (define go-e2e-test (fn (name actual expected) (set! go-e2e-test-count (+ go-e2e-test-count 1)) (if (= actual expected) (set! go-e2e-test-pass (+ go-e2e-test-pass 1)) (append! go-e2e-test-fails {:name name :expected expected :actual actual})))) (define go-e2e-env (go-env-extend (go-env-extend go-env-builtins "strings" go-std-strings) "strconv" go-std-strconv)) (define go-e2e-run (fn (src-list) (go-eval-program go-e2e-env (map go-parse src-list)))) ;; ── 1. Sieve via boolean slice (no modulo needed) ──────────────── (go-e2e-test "e2e: sieve-of-Eratosthenes via boolean slice — count primes ≤ 30" (let ((env (go-e2e-run (list ;; sieve[i] true means i is COMPOSITE (saves the ;; default-bool initialisation for primes). "sieve := []bool{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}" "for p := 2; p < 31; p = p + 1 { if sieve[p] == false { for k := p + p; k < 31; k = k + p { sieve[k] = true } } }" "count := 0" "for i := 2; i < 31; i = i + 1 { if sieve[i] == false { count = count + 1 } }")))) (go-env-lookup env "count")) ;; primes ≤ 30: 2,3,5,7,11,13,17,19,23,29 = 10 10) ;; ── 1b. Range-membership check (works without mod) ─────────────── (go-e2e-test "e2e: linear search across slice of strings" (let ((env (go-e2e-run (list "words := []string{\"apple\", \"banana\", \"cherry\", \"date\"}" "func indexOf(xs []string, target string) int { for i, v := range xs { if v == target { return i } } ; return -1 }" "i := indexOf(words, \"cherry\")" "missing := indexOf(words, \"xyz\")")))) (list (go-env-lookup env "i") (go-env-lookup env "missing"))) (list 2 -1)) ;; ── 2. Reverse a slice ─────────────────────────────────────────── (go-e2e-test "e2e: reverse a slice of ints" (let ((env (go-e2e-run (list "func reverse(xs []int) []int { r := []int{} ; for i := len(xs) - 1; i >= 0; i = i - 1 { r = append(r, xs[i]) } ; return r }" "out := reverse([]int{1, 2, 3, 4, 5})")))) (go-env-lookup env "out")) (list :go-slice (list 5 4 3 2 1))) ;; ── 3. Fibonacci (recursive) ───────────────────────────────────── (go-e2e-test "e2e: fib(10) = 55" (let ((env (go-e2e-run (list "func fib(n int) int { if n < 2 { return n } ; return fib(n-1) + fib(n-2) }" "r := fib(10)")))) (go-env-lookup env "r")) 55) ;; ── 4. Sum-of-squares via Map+Reduce ───────────────────────────── (go-e2e-test "e2e: sum-of-squares 1..5 via Map+Reduce" (let ((env (go-e2e-run (list "func Map[T any, U any](xs []T, f func(T) U) []U { r := []int{} ; for i, v := range xs { r = append(r, f(v)) } ; return r }" "func Reduce[T any, U any](xs []T, seed U, f func(U, T) U) U { acc := seed ; for i, v := range xs { acc = f(acc, v) } ; return acc }" "func sq(x int) int { return x * x }" "func add(a int, b int) int { return a + b }" "squares := Map([]int{1, 2, 3, 4, 5}, sq)" "total := Reduce(squares, 0, add)")))) (go-env-lookup env "total")) ;; 1 + 4 + 9 + 16 + 25 = 55 55) ;; ── 5. Word frequency counter ──────────────────────────────────── (go-e2e-test "e2e: word-frequency over a sentence" (let ((env (go-e2e-run (list "text := \"the quick brown fox jumps over the lazy dog the\"" "words := strings.Split(text, \" \")" "counts := map[string]int{}" "for i, w := range words { counts[w] = counts[w] + 1 }" "the_count := counts[\"the\"]" "fox_count := counts[\"fox\"]" "dog_count := counts[\"dog\"]")))) (list (go-env-lookup env "the_count") (go-env-lookup env "fox_count") (go-env-lookup env "dog_count"))) (list 3 1 1)) ;; ── 6. Pipeline via channels ───────────────────────────────────── (go-e2e-test "e2e: pipeline — generate, square, sum" (let ((env (go-e2e-run (list "func gen(c chan int, n int) { for i := 1; i <= n; i = i + 1 { c <- i } ; close(c) }" "func sq(in chan int, out chan int) { for v := range in { out <- v * v } ; close(out) }" "src := make()" "sqs := make()" "go gen(src, 4)" "go sq(src, sqs)" "total := 0" "for v := range sqs { total = total + v }")))) (go-env-lookup env "total")) ;; 1+4+9+16 = 30 30) ;; ── 7. Worker pool draining a job channel ──────────────────────── (go-e2e-test "e2e: worker pool — sum of doubled jobs" (let ((env (go-e2e-run (list "func worker(jobs chan int, results chan int) { for j := range jobs { results <- j * 2 } }" "jobs := make()" "results := make()" "jobs <- 10 ; jobs <- 20 ; jobs <- 30" "close(jobs)" "go worker(jobs, results)" "close(results)" "sum := 0" "for r := range results { sum = sum + r }")))) (go-env-lookup env "sum")) ;; 20 + 40 + 60 = 120 120) ;; ── 8. Bubble sort ─────────────────────────────────────────────── (go-e2e-test "e2e: bubble sort ascending" (let ((env (go-e2e-run (list "func bubble(xs []int) []int { n := len(xs) ; for i := 0; i < n; i = i + 1 { for j := 0; j < n - 1; j = j + 1 { if xs[j] > xs[j+1] { tmp := xs[j] ; xs[j] = xs[j+1] ; xs[j+1] = tmp } } } ; return xs }" "out := bubble([]int{3, 1, 4, 1, 5, 9, 2, 6})")))) (go-env-lookup env "out")) (list :go-slice (list 1 1 2 3 4 5 6 9))) ;; ── 9. String reverse using strings.Split + reverse + Join ────── (go-e2e-test "e2e: reverse words in a sentence" (let ((env (go-e2e-run (list "func rev(xs []string) []string { r := []string{} ; for i := len(xs) - 1; i >= 0; i = i - 1 { r = append(r, xs[i]) } ; return r }" "text := \"go on sx\"" "out := strings.Join(rev(strings.Split(text, \" \")), \"-\")")))) (go-env-lookup env "out")) "sx-on-go") ;; ── 10. Counting occurrences via Filter ────────────────────────── (go-e2e-test "e2e: count even numbers via Filter+len" (let ((env (go-e2e-run (list "func Filter[T any](xs []T, p func(T) bool) []T { r := []int{} ; for i, v := range xs { if p(v) { r = append(r, v) } } ; return r }" "func gt5(x int) bool { return x > 5 }" "n := len(Filter([]int{1, 2, 6, 3, 7, 8, 4, 9}, gt5))")))) (go-env-lookup env "n")) ;; gt5: 6,7,8,9 = 4 4) ;; ── 11. Recursive ackermann (small inputs) ─────────────────────── (go-e2e-test "e2e: ackermann(2, 3) = 9" (let ((env (go-e2e-run (list "func ack(m int, n int) int { if m == 0 { return n + 1 } ; if n == 0 { return ack(m - 1, 1) } ; return ack(m - 1, ack(m, n - 1)) }" "r := ack(2, 3)")))) (go-env-lookup env "r")) 9) ;; ── 12. Defer + recover smoke test ─────────────────────────────── (go-e2e-test "e2e: defer + recover in real-fn flow" (let ((env (go-e2e-run (list "func safeDivide(a int, b int) int { defer recover() ; if b == 0 { panic(\"div by zero\") } ; return a / b }" "r := safeDivide(10, 0)" "after := 99")))) (go-env-lookup env "after")) 99) (define go-e2e-test-summary (str "e2e " go-e2e-test-pass "/" go-e2e-test-count))