fix weird interaction between let* and fn* and atoms
authorAnotherTest <ali.mpfard@gmail.com>
Thu, 9 Jan 2020 16:07:24 +0000 (19:37 +0330)
committerAnotherTest <ali.mpfard@gmail.com>
Thu, 9 Jan 2020 16:07:24 +0000 (19:37 +0330)
jq/core.jq
jq/env.jq
jq/interp.jq
jq/printer.jq
jq/run
jq/stepA_mal.jq [new file with mode: 0644]
jq/utils.jq

index 75e8ae6..ab216af 100644 (file)
@@ -238,6 +238,56 @@ def core_identify:
             kind: "fn",
             function: "vals",
             inputs: 1
+        },
+        "string?": {
+            kind: "fn",
+            function: "string?",
+            inputs: 1
+        },
+        "fn?": {
+            kind: "fn",
+            function: "fn?",
+            inputs: 1
+        },
+        "number?": {
+            kind: "fn",
+            function: "number?",
+            inputs: 1
+        },
+        "macro?": {
+            kind: "fn",
+            function: "macro?",
+            inputs: 1
+        },
+        "readline": {
+            kind: "fn",
+            function: "readline",
+            inputs: 1
+        },
+        "time-ms": {
+            kind: "fn",
+            function: "time-ms",
+            inputs: 0
+        },
+        "meta": {
+            kind: "fn",
+            function: "meta",
+            inputs: 1
+        },
+        "with-meta": {
+            kind: "fn",
+            function: "with-meta",
+            inputs: 2
+        },
+        "seq": {
+            kind: "fn",
+            function: "seq",
+            inputs: 1
+        },
+        "conj": {
+            kind: "fn",
+            function: "conj",
+            inputs: -3
         }
     };
 
@@ -308,7 +358,7 @@ def core_interp(arguments; env):
     ) // (
         select(.function == "read-string") | arguments | first.value | read_str | read_form.value
     ) // (
-        select(.function == "atom") | arguments | first | wrap2("atom"; {names: []})
+        select(.function == "atom") | arguments | first | wrap2("atom"; {names: [], identity: now, last_modified: now})
     ) // (
         select(.function == "atom?") | null | wrap(arguments | first.kind == "atom" | tostring)
     ) // (
@@ -319,6 +369,7 @@ def core_interp(arguments; env):
         select(.function == "concat") | arguments | map(.value) | (add//[]) | wrap("list")
     ) // (
         select(.function == "nth")
+            | _debug(arguments)
             | arguments[0].value as $lst
             | arguments[1].value as $idx
             | if ($lst|length < $idx) or ($idx < 0) then
@@ -398,4 +449,20 @@ def core_interp(arguments; env):
         select(.function == "keys") | arguments[0].value | with_entries(.value as $v | .key as $k | {key: $k, value: {value: $k, kind: $v.kkind}}) | to_entries | map(.value) | wrap("list")
     ) // (
         select(.function == "vals") | arguments[0].value | map(.value) | to_entries | map(.value) | wrap("list")
+    ) // (
+        select(.function == "string?") | null | wrap((arguments[0].kind == "string") | tostring)
+    ) // (
+        select(.function == "fn?") | null | wrap((arguments[0].kind == "fn" or arguments[0].kind == "function") | tostring)
+    ) // (
+        select(.function == "number?") | null | wrap((arguments[0].kind == "number") | tostring)
+    ) // (
+        select(.function == "macro?") | null | wrap((arguments[0].is_macro == true) | tostring)
+    ) // (
+        select(.function == "readline") | arguments[0].value | __readline | wrap("string")
+    ) // (
+        select(.function == "time-ms") | now * 1000 | wrap("number")
+    ) // (
+        select(.function == "meta") | arguments[0].meta // {kind:"nil"}
+    ) // (
+        select(.function == "with-meta") | arguments[0] | .meta |= arguments[1]
     ) // jqmal_error("Unknown native function \(.function)");
\ No newline at end of file
index 5dc0338..97ceea2 100644 (file)
--- a/jq/env.jq
+++ b/jq/env.jq
@@ -69,7 +69,7 @@ def inform_function_multi(names):
     );
 
 def env_multiset(keys; value):
