From c976378363812d5725b0c6ae3a767e4b33e0a0c3 Mon Sep 17 00:00:00 2001 From: alex-eg Date: Mon, 14 Sep 2026 23:35:48 +0300 Subject: [PATCH] add module testing Also fix module import --- Makefile | 9 +++++++-- sex-modules.scm | 13 ++++++++----- tests/modules/Makefile | 28 ++++++++++++++++++++++++++++ tests/modules/greet-app.sex | 19 +++++++++++++++++++ tests/modules/greet.sex | 18 ++++++++++++++++++ 5 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 tests/modules/Makefile create mode 100644 tests/modules/greet-app.sex create mode 100644 tests/modules/greet.sex diff --git a/Makefile b/Makefile index a8a55c5..a540e4b 100644 --- a/Makefile +++ b/Makefile @@ -60,13 +60,18 @@ sextest: SEX_TEST_PROGRAMS = hello-world lists comments unicode +# Multi-module linking is checked end to end; see tests/modules/Makefile. +check-modules: sexc + @$(MAKE) --no-print-directory -C ./tests/modules check SEXC=../../sexc + run-tests: sexc sex-tests sextest - ./sex-tests && ./sextest --sexc=./sexc $(SEX_TEST_PROGRAMS:%=./tests/sex-programs/%.sex) + ./sex-tests && ./sextest --sexc=./sexc $(SEX_TEST_PROGRAMS:%=./tests/sex-programs/%.sex) && $(MAKE) check-modules clean: rm -f $(OBJ) main.o rm -f *.import.scm rm -f *.link rm -f sexc sex-tests sextest + $(MAKE) -C ./tests/modules clean -.PHONY: clean run-tests sex-tests sextest +.PHONY: clean run-tests sex-tests sextest check-modules diff --git a/sex-modules.scm b/sex-modules.scm index c2a9d61..48dace6 100644 --- a/sex-modules.scm +++ b/sex-modules.scm @@ -68,11 +68,14 @@ (case (car form) ((pub) (case (cadr form) - ((fn) ; replace with prototype - ;; fn type name (arg-list) (body) - ;; 1 2 3 4 - we need first 4 - (cons (copy-form-source! form (take (cdr form) 4)) acc)) - ((define defmacro import include struct typedef union var) + ;; A function is reduced to a prototype and keeps its `pub', so + ;; the importing unit declares it with external linkage + ((fn) + (cons (copy-form-source! form (take form 5)) acc)) + ;; A variable becomes an `extern' declaration + ((var) + (cons (copy-form-source! form (cons 'extern (take (cdr form) 3))) acc)) + ((define defmacro import include struct typedef union) (cons (copy-form-source! form (cdr form)) acc)) (else (sex-error form "pub must be followed by a definition" form)))) (else acc))) diff --git a/tests/modules/Makefile b/tests/modules/Makefile new file mode 100644 index 0000000..d3d604c --- /dev/null +++ b/tests/modules/Makefile @@ -0,0 +1,28 @@ +# Multi-module linking. +# +# Modules are only testable end to end, and nothing else in the suite +# links more than one translation unit. Three things have to hold at +# once: an imported `pub fn' comes out as a prototype with external +# linkage, an imported `pub var' as an extern declaration, and the +# module's object survives being passed after `--'. Get any of them +# wrong and this fails to link -- or, in the `pub var' case, links and +# quietly counts into a private copy. + +SEXC ?= ../../sexc + +EXPECTED = hello, world\nhello, sex\n2 greetings + +check: + @$(SEXC) greet.sex -c -o greet.o + @$(SEXC) greet-app.sex -o greet-app -- greet.o + @if [ "`./greet-app`" = "`printf '$(EXPECTED)\n'`" ]; then \ + echo "modules ok"; \ + else \ + echo "modules FAILED, got:"; ./greet-app; $(MAKE) clean; exit 1; \ + fi + @$(MAKE) --no-print-directory clean + +clean: + @rm -f greet.o greet-app + +.PHONY: check clean diff --git a/tests/modules/greet-app.sex b/tests/modules/greet-app.sex new file mode 100644 index 0000000..4ecf5f0 --- /dev/null +++ b/tests/modules/greet-app.sex @@ -0,0 +1,19 @@ +;;; Uses the greet module. Build both, then link them: +;;; +;;; ./sexc example/greet.sex -c -o greet.o +;;; ./sexc example/greet-app.sex -o greet-app -- greet.o +;;; +;;; `(import greet)' pastes greet's public declarations here: `greet' +;;; as a prototype and `greet-count' as an extern. Both keep external +;;; linkage, so they refer to the one definition in greet.o rather than +;;; to private copies. + +(include stdio.h) + +(import greet) + +(pub fn main () int + (greet "world") + (greet "sex") + (printf "%d greetings\n" greet-count) + (return 0)) diff --git a/tests/modules/greet.sex b/tests/modules/greet.sex new file mode 100644 index 0000000..094333a --- /dev/null +++ b/tests/modules/greet.sex @@ -0,0 +1,18 @@ +;;; A module. Everything marked `pub' forms its public interface; +;;; everything else is private to this file. +;;; +;;; Importing a module does not link it: it pastes the declarations, so +;;; the compiled object still has to be handed to the C compiler. See +;;; greet-app.sex. + +(include stdio.h) + +(pub var greet-count int 0) + +(pub fn greet ((name (* const char))) void + (++ greet-count) + (printf "hello, %s\n" name)) + +;;; Not `pub': invisible to importers, and static in the generated C. +(fn unused-helper () void + (printf "private\n"))