flor 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +14 -1
  3. data/CREDITS.md +1 -0
  4. data/LICENSE.txt +1 -1
  5. data/Makefile +6 -2
  6. data/README.md +2 -1
  7. data/flor.gemspec +12 -2
  8. data/lib/flor.rb +3 -3
  9. data/lib/flor/colours.rb +1 -1
  10. data/lib/flor/conf.rb +3 -4
  11. data/lib/flor/core/executor.rb +31 -61
  12. data/lib/flor/core/node.rb +213 -96
  13. data/lib/flor/core/procedure.rb +194 -75
  14. data/lib/flor/core/texecutor.rb +6 -7
  15. data/lib/flor/djan.rb +41 -22
  16. data/lib/flor/flor.rb +137 -42
  17. data/lib/flor/id.rb +77 -59
  18. data/lib/flor/log.rb +43 -22
  19. data/lib/flor/migrations/0001_tables.rb +7 -7
  20. data/lib/flor/parser.rb +271 -74
  21. data/lib/flor/pcore/_apply.rb +108 -0
  22. data/lib/flor/pcore/_atom.rb +2 -4
  23. data/lib/flor/pcore/_att.rb +54 -37
  24. data/lib/flor/pcore/_dmute.rb +18 -0
  25. data/lib/flor/pcore/_dol.rb +17 -0
  26. data/lib/flor/pcore/_dqs.rb +35 -0
  27. data/lib/flor/pcore/_head.rb +25 -0
  28. data/lib/flor/pcore/_obj.rb +1 -3
  29. data/lib/flor/pcore/_pat_guard.rb +1 -1
  30. data/lib/flor/pcore/_pat_obj.rb +11 -3
  31. data/lib/flor/pcore/_pat_regex.rb +16 -2
  32. data/lib/flor/pcore/_ref.rb +51 -0
  33. data/lib/flor/pcore/_rxs.rb +27 -0
  34. data/lib/flor/pcore/_val.rb +11 -6
  35. data/lib/flor/pcore/{logo.rb → andor.rb} +4 -6
  36. data/lib/flor/pcore/apply.rb +72 -2
  37. data/lib/flor/pcore/arith.rb +16 -4
  38. data/lib/flor/pcore/array_qmark.rb +100 -0
  39. data/lib/flor/pcore/break.rb +1 -2
  40. data/lib/flor/pcore/case.rb +1 -1
  41. data/lib/flor/pcore/cmp.rb +3 -2
  42. data/lib/flor/pcore/collect.rb +2 -2
  43. data/lib/flor/pcore/cond.rb +19 -1
  44. data/lib/flor/pcore/cursor.rb +12 -11
  45. data/lib/flor/pcore/define.rb +30 -4
  46. data/lib/flor/pcore/do_return.rb +3 -0
  47. data/lib/flor/pcore/flatten.rb +39 -0
  48. data/lib/flor/pcore/if.rb +15 -5
  49. data/lib/flor/pcore/includes.rb +5 -2
  50. data/lib/flor/pcore/inject.rb +1 -1
  51. data/lib/flor/pcore/iterator.rb +28 -18
  52. data/lib/flor/pcore/keys.rb +2 -2
  53. data/lib/flor/pcore/map.rb +19 -1
  54. data/lib/flor/pcore/match.rb +2 -2
  55. data/lib/flor/pcore/matchr.rb +18 -5
  56. data/lib/flor/pcore/max.rb +51 -0
  57. data/lib/flor/pcore/merge.rb +134 -0
  58. data/lib/flor/pcore/move.rb +1 -1
  59. data/lib/flor/pcore/noret.rb +1 -1
  60. data/lib/flor/pcore/not.rb +15 -1
  61. data/lib/flor/pcore/on.rb +11 -0
  62. data/lib/flor/pcore/on_cancel.rb +5 -1
  63. data/lib/flor/pcore/on_error.rb +69 -4
  64. data/lib/flor/pcore/push.rb +4 -9
  65. data/lib/flor/pcore/range.rb +5 -5
  66. data/lib/flor/pcore/reduce.rb +5 -18
  67. data/lib/flor/pcore/return.rb +26 -0
  68. data/lib/flor/pcore/reverse.rb +4 -0
  69. data/lib/flor/pcore/sequence.rb +8 -1
  70. data/lib/flor/pcore/set.rb +74 -15
  71. data/lib/flor/pcore/shuffle.rb +71 -0
  72. data/lib/flor/pcore/slice.rb +137 -0
  73. data/lib/flor/pcore/sort.rb +244 -0
  74. data/lib/flor/pcore/sort_by.rb +67 -0
  75. data/lib/flor/pcore/split.rb +39 -0
  76. data/lib/flor/pcore/stall.rb +1 -1
  77. data/lib/flor/pcore/strings.rb +123 -0
  78. data/lib/flor/pcore/timestamp.rb +34 -0
  79. data/lib/flor/pcore/to_array.rb +2 -3
  80. data/lib/flor/pcore/twig.rb +1 -1
  81. data/lib/flor/pcore/type_of.rb +37 -0
  82. data/lib/flor/pcore/until.rb +3 -3
  83. data/lib/flor/punit/cancel.rb +3 -3
  84. data/lib/flor/punit/ccollect.rb +29 -0
  85. data/lib/flor/punit/cmap.rb +76 -20
  86. data/lib/flor/punit/concurrence.rb +440 -33
  87. data/lib/flor/punit/cron.rb +1 -1
  88. data/lib/flor/punit/every.rb +1 -1
  89. data/lib/flor/punit/graft.rb +2 -3
  90. data/lib/flor/punit/on_timeout.rb +5 -1
  91. data/lib/flor/punit/part.rb +63 -0
  92. data/lib/flor/punit/schedule.rb +1 -1
  93. data/lib/flor/punit/task.rb +52 -10
  94. data/lib/flor/punit/trap.rb +4 -5
  95. data/lib/flor/tools/shell.rb +37 -18
  96. data/lib/flor/unit/caller.rb +23 -11
  97. data/lib/flor/unit/executor.rb +33 -12
  98. data/lib/flor/unit/ganger.rb +10 -1
  99. data/lib/flor/unit/hook.rb +2 -1
  100. data/lib/flor/unit/hooker.rb +13 -2
  101. data/lib/flor/unit/loader.rb +7 -7
  102. data/lib/flor/unit/logger.rb +15 -17
  103. data/lib/flor/unit/models.rb +4 -2
  104. data/lib/flor/unit/models/execution.rb +83 -38
  105. data/lib/flor/unit/models/message.rb +16 -0
  106. data/lib/flor/unit/models/pointer.rb +24 -0
  107. data/lib/flor/unit/models/timer.rb +25 -4
  108. data/lib/flor/unit/models/trace.rb +14 -0
  109. data/lib/flor/unit/models/trap.rb +39 -14
  110. data/lib/flor/unit/scheduler.rb +11 -7
  111. data/lib/flor/unit/storage.rb +55 -39
  112. data/lib/flor/unit/taskers.rb +17 -14
  113. data/lib/flor/unit/waiter.rb +4 -3
  114. metadata +40 -10
  115. data/lib/flor/changes.rb +0 -26
  116. data/lib/flor/dollar.rb +0 -224
  117. data/lib/flor/unit/hooks.rb +0 -37
