diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-03-07 20:58:30 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-03-07 20:58:51 +0000 |
| commit | 6a7a2ddaca798f3cccac394d1fb9f317cbe90de6 (patch) | |
| tree | 4e49e53057b39f75813311ede13f87d238061fe6 /core | |
| parent | spellcheck (diff) | |
| download | alive-6a7a2ddaca798f3cccac394d1fb9f317cbe90de6.tar.gz alive-6a7a2ddaca798f3cccac394d1fb9f317cbe90de6.zip | |
add ldoc documentation
Diffstat (limited to 'core')
| -rw-r--r-- | core/base.moon | 298 | ||||
| -rw-r--r-- | core/base/action.moon | 95 | ||||
| -rw-r--r-- | core/base/fndef.moon | 22 | ||||
| -rw-r--r-- | core/base/init.moon | 35 | ||||
| -rw-r--r-- | core/base/input.moon | 131 | ||||
| -rw-r--r-- | core/base/io.moon | 34 | ||||
| -rw-r--r-- | core/base/match.moon | 93 | ||||
| -rw-r--r-- | core/base/op.moon | 132 | ||||
| -rw-r--r-- | core/cell.moon | 121 | ||||
| -rw-r--r-- | core/config.ld | 4 | ||||
| -rw-r--r-- | core/init.moon | 27 | ||||
| -rw-r--r-- | core/invoke.moon | 4 | ||||
| -rw-r--r-- | core/parsing.moon | 19 | ||||
| -rw-r--r-- | core/pattern.moon | 65 | ||||
| -rw-r--r-- | core/registry.moon | 35 | ||||
| -rw-r--r-- | core/result.moon | 56 | ||||
| -rw-r--r-- | core/scope.moon | 46 | ||||
| -rw-r--r-- | core/tag.moon | 63 | ||||
| -rw-r--r-- | core/value.moon | 115 | ||||
| -rw-r--r-- | core/version.moon | 14 |
20 files changed, 939 insertions, 470 deletions
diff --git a/core/base.moon b/core/base.moon deleted file mode 100644 index 7962545..0000000 --- a/core/base.moon +++ /dev/null @@ -1,298 +0,0 @@ --- base definitions for extensions -import Value from require 'core.value' -import Result from require 'core.result' -import match from require 'core.pattern' - -unpack or= table.unpack - --- an incoming side-effect adapter, polled by the main event loop to pump --- events into the dataflow graph. --- --- subclasses must implement this interface: --- --- :new() - construct a new instance --- --- must prepare the instance for :dirty(). --- --- :tick() - poll for changes --- --- called every frame by the event loop to update internal state. --- --- :dirty() - whether this adapter requires processing --- --- must return a boolean indicating whether `Op`s that refer to this instance --- via `IOInput` should be notified (via `Op:tick()`). May be called multiple --- multiple times. May be called before :tick() on the first frame after --- construction. --- -class IO - -- called in the main event loop - tick: => - - -- whether a tree update is necessary - dirty: => - --- a persistent expression Operator --- --- subclasses must implement this interface: --- --- :new() - construct a new instance --- --- the super-constructor can be used to construct a `Value` instance in @out. --- --- :setup(inputs, scope) - parse arguments and patch self --- --- called once every eval-cycle. `inputs` is a list of `Result`s that are the --- argument to this op. The `inputs` have to be wrapped in `Input` instances --- to define update behaviour. Use `match` to parse them, then delegate to --- super to patch the `Input` instances. --- --- :tick(setup) - handle incoming events and update @out --- --- called once per frame if any inputs are dirty. Some `Input`s (like --- `ValueInput`) have special behaviour immediately after :setup(). You can --- detect this using the `setup` parameter, which is true the first time --- :tick() is called after :setup(). :tick() is not called immediately after --- :setup() if no `@inputs` are dirty. Update @out here. --- --- :destroy() - called when the Op is destroyed --- --- .out - a `Value` instance representing this Op's computed output value. --- --- @out must be set to a `Value` instance once :setup() finishes. @out must --- not change type, be removed or replaced outside of :new() and :setup(). --- @out should have a value assigned via :set() or the `Value` constructor --- once :tick(true) is called. If @out's value is not initialized in :new() --- or :setup(), the implementation must make sure :tick(true) is called at --- least on the first eval-cycle the Op goes through, e.g. by using a --- `ValueInput`. --- -class Op --- super-implementations for extensions - -- if `type` is passed, an output stream is instantiated. - -- if `init` is passed, the stream is initialized to that Lua value. - -- it is okay not to use this and create the output stream in :setup() if the - -- type is not known at this time. - new: (type, init) => - if type - @out = Value type, init - - -- setups previous @inputs, if any, with the new inputs, and writes them to - -- `@inputs`. The `inputs` table can be nested with string or integer keys, - -- but all leaf-entries must be `Input` instances. It must not contain loops - -- or instances of other classes. - setup: do - do_setup = (old, cur) -> - for k, cur_val in pairs cur - old_val = old and old[k] - - -- are these inputs or nested tables? - cur_plain = cur_val and not cur_val.__class - old_plain = old_val and not old_val.__class - - if cur_plain and old_plain - -- both are tables, recurse - do_setup old_val, cur_val - elseif not (cur_plain or old_plain) - -- both are streams (or nil), setup them - cur_val\setup old_val - - (inputs) => - old_inputs = @inputs - @inputs = inputs - do_setup old_inputs, @inputs - - tick: => - destroy: => - --- utilities - -- iterate over the (potentially nested) inputs table - all_inputs: do - do_yield = (table) -> - for k, v in pairs table - if v.__class - coroutine.yield v - else - do_yield v - - => coroutine.wrap -> do_yield @inputs - - unwrap_all: do - do_unwrap = (value) -> - if value.__class - value\unwrap! - else - {k, do_unwrap v for k,v in pairs value} - - => do_unwrap @inputs - - assert_types: (...) => - num = select '#', ... - assert #@inputs >= num, "argument count mismatch" - @assert_first_types ... - - assert_first_types: (...) => - num = select '#', ... - for i = 1, num - expect = select i, ... - assert @inputs[i].type == expect, "expected argument #{i} of #{@} to be of type #{expect} but found #{@inputs[i]}" - --- static - __tostring: => "<op: #{@@__name}>" - __inherited: (cls) => cls.__base.__tostring = @__tostring - --- a builtin / special form / cell-evaluation strategy. --- --- responsible for quoting/evaluating subexpressions, instantiating and patching --- Ops updating the current Scope, etc. See core.builtin and core.invoke for --- many examples. -class Action - -- head: the (:eval'd) head of the Cell to evaluate (a Const) - -- tag: the Tag of the expression to evaluate - new: (head, @tag) => - @patch head - - -- * eval args - -- * perform scope effects - -- * patch nested exprs - -- * return runtime-tree value - eval: (scope, tail) => error "not implemented" - - -- free resources - destroy: => - - -- update this instance for :eval() with new head - -- if :patch() returns false, this instance is :destroy'ed and recreated - -- instead must *not* return false when called after :new() - -- only considered if Action types match - patch: (head) => - if head == @head - true - - @head = head - --- static - -- find & patch the action for the expression with Tag 'tag' if it exists, - -- and is compatible with the new Cell contents, otherwise instantiate it. - -- register the action with the tag, evaluate it and return the Result - @eval_cell: (scope, tag, head, tail) => - last = tag\last! - compatible = last and - (last.__class == @) and - (last\patch head) and - last - - L\trace if compatible - "reusing #{last} for #{tag} <#{@__name} #{head}>" - else if last - "replacing #{last} with new #{tag} <#{@__name} #{head}>" - else - "initializing #{tag} <#{@__name} #{head}>" - - action = if compatible - tag\keep compatible - compatible - else - last\destroy! if last - with next = @ head, tag - tag\replace next - - action\eval scope, tail - - __tostring: => "<#{@@__name} #{@head}>" - __inherited: (cls) => cls.__base.__tostring = @__tostring - --- an ALV function definition --- --- when called, expands its body with params bound to the fn arguments --- (see core.invoke.fn-invoke) -class FnDef - -- params: sequence of (:quote'd) symbols, each naming a function parameter - -- body: (:quote'd) expression the function evaluates to - -- scope: the lexical scope the function was defined in (closure) - new: (@params, @body, @scope) => - - __tostring: => - "(fn (#{table.concat [p\stringify! for p in *@params], ' '}) ...)" - --- an update scheduling policy for `Op`. --- --- subclasses must implement this interface: --- --- :new(value) - create an instance --- --- `value` is either a `Value` or a `Result` instance and should be unwrapped. --- --- :setup(prev) - copy state from old instance --- --- called by `Op:setup()` with another `Input` instance or `nil` once this instance is --- registered. Must prepare this instance for :dirty(). --- ---- :dirty() - whether this input requires processing --- --- must return a boolean indicating whether `Op`s that refer to this instance --- should be notified (via `Op:tick()`). --- --- :finish_setup() - leave setup state --- --- called after the Op has completed (or skipped) its first `Op:tick()` after --- `Op:setup()`. Must prepare this instance for dataflow operation. --- -class Input - new: (value) => - assert value, "nil passed to Input: #{value}" - @stream = switch value.__class - when Result - assert value.value, "Input from result without value!" - when Value - value - else - error "Input from unknown value: #{value}" - - setup: (previous) => - - finish_setup: => - dirty: => @stream\dirty! - unwrap: => @stream\unwrap! - type: => @stream.type - - __call: => @stream\unwrap! - __tostring: => "#{@@__name}:#{@stream}" - __inherited: (cls) => - cls.__base.__call = @__call - cls.__base.__tostring = @__tostring - --- Never marked dirty. Use this for input streams that are only read when --- another input fires. -class ColdInput extends Input - dirty: => false - --- Marked dirty for the setup-tick if old and new stream differ in current --- value. This is the most common `Input` strategy. Should be used whenever a --- value denotes state. -class ValueInput extends Input - setup: (old) => @dirty_setup = not old or @stream != old.stream - finish_setup: => @dirty_setup = false - dirty: => @dirty_setup or @stream\dirty! - --- Only marked dirty if the input stream itself is dirty. Should be used --- whenever a value denotes a momentary event or impulse. -class EventInput extends Input - --- Marked dirty when an IO object is dirty. Must be used for IO values. -class IOInput extends Input - impure: true - dirty: => @stream\unwrap!\dirty! - -{ - :IO - :Op - :Action - :FnDef - - :ValueInput, :EventInput, :IOInput, :ColdInput - - -- redundant exports, to keep anything an extension might need in one import - :Value, :Result - :match -} diff --git a/core/base/action.moon b/core/base/action.moon new file mode 100644 index 0000000..0b9dc77 --- /dev/null +++ b/core/base/action.moon @@ -0,0 +1,95 @@ +---- +-- Builtin / Special Form / `Cell`-evaluation Strategy. +-- +-- Responsible for quoting/evaluating subexpressions, instantiating and patching +-- `Op`s, updating the current `Scope`, etc. +-- See `builtin` and `invoke` for examples. +-- +-- @classmod Action + +class Action +--- Action interface. +-- +-- methods that have to be implemented by `Action` implementations. +-- @section interface + + --- create a new instance + -- @tparam Value head the (`\eval`d) head of the Cell to evaluate + -- @tparam Tag tag the Tag of the expression to evaluate + new: (head, @tag) => + @patch head + + --- perform the actual evaluation + -- + -- Implementations should: + -- + -- - eval or quote `tail` values + -- - perform scope effects + -- - wrap all child-results + -- + -- @tparam Scope scope the active scope + -- @tparam {AST,...} tail the arguments to this expression + -- @treturn Result the result of this evaluation + eval: (scope, tail) => error "not implemented" + + --- free resources + destroy: => + + --- attempt to update this instance with a new `@head` prior to `\eval`. + -- + -- If `\patch` returns `false`, this instance is `\destroy`ed and recreated. + -- Must *not* return `false` when called immediately after `\new`. + -- Only considered if Action types of old and new expression match. + -- + -- @tparam AST head the new head value + -- @treturn bool whether patching was successful + patch: (head) => + if head == @head + true + + @head = head + +--- static functions +-- @section static + + --- get-or-update an `Action` for a given tag, then evaluate it. + -- + -- Find the action for the expression with `Tag` `tag` if it exists, + -- and is compatible with the new `head`, otherwise instantiate one. + -- Register the `Action` with `tag`, evaluate it and return the `Result`. + -- + -- @tparam Scope scope the active scope + -- @tparam Tag tag the tag of the `Cell` being evaluated + -- @tparam Value head the (`\eval`d) head of the `Cell` being evaluated + -- @tparam {Ast,...} tail the raw AST parameters to the `Cell` being evaluated + -- @treturn Result the result of evaluation + eval_cell: (scope, tag, head, tail) => + last = tag\last! + compatible = last and + (last.__class == @) and + (last\patch head) and + last + + L\trace if compatible + "reusing #{last} for #{tag} <#{@__name} #{head}>" + else if last + "replacing #{last} with new #{tag} <#{@__name} #{head}>" + else + "initializing #{tag} <#{@__name} #{head}>" + + action = if compatible + tag\keep compatible + compatible + else + last\destroy! if last + with next = @ head, tag + tag\replace next + + action\eval scope, tail + + __tostring: => "<#{@@__name} #{@head}>" + __inherited: (cls) => cls.__base.__tostring = @__tostring + +{ + :Action +} diff --git a/core/base/fndef.moon b/core/base/fndef.moon new file mode 100644 index 0000000..b6c284b --- /dev/null +++ b/core/base/fndef.moon @@ -0,0 +1,22 @@ +---- +-- `alive` user-function definition (`fndef`). +-- +-- When called, expands to its body with params bound to the fn arguments (see +-- `invoke.fn_invoke`). +-- +-- @classmod FnDef + +class FnDef + --- create a new instance + -- + -- @tparam {Value,...} params (`\quote`d) naming the function parameters + -- @tparam AST body (`\quote`d) expression the function evaluates to + -- @tparam Scope scope the lexical scope the function was defined in (closure) + new: (@params, @body, @scope) => + + __tostring: => + "(fn (#{table.concat [p\stringify! for p in *@params], ' '}) ...)" + +{ + :FnDef +} diff --git a/core/base/init.moon b/core/base/init.moon new file mode 100644 index 0000000..64b34f4 --- /dev/null +++ b/core/base/init.moon @@ -0,0 +1,35 @@ +---- +-- Base definitions for extensions. +-- +-- This module exports the following classes that extension modules may need: +-- +-- @module base +-- @see IO +-- @see Op +-- @see Action +-- @see FnDef +-- @see Input +-- @see match +-- @see Value +-- @see Result + +import IO from require 'core.base.io' +import Op from require 'core.base.op' +import Action from require 'core.base.action' +import FnDef from require 'core.base.fndef' +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' + +{ + :IO + :Op + :Action + :FnDef + :Input + :match + + -- redundant exports, to keep anything an extension might need in one import + :Value, :Result +} diff --git a/core/base/input.moon b/core/base/input.moon new file mode 100644 index 0000000..14c812e --- /dev/null +++ b/core/base/input.moon @@ -0,0 +1,131 @@ +---- +-- Update scheduling policy for `Op` arguments. +-- +-- @classmod Input +import Value from require 'core.value' +import Result from require 'core.result' + +local ColdInput, ValueInput, EventInput, IOInput + +class Input +--- Input interface. +-- +-- Methods that have to be implemented by `Input` implementations. +-- @section interface + + --- create an instance (optional). + -- + -- `value` is either a `Value` or a `Result` instance and should be + -- unwrapped and assigned to `@stream`. + -- + -- @function new + -- @tparam Value|Result value + new: (value) => + assert value, "nil passed to Input: #{value}" + @stream = switch value.__class + when Result + assert value.value, "Input from result without value!" + when Value + value + else + error "Input from unknown value: #{value}" + + --- copy state from old instance (optional). + -- + -- called by `Op``\setup` with another `Input` instance or `nil` once this instance is + -- registered. Must prepare this instance for `\dirty`. + -- + -- May enter a 'setup state' that is exited using `\finish_setup`. + -- + -- @function setup + -- @tparam `Input?` prev previous `Input` intance or nil + setup: (prev) => + + --- whether this input requires processing (optional). + -- + -- must return a boolean indicating whether `Op`s that refer to this instance + -- should be notified (via `Op``\tick`). If not overwritten, delegates to + -- `@stream\dirty`. + -- + -- @treturn bool whether processing is necessary + dirty: => @stream\dirty! + + --- leave setup state (optional). + -- + -- called after the `Op` has completed (or skipped) its first `Op``\tick` after + -- `Op``\setup`. Must prepare this instance for dataflow operation. + finish_setup: => + + --- unwrap to Lua value (optional). + -- + -- @treturn any the raw Lua value + unwrap: => @stream\unwrap! + + --- return the type name of this `Input` (optional). + type: => @stream.type + +--- methods. +-- +-- @section methods + + --- alias for `\unwrap`. + __call: => @stream\unwrap! + + __tostring: => "#{@@__name}:#{@stream}" + __inherited: (cls) => + cls.__base.__call = @__call + cls.__base.__tostring = @__tostring + +--- constructors +-- @section constructors + + --- Create a `ColdInput`. + -- + -- Never marked dirty. Use this for input streams that are only read when + -- another `Input` is dirty. + -- + -- @tparam Value|Result value + cold: (value) -> ColdInput value + + --- Create a `ValueInput`. + -- + -- Marked dirty for the eval-tick if old and new `Value` differ. This is the + -- most common `Input` strategy. Should be used whenever a + -- value denotes state. + -- + -- @tparam Value|Result value + value: (value) -> ValueInput value + + --- Create an `EventInput`. + -- + -- Only marked dirty if the `Value` itself is dirty. Should be used whenever + -- an `Input` denotes a momentary event or impulse. + -- + -- @tparam Value|Result value + event: (value) -> EventInput value + + --- Create an `IOInput`. + -- + -- Marked dirty only when an `IO` is dirty. Must be used only for `Value`s + -- which `\unwrap` to `IO` instances. + -- + -- @tparam Value|Result value + io: (value) -> IOInput value + +class ColdInput extends Input + dirty: => false + +class ValueInput extends Input + setup: (old) => @dirty_setup = not old or @stream != old.stream + finish_setup: => @dirty_setup = false + dirty: => @dirty_setup or @stream\dirty! + +class EventInput extends Input + +class IOInput extends Input + impure: true + dirty: => @stream\unwrap!\dirty! + +{ + :Input +} diff --git a/core/base/io.moon b/core/base/io.moon new file mode 100644 index 0000000..e7399e8 --- /dev/null +++ b/core/base/io.moon @@ -0,0 +1,34 @@ +---- +-- Incoming side-effect adapter, polled by the main event loop to pump +-- events into the dataflow graph. +-- +-- @classmod IO + +class IO +--- IO interface. +-- +-- methods that have to be implemented by `IO` implementations. +-- @section interface + + --- construct a new instance. + -- + -- Must prepare the instance for `\dirty` to be called. + new: => + + --- poll for changes. + -- + -- Called every frame by the main event loop to update internal state. + tick: => + + --- check whether this adapter requires processing. + -- + -- Must return a boolean indicating whether `Op`s that refer to this instance + -- via `Input``.io` should be notified (via `Op``\tick`). May be called multiple + -- times. May be called before `\tick` on the first frame after construction. + -- + -- @treturn bool whether processing is required + dirty: => + +{ + :IO +} diff --git a/core/base/match.moon b/core/base/match.moon new file mode 100644 index 0000000..a20bcdc --- /dev/null +++ b/core/base/match.moon @@ -0,0 +1,93 @@ +---- +-- Utilities for matching `Result` types. +-- +-- @module match +unpack or= table.unpack + +class Pattern + new: (opts) => + if 'string' == type opts + splat, const, type, opt = opts\match '^(%*?)(=?)([%w%-%_%/]+)(%??)$' + assert type, "couldn't parse type pattern '#{opts}'" + opts = { + :type + splat: splat == '*' + const: const == '=' + opt: opt == '?' + } + + @type = opts.type + @const = opts.const + @opt = opts.opt + @splat = opts.splat + + matches: (result) => + return false unless result + + if @const + return false unless result\is_const! + + if not result.value + return @type == 'nil' + + return true if @type == 'any' + + result.value.type == @type + + match: (results) => + if @splat + matched = while @matches results[1] + table.remove results, 1 + + assert @opt or #matched > 0, "expected at least one argument for spread" + matched + else + matches = @matches results[1] + assert @opt or matches, "couldn't match argument #{results[1]} as #{@}" + if matches then table.remove results, 1 + + __tostring: => + str = @type + str = '*' .. str if @splat + str = '=' .. str if @const + str = str .. '?' if @opt + str + +--- match inputs to a argument type definition. +-- +-- `pattern` is a string of type entries. Every type entry can be like this: +-- +-- - `any` - matches one `Result` and returns it. +-- - `typename` - matches one `Result` of type `typename` and returns it. +-- - `=typename` - matches one eval-time const `Result`s of type `typname` and +-- returns it. +-- - `typename?` - matches what `typename` would match, if possible (greedy). +-- Otherwise returns `nil`. +-- - `*typename` - matches as many `typename` `Result`s as possible (greedy). +-- Throws if there isn't at least one such `Result`. Returns a sequence of +-- `Result`s. +-- - `*typename?` - like `*typename`, except it also matches zero `Result`s. +-- - `*=typename`, `=typename?` and `*=typename?` behave as expected. +-- +-- Except for `typename?` and `*typename?`, all entries throw if they cannot +-- match the next `Result` in `inputs`. +-- +-- Throws if there are leftover `inputs` after matching all of `pattern`. +-- +-- @tparam string pattern the argument type definition +-- @tparam {Result,...} inputs the list of inputs +-- @treturn {Result|{Result,...},...} the inputs as matched against `pattern` +match = (pattern, inputs) -> + patterns = while pattern + pat, rest = pattern\match '^([^ ]+) (.*)$' + pat = pattern unless pat + pattern = rest + Pattern pat + values = [p\match inputs for p in *patterns] + assert #inputs == 0, "#{#inputs} extra arguments given!" + values + +{ + :Pattern + :match +} diff --git a/core/base/op.moon b/core/base/op.moon new file mode 100644 index 0000000..6d1ec98 --- /dev/null +++ b/core/base/op.moon @@ -0,0 +1,132 @@ +---- +-- Persistent expression Operator. +-- +-- @classmod Op +import Value from require 'core.value' + +class Op +--- Op interface. +-- +-- methods that have to be implemented by `Op` implementations. +-- @section interface + + --- construct a new instance. + -- + -- The super-constructor can be used to construct a `Value` instance in `@out`. + -- + -- @function new + + --- parse arguments and patch self. + -- + -- Called once every eval-cycle. `inputs` is a list of `Result`s that are the + -- argument to this op. The `inputs` have to be wrapped in `Input` instances + -- to define update behaviour. Use `base.match` to parse them, then delegate to + -- `super\setup` to patch the `Input` instances. + -- + -- @function setup + -- @tparam inputs [`Result`] a sequence of `Result`s + -- @tparam scope Scope the active scope + + --- handle incoming events and update `@out` (optional). + -- + -- Called once per frame if any `Input`s are dirty. Some `Input`s (like + -- `ValueInput`) have special behaviour immediately after `\setup`, that can + -- cause them to become dirty at eval-time. In this case, an eval-time tick + -- is executed. You can detect this using the `setup` parameter. + -- + -- `\tick` is called after `\setup`. `tick` is not called immediately after + -- `\setup` if no `@inputs` are dirty. Update `@out` here. + -- + -- @tparam bool setup whether this is an eval-time tick + tick: => + + --- called when the Op is destroyed (optional). + destroy: => + + --- a `Value` instance representing this Op's computed output value. + -- + -- Must be set to a `Value` instance once `\setup` finishes. Must not change + -- type, be removed or replaced outside of `\new` and `\setup`. Should have a + -- value assigned via `\set` or the `Value` constructor once `\tick` is + -- called the first time. If `@out`'s value is not initialized in `\new` + -- or `\setup`, the implementation must make sure `\tick(true)` is called at + -- least on the first eval-cycle the Op goes through, e.g. by using a + -- `ValueInput`. + -- + -- @tfield Value out + +--- implementation utilities. +-- +-- super-methods and utilities for use by implementations. +-- @section super + + --- if `type` is passed, an output stream is instantiated. + -- if `init` is passed, the stream is initialized to that Lua value. + -- it is okay not to use this and create the output stream in :setup() if the + -- type is not known at this time. + -- + -- @tparam[opt] string type the type-name for `@out` + -- @tparam[optchain] any init the initial value for `@out` + new: (type, init) => + if type + @out = Value type, init + + --- setup previous `@inputs`, if any, with the new inputs, and write them to + -- `@inputs`. The `inputs` table can be nested with string or integer keys, + -- but all leaf-entries must be `Input` instances. It must not contain loops + -- or instances of other classes. + -- + -- @tparam table inputs table of `Input`s + setup: do + do_setup = (old, cur) -> + for k, cur_val in pairs cur + old_val = old and old[k] + + -- are these inputs or nested tables? + cur_plain = cur_val and not cur_val.__class + old_plain = old_val and not old_val.__class + + if cur_plain and old_plain + -- both are tables, recurse + do_setup old_val, cur_val + elseif not (cur_plain or old_plain) + -- both are streams (or nil), setup them + cur_val\setup old_val + + (inputs) => + old_inputs = @inputs + @inputs = inputs + do_setup old_inputs, @inputs + + --- yield all `Input`s from the (potentially nested) inputs table + -- + -- @treturn iterator iterator over all `Input`s + all_inputs: do + do_yield = (table) -> + for k, v in pairs table + if v.__class + coroutine.yield v + else + do_yield v + + => coroutine.wrap -> do_yield @inputs + + --- `\unwrap` all `Input`s in `@inputs` and return a table with the same + -- shape. + -- + -- @treturn table the values of all `Input`s + unwrap_all: do + do_unwrap = (value) -> + if value.__class + value\unwrap! + else + {k, do_unwrap v for k,v in pairs value} + + => do_unwrap @inputs + + __tostring: => "<op: #{@@__name}>" + __inherited: (cls) => cls.__base.__tostring = @__tostring + +{ + :Op +} diff --git a/core/cell.moon b/core/cell.moon index 850a9db..4d46b64 100644 --- a/core/cell.moon +++ b/core/cell.moon @@ -1,12 +1,60 @@ --- ALV Cell type +---- +-- S-Expression Cell, implements the `AST` interface. +-- +-- Consists of a head expression and any number of tail expressions (both `AST` +-- nodes), a `Tag`, and optionally the internal whitespace as parsed. +-- +-- @classmod Cell import Value from require 'core.value' import op_invoke, fn_invoke from require 'core.invoke' import Tag from require 'core.tag' --- An S-Expression with a head expression and any number of tail expressions, --- an optional tag, and optionally the internal whitespace as parsed. +local RootCell + class Cell --- AST interface +--- methods +-- @section methods + + -- tag: the parsed Tag + -- children: sequence of child AST Nodes + -- white: optional sequence of whitespace segments ([0 .. #@children]) + new: (@tag=Tag.blank!, @children, @white) => + if not @white + @white = [' ' for i=1,#@children] + @white[0] = '' + + assert #@white == #@children, "mismatched whitespace length" + + --- get the head of the cell. + -- + -- @treturn AST + head: => @children[1] + + --- get the tail of the cell. + -- + -- @treturn {AST,...} + tail: => [c for c in *@children[2,]] + + __tostring: => @stringify 2 + +--- AST interface +-- +-- `Cell` implements the `AST` interface. +-- @section ast + + --- evaluate this Cell. + -- + -- `\eval`uates the head of the expression, and finds the appropriate + -- `Action` to invoke: + -- + -- - if head is an `opdef`, use `invoke.op_invoke` + -- - if head is a `fndef`, use `invoke.fn_invoke` + -- - if head is a `builtin`, unwrap it + -- + -- then calls `\eval_cell` on the `Action`. + -- + -- @tparam Scope scope the scope to evaluate in + -- @treturn Result the evaluation result eval: (scope) => head = assert @head!, "cannot evaluate expr without head" head = (head\eval scope)\const! @@ -24,19 +72,36 @@ class Cell Action\eval_cell scope, @tag, head, @tail! - -- quoting a Cell recursively quotes children, but preserves identity. This - -- means that a quoted Cell may only be 'used' once. Use :clone() otherwise. + --- quote this Cell, preserving its identity. + -- + -- Recursively quotes children, but preserves identity (i.e, shares the + -- `Tag`). A quoted Cell may only be 'used' once. If you want to `\eval` a + -- `Cell` multiple times, use `\clone`. + -- + -- @treturn Cell quoted quote: (scope) => children = [child\quote scope for child in *@children] Cell @tag, children, @white + --- create a clone with its own identity. + -- -- creates a clone of this Cell with its own identity by prepending a `parent` - -- tag and cloning all child expressoins recursively. + -- to `@tag` and cloning all child expressoins recursively. + -- + -- @treturn Cell clone clone: (parent) => tag = @tag\clone parent children = [child\clone parent for child in *@children] Cell tag, children, @white + --- stringify this Cell. + -- + -- if `depth` is passed, does not faithfully recreate the original string but + -- rather create useful debug output. + -- + -- @tparam[opt] int depth the maximum depth, defaults to infinite + -- @treturn string the exact string this Cell was parsed from, unless `@tag` + -- changed stringify: (depth=-1) => buf = '' buf ..= if depth > 0 then '' else @white[0] @@ -54,22 +119,8 @@ class Cell '(' .. tag .. buf .. ')' --- internal - -- tag: the parsed Tag - -- children: sequence of child AST Nodes - -- white: optional sequence of whitespace segments ([0 .. #@children]) - new: (@tag=Tag.blank!, @children, @white) => - if not @white - @white = [' ' for i=1,#@children] - @white[0] = '' - - assert #@white == #@children, "mismatched whitespace length" - - head: => @children[1] - tail: => [c for c in *@children[2,]] - --- static - __tostring: => @stringify 2 +--- static functions +-- @section static parse_args = (tag, parts) -> if not parts @@ -82,13 +133,26 @@ class Cell white[i/2] = parts[i+1] tag, children, white - @parse: (...) => + --- parse a Cell (for parsing with Lpeg). + -- + -- @tparam[opt] Tag tag + -- @tparam table parts + -- @treturn Cell + parse: (...) => tag, children, white = parse_args ... @ tag, children, white --- A parenthesis-less Cell (root of an ALV document). --- --- has an implicit head of 'do'. + --- parse a root Cell (for parsing with Lpeg). + -- + -- Root-Cells are at the root of an ALV document. + -- They have an implicit head of 'do' and a `[0]` tag. + -- + -- @tparam table parts + -- @treturn Cell + parse_root: (parts) => + RootCell.parse RootCell, (Tag\parse '0'), parts + +-- @type RootCell class RootCell extends Cell head: => Value.sym 'do' tail: => @children @@ -103,9 +167,6 @@ class RootCell extends Cell buf - @parse: (...) => - @__parent.parse @, (Tag\parse '0'), ... - { :Cell :RootCell diff --git a/core/config.ld b/core/config.ld new file mode 100644 index 0000000..1c07337 --- /dev/null +++ b/core/config.ld @@ -0,0 +1,4 @@ +project = 'alive copilot' +title = 'alive copilot reference' +format = 'discount' +dir = 'docs/copilot' diff --git a/core/init.moon b/core/init.moon index 7122e0f..c4e8ffd 100644 --- a/core/init.moon +++ b/core/init.moon @@ -1,3 +1,17 @@ +---- +-- `alive` public API. +-- +-- @see Value +-- @see Result +-- @see Cell +-- @see Scope +-- @see Registry +-- @see Tag +-- @field globals +-- @field parse +-- @field eval +-- +-- @module init L or= setmetatable {}, __index: => -> import Value from require 'core.value' @@ -6,7 +20,7 @@ import Scope from require 'core.scope' import Registry from require 'core.registry' import Tag from require 'core.tag' -import Cell, RootCell from require 'core.cell' +import Cell from require 'core.cell' import cell, program from require 'core.parsing' with require 'core.cycle' @@ -14,6 +28,17 @@ with require 'core.cycle' globals = Scope.from_table require 'core.builtin' +--- exports +-- @table exports +-- @tfield Value Value +-- @tfield Result Result +-- @tfield Cell Cell +-- @tfield RootCell RootCell +-- @tfield Scope Scope +-- @tfield Registry Registry +-- @tfield Tag Tag +-- @tfield Scope globals global definitons +-- @tfield parse function to turn a `string` into a root `Cell` { :Value, :Result :Cell, :RootCell diff --git a/core/invoke.moon b/core/invoke.moon index 7ff16dc..69ce881 100644 --- a/core/invoke.moon +++ b/core/invoke.moon @@ -1,3 +1,7 @@ +---- +-- Builtins for invoking `Op`s and `FnDef`s. +-- +-- @module invoke import Value from require 'core.value' import Result from require 'core.result' import Action from require 'core.base' diff --git a/core/parsing.moon b/core/parsing.moon index 8a99d89..911f473 100644 --- a/core/parsing.moon +++ b/core/parsing.moon @@ -1,5 +1,9 @@ +---- +-- Lpeg Grammar for parsing `alive` code. +-- +-- @module parsing import Value from require 'core.value' -import Cell, RootCell from require 'core.cell' +import Cell from require 'core.cell' import Tag from require 'core.tag' import R, S, P, V, C, Ct from require 'lpeg' @@ -35,7 +39,7 @@ tag = (P '[') * (digit^1 / Tag\parse) * (P ']') cell = (P '(') * tag^-1 * (V 'explist') * (P ')') / Cell\parse root = P { - (V 'explist') / RootCell\parse + (V 'explist') / Cell\parse_root :expr, :explist, :cell } @@ -46,6 +50,17 @@ cell = P { program = root * -1 +--- exports +-- @table exports +-- @tfield pattern comment +-- @tfield pattern space +-- @tfield pattern atom +-- @tfield pattern expr +-- @tfield pattern explist +-- @tfield pattern explist +-- @tfield pattern cell +-- @tfield pattern root +-- @tfield pattern program the main parsing entrypoint { :comment :space diff --git a/core/pattern.moon b/core/pattern.moon deleted file mode 100644 index 13497af..0000000 --- a/core/pattern.moon +++ /dev/null @@ -1,65 +0,0 @@ -unpack or= table.unpack - -class Pattern - new: (opts) => - if 'string' == type opts - splat, const, type, opt = opts\match '^(%*?)(=?)([%w%-%_%/]+)(%??)$' - assert type, "couldn't parse type pattern '#{opts}'" - opts = { - :type - splat: splat == '*' - const: const == '=' - opt: opt == '?' - } - - @type = opts.type - @const = opts.const - @opt = opts.opt - @splat = opts.splat - - matches: (result) => - return false unless result - - if @const - return false unless result\is_const! - - if not result.value - return @type == 'nil' - - return true if @type == 'any' - - result.value.type == @type - - match: (results) => - if @splat - matched = while @matches results[1] - table.remove results, 1 - - assert @opt or #matched > 0, "expected at least one argument for spread" - matched - else - matches = @matches results[1] - assert @opt or matches, "couldn't match argument #{results[1]} as #{@}" - if matches then table.remove results, 1 - - __tostring: => - str = @type - str = '*' .. str if @splat - str = '=' .. str if @const - str = str .. '?' if @opt - str - -match = (pattern, results) -> - patterns = while pattern - pat, rest = pattern\match '^([^ ]+) (.*)$' - pat = pattern unless pat - pattern = rest - Pattern pat - values = [p\match results for p in *patterns] - assert #results == 0, "#{#results} extra arguments given!" - values - -{ - :Pattern - :match -} diff --git a/core/registry.moon b/core/registry.moon index fca7e5d..edce9bd 100644 --- a/core/registry.moon +++ b/core/registry.moon @@ -1,14 +1,12 @@ +---- +-- `Tag` Registry. +-- +-- @classmod Registry + import Value from require 'core.value' import Result from require 'core.result' class Registry - new: () => - @map = {} - @io = {} - - @tick = 0 - @kr = Result value: Value.bool true - -- methods for Tag last: (index) => @last_map[index] @@ -24,8 +22,23 @@ class Registry active: -> assert Registry.active_registry, "no active Registry!" --- public methods + next_tag: => #@map + 1 + +--- methods +-- @section methods + --- create an instance. + new: => + @map = {} + @io = {} + + @tick = 0 + @kr = Result value: Value.bool true + + --- wrap a function with an eval-cycle. + -- + -- @tparam function fn + -- @treturn function `fn` wrapped with eval-cycle logic wrap_eval: (fn) => (...) -> @grab! @last_map, @map, @pending = @map, {}, {} @@ -47,6 +60,10 @@ class Registry @release! + --- wrap a function with a tick. + -- + -- @tparam function fn + -- @treturn function `fn` wrapped with tick logic wrap_tick: (fn) => (...) -> @grab! @tick += 1 @@ -66,8 +83,6 @@ class Registry assert @ == @@active_registry, "not the active registry!" @@active_registry, @prev = @prev, nil - next_tag: => #@map + 1 - class SimpleRegistry extends Registry new: => @cnt = 1 diff --git a/core/result.moon b/core/result.moon index 7fda8e5..9f0bce7 100644 --- a/core/result.moon +++ b/core/result.moon @@ -1,16 +1,26 @@ +---- +-- Result of evaluating an expression. +-- +-- Contains (all optional): +-- +-- - `@value`: a `Value` +-- - `@op`: an `Op` (to update) +-- - `@children`: a lsit of child `Results` (from subexpressions) +-- - `@side_inputs`: cached list of all `Inputs` affecting any `Op` in this +-- subtree +-- +-- `Result`s form a tree that controls execution order and message passing +-- between `Op`s. +-- +-- @classmod Result import base from require 'core.cycle' --- Result of evaluating an expression --- carries (all optional): --- - a Value --- - an Op (to update) --- - children (results of subexpressions that were evaluated) --- - cached list of all Dispatchers affecting all Ops in the subtree --- --- Results form a tree that controls execution order and message passing --- between Ops. class Result - -- params: table with optional keys op, value, children +--- methods +-- @section methods + + --- create a new Result. + -- @param params table with optional keys op, value, children. default: {} new: (params={}) => @value = params.value @op = params.op @@ -28,35 +38,38 @@ class Result if input.impure or not is_child[input.stream] @side_inputs[input.stream] = input + --- return whether this Result's value is const. is_const: => not next @side_inputs - -- asserts value-constness and returns the value + --- assert value-constness and returns the value. + -- @tparam[opt] string msg the error message to throw const: (msg) => assert not (next @side_inputs), msg or "eval-time const expected" @value - -- asserts a value exists and returns its type + --- assert this result has a value, returns its type. type: => assert @value, "Result with value expected" @value.type - -- create a value-copy of this result that has the same impulses but without - -- affecting the original's update logic + --- create a copy of this result with value-copy semantics. + -- the copy has the same @value and @side_inputs, but will not update + -- anything on \tick. make_ref: => with Result value: @value .side_inputs = @side_inputs - -- tick all IO instances that are effecting this (sub) tree - -- should be called once per frame on the root, right before tick + --- tick all IO instances that are effecting this (sub)tree. + -- should be called once per frame on the root, right before tick. tick_io: => for stream, input in pairs @side_inputs if input.__class == base.IOInput io = input! io\tick! - -- in depth-first order, tick all Ops who have dirty Stream inputs or impulses + --- in depth-first order, tick all Ops which have dirty Inputs. -- - -- short-circuits if there are no dirty Streams in the entire subtree + -- short-circuits if there are no dirty Inputs in the entire subtree tick: => any_dirty = false for stream, input in pairs @side_inputs @@ -64,15 +77,15 @@ class Result any_dirty = true break - -- early-out if no streams are dirty in this whole subtree + -- early-out if no Inputs are dirty in this whole subtree return unless any_dirty for child in *@children child\tick! if @op - -- we have to check self_dirty here, because streams from child - -- expressions might have changed + -- we have to check self_dirty here, because Inputs from children may + -- have become dirty due to \tick self_dirty = false for stream in @op\all_inputs! if stream\dirty! @@ -84,7 +97,6 @@ class Result @op\tick! --- static __tostring: => buf = "<result=#{@value}" buf ..= " #{@op}" if @op diff --git a/core/scope.moon b/core/scope.moon index 4ed95d1..c61a446 100644 --- a/core/scope.moon +++ b/core/scope.moon @@ -1,14 +1,36 @@ +---- +-- Mapping from `sym`s to `Result`s. +-- +-- @classmod Scope import Value from require 'core.value' import Result from require 'core.result' class Scope +--- methods +-- @section methods + + --- create an instance. + -- + -- @tparam[opt] Scope parent a parent this scope inherits definitions from + -- @tparam[opt] Scope dynamic_parent a parent scope that should be checked for + -- dynamic definitions new: (@parent, @dynamic_parent) => @values = {} + --- set a Lua value in the scope. + -- + -- wraps `val` in a `Value` and `Result` before calling `\set`. + -- + -- @tparam string key + -- @tparam any val set_raw: (key, val) => value = Value.wrap val, key @values[key] = Result :value + --- set a symbol to a `Result`. + -- + -- @tparam string key + -- @tparam Result val set: (key, val) => L\trace "setting #{key} = #{val} in #{@}" assert val.__class == Result, "expected #{key}=#{val} to be Result" @@ -20,6 +42,11 @@ class Scope parent or= @parent return parent and L\push parent\get, key + --- resolve a key in this Scope. + -- + -- @tparam string key the key to resolve + -- @tparam[opt] string prefix a prefix (for internal use with nested scopes) + -- @treturn ?Result the value of the definition that was found, or `nil` get: (key, prefix='') => L\debug "checking for #{key} in #{@}" if val = @values[key] @@ -35,11 +62,26 @@ class Scope assert child and child.value.type == 'scope', "#{start} is not a scope (looking for #{key})" child.value\unwrap!\get rest, "#{prefix}#{start}/" + --- copy definitions from another scope. + -- + -- copies all definitions from `other`. Does not copy inherited definitions. + -- + -- @tparam Scope other use: (other) => L\trace "using defs from #{other} in #{@}" for k, v in pairs other.values @values[k] = v +--- static functions +-- @section static + + --- converts a Lua table to a Scope. + -- + -- `tbl` may contain more tables (or `Scope`s). + -- Uses `Value``.wrap` on the values recursively. + -- + -- @tparam table tbl the table to convert + -- @treturn Scope from_table: (tbl) -> with Scope! for k, v in pairs tbl @@ -64,4 +106,6 @@ class Scope buf ..= ">" buf -:Scope +{ + :Scope +} diff --git a/core/tag.moon b/core/tag.moon index f38d36a..5e6de24 100644 --- a/core/tag.moon +++ b/core/tag.moon @@ -1,3 +1,12 @@ +---- +-- Identity provider for `Cell`s and `Action`s. +-- +-- Tags are one of: +-- - 'blank' (`[?]`, to be auto-assigned by the Copilot) +-- - literal (`[1]`) +-- - cloned (`[X.Y]`, obtained by cloning Y with parent X) +-- +-- @classmod Tag import Registry from require 'core.registry' local ClonedTag @@ -10,30 +19,47 @@ class DummyReg dummy = DummyReg! class Tag - -- obtain the value that was previously registered (using keep or replace) for - -- this tag +--- methods +-- @section methods + + --- obtain the registered value of the last eval-cycle. + -- + -- Obtain the value that was previously registered (using `\keep` or + -- `\replace`) for this tag on the last eval-cylce. + -- + -- @treturn ?any last: => if index = @index! Registry.active!\last index - -- assert that `expr` is the value that was previously registered for this - -- tag, and keep it for the current eval cycle. - -- fails for blank tags. + --- keep using a the value from the last eval-cycle. + -- + -- Assert that `expr` is the value that was previously registered for this + -- tag, and keep it for the current eval cycle. Fails for blank tags. + -- + -- @tparam any expr the value to register keep: (expr) => index = assert @index! assert expr == Registry.active!\last index Registry.active!\replace index, expr - -- register `expr` for this tag for the current eval cycle. - -- registers blank tags. + --- register `expr` for this tag for the current eval cycle. + -- + -- Will mark blank tags for auto-assignment at the end of the eval cycle. + -- + -- @tparam any expr the value to register replace: (expr) => if index = @index! Registry.active!\replace index, expr else Registry.active!\init @, expr - -- create a copy of this tag scoped to a `parent` tag. - -- registers blank tags. + --- create a copy of this tag scoped to a `parent` tag. + -- + -- Will mark blank tags for auto-assignment at the end of the eval cycle. + -- + -- @tparam Tag parent the parent tag + -- @treturn Tag the cloned tag clone: (parent) => -- ensure this tag is registered for the current eval cycle, -- even if it is blank and has no associated value @@ -45,6 +71,9 @@ class Tag assert parent, "need parent to clone!" ClonedTag @, parent + stringify: => if @value then "[#{@value}]" else '' + __tostring: => if @value then "#{@value}" else '?' + -- internal new: (@value) => @@ -56,13 +85,19 @@ class Tag assert not @value, "#{@} is not blank" @value = value --- static +--- static functions +-- @section static - @blank: -> Tag! - @parse: (num) => @ tonumber num + --- create a blank `Tag`. + -- + -- @treturn Tag + blank: -> Tag! - stringify: => if @value then "[#{@value}]" else '' - __tostring: => if @value then "#{@value}" else '?' + --- parse a `Tag` (for Lpeg parsing). + -- + -- @tparam string num the number-string + -- @treturn Tag + parse: (num) => @ tonumber num class ClonedTag extends Tag new: (@original, @parent) => diff --git a/core/value.moon b/core/value.moon index 26771ea..c6ed42d 100644 --- a/core/value.moon +++ b/core/value.moon @@ -1,3 +1,7 @@ +---- +-- `alive` Value(stream), implements the `AST` inteface. +-- +-- @classmod Value import Result from require 'core.result' import scope, base, registry from require 'core.cycle' @@ -7,26 +11,63 @@ ancestor = (klass) -> klass = klass.__parent klass --- ALV Type wrapper class Value - -- @type - type name. - -- builtin types: * literals: sym, num, bool - -- * scope, opdef, fndef, builtin - -- @value - Lua value - access through :unwrap() +--- methods +-- @section methods + + --- construct a new Value. + -- + -- @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) => - @updated = nil + --- return whether this Value was changed in the current tick. + -- + -- @treturn bool dirty: => @updated == registry.Registry.active!.tick + --- update this Value. + -- + -- Marks this Value as dirty for the remainder of the current tick. set: (@value) => @updated = registry.Registry.active!.tick - -- unwrap to the Lua type - -- asserts @type == type, msg if given + --- 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 @value unwrap: (type, msg) => assert type == @type, msg or "#{@} is not a #{type}" if type @value --- AST interface + --- alias for `\unwrap`. + __call: (...) => @unwrap ... + + --- compare two values. + -- + -- Compares two `Value`s by comparing their types and their Lua values. + __eq: (other) => other.type == @type and other.value == @value + + __tostring: => + value = if 'table' == (type @value) and rawget @value, '__base' then @value.__name else @value + "<#{@@__name} #{@type}: #{value}>" + +--- AST interface +-- +-- `Value` 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 Result the evaluation result eval: (scope) => switch @type when 'num', 'str' @@ -36,24 +77,33 @@ class Value else error "cannot evaluate #{@}" + --- quote this literal constant. + -- + -- @treturn Value self quote: => @ + --- stringify this literal constant. + -- + -- Throws an error if `@raw` is not set. + -- + -- @treturn string the exact string this Value was parsed from stringify: => assert @raw, "stringifying Value that wasn't parsed" + --- clone this literal constant. + -- + -- @treturn Value self clone: (prefix) => @ - -- in case of doubt: - -- clone: (prefix) => Value @type, @value, @raw --- static - __tostring: => - value = if 'table' == (type @value) and rawget @value, '__base' then @value.__name else @value - "<#{@@__name} #{@type}: #{value}>" - __call: (...) => @unwrap ... - __eq: (other) => other.type == @type and other.value == @value - - -- wrap a Lua type - @wrap: (val, name='(unknown)') -> +--- static functions +-- @section static + --- 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) + wrap: (val, name='(unknown)') -> typ = switch type val when 'number' then 'num' when 'string' then 'str' @@ -83,16 +133,31 @@ class Value Value typ, val unescape = (str) -> str\gsub '\\([\'"\\])', '%1' - @parse: (type, sep) => + --- 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 - @num: (num) -> Value 'num', num, tostring num - @str: (str) -> Value 'str', str, "'#{str}'" - @sym: (sym) -> Value 'sym', sym, sym - @bool: (bool) -> Value 'bool', bool, tostring bool + --- create a constant number. + -- @tparam number num the number + num: (num) -> Value 'num', num, tostring num + + --- create a constant string. + -- @tparam string str the string + str: (str) -> Value 'str', str, "'#{str}'" + + --- create a constant symbol. + -- @tparam string sym the symbol + sym: (sym) -> Value 'sym', sym, sym + + --- create a constant boolean. + -- @tparam boolean bool the boolean + bool: (bool) -> Value 'bool', bool, tostring bool { :Value diff --git a/core/version.moon b/core/version.moon index 029dd54..c33a8a1 100644 --- a/core/version.moon +++ b/core/version.moon @@ -1,5 +1,15 @@ +---- +-- `alive` source code version information +-- +-- @module version + +--- exports +-- @table exports +-- @tfield string tag the last versions git tag +-- @tfield string rev_short the short git revision hash +-- @tfield string rev_long the full git revision hash { tag: "v0.0" - rev_short: "4742960" - rev_long: "4742960c455251f306bf271d50f69724b8beba04" + rev_short: "891c138" + rev_long: "891c138c8a8a4c9269f3eb70f424b5c1ea3898b0" } |