-    (if value.kind == "function" then
+    (if value.kind == "function" then # multiset not allowed on atoms
         value | inform_function_multi(keys)
     else
         value
@@ -87,8 +87,20 @@ def env_multiset(env; keys; value):
 
 def env_set($key; $value):
     (if $value.kind == "function" or $value.kind == "atom" then
-        # inform the function of its names
-        $value | inform_function($key)
+        # inform the function/atom of its names
+        ($value |
+        if $value.kind == "atom" then
+            # check if the one we have is newer
+            env_req(env; key) as $ours |
+            if $ours.last_modified > $value.last_modified then
+                $ours
+            else
+                # update modification timestamp
+                $value | .last_modified |= now
+            end
+        else
+            .
+        end) | inform_function($key)
     else 
         $value
     end) as $value | {
@@ -98,26 +110,90 @@ def env_set($key; $value):
         dirty_atoms: .dirty_atoms
     };
 
-def env_dump_keys(atoms):
-    def _dump0:
-        [ .environment // {} | to_entries[] | select(.value.kind != "atom") | .key ];
+def env_dump_keys:
     def _dump1:
         .environment // {} | keys;
     if . == null then [] else
         if .parent == null then
-            (if atoms then _dump1 else _dump0 end + (.fallback | env_dump_keys(atoms))) | unique
+            (
+                _dump1 +
+                (.fallback | env_dump_keys)
+            )
         else
-            (.parent | env_dump_keys(atoms) + (if atoms then _dump1 else _dump0 end) + (.fallback | env_dump_keys(atoms))) | unique
+            (
+                _dump1 +
+                (.parent | env_dump_keys) +
+                (.fallback | env_dump_keys)
+            )
+        end | unique
+    end;
+
+def env_find(env):
+    if env.environment[.] == null then
+        if env.parent then
+            env_find(env.parent) // if env.fallback then env_find(env.fallback) else null end
+        else
+            null
         end
+    else
+        env
     end;
 
-def env_dump_keys:
-    env_dump_keys(false);
+def env_get(env):
+    . as $key | $key | env_find(env).environment[$key] as $value |
+    if $value == null then
+        jqmal_error("'\($key)' not found")
+    else
+        if $value.kind == "atom" then
+            $value.identity as $id |
+            $key | env_find(env.parent).environment[$key] as $possibly_newer |
+            if $possibly_newer.identity == $id and $possibly_newer.last_modified > $value.last_modified then
+                $possibly_newer
+            else
+                $value
+            end
+        else
+            $value
+        end
+    end;
+
+def env_get(env; key):
+    key | env_get(env);
+
+def env_req(env; key):
+    key as $key | key | env_find(env).environment[$key] as $value |
+    if $value == null then
+        null
+    else
+        if $value.kind == "atom" then
+            $value.identity as $id |
+            $key | env_find(env.parent).environment[$key] as $possibly_newer |
+            if $possibly_newer.identity == $id and $possibly_newer.last_modified > $value.last_modified then
+                $possibly_newer
+            else
+                $value
+            end
+        else
+            $value
+        end
+    end;
 
 def env_set(env; $key; $value):
     (if $value.kind == "function" or $value.kind == "atom" then
         # inform the function/atom of its names
-        $value | (.names += [$key]) | (.names |= unique)
+        $value | (.names += [$key]) | (.names |= unique) |
+        if $value.kind == "atom" then
+            # check if the one we have is newer
+            env_req(env; $key) as $ours |
+            if $ours.last_modified > $value.last_modified then
+                $ours
+            else
+                # update modification timestamp
+                $value | .last_modified |= now
+            end
+        else
+            .
+        end
     else 
         $value
     end) as $value | {
@@ -127,17 +203,6 @@ def env_set(env; $key; $value):
         dirty_atoms: env.dirty_atoms
     };
 
-def env_find(env):
-    if env.environment[.] == null then
-        if env.parent then
-            env_find(env.parent) // if env.fallback then env_find(env.fallback) else null end
-        else
-            null
-        end
-    else
-        env
-    end;
-
 def env_setfallback(env; fallback):
     {
         parent: env.parent,
@@ -145,15 +210,6 @@ def env_setfallback(env; fallback):
         environment: env.environment,
         dirty_atoms: env.dirty_atoms
     };
-
-def env_get(env):
-    . as $key | env_find(env).environment[$key] // jqmal_error("'\($key)' not found");
-
-def env_get(env; key):
-    key | env_get(env);
-
-def env_req(env; key):
-    key as $key | key | env_find(env).environment[$key] // null;
     
 def addEnv(env):
     {
index 949337c..ddac8e5 100644 (file)
@@ -6,7 +6,7 @@ include "printer";
 def arg_check(args):
     if .inputs < 0 then
         if (abs(.inputs) - 1) > (args | length) then
-            jqmal_error("Invalid number of arguments (expected at least \(abs(.inputs) - 1), got \(args|length): \(args | wrap("vector") | pr_str))")
+            jqmal_error("Invalid number of arguments (expected at least \(abs(.inputs) - 1), got \(args|length))")
         else
             .
         end
@@ -107,6 +107,7 @@ def addFrees(newEnv; frees):
 def interpret(arguments; env; _eval):
     extractReplEnv(env) as $replEnv |
     hasReplEnv(env) as $hasReplEnv |
+    (if $DEBUG then _debug("INTERP: \(. | pr_str)") else . end) |
     (select(.kind == "fn") |
         arg_check(arguments) | 
             (select(.function == "eval") | 
@@ -122,7 +123,11 @@ def interpret(arguments; env; _eval):
             (select(.function == "reset!") | 
                 # env modifying function
                 arguments[0].names as $names |
-                arguments[1]|wrap2("atom"; {names: $names}) as $value |
+                arguments[1]|wrap2("atom"; {
+                    names: $names,
+                    identity: arguments[0].identity,
+                    last_modified: now
+                }) as $value |
                 (reduce $names[] as $name (
                     env;
                     . as $env | env_set_($env; $name; $value)
@@ -136,7 +141,11 @@ def interpret(arguments; env; _eval):
                 arguments[1] as $function |
                 ([$initValue] + arguments[2:]) as $args |
                 ($function | interpret($args; env; _eval)) as $newEnvValue |
-                $newEnvValue.expr|wrap2("atom"; {names: $names}) as $newValue |
+                $newEnvValue.expr | wrap2("atom"; {
+                    names: $names,
+                    identity: arguments[0].identity,
+                    last_modified: now
+                }) as $newValue |
                 $newEnvValue.env as $newEnv |
                 (reduce $names[] as $name (
                     $newEnv;
index ffadf5a..88f6cad 100644 (file)
@@ -17,9 +17,9 @@ def pr_str(opt):
     (select(.kind == "nil")     | "nil") //
     (select(.kind == "true")    | "true") //
     (select(.kind == "false")   | "false") //
-    (select(.kind == "fn")      | "#<fn>") //
+    (select(.kind == "fn")      | "#<fn \(.function)>") //
     (select(.kind == "function")| "#<function \([":anon"] + .names | join(", "))>") //
-    (select(.kind == "atom")| "(atom \(.value | pr_str(opt)))") //
+    (select(.kind == "atom")    | "(atom \(.value | pr_str(opt)))") //
     "#<Unknown \(.kind) in \(.)>";
 
 def pr_str:
diff --git a/jq/run b/jq/run
index 4001929..04e5377 100755 (executable)
--- a/jq/run
+++ b/jq/run
@@ -1,6 +1,8 @@
 #!/bin/bash
 
 # let's do some sorcery to bestow IO upon jq
+XDEBUG=${DEBUG:-false}
+SELFDEBUG=${RUNDEBUG:-false}
 runjq() {
     pipe_name=$(mktemp)
     rm -f $pipe_name $ipipe_name
@@ -12,9 +14,10 @@ runjq() {
         while [[ -e $pipe_name ]]; do
             timeout 1 cat $pipe_name
         done&
-    ) | jq -nrRM -f "$(dirname "$0")/${STEP:-stepA_mal}.jq" --args "${@}" |&\
+    ) | jq --argjson DEBUG $XDEBUG -nrRM -f "$(dirname "$0")/${STEP:-stepA_mal}.jq" --args "${@}" |&\
     tee \
         >(jq -Rr 'try fromjson[1]|if type == "string" then . else empty end') \
+        >(jq -Rr "if $SELFDEBUG then . else empty end") \
         >(while read -r line; do
             command=$(echo $line | jq -c 'try if .[1] | has("command") then .[1].command else empty end' 2>/dev/null)
             if [[ $command ]]; then
diff --git a/jq/stepA_mal.jq b/jq/stepA_mal.jq
new file mode 100644 (file)
index 0000000..1150667
--- /dev/null
@@ -0,0 +1,388 @@
+include "reader";
+include "printer";
+include "utils";
+include "interp";
+include "env";
+include "core";
+
+def read_line:
+    . as $in
+    | label $top
+    | _readline;
+
+def READ:
+    read_str | read_form | .value;
+
+def recurseflip(x; y):
+    recurse(y; x);
+
+def TCOWrap(env; retenv; continue):
+    {
+        ast: .,
+        env: env,
+        ret_env: retenv,
+        finish: (continue | not),
+        cont: true # set inside
+    };
+
+def _symbol(name):
+    {
+        kind: "symbol",
+        value: name
+    };
+
+def _symbol_v(name):
+    if .kind == "symbol" then
+        .value == name
+    else
+        false
+    end;
+
+def quasiquote:
+    if isPair then
+        .value as $value | null |
+        if ($value[0] | _symbol_v("unquote")) then
+            $value[1]
+        else
+            if isPair($value[0]) and ($value[0].value[0] | _symbol_v("splice-unquote")) then
+                    [_symbol("concat")] +
+                    [$value[0].value[1]] + 
+                    [($value[1:] | wrap("list") | quasiquote)] | wrap("list")
+            else
+                    [_symbol("cons")] + 
+                    [($value[0] | quasiquote)] +
+                    [($value[1:] | wrap("list") | quasiquote)] | wrap("list")
+            end
+        end
+    else
+            [_symbol("quote")] + 
+            [.] | wrap("list")
+    end;
+
+def set_macro_function:
+    if .kind != "function" then
+        jqmal_error("expected a function to be defined by defmacro!")
+    else
+        .is_macro |= true
+    end;
+
+def is_macro_call(env):
+    if .kind != "list" then
+        false
+    else
+        if (.value|first.kind == "symbol") then
+            env_req(env; .value|first.value)
+            | if .kind != "function" then
+                false
+            else 
+                .is_macro
+            end
+        else
+            false
+        end
+    end;
+
+def EVAL(env):
+    def _eval_here:
+        .env as $env | .expr | EVAL($env);
+
+    def _interpret($_menv):
+        reduce .value[] as $elem (
+            [];
+            . as $dot | $elem | EVAL($_menv) as $eval_env |
+                ($dot + [$eval_env.expr])
+        ) | . as $expr | first |
+                interpret($expr[1:]; $_menv; _eval_here);
+
+    def macroexpand(env):
+        [ while(is_macro_call(env | unwrapCurrentEnv);
+            _interpret(env).expr) // . ]
+        | first
+        | if is_macro_call(env | unwrapCurrentEnv) then
+            _interpret(env).expr
+          else
+            .
+          end;
+
+    def hmap_with_env:
+        .env as $env | .list as $list |
+            if $list|length == 0 then
+                empty
+            else
+                $list[0] as $elem |
+                $list[1:] as $rest |
+                    $elem.value.value | EVAL($env) as $resv |
+                        {
+                            value: {
+                                key: $elem.key,
+                                value: { kkind: $elem.value.kkind, value: $resv.expr }
+                            },
+                            env: env
+                        },
+                        ({env: $resv.env, list: $rest} | hmap_with_env)
+            end;
+    def map_with_env:
+        .env as $env | .list as $list |
+            if $list|length == 0 then
+                empty
+            else
+                $list[0] as $elem |
+                $list[1:] as $rest |
+                    $elem | EVAL($env) as $resv |
+                        { value: $resv.expr, env: env },
+                        ({env: $resv.env, list: $rest} | map_with_env)
+            end;
+    def eval_ast(env):
+        (select(.kind == "vector") |
+            if .value|length == 0 then
+                {
+                    kind: "vector",
+                    value: []
+                }
+            else
+                [ { env: env, list: .value } | map_with_env ] as $res |
+                {
+                    kind: "vector",
+                    value: $res | map(.value)
+                }
+            end
+        ) //
+        (select(.kind == "hashmap") |
+            [ { env: env, list: (.value | to_entries) } | hmap_with_env ] as $res |
+            {
+                kind: "hashmap",
+                value: $res | map(.value) | from_entries
+            }
+        ) //
+        (select(.kind == "function") |
+            .# return this unchanged, since it can only be applied to
+        ) //
+        (select(.kind == "symbol") |
+            .value | env_get(env | unwrapCurrentEnv)
+        ) // .;
+
+    . as $ast
+    | { env: env, ast: ., cont: true, finish: false, ret_env: null }
+    | [ recurseflip(.cont;
+        .env as $_menv
+        | (if $DEBUG then _debug("EVAL: \($ast | pr_str($_menv))") else . end)
+        | if .finish then
+            .cont |= false
+        else
+            (.ret_env//.env) as  $_retenv
+            | .ret_env as $_orig_retenv
+            | .ast
+            | . as $init
+            | $_menv | unwrapCurrentEnv as $currentEnv # unwrap env "package"
+            | $_menv | unwrapReplEnv    as $replEnv    # -
+            | $init
+            |
+            (select(.kind == "list") |
+                macroexpand($_menv) |
+                if .kind != "list" then
+                    eval_ast($_menv) | TCOWrap($_menv; $_orig_retenv; false)
+                else 
+                    if .value | length == 0 then 
+                        . | TCOWrap($_menv; $_orig_retenv; false)
+                    else
+                        (
+                            (
+                                .value | select(.[0].value == "def!") as $value |
+                                    ($value[2] | EVAL($_menv)) as $evval |
+                                        addToEnv($evval; $value[1].value) as $val |
+                                        $val.expr | TCOWrap($val.env; $_orig_retenv; false)
+                            ) //
+                            (
+                                .value | select(.[0].value == "defmacro!") as $value |
+                                    ($value[2] | EVAL($_menv) | (.expr |= set_macro_function)) as $evval |
+                                        addToEnv($evval; $value[1].value) as $val |
+                                        $val.expr | TCOWrap($val.env; $_orig_retenv; false)
+                            ) //
+                            (
+                                .value | select(.[0].value == "let*") as $value |
+                                    ($currentEnv | pureChildEnv | wrapEnv($replEnv)) as $subenv |
+                                        (reduce ($value[1].value | nwise(2)) as $xvalue (
+                                            $subenv;
+                                            . as $env | $xvalue[1] | EVAL($env) as $expenv |
+                                                env_set_($expenv.env; $xvalue[0].value; $expenv.expr))) as $env
+                                                    | $value[2] | TCOWrap($env; $_retenv; true)
+                            ) //
+                            (
+                                .value | select(.[0].value == "do") as $value |
+                                    (reduce ($value[1:][]) as $xvalue (
+                                        { env: $_menv, expr: {kind:"nil"} };
+                                        .env as $env | $xvalue | EVAL($env)
+                                    )) | . as $ex | .expr | TCOWrap($ex.env; $_orig_retenv; false)
+                            ) //
+                            (
+                                .value | select(.[0].value == "try*") as $value |
+                                    try (
+                                        $value[1] | EVAL($_menv) as $exp | $exp.expr | TCOWrap($exp.env; $_orig_retenv; false)
+                                    ) catch ( . as $exc |
+                                        if $value[2] then
+                                            if ($value[2].value[0] | _symbol_v("catch*")) then
+                                                (if ($exc | is_jqmal_error) then
+                                                    $exc[19:] as $ex |
+                                                        try (
+                                                            $ex 
+                                                            | fromjson
+                                                        ) catch (
+                                                            $ex |
+                                                            wrap("string")
+                                                        )
+                                                else 
+                                                    $exc|wrap("string")
+                                                end) as $exc |
+                                                $value[2].value[2] | EVAL($currentEnv | childEnv([$value[2].value[1].value]; [$exc]) | wrapEnv($replEnv)) as $ex |
+                                                $ex.expr | TCOWrap($ex.env; $_retenv; false)
+                                            else
+                                                error($exc)
+                                            end
+                                        else
+                                            error($exc)
+                                        end
+                                    )
+                            ) //
+                            (
+                                .value | select(.[0].value == "if") as $value |
+                                    $value[1] | EVAL($_menv) as $condenv |
+                                        (if (["false", "nil"] | contains([$condenv.expr.kind])) then
+                                            ($value[3] // {kind:"nil"})
+                                        else
+                                            $value[2]
+                                        end) | TCOWrap($condenv.env; $_orig_retenv; true)
+                            ) //
+                            (
+                                .value | select(.[0].value == "fn*") as $value |
+                                    # we can't do what the guide says, so we'll skip over this
+                                    # and ues the later implementation
+                                    # (fn* args body)
+                                    $value[1].value | map(.value) as $binds | 
+                                    ($value[2] | find_free_references($currentEnv | env_dump_keys + $binds)) as $free_referencess | {
+                                        kind: "function",
+                                        binds: $binds,
+                                        env: $_menv,
+                                        body: $value[2],
+                                        names: [], # we can't do that circular reference this
+                                        free_referencess: $free_referencess,  # for dynamically scoped variables
+                                        is_macro: false
+                                } | TCOWrap($_menv; $_orig_retenv; false)
+                            ) //
+                            (
+                                .value | select(.[0].value == "quote") as $value |
+                                    $value[1] | TCOWrap($_menv; $_orig_retenv; false)
+                            ) //
+                            (
+                                .value | select(.[0].value == "quasiquote") as $value |
+                                    $value[1] | quasiquote | TCOWrap($_menv; $_orig_retenv; true)
+                            ) //
+                            (
+                                .value | select(.[0].value == "macroexpand") as $value |
+                                    $value[1] | macroexpand(env) | TCOWrap($_menv; $_orig_retenv; false)
+                            ) //
+                            (
+                                . as $dot | _interpret($_menv) as $exprenv |
+                                        $exprenv.expr | TCOWrap($exprenv.env; $_orig_retenv; false)
+                            ) //
+                                TCOWrap($_menv; $_orig_retenv; false)
+                        )
+                    end
+                end
+            ) //
+                (eval_ast($_menv) | TCOWrap($_menv; $_orig_retenv; false))
+        end
+    ) ] 
+    | last as $result
+    | ($result.ret_env // $result.env) as $env
+    | $result.ast
+    | addEnv($env);
+
+def PRINT(env):
+    pr_str(env);
+
+def rep(env):
+    READ | EVAL(env) as $expenv |
+        if $expenv.expr != null then
+            $expenv.expr | PRINT($expenv.env)
+        else
+            null
+        end | addEnv($expenv.env);
+
+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:
+    {
+        parent: null,
+        environment: ({
+            "+": {
+                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"
+            },
+            "eval": {
+                kind: "fn",
+                inputs: 1,
+                function: "eval"
+            }
+        } + core_identify),
+        dirty_atoms: [],
+        fallback: null
+    };
+
+def repl(env):
+    def xrepl:
+        (.env as $env | try repl_($env) catch addEnv($env)) as $expenv |
+            {
+                value: $expenv.expr,
+                stop: false,
+                env: ($expenv.env // .env)
+            } | ., xrepl;
+    {stop: false, env: env} | xrepl | if .value then (.value | _print) else empty end;
+
+def eval_ign(expr):
+    . as $env | expr | rep($env) | .env;
+
+def eval_val(expr):
+    . as $env | expr | rep($env) | .expr;
+
+def getEnv:
+    replEnv
+    | wrapEnv
+    | eval_ign("(def! *host-language* \"jq\")")
+    | eval_ign("(def! not (fn* (a) (if a false true)))")
+    | eval_ign("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\\nnil)\")))))))")
+    | 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)))))))")
+    ;
+
+def main:
+    if $ARGS.positional|length > 0 then
+        try (
+            getEnv as $env |
+            env_set_($env; "*ARGV*"; $ARGS.positional[1:] | map(wrap("string")) | wrap("list")) |
+            eval_val("(load-file \($ARGS.positional[0] | tojson))")
+        ) catch (
+            _print
+        )
+    else
+        repl( getEnv as $env | env_set_($env; "*ARGV*"; [] | wrap("list")) )
+    end;
+
+main
\ No newline at end of file
index 0186264..552f63e 100644 (file)
@@ -1,3 +1,12 @@
+def _debug(ex):
+    . as $top
+    | ex
+    | debug
+    | $top;
+
+def _print:
+    debug;
+
 def nwise(n):
     def _nwise:
         if length <= n then 
@@ -72,7 +81,7 @@ def find_free_references(keys):
         else if "vector" == $dot.kind then
             ($dot.value | map(_refs) | reduce .[] as $x ([]; . + $x))
         else if "hashmap" == $dot.kind then
-            ([$dot.value | from_entries | map({kind: .value.kkind, value: .key}, .value.value)] | map(_refs) | reduce .[] as $x ([]; . + $x))
+            ([$dot.value | to_entries[] | ({kind: .value.kkind, value: .key}, .value.value) ] | map(_refs) | reduce .[] as $x ([]; . + $x))
         else
             []
         end end end end
@@ -115,20 +124,20 @@ def issue_extern(cmd; options):
 def issue_extern(cmd):
     issue_extern(cmd; {});
 
-def _debug(ex):
-    . as $top
-    | ex
-    | debug
-    | $top;
-
-def _print:
-    debug;
-
 def _readline:
       []
     | issue_extern("readline"; {nowait: false})
     ;
 
+def __readline(prompt):
+    . as $top 
+    | prompt
+    | _print
+    | _readline;
+
+def __readline:
+    __readline(.);
+
 def _write_to_file(name):
     . as $value
     | [(name|tojson), (.|tojson), (false|tojson)]