diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-03-03 10:52:39 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-03-05 10:36:22 +0000 |
| commit | 13fdf952138454184f42f50e15c5c4974b2c7eed (patch) | |
| tree | 56405051078b17766695cf4b1c659c8fd68d3035 | |
| parent | document more interfaces (diff) | |
| download | alive-0.0.tar.gz alive-0.0.zip | |
refactoring cyclic requiresv0.0
| -rw-r--r-- | core/base.moon | 3 | ||||
| -rw-r--r-- | core/cycle.moon | 26 | ||||
| -rw-r--r-- | core/init.moon | 8 | ||||
| -rw-r--r-- | core/registry.moon | 3 | ||||
| -rw-r--r-- | core/result.moon | 97 | ||||
| -rw-r--r-- | core/scope.moon | 3 | ||||
| -rw-r--r-- | core/value.moon | 118 | ||||
| -rw-r--r-- | spec/core/cell_spec.moon | 3 | ||||
| -rw-r--r-- | spec/core/parsing_spec.moon | 3 | ||||
| -rw-r--r-- | spec/core/pattern_spec.moon | 2 |
10 files changed, 149 insertions, 117 deletions
diff --git a/core/base.moon b/core/base.moon index 8bd6271..7962545 100644 --- a/core/base.moon +++ b/core/base.moon @@ -1,5 +1,6 @@ -- base definitions for extensions -import Value, Result from require 'core.value' +import Value from require 'core.value' +import Result from require 'core.result' import match from require 'core.pattern' unpack or= table.unpack diff --git a/core/cycle.moon b/core/cycle.moon new file mode 100644 index 0000000..57d4a75 --- /dev/null +++ b/core/cycle.moon @@ -0,0 +1,26 @@ +-- late-resolve cyclic dependencies +-- +-- this module provides a proxy for resolving values from modules which cannot +-- be loaded due to cyclic dependencies. Instead of +-- +-- import Something from require 'core.somewhere' +-- Something ... +-- +-- use +-- +-- import somewhere from require 'core.cycle' +-- somewhere.Something ... +-- +-- Make sure cycle:load() is called before you access or dereference +-- `somewhere`. + +load = => + for name, module in pairs @ + for k, v in pairs require "core.#{name}" + module[k] = v + +setmetatable {}, __index: (key) => + return load if key == 'load' + + with v = {} + rawset @, key, v diff --git a/core/init.moon b/core/init.moon index 73f63f9..7122e0f 100644 --- a/core/init.moon +++ b/core/init.moon @@ -1,15 +1,17 @@ L or= setmetatable {}, __index: => -> -import Value, Result, load_ from require 'core.value' +import Value from require 'core.value' +import Result from require 'core.result' import Scope from require 'core.scope' -load_! - import Registry from require 'core.registry' import Tag from require 'core.tag' import Cell, RootCell from require 'core.cell' import cell, program from require 'core.parsing' +with require 'core.cycle' + \load! + globals = Scope.from_table require 'core.builtin' { diff --git a/core/registry.moon b/core/registry.moon index ebe429c..fca7e5d 100644 --- a/core/registry.moon +++ b/core/registry.moon @@ -1,4 +1,5 @@ -import Result, Value from require 'core.value' +import Value from require 'core.value' +import Result from require 'core.result' class Registry new: () => diff --git a/core/result.moon b/core/result.moon new file mode 100644 index 0000000..7fda8e5 --- /dev/null +++ b/core/result.moon @@ -0,0 +1,97 @@ +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 + new: (params={}) => + @value = params.value + @op = params.op + @children = params.children or {} + + @side_inputs, is_child = {}, {} + for child in *@children + for s, d in pairs child.side_inputs + @side_inputs[s] = d + if child.value + is_child[child.value] = true + + if @op + for input in @op\all_inputs! + if input.impure or not is_child[input.stream] + @side_inputs[input.stream] = input + + is_const: => not next @side_inputs + + -- asserts value-constness and returns the value + const: (msg) => + assert not (next @side_inputs), msg or "eval-time const expected" + @value + + -- asserts a value exists and 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 + 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_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 + -- + -- short-circuits if there are no dirty Streams in the entire subtree + tick: => + any_dirty = false + for stream, input in pairs @side_inputs + if input\dirty! + any_dirty = true + break + + -- early-out if no streams 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 + self_dirty = false + for stream in @op\all_inputs! + if stream\dirty! + self_dirty = true + break + + L\trace "#{@op} is #{if self_dirty then 'dirty' else 'clean'}" + return unless self_dirty + + @op\tick! + +-- static + __tostring: => + buf = "<result=#{@value}" + buf ..= " #{@op}" if @op + buf ..= " (#{#@children} children)" if #@children > 0 + buf ..= ">" + buf + +{ + :Result +} diff --git a/core/scope.moon b/core/scope.moon index 5e73f64..4ed95d1 100644 --- a/core/scope.moon +++ b/core/scope.moon @@ -1,4 +1,5 @@ -import Result, Value from require 'core.value' +import Value from require 'core.value' +import Result from require 'core.result' class Scope new: (@parent, @dynamic_parent) => diff --git a/core/value.moon b/core/value.moon index b01b28f..26771ea 100644 --- a/core/value.moon +++ b/core/value.moon @@ -1,9 +1,5 @@ --- ALV Value types -local Scope, Registry, Op, Action, IOInput, FnDef -load_ = -> - import Scope from require 'core.scope' - import Registry from require 'core.registry' - import Op, Action, IOInput, FnDef from require 'core.base' +import Result from require 'core.result' +import scope, base, registry from require 'core.cycle' ancestor = (klass) -> assert klass, "cant find the ancestor of nil" @@ -11,98 +7,6 @@ ancestor = (klass) -> klass = klass.__parent klass --- 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 - new: (params={}) => - @value = params.value - @op = params.op - @children = params.children or {} - - @side_inputs, is_child = {}, {} - for child in *@children - for s, d in pairs child.side_inputs - @side_inputs[s] = d - if child.value - is_child[child.value] = true - - if @op - for input in @op\all_inputs! - if input.impure or not is_child[input.stream] - @side_inputs[input.stream] = input - - is_const: => not next @side_inputs - - -- asserts value-constness and returns the value - const: (msg) => - assert not (next @side_inputs), msg or "eval-time const expected" - @value - - -- asserts a value exists and 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 - 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_io: => - for stream, input in pairs @side_inputs - if input.__class == IOInput - io = input! - io\tick! - - -- in depth-first order, tick all Ops who have dirty Stream inputs or impulses - -- - -- short-circuits if there are no dirty Streams in the entire subtree - tick: => - any_dirty = false - for stream, input in pairs @side_inputs - if input\dirty! - any_dirty = true - break - - -- early-out if no streams 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 - self_dirty = false - for stream in @op\all_inputs! - if stream\dirty! - self_dirty = true - break - - L\trace "#{@op} is #{if self_dirty then 'dirty' else 'clean'}" - return unless self_dirty - - @op\tick! - --- static - __tostring: => - buf = "<result=#{@value}" - buf ..= " #{@op}" if @op - buf ..= " (#{#@children} children)" if #@children > 0 - buf ..= ">" - buf - -- ALV Type wrapper class Value -- @type - type name. @@ -112,9 +16,9 @@ class Value new: (@type, @value, @raw) => @updated = nil - dirty: => @updated == Registry.active!.tick + dirty: => @updated == registry.Registry.active!.tick - set: (@value) => @updated = Registry.active!.tick + set: (@value) => @updated = registry.Registry.active!.tick -- unwrap to the Lua type -- asserts @type == type, msg if given @@ -149,29 +53,30 @@ class Value -- wrap a Lua type @wrap: (val, name='(unknown)') -> + typ = switch type val when 'number' then 'num' when 'string' then 'str' when 'table' - if base = rawget val, '__base' + if rawget val, '__base' -- a class switch ancestor val - when Op then 'opdef' - when Action then 'builtin' + when base.Op then 'opdef' + when base.Action then 'builtin' else error "#{name}: cannot wrap class '#{val.__name}'" elseif val.__class -- an instance switch ancestor val.__class - when Scope then 'scope' - when FnDef then 'fndef' + when scope.Scope then 'scope' + when base.FnDef then 'fndef' when Value return val else error "#{name}: cannot wrap '#{val.__class.__name}' instance" else -- plain table - return Value 'scope', Scope.from_table val + return Value 'scope', scope.Scope.from_table val else error "#{name}: cannot wrap Lua type '#{type val}'" @@ -190,7 +95,6 @@ class Value @bool: (bool) -> Value 'bool', bool, tostring bool { - :Result :Value :load_ } diff --git a/spec/core/cell_spec.moon b/spec/core/cell_spec.moon index fcf9861..78861ab 100644 --- a/spec/core/cell_spec.moon +++ b/spec/core/cell_spec.moon @@ -1,5 +1,4 @@ -import Cell, RootCell, Value, Scope, globals from require 'core' -import Registry from require 'core.registry' +import Cell, RootCell, Value, Scope, Registry, globals from require 'core' import Logger from require 'logger' Logger.init 'silent' diff --git a/spec/core/parsing_spec.moon b/spec/core/parsing_spec.moon index ddf760f..7f4bb06 100644 --- a/spec/core/parsing_spec.moon +++ b/spec/core/parsing_spec.moon @@ -1,4 +1,5 @@ -import space, atom, expr, explist, cell, program, comment from require 'core.parsing' +import space, atom, expr, explist, cell, program, comment + from require 'core.parsing' import Value from require 'core' import Logger from require 'logger' Logger.init 'silent' diff --git a/spec/core/pattern_spec.moon b/spec/core/pattern_spec.moon index c218dd2..64dbef2 100644 --- a/spec/core/pattern_spec.moon +++ b/spec/core/pattern_spec.moon @@ -1,5 +1,5 @@ import Pattern, match from require 'core.pattern' -import Result, Value from require 'core.value' +import Result, Value from require 'core' -- wrap in non-const result wrap = (value) -> |
