mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 21:57:38 +03:00
- Reorder README to have implementation list after "learning tool" bullet. - This also moves tests/ and libs/ into impls. It would be preferrable to have these directories at the top level. However, this causes difficulties with the wasm implementations which need pre-open directories and have trouble with paths starting with "../../". So in lieu of that, symlink those directories to the top-level. - Move the run_argv_test.sh script into the tests directory for general hygiene.
121 lines
2.9 KiB
Plaintext
121 lines
2.9 KiB
Plaintext
include "reader";
|
|
include "printer";
|
|
include "utils";
|
|
|
|
def read_line:
|
|
. as $in
|
|
| label $top
|
|
| _readline;
|
|
|
|
def READ:
|
|
read_str | read_form | .value;
|
|
|
|
def lookup(env):
|
|
env[.] //
|
|
jqmal_error("'\(.)' not found");
|
|
|
|
def arg_check(args):
|
|
if .inputs != (args|length) then
|
|
jqmal_error("Invalid number of arguments (expected \(.inputs), got \(args|length))")
|
|
else
|
|
.
|
|
end;
|
|
|
|
def interpret(arguments; env):
|
|
(select(.kind == "fn") |
|
|
arg_check(arguments) |
|
|
(
|
|
select(.function == "number_add") |
|
|
arguments | map(.value) | .[0] + .[1] | wrap("number")
|
|
) // (
|
|
select(.function == "number_sub") |
|
|
arguments | map(.value) | .[0] - .[1] | wrap("number")
|
|
) // (
|
|
select(.function == "number_mul") |
|
|
arguments | map(.value) | .[0] * .[1] | wrap("number")
|
|
) // (
|
|
select(.function == "number_div") |
|
|
arguments | map(.value) | .[0] / .[1] | wrap("number")
|
|
)
|
|
) //
|
|
jqmal_error("Unsupported native function kind \(.kind)");
|
|
|
|
def EVAL(env):
|
|
def eval_ast:
|
|
(select(.kind == "symbol") | .value | lookup(env)) //
|
|
(select(.kind == "list") | {
|
|
kind: "list",
|
|
value: .value | map(EVAL(env))
|
|
}) // .;
|
|
(select(.kind == "list") |
|
|
if .value | length == 0 then
|
|
.
|
|
else
|
|
eval_ast|.value as $evald | $evald | first | interpret($evald[1:]; env)
|
|
end
|
|
) //
|
|
(select(.kind == "vector") |
|
|
{
|
|
kind: "vector",
|
|
value: .value|map(EVAL(env))
|
|
}
|
|
) //
|
|
(select(.kind == "hashmap") |
|
|
{
|
|
kind: "hashmap",
|
|
value: .value|map_values(.value |= EVAL(env))
|
|
}
|
|
) // eval_ast;
|
|
|
|
def PRINT:
|
|
pr_str;
|
|
|
|
def rep(env):
|
|
READ | EVAL(env) |
|
|
if . != null then
|
|
PRINT
|
|
else
|
|
null
|
|
end;
|
|
|
|
def repl_(env):
|
|
("user> " | _print) |
|
|
(read_line | rep(env));
|
|
|
|
# we don't have no indirect functions, so we'll have to interpret the old way
|
|
def replEnv:
|
|
{
|
|
"+": {
|
|
kind: "fn", # native function
|
|
inputs: 2,
|
|
function: "number_add"
|
|
},
|
|
"-": {
|
|
kind: "fn", # native function
|
|
inputs: 2,
|
|
function: "number_sub"
|
|
},
|
|
"*": {
|
|
kind: "fn", # native function
|
|
inputs: 2,
|
|
function: "number_mul"
|
|
},
|
|
"/": {
|
|
kind: "fn", # native function
|
|
inputs: 2,
|
|
function: "number_div"
|
|
},
|
|
};
|
|
|
|
def repl(env):
|
|
{continue: true} | while(
|
|
.continue;
|
|
try {value: repl_(env), continue: true}
|
|
catch
|
|
if is_jqmal_error then
|
|
{value: "Error: \(.)", continue: true}
|
|
else
|
|
{value: ., continue: false}
|
|
end) | if .value then .value|_display else empty end;
|
|
|
|
repl(replEnv) |