flor 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +9 -0
  3. data/CREDITS.md +21 -0
  4. data/LICENSE.txt +4 -1
  5. data/Makefile +4 -0
  6. data/README.md +8 -0
  7. data/flor.gemspec +10 -10
  8. data/lib/flor.rb +2 -2
  9. data/lib/flor/changes.rb +3 -3
  10. data/lib/flor/colours.rb +14 -8
  11. data/lib/flor/conf.rb +63 -58
  12. data/lib/flor/core.rb +4 -4
  13. data/lib/flor/core/executor.rb +65 -29
  14. data/lib/flor/core/node.rb +37 -20
  15. data/lib/flor/core/procedure.rb +182 -40
  16. data/lib/flor/core/texecutor.rb +125 -52
  17. data/lib/flor/djan.rb +111 -82
  18. data/lib/flor/dollar.rb +31 -30
  19. data/lib/flor/flor.rb +314 -237
  20. data/lib/flor/id.rb +7 -2
  21. data/lib/flor/log.rb +250 -245
  22. data/lib/flor/parser.rb +72 -38
  23. data/lib/flor/pcore/_arr.rb +10 -10
  24. data/lib/flor/pcore/_att.rb +49 -14
  25. data/lib/flor/pcore/_coll.rb +18 -0
  26. data/lib/flor/pcore/_obj.rb +23 -7
  27. data/lib/flor/pcore/_pat_.rb +1 -1
  28. data/lib/flor/pcore/_pat_guard.rb +8 -0
  29. data/lib/flor/pcore/_pat_obj.rb +3 -3
  30. data/lib/flor/pcore/_pat_or.rb +4 -0
  31. data/lib/flor/pcore/_pat_regex.rb +24 -0
  32. data/lib/flor/pcore/_skip.rb +4 -0
  33. data/lib/flor/pcore/_val.rb +0 -1
  34. data/lib/flor/pcore/all.rb +111 -0
  35. data/lib/flor/pcore/any.rb +83 -0
  36. data/lib/flor/pcore/arith.rb +35 -6
  37. data/lib/flor/pcore/break.rb +39 -1
  38. data/lib/flor/pcore/case.rb +82 -4
  39. data/lib/flor/pcore/cmp.rb +7 -7
  40. data/lib/flor/pcore/collect.rb +50 -0
  41. data/lib/flor/pcore/cond.rb +17 -3
  42. data/lib/flor/pcore/cursor.rb +8 -2
  43. data/lib/flor/pcore/detect.rb +45 -0
  44. data/lib/flor/pcore/each.rb +52 -0
  45. data/lib/flor/pcore/empty.rb +60 -0
  46. data/lib/flor/pcore/filter.rb +94 -0
  47. data/lib/flor/pcore/find.rb +67 -0
  48. data/lib/flor/pcore/for_each.rb +65 -0
  49. data/lib/flor/pcore/includes.rb +32 -0
  50. data/lib/flor/pcore/inject.rb +55 -0
  51. data/lib/flor/pcore/iterator.rb +151 -0
  52. data/lib/flor/pcore/keys.rb +60 -0
  53. data/lib/flor/pcore/length.rb +34 -7
  54. data/lib/flor/pcore/logo.rb +18 -0
  55. data/lib/flor/pcore/loop.rb +4 -0
  56. data/lib/flor/pcore/map.rb +77 -46
  57. data/lib/flor/pcore/match.rb +8 -2
  58. data/lib/flor/pcore/matchr.rb +4 -5
  59. data/lib/flor/pcore/move.rb +3 -3
  60. data/lib/flor/pcore/noeval.rb +13 -0
  61. data/lib/flor/pcore/not.rb +16 -0
  62. data/lib/flor/pcore/on.rb +172 -0
  63. data/lib/flor/pcore/on_cancel.rb +54 -0
  64. data/lib/flor/pcore/on_error.rb +68 -0
  65. data/lib/flor/pcore/rand.rb +2 -2
  66. data/lib/flor/pcore/range.rb +2 -1
  67. data/lib/flor/pcore/reduce.rb +124 -0
  68. data/lib/flor/pcore/reverse.rb +46 -0
  69. data/lib/flor/pcore/select.rb +72 -0
  70. data/lib/flor/pcore/set.rb +8 -0
  71. data/lib/flor/pcore/stall.rb +10 -0
  72. data/lib/flor/pcore/to_array.rb +61 -0
  73. data/lib/flor/pcore/until.rb +34 -0
  74. data/lib/flor/punit/cancel.rb +30 -5
  75. data/lib/flor/punit/ccollect.rb +11 -0
  76. data/lib/flor/punit/cmap.rb +10 -5
  77. data/lib/flor/punit/concurrence.rb +42 -51
  78. data/lib/flor/punit/cron.rb +33 -0
  79. data/lib/flor/punit/do_trap.rb +42 -0
  80. data/lib/flor/punit/every.rb +48 -13
  81. data/lib/flor/punit/graft.rb +3 -3
  82. data/lib/flor/punit/on_timeout.rb +38 -0
  83. data/lib/flor/punit/schedule.rb +69 -6
  84. data/lib/flor/punit/signal.rb +54 -0
  85. data/lib/flor/punit/sleep.rb +1 -1
  86. data/lib/flor/punit/task.rb +4 -1
  87. data/lib/flor/punit/trap.rb +188 -13
  88. data/lib/flor/tools/shell.rb +408 -62
  89. data/lib/flor/tools/shell_out.rb +31 -0
  90. data/lib/flor/unit.rb +1 -1
  91. data/lib/flor/unit/caller.rb +177 -0
  92. data/lib/flor/unit/executor.rb +1 -0
  93. data/lib/flor/unit/ganger.rb +15 -21
  94. data/lib/flor/unit/hook.rb +1 -1
  95. data/lib/flor/unit/hooker.rb +22 -10
  96. data/lib/flor/unit/loader.rb +22 -22
  97. data/lib/flor/unit/logger.rb +63 -36
  98. data/lib/flor/unit/models.rb +6 -1
  99. data/lib/flor/unit/models/execution.rb +12 -1
  100. data/lib/flor/unit/models/message.rb +7 -0
  101. data/lib/flor/unit/models/trap.rb +31 -17
  102. data/lib/flor/unit/scheduler.rb +18 -10
  103. data/lib/flor/unit/storage.rb +83 -23
  104. data/lib/flor/unit/waiter.rb +1 -2
  105. metadata +96 -52
  106. data/lib/flor/deep.rb +0 -144
  107. data/lib/flor/punit/on.rb +0 -57
  108. data/lib/flor/unit/runner.rb +0 -84
  109. data/match.md +0 -22
