commit 8e6c12f7c9592d655124146d115a456b531a6b9a
parent e25b794c8b51b72531be053c2f73e2f7d47374df
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 21 Apr 2026 17:10:54 -0700
lisp: concat prelude in make
Diffstat:
2 files changed, 60 insertions(+), 8 deletions(-)
diff --git a/Makefile b/Makefile
@@ -34,7 +34,11 @@ endif
# Per-program runtime arguments. The lisp interpreter reads its script
# from argv[1] (docs/LISP.md step 8). Default to its smoke fixture so
# `make run-all` stays a smoke test.
-RUN_ARGS_lisp ?= tests/lisp/00-identity.scm
+#
+# Lisp fixtures get src/prelude.scm cat'd in front of them (see the
+# $(COMBINED_DIR) rule below) so map/filter/fold are in scope without
+# the prelude living inside lisp.M1.
+RUN_ARGS_lisp ?= $(COMBINED_DIR)/00-identity.scm
RUN_ARGS := $(RUN_ARGS_$(PROG))
# Map P1 ARCH -> Linux-platform tag for the container.
@@ -69,8 +73,9 @@ RUNTIME_IMAGE_amd64 := $(ALPINE_IMAGE)@sha256:4d889c14e7d5a73929ab00be2ef8ff22
RUNTIME_IMAGE_riscv64 := $(ALPINE_IMAGE)@sha256:667d07bf2f6239f094f64b5682c8ffbe24c9f3139b1fb854f85caf931a3d7439
RUNTIME_IMAGE := $(RUNTIME_IMAGE_$(ARCH))
-OUT_DIR := build/$(ARCH)
-TOOLS_DIR := $(OUT_DIR)/tools
+OUT_DIR := build/$(ARCH)
+TOOLS_DIR := $(OUT_DIR)/tools
+COMBINED_DIR := $(OUT_DIR)/combined
# stage0-posix uses mixed-case arch dirs (AArch64, AMD64) that don't match
# our lowercase ARCH. Map them so build/upstream/ mirrors upstream layout.
@@ -114,9 +119,15 @@ toolchain: $(TOOLS_DIR)/M0
populate-upstream: $(UPSTREAM_STAMP)
-$(OUT_DIR) $(TOOLS_DIR):
+$(OUT_DIR) $(TOOLS_DIR) $(COMBINED_DIR):
mkdir -p $@
+# Prepend src/prelude.scm to every lisp fixture via plain cat, producing
+# a standalone .scm the interpreter can eval with its single-argv
+# contract. Host-side — no podman needed, it's just byte concatenation.
+$(COMBINED_DIR)/%.scm: tests/lisp/%.scm src/prelude.scm | $(COMBINED_DIR)
+ cat src/prelude.scm $< > $@
+
$(IMAGE_STAMP): | $(OUT_DIR)
podman pull --platform $(PLATFORM) $(RUNTIME_IMAGE)
@touch $@
@@ -167,7 +178,10 @@ $(OUT_DIR)/$(PROG): $(OUT_DIR)/$(PROG).hex2 $(UPSTREAM_DIR)/$(ARCH_DIR)/ELF-$(AR
$(TOOLS_DIR)/catm $(OUT_DIR)/$(PROG).linked.hex2 $(UPSTREAM_DIR)/$(ARCH_DIR)/ELF-$(ARCH).hex2 $(OUT_DIR)/$(PROG).hex2 ; \
$(TOOLS_DIR)/hex2-0 $(OUT_DIR)/$(PROG).linked.hex2 $(OUT_DIR)/$(PROG)'
-run: $(OUT_DIR)/$(PROG) | $(IMAGE_STAMP)
+# $(RUN_ARGS) is listed as a prerequisite so that lisp's concat'd fixture
+# is materialised before the interpreter runs. Empty for hello/demo and
+# therefore a no-op there.
+run: $(OUT_DIR)/$(PROG) $(RUN_ARGS) | $(IMAGE_STAMP)
$(PODMAN) ./$(OUT_DIR)/$(PROG) $(RUN_ARGS)
# `-` prefix: continue past non-zero exit. demo.M1 exits with the computed
@@ -198,14 +212,29 @@ LISP_TESTS := \
tests/lisp/14-io.scm \
tests/lisp/14-tagpred.scm \
tests/lisp/15-pred.scm \
- tests/lisp/16-prelude.scm
+ tests/lisp/16-prelude.scm \
+ tests/lisp/20-quote.scm \
+ tests/lisp/21-neg-hex.scm \
+ tests/lisp/22-char.scm \
+ tests/lisp/23-vector.scm \
+ tests/lisp/24-dotted.scm \
+ tests/lisp/25-set.scm \
+ tests/lisp/26-let.scm \
+ tests/lisp/27-cond.scm \
+ tests/lisp/28-quasi.scm \
+ tests/lisp/29-innerdef.scm
+
+# Build the prelude-prefixed twin of every fixture and feed that to the
+# interpreter; .expected still lives next to the original .scm.
+LISP_TESTS_COMBINED := $(patsubst tests/lisp/%.scm,$(COMBINED_DIR)/%.scm,$(LISP_TESTS))
-test-lisp: | $(IMAGE_STAMP)
+test-lisp: $(LISP_TESTS_COMBINED) | $(IMAGE_STAMP)
@$(MAKE) --no-print-directory PROG=lisp ARCH=$(ARCH) build/$(ARCH)/lisp
@pass=0; fail=0; \
for scm in $(LISP_TESTS); do \
expected="$${scm%.scm}.expected"; \
- actual=$$($(PODMAN) ./build/$(ARCH)/lisp "$$scm" || true); \
+ combined="$(COMBINED_DIR)/$$(basename $$scm)"; \
+ actual=$$($(PODMAN) ./build/$(ARCH)/lisp "$$combined" || true); \
want=$$(cat "$$expected" 2>/dev/null || printf ''); \
if [ "$$actual" = "$$want" ]; then \
echo " PASS [$(ARCH)] $$scm"; \
diff --git a/src/prelude.scm b/src/prelude.scm
@@ -0,0 +1,23 @@
+;; Prelude: map / filter / fold. Concatenated ahead of every user script
+;; by the Makefile (cat src/prelude.scm $user.scm > combined.scm) so the
+;; interpreter keeps its single-argv contract and lisp.M1 stays free of
+;; embedded Scheme source.
+(define map
+ (lambda (f xs)
+ (if (null? xs)
+ (quote ())
+ (cons (f (car xs)) (map f (cdr xs))))))
+
+(define filter
+ (lambda (p xs)
+ (if (null? xs)
+ (quote ())
+ (if (p (car xs))
+ (cons (car xs) (filter p (cdr xs)))
+ (filter p (cdr xs))))))
+
+(define fold
+ (lambda (f acc xs)
+ (if (null? xs)
+ acc
+ (fold f (f acc (car xs)) (cdr xs)))))