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
@@ -6,6 +6,14 @@ class Flor::Procedure < Flor::Node
6
6
  #
7
7
  RVARS = %w[ idx ]
8
8
 
9
+ # Attributes that when given alone are turned to "true" attributes.
10
+ #
11
+ # For example `sequence flank` gets turned to `sequence flank: true`
12
+ #
13
+ # The transformation occurs in Flor::Pro::Att ("_att").
14
+ #
15
+ TRUE_ATTS = %w[ flank off disabled ]
16
+
9
17
  class << self
10
18
 
11
19
  def inherited(subclass)
@@ -20,9 +28,12 @@ class Flor::Procedure < Flor::Node
20
28
 
21
29
  def names(*names)
22
30
 
23
- names = names.flatten
24
- @names = names if names.any?
25
- @core = !! caller.find { |l| l.match(/flor\/pcore/) } if names.any?
31
+ @names = [] unless defined?(@names)
32
+
33
+ if (names = names.flatten).any?
34
+ @names = names
35
+ @core = !! caller.find { |l| l.match(/flor\/pcore/) }
36
+ end
26
37
 
27
38
  @names
28
39
  end
@@ -33,18 +44,11 @@ class Flor::Procedure < Flor::Node
33
44
 
34
45
  def make(executor, node, message)
35
46
 
36
- heap = node['heat'] ? node['heap'] : nil
37
-
38
- fail ArgumentError.new(
39
- "cannot determine procedure " +
40
- "#{{ heat: node['heat'], heap: node['heap'] }.inspect}"
41
- ) unless heap
42
-
47
+ heap = node['heap']
43
48
  heac = self[heap]
44
49
 
45
- fail NameError.new(
46
- "unknown procedure #{heap.inspect}"
47
- ) unless heac
50
+ fail NameError.new("unknown procedure #{heap.inspect}") \
51
+ unless heac
48
52
 
49
53
  heac.new(executor, node, message)
50
54
  end
@@ -55,9 +59,23 @@ class Flor::Procedure < Flor::Node
55
59
  # empty default implementation
56
60
  end
57
61
 
58
- def prepare_on_receive_last(tree)
62
+ def prepare_on_receive_last(on_x, max=-1)
63
+
64
+ on_x
65
+ .inject([]) { |a, (criteria, mop)|
59
66
 
60
- apply(tree.shift, [ @message ], tree[2])
67
+ next a if max > 0 && a.size >= max
68
+ next a unless match_on?(criteria)
69
+
70
+ msg = Flor.dup(@message)
71
+
72
+ a.concat(
73
+ if Flor.is_message?(mop)
74
+ [ Flor.dup(mop).merge!('msg' => msg) ]
75
+ else # procedure
76
+ args = [ [ 'msg', msg ], [ 'err', msg['error'] ] ]
77
+ apply(mop, args, mop[2])
78
+ end) }
61
79
  end
62
80
 
63
81
  def trigger_on_error
@@ -67,7 +85,7 @@ class Flor::Procedure < Flor::Node
67
85
  close_node('on-error')
68
86
 
69
87
  @node['on_receive_last'] =
70
- apply(@node['on_error'].shift, [ @message, @message['error'] ], tree[2])
88
+ prepare_on_receive_last(@node['on_error'], 1)
71
89
 
72
90
  do_wrap_cancel_children ||
73
91
  do_receive # which should trigger 'on_receive_last'
@@ -94,7 +112,7 @@ class Flor::Procedure < Flor::Node
94
112
  def flank
95
113
 
96
114
  @node['tree'] = Flor.dup(tree)
97
- @node['noreply'] = true
115
+ @node['replyto'] = nil
98
116
 
99
117
  wrap('nid' => parent, 'flavour' => 'flank')
100
118
  end
@@ -181,6 +199,15 @@ class Flor::Procedure < Flor::Node
181
199
  nil
182
200
  end
183
201
 
202
+ def atts(*keys)
203
+
204
+ return nil unless @node['atts']
205
+
206
+ @node['atts']
207
+ .select { |k, _| keys.include?(k == nil ? nil : k.to_s) }
208
+ .collect { |_, v| v }
209
+ end
210
+
184
211
  def has_att?(k)
185
212
 
186
213
  @node['atts'].collect(&:first).include?(k)
