aboutsummaryrefslogtreecommitdiffstats
path: root/alv/result
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-05-08 09:41:08 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-02 14:23:21 +0000
commit99ca67938c0145db37182bdb709d9424a20dfee0 (patch)
tree6df4e2b5fde650aa3c7e36893eb1780f932d56bd /alv/result
parentrelease v0.1 (diff)
downloadalive-99ca67938c0145db37182bdb709d9424a20dfee0.tar.gz
alive-99ca67938c0145db37182bdb709d9424a20dfee0.zip
wip new type system and refactoring
- Result -> RTNode - Stream -> Result - ValueStream -> SigStream - EventStream -> EvtStream
Diffstat (limited to 'alv/result')
-rw-r--r--alv/result/base.moon78
-rw-r--r--alv/result/const.moon196
-rw-r--r--alv/result/evt.moon95
-rw-r--r--alv/result/init.moon21
-rw-r--r--alv/result/io.moon55
-rw-r--r--alv/result/sig.moon121
6 files changed, 566 insertions, 0 deletions
diff --git a/alv/result/base.moon b/alv/result/base.moon
new file mode 100644
index 0000000..26af4b4
--- /dev/null
+++ b/alv/result/base.moon
@@ -0,0 +1,78 @@
+----
+-- base Stream interface.
+--
+-- implemented by `Constant`, `SigStream`, `EvtStream`, and `IOStream`.
+--
+-- @classmod Stream
+
+class Stream
+--- Stream interface.
+--
+-- Methods that have to be implemented by `Stream` implementations
+-- (`SigStream`, `EvtStream`, `IOStream`).
+--
+-- @section interface
+
+ --- return whether this Stream was changed in the current tick.
+ --
+ -- @function dirty
+ -- @treturn boolean
+
+ --- create a mutable copy of this Stream.
+ --
+ -- Used to insulate eval-cycles from each other.
+ --
+ -- @function fork
+ -- @treturn Stream
+
+ __tostring: => "<#{@type}#{@metatype} #{@type\pp @value}>"
+ __inherited: (cls) => cls.__base.__tostring or= @__tostring
+
+ --- the type name of this Stream's value.
+ --
+ -- the following builtin typenames are used:
+ --
+ -- - `str` - strings, `value` is a Lua string
+ -- - `sym` - symbols, `value` is a Lua string
+ -- - `num` - numbers, `value` is a Lua number
+ -- - `bool` - booleans, `value` is a Lua boolean
+ -- - `bang` - trigger signals, `value` is a Lua boolean
+ -- - `opdef` - `value` is an `Op` subclass
+ -- - `builtin` - `value` is a `Builtin` subclass
+ -- - `fndef` - `value` is a `FnDef` instance
+ -- - `scope` - `value` is a `Scope` instance
+ --
+ -- @tfield string type
+
+ --- the metatype string for this Stream.
+ --
+ -- one of `=` (`Constant`), `~` (`SigStream`),
+ -- `!` (`EvtStream` and `IOStream`).
+ --
+ -- @tfield string type
+
+ --- documentation metadata.
+ --
+ -- an optional table containing metadata for error messages and
+ -- documentation. The following keys are recognized:
+ --
+ -- - `name`: optional name
+ -- - `summary`: single-line description (markdown)
+ -- - `examples`: optional list of single-line code examples
+ -- - `description`: optional full-text description (markdown)
+ --
+ -- @tfield ?table meta
+
+--- static functions
+-- @section static
+
+ --- construct a new Stream.
+ --
+ -- @classmethod
+ -- @tparam string type the type name
+ -- @tparam ?table meta the `meta` table
+ new: (@type, @meta={}) =>
+
+{
+ :Stream
+}
diff --git a/alv/result/const.moon b/alv/result/const.moon
new file mode 100644
index 0000000..10bc943
--- /dev/null
+++ b/alv/result/const.moon
@@ -0,0 +1,196 @@
+----
+-- Constant Value.
+--
+-- Implements the `Stream` and `AST` inteface.
+--
+-- @classmod Constant
+import Stream from require 'alv.result.base'
+import RTNode from require 'alv.rtnode'
+import Error from require 'alv.error'
+import scope, base from require 'alv.cycle'
+import Primitive from require 'alv.types'
+
+num = Primitive 'num'
+str = Primitive 'str'
+sym = Primitive 'sym'
+bool = Primitive 'bool'
+
+ancestor = (klass) ->
+ assert klass, "cant find the ancestor of nil"
+ while klass.__parent
+ klass = klass.__parent
+ klass
+
+class Constant extends Stream
+ --- Whether this Result is dirty.
+ --
+ -- @tresult bool always `false`.
+ dirty: => false
+
+ --- unwrap to the Lua type.
+ --
+ -- Asserts `@type == type` if `type` is given.
+ --
+ -- @tparam[opt] string type the type to check for
+ -- @tparam[optchain] string msg message to throw if type don't match
+ -- @treturn any `value`
+ unwrap: (type, msg) =>
+ assert type == @type, msg or "#{@} is not a #{type}" if type
+ @value
+
+ --- create a mutable copy of this stream.
+ --
+ -- Used to insulate eval-cycles from each other.
+ --
+ -- @treturn Constant
+ fork: => @
+
+ --- alias for `unwrap`.
+ __call: (...) => @unwrap ...
+
+ --- compare two values.
+ --
+ -- Compares two `SigStream`s by comparing their types and their Lua values.
+ __eq: (other) => other.type == @type and other.value == @value
+
+ --- Stream metatype.
+ --
+ -- @tfield string metatype (`=`)
+ metatype: '='
+
+--- AST interface
+--
+-- `SignStream` implements the `AST` interface.
+-- @section ast
+
+ --- evaluate this literal constant.
+ --
+ -- Throws an error if `type` is not a literal (`num`, `str` or `sym`).
+ -- Returns an eval-time const result for `num` and `str`.
+ -- Resolves `sym`s in `scope` and returns a reference to them.
+ --
+ -- @tparam Scope scope the scope to evaluate in
+ -- @treturn RTNode the evaluation result
+ eval: (scope) =>
+ return RTNode value: @ if @literal
+
+ switch @type
+ when num, str
+ RTNode value: @
+ when sym
+ Error.wrap "resolving symbol '#{@value}'", scope\get, @value
+ else
+ error "cannot evaluate #{@}"
+
+ --- stringify this literal constant.
+ --
+ -- Throws an error if `raw` is not set.
+ --
+ -- @treturn string the exact string this stream was parsed from
+ stringify: => @raw
+
+ --- clone this literal constant.
+ --
+ -- @treturn SignStream self
+ clone: (prefix) => @
+
+--- static functions
+-- @section static
+
+ --- construct a new Constant.
+ --
+ -- @classmethod
+ -- @tparam string type the type name
+ -- @tparam any value the Lua value to be accessed through `unwrap`
+ -- @tparam string raw the raw string that resulted in this value. Used by `parsing`.
+ new: (type, @value, @raw) => super type
+
+ unescape = (str) -> str\gsub '\\([\'"\\])', '%1'
+ --- create a capture-function (for parsing with Lpeg).
+ --
+ -- @tparam string type the type name (one of `num`, `sym` or `str`)
+ -- @tparam string sep the seperator char (only for `str`)
+ @parse: (type, sep) =>
+ switch type
+ when 'num' then (match) -> @ num, (tonumber match), match
+ when 'sym' then (match) -> @ sym, match, match
+ when 'str' then (match) -> @ str, (unescape match), sep .. match .. sep
+
+ --- wrap a Lua value.
+ --
+ -- Attempts to guess the type and wrap a Lua value.
+ --
+ -- @tparam any val the value to wrap
+ -- @tparam[opt] string name the name of this value (for error logging)
+ -- @treturn Constant
+ @wrap: (val, name='(unknown)') ->
+ typ = switch type val
+ when 'number' then 'num'
+ when 'string' then 'str'
+ when 'table'
+ if rawget val, '__base'
+ -- a class
+ switch ancestor val
+ when base.Op then 'opdef'
+ when base.Builtin then 'builtin'
+ else
+ error "#{name}: cannot wrap class '#{val.__name}'"
+ elseif val.__class
+ -- an instance
+ switch ancestor val.__class
+ when scope.Scope then 'scope'
+ when base.FnDef then 'fndef'
+ when Stream then return val
+ else
+ error "#{name}: cannot wrap '#{val.__class.__name}' instance"
+ else
+ -- plain table
+ val = scope.Scope.from_table val
+ 'scope'
+ else
+ error "#{name}: cannot wrap Lua type '#{type val}'"
+
+ Constant (Primitive typ), val
+
+ --- create a constant number.
+ -- @tparam number val the number
+ -- @treturn Constant
+ @num: (val) -> Constant num, val, tostring val
+
+ --- create a constant string.
+ -- @tparam string val the string
+ -- @treturn Constant
+ @str: (val) -> Constant str, val, "'#{val}'"
+
+ --- create a constant symbol.
+ -- @tparam string val the symbol
+ -- @treturn Constant
+ @sym: (val) -> Constant sym, val, val
+
+ --- create a constant boolean.
+ -- @tparam boolean val the boolean
+ -- @treturn Constant
+ @bool: (val) -> Constant bool, val, tostring val
+
+ --- create a forced-literal Constant.
+ --
+ -- For internal use in `Builtin`s only.
+ --
+ -- @treturn Constant
+ @literal: (...) ->
+ with Constant ...
+ .literal = true
+
+ --- wrap and document a value.
+ --
+ -- wraps `args.value` using `wrap`, then assigns `meta`.
+ --
+ -- @tparam table args table with keys `value` and `meta`
+ -- @treturn Constant
+ @meta: (args) ->
+ with Constant.wrap args.value
+ .meta = args.meta if args.meta
+
+{
+ :Constant
+}
diff --git a/alv/result/evt.moon b/alv/result/evt.moon
new file mode 100644
index 0000000..0559a70
--- /dev/null
+++ b/alv/result/evt.moon
@@ -0,0 +1,95 @@
+----
+-- Stream of momentary events.
+--
+-- @classmod EvtStream
+import Stream from require 'alv.result.base'
+
+class EvtStream extends Stream
+--- members
+-- @section members
+
+ --- return whether this stream was changed in the current tick.
+ --
+ -- @treturn bool
+ dirty: => @updated == COPILOT.T
+
+ --- push an event value into the stream.
+ --
+ -- Marks this stream as dirty for the remainder of the current tick.
+ --
+ -- @tparam any event
+ add: (event) =>
+ if not @dirty!
+ @events = {}
+
+ @updated = COPILOT.T
+ table.insert @events, event
+
+ --- get the sequence of current events (if any).
+ --
+ -- Returns `events` if `dirty`, or an empty table otherwise.
+ -- Asserts `@type == type` if `type` is given.
+ --
+ -- @tparam[opt] string type the type to check for
+ -- @tparam[optchain] string msg message to throw if type don't match
+ -- @treturn {any,...} `events`
+ unwrap: (type, msg) =>
+ assert type == @type, msg or "#{@} is not a #{type}" if type
+ if @dirty! then @events else {}
+
+ --- create a mutable copy of this stream.
+ --
+ -- Used to wrap insulate eval-cycles from each other.
+ --
+ -- @treturn EvtStream
+ fork: => @@ @type
+
+ --- alias for `unwrap`.
+ __call: (...) => @unwrap ...
+ __tostring: => "<#{@type}#{@metatype} #{@type\pp @value}>"
+
+ --- Stream metatype.
+ --
+ -- @tfield string metatype (`!`)
+ metatype: '!'
+
+ --- the type name of the stream.
+ --
+ -- the following builtin typenames are used:
+ --
+ -- - `str` - strings, `value` is a Lua string
+ -- - `sym` - symbols, `value` is a Lua string
+ -- - `num` - numbers, `value` is a Lua number
+ -- - `bool` - booleans, `value` is a Lua boolean
+ -- - `bang` - trigger signals, `value` is a Lua boolean
+ -- - `opdef` - `value` is an `Op` subclass
+ -- - `builtin` - `value` is a `Builtin` subclass
+ -- - `fndef` - `value` is a `FnDef` instance
+ -- - `scope` - `value` is a `Scope` instance
+ --
+ -- @tfield string type
+
+ --- documentation metadata.
+ --
+ -- an optional table containing metadata for error messages and
+ -- documentation. The following keys are recognized:
+ --
+ -- - `name`: optional name
+ -- - `summary`: single-line description (markdown)
+ -- - `examples`: optional list of single-line code examples
+ -- - `description`: optional full-text description (markdown)
+ --
+ -- @tfield ?table meta
+
+--- static functions
+-- @section static
+
+ --- construct a new EvtStream.
+ --
+ -- @classmethod
+ -- @tparam string type the type name
+ new: (type) => super type
+
+{
+ :EvtStream
+}
diff --git a/alv/result/init.moon b/alv/result/init.moon
new file mode 100644
index 0000000..aa7a115
--- /dev/null
+++ b/alv/result/init.moon
@@ -0,0 +1,21 @@
+----
+-- `Stream` interface and implementations.
+--
+-- @see Stream
+-- @see Constant
+-- @see SigStream
+-- @see EvtStream
+-- @see IOStream
+--
+-- @module stream
+import Constant from require 'alv.result.const'
+import SigStream from require 'alv.result.sig'
+import EvtStream from require 'alv.result.evt'
+import IOStream from require 'alv.result.io'
+
+{
+ :Constant
+ :SigStream
+ :EvtStream
+ :IOStream
+}
diff --git a/alv/result/io.moon b/alv/result/io.moon
new file mode 100644
index 0000000..b12322c
--- /dev/null
+++ b/alv/result/io.moon
@@ -0,0 +1,55 @@
+----
+-- Stream of external side-effects.
+--
+-- Unlike other `Stream`s, this is not updated/set by an `Op` instace, but is
+-- continuously polled for changes by the runtime and may mark itself as
+-- *dirty* at any point in time. All runtime execution happens due to IOStream
+-- updates, which ripple through the `RTNode` tree.
+--
+-- @classmod IOStream
+import EvtStream from require 'alv.result.evt'
+
+class IOStream extends EvtStream
+--- IOStream interface.
+--
+-- methods that have to be implemented by `IOStream` implementations.
+-- @section interface
+
+ --- construct a new IOStream.
+ --
+ -- Must prepare the instance for `dirty` to be called.
+ -- The super-constructor should be called to set `Stream.type`.
+ --
+ -- @classmethod
+ -- @tparam string type the typename of this stream.
+ new: (type) => super type
+
+ --- create a mutable copy of this stream.
+ --
+ -- Used to wrap insulate eval-cycles from each other.
+ --
+ -- @treturn IOStream
+ fork: => @
+
+ --- poll for changes.
+ --
+ -- Called every frame by the main event loop to update internal state.
+ poll: =>
+
+ --- check whether this adapter requires processing.
+ --
+ -- Must return a boolean indicating whether `Op`s that refer to this instance
+ -- via `Input.hot` should be notified (via `Op:tick`). May be called multiple
+ -- times. May be called before `poll` on the first frame after construction.
+ --
+ -- If this is not overriden, the `EvtStream` interface can be used, see
+ -- `EvtStream.add`, `EvtStream.unwrap`, and `EvtStream.dirty`.
+ --
+ -- @function dirty
+ -- @treturn bool whether processing is required
+
+ __inherited: (cls) => cls.__base.__tostring or= @__tostring
+
+{
+ :IOStream
+}
diff --git a/alv/result/sig.moon b/alv/result/sig.moon
new file mode 100644
index 0000000..d7c18a3
--- /dev/null
+++ b/alv/result/sig.moon
@@ -0,0 +1,121 @@
+----
+-- Continuous stream of values.
+--
+-- @classmod SigStream
+import Stream from require 'alv.result.base'
+import Primitive from require 'alv.types'
+
+class SigStream extends Stream
+--- members
+-- @section members
+
+ --- return whether this stream was changed in the current tick.
+ --
+ -- @treturn bool
+ dirty: => @updated == COPILOT.T
+
+ --- update this stream's value.
+ --
+ -- Marks this stream as dirty for the remainder of the current tick.
+ set: (value) =>
+ if value != @value
+ @value = value
+ @updated = COPILOT.T
+
+ --- unwrap to the Lua type.
+ --
+ -- Asserts `@type == type` if `type` is given.
+ --
+ -- @tparam[opt] string type the type to check for
+ -- @tparam[optchain] string msg message to throw if type don't match
+ -- @treturn any `value`
+ unwrap: (type, msg) =>
+ assert type == @type, msg or "#{@} is not a #{type}" if type
+ @value
+
+ --- create a mutable copy of this stream.
+ --
+ -- Used to insulate eval-cycles from each other.
+ --
+ -- @treturn SigStream
+ fork: =>
+ with @@ @type, @value
+ .updated = @updated
+
+ --- alias for `unwrap`.
+ __call: (...) => @unwrap ...
+
+ --- compare two values.
+ --
+ -- Compares two `SigStream`s by comparing their types and their Lua values.
+ __eq: (other) => other.type == @type and other.value == @value
+
+ --- Stream metatype.
+ --
+ -- @tfield string metatype (`~`)
+ metatype: '~'
+
+ --- the type name of this stream.
+ --
+ -- the following builtin typenames are used:
+ --
+ -- - `str` - strings, `value` is a Lua string
+ -- - `sym` - symbols, `value` is a Lua string
+ -- - `num` - numbers, `value` is a Lua number
+ -- - `bool` - booleans, `value` is a Lua boolean
+ -- - `bang` - trigger signals, `value` is a Lua boolean
+ -- - `opdef` - `value` is an `Op` subclass
+ -- - `builtin` - `value` is a `Builtin` subclass
+ -- - `fndef` - `value` is a `FnDef` instance
+ -- - `scope` - `value` is a `Scope` instance
+ --
+ -- @tfield string type
+
+ --- the wrapped Lua value.
+ -- @tfield any value
+
+ --- documentation metadata.
+ --
+ -- an optional table containing metadata for error messages and
+ -- documentation. The following keys are recognized:
+ --
+ -- - `name`: optional name
+ -- - `summary`: single-line description (markdown)
+ -- - `examples`: optional list of single-line code examples
+ -- - `description`: optional full-text description (markdown)
+ --
+ -- @tfield ?table meta
+
+--- static functions
+-- @section static
+
+ --- construct a new SigStream.
+ --
+ -- @classmethod
+ -- @tparam string type the type name
+ -- @tparam any value the Lua value to be accessed through `unwrap`
+ new: (type, @value) => super type
+
+ --- create a number stream.
+ -- @tparam number val the number
+ -- @treturn SigStream
+ @num: (val) -> SigStream Primitive'num', val
+
+ --- create a string stream.
+ -- @tparam string val the string
+ -- @treturn SigStream
+ @str: (val) -> SigStream Primitive'str', val
+
+ --- create a symbol stream.
+ -- @tparam string val the symbol
+ -- @treturn symbol
+ @sym: (val) -> SigStream Primitive'sym', val
+
+ --- create a boolean stream.
+ -- @tparam boolean val the boolean
+ -- @treturn SigStream
+ @bool: (val) -> SigStream Primitive'bool', val
+
+{
+ :SigStream
+}