step2 - eval
authorAnotherTest <ali.mpfard@gmail.com>
Sun, 5 Jan 2020 15:05:49 +0000 (18:35 +0330)
committerAnotherTest <ali.mpfard@gmail.com>
Tue, 7 Jan 2020 20:35:33 +0000 (00:05 +0330)
Since we can't store functions, this is gonna be a _mess_

jq/interp.jq [new file with mode: 0644]
jq/printer.jq
jq/reader.jq
jq/step1_read_print.jq
jq/step2_eval.jq [new file with mode: 0644]
jq/utils.jq [new file with mode: 0644]

diff --git a/jq/interp.jq b/jq/interp.jq
new file mode 100644 (file)
index 0000000..5164280
--- /dev/null
@@ -0,0 +1,24 @@
+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
index 41638e6..9b1d06b 100644 (file)
@@ -11,9 +11,9 @@ def pr_str:
     (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") //
index a681888..6b958a8 100644 (file)
@@ -88,13 +88,13 @@ def read_form_(depth):
             # 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 |
@@ -105,19 +105,19 @@ def read_form_(depth):
                         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 |
@@ -128,26 +128,26 @@ def read_form_(depth):
                         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
@@ -155,8 +155,8 @@ def read_form_(depth):
                             tokens: $result.tokens[1:],
                             value: {
                                 kind: "hashmap",
-                                values:
-                                    [ $result.values | 
+                                value:
+                                    [ $result.value | 
                                         nwise(2) | 
                                         try {
                                             key: (.[0] | extract_string),
@@ -177,7 +177,7 @@ def read_form_(depth):
                     tokens: .tokens,
                     value: {
                         kind: "list",
-                        values: [
+                        value: [
                             {
                                 kind: "symbol",
                                 value: "quote"
@@ -193,7 +193,7 @@ def read_form_(depth):
                     tokens: .tokens,
                     value: {
                         kind: "list",
-                        values: [
+                        value: [
                             {
                                 kind: "symbol",
                                 value: "quasiquote"
@@ -209,7 +209,7 @@ def read_form_(depth):
                     tokens: .tokens,
                     value: {
                         kind: "list",
-                        values: [
+                        value: [
                             {
                                 kind: "symbol",
                                 value: "unquote"
@@ -225,7 +225,7 @@ def read_form_(depth):
                     tokens: .tokens,
                     value: {
                         kind: "list",
-                        values: [
+                        value: [
                             {
                                 kind: "symbol",
                                 value: "splice-unquote"
@@ -241,7 +241,7 @@ def read_form_(depth):
                     tokens: .tokens,
                     value: {
                         kind: "list",
-                        values: [
+                        value: [
                             {
                                 kind: "symbol",
                                 value: "deref"
@@ -257,7 +257,7 @@ def read_form_(depth):
                     tokens: $value.tokens,
                     value: {
                         kind: "list",
-                        values: [
+                        value: [
                             {
                                 kind: "symbol",
                                 value: "with-meta"
index 1779779..7d416d4 100644 (file)
@@ -8,10 +8,10 @@ def read_line:
     | input;
 
 def READ:
-    read_str;
+    read_str | read_form | .value;
 
 def EVAL:
-    read_form | .value;
+    .;
 
 def PRINT:
     pr_str;
diff --git a/jq/step2_eval.jq b/jq/step2_eval.jq
new file mode 100644 (file)
index 0000000..b54cd6f
--- /dev/null
@@ -0,0 +1,105 @@
+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
diff --git a/jq/utils.jq b/jq/utils.jq
new file mode 100644 (file)
index 0000000..c031aeb
--- /dev/null
@@ -0,0 +1,20 @@
+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