From 759fc63caea9e0ff99d8f3a79925ed3e4b6cee3d Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Fri, 27 Jun 2025 18:48:27 -0400 Subject: [PATCH] ugh. spin my wheels a lot. decide to start work on the receive special form --- assets/test_prelude.ld | 17 +- may_2025_thoughts.md | 216 +++++++++- sandbox.ld | 49 ++- sandbox_run.txt | 959 +++++++++++------------------------------ src/lib.rs | 4 +- src/vm.rs | 78 +++- src/world.rs | 82 +--- 7 files changed, 568 insertions(+), 837 deletions(-) diff --git a/assets/test_prelude.ld b/assets/test_prelude.ld index b0eeb73..189d58c 100644 --- a/assets/test_prelude.ld +++ b/assets/test_prelude.ld @@ -1252,7 +1252,13 @@ fn msgs { fn flush! { "Clears the current process's mailbox." - () -> base :process (:flush)} + () -> base :process (:flush) +} + +fn flush_i! { + "Flushes the message at the indicated index in the current process's mailbox." + (i as :number) -> base :process (:flush_i, i) +} fn sleep! { "Puts the current process to sleep for at least the specified number of milliseconds." @@ -1265,11 +1271,12 @@ fn sleep! { msgs spawn! yield! - - alive? - link! - flush! sleep! + alive? + flush! + flush_i! + + link! abs abs diff --git a/may_2025_thoughts.md b/may_2025_thoughts.md index 6872a04..e4b19e9 100644 --- a/may_2025_thoughts.md +++ b/may_2025_thoughts.md @@ -121,7 +121,7 @@ A few thoughts: * Function calls should be different from tuple pattern matching. Tuples are currently (and maybe forever?) allocated on the heap. Function calls should *not* have to pass through the heap. The good news: `Arguments` is already a different AST node type than `Tuple`; we'll want an `ArgumentsPattern` pattern node type that's different from (and thus compiled differently than) `TuplePattern`. They'll be similar--the matching logic is the same, after all--but the arguments will be on the stack already, and won't need to be unpacked in the same way. - One difficulty will be matching against different arities? But actually, we should compile these arities as different functions. - Given splats, can we actually compile functions into different arities? Consider the following: - ``` + ```ludus fn foo { (x) -> & arity 1 (y, z) -> & arity 2 @@ -161,7 +161,7 @@ The `loop` compilation is _almost_ the same as a function body. That said, the t A few possibilities: * Probably the best option: enforce a new requirement that splat patterns in function clauses *must* be longer than any explicit arity of the function. So, taking the above: - ``` + ```ludus fn foo { (x) -> & arity 1 (y, z) -> & arity 2 @@ -171,7 +171,7 @@ A few possibilities: ``` This would give you a validation error that splats must be longer than any other arity. Similarly, we could enforce this: -``` +```ludus fn foo { (x) -> & arity 1 (x, y) -> & arity 2 @@ -340,7 +340,7 @@ And then: quality of life improvements: ### Bugs discovered while trying to compile prelude #### 2025-06-20 Consider the following code: -``` +```ludus fn one { (x as :number) -> { fn two () -> :number @@ -400,7 +400,7 @@ What we need to have happen is that if a function is closing over values _inside I think I need to consult Uncle Bob Nystrom to get a sense of what to do here. *** So I found the minimal test case: -``` +```ludus let foo = { let thing = :thing let bar = :bar @@ -445,7 +445,7 @@ To the best of my ability to tell, `if` has proper stack behaviour. So the question is what's happening in the interaction between the `jump_if_false` instruction and `recur`. To wit, the following code works just fine: -``` +```ludus fn not { (false) -> true (nil) -> true @@ -463,7 +463,7 @@ loop ([1, 2, 3]) with { ``` But the following code does not: -``` +```ludus let test = 2 loop ([1, 2, 3]) with { ([]) -> false @@ -522,11 +522,7 @@ SOLUTION: test to see if the function has been forward-declared, and if it has, NEW PROBLEM: a lot of instructions in the VM don't properly offset from the call frame's stack base, which leads to weirdness when doing things inside function calls. NEW SOLUTION: create a function that does the offset properly, and replace everywhere we directly access the stack. -<<<<<<< HEAD -<<<<<<< Updated upstream This is the thing I am about to do -||||||| Stash base -This is the thing I am about to do. ### I think the interpreter, uh, works? #### 2025-06-24 @@ -832,12 +828,12 @@ I've implemented what I decribe above. It works! I'm low-key astonished. It perfectly handles an infinitely recurring process! What the fuck. Anyway, things left to do: * [ ] `receive` forms are the big one: they require threading through the whole interpreter -* [ ] implement the missing process functions at the end of prelude +* [x] implement the missing process functions at the end of prelude * [ ] research how Elixir/Erlang's abstractions over processes work (I'm especially interested in how to make synchronous-looking calls); take a look especially at https://medium.com/qixxit-development/build-your-own-genserver-in-49-lines-of-code-1a9db07b6f13 * [ ] write some examples just using these simple tools (not even GenServer, etc.) to see how they work, and to start building curriculum * [ ] develop a design for how to deal with asynchronous io with js -``` +```ludus fn agent/get (pid) -> { send (pid, (:get, self ())) receive { @@ -872,5 +868,199 @@ Two things that pop out to me: * The way this works is just to yield immediately. This actually makes a lot of sense. If we put them next to one another, there's no risk that there'll be backlogged `(:response, x)` messages in the mbx, right? But that makes me a little queasy. * The way `gen_server` works is pretty deep inversion of control; you effectively write callbacks for the `gen_server` to call. I'm not sure that's how we want to do things in Ludus; it's a handy pattern, and easy. But it's not simple. But also worth investigating. In any event, it's the foundation of all the other process patterns Elixir has developed. I need an intuiation around it. +### Rethinking reception +#### 2025-06-27 +So one thing that's stuck with me is that in Elixir, `receive` isn't a special form: it's a function that takes a block. +It may be a macro, but it's still mostly normalish, and doesn't invovle compiler shenanigans. + +So, this is what I want to write: + +```ludus +fn receiver () -> receive { + :foo -> :bar + :bar -> :baz + :baz -> :quux +} +``` +There's a way to model this without the magic of receive. +Imagine instead a function that just receives a message and matches against it: +```ludus +fn receive_msg (msg) -> match msg with { + :foo -> :bar + :bar -> :baz + :baz -> :quux +} +``` +But if there's no matching message clause, we get a panic. +And panics stop the world. +Meanwhile, we need to know whether the message matched. +So this desugaring goes a little further: +```ludus +fn receive_msg (msg) -> match msg with { + :foo -> :bar + :bar -> :baz + :baz -> :quux + _ -> :does_not_understand +} +``` +This way we avoid a panic when there's no matching message. +There's an easy wrapping function which looks like this: +```ludus +fn receive (receiver) -> { + let my_msgs = msgs () + loop (my_msgs, 0) with { + ([], _) -> yield! () + (xs, i) -> match receiver(first (xs)) with { + :does_not_understand -> recur (rest (xs), inc (i)) + x -> { + flush_n! (i) + x + } + } + } +} + +receive (receive_msg) +``` +There's a thing I both like and don't like about this. +The fact that we use a magic keyword, `:does_not_understand`, means it's actually easy to hook into the behaviour of not understanding. +I don't know if we should panic on a process receiving a message it doesn't understand. +Maybe we do that for now? +Crash early and often; thanks Erlang. +And so we do have to worry about the last clause. + +At an implementation level, it's worth noting that we're not optimizing for scanning through the messages--we'll scan through any messages we don't understand every time we call `receive`. +That's probably fine! + +So now the thing, without sugar, is just: +```ludus +fn agent (x) -> receive (fn (msg) { + (:get, pid) -> { + send (pid, (:response, x)) + agent (x) + } + (:set, y) -> agent(y) + (:update, f) -> agent (f (x)) + _ -> :does_not_understand +}) +``` +So I don't need any sugar to make `receive` work? +And it doesn't even need to hook into the vm? +What? + +#### some time later +It turns out I do. +The problem is that the `flush_i` instruction never gets called from Ludus in a thing like an agent, because of the recursive call. +So the flushing would need to happen in the receiver. +A few things that are wrong right now: +* [ ] `loop`/`recur` is still giving me a very headache. It breaks stack discipline to avoid packing tuples into a heap-allocated vec. There may be a way to fix this in the current compiler scheme around how I do and don't handle arguments--make recur stupider and don't bother loading anything. A different solution would be to desugar loop into an anonymous function call. A final solution would be just to use a heap-allocated tuple. + +Minimal failing case: +```ludus +match (nil) with { + (x) -> match x with { + (y) -> recur (y) + } +} +``` +* [ ] The amount of sugar needed to get to `receive` without making a special form is too great. + +In particular, function arities need to be changed, flushes need to be inserted, anonymous lambdas need to be created (which can't be multi-clause), etc. + +* [ ] There was another bug that I was going to write down and fix, but I forgot what it was. Something with processes. + +* [ ] Also: the `butlast` bug is still outstanding: `base :slice` causes a panic in that function, but not the call to the Ludus function. Still have to investigate that one. + +* [ ] In testing this, it's looking like `match` is misbehaving; none of the matches that *should* happen in my fully sugarless `receive` testing are matching how they ought. + +I haven't got to a minimal case, but here's what's not working: +```ludus + fn receive (receiver) -> { + fn looper { + ([], _) -> yield! () + (xs, i) -> { + print!("looping through messages:", xs) + match receiver (first (xs), i) with { + :does_not_understand -> looper (rest (xs), inc (i)) + x -> x + }} + } + print! ("receiving in", self (), "with messages", msgs()) + looper (msgs (), 0) +} + +fn agent (x) -> receive (fn (msg, i) -> { +print!("received msg in agent: ", msg) +match msg with { + (:get, pid) -> { + flush_i! (i) + print!("getted from {pid}") + send (pid, (:response, x)) + agent (x) + } + (:set, y) -> {flush_i!(i); print!("setted! {y}"); agent (y)} + (:update, f) -> {flush_i!(i);print!("updated: {f}"); agent (f (x))} + y -> {print!("no agent reception match!!!! {y}");:does_not_understand} +} +}) + +fn agent/get (pid) -> { + send (pid, (:get, self ())) + yield! () + receive (fn (msg, i) -> match msg with { + (:response, x) -> {flush_i! (i); x} + }) +} + +fn agent/set (pid, val) -> send (pid, (:set, val)) + +fn agent/update (pid, f) -> send (pid, (:update, f)) + +let counter = spawn! (fn () -> agent (0)) +agent/set (counter, 12) +agent/update (counter, inc) +agent/update (counter, mult(_, 3)) +agent/get (counter) +``` + +_I haven't been able to recreate this, and the code above is subtle enough that the Ludus may be behaving as expected; the following works as expected:_ + +```ludus +fn receive (receiver) -> { + print! ("receiving in", self (), "with msgs", msgs()) + if empty? (msgs ()) + then {yield! (); receive (receiver)} + else do msgs () > first > receiver +} + +fn foo? (val) -> receive (fn (msg) -> match report!("scrutinee is", msg) with { + (:report) -> { + print! ("LUDUS SAYS ==> value is {val}") + flush! () + foo? (val) + } + (:set, x) -> { + print! ("LUDUS SAYS ==> foo! was {val}, now is {x}") + flush! () + foo? (x) + } + (:get, pid) -> { + print! ("LUDUS SAYS ==> value is {val}") + send (pid, (:response, val)) + flush! () + foo? (val) + } + x -> print! ("LUDUS SAYS ==> no match, got {x}") +}) + +let foo = spawn! (fn () -> foo? (42)) +print! (foo) +send (foo, (:set, 23)) +yield! () +send (foo, (:get, self ())) +yield! () +fn id (x) -> x +receive(id) +``` diff --git a/sandbox.ld b/sandbox.ld index 2a0efc4..8886bdd 100644 --- a/sandbox.ld +++ b/sandbox.ld @@ -1,20 +1,35 @@ -fn reporter () -> { - print! (self (), msgs ()) +fn receive (receiver) -> { + print! ("receiving in", self (), "with msgs", msgs()) + if empty? (msgs ()) + then {yield! (); receive (receiver)} + else do msgs () > first > receiver } -fn printer () -> { - print!("LUDUS SAYS ==> hi") - sleep! (1) - printer () -} +fn foo? (val) -> receive (fn (msg) -> match report!("scrutinee is", msg) with { + (:report) -> { + print! ("LUDUS SAYS ==> value is {val}") + flush! () + foo? (val) + } + (:set, x) -> { + print! ("LUDUS SAYS ==> foo! was {val}, now is {x}") + flush! () + foo? (x) + } + (:get, pid) -> { + print! ("LUDUS SAYS ==> value is {val}") + send (pid, (:response, val)) + flush! () + foo? (val) + } + x -> print! ("LUDUS SAYS ==> no match, got {x}") +}) - -let foo = spawn! (printer) -let bar = spawn! (reporter) - -send (bar, [:foo, :bar, :baz]) - -& yield! () -sleep! (5) - -:done +let foo = spawn! (fn () -> foo? (42)) +print! (foo) +send (foo, (:set, 23)) +yield! () +send (foo, (:get, self ())) +yield! () +fn id (x) -> x +receive(id) diff --git a/sandbox_run.txt b/sandbox_run.txt index 8d99c9c..1512ecf 100644 --- a/sandbox_run.txt +++ b/sandbox_run.txt @@ -1,32 +1,16 @@ -{ - - box foos = [] - fn foo! { - () -> update! (foos, append (_, :foo) ) -} - fn foos! { - () -> repeat 4 { -{ - - foo! () -} -} -} - foos! () - unbox (foos) -} -closing over in type at 1: #{:list fn list/base... -closing over in eq? at 1: #{:list fn list/base... +entering world loop; active process is axolotl_0 +closing over in type at 1: #{:sin fn sin/base, ... +closing over in eq? at 1: #{:sin fn sin/base, ... closing over in eq? at 2: fn eq? -closing over in first at 1: #{:list fn list/base... -closing over in rest at 1: #{:list fn list/base... -closing over in inc at 1: #{:list fn list/base... -closing over in dec at 1: #{:list fn list/base... -closing over in count at 1: #{:list fn list/base... +closing over in first at 1: #{:sin fn sin/base, ... +closing over in rest at 1: #{:sin fn sin/base, ... +closing over in inc at 1: #{:sin fn sin/base, ... +closing over in dec at 1: #{:sin fn sin/base, ... +closing over in count at 1: #{:sin fn sin/base, ... closing over in any? at 1: fn empty? closing over in any? at 2: fn not -closing over in list at 1: #{:list fn list/base... -closing over in append at 1: #{:list fn list/base... +closing over in list at 1: #{:sin fn sin/base, ... +closing over in append at 1: #{:sin fn sin/base, ... closing over in fold at 1: fn fold closing over in fold at 2: fn first closing over in fold at 3: fn rest @@ -41,31 +25,29 @@ closing over in filter at 2: fn append closing over in filter at 3: fn fold closing over in keep at 1: fn some? closing over in keep at 2: fn filter -closing over in concat at 1: #{:list fn list/base... +closing over in concat at 1: #{:sin fn sin/base, ... closing over in concat at 2: fn concat closing over in concat at 3: fn fold closing over in contains? at 1: fn first closing over in contains? at 2: fn eq? closing over in contains? at 3: fn rest -closing over in print! at 1: #{:list fn list/base... -closing over in show at 1: #{:list fn list/base... -closing over in report! at 1: fn print! -closing over in report! at 2: fn show -closing over in report! at 3: fn concat -closing over in doc! at 1: #{:list fn list/base... -closing over in doc! at 2: fn print! +closing over in unbox at 1: #{:sin fn sin/base, ... +closing over in store! at 1: #{:sin fn sin/base, ... +closing over in update! at 1: fn unbox +closing over in update! at 2: fn store! +closing over in show at 1: #{:sin fn sin/base, ... closing over in string at 1: fn show closing over in string at 2: fn string closing over in string at 3: fn concat closing over in join at 1: fn join closing over in join at 2: fn concat closing over in join at 3: fn fold -closing over in split at 1: #{:list fn list/base... -closing over in trim at 1: #{:list fn list/base... -closing over in upcase at 1: #{:list fn list/base... -closing over in downcase at 1: #{:list fn list/base... -closing over in chars at 1: #{:list fn list/base... -closing over in chars/safe at 1: #{:list fn list/base... +closing over in split at 1: #{:sin fn sin/base, ... +closing over in trim at 1: #{:sin fn sin/base, ... +closing over in upcase at 1: #{:sin fn sin/base, ... +closing over in downcase at 1: #{:sin fn sin/base, ... +closing over in chars at 1: #{:sin fn sin/base, ... +closing over in chars/safe at 1: #{:sin fn sin/base, ... closing over in strip at 1: fn strip closing over in words at 1: fn strip closing over in words at 2: fn split @@ -73,25 +55,33 @@ closing over in words at 3: fn empty? closing over in words at 4: fn append closing over in words at 5: fn fold closing over in sentence at 1: fn join -closing over in to_number at 1: #{:list fn list/base... -closing over in unbox at 1: #{:list fn list/base... -closing over in store! at 1: #{:list fn list/base... -closing over in update! at 1: fn unbox -closing over in update! at 2: fn store! -closing over in add at 1: #{:list fn list/base... +closing over in to_number at 1: #{:sin fn sin/base, ... +closing over in print! at 1: fn string +closing over in print! at 2: fn map +closing over in print! at 3: fn join +closing over in print! at 4: #{:sin fn sin/base, ... +closing over in print! at 5: box { [] } +closing over in print! at 6: fn append +closing over in print! at 7: fn update! +closing over in report! at 1: fn print! +closing over in report! at 2: fn show +closing over in report! at 3: fn concat +closing over in doc! at 1: #{:sin fn sin/base, ... +closing over in doc! at 2: fn print! +closing over in add at 1: #{:sin fn sin/base, ... closing over in add at 2: fn add closing over in add at 3: fn fold -closing over in sub at 1: #{:list fn list/base... +closing over in sub at 1: #{:sin fn sin/base, ... closing over in sub at 2: fn sub closing over in sub at 3: fn fold -closing over in mult at 1: #{:list fn list/base... +closing over in mult at 1: #{:sin fn sin/base, ... closing over in mult at 2: fn mult closing over in mult at 3: fn fold -closing over in div at 1: #{:list fn list/base... +closing over in div at 1: #{:sin fn sin/base, ... closing over in div at 2: fn mult closing over in div at 3: fn fold closing over in div at 4: fn div -closing over in div/0 at 1: #{:list fn list/base... +closing over in div/0 at 1: #{:sin fn sin/base, ... closing over in div/0 at 2: fn mult closing over in div/0 at 3: fn fold closing over in div/0 at 4: fn div/0 @@ -103,10 +93,10 @@ closing over in inv at 1: fn div closing over in inv/0 at 1: fn div/0 closing over in inv/safe at 1: fn div/safe closing over in neg at 1: fn mult -closing over in gt? at 1: #{:list fn list/base... -closing over in gte? at 1: #{:list fn list/base... -closing over in lt? at 1: #{:list fn list/base... -closing over in lte? at 1: #{:list fn list/base... +closing over in gt? at 1: #{:sin fn sin/base, ... +closing over in gte? at 1: #{:sin fn sin/base, ... +closing over in lt? at 1: #{:sin fn sin/base, ... +closing over in lte? at 1: #{:sin fn sin/base, ... closing over in between? at 1: fn gte? closing over in between? at 2: fn lt? closing over in neg? at 1: fn lt? @@ -126,13 +116,13 @@ closing over in rad/deg at 1: 6.283185307179586 closing over in rad/deg at 2: fn div closing over in rad/deg at 3: fn mult closing over in sin at 1: fn turn/rad -closing over in sin at 2: #{:list fn list/base... +closing over in sin at 2: #{:sin fn sin/base, ... closing over in sin at 3: fn deg/rad closing over in cos at 1: fn turn/rad -closing over in cos at 2: #{:list fn list/base... +closing over in cos at 2: #{:sin fn sin/base, ... closing over in cos at 3: fn deg/rad closing over in tan at 1: fn turn/rad -closing over in tan at 2: #{:list fn list/base... +closing over in tan at 2: #{:sin fn sin/base, ... closing over in tan at 3: fn deg/rad closing over in rotate at 1: fn rotate closing over in rotate at 2: fn cos @@ -140,15 +130,15 @@ closing over in rotate at 3: fn mult closing over in rotate at 4: fn sin closing over in rotate at 5: fn sub closing over in rotate at 6: fn add -closing over in atan/2 at 1: #{:list fn list/base... +closing over in atan/2 at 1: #{:sin fn sin/base, ... closing over in atan/2 at 2: fn rad/turn closing over in atan/2 at 3: fn atan/2 closing over in atan/2 at 4: fn rad/deg closing over in angle at 1: fn atan/2 closing over in angle at 2: fn sub -closing over in mod at 1: #{:list fn list/base... -closing over in mod/0 at 1: #{:list fn list/base... -closing over in mod/safe at 1: #{:list fn list/base... +closing over in mod at 1: #{:sin fn sin/base, ... +closing over in mod/0 at 1: #{:sin fn sin/base, ... +closing over in mod/safe at 1: #{:sin fn sin/base, ... closing over in even? at 1: fn mod closing over in even? at 2: fn eq? closing over in odd? at 1: fn mod @@ -156,10 +146,10 @@ closing over in odd? at 2: fn eq? closing over in square at 1: fn mult closing over in sqrt at 1: fn neg? closing over in sqrt at 2: fn not -closing over in sqrt at 3: #{:list fn list/base... +closing over in sqrt at 3: #{:sin fn sin/base, ... closing over in sqrt/safe at 1: fn neg? closing over in sqrt/safe at 2: fn not -closing over in sqrt/safe at 3: #{:list fn list/base... +closing over in sqrt/safe at 3: #{:sin fn sin/base, ... closing over in sum_of_squares at 1: fn square closing over in sum_of_squares at 2: fn add closing over in sum_of_squares at 3: fn sum_of_squares @@ -171,11 +161,11 @@ closing over in heading/vector at 1: fn neg closing over in heading/vector at 2: fn add closing over in heading/vector at 3: fn cos closing over in heading/vector at 4: fn sin -closing over in floor at 1: #{:list fn list/base... -closing over in ceil at 1: #{:list fn list/base... -closing over in round at 1: #{:list fn list/base... -closing over in range at 1: #{:list fn list/base... -closing over in at at 1: #{:list fn list/base... +closing over in floor at 1: #{:sin fn sin/base, ... +closing over in ceil at 1: #{:sin fn sin/base, ... +closing over in round at 1: #{:sin fn sin/base, ... +closing over in range at 1: #{:sin fn sin/base, ... +closing over in at at 1: #{:sin fn sin/base, ... closing over in second at 1: fn ordered? closing over in second at 2: fn at closing over in last at 1: fn ordered? @@ -187,14 +177,14 @@ closing over in slice at 2: fn gte? closing over in slice at 3: fn count closing over in slice at 4: fn gt? closing over in slice at 5: fn neg? -closing over in slice at 6: #{:list fn list/base... +closing over in slice at 6: #{:sin fn sin/base, ... closing over in butlast at 1: fn count closing over in butlast at 2: fn dec closing over in butlast at 3: fn slice -closing over in assoc at 1: #{:list fn list/base... -closing over in dissoc at 1: #{:list fn list/base... +closing over in assoc at 1: #{:sin fn sin/base, ... +closing over in dissoc at 1: #{:sin fn sin/base, ... closing over in get at 1: fn get -closing over in get at 2: #{:list fn list/base... +closing over in get at 2: #{:sin fn sin/base, ... closing over in update at 1: fn get closing over in update at 2: fn assoc closing over in keys at 1: fn list @@ -211,7 +201,7 @@ closing over in dict at 2: fn fold closing over in dict at 3: fn list closing over in dict at 4: fn dict closing over in each! at 1: fn each! -closing over in random at 1: #{:list fn list/base... +closing over in random at 1: #{:sin fn sin/base, ... closing over in random at 2: fn random closing over in random at 3: fn mult closing over in random at 4: fn sub @@ -226,7 +216,7 @@ closing over in random_int at 2: fn floor closing over in add_command! at 1: box { [] } closing over in add_command! at 2: fn append closing over in add_command! at 3: fn update! -closing over in add_command! at 4: box { #{:position (0... +closing over in add_command! at 4: box { #{:penwidth 1,... closing over in add_command! at 5: fn unbox closing over in add_command! at 6: fn apply_command closing over in add_command! at 7: fn store! @@ -253,665 +243,202 @@ closing over in apply_command at 3: fn update closing over in apply_command at 4: fn sub closing over in apply_command at 5: fn heading/vector closing over in apply_command at 6: fn mult -closing over in position at 1: box { #{:position (0... +closing over in position at 1: box { #{:penwidth 1,... closing over in position at 2: fn unbox -closing over in heading at 1: box { #{:position (0... +closing over in heading at 1: box { #{:penwidth 1,... closing over in heading at 2: fn unbox -closing over in pendown? at 1: box { #{:position (0... +closing over in pendown? at 1: box { #{:penwidth 1,... closing over in pendown? at 2: fn unbox -closing over in pencolor at 1: box { #{:position (0... +closing over in pencolor at 1: box { #{:penwidth 1,... closing over in pencolor at 2: fn unbox -closing over in penwidth at 1: box { #{:position (0... +closing over in penwidth at 1: box { #{:penwidth 1,... closing over in penwidth at 2: fn unbox -binding `foos` in sandbox -stack depth: 1; match depth: 0 -at stack index: 0 -new locals: foos@0//0 -binding `foo!` in sandbox -stack depth: 2; match depth: 0 -at stack index: 1 -new locals: foos@0//0|foo!@1//0 -***function clause matching: : () -***calling function update! stack depth: 0 -resolving binding `foos` in foo! -locals: -as enclosing upvalue 0 -***calling function append stack depth: 1 -resolving binding `append` in foo! -locals: -as enclosing upvalue 1 -resolving binding `update!` in foo! -locals: -as enclosing upvalue 2 -***after 2 args stack depth: 3 -=== function chuncktion: foo!/0 === -IDX | CODE | INFO -0000: reset_match -0001: ***function clause matching: : () -0003: match -0004: jump 00000 -0007: jump_if_no_match 00034 -0010: ***calling function update! stack depth: 0 -0012: resolving binding `foos` in foo! -locals: -0014: as enclosing upvalue 0 -0016: get_upvalue 000 -0018: ***calling function append stack depth: 1 -0020: nothing -0021: constant 00000: :foo -0024: resolving binding `append` in foo! -locals: -0026: as enclosing upvalue 1 -0028: get_upvalue 001 -0030: partial 002 -0032: resolving binding `update!` in foo! -locals: -0034: as enclosing upvalue 2 -0036: get_upvalue 002 -0038: ***after 2 args stack depth: 3 -0040: tail_call 002 -0042: store -0043: return -0044: panic_no_match -resolving binding `foos` in sandbox -locals: foos@0//0|foo!@1//0 -at locals position 0 -resolving binding `append` in sandbox -locals: foos@0//0|foo!@1//0 -as global -resolving binding `update!` in sandbox -locals: foos@0//0|foo!@1//0 -as global -binding `foos!` in sandbox +closing over in self at 1: #{:sin fn sin/base, ... +closing over in send at 1: #{:sin fn sin/base, ... +closing over in spawn! at 1: #{:sin fn sin/base, ... +closing over in yield! at 1: #{:sin fn sin/base, ... +closing over in alive? at 1: #{:sin fn sin/base, ... +closing over in link! at 1: fn link! +closing over in link! at 2: #{:sin fn sin/base, ... +closing over in msgs at 1: #{:sin fn sin/base, ... +closing over in flush! at 1: #{:sin fn sin/base, ... +closing over in flush_i! at 1: #{:sin fn sin/base, ... +closing over in sleep! at 1: #{:sin fn sin/base, ... +yielded from axolotl_0 +***match clause: : (:set, x) +binding `x` in sandbox stack depth: 3; match depth: 0 at stack index: 2 -new locals: foos@0//0|foo!@1//0|foos!@2//0 -***function clause matching: : () -***calling function foo! stack depth: 1 -resolving binding `foo!` in foos! -locals: -as enclosing upvalue 0 -***after 0 args stack depth: 2 -leaving scope 1 -***leaving block before pop stack depth: 1 -popping back from 1 to 1 -=== function chuncktion: foos!/0 === -IDX | CODE | INFO -0000: reset_match -0001: ***function clause matching: : () -0003: match -0004: jump 00000 -0007: jump_if_no_match 00042 -0010: constant 00000: 4 -0013: truncate -0014: jump 00001 -0017: decrement -0018: duplicate -0019: jump_if_zero 00024 -0022: ***calling function foo! stack depth: 1 -0024: resolving binding `foo!` in foos! -locals: -0026: as enclosing upvalue 0 -0028: get_upvalue 000 -0030: ***after 0 args stack depth: 2 -0032: tail_call 000 -0034: store -0035: leaving scope 1 -0037: ***leaving block before pop stack depth: 1 -0039: popping back from 1 to 1 -0041: load -0042: pop -0043: jump_back 00026 -0046: pop -0047: constant 00001: nil -0050: store -0051: return -0052: panic_no_match -resolving binding `foo!` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 -at locals position 1 -***calling function foos! stack depth: 3 -resolving binding `foos!` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 +new locals: x@2//1 +resolving binding `x` in sandbox +locals: x@2//1 at locals position 2 -***after 0 args stack depth: 4 -***calling function unbox stack depth: 3 -resolving binding `foos` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 -at locals position 0 -resolving binding `unbox` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 -as global -***after 1 args stack depth: 5 +leaving scope 1 +releasing binding x@2//1 leaving scope 0 -releasing binding foos!@2//0 -releasing binding foo!@1//0 -releasing binding foos@0//0 -***leaving block before pop stack depth: 3 -popping back from 3 to 0 +***leaving block before pop stack depth: 1 +popping back from 1 to 0 === source code === -box foos = [] +& fn receive (receiver) -> { +& fn looper { +& ([], _) -> yield! () +& (xs, i) -> { +& print!("looping through messages:", xs) +& match receiver (first (xs), i) with { +& :does_not_understand -> looper (rest (xs), inc (i)) +& x -> x +& }} +& } +& print! ("receiving in", self (), "with messages", msgs()) +& looper (msgs (), 0) +& } -fn foo! () -> update! (foos, append (_, :foo)) +& fn agent (x) -> receive (fn (msg, i) -> { +& print!("received msg in agent: ", msg) +& match msg with { +& (:get, pid) -> { +& flush_i! (i) +& print!("getted from {pid}") +& send (pid, (:response, x)) +& agent (x) +& } +& (:set, y) -> {flush_i!(i); print!("setted! {y}"); agent (y)} +& (:update, f) -> {flush_i!(i);print!("updated: {f}"); agent (f (x))} +& y -> {print!("no agent reception match!!!! {y}");:does_not_understand} +& } +& }) -fn foos! () -> repeat 4 { - foo! () +& fn agent/get (pid) -> { +& send (pid, (:get, self ())) +& yield! () +& receive (fn (msg, i) -> match msg with { +& (:response, x) -> {flush_i! (i); x} +& }) +& } + +& fn agent/set (pid, val) -> send (pid, (:set, val)) + +& fn agent/update (pid, f) -> send (pid, (:update, f)) + +& let counter = spawn! (fn () -> agent (0)) +& agent/set (counter, 12) + +match (:set, 12) with { + (:set, x) -> x } -foos! () - -unbox (foos) - === chunk: sandbox === IDX | CODE | INFO -0000: push_list -0001: push_box 082 -0003: noop -0004: stack depth: 1; match depth: 0 -0006: at stack index: 0 -0008: new locals: foos@0//0 -0010: constant 00000: fn foo! -0013: binding `foo!` in sandbox -0015: stack depth: 2; match depth: 0 -0017: at stack index: 1 -0019: new locals: foos@0//0|foo!@1//0 -0021: resolving binding `foos` in sandbox -locals: foos@0//0|foo!@1//0 -0023: at locals position 0 -0025: push_binding 000 -0027: set_upvalue -0028: resolving binding `append` in sandbox -locals: foos@0//0|foo!@1//0 -0030: as global -0032: constant 00001: :append -0035: push_global -0036: set_upvalue -0037: resolving binding `update!` in sandbox -locals: foos@0//0|foo!@1//0 -0039: as global -0041: constant 00002: :update! -0044: push_global -0045: set_upvalue -0046: constant 00003: fn foos! -0049: binding `foos!` in sandbox -0051: stack depth: 3; match depth: 0 -0053: at stack index: 2 -0055: new locals: foos@0//0|foo!@1//0|foos!@2//0 -0057: resolving binding `foo!` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 -0059: at locals position 1 -0061: push_binding 001 -0063: set_upvalue -0064: ***calling function foos! stack depth: 3 -0066: resolving binding `foos!` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 -0068: at locals position 2 -0070: push_binding 002 -0072: ***after 0 args stack depth: 4 -0074: call 000 -0076: pop -0077: ***calling function unbox stack depth: 3 -0079: resolving binding `foos` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 -0081: at locals position 0 -0083: push_binding 000 -0085: resolving binding `unbox` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 -0087: as global -0089: constant 00004: :unbox -0092: push_global -0093: ***after 1 args stack depth: 5 -0095: call 001 -0097: store -0098: leaving scope 0 -0100: releasing binding foos!@2//0 -0102: releasing binding foo!@1//0 -0104: releasing binding foos@0//0 -0106: ***leaving block before pop stack depth: 3 -0108: popping back from 3 to 0 -0110: pop_n 003 -0112: load +0000: constant 00000: :set +0003: constant 00001: 12 +0006: push_tuple 002 +0008: ***match clause: : (:set, x) +0010: match_tuple 002 +0012: jump_if_no_match 00028 +0015: load_tuple +0016: match_depth 001 +0018: match_constant 00000: :set +0021: jump_if_no_match 00017 +0024: match_depth 000 +0026: match +0027: binding `x` in sandbox +0029: stack depth: 3; match depth: 0 +0031: at stack index: 2 +0033: new locals: x@2//1 +0035: jump_if_no_match 00003 +0038: jump 00002 +0041: pop_n 002 +0043: jump_if_no_match 00016 +0046: resolving binding `x` in sandbox +locals: x@2//1 +0048: at locals position 2 +0050: push_binding 002 +0052: store +0053: leaving scope 1 +0055: releasing binding x@2//1 +0057: pop_n 002 +0059: jump 00001 +0062: panic_no_match +0063: load +0064: store +0065: leaving scope 0 +0067: ***leaving block before pop stack depth: 1 +0069: popping back from 1 to 0 +0071: pop +0072: load === vm run === -0000: [] (_,_,_,_,_,_,_,_) -0000: push_list -0001: [->[]<-] (_,_,_,_,_,_,_,_) -0001: push_box 082 -0002: [->box { [] }<-] (_,_,_,_,_,_,_,_) -0002: binding `foos` in sandbox -0004: [->box { [] }<-] (_,_,_,_,_,_,_,_) -0004: stack depth: 1; match depth: 0 -0006: [->box { [] }<-] (_,_,_,_,_,_,_,_) -0006: at stack index: 0 -0008: [->box { [] }<-] (_,_,_,_,_,_,_,_) -0008: new locals: foos@0//0 -0010: [->box { [] }<-] (_,_,_,_,_,_,_,_) -0010: constant 00000: fn foo! -0013: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0013: binding `foo!` in sandbox -0015: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0015: stack depth: 2; match depth: 0 -0017: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0017: at stack index: 1 -0019: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0019: new locals: foos@0//0|foo!@1//0 -0021: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0021: resolving binding `foos` in sandbox -locals: foos@0//0|foo!@1//0 -0023: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0023: at locals position 0 -0025: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0025: push_binding 000 -0027: [->box { [] }<-|fn foo!|box { [] }] (_,_,_,_,_,_,_,_) -0027: set_upvalue -closing over in foo! at 1: box { [] } -0028: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0028: resolving binding `append` in sandbox -locals: foos@0//0|foo!@1//0 -0030: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0030: as global -0032: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0032: constant 00001: :append -0035: [->box { [] }<-|fn foo!|:append] (_,_,_,_,_,_,_,_) -0035: push_global -0036: [->box { [] }<-|fn foo!|fn append] (_,_,_,_,_,_,_,_) -0036: set_upvalue -closing over in foo! at 2: fn append -0037: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0037: resolving binding `update!` in sandbox -locals: foos@0//0|foo!@1//0 -0039: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0039: as global -0041: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0041: constant 00002: :update! -0044: [->box { [] }<-|fn foo!|:update!] (_,_,_,_,_,_,_,_) -0044: push_global -0045: [->box { [] }<-|fn foo!|fn update!] (_,_,_,_,_,_,_,_) -0045: set_upvalue -closing over in foo! at 3: fn update! -0046: [->box { [] }<-|fn foo!] (_,_,_,_,_,_,_,_) -0046: constant 00003: fn foos! -0049: [->box { [] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0049: binding `foos!` in sandbox -0051: [->box { [] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0051: stack depth: 3; match depth: 0 -0053: [->box { [] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0053: at stack index: 2 -0055: [->box { [] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0055: new locals: foos@0//0|foo!@1//0|foos!@2//0 -0057: [->box { [] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0057: resolving binding `foo!` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 -0059: [->box { [] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0059: at locals position 1 -0061: [->box { [] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0061: push_binding 001 -0063: [->box { [] }<-|fn foo!|fn foos!|fn foo!] (_,_,_,_,_,_,_,_) -0063: set_upvalue -closing over in foos! at 1: fn foo! -0064: [->box { [] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0064: ***calling function foos! stack depth: 3 -0066: [->box { [] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0066: resolving binding `foos!` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 -0068: [->box { [] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0068: at locals position 2 -0070: [->box { [] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0070: push_binding 002 -0072: [->box { [] }<-|fn foo!|fn foos!|fn foos!] (_,_,_,_,_,_,_,_) -0072: ***after 0 args stack depth: 4 -0074: [->box { [] }<-|fn foo!|fn foos!|fn foos!] (_,_,_,_,_,_,_,_) -0074: call 000 -=== calling into fn foos!/0 === -0000: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0000: reset_match -0001: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0001: ***function clause matching: : () -0003: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0003: match -0004: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0004: jump 00000 -0007: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0007: jump_if_no_match 00042 -0010: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0010: constant 00000: 4 -0013: [box { [] }|fn foo!|fn foos!|->4<-] (_,_,_,_,_,_,_,_) -0013: truncate -0014: [box { [] }|fn foo!|fn foos!|->4<-] (_,_,_,_,_,_,_,_) -0014: jump 00001 -0018: [box { [] }|fn foo!|fn foos!|->4<-] (_,_,_,_,_,_,_,_) -0018: duplicate -0019: [box { [] }|fn foo!|fn foos!|->4<-|4] (_,_,_,_,_,_,_,_) -0019: jump_if_zero 00024 -0022: [box { [] }|fn foo!|fn foos!|->4<-] (_,_,_,_,_,_,_,_) -0022: ***calling function foo! stack depth: 1 -0024: [box { [] }|fn foo!|fn foos!|->4<-] (_,_,_,_,_,_,_,_) -0024: resolving binding `foo!` in foos! -locals: -0026: [box { [] }|fn foo!|fn foos!|->4<-] (_,_,_,_,_,_,_,_) -0026: as enclosing upvalue 0 -0028: [box { [] }|fn foo!|fn foos!|->4<-] (_,_,_,_,_,_,_,_) -0028: get_upvalue 000 -0030: [box { [] }|fn foo!|fn foos!|->4<-|fn foo!] (_,_,_,_,_,_,_,_) -0030: ***after 0 args stack depth: 2 -0032: [box { [] }|fn foo!|fn foos!|->4<-|fn foo!] (_,_,_,_,_,_,_,_) -0032: tail_call 000 -=== tail call into fn foo!/0 from foos! === -0000: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0000: reset_match -0001: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0001: ***function clause matching: : () -0003: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0003: match -0004: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0004: jump 00000 -0007: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0007: jump_if_no_match 00034 -0010: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0010: ***calling function update! stack depth: 0 -0012: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0012: resolving binding `foos` in foo! -locals: -0014: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0014: as enclosing upvalue 0 -0016: [box { [] }|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0016: get_upvalue 000 -0018: [box { [] }|fn foo!|fn foos!|->box { [] }<-] (_,_,_,_,_,_,_,_) -0018: ***calling function append stack depth: 1 -0020: [box { [] }|fn foo!|fn foos!|->box { [] }<-] (_,_,_,_,_,_,_,_) -0020: nothing -0021: [box { [] }|fn foo!|fn foos!|->box { [] }<-|_] (_,_,_,_,_,_,_,_) -0021: constant 00000: :foo -0024: [box { [] }|fn foo!|fn foos!|->box { [] }<-|_|:foo] (_,_,_,_,_,_,_,_) -0024: resolving binding `append` in foo! -locals: -0026: [box { [] }|fn foo!|fn foos!|->box { [] }<-|_|:foo] (_,_,_,_,_,_,_,_) -0026: as enclosing upvalue 1 -0028: [box { [] }|fn foo!|fn foos!|->box { [] }<-|_|:foo] (_,_,_,_,_,_,_,_) -0028: get_upvalue 001 -0030: [box { [] }|fn foo!|fn foos!|->box { [] }<-|_|:foo|fn append] (_,_,_,_,_,_,_,_) -0030: partial 002 -0032: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0032: resolving binding `update!` in foo! -locals: -0034: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0034: as enclosing upvalue 2 -0036: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0036: get_upvalue 002 -0038: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|fn update!] (_,_,_,_,_,_,_,_) -0038: ***after 2 args stack depth: 3 -0040: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|fn update!] (_,_,_,_,_,_,_,_) -0040: tail_call 002 -=== tail call into fn update!/2 from foo! === -0000: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0000: reset_match -0001: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0001: match_depth 001 -0003: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0003: constant 00000: :box -0006: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|:box] (_,_,_,_,_,_,_,_) -0006: match_type -0007: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0007: jump_if_no_match 00012 -0010: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0010: match_depth 000 -0012: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0012: constant 00001: :fn -0015: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|:fn] (_,_,_,_,_,_,_,_) -0015: match_type -0016: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0016: jump_if_no_match 00003 -0019: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0019: jump 00000 -0022: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0022: jump_if_no_match 00034 -0025: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial] (_,_,_,_,_,_,_,_) -0025: push_binding 000 -0027: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|box { [] }] (_,_,_,_,_,_,_,_) -0027: get_upvalue 000 -0029: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|box { [] }|fn unbox] (_,_,_,_,_,_,_,_) -0029: call 001 -=== calling into fn unbox/1 === -0000: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-] (_,_,_,_,_,_,_,_) -0000: reset_match -0001: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-] (_,_,_,_,_,_,_,_) -0001: match_depth 000 -0003: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-] (_,_,_,_,_,_,_,_) -0003: constant 00000: :box -0006: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-|:box] (_,_,_,_,_,_,_,_) -0006: match_type -0007: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-] (_,_,_,_,_,_,_,_) -0007: jump_if_no_match 00003 -0010: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-] (_,_,_,_,_,_,_,_) -0010: jump 00000 -0013: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-] (_,_,_,_,_,_,_,_) -0013: jump_if_no_match 00015 -0016: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-] (_,_,_,_,_,_,_,_) -0016: get_upvalue 000 -0018: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-|#{:list fn list/base...] (_,_,_,_,_,_,_,_) -0018: constant 00001: :unbox -0021: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-|#{:list fn list/base...|:unbox] (_,_,_,_,_,_,_,_) -0021: get_key -0022: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-|fn unbox/base] (_,_,_,_,_,_,_,_) -0022: store -0023: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-] (fn unbox/base,_,_,_,_,_,_,_) -0023: push_binding 000 -0025: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-|box { [] }] (fn unbox/base,_,_,_,_,_,_,_) -0025: load -0026: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-|box { [] }|fn unbox/base] (_,_,_,_,_,_,_,_) -0026: tail_call 001 -=== tail call into fn unbox/base/1 from unbox === -0028: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-|[]] (_,_,_,_,_,_,_,_) -0028: store -0029: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|->box { [] }<-] ([],_,_,_,_,_,_,_) -0029: pop -0030: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial] ([],_,_,_,_,_,_,_) -0030: return -== returning from fn unbox == -0031: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]] (_,_,_,_,_,_,_,_) -0031: reset_match -0032: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]] (_,_,_,_,_,_,_,_) -0032: match -0033: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]] (_,_,_,_,_,_,_,_) -0033: panic_if_no_match -0034: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]] (_,_,_,_,_,_,_,_) -0034: push_binding 002 -0036: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]|[]] (_,_,_,_,_,_,_,_) -0036: push_binding 001 -0038: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]|[]|fn append/partial] (_,_,_,_,_,_,_,_) -0038: call 001 -=== calling into fn append/partial/1 === -0000: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] (_,_,_,_,_,_,_,_) -0000: reset_match -0001: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] (_,_,_,_,_,_,_,_) -0001: match_depth 001 -0003: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] (_,_,_,_,_,_,_,_) -0003: constant 00000: :list -0006: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo|:list] (_,_,_,_,_,_,_,_) -0006: match_type -0007: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] (_,_,_,_,_,_,_,_) -0007: jump_if_no_match 00009 -0010: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] (_,_,_,_,_,_,_,_) -0010: match_depth 000 -0012: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] (_,_,_,_,_,_,_,_) -0012: match -0013: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] (_,_,_,_,_,_,_,_) -0013: jump_if_no_match 00003 -0016: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] (_,_,_,_,_,_,_,_) -0016: jump 00000 -0019: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] (_,_,_,_,_,_,_,_) -0019: jump_if_no_match 00018 -0022: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] (_,_,_,_,_,_,_,_) -0022: get_upvalue 000 -0024: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo|#{:list fn list/base...] (_,_,_,_,_,_,_,_) -0024: constant 00001: :append -0027: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo|#{:list fn list/base...|:append] (_,_,_,_,_,_,_,_) -0027: get_key -0028: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo|fn append/base] (_,_,_,_,_,_,_,_) -0028: store -0029: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] (fn append/base,_,_,_,_,_,_,_) -0029: push_binding 000 -0031: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo|[]] (fn append/base,_,_,_,_,_,_,_) -0031: push_binding 001 -0033: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo|[]|:foo] (fn append/base,_,_,_,_,_,_,_) -0033: load -0034: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo|[]|:foo|fn append/base] (_,_,_,_,_,_,_,_) -0034: tail_call 002 -=== tail call into fn append/base/2 from append === -0036: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo|[:foo]] (_,_,_,_,_,_,_,_) -0036: store -0037: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]|->[]<-|:foo] ([:foo],_,_,_,_,_,_,_) -0037: pop_n 002 -0039: [box { [] }|fn foo!|fn foos!|box { [] }|fn append/partial|[]] ([:foo],_,_,_,_,_,_,_) -0039: return -== returning from fn append == -0040: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]|[:foo]] (_,_,_,_,_,_,_,_) -0040: reset_match -0041: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]|[:foo]] (_,_,_,_,_,_,_,_) -0041: match -0042: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]|[:foo]] (_,_,_,_,_,_,_,_) -0042: panic_if_no_match -0043: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]|[:foo]] (_,_,_,_,_,_,_,_) -0043: push_binding 000 -0045: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]|[:foo]|box { [] }] (_,_,_,_,_,_,_,_) -0045: push_binding 003 -0047: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]|[:foo]|box { [] }|[:foo]] (_,_,_,_,_,_,_,_) -0047: get_upvalue 001 -0049: [box { [] }|fn foo!|fn foos!|->box { [] }<-|fn append/partial|[]|[:foo]|box { [] }|[:foo]|fn store!] (_,_,_,_,_,_,_,_) -0049: tail_call 002 -=== tail call into fn store!/2 from update! === -0000: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0000: reset_match -0001: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0001: match_depth 001 -0003: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0003: constant 00000: :box -0006: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]|:box] (_,_,_,_,_,_,_,_) -0006: match_type -0007: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0007: jump_if_no_match 00009 -0010: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0010: match_depth 000 -0012: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0012: match -0013: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0013: jump_if_no_match 00003 -0016: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0016: jump 00000 -0019: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0019: jump_if_no_match 00023 -0022: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0022: get_upvalue 000 -0024: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]|#{:list fn list/base...] (_,_,_,_,_,_,_,_) -0024: constant 00001: :store! -0027: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]|#{:list fn list/base...|:store!] (_,_,_,_,_,_,_,_) -0027: get_key -0028: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]|fn store!/base] (_,_,_,_,_,_,_,_) -0028: store -0029: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]] (fn store!/base,_,_,_,_,_,_,_) -0029: push_binding 000 -0031: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]|box { [] }] (fn store!/base,_,_,_,_,_,_,_) -0031: push_binding 001 -0033: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]|box { [] }|[:foo]] (fn store!/base,_,_,_,_,_,_,_) -0033: load -0034: [box { [] }|fn foo!|fn foos!|->box { [] }<-|[:foo]|box { [] }|[:foo]|fn store!/base] (_,_,_,_,_,_,_,_) -0034: call 002 -=== calling into fn store!/base/2 === -0036: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|[:foo]|[:foo]] (_,_,_,_,_,_,_,_) -0036: pop -0037: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0037: push_binding 001 -0039: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|[:foo]|[:foo]] (_,_,_,_,_,_,_,_) -0039: store -0040: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|[:foo]] ([:foo],_,_,_,_,_,_,_) -0040: load -0041: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|[:foo]|[:foo]] (_,_,_,_,_,_,_,_) -0041: store -0042: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|[:foo]] ([:foo],_,_,_,_,_,_,_) -0042: pop_n 002 -0044: [box { [:foo] }|fn foo!|fn foos!] ([:foo],_,_,_,_,_,_,_) -0044: return -== returning from fn store! == -0076: [->box { [:foo] }<-|fn foo!|fn foos!|[:foo]] (_,_,_,_,_,_,_,_) -0076: pop -0077: [->box { [:foo] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0077: ***calling function unbox stack depth: 3 -0079: [->box { [:foo] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0079: resolving binding `foos` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 -0081: [->box { [:foo] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0081: at locals position 0 -0083: [->box { [:foo] }<-|fn foo!|fn foos!] (_,_,_,_,_,_,_,_) -0083: push_binding 000 -0085: [->box { [:foo] }<-|fn foo!|fn foos!|box { [:foo] }] (_,_,_,_,_,_,_,_) -0085: resolving binding `unbox` in sandbox -locals: foos@0//0|foo!@1//0|foos!@2//0 -0087: [->box { [:foo] }<-|fn foo!|fn foos!|box { [:foo] }] (_,_,_,_,_,_,_,_) -0087: as global -0089: [->box { [:foo] }<-|fn foo!|fn foos!|box { [:foo] }] (_,_,_,_,_,_,_,_) -0089: constant 00004: :unbox -0092: [->box { [:foo] }<-|fn foo!|fn foos!|box { [:foo] }|:unbox] (_,_,_,_,_,_,_,_) -0092: push_global -0093: [->box { [:foo] }<-|fn foo!|fn foos!|box { [:foo] }|fn unbox] (_,_,_,_,_,_,_,_) -0093: ***after 1 args stack depth: 5 -0095: [->box { [:foo] }<-|fn foo!|fn foos!|box { [:foo] }|fn unbox] (_,_,_,_,_,_,_,_) -0095: call 001 -=== calling into fn unbox/1 === -0000: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-] (_,_,_,_,_,_,_,_) -0000: reset_match -0001: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-] (_,_,_,_,_,_,_,_) -0001: match_depth 000 -0003: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-] (_,_,_,_,_,_,_,_) -0003: constant 00000: :box -0006: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|:box] (_,_,_,_,_,_,_,_) -0006: match_type -0007: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-] (_,_,_,_,_,_,_,_) -0007: jump_if_no_match 00003 -0010: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-] (_,_,_,_,_,_,_,_) -0010: jump 00000 -0013: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-] (_,_,_,_,_,_,_,_) -0013: jump_if_no_match 00015 -0016: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-] (_,_,_,_,_,_,_,_) -0016: get_upvalue 000 -0018: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|#{:list fn list/base...] (_,_,_,_,_,_,_,_) -0018: constant 00001: :unbox -0021: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|#{:list fn list/base...|:unbox] (_,_,_,_,_,_,_,_) -0021: get_key -0022: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|fn unbox/base] (_,_,_,_,_,_,_,_) -0022: store -0023: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-] (fn unbox/base,_,_,_,_,_,_,_) -0023: push_binding 000 -0025: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|box { [:foo] }] (fn unbox/base,_,_,_,_,_,_,_) -0025: load -0026: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|box { [:foo] }|fn unbox/base] (_,_,_,_,_,_,_,_) -0026: tail_call 001 -=== tail call into fn unbox/base/1 from unbox === -0028: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-|[:foo]] (_,_,_,_,_,_,_,_) -0028: store -0029: [box { [:foo] }|fn foo!|fn foos!|->box { [:foo] }<-] ([:foo],_,_,_,_,_,_,_) -0029: pop -0030: [box { [:foo] }|fn foo!|fn foos!] ([:foo],_,_,_,_,_,_,_) -0030: return -== returning from fn unbox == -0097: [->box { [:foo] }<-|fn foo!|fn foos!|[:foo]] (_,_,_,_,_,_,_,_) -0097: store -0098: [->box { [:foo] }<-|fn foo!|fn foos!] ([:foo],_,_,_,_,_,_,_) -0098: leaving scope 0 -0100: [->box { [:foo] }<-|fn foo!|fn foos!] ([:foo],_,_,_,_,_,_,_) -0100: releasing binding foos!@2//0 -0102: [->box { [:foo] }<-|fn foo!|fn foos!] ([:foo],_,_,_,_,_,_,_) -0102: releasing binding foo!@1//0 -0104: [->box { [:foo] }<-|fn foo!|fn foos!] ([:foo],_,_,_,_,_,_,_) -0104: releasing binding foos@0//0 -0106: [->box { [:foo] }<-|fn foo!|fn foos!] ([:foo],_,_,_,_,_,_,_) -0106: ***leaving block before pop stack depth: 3 -0108: [->box { [:foo] }<-|fn foo!|fn foos!] ([:foo],_,_,_,_,_,_,_) -0108: popping back from 3 to 0 -0110: [->box { [:foo] }<-|fn foo!|fn foos!] ([:foo],_,_,_,_,_,_,_) -0110: pop_n 003 -0112: [] ([:foo],_,_,_,_,_,_,_) -0112: load -0112: [] (_,_,_,_,_,_,_,_) -[:foo] +entering world loop; active process is cormorant_0 +0000: [] (_,_,_,_,_,_,_,_) cormorant_0 {} +0000: constant 00000: :set +0003: [->:set<-] (_,_,_,_,_,_,_,_) cormorant_0 {} +0003: constant 00001: 12 +0006: [->:set<-|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0006: push_tuple 002 +0008: [->(:set, 12)<-] (_,_,_,_,_,_,_,_) cormorant_0 {} +0008: ***match clause: : (:set, x) +0010: [->(:set, 12)<-] (_,_,_,_,_,_,_,_) cormorant_0 {} +0010: match_tuple 002 +0012: [->(:set, 12)<-] (_,_,_,_,_,_,_,_) cormorant_0 {} +0012: jump_if_no_match 00028 +0015: [->(:set, 12)<-] (_,_,_,_,_,_,_,_) cormorant_0 {} +0015: load_tuple +0016: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0016: match_depth 001 +0018: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0018: match_constant 00000: :set +0021: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0021: jump_if_no_match 00017 +0024: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0024: match_depth 000 +0026: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0026: match +0027: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0027: binding `x` in sandbox +0029: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0029: stack depth: 3; match depth: 0 +0031: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0031: at stack index: 2 +0033: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0033: new locals: x@2//1 +0035: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0035: jump_if_no_match 00003 +0038: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0038: jump 00002 +0043: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0043: jump_if_no_match 00016 +0046: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0046: resolving binding `x` in sandbox +locals: x@2//1 +0048: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0048: at locals position 2 +0050: [->(:set, 12)<-|:set|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0050: push_binding 002 +0052: [->(:set, 12)<-|:set|12|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0052: store +0053: [->(:set, 12)<-|:set|12] (12,_,_,_,_,_,_,_) cormorant_0 {} +0053: leaving scope 1 +0055: [->(:set, 12)<-|:set|12] (12,_,_,_,_,_,_,_) cormorant_0 {} +0055: releasing binding x@2//1 +0057: [->(:set, 12)<-|:set|12] (12,_,_,_,_,_,_,_) cormorant_0 {} +0057: pop_n 002 +0059: [->(:set, 12)<-] (12,_,_,_,_,_,_,_) cormorant_0 {} +0059: jump 00001 +0063: [->(:set, 12)<-] (12,_,_,_,_,_,_,_) cormorant_0 {} +0063: load +0064: [->(:set, 12)<-|12] (_,_,_,_,_,_,_,_) cormorant_0 {} +0064: store +0065: [->(:set, 12)<-] (12,_,_,_,_,_,_,_) cormorant_0 {} +0065: leaving scope 0 +0067: [->(:set, 12)<-] (12,_,_,_,_,_,_,_) cormorant_0 {} +0067: ***leaving block before pop stack depth: 1 +0069: [->(:set, 12)<-] (12,_,_,_,_,_,_,_) cormorant_0 {} +0069: popping back from 1 to 0 +0071: [->(:set, 12)<-] (12,_,_,_,_,_,_,_) cormorant_0 {} +0071: pop +0072: [] (12,_,_,_,_,_,_,_) cormorant_0 {} +0072: load +yielded from cormorant_0 +{"result":"12","io":{"stdout":{"proto":["text-stream","0.1.0"],"data":""},"turtle":{"proto":["turtle-graphics","0.1.0"],"data":[]}}} diff --git a/src/lib.rs b/src/lib.rs index af892a5..f1a02c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,8 +184,8 @@ pub fn ludus(src: String) -> String { // TODO: use serde_json to make this more robust? format!( -"{{\"result\":\"{output}\",\"io\":{{\"stdout\":{{\"proto\":[\"text-stream\",\"0.1.0\"],\"data\":\"{console}\"}},\"turtle\":{{\"proto\":[\"turtle-graphics\",\"0.1.0\"],\"data\":{commands}}}}}}}" - ) + "{{\"result\":\"{output}\",\"io\":{{\"stdout\":{{\"proto\":[\"text-stream\",\"0.1.0\"],\"data\":\"{console}\"}},\"turtle\":{{\"proto\":[\"turtle-graphics\",\"0.1.0\"],\"data\":{commands}}}}}}}" + ) } pub fn fmt(src: &'static str) -> Result { diff --git a/src/vm.rs b/src/vm.rs index 06f222b..3234f71 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -13,7 +13,7 @@ use std::fmt; use std::mem::swap; use std::rc::Rc; -const MAX_REDUCTIONS: usize = 100; +const MAX_REDUCTIONS: usize = 1000; #[derive(Debug, Clone, PartialEq)] pub enum Panic { @@ -91,7 +91,7 @@ pub struct Creature { pub result: Option>, debug: bool, last_code: usize, - pub id: &'static str, + pub pid: &'static str, pub mbx: VecDeque, pub reductions: usize, pub zoo: Rc>, @@ -100,7 +100,7 @@ pub struct Creature { impl std::fmt::Display for Creature { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Creature. {} @{}", self.id, self.ip) + write!(f, "Creature. {} @{}", self.pid, self.ip) } } @@ -136,7 +136,7 @@ impl Creature { result: None, debug, last_code: 0, - id: "", + pid: "", zoo, mbx: VecDeque::new(), reductions: 0, @@ -197,7 +197,7 @@ impl Creature { .join("/"); println!( "{:04}: [{inner}] ({register}) {} {{{mbx}}}", - self.last_code, self.id + self.last_code, self.pid ); } @@ -230,11 +230,13 @@ impl Creature { pub fn panic(&mut self, msg: &'static str) { let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack()); + println!("process {} panicked!\n{msg}", self.pid); self.result = Some(Err(Panic::String(msg))); } pub fn panic_with(&mut self, msg: String) { let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack()); + println!("process {} panicked!\n{msg}", self.pid); self.result = Some(Err(Panic::String(msg))); } @@ -266,22 +268,35 @@ impl Creature { } fn handle_msg(&mut self, args: Vec) { - println!("message received! {}", args[0]); + println!("message received by {}: {}", self.pid, args[0]); let Value::Keyword(msg) = args.first().unwrap() else { return self.panic("malformed message to Process"); }; match *msg { - "self" => self.push(Value::Keyword(self.id)), + "self" => self.push(Value::Keyword(self.pid)), "msgs" => { let msgs = self.mbx.iter().cloned().collect::>(); let msgs = Vector::from(msgs); + println!( + "delivering messages: {}", + msgs.iter() + .map(|x| x.show()) + .collect::>() + .join(" | ") + ); self.push(Value::List(Box::new(msgs))); } "send" => { let Value::Keyword(pid) = args[1] else { return self.panic("malformed pid"); }; - if self.id == pid { + println!( + "sending msg from {} to {} of {}", + self.pid, + pid, + args[2].show() + ); + if self.pid == pid { self.mbx.push_back(args[2].clone()); } else { self.zoo @@ -300,7 +315,7 @@ impl Creature { } "yield" => { self.r#yield = true; - println!("yielding from {}", self.id); + println!("yielding from {}", self.pid); self.push(Value::Keyword("ok")); } "alive" => { @@ -317,28 +332,64 @@ impl Creature { "link" => todo!(), "flush" => { self.mbx = VecDeque::new(); + println!("flushing messages in {}", self.pid); self.push(Value::Keyword("ok")); } "sleep" => { - println!("sleeping {} for {}", self.id, args[1]); + println!("sleeping {} for {}", self.pid, args[1]); let Value::Number(ms) = args[1] else { unreachable!() }; - self.zoo.as_ref().borrow_mut().sleep(self.id, ms as usize); + self.zoo.as_ref().borrow_mut().sleep(self.pid, ms as usize); self.r#yield = true; self.push(Value::Keyword("ok")); } + // "flush_i" => { + // let Value::Number(n) = args[1] else { + // unreachable!() + // }; + // println!("flushing message at {n}"); + // self.mbx.remove(n as usize); + // println!( + // "mailbox is now: {}", + // self.mbx + // .iter() + // .map(|msg| msg.to_string()) + // .collect::>() + // .join(" | ") + // ); + // self.push(Value::Keyword("ok")); + // } msg => panic!("Process does not understand message: {msg}"), } } pub fn interpret(&mut self) { + println!("starting process {}", self.pid); + println!( + "mbx: {}", + self.mbx + .iter() + .map(|x| x.show()) + .collect::>() + .join(" | ") + ); loop { if self.at_end() { - self.result = Some(Ok(self.stack.pop().unwrap())); + let result = self.stack.pop().unwrap(); + println!("process {} has returned {result}", self.pid); + self.result = Some(Ok(result)); return; } - if self.reductions >= MAX_REDUCTIONS || self.r#yield { + if self.r#yield { + println!("process {} has explicitly yielded", self.pid); + return; + } + if self.reductions >= MAX_REDUCTIONS { + println!( + "process {} is yielding after {MAX_REDUCTIONS} reductions", + self.pid + ); return; } let code = self.read(); @@ -1144,6 +1195,7 @@ impl Creature { self.push(value); } None => { + println!("process {} has returned with {}", self.pid, value); self.result = Some(Ok(value)); return; } diff --git a/src/world.rs b/src/world.rs index 5034323..ab5cdde 100644 --- a/src/world.rs +++ b/src/world.rs @@ -106,7 +106,7 @@ impl Zoo { if self.empty.is_empty() { let id = self.new_id(); let idx = self.procs.len(); - proc.id = id; + proc.pid = id; self.procs.push(Status::Nested(proc)); self.ids.insert(id, idx); id @@ -114,7 +114,7 @@ impl Zoo { let idx = self.empty.pop().unwrap(); let rand = ran_u8() as usize % 24; let id = format!("{}_{idx}", ANIMALS[rand]).leak(); - proc.id = id; + proc.pid = id; self.ids.insert(id, idx); self.procs[idx] = Status::Nested(proc); id @@ -186,7 +186,7 @@ impl Zoo { } pub fn release(&mut self, proc: Creature) { - let id = proc.id; + let id = proc.pid; if let Some(idx) = self.ids.get(id) { let mut proc = Status::Nested(proc); swap(&mut proc, &mut self.procs[*idx]); @@ -203,7 +203,7 @@ impl Zoo { match &self.procs[self.active_idx] { Status::Empty => false, Status::Borrowed => false, - Status::Nested(proc) => !self.sleeping.contains_key(proc.id), + Status::Nested(proc) => !self.sleeping.contains_key(proc.pid), } } @@ -219,7 +219,7 @@ impl Zoo { } match &self.procs[self.active_idx] { Status::Empty | Status::Borrowed => unreachable!(), - Status::Nested(proc) => proc.id, + Status::Nested(proc) => proc.pid, } } @@ -253,26 +253,11 @@ impl World { } } - // pub fn spawn(&mut self, proc: Creature) -> Value { - // let id = self.zoo.put(proc); - // Value::Keyword(id) - // } - - // pub fn send_msg(&mut self, id: &'static str, msg: Value) { - // let mut proc = self.zoo.catch(id); - // proc.receive(msg); - // self.zoo.release(proc); - // } - // fn next(&mut self) { let mut active = None; swap(&mut active, &mut self.active); let mut zoo = self.zoo.as_ref().borrow_mut(); zoo.release(active.unwrap()); - // at the moment, active is None, process is released. - // zoo should NOT need an id--it has a representation of the current active process - // world has an active process for memory reasons - // not for state-keeping reasons let new_active_id = zoo.next(); let mut new_active_proc = zoo.catch(new_active_id); new_active_proc.reset_reductions(); @@ -280,62 +265,13 @@ impl World { swap(&mut new_active_opt, &mut self.active); } - // fn old_next(&mut self) { - // let id = self - // .zoo - // .as_ref() - // .borrow_mut() - // .next(self.active.as_ref().unwrap().id); - // println!("next id is {id}"); - // let mut active = None; - // std::mem::swap(&mut active, &mut self.active); - // let mut holding_pen = self.zoo.as_ref().borrow_mut().catch(id); - // let mut active = active.unwrap(); - // std::mem::swap(&mut active, &mut holding_pen); - // println!("now in the holding pen: {}", holding_pen.id); - // holding_pen.reset_reductions(); - // self.zoo.as_ref().borrow_mut().release(holding_pen); - // let mut active = Some(active); - // std::mem::swap(&mut active, &mut self.active); - // } - - // pub fn sleep(&mut self, id: &'static str) { - // // check if the id is the actually active process - // if self.active.id != id { - // panic!("attempted to sleep a process from outside that process: active = {}; to sleep: = {id}", self.active.id); - // } - // self.next(id); - // } - - // pub fn panic(&mut self, id: &'static str, panic: Panic) { - // // TODO: devise some way of linking processes (study the BEAM on this) - // // check if the id is active - // if self.active.id != id { - // panic!("attempted to panic from a process from outside that process: active = {}; panicking = {id}; panic = {panic}", self.active.id); - // } - // // check if the process is `main`, and crash the program if it is - // if self.main == id { - // self.result = self.active.result.clone(); - // } - // // kill the process - // self.zoo.kill(id); - // self.next(id); - // } - - // pub fn complete(&mut self) { - // if self.main == self.active.id { - // self.result = self.active.result.clone(); - // } - // self.next(id); - // } - pub fn activate_main(&mut self) { let main = self.zoo.as_ref().borrow_mut().catch(self.main); self.active = Some(main); } pub fn active_id(&mut self) -> &'static str { - self.active.as_ref().unwrap().id + self.active.as_ref().unwrap().pid } pub fn kill_active(&mut self) { @@ -363,7 +299,11 @@ impl World { self.result = self.active_result().clone(); return; } - println!("process died: {}", self.active_id()); + println!( + "process {} died with {:?}", + self.active_id(), + self.active_result().clone() + ); self.kill_active(); } }