flor 0.14.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +9 -0
- data/CREDITS.md +21 -0
- data/LICENSE.txt +4 -1
- data/Makefile +4 -0
- data/README.md +8 -0
- data/flor.gemspec +10 -10
- data/lib/flor.rb +2 -2
- data/lib/flor/changes.rb +3 -3
- data/lib/flor/colours.rb +14 -8
- data/lib/flor/conf.rb +63 -58
- data/lib/flor/core.rb +4 -4
- data/lib/flor/core/executor.rb +65 -29
- data/lib/flor/core/node.rb +37 -20
- data/lib/flor/core/procedure.rb +182 -40
- data/lib/flor/core/texecutor.rb +125 -52
- data/lib/flor/djan.rb +111 -82
- data/lib/flor/dollar.rb +31 -30
- data/lib/flor/flor.rb +314 -237
- data/lib/flor/id.rb +7 -2
- data/lib/flor/log.rb +250 -245
- data/lib/flor/parser.rb +72 -38
- data/lib/flor/pcore/_arr.rb +10 -10
- data/lib/flor/pcore/_att.rb +49 -14
- data/lib/flor/pcore/_coll.rb +18 -0
- data/lib/flor/pcore/_obj.rb +23 -7
- data/lib/flor/pcore/_pat_.rb +1 -1
- data/lib/flor/pcore/_pat_guard.rb +8 -0
- data/lib/flor/pcore/_pat_obj.rb +3 -3
- data/lib/flor/pcore/_pat_or.rb +4 -0
- data/lib/flor/pcore/_pat_regex.rb +24 -0
- data/lib/flor/pcore/_skip.rb +4 -0
- data/lib/flor/pcore/_val.rb +0 -1
- data/lib/flor/pcore/all.rb +111 -0
- data/lib/flor/pcore/any.rb +83 -0
- data/lib/flor/pcore/arith.rb +35 -6
- data/lib/flor/pcore/break.rb +39 -1
- data/lib/flor/pcore/case.rb +82 -4
- data/lib/flor/pcore/cmp.rb +7 -7
- data/lib/flor/pcore/collect.rb +50 -0
- data/lib/flor/pcore/cond.rb +17 -3
- data/lib/flor/pcore/cursor.rb +8 -2
- data/lib/flor/pcore/detect.rb +45 -0
- data/lib/flor/pcore/each.rb +52 -0
- data/lib/flor/pcore/empty.rb +60 -0
- data/lib/flor/pcore/filter.rb +94 -0
- data/lib/flor/pcore/find.rb +67 -0
- data/lib/flor/pcore/for_each.rb +65 -0
- data/lib/flor/pcore/includes.rb +32 -0
- data/lib/flor/pcore/inject.rb +55 -0
- data/lib/flor/pcore/iterator.rb +151 -0
- data/lib/flor/pcore/keys.rb +60 -0
- data/lib/flor/pcore/length.rb +34 -7
- data/lib/flor/pcore/logo.rb +18 -0
- data/lib/flor/pcore/loop.rb +4 -0
- data/lib/flor/pcore/map.rb +77 -46
- data/lib/flor/pcore/match.rb +8 -2
- data/lib/flor/pcore/matchr.rb +4 -5
- data/lib/flor/pcore/move.rb +3 -3
- data/lib/flor/pcore/noeval.rb +13 -0
- data/lib/flor/pcore/not.rb +16 -0
- data/lib/flor/pcore/on.rb +172 -0
- data/lib/flor/pcore/on_cancel.rb +54 -0
- data/lib/flor/pcore/on_error.rb +68 -0
- data/lib/flor/pcore/rand.rb +2 -2
- data/lib/flor/pcore/range.rb +2 -1
- data/lib/flor/pcore/reduce.rb +124 -0
- data/lib/flor/pcore/reverse.rb +46 -0
- data/lib/flor/pcore/select.rb +72 -0
- data/lib/flor/pcore/set.rb +8 -0
- data/lib/flor/pcore/stall.rb +10 -0
- data/lib/flor/pcore/to_array.rb +61 -0
- data/lib/flor/pcore/until.rb +34 -0
- data/lib/flor/punit/cancel.rb +30 -5
- data/lib/flor/punit/ccollect.rb +11 -0
- data/lib/flor/punit/cmap.rb +10 -5
- data/lib/flor/punit/concurrence.rb +42 -51
- data/lib/flor/punit/cron.rb +33 -0
- data/lib/flor/punit/do_trap.rb +42 -0
- data/lib/flor/punit/every.rb +48 -13
- data/lib/flor/punit/graft.rb +3 -3
- data/lib/flor/punit/on_timeout.rb +38 -0
- data/lib/flor/punit/schedule.rb +69 -6
- data/lib/flor/punit/signal.rb +54 -0
- data/lib/flor/punit/sleep.rb +1 -1
- data/lib/flor/punit/task.rb +4 -1
- data/lib/flor/punit/trap.rb +188 -13
- data/lib/flor/tools/shell.rb +408 -62
- data/lib/flor/tools/shell_out.rb +31 -0
- data/lib/flor/unit.rb +1 -1
- data/lib/flor/unit/caller.rb +177 -0
- data/lib/flor/unit/executor.rb +1 -0
- data/lib/flor/unit/ganger.rb +15 -21
- data/lib/flor/unit/hook.rb +1 -1
- data/lib/flor/unit/hooker.rb +22 -10
- data/lib/flor/unit/loader.rb +22 -22
- data/lib/flor/unit/logger.rb +63 -36
- data/lib/flor/unit/models.rb +6 -1
- data/lib/flor/unit/models/execution.rb +12 -1
- data/lib/flor/unit/models/message.rb +7 -0
- data/lib/flor/unit/models/trap.rb +31 -17
- data/lib/flor/unit/scheduler.rb +18 -10
- data/lib/flor/unit/storage.rb +83 -23
- data/lib/flor/unit/waiter.rb +1 -2
- metadata +96 -52
- data/lib/flor/deep.rb +0 -144
- data/lib/flor/punit/on.rb +0 -57
- data/lib/flor/unit/runner.rb +0 -84
- data/match.md +0 -22
data/lib/flor/pcore/_pat_or.rb
CHANGED
@@ -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
|
+
|
data/lib/flor/pcore/_skip.rb
CHANGED
data/lib/flor/pcore/_val.rb
CHANGED
@@ -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
|
+
|
data/lib/flor/pcore/arith.rb
CHANGED
@@ -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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
data/lib/flor/pcore/break.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
class Flor::Pro::Break < Flor::Procedure
|
3
3
|
#
|
4
|
-
# Breaks or continues a "while" or "
|
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
|
|
data/lib/flor/pcore/case.rb
CHANGED
@@ -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
|
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
|
-
|
97
|
-
|
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
|
-
|
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
|
|