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
@@ -0,0 +1,68 @@
1
+
2
+ class Flor::Pro::OnError < Flor::Procedure
3
+ #
4
+ # Counterpart to the on_error: attribute.
5
+ #
6
+ # Takes a function definition (or a variable pointing to one of them)
7
+ # and makes sure the function is called when an error occurs at the
8
+ # current level or below.
9
+ #
10
+ # ```
11
+ # sequence
12
+ # set f.l []
13
+ # on_error (def err \ push f.l err.error.msg)
14
+ # push f.l 0
15
+ # push f.l x # <-- will fail because `x` is unknown
16
+ # push f.l 1
17
+ # ```
18
+ # Where the field `l` ends up containing
19
+ # `[ 0, "don't know how to apply \"x\"" ]`.
20
+ #
21
+ # ```
22
+ # set f.l []
23
+ #
24
+ # define error_handler err
25
+ # push f.l err.error.msg
26
+ #
27
+ # sequence
28
+ # on_error error_handler
29
+ # # ...
30
+ # ```
31
+ #
32
+ # ## on and on_error
33
+ #
34
+ # "on_error" is made to allow for `on error`, so that:
35
+ # ```
36
+ # sequence
37
+ # on error
38
+ # push f.l err.msg # a block with an `err` variable
39
+ # # ...
40
+ # ```
41
+ # gets turned into:
42
+ # ```
43
+ # sequence
44
+ # on_error
45
+ # def err # a anonymous function definition with an `err` argument
46
+ # push f.l err.msg
47
+ # # ...
48
+ # ```
49
+ #
50
+ # ## see also
51
+ #
52
+ # On, on_cancel.
53
+
54
+ name 'on_error'
55
+
56
+ def pre_execute
57
+
58
+ unatt_unkeyed_children
59
+ end
60
+
61
+ def receive_non_att
62
+
63
+ store_on(:error)
64
+
65
+ super
66
+ end
67
+ end
68
+
@@ -30,8 +30,8 @@ class Flor::Pro::Rand < Flor::Procedure
30
30
 
31
31
  a, b = determine_bounds
32
32
 
