A Lisp implemented in AWK
# SPDX-License-Identifier: BSD-2-Clause

AWK = /usr/bin/awk

# used to build the single glotawk file.
BUILD_AWK   ?= $(AWK)
BUILD_AWK_F ?= $(AWK_F)
# used to run the tests using the built glotawk.
TEST_AWK    ?= $(AWK)
TEST_AWK_F  ?= $(AWK_F)
# this one is not run for build or testing. it's written in the
# shebang at the top of the built glotawk.
#
# now, with shebangs, you only dependably get to supply one argument -
# you could say #!/usr/bin/gawk -f or #!/usr/bin/gawk -cf, but you
# can't say #!/usr/bin/gawk -c -f. so you have to set TARGET_AWK_F to
# have only one bundle of switches, having zero or more other
# switches, and ending with f.
TARGET_AWK_F ?= $(AWK) -f

all: glotawk check benchmark

help = "\n\
Targets:\n\
        glotawk:       build the interpreter, a single large\n\
                       executable AWK script\n\
        check:         run tests\n\
        benchmark:     run a few benchmarks\n\
        all (default): glotawk, check, and benchmark\n\
        clean:         get rid of build products\n\
        heapviz:       fancy Graphviz view of initial heap and GC\n\
\n\
Variables:\n\
        AWK            the awk used if none of the below are given\n\
        BUILD_AWK      the awk used to build glotawk\n\
        TEST_AWK       the awk used to test the built glotawk\n\
        TARGET_AWK_F   the awk -f to write in the shebang at the top of glotawk\n\
"
help:
	@echo ${help} >&2

depend: .depend
	:

.depend:
	$(BUILD_AWK) -v BUILD_AWK="$(BUILD_AWK)" -f tmpl-depends.awk *.tmpl.awk > .depend

glotawk-build.awk:
	$(BUILD_AWK) -v BUILD_AWK="$(BUILD_AWK)" -v TARGET_AWK_F="$(TARGET_AWK_F)" -f tmpl.awk glotawk-build.tmpl.awk > $@

image.awk: glotawk-build.awk lib-eval.awk lib.glotawk
	(echo "(dump-dot \"before-gc.dot\")"; echo "(gc-dot \"marks.dot\" \"sweeps.dot\")"; echo "(dump \"$@\")"; echo "(dump-dot \"after-gc.dot\")") | $(BUILD_AWK) -f glotawk-build.awk

glotawk: glotawk-build.awk
	$(BUILD_AWK) -v BUILD_AWK="$(BUILD_AWK)" -v TARGET_AWK_F="$(TARGET_AWK_F)" -f tmpl.awk glotawk-run.tmpl.awk > $@
	chmod a+x $@

test: check
check: glotawk sane_lisp
	TEST_AWK="$(TEST_AWK)" ./sane_lisp; x=$$?; if [ $$x -eq 0 ]; then echo --pass--; else echo --FAIL--; fi; return $$x

clean:
	rm -f glotawk glotawk-build.awk image.awk heap-nomarks.dot gc-marks.dot gc-sweeps.dot heap.dot .depend

A_HUNDRED_THINGS = 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 \
                   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 \
                   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 \
                   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 \
                   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

benchmark: glotawk
	@echo --- To run benchmark.glotawk ---
	bash -c "time for x in $(A_HUNDRED_THINGS); do $(TEST_AWK) -f ./glotawk benchmark.glotawk >/dev/null; echo -n .; done; echo"
	@echo --- Just to start up ---
	bash -c "time for x in $(A_HUNDRED_THINGS); do echo 3 | $(TEST_AWK) -f ./glotawk >/dev/null; echo -n .; done; echo"

heap-nomarks.dot: glotawk
	echo "(dump-dot \"$@\")" | ./glotawk

gc-marks.dot gc-sweeps.dot: glotawk
	echo "(gc-dot \"gc-marks.dot\" \"gc-sweeps.dot\")" | $(BUILD_AWK) -f ./glotawk -v LOG_LEVEL=3

heap.dot: glotawk heap-nomarks.dot gc-marks.dot gc-sweeps.dot
	cat heap-nomarks.dot | \
		sed -e '/^\/\*mark colors\*\/$$/r gc-marks.dot' | \
		sed -e '/^\/\*sweep colors\*\/$$/r gc-sweeps.dot' > $@

heapviz: heap.dot
	dot -Tx11 heap.dot

.PHONY: all depend check test clean benchmark heapviz
.MAKE: .depend