@@ -229,6 +256,14 @@ class Flor::Procedure < Flor::Node
229
256
  wrap(hh)
230
257
  end
231
258
 
259
+ def stays_an_att?(t)
260
+
261
+ t[1].length == 1 &&
262
+ t[1][0].is_a?(Array) &&
263
+ t[1][0][1] == [] &&
264
+ TRUE_ATTS.include?(t[1][0][0])
265
+ end
266
+
232
267
  def unatt_unkeyed_children(first_only=false)
233
268
 
234
269
  found = false
@@ -237,12 +272,13 @@ class Flor::Procedure < Flor::Node
237
272
  att_children.partition { |c|
238
273
  if found
239
274
  false
275
+ elsif stays_an_att?(c)
276
+ false
240
277
  else
241
278
  is_unkeyed = c[1].size == 1
242
279
  found = true if is_unkeyed && first_only
243
280
  is_unkeyed
244
- end
245
- }
281
+ end }
246
282
 
247
283
  unkeyed = unkeyed
248
284
  .collect { |c| c[1].first }
@@ -253,6 +289,28 @@ class Flor::Procedure < Flor::Node
253
289
  @node['tree'] = [ tree[0], cn, tree[2] ] if cn != children
254
290
  end
255
291
 
292
+ def rep_first_child
293
+
294
+ hd, cn, ln = tree
295
+
296
+ ri = cn.index { |ct| ct[0] == '_ref' || Flor.is_single_ref_tree?(ct) }
297
+
298
+ return unless ri
299
+
300
+ cn1 = cn.dup
301
+ rt = cn[ri]
302
+
303
+ cn1[ri] =
304
+ if rt[0] == '_ref'
305
+ [ '_rep', rt[1], rt[2] ]
306
+ else
307
+ s, _, l = rt
308
+ [ '_rep', [ [ '_sqs', s, l ] ], l ]
309
+ end
310
+
311
+ @node['tree'] = [ hd, cn1, ln ]
312
+ end
313
+
256
314
  def unatt_first_unkeyed_child
257
315
 
258
316
  unatt_unkeyed_children(true)
@@ -268,7 +326,9 @@ class Flor::Procedure < Flor::Node
268
326
  cn = Flor.dup(children)
269
327
  cn[ci] = [ '_sqs', c[0], c[2] ]
270
328
 
271
- @node['tree'] = [ tree[0], cn, tree[2] ]
329
+ t = tree
330
+
331
+ @node['tree'] = [ t[0], cn, t[2] ]
272
332
  end
273
333
 
274
334
  def stringify_first_child
@@ -292,7 +352,7 @@ class Flor::Procedure < Flor::Node
292
352
  receive
293
353
  end
294
354
 
295
- # The executor calls #do_receive, while most procedure implementations
355
+ # The executor calls #do_receive, while procedure implementations
296
356
  # override #receive...
297
357
  #
298
358
  def do_receive
@@ -302,15 +362,28 @@ class Flor::Procedure < Flor::Node
302
362
  from_child = nil
303
363
  from_child = cnodes.delete(from) if cnodes_any? && remove
304
364
 
305
- if node_closed?
306
- return receive_from_child_when_closed \
307
- if from_child || node_status_flavour
308
- return receive_when_closed
365
+ if di = @message['disable']
366
+ #
367
+ # the off: and the disable:/disabled: can achieve that
368
+
369
+ wrap_reply('disabled' => di)
370
+
371
+ elsif node_closed?
372
+
373
+ if from_child || node_status_flavour
374
+ receive_from_child_when_closed
375
+ else
376
+ receive_when_closed
377
+ end
378
+
309
379
  elsif node_ended?
310
- return receive_when_ended
311
- end
312
380
 
313
- receive
381
+ receive_when_ended
382
+
383
+ else
384
+
385
+ receive
386
+ end
314
387
  end
315
388
 
316
389
  def message_cause
@@ -328,14 +401,6 @@ class Flor::Procedure < Flor::Node
328
401
 
329
402
  c = message_cause
330
403
 
331
- #puts "<<<"
332
- ##p @message
333
- ##p @message['cause']
334
- #p node_status
335
- ##p @node['on_error']
336
- #p c
337
- ##puts caller[0, 3]
338
- #puts ">>>"
339
404
  open_node \
340
405
  unless
341
406
  (node_status_flavour == 'on-error') || # TODO use the cause ???
