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
|
----
-- `Tag` Registry.
--
-- @classmod Registry
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 Tag tag the Tag to register
-- @tparam any expr the registration value
-- @tparam[default=false] boolean ignore_dup ignore duplicate registrations
register: (tag, expr, ignore_dup=false) =>
index = tag\index!
if index
if not @map[index] or ignore_dup
L\trace "reg: setting #{index} to #{expr}"
@map[index] = expr
else
L\warn "duplicate tag [#{index}], reassigning repeated occurrence"
tag\set nil
table.insert @pending, { :tag, :expr }
else
L\trace "reg: init #{tag} to #{expr}"
table.insert @pending, { :tag, :expr }
--- members
-- @section members
--- begin an evaluation cycle.
--
-- All calls go `begin_eval` must be matched with either a call to
-- `end_eval` or `rollback_eval`.
begin_eval: =>
assert not @map, "unfinished evaluation cycle"
@map, @pending = {}, {}
--- abort an evaluation cycle.
rollback_eval: =>
assert @map, "no eval cycle to abort"
for { :tag, :expr } in *@pending
expr\destroy!
@map, @pending = nil, nil
--- end an evaluation cycle.
--
-- Register all pending `Tag`s and destroy all orphaned registrations.
-- @treturn bool whether any changes to the AST were made
end_eval: =>
for tag, val in pairs @last_map
val\destroy!
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
dirty = #@pending > 0
@last_map, @map, @pending = @map, nil, nil
dirty
--- destroy this Registry and all associated Registrations.
-- needs to be called *after* `:eval`.
destroy: =>
assert not @tag, "unfinished evaluation cycle"
for tag, val in pairs @last_map
val\destroy!
@last_map = {}
--- static functions
-- @section static
--- create a new Registry.
-- @classmethod
new: =>
@last_map = {}
class SimpleRegistry extends Registry
new: =>
@cnt = 1
init: (tag, expr) =>
tag\set @cnt
@cnt += 1
last: (index) =>
register: (index, expr) =>
{
:Registry
:SimpleRegistry
}
|