diff --git a/assets/test_prelude.ld b/assets/test_prelude.ld index 5c6702f..e98d011 100644 --- a/assets/test_prelude.ld +++ b/assets/test_prelude.ld @@ -1029,16 +1029,10 @@ box turtle_state = turtle_init fn apply_command fn add_command! (command) -> { - print! ("adding {command}") update! (turtle_commands, append (_, command)) - print! ("added command to commands") - print! (turtle_commands) let prev = unbox (turtle_state) - print! ("previous state: {prev}") let curr = apply_command (prev, command) - print! ("new state: {curr}") store! (turtle_state, curr) - print! ("stored state: {turtle_state}") :ok } @@ -1153,7 +1147,6 @@ fn loadstate! { fn apply_command { "Takes a turtle state and a command and calculates a new state." (state, command) -> { - print!("apply_command:\n{state}\n{command}") match command with { (:goto, (x, y)) -> assoc (state, :position, (x, y)) (:home) -> do state > diff --git a/may_2025_thoughts.md b/may_2025_thoughts.md index 1496c66..ca7bc86 100644 --- a/may_2025_thoughts.md +++ b/may_2025_thoughts.md @@ -518,3 +518,8 @@ PROBLEM: forward-declared functions weren't at the top of the stack when `Op::Se So all of `apply_command`'s upvalues were being attached to the function declared before it (which was sitting right there at the top of the stack.) SOLUTION: test to see if the function has been forward-declared, and if it has, bring it to the top fo the stack. + +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. +This is the thing I am about to do diff --git a/sandbox.ld b/sandbox.ld index 9e65145..653a8a6 100644 --- a/sandbox.ld +++ b/sandbox.ld @@ -1,41 +1,11 @@ -& fn apply_command { -& "Takes a turtle state and a command and calculates a new state." -& (state, command) -> match command with { -& (:goto, (x, y)) -> assoc (state, :position, (x, y)) -& (:home) -> do state > -& assoc (_, :position, (0, 0)) > -& assoc (_, :heading, 0) -& (:clear) -> do state > -& assoc (state, :position, (0, 0)) > -& assoc (_, :heading, 0) -& (:right, turns) -> update (state, :heading, add (_, turns)) -& (:left, turns) -> update (state, :heading, sub (_, turns)) -& (:forward, steps) -> { -& let #{heading, position, ...} = state -& let unit = heading/vector (heading) -& let vect = mult (steps, unit) -& update (state, :position, add (vect, _)) -& } -& (:back, steps) -> { -& let #{heading, position, ...} = state -& let unit = heading/vector (heading) -& let vect = mult (steps, unit) -& update (state, :position, sub (_, vect)) -& } -& (:penup) -> assoc (state, :pendown?, false) -& (:pendown) -> assoc (state, :pendown?, true) -& (:penwidth, pixels) -> assoc (state, :penwidth, pixels) -& (:pencolor, color) -> assoc (state, :pencolor, color) -& (:setheading, heading) -> assoc (state, :heading, heading) -& (:loadstate, position, heading, visible?, pendown?, penwidth, pencolor) -> #{position, heading, visible?, pendown?, penwidth, pencolor} -& (:show) -> assoc (state, :visible?, true) -& (:hide) -> assoc (state, :visible?, false) -& (:background, _) -> state -& } -& } +box foos = [] -fn many_args (_, _, x, _) -> x +fn foo! () -> update! (foos, append (_, :foo)) -let fewer_args = many_args (:foo, :bar, _, :baz) +fn foos! () -> repeat 4 { + foo! () +} -fewer_args(:quux) +foos! () + +unbox (foos) diff --git a/sandbox_run.txt b/sandbox_run.txt index a5a9d3b..8d99c9c 100644 --- a/sandbox_run.txt +++ b/sandbox_run.txt @@ -1,19 +1,32 @@ { - :foo + box foos = [] + fn foo! { + () -> update! (foos, append (_, :foo) ) } -closing over in type at 1: #{:cos fn cos/base, ... -closing over in eq? at 1: #{:cos fn cos/base, ... + 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... closing over in eq? at 2: fn eq? -closing over in first at 1: #{:cos fn cos/base, ... -closing over in rest at 1: #{:cos fn cos/base, ... -closing over in inc at 1: #{:cos fn cos/base, ... -closing over in dec at 1: #{:cos fn cos/base, ... -closing over in count at 1: #{:cos fn cos/base, ... +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 any? at 1: fn empty? closing over in any? at 2: fn not -closing over in list at 1: #{:cos fn cos/base, ... -closing over in append at 1: #{:cos fn cos/base, ... +closing over in list at 1: #{:list fn list/base... +closing over in append at 1: #{:list fn list/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 @@ -28,18 +41,18 @@ 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: #{:cos fn cos/base, ... +closing over in concat at 1: #{:list fn list/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: #{:cos fn cos/base, ... -closing over in show at 1: #{:cos fn cos/base, ... +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: #{:cos fn cos/base, ... +closing over in doc! at 1: #{:list fn list/base... closing over in doc! at 2: fn print! closing over in string at 1: fn show closing over in string at 2: fn string @@ -47,12 +60,12 @@ 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: #{:cos fn cos/base, ... -closing over in trim at 1: #{:cos fn cos/base, ... -closing over in upcase at 1: #{:cos fn cos/base, ... -closing over in downcase at 1: #{:cos fn cos/base, ... -closing over in chars at 1: #{:cos fn cos/base, ... -closing over in chars/safe at 1: #{:cos fn cos/base, ... +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 strip at 1: fn strip closing over in words at 1: fn strip closing over in words at 2: fn split @@ -60,25 +73,25 @@ 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: #{:cos fn cos/base, ... -closing over in unbox at 1: #{:cos fn cos/base, ... -closing over in store! at 1: #{:cos fn cos/base, ... +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: #{:cos fn cos/base, ... +closing over in add at 1: #{:list fn list/base... closing over in add at 2: fn add closing over in add at 3: fn fold -closing over in sub at 1: #{:cos fn cos/base, ... +closing over in sub at 1: #{:list fn list/base... closing over in sub at 2: fn sub closing over in sub at 3: fn fold -closing over in mult at 1: #{:cos fn cos/base, ... +closing over in mult at 1: #{:list fn list/base... closing over in mult at 2: fn mult closing over in mult at 3: fn fold -closing over in div at 1: #{:cos fn cos/base, ... +closing over in div at 1: #{:list fn list/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: #{:cos fn cos/base, ... +closing over in div/0 at 1: #{:list fn list/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 @@ -90,10 +103,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: #{:cos fn cos/base, ... -closing over in gte? at 1: #{:cos fn cos/base, ... -closing over in lt? at 1: #{:cos fn cos/base, ... -closing over in lte? at 1: #{:cos fn cos/base, ... +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 between? at 1: fn gte? closing over in between? at 2: fn lt? closing over in neg? at 1: fn lt? @@ -113,13 +126,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: #{:cos fn cos/base, ... +closing over in sin at 2: #{:list fn list/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: #{:cos fn cos/base, ... +closing over in cos at 2: #{:list fn list/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: #{:cos fn cos/base, ... +closing over in tan at 2: #{:list fn list/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 @@ -127,15 +140,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: #{:cos fn cos/base, ... +closing over in atan/2 at 1: #{:list fn list/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: #{:cos fn cos/base, ... -closing over in mod/0 at 1: #{:cos fn cos/base, ... -closing over in mod/safe at 1: #{:cos fn cos/base, ... +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 even? at 1: fn mod closing over in even? at 2: fn eq? closing over in odd? at 1: fn mod @@ -143,10 +156,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: #{:cos fn cos/base, ... +closing over in sqrt at 3: #{:list fn list/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: #{:cos fn cos/base, ... +closing over in sqrt/safe at 3: #{:list fn list/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 @@ -158,11 +171,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: #{:cos fn cos/base, ... -closing over in ceil at 1: #{:cos fn cos/base, ... -closing over in round at 1: #{:cos fn cos/base, ... -closing over in range at 1: #{:cos fn cos/base, ... -closing over in at at 1: #{:cos fn cos/base, ... +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 second at 1: fn ordered? closing over in second at 2: fn at closing over in last at 1: fn ordered? @@ -174,14 +187,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: #{:cos fn cos/base, ... +closing over in slice at 6: #{:list fn list/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: #{:cos fn cos/base, ... -closing over in dissoc at 1: #{:cos fn cos/base, ... +closing over in assoc at 1: #{:list fn list/base... +closing over in dissoc at 1: #{:list fn list/base... closing over in get at 1: fn get -closing over in get at 2: #{:cos fn cos/base, ... +closing over in get at 2: #{:list fn list/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 @@ -198,7 +211,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: #{:cos fn cos/base, ... +closing over in random at 1: #{:list fn list/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 @@ -213,7 +226,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 { #{:pendown? tr... +closing over in add_command! at 4: box { #{:position (0... 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! @@ -234,21 +247,671 @@ closing over in setheading! at 1: fn add_command! closing over in showturtle! at 1: fn add_command! closing over in hideturtle! at 1: fn add_command! closing over in loadstate! at 1: fn add_command! -closing over in foo at 1: fn hideturtle! -closing over in foo at 2: fn assoc -closing over in foo at 3: fn add -closing over in foo at 4: fn update -closing over in foo at 5: fn sub -closing over in foo at 6: fn heading/vector -closing over in foo at 7: fn mult -closing over in position at 1: box { #{:pendown? tr... +closing over in apply_command at 1: fn assoc +closing over in apply_command at 2: fn add +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 2: fn unbox -closing over in heading at 1: box { #{:pendown? tr... +closing over in heading at 1: box { #{:position (0... closing over in heading at 2: fn unbox -closing over in pendown? at 1: box { #{:pendown? tr... +closing over in pendown? at 1: box { #{:position (0... closing over in pendown? at 2: fn unbox -closing over in pencolor at 1: box { #{:pendown? tr... +closing over in pencolor at 1: box { #{:position (0... closing over in pencolor at 2: fn unbox -closing over in penwidth at 1: box { #{:pendown? tr... +closing over in penwidth at 1: box { #{:position (0... closing over in penwidth at 2: fn unbox -:foo +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 +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 +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 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 +=== source code === +box foos = [] + +fn foo! () -> update! (foos, append (_, :foo)) + +fn foos! () -> repeat 4 { + foo! () +} + +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 + + + +=== 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] diff --git a/src/chunk.rs b/src/chunk.rs index 98409a3..6551278 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -27,11 +27,11 @@ impl Chunk { match op { Pop | Store | Stash | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse | PanicIfNoMatch | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch | TypeOf - | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq | Add | Sub + | Duplicate | Decrement | ToInt | Noop | LoadTuple | LoadList | Eq | Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString - | ConcatStrings | Stringify | MatchType | Return | Match | Print | AppendList - | ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing | PushGlobal - | SetUpvalue => { + | ConcatStrings | Stringify | MatchType | Return | UnconditionalMatch | Print + | AppendList | ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing + | PushGlobal | SetUpvalue => { println!("{i:04}: {op}") } Constant | MatchConstant => { diff --git a/src/compiler.rs b/src/compiler.rs index 7cd0595..7a03288 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -521,12 +521,12 @@ impl Compiler { self.report_depth("after let binding"); } WordPattern(name) => { - self.emit_op(Op::Match); + self.emit_op(Op::UnconditionalMatch); self.bind(name); } Word(name) | Splat(name) => self.resolve_binding(name), PlaceholderPattern => { - self.emit_op(Op::Match); + self.emit_op(Op::UnconditionalMatch); } NilPattern => { self.emit_op(Op::MatchNil); @@ -1124,7 +1124,7 @@ impl Compiler { tup_jump_idxes.push(compiler.stub_jump(Op::JumpIfNoMatch)); } if pattern.is_empty() { - compiler.emit_op(Op::Match); + compiler.emit_op(Op::UnconditionalMatch); } let jump_idx = compiler.stub_jump(Op::Jump); for idx in tup_jump_idxes { @@ -1239,8 +1239,10 @@ impl Compiler { } FnBody(_) => unreachable!(), Repeat(times, body) => { + let tail_pos = self.tail_pos; + self.tail_pos = false; self.visit(times); - self.emit_op(Op::Truncate); + self.emit_op(Op::ToInt); // skip the decrement the first time self.emit_op(Op::Jump); self.emit_byte(0); @@ -1260,6 +1262,7 @@ impl Compiler { self.patch_jump(jiz_idx, self.len() - repeat_begin - 4); self.pop(); self.emit_constant(Value::Nil); + self.tail_pos = tail_pos; } Loop(value, clauses) => { self.report_depth("entering loop"); diff --git a/src/main.rs b/src/main.rs index b9674ef..18e7f31 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use imbl::HashMap; use std::env; use std::fs; -const DEBUG_SCRIPT_COMPILE: bool = false; +const DEBUG_SCRIPT_COMPILE: bool = true; const DEBUG_SCRIPT_RUN: bool = true; const DEBUG_PRELUDE_COMPILE: bool = false; const DEBUG_PRELUDE_RUN: bool = false; diff --git a/src/op.rs b/src/op.rs index b911e6b..bfe0252 100644 --- a/src/op.rs +++ b/src/op.rs @@ -21,7 +21,7 @@ pub enum Op { Load, LoadN, ResetMatch, - Match, + UnconditionalMatch, MatchNil, MatchTrue, MatchFalse, @@ -60,7 +60,7 @@ pub enum Op { JumpIfZero, Duplicate, Decrement, - Truncate, + ToInt, MatchDepth, Panic, EmptyString, @@ -152,7 +152,7 @@ impl std::fmt::Display for Op { Stash => "stash", Load => "load", LoadN => "load_n", - Match => "match", + UnconditionalMatch => "match", MatchNil => "match_nil", MatchTrue => "match_true", MatchFalse => "match_false", @@ -191,7 +191,7 @@ impl std::fmt::Display for Op { JumpBack => "jump_back", JumpIfZero => "jump_if_zero", Decrement => "decrement", - Truncate => "truncate", + ToInt => "truncate", Duplicate => "duplicate", MatchDepth => "match_depth", Panic => "panic", diff --git a/src/value.rs b/src/value.rs index 2f1e8c4..2f387f9 100644 --- a/src/value.rs +++ b/src/value.rs @@ -29,7 +29,9 @@ impl LFn { let shown = value.show(); closed.borrow_mut().push(value); let pos = closed.borrow().len(); - println!("closing over in {} at {pos}: {shown}", self.name(),); + if crate::DEBUG_SCRIPT_RUN { + println!("closing over in {} at {pos}: {shown}", self.name(),); + } } } } diff --git a/src/vm.rs b/src/vm.rs index 589f32e..e2260fe 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -90,6 +90,7 @@ pub struct Vm { pub match_depth: u8, pub result: Option>, debug: bool, + last_code: usize, } impl Vm { @@ -119,6 +120,7 @@ impl Vm { match_depth: 0, result: None, debug, + last_code: 0, } } @@ -148,24 +150,18 @@ impl Vm { } } let inner = inner.join("|"); - // let inner = self - // .stack - // .iter() - // .map(|val| val.show()) - // .collect::>() - // .join("|"); let register = self .return_register .iter() .map(|val| val.to_string()) .collect::>() .join(","); - println!("{:04}: [{inner}] ({register})", self.ip); + println!("{:04}: [{inner}] ({register})", self.last_code); } fn print_debug(&self) { self.print_stack(); - let mut ip = self.ip; + let mut ip = self.last_code; self.chunk().dissasemble_instr(&mut ip); } @@ -200,120 +196,104 @@ impl Vm { self.result = Some(Err(Panic::String(msg))); } + fn get_value_at(&mut self, idx: u8) -> Value { + let idx = idx as usize; + let idx = idx + self.frame.stack_base; + self.stack[idx].clone() + } + + fn get_scrutinee(&mut self) -> Value { + let idx = self.stack.len() - self.match_depth as usize - 1; + self.stack[idx].clone() + } + + fn read(&mut self) -> u8 { + let code = self.chunk().bytecode[self.ip]; + self.ip += 1; + code + } + + fn read2(&mut self) -> usize { + let high = self.read(); + let low = self.read(); + combine_bytes(high, low) + } + + fn at_end(&mut self) -> bool { + self.ip >= self.chunk().bytecode.len() + } + pub fn interpret(&mut self) { loop { - let Some(byte) = self.chunk().bytecode.get(self.ip) else { + if self.at_end() { self.result = Some(Ok(self.stack.pop().unwrap())); return; - }; + } + let code = self.read(); if self.debug { + self.last_code = self.ip - 1; self.print_debug(); } - let op = Op::from_u8(*byte).unwrap(); + let op = Op::from_u8(code).unwrap(); use Op::*; match op { - Noop => { - self.ip += 1; - } - Nil => { - self.push(Value::Nil); - self.ip += 1; - } - Nothing => { - self.push(Value::Nothing); - self.ip += 1; - } - True => { - self.push(Value::True); - self.ip += 1; - } - False => { - self.push(Value::False); - self.ip += 1; + Noop => (), + Nil => self.push(Value::Nil), + Nothing => self.push(Value::Nothing), + True => self.push(Value::True), + False => self.push(Value::False), + Msg => { + let _ = self.read(); } Constant => { - let high = self.chunk().bytecode[self.ip + 1]; - let low = self.chunk().bytecode[self.ip + 2]; - let const_idx = combine_bytes(high, low); + let const_idx = self.read2(); let value = self.chunk().constants[const_idx].clone(); self.push(value); - self.ip += 3; } Jump => { - let high = self.chunk().bytecode[self.ip + 1]; - let low = self.chunk().bytecode[self.ip + 2]; - let jump_len = combine_bytes(high, low); - // let jump_len = self.chunk().bytecode[self.ip + 1]; - self.ip += jump_len + 3; + let jump_len = self.read2(); + self.ip += jump_len; } JumpBack => { - let high = self.chunk().bytecode[self.ip + 1]; - let low = self.chunk().bytecode[self.ip + 2]; - let jump_len = combine_bytes(high, low); - // let jump_len = self.chunk().bytecode[self.ip + 1]; - self.ip -= jump_len; + let jump_len = self.read2(); + self.ip -= jump_len + 3; } JumpIfFalse => { - let high = self.chunk().bytecode[self.ip + 1]; - let low = self.chunk().bytecode[self.ip + 2]; - let jump_len = combine_bytes(high, low); - // let jump_len = self.chunk().bytecode[self.ip + 1]; + let jump_len = self.read2(); let cond = self.pop(); match cond { - Value::Nil | Value::False => { - self.ip += jump_len + 3; - } - _ => { - self.ip += 3; - } + Value::Nil | Value::False => self.ip += jump_len, + _ => (), } } JumpIfTrue => { - let high = self.chunk().bytecode[self.ip + 1]; - let low = self.chunk().bytecode[self.ip + 2]; - let jump_len = combine_bytes(high, low); - // let jump_len = self.chunk().bytecode[self.ip + 1]; + let jump_len = self.read2(); let cond = self.pop(); match cond { - Value::Nil | Value::False => { - self.ip += 3; - } - _ => { - self.ip += jump_len + 3; - } + Value::Nil | Value::False => (), + _ => self.ip += jump_len, } } JumpIfZero => { - let high = self.chunk().bytecode[self.ip + 1]; - let low = self.chunk().bytecode[self.ip + 2]; - let jump_len = combine_bytes(high, low); - // let jump_len = self.chunk().bytecode[self.ip + 1]; + let jump_len = self.read2(); let cond = self.pop(); match cond { - Value::Number(x) if x <= 0.0 => { - self.ip += jump_len + 3; - } - Value::Number(..) => { - self.ip += 3; - } + Value::Number(x) if x <= 0.0 => self.ip += jump_len, + Value::Number(..) => (), _ => return self.panic("repeat requires a number"), } } Pop => { self.pop(); - self.ip += 1; } PopN => { - let n = self.chunk().bytecode[self.ip + 1] as usize; + let n = self.read() as usize; self.stack.truncate(self.stack.len() - n); - self.ip += 2; } PushBinding => { - let binding_idx = - self.chunk().bytecode[self.ip + 1] as usize + self.frame.stack_base; - let binding_value = self.stack[binding_idx].clone(); - self.push(binding_value); - self.ip += 2; + let idx = self.read(); + let value = self.get_value_at(idx); + self.push(value); } PushGlobal => { let key = self.pop(); @@ -322,98 +302,74 @@ impl Vm { }; let value = self.chunk().env.get(name).unwrap(); self.push(value.clone()); - self.ip += 1; } Store => { self.return_register[0] = self.pop(); - self.ip += 1; } StoreN => { - let n = self.chunk().bytecode[self.ip + 1] as usize; + let n = self.read() as usize; for i in (0..n).rev() { self.return_register[i] = self.pop(); } - self.ip += 2; } Stash => { self.return_register[0] = self.peek().clone(); - self.ip += 1; } Load => { let mut value = Value::Nothing; swap(&mut self.return_register[0], &mut value); self.push(value); - self.ip += 1; } LoadN => { - let n = self.chunk().bytecode[self.ip + 1] as usize; + let n = self.read() as usize; for i in 0..n { let mut value = Value::Nothing; swap(&mut self.return_register[i], &mut value); self.push(value); } - self.ip += 2; } ResetMatch => { self.matches = false; self.match_depth = 0; - self.ip += 1; } - Match => { + UnconditionalMatch => { self.matches = true; - self.ip += 1; } MatchType => { let as_type = self.pop(); let Value::Keyword(as_type) = as_type else { unreachable!() }; - let idx = self.stack.len() - self.match_depth as usize - 1; - let val_type = self.stack[idx].type_of(); + let value = self.get_scrutinee(); + let val_type = value.type_of(); self.matches = val_type == as_type; - self.ip += 1; } MatchNil => { - let idx = self.stack.len() - self.match_depth as usize - 1; - if self.stack[idx] == Value::Nil { - self.matches = true; - }; - self.ip += 1; + let value = self.get_scrutinee(); + self.matches = value == Value::Nil; } MatchTrue => { - let idx = self.stack.len() - self.match_depth as usize - 1; - if self.stack[idx] == Value::True { - self.matches = true; - }; - self.ip += 1; + let value = self.get_scrutinee(); + self.matches = value == Value::True; } MatchFalse => { - let idx = self.stack.len() - self.match_depth as usize - 1; - if self.stack[idx] == Value::False { - self.matches = true; - } - self.ip += 1; + let value = self.get_scrutinee(); + self.matches = value == Value::False; } PanicIfNoMatch => { if !self.matches { return self.panic("no match"); - } else { - self.ip += 1; } } MatchConstant => { - let high = self.chunk().bytecode[self.ip + 1]; - let low = self.chunk().bytecode[self.ip + 2]; - let const_idx = combine_bytes(high, low); - let idx = self.stack.len() - self.match_depth as usize - 1; - self.matches = self.stack[idx] == self.chunk().constants[const_idx]; - self.ip += 3; + let const_idx = self.read2(); + let scrutinee = self.get_scrutinee(); + // let idx = self.stack.len() - self.match_depth as usize - 1; + self.matches = scrutinee == self.chunk().constants[const_idx]; } MatchString => { - let pattern_idx = self.chunk().bytecode[self.ip + 1]; - self.ip += 2; - let scrutinee_idx = self.stack.len() - self.match_depth as usize - 1; - let scrutinee = self.stack[scrutinee_idx].clone(); + let pattern_idx = self.read(); + let scrutinee = self.get_scrutinee(); self.matches = match scrutinee { Value::String(str) => self.chunk().string_patterns[pattern_idx as usize] .re @@ -425,13 +381,12 @@ impl Vm { }; } PushStringMatches => { - let pattern_idx = self.chunk().bytecode[self.ip + 1]; - self.ip += 2; + let pattern_idx = self.read(); let pattern_len = self.chunk().string_patterns[pattern_idx as usize] .words .len(); - let scrutinee_idx = self.stack.len() - self.match_depth as usize - 1; - let scrutinee = self.stack[scrutinee_idx].clone(); + // let scrutinee_idx = self.stack.len() - self.match_depth as usize - 1; + let scrutinee = self.get_scrutinee(); let scrutinee = match scrutinee { Value::String(str) => str.as_ref().clone(), Value::Interned(str) => str.to_string(), @@ -447,35 +402,32 @@ impl Vm { self.match_depth += pattern_len as u8; } MatchTuple => { - let idx = self.stack.len() - self.match_depth as usize - 1; - let tuple_len = self.chunk().bytecode[self.ip + 1]; - let scrutinee = self.stack[idx].clone(); + // let idx = self.stack.len() - self.match_depth as usize - 1; + let tuple_len = self.read() as usize; + let scrutinee = self.get_scrutinee(); match scrutinee { - Value::Tuple(members) => self.matches = members.len() == tuple_len as usize, + Value::Tuple(members) => self.matches = members.len() == tuple_len, _ => self.matches = false, }; - self.ip += 2; } MatchSplattedTuple => { - let idx = self.stack.len() - self.match_depth as usize - 1; - let patt_len = self.chunk().bytecode[self.ip + 1]; - let scrutinee = self.stack[idx].clone(); + let patt_len = self.read() as usize; + let scrutinee = self.get_scrutinee(); match scrutinee { - Value::Tuple(members) => self.matches = members.len() >= patt_len as usize, + Value::Tuple(members) => self.matches = members.len() >= patt_len, _ => self.matches = false, } - self.ip += 2; } PushTuple => { - let tuple_len = self.chunk().bytecode[self.ip + 1]; - let tuple_members = self.stack.split_off(self.stack.len() - tuple_len as usize); + let tuple_len = self.read() as usize; + let tuple_members = self.stack.split_off(self.stack.len() - tuple_len); let tuple = Value::Tuple(Rc::new(tuple_members)); self.push(tuple); - self.ip += 2; } LoadTuple => { - let idx = self.stack.len() - self.match_depth as usize - 1; - let tuple = self.stack[idx].clone(); + // let idx = self.stack.len() - self.match_depth as usize - 1; + // let tuple = self.stack[idx].clone(); + let tuple = self.get_scrutinee(); match tuple { Value::Tuple(members) => { for member in members.iter() { @@ -484,12 +436,10 @@ impl Vm { } _ => return self.panic("internal error: expected tuple"), }; - self.ip += 1; } LoadSplattedTuple => { - let load_len = self.chunk().bytecode[self.ip + 1] as usize; - let idx = self.stack.len() - self.match_depth as usize - 1; - let tuple = self.stack[idx].clone(); + let load_len = self.read() as usize; + let tuple = self.get_scrutinee(); let Value::Tuple(members) = tuple else { return self.panic("internal error: expected tuple"); }; @@ -501,11 +451,9 @@ impl Vm { splatted.push_back(members[i].clone()); } self.push(Value::List(Box::new(splatted))); - self.ip += 2; } PushList => { self.push(Value::List(Box::new(Vector::new()))); - self.ip += 1; } AppendList => { let value = self.pop(); @@ -515,7 +463,6 @@ impl Vm { }; list.push_back(value); self.push(Value::List(list)); - self.ip += 1; } ConcatList => { let splatted = self.pop(); @@ -528,31 +475,28 @@ impl Vm { }; target.append(*splatted); self.push(Value::List(target)); - self.ip += 1; } MatchList => { - let idx = self.stack.len() - self.match_depth as usize - 1; - let list_len = self.chunk().bytecode[self.ip + 1]; - let scrutinee = self.stack[idx].clone(); + // let idx = self.stack.len() - self.match_depth as usize - 1; + let list_len = self.read() as usize; + let scrutinee = self.get_scrutinee(); match scrutinee { - Value::List(members) => self.matches = members.len() == list_len as usize, + Value::List(members) => self.matches = members.len() == list_len, _ => self.matches = false, }; - self.ip += 2; } MatchSplattedList => { - let idx = self.stack.len() - self.match_depth as usize - 1; - let patt_len = self.chunk().bytecode[self.ip + 1]; - let scrutinee = self.stack[idx].clone(); + // let idx = self.stack.len() - self.match_depth as usize - 1; + let patt_len = self.read() as usize; + let scrutinee = self.get_scrutinee(); match scrutinee { - Value::List(members) => self.matches = members.len() >= patt_len as usize, + Value::List(members) => self.matches = members.len() >= patt_len, _ => self.matches = false, } - self.ip += 2; } LoadList => { - let idx = self.stack.len() - self.match_depth as usize - 1; - let list = self.stack[idx].clone(); + // let idx = self.stack.len() - self.match_depth as usize - 1; + let list = self.get_scrutinee(); match list { Value::List(members) => { for member in members.iter() { @@ -561,12 +505,10 @@ impl Vm { } _ => return self.panic("internal error: expected list"), }; - self.ip += 1; } LoadSplattedList => { - let loaded_len = self.chunk().bytecode[self.ip + 1] as usize; - let idx = self.stack.len() - self.match_depth as usize - 1; - let list = self.stack[idx].clone(); + let loaded_len = self.read() as usize; + let list = self.get_scrutinee(); let Value::List(members) = list else { return self.panic("internal error: expected list"); }; @@ -575,11 +517,9 @@ impl Vm { } let splatted = Value::List(Box::new(members.skip(loaded_len - 1))); self.push(splatted); - self.ip += 2; } PushDict => { self.push(Value::Dict(Box::new(HashMap::new()))); - self.ip += 1; } AppendDict => { let value = self.pop(); @@ -591,7 +531,6 @@ impl Vm { }; dict.insert(key, value); self.push(Value::Dict(dict)); - self.ip += 1; } ConcatDict => { let Value::Dict(splatted) = self.pop() else { @@ -602,11 +541,10 @@ impl Vm { }; let union = splatted.union(*target); self.push(Value::Dict(Box::new(union))); - self.ip += 1; } LoadDictValue => { - let dict_idx = self.chunk().bytecode[self.ip + 1] as usize; - let dict = match self.stack[dict_idx].clone() { + let dict_idx = self.read(); + let dict = match self.get_value_at(dict_idx) { Value::Dict(dict) => dict, value => { println!( @@ -621,27 +559,24 @@ impl Vm { }; let value = dict.get(&key).unwrap_or(&Value::Nil); self.push(value.clone()); - self.ip += 2; } MatchDict => { - let idx = self.stack.len() - self.match_depth as usize - 1; - let dict_len = self.chunk().bytecode[self.ip + 1]; - let scrutinee = self.stack[idx].clone(); + // let idx = self.stack.len() - self.match_depth as usize - 1; + let dict_len = self.read(); + let scrutinee = self.get_scrutinee(); match scrutinee { Value::Dict(members) => self.matches = members.len() == dict_len as usize, _ => self.matches = false, }; - self.ip += 2; } MatchSplattedDict => { - let idx = self.stack.len() - self.match_depth as usize - 1; - let patt_len = self.chunk().bytecode[self.ip + 1]; - let scrutinee = self.stack[idx].clone(); + // let idx = self.stack.len() - self.match_depth as usize - 1; + let patt_len = self.read() as usize; + let scrutinee = self.get_scrutinee(); match scrutinee { - Value::Dict(members) => self.matches = members.len() >= patt_len as usize, + Value::Dict(members) => self.matches = members.len() >= patt_len, _ => self.matches = false, } - self.ip += 2; } DropDictEntry => { let Value::Keyword(key_to_drop) = self.pop() else { @@ -652,12 +587,10 @@ impl Vm { }; dict.remove(key_to_drop); self.push(Value::Dict(dict)); - self.ip += 1; } PushBox => { let val = self.pop(); self.push(Value::Box(Rc::new(RefCell::new(val)))); - self.ip += 1; } GetKey => { let key = self.pop(); @@ -670,40 +603,29 @@ impl Vm { _ => Value::Nil, }; self.push(value); - self.ip += 1; } JumpIfNoMatch => { - let high = self.chunk().bytecode[self.ip + 1]; - let low = self.chunk().bytecode[self.ip + 2]; - let jump_len = combine_bytes(high, low); + let jump_len = self.read2(); // let jump_len = self.chunk().bytecode[self.ip + 1] as usize; if !self.matches { - self.ip += jump_len + 3; - } else { - self.ip += 3; + self.ip += jump_len } } JumpIfMatch => { - let high = self.chunk().bytecode[self.ip + 1]; - let low = self.chunk().bytecode[self.ip + 2]; - let jump_len = combine_bytes(high, low); + let jump_len = self.read2(); if self.matches { - self.ip += jump_len + 3; - } else { - self.ip += 3; + self.ip += jump_len; } } TypeOf => { let val = self.pop(); let type_of = Value::Keyword(val.type_of()); self.push(type_of); - self.ip += 1; } - Truncate => { + ToInt => { let val = self.pop(); if let Value::Number(x) = val { self.push(Value::Number(x as usize as f64)); - self.ip += 1; } else { return self.panic("repeat requires a number"); } @@ -712,18 +634,15 @@ impl Vm { let val = self.pop(); if let Value::Number(x) = val { self.push(Value::Number(x - 1.0)); - self.ip += 1; } else { return self.panic("you may only decrement a number"); } } Duplicate => { self.push(self.peek().clone()); - self.ip += 1; } MatchDepth => { - self.match_depth = self.chunk().bytecode[self.ip + 1]; - self.ip += 2; + self.match_depth = self.read(); } PanicNoWhen | PanicNoMatch => { return self.panic("no match"); @@ -736,7 +655,6 @@ impl Vm { } else { self.push(Value::False) } - self.ip += 1; } Add => { let first = self.pop(); @@ -746,7 +664,6 @@ impl Vm { } else { return self.panic("`add` requires two numbers"); } - self.ip += 1; } Sub => { let first = self.pop(); @@ -756,7 +673,6 @@ impl Vm { } else { return self.panic("`sub` requires two numbers"); } - self.ip += 1; } Mult => { let first = self.pop(); @@ -766,7 +682,6 @@ impl Vm { } else { return self.panic("`mult` requires two numbers"); } - self.ip += 1; } Div => { let first = self.pop(); @@ -779,7 +694,6 @@ impl Vm { } else { return self.panic("`div` requires two numbers"); } - self.ip += 1; } Unbox => { let the_box = self.pop(); @@ -789,7 +703,6 @@ impl Vm { return self.panic("`unbox` requires a box"); }; self.push(inner); - self.ip += 1; } BoxStore => { let new_value = self.pop(); @@ -800,14 +713,12 @@ impl Vm { return self.panic("`store` requires a box"); } self.push(new_value); - self.ip += 1; } Assert => { let value = self.stack.last().unwrap(); if let Value::Nil | Value::False = value { return self.panic("asserted falsy value"); } - self.ip += 1; } Get => { let key = self.pop(); @@ -820,7 +731,6 @@ impl Vm { _ => return self.panic("keys must be keywords"), }; self.push(value); - self.ip += 1; } At => { let idx = self.pop(); @@ -836,7 +746,6 @@ impl Vm { _ => return self.panic("indexes must be numbers"), }; self.push(value); - self.ip += 1; } Not => { let value = self.pop(); @@ -845,7 +754,6 @@ impl Vm { _ => Value::False, }; self.push(negated); - self.ip += 1; } Panic => { let msg = self.pop().show(); @@ -853,7 +761,6 @@ impl Vm { } EmptyString => { self.push(Value::String(Rc::new("".to_string()))); - self.ip += 1; } //TODO: don't use the schlemiel's algo here ConcatStrings => { @@ -868,18 +775,15 @@ impl Vm { _ => unreachable!(), }; self.push(combined); - self.ip += 1; } Stringify => { let to_stringify = self.pop(); let the_string = to_stringify.stringify(); let stringified = Value::String(Rc::new(the_string)); self.push(stringified); - self.ip += 1; } Partial => { - let arity = self.chunk().bytecode[self.ip + 1]; - self.ip += 2; + let arity = self.read(); let the_fn = self.pop(); let Value::Fn(ref inner) = the_fn else { return self.panic("only functions may be partially applied"); @@ -893,24 +797,23 @@ impl Vm { self.push(Value::Partial(Rc::new(partial))); } TailCall => { - let arity = self.chunk().bytecode[self.ip + 1]; - self.ip += 2; + let arity = self.read(); - let val = self.pop(); + let called = self.pop(); if self.debug { println!( - "=== tail call into {val}/{arity} from {} ===", + "=== tail call into {called}/{arity} from {} ===", self.frame.function.as_fn().name() ); } - match val { + match called { Value::Fn(_) => { - if !val.as_fn().accepts(arity) { + if !called.as_fn().accepts(arity) { return self.panic_with(format!( "wrong number of arguments to {} passing {arity} args", - val.show() + called.show() )); } // first put the arguments in the register @@ -930,7 +833,7 @@ impl Vm { i += 1; } - let splat_arity = val.as_fn().splat_arity(); + let splat_arity = called.as_fn().splat_arity(); if splat_arity > 0 && arity >= splat_arity { let splatted_args = self.stack.split_off( self.stack.len() - (arity - splat_arity) as usize - 1, @@ -945,7 +848,7 @@ impl Vm { }; let mut frame = CallFrame { - function: val, + function: called, arity, stack_base: self.stack.len() - arity as usize, ip: 0, @@ -1010,28 +913,29 @@ impl Vm { self.call_stack.push(frame); self.ip = 0; } - _ => return self.panic_with(format!("{} is not a function", val.show())), + _ => { + return self.panic_with(format!("{} is not a function", called.show())) + } } } Call => { - let arity = self.chunk().bytecode[self.ip + 1]; - self.ip += 2; + let arity = self.read(); - let val = self.pop(); + let called = self.pop(); if self.debug { - println!("=== calling into {val}/{arity} ==="); + println!("=== calling into {called}/{arity} ==="); } - match val { + match called { Value::Fn(_) => { - if !val.as_fn().accepts(arity) { + if !called.as_fn().accepts(arity) { return self.panic_with(format!( "wrong number of arguments to {} passing {arity} args", - val.show() + called.show() )); } - let splat_arity = val.as_fn().splat_arity(); + let splat_arity = called.as_fn().splat_arity(); if splat_arity > 0 && arity >= splat_arity { let splatted_args = self.stack.split_off( self.stack.len() - (arity - splat_arity) as usize - 1, @@ -1045,7 +949,7 @@ impl Vm { arity }; let mut frame = CallFrame { - function: val, + function: called, arity, stack_base: self.stack.len() - arity as usize, ip: 0, @@ -1100,7 +1004,9 @@ impl Vm { self.call_stack.push(frame); self.ip = 0; } - _ => return self.panic_with(format!("{} is not a function", val.show())), + _ => { + return self.panic_with(format!("{} is not a function", called.show())) + } } } Return => { @@ -1116,7 +1022,6 @@ impl Vm { Print => { println!("{}", self.pop().show()); self.push(Value::Keyword("ok")); - self.ip += 1; } SetUpvalue => { let value = self.pop(); @@ -1124,20 +1029,15 @@ impl Vm { panic!("expected function closing over value, got {}", self.peek()); }; lfn.close(value); - self.ip += 1; } GetUpvalue => { - let idx = self.chunk().bytecode[self.ip + 1]; - self.ip += 2; + let idx = self.read(); if let Value::Fn(ref inner) = self.frame.function { self.push(inner.as_ref().upvalue(idx)); } else { unreachable!(); } } - Msg => { - self.ip += 2; - } } } }