flor 0.15.0 → 0.16.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.
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
@@ -0,0 +1,51 @@
1
+
2
+ class Flor::Pro::Ref < Flor::Procedure
3
+
4
+ names %w[ _ref _rep ]
5
+
6
+ def pre_execute
7
+
8
+ @node['rets'] = []
9
+ end
10
+
11
+ def receive_last
12
+
13
+ rs = @node['rets']
14
+ rs = rs[0] if rs.size == 1 && rs[0].match(/[.\[]/)
15
+ pa = Dense::Path.make(rs).to_a
16
+
17
+ payload['ret'] =
18
+ if tree[0] == '_rep'
19
+ pa
20
+ elsif pa.size == 2 && pa[1] == 'ret' && field?(pa)
21
+ parent ?
22
+ parent_node_procedure.node_payload_ret :
23
+ node_payload_ret
24
+ else
25
+ lookup_value(pa)
26
+ end
27
+
28
+ super
29
+ end
30
+
31
+ protected
32
+
33
+ def field?(path)
34
+
35
+ (s = path[0]) && s.is_a?(String) && s.match(/\Af(ld|ield)?\z/)
36
+ end
37
+
38
+ def lookup_value(path)
39
+
40
+ super(path)
41
+
42
+ rescue KeyError => ke
43
+
44
+ return nil if field?(ke.work_path)
45
+ return nil if child_id == 1 && (n = parent_node) && n['heat0'] == '_head'
46
+ return nil if ke.miss[1].any? && ke.miss[4].empty?
47
+
48
+ raise
49
+ end
50
+ end
51
+
@@ -0,0 +1,27 @@
1
+
2
+ class Flor::Pro::RegularExpressionString < Flor::Procedure
3
+
4
+ name '_rxs'
5
+
6
+ def pre_execute
7
+
8
+ @node['rets'] = []
9
+ @node['atts'] = []
10
+ end
11
+
12
+ def execute_child(index=0, sub=nil, h=nil)
13
+
14
+ payload['ret'] = node_payload_ret
15
+ # always pass the noe_payload_ret to children
16
+
17
+ super
18
+ end
19
+
20
+ def receive_last
21
+
22
+ rex = [ '_rxs', "/#{@node['rets'].join}/#{att('rxopts')}", tree[2] ]
23
+
24
+ wrap('ret' => rex)
25
+ end
26
+ end
27
+
@@ -5,15 +5,20 @@ class Flor::Pro::Val < Flor::Procedure
5
5
 
6
6
  def wrap_reply
7
7
 
8
- if node_open?
8
+ payload['ret'] = tree_to_value(@node['heat']) \
9
+ if node_open?
9
10
 
10
- heat = @node['heat']
11
- heat = nil if heat[0] == '_nul'
11
+ super
12
+ end
12
13
 
13
- payload['ret'] = heat
14
- end
14
+ protected
15
15
 
16
- super
16
+ def tree_to_value(t)
17
+
18
+ case t[0]
19
+ when '_func', '_proc', '_tasker' then t
20
+ else t[1]
21
+ end
17
22
  end
18
23
  end
19
24
 
@@ -1,5 +1,5 @@
1
1
 
2
- class Flor::Pro::Logo < Flor::Procedure
2
+ class Flor::Pro::Andor < Flor::Procedure
3
3
  #
4
4
  # When `and` evaluates the children and returns false as soon
5
5
  # as one of returns a falsy value. Returns true else.
@@ -23,7 +23,7 @@ class Flor::Pro::Logo < Flor::Procedure
23
23
 
24
24
  def execute
25
25
 
26
- payload['ret'] = @node['heat0'] == 'and'
26
+ payload['ret'] = (heap == 'and')
27
27
 
28
28
  super
29
29
  end
@@ -32,11 +32,9 @@ class Flor::Pro::Logo < Flor::Procedure
32
32
 
33
33
  c = children[@fcid]; return super if c[0] == '_att' && [1].size == 2
34
34
 
35
- h0 = @node['heat0']
36
-
37
35
  ret = Flor.true?(payload['ret'])
38
36
 
39
- return wrap_reply if ((h0 == 'or' && ret) || (h0 == 'and' && ! ret))
37
+ return wrap_reply if ((heap == 'or' && ret) || (heap == 'and' && ! ret))
40
38
 
41
39
  super
42
40
  end
@@ -51,7 +49,7 @@ class Flor::Pro::Logo < Flor::Procedure
51
49
  # def receive_last
52
50
  #
53
51
  # payload['ret'] =
54
- # if @node['heat0'] == 'or'
52
+ # if heap == 'or'
55
53
  # !! @node['rets'].index { |r| Flor.true?(r) }
56
54
  # else
57
55
  # ! @node['rets'].index { |r| Flor.false?(r) }
@@ -23,6 +23,41 @@ class Flor::Pro::Apply < Flor::Procedure
23
23
  # ```
24
24
  # where flor figures out by itself it has to use this "apply" procedure
25
25
  # to call the function.
26
+ #
27
+ # ## rubyesque blocks
28
+ #
29
+ # In Ruby, one case pass a block on a function call:
30
+ # ```ruby
31
+ # def f(i)
32
+ # i * yield
33
+ # end
34
+ # p f(5) { |j| 10 }
35
+ # ```
36
+ # which just prints `50`.
37
+ #
38
+ # This can be achieved in flor like this:
39
+ # ```
40
+ # define f i
41
+ # * i (yield _)
42
+ # f 5
43
+ # 10
44
+ # echo f.ret
45
+ # ```
46
+ #
47
+ # If one needs to have a "block" with parameters, it can be done by having
48
+ # an anonymous function definition has the only thing in the block:
49
+ # ```
50
+ # define f i
51
+ # + i (yield i)
52
+ # f 5
53
+ # def j
54
+ # * 3 j
55
+ # f.ret #=> 20
56
+ # ```
57
+ #
58
+ # ## see also
59
+ #
60
+ # define.
26
61
 
27
62
  name 'apply'
28
63
 
@@ -38,22 +73,57 @@ class Flor::Pro::Apply < Flor::Procedure
38
73
  super
39
74
  end
40
75
 
76
+ def receive_last_att
77
+
78
+ return do_apply unless tree[1][@ncid]
79
+
80
+ rewrite_block_into_function
81
+
82
+ super
83
+ end
84
+
41
85
  def receive_last
42
86
 
43
- args = @node['atts'].collect(&:last)
87
+ do_apply
88
+ end
44
89
 
90
+ protected
91
+
92
+ def do_apply
93
+
94
+ args = @node['atts']
45
95
  nht = @node['heat']
46
96
 
47
97
  src =
48
98
  Flor.is_proc_tree?(nht) && nht[1]['proc'] == 'apply' ?
49
- args.shift :
99
+ args.shift[1] :
50
100
  nht
51
101
 
102
+ args << [ 'yield', payload_ret ] \
103
+ if ! from_att? && Flor.is_func_tree?(payload_ret)
104
+
52
105
  ms = apply(src, args, tree[2])
53
106
 
54
107
  @node['applied'] = ms.first['nid']
55
108
 
56
109
  ms
57
110
  end
111
+
112
+ def rewrite_block_into_function
113
+
114
+ t = tree
115
+ cn = t[1][@ncid..-1]
116
+ c0 = cn[0]
117
+
118
+ return if cn.size == 1 && %w[ def fun ].include?(c0[0])
119
+ #
120
+ # if the single child is a "def" or "fun", no need to rewrite,
121
+ # that single child function becomes the block
122
+
123
+ bt = [ 'def', cn, t[2] ]
124
+ t[1] = (@fcid ? t[1][0..@fcid] : []) + [ bt ]
125
+
126
+ @node['tree'] = t
127
+ end
58
128
  end
59
129
 
@@ -42,16 +42,28 @@ class Flor::Pro::Arith < Flor::Procedure
42
42
  fail Flor::FlorError.new('modulo % requires at least 2 arguments', self) \
43
43
  if sign == :% && count < 2
44
44
 
45
- payload['ret'] =
45
+ ret =
46
46
  if @node['rets'].compact.empty?
47
47
  DEFAULTS[sign]
48
48
  elsif sign == :+
49
- @node['rets'].reduce { |r, e| r + (r.is_a?(String) ? e.to_s : e) }
49
+ @node['rets'].reduce { |r, e|
50
+ # TODO use djan instead of #to_s?
51
+ # TODO use JSON instead of #to_s or djan?
52
+ r + (r.is_a?(String) ? e.to_s : e) }
50
53
  else
51
- @node['rets'].reduce(&sign)
54
+ rets = @node['rets']
55
+ rets = rets.collect(&:to_f) \
56
+ if sign == :/ || rets.find { |r| r.is_a?(Float) }
57
+ rets.reduce(&sign)
52
58
  end
53
59
 
54
- wrap_reply
60
+ unless ret.is_a?(String)
61
+ round = ret.round
62
+ ret = round if round.to_f.to_s == ret.to_f.to_s
63
+ end
64
+ # follow JSON somehow, in show "1.0" as "1"...
65
+
66
+ wrap_reply('ret' => ret)
55
67
  end
56
68
  end
57
69
 
@@ -0,0 +1,100 @@
1
+
2
+ class Flor::Pro::ArrayQmark < Flor::Procedure
3
+ #
4
+ # Returns true if the argument or the incoming ret matches in type.
5
+ #
6
+ # ```
7
+ # array? [] # => true,
8
+ # []; array? _ # => true,
9
+ # array? false # => false,
10
+ # false; array? _ # => false,
11
+ #
12
+ # object? {} # => true,
13
+ # object? false # => false,
14
+ #
15
+ # number? 0 # => true,
16
+ # number? 0.1 # => true,
17
+ # number? "dang" # => false,
18
+ # number? [] # => false,
19
+ #
20
+ # string? "hello" # => true,
21
+ # string? [] # => false,
22
+ #
23
+ # true? true # => true,
24
+ # true? false # => false,
25
+ # true? 0 # => false,
26
+ #
27
+ # boolean? true # => true,
28
+ # boolean? false # => true,
29
+ # boolean? [] # => false,
30
+ #
31
+ # null? null # => true,
32
+ # null? 0 # => false,
33
+ #
34
+ # false? false # => true,
35
+ # false? true # => false,
36
+ # false? "false" # => false,
37
+ #
38
+ # nil? null # => true,
39
+ # nil? 0 # => false,
40
+ #
41
+ # pair? [ 0 1 ] # => true,
42
+ # pair? [] # => false,
43
+ # pair? 0 # => false,
44
+ #
45
+ # float? 1.0 # => true,
46
+ # float? 1 # => false,
47
+ # float? {} # => false,
48
+ #
49
+ # boolean? true tag: "xxx" # => true,
50
+ # true; boolean? tag: "xxx" # => true,
51
+ # string? {} tag: "xxx" # => false,
52
+ # {}; string? tag: "xxx" # => false,
53
+ # ```
54
+ #
55
+ # ## see also
56
+ #
57
+ # type-of, type
58
+
59
+ names %w[
60
+ array? object? boolean? number? string? null?
61
+ list? dict? hash? nil?
62
+ false? true?
63
+ pair? float? ]
64
+
65
+ def pre_execute
66
+
67
+ @node['ret'] = receive_payload_ret
68
+
69
+ unatt_unkeyed_children
70
+ end
71
+
72
+ def receive_last
73
+
74
+ t = Flor.type(@node['ret'])
75
+
76
+ r =
77
+ case h = heap
78
+
79
+ when 'array?', 'list?' then t == :array
80
+ when 'object?', 'hash?' 'dict?' then t == :object
81
+ when 'boolean?' then t == :boolean
82
+ when 'number?' then t == :number
83
+ when 'string?' then t == :string
84
+ when 'null?', 'nil?' then t == :null
85
+
86
+ when 'false?' then @node['ret'] == false
87
+ when 'true?' then @node['ret'] == true
88
+
89
+ when 'pair?' then t == :array && @node['ret'].length == 2
90
+ when 'float?' then t == :number && @node['ret'].to_s.index('.') != nil
91
+
92
+ else fail(Flor::FlorError.new("#{h.inspect} not yet implemented", self))
93
+ end
94
+
95
+ wrap_reply('ret' => r)
96
+ end
97
+
98
+ def receive_payload_ret; payload['ret']; end # don't duplicate the ret
99
+ end
100
+
@@ -60,7 +60,6 @@ class Flor::Pro::Break < Flor::Procedure
60
60
 
61
61
  ref = att('ref')
62
62
  nid = tags_to_nids(ref).first || @node['heat'][1]['nid']
63
- #p [ :break, @node['heap'], nid ]
64
63
 
65
64
  payload['ret'] = att(nil) if has_att?(nil)
66
65
 
@@ -68,7 +67,7 @@ class Flor::Pro::Break < Flor::Procedure
68
67
 
69
68
  if nid
70
69
 
71
- ms += wrap_cancel('nid' => nid, 'flavour' => @node['heap'])
70
+ ms += wrap_cancel('nid' => nid, 'flavour' => heap)
72
71
  end
73
72
 
74
73
  unless is_ancestor_node?(nid)
@@ -87,7 +87,7 @@ class Flor::Pro::Case < Flor::Procedure
87
87
  #
88
88
  # ## see also
89
89
  #
90
- # Match.
90
+ # Match, cond, if.
91
91
 
92
92
  name 'case'
93
93
 
@@ -1,7 +1,7 @@
1
1
 
2
2
  class Flor::Pro::Cmp < Flor::Procedure
3
3
 
4
- names %w[ = == < > ]
4
+ names %w[ = == < > <= >= != <> ]
5
5
 
6
6
  def pre_execute
7
7
 
@@ -14,7 +14,8 @@ class Flor::Pro::Cmp < Flor::Procedure
14
14
  if @node['rets'].size > 1
15
15
  case tree[0]
16
16
  when '=', '==' then check_equal
17
- when '<', '>' then check_lesser
17
+ when '!=', '<>' then ! check_equal
18
+ when '<', '>', '>=', '<=' then check_lesser
18
19
  else true
19
20
  end
20
21
  else
@@ -4,7 +4,7 @@ require 'flor/pcore/iterator'
4
4
 
5
5
  class Flor::Pro::Collect < Flor::Macro::Iterator
6
6
  #
7
- # Collect is a simplified version of [map](map.md).
7
+ # A simplified version of [map](map.md).
8
8
  #
9
9
  # ```
10
10
  # map [ 1, 2, 3 ]
@@ -38,7 +38,7 @@ class Flor::Pro::Collect < Flor::Macro::Iterator
38
38
  #
39
39
  # ## see also
40
40
  #
41
- # Map.
41
+ # Map, cmap, ccollect.
42
42
 
43
43
  name 'collect'
44
44
 
@@ -35,9 +35,27 @@ class Flor::Pro::Cond < Flor::Procedure
35
35
  # else | "ten or bigger"
36
36
  # ```
37
37
  #
38
+ # Please note that, as for [if](if.md), composite consequences have to be
39
+ # "packaged":
40
+ # ```
41
+ # cond
42
+ # a < 4
43
+ # sequence
44
+ # do_this
45
+ # do_that
46
+ # a < 7
47
+ # concurrence
48
+ # do_this
49
+ # do_that
50
+ # else
51
+ # sequence
52
+ # do_this 'default'
53
+ # do_that 'default'
54
+ # ```
55
+ #
38
56
  # ## see also
39
57
  #
40
- # If, match.
58
+ # If, match, case.
41
59
 
42
60
  name 'cond'
43
61