--- /dev/null
+include "utils";
+
+def arg_check(args):
+ if .inputs != (args|length) then
+ jqmal_error("Invalid number of arguments (expected \(.inputs) got \(args|length): \(args))")
+ else
+ .
+ end;
+
+
+def interpret(arguments; env):
+ 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("Unknown function \(.function)");
\ No newline at end of file
(select(.kind == "string") | .value | tojson) //
(select(.kind == "keyword") | ":\(.value)") //
(select(.kind == "number") | .value | tostring) //
- (select(.kind == "list") | .values | map(pr_str) | join(" ") | "(\(.))") //
- (select(.kind == "vector") | .values | map(pr_str) | join(" ") | "[\(.)]") //
- (select(.kind == "hashmap") | .values | to_entries | _reconstruct_hash | add // [] | map(pr_str) | join(" ") | "{\(.)}") //
+ (select(.kind == "list") | .value | map(pr_str) | join(" ") | "(\(.))") //
+ (select(.kind == "vector") | .value | map(pr_str) | join(" ") | "[\(.)]") //
+ (select(.kind == "hashmap") | .value | to_entries | _reconstruct_hash | add // [] | map(pr_str) | join(" ") | "{\(.)}") //
(select(.kind == "nil") | "nil") //
(select(.kind == "true") | "true") //
(select(.kind == "false") | "false") //
# read_list
else
if $lookahead | test("^\\(") then
- [ (.tokens |= .[1:]) | {tokens: .tokens, values: [], finish: false} | (until(.finish;
+ [ (.tokens |= .[1:]) | {tokens: .tokens, value: [], finish: false} | (until(.finish;
if try (.tokens | first | test("^\\)")) catch true then
.finish |= true
else
. as $orig | read_form_(depth+1) as $res | {
tokens: $res.tokens,
- values: ($orig.values + [$res.value]),
+ value: ($orig.value + [$res.value]),
finish: $orig.finish
}
end)) ] | map(select(.tokens)) | last as $result |
tokens: $result.tokens[1:],
value: {
kind: "list",
- values: $result.values
+ value: $result.value
},
}
end
# read_list '['
else if $lookahead | test("^\\[") then
- [ (.tokens |= .[1:]) | {tokens: .tokens, values: [], finish: false} | (until(.finish;
+ [ (.tokens |= .[1:]) | {tokens: .tokens, value: [], finish: false} | (until(.finish;
if try (.tokens | first | test("^\\]")) catch true then
.finish |= true
else
. as $orig | read_form_(depth+1) as $res | {
tokens: $res.tokens,
- values: ($orig.values + [$res.value]),
+ value: ($orig.value + [$res.value]),
finish: $orig.finish
}
end)) ] | map(select(.tokens)) | last as $result |
tokens: $result.tokens[1:],
value: {
kind: "vector",
- values: $result.values
+ value: $result.value
},
}
end
# read_list '{'
else if $lookahead | test("^\\{") then
- [ (.tokens |= .[1:]) | {tokens: .tokens, values: [], finish: false} | (until(.finish;
+ [ (.tokens |= .[1:]) | {tokens: .tokens, value: [], finish: false} | (until(.finish;
if try (.tokens | first | test("^\\}")) catch true then
.finish |= true
else
. as $orig | read_form_(depth+1) as $res | {
tokens: $res.tokens,
- values: ($orig.values + [$res.value]),
+ value: ($orig.value + [$res.value]),
finish: $orig.finish
}
end)) ] | map(select(.tokens)) | last as $result |
if $result.tokens | first != "}" then
jqmal_error("unbalanced braces in \($result.tokens)")
else
- if $result.values | length % 2 == 1 then
+ if $result.value | length % 2 == 1 then
# odd number of elements not allowed
jqmal_error("Odd number of parameters to assoc")
else
tokens: $result.tokens[1:],
value: {
kind: "hashmap",
- values:
- [ $result.values |
+ value:
+ [ $result.value |
nwise(2) |
try {
key: (.[0] | extract_string),
tokens: .tokens,
value: {
kind: "list",
- values: [
+ value: [
{
kind: "symbol",
value: "quote"
tokens: .tokens,
value: {
kind: "list",
- values: [
+ value: [
{
kind: "symbol",
value: "quasiquote"
tokens: .tokens,
value: {
kind: "list",
- values: [
+ value: [
{
kind: "symbol",
value: "unquote"
tokens: .tokens,
value: {
kind: "list",
- values: [
+ value: [
{
kind: "symbol",
value: "splice-unquote"
tokens: .tokens,
value: {
kind: "list",
- values: [
+ value: [
{
kind: "symbol",
value: "deref"
tokens: $value.tokens,
value: {
kind: "list",
- values: [
+ value: [
{
kind: "symbol",
value: "with-meta"
| input;
def READ:
- read_str;
+ read_str | read_form | .value;
def EVAL:
- read_form | .value;
+ .;
def PRINT:
pr_str;
--- /dev/null
+include "reader";
+include "printer";
+include "utils";
+include "interp";
+
+def read_line:
+ . as $in
+ | label $top
+ | input;
+
+def READ:
+ read_str | read_form | .value;
+
+def lookup(env):
+ env.environment[.] //
+ if env.parent then
+ lookup(env.parent)
+ else
+ jqmal_error("Symbol \(.) not found")
+ end;
+
+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> " | stderr) |
+ (read_line | rep(env));
+
+def childEnv(binds; value):
+ {
+ parent: .,
+ environment: [binds, value] | transpose | map({(.[0]): .[1]}) | from_entries
+ };
+
+# we don't have no indirect functions, so we'll have to interpret the old way
+def replEnv:
+ {
+ parent: null,
+ environment: {
+ "+": {
+ inputs: 2,
+ function: "number_add"
+ },
+ "-": {
+ inputs: 2,
+ function: "number_sub"
+ },
+ "*": {
+ inputs: 2,
+ function: "number_mul"
+ },
+ "/": {
+ 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 else empty end;
+
+repl(replEnv)
\ No newline at end of file
--- /dev/null
+def nwise(n):
+ def _nwise:
+ if length <= n then
+ .
+ else
+ .[0:n], (.[n:] | _nwise)
+ end;
+ _nwise;
+
+def jqmal_error(e):
+ error("JqMAL :: " + e);
+
+def is_jqmal_error:
+ startswith("JqMAL :: ");
+
+def wrap(kind):
+ {
+ kind: kind,
+ value: .
+ };
\ No newline at end of file