dramatis 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +7 -0
- data/License.txt +20 -0
- data/Manifest.txt +119 -0
- data/README.txt +57 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +70 -0
- data/config/requirements.rb +17 -0
- data/examples/README.txt +20 -0
- data/examples/auction.rb +90 -0
- data/examples/bank/bank.rb +7 -0
- data/examples/bank/bank_test.rb +7 -0
- data/examples/exception.rb +40 -0
- data/examples/fib/conservative.rb +50 -0
- data/examples/fib/future.rb +5 -0
- data/examples/fib/original.rb +33 -0
- data/examples/fib/threads.rb +51 -0
- data/examples/im/distributed/chat/client.rb +49 -0
- data/examples/im/distributed/chat/screen/fox.rb +92 -0
- data/examples/im/distributed/chat/screen.rb +11 -0
- data/examples/im/distributed/chat/server.rb +72 -0
- data/examples/im/distributed/chat.rb +5 -0
- data/examples/im/distributed/client.rb +9 -0
- data/examples/im/distributed/run.rb +18 -0
- data/examples/im/distributed/server.rb +11 -0
- data/examples/im/single/chat/client.rb +50 -0
- data/examples/im/single/chat/screen/fox.rb +96 -0
- data/examples/im/single/chat/screen/wxs.rb +63 -0
- data/examples/im/single/chat/screen.rb +11 -0
- data/examples/im/single/chat/server.rb +72 -0
- data/examples/im/single/chat.rb +5 -0
- data/examples/im/single/fox.rb +18 -0
- data/examples/im/single/wxchat.rb +19 -0
- data/examples/pingpong/actor.rb +33 -0
- data/examples/pingpong/actor_rec.rb +34 -0
- data/examples/pingpong/pingpong.txt +315 -0
- data/examples/pingpong/scala.rb +41 -0
- data/examples/pingpong/serial.rb +26 -0
- data/examples/pretty.txt +108 -0
- data/examples/telephone/.irbrc +2 -0
- data/examples/telephone/3esl.txt +21877 -0
- data/examples/telephone/fifth/kid.rb +36 -0
- data/examples/telephone/fifth/run.rb +26 -0
- data/examples/telephone/first/kid.rb +31 -0
- data/examples/telephone/first/run.rb +20 -0
- data/examples/telephone/fourth/kid.rb +31 -0
- data/examples/telephone/fourth/run.rb +26 -0
- data/examples/telephone/mangler.rb +53 -0
- data/examples/telephone/second/kid.rb +26 -0
- data/examples/telephone/second/run.rb +20 -0
- data/examples/telephone/seventh/kid.rb +40 -0
- data/examples/telephone/seventh/run.rb +35 -0
- data/examples/telephone/seventh/test.rb +28 -0
- data/examples/telephone/seventh/test2.rb +10 -0
- data/examples/telephone/sixth/kid.rb +39 -0
- data/examples/telephone/sixth/run.rb +26 -0
- data/examples/telephone/third/kid.rb +31 -0
- data/examples/telephone/third/run.rb +21 -0
- data/lib/dramatis/actor/interface.rb +118 -0
- data/lib/dramatis/actor/name/interface.rb +128 -0
- data/lib/dramatis/actor/name.rb +44 -0
- data/lib/dramatis/actor.rb +96 -0
- data/lib/dramatis/deadlock.rb +123 -0
- data/lib/dramatis/error/uncaught.rb +19 -0
- data/lib/dramatis/error.rb +125 -0
- data/lib/dramatis/future/interface.rb +45 -0
- data/lib/dramatis/future.rb +32 -0
- data/lib/dramatis/runtime/actor/main.rb +3 -0
- data/lib/dramatis/runtime/actor.rb +294 -0
- data/lib/dramatis/runtime/gate.rb +244 -0
- data/lib/dramatis/runtime/scheduler.rb +374 -0
- data/lib/dramatis/runtime/task.rb +390 -0
- data/lib/dramatis/runtime/thread_pool.rb +149 -0
- data/lib/dramatis/runtime/timer.rb +5 -0
- data/lib/dramatis/runtime.rb +129 -0
- data/lib/dramatis/shoes/runtime.rb +7 -0
- data/lib/dramatis/shoes.rb +14 -0
- data/lib/dramatis/version.rb +8 -0
- data/lib/dramatis.rb +73 -0
- data/log/debug.log +0 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/spec/dramatis/actor/become_spec.rb +17 -0
- data/spec/dramatis/actor/future_spec.rb +189 -0
- data/spec/dramatis/actor/name_spec.rb +141 -0
- data/spec/dramatis/actor/task_spec.rb +75 -0
- data/spec/dramatis/actor_spec.rb +492 -0
- data/spec/dramatis/dramatis_spec.rb +23 -0
- data/spec/dramatis/exc_spec.rb +78 -0
- data/spec/dramatis/runtime/gate_spec.rb +57 -0
- data/spec/dramatis/runtime/thread_pool.rb +30 -0
- data/spec/dramatis/shoes_spec.rb +11 -0
- data/spec/dramatis/simple_spec.rb +32 -0
- data/spec/exp_spec.rb +21 -0
- data/spec/simple2_spec.rb +36 -0
- data/spec/simple_spec.rb +30 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/thread_spec.rb +13 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/website.rake +17 -0
- data/test/jruby_lm.rb +13 -0
- data/test/test.rb +19 -0
- data/test/test10.rb +43 -0
- data/test/test11.rb +45 -0
- data/test/test12.rb +60 -0
- data/test/test13.rb +71 -0
- data/test/test2.rb +12 -0
- data/test/test3.rb +10 -0
- data/test/test4.rb +29 -0
- data/test/test5.rb +8 -0
- data/test/test6.rb +32 -0
- data/test/test7.rb +48 -0
- data/test/test8.rb +133 -0
- data/test/test9.rb +105 -0
- data/test/test_exc.rb +22 -0
- metadata +180 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
module Dramatis; end
|
|
2
|
+
class Dramatis::Runtime; end
|
|
3
|
+
|
|
4
|
+
require 'dramatis/runtime/task'
|
|
5
|
+
require 'dramatis/runtime/gate'
|
|
6
|
+
require 'dramatis/runtime/timer'
|
|
7
|
+
require 'dramatis/actor/interface'
|
|
8
|
+
require 'dramatis/actor/name'
|
|
9
|
+
require 'thread'
|
|
10
|
+
|
|
11
|
+
begin require 'pp'; rescue Exception; end
|
|
12
|
+
|
|
13
|
+
class Dramatis::Runtime::Actor #:nodoc: all
|
|
14
|
+
|
|
15
|
+
def name
|
|
16
|
+
@name ||= Dramatis::Actor::Name.new self
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
attr_reader :object_interface
|
|
20
|
+
attr_reader :object
|
|
21
|
+
attr_reader :gate
|
|
22
|
+
attr_reader :call_thread
|
|
23
|
+
|
|
24
|
+
def initialize object = nil
|
|
25
|
+
@call_threading = false
|
|
26
|
+
@call_thread = nil
|
|
27
|
+
@object = object
|
|
28
|
+
@gate = Dramatis::Runtime::Gate.new
|
|
29
|
+
if !object
|
|
30
|
+
@gate.refuse :object
|
|
31
|
+
end
|
|
32
|
+
@gate.always( ( [ :object, :dramatis_exception ] ), true )
|
|
33
|
+
blocked!
|
|
34
|
+
@queue = []
|
|
35
|
+
@mutex = Mutex.new
|
|
36
|
+
@continuations = {}
|
|
37
|
+
@object_interface = Dramatis::Actor::Interface.new self
|
|
38
|
+
Dramatis::Runtime::Scheduler.current << self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def call_threading?
|
|
42
|
+
@call_threading
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def enable_call_threading
|
|
46
|
+
@call_threading = true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def current_call_thread? that
|
|
50
|
+
# warn "current_call_thread? #{@call_thread} #{(@call_thread and (@call_thread == that)).inspect} #{that}"
|
|
51
|
+
@call_thread and @call_thread == that
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def object_initialize *args
|
|
55
|
+
@gate.accept :object
|
|
56
|
+
@object.send :initialize, *args
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def yield; end
|
|
60
|
+
|
|
61
|
+
def bind object
|
|
62
|
+
raise Dramatis::Error::Bind if @object
|
|
63
|
+
@object = object
|
|
64
|
+
@gate.accept :object
|
|
65
|
+
name
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def exception exception
|
|
69
|
+
if @object.respond_to? :dramatis_exception
|
|
70
|
+
@object.dramatis_exception exception
|
|
71
|
+
else
|
|
72
|
+
Dramatis::Runtime.current.exception exception
|
|
73
|
+
end
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def deadlock e
|
|
78
|
+
tasks = nil
|
|
79
|
+
@mutex.synchronize do
|
|
80
|
+
tasks = @queue.dup
|
|
81
|
+
@queue.clear
|
|
82
|
+
end
|
|
83
|
+
tasks.each { |task| task.exception e }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def register_continuation c
|
|
87
|
+
# p "selfish", self, @continuations
|
|
88
|
+
# pp "csr", c.to_s
|
|
89
|
+
@continuations[c.to_s] = c
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def actor_send args, opts
|
|
93
|
+
common_send :actor, args, opts
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def object_send args, opts
|
|
97
|
+
t = nil
|
|
98
|
+
if opts[:continuation_send]
|
|
99
|
+
t = :continuation
|
|
100
|
+
args.unshift opts[:continuation_send]
|
|
101
|
+
else
|
|
102
|
+
t = :object
|
|
103
|
+
end
|
|
104
|
+
common_send t, args, opts
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def common_send dest, args, opts
|
|
108
|
+
|
|
109
|
+
# warn "common send #{self} #{dest} #{args[0]}"
|
|
110
|
+
# warn "common send #{self} #{dest} #{args.join(' ')} #{opts.to_a.join(' ' )}"
|
|
111
|
+
|
|
112
|
+
task = Dramatis::Runtime::Task.new( self, dest, args, opts )
|
|
113
|
+
|
|
114
|
+
# warn "#{task.type} #{task.method}"
|
|
115
|
+
# warn "#{self} #{Thread.current} common send r? #{runnable?} g? #{@gate.accepts?( *( [ task.type, task.method ] + task.arguments ) ) } q #{@queue.length}"
|
|
116
|
+
@mutex.synchronize do
|
|
117
|
+
# FIX arguments to gate
|
|
118
|
+
if !runnable? and ( @gate.accepts?( *( [ task.type, task.method ] + task.arguments ) ) or current_call_thread?( task.call_thread ) )
|
|
119
|
+
runnable!
|
|
120
|
+
Dramatis::Runtime::Scheduler.current.schedule task
|
|
121
|
+
else
|
|
122
|
+
# warn "+>schd #{self} #{@queue.join(' ')}"
|
|
123
|
+
@queue << task
|
|
124
|
+
# warn "+<schd #{self} #{@queue.join(' ')}"
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
task.queued
|
|
129
|
+
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def deliver dest, args, continuation, call_thread
|
|
133
|
+
old_call_thread = @call_thread
|
|
134
|
+
begin
|
|
135
|
+
@call_thread = call_thread
|
|
136
|
+
method = args.shift
|
|
137
|
+
# warn "switch " + dest.to_s + " " + method.to_s
|
|
138
|
+
result =
|
|
139
|
+
case dest
|
|
140
|
+
when :actor
|
|
141
|
+
# FIX: do name folding; needs test
|
|
142
|
+
self.send method, *args
|
|
143
|
+
# p "sent actor #{method}"
|
|
144
|
+
when :object
|
|
145
|
+
# p "send object #{@object} #{method} #{args.length}"
|
|
146
|
+
v = @object.send method, *args
|
|
147
|
+
if v.object_id == @object.object_id
|
|
148
|
+
v = name
|
|
149
|
+
end
|
|
150
|
+
# p "sent object #{method}"
|
|
151
|
+
v
|
|
152
|
+
when :continuation
|
|
153
|
+
# FIX: name folding?
|
|
154
|
+
# p "send continuation #{method}"
|
|
155
|
+
continuation_name = method
|
|
156
|
+
# warn "c is #{continuation_name}"
|
|
157
|
+
c = @continuations[continuation_name]
|
|
158
|
+
# pp "cs", @continuations.keys
|
|
159
|
+
raise "hell 0 #{Thread.current}" if !c
|
|
160
|
+
method = args.shift
|
|
161
|
+
method = case method
|
|
162
|
+
when :result; :continuation_result
|
|
163
|
+
when :exception; :continuation_exception
|
|
164
|
+
else; raise "hell *"
|
|
165
|
+
end
|
|
166
|
+
# pp c.to_s, "send", method, args
|
|
167
|
+
c.send method, *args
|
|
168
|
+
@continuations.delete continuation_name
|
|
169
|
+
# pp "csd", continuation_name, @continuations.keys
|
|
170
|
+
else
|
|
171
|
+
raise "hell 1: " + @dest.to_s
|
|
172
|
+
end
|
|
173
|
+
# p "call c '#{result}'"
|
|
174
|
+
# p continuation.to_s
|
|
175
|
+
continuation.result result
|
|
176
|
+
# p "called c #{result}"
|
|
177
|
+
rescue Exception => exception
|
|
178
|
+
# pp "0 exception ", exception
|
|
179
|
+
# pp exception.backtrace
|
|
180
|
+
begin
|
|
181
|
+
continuation.exception exception
|
|
182
|
+
rescue Exception => e
|
|
183
|
+
warn "double exception fault: #{e}"
|
|
184
|
+
pp e
|
|
185
|
+
raise e
|
|
186
|
+
end
|
|
187
|
+
ensure
|
|
188
|
+
@call_thread = old_call_thread
|
|
189
|
+
schedule
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# note called from task.rb, too
|
|
194
|
+
def schedule continuation = nil
|
|
195
|
+
@mutex.synchronize do
|
|
196
|
+
# warn ">schd #{self} #{@state} #{@queue.join(' ')}"
|
|
197
|
+
task = nil
|
|
198
|
+
index = 0
|
|
199
|
+
while task == nil and index < @queue.length do
|
|
200
|
+
candidate = @queue[index]
|
|
201
|
+
# FIX arugments?
|
|
202
|
+
if @gate.accepts?( *( [ candidate.type, candidate.method ] + candidate.arguments ) ) or
|
|
203
|
+
current_call_thread? candidate.call_thread
|
|
204
|
+
task = candidate
|
|
205
|
+
@queue[index,1] = []
|
|
206
|
+
end
|
|
207
|
+
index += 1
|
|
208
|
+
end
|
|
209
|
+
if task
|
|
210
|
+
Dramatis::Runtime::Scheduler.current.schedule task
|
|
211
|
+
else
|
|
212
|
+
blocked!
|
|
213
|
+
end
|
|
214
|
+
# warn "<schd #{self} #{@queue.join(' ')} #{@state} #{Thread.current}"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def blocked!
|
|
219
|
+
# warn "blocked! #{self} #{@state}"
|
|
220
|
+
@state = :blocked
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def runnable!
|
|
224
|
+
# warn "runnable! #{self} #{@state}"
|
|
225
|
+
@state = :runnable
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def runnable?
|
|
229
|
+
# warn "runnable? #{self} #{@state}"
|
|
230
|
+
@state == :runnable
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def timeout value, *args
|
|
234
|
+
@timer ||= Dramatis::Runtime::Timer.new
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Might be nice if this was broken out into another file ... YAGNI?
|
|
240
|
+
# This needs to be reworked. External threads and the main thread are
|
|
241
|
+
# related but different (there can only be one main thread). Mutliple
|
|
242
|
+
# at_exit calls are suspect. Of course, if the main actor tracks multilple
|
|
243
|
+
# runtimes, it might be okay.
|
|
244
|
+
|
|
245
|
+
class Dramatis::Runtime::Actor::Main < Dramatis::Runtime::Actor #:nodoc: all
|
|
246
|
+
|
|
247
|
+
class DefaultBehavior
|
|
248
|
+
|
|
249
|
+
class Exception < ::Exception; end
|
|
250
|
+
|
|
251
|
+
def method_missing *args
|
|
252
|
+
raise Exception.new( "must use Actor#become to enable main actor" )
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def dramatis_exception e
|
|
256
|
+
if Dramatis::Runtime.current.warnings?
|
|
257
|
+
warn "exception on main thread: #{e}"
|
|
258
|
+
# pp caller
|
|
259
|
+
end
|
|
260
|
+
Dramatis::Runtime.current.exception e
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
@@current = nil
|
|
266
|
+
|
|
267
|
+
def self.current
|
|
268
|
+
@@current ||= self.new
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def self.reset
|
|
272
|
+
@@current = nil
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def quiesce
|
|
276
|
+
schedule
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def finalize
|
|
280
|
+
if !@at_exit_run
|
|
281
|
+
@at_exit_run = true
|
|
282
|
+
schedule
|
|
283
|
+
Dramatis::Runtime::Scheduler.current.main_at_exit
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def initialize
|
|
288
|
+
super DefaultBehavior.new
|
|
289
|
+
@at_exit_run = false
|
|
290
|
+
at_exit { finalize }
|
|
291
|
+
runnable!
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
end
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
module Dramatis; end
|
|
2
|
+
class Dramatis::Runtime; end
|
|
3
|
+
|
|
4
|
+
begin require 'pp'; rescue Exception; end
|
|
5
|
+
|
|
6
|
+
class Dramatis::Runtime::Gate #:nodoc: all
|
|
7
|
+
|
|
8
|
+
def self.new
|
|
9
|
+
gate = Case.new
|
|
10
|
+
gate.accept Object
|
|
11
|
+
gate.accept :actor
|
|
12
|
+
gate.accept :continuation
|
|
13
|
+
gate.accept :object
|
|
14
|
+
gate
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class Case
|
|
18
|
+
def always args, value, options = {}
|
|
19
|
+
_change @always, Array( args ), value, options
|
|
20
|
+
end
|
|
21
|
+
def only args, options = {}
|
|
22
|
+
_change @list, [ :object ], false, options
|
|
23
|
+
_change @list, [ :continuation ], false, options
|
|
24
|
+
_change @list, [ :continuation, Object, :exception ], true, options
|
|
25
|
+
_change @list, Array( args ), true, options
|
|
26
|
+
end
|
|
27
|
+
def default args, options = {}
|
|
28
|
+
_change @list, args, nil, options
|
|
29
|
+
end
|
|
30
|
+
def default_by_tag tag
|
|
31
|
+
_change @list, nil, nil, { :tag => tag }
|
|
32
|
+
end
|
|
33
|
+
def _change list, args, value, options = {}
|
|
34
|
+
inplace = options[ :inplace ]
|
|
35
|
+
tag = options[ :tag ]
|
|
36
|
+
# pp ">> #{args and args.join(' ')} #{value} #{inplace} #{tag}", list
|
|
37
|
+
prepend = true
|
|
38
|
+
tbd = []
|
|
39
|
+
list.each_with_index do |entry, list_index|
|
|
40
|
+
vector, result, entry_tag = entry
|
|
41
|
+
matches = true
|
|
42
|
+
if tag and entry_tag != tag
|
|
43
|
+
# p "#{tag} tag and #{entry_tag} dont' match"
|
|
44
|
+
next
|
|
45
|
+
end
|
|
46
|
+
if args
|
|
47
|
+
args.each_with_index do |arg, arg_index|
|
|
48
|
+
# p "compare #{vector[arg_index]} #{arg}"
|
|
49
|
+
if vector[arg_index] != arg
|
|
50
|
+
# p "matches"
|
|
51
|
+
matches = false
|
|
52
|
+
break
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
if matches
|
|
57
|
+
# p "matched"
|
|
58
|
+
if inplace and value != nil
|
|
59
|
+
# p "inplace"
|
|
60
|
+
list[list_index][1] = value
|
|
61
|
+
# FIX: tag?
|
|
62
|
+
prepend = false
|
|
63
|
+
else
|
|
64
|
+
# p "schedule remove #{list[list_index].join(' ')} at #{list_index}"
|
|
65
|
+
tbd << list_index
|
|
66
|
+
# list[list_index,1] = []
|
|
67
|
+
end
|
|
68
|
+
# break
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
tbd.reverse.each do |index|
|
|
72
|
+
# p "remove #{index} #{list[index].join(' ')} at #{index}"
|
|
73
|
+
list[index,1] = []
|
|
74
|
+
end
|
|
75
|
+
if prepend and value != nil
|
|
76
|
+
list.unshift [ args, value, tag ]
|
|
77
|
+
end
|
|
78
|
+
# pp "<< #{args and args.join(' ')} #{value} #{inplace} #{tag}", list
|
|
79
|
+
end
|
|
80
|
+
def change args, value, inplace
|
|
81
|
+
# pp "> #{args.join(' ')} #{value} #{inplace}", @list
|
|
82
|
+
prepend = true
|
|
83
|
+
@list.each_with_index do |entry, list_index|
|
|
84
|
+
vector, result = entry
|
|
85
|
+
matches = true
|
|
86
|
+
args.each_with_index do |arg, arg_index|
|
|
87
|
+
# p "compare #{vector[arg_index]} #{arg}"
|
|
88
|
+
if vector[arg_index] != arg
|
|
89
|
+
# p "matches"
|
|
90
|
+
matches = false
|
|
91
|
+
break
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
if matches
|
|
95
|
+
# p "matched"
|
|
96
|
+
if inplace
|
|
97
|
+
# p "inplace"
|
|
98
|
+
@list[list_index][1] = value
|
|
99
|
+
# FIX: tag?
|
|
100
|
+
prepend = false
|
|
101
|
+
else
|
|
102
|
+
@list[list_index,1] = []
|
|
103
|
+
end
|
|
104
|
+
break
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
if prepend
|
|
108
|
+
@list.unshift [ args, value ]
|
|
109
|
+
end
|
|
110
|
+
# pp "<", @list
|
|
111
|
+
end
|
|
112
|
+
def accept *args
|
|
113
|
+
change args, true, false
|
|
114
|
+
end
|
|
115
|
+
def refuse *args
|
|
116
|
+
change args, false, false
|
|
117
|
+
end
|
|
118
|
+
def update value, *args
|
|
119
|
+
end
|
|
120
|
+
def accepts? *args
|
|
121
|
+
# p "accepts? #{args.join(" ")}"
|
|
122
|
+
accepted = nil
|
|
123
|
+
( @always + @list ).each do |entry|
|
|
124
|
+
vector, result = entry
|
|
125
|
+
matches = true
|
|
126
|
+
vector.each_with_index do |v, i|
|
|
127
|
+
# warn "#{v} #{args[i]} => #{v === args[i]} so #{result}"
|
|
128
|
+
if not( v === args[i] )
|
|
129
|
+
matches = false
|
|
130
|
+
break
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
if matches
|
|
134
|
+
accepted = result
|
|
135
|
+
break
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
# warn "last '#{accepted}'"
|
|
139
|
+
# p "accepts? #{args.join(" ")} => '#{accepted}'"
|
|
140
|
+
accepted == true
|
|
141
|
+
end
|
|
142
|
+
def initialize
|
|
143
|
+
@always = []
|
|
144
|
+
@list = []
|
|
145
|
+
end
|
|
146
|
+
def list
|
|
147
|
+
@list
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
class Constant
|
|
152
|
+
def initialize constant
|
|
153
|
+
@constant = constant
|
|
154
|
+
end
|
|
155
|
+
def accepts? *args
|
|
156
|
+
@constant
|
|
157
|
+
end
|
|
158
|
+
def refuse *args
|
|
159
|
+
warn "HELLL"
|
|
160
|
+
raise "hell"
|
|
161
|
+
end
|
|
162
|
+
def accept *args
|
|
163
|
+
warn "HELLL"
|
|
164
|
+
raise "hell"
|
|
165
|
+
end
|
|
166
|
+
def default *args
|
|
167
|
+
warn "HELLL"
|
|
168
|
+
raise "hell"
|
|
169
|
+
end
|
|
170
|
+
def set_default *args
|
|
171
|
+
warn "HELLL"
|
|
172
|
+
raise "hell"
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
ACCEPT = Constant.new true
|
|
177
|
+
REJECT = Constant.new false
|
|
178
|
+
|
|
179
|
+
class Hash
|
|
180
|
+
|
|
181
|
+
def initialize default, hash = {}
|
|
182
|
+
@default = default
|
|
183
|
+
@hash = hash.clone
|
|
184
|
+
@queued = {}
|
|
185
|
+
track and warn "create default = #{@default} hash = [#{@hash.keys.join(' ')}]"
|
|
186
|
+
@always = []
|
|
187
|
+
@call_thread = nil
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def track; false; end
|
|
191
|
+
|
|
192
|
+
def always *args
|
|
193
|
+
track and warn "always [#{args.join(' ')}]"
|
|
194
|
+
@always << args
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def refuse *args
|
|
198
|
+
track and warn "refuse [#{args.join(' ')}]"
|
|
199
|
+
if args.length == 1
|
|
200
|
+
@hash[args[0]] = REJECT
|
|
201
|
+
else
|
|
202
|
+
@hash[args[0]].refuse( *args[1,args.length] )
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def accept *args
|
|
207
|
+
track and warn "accept [#{args.join(' ')}]"
|
|
208
|
+
if args.length == 1
|
|
209
|
+
@hash[args[0]] = ACCEPT
|
|
210
|
+
else
|
|
211
|
+
@hash[args[0]].accept( *args[1,args.length] )
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def default *args
|
|
216
|
+
track and warn "default [#{args.join(' ')}]"
|
|
217
|
+
if args.length == 1
|
|
218
|
+
@hash.delete args[0]
|
|
219
|
+
else
|
|
220
|
+
@hash[args[0]].default( *args[1,args.length] )
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def set_default value, *args
|
|
225
|
+
track and warn "default = #{value} [#{args.join(' ')}] #{args.length}"
|
|
226
|
+
if args.length == 0
|
|
227
|
+
@default = value
|
|
228
|
+
else
|
|
229
|
+
@hash[args[0]].set_default value, *args[1,args.length]
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def accepts? *args
|
|
234
|
+
v = nil
|
|
235
|
+
if v == nil
|
|
236
|
+
v = @hash.has_key?( args[0] ) ? @hash[args[0]].accepts?( *args[1,args.length] ) : @default
|
|
237
|
+
end
|
|
238
|
+
track and warn "accepts? [#{args.join(' ')}] => #{v}"
|
|
239
|
+
v
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
end
|