diff options
Diffstat (limited to 'make')
| -rw-r--r-- | make/Makefile | 23 | ||||
| -rw-r--r-- | make/gmsl.mk | 115 | ||||
| -rwxr-xr-x | make/reader.mk | 170 | ||||
| -rw-r--r-- | make/readline.mk | 15 | ||||
| -rw-r--r-- | make/step0_repl.mk | 26 | ||||
| -rw-r--r-- | make/step1_read_print.mk | 31 | ||||
| -rw-r--r-- | make/step2_eval.mk | 71 | ||||
| -rw-r--r-- | make/step3_env.mk | 93 | ||||
| -rw-r--r-- | make/step4_if_fn_do.mk | 112 | ||||
| -rw-r--r-- | make/step6_file.mk | 130 | ||||
| -rw-r--r-- | make/step7_quote.mk | 147 | ||||
| -rw-r--r-- | make/step8_macros.mk | 170 | ||||
| -rw-r--r-- | make/step9_interop.mk | 174 | ||||
| -rw-r--r-- | make/stepA_more.mk | 192 | ||||
| -rw-r--r-- | make/tests/common.mk | 18 | ||||
| -rw-r--r-- | make/tests/reader.mk | 76 | ||||
| -rw-r--r-- | make/tests/step9_interop.mk | 14 | ||||
| -rw-r--r-- | make/tests/types.mk | 304 | ||||
| -rw-r--r-- | make/types.mk | 484 | ||||
| -rw-r--r-- | make/util.mk | 72 |
20 files changed, 2437 insertions, 0 deletions
diff --git a/make/Makefile b/make/Makefile new file mode 100644 index 0000000..1110397 --- /dev/null +++ b/make/Makefile @@ -0,0 +1,23 @@ + +TESTS = tests/types.mk tests/reader.mk tests/step9_interop.mk + +SOURCES = util.mk readline.mk gmsl.mk types.mk reader.mk stepA_more.mk + +mal.mk: $(SOURCES) + echo "#!/usr/bin/make -f" > $@ + cat $+ | grep -v "^include " >> $@ + chmod +x $@ + +clean: + rm -f mal.mk + +.PHONY: stats tests $(TESTS) + +stats: $(SOURCES) + @wc $^ + +tests: $(TESTS) + +$(TESTS): + @echo "Running $@"; \ + make -f $@ || exit 1; \ diff --git a/make/gmsl.mk b/make/gmsl.mk new file mode 100644 index 0000000..e988d2c --- /dev/null +++ b/make/gmsl.mk @@ -0,0 +1,115 @@ +# +# mal (Make Lisp) trimmed and namespaced GMSL functions/definitions +# - derived from the GMSL 1.1.3 +# + +ifndef __mal_gmsl_included +__mal_gmsl_included := true + +# ---------------------------------------------------------------------------- +# +# GNU Make Standard Library (GMSL) +# +# A library of functions to be used with GNU Make's $(call) that +# provides functionality not available in standard GNU Make. +# +# Copyright (c) 2005-2013 John Graham-Cumming +# +# This file is part of GMSL +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither the name of the John Graham-Cumming nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# ---------------------------------------------------------------------------- + + +# Numbers +__gmsl_sixteen := x x x x x x x x x x x x x x x x +__gmsl_input_int := $(foreach a,$(__gmsl_sixteen), \ + $(foreach b,$(__gmsl_sixteen), \ + $(foreach c,$(__gmsl_sixteen), \ + $(__gmsl_sixteen))))) + +int_decode = $(words $1) +int_encode = $(wordlist 1,$1,$(__gmsl_input_int)) + +__gmsl_int_wrap = $(call int_decode,$(call $1,$(call int_encode,$2),$(call int_encode,$3))) + +int_plus = $(strip $1 $2) +int_subtract = $(strip $(if $(call int_gte,$1,$2), \ + $(filter-out xx,$(join $1,$2)), \ + $(warning Subtraction underflow))) +int_multiply = $(strip $(foreach a,$1,$2)) +# _error function must be provided to report/catch division by zero +int_divide = $(strip $(if $2, \ + $(if $(call int_gte,$1,$2), \ + x $(call int_divide,$(call int_subtract,$1,$2),$2),), \ + $(call _error,Division by zero))) + +int_max = $(subst xx,x,$(join $1,$2)) +int_min = $(subst xx,x,$(filter xx,$(join $1,$2))) +int_gt = $(strip $(filter-out $(words $2),$(words $(call int_max,$1,$2)))) +int_gte = $(strip $(call int_gt,$1,$2)$(call int_eq,$1,$2)) +int_lt = $(strip $(filter-out $(words $1),$(words $(call int_max,$1,$2)))) +int_lte = $(strip $(call int_lt,$1,$2)$(call int_eq,$1,$2)) +int_eq = $(strip $(filter $(words $1),$(words $2))) +int_ne = $(strip $(filter-out $(words $1),$(words $2))) + +gmsl_plus = $(call __gmsl_int_wrap,int_plus,$1,$2) +gmsl_subtract = $(call __gmsl_int_wrap,int_subtract,$1,$2) +gmsl_multiply = $(call __gmsl_int_wrap,int_multiply,$1,$2) +gmsl_divide = $(call __gmsl_int_wrap,int_divide,$1,$2) + + +# Strings + +__gmsl_characters := A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +__gmsl_characters += a b c d e f g h i j k l m n o p q r s t u v w x y z +__gmsl_characters += 0 1 2 3 4 5 6 7 8 9 +__gmsl_characters += ` ~ ! @ \# $$ % ^ & * ( ) - _ = + +__gmsl_characters += { } [ ] \ : ; ' " < > , . / ? | +__syntax_highlight_protect = #"'` + + +__gmsl_space := +__gmsl_space += + +gmsl_strlen = $(strip $(eval __temp := $(subst $(__gmsl_space),x,$1)) \ + $(foreach a,$(__gmsl_characters),$(eval __temp := $$(subst $$a,x,$(__temp)))) \ + $(eval __temp := $(subst x,x ,$(__temp))) \ + $(words $(__temp))) + +gmsl_merge = $(strip $(if $2, \ + $(if $(call _EQ,1,$(words $2)), \ + $2,$(firstword $2)$1$(call gmsl_merge,$1,$(wordlist 2,$(words $2),$2))))) + +gmsl_pairmap = $(strip \ + $(if $2$3,$(call $1,$(word 1,$2),$(word 1,$3)) \ + $(call gmsl_pairmap,$1,$(wordlist 2,$(words $2),$2),$(wordlist 2,$(words $3),$3)))) + +endif diff --git a/make/reader.mk b/make/reader.mk new file mode 100755 index 0000000..ce3b078 --- /dev/null +++ b/make/reader.mk @@ -0,0 +1,170 @@ +# +# mal (Make Lisp) Parser/Reader +# + +ifndef __mal_reader_included +__mal_reader_included := true + +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)util.mk +include $(_TOP_DIR)types.mk +include $(_TOP_DIR)readline.mk + +READER_DEBUG ?= + +_TOKEN_DELIMS := $(SEMI) $(COMMA) $(DQUOTE) $(QQUOTE) $(_SP) $(_NL) $(_LC) $(_RC) $(_LP) $(_RP) $(LBRACKET) $(RBRACKET) + +define READ_NUMBER +$(foreach ch,$(word 1,$($(1))),\ + $(if $(ch),\ + $(if $(filter $(_TOKEN_DELIMS),$(ch)),\ + ,\ + $(if $(filter-out $(NUMBERS),$(ch)),\ + $(call _error,Invalid number character '$(ch)'),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(and $(READER_DEBUG),$(info READ_NUMBER ch: $(ch) | $($(1))))\ + $(ch)$(strip $(call READ_NUMBER,$(1))))),\ + )) +endef + +define READ_STRING +$(foreach ch,$(word 1,$($(1))),\ + $(if $(ch),\ + $(if $(and $(filter \,$(ch)),$(filter $(DQUOTE),$(word 2,$($(1))))),\ + $(eval $(1) := $(wordlist 3,$(words $($(1))),$($(1))))\ + $(and $(READER_DEBUG),$(info READ_STRING ch: \$(word 1,$($(1))) | $($(1))))\ + $(DQUOTE) $(strip $(call READ_STRING,$(1))),\ + $(if $(filter $(DQUOTE),$(ch)),\ + ,\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(and $(READER_DEBUG),$(info READ_STRING ch: $(ch) | $($(1))))\ + $(ch) $(strip $(call READ_STRING,$(1))))),)) +endef + +define READ_SYMBOL +$(foreach ch,$(word 1,$($(1))),\ + $(if $(ch),\ + $(if $(filter $(_TOKEN_DELIMS),$(ch)),\ + ,\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(and $(READER_DEBUG),$(info READ_SYMBOL ch: $(ch) | $($(1))))\ + $(ch)$(strip $(call READ_SYMBOL,$(1)))),\ + )) +endef + +define READ_ATOM +$(foreach ch,$(word 1,$($(1))),\ + $(if $(filter $(NUMBERS),$(ch)),\ + $(call number,$(call READ_NUMBER,$(1))),\ + $(if $(filter $(DQUOTE),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(call _string,$(strip $(call READ_STRING,$(1))))\ + $(eval $(if $(filter $(DQUOTE),$(word 1,$($(1)))),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1)))),\ + $(call _error,Expected '$(DQUOTE)' in; $($(1))))),\ + $(foreach sym,$(call READ_SYMBOL,$(1)),\ + $(if $(call _EQ,nil,$(sym)),\ + $(__nil),\ + $(if $(call _EQ,true,$(sym)),\ + $(__true),\ + $(if $(call _EQ,false,$(sym)),\ + $(__false),\ + $(call symbol,$(sym))))))))) +endef + +# read and return tokens until $(2) found +define READ_UNTIL +$(and $(READER_DEBUG),$(info READ_UNTIL: $($(1)) [$(2) $(3)])) +$(foreach ch,$(word 1,$($(1))),\ + $(if $(ch),\ + $(if $(filter $(2),$(ch)),\ + ,\ + $(call READ_FORM,$(1))\ + $(call READ_UNTIL,$(1),$(2),$(3))),\ + $(call _error,Expected '$(3)'))) +endef + +define DROP_UNTIL +$(and $(READER_DEBUG),$(info DROP_UNTIL: $($(1)) [$(2)])) +$(foreach ch,$(word 1,$($(1))),\ + $(if $(ch),\ + $(if $(filter $(2),$(ch)),\ + ,\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(call DROP_UNTIL,$(1),$(2),$(3))),\ + )) +endef + +define READ_SPACES +$(and $(READER_DEBUG),$(info READ_SPACES: $($(1)))) +$(foreach ch,$(word 1,$($(1))),\ + $(if $(filter $(_SP) $(_NL) $(COMMA),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(call READ_SPACES,$(1)),)) +endef + +define READ_FORM +$(and $(READER_DEBUG),$(info READ_FORM: $($(1)))) +$(call READ_SPACES,$(1)) +$(foreach ch,$(word 1,$($(1))),\ + $(if $(filter $(SEMI),$(ch)),\ + $(call DROP_UNTIL,$(1),$(_NL)),\ + $(if $(filter $(SQUOTE),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(call list,$(call symbol,quote) $(strip $(call READ_FORM,$(1)))),\ + $(if $(filter $(QQUOTE),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(call list,$(call symbol,quasiquote) $(strip $(call READ_FORM,$(1)))),\ + $(if $(filter $(UNQUOTE),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(call list,$(call symbol,unquote) $(strip $(call READ_FORM,$(1)))),\ + $(if $(filter $(_SUQ),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(call list,$(call symbol,splice-unquote) $(strip $(call READ_FORM,$(1)))),\ + $(if $(filter $(CARET),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(foreach meta,$(strip $(call READ_FORM,$(1))),\ + $(call list,$(call symbol,with-meta) $(strip $(call READ_FORM,$(1))) $(meta))),\ + $(if $(filter $(ATSIGN),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(call list,$(call symbol,deref) $(strip $(call READ_FORM,$(1)))),\ + $(if $(filter $(_RC),$(ch)),\ + $(call _error,Unexpected '$(RCURLY)'),\ + $(if $(filter $(_LC),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(foreach thm,$(call hash_map),\ + $(call do,$(call _assoc_seq!,$(thm),$(strip $(call READ_UNTIL,$(1),$(_RC),$(RCURLY)))))\ + $(eval $(if $(filter $(_RC),$(word 1,$($(1)))),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1)))),\ + $(call _error,Expected '$(RCURLY)')))\ + $(thm)),\ + $(if $(filter $(_RP),$(ch)),\ + $(call _error,Unexpected '$(RPAREN)'),\ + $(if $(filter $(_LP),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(foreach tlist,$(call _list),\ + $(eval $(foreach item,$(strip $(call READ_UNTIL,$(1),$(_RP),$(RPAREN))),\ + $(call do,$(call _conj!,$(tlist),$(item)))))\ + $(eval $(if $(filter $(_RP),$(word 1,$($(1)))),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1)))),\ + $(call _error,Expected '$(RPAREN)')))\ + $(tlist)),\ + $(if $(filter $(RBRACKET),$(ch)),\ + $(call _error,Unexpected '$(RBRACKET)'),\ + $(if $(filter $(LBRACKET),$(ch)),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\ + $(foreach tvec,$(call _vector),\ + $(eval $(foreach item,$(strip $(call READ_UNTIL,$(1),$(RBRACKET),$(RBRACKET))),\ + $(call do,$(call _conj!,$(tvec),$(item)))))\ + $(eval $(if $(filter $(RBRACKET),$(word 1,$($(1)))),\ + $(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1)))),\ + $(call _error,Expected '$(RBRACKET)')))\ + $(tvec)),\ + $(call READ_ATOM,$(1)))))))))))))))) +$(call READ_SPACES,$(1)) +endef + +# read-str from a raw "string" or from a string object +READ_STR = $(strip $(eval __reader_temp := $(call str_encode,$(if $(call _string?,$(1)),$(call str_decode,$($(1)_value)),$(1))))$(call READ_FORM,__reader_temp)) + +endif diff --git a/make/readline.mk b/make/readline.mk new file mode 100644 index 0000000..1208f5c --- /dev/null +++ b/make/readline.mk @@ -0,0 +1,15 @@ +# +# mal (Make Lisp) shell readline wrapper +# + +ifndef __mal_readline_included +__mal_readline_included := true + +# Call bash read/readline. Since each call is in a separate shell +# instance we need to restore and save after each call in order to +# have readline history. +READLINE_EOF := +READLINE_HISTORY_FILE := $${HOME}/.mal-history +READLINE = $(eval __readline_temp := $(shell history -r $(READLINE_HISTORY_FILE); read -u 0 -r -e -p $(if $(1),$(1),"user> ") line && history -s -- "$${line}" && history -a $(READLINE_HISTORY_FILE) && echo "$${line}" || echo "__||EOF||__"))$(if $(filter __||EOF||__,$(__readline_temp)),$(eval READLINE_EOF := yes),$(__readline_temp)) + +endif diff --git a/make/step0_repl.mk b/make/step0_repl.mk new file mode 100644 index 0000000..b8b1309 --- /dev/null +++ b/make/step0_repl.mk @@ -0,0 +1,26 @@ +# +# mal (Make Lisp) +# +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)readline.mk + +SHELL := /bin/bash + +define READ +$(call READLINE) +endef + +define EVAL +$(if $(READLINE_EOF),,\ + $(if $(findstring =,$(1)),$(eval $(1))$($(word 1,$(1))),$(eval __return := $(1))$(__return))) +endef + +define PRINT +$(1) +endef + +REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ))))) +REPL = $(info $(call REP))$(if $(READLINE_EOF),,$(call REPL)) + +# Call the read-eval-print loop +$(call REPL) diff --git a/make/step1_read_print.mk b/make/step1_read_print.mk new file mode 100644 index 0000000..710cd1d --- /dev/null +++ b/make/step1_read_print.mk @@ -0,0 +1,31 @@ +# +# mal (Make Lisp) +# +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)types.mk +include $(_TOP_DIR)reader.mk + +SHELL := /bin/bash +INTERACTIVE ?= yes + +# READ: read and parse input +define READ +$(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE,"user> ")))) +endef + +# EVAL: just return the input +define EVAL +$(if $(READLINE_EOF)$(__ERROR),,$(1)) +endef + +# PRINT: +define PRINT +$(if $(__ERROR),Error: $(call _pr_str,$(__ERROR),yes),$(if $(1),$(call _pr_str,$(1),yes)))$(if $(__ERROR),$(eval __ERROR :=),) +endef + +# REPL: read, eval, print, loop +REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) +REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) + +# Call the read-eval-print loop +$(if $(strip $(INTERACTIVE)),$(call REPL)) diff --git a/make/step2_eval.mk b/make/step2_eval.mk new file mode 100644 index 0000000..62cd415 --- /dev/null +++ b/make/step2_eval.mk @@ -0,0 +1,71 @@ +# +# mal (Make Lisp) +# +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)types.mk +include $(_TOP_DIR)reader.mk + +SHELL := /bin/bash +INTERACTIVE ?= yes +EVAL_DEBUG ?= + +# READ: read and parse input +define READ +$(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE,"user> ")))) +endef + +# EVAL: evaluate the parameter +define EVAL_AST +$(strip \ + $(and $(EVAL_DEBUG),$(info EVAL_AST: $(call _pr_str,$(1))))\ + $(if $(call _symbol?,$(1)),\ + $(foreach key,$($(1)_value),\ + $(if $(call _contains?,$(2),$(key)),\ + $(call _get,$(2),$(key)),\ + $(call _error,'$(key)' not found in REPL_ENV ($(2))))),\ + $(if $(call _list?,$(1)),\ + $(call _smap,EVAL,$(1),$(2)),\ + $(if $(call _vector?,$(1)),\ + $(call _smap_vec,EVAL,$(1),$(2)),\ + $(if $(call _hash_map?,$(1)),\ + $(foreach new_hmap,$(call __new_obj,hmap),\ + $(foreach v,$(call __get_obj_values,$(1)),\ + $(eval $(v:$(1)_%=$(new_hmap)_%) := $(call EVAL,$($(v)),$(2))))\ + $(eval $(new_hmap)_size := $($(1)_size))\ + $(new_hmap)),\ + $(1)))))) +endef + +define EVAL_INVOKE +$(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL_INVOKE: $(call _pr_str,$(1))))\ + $(foreach el,$(call EVAL_AST,$(1),$(2)),\ + $(call _apply,$(call sfirst,$(el)),$(call srest,$(el))))) +endef + +define EVAL +$(strip $(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL: $(call _pr_str,$(1))))\ + $(if $(call _list?,$(1)),\ + $(strip $(call EVAL_INVOKE,$(1),$(2))),\ + $(call EVAL_AST,$(1),$(2))))) +endef + + +# PRINT: +define PRINT +$(if $(__ERROR),Error: $(call _pr_str,$(__ERROR),yes),$(if $(1),$(call _pr_str,$(1),yes)))$(if $(__ERROR),$(eval __ERROR :=),) +endef + +# REPL: +REPL_ENV := $(call _hash_map) +REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) +REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) + +$(call do,$(call _assoc!,$(REPL_ENV),+,number_plus)) +$(call do,$(call _assoc!,$(REPL_ENV),-,number_subtract)) +$(call do,$(call _assoc!,$(REPL_ENV),*,number_multiply)) +$(call do,$(call _assoc!,$(REPL_ENV),/,number_divide)) + +# Call the read-eval-print loop +$(if $(strip $(INTERACTIVE)),$(call REPL)) diff --git a/make/step3_env.mk b/make/step3_env.mk new file mode 100644 index 0000000..4f3f070 --- /dev/null +++ b/make/step3_env.mk @@ -0,0 +1,93 @@ +# +# mal (Make Lisp) +# +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)types.mk +include $(_TOP_DIR)reader.mk + +SHELL := /bin/bash +INTERACTIVE ?= yes +EVAL_DEBUG ?= + +# READ: read and parse input +define READ +$(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE,"user> ")))) +endef + +# EVAL: evaluate the parameter +define LET +$(strip \ + $(word 1,$(2) \ + $(foreach var,$(call _nth,$(1),0),\ + $(foreach val,$(call _nth,$(1),1),\ + $(call ENV_SET,$(2),$($(var)_value),$(call EVAL,$(val),$(2)))\ + $(foreach left,$(call srest,$(call srest,$(1))), + $(if $(call _EQ,0,$(call _count,$(left))),\ + ,\ + $(call LET,$(left),$(2)))))))) +endef + +define EVAL_AST +$(strip \ + $(and $(EVAL_DEBUG),$(info EVAL_AST: $(call _pr_str,$(1))))\ + $(if $(call _symbol?,$(1)),\ + $(foreach key,$($(1)_value),\ + $(call ENV_GET,$(2),$(key))),\ + $(if $(call _list?,$(1)),\ + $(call _smap,EVAL,$(1),$(2)),\ + $(if $(call _vector?,$(1)),\ + $(call _smap_vec,EVAL,$(1),$(2)),\ + $(if $(call _hash_map?,$(1)),\ + $(foreach new_hmap,$(call __new_obj,hmap),\ + $(foreach v,$(call __get_obj_values,$(1)),\ + $(eval $(v:$(1)_%=$(new_hmap)_%) := $(call EVAL,$($(v)),$(2))))\ + $(eval $(new_hmap)_size := $($(1)_size))\ + $(new_hmap)),\ + $(1)))))) +endef + +define EVAL_INVOKE +$(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL_INVOKE: $(call _pr_str,$(1)))) + $(foreach a0,$(call _nth,$(1),0),\ + $(if $(call _EQ,def!,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach res,$(call EVAL,$(a2),$(2)),\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(call _EQ,let*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call EVAL,$(a2),$(call LET,$(a1),$(call ENV,$(2)))))),\ + $(foreach el,$(call EVAL_AST,$(1),$(2)),\ + $(call _apply,$(call sfirst,$(el)),$(call srest,$(el)))))))) +endef + +define EVAL +$(strip $(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL: $(call _pr_str,$(1))))\ + $(if $(call _list?,$(1)),\ + $(strip $(call EVAL_INVOKE,$(1),$(2))),\ + $(call EVAL_AST,$(1),$(2))))) +endef + + +# PRINT: +define PRINT +$(if $(__ERROR),Error: $(call _pr_str,$(__ERROR),yes),$(if $(1),$(call _pr_str,$(1),yes)))$(if $(__ERROR),$(eval __ERROR :=),) +endef + +# REPL: +REPL_ENV := $(call ENV) +REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) +REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) + +# Setup the environment +_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) +$(call _ref,+,number_plus) +$(call _ref,-,number_subtract) +$(call _ref,*,number_multiply) +$(call _ref,/,number_divide) + +# Call the read-eval-print loop +$(if $(strip $(INTERACTIVE)),$(call REPL)) diff --git a/make/step4_if_fn_do.mk b/make/step4_if_fn_do.mk new file mode 100644 index 0000000..d08998d --- /dev/null +++ b/make/step4_if_fn_do.mk @@ -0,0 +1,112 @@ +# +# mal (Make Lisp) +# +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)types.mk +include $(_TOP_DIR)reader.mk + +SHELL := /bin/bash +INTERACTIVE ?= yes +EVAL_DEBUG ?= + +# READ: read and parse input +define READ +$(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE,"user> ")))) +endef + +# EVAL: evaluate the parameter +define LET +$(strip \ + $(word 1,$(2) \ + $(foreach var,$(call _nth,$(1),0),\ + $(foreach val,$(call _nth,$(1),1),\ + $(call ENV_SET,$(2),$($(var)_value),$(call EVAL,$(val),$(2)))\ + $(foreach left,$(call srest,$(call srest,$(1))), + $(if $(call _EQ,0,$(call _count,$(left))),\ + ,\ + $(call LET,$(left),$(2)))))))) +endef + +define EVAL_AST +$(strip \ + $(and $(EVAL_DEBUG),$(info EVAL_AST: $(call _pr_str,$(1))))\ + $(if $(call _symbol?,$(1)),\ + $(foreach key,$($(1)_value),\ + $(call ENV_GET,$(2),$(key))),\ + $(if $(call _list?,$(1)),\ + $(call _smap,EVAL,$(1),$(2)),\ + $(if $(call _vector?,$(1)),\ + $(call _smap_vec,EVAL,$(1),$(2)),\ + $(if $(call _hash_map?,$(1)),\ + $(foreach new_hmap,$(call __new_obj,hmap),\ + $(foreach v,$(call __get_obj_values,$(1)),\ + $(eval $(v:$(1)_%=$(new_hmap)_%) := $(call EVAL,$($(v)),$(2))))\ + $(eval $(new_hmap)_size := $($(1)_size))\ + $(new_hmap)),\ + $(1)))))) +endef + +define EVAL_INVOKE +$(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL_INVOKE: $(call _pr_str,$(1)))) + $(foreach a0,$(call _nth,$(1),0),\ + $(if $(call _EQ,def!,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach res,$(call EVAL,$(a2),$(2)),\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(call _EQ,let*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call EVAL,$(a2),$(call LET,$(a1),$(call ENV,$(2)))))),\ + $(if $(call _EQ,do,$($(a0)_value)),\ + $(call slast,$(call EVAL_AST,$(call srest,$(1)),$(2))),\ + $(if $(call _EQ,if,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach cond,$(call EVAL,$(a1),$(2)),\ + $(if $(or $(call _EQ,$(__nil),$(cond)),$(call _EQ,$(__false),$(cond))),\ + $(foreach a3,$(call _nth,$(1),3),$(call EVAL,$(a3),$(2))),\ + $(call EVAL,$(a2),$(2)))))),\ + $(if $(call _EQ,fn*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\ + $(foreach el,$(call EVAL_AST,$(1),$(2)),\ + $(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\ + $(foreach f,$(call sfirst,$(el)),\ + $(foreach args,$(call srest,$(el)),\ + $(call apply,$(f),$(args)))))))))))) +endef + +define EVAL +$(strip $(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL: $(call _pr_str,$(1))))\ + $(if $(call _list?,$(1)),\ + $(word 1,$(strip $(call EVAL_INVOKE,$(1),$(2)) $(__nil))),\ + $(call EVAL_AST,$(1),$(2))))) +endef + + +# PRINT: +define PRINT +$(if $(__ERROR),Error: $(call _pr_str,$(__ERROR),yes),$(if $(1),$(call _pr_str,$(1),yes)))$(if $(__ERROR),$(eval __ERROR :=),) +endef + +# REPL: +REPL_ENV := $(call ENV) +REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) +REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) + +# Setup the environment +_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1)))) + +# Import types functions +_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),) +$(call _import_types,$(types_ns)) + +# Defined in terms of the language itself +$(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) + +# Call the read-eval-print loop +$(if $(strip $(INTERACTIVE)),$(call REPL)) diff --git a/make/step6_file.mk b/make/step6_file.mk new file mode 100644 index 0000000..da6de04 --- /dev/null +++ b/make/step6_file.mk @@ -0,0 +1,130 @@ +# +# mal (Make Lisp) +# +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)types.mk +include $(_TOP_DIR)reader.mk + +SHELL := /bin/bash +INTERACTIVE ?= yes +EVAL_DEBUG ?= + +# READ: read and parse input +define READ +$(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE,"user> ")))) +endef + +# EVAL: evaluate the parameter +define LET +$(strip \ + $(word 1,$(2) \ + $(foreach var,$(call _nth,$(1),0),\ + $(foreach val,$(call _nth,$(1),1),\ + $(call ENV_SET,$(2),$($(var)_value),$(call EVAL,$(val),$(2)))\ + $(foreach left,$(call srest,$(call srest,$(1))), + $(if $(call _EQ,0,$(call _count,$(left))),\ + ,\ + $(call LET,$(left),$(2)))))))) +endef + +define EVAL_AST +$(strip \ + $(and $(EVAL_DEBUG),$(info EVAL_AST: $(call _pr_str,$(1))))\ + $(if $(call _symbol?,$(1)),\ + $(foreach key,$($(1)_value),\ + $(call ENV_GET,$(2),$(key))),\ + $(if $(call _list?,$(1)),\ + $(call _smap,EVAL,$(1),$(2)),\ + $(if $(call _vector?,$(1)),\ + $(call _smap_vec,EVAL,$(1),$(2)),\ + $(if $(call _hash_map?,$(1)),\ + $(foreach new_hmap,$(call __new_obj,hmap),\ + $(foreach v,$(call __get_obj_values,$(1)),\ + $(eval $(v:$(1)_%=$(new_hmap)_%) := $(call EVAL,$($(v)),$(2))))\ + $(eval $(new_hmap)_size := $($(1)_size))\ + $(new_hmap)),\ + $(1)))))) +endef + +define EVAL_INVOKE +$(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL_INVOKE: $(call _pr_str,$(1)))) + $(foreach a0,$(call _nth,$(1),0),\ + $(if $(call _EQ,def!,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach res,$(call EVAL,$(a2),$(2)),\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(call _EQ,let*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call EVAL,$(a2),$(call LET,$(a1),$(call ENV,$(2)))))),\ + $(if $(call _EQ,do,$($(a0)_value)),\ + $(call slast,$(call EVAL_AST,$(call srest,$(1)),$(2))),\ + $(if $(call _EQ,if,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach cond,$(call EVAL,$(a1),$(2)),\ + $(if $(or $(call _EQ,$(__nil),$(cond)),$(call _EQ,$(__false),$(cond))),\ + $(foreach a3,$(call _nth,$(1),3),$(call EVAL,$(a3),$(2))),\ + $(call EVAL,$(a2),$(2)))))),\ + $(if $(call _EQ,fn*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\ + $(foreach el,$(call EVAL_AST,$(1),$(2)),\ + $(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\ + $(foreach f,$(call sfirst,$(el)),\ + $(foreach args,$(call srest,$(el)),\ + $(call apply,$(f),$(args)))))))))))) +endef + +define EVAL +$(strip $(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL: $(call _pr_str,$(1))))\ + $(if $(call _list?,$(1)),\ + $(word 1,$(strip $(call EVAL_INVOKE,$(1),$(2)) $(__nil))),\ + $(call EVAL_AST,$(1),$(2))))) +endef + + +# PRINT: +define PRINT +$(if $(__ERROR),Error: $(call _pr_str,$(__ERROR),yes),$(if $(1),$(call _pr_str,$(1),yes)))$(if $(__ERROR),$(eval __ERROR :=),) +endef + +# REPL: +REPL_ENV := $(call ENV) +REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) +REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) + +# Setup the environment +_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) +_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1)))) + +# Import types functions +_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),) +$(call _import_types,$(types_ns)) + +$(call _ref,read-string,$(call function,$$(call READ_STR,$$(1)))) +$(call _ref,eval,$(call function,$$(call EVAL,$$(1),$$(REPL_ENV)))) + +_slurp = $(call string,$(call _read_file,$(1))) +_slurp_do = $(call string,(do $(call _read_file,$(1)))) +$(call _ref,slurp,$(call function,$$(call _slurp,$$(call str_decode,$$($$(1)_value))))) +$(call _ref,slurp-do,$(call function,$$(call _slurp_do,$$(call str_decode,$$($$(1)_value))))) + +# Defined in terms of the language itself +$(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) +$(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (slurp-do f))))) )) + +# Load and eval any files specified on the command line +$(if $(MAKECMDGOALS),\ + $(foreach file,$(MAKECMDGOALS),$(call do,$(call REP, (load-file "$(file)") )))\ + $(eval INTERACTIVE :=),) +.PHONY: none $(MAKECMDGOALS) +none $(MAKECMDGOALS): + @true + +# Call the read-eval-print loop +$(if $(strip $(INTERACTIVE)),$(call REPL)) diff --git a/make/step7_quote.mk b/make/step7_quote.mk new file mode 100644 index 0000000..a8695da --- /dev/null +++ b/make/step7_quote.mk @@ -0,0 +1,147 @@ +# +# mal (Make Lisp) +# +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)types.mk +include $(_TOP_DIR)reader.mk + +SHELL := /bin/bash +INTERACTIVE ?= yes +EVAL_DEBUG ?= + +# READ: read and parse input +define READ +$(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE,"user> ")))) +endef + +# EVAL: evaluate the parameter +IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call _count,$(1))),,true),) + +define QUASIQUOTE +$(strip \ + $(if $(call _NOT,$(call IS_PAIR,$(1))),\ + $(call list,$(call symbol,quote) $(1)),\ + $(if $(call _EQ,unquote,$($(call _nth,$(1),0)_value)),\ + $(call _nth,$(1),1),\ + $(if $(and $(call IS_PAIR,$(call _nth,$(1),0)),$(call _EQ,splice-unquote,$($(call _nth,$(call _nth,$(1),0),0)_value))),\ + $(call list,$(call symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\ + $(call list,$(call symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1)))))))) +endef + +define LET +$(strip \ + $(word 1,$(2) \ + $(foreach var,$(call _nth,$(1),0),\ + $(foreach val,$(call _nth,$(1),1),\ + $(call ENV_SET,$(2),$($(var)_value),$(call EVAL,$(val),$(2)))\ + $(foreach left,$(call srest,$(call srest,$(1))), + $(if $(call _EQ,0,$(call _count,$(left))),\ + ,\ + $(call LET,$(left),$(2)))))))) +endef + +define EVAL_AST +$(strip \ + $(and $(EVAL_DEBUG),$(info EVAL_AST: $(call _pr_str,$(1))))\ + $(if $(call _symbol?,$(1)),\ + $(foreach key,$($(1)_value),\ + $(call ENV_GET,$(2),$(key))),\ + $(if $(call _list?,$(1)),\ + $(call _smap,EVAL,$(1),$(2)),\ + $(if $(call _vector?,$(1)),\ + $(call _smap_vec,EVAL,$(1),$(2)),\ + $(if $(call _hash_map?,$(1)),\ + $(foreach new_hmap,$(call __new_obj,hmap),\ + $(foreach v,$(call __get_obj_values,$(1)),\ + $(eval $(v:$(1)_%=$(new_hmap)_%) := $(call EVAL,$($(v)),$(2))))\ + $(eval $(new_hmap)_size := $($(1)_size))\ + $(new_hmap)),\ + $(1)))))) +endef + +define EVAL_INVOKE +$(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL_INVOKE: $(call _pr_str,$(1)))) + $(foreach a0,$(call _nth,$(1),0),\ + $(if $(call _EQ,def!,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach res,$(call EVAL,$(a2),$(2)),\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(call _EQ,let*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call EVAL,$(a2),$(call LET,$(a1),$(call ENV,$(2)))))),\ + $(if $(call _EQ,quote,$($(a0)_value)),\ + $(call _nth,$(1),1),\ + $(if $(call _EQ,quasiquote,$($(a0)_value)),\ + $(call EVAL,$(call QUASIQUOTE,$(call _nth,$(1),1)),$(2)),\ + $(if $(call _EQ,do,$($(a0)_value)),\ + $(call slast,$(call EVAL_AST,$(call srest,$(1)),$(2))),\ + $(if $(call _EQ,if,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach cond,$(call EVAL,$(a1),$(2)),\ + $(if $(or $(call _EQ,$(__nil),$(cond)),$(call _EQ,$(__false),$(cond))),\ + $(foreach a3,$(call _nth,$(1),3),$(call EVAL,$(a3),$(2))),\ + $(call EVAL,$(a2),$(2)))))),\ + $(if $(call _EQ,fn*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\ + $(foreach el,$(call EVAL_AST,$(1),$(2)),\ + $(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\ + $(foreach f,$(call sfirst,$(el)),\ + $(foreach args,$(call srest,$(el)),\ + $(call apply,$(f),$(args)))))))))))))) +endef + +define EVAL +$(strip $(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL: $(call _pr_str,$(1))))\ + $(if $(call _list?,$(1)),\ + $(word 1,$(strip $(call EVAL_INVOKE,$(1),$(2)) $(__nil))),\ + $(call EVAL_AST,$(1),$(2))))) +endef + + +# PRINT: +define PRINT +$(if $(__ERROR),Error: $(call _pr_str,$(__ERROR),yes),$(if $(1),$(call _pr_str,$(1),yes)))$(if $(__ERROR),$(eval __ERROR :=),) +endef + +# REPL: +REPL_ENV := $(call ENV) +REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) +REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) + +# Setup the environment +_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) +_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1)))) + +# Import types functions +_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),) +$(call _import_types,$(types_ns)) + +$(call _ref,read-string,$(call function,$$(call READ_STR,$$(1)))) +$(call _ref,eval,$(call function,$$(call EVAL,$$(1),$$(REPL_ENV)))) + +_slurp = $(call string,$(call _read_file,$(1))) +_slurp_do = $(call string,(do $(call _read_file,$(1)))) +$(call _ref,slurp,$(call function,$$(call _slurp,$$(call str_decode,$$($$(1)_value))))) +$(call _ref,slurp-do,$(call function,$$(call _slurp_do,$$(call str_decode,$$($$(1)_value))))) + +# Defined in terms of the language itself +$(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) +$(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (slurp-do f))))) )) + +# Load and eval any files specified on the command line +$(if $(MAKECMDGOALS),\ + $(foreach file,$(MAKECMDGOALS),$(call do,$(call REP, (load-file "$(file)") )))\ + $(eval INTERACTIVE :=),) +.PHONY: none $(MAKECMDGOALS) +none $(MAKECMDGOALS): + @true + +# Call the read-eval-print loop +$(if $(strip $(INTERACTIVE)),$(call REPL)) diff --git a/make/step8_macros.mk b/make/step8_macros.mk new file mode 100644 index 0000000..2b4e33b --- /dev/null +++ b/make/step8_macros.mk @@ -0,0 +1,170 @@ +# +# mal (Make Lisp) +# +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)types.mk +include $(_TOP_DIR)reader.mk + +SHELL := /bin/bash +INTERACTIVE ?= yes +EVAL_DEBUG ?= + +# READ: read and parse input +define READ +$(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE,"user> ")))) +endef + +# EVAL: evaluate the parameter +IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call _count,$(1))),,true),) + +define QUASIQUOTE +$(strip \ + $(if $(call _NOT,$(call IS_PAIR,$(1))),\ + $(call list,$(call symbol,quote) $(1)),\ + $(if $(call _EQ,unquote,$($(call _nth,$(1),0)_value)),\ + $(call _nth,$(1),1),\ + $(if $(and $(call IS_PAIR,$(call _nth,$(1),0)),$(call _EQ,splice-unquote,$($(call _nth,$(call _nth,$(1),0),0)_value))),\ + $(call list,$(call symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\ + $(call list,$(call symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1)))))))) +endef + +define IS_MACRO_CALL +$(if $(call _list?,$(1)),$(call ENV_FIND,$(2),_macro_$($(call _nth,$(1),0)_value)),) +endef + +define MACROEXPAND +$(strip $(if $(__ERROR),,\ + $(if $(call IS_MACRO_CALL,$(1),$(2)),\ + $(foreach mac,$(call ENV_GET,$(2),$($(call _nth,$(1),0)_value)),\ + $(call MACROEXPAND,$(call apply,$(mac),$(call srest,$(1))),$(2))),\ + $(1)))) +endef + +define LET +$(strip \ + $(word 1,$(2) \ + $(foreach var,$(call _nth,$(1),0),\ + $(foreach val,$(call _nth,$(1),1),\ + $(call ENV_SET,$(2),$($(var)_value),$(call EVAL,$(val),$(2)))\ + $(foreach left,$(call srest,$(call srest,$(1))), + $(if $(call _EQ,0,$(call _count,$(left))),\ + ,\ + $(call LET,$(left),$(2)))))))) +endef + +define EVAL_AST +$(strip \ + $(and $(EVAL_DEBUG),$(info EVAL_AST: $(call _pr_str,$(1))))\ + $(if $(call _symbol?,$(1)),\ + $(foreach key,$($(1)_value),\ + $(call ENV_GET,$(2),$(key))),\ + $(if $(call _list?,$(1)),\ + $(call _smap,EVAL,$(1),$(2)),\ + $(if $(call _vector?,$(1)),\ + $(call _smap_vec,EVAL,$(1),$(2)),\ + $(if $(call _hash_map?,$(1)),\ + $(foreach new_hmap,$(call __new_obj,hmap),\ + $(foreach v,$(call __get_obj_values,$(1)),\ + $(eval $(v:$(1)_%=$(new_hmap)_%) := $(call EVAL,$($(v)),$(2))))\ + $(eval $(new_hmap)_size := $($(1)_size))\ + $(new_hmap)),\ + $(1)))))) +endef + +define EVAL_INVOKE +$(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL_INVOKE: $(call _pr_str,$(1)))) + $(foreach a0,$(call _nth,$(1),0),\ + $(if $(call _EQ,def!,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach res,$(call EVAL,$(a2),$(2)),\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(call _EQ,let*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call EVAL,$(a2),$(call LET,$(a1),$(call ENV,$(2)))))),\ + $(if $(call _EQ,quote,$($(a0)_value)),\ + $(call _nth,$(1),1),\ + $(if $(call _EQ,quasiquote,$($(a0)_value)),\ + $(call EVAL,$(call QUASIQUOTE,$(call _nth,$(1),1)),$(2)),\ + $(if $(call _EQ,defmacro!,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach res,$(call EVAL,$(a2),$(2)),\ + $(if $(call ENV_SET,$(2),_macro_$($(a1)_value),true),,)\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(call _EQ,macroexpand,$($(a0)_value)),\ + $(call MACROEXPAND,$(call _nth,$(1),1),$(2)),\ + $(if $(call _EQ,do,$($(a0)_value)),\ + $(call slast,$(call EVAL_AST,$(call srest,$(1)),$(2))),\ + $(if $(call _EQ,if,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach cond,$(call EVAL,$(a1),$(2)),\ + $(if $(or $(call _EQ,$(__nil),$(cond)),$(call _EQ,$(__false),$(cond))),\ + $(foreach a3,$(call _nth,$(1),3),$(call EVAL,$(a3),$(2))),\ + $(call EVAL,$(a2),$(2)))))),\ + $(if $(call _EQ,fn*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\ + $(foreach el,$(call EVAL_AST,$(1),$(2)),\ + $(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\ + $(foreach f,$(call sfirst,$(el)),\ + $(foreach args,$(call srest,$(el)),\ + $(call apply,$(f),$(args)))))))))))))))) +endef + +define EVAL +$(strip $(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL: $(call _pr_str,$(1))))\ + $(if $(call _list?,$(1)),\ + $(foreach ast,$(call MACROEXPAND,$(1),$(2)), + $(if $(call _list?,$(ast)),\ + $(word 1,$(strip $(call EVAL_INVOKE,$(ast),$(2)) $(__nil))),\ + $(ast))),\ + $(call EVAL_AST,$(1),$(2))))) +endef + + +# PRINT: +define PRINT +$(if $(__ERROR),Error: $(call _pr_str,$(__ERROR),yes),$(if $(1),$(call _pr_str,$(1),yes)))$(if $(__ERROR),$(eval __ERROR :=),) +endef + +# REPL: +REPL_ENV := $(call ENV) +REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) +REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) + +# Setup the environment +_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) +_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1)))) + +# Import types functions +_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),) +$(call _import_types,$(types_ns)) + +$(call _ref,read-string,$(call function,$$(call READ_STR,$$(1)))) +$(call _ref,eval,$(call function,$$(call EVAL,$$(1),$$(REPL_ENV)))) + +_slurp = $(call string,$(call _read_file,$(1))) +_slurp_do = $(call string,(do $(call _read_file,$(1)))) +$(call _ref,slurp,$(call function,$$(call _slurp,$$(call str_decode,$$($$(1)_value))))) +$(call _ref,slurp-do,$(call function,$$(call _slurp_do,$$(call str_decode,$$($$(1)_value))))) + +# Defined in terms of the language itself +$(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) +$(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (slurp-do f))))) )) + +# Load and eval any files specified on the command line +$(if $(MAKECMDGOALS),\ + $(foreach file,$(MAKECMDGOALS),$(call do,$(call REP, (load-file "$(file)") )))\ + $(eval INTERACTIVE :=),) +.PHONY: none $(MAKECMDGOALS) +none $(MAKECMDGOALS): + @true + +# Call the read-eval-print loop +$(if $(strip $(INTERACTIVE)),$(call REPL)) diff --git a/make/step9_interop.mk b/make/step9_interop.mk new file mode 100644 index 0000000..a3d2b5e --- /dev/null +++ b/make/step9_interop.mk @@ -0,0 +1,174 @@ +# +# mal (Make Lisp) +# +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)types.mk +include $(_TOP_DIR)reader.mk + +SHELL := /bin/bash +INTERACTIVE ?= yes +EVAL_DEBUG ?= + +# READ: read and parse input +define READ +$(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE,"user> ")))) +endef + +# EVAL: evaluate the parameter +IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call _count,$(1))),,true),) + +define QUASIQUOTE +$(strip \ + $(if $(call _NOT,$(call IS_PAIR,$(1))),\ + $(call list,$(call symbol,quote) $(1)),\ + $(if $(call _EQ,unquote,$($(call _nth,$(1),0)_value)),\ + $(call _nth,$(1),1),\ + $(if $(and $(call IS_PAIR,$(call _nth,$(1),0)),$(call _EQ,splice-unquote,$($(call _nth,$(call _nth,$(1),0),0)_value))),\ + $(call list,$(call symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\ + $(call list,$(call symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1)))))))) +endef + +define IS_MACRO_CALL +$(if $(call _list?,$(1)),$(call ENV_FIND,$(2),_macro_$($(call _nth,$(1),0)_value)),) +endef + +define MACROEXPAND +$(strip $(if $(__ERROR),,\ + $(if $(call IS_MACRO_CALL,$(1),$(2)),\ + $(foreach mac,$(call ENV_GET,$(2),$($(call _nth,$(1),0)_value)),\ + $(call MACROEXPAND,$(call apply,$(mac),$(call srest,$(1))),$(2))),\ + $(1)))) +endef + +define LET +$(strip \ + $(word 1,$(2) \ + $(foreach var,$(call _nth,$(1),0),\ + $(foreach val,$(call _nth,$(1),1),\ + $(call ENV_SET,$(2),$($(var)_value),$(call EVAL,$(val),$(2)))\ + $(foreach left,$(call srest,$(call srest,$(1))), + $(if $(call _EQ,0,$(call _count,$(left))),\ + ,\ + $(call LET,$(left),$(2)))))))) +endef + +define EVAL_AST +$(strip \ + $(and $(EVAL_DEBUG),$(info EVAL_AST: $(call _pr_str,$(1))))\ + $(if $(call _symbol?,$(1)),\ + $(foreach key,$($(1)_value),\ + $(call ENV_GET,$(2),$(key))),\ + $(if $(call _list?,$(1)),\ + $(call _smap,EVAL,$(1),$(2)),\ + $(if $(call _vector?,$(1)),\ + $(call _smap_vec,EVAL,$(1),$(2)),\ + $(if $(call _hash_map?,$(1)),\ + $(foreach new_hmap,$(call __new_obj,hmap),\ + $(foreach v,$(call __get_obj_values,$(1)),\ + $(eval $(v:$(1)_%=$(new_hmap)_%) := $(call EVAL,$($(v)),$(2))))\ + $(eval $(new_hmap)_size := $($(1)_size))\ + $(new_hmap)),\ + $(1)))))) +endef + +define EVAL_INVOKE +$(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL_INVOKE: $(call _pr_str,$(1)))) + $(foreach a0,$(call _nth,$(1),0),\ + $(if $(call _EQ,def!,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach res,$(call EVAL,$(a2),$(2)),\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(call _EQ,let*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call EVAL,$(a2),$(call LET,$(a1),$(call ENV,$(2)))))),\ + $(if $(call _EQ,quote,$($(a0)_value)),\ + $(call _nth,$(1),1),\ + $(if $(call _EQ,quasiquote,$($(a0)_value)),\ + $(call EVAL,$(call QUASIQUOTE,$(call _nth,$(1),1)),$(2)),\ + $(if $(call _EQ,defmacro!,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach res,$(call EVAL,$(a2),$(2)),\ + $(if $(call ENV_SET,$(2),_macro_$($(a1)_value),true),,)\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(call _EQ,macroexpand,$($(a0)_value)),\ + $(call MACROEXPAND,$(call _nth,$(1),1),$(2)),\ + $(if $(call _EQ,make*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(and $(EVAL_DEBUG),$(info make*: $$(eval __result := $(call str_decode,$(value $(a1)_value)))))\ + $(eval __result := $(call str_decode,$(value $(a1)_value)))$(call READ_STR,$(__result))),\ + $(if $(call _EQ,do,$($(a0)_value)),\ + $(call slast,$(call EVAL_AST,$(call srest,$(1)),$(2))),\ + $(if $(call _EQ,if,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach cond,$(call EVAL,$(a1),$(2)),\ + $(if $(or $(call _EQ,$(__nil),$(cond)),$(call _EQ,$(__false),$(cond))),\ + $(foreach a3,$(call _nth,$(1),3),$(call EVAL,$(a3),$(2))),\ + $(call EVAL,$(a2),$(2)))))),\ + $(if $(call _EQ,fn*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\ + $(foreach el,$(call EVAL_AST,$(1),$(2)),\ + $(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\ + $(foreach f,$(call sfirst,$(el)),\ + $(foreach args,$(call srest,$(el)),\ + $(call apply,$(f),$(args))))))))))))))))) +endef + +define EVAL +$(strip $(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL: $(call _pr_str,$(1))))\ + $(if $(call _list?,$(1)),\ + $(foreach ast,$(call MACROEXPAND,$(1),$(2)), + $(if $(call _list?,$(ast)),\ + $(word 1,$(strip $(call EVAL_INVOKE,$(ast),$(2)) $(__nil))),\ + $(ast))),\ + $(call EVAL_AST,$(1),$(2))))) +endef + + +# PRINT: +define PRINT +$(if $(__ERROR),Error: $(call _pr_str,$(__ERROR),yes),$(if $(1),$(call _pr_str,$(1),yes)))$(if $(__ERROR),$(eval __ERROR :=),) +endef + +# REPL: +REPL_ENV := $(call ENV) +REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) +REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) + +# Setup the environment +_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) +_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1)))) + +# Import types functions +_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),) +$(call _import_types,$(types_ns)) + +$(call _ref,read-string,$(call function,$$(call READ_STR,$$(1)))) +$(call _ref,eval,$(call function,$$(call EVAL,$$(1),$$(REPL_ENV)))) + +_slurp = $(call string,$(call _read_file,$(1))) +_slurp_do = $(call string,(do $(call _read_file,$(1)))) +$(call _ref,slurp,$(call function,$$(call _slurp,$$(call str_decode,$$($$(1)_value))))) +$(call _ref,slurp-do,$(call function,$$(call _slurp_do,$$(call str_decode,$$($$(1)_value))))) + +# Defined in terms of the language itself +$(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) +$(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (slurp-do f))))) )) + +# Load and eval any files specified on the command line +$(if $(MAKECMDGOALS),\ + $(foreach file,$(MAKECMDGOALS),$(call do,$(call REP, (load-file "$(file)") )))\ + $(eval INTERACTIVE :=),) +.PHONY: none $(MAKECMDGOALS) +none $(MAKECMDGOALS): + @true + +# Call the read-eval-print loop +$(if $(strip $(INTERACTIVE)),$(call REPL)) diff --git a/make/stepA_more.mk b/make/stepA_more.mk new file mode 100644 index 0000000..ec32d85 --- /dev/null +++ b/make/stepA_more.mk @@ -0,0 +1,192 @@ +# +# mal (Make Lisp) +# +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)types.mk +include $(_TOP_DIR)reader.mk + +SHELL := /bin/bash +INTERACTIVE ?= yes +EVAL_DEBUG ?= + +# READ: read and parse input +define READ +$(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE,"user> ")))) +endef + +# EVAL: evaluate the parameter +IS_PAIR = $(if $(call _EQ,list,$(call _obj_type,$(1))),$(if $(call _EQ,0,$(call _count,$(1))),,true),) + +define QUASIQUOTE +$(strip \ + $(if $(call _NOT,$(call IS_PAIR,$(1))),\ + $(call list,$(call symbol,quote) $(1)),\ + $(if $(call _EQ,unquote,$($(call _nth,$(1),0)_value)),\ + $(call _nth,$(1),1),\ + $(if $(and $(call IS_PAIR,$(call _nth,$(1),0)),$(call _EQ,splice-unquote,$($(call _nth,$(call _nth,$(1),0),0)_value))),\ + $(call list,$(call symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\ + $(call list,$(call symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1)))))))) +endef + +define IS_MACRO_CALL +$(if $(call _list?,$(1)),$(call ENV_FIND,$(2),_macro_$($(call _nth,$(1),0)_value)),) +endef + +define MACROEXPAND +$(strip $(if $(__ERROR),,\ + $(if $(call IS_MACRO_CALL,$(1),$(2)),\ + $(foreach mac,$(call ENV_GET,$(2),$($(call _nth,$(1),0)_value)),\ + $(call MACROEXPAND,$(call apply,$(mac),$(call srest,$(1))),$(2))),\ + $(1)))) +endef + +define LET +$(strip \ + $(word 1,$(2) \ + $(foreach var,$(call _nth,$(1),0),\ + $(foreach val,$(call _nth,$(1),1),\ + $(call ENV_SET,$(2),$($(var)_value),$(call EVAL,$(val),$(2)))\ + $(foreach left,$(call srest,$(call srest,$(1))), + $(if $(call _EQ,0,$(call _count,$(left))),\ + ,\ + $(call LET,$(left),$(2)))))))) +endef + +define EVAL_AST +$(strip \ + $(and $(EVAL_DEBUG),$(info EVAL_AST: $(call _pr_str,$(1))))\ + $(if $(call _symbol?,$(1)),\ + $(foreach key,$($(1)_value),\ + $(call ENV_GET,$(2),$(key))),\ + $(if $(call _list?,$(1)),\ + $(call _smap,EVAL,$(1),$(2)),\ + $(if $(call _vector?,$(1)),\ + $(call _smap_vec,EVAL,$(1),$(2)),\ + $(if $(call _hash_map?,$(1)),\ + $(foreach new_hmap,$(call __new_obj,hmap),\ + $(foreach v,$(call __get_obj_values,$(1)),\ + $(eval $(v:$(1)_%=$(new_hmap)_%) := $(call EVAL,$($(v)),$(2))))\ + $(eval $(new_hmap)_size := $($(1)_size))\ + $(new_hmap)),\ + $(1)))))) +endef + +define EVAL_INVOKE +$(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL_INVOKE: $(call _pr_str,$(1)))) + $(foreach a0,$(call _nth,$(1),0),\ + $(if $(call _EQ,def!,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach res,$(call EVAL,$(a2),$(2)),\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(call _EQ,let*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call EVAL,$(a2),$(call LET,$(a1),$(call ENV,$(2)))))),\ + $(if $(call _EQ,quote,$($(a0)_value)),\ + $(call _nth,$(1),1),\ + $(if $(call _EQ,quasiquote,$($(a0)_value)),\ + $(call EVAL,$(call QUASIQUOTE,$(call _nth,$(1),1)),$(2)),\ + $(if $(call _EQ,defmacro!,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach res,$(call EVAL,$(a2),$(2)),\ + $(if $(call ENV_SET,$(2),_macro_$($(a1)_value),true),,)\ + $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ + $(if $(call _EQ,macroexpand,$($(a0)_value)),\ + $(call MACROEXPAND,$(call _nth,$(1),1),$(2)),\ + $(if $(call _EQ,make*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(and $(EVAL_DEBUG),$(info make*: $$(eval __result := $(call str_decode,$(value $(a1)_value)))))\ + $(eval __result := $(call str_decode,$(value $(a1)_value)))$(call READ_STR,$(__result))),\ + $(if $(call _EQ,try*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach res,$(call EVAL,$(a1),$(2)),\ + $(if $(__ERROR),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach a20,$(call _nth,$(a2),0),\ + $(if $(call _EQ,catch*,$($(a20)_value)),\ + $(foreach a21,$(call _nth,$(a2),1),\ + $(foreach a22,$(call _nth,$(a2),2),\ + $(foreach binds,$(call list,$(a21)),\ + $(foreach catch_env,$(call ENV,$(2),$(binds),$(__ERROR)),\ + $(eval __ERROR :=)\ + $(call EVAL,$(a22),$(catch_env)))))),\ + $(res)))),\ + $(res)))),\ + $(if $(call _EQ,do,$($(a0)_value)),\ + $(call slast,$(call EVAL_AST,$(call srest,$(1)),$(2))),\ + $(if $(call _EQ,if,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(foreach cond,$(call EVAL,$(a1),$(2)),\ + $(if $(or $(call _EQ,$(__nil),$(cond)),$(call _EQ,$(__false),$(cond))),\ + $(foreach a3,$(call _nth,$(1),3),$(call EVAL,$(a3),$(2))),\ + $(call EVAL,$(a2),$(2)))))),\ + $(if $(call _EQ,fn*,$($(a0)_value)),\ + $(foreach a1,$(call _nth,$(1),1),\ + $(foreach a2,$(call _nth,$(1),2),\ + $(call function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\ + $(foreach el,$(call EVAL_AST,$(1),$(2)),\ + $(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\ + $(foreach f,$(call sfirst,$(el)),\ + $(foreach args,$(call srest,$(el)),\ + $(call apply,$(f),$(args)))))))))))))))))) +endef + +define EVAL +$(strip $(if $(__ERROR),,\ + $(and $(EVAL_DEBUG),$(info EVAL: $(call _pr_str,$(1))))\ + $(if $(call _list?,$(1)),\ + $(foreach ast,$(call MACROEXPAND,$(1),$(2)), + $(if $(call _list?,$(ast)),\ + $(word 1,$(strip $(call EVAL_INVOKE,$(ast),$(2)) $(__nil))),\ + $(ast))),\ + $(call EVAL_AST,$(1),$(2))))) +endef + + +# PRINT: +define PRINT +$(if $(__ERROR),Error: $(call _pr_str,$(__ERROR),yes),$(if $(1),$(call _pr_str,$(1),yes)))$(if $(__ERROR),$(eval __ERROR :=),) +endef + +# REPL: +REPL_ENV := $(call ENV) +REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) +REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) + +# Setup the environment +_ref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(if $(2),$(2),$(1)))) +_fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call function,$$(call $(2),$$1)))) + +# Import types functions +_import_types = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_types,$(wordlist 3,$(words $(1)),$(1))),) +$(call _import_types,$(types_ns)) + +$(call _ref,readline,$(call function,$$(foreach res,$$(call string,$$(call READLINE,"$$(call str_decode,$$($$(1)_value))")),$$(if $$(READLINE_EOF),$$(__nil),$$(res))))) +$(call _ref,read-string,$(call function,$$(call READ_STR,$$(1)))) +$(call _ref,eval,$(call function,$$(call EVAL,$$(1),$$(REPL_ENV)))) + +_slurp = $(call string,$(call _read_file,$(1))) +_slurp_do = $(call string,(do $(call _read_file,$(1)))) +$(call _ref,slurp,$(call function,$$(call _slurp,$$(call str_decode,$$($$(1)_value))))) +$(call _ref,slurp-do,$(call function,$$(call _slurp_do,$$(call str_decode,$$($$(1)_value))))) + +# Defined in terms of the language itself +$(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) +$(call do,$(call REP, (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw "odd number of forms to cond")) (cons 'cond (rest (rest xs))))))) )) +$(call do,$(call REP, (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs)))))))) )) +$(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (slurp-do f))))) )) + +# Load and eval any files specified on the command line +$(if $(MAKECMDGOALS),\ + $(foreach file,$(MAKECMDGOALS),$(call do,$(call REP, (load-file "$(file)") )))\ + $(eval INTERACTIVE :=),) +.PHONY: none $(MAKECMDGOALS) +none $(MAKECMDGOALS): + @true + +# Call the read-eval-print loop +$(if $(strip $(INTERACTIVE)),$(call REPL)) diff --git a/make/tests/common.mk b/make/tests/common.mk new file mode 100644 index 0000000..55b931d --- /dev/null +++ b/make/tests/common.mk @@ -0,0 +1,18 @@ + +# assert macros +assert = $(if $1,,$(error assert failure: $2)) +assert_not = $(if $1,$(error assert_not: $2),) +assert_eq = $(if $(call _EQ,$(1),$(2)),,$(error assert_eq failure: $(1) != $(2): $(3))) +# With debug: +#assert_eq = $(info 1: $(1))$(info 2: $(2))$(info 3: $(3))$(if $(call _EQ,$(1),$(2)),,$(error assert_eq failure: $(3))) + + +# REPL related wrappers +test_read = $(call READ_STR,$(1)) +ifndef MACROEXPAND +define MACROEXPAND +$(1) +endef +endif +test_re = $(strip $(call EVAL,$(call MACROEXPAND,$(strip $(call test_read,$(1))),$(REPL_ENV)),$(REPL_ENV))) +test_rep = $(call PRINT,$(strip $(call EVAL,$(call MACROEXPAND,$(strip $(call test_read,$(1))),$(REPL_ENV)),$(REPL_ENV)))) diff --git a/make/tests/reader.mk b/make/tests/reader.mk new file mode 100644 index 0000000..672d27b --- /dev/null +++ b/make/tests/reader.mk @@ -0,0 +1,76 @@ +INTERACTIVE = no + +include tests/common.mk +include reader.mk + +_tonum = $(call int_decode,$($(1)_value)) + +$(info Testing READ_STR of numbers) +$(call assert_eq,2,$(call _tonum,$(call READ_STR,2))) +$(call assert_eq,12345,$(call _tonum,$(call READ_STR,12345))) +$(call assert_eq,12345,$(call _tonum,$(call READ_STR,12345 "abc"))) +$(call assert_eq,12345,$(call _tonum,$(call READ_STR,12345"abc"))) +$(call assert_eq,12345,$(call _tonum,$(call READ_STR,12345(1)))) + +$(info Testing READ_STR of symbols) +$(call assert_eq,abc,$($(call READ_STR,abc)_value)) +$(call assert_eq,abc,$($(call READ_STR,abc )_value)) +$(call assert_eq,abc,$($(call READ_STR,abc"a str")_value)) +$(call assert_eq,abc,$($(call READ_STR,abc(2 3))_value)) + +$(info Testing READ_STR of strings) +$(call assert_eq,a string,$(call str_decode,$($(call READ_STR,"a string")_value))) +$(call assert_eq,a string (with parens),$(call str_decode,$($(call READ_STR,"a string (with parens)")_value))) +$(call assert_eq,a string,$(call str_decode,$($(call READ_STR,"a string"())_value))) +$(call assert_eq,a string,$(call str_decode,$($(call READ_STR,"a string"123)_value))) +$(call assert_eq,a string,$(call str_decode,$($(call READ_STR,"a string"abc)_value))) +$(call assert_eq,,$(call str_decode,$($(call READ_STR,"")_value))) +$(call assert_eq,abc ,$(call str_decode,$($(call READ_STR,"abc ")_value))) +$(call assert_eq, abc,$(call str_decode,$($(call READ_STR," abc")_value))) +$(call assert_eq,$$abc,$(call str_decode,$($(call READ_STR,"$$abc")_value))) +$(call assert_eq,abc$$(),$(call str_decode,$($(call READ_STR,"abc$$()")_value))) +$(call assert_eq,"xyz",$(call str_decode,$($(call READ_STR,"\"xyz\"")_value))) + +$(info Testing READ_STR of lists) +$(call assert_eq,2,$(call _count,$(call READ_STR,(2 3)))) +$(call assert_eq,2,$(call _tonum,$(call sfirst,$(call READ_STR,(2 3))))) +$(call assert_eq,3,$(call _tonum,$(call sfirst,$(call srest,$(call READ_STR,(2 3)))))) +L := $(strip $(call READ_STR,(+ 1 2 "str1" "string (with parens) and 'single quotes'"))) +$(call assert_eq,5,$(call _count,$(L))) +$(call assert_eq,str1,$(call str_decode,$($(call _nth,$(L),3)_value))) +$(call assert_eq,string (with parens) and 'single quotes',$(call str_decode,$($(call _nth,$(L),4)_value))) + +$(info Testing READ_STR of vectors) +$(call assert_eq,2,$(call _count,$(call READ_STR,[2 3]))) +$(call assert_eq,2,$(call _tonum,$(call sfirst,$(call READ_STR,[2 3])))) +$(call assert_eq,3,$(call _tonum,$(call sfirst,$(call srest,$(call READ_STR,[2 3]))))) +L := $(strip $(call READ_STR,[+ 1 2 "str1" "string (with parens) and 'single quotes'"])) +$(call assert_eq,5,$(call _count,$(L))) +$(call assert_eq,str1,$(call str_decode,$($(call _nth,$(L),3)_value))) +$(call assert_eq,string (with parens) and 'single quotes',$(call str_decode,$($(call _nth,$(L),4)_value))) + +$(info Testing READ_STR of quote/quasiquote) +$(call assert_eq,quote,$($(call _nth,$(call READ_STR,'1),0)_value)) #' +$(call assert_eq,1,$(call _tonum,$(call _nth,$(call READ_STR,'1),1))) #' +$(call assert_eq,quote,$($(call _nth,$(call READ_STR,'(1 2 3)),0)_value)) #' +$(call assert_eq,3,$(call _tonum,$(call _nth,$(call _nth,$(call READ_STR,'(1 2 3)),1),2))) #' + +$(call assert_eq,quasiquote,$($(call _nth,$(call READ_STR,`1),0)_value)) +$(call assert_eq,1,$(call _tonum,$(call _nth,$(call READ_STR,`1),1))) +$(call assert_eq,quasiquote,$($(call _nth,$(call READ_STR,`(1 2 3)),0)_value)) +$(call assert_eq,3,$(call _tonum,$(call _nth,$(call _nth,$(call READ_STR,`(1 2 3)),1),2))) + +$(call assert_eq,unquote,$($(call _nth,$(call READ_STR,~1),0)_value)) +$(call assert_eq,1,$(call _tonum,$(call _nth,$(call READ_STR,~1),1))) +$(call assert_eq,unquote,$($(call _nth,$(call READ_STR,~(1 2 3)),0)_value)) +$(call assert_eq,3,$(call _tonum,$(call _nth,$(call _nth,$(call READ_STR,~(1 2 3)),1),2))) + +$(call assert_eq,splice-unquote,$($(call _nth,$(call READ_STR,~@1),0)_value)) +$(call assert_eq,1,$(call _tonum,$(call _nth,$(call READ_STR,~@1),1))) +$(call assert_eq,splice-unquote,$($(call _nth,$(call READ_STR,~@(1 2 3)),0)_value)) +$(call assert_eq,3,$(call _tonum,$(call _nth,$(call _nth,$(call READ_STR,~@(1 2 3)),1),2))) + + +.PHONY: all +all: + @echo "All tests completed" diff --git a/make/tests/step9_interop.mk b/make/tests/step9_interop.mk new file mode 100644 index 0000000..0e44a88 --- /dev/null +++ b/make/tests/step9_interop.mk @@ -0,0 +1,14 @@ +INTERACTIVE = + +include tests/common.mk +include step9_interop.mk + +$(info Testing trivial macros) +$(call assert_eq,7,$(call test_rep, (make* "7") )) +$(call assert_eq,"XaY XbY XcY",$(call test_rep, (make* "\"$(foreach v,a b c,X$(v)Y)\"") )) +$(call assert_eq,(2 3 4),$(call test_rep, (make* "($(foreach v,1 2 3,$(call gmsl_plus,1,$(v))))") )) + + +.PHONY: all +all: + @echo "All tests completed" diff --git a/make/tests/types.mk b/make/tests/types.mk new file mode 100644 index 0000000..c1c1849 --- /dev/null +++ b/make/tests/types.mk @@ -0,0 +1,304 @@ +include tests/common.mk +include types.mk + +# treat an expression as a statement +do = $(eval __tmp := $(1)) + + +$(info Testing foreach as a let form) + +$(call assert_eq,XbX,$(foreach local_var,b,X$(local_var)X),\ + Using foreach as 'let' failed) + + +$(info Testing type function) +$(call assert_eq,make,$(call _obj_type,xyz),\ + (type xyz) is not 'make') +$(call assert_eq,nil,$(call _obj_type,$(__nil)),\ + (type $$(__nil)) is not 'nil') +$(call assert_eq,true,$(call _obj_type,$(__true)),\ + (type $$(__true)) is not 'true') +$(call assert_eq,false,$(call _obj_type,$(__false)),\ + (type $$(__false)) is not 'false') + + +$(info Testing number? function) + +$(call assert_eq,number,$(call _obj_type,$(call number,1))) +$(call assert_eq,number,$(call _obj_type,$(call number,10))) +$(call assert_eq,number,$(call _obj_type,$(call number,12345))) + + +$(info Testing symbols) + +$(call assert_eq,symbol,$(call _obj_type,$(call symbol,abc)),\ + (type (symbol abc)) is not 'symbol') +SYM1 := $(call symbol,a sym value) +$(call assert_eq,a sym value,$($(SYM1)_value)) +$(call assert_eq,$(__true),$(call symbol?,$(SYM1))) + + +$(info Testing strings) + +$(call assert_eq,string,$(call _obj_type,$(call string,abc)),\ + (type (string abc)) is not string) + +STR1 := $(call string,a string value) +$(call assert_eq,a string value,$(call str_decode,$($(STR1)_value))) +$(call assert_eq,$(__true),$(call string?,$(STR1))) +$(call assert_eq,14,$(call _count,$(STR1))) + +STR2 := $(call string,a string (with parens)) +$(call assert_eq,a string (with parens),$(call str_decode,$($(STR2)_value))) +$(call assert_eq,$(__true),$(call string?,$(STR2))) +$(call assert_eq,22,$(call _count,$(STR2))) + +$(info Testing strings (subs)) +$(call assert_eq,a string (with parens),$(call str_decode,$($(call subs,$(STR2),$(call number,2))_value))) +$(call assert_eq,a string,$(call str_decode,$($(call subs,$(STR2),$(call number,0),$(call number,8))_value))) + +$(info Testing strings (str)) +$(call assert_eq,a string value - a string (with parens),$(call str_decode,$($(call str,$(STR1) $(call string, - ) $(STR2))_value))) + + +$(info Testing function objects) + +$(call assert_eq,function,$(call _obj_type,$(call function,abc)),\ + (type (function abc)) is not 'function') +FN1 := $(call function,arg1:'$$(word 1,$$(1))' arg2:'$$(word 2,$$(1))') +$(call assert_eq,$(__true),$(call function?,$(FN1))) +$(call assert_eq,arg1:'A' arg2:'B',$(call apply,$(FN1),$(call list,A B))) + + +$(info Testing lists) + +$(call assert_eq,list,$(call _obj_type,$(call list)),\ + (type (list)) is not 'list') + +$(info Testing lists (cons)) +L1 := $(call cons,P $(call list)) +L2 := $(call cons,Q $(L1)) +$(call assert_eq,$(__true),$(call list?,$(L1))) +$(call assert_eq,$(__true),$(call list?,$(L2))) +$(call assert_eq,P,$(call sfirst,$(L1))) +$(call assert_eq,2,$(call _count,$(L2))) +$(call assert_eq,Q,$(call sfirst,$(L2))) +$(call assert_eq,P,$(call _nth,$(L2),1)) +$(call assert_eq,$(__true),$(call equal?,$(L1) $(call srest,$(L2)))) + +$(info Testing lists (concat)) +L1_2 := $(call concat,$(L1) $(L2)) +$(call assert_eq,3,$(call _count,$(L1_2))) +$(call assert_eq,P,$(call sfirst,$(L1_2))) +$(call assert_eq,Q,$(call _nth,$(L1_2),1)) +$(call assert_eq,P,$(call _nth,$(L1_2),2)) +$(call assert_eq,$(__true),$(call equal?,$(L2) $(call srest,$(L1_2)))) + +$(info Testing lists (conj)) +L3 := $(call _conj!,$(call list),A B) +L4 := $(call _conj!,$(call list),X $(L3)) +$(call assert_eq,$(__true),$(call list?,$(L3)),\ + (list? $$(L3))) +$(call assert_eq,$(__true),$(call list?,$(L4)),\ + (list? $$(L3))) +$(call assert_eq,A,$(call sfirst,$(L3)),\ + (sfirst $$(L3)) is not 'A') +$(call assert_eq,X,$(call sfirst,$(L4)),\ + (sfirst $$(L4)) is not 'X') +$(call assert_eq,$(__true),$(call list?,$(call _nth,$(L4),1)),\ + (_nth $$(L4),1) is not a list) +$(call assert_eq,A,$(call sfirst,$(call _nth,$(L4),1)),\ + (first (_nth $$(L4),1)) is not 'A') + + +$(info Testing hash_maps) + +X := $(call hash_map) +$(call assert_eq,$(__true),$(call hash_map?,$(X)),\ + (hash_map? $$(X))) +$(call assert_eq,$(__false),$(call vector?,$(X)),\ + (vector? $$(X))) + +mykey := $(call _string,a) +$(call assert_not,$(call _get,$(X),a),\ + (get $$(X),a)) +$(call assert_eq,$(__false),$(call contains?,$(X),$(mykey)),\ + (contains? $$(X),a)) +$(call do,$(call _assoc!,$(X),a,value of X a)) +$(call assert_eq,value of X a,$(call _get,$(X),a),\ + (get $$(X),a) is not 'value of Xa') +$(call assert_eq,$(__true),$(call contains?,$(X) $(mykey)),\ + (contains? $$(X),a)) + +Y := $(call hash_map) +$(call assert_eq,0,$(call _count,$(Y)),\ + (_count $$(Y))) +$(call do,$(call _assoc!,$(Y),a,value of Y a)) +$(call assert_eq,1,$(call _count,$(Y)),\ + (_count $$(Y))) +$(call do,$(call _assoc!,$(Y),b,value of Y b)) +$(call assert_eq,2,$(call _count,$(Y)),\ + (_count $$(Y))) +$(call assert_eq,value of Y a,$(call _get,$(Y),a),\ + (get $$(Y),a) is not 'value of Y a') +$(call assert_eq,value of Y b,$(call _get,$(Y),b),\ + (get $$(Y),b) is not 'value of Y b') +$(call assert_eq,value of Y a value of Y b,$(call raw_flat,$(Y),b),\ + (raw_flat $(Y)) is not 'value of Y a value of Y b') + +$(call do,$(call _assoc!,$(X),b,$(Y))) +$(call assert_eq,2,$(call _count,$(Y),a),\ + (_count $$(Y)) should still be 2) + +$(call assert_eq,$(__true),$(call hash_map?,$(call _get,$(X),b)),\ + (hash_map? (get $$(X),b))) + +$(call assert_eq,$(call _get,$(call _get,$(X),b),a),value of Y a,\ + (get (get $(X),b),a) is not 'value of Y a') +$(call assert_eq,$(call _get,$(call _get,$(X),b),b),value of Y b,\ + (get (get $(X),b),b) is not 'value of Y b') + +$(call do,$(call _dissoc!,$(Y),a)) +$(call assert_eq,1,$(call _count,$(Y)),\ + (_count $$(Y)) should now be 1) +$(call assert_not,$(call _get,$(Y),a),\ + (get $$(Y),a)) +$(call do,$(call _dissoc!,$(Y),b)) +$(call assert_eq,0,$(call _count,$(Y)),\ + (_count $$(Y)) should now be 0) +$(call assert_not,$(call _get,$(Y),b),\ + (get $$(Y),b)) + + +$(info Testing vectors) + +V1 := $(call _conj!,$(call vector),first.vector.value second.vector.value third.vector.value) +$(call assert_eq,$(__true),$(call vector?,$(V1)),\ + (vector? $$(V1))) +$(call assert_eq,first.vector.value,$(call _nth,$(V1),0)) +$(call assert_eq,second.vector.value,$(call _nth,$(V1),1)) +$(call assert_eq,third.vector.value,$(call _nth,$(V1),2)) +$(call assert_eq,third.vector.value,$(call slast,$(V1))) +$(call assert_eq,3,$(call _count,$(V1))) + +V2 := $(call _conj!,$(call vector),A B C) +$(call assert_eq,3,$(call _count,$(V2)),\ + (_count $$(V2)) is not 3) +$(call assert_eq,A B C,$($(V2)_value)) +$(call assert_eq,A,$(call sfirst,$(V2)),\ + (first $$(V2)) is not 'A') +$(call assert_eq,$(__true),$(call list?,$(call srest,$(V2))),\ + (rest $$(V2)) is not a vector) +$(call assert_eq,B C,$($(call srest,$(V2))_value)) +$(call assert_eq,B,$(call sfirst,$(call srest,$(V2))),\ + (first (rest $$(V2))) is not 'B') +$(call assert_eq,C,$(call sfirst,$(call srest,$(call srest,$(V2)))),\ + (first (rest (rest $$(V2)))) is not 'C') +$(call assert_eq,C,$(call _nth,$(V2),2),\ + (_nth $$(V2),2) is not 'C') + +V2_1 := $(call _conj!,$(V2),$(V1)) +$(call assert_eq,4,$(call _count,$(V2_1)),\ + (_count $$(V2_1)) is not 4) +$(call assert_eq,C,$(call _nth,$(V2_1),2),\ + (_nth $$(V2_1),2) is no longer 'C') +$(call assert_eq,$(__true),$(call vector?,$(call _nth,$(V2_1),3)),\ + (_nth $$(V2_1),3) is not a vector) +$(call assert_eq,second.vector.value,$(call _nth,$(call _nth,$(V2_1),3),1),\ + (_nth (_nth $$(V2_1),3),1) is not 'second.vector.value') + +$(info Testing vectors (rest)) + +V3 := $(call srest,$(V2_1)) +$(call assert_eq,3,$(call _count,$(V3)),\ + (_count $$(V3)) is not 3) +$(call assert_eq,B,$(call sfirst,$(V3)),\ + (first $$(V3)) is not 'B') +$(call assert_eq,$(__true),$(call vector?,$(call _nth,$(V3),2)),\ + (_nth $$(V3),2) is not a vector) +$(call assert_eq,second.vector.value,$(call _nth,$(call _nth,$(V3),2),1),\ + (_nth (_nth $$(V3),2),1) is not 'second.vector.value') + +$(info Testing vectors (contains?)) + +$(call assert_eq,$(__true),$(call _contains?,$(V2_1),0),\ + (contains? $$(V2_1),0)) +$(call assert_eq,,$(call _contains?,$(V2_1),7),\ + (contains? $$(V2_1),7)) + + +$(info Testing _apply function) + +label_args = $(word 1,$(1))$(word 2,$(1))$(word 3,$(1))$(word 4,$(1)) +$(call assert_eq,,$(call _apply,label_args,$(call list))) +$(call assert_eq,A,$(call _apply,label_args,$(call list,A))) +$(call assert_eq,AB,$(call _apply,label_args,$(call list,A B))) +$(call assert_eq,ABCD,$(call _apply,label_args,$(call list,A B C D))) + + +$(info Testing smap function) + +L5 := $(call _conj!,$(call list),$(call number,1) $(call number,2) $(call number,3)) +inc = $(call number_plus,$(call number,1) $(1)) +$(call assert_eq,(2 3 4),$(call _pr_str,$(call _smap,inc,$(L5)))) +inc_func := $(call function,$$(call number_plus,$$(call number,1) $$(1))) +$(call assert_eq,(2 3 4),$(call _pr_str,$(call smap,$(inc_func) $(L5)))) + + +$(info Testing equal? function) +$(call assert_eq,$(__true),$(call equal?,2 2)) +$(call assert_eq,$(__false),$(call equal?,2 3)) +$(call assert_eq,$(__false),$(call equal?,2 3)) +$(call assert_eq,$(__true),$(call equal?,abc abc)) +$(call assert_eq,$(__false),$(call equal?,abc abz)) +$(call assert_eq,$(__false),$(call equal?,zbc abc)) +$(call assert_eq,$(__true),$(call equal?,$(call string,abc) $(call string,abc))) +$(call assert_eq,$(__false),$(call equal?,$(call string,abc) $(call string,abz))) +$(call assert_eq,$(__false),$(call equal?,$(call string,zbc) $(call string,abc))) +$(call assert_eq,$(__true),$(call equal?,$(call symbol,abc) $(call symbol,abc))) +$(call assert_eq,$(__false),$(call equal?,$(call symbol,abc) $(call symbol,abz))) +$(call assert_eq,$(__false),$(call equal?,$(call symbol,zbc) $(call symbol,abc))) +L6 := $(call _conj!,$(call list),1 2 3) +L7 := $(call _conj!,$(call list),1 2 3) +L8 := $(call _conj!,$(call list),1 2 Z) +L9 := $(call _conj!,$(call list),Z 2 3) +L10 := $(call _conj!,$(call list),1 2) +$(call assert_eq,$(__true),$(call equal?,$(L6) $(L7))) +$(call assert_eq,$(__false),$(call equal?,$(L6) $(L8))) +$(call assert_eq,$(__false),$(call equal?,$(L6) $(L9))) +$(call assert_eq,$(__false),$(call equal?,$(L6) $(L10))) +$(call assert_eq,$(__false),$(call equal?,$(L10) $(L6))) + + +$(info Testing empty? function) +$(call assert_eq,$(__true),$(call empty?,$(call list))) +$(call assert_eq,$(__false),$(call empty?,$(call list,1))) + + +$(info Testing ENV (1 level)) +env1 := $(call ENV) +$(call assert_eq,,$(call ENV_GET,$(env1),a)) +$(call assert_eq,$(env1),$(call ENV_SET,$(env1),a,val_a)) +$(call assert_eq,$(env1),$(call ENV_SET,$(env1),b,val_b)) +$(call assert_eq,$(env1),$(call ENV_SET,$(env1),=,val_eq)) +$(call assert_eq,val_a,$(call ENV_GET,$(env1),a)) +$(call assert_eq,val_b,$(call ENV_GET,$(env1),b)) +$(call assert_eq,val_eq,$(call ENV_GET,$(env1),=)) +$(call assert_eq,hash_map,$(call _obj_type,$(call ENV_FIND,$(env1),a))) +$(call assert_eq,val_a,$(call _get,$(call ENV_FIND,$(env1),a),a)) + +$(info Testing ENV (2 levels)) +env2 := $(call ENV,$(env1)) +$(call assert_eq,$(env2),$(call ENV_SET,$(env2),b,val_b2)) +$(call assert_eq,$(env2),$(call ENV_SET,$(env2),c,val_c)) +$(call assert_eq,$(env1),$(call ENV_FIND,$(env2),a)) +$(call assert_eq,$(env2),$(call ENV_FIND,$(env2),b)) +$(call assert_eq,$(env2),$(call ENV_FIND,$(env2),c)) +$(call assert_eq,val_a,$(call ENV_GET,$(env2),a)) +$(call assert_eq,val_b2,$(call ENV_GET,$(env2),b)) +$(call assert_eq,val_c,$(call ENV_GET,$(env2),c)) + + +.PHONY: all +all: + @echo "All tests completed" diff --git a/make/types.mk b/make/types.mk new file mode 100644 index 0000000..234ca51 --- /dev/null +++ b/make/types.mk @@ -0,0 +1,484 @@ +# +# mal (Make Lisp) Object Types and Functions +# + +ifndef __mal_types_included +__mal_types_included := true + +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)util.mk + +# magic is \u2344 \u204a +__obj_magic = ⍄⁊ +# \u2256 +__equal = ≛ +__obj_hash_code = 0 + +__new_obj_hash_code = $(eval __obj_hash_code := $(call gmsl_plus,1,$(__obj_hash_code)))$(__obj_hash_code) + +__new_obj = $(__obj_magic)_$(1)_$(call __new_obj_hash_code) + +__new_obj_like = $(foreach type,$(word 2,$(subst _, ,$(1))),$(__obj_magic)_$(type)_$(__new_obj_hash_code)) + +__get_obj_values = $(strip \ + $(if $(filter $(__obj_magic)_hmap_%,$(1)),\ + $(sort $(foreach v,$(filter %_value,$(filter $(1)_%,$(.VARIABLES))),$(if $(call _undefined?,$(v)),,$(v)))),\ + $($(1)_value))) + +__ERROR := + +# +# General functions +# + +# Return the type of the object (or "make" if it's not a object +_obj_type = $(strip \ + $(if $(filter $(__obj_magic)_symb_%,$(1)),symbol,\ + $(if $(filter $(__obj_magic)_list_%,$(1)),list,\ + $(if $(filter $(__obj_magic)_numb_%,$(1)),number,\ + $(if $(filter $(__obj_magic)_func_%,$(1)),function,\ + $(if $(filter $(__obj_magic)_strn_%,$(1)),string,\ + $(if $(filter $(__obj_magic)__nil_%,$(1)),nil,\ + $(if $(filter $(__obj_magic)_true_%,$(1)),true,\ + $(if $(filter $(__obj_magic)_fals_%,$(1)),false,\ + $(if $(filter $(__obj_magic)_vect_%,$(1)),vector,\ + $(if $(filter $(__obj_magic)_atom_%,$(1)),atom,\ + $(if $(filter $(__obj_magic)_hmap_%,$(1)),hash_map,\ + $(if $(filter $(__obj_magic)_undf_%,$(1)),undefined,\ + make))))))))))))) +obj_type = $(call string,$(call _obj_type,$(1))) + +# return a printable form of the argument, the second parameter is +# 'print_readably' which backslashes quotes in string values +_pr_str = $(if $(1),$(foreach ot,$(call _obj_type,$(1)),$(if $(call _EQ,make,$(ot)),$(call _error,_pr_str failed on $(1)),$(call $(ot)_pr_str,$(1),$(2)))),) + +# Like _pr_str but takes multiple values in first argument, the second +# parameter is 'print_readably' which backslashes quotes in string +# values, the third parameter is the delimeter to use between each +# _pr_str'd value +_pr_str_mult = $(call _pr_str,$(word 1,$(1)),$(2))$(if $(word 2,$(1)),$(3)$(call _pr_str_mult,$(wordlist 2,$(words $(1)),$(1)),$(2),$(3)),) + +pr_str = $(call string,$(call _pr_str_mult,$(1),yes, )) +str = $(call string,$(call _pr_str_mult,$(1),,)) +prn = $(info $(call _pr_str_mult,$(1),yes, )) +println = $(info $(subst \n,$(NEWLINE),$(call _pr_str_mult,$(1),, ))) + +_clone_obj = $(strip \ + $(foreach new_hcode,$(call __new_obj_hash_code),\ + $(foreach new_obj,$(foreach type,$(word 2,$(subst _, ,$(1))),$(__obj_magic)_$(type)_$(new_hcode)),\ + $(if $(filter $(__obj_magic)_hmap_%,$(1)),\ + $(foreach v,$(call __get_obj_values,$(1)),\ + $(eval $(v:$(1)_%=$(new_obj)_%) := $($(v))))\ + $(eval $(new_obj)_size := $($(1)_size)),\ + $(if $(filter $(__obj_magic)_func_%,$(1)),\ + $(eval $(new_obj)_value = $(value $(1)_value)),\ + $(eval $(new_obj)_value := $(strip $($(1)_value)))))\ + $(new_obj)))) + +with_meta = $(strip \ + $(foreach new_obj,$(call _clone_obj,$(word 1,$(1))),\ + $(eval $(new_obj)_meta := $(strip $(word 2,$(1))))\ + $(new_obj))) + +meta = $(strip $($(1)_meta)) + + +# +# Special atomic values +# +__undefined = $(__obj_magic)_undf_0 +__nil = $(__obj_magic)__nil_0 +__true = $(__obj_magic)_true_0 +__false = $(__obj_magic)_fals_0 + +_undefined? = $(or $(call _EQ,undefined,$(origin $(1))),$(filter $(__undefined),$($(1)))) +undefined? = $(if $(call _undefined?,$(1)),$(__true),$(__false)) + +_nil? = $(if $(filter $(__obj_magic)__nil_%,$(1)),$(__true),) +nil? = $(if $(call _nil?,$(1)),$(__true),$(__false)) +nil_pr_str = nil + +_true? = $(if $(filter $(__obj_magic)_true_%,$(1)),$(__true),) +true? = $(if $(call _true?,$(1)),$(__true),$(__false)) +true_pr_str = true + +_false? = $(if $(filter $(__obj_magic)_fals_%,$(1)),$(__true),) +false? = $(if $(call _false?,$(1)),$(__true),$(__false)) +false_pr_str = false + + +# +# Numbers +# + +_pnumber = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_numb_$(hcode)$(eval $(__obj_magic)_numb_$(hcode)_value := $(1))) +number = $(call _pnumber,$(call int_encode,$(1))) + +_number? = $(if $(filter $(__obj_magic)_numb_%,$(1)),$(__true),) +number? = $(if $(call _number?,$(1)),$(__true),$(__false)) + +number_pr_str = $(call int_decode,$($(1)_value)) + +number_plus = $(call _pnumber,$(call int_plus,$($(word 1,$(1))_value),$($(word 2,$(1))_value))) +number_subtract = $(call _pnumber,$(call int_subtract,$($(word 1,$(1))_value),$($(word 2,$(1))_value))) +number_multiply = $(call _pnumber,$(call int_multiply,$($(word 1,$(1))_value),$($(word 2,$(1))_value))) +number_divide = $(call _pnumber,$(call int_divide,$($(word 1,$(1))_value),$($(word 2,$(1))_value))) + +number_gt = $(if $(call int_gt,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false)) +number_gte = $(if $(call int_gte,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false)) +number_lt = $(if $(call int_lt,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false)) +number_lte = $(if $(call int_lte,$($(word 1,$(1))_value),$($(word 2,$(1))_value)),$(__true),$(__false)) + + +# +# Symbols +# +symbol = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_symb_$(hcode)$(eval $(__obj_magic)_symb_$(hcode)_value := $(1))) + +_symbol? = $(if $(filter $(__obj_magic)_symb_%,$(1)),$(__true),) +symbol? = $(if $(call _symbol?,$(1)),$(__true),$(__false)) + +symbol_pr_str = $($(1)_value) + +# +# Strings +# +_string = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_strn_$(hcode)$(eval $(__obj_magic)_strn_$(hcode)_value := $(1))) +string = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_strn_$(hcode)$(eval $(__obj_magic)_strn_$(hcode)_value := $(call str_encode,$(1)))) + +_string? = $(if $(filter $(__obj_magic)_strn_%,$(1)),$(__true),) +string? = $(if $(call _string?,$(1)),$(__true),$(__false)) + +string_pr_str = $(if $(2),"$(subst $(DQUOTE),$(ESC_DQUOTE),$(subst $(SLASH),$(SLASH)$(SLASH),$(call str_decode,$($(1)_value))))",$(call str_decode,$($(1)_value))) + +subs = $(strip \ + $(foreach start,$(call gmsl_plus,1,$(call int_decode,$($(word 2,$(1))_value))),\ + $(foreach end,$(if $(3),$(call int_decode,$($(3)_value)),$(words $($(word 1,$(1))_value))),\ + $(call _string,$(wordlist $(start),$(end),$($(word 1,$(1))_value)))))) + +# +# Function objects +# + +# Return a function object. The first parameter is the +# function/macro 'source'. Note that any $ must be escaped as $$ to be +# preserved and become positional arguments for when the +# function/macro is later invoked. +function = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_func_$(hcode)$(eval $(__obj_magic)_func_$(hcode)_value = $(1))) + +_function? = $(if $(filter $(__obj_magic)_func_%,$(1)),$(__true),) +function? = $(if $(call _function?,$(1)),$(__true),$(__false)) + +function_pr_str = <$(if $(word 6,$(value $(1)_value)),$(wordlist 1,5,$(value $(1)_value))...,$(value $(1)_value))> + +# Takes a function name and a list object of arguments and invokes +# the function with space separated arguments +_apply = $(call $(1),$($(2)_value)) + +# Takes a function object and a list object of arguments and invokes +# the function with space separated arguments +apply = $(call $(1)_value,$($(2)_value)) + +# Takes a space separated arguments and invokes the first argument +# (function object) using the remaining arguments. +sapply = $(call $(word 1,$(1))_value,$($(word 2,$(1))_value)) + +# +# hash maps (associative arrays) +# + +# create a new anonymous empty hash map +_hash_map = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_hmap_$(hcode)$(eval $(__obj_magic)_hmap_$(hcode)_size := 0)) +hash_map = $(word 1,$(foreach new_hmap,$(call _hash_map),$(new_hmap) $(if $(1),$(call _assoc_seq!,$(new_hmap),$(1))))) + +_hash_map? = $(if $(filter $(__obj_magic)_hmap_%,$(1)),$(__true),) +hash_map? = $(if $(call _hash_map?,$(1)),$(__true),$(__false)) + +hash_map_pr_str = {$(foreach v,$(call __get_obj_values,$(1)),"$(foreach hcode,$(word 3,$(subst _, ,$(1))),$(patsubst $(1)_%,%,$(v:%_value=%)))" $(call _pr_str,$($(v)),$(2)))} + +# Set multiple key/values in a map +_assoc_seq! = $(call _assoc!,$(1),$(call str_decode,$($(word 1,$(2))_value)),$(word 2,$(2)))$(if $(word 3,$(2)),$(call _assoc_seq!,$(1),$(wordlist 3,$(words $(2)),$(2))),) + +# set a key/value in the hash map +_assoc! = $(foreach k,$(subst =,$(__equal),$(2)),$(if $(call _undefined?,$(1)_$(k)_value),$(eval $(1)_size := $(call gmsl_plus,$($(1)_size),1)),)$(eval $(1)_$(k)_value := $(3))$(1)) + +# set a key/value in a copy of the hash map +# TODO: multiple arguments +assoc = $(foreach hm,$(call _clone_obj,$(word 1,$(1))),$(call _assoc!,$(hm),$(call str_decode,$($(word 2,$(1))_value)),$(word 3,$(1)))) + +# unset a key in the hash map +_dissoc! = $(foreach k,$(subst =,$(__equal),$(2)),$(if $(call _undefined?,$(1)_$(k)_value),,$(eval $(1)_$(k)_value := $(__undefined))$(eval $(1)_size := $(call gmsl_subtract,$($(1)_size),1))))$(1) + +# unset a key in a copy of the hash map +# TODO: this could be made more efficient by not copying the key in +# the first place +# TODO: multiple arguments +dissoc = $(foreach hm,$(call _clone_obj,$(word 1,$(1))),$(call _dissoc!,$(hm),$(call str_decode,$($(word 2,$(1))_value)))) + +keys = $(foreach new_list,$(call _list),$(new_list)$(eval $(new_list)_value := $(foreach v,$(call __get_obj_values,$(1)),$(call string,$(word 4,$(subst _, ,$(v))))))) + +vals = $(foreach new_list,$(call _list),$(new_list)$(eval $(new_list)_value := $(foreach v,$(call __get_obj_values,$(1)),$($(v))))) + + + +# Hash map and vector functions + +# retrieve the value of a plain string key from the hash map, or +# retrive a vector by plain index +_get = $(strip \ + $(if $(call _hash_map?,$(1)),\ + $(foreach k,$(subst =,$(__equal),$(2)),$(if $(call _undefined?,$(1)_$(k)_value),,$($(1)_$(k)_value))),\ + $(if $(call _vector?,$(1)),\ + $(word $(call gmsl_plus,1,$(2)),$($(1)_value)),\ + ,))) + +# retrieve the value of a string key object from the hash map, or +# retrive a vector by number object index +get = $(strip \ + $(if $(call _hash_map?,$(word 1,$(1))),\ + $(call _get,$(word 1,$(1)),$(call str_decode,$($(word 2,$(1))_value))),\ + $(call _get,$(word 1,$(1)),$(call number_pr_str,$(word 2,$(1)))))) + +_contains? = $(strip \ + $(if $(call _hash_map?,$(1)),\ + $(foreach k,$(subst =,$(__equal),$(2)),$(if $(call _undefined?,$(1)_$(k)_value),,$(__true))),\ + $(if $(call _vector?,$(1)),\ + $(if $(word $(call gmsl_plus,1,$(2)),$($(1)_value)),$(__true),),\ + ,))) +contains? = $(if $(call _contains?,$(word 1,$(1)),$(call str_decode,$($(word 2,$(1))_value))),$(__true),$(__false)) + + +# +# Errors/Exceptions +# +_error = $(eval __ERROR := $(call string,$(1))) +throw = $(eval __ERROR := $(1)) + + +# +# vectors +# + +_vector = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_vect_$(hcode)) +vector = $(word 1,$(foreach new_vect,$(call _vector),$(new_vect) $(eval $(new_vect)_value := $1))) + +_vector? = $(if $(filter $(__obj_magic)_vect_%,$(1)),$(__true),) +vector? = $(if $(call _vector?,$(1)),$(__true),$(__false)) + +vector_pr_str = [$(foreach v,$(call __get_obj_values,$(1)),$(call _pr_str,$(v),$(2)))] + +# +# list (same as vectors for now) +# + +_list = $(foreach hcode,$(call __new_obj_hash_code),$(__obj_magic)_list_$(hcode)) +list = $(word 1,$(foreach new_list,$(call _list),$(new_list) $(eval $(new_list)_value := $1))) + +_list? = $(if $(filter $(__obj_magic)_list_%,$(1)),$(__true),) +list? = $(if $(call _list?,$(1)),$(__true),$(__false)) + +list_pr_str = ($(foreach v,$(call __get_obj_values,$(1)),$(call _pr_str,$(v),$(2)))) + +cons = $(word 1,$(foreach new_list,$(call _list),$(new_list) $(eval $(new_list)_value := $(strip $(word 1,$(1)) $(call __get_obj_values,$(word 2,$(1))))))) + + +# +# atoms +# +atom = $(strip \ + $(foreach hcode,$(call __new_obj_hash_code),\ + $(foreach new_atom,$(__obj_magic)_atom_$(hcode),\ + $(new_atom)\ + $(eval $(new_atom)_value := $(1))))) + +_atom? = $(if $(filter $(__obj_magic)_atom_%,$(1)),$(__true),) +atom? = $(if $(call _atom?,$(1)),$(__true),$(__false)) + +atom_pr_str = (atom $(call _pr_str,$($(1)_value),$(2))) + +deref = $($(1)_value) + +reset! = $(eval $(word 1,$(1))_value := $(word 2,$(1)))$(word 2,$(1)) + +swap! = $(foreach resp,$(call $(word 2,$(1))_value,$($(word 1,$(1))_value) $(wordlist 3,$(words $(1)),$(1))),\ + $(eval $(word 1,$(1))_value := $(resp))\ + $(resp)) + + +# +# sequence operations +# + +_sequential? = $(if $(filter $(__obj_magic)_list_% $(__obj_magic)_vect_%,$(1)),$(__true),) +sequential? = $(if $(call _sequential?,$(1)),$(__true),$(__false)) + +raw_flat = $(foreach val,$(call __get_obj_values,$(1)),$($(val))) + +_nth = $(word $(call gmsl_plus,1,$(2)),$($(1)_value)) + +nth = $(word $(call gmsl_plus,1,$(call int_decode,$($(word 2,$(1))_value))),$($(word 1,$(1))_value)) + +empty? = $(if $(call _EQ,0,$(if $(call _hash_map?,$(1)),$($(1)_size),$(words $($(1)_value)))),$(__true),$(__false)) + +concat = $(word 1,$(foreach new_list,$(call _list),$(new_list) $(eval $(new_list)_value := $(strip $(foreach lst,$1,$(call __get_obj_values,$(lst))))))) + +conj = $(word 1,$(foreach new_list,$(call __new_obj_like,$(word 1,$(1))),$(new_list) $(eval $(new_list)_value := $(strip $($(word 1,$(1))_value) $(wordlist 2,$(words $(1)),$(1)))))) + +# conj that mutates a sequence in-place to append the call arguments +_conj! = $(eval $(1)_value := $(strip $($(1)_value) $2 $3 $4 $5 $6 $7 $8 $9 $(10) $(11) $(12) $(13) $(14) $(15) $(16) $(17) $(18) $(19) $(20)))$(1) + +_count = $(strip \ + $(if $(call _hash_map?,$(1)),\ + $($(1)_size),\ + $(words $($(1)_value)))) +count = $(call number,$(call _count,$(1))) + +sfirst = $(word 1,$($(1)_value)) + +slast = $(word $(words $($(1)_value)),$($(1)_value)) + +# Creates a new vector/list of the everything after but the first +# element +srest = $(word 1,$(foreach new_list,$(call _list),$(new_list) $(eval $(new_list)_value := $(wordlist 2,$(words $($(1)_value)),$($(1)_value))))) + +# maps a make function over a list object, using mutating _conj! +_smap = $(word 1,\ + $(foreach new_list,$(call _list),\ + $(new_list)\ + $(foreach v,$(call __get_obj_values,$(2)),\ + $(call _conj!,$(new_list),$(call $(1),$(v),$(3),$(4)))))) + +# Same as _smap but returns a vector +_smap_vec = $(word 1,\ + $(foreach new_vector,$(call vector),\ + $(new_vector)\ + $(foreach v,$(call __get_obj_values,$(2)),\ + $(call _conj!,$(new_vector),$(call $(1),$(v),$(3),$(4)))))) + +# Map a function object over a list object +smap = $(strip\ + $(foreach func,$(word 1,$(1)),\ + $(foreach lst,$(word 2,$(1)),\ + $(foreach type,$(word 2,$(subst _, ,$(lst))),\ + $(foreach new_hcode,$(call __new_obj_hash_code),\ + $(foreach sz,$(words $(call __get_obj_values,$(lst))),\ + $(eval $(__obj_magic)_$(type)_$(new_hcode)_value := $(strip \ + $(foreach val,$(call __get_obj_values,$(lst)),\ + $(call $(func)_value,$(val))))))\ + $(__obj_magic)_$(type)_$(new_hcode)))))) + + + +_equal? = $(strip \ + $(foreach ot1,$(call _obj_type,$(1)),$(foreach ot2,$(call _obj_type,$(2)),\ + $(if $(or $(call _EQ,$(ot1),$(ot2)),\ + $(and $(call _sequential?,$(1)),$(call _sequential?,$(2)))),\ + $(if $(or $(call _string?,$(1)),$(call _symbol?,$(1)),$(call _number?,$(1))),\ + $(call _EQ,$($(1)_value),$($(2)_value)),\ + $(if $(or $(call _vector?,$(1)),$(call _list?,$(1)),$(call _hash_map?,$(1))),\ + $(if $(and $(call _EQ,$(call _count,$(1)),$(call _count,$(2))),\ + $(call _EQ,$(call _count,$(1)),$(words $(call gmsl_pairmap,_equal?,$(call __get_obj_values,$(1)),$(call __get_obj_values,$(2)))))),$(__true),),\ + $(call _EQ,$(1),$(2)))))))) + +equal? = $(if $(call _equal?,$(word 1,$(1)),$(word 2,$(1))),$(__true),$(__false)) + +# +# ENV +# + +# An ENV environment is a hash-map with an __outer__ reference to an +# outer environment +define BIND_ARGS +$(strip \ + $(word 1,$(1) \ + $(foreach fparam,$(call _nth,$(2),0),\ + $(if $(call _EQ,&,$($(fparam)_value)), + $(call ENV_SET,$(1),$($(call _nth,$(2),1)_value),$(strip \ + $(foreach new_list,$(call _list), + $(word 1,$(new_list) \ + $(foreach val,$(3),$(call _conj!,$(new_list),$(val))))))),\ + $(foreach val,$(word 1,$(3)),\ + $(call ENV_SET,$(1),$($(fparam)_value),$(val))\ + $(foreach left,$(call srest,$(2)),\ + $(if $(call _EQ,0,$(call _count,$(left))),\ + ,\ + $(call BIND_ARGS,$(1),$(left),$(wordlist 2,$(words $(3)),$(3)))))))))) +endef + +# Create a new ENV and optional bind values in it +# $(1): outer environment (set as a key named __outer__) +# $(2): list/vector object of bind forms +# $(3): space separated list of expressions to bind +ENV = $(strip $(foreach new_env,$(call _assoc!,$(call _hash_map),__outer__,$(if $(1),$(1),$(__nil))),$(if $(2),$(call BIND_ARGS,$(new_env),$(2),$(3)),$(new_env)))) +ENV_FIND = $(strip \ + $(if $(call _contains?,$(1),$(subst =,$(__equal),$(2))),\ + $(1),\ + $(if $(call _EQ,$(__nil),$(call _get,$(1),__outer__)),\ + ,\ + $(call ENV_FIND,$(call _get,$(1),__outer__),$(2))))) + +ENV_GET = $(foreach env,|$(call ENV_FIND,$(1),$(2))|,$(if $(call _EQ,||,$(env)),$(call _error,'$(2)' not found),$(call _get,$(strip $(subst |,,$(env))),$(subst =,$(__equal),$(2))))) + +ENV_SET = $(if $(call _assoc!,$(1),$(subst =,$(__equal),$(2)),$(3)),$(1),) + +# +# Visualize Objects in memory +# + +__var_name = $(word 2,$(subst _, ,$(1)))_$(word 3,$(subst _, ,$(1))) +__var_idx := 0 +__var_print = $(foreach v,$(1),\ + $(foreach var,$(call __var_name,$(v)),\ + $(if $(or $(call _list?,$(v)),$(call _vector?,$(v))),\ + $(info $(2)$(var):)\ + $(eval __var_idx := $(call gmsl_plus,1,$(__var_idx)))\ + $(foreach lidx,__lidx_$(__var_idx),\ + $(eval $(lidx) := 0)\ + $(foreach val,$($(v)_value),\ + $(call __var_print,$(val),$(2)$(SPACE)$(SPACE)$($(lidx)): )\ + $(eval $(lidx) := $(call gmsl_plus,1,$($(lidx)))))),\ + $(if $(call _hash_map?,$(v)),\ + $(info $(2)$(var):)\ + $(foreach vkey,$(filter $(v)_%,$(.VARIABLES)),\ + $(foreach key,$(word 4,$(subst _, ,$(vkey))),\ + $(info $(2)$(SPACE)$(SPACE)$(subst $(__equal),=,$(key)): )\ + $(call __var_print,$($(vkey)),$(2)$(SPACE)$(SPACE)$(SPACE)$(SPACE)))),\ + $(if $(call _symbol?,$(v)),\ + $(info $(2)$(var): $($(v)_value)),\ + $(if $(call _number?,$(v)),\ + $(info $(2)$(var): $(call int_decode,$($(v)_value))),\ + $(if $(call _nil?,$(v)),\ + $(info $(2)nil),\ + $(if $(call _function?,$(v)),\ + $(if $(word 6,$(value $(v)_value)),\ + $(info $(2)$(var): $(wordlist 1,5,$(value $(v)_value))...),\ + $(info $(2)$(var): $(value $(v)_value))),\ + $(info $(2)$(var): ...))))))))) + + +visualize_memory = $(foreach var,$(sort $(foreach vv,$(filter $(__obj_magic)_%,$(.VARIABLES)),$(call __var_name,$(vv)))),$(call __var_print,$(__obj_magic)_$(var))) + +# +# Namespace of type functions +# +types_ns = pr-str pr_str str str prn prn println println \ + with-meta with_meta meta meta \ + type obj_type = equal? \ + nil? nil? true? true? false? false? \ + number? number? \ + > number_gt >= number_gte < number_lt <= number_lte \ + + number_plus - number_subtract * number_multiply / number_divide \ + symbol? symbol? function? function? \ + string? string? subs subs \ + hash-map hash_map map? hash_map? assoc assoc dissoc dissoc \ + get get contains? contains? keys keys vals vals \ + throw throw \ + list list list? list? \ + vector vector vector? vector? \ + atom atom atom? atom? deref deref reset! reset! swap! swap! \ + sequential? sequential? \ + cons cons nth nth empty? empty? count count concat concat \ + conj conj first sfirst last slast rest srest \ + apply sapply map smap \ + +endif diff --git a/make/util.mk b/make/util.mk new file mode 100644 index 0000000..43923fc --- /dev/null +++ b/make/util.mk @@ -0,0 +1,72 @@ +# +# mal (Make Lisp) utility functions/definitions +# + +ifndef __mal_util_included +__mal_util_included := true + +_TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(_TOP_DIR)gmsl.mk + +SEMI := ; +COMMA := , +LCURLY := { +RCURLY := } +LPAREN := ( +RPAREN := ) +LBRACKET := [ +RBRACKET := ] +DQUOTE := "# " +SLASH := $(strip \ ) +ESC_DQUOTE := $(SLASH)$(DQUOTE) +SQUOTE := '# ' +QQUOTE := `# ` +SPACE := +SPACE += +NUMBERS := 0 1 2 3 4 5 6 7 8 9 +UNQUOTE := ~ +SPLICE_UNQUOTE := ~@ +define NEWLINE + + +endef +CARET := ^ +ATSIGN := @ + +# \u00ab +_LP := « +# \u00bb +_RP := » +# \u00ed +_LC := í +# \u00ec +_RC := ì +## \u00a7 +_SP := § +## \u00ae +_SUQ := ® +## \u015e +_DOL := Ş +## \u00b6 +_NL := ¶ +## \u00a8 +###_EDQ := ¨ + + +# +# Utility functions +# + +_EQ = $(if $(subst x$1,,x$2)$(subst x$2,,x$1),,true) + +_NOT = $(if $1,,true) + +# READ: read and parse input +str_encode = $(strip $(eval __temp := $$(subst $$$$,$(_DOL) ,$$(subst $(SPLICE_UNQUOTE),$(_SUQ) ,$$(subst $$(LPAREN),$$(_LP) ,$$(subst $$(RPAREN),$$(_RP) ,$$(subst $$(LCURLY),$$(_LC) ,$$(subst $$(RCURLY),$$(_RC) ,$$(subst $$(NEWLINE),$$(_NL) ,$$(subst $$(SPACE),$(_SP) ,$$1)))))))))$(foreach a,$(__gmsl_characters),$(eval __temp := $$(subst $$a,$$a$$(SPACE),$(__temp))))$(__temp)) + +str_decode = $(subst $(_SP),$(SPACE),$(subst $(_NL),$(NEWLINE),$(subst $(_LC),$(LCURLY),$(subst $(_RC),$(RCURLY),$(subst $(_LP),$(LPAREN),$(subst $(_RP),$(RPAREN),$(subst $(_SUQ),$(SPLICE_UNQUOTE),$(subst $(_DOL),$$,$(strip $(call gmsl_merge,,$(1))))))))))) + +# Read a whole file substituting newlines with $(_NL) +_read_file = $(subst $(_NL),$(NEWLINE),$(shell out=""; while read -r l; do out="$${out}$${l}$(_NL)"; done < $(1); echo "$$out")) + +endif |
