14 read_str | read_form | .value;
16 def recurseflip(x; y):
19 def TCOWrap(env; retenv; continue):
23 ret_env: (if retenv != null then (retenv | setpath(["atoms"]; env.atoms)) else retenv end),
24 finish: (continue | not),
25 cont: true # set inside
35 if .kind == "symbol" then
43 .value as $value | null |
44 if ($value[0] | _symbol_v("unquote")) then
47 if isPair($value[0]) and ($value[0].value[0] | _symbol_v("splice-unquote")) then
49 [$value[0].value[1]] +
50 [($value[1:] | wrap("list") | quasiquote)] | wrap("list")
53 [($value[0] | quasiquote)] +
54 [($value[1:] | wrap("list") | quasiquote)] | wrap("list")
62 def set_macro_function:
63 if .kind != "function" then
64 jqmal_error("expected a function to be defined by defmacro!")
69 def is_macro_call(env):
70 if .kind != "list" then
73 if (.value|first.kind == "symbol") then
74 env_req(env; .value|first.value)
75 | if .kind != "function" then
87 .env as $env | .expr | EVAL($env);
89 def _interpret($_menv):
90 reduce .value[] as $elem (
91 {env: $_menv, val: []};
92 . as $dot | $elem | EVAL($dot.env) as $eval_env |
93 ($dot.env | setpath(["atoms"]; $eval_env.env.atoms)) as $_menv |
94 {env: $_menv, val: ($dot.val + [$eval_env.expr])}
95 ) | . as $expr | $expr.val | first |
96 interpret($expr.val[1:]; $expr.env; _eval_here);
101 [ while(is_macro_call(env | unwrapCurrentEnv);
103 | ($dot.value[0] | EVAL(env).expr) as $fn
104 | $dot.value[1:] as $args
106 | interpret($args; env; _eval_here).expr) // . ]
108 | if is_macro_call(env | unwrapCurrentEnv) then
110 | ($dot.value[0] | EVAL(env).expr) as $fn
111 | $dot.value[1:] as $args
113 | interpret($args; env; _eval_here).expr
120 .env as $env | .list as $list |
121 if $list|length == 0 then
126 $elem.value.value | EVAL($env) as $resv |
130 value: { kkind: $elem.value.kkind, value: $resv.expr }
134 ({env: $resv.env, list: $rest} | hmap_with_env)
137 .env as $env | .list as $list |
138 if $list|length == 0 then
143 $elem | EVAL($env) as $resv |
144 { value: $resv.expr, env: env },
145 ({env: $resv.env, list: $rest} | map_with_env)
148 (select(.kind == "vector") |
149 if .value|length == 0 then
155 [ { env: env, list: .value } | map_with_env ] as $res |
158 value: $res | map(.value)
162 (select(.kind == "hashmap") |
163 [ { env: env, list: (.value | to_entries) } | hmap_with_env ] as $res |
166 value: $res | map(.value) | from_entries
169 (select(.kind == "function") |
170 .# return this unchanged, since it can only be applied to
172 (select(.kind == "symbol") |
173 .value | env_get(env | unwrapCurrentEnv)
177 | { env: env, ast: ., cont: true, finish: false, ret_env: null }
178 | [ recurseflip(.cont;
183 (.ret_env//.env) as $_retenv
184 | .ret_env as $_orig_retenv
187 | $_menv | unwrapCurrentEnv as $currentEnv # unwrap env "package"
188 | $_menv | unwrapReplEnv as $replEnv # -
191 (select(.kind == "list") |
192 macroexpand($_menv) |
193 if .kind != "list" then
194 eval_ast($_menv) | TCOWrap($_menv; $_orig_retenv; false)
196 if .value | length == 0 then
197 . | TCOWrap($_menv; $_orig_retenv; false)
201 .value | select(.[0].value == "def!") as $value |
202 ($value[2] | EVAL($_menv)) as $evval |
203 addToEnv($evval; $value[1].value) as $val |
204 $val.expr | TCOWrap($val.env; $_orig_retenv; false)
207 .value | select(.[0].value == "defmacro!") as $value |
208 ($value[2] | EVAL($_menv) | (.expr |= set_macro_function)) as $evval |
209 addToEnv($evval; $value[1].value) as $val |
210 $val.expr | TCOWrap($val.env; $_orig_retenv; false)
213 .value | select(.[0].value == "let*") as $value |
214 ($currentEnv | pureChildEnv | wrapEnv($replEnv; $_menv.atoms)) as $_menv |
215 (reduce ($value[1].value | nwise(2)) as $xvalue (
217 . as $env | $xvalue[1] | EVAL($env) as $expenv |
218 env_set_($expenv.env; $xvalue[0].value; $expenv.expr))) as $env
219 | $value[2] | TCOWrap($env; $_retenv; true)
222 .value | select(.[0].value == "do") as $value |
223 (reduce ($value[1:][]) as $xvalue (
224 { env: $_menv, expr: {kind:"nil"} };
225 .env as $env | $xvalue | EVAL($env)
226 )) | . as $ex | .expr | TCOWrap($ex.env; $_orig_retenv; false)
229 .value | select(.[0].value == "try*") as $value |
231 $value[1] | EVAL($_menv) as $exp | $exp.expr | TCOWrap($exp.env; $_orig_retenv; false)
232 ) catch ( . as $exc |
234 if ($value[2].value[0] | _symbol_v("catch*")) then
235 (if ($exc | is_jqmal_error) then
247 $value[2].value[2] | EVAL($currentEnv | childEnv([$value[2].value[1].value]; [$exc]) | wrapEnv($replEnv; $_menv.atoms)) as $ex |
248 $ex.expr | TCOWrap($ex.env; $_retenv; false)
258 .value | select(.[0].value == "if") as $value |
259 $value[1] | EVAL($_menv) as $condenv |
260 (if (["false", "nil"] | contains([$condenv.expr.kind])) then
261 ($value[3] // {kind:"nil"})
264 end) | TCOWrap($condenv.env; $_orig_retenv; true)
267 .value | select(.[0].value == "fn*") as $value |
269 $value[1].value | map(.value) as $binds |
270 ($value[2] | find_free_references($currentEnv | env_dump_keys + $binds)) as $free_referencess | {
273 env: ($_menv | env_remove_references($free_referencess)),
275 names: [], # we can't do that circular reference thing
276 free_referencess: $free_referencess, # for dynamically scoped variables
278 } | TCOWrap($_menv; $_orig_retenv; false)
281 .value | select(.[0].value == "quote") as $value |
282 $value[1] | TCOWrap($_menv; $_orig_retenv; false)
285 .value | select(.[0].value == "quasiquote") as $value |
286 $value[1] | quasiquote | TCOWrap($_menv; $_orig_retenv; true)
289 .value | select(.[0].value == "macroexpand") as $value |
290 $value[1] | macroexpand(env) | TCOWrap($_menv; $_orig_retenv; false)
293 . as $dot | _interpret($_menv) as $exprenv |
294 $exprenv.expr | TCOWrap($exprenv.env; $_orig_retenv; false)
296 TCOWrap($_menv; $_orig_retenv; false)
301 (eval_ast($_menv) | TCOWrap($_menv; $_orig_retenv; false))
305 | ($result.ret_env // $result.env) as $env
313 READ | EVAL(env) as $expenv |
314 if $expenv.expr != null then
315 $expenv.expr | PRINT($expenv.env)
318 end | addEnv($expenv.env);
321 ("user> " | _print) |
322 (read_line | rep(env));
324 # we don't have no indirect functions, so we'll have to interpret the old way
330 kind: "fn", # native function
332 function: "number_add"
335 kind: "fn", # native function
337 function: "number_sub"
340 kind: "fn", # native function
342 function: "number_mul"
345 kind: "fn", # native function
347 function: "number_div"
360 (.env as $env | try repl_($env) catch addEnv($env)) as $expenv |
364 env: ($expenv.env // .env)
366 {stop: false, env: env} | xrepl | if .value then (.value | _display) else empty end;
369 . as $env | expr | rep($env) | .env;
372 . as $env | expr | rep($env) | .expr;
377 | eval_ign("(def! not (fn* (a) (if a false true)))")
378 | eval_ign("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))))")
379 | eval_ign("(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)))))))")
383 if $ARGS.positional|length > 0 then
385 env_set_($env; "*ARGV*"; $ARGS.positional[1:] | map(wrap("string")) | wrap("list")) |
386 eval_val("(load-file \($ARGS.positional[0] | tojson))") |
389 repl( getEnv as $env | env_set_($env; "*ARGV*"; [] | wrap("list")) )