1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
----
-- 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'
import Scope from require 'core.scope'
import Error from require 'core.error'
get_name = (value, raw) ->
meta = if value.meta then value.meta.name
locl = if raw and raw.type == 'sym' then raw!
if locl
if meta and meta != locl
"'#{meta}' (local '#{locl}')"
else
"'#{locl}'"
else if meta
"'#{meta}'"
else
"(unnamed)"
--- `Action` implementation that invokes an `Op`.
--
-- @type op_invoke
class op_invoke extends Action
--- `Action:setup` implementation.
--
-- `Op:fork`s the `prev`'s `Op` instance if given. Creates a new instance
-- otherwise.
setup: (prev) =>
if prev
@op = prev.op\fork!
else
def = @head\unwrap 'opdef', "cant op-invoke #{@head}"
@op = def!
--- `Action:destroy` implementation.
--
-- calls `op`:@{Op:destroy|destroy}.
destroy: => @op\destroy!
--- evaluate an `Op` invocation.
--
-- `AST:eval`s the tail, and passes the result to `op`:@{Op:setup|setup}. Then
-- checks if any of `op`:@{Op:all_inputs|all_inputs} are @{Input:dirty|dirty},
-- and if so, calls `op`:@{Op:tick|tick}.
--
-- The `Result` contains `op`, `Op.value` and all the `Result`s from the tail.
--
-- @tparam Scope scope the active scope
-- @tparam {AST,...} tail the arguments to this expression
-- @treturn Result
eval: (scope, tail) =>
children = [L\push expr\eval, scope for expr in *tail]
frame = "invoking op #{get_name @head, @cell\head!} at [#{@tag}]"
Error.wrap frame, @op\setup, [result for result in *children], scope
any_dirty = false
for input in @op\all_inputs!
if input\dirty!
any_dirty = true
break
if any_dirty
@op\tick true
for input in @op\all_inputs!
input\finish_setup!
Result :children, value: @op.out, op: @op
--- The `Op` instance.
--
-- @tfield Op op
--- `Action` implementation that invokes a `FnDef`.
--
-- @type fn_invoke
class fn_invoke extends Action
--- evaluate a user-function invocation.
--
-- Creates a new `Scope` that inherits from `FnDef.scope` and has
-- `outer_scope` as an additional parent for dynamic symbol resolution.
-- Then `AST:eval`s the tail in `outer_scope`, and defines the results to the
-- names in `FnDef.params` in the newly created scope. Lastly, `AST:clone`s
-- `FnDef.body` with the prefix `Action.tag`, and `AST:eval`s it in the newly
-- created `Scope`.
--
-- The `Result` contains the `Value` from the cloned AST, and its children are
-- all the `Result`s from evaluating the tail as well as the cloned `AST`s.
--
-- @tparam Scope outer_scope the active scope
-- @tparam {AST,...} tail the arguments to this expression
-- @treturn Result the result of this evaluation
eval: (outer_scope, tail) =>
name = get_name @head, @cell\head!
frame = "invoking function #{name} at [#{@tag}]"
{ :params, :body, :scope } = @head\unwrap 'fndef', "cant fn-invoke #{@head}"
if #params != #tail
error with Error 'argument', "expected #{#params} arguments, found #{#tail}"
\add_frame frame
fn_scope = Scope scope, outer_scope
children = for i=1,#params
name = params[i]\unwrap 'sym'
with L\push tail[i]\eval, outer_scope
fn_scope\set name, \make_ref!
clone = body\clone @tag
result = Error.wrap frame, clone\eval, fn_scope
table.insert children, result
Result :children, value: result.value
{
:op_invoke, :fn_invoke
}
|