8 if (abs(.inputs) - 1) > (args | length) then
9 jqmal_error("Invalid number of arguments (expected at least \(abs(.inputs) - 1), got \(args|length))")
13 else if .inputs != (args|length) then
14 jqmal_error("Invalid number of arguments (expected \(.inputs), got \(args|length))")
19 def extractReplEnv(env):
23 env | .currentEnv // .;
25 def updateReplEnv(renv):
34 ({ env: ., path: [] } | findpath) as $path |
37 def extractCurrentReplEnv(env):
46 if env.currentEnv != null then
47 ({ env: env.currentEnv, path: [] } | findpath) as $path |
48 env.currentEnv | getpath($path)
53 def extractAtoms(env):
56 def addFrees(newEnv; frees):
58 | reduce frees[] as $free (
61 | extractEnv(newEnv) as $env
62 | env_req($env; $free) as $lookup
63 | if $lookup != null then
64 env_set_(.; $free; $lookup)
71 def interpret(arguments; env; _eval):
72 extractReplEnv(env) as $replEnv |
73 extractAtoms(env) as $envAtoms |
74 (if $DEBUG then _debug("INTERP: \(. | pr_str(env))") else . end) |
75 (select(.kind == "fn") |
76 arg_check(arguments) |
77 (select(.function == "eval") |
79 { expr: arguments[0], env: $replEnv|wrapEnv($replEnv; $envAtoms) }
82 | extractReplEnv($xenv) as $xreplenv
84 ["env", "currentEnv"];
85 extractEnv(env) | updateReplEnv($xreplenv))
87 (select(.function == "reset!") |
88 # env modifying function
89 arguments[0].identity as $id |
90 ($envAtoms | setpath([$id]; arguments[1])) as $envAtoms |
91 arguments[1] | addEnv(env | setpath(["atoms"]; $envAtoms))
93 (select(.function == "swap!") |
94 # env modifying function
95 arguments[0].identity as $id |
96 $envAtoms[$id] as $initValue |
97 arguments[1] as $function |
98 ([$initValue] + arguments[2:]) as $args |
99 ($function | interpret($args; env; _eval)) as $newEnvValue |
100 ($envAtoms | setpath([$id]; $newEnvValue.expr)) as $envAtoms |
101 $newEnvValue.expr | addEnv(env | setpath(["atoms"]; $envAtoms))
102 ) // (select(.function == "atom") |
103 (now|tostring) as $id |
104 {kind: "atom", identity: $id} as $value |
105 ($envAtoms | setpath([$id]; arguments[0])) as $envAtoms |
106 $value | addEnv(env | setpath(["atoms"]; $envAtoms))
107 ) // (select(.function == "deref") |
108 $envAtoms[arguments[0].identity] | addEnv(env)
110 (select(.function == "apply") |
111 # (apply F ...T A) -> (F ...T ...A)
113 | ($args|first) as $F
114 | ($args|last.value) as $A
116 | $F | interpret([$T[], $A[]]; env; _eval)
118 (select(.function == "map") |
122 | (reduce $L[] as $elem (
125 ($F | interpret([$elem]; $dot.env; _eval)) as $val |
127 val: (.val + [$val.expr]),
128 env: (.env | setpath(["atoms"]; $val.env.atoms))
131 | $ex.val | wrap("list") | addEnv($ex.env)
133 (core_interp(arguments; env) | addEnv(env))
135 (select(.kind == "function") as $fn |
137 (.body | pr_str(env)) as $src |
138 # _debug("INTERP " + $src) |
139 # _debug("FREES " + ($fn.free_referencess | tostring)) |
140 env_setfallback(extractEnv(.env | addFrees(env; $fn.free_referencess)); extractEnv(env)) | childEnv($fn.binds; arguments) as $fnEnv |
141 # tell it about its surroundings
142 (reduce $fn.free_referencess[] as $name (
144 . as $env | try env_set_(
147 $name | env_get(env) | . as $xvalue
148 | if $xvalue.kind == "function" then
149 setpath(["free_referencess"]; $fn.free_referencess)
153 ) catch $env)) as $fnEnv |
154 # tell it about itself
155 env_multiset($fnEnv; $fn.names; $fn) as $fnEnv |
157 env: env_multiset($fnEnv; $fn.names; $fn)
158 | wrapEnv($replEnv; $envAtoms),
162 # | _debug("FNEXEC " + (.expr | pr_str) + " " + (env_req($dot.env; $fn.binds[0]) | pr_str))
165 | (extractReplEnv($envexp.env)) as $xreplenv
170 | updateReplEnv($xreplenv)
171 | wrapEnv($xreplenv; $envexp.env.atoms)
174 # | _debug("FNPOST " + (.expr | pr_str) + " " + (env_req($dot.expr.env; $fn.binds[0]) | pr_str))
175 # | _debug("INTERP " + $src + " = " + (.expr|pr_str))
177 jqmal_error("Unsupported function kind \(.kind)");