about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--shell/eval.mu80
-rw-r--r--shell/print.mu31
2 files changed, 99 insertions, 12 deletions
diff --git a/shell/eval.mu b/shell/eval.mu
index 211789c8..045d8225 100644
--- a/shell/eval.mu
+++ b/shell/eval.mu
@@ -162,9 +162,85 @@ fn apply-function params-ah: (addr handle cell), args-ah: (addr handle cell), _b
   # `out` contains result of evaluating final expression
 }
 
+# Bind params to corresponding args and add the bindings to old-env. Return
+# the result in env-ah.
+#
+# We never modify old-env, but we point to it. This way other parts of the
+# interpreter can continue using old-env, and everything works harmoniously
+# even though no cells are copied around.
+#
+# env should always be a DAG (ignoring internals of values). It doesn't have
+# to be a tree (some values may be shared), but there are also no cycles.
+#
+# Learn more: https://en.wikipedia.org/wiki/Persistent_data_structure
 fn push-bindings _params-ah: (addr handle cell), _args-ah: (addr handle cell), old-env-h: (handle cell), env-ah: (addr handle cell), trace: (addr trace) {
-  # no bindings for now
-  copy-handle old-env-h, env-ah
+  var params-ah/edx: (addr handle cell) <- copy _params-ah
+  var args-ah/ebx: (addr handle cell) <- copy _args-ah
+  var _params/eax: (addr cell) <- lookup *params-ah
+  var params/esi: (addr cell) <- copy _params
+  {
+    var params-is-nil?/eax: boolean <- is-nil? params
+    compare params-is-nil?, 0/false
+    break-if-=
+    # nil is a literal
+    trace-text trace, "eval", "done with push-bindings"
+    copy-handle old-env-h, env-ah
+    trace-higher trace
+    return
+  }
+  # Params can only be symbols or pairs. Args can be anything.
+  # trace "pushing bindings from " params " to " args {{{
+  {
+    var stream-storage: (stream byte 0x40)
+    var stream/ecx: (addr stream byte) <- address stream-storage
+    write stream, "pushing bindings from "
+    print-cell params-ah, stream, 0/no-trace
+    write stream, " to "
+    print-cell args-ah, stream, 0/no-trace
+    trace trace, "eval", stream
+  }
+  # }}}
+  trace-lower trace
+  var params-type/eax: (addr int) <- get params, type
+  compare *params-type, 2/symbol
+  {
+    break-if-!=
+    trace-text trace, "eval", "symbol; binding to all remaining args"
+    # create a new binding
+    var new-binding-storage: (handle cell)
+    var new-binding-ah/eax: (addr handle cell) <- address new-binding-storage
+    new-pair new-binding-ah, *params-ah, *args-ah
+    # push it to env
+    new-pair env-ah, *new-binding-ah, old-env-h
+    trace-higher trace
+    return
+  }
+  compare *params-type, 0/pair
+  {
+    break-if-=
+    error trace, "cannot bind a non-symbol"
+    trace-higher trace
+    return
+  }
+  var _args/eax: (addr cell) <- lookup *args-ah
+  var args/edi: (addr cell) <- copy _args
+  # params is now a pair, so args must be also
+  var args-type/eax: (addr int) <- get args, type
+  compare *args-type, 0/pair
+  {
+    break-if-=
+    error trace, "args not in a proper list"
+    trace-higher trace
+    return
+  }
+  var intermediate-env-storage: (handle cell)
+  var intermediate-env-ah/edx: (addr handle cell) <- address intermediate-env-storage
+  var first-param-ah/eax: (addr handle cell) <- get params, left
+  var first-arg-ah/ecx: (addr handle cell) <- get args, left
+  push-bindings first-param-ah, first-arg-ah, old-env-h, intermediate-env-ah, trace
+  var remaining-params-ah/eax: (addr handle cell) <- get params, right
+  var remaining-args-ah/ecx: (addr handle cell) <- get args, right
+  push-bindings remaining-params-ah, remaining-args-ah, *intermediate-env-ah, env-ah, trace
 }
 
 fn apply-primitive _f: (addr cell), args-ah: (addr handle cell), out: (addr handle cell), env-h: (handle cell), trace: (addr trace) {
diff --git a/shell/print.mu b/shell/print.mu
index 46782705..22c00543 100644
--- a/shell/print.mu
+++ b/shell/print.mu
@@ -79,16 +79,6 @@ fn print-list _in: (addr cell), out: (addr stream byte), trace: (addr trace) {
   write out, "("
   $print-list:loop: {
     var left/ecx: (addr handle cell) <- get curr, left
-    {
-      var left-addr/eax: (addr cell) <- lookup *left
-      var left-is-nil?/eax: boolean <- is-nil? left-addr
-      compare left-is-nil?, 0/false
-      {
-        break-if-=
-        trace-text trace, "print", "left is null"
-        break $print-list:loop
-      }
-    }
     print-cell left, out, trace
     var right/ecx: (addr handle cell) <- get curr, right
     var right-addr/eax: (addr cell) <- lookup *right
@@ -251,6 +241,27 @@ fn test-print-cell-list {
   check-stream-equal out, "(64 abc)", "F - test-print-cell-list"
 }
 
+fn test-print-cell-list-of-nil {
+  # list = cons "abc", nil
+  var left-storage: (handle cell)
+  var left/ecx: (addr handle cell) <- address left-storage
+  allocate-pair left
+  var nil-storage: (handle cell)
+  var nil/edx: (addr handle cell) <- address nil-storage
+  allocate-pair nil
+  var list-storage: (handle cell)
+  var list/esi: (addr handle cell) <- address list-storage
+  new-pair list, *left, *nil
+  # list = cons 64, list
+  new-integer left, 0x40
+  new-pair list, *left, *list
+  #
+  var out-storage: (stream byte 0x40)
+  var out/edi: (addr stream byte) <- address out-storage
+  print-cell list, out, 0/no-trace
+  check-stream-equal out, "(64 ())", "F - test-print-cell-list-nil"
+}
+
 fn test-print-dotted-list {
   # list = cons 64, "abc"
   var left-storage: (handle cell)