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
|
----
-- `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) => if tag = @last_map[index] then tag.builtin
--- set the current registration.
--
-- @tparam Tag tag the Tag to register
-- @treturn boolean whether the tag is new
register: (tag) =>
index = tag\index!
if index and not @map[index] or ignore_dup
L\trace "reg: registering #{index} (#{ignore_dup})"
@map[index] = tag
nil
else
if index
L\warn "duplicate tag [#{index}], reassigning repeated occurrence"
tag\set nil
table.insert @pending, tag
L\trace "reg: registered pending"
true
--- members
-- @section members
--- begin an evaluation cycle.
--
-- All calls go `begin_eval` must be matched with a call to
-- `assign_tags` followed by either `end_eval` or `rollback_eval`.
begin_eval: =>
assert not @map, "unfinished evaluation cycle"
@map, @pending = {}, {}
--- assign unique tags to pending registrations.
--
-- Assigns a unique number to each tag added in this evaluation cycle.
assign_tags: =>
next_tag = 1
for tag in *@pending
if not tag\index!
while @map[next_tag]
next_tag += 1
L\trace "assigned new tag #{next_tag} to #{tag}"
tag\set next_tag
@map[tag\index!] = tag
--- abort an evaluation cycle.
--
-- Destroys all pending registrations
rollback_eval: =>
assert @map, "no eval cycle to abort"
for tag in *@pending
if builtin = tag.builtin
builtin\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: =>
-- destroy old registrations
for index, tag in pairs @last_map
if builtin = tag.builtin
builtin\destroy!
-- mark all regisrations complete
for index, tag in pairs @map
tag.pending = nil
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 index, tag in pairs @last_map
if builtin = tag.builtin
builtin\destroy!
@last_map = {}
--- the module associated with this Registry
-- @tfield Module module
--- static functions
-- @section static
--- create a new Registry.
-- @classmethod
-- @tparam Module module
new: (@module) =>
@last_map = {}
{
:Registry
}
|