@@ -359,7 +424,7 @@ class Flor::Procedure < Flor::Node
359
424
 
360
425
  def receive_from_child_when_closed
361
426
 
362
- (cnodes.empty? && pop_on_receive_last) || wrap_reply
427
+ (cnodes_empty? && pop_on_receive_last) || wrap_reply
363
428
  end
364
429
 
365
430
  def receive_when_closed
@@ -380,7 +445,21 @@ class Flor::Procedure < Flor::Node
380
445
 
381
446
  def from_att?
382
447
 
383
- @fcid && (c = children[@fcid]) && c[0] == '_att'
448
+ @fcid &&
449
+ Flor.same_branch?(nid, from) &&
450
+ (c = children[@fcid]) &&
451
+ c[0] == '_att'
452
+ end
453
+
454
+ def from_sub_nid
455
+
456
+ i = from.split('-')[1]
457
+ i ? i.to_i : false
458
+ end
459
+
460
+ def from_error_handler?
461
+
462
+ message['from_on_error'] == nid
384
463
  end
385
464
 
386
465
  def last_receive?
@@ -448,19 +527,25 @@ class Flor::Procedure < Flor::Node
448
527
  #
449
528
  # Has no effect if there is no parent node.
450
529
  #
451
- def store_on(key, prc=nil)
452
-
453
- pnode = parent_node; return unless pnode
454
-
455
- prc ||= payload['ret']
530
+ def store_on(key, prc=payload['ret'], criteria=[ '*' ])
456
531
 
457
532
  return unless Flor.is_func_tree?(prc)
458
533
 
534
+ pnode =
535
+ @node; loop do
536
+ pnode = parent_node(pnode)
537
+ return unless pnode
538
+ break unless %w[ _if _unless ].include?(pnode['heap'])
539
+ end
540
+
459
541
  flavour = "on_#{key}"
460
542
 
461
543
  prc[1][flavour] = true
462
544
 
463
- (pnode[flavour] ||= []) << prc
545
+ a = (pnode[flavour] ||= [])
546
+ i = a.index { |e| e[0] == criteria } || -1
547
+ #
548
+ a.insert(i, [ criteria, prc ])
464
549
  end
465
550
 
466
551
  # Used by 'cursor' (and 'loop') when
@@ -491,9 +576,17 @@ class Flor::Procedure < Flor::Node
491
576
  m = {}
492
577
  m['point'] = 'receive'
493
578
  m['exid'] = exid
494
- m['nid'] = @node['noreply'] ? nil : parent
495
579
  m['from'] = nid
496
580
 
581
+ m['nid'] = # what node should receive this reply?
582
+ if @node['noreply'] # for backward compatibility 2018-10-29
583
+ nil
584
+ elsif @node.has_key?('replyto')
585
+ @node['replyto'] # nil or something like '0_1' or '0_2-4'
586
+ else
587
+ parent
588
+ end
589
+
497
590
  m['sm'] = @message['m']
498
591
 
499
592
  m['cause'] = @message['cause'] if @message.has_key?('cause')
@@ -587,43 +680,70 @@ class Flor::Procedure < Flor::Node
587
680
  r
588
681
  end
589
682
 
590
- def set_var(mode, k, v)
683
+ def set_var(mode, path, v)
591
684
 
592
- fail IndexError.new("cannot set domain variables") if mode == 'd'
685
+ fail IndexError.new(
686
+ 'cannot set domain variables') if mode == 'd'
593
687
 
594
688
  begin
595
689
 
596
- node = lookup_var_node(@node, mode, k)
597
- node = lookup_var_node(@node, 'l', k) if node.nil? && mode == ''
690
+ node = lookup_var_node(@node, mode, path)
691
+ node = lookup_var_node(@node, 'l', path) if node.nil? && mode == ''
598
692
 
599
- return Dense.set(node['vars'], k, v) if node
693
+ return Dense.set(node['vars'], path, v) if node
600
694
 
601
695
  rescue IndexError
602
696
  end
603
697
 