@@ -20,6 +20,10 @@ class Flor::Pro::PatOr < Flor::Pro::PatContainer
20
20
  b = payload.delete('_pat_binding')
21
21
  return wrap_match_reply(b) if b
22
22
 
23
+ elsif ct == '_' && val != '_'
24
+
25
+ return wrap_no_match_reply
26
+
23
27
  elsif payload['ret'] == val
24
28
 
25
29
  return wrap_match_reply({})
@@ -0,0 +1,24 @@
1
+
2
+ require 'flor/pcore/_pat_'
3
+
4
+
5
+ class Flor::Pro::PatRegex < Flor::Pro::PatContainer
6
+
7
+ name '_pat_regex'
8
+
9
+ def execute
10
+
11
+ return wrap_no_match_reply unless val.is_a?(String)
12
+
13
+ rex = Flor.to_regex(tree[1])
14
+ m = rex.match(val)
15
+
16
+ return wrap_no_match_reply unless m
17
+
18
+ payload['_pat_binding'] = { 'matched' => val, 'match' => m.to_a }
19
+ payload.delete('_pat_val')
20
+
21
+ wrap_reply
22
+ end
23
+ end
24
+
@@ -14,6 +14,10 @@ class Flor::Pro::Skip < Flor::Procedure
14
14
  # _skip 7 # after 7 messages will go on
15
15
  # break ref: 'xx'
16
16
  # ```
17
+ #
18
+ # ## see also
19
+ #
20
+ # Stall.
17
21
 
18
22
  name '_skip'
19
23
 
@@ -8,7 +8,6 @@ class Flor::Pro::Val < Flor::Procedure
8
8
  if node_open?
9
9
 
10
10
  heat = @node['heat']
11
- #heat = nil if heat == [ '_proc', 'val', -1 ] || heat[0] == '_nul'
12
11
  heat = nil if heat[0] == '_nul'
13
12
 
14
13
  payload['ret'] = heat
