-
Notifications
You must be signed in to change notification settings - Fork 0
/
mix.lua
155 lines (113 loc) · 3.13 KB
/
mix.lua
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
146
147
148
149
150
151
152
153
154
155
-- Behaviors:
-- A Behavior is a table containing a list of objects ("all"),
-- a table of methods ("methods"), and functions that are called
-- when an object becomes or resigns the behavior.
function behavior()
local b = { all = table.new(),
methods = table.new(),
on_become = function(obj, behavior) end,
on_resign = function(obj, behavior) end }
return b
end
-- Adding a few utilities to table
table.__index = table
function table.new()
local t = {}
setmetatable(t, table)
return t
end
function table.map(list, fn)
local new_list = {}
setmetatable(new_list, table)
for k,v in pairs(list) do
new_list[k] = fn(v, k)
end
return new_list
end
function table.delete(haystack, needle)
local idx = table.find(haystack, needle)
if idx then table.remove(haystack, idx) end
return haystack
end
function table.reject(haystack, filter)
local t = {}
setmetatable(t, table)
for k,v in ipairs(haystack) do
if not filter(v, k) then table.insert(t, v) end
end
return t
end
function table.find(haystack, needle)
for k, v in pairs(haystack) do
if v == needle then
return k
end
end
return nil
end
function table.select(haystack, filter)
local t = {}
setmetatable(t, table)
for k,v in pairs(haystack) do
if filter(v, k) then t:insert(v) end
end
return t
end
-- A couple basic behaviors:
-- Object is the basic behavior everything starts out with, with
-- functions to handle becoming and resigning behaviors.
Object = behavior()
-- Object's methods:
-- Any object can become a behavior, or it can
-- resign a behavior.
function Object.methods.become(obj, behavior)
local m = getmetatable(obj)
table.insert(m.behaviors, 1, behavior)
behavior.all[obj] = obj
behavior.on_become(obj, behavior)
return obj
end
function Object.methods.resign(obj, behavior)
local m = getmetatable(obj)
behavior.on_resign(obj, behavior)
behavior.all[obj] = nil
table.delete(m.behaviors, behavior)
return obj
end
-- This is how all methods get found. We search the stack of
-- behaviors (most recent to earliest) for the first method
-- called that.
function Object.dispatch(obj, message_name)
local m = getmetatable(obj)
if not m then return nil end
for _, behavior in ipairs(m.behaviors) do
if behavior.methods[message_name] then return behavior.methods[message_name] end
end
return nil
end
-- Creates a new object: An object has a metatable with a
-- behaviors stack and Object.dispatch as its __index. So
-- make one of those and let it become an Object. We can
-- alse pass in a varargs list of different behaviors to
-- become.
function new(obj, ...)
obj = obj or {}
setmetatable(obj,
{ behaviors={}, __index=Object.dispatch })
Object.methods.become(obj, Object)
for _, behavior in ipairs(arg) do
obj:become(behavior)
end
return obj
end
----------------------------------------
A = behavior()
function A.methods.foo(self, x) return self.x * x end
function A.on_become(self) self.x = 10 end
o = new(nil, A)
print(o:foo(9), 90)
print(o.x, 10)
print(A.all[o], o)
o:resign(A)
print(o.foo, nil)
print(A.all[o], nil)