@@ -67,6 +67,209 @@ class Flor::Pro::Concurrence < Flor::Procedure
67
67
  # task 'alpha'
68
68
  # task 'bravo'
69
69
  # ```
70
+ #
71
+ # ## on_receive: / receiver:
72
+ #
73
+ # Sets a function that is to be run each time a concurrence branch replies.
74
+ # Should return a boolean, `true` for the concurrence to end (and trigger
75
+ # the merging) or `false` for the concurrence to go on (and replies from
76
+ # other branches to be received).
77
+ #
78
+ # In this example, the receiver is actually an implementation of the default
79
+ # receive behaviour, "concurrence" merges as soon as all the children have
80
+ # replied (`>= (length replies) branch_count`).
81
+ # ```
82
+ # define r reply, from, replies, branch_count
83
+ # >= (length replies) branch_count
84
+ # concurrence on_receive: r
85
+ # + 1 2
86
+ # + 3 4
87
+ # ```
88
+ #
89
+ # The receiver can be used to change the reply payload. Instead of
90
+ # returning a boolean, it can return an object with the `done:` and
91
+ # the `payload:` keys:
92
+ # ```
93
+ # define r reply, from, replies, branch_count, over
94
+ # set reply.ret (+ reply.ret 10)
95
+ # { done: (>= (length replies) branch_count), payload: reply }
96
+ # concurrence on_receive: r
97
+ # + 1 2
98
+ # + 3 4
99
+ # ```
100
+ # The first branch thus returns `1 + 2 + 10`, while the second one returns
101
+ # `3 + 4 + 10`.
102
+ #
103
+ # The signature for receiver functions is:
104
+ # `define r reply, from, replies, branch_count`
105
+ #
106
+ # * _reply_ the current reply, here something like `{ ret: 3 }`.
107
+ # * _from_ a string like "0_1_1", the nid of the node that emitted
108
+ # the current reply.
109
+ # * _replies_ an object indexing the replies received so far, like
110
+ # `{ "0_1_1" => { "ret" => 13 } }`.
111
+ # * _branch_count_ simply contains the count of branches. It should be
112
+ # superior or equal to the size of _rets_ and _replies_.
113
+ # * _over_ is set to `true` if a previous receiver call said the
114
+ # concurrence should end. It is set to `false` else. So it's `true`
115
+ # for replies post-merge. It might happen for children answering
116
+ # right after the merge limit and children of concurrences that
117
+ # wait for all the replies, see the `remaining:` attribute above.
118
+ #
119
+ # ## on_receive (non-attribute)
120
+ #
121
+ # Sometimes, it's better to declutter the concurrence and write the
122
+ # on_receive as a 'special' child rather than a attribute:
123
+ #
124
+ # ```
125
+ # define r reply, from, replies, branch_count
126
+ # >= (length replies) branch_count
127
+ # concurrence on_receive: r
128
+ # + 1 2
129
+ # + 3 4
130
+ # ```
131
+ # becomes
132
+ # ```
133
+ # concurrence tag: 'x'
134
+ # on_receive (def \ >= (length replies) branch_count)
135
+ # + 12 34
136
+ # + 56 78
137
+ # ```
138
+ # One can even express the function has a 'block':
139
+ # ```
140
+ # concurrence tag: 'x'
141
+ # on_receive
142
+ # >= (length replies) branch_count
143
+ # + 12 34
144
+ # + 56 78
145
+ # ```
146
+ #
147
+ # ## on_merge: / merger:
148
+ #
149
+ # the function given to `on_merge:` or `merger:` is called once the
150
+ # concurrence has gathered enough replies (or the right replies,
151
+ # depending on `on_receive:` / `receiver:` or `expect:`).
152
+ #
153
+ # In the example below, the merging function take all the `f.ret` and
154
+ # selects the maximum one:
155
+ # ```
156
+ # define m rets, replies, branch_count
157
+ # rets | values _ | max _
158
+ # concurrence on_merge: m
159
+ # + 3 4 5
160
+ # + 6 7 8
161
+ # + 1 2 3
162
+ # ```
163
+ # It can be shortened to:
164
+ # ```
165
+ # concurrence on_merge: (def rets \ rets | values _ | max _)
166
+ # + 3 4 5
167
+ # + 6 7 8
168
+ # + 1 2 3
169
+ # ```
170
+ # `rets` looks like `{ "0_1_1" => 12, "0_1_2" => 21, "0_1_3" => 6 }`,
171
+ # hence the `rets | values _ | max _`.
172
+ #
173
+ # The signature for the merge function looks like:
174
+ # `define m rets, replies, branch_count`
175
+ #
176
+ # * _rets_ is the object collecting the `f.ret` of the replies to merge,
177
+ # like `{ "0_1_1" => 12, "0_1_2" => 21, "0_1_3" => 6 }` as seen above.
178
+ # * _replies_ is the equivalent but for the whole reply payload (fields),
179
+ # like `{"0_1"=>{"ret"=>12}, "0_2"=>{"ret"=>21}, "0_3"=>{"ret"=>6}}`.
180
+ # * _branch_count_ simply contains the count of branches. It should be
181
+ # superior or equal to the size of _rets_ and _replies_.
182
+ #
183
+ # ## on_merge (non-attribute)
184
+ #
185
+ # Like `receiver:` / `:on_receive` has the `on_receive` construct, there
186
+ # is the `on_merge` construct which accepts a function or a block:
187
+ # ```
188
+ # concurrence
189
+ # on_merge (def rs \ rs | values _ | max _)
190
+ # + 1 4 5
191
+ # + 3 7 8
192
+ # + 6 2 3
193
+ # ```
194
+ # or
195
+ # ```
196
+ # concurrence
197
+ # on_merge
198
+ # rets | values _ | min _
199
+ # + 3 4 5
200
+ # + 6 7 8
201
+ # + 1 2 3
202
+ # ```
203
+ #
204
+ # ## child_on_error: / children_on_error:
205
+ #
206
+ # Setting the common attribute `on_error:` on a concurrence is OK, but it
207
+ # only catches a single error and then the flow resumes after the concurrence:
208
+ # ```
209
+ # sequence
210
+ # set l []
211
+ # concurrence on_error:
212
+ # (def msg err \ push l "error at $(msg.nid): $(err.msg)")
213
+ # push l 0
214
+ # push l x # fails because 'x' is unknown)
215
+ # push l 2
216
+ # ```
217
+ # The `push l x` here fails, the on_error is triggered and the flow resumes
218
+ # at `push l 2`. `l` ends up containing
219
+ # `[ 0, "error at 0_1_2_1: cannot find \"x\"", 2 ]`.
220
+ #
221
+ # It is easy to set an `on_error:` on each child:
222
+ # ```
223
+ # sequence
224
+ # set l []
225
+ # define oe msg err
226
+ # push l "error at $(msg.nid): $(err.msg)"
227
+ # concurrence
228
+ # push l 0 on_error: oe
229
+ # push l x on_error: oe
230
+ # push l 2
231
+ # ```
232
+ # But that can also be written as:
233
+ # ```
234
+ # sequence
235
+ # set l []
236
+ # concurrence child_on_error:
237
+ # (def msg err \ push l "error at $(msg.nid): $(err.msg)")
238
+ # push l 0
239
+ # push l x
240
+ # push l 2
241
+ # ```
242
+ # `child_on_error` can also be written as `children_on_error`.
243
+ #
244
+ # The signature for the error handler is:
245
+ # * _msg_ the message (usually `point: 'failed'`) communicating the error
246
+ # * _err_ contains the error itself, it's a shortcut to `msg.error`
247
+ #
248
+ #
249
+ # ## child_on_error / children_on_error (non-attribute)
250
+ #
251
+ # Those who prefer to tie handlers via a node rather than an attribute
252
+ # can do so:
253
+ # ```
254
+ # sequence
255
+ # set l []
256
+ # concurrence
257
+ # child_on_error (def msg err \ push l "error at $(msg.nid): $(err.msg)")
258
+ # push l 0
259
+ # push l x
260
+ # push l 2
261
+ # ```
262
+ # One step further, with a block:
263
+ # ```
264
+ # sequence
265
+ # set l []
266
+ # concurrence
267
+ # child_on_error
268
+ # push l "error at $(msg.nid): $(err.msg)")
269
+ # push l 0
270
+ # push l x
271
+ # push l 2
272
+ # ```
70
273
 