@@ -0,0 +1,111 @@
1
+
2
+ require 'flor/pcore/iterator'
3
+
4
+
5
+ class Flor::Pro::All < Flor::Pro::Iterator
6
+ #
7
+ # Returns true if all the elements in a collection return true
8
+ # for the given function.
9
+ #
10
+ # ```
11
+ # all? [ 1, 2, 3 ]
12
+ # def elt \ elt > 0
13
+ # #
14
+ # # yields true
15
+ #
16
+ # all? [ 1, 2, 3 ]
17
+ # def elt \ elt > 2
18
+ # #
19
+ # # yields false
20
+ # ```
21
+ #
22
+ # ```
23
+ # all? { a: 'A', b: 'B' }
24
+ # def key, val \ val == 'A' or val == 'B'
25
+ # #
26
+ # # yields true
27
+ # ```
28
+ #
29
+ # ### without a function
30
+ #
31
+ # For an array, yields true if all the elements are "trueish" (not nil,
32
+ # not false).
33
+ #
34
+ # ```
35
+ # all? [] # yields true
36
+ # all? [ 1 2 3 ] # yields true
37
+ # all? [ 1 false 3 ] # yields false
38
+ # ```
39
+ #
40
+ # For an object, yields true if all the values are trueish.
41
+ #
42
+ # ```
43
+ # all? {} # yields true
44
+ # all? { a: 'A', b: 'B', c: 'C' } # yields true
45
+ # all? { a: 'A', f: false, c: 'C' } # yields false
46
+ # ```
47
+ #
48
+ # ## incoming ret
49
+ #
50
+ # ```
51
+ # []
52
+ # all? # yields true
53
+ # [ 1 2 3 ]
54
+ # all? # yields true
55
+ # [ 1 false 3 ]
56
+ # all? # yields false
57
+ # ```
58
+ #
59
+ # ## iterating and functions
60
+ #
61
+ # Iterating functions accept 0 to 3 arguments when iterating over an
62
+ # array and 0 to 4 arguments when iterating over an object.
63
+ #
64
+ # Those arguments are `[ value, index, length ]` for arrays.
65
+ # They are `[ key, value, index, length ]` for objects.
66
+ #
67
+ # The corresponding `key`, `val`, `idx` and `len` variables are also
68
+ # set in the closure for the function call.
69
+ #
70
+ # ## see also
71
+ #
72
+ # Any?
73
+
74
+ name 'all?'
75
+
76
+ protected
77
+
78
+ def receive_iteration
79
+
80
+ # nothing to do
81
+ end
82
+
83
+ def function_mandatory?
84
+
85
+ false
86
+ end
87
+
88
+ def no_iterate
89
+
90
+ ret =
91
+ case col = @node['ocol']
92
+ when Array then col.all?
93
+ when Hash then col.values.all?
94
+ else false
95
+ end
96
+
97
+ wrap_reply('ret' => ret)
98
+ end
99
+
100
+ def iterator_over?
101
+
102
+ super ||
103
+ (@node['idx'] > 0 && ( ! Flor.true?(payload['ret'])))
104
+ end
105
+
106
+ def iterator_result
107
+
108
+ Flor.true?(payload['ret'])
109
+ end
110
+ end
111
+
@@ -0,0 +1,83 @@
1
+
2
+ require 'flor/pcore/find'
3
+
4
+
5
+ class Flor::Pro::Any < Flor::Pro::Find
6
+ #
7
+ # Returns `true` if at least one of the member of a collection returns
8
+ # something trueish for the given function. Returns `false` else.
9
+ #
10
+ # ```
11
+ # any? [ 1, 2, 3 ]
12
+ # def elt
13
+ # (elt % 2) == 0
14
+ # # yields `true` thanks to element `2`
15
+ # ```
16
+ #
17
+ # ```
18
+ # any? { a: 'A', b: 'B', c: 'C' }
19
+ # def key, val \ val == 'B'
20
+ # # yields `true` thanks to entry { b: 'B' }
21
+ # ```
22
+ #
23
+ # ## without a function
24
+ #
25
+ # It's OK to use "any?" without a function, it'll simply return
26
+ # false if the collection is empty, true else.
27
+ #
28
+ # ```
29
+ # any? [] # yields false
30
+ # any? [ 1 ] # yields true
31
+ # any? {} # yields false
32
+ # any? { a: 'A' } # yields true
33
+ # ```
34
+ #
35
+ # ## iterating and functions
36
+ #
37
+ # Iterating functions accept 0 to 3 arguments when iterating over an
38
+ # array and 0 to 4 arguments when iterating over an object.
39
+ #
40
+ # Those arguments are `[ value, index, length ]` for arrays.
41
+ # They are `[ key, value, index, length ]` for objects.
42
+ #
43
+ # The corresponding `key`, `val`, `idx` and `len` variables are also
44
+ # set in the closure for the function call.
45
+ #
46
+ # ## incoming ret array/object
47
+ #
48
+ # If not fed an array or object directly, "any?" will pick it from the
49
+ # payload "ret" field.
50
+ #
51
+ # ```
52
+ # [ 1, 2, 3 ]
53
+ # any? _ # yields true
54
+ # any? (def elt \ elt == 3) # yields true
55
+ # []
56
+ # any? _ # yields false
57
+ # any? (def elt \ elt == 3) # yields false
58
+ # ```
59
+ #
60
+ # ## see also
61
+ #
62
+ # Find, all?.
63
+
64
+ name 'any?'
65
+
66
+ protected
67
+
68
+ def function_mandatory?
69
+
70
+ false
71
+ end
72
+
73
+ def no_iterate
74
+
75
+ wrap_reply('ret' => @node['ocol'].any?)
76
+ end
77
+
78
+ def iterator_result
79
+
80
+ super != nil
81
+ end
82
+ end
83
+
@@ -1,5 +1,29 @@
1
1
 
