Commit | Line | Data |
---|---|---|
31690700 JM |
1 | # |
2 | # mal (Make Lisp) | |
3 | # | |
4 | _TOP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) | |
5 | include $(_TOP_DIR)types.mk | |
6 | include $(_TOP_DIR)reader.mk | |
ea81a808 JM |
7 | include $(_TOP_DIR)printer.mk |
8 | include $(_TOP_DIR)env.mk | |
9 | include $(_TOP_DIR)core.mk | |
31690700 JM |
10 | |
11 | SHELL := /bin/bash | |
12 | INTERACTIVE ?= yes | |
13 | EVAL_DEBUG ?= | |
14 | ||
15 | # READ: read and parse input | |
16 | define READ | |
17 | $(if $(READLINE_EOF)$(__ERROR),,$(call READ_STR,$(if $(1),$(1),$(call READLINE,"user> ")))) | |
18 | endef | |
19 | ||
20 | # EVAL: evaluate the parameter | |
db4c329a | 21 | IS_PAIR = $(if $(call _sequential?,$(1)),$(if $(call _EQ,0,$(call _count,$(1))),,true),) |
31690700 JM |
22 | |
23 | define QUASIQUOTE | |
24 | $(strip \ | |
25 | $(if $(call _NOT,$(call IS_PAIR,$(1))),\ | |
ea81a808 | 26 | $(call _list,$(call _symbol,quote) $(1)),\ |
31690700 JM |
27 | $(if $(call _EQ,unquote,$($(call _nth,$(1),0)_value)),\ |
28 | $(call _nth,$(1),1),\ | |
29 | $(if $(and $(call IS_PAIR,$(call _nth,$(1),0)),$(call _EQ,splice-unquote,$($(call _nth,$(call _nth,$(1),0),0)_value))),\ | |
ea81a808 JM |
30 | $(call _list,$(call _symbol,concat) $(call _nth,$(call _nth,$(1),0),1) $(call QUASIQUOTE,$(call srest,$(1)))),\ |
31 | $(call _list,$(call _symbol,cons) $(call QUASIQUOTE,$(call _nth,$(1),0)) $(call QUASIQUOTE,$(call srest,$(1)))))))) | |
31690700 JM |
32 | endef |
33 | ||
34 | define IS_MACRO_CALL | |
35 | $(if $(call _list?,$(1)),$(call ENV_FIND,$(2),_macro_$($(call _nth,$(1),0)_value)),) | |
36 | endef | |
37 | ||
38 | define MACROEXPAND | |
39 | $(strip $(if $(__ERROR),,\ | |
40 | $(if $(call IS_MACRO_CALL,$(1),$(2)),\ | |
41 | $(foreach mac,$(call ENV_GET,$(2),$($(call _nth,$(1),0)_value)),\ | |
42 | $(call MACROEXPAND,$(call apply,$(mac),$(call srest,$(1))),$(2))),\ | |
43 | $(1)))) | |
44 | endef | |
45 | ||
46 | define LET | |
47 | $(strip \ | |
48 | $(word 1,$(2) \ | |
49 | $(foreach var,$(call _nth,$(1),0),\ | |
50 | $(foreach val,$(call _nth,$(1),1),\ | |
51 | $(call ENV_SET,$(2),$($(var)_value),$(call EVAL,$(val),$(2)))\ | |
52 | $(foreach left,$(call srest,$(call srest,$(1))), | |
53 | $(if $(call _EQ,0,$(call _count,$(left))),\ | |
54 | ,\ | |
55 | $(call LET,$(left),$(2)))))))) | |
56 | endef | |
57 | ||
58 | define EVAL_AST | |
59 | $(strip \ | |
60 | $(and $(EVAL_DEBUG),$(info EVAL_AST: $(call _pr_str,$(1))))\ | |
61 | $(if $(call _symbol?,$(1)),\ | |
62 | $(foreach key,$($(1)_value),\ | |
63 | $(call ENV_GET,$(2),$(key))),\ | |
64 | $(if $(call _list?,$(1)),\ | |
65 | $(call _smap,EVAL,$(1),$(2)),\ | |
66 | $(if $(call _vector?,$(1)),\ | |
67 | $(call _smap_vec,EVAL,$(1),$(2)),\ | |
68 | $(if $(call _hash_map?,$(1)),\ | |
69 | $(foreach new_hmap,$(call __new_obj,hmap),\ | |
70 | $(foreach v,$(call __get_obj_values,$(1)),\ | |
71 | $(eval $(v:$(1)_%=$(new_hmap)_%) := $(call EVAL,$($(v)),$(2))))\ | |
72 | $(eval $(new_hmap)_size := $($(1)_size))\ | |
73 | $(new_hmap)),\ | |
74 | $(1)))))) | |
75 | endef | |
76 | ||
77 | define EVAL_INVOKE | |
78 | $(if $(__ERROR),,\ | |
79 | $(and $(EVAL_DEBUG),$(info EVAL_INVOKE: $(call _pr_str,$(1)))) | |
80 | $(foreach a0,$(call _nth,$(1),0),\ | |
81 | $(if $(call _EQ,def!,$($(a0)_value)),\ | |
82 | $(foreach a1,$(call _nth,$(1),1),\ | |
83 | $(foreach a2,$(call _nth,$(1),2),\ | |
84 | $(foreach res,$(call EVAL,$(a2),$(2)),\ | |
85 | $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ | |
86 | $(if $(call _EQ,let*,$($(a0)_value)),\ | |
87 | $(foreach a1,$(call _nth,$(1),1),\ | |
88 | $(foreach a2,$(call _nth,$(1),2),\ | |
89 | $(call EVAL,$(a2),$(call LET,$(a1),$(call ENV,$(2)))))),\ | |
90 | $(if $(call _EQ,quote,$($(a0)_value)),\ | |
91 | $(call _nth,$(1),1),\ | |
92 | $(if $(call _EQ,quasiquote,$($(a0)_value)),\ | |
93 | $(call EVAL,$(call QUASIQUOTE,$(call _nth,$(1),1)),$(2)),\ | |
94 | $(if $(call _EQ,defmacro!,$($(a0)_value)),\ | |
95 | $(foreach a1,$(call _nth,$(1),1),\ | |
96 | $(foreach a2,$(call _nth,$(1),2),\ | |
97 | $(foreach res,$(call EVAL,$(a2),$(2)),\ | |
98 | $(if $(call ENV_SET,$(2),_macro_$($(a1)_value),true),,)\ | |
99 | $(if $(call ENV_SET,$(2),$($(a1)_value),$(res)),$(res),)))),\ | |
100 | $(if $(call _EQ,macroexpand,$($(a0)_value)),\ | |
101 | $(call MACROEXPAND,$(call _nth,$(1),1),$(2)),\ | |
102 | $(if $(call _EQ,make*,$($(a0)_value)),\ | |
103 | $(foreach a1,$(call _nth,$(1),1),\ | |
104 | $(and $(EVAL_DEBUG),$(info make*: $$(eval __result := $(call str_decode,$(value $(a1)_value)))))\ | |
cc021efe | 105 | $(eval __result := $(call str_decode,$(value $(a1)_value)))$(call _string,$(__result))),\ |
31690700 JM |
106 | $(if $(call _EQ,try*,$($(a0)_value)),\ |
107 | $(foreach a1,$(call _nth,$(1),1),\ | |
108 | $(foreach res,$(call EVAL,$(a1),$(2)),\ | |
109 | $(if $(__ERROR),\ | |
110 | $(foreach a2,$(call _nth,$(1),2),\ | |
111 | $(foreach a20,$(call _nth,$(a2),0),\ | |
112 | $(if $(call _EQ,catch*,$($(a20)_value)),\ | |
113 | $(foreach a21,$(call _nth,$(a2),1),\ | |
114 | $(foreach a22,$(call _nth,$(a2),2),\ | |
ea81a808 | 115 | $(foreach binds,$(call _list,$(a21)),\ |
31690700 JM |
116 | $(foreach catch_env,$(call ENV,$(2),$(binds),$(__ERROR)),\ |
117 | $(eval __ERROR :=)\ | |
118 | $(call EVAL,$(a22),$(catch_env)))))),\ | |
119 | $(res)))),\ | |
120 | $(res)))),\ | |
121 | $(if $(call _EQ,do,$($(a0)_value)),\ | |
122 | $(call slast,$(call EVAL_AST,$(call srest,$(1)),$(2))),\ | |
123 | $(if $(call _EQ,if,$($(a0)_value)),\ | |
124 | $(foreach a1,$(call _nth,$(1),1),\ | |
125 | $(foreach a2,$(call _nth,$(1),2),\ | |
126 | $(foreach cond,$(call EVAL,$(a1),$(2)),\ | |
127 | $(if $(or $(call _EQ,$(__nil),$(cond)),$(call _EQ,$(__false),$(cond))),\ | |
128 | $(foreach a3,$(call _nth,$(1),3),$(call EVAL,$(a3),$(2))),\ | |
129 | $(call EVAL,$(a2),$(2)))))),\ | |
130 | $(if $(call _EQ,fn*,$($(a0)_value)),\ | |
131 | $(foreach a1,$(call _nth,$(1),1),\ | |
132 | $(foreach a2,$(call _nth,$(1),2),\ | |
ea81a808 | 133 | $(call _function,$$(call EVAL,$(a2),$$(call ENV,$(2),$(a1),$$1))))),\ |
31690700 JM |
134 | $(foreach el,$(call EVAL_AST,$(1),$(2)),\ |
135 | $(and $(EVAL_DEBUG),$(info invoke: $(call _pr_str,$(el))))\ | |
136 | $(foreach f,$(call sfirst,$(el)),\ | |
137 | $(foreach args,$(call srest,$(el)),\ | |
138 | $(call apply,$(f),$(args)))))))))))))))))) | |
139 | endef | |
140 | ||
141 | define EVAL | |
142 | $(strip $(if $(__ERROR),,\ | |
143 | $(and $(EVAL_DEBUG),$(info EVAL: $(call _pr_str,$(1))))\ | |
144 | $(if $(call _list?,$(1)),\ | |
145 | $(foreach ast,$(call MACROEXPAND,$(1),$(2)), | |
146 | $(if $(call _list?,$(ast)),\ | |
147 | $(word 1,$(strip $(call EVAL_INVOKE,$(ast),$(2)) $(__nil))),\ | |
148 | $(ast))),\ | |
149 | $(call EVAL_AST,$(1),$(2))))) | |
150 | endef | |
151 | ||
152 | ||
153 | # PRINT: | |
154 | define PRINT | |
155 | $(if $(__ERROR),Error: $(call _pr_str,$(__ERROR),yes),$(if $(1),$(call _pr_str,$(1),yes)))$(if $(__ERROR),$(eval __ERROR :=),) | |
156 | endef | |
157 | ||
158 | # REPL: | |
159 | REPL_ENV := $(call ENV) | |
160 | REP = $(call PRINT,$(strip $(call EVAL,$(strip $(call READ,$(1))),$(REPL_ENV)))) | |
161 | REPL = $(info $(call REP,$(call READLINE,"user> ")))$(if $(READLINE_EOF),,$(call REPL)) | |
162 | ||
8cb5cda4 | 163 | # core.mk: defined using Make |
ea81a808 | 164 | _fref = $(eval REPL_ENV := $(call ENV_SET,$(REPL_ENV),$(1),$(call _function,$$(call $(2),$$1)))) |
ea81a808 JM |
165 | _import_core = $(if $(strip $(1)),$(call _fref,$(word 1,$(1)),$(word 2,$(1)))$(call _import_core,$(wordlist 3,$(words $(1)),$(1))),) |
166 | $(call _import_core,$(core_ns)) | |
8cb5cda4 | 167 | REPL_ENV := $(call ENV_SET,$(REPL_ENV),eval,$(call _function,$$(call EVAL,$$(1),$$(REPL_ENV)))) |
86b689f3 JM |
168 | _argv := $(call _list) |
169 | REPL_ENV := $(call ENV_SET,$(REPL_ENV),*ARGV*,$(_argv)) | |
31690700 | 170 | |
8cb5cda4 | 171 | # core.mal: defined in terms of the language itself |
db4c329a | 172 | $(call do,$(call REP, (def! *host-language* "make") )) |
31690700 | 173 | $(call do,$(call REP, (def! not (fn* (a) (if a false true))) )) |
8cb5cda4 | 174 | $(call do,$(call REP, (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) )) |
31690700 JM |
175 | $(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))))))) )) |
176 | $(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)))))))) )) | |
31690700 JM |
177 | |
178 | # Load and eval any files specified on the command line | |
179 | $(if $(MAKECMDGOALS),\ | |
86b689f3 JM |
180 | $(foreach arg,$(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)),\ |
181 | $(call do,$(call _conj!,$(_argv),$(call _string,$(arg)))))\ | |
182 | $(call do,$(call REP, (load-file "$(word 1,$(MAKECMDGOALS))") )) \ | |
31690700 | 183 | $(eval INTERACTIVE :=),) |
31690700 | 184 | |
86b689f3 JM |
185 | # repl loop |
186 | $(if $(strip $(INTERACTIVE)),\ | |
187 | $(call do,$(call REP, (println (str "Mal [" *host-language* "]")) )) \ | |
188 | $(call REPL)) | |
cc021efe JM |
189 | |
190 | .PHONY: none $(MAKECMDGOALS) | |
191 | none $(MAKECMDGOALS): | |
192 | @true |