diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-03-20 20:07:37 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-03-20 20:08:25 +0000 |
| commit | 04eb8afc8367e51c15c507740b2c55fce2569b0d (patch) | |
| tree | 01c5a53fbcd72d9a05009792126af5c54c8486ae | |
| parent | docs/index: mention license + repo (diff) | |
| download | alive-04eb8afc8367e51c15c507740b2c55fce2569b0d.tar.gz alive-04eb8afc8367e51c15c507740b2c55fce2569b0d.zip | |
language Error tracking
Close #3
| -rw-r--r-- | copilot.moon | 7 | ||||
| -rw-r--r-- | core/base/init.moon | 4 | ||||
| -rw-r--r-- | core/base/match.moon | 7 | ||||
| -rw-r--r-- | core/error.moon | 85 | ||||
| -rw-r--r-- | core/init.moon | 4 | ||||
| -rw-r--r-- | core/invoke.moon | 7 | ||||
| -rw-r--r-- | core/value.moon | 3 | ||||
| -rw-r--r-- | core/version.moon | 4 | ||||
| -rw-r--r-- | lib/logic.moon | 4 | ||||
| -rw-r--r-- | lib/math.moon | 6 | ||||
| -rw-r--r-- | lib/midi/core.moon | 4 | ||||
| -rw-r--r-- | lib/pilot.moon | 6 | ||||
| -rw-r--r-- | lib/sc.moon | 8 | ||||
| -rw-r--r-- | lib/util.moon | 7 |
14 files changed, 125 insertions, 31 deletions
diff --git a/copilot.moon b/copilot.moon index a196d9b..600d2ed 100644 --- a/copilot.moon +++ b/copilot.moon @@ -1,5 +1,5 @@ lfs = require 'lfs' -import parse, globals, Scope, Registry from require 'core' +import parse, globals, Scope, Error, Registry from require 'core' slurp = (file) -> file = io.open file, 'r' @@ -40,8 +40,9 @@ class Copilot return scope = Scope globals - root = L\try "error evaluating:", ast\eval, scope, @registry - if not root + ok, root = Error.try "evaluating '#{@file}'", ast\eval, scope, @registry + if not ok + print root @registry\rollback_eval! return diff --git a/core/base/init.moon b/core/base/init.moon index 64b34f4..bb35140 100644 --- a/core/base/init.moon +++ b/core/base/init.moon @@ -12,6 +12,7 @@ -- @see match -- @see Value -- @see Result +-- @see Error import IO from require 'core.base.io' import Op from require 'core.base.op' @@ -21,6 +22,7 @@ import Input from require 'core.base.input' import match from require 'core.base.match' import Value from require 'core.value' import Result from require 'core.result' +import Error from require 'core.error' { :IO @@ -31,5 +33,5 @@ import Result from require 'core.result' :match -- redundant exports, to keep anything an extension might need in one import - :Value, :Result + :Value, :Result, :Error } diff --git a/core/base/match.moon b/core/base/match.moon index a20bcdc..cb82ac4 100644 --- a/core/base/match.moon +++ b/core/base/match.moon @@ -2,6 +2,7 @@ -- Utilities for matching `Result` types. -- -- @module match +import Error from require 'core.error' unpack or= table.unpack class Pattern @@ -39,11 +40,11 @@ class Pattern matched = while @matches results[1] table.remove results, 1 - assert @opt or #matched > 0, "expected at least one argument for spread" + assert @opt or #matched > 0, Error 'argument', "expected at least one argument for spread" matched else matches = @matches results[1] - assert @opt or matches, "couldn't match argument #{results[1]} as #{@}" + assert @opt or matches, Error 'argument', "argument #{results[1].value} incompatible with expected type #{@}" if matches then table.remove results, 1 __tostring: => @@ -84,7 +85,7 @@ match = (pattern, inputs) -> pattern = rest Pattern pat values = [p\match inputs for p in *patterns] - assert #inputs == 0, "#{#inputs} extra arguments given!" + assert #inputs == 0, Error 'argument', "#{#inputs} extra arguments given!" values { diff --git a/core/error.moon b/core/error.moon new file mode 100644 index 0000000..a06c359 --- /dev/null +++ b/core/error.moon @@ -0,0 +1,85 @@ +---- +-- Language error and traceback. +-- +-- @classmod Error +class Error +--- members +-- @section members + + --- append a traceback frame. + -- + -- `where` should denote where the Error occured and fit grammatically to + -- complete the sentence `"{error} occured while {where}"` + -- + -- @tparam string where + add_frame: (where) => @trace ..= "\n while #{where}" + + __tostring: => "#{@kind} error: #{@message}#{@detail}#{@trace}" + +--- static functions +-- @section static + + --- create a new Error. + -- + -- `kind` should be one of: + -- + -- - `'reference'`: error concerning symbol resolution + -- - `'argument'`: error concerning Op argument matching + -- - `'implementation'`: error in the Lua/MoonScript implementation of alive. + -- Should not occur in normal operation, and constitutes a bug. + -- + -- @classmethod + -- @tparam string kind + -- @tparam string message + -- @tparam ?string detail + new: (@kind, @message, @detail) => + @trace = "" + + handler = (err) -> + if err.__class == Error + err + else + Error 'implementation', err, debug.traceback 'Lua error below:', 2 + --- Wrap function errors in a traceback frame. + -- + -- Execute `fn(...)`, and turn any error thrown as a result into an + -- `Error` instance, before re-throwing it. + -- + -- When `Error` instances are caught, `frame` is added to the traceback. + -- All other error values are turned into `'implementation'` Errors. + -- + -- @tparam string frame + -- @tparam function fn + @wrap: (frame, fn, ...) -> + results = { xpcall fn, handler, ... } + ok = table.remove results, 1 + if ok + unpack results + else + error with results[1] + \add_frame frame + + --- Capture and wrap function errors in traceback frame. + -- + -- Execute `fn(...)`, and turn any error thrown as a result into an + -- `Error` instance, before re-throwing it. + -- + -- When `Error` instances are caught, `frame` is added to the traceback. + -- All other error values are turned into `'implementation'` Errors. + -- + -- @tparam string frame + -- @tparam function fn + -- @treturn boolean `ok` true if exeuction suceeded without errors + -- @treturn Error|any `error_or_results` the `Error` instance or results + @try: (frame, fn, ...) -> + results = { xpcall fn, handler, ... } + ok = table.remove results, 1 + if ok + ok, unpack results + else + ok, with results[1] + \add_frame frame + +{ + :Error +} diff --git a/core/init.moon b/core/init.moon index a5e892d..00e3cd4 100644 --- a/core/init.moon +++ b/core/init.moon @@ -17,6 +17,7 @@ L or= setmetatable {}, __index: => -> import Value from require 'core.value' import Result from require 'core.result' import Scope from require 'core.scope' +import Error from require 'core.error' import Registry, SimpleRegistry from require 'core.registry' import Tag from require 'core.tag' @@ -35,6 +36,7 @@ globals = Scope.from_table require 'core.builtin' -- @tfield Cell Cell -- @tfield RootCell RootCell -- @tfield Scope Scope +-- @tfield Error Error -- @tfield Registry Registry -- @tfield Tag Tag -- @tfield Scope globals global definitons @@ -42,7 +44,7 @@ globals = Scope.from_table require 'core.builtin' { :Value, :Result :Cell, :RootCell - :Scope + :Scope, :Error :Registry, :SimpleRegistry, :Tag diff --git a/core/invoke.moon b/core/invoke.moon index 736f284..97d3949 100644 --- a/core/invoke.moon +++ b/core/invoke.moon @@ -6,6 +6,7 @@ import Value from require 'core.value' import Result from require 'core.result' import Action from require 'core.base' import Scope from require 'core.scope' +import Error from require 'core.error' --- `Action` implementation that invokes an `Op`. -- @@ -40,7 +41,7 @@ class op_invoke extends Action -- @treturn Result eval: (scope, tail) => children = [L\push expr\eval, scope for expr in *tail] - @op\setup [result for result in *children], scope + Error.wrap "invoking #{@op}#{@tag}", @op\setup, [result for result in *children], scope any_dirty = false for input in @op\all_inputs! @@ -91,8 +92,8 @@ class fn_invoke extends Action with L\push tail[i]\eval, outer_scope fn_scope\set name, \make_ref! - body = body\clone @tag - result = body\eval fn_scope + clone = body\clone @tag + result = Error.wrap "invoking function #{body.tag} at #{@tag}", clone\eval, fn_scope table.insert children, result Result :children, value: result.value diff --git a/core/value.moon b/core/value.moon index bda6127..7f5a93d 100644 --- a/core/value.moon +++ b/core/value.moon @@ -3,6 +3,7 @@ -- -- @classmod Value import Result from require 'core.result' +import Error from require 'core.error' import scope, base, registry from require 'core.cycle' ancestor = (klass) -> @@ -107,7 +108,7 @@ class Value when 'num', 'str' Result value: @ when 'sym' - assert (scope\get @value), "undefined reference to symbol '#{@value}'" + assert (scope\get @value), Error 'reference', "undefined symbol '#{@value}'" else error "cannot evaluate #{@}" diff --git a/core/version.moon b/core/version.moon index e5a965c..af97600 100644 --- a/core/version.moon +++ b/core/version.moon @@ -12,8 +12,8 @@ -- @tfield string web the repo web URL { tag: "v0.0" - rev_short: "23f893a" - rev_long: "23f893a1303b35e11d7103d892c40b7e8d209a06" + rev_short: "662def4" + rev_long: "662def4ef082412147ae8126e80065d245f4b426" repo: "https://github.com/s-ol/alivecoding.git" web: "https://github.com/s-ol/alivecoding/tree/v0.0" } diff --git a/lib/logic.moon b/lib/logic.moon index 7cf8695..7264be0 100644 --- a/lib/logic.moon +++ b/lib/logic.moon @@ -1,4 +1,4 @@ -import Op, Input, match from require 'core.base' +import Op, Input, Error, match from require 'core.base' all_same = (first, list) -> for v in *list @@ -72,7 +72,7 @@ class not_eq extends Op new: => super 'bool' setup: (inputs) => - assert #inputs > 1, "neq need at least two values" + assert #inputs > 1, Error 'argument', "need at least two values" super [Input.value v for v in *inputs] tick: => diff --git a/lib/math.moon b/lib/math.moon index 02ba1e1..84e6682 100644 --- a/lib/math.moon +++ b/lib/math.moon @@ -1,4 +1,4 @@ -import Op, Value, Input, match from require 'core.base' +import Op, Value, Error, Input, match from require 'core.base' unpack or= table.unpack class ReduceOp extends Op @@ -71,7 +71,9 @@ func_op = (name, arity, func) -> setup: (inputs) => { params } = match '*num', inputs - assert #params == arity, "#{@} needs exactly #{arity} parameters" if arity != '*' + if arity != '*' + err = Error 'argument', "need exactly #{arity} arguments" + assert #params == arity, err super [Input.value p for p in *params] tick: => @out\set func unpack @unwrap_all! diff --git a/lib/midi/core.moon b/lib/midi/core.moon index 8378b1d..75defdf 100644 --- a/lib/midi/core.moon +++ b/lib/midi/core.moon @@ -1,4 +1,4 @@ -import IO, Op, Registry, Input, match from require 'core.base' +import IO, Op, Registry, Input, Error, match from require 'core.base' import RtMidiIn, RtMidiOut, RtMidi from require 'luartmidi' import band, bor, lshift, rshift from require 'bit32' @@ -49,7 +49,7 @@ class MidiPort extends IO coroutine.yield msg send: (status, chan, a, b) => - assert @out, "#{@} is not an output or bidirectional port" + assert @out, Error 'type', "#{@} is not an output or bidirectional port" if 'string' == type 'status' status = bor (lshift rMIDI[status], 4), chan @out\sendmessage status, a, b diff --git a/lib/pilot.moon b/lib/pilot.moon index f24270e..a687e48 100644 --- a/lib/pilot.moon +++ b/lib/pilot.moon @@ -1,4 +1,4 @@ -import Op, Input, match from require 'core.base' +import Op, Input, Error, match from require 'core.base' import udp from require 'socket' local conn @@ -25,7 +25,7 @@ class play extends Op setup: (inputs) => { trig, args } = match 'bang *any', inputs - assert #args < 6, "too many arguments!" + assert #args < 6, Error 'argument', "too many arguments!" super trig: Input.event trig args: [Input.cold a for a in *args] @@ -40,7 +40,7 @@ class play_ extends Op setup: (inputs) => { chan, octv, note, args } = match 'any any any *any', inputs - assert #args < 3, "too many arguments!" + assert #args < 3, Error 'argument', "too many arguments!" super chan: Input.cold chan octv: Input.cold octv diff --git a/lib/sc.moon b/lib/sc.moon index ebdcd3b..7562620 100644 --- a/lib/sc.moon +++ b/lib/sc.moon @@ -1,4 +1,4 @@ -import Op, Input, match from require 'core.base' +import Op, Input, Error, match from require 'core.base' import pack from require 'osc' import dns, udp from require 'socket' @@ -8,11 +8,11 @@ class play extends Op setup: (inputs) => { socket, synth, trig, ctrls } = match 'udp/socket str bang *any?', inputs - assert #ctrls % 2 == 0, "parameters need to be specified as pairs" + assert #ctrls % 2 == 0, Error 'argument', "parameters need to be specified as pairs" for key in *ctrls[1,,2] - assert key\type! == 'str', "ony strings are supported as control names" + assert key\type! == 'str', Error 'argument', "ony strings are supported as control names" for val in *ctrls[2,,2] - assert val\type! == 'num', "only numbers are supported as control values" + assert val\type! == 'num', Error 'argument', "only numbers are supported as control values" super trig: Input.event trig diff --git a/lib/util.moon b/lib/util.moon index 1fddca4..f816eee 100644 --- a/lib/util.moon +++ b/lib/util.moon @@ -1,5 +1,4 @@ -import Op, Value, Input, match - from require 'core.base' +import Op, Value, Input, Error, match from require 'core.base' all_same = (list) -> for v in *list[2,] @@ -19,7 +18,7 @@ when i is a num, it is (floor)ed and the matching argument (starting from 0) is { i, values } = match 'any *any', inputs i_type = i\type! - assert i_type == 'bool' or i_type == 'num', "#{@}: i has to be bool or num" + assert i_type == 'bool' or i_type == 'num', Error 'argument', "i has to be bool or num" typ = all_same [v\type! for v in *values] @out = Value typ if not @out or typ != @out.type @@ -50,7 +49,7 @@ when i is a num, it is (floor)ed and the matching argument (starting from 0) is { i, values } = match 'any *any', inputs i_type = i\type! - assert i_type == 'bool' or i_type == 'num', "#{@}: i has to be bool or num" + assert i_type == 'bool' or i_type == 'num', Error 'argument', "i has to be bool or num" typ = all_same [v\type! for v in *values] @out = Value typ if not @out or typ != @out.type |