71
274
  name 'concurrence'
72
275
 
@@ -74,6 +277,11 @@ class Flor::Pro::Concurrence < Flor::Procedure
74
277
 
75
278
  @node['atts'] = []
76
279
  @node['payloads'] = {}
280
+
281
+ @node['on_receive_nids'] = nil
282
+ @node['on_receive_queue'] = []
283
+
284
+ pre_execute_rewrite
77
285
  end
78
286
 
79
287
  def receive_last_att
@@ -83,8 +291,17 @@ class Flor::Pro::Concurrence < Flor::Procedure
83
291
  @node['receiver'] = determine_receiver
84
292
  @node['merger'] = determine_merger
85
293
 
86
- (@ncid..children.size - 1)
87
- .map { |i| execute_child(i, 0, 'payload' => payload.copy_current) }
294
+
295
+ branches = (@ncid..children.size - 1).to_a
296
+ @node['branch_count'] = branches.count
297
+
298
+ coe = att('children_on_error', 'child_on_error')
299
+ # might be nil
300
+
301
+ branches
302
+ .map { |i|
303
+ execute_child(
304
+ i, 0, 'payload' => payload.copy_current, 'on_error_handler' => coe) }
88
305
  .flatten(1)
89
306
  #
90
307
  # call execute for each of the (non _att) children
