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
@@ -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