2
2
  class Flor::Pro::Arith < Flor::Procedure
3
+ #
4
+ # The base implementation for + - + / %
5
+ #
6
+ # ```
7
+ # (+ 1 2 3) # ==> 6
8
+ #
9
+ # +
10
+ # 1
11
+ # 2
12
+ # 3 # ==> 6
13
+ #
14
+ # + \ 1; 2; 3 # ==> 6
15
+ # ```
16
+ #
17
+ # ```
18
+ # 1 - 2 - 3 # ==> -4
19
+ #
20
+ # -
21
+ # 1
22
+ # 2
23
+ # 3 # ==> -4
24
+ # ```
25
+ #
26
+ # ...
3
27
 
4
28
  names %w[ + - * / % ]
5
29
 
@@ -15,12 +39,17 @@ class Flor::Pro::Arith < Flor::Procedure
15
39
  sign = tree.first.to_sym
16
40
  count = @node['rets'].size
17
41
 
18
- if sign == :% && count < 2
19
- fail ArgumentError.new(
20
- "modulo % requires at least 2 arguments (line #{tree[2]})")
21
- end
22
-
23
- payload['ret'] = @node['rets'].reduce(&sign) || DEFAULTS[sign]
42
+ fail Flor::FlorError.new('modulo % requires at least 2 arguments', self) \
43
+ if sign == :% && count < 2
44
+
45
+ payload['ret'] =
46
+ if @node['rets'].compact.empty?
47
+ DEFAULTS[sign]
48
+ elsif sign == :+
49
+ @node['rets'].reduce { |r, e| r + (r.is_a?(String) ? e.to_s : e) }
50
+ else
51
+ @node['rets'].reduce(&sign)
52
+ end
24
53
 
25
54
  wrap_reply
26
55
  end
@@ -1,7 +1,7 @@
1
1
 
2
2
  class Flor::Pro::Break < Flor::Procedure
3
3
  #
4
- # Breaks or continues a "while" or "until".
4
+ # Breaks or continues a "while", "until", "loop" or an "cursor".
5
5
  #
6
6
  # ```
7
7
  # until false
@@ -10,6 +10,44 @@ class Flor::Pro::Break < Flor::Procedure
10
10
  # break if f.x == 1
11
11
  # # do something more
12
12
  # ```
13
+ #
14
+ # ## ref:
15
+ #
16
+ # Break and continue may be used from outside a loop, thanks to the
17
+ # `ref:` attribute:
18
+ #
19
+ # ```
20
+ # set l []
21
+ # concurrence
22
+ # cursor tag: 'x0'
23
+ # push l 0
24
+ # stall _
25
+ # sequence
26
+ # push l 1
27
+ # break ref: 'x0'
28
+ #
29
+ # # where l ends up containing [ 1, 0 ]
30
+ # ```
31
+ #
32
+ # ## "aliasing"
33
+ #
34
+ # A continue or a break may be "aliased", in other words stored in a
35
+ # local variable for reference in a sub-loop.
36
+ #
37
+ # ```
38
+ # cursor
39
+ # set outer-continue continue
40
+ # push f.l "$(nid)"
41
+ # cursor
42
+ # push f.l "$(nid)"
43
+ # outer-continue _ if "$(nid)" == '0_2_1_0_0'
44
+ #
45
+ # # where l yields [ '0_1_1', '0_2_0_1', '0_1_1-1', '0_2_0_1-1' ]
46
+ # ```
47
+ #
48
+ # ## see also
49
+ #
50
+ # While, until, loop and cursor.
13
51
 