@@ -92,44 +309,186 @@ class Flor::Pro::Concurrence < Flor::Procedure
92
309
 
93
310
  def receive_non_att
94
311
 
95
- @node['payloads'][@message['from']] = @message['payload']
312
+ if message['from_on']
313
+ super
314
+ #receive_from_on
315
+ elsif Flor.same_sub?(nid, from)
316
+ receive_from_branch
317
+ elsif from_error_handler?
318
+ wrap_reply
319
+ elsif @node['on_receive_nids'] && @node['on_receive_nids'][0] == from
320
+ receive_from_receiver
321
+ else
322
+ receive_from_merger
323
+ end
324
+ end
325
+
326
+ protected
327
+
328
+
329
+ def receive_from_child_when_closed
330
+
331
+ ms = receive
332
+
333
+ return [] if ms.empty?
334
+
335
+ pop_on_receive_last || ms
336
+ end
337
+
338
+ def receive_from_branch
339
+
340
+ @node['payloads'][from] = @message['payload']
341
+
342
+ apply_receiver
343
+ end
344
+
345
+ def apply_receiver
346
+
347
+ if @node['receiver'].is_a?(String)
348
+ apply_receiver_method
349
+ else
350
+ apply_receiver_function
351
+ end
352
+ end
353
+
354
+ def apply_receiver_method
355
+
356
+ ret = send('rm__' + @node['receiver'])
357
+ msg = { 'payload' => { 'ret' => ret } }
358
+
359
+ receive_from_receiver(msg)
360
+ end
361
+
362
+ def apply_receiver_function
363
+
364
+ @node['on_receive_queue'] << from
365
+
366
+ dequeue_receiver_function
367
+ end
368
+
369
+ def dequeue_receiver_function
370
+
371
+ if @node['on_receive_nids']
372
+ []
373
+ elsif f = @node['on_receive_queue'].shift
374
+ ms = apply(@node['receiver'], receiver_args(f), tree[2])
375
+ @node['on_receive_nids'] = [ ms.first['nid'], f ]
376
+ ms
377
+ else
378
+ []
379
+ end
380
+ end
381
+
382
+ def receiver_args(from)
383
+
384
+ rs = Flor.dup(@node['payloads'])
385
+
386
+ [ [ 'reply', rs[from] ],
387
+ [ 'from', from ],
388
+ [ 'replies', rs ],
389
+ [ 'branch_count', @node['branch_count'] ],
390
+ [ 'over', !! @node['over'] ] ]
391
+ end
392
+
393
+ def receive_from_receiver(msg=message)
394
+
395
+ ret = msg['payload']['ret']
396
+ over = @node['over']
397
+
398
+ if ret.is_a?(Hash) && ret.keys == %w[ done payload ]
399
+ over = over || ret['done']
400
+ from = @node['on_receive_nids'][1]
401
+ @node['payloads'][from] = ret['payload']
402
+ else
403
+ over = over || ret
404
+ end
405
+
406
+ @node['on_receive_nids'] = nil
96
407
 
