aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-03-20 20:07:37 +0000
committers-ol <s-ol@users.noreply.github.com>2020-03-20 20:08:25 +0000
commit04eb8afc8367e51c15c507740b2c55fce2569b0d (patch)
tree01c5a53fbcd72d9a05009792126af5c54c8486ae
parentdocs/index: mention license + repo (diff)
downloadalive-04eb8afc8367e51c15c507740b2c55fce2569b0d.tar.gz
alive-04eb8afc8367e51c15c507740b2c55fce2569b0d.zip
language Error tracking
Close #3
-rw-r--r--copilot.moon7
-rw-r--r--core/base/init.moon4
-rw-r--r--core/base/match.moon7
-rw-r--r--core/error.moon85
-rw-r--r--core/init.moon4
-rw-r--r--core/invoke.moon7
-rw-r--r--core/value.moon3
-rw-r--r--core/version.moon4
-rw-r--r--lib/logic.moon4
-rw-r--r--lib/math.moon6
-rw-r--r--lib/midi/core.moon4
-rw-r--r--lib/pilot.moon6
-rw-r--r--lib/sc.moon8
-rw-r--r--lib/util.moon7
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