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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
----
-- `Tag` Registry.
--
-- @classmod Registry
import Result from require 'core.result'
import Error from require 'core.error'
class Registry
--- internals for `Tag`
-- @section internals
--- lookup the last registration.
--
-- @tparam number|string index the registration index
-- @treturn any
last: (index) => @last_map[index]
--- set the current registration.
--
-- @tparam string\number index the registration index
-- @tparam any expr the registration value
-- @tparam[default=false] boolean ignore_dup ignore duplicate registrations
register: (index, expr, ignore_dup=false) =>
L\trace "reg: setting #{index} to #{expr}"
if not ignore_dup and @map[index]
error Error 'tag', "duplicate tags [#{index}]!"
@map[index] = expr
--- request identity and registration for blank tag.
--
-- @tparam Tag tag the blank tag
-- @tparam any expr the registration value
init: (tag, expr) =>
L\trace "reg: init pending to #{expr}"
table.insert @pending, { :tag, :expr }
--- members
-- @section members
--- begin an evaluation cycle.
--
-- Begin an evaltime cycle (and tick).
-- Set the active Registry and clear out pending registrations.
--
-- All calls go `begin_eval` must be matched with either a call to
-- `end_eval` or `rollback_eval`.
begin_eval: =>
@latest_map = @last_map
@begin_tick!
@map, @pending = {}, {}
--- end an evaluation cycle.
--
-- Register all pending `Tag`s and destroy all orphaned registrations.
-- Unset the active Registry.
end_eval: =>
for tag, val in pairs @last_map
val\destroy! unless @map[tag]
for { :tag, :expr } in *@pending
-- tag was solved by another pending registration
-- (e.g. first [A] is solved, then [5.A] is solved)
continue if tag\index!
next_tag = #@map + 1
L\trace "assigned new tag #{next_tag} to #{tag} #{expr}"
tag\set next_tag
@map[tag\index!] = expr
@last_map = @map
@end_tick!
--- abort an evaluation cycle.
--
-- Unset the active Registry.
rollback_eval: =>
@end_tick!
--- begin a run cycle.
--
-- Increment the tick index and set the active Registry.
begin_tick: =>
@grab!
@next_tick!
--- end a run cycle.
--
-- Unset the active Registry.
end_tick: =>
@release!
--- manually increment the tick index (for testing).
next_tick: =>
@tick += 1
--- set the active Registry.
grab: =>
assert not @prev, "already have a previous registry? #{@prev}"
@prev, Registry.active_registry = Registry.active_registry, @
--- unset the active Registry.
release: =>
assert @ == Registry.active_registry, "not the active registry!"
Registry.active_registry, @prev = @prev, nil
--- static functions
-- @section static
--- create a new Registry.
-- @classmethod
new: =>
@last_map, @map = {}, {}
@tick = 0
--- get the active Registry.
--
-- Raises an erro when there is no active Regsitry.
--
-- @treturn Registry
@active: -> assert Registry.active_registry, "no active Registry!"
class SimpleRegistry extends Registry
new: =>
@cnt = 1
@tick = 0
next_tick: =>
@tick += 1
init: (tag, expr) =>
tag\set @cnt
@cnt += 1
last: (index) =>
register: (index, expr) =>
wrap: (fn) => (...) ->
@grab!
with fn ...
@release!
{
:Registry
:SimpleRegistry
}
|