604
- fail IndexError.new("couldn't set var #{mode}v.#{k}")
698
+ fail IndexError.new(
699
+ "couldn't set var #{Flor.path_to_s([ "#{mode}v" ] + path)}")
605
700
  end
606
701
 
607
- def set_field(k, v)
702
+ def set_field(path, v)
608
703
 
609
- Dense.set(payload.copy, k, v)
704
+ Dense.set(payload.copy, path, v)
610
705
 
611
706
  rescue IndexError
612
707
 
613
- fail IndexError.new("couldn't set field #{k}")
708
+ fail IndexError.new(
709
+ "couldn't set field #{Flor.path_to_s(path)}")
710
+ end
711
+
712
+ def set_value(path, value)
713
+
714
+ path = Dense::Path.make(path).to_a if path.is_a?(String)
715
+ #p [ path, '<-', value ]
716
+
717
+ if path.length < 2
718
+ set_var('', path, value)
719
+ else
720
+ case path.first
721
+ when /\Af(?:ld|ield)?\z/
722
+ set_field(path[1..-1], value)
723
+ when /\A([lgd]?)v(?:ar|ariable)?\z/
724
+ set_var($1, path[1..-1], value)
725
+ else
726
+ set_var('', path, value)
727
+ end
728
+ end
614
729
  end
615
730
 
616
- def set_value(k, v)
731
+ def splat_value(paths, value)
732
+
733
+ val = value.dup
617
734
 
618
- return if k == '_'
735
+ while pa = paths.shift
619
736
 
620
- cat, mod, key = key_split(k)
737
+ pa = Dense::Path.make(pa).to_a if pa.is_a?(String)
621
738
 
622
- case cat[0, 1]
623
- when 'f' then set_field(key, v)
624
- when 'v' then set_var(mod, key, v)
625
- #when 'w' then set_war(key, v)
626
- else fail IndexError.new("don't know how to set #{k.inspect}")
739
+ if m = pa.last.match(Flor::SPLAT_REGEX)
740
+ k, u = m[1, 2]
741
+ l = (u == '_') ? val.length - paths.length : u.to_i
742
+ set_value(pa[0..-2] + [ k ], val.take(l)) if k.length > 0
743
+ val = val.drop(l)
744
+ else
745
+ set_value(pa, val.shift)
746
+ end
627
747
  end
628
748
  end
629
749
 
@@ -639,35 +759,34 @@ class Flor::Procedure < Flor::Node
639
759
  cni = fun[1]['cnid'] # closure nid
640
760
 
641
761
  t = fun[1]['tree']
642
- t = t || lookup_tree(fni) # TODO when fun[1]['tree'] is settled, drop me
762
+ #t = t || lookup_tree(fni) # TODO when fun[1]['tree'] is settled, drop me
643
763
  fail ArgumentError.new("couldn't find function at #{fni}") unless t
644
764
 
645
765
  t = t[0] if t[0].is_a?(Array)
646
766
  t = t[1][0] if t[0] == '_att'
647
767
 
648
- sig = t[1].select { |c| c[0] == '_att' }
649
- sig = sig.drop(1) if t[0] == 'define'
768
+ t[1][0][0] = '_name' if t[0] == 'define'
650
769
 
651
- vars = opts[:vars] || {}
652
- #
653
- vars['arguments'] = args # should I dup?
654
- #
655
- sig.each_with_index do |att, i|
656
- key = att[1].first[0]
657
- vars[key] = args[i]
658
- end
770
+ #vars = opts[:vars] || {}
771
+ #vars['arguments'] = args # Should I dup? Dup upstream?
659
772
 
660
773
  ms = wrap(
661
774
  'point' => 'execute',
662
775
  'nid' => ani,
663
776
  'tree' => [ '_apply', t[1], line ],
664
- 'vars' => vars,
777
+ 'arguments' => args,
665
778
  'cnid' => cni)
666
779
 
667
780
  if oe = fun[1]['on_error']
668
781
  ms.first['on_error'] = oe
669
782
  end
670
783
 
784
+ #if fs = opts[:fields]
785
+ # ms.first['payload'].merge!(fs)
786
+ #end
787
+ #
788
+ # an idea from "sort" apply, may be useful later on...
789
+
671
790
  ms
672
791
  end
673
792
 
@@ -739,7 +858,7 @@ class Flor::Procedure < Flor::Node
739
858
  end
740
859
 
741
860
  # Handle an incoming cancel message when the node has closed.
742
- # Open for override (overridden by "cursor" and "until")
861
+ # Open for override (overridden by "sequence", "cursor", and "until")
743
862
  #
744
863
  def cancel_when_closed
745
864