diff --git a/assets/test_prelude.ld b/assets/test_prelude.ld index e98d011..b9e61e3 100644 --- a/assets/test_prelude.ld +++ b/assets/test_prelude.ld @@ -834,7 +834,7 @@ fn slice { fn butlast { "Returns a list, omitting the last element." - (xs as :list) -> slice (xs, 0, dec (count (xs))) + (xs as :list) -> base :slice (xs, 0, dec (count (xs))) } &&& keywords: funny names diff --git a/may_2025_thoughts.md b/may_2025_thoughts.md index ca7bc86..44cf389 100644 --- a/may_2025_thoughts.md +++ b/may_2025_thoughts.md @@ -496,9 +496,9 @@ Here's a list of things that need doing: - [ ] test this - I did not fix it. * [x] Dict patterns are giving me stack discipline grief. Why is stack discipline so hard? -* [ ] This is in the service of getting turtle graphics working +* [x] This is in the service of getting turtle graphics working * Other forms in the language need help: - * [ ] repeat needs its stack discipline updated, it currently crashes the compiler + * [x] repeat needs its stack discipline updated, it currently crashes the compiler ### More closure problems #### 2025-06-23 @@ -522,4 +522,110 @@ 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. -This is the thing I am about to do +This is the thing I am about to do. + +### I think the interpreter, uh, works? +#### 2025-06-24 +I'm sure I'll find some small problems. +But right now the thing works. +At the moment, I'm thinking about how to get good error messages. +Panics are difficult. +And I'm worried about ariadne as the error reporting crate. +Since it writes to stdout, it has all kinds of escape codes. +I need a plain ass string, at least for the web frontend. +So. + +Current task, however, is how to get reasonable panic error messages. +Let's simplify the problem. + +First, let's get tracebacks and line numbers. +We use Chumsky spanned ASTs. +The span is just a range into an str. +What I can do is pretty stupidly just generate line numbers from the spans in the compiler, and from there, get a reasonable traceback. +So instead of using Ariadne's fancy report builder, let's just do something more what we already have here: + +``` +Ludus panicked! no match + on line 1 in input, + calling: add + with arguments: ("foo") + expected match with one of: + () + (x as :number) + (x as :number, y as :number) + (x, y, ...zs) + ((x1, y1), (x2, y2)) + >>> add ("foo") +......^ +``` +We need: +* the location of the function call in terms of the line number +* the arguments used +* the patterns expected to match (special cases: `let` vs `match` vs `fn`) + +That means, for bookkeeping, we need: +* In the compiler, line number +* In the VM, the arguments +* In the VM, the pattern AST node. + +In Janet-Ludus, there are only a few types of panic: +* `fn-no-match`: no match against a function call +* `let-no-match`: no match against a `let` binding +* `match-no-match`: no match against a `match` form +* `generic-panic`: everything else +* `runtime-error`: an internal Ludus error + +The first three are simply formatting differences. +There are no tracebacks. + +Tracebacks should be easy enough, although there's some fiddly bits. +While it's nice to have the carret, the brutalist attempt here should be just to give us the line--since the carret isn't exactly precise in the Janet interpereter. +And the traceback should look something like: + +``` + calling foo with (:bar, :baz) + at line 12 in input + calling bar with () + at line 34 in prelude + calling baz with (1, 2, 3) + at line 12 in input +``` + +Which means, again: function names, ip->line conversion, and arguments. + +The runtime needs a representation of the patterns in _any_ matching form. +The speed is so much greater now that I'm not so concerned about little optimizations. +So: a chunk needs a vec of patterns-representations. (I'm thinking simply a `Vec`.) +So does a function, for `doc!`. +Same same re: `Vec`. +A VM needs a register for the scrutinee (which with function calls is just the arguments, already captured). +A VM also needs a register for the pattern. +So when there's a no match, we just yank the pattern and the scrutinee out of these registers. + +This all seems very straightforward compared to the compiling & VM stuff. + +Here's some stub code I wrote for dealing with ranges, source, line numbers: + +```rust +let str = "foo bar baz\nquux frob\nthing thing thing"; +let range = 0..4; + +println!("{}", str.get(range).unwrap()); + +let lines: Vec<&str> = str.split_terminator("\n").collect(); + +println!("{:?}", lines); + +let idx = 20; + +let mut line_no = 1; +for i in 0..idx { + if str.chars().nth(i).unwrap() == '\n' { + line_no += 1; + } +} + +println!("line {line_no}: {}", lines[line_no - 1]); +``` + + diff --git a/sandbox.ld b/sandbox.ld index ec9a4dc..bccbfdd 100644 --- a/sandbox.ld +++ b/sandbox.ld @@ -1,19 +1,5 @@ -fn circle! () -> repeat 20 { - fd! (2) - rt! (inv (20)) +let foo = :foo + +repeat foo { + print! ("hi") } - -fn flower! () -> repeat 10 { - circle! () - rt! (inv (10)) -} - -fn garland! () -> repeat 10 { - flower! () - fd! (30) -} - -garland! () - -do turtle_commands > unbox > print! -do turtle_state > unbox > print! diff --git a/sandbox_run.txt b/sandbox_run.txt index 8d99c9c..53d16c6 100644 --- a/sandbox_run.txt +++ b/sandbox_run.txt @@ -1,32 +1,15 @@ -{ - - 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... +closing over in type at 1: #{:tan fn tan/base, ... +closing over in eq? at 1: #{:tan fn tan/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: #{:tan fn tan/base, ... +closing over in rest at 1: #{:tan fn tan/base, ... +closing over in inc at 1: #{:tan fn tan/base, ... +closing over in dec at 1: #{:tan fn tan/base, ... +closing over in count at 1: #{:tan fn tan/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: #{:tan fn tan/base, ... +closing over in append at 1: #{:tan fn tan/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,18 +24,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: #{:list fn list/base... +closing over in concat at 1: #{:tan fn tan/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 print! at 1: #{:tan fn tan/base, ... +closing over in show at 1: #{:tan fn tan/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 1: #{:tan fn tan/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 @@ -60,12 +43,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: #{: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: #{:tan fn tan/base, ... +closing over in trim at 1: #{:tan fn tan/base, ... +closing over in upcase at 1: #{:tan fn tan/base, ... +closing over in downcase at 1: #{:tan fn tan/base, ... +closing over in chars at 1: #{:tan fn tan/base, ... +closing over in chars/safe at 1: #{:tan fn tan/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 +56,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: #{: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 to_number at 1: #{:tan fn tan/base, ... +closing over in unbox at 1: #{:tan fn tan/base, ... +closing over in store! at 1: #{:tan fn tan/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 add at 1: #{:tan fn tan/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: #{:tan fn tan/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: #{:tan fn tan/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: #{:tan fn tan/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: #{:tan fn tan/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 +86,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: #{:tan fn tan/base, ... +closing over in gte? at 1: #{:tan fn tan/base, ... +closing over in lt? at 1: #{:tan fn tan/base, ... +closing over in lte? at 1: #{:tan fn tan/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 +109,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: #{:tan fn tan/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: #{:tan fn tan/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: #{:tan fn tan/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 +123,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: #{:tan fn tan/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: #{:tan fn tan/base, ... +closing over in mod/0 at 1: #{:tan fn tan/base, ... +closing over in mod/safe at 1: #{:tan fn tan/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 +139,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: #{:tan fn tan/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: #{:tan fn tan/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 +154,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: #{:tan fn tan/base, ... +closing over in ceil at 1: #{:tan fn tan/base, ... +closing over in round at 1: #{:tan fn tan/base, ... +closing over in range at 1: #{:tan fn tan/base, ... +closing over in at at 1: #{:tan fn tan/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 +170,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: #{:tan fn tan/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: #{:tan fn tan/base, ... +closing over in dissoc at 1: #{:tan fn tan/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: #{:tan fn tan/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 +194,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: #{:tan fn tan/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 +209,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 { #{:pencolor :w... 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 +236,474 @@ 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 { #{:pencolor :w... closing over in position at 2: fn unbox -closing over in heading at 1: box { #{:position (0... +closing over in heading at 1: box { #{:pencolor :w... closing over in heading at 2: fn unbox -closing over in pendown? at 1: box { #{:position (0... +closing over in pendown? at 1: box { #{:pencolor :w... closing over in pendown? at 2: fn unbox -closing over in pencolor at 1: box { #{:position (0... +closing over in pencolor at 1: box { #{:pencolor :w... closing over in pencolor at 2: fn unbox -closing over in penwidth at 1: box { #{:position (0... +closing over in penwidth at 1: box { #{:pencolor :w... closing over in penwidth at 2: fn unbox -binding `foos` in sandbox +binding `hellos!` in sandbox.ld 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 +new locals: hellos!@0//0 +***function clause matching: : (times) +binding `times` in hellos! +stack depth: 1; match depth: 0 +at stack index: 0 +new locals: times@0//0 +resolving binding `times` in hellos! +locals: times@0//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: +***calling function print! stack depth: 2 +resolving binding `print!` in hellos! +locals: times@0//0 as enclosing upvalue 0 -***after 0 args stack depth: 2 +***after 1 args stack depth: 4 leaving scope 1 -***leaving block before pop stack depth: 1 -popping back from 1 to 1 -=== function chuncktion: foos!/0 === +***leaving block before pop stack depth: 2 +popping back from 2 to 2 +=== function chuncktion: hellos!/1 === 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 +0001: ***function clause matching: : (times) +0003: match_depth 000 +0005: match +0006: binding `times` in hellos! +0008: stack depth: 1; match depth: 0 +0010: at stack index: 0 +0012: new locals: times@0//0 +0014: jump_if_no_match 00003 +0017: jump 00000 +0020: jump_if_no_match 00049 +0023: resolving binding `times` in hellos! +locals: times@0//0 +0025: at locals position 0 +0027: push_binding 000 +0029: truncate +0030: jump 00001 +0033: decrement +0034: duplicate +0035: jump_if_zero 00027 +0038: ***calling function print! stack depth: 2 +0040: constant 00000: "hi!" +0043: resolving binding `print!` in hellos! +locals: times@0//0 +0045: as enclosing upvalue 0 +0047: get_upvalue 000 +0049: ***after 1 args stack depth: 4 +0051: call 001 +0053: store +0054: leaving scope 1 +0056: ***leaving block before pop stack depth: 2 +0058: popping back from 2 to 2 +0060: load +0061: pop +0062: jump_back 00029 +0065: pop +0066: constant 00001: nil +0069: store +0070: pop +0071: return +0072: panic_no_match +resolving binding `print!` in sandbox.ld +locals: hellos!@0//0 as global -***after 1 args stack depth: 5 +***calling function hellos! stack depth: 1 +resolving binding `hellos!` in sandbox.ld +locals: hellos!@0//0 +at locals position 0 +***after 1 args stack depth: 3 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 +releasing binding hellos!@0//0 +***leaving block before pop stack depth: 1 +popping back from 1 to 0 === source code === -box foos = [] - -fn foo! () -> update! (foos, append (_, :foo)) - -fn foos! () -> repeat 4 { - foo! () +fn hellos! (times) -> repeat times { + print! ("hi!") } -foos! () -unbox (foos) +hellos! (:foo) -=== chunk: sandbox === +=== chunk: sandbox.ld === 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: fn hellos! +0003: binding `hellos!` in sandbox.ld +0005: stack depth: 1; match depth: 0 +0007: at stack index: 0 +0009: new locals: hellos!@0//0 +0011: resolving binding `print!` in sandbox.ld +locals: hellos!@0//0 +0013: as global +0015: constant 00001: :print! +0018: push_global +0019: set_upvalue +0020: ***calling function hellos! stack depth: 1 +0022: constant 00002: :foo +0025: resolving binding `hellos!` in sandbox.ld +locals: hellos!@0//0 +0027: at locals position 0 +0029: push_binding 000 +0031: ***after 1 args stack depth: 3 +0033: call 001 +0035: store +0036: leaving scope 0 +0038: releasing binding hellos!@0//0 +0040: ***leaving block before pop stack depth: 1 +0042: popping back from 1 to 0 +0044: pop +0045: 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,_,_,_,_,_,_,_) +0000: constant 00000: fn hellos! +0003: [->fn hellos!<-] (_,_,_,_,_,_,_,_) +0003: binding `hellos!` in sandbox.ld +0005: [->fn hellos!<-] (_,_,_,_,_,_,_,_) +0005: stack depth: 1; match depth: 0 +0007: [->fn hellos!<-] (_,_,_,_,_,_,_,_) +0007: at stack index: 0 +0009: [->fn hellos!<-] (_,_,_,_,_,_,_,_) +0009: new locals: hellos!@0//0 +0011: [->fn hellos!<-] (_,_,_,_,_,_,_,_) +0011: resolving binding `print!` in sandbox.ld +locals: hellos!@0//0 +0013: [->fn hellos!<-] (_,_,_,_,_,_,_,_) +0013: as global +0015: [->fn hellos!<-] (_,_,_,_,_,_,_,_) +0015: constant 00001: :print! +0018: [->fn hellos!<-|:print!] (_,_,_,_,_,_,_,_) +0018: push_global +0019: [->fn hellos!<-|fn print!] (_,_,_,_,_,_,_,_) +0019: set_upvalue +closing over in hellos! at 1: fn print! +0020: [->fn hellos!<-] (_,_,_,_,_,_,_,_) +0020: ***calling function hellos! stack depth: 1 +0022: [->fn hellos!<-] (_,_,_,_,_,_,_,_) +0022: constant 00002: :foo +0025: [->fn hellos!<-|:foo] (_,_,_,_,_,_,_,_) +0025: resolving binding `hellos!` in sandbox.ld +locals: hellos!@0//0 +0027: [->fn hellos!<-|:foo] (_,_,_,_,_,_,_,_) +0027: at locals position 0 +0029: [->fn hellos!<-|:foo] (_,_,_,_,_,_,_,_) 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]] (_,_,_,_,_,_,_,_) +0031: [->fn hellos!<-|:foo|fn hellos!] (_,_,_,_,_,_,_,_) +0031: ***after 1 args stack depth: 3 +0033: [->fn hellos!<-|:foo|fn hellos!] (_,_,_,_,_,_,_,_) +0033: call 001 +=== calling into fn hellos!/1 === +0000: [fn hellos!|->: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] +0001: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0001: ***function clause matching: : (times) +0003: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0003: match_depth 000 +0005: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0005: match +0006: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0006: binding `times` in hellos! +0008: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0008: stack depth: 1; match depth: 0 +0010: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0010: at stack index: 0 +0012: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0012: new locals: times@0//0 +0014: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0014: jump_if_no_match 00003 +0017: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0017: jump 00000 +0020: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0020: jump_if_no_match 00049 +0023: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0023: resolving binding `times` in hellos! +locals: times@0//0 +0025: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0025: at locals position 0 +0027: [fn hellos!|->:foo<-] (_,_,_,_,_,_,_,_) +0027: push_binding 000 +0029: [fn hellos!|->:foo<-|:foo] (_,_,_,_,_,_,_,_) +0029: truncate +got frame: CallFrame: hellos!/1 @ 30 +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +12..17 || times +12..17 || times +12..17 || times +12..17 || times +12..17 || times +12..17 || times +12..17 || times +12..17 || times +12..17 || times +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +29..34 || times +29..34 || times +29..34 || times +29..34 || times +29..34 || times +29..34 || times +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +38..52 || print! ("hi!") +38..52 || print! ("hi!") +46..51 || "hi!" +38..52 || print! ("hi!") +38..52 || print! ("hi!") +38..52 || print! ("hi!") +38..52 || print! ("hi!") +38..52 || print! ("hi!") +38..52 || print! ("hi!") +38..52 || print! ("hi!") +38..52 || print! ("hi!") +38..52 || print! ("hi!") +38..52 || print! ("hi!") +35..54 || { + + print! ("hi!") +} +35..54 || { + + print! ("hi!") +} +35..54 || { + + print! ("hi!") +} +35..54 || { + + print! ("hi!") +} +35..54 || { + + print! ("hi!") +} +35..54 || { + + print! ("hi!") +} +35..54 || { + + print! ("hi!") +} +35..54 || { + + print! ("hi!") +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +22..54 || repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +11..54 || (times) -> repeat times { +{ + + print! ("hi!") +} +} +at instr: 30 +repeat times { +{ + + print! ("hi!") +} +} +22..54 +CallFrame: user script/0 @ 35 +Error: Ludus panicked! `repeat` expects a number; it got `:foo` + ╭─[ sandbox.ld:1:23 ] + │ + 1 │ ╭─▶ fn hellos! (times) -> repeat times { +  ┆ ┆ + 6 │ ├─▶ hellos! (:foo) +  │ │ +  │ ╰──────────────────── (1) calling `user script` with () +───╯ diff --git a/src/chunk.rs b/src/chunk.rs index 6551278..5a4ddaf 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -1,5 +1,8 @@ use crate::op::Op; +use crate::parser::Ast; +use crate::spans::Spanned; use crate::value::Value; +use chumsky::prelude::SimpleSpan; use imbl::HashMap; use num_traits::FromPrimitive; use regex::Regex; @@ -14,13 +17,32 @@ pub struct StrPattern { pub struct Chunk { pub constants: Vec, pub bytecode: Vec, + pub nodes: Vec<&'static Ast>, + pub spans: Vec, pub keywords: Vec<&'static str>, pub string_patterns: Vec, pub env: HashMap<&'static str, Value>, pub msgs: Vec, + pub src: &'static str, + pub input: &'static str, } impl Chunk { + pub fn new(env: HashMap<&'static str, Value>, src: &'static str, input: &'static str) -> Chunk { + Chunk { + constants: vec![], + bytecode: vec![], + nodes: vec![], + spans: vec![], + keywords: vec![], + string_patterns: vec![], + env, + msgs: vec![], + src, + input, + } + } + pub fn dissasemble_instr(&self, i: &mut usize) { let op = Op::from_u8(self.bytecode[*i]).unwrap(); use Op::*; diff --git a/src/compiler.rs b/src/compiler.rs index 7a03288..a9424b3 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -62,18 +62,17 @@ fn get_builtin(name: &str, arity: usize) -> Option { None } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Compiler { pub chunk: Chunk, pub bindings: Vec, pub scope_depth: isize, pub match_depth: usize, pub stack_depth: usize, - pub spans: Vec, - pub nodes: Vec<&'static Ast>, pub ast: &'static Ast, pub span: SimpleSpan, pub src: &'static str, + pub input: &'static str, pub name: &'static str, pub depth: usize, pub upvalues: Vec<&'static str>, @@ -101,18 +100,13 @@ impl Compiler { ast: &'static Spanned, name: &'static str, src: &'static str, + input: &'static str, depth: usize, env: imbl::HashMap<&'static str, Value>, debug: bool, ) -> Compiler { - let chunk = Chunk { - constants: vec![], - bytecode: vec![], - keywords: vec![], - string_patterns: vec![], - env, - msgs: vec![], - }; + let chunk = Chunk::new(env, src, input); + Compiler { chunk, bindings: vec![], @@ -120,13 +114,12 @@ impl Compiler { scope_depth: -1, match_depth: 0, stack_depth: 0, - spans: vec![], - nodes: vec![], ast: &ast.0, span: ast.1, loop_info: vec![], upvalues: vec![], src, + input, name, tail_pos: false, debug, @@ -222,12 +215,14 @@ impl Compiler { fn emit_op(&mut self, op: Op) { self.chunk.bytecode.push(op as u8); - self.spans.push(self.span); + self.chunk.spans.push(self.span); + self.chunk.nodes.push(self.ast); } fn emit_byte(&mut self, byte: usize) { self.chunk.bytecode.push(byte as u8); - self.spans.push(self.span); + self.chunk.spans.push(self.span); + self.chunk.nodes.push(self.ast); } fn len(&self) -> usize { @@ -1096,6 +1091,7 @@ impl Compiler { clause, name, self.src, + self.input, self.depth + 1, self.chunk.env.clone(), self.debug, diff --git a/src/errors.rs b/src/errors.rs index a3e374c..c04353e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,9 +1,90 @@ // use crate::process::{LErr, Trace}; +use crate::parser::Ast; +use crate::spans::Spanned; use crate::validator::VErr; use crate::value::Value; +use crate::vm::CallFrame; use ariadne::{sources, Color, Label, Report, ReportKind}; use std::collections::HashSet; +#[derive(Debug, Clone, PartialEq)] +pub enum PanicInfo { + RepeatNumberType(Value), + NoMatch, + LetMatchFail, + MatchFail, + FnMatchFail, + Internal(&'static str), + ListSplat(Value), + DictSplat(Value), + DecType(Value), + WhenFail, + AddType(Value, Value), + SubType(Value, Value), + MultType(Value, Value), + DivType(Value, Value), + UnboxType(Value), + StoreType(Value), + Assert, + Explicit(Value), + GetType(Value), + AtIdxType(Value), + AtCollectionType(Value), + PartialType(Value), + ArityMismatch(Value, Vec), + CallType(Value), + DivisionByZero, +} + +impl PanicInfo { + pub fn msg(&self) -> String { + use PanicInfo::*; + match self { + RepeatNumberType(value) => format!("`repeat` expects a number; it got `{value}`"), + _ => todo!(), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Panic { + pub frame: CallFrame, + pub call_stack: Vec, + pub info: PanicInfo, +} + +impl Panic { + pub fn report(&self) { + println!("got frame: {}", self.frame); + let mut srcs = HashSet::new(); + let ast_spans = std::iter::zip( + self.frame.chunk().spans.iter(), + self.frame.chunk().nodes.iter(), + ); + let ast_spans = ast_spans + .map(|(span, ast)| format!("{span} || {}", ast.show())) + .collect::>() + .join("\n"); + println!("{ast_spans}"); + let msg = self.info.msg(); + let input = self.frame.chunk().input; + let src = self.frame.chunk().src; + srcs.insert((input, src)); + let mut traceback = format!("Ludus panicked! {msg}"); + for frame in self.call_stack.iter().rev() { + let name = frame.function.as_fn().name(); + let args = frame + .args + .iter() + .map(|arg| arg.show()) + .collect::>() + .join(", "); + traceback = format!("{traceback}\n calling `{name}` with ({args})"); + } + println!("{traceback}"); + } +} + // pub fn report_panic(err: LErr) { // let mut srcs = HashSet::new(); // let mut stack = vec![]; diff --git a/src/main.rs b/src/main.rs index 3b26244..e08513f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,8 @@ use imbl::HashMap; use std::env; use std::fs; -const DEBUG_SCRIPT_COMPILE: bool = false; -const DEBUG_SCRIPT_RUN: 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; @@ -73,6 +73,7 @@ pub fn prelude() -> HashMap<&'static str, Value> { parsed, "prelude", PRELUDE, + "prelude", 0, HashMap::new(), DEBUG_PRELUDE_COMPILE, @@ -90,7 +91,7 @@ pub fn prelude() -> HashMap<&'static str, Value> { } } -pub fn run(src: &'static str) { +pub fn run(src: &'static str, input: &'static str) { let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); if !lex_errs.is_empty() { println!("{:?}", lex_errs); @@ -115,7 +116,7 @@ pub fn run(src: &'static str) { let prelude = prelude(); // let prelude = imbl::HashMap::new(); - let mut validator = Validator::new(&parsed.0, &parsed.1, "user input", src, prelude.clone()); + let mut validator = Validator::new(&parsed.0, &parsed.1, input, src, prelude.clone()); validator.validate(); if !validator.errors.is_empty() { @@ -124,7 +125,7 @@ pub fn run(src: &'static str) { return; } - let mut compiler = Compiler::new(parsed, "sandbox", src, 0, prelude, DEBUG_SCRIPT_COMPILE); + let mut compiler = Compiler::new(parsed, input, src, input, 0, prelude, DEBUG_SCRIPT_COMPILE); // let base = base::make_base(); // compiler.emit_constant(base); // compiler.bind("base"); @@ -147,7 +148,10 @@ pub fn run(src: &'static str) { let result = vm.run(); let output = match result { Ok(val) => val.to_string(), - Err(panic) => format!("Ludus panicked! {panic}"), + Err(panic) => { + panic.report(); + std::process::exit(1) + } }; if DEBUG_SCRIPT_RUN { vm.print_stack(); @@ -180,11 +184,28 @@ pub fn ld_fmt(src: &'static str) -> Result { } pub fn main() { - env::set_var("RUST_BACKTRACE", "1"); - let src: &'static str = fs::read_to_string("sandbox.ld").unwrap().leak(); - match ld_fmt(src) { - Ok(src) => println!("{}", src), - Err(msg) => println!("Could not format source with errors:\n{}", msg), + // env::set_var("RUST_BACKTRACE", "1"); + // let src: &'static str = fs::read_to_string("sandbox.ld").unwrap().leak(); + // run(src, "sandbox.ld"); + let str = "foo bar baz\nquux frob\nthing thing thing"; + let range = 0..4; + + println!("{}", range.start); + + println!("{}", str.get(range).unwrap()); + + let lines: Vec<&str> = str.split_terminator("\n").collect(); + + println!("{:?}", lines); + + let idx = 20; + + let mut line_no = 1; + for i in 0..idx { + if str.chars().nth(i).unwrap() == '\n' { + line_no += 1; + } } - run(src); + + println!("line {line_no}: {}", lines[line_no - 1]); } diff --git a/src/parser.rs b/src/parser.rs index f882ff2..476c447 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4,35 +4,6 @@ use chumsky::{input::ValueInput, prelude::*, recursive::Recursive}; use std::fmt; use struct_scalpel::Dissectible; -// #[derive(Clone, Debug, PartialEq)] -// pub struct WhenClause { -// pub cond: Spanned, -// pub body: Spanned, -// } - -// impl fmt::Display for WhenClause { -// fn fmt(self: &WhenClause, f: &mut fmt::Formatter) -> fmt::Result { -// write!(f, "cond: {}, body: {}", self.cond.0, self.body.0) -// } -// } - -// #[derive(Clone, Debug, PartialEq)] -// pub struct MatchClause { -// pub patt: Spanned, -// pub guard: Option>, -// pub body: Spanned, -// } - -// impl fmt::Display for MatchClause { -// fn fmt(self: &MatchClause, f: &mut fmt::Formatter) -> fmt::Result { -// write!( -// f, -// "pattern: {}, guard: {:?} body: {}", -// self.patt.0, self.guard, self.body.0 -// ) -// } -// } - #[derive(Clone, Debug, PartialEq, Eq)] pub enum StringPart { Data(String), @@ -219,14 +190,12 @@ impl Ast { .collect::>() .join("\n ") ), - FnBody(clauses) => format!( - "{}", - clauses - .iter() - .map(|(clause, _)| clause.show()) - .collect::>() - .join("\n ") - ), + FnBody(clauses) => clauses + .iter() + .map(|(clause, _)| clause.show()) + .collect::>() + .join("\n ") + .to_string(), Fn(name, body, doc) => { let mut out = format!("fn {name} {{\n"); if let Some(doc) = doc { @@ -267,7 +236,7 @@ impl Ast { .join(", ") ), MatchClause(pattern, guard, body) => { - let mut out = format!("{}", pattern.0.show()); + let mut out = pattern.0.show().to_string(); if let Some(guard) = guard.as_ref() { out = format!("{out} if {}", guard.0.show()); } @@ -523,75 +492,6 @@ impl fmt::Debug for StringMatcher { } } -// #[derive(Clone, Debug, PartialEq)] -// pub enum Pattern { -// Nil, -// Boolean(bool), -// Number(f64), -// String(&'static str), -// Interpolated(Vec>, StringMatcher), -// Keyword(&'static str), -// Word(&'static str), -// As(&'static str, &'static str), -// Splattern(Box>), -// Placeholder, -// Tuple(Vec>), -// List(Vec>), -// Pair(&'static str, Box>), -// Dict(Vec>), -// } - -// impl fmt::Display for Pattern { -// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -// match self { -// Pattern::Nil => write!(f, "nil"), -// Pattern::Boolean(b) => write!(f, "{}", b), -// Pattern::Number(n) => write!(f, "{}", n), -// Pattern::String(s) => write!(f, "{}", s), -// Pattern::Keyword(k) => write!(f, ":{}", k), -// Pattern::Word(w) => write!(f, "{}", w), -// Pattern::As(w, t) => write!(f, "{} as {}", w, t), -// Pattern::Splattern(p) => write!(f, "...{}", p.0), -// Pattern::Placeholder => write!(f, "_"), -// Pattern::Tuple(t) => write!( -// f, -// "({})", -// t.iter() -// .map(|x| x.0.to_string()) -// .collect::>() -// .join(", ") -// ), -// Pattern::List(l) => write!( -// f, -// "({})", -// l.iter() -// .map(|x| x.0.to_string()) -// .collect::>() -// .join(", ") -// ), -// Pattern::Dict(entries) => write!( -// f, -// "#{{{}}}", -// entries -// .iter() -// .map(|(pair, _)| pair.to_string()) -// .collect::>() -// .join(", ") -// ), -// Pattern::Pair(key, value) => write!(f, ":{} {}", key, value.0), -// Pattern::Interpolated(strprts, _) => write!( -// f, -// "interpolated: \"{}\"", -// strprts -// .iter() -// .map(|part| part.0.to_string()) -// .collect::>() -// .join("") -// ), -// } -// } -// } - fn is_word_char(c: char) -> bool { if c.is_ascii_alphanumeric() { return true; diff --git a/src/vm.rs b/src/vm.rs index e2260fe..f2ac5e3 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,8 +1,7 @@ use crate::base::BaseFn; use crate::chunk::Chunk; +use crate::errors::{Panic, PanicInfo}; use crate::op::Op; -use crate::parser::Ast; -use crate::spans::Spanned; use crate::value::{LFn, Value}; use imbl::{HashMap, Vector}; use num_traits::FromPrimitive; @@ -12,43 +11,11 @@ use std::mem::swap; use std::rc::Rc; #[derive(Debug, Clone, PartialEq)] -// pub struct Panic { -// pub input: &'static str, -// pub src: &'static str, -// pub msg: String, -// pub span: SimpleSpan, -// pub trace: Vec, -// pub extra: String, -// } -pub enum Panic { - Str(&'static str), - String(String), -} - -impl fmt::Display for Panic { - fn fmt(self: &Panic, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Panic::Str(msg) => write!(f, "{msg}"), - Panic::String(msg) => write!(f, "{msg}"), - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Trace { - pub callee: Spanned, - pub caller: Spanned, - pub function: Value, - pub arguments: Value, - pub input: &'static str, - pub src: &'static str, -} - pub struct CallFrame { pub function: Value, - pub arity: u8, pub stack_base: usize, pub ip: usize, + pub args: Vec, } impl CallFrame { @@ -56,7 +23,7 @@ impl CallFrame { let Value::Fn(ref function) = self.function else { unreachable!() }; - function.chunk(self.arity) + function.chunk(self.args.len() as u8) } } @@ -69,7 +36,7 @@ impl fmt::Display for CallFrame { f, "CallFrame: {}/{} @ {}", function.name(), - self.arity, + self.args.len(), self.ip ) } @@ -108,7 +75,7 @@ impl Vm { function: base_fn.clone(), stack_base: 0, ip: 0, - arity: 0, + args: vec![], }; Vm { stack: vec![], @@ -186,15 +153,21 @@ impl Vm { stack } - pub fn panic(&mut self, msg: &'static str) { - let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack()); - self.result = Some(Err(Panic::String(msg))); + pub fn panic(&mut self, info: PanicInfo) { + let mut frame = self.frame.clone(); + frame.ip = self.ip; + let panic = Panic { + info, + frame, + call_stack: self.call_stack.clone(), + }; + self.result = Some(Err(panic)); } - pub fn panic_with(&mut self, msg: String) { - let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack()); - 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()); + // self.result = Some(Err(Panic::String(msg))); + // } fn get_value_at(&mut self, idx: u8) -> Value { let idx = idx as usize; @@ -280,7 +253,7 @@ impl Vm { match cond { Value::Number(x) if x <= 0.0 => self.ip += jump_len, Value::Number(..) => (), - _ => return self.panic("repeat requires a number"), + _ => return self.panic(PanicInfo::RepeatNumberType(cond.clone())), } } Pop => { @@ -358,7 +331,7 @@ impl Vm { } PanicIfNoMatch => { if !self.matches { - return self.panic("no match"); + return self.panic(PanicInfo::NoMatch); } } MatchConstant => { @@ -434,14 +407,14 @@ impl Vm { self.push(member.clone()); } } - _ => return self.panic("internal error: expected tuple"), + _ => return self.panic(PanicInfo::Internal("expected tuple")), }; } LoadSplattedTuple => { 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"); + return self.panic(PanicInfo::Internal("expected tuple")); }; for i in 0..load_len - 1 { self.push(members[i].clone()); @@ -458,8 +431,9 @@ impl Vm { AppendList => { let value = self.pop(); let list = self.pop(); - let Value::List(mut list) = list else { - return self.panic("only lists may be splatted into lists"); + let mut list = match list { + Value::List(mut list) => list, + not_list => return self.panic(PanicInfo::ListSplat(not_list.clone())), }; list.push_back(value); self.push(Value::List(list)); @@ -470,8 +444,9 @@ impl Vm { let Value::List(mut target) = target else { unreachable!() }; - let Value::List(splatted) = splatted else { - return self.panic("only lists may be splatted into lists"); + let splatted = match splatted { + Value::List(list) => list, + not_list => return self.panic(PanicInfo::ListSplat(not_list.clone())), }; target.append(*splatted); self.push(Value::List(target)); @@ -503,14 +478,14 @@ impl Vm { self.push(member.clone()); } } - _ => return self.panic("internal error: expected list"), + _ => return self.panic(PanicInfo::Internal("expected list")), }; } LoadSplattedList => { 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"); + return self.panic(PanicInfo::Internal("expected list")); }; for i in 0..loaded_len - 1 { self.push(members[i].clone()); @@ -533,11 +508,13 @@ impl Vm { self.push(Value::Dict(dict)); } ConcatDict => { - let Value::Dict(splatted) = self.pop() else { - return self.panic("only dicts may be splatted into dicts"); + let splatted = match self.pop() { + Value::Dict(dict) => dict, + not_dict => return self.panic(PanicInfo::DictSplat(not_dict.clone())), }; - let Value::Dict(target) = self.pop() else { - unreachable!() + let target = match self.pop() { + Value::Dict(dict) => dict, + not_dict => return self.panic(PanicInfo::Internal("expected dict")), }; let union = splatted.union(*target); self.push(Value::Dict(Box::new(union))); @@ -547,15 +524,12 @@ impl Vm { let dict = match self.get_value_at(dict_idx) { Value::Dict(dict) => dict, value => { - println!( - "internal Ludus error in function {}", - self.frame.function.as_fn().name() - ); unreachable!("expected dict, got {value}") } }; - let Value::Keyword(key) = self.pop() else { - unreachable!("expected keyword, got something else") + let key = match self.pop() { + Value::Keyword(key) => key, + not_keyword => unreachable!("expected keyword, got {not_keyword}"), }; let value = dict.get(&key).unwrap_or(&Value::Nil); self.push(value.clone()); @@ -622,31 +596,22 @@ impl Vm { let type_of = Value::Keyword(val.type_of()); self.push(type_of); } - ToInt => { - let val = self.pop(); - if let Value::Number(x) = val { - self.push(Value::Number(x as usize as f64)); - } else { - return self.panic("repeat requires a number"); - } - } - Decrement => { - let val = self.pop(); - if let Value::Number(x) = val { - self.push(Value::Number(x - 1.0)); - } else { - return self.panic("you may only decrement a number"); - } - } + ToInt => match self.pop() { + Value::Number(x) => self.push(Value::Number(x as usize as f64)), + not_number => return self.panic(PanicInfo::RepeatNumberType(not_number)), + }, + Decrement => match self.pop() { + Value::Number(x) => self.push(Value::Number(x - 1.0)), + not_number => return self.panic(PanicInfo::DecType(not_number)), + }, Duplicate => { self.push(self.peek().clone()); } MatchDepth => { self.match_depth = self.read(); } - PanicNoWhen | PanicNoMatch => { - return self.panic("no match"); - } + PanicNoWhen => return self.panic(PanicInfo::WhenFail), + PanicNoMatch => return self.panic(PanicInfo::NoMatch), Eq => { let first = self.pop(); let second = self.pop(); @@ -659,65 +624,61 @@ impl Vm { Add => { let first = self.pop(); let second = self.pop(); - if let (Value::Number(x), Value::Number(y)) = (first, second) { - self.push(Value::Number(x + y)) - } else { - return self.panic("`add` requires two numbers"); + match (first, second) { + (Value::Number(x), Value::Number(y)) => self.push(Value::Number(x + y)), + (x, y) => return self.panic(PanicInfo::AddType(y, x)), } } Sub => { let first = self.pop(); let second = self.pop(); - if let (Value::Number(x), Value::Number(y)) = (first, second) { - self.push(Value::Number(y - x)) - } else { - return self.panic("`sub` requires two numbers"); + match (first, second) { + (Value::Number(x), Value::Number(y)) => self.push(Value::Number(y - x)), + (x, y) => return self.panic(PanicInfo::SubType(y, x)), } } Mult => { let first = self.pop(); let second = self.pop(); - if let (Value::Number(x), Value::Number(y)) = (first, second) { - self.push(Value::Number(x * y)) - } else { - return self.panic("`mult` requires two numbers"); + match (first, second) { + (Value::Number(x), Value::Number(y)) => self.push(Value::Number(x * y)), + (x, y) => return self.panic(PanicInfo::MultType(y, x)), } } Div => { let first = self.pop(); let second = self.pop(); - if let (Value::Number(x), Value::Number(y)) = (first, second) { - if x == 0.0 { - return self.panic("division by 0"); + match (first, second) { + (Value::Number(0.0), Value::Number(_)) => { + return self.panic(PanicInfo::DivisionByZero) } - self.push(Value::Number(y / x)) - } else { - return self.panic("`div` requires two numbers"); + (Value::Number(x), Value::Number(y)) => self.push(Value::Number(y / x)), + (x, y) => return self.panic(PanicInfo::DivType(y, x)), } } Unbox => { let the_box = self.pop(); - let inner = if let Value::Box(b) = the_box { - b.borrow().clone() - } else { - return self.panic("`unbox` requires a box"); + let inner = match the_box { + Value::Box(b) => b.borrow().clone(), + not_a_box => return self.panic(PanicInfo::UnboxType(not_a_box)), }; self.push(inner); } BoxStore => { let new_value = self.pop(); let the_box = self.pop(); - if let Value::Box(b) = the_box { - b.replace(new_value.clone()); - } else { - return self.panic("`store` requires a box"); + match the_box { + Value::Box(b) => { + b.replace(new_value.clone()); + } + not_a_box => return self.panic(PanicInfo::StoreType(not_a_box)), } self.push(new_value); } Assert => { let value = self.stack.last().unwrap(); if let Value::Nil | Value::False = value { - return self.panic("asserted falsy value"); + return self.panic(PanicInfo::Assert); } } Get => { @@ -728,7 +689,7 @@ impl Vm { d.as_ref().get(&k).unwrap_or(&Value::Nil).clone() } (Value::Keyword(_), _) => Value::Nil, - _ => return self.panic("keys must be keywords"), + (not_a_keyword, _) => return self.panic(PanicInfo::GetType(not_a_keyword)), }; self.push(value); } @@ -742,8 +703,10 @@ impl Vm { (Value::Tuple(t), Value::Number(i)) => { t.get(i as usize).unwrap_or(&Value::Nil).clone() } - (_, Value::Number(_)) => Value::Nil, - _ => return self.panic("indexes must be numbers"), + (coll, Value::Number(_)) => { + return self.panic(PanicInfo::AtCollectionType(coll)) + } + (coll, idx) => return self.panic(PanicInfo::AtIdxType(idx)), }; self.push(value); } @@ -756,8 +719,8 @@ impl Vm { self.push(negated); } Panic => { - let msg = self.pop().show(); - return self.panic_with(msg); + let msg = self.pop(); + return self.panic(PanicInfo::Explicit(msg)); } EmptyString => { self.push(Value::String(Rc::new("".to_string()))); @@ -786,7 +749,7 @@ impl Vm { 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"); + return self.panic(PanicInfo::PartialType(the_fn)); }; let args = self.stack.split_off(self.stack.len() - arity as usize); let partial = crate::value::Partial { @@ -810,15 +773,13 @@ impl Vm { match called { Value::Fn(_) => { + let args = self.stack.split_off(self.stack.len() - arity as usize); if !called.as_fn().accepts(arity) { - return self.panic_with(format!( - "wrong number of arguments to {} passing {arity} args", - called.show() - )); + return self.panic(PanicInfo::ArityMismatch(called, args)); } // first put the arguments in the register for i in 0..arity as usize { - self.return_register[arity as usize - i - 1] = self.pop(); + self.return_register[i] = args[i].clone(); } // self.print_stack(); @@ -849,7 +810,7 @@ impl Vm { let mut frame = CallFrame { function: called, - arity, + args, stack_base: self.stack.len() - arity as usize, ip: 0, }; @@ -874,7 +835,9 @@ impl Vm { let x = &self.pop(); f(x, y, z) } - _ => return self.panic("internal ludus error"), + _ => { + return self.panic(PanicInfo::Internal("no match calling base")) + } }; // // algo: // // clear the stack @@ -891,19 +854,19 @@ impl Vm { } Value::Partial(partial) => { let last_arg = self.pop(); - let args = &partial.args; - for arg in args { - if *arg == Value::Nothing { - self.push(last_arg.clone()); - } else { - self.push(arg.clone()); - } + let mut args = partial.args.clone(); + let placeholder_idx = + args.iter().position(|arg| *arg == Value::Nothing).unwrap(); + args[placeholder_idx] = last_arg; + for arg in args.iter() { + self.push(arg.clone()); } let the_fn = partial.function.clone(); + let arity = args.len(); let mut frame = CallFrame { function: the_fn, - arity: args.len() as u8, - stack_base: self.stack.len() - args.len(), + args, + stack_base: self.stack.len() - arity, ip: 0, }; @@ -913,9 +876,7 @@ impl Vm { self.call_stack.push(frame); self.ip = 0; } - _ => { - return self.panic_with(format!("{} is not a function", called.show())) - } + _ => return self.panic(PanicInfo::CallType(called)), } } Call => { @@ -930,10 +891,8 @@ impl Vm { match called { Value::Fn(_) => { if !called.as_fn().accepts(arity) { - return self.panic_with(format!( - "wrong number of arguments to {} passing {arity} args", - called.show() - )); + let args = self.stack.split_off(self.stack.len() - arity as usize); + return self.panic(PanicInfo::ArityMismatch(called, args)); } let splat_arity = called.as_fn().splat_arity(); if splat_arity > 0 && arity >= splat_arity { @@ -948,9 +907,13 @@ impl Vm { } else { arity }; + let mut args = vec![]; + for offset in (0..arity as usize).rev() { + args.push(self.stack[self.stack.len() - offset - 1].clone()); + } let mut frame = CallFrame { function: called, - arity, + args, stack_base: self.stack.len() - arity as usize, ip: 0, }; @@ -976,25 +939,25 @@ impl Vm { let x = &self.pop(); f(x, y, z) } - _ => return self.panic("internal ludus error"), + _ => unreachable!(), }; self.push(value); } Value::Partial(partial) => { let last_arg = self.pop(); - let args = &partial.args; - for arg in args { - if *arg == Value::Nothing { - self.push(last_arg.clone()); - } else { - self.push(arg.clone()); - } + let mut args = partial.args.clone(); + let placeholder_idx = + args.iter().position(|arg| *arg == Value::Nothing).unwrap(); + args[placeholder_idx] = last_arg; + for arg in args.iter() { + self.push(arg.clone()); } let the_fn = partial.function.clone(); + let arity = args.len(); let mut frame = CallFrame { function: the_fn, - arity: args.len() as u8, - stack_base: self.stack.len() - args.len(), + args, + stack_base: self.stack.len() - arity, ip: 0, }; @@ -1004,9 +967,7 @@ impl Vm { self.call_stack.push(frame); self.ip = 0; } - _ => { - return self.panic_with(format!("{} is not a function", called.show())) - } + _ => return self.panic(PanicInfo::CallType(called)), } } Return => {