97
- over = @node['over'] || invoke_receiver
98
408
  just_over = over && ! @node['over']
409
+
99
410
  @node['over'] ||= just_over
100
411
 
101
- @node['merged_payload'] ||= just_over ? invoke_merger : nil
102
- rem = just_over ? (att(:remaining, :rem) || 'cancel') : nil
412
+ if just_over
413
+ apply_merger
414
+ elsif ! over
415
+ [] # wait for more branches
416
+ else
417
+ receive_from_merger(nil)
418
+ end +
419
+ dequeue_receiver_function
420
+ end
103
421
 
104
- cancel_children(rem) + reply_to_parent(rem)
422
+ def apply_merger
423
+
424
+ if @node['merger'].is_a?(String)
425
+ apply_merger_method
426
+ else
427
+ apply_merger_function
428
+ end
105
429
  end
106
430
 
107
- def cancel_children(rem)
431
+ def apply_merger_method
108
432
 
109
- (rem && rem != 'forget') ? wrap_cancel_children : []
433
+ pld = send('mm__' + @node['merger'])
434
+ msg = { 'payload' => pld }
435
+
436
+ receive_from_merger(msg)
110
437
  end
111
438
 
112
- def reply_to_parent(rem)
439
+ def apply_merger_function
113
440
 
114
- return [] \
115
- if @node['replied']
116
- return [] \
117
- if @node['payloads'].size < non_att_count && ( ! rem || rem == 'wait')
441
+ apply(@node['merger'], merger_args, tree[2])
442
+ end
118
443
 
119
- @node['replied'] = true
120
- wrap_reply('payload' => @node['merged_payload'])
444
+ def merger_args
445
+
446
+ rs = Flor.dup(@node['payloads'])
447
+
448
+ [ [ 'rets', rs.inject({}) { |h, (k, v)| h[k] = v['ret']; h } ],
449
+ [ 'replies', rs ],
450
+ [ 'branch_count', @node['branch_count'] ] ]
121
451
  end
122
452
 
123
- def receive_from_child_when_closed
453
+ def receive_from_merger(msg=message)
124
454
 
125
- ms = receive
455
+ pl = msg ? msg['payload'] : {}
456
+ ret = pl['ret']
126
457
 