33
- fail ArgumentError.new(
34
- "'rand' expects an integer or a float"
33
+ fail Flor::FlorError.new(
34
+ "'#{tree[0]}' expects an integer or a float", self
35
35
  ) unless is_number?(a) && is_number?(b)
36
36
 
37
37
  payload['ret'] = Random.rand(a...b)
@@ -49,7 +49,8 @@ class Flor::Pro::Range < Flor::Procedure
49
49
  edn = aedn if aedn
50
50
  ste = aste if aste
51
51
 
52
- fail ArgumentError.new("#{@node['heat0']} step is 0") if ste == 0
52
+ fail Flor::FlorError.new("#{@node['heat0']} step is 0", self) \
53
+ if ste == 0
53
54
 
54
55
  #payload['ret'] = (sta..edn - 1).step(ste).to_a
55
56
  # doesn't accept negative steps
@@ -0,0 +1,124 @@
1
+
2
+ class Flor::Pro::Reduce < Flor::Pro::Iterator
3
+ #
4
+ # Reduce takes a collection and a function. It reduces the collection
5
+ # to a single result thanks to the function.
6
+ #
7
+ # ```
8
+ # reduce [ '0', 1, 'b', 3 ]
9
+ # def result element
10
+ # result + element
11
+ # # --> "01b3"
12
+ # ```
13
+ #
14
+ # An initial value is accepted (generally after the collection)
15
+ #
16
+ # ```
17
+ # reduce [ 0, 1, 2, 3, 4 ] 10
18
+ # def result i \ result + i
19
+ # # --> 20
20
+ # ```
21
+ #
22
+ # Passing a proc is OK too, but, in the case of a mathematical expression
23
+ # prefixing it with `v.` prevents premature rewriting...
24
+ #
25
+ # ```
26
+ # reduce [ 0, 1, 2, 3, 4 ] 10 v.+
27
+ # # --> 20
28
+ # ```
29
+ #
30
+ # ## iterating and functions
31
+ #
32
+ # Iterating functions accept 0 to 3 arguments when iterating over an
33
+ # array and 0 to 4 arguments when iterating over an object.
34
+ #
35
+ # Those arguments are `[ result, value, index, length ]` for arrays.
36
+ # They are `[ result, key, value, index, length ]` for objects.
37
+ #
38
+ # The corresponding `res`, `key`, `val`, `idx` and `len` variables are also
39
+ # set in the closure for the function call.
40
+ #
41
+ # ## see also
42
+ #
43
+ # Inject.
44
+
45
+ name 'reduce'
46
+
47
+ protected
48
+
49
+ def prepare_iterations
50
+
51
+ @node['args']
52
+ .each { |a|
53
+ if Flor.is_func_tree?(a)
54
+ @node['fun'] ||= a
55
+ elsif Flor.is_proc_tree?(a)
56
+ @node['fun'] ||= proc_to_fun(a)
57
+ elsif a.is_a?(Array) || a.is_a?(Hash)
58
+ @node['ocol'] ||= a
59
+ else
60
+ @node['res'] ||= a
61
+ end }
62
+
63
+ @node['ocol'] ||= node_payload_ret
64
+ ocol = @node['ocol']
65
+
66
+ fail Flor::FlorError.new(
67
+ "function not given to #{heap.inspect}", self
68
+ ) unless @node['fun']
69
+ fail Flor::FlorError.new(
70
+ "collection not given to #{heap.inspect}", self
71
+ ) unless ocol.is_a?(Array) || ocol.is_a?(Hash)
72
+
73
+ @node['col'] = Flor.to_coll(@node['ocol'])
74
+
75
+ @node['res'] ||= @node['col'].shift
76
+
77
+ @node['args'] = nil
78
+ end
79
+
80
+ def determine_iteration_vars
81
+
82
+ res = @node['res']
83
+ idx = @node['idx']
84
+ elt = @node['col'][idx]
85
+ len = @node['col'].length
86
+
87
+ if @node['ocol'].is_a?(Array)
88
+ { 'res' => res, 'elt' => elt,
89
+ 'idx' => idx, 'len' => len }
90
+ else
91
+ { 'res' => res, 'key' => elt[0], 'val' => elt[1],
92
+ 'idx' => idx, 'len' => len }
93
+ end
94
+ end
95
+
96
+ def receive_iteration
97
+
98
+ @node['res'] = payload['ret']
99
+ end
100
+
101
+ def iterator_result
102
+
103
+ @node['res']
104
+ end
105
+
106
+ def proc_to_fun(prc)
107
+
108
+ h = prc[1]['proc']
109
+ l = tree[2]
110
+
111
+ [ '_func',
112
+ { 'nid' => "#{nid}_0_1",
113
+ 'tree' =>
114
+ [ 'def', [
115
+ [ '_att', [ [ 'r', [], l ] ], l ],
116
+ [ '_att', [ [ 'x', [], l ] ], l ],
117
+ [ h, [ [ 'r', [], l ], [ 'x', [], l ] ], l ]
118
+ ], l ],
119
+ 'cnid' => '0', #
120
+ 'fun' => 0 }, # TODO really?
121
+ l ]
122
+ end
123
+ end
124
+
@@ -0,0 +1,46 @@
1
+
2
+ class Flor::Pro::Reverse < Flor::Procedure
3
+ #
4
+ # Reverses an array or a string.
5
+ #
6
+ # ```
7
+ # reverse [ 0, 2, 4 ]
8
+ # # --> sets f.ret to [ 4, 2, 0 ]
9
+ # reverse "melimelo"
10
+ # # --> sets f.ret to "olemilem"
11
+ # ```
12
+ #
13
+ # Reverses f.ret if there are no arguments
14
+ # ```
15
+ # [ 5, 6, 4 ] # sets f.ret to [ 5, 6, 4 ]
16
+ # reverse _ # sets f.ret to [ 4, 6, 5 ]
17
+ # ```
18
+ #
19
+ # Will fail if it finds nothing reversable.
20
+
21
+ name 'reverse'
22
+
23
+ def pre_execute
24
+
25
+ @node['ret'] = receive_payload_ret
26
+
27
+ unatt_unkeyed_children
28
+ end
29
+
30
+ def receive_payload_ret
31
+
32
+ r = payload['ret']
33
+ r.respond_to?(:reverse) ? r.reverse : false
34
+ end
35
+
36
+ def receive_last
37
+
38
+ r =
39
+ @node['ret'] ||
40
+ fail(
41
+ Flor::FlorError.new('found no argument that could be reversed', self))
42
+
43
+ wrap_reply('ret' => r)
44
+ end
45
+ end
46
+
@@ -0,0 +1,72 @@
1
+
2
+ require 'flor/pcore/iterator'
3
+
4
+
5
+ class Flor::Pro::Select < Flor::Macro::Iterator
6
+ #
7
+ # Filters a collection
8
+ #
9
+ # "select" and "reject" are the 'block-oriented' children of
10
+ # "filter" and "filter-out" respectively.
11
+ #
12
+ # ```
13
+ # select [ 1, 2, 3, 4, 5 ]
14
+ # = (elt % 2) 1
15
+ #
16
+ # # f.ret --> [ 1, 3, 5 ]
17
+ # ```
18
+ #
19
+ # Note that the equivalent "filter" is:
20
+ # ```
21
+ # filter [ 1, 2, 3, 4, 5 ]
22
+ # def x
23
+ # = (x % 2) 1
24
+ # ```
25
+ #
26
+ # The blocks understand `elt` (the current element), `idx` (the current
27
+ # zero-based index), and `key` (the current key for an object/hash).
28
+ #
29
+ # ## with objects (hashes)
30
+ #
31
+ # ```
32
+ # select { a: 'A', b: 'B', c: 'C', d: 'D' }
33
+ # key == 'a' or val == 'C' or idx == 3
34
+ #
35
+ # # f.ret --> { 'a' => 'A', 'c' => 'C', 'd' => 'D' }
36
+ # ```
37
+ #
38
+ # ## reject
39
+ #
40
+ # "reject" is the negative of "select".
41
+ #
42
+ # ```
43
+ # reject [ 1, 2, 3, 4, 5 ]
44
+ # (elt % 2) == 0
45
+ #
46
+ # # f.ret --> [ 1, 3, 5 ]
47
+ # ```
48
+ #
49
+ # ## iterating blocks
50
+ #
51
+ # Iterating blocks are given 3 to 4 local variables.
52
+ #
53
+ # A block iterating over an array will receive `elt` (the current element
54
+ # of the iteration), `idx` (the zero-based index of the current element),
55
+ # and `len` (the length of the array).
56
+ #
57
+ # A block iterating over an object will receive `key` (the current string
58
+ # key), `val` (the current value), `idx` (the zero-based index of the
59
+ # current key/val), and `len` (the length of the object).
60
+ #
61
+ # ## see also
62
+ #
63
+ # filter, map, reject, and collect.
64
+
65
+ names %w[ select reject ]
66
+
67
+ def rewrite_tree
68
+
69
+ rewrite_iterator_tree(heap == 'select' ? 'filter' : 'filter-out')
70
+ end
71
+ end
72
+
@@ -77,6 +77,14 @@ class Flor::Pro::Set < Flor::Procedure
77
77
  @node['refs'] = []
78
78
  end
79
79
 
80
+ def execute_child(index=0, sub=nil, h=nil)
81
+
82
+ payload['ret'] = node_payload_ret \
83
+ if children[index]
84
+
85
+ super(index, sub, h)
86
+ end
87
+
80
88
  def receive_non_att
81
89
 
82
90
  ret = payload['ret']
@@ -1,5 +1,15 @@
1
1
 
2
2
  class Flor::Pro::Stall < Flor::Procedure
3
+ #
4
+ # "stall" is mostly used in flor tests. It simply dead ends.
5
+ #
6
+ # It receives its execution message, executes all its attributes but
7
+ # does not answer to its parent procedure, effectively stalling
8
+ # its branch of the execution.
9
+ #
10
+ # ## see also
11
+ #
12
+ # _skip
3
13
 
4
14
  name 'stall'
5
15
 
@@ -0,0 +1,61 @@
1
+
2
+ class Flor::Pro::ToArray < Flor::Procedure
3
+ #
4
+ # "to-array", turns an argument into an array, "to-object" turns it into
5
+ # an object.
6
+ #
7
+ # ```
8
+ # to-array [ 0 1 2 ]
9
+ # # --> [ 0 1 2 ] # (left intact)
10
+ #
11
+ # to-array 123
12
+ # # --> [ 123 ]
13
+ #
14
+ # to-array { a: 'A', b: 'B' }
15
+ # # --> [ [ 'a', 'A' ], [ 'b', 'B' ] ]
16
+ # ```
17
+ #
18
+ # and
19
+ #
20
+ # ```
21
+ # to-object [ 'a' 'A' 'b' 'B' 'c' 'C' ]
22
+ # # --> { 'a': 'A', b: 'B', c: 'C' }
23
+ # ```
24
+
25
+ names %w[ to-array to-object ]
26
+
27
+ def pre_execute
28
+
29
+ @node['ret'] = receive_payload_ret
30
+
31
+ unatt_unkeyed_children
32
+ end
33
+
34
+ def receive_last
35
+
36
+ wrap_reply('ret' => (heap == 'to-object') ? to_object : to_array)
37
+ end
38
+
39
+ protected
40
+
41
+ def to_array
42
+
43
+ Flor.to_coll(@node['ret'])
44
+ end
45
+
46
+ def to_object
47
+
48
+ r = @node['ret']
49
+
50
+ fail Flor::FlorError.new('to-object wants an array (or an object)', self) \
51
+ unless r.is_a?(Array) || r.is_a?(Hash)
52
+
53
+ fail Flor::FlorError.new('to-object expects array with even length', self) \
54
+ if r.is_a?(Array) && r.length.odd?
55
+
56
+ r = r.each_slice(2).to_a if r.find { |e| ! e.is_a?(Array) }
57
+
58
+ Hash[r]
59
+ end
60
+ end
61
+
@@ -1,5 +1,39 @@
1
1
 
2
2
  class Flor::Pro::Until < Flor::Procedure
3
+ #
4
+ # `until` loops until a condition evaluates to true.
5
+ # `while` loops while a condition evaluates to true.
6
+ #
7
+ # ```
8
+ # set i 0
9
+ # until i == 7
10
+ # task 'bob' "verify counter ($(i))"
11
+ # set i (i + 1)
12
+ # ```
13
+ #
14
+ # ```
15
+ # set i 0
16
+ # while i < 7
17
+ # task 'bob' "verify counter ($(i))"
18
+ # set i (i + 1)
19
+ # ```
20
+ #
21
+ # `until` and `while` understand `break` and `continue`, like `cursor` and
22
+ # `loop` do.
23
+ #
24
+ # ```
25
+ # until
26
+ # false
27
+ # push f.l 0
28
+ # set outer-break break # alias local break to "outer-break"
29
+ # until false
30
+ # push f.l 'a'
31
+ # outer-break 'x'
32
+ # ```
33
+ #
34
+ # ## see also
35
+ #
36
+ # Break, continue, cursor, loop.
3
37
 
4
38
  names 'until', 'while'
5
39