11 read_str | read_form | .value;
13 # Environment functions
21 def env_set(env; $key; $value):
24 environment: (env.environment + (env.environment | .[$key] |= $value)) # merge together, as .environment[key] |= value does not work
28 if env.environment[.] == null then
38 def addToEnv(envexp; name):
41 env: env_set(envexp.env; name; envexp.expr)
45 . as $key | $key | env_find(env).environment[$key] as $value |
46 if $value == null then
47 jqmal_error("'\($key)' not found")
61 if .inputs != (args|length) then
62 jqmal_error("Invalid number of arguments (expected \(.inputs), got \(args|length))")
67 def interpret(arguments; env):
68 (select(.kind == "fn") |
69 arg_check(arguments) |
71 select(.function == "number_add") |
72 arguments | map(.value) | .[0] + .[1] | wrap("number")
74 select(.function == "number_sub") |
75 arguments | map(.value) | .[0] - .[1] | wrap("number")
77 select(.function == "number_mul") |
78 arguments | map(.value) | .[0] * .[1] | wrap("number")
80 select(.function == "number_div") |
81 arguments | map(.value) | .[0] / .[1] | wrap("number")
84 jqmal_error("Unsupported native function kind \(.kind)");
88 .env as $env | .list as $list |
89 if $list|length == 0 then
94 $elem[1] | EVAL($env) as $resv |
95 { value: [$elem[0], $resv.expr], env: env },
96 ({env: $resv.env, list: $rest} | hmap_with_env)
99 .env as $env | .list as $list |
100 if $list|length == 0 then
105 $elem | EVAL($env) as $resv |
106 { value: $resv.expr, env: env },
107 ({env: $resv.env, list: $rest} | map_with_env)
109 (select(.kind == "list") |
110 if .value | length == 0 then
115 .value | select(.[0].value == "def!") as $value |
116 ($value[2] | EVAL(env)) as $evval |
117 addToEnv($evval; $value[1].value)
120 .value | select(.[0].value == "let*") as $value |
121 (env | pureChildEnv) as $subenv |
122 (reduce ($value[1].value | nwise(2)) as $xvalue (
124 . as $env | $xvalue[1] | EVAL($env) as $expenv |
125 env_set($expenv.env; $xvalue[0].value; $expenv.expr))) as $env
126 | $value[2] | { expr: EVAL($env).expr, env: env }
129 reduce .value[] as $elem (
131 . as $dot | $elem | EVAL(env) as $eval_env |
132 ($dot + [$eval_env.expr])
133 ) | { expr: ., env: env } as $ev
135 interpret($ev.expr[1:]; $ev.env)
141 (select(.kind == "vector") |
142 [ { env: env, list: .value } | map_with_env ] as $res |
145 value: $res | map(.value)
146 } | addEnv($res | last.env)
148 (select(.kind == "hashmap") |
149 [ { env: env, list: .value | to_entries } | hmap_with_env ] as $res |
152 value: $res | map(.value) | from_entries
153 } | addEnv($res | last.env)
155 (select(.kind == "symbol") |
156 .value | env_get(env) | addEnv(env)
163 READ | EVAL(env) as $expenv |
164 if $expenv.expr != null then
168 end | addEnv($expenv.env);
171 ("user> " | _print) |
172 (read_line | rep(env));
174 def childEnv(binds; value):
177 environment: [binds, value] | transpose | map({(.[0]): .[1]}) | from_entries
180 # we don't have no indirect functions, so we'll have to interpret the old way
186 kind: "fn", # native function
188 function: "number_add"
191 kind: "fn", # native function
193 function: "number_sub"
196 kind: "fn", # native function
198 function: "number_mul"
201 kind: "fn", # native function
203 function: "number_div"
210 (.env as $env | try repl_($env) catch addEnv($env)) as $expenv |
214 env: ($expenv.env // .env)
216 {stop: false, env: env} | xrepl | if .value then (.value | _display) else empty end;