flor 0.0.1 → 0.9.0
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/CHANGELOG.md +13 -0
- data/LICENSE.txt +1 -1
- data/Makefile +66 -0
- data/README.md +57 -0
- data/fail.txt +7 -0
- data/flor.gemspec +12 -9
- data/intercepted.txt +123 -0
- data/lib/flor/colours.rb +140 -0
- data/lib/flor/conf.rb +88 -0
- data/lib/flor/core/executor.rb +473 -0
- data/lib/flor/core/node.rb +397 -0
- data/lib/flor/core/procedure.rb +600 -0
- data/lib/flor/core/texecutor.rb +209 -0
- data/lib/flor/core.rb +93 -0
- data/lib/flor/dollar.rb +248 -0
- data/lib/flor/errors.rb +36 -0
- data/lib/flor/flor.rb +556 -0
- data/lib/flor/log.rb +336 -0
- data/lib/flor/migrations/0001_tables.rb +122 -0
- data/lib/flor/parser.rb +414 -0
- data/lib/flor/pcore/_arr.rb +49 -0
- data/lib/flor/pcore/_atom.rb +43 -0
- data/lib/flor/pcore/_att.rb +160 -0
- data/lib/flor/pcore/_dump.rb +60 -0
- data/lib/flor/pcore/_err.rb +30 -0
- data/lib/flor/pcore/_happly.rb +73 -0
- data/lib/flor/pcore/_obj.rb +65 -0
- data/lib/flor/pcore/_skip.rb +63 -0
- data/lib/flor/pcore/apply.rb +60 -0
- data/lib/flor/pcore/arith.rb +46 -0
- data/lib/flor/pcore/break.rb +71 -0
- data/lib/flor/pcore/cmp.rb +72 -0
- data/lib/flor/pcore/cond.rb +57 -0
- data/lib/flor/pcore/cursor.rb +223 -0
- data/lib/flor/pcore/define.rb +96 -0
- data/lib/flor/pcore/fail.rb +45 -0
- data/lib/flor/pcore/ife.rb +56 -0
- data/lib/flor/pcore/loop.rb +53 -0
- data/lib/flor/pcore/map.rb +75 -0
- data/lib/flor/pcore/match.rb +70 -0
- data/lib/flor/pcore/move.rb +65 -0
- data/lib/flor/pcore/noeval.rb +46 -0
- data/lib/flor/pcore/noret.rb +47 -0
- data/lib/flor/pcore/push.rb +69 -0
- data/lib/flor/pcore/sequence.rb +39 -0
- data/lib/flor/pcore/set.rb +76 -0
- data/lib/flor/pcore/stall.rb +35 -0
- data/lib/flor/pcore/until.rb +122 -0
- data/lib/flor/pcore/val.rb +40 -0
- data/lib/flor/punit/cancel.rb +69 -0
- data/lib/flor/punit/cmap.rb +76 -0
- data/lib/flor/punit/concurrence.rb +149 -0
- data/lib/flor/punit/every.rb +46 -0
- data/lib/flor/punit/on.rb +81 -0
- data/lib/flor/punit/schedule.rb +68 -0
- data/lib/flor/punit/signal.rb +47 -0
- data/lib/flor/punit/sleep.rb +53 -0
- data/lib/flor/punit/task.rb +109 -0
- data/lib/flor/punit/trace.rb +51 -0
- data/lib/flor/punit/trap.rb +100 -0
- data/lib/flor/to_string.rb +81 -0
- data/lib/flor/tools/env.rb +103 -0
- data/lib/flor/tools/repl.rb +231 -0
- data/lib/flor/unit/executor.rb +260 -0
- data/lib/flor/unit/hooker.rb +186 -0
- data/lib/flor/unit/journal.rb +52 -0
- data/lib/flor/unit/loader.rb +181 -0
- data/lib/flor/unit/logger.rb +181 -0
- data/lib/flor/unit/models/execution.rb +105 -0
- data/lib/flor/unit/models/pointer.rb +31 -0
- data/lib/flor/unit/models/timer.rb +52 -0
- data/lib/flor/unit/models/trace.rb +31 -0
- data/lib/flor/unit/models/trap.rb +130 -0
- data/lib/flor/unit/models.rb +106 -0
- data/lib/flor/unit/scheduler.rb +419 -0
- data/lib/flor/unit/storage.rb +633 -0
- data/lib/flor/unit/tasker.rb +191 -0
- data/lib/flor/unit/waiter.rb +146 -0
- data/lib/flor/unit/wlist.rb +77 -0
- data/lib/flor/unit.rb +50 -0
- data/lib/flor.rb +40 -3
- metadata +152 -22
- checksums.yaml +0 -7
- data/Rakefile +0 -52
@@ -0,0 +1,600 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015-2017, John Mettraux, jmettraux+flor@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
class Flor::Procedure < Flor::Node
|
27
|
+
|
28
|
+
def self.inherited(subclass)
|
29
|
+
|
30
|
+
(@@inherited ||= []) << subclass
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.[](name)
|
34
|
+
|
35
|
+
@@inherited.find { |k| k.names && k.names.include?(name) }
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
|
40
|
+
def names(*names)
|
41
|
+
|
42
|
+
names = names.flatten
|
43
|
+
@names = names if names.any?
|
44
|
+
|
45
|
+
@names
|
46
|
+
end
|
47
|
+
|
48
|
+
alias :name :names
|
49
|
+
end
|
50
|
+
|
51
|
+
def pre_execute
|
52
|
+
|
53
|
+
# empty default implementation
|
54
|
+
end
|
55
|
+
|
56
|
+
def trigger_on_error
|
57
|
+
|
58
|
+
@message['on_error'] = true
|
59
|
+
|
60
|
+
close_node('on-error')
|
61
|
+
|
62
|
+
@node['on_receive_last'] =
|
63
|
+
apply(@node['on_error'].shift, [ @message ], tree[2])
|
64
|
+
|
65
|
+
nids = @node['cnodes']
|
66
|
+
|
67
|
+
if nids && nids.any?
|
68
|
+
cancel_children
|
69
|
+
else
|
70
|
+
do_receive # which should trigger 'on_receive_last'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def debug_tree(nid=nil)
|
75
|
+
|
76
|
+
nid ||= self.nid
|
77
|
+
tree = lookup_tree(nid)
|
78
|
+
|
79
|
+
Flor.print_tree(tree, nid)
|
80
|
+
end
|
81
|
+
|
82
|
+
def debug_msg(msg=message)
|
83
|
+
|
84
|
+
Flor.detail_msg(@executor, msg)
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def counter_next(k)
|
90
|
+
|
91
|
+
@executor.counter_next(k)
|
92
|
+
end
|
93
|
+
|
94
|
+
def stack_status(flavour, status)
|
95
|
+
|
96
|
+
h = {
|
97
|
+
'point' => @message['point'], 'status' => status, 'ctime' => Flor.tstamp }
|
98
|
+
h['flavour'] = flavour if flavour
|
99
|
+
mm = @message['m']; h['m'] = mm if mm
|
100
|
+
mf = @message['from']; h['from'] = mf if mf
|
101
|
+
|
102
|
+
s = node_status
|
103
|
+
@node['status'].pop if s['m'] == h['m'] && s['status'] != 'ended'
|
104
|
+
# only keep the latest effect of a message (probably "ended")
|
105
|
+
|
106
|
+
@node['status'] << h
|
107
|
+
end
|
108
|
+
|
109
|
+
def close_node(flavour=@message['flavour'])
|
110
|
+
stack_status(flavour, 'closed')
|
111
|
+
end
|
112
|
+
def open_node
|
113
|
+
stack_status(@message['flavour'], nil)
|
114
|
+
end
|
115
|
+
def end_node
|
116
|
+
stack_status(@message['flavour'], 'ended')
|
117
|
+
end
|
118
|
+
|
119
|
+
def children
|
120
|
+
|
121
|
+
tree[1]
|
122
|
+
end
|
123
|
+
|
124
|
+
def att_children
|
125
|
+
|
126
|
+
children.select { |c| c[0] == '_att' }
|
127
|
+
end
|
128
|
+
|
129
|
+
def non_att_children
|
130
|
+
|
131
|
+
children.select { |c| c[0] != '_att' }
|
132
|
+
end
|
133
|
+
|
134
|
+
def unkeyed_children
|
135
|
+
|
136
|
+
children.select { |c| c[0] != '_att' || c[1].size == 1 }
|
137
|
+
end
|
138
|
+
|
139
|
+
def first_unkeyed_child_id
|
140
|
+
|
141
|
+
children.index { |c| c[0] != '_att' || c[1].size == 1 }
|
142
|
+
end
|
143
|
+
|
144
|
+
def first_non_att_child_id
|
145
|
+
|
146
|
+
children.index { |c| c[0] != '_att' }
|
147
|
+
end
|
148
|
+
|
149
|
+
def att(*keys)
|
150
|
+
|
151
|
+
return nil unless @node['atts']
|
152
|
+
|
153
|
+
keys.each do |k|
|
154
|
+
k = k.to_s unless k == nil
|
155
|
+
a = @node['atts'].assoc(k)
|
156
|
+
return a.last if a
|
157
|
+
end
|
158
|
+
|
159
|
+
nil
|
160
|
+
end
|
161
|
+
|
162
|
+
def has_att?(k)
|
163
|
+
|
164
|
+
@node['atts'].collect(&:first).include?(k)
|
165
|
+
end
|
166
|
+
|
167
|
+
def att_a(*keys)
|
168
|
+
|
169
|
+
if keys.last == nil
|
170
|
+
keys.pop
|
171
|
+
Flor.to_a(att(*keys))
|
172
|
+
else
|
173
|
+
Array(att(*keys))
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def tags_to_nids(tags)
|
178
|
+
|
179
|
+
tags = Array(tags)
|
180
|
+
|
181
|
+
@execution['nodes'].inject([]) { |a, (nid, n)|
|
182
|
+
a << nid if ((n['tags'] || []) & tags).any?
|
183
|
+
a
|
184
|
+
}
|
185
|
+
end
|
186
|
+
|
187
|
+
def execute_child(index=0, sub=nil, h=nil)
|
188
|
+
|
189
|
+
return reply \
|
190
|
+
if index < 0 || ( ! tree[1].is_a?(Array)) || tree[1][index] == nil
|
191
|
+
|
192
|
+
sub = counter_next('subs') if sub == true
|
193
|
+
|
194
|
+
cnid = Flor.child_nid(nid, index, sub)
|
195
|
+
|
196
|
+
hh = {
|
197
|
+
'point' => 'execute',
|
198
|
+
'nid' => cnid,
|
199
|
+
'tree' => tree[1][index],
|
200
|
+
'payload' => payload.current }
|
201
|
+
hh.merge!(h) if h
|
202
|
+
|
203
|
+
reply(hh)
|
204
|
+
end
|
205
|
+
|
206
|
+
def unatt_unkeyed_children(first_only=false)
|
207
|
+
|
208
|
+
found = false
|
209
|
+
|
210
|
+
unkeyed, keyed =
|
211
|
+
att_children.partition { |c|
|
212
|
+
if found
|
213
|
+
false
|
214
|
+
else
|
215
|
+
is_unkeyed = c[1].size == 1
|
216
|
+
found = true if is_unkeyed && first_only
|
217
|
+
is_unkeyed
|
218
|
+
end
|
219
|
+
}
|
220
|
+
|
221
|
+
unkeyed = unkeyed
|
222
|
+
.collect { |c| c[1].first }
|
223
|
+
.reject { |c| c[0] == '_' && c[1] == [] }
|
224
|
+
|
225
|
+
cn = keyed + unkeyed + non_att_children
|
226
|
+
|
227
|
+
@node['tree'] = [ tree[0], cn, tree[2] ] if cn != children
|
228
|
+
end
|
229
|
+
|
230
|
+
def unatt_first_unkeyed_child
|
231
|
+
|
232
|
+
unatt_unkeyed_children(true)
|
233
|
+
end
|
234
|
+
|
235
|
+
def stringify_first_child
|
236
|
+
|
237
|
+
c = non_att_children.first
|
238
|
+
return unless c
|
239
|
+
return unless c[1] == [] && c[0].is_a?(String)
|
240
|
+
|
241
|
+
ci = children.index(c)
|
242
|
+
cn = Flor.dup(children)
|
243
|
+
cn[ci] = [ '_sqs', c[0], c[2] ]
|
244
|
+
|
245
|
+
@node['tree'] = [ tree[0], cn, tree[2] ]
|
246
|
+
end
|
247
|
+
|
248
|
+
def execute
|
249
|
+
|
250
|
+
receive
|
251
|
+
end
|
252
|
+
|
253
|
+
# The executor calls #do_receive, while most procedure implementations
|
254
|
+
# override #receive...
|
255
|
+
#
|
256
|
+
def do_receive
|
257
|
+
|
258
|
+
from_child =
|
259
|
+
if ns = @node['cnodes']
|
260
|
+
ns.delete(from)
|
261
|
+
else
|
262
|
+
nil
|
263
|
+
end
|
264
|
+
|
265
|
+
if node_closed?
|
266
|
+
return receive_from_child_when_closed if from_child
|
267
|
+
return receive_when_closed
|
268
|
+
elsif node_ended?
|
269
|
+
return receive_when_ended
|
270
|
+
end
|
271
|
+
|
272
|
+
receive
|
273
|
+
end
|
274
|
+
|
275
|
+
def pop_on_receive_last
|
276
|
+
|
277
|
+
orl = @node['on_receive_last']
|
278
|
+
|
279
|
+
return nil unless orl
|
280
|
+
return nil if orl.empty?
|
281
|
+
|
282
|
+
open_node unless node_status['flavour'] == 'on-error'
|
283
|
+
@node['on_receive_last'] = []
|
284
|
+
|
285
|
+
@node['mtime'] = Flor.tstamp
|
286
|
+
|
287
|
+
orl
|
288
|
+
end
|
289
|
+
|
290
|
+
def receive_from_child_when_closed
|
291
|
+
|
292
|
+
(@node['cnodes'].empty? && pop_on_receive_last) || reply
|
293
|
+
end
|
294
|
+
|
295
|
+
def receive_when_closed
|
296
|
+
|
297
|
+
[]
|
298
|
+
end
|
299
|
+
|
300
|
+
def receive_when_ended
|
301
|
+
|
302
|
+
[]
|
303
|
+
end
|
304
|
+
|
305
|
+
def receive
|
306
|
+
|
307
|
+
@fcid = point == 'receive' ? Flor.child_id(from) : nil
|
308
|
+
@ncid = (@fcid || -1) + 1
|
309
|
+
|
310
|
+
return receive_first if @fcid == nil
|
311
|
+
|
312
|
+
child = children[@fcid]
|
313
|
+
|
314
|
+
return receive_att if child && child[0] == '_att'
|
315
|
+
|
316
|
+
receive_non_att
|
317
|
+
end
|
318
|
+
|
319
|
+
def receive_first
|
320
|
+
|
321
|
+
return receive_last_att if children[0] && children[0][0] != '_att'
|
322
|
+
execute_child(@ncid)
|
323
|
+
end
|
324
|
+
|
325
|
+
def receive_att
|
326
|
+
|
327
|
+
nctree = children[@ncid]
|
328
|
+
|
329
|
+
return receive_last_att if nctree == nil || nctree[0] != '_att'
|
330
|
+
execute_child(@ncid)
|
331
|
+
end
|
332
|
+
|
333
|
+
def receive_last_att
|
334
|
+
|
335
|
+
return receive_last if children[@ncid] == nil
|
336
|
+
execute_child(@ncid)
|
337
|
+
end
|
338
|
+
|
339
|
+
def receive_non_att
|
340
|
+
|
341
|
+
if @node['rets']
|
342
|
+
@node['rets'] << Flor.dup(payload['ret'])
|
343
|
+
@node['mtime'] = Flor.tstamp
|
344
|
+
end
|
345
|
+
|
346
|
+
return receive_last if children[@ncid] == nil
|
347
|
+
execute_child(@ncid)
|
348
|
+
end
|
349
|
+
|
350
|
+
def receive_last
|
351
|
+
|
352
|
+
reply
|
353
|
+
end
|
354
|
+
|
355
|
+
# Used by 'cursor' (and 'loop') when
|
356
|
+
# ```
|
357
|
+
# cursor 'main'
|
358
|
+
# # is equivalent to:
|
359
|
+
# cursor tag: 'main'
|
360
|
+
# ```
|
361
|
+
#
|
362
|
+
def receive_unkeyed_tag_att
|
363
|
+
|
364
|
+
return [] if @node['tags']
|
365
|
+
# "tag:" encountered, walk away
|
366
|
+
|
367
|
+
ret = @message['payload']['ret']
|
368
|
+
ret = Array(ret).flatten
|
369
|
+
ret = nil unless ret.any? && ret.all? { |e| e.is_a?(String) }
|
370
|
+
|
371
|
+
return [] unless ret
|
372
|
+
|
373
|
+
(@node['tags'] ||= []).concat(ret)
|
374
|
+
|
375
|
+
reply('point' => 'entered', 'nid' => nid, 'tags' => ret)
|
376
|
+
end
|
377
|
+
|
378
|
+
def reply(h={})
|
379
|
+
|
380
|
+
m = {}
|
381
|
+
m['point'] = 'receive'
|
382
|
+
m['exid'] = exid
|
383
|
+
m['nid'] = parent
|
384
|
+
m['from'] = nid
|
385
|
+
|
386
|
+
m['sm'] = @message['m']
|
387
|
+
|
388
|
+
ret = :no
|
389
|
+
ret = h.delete('ret') if h.has_key?('ret')
|
390
|
+
|
391
|
+
m['payload'] = payload.current
|
392
|
+
|
393
|
+
m.merge!(h)
|
394
|
+
|
395
|
+
m['payload']['ret'] = ret if ret != :no
|
396
|
+
|
397
|
+
end_node if m['nid'] == parent && m['point'] == 'receive'
|
398
|
+
|
399
|
+
[ m ]
|
400
|
+
end
|
401
|
+
|
402
|
+
def queue(h); reply(h); end
|
403
|
+
|
404
|
+
def error_reply(o)
|
405
|
+
|
406
|
+
reply('point' => 'failed', 'error' => Flor.to_error(o))
|
407
|
+
end
|
408
|
+
|
409
|
+
def schedule(h)
|
410
|
+
|
411
|
+
h['point'] ||= 'schedule'
|
412
|
+
h['payload'] ||= {}
|
413
|
+
h['nid'] ||= nid
|
414
|
+
|
415
|
+
reply(h)
|
416
|
+
end
|
417
|
+
|
418
|
+
def lookup_var_node(node, mode, k=nil)
|
419
|
+
|
420
|
+
vars = node['vars']
|
421
|
+
|
422
|
+
if vars
|
423
|
+
return node if mode == 'l'
|
424
|
+
return node if mode == '' && Flor.deep_has_key?(vars, k)
|
425
|
+
end
|
426
|
+
|
427
|
+
if cnode = mode == '' && @execution['nodes'][node['cnid']]
|
428
|
+
return cnode if Flor.deep_has_key?(cnode['vars'], k)
|
429
|
+
end
|
430
|
+
|
431
|
+
par = parent_node(node)
|
432
|
+
|
433
|
+
return node if vars && par == nil && mode == 'g'
|
434
|
+
return lookup_var_node(par, mode, k) if par
|
435
|
+
|
436
|
+
nil
|
437
|
+
end
|
438
|
+
|
439
|
+
def set_var(mode, k, v)
|
440
|
+
|
441
|
+
fail IndexError.new("cannot set domain variables") if mode == 'd'
|
442
|
+
|
443
|
+
node = lookup_var_node(@node, mode, k)
|
444
|
+
node = lookup_var_node(@node, 'l', k) if node.nil? && mode == ''
|
445
|
+
|
446
|
+
if node
|
447
|
+
|
448
|
+
b, v = Flor.deep_set(node['vars'], k, v)
|
449
|
+
|
450
|
+
return v if b
|
451
|
+
end
|
452
|
+
|
453
|
+
fail IndexError.new("couldn't set var #{mode}v.#{k}")
|
454
|
+
end
|
455
|
+
|
456
|
+
def set_field(k, v)
|
457
|
+
|
458
|
+
success, value = Flor.deep_set(payload.copy, k, v)
|
459
|
+
|
460
|
+
fail IndexError.new("couldn't set field #{k}") unless success
|
461
|
+
|
462
|
+
value
|
463
|
+
end
|
464
|
+
|
465
|
+
def set_value(k, v)
|
466
|
+
|
467
|
+
return if k == '_'
|
468
|
+
|
469
|
+
cat, mod, key = key_split(k)
|
470
|
+
|
471
|
+
case cat[0, 1]
|
472
|
+
when 'f' then set_field(key, v)
|
473
|
+
when 'v' then set_var(mod, key, v)
|
474
|
+
#when 'w' then set_war(key, v)
|
475
|
+
else fail IndexError.new("don't know how to set #{k.inspect}")
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
def apply(fun, args, line, anid=true)
|
480
|
+
|
481
|
+
fni = fun[1]['nid'] # fun nid
|
482
|
+
ani = anid ? Flor.sub_nid(fni, counter_next('subs')) : fni
|
483
|
+
# the "trap" apply doesn't want a subid generated before it triggers...
|
484
|
+
|
485
|
+
cni = fun[1]['cnid'] # closure nid
|
486
|
+
|
487
|
+
t = lookup_tree(fni)
|
488
|
+
t = t[0] if fun[1]['head']
|
489
|
+
|
490
|
+
sig = t[1].select { |c| c[0] == '_att' }
|
491
|
+
sig = sig.drop(1) if t[0] == 'define'
|
492
|
+
|
493
|
+
vars = {}
|
494
|
+
vars['arguments'] = args # should I dup?
|
495
|
+
sig.each_with_index do |att, i|
|
496
|
+
key = att[1].first[0]
|
497
|
+
vars[key] = args[i]
|
498
|
+
end
|
499
|
+
|
500
|
+
ms = reply(
|
501
|
+
'point' => 'execute',
|
502
|
+
'nid' => ani,
|
503
|
+
'tree' => [ '_apply', t[1], line ],
|
504
|
+
'vars' => vars,
|
505
|
+
'cnid' => cni)
|
506
|
+
|
507
|
+
if oe = fun[1]['on_error']
|
508
|
+
ms.first['on_error'] = oe
|
509
|
+
end
|
510
|
+
|
511
|
+
ms
|
512
|
+
end
|
513
|
+
|
514
|
+
def cancel_nodes(nids)
|
515
|
+
|
516
|
+
(nids || [])
|
517
|
+
.collect { |i| reply('point' => 'cancel', 'nid' => i, 'from' => nid) }
|
518
|
+
.flatten(1)
|
519
|
+
end
|
520
|
+
|
521
|
+
def cancel_reply # "cancel" as a noun
|
522
|
+
|
523
|
+
reply(
|
524
|
+
'cause' => 'cancel',
|
525
|
+
'payload' => @message['payload'] || @node['payload'])
|
526
|
+
end
|
527
|
+
|
528
|
+
def cancel_children
|
529
|
+
|
530
|
+
cancel_nodes(@node['cnodes'])
|
531
|
+
end
|
532
|
+
|
533
|
+
# The executor calls #do_cancel, while most procedure implementations
|
534
|
+
# override #cancel...
|
535
|
+
#
|
536
|
+
def do_cancel
|
537
|
+
|
538
|
+
return kill if @message['flavour'] == 'kill'
|
539
|
+
|
540
|
+
return cancel_when_ended if node_ended?
|
541
|
+
return cancel_when_closed if node_closed?
|
542
|
+
|
543
|
+
cancel
|
544
|
+
end
|
545
|
+
|
546
|
+
def cancel_when_ended
|
547
|
+
|
548
|
+
[] # node has already emitted reply to parent, ignore any later request
|
549
|
+
end
|
550
|
+
|
551
|
+
def cancel_when_closed
|
552
|
+
|
553
|
+
[] # by default, no effect
|
554
|
+
end
|
555
|
+
|
556
|
+
def cancel
|
557
|
+
|
558
|
+
close_node
|
559
|
+
|
560
|
+
nids = @node['cnodes']
|
561
|
+
|
562
|
+
if nids && nids.any?
|
563
|
+
cancel_children
|
564
|
+
else
|
565
|
+
cancel_reply
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
def kill
|
570
|
+
|
571
|
+
return [] if node_ended?
|
572
|
+
|
573
|
+
reply + cancel_children
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
|
578
|
+
# Not really a procedure, more like a macro, rewrites its tree and returns
|
579
|
+
# a new message to queue (with a rewritten tree).
|
580
|
+
#
|
581
|
+
class Flor::Macro < Flor::Procedure
|
582
|
+
|
583
|
+
# Called by the executor.
|
584
|
+
#
|
585
|
+
def rewrite
|
586
|
+
|
587
|
+
t = rewrite_tree
|
588
|
+
|
589
|
+
m = @message.dup
|
590
|
+
m['tree'] = t
|
591
|
+
m['rewritten'] = tree
|
592
|
+
|
593
|
+
m
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
# A namespace for primitive procedures
|
598
|
+
#
|
599
|
+
module Flor::Pro; end
|
600
|
+
|