14
52
  name 'break', 'continue'
15
53
 
@@ -24,6 +24,14 @@ class Flor::Pro::Case < Flor::Procedure
24
24
  # 'high'
25
25
  # ```
26
26
  #
27
+ # Non-array values are OK:
28
+ # ```
29
+ # case level
30
+ # 0; 'zero'
31
+ # 1; 'one'
32
+ # else; 'dunno'
33
+ # ```
34
+ #
27
35
  # ## else
28
36
  #
29
37
  # As seen in the example above, an "else" in lieu of an array acts as
@@ -31,6 +39,55 @@ class Flor::Pro::Case < Flor::Procedure
31
39
  #
32
40
  # If there is no else and no matching array, the case terminates and
33
41
  # doesn't set the field "ret".
42
+ #
43
+ # ### v.matched and $(matched)
44
+ #
45
+ # When it successfully matches, the matched value (the argument of the
46
+ # "case") is placed in the local (local to the then or else branch)
47
+ # variables under 'matched'.
48
+ #
49
+ # ```
50
+ # case 6
51
+ # 5; 'five'
52
+ # [ 1, 6 ]; v.matched
53
+ # else; 'zilch'
54
+ # # returns, well, 6...
55
+ # ```
56
+ #
57
+ # ```
58
+ # case 6
59
+ # 5; 'five'
60
+ # [ 1, 6 ]; "matched! >$(matched)<"
61
+ # else; 'zilch'
62
+ # # returns "matched! >6<"
63
+ # ```
64
+ #
65
+ # ## regular expressions
66
+ #
67
+ # It's OK to match with regular expressions:
68
+ # ```
69
+ # case 'ovomolzin'
70
+ # /a+/; 'ahahah'
71
+ # [ /u+/, /o+/ ]; 'ohohoh' # <--- matches here
72
+ # else; 'else'
73
+ # ```
74
+ #
75
+ # ### v.match and $(match)
76
+ #
77
+ # When matching with a regular expression, the local variable 'matched' is
78
+ # set, as seen above, but also 'match':
79
+ #
80
+ # ```
81
+ # case 'ovomolzin'
82
+ # /a+/; 'ahahah'
83
+ # [ /u+/, /^ovo(.+)$/ ]; "matched:$(match.1)"
84
+ # else; 'else'
85
+ # # yields "matched:molzin"
86
+ # ```
87
+ #
88
+ # ## see also
89
+ #
90
+ # Match.
34
91
 
35
92
  name 'case'
36
93
 
@@ -38,7 +95,7 @@ class Flor::Pro::Case < Flor::Procedure
38
95
 
39
96
  unatt_unkeyed_children
40
97
 
41
- @node['val'] = payload['ret'] if non_att_children.size.even?
98
+ @node['val'] = payload['ret'] if non_att_count.even?
42
99
  end
43
100
 
44
101
  def receive
@@ -93,10 +150,31 @@ class Flor::Pro::Case < Flor::Procedure
93
150
 
94
151
  def match?
95
152
 
96
- a = payload['ret']
97
- a = a.nil? ? [ a ] : Array(a)
153
+ v = @node['val']
154
+
155
+ array.each do |e|
156
+ m = do_match?(e, v)
157
+ return m if m
158
+ end
98
159
 
99
- a.include?(@node['val'])
160
+ nil
161
+ end
162
+
163
+ def do_match?(elt, val)
164
+
165
+ return { 'matched' => elt } if elt == val
166
+
167
+ m = val.is_a?(String) && elt.is_a?(Regexp) && elt.match(val)
168
+ return { 'matched' => elt, 'match' => m.to_a } if m
169
+
170
+ nil
171
+ end
172
+
173
+ def array
174
+
175
+ a = payload['ret']
176
+ a = [ a ] if Flor.is_regex_tree?(a) || ! a.is_a?(Array)
177
+ a.collect { |e| Flor.is_regex_tree?(e) ? Flor.to_regex(e) : e }
100
178
  end
101
179
  end
102
180