127
- return [] if ms.empty?
458
+ pl = ret['payload'] \
459
+ if ret.is_a?(Hash) && ret.keys == %w[ done payload ]
128
460
 
129
- pop_on_receive_last || ms
461
+ # TODO somehow, what if done is false, should we un-over the concurrence?
462
+
463
+ @node['merged_payload'] = pl \
464
+ if msg && ! @node.has_key?('merged_payload')
465
+
466
+ rem = determine_remainder
467
+
468
+ cancel_children(rem) + reply_to_parent(rem)
130
469
  end
131
470
 
132
- protected
471
+ def rm__default_receive
472
+
473
+ @node['payloads'].size >= non_att_count
474
+ end
475
+
476
+ def rm__expect_integer_receive
477
+
478
+ @node['payloads'].size >= att(:expect)
479
+ end
480
+
481
+ def mm__default_merge
482
+
483
+ @node['payloads'].values
484
+ .reverse
485
+ .inject({}) { |h, pl| h.merge!(pl) }
486
+ end
487
+
488
+ def determine_remainder
489
+
490
+ att(:remaining, :rem) || 'cancel'
491
+ end
133
492
 
134
493
  def determine_receiver
135
494
 
@@ -137,32 +496,80 @@ class Flor::Pro::Concurrence < Flor::Procedure
137
496
 
138
497
  return 'expect_integer_receive' if ex && ex.is_a?(Integer) && ex > 0
139
498
 
140
- 'default_receive'
499
+ att(:on_receive, :receiver) || 'default_receive'
141
500
  end
142
501
 
143
502
  def determine_merger
144
503
 
145
- 'default_merge'
504
+ att(:on_merge, :merger) || 'default_merge'
146
505
  end
147
506
 
148
- def invoke_receiver; self.send(@node['receiver']); end
149
- def invoke_merger; self.send(@node['merger']); end
507
+ def cancel_children(rem)
150
508
 
151
- def default_receive
509
+ (rem && rem != 'forget') ? wrap_cancel_children : []
510
+ end
152
511
 
153
- @node['payloads'].size >= non_att_count
512
+ def reply_to_parent(rem)
513
+
514
+ return [] \
515
+ if @node['replied']
516
+ return [] \
517
+ if @node['payloads'].size < non_att_count && ( ! rem || rem == 'wait')
518
+
519
+ @node['replied'] = true
520
+
521
+ wrap_reply('payload' => @node['merged_payload'])
154
522
  end
155
523
 
156
- def expect_integer_receive
524
+ def make_on_def(cn, l)
157
525
 
158
- @node['payloads'].size >= att(:expect)
526
+ c0 = cn.length == 1 ? cn[0] : nil
527
+
528
+ return c0 if Flor.is_definition_tree?(c0)
529
+ return make_on_def(c0[1], l) if Flor.is_att_tree?(c0)
530
+
531
+ [ 'def', cn, l ]
159
532
  end
160
533
 
161
- def default_merge
534
+ def rewrite_as_attribute(child_tree)
162
535
 
163
- @node['payloads'].values
164
- .reverse
165
- .inject({}) { |h, pl| h.merge!(pl) }
536
+ ct0, ct1, ct2 = child_tree
537
+
538
+ k = [ ct0, [], ct2 ]
539
+ v = make_on_def(ct1, ct2)
540
+
541
+ [ '_att', [ k, v ], ct2 ]
542
+ end
543
+
544
+ REWRITE_AS_ATTS = %w[
545
+ on_receive on_merge
546
+ child_on_error children_on_error ]
547
+ #
548
+ # heads of the child nodes that should get rewritten as attributes
549
+ # of the concurrence ...
550
+
551
+ def pre_execute_rewrite
552
+
553
+ t = tree
554
+ t1 = t[1]
555
+
556
+ return if t1.empty?
557
+
558
+ atts, cldn =
559
+ t1.inject([ [], [] ]) { |r, ct|
560
+ if ct[0] == '_att'
561
+ r[0] << ct
562
+ elsif REWRITE_AS_ATTS.include?(ct[0])
563
+ r[0] << rewrite_as_attribute(ct)
564
+ else
565
+ r[1] << ct
566
+ end
567
+ r }
568
+
569
+ nt1 = atts + cldn
570
+
571
+ @node['tree'] = [ t[0], nt1, *t[2..-1] ] \
572
+ if nt1 != t1
166
573
  end
167
574
  end
168
575