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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 5d8fca99889bde91b7433d98d674f44c810c041421ce61eef1857e80930e8cef
4
- data.tar.gz: 7a06654dbe17ac06aa3afeab0a04b9958851bdaf393df0d48f994dae4681c072
2
+ SHA1:
3
+ metadata.gz: b973449e80aa849aee7d3fade7ab13fe57a0b6a1
4
+ data.tar.gz: a09d4a0b88d453b42742bb8814d5cee1f957c2c2
5
5
  SHA512:
6
- metadata.gz: 0a17e80b8bf6207bd3688f2c57172a51ac471c5f3beaf28803ff8ce41587dc6b181b5295b041dbfa586e6556ee045be56553164e6b09183745c0cfd2562d8c0c
7
- data.tar.gz: fad82fa8619af8623b6312d6a4f234f5433d71edcbd05964367c8cde547be357f08d77e9c0e99078f2c71cbf74bfbf7691cae62025f53ce7537ad91cb0ef4d74
6
+ metadata.gz: 139a8cf68527edcdff12137a9ac831eee6fb9867a49ed9fe24d462efce7f3d53f9396de2302b449f963c1e74fe4e67b5590f04ad5605a6996bb35e6e0f7015ad
7
+ data.tar.gz: 9de9e3bb4f12f2cb1b48ae0aa4d024ce4ce557b380d5b476c03dae7a7db63480ec041269702102a40165a3ed8ecb290d7b2c77299f999fdf605071976c2f99ea
@@ -1,5 +1,18 @@
1
1
 
2
- # flor CHANGELOG.md
2
+ # CHANGELOG.md
3
+
4
+
5
+ ## flor 1.0.0 not yet released
6
+
7
+
8
+ ## flor 0.16.0 released 2019-02-04
9
+
10
+ - Many many improvements
11
+ - Include "undense" work ("_ref" and friends)
12
+ - Include "undense" work (killing the dollar subsystem)
13
+ - Fix dereserving delayed messages
14
+ - allow for cancel behaviour when "cursor", "sequence", and "until"
15
+ node_status flavour is "on-error"
3
16
 
4
17
 
5
18
  ## flor 0.15.0 released 2018-06-15
data/CREDITS.md CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  ## Contributors
5
5
 
6
+ * Jeffrey Hicks - https://github.com/jrhicks
6
7
  * David Verrier - https://github.com/dverrier
7
8
  * Tsunehisa Doi - https://github.com/dmicky0419
8
9
  * Jean-François Rioux - https://github.com/jfrioux
@@ -1,5 +1,5 @@
1
1
 
2
- Copyright (c) 2015-2018, John Mettraux, jmettraux+flor@gmail.com
2
+ Copyright (c) 2015-2019, John Mettraux, jmettraux+flor@gmail.com
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to deal
data/Makefile CHANGED
@@ -13,12 +13,15 @@ cl: count_lines
13
13
 
14
14
  gemspec_validate:
15
15
  @echo "---"
16
- ruby -e "s = eval(File.read(Dir['*.gemspec'].first)); s.validate"
16
+ ruby -e "s = eval(File.read(Dir['*.gemspec'].first)); p s.validate"
17
17
  @echo "---"
18
18
 
19
19
  name: gemspec_validate
20
20
  @echo "$(NAME) $(VERSION)"
21
21
 
22
+ cw:
23
+ find lib -name "*.rb" -exec ruby -cw {} \; | grep lib
24
+
22
25
  syncver:
23
26
  sed -E -i '' "s/VERSION = ['0-9.]+/VERSION = '$(shell grep -E "$(NAME) ([0-9.]+)" CHANGELOG.md | head -1 | sed -E 's/[^0-9\.]//g')'/" lib/$(NAME).rb
24
27
  bundle install
@@ -81,5 +84,6 @@ cleanshell:
81
84
  rm -fR envs/shell/var/tasks/*
82
85
  rm -f .log.txt
83
86
 
84
- .PHONY: doc shell cleanshell
87
+ .PHONY: \
88
+ count_lines gemspec_validate name cw build push spec doc shell cleanshell
85
89
 
data/README.md CHANGED
@@ -32,8 +32,9 @@ See [doc/](doc/).
32
32
 
33
33
  ## blog posts and presentations
34
34
 
35
+ * [the flor language](http://jmettraux.skepti.ch/20180927.html?t=the_flor_language) - on the flor workflow definition language itself
35
36
  * [Flor, hubristic interpreter](http://rubykaigi.org/2017/presentations/jmettraux.html) - RubyKaigi 2017, Hiroshima - presentation
36
- * [flor design 0](http://jmettraux.skepti.ch/20171021.html?t=flor_design_0) - running a simple execution, what happens - blog post
37
+ * [flor design 0](http://jmettraux.skepti.ch/20171021.html?t=flor_design_0&f=readme) - running a simple execution, what happens - blog post
37
38
  * [flor, branch to branch](https://speakerdeck.com/jmettraux/flor-branch-to-branch) - q1 2017 - very dry deck
38
39
  * [flor 2017](https://speakerdeck.com/jmettraux/flor-2017) - q1 2017 - very dry deck
39
40
 
@@ -15,6 +15,16 @@ Gem::Specification.new do |s|
15
15
  s.license = 'MIT'
16
16
  s.summary = 'A Ruby workflow engine'
17
17
 
18
+ s.metadata = {
19
+ 'changelog_uri' => s.homepage + '/flor/blob/master/CHANGELOG.md',
20
+ 'documentation_uri' => s.homepage + '/flor/tree/master/doc',
21
+ 'bug_tracker_uri' => s.homepage + '/flor/issues',
22
+ 'mailing_list_uri' => 'https://groups.google.com/forum/#!forum/floraison',
23
+ 'homepage_uri' => s.homepage + '/flor',
24
+ 'source_code_uri' => s.homepage + '/flor',
25
+ #'wiki_uri' => s.homepage + '/flor/wiki',
26
+ }
27
+
18
28
  s.description = %{
19
29
  A Ruby workflow engine (ruote next generation)
20
30
  }.strip
@@ -31,8 +41,8 @@ A Ruby workflow engine (ruote next generation)
31
41
  s.add_runtime_dependency 'munemo', '~> 1.0', '>= 1.0.1'
32
42
  s.add_runtime_dependency 'raabro', '~> 1.1', '>= 1.1.5'
33
43
  #s.add_runtime_dependency 'rufus-lru', '~> 1.1'
34
- s.add_runtime_dependency 'fugit', '~> 1.1'
35
- s.add_runtime_dependency 'dense', '~> 1.1', '>= 1.1.1'
44
+ s.add_runtime_dependency 'fugit', '~> 1.1', '>= 1.1.8'
45
+ s.add_runtime_dependency 'dense', '~> 1.1', '>= 1.1.6'
36
46
 
37
47
  s.add_runtime_dependency 'sequel', '~> 4'
38
48
 
@@ -5,6 +5,7 @@ require 'logger'
5
5
  require 'thread'
6
6
  require 'digest'
7
7
  require 'socket'
8
+ require 'forwardable'
8
9
 
9
10
  require 'munemo'
10
11
  require 'raabro'
@@ -13,7 +14,8 @@ require 'dense'
13
14
 
14
15
  module Flor
15
16
 
16
- VERSION = '0.15.0'
17
+ VERSION = '0.16.0'
18
+ #VERSION = '1.0.0'
17
19
  end
18
20
 
19
21
  require 'flor/colours'
@@ -22,11 +24,9 @@ require 'flor/djan'
22
24
  require 'flor/id'
23
25
  require 'flor/log'
24
26
  require 'flor/flor'
25
- require 'flor/dollar'
26
27
  require 'flor/errors'
27
28
  require 'flor/parser'
28
29
  require 'flor/conf'
29
- require 'flor/changes'
30
30
  require 'flor/to_string'
31
31
 
32
32
  require 'flor/core'
@@ -28,7 +28,7 @@ module Flor
28
28
  if v.match(/\A\d/)
29
29
  class_eval(%{
30
30
  def #{k}(s=nil)
31
- s ? "[#{v}m" + s + "": "[#{v}m"
31
+ s ? "[#{v}m" + s + "" : "[#{v}m"
32
32
  end })
33
33
  else
34
34
  class_eval(
@@ -114,13 +114,12 @@ module Flor
114
114
  a = plus - minus
115
115
 
116
116
  h =
117
- a.inject({}) { |h, kv|
117
+ a.inject({}) { |hh, kv|
118
118
  k, v = kv.split(':')
119
119
  k = 'sto' if k == 'db'
120
120
  k = "log_#{k}" if LOG_ALL_KEYS.include?(k)
121
- h[k] = v ? JSON.parse(v) : true
122
- h
123
- }
121
+ hh[k] = v ? JSON.parse(v) : true
122
+ hh }
124
123
  LOG_ALL_KEYS.each { |k| h["log_#{k}"] = 1 } if h['log_all']
125
124
  LOG_DBG_KEYS.each { |k| h["log_#{k}"] = 1 } if h['log_dbg']
126
125
 
@@ -15,6 +15,8 @@ module Flor
15
15
 
16
16
  @hooks = hooks # raw hooks if any, fresh from the loader
17
17
  @traps = traps # array of Trap instances
18
+
19
+ @htraps = nil
18
20
  end
19
21
 
20
22
  def conf; @unit.conf; end
@@ -144,6 +146,10 @@ module Flor
144
146
  # cnid: closure nid
145
147
  # dbg: used to debug messages (useful @node['dbg'] when 'receive')
146
148
 
149
+ if oeh = message['on_error_handler']
150
+ node['on_error'] = [ [ [ '*' ], oeh ] ]
151
+ end
152
+
147
153
  @execution['nodes'][nid] = node
148
154
  end
149
155
 
@@ -158,9 +164,7 @@ module Flor
158
164
  make_node(message) :
159
165
  @execution['nodes'][nid]
160
166
 
161
- return unless node
162
-
163
- return if node['heat']
167
+ return if node.nil? || node['heat']
164
168
 
165
169
  n = Flor::Node.new(self, node, message)
166
170
 
@@ -173,34 +177,33 @@ module Flor
173
177
  node['tree'] = mt if mt && (mt != nt)
174
178
  tree = node['tree'] || nt
175
179
 
176
- t0 = tree[0]
177
- t0 = (t0.is_a?(Array) && t0[0] == '_dqs') ? n.expand(t0[1]) : t0
178
-
179
- node['heat0'] = tree[0]
180
+ node['heat0'] = t0 = tree[0]
180
181
  node['heat'] = heat = n.deref(t0)
181
- node['heap'] = heap = n.reheap(tree, heat)
182
182
 
183
- # "exceptions"
183
+ if heat == nil && ! message['accept_symbol']
184
184
 
185
- if heat == nil && tree[0].index('.') && tree[1].empty?
186
- #
187
- # a field reference that points to nothing returns null
185
+ node['heat'] = '(none)'
188
186
 
189
- node['heat0'] = '_nul'
190
- node['heat'] = '_nul'
191
- node['heap'] = '_nul'
187
+ fail FlorError.new("cannot find #{t0.inspect}", n) if tree[1].empty?
188
+ fail FlorError.new("don't know how to apply #{t0.inspect}", n)
189
+ # TODO how about _ref and letting that procedure fail
190
+ end
192
191
 
193
- elsif message['accept_symbol'] && heat == nil
192
+ node['heap'] = heap = n.reheap(tree, heat)
193
+
194
+ # "exceptions"
195
+
196
+ if heat == nil #&& message['accept_symbol']
194
197
  #
195
198
  # tag: et al
196
199
 
197
- node['tree'] = message['tree'] = t = [ '_dqs', tree[0], tree[2] ]
200
+ node['tree'] = message['tree'] = t = [ '_sqs', tree[0], tree[2] ]
198
201
 
199
202
  node['heat0'] = t[0]
200
203
  node['heat'] = h = n.deref(t[0])
201
204
  node['heap'] = n.reheap(t, h)
202
205
 
203
- elsif heap == 'task' && heat[0] == '_task'
206
+ elsif heap == 'task' && heat[0] == '_tasker'
204
207
  #
205
208
  # rewrite `alpha` into `task alpha`
206
209
 
@@ -211,7 +214,7 @@ module Flor
211
214
  message['tree'][0] =
212
215
  'task'
213
216
  message['tree'][1].unshift(
214
- [ '_att', [ [ '_sqs', heat[1]['task'], l ] ], l ])
217
+ [ '_att', [ [ '_sqs', heat[1]['tasker'], l ] ], l ])
215
218
  end
216
219
  end
217
220
 
@@ -222,16 +225,7 @@ module Flor
222
225
 
223
226
  def apply(node, message)
224
227
 
225
- heap =
226
- if node['heat']
227
- node['heap']
228
- else
229
- node['failure'] ? '_err' : nil
230
- end
231
-
232
- return error_reply(
233
- node, message, "don't know how to apply #{node['heat0'].inspect}"
234
- ) if heap == nil
228
+ heap = node['heap']
235
229
 
236
230
  heac = Flor::Procedure[heap]
237
231
  fail NameError.new("unknown procedure #{heap.inspect}") unless heac
@@ -283,11 +277,11 @@ module Flor
283
277
 
284
278
  def leave_node(message)
285
279
 
280
+ return [] if %w[ flank part ].include?(message['flavour'])
281
+
286
282
  fnid = message['from']; return [] unless fnid
287
283
  fnode = @execution['nodes'][fnid]; return [] unless fnode
288
284
 
289
- return [] if message['flavour'] == 'flank'
290
-
291
285
  remove_node(message, fnode) +
292
286
  leave_tags(message, fnode)
293
287
  end
@@ -380,36 +374,6 @@ module Flor
380
374
  [ m ]
381
375
  end
382
376
 
383
- def task(message)
384
-
385
- return error_reply(
386
- node(message['nid']),
387
- message,
388
- "don't know how to apply #{message['tasker'].inspect}"
389
- ) if message['routed'] == false
390
-
391
- @execution['tasks'][message['nid']] =
392
- { 'tasker' => message['tasker'], 'name' => message['taskname'] }
393
- #
394
- # FIXME is it in use???
395
-
396
- @unit.ganger.task(self, message)
397
- end
398
- alias detask task
399
-
400
- def return(message)
401
-
402
- @execution['tasks'].delete(message['nid'])
403
- #
404
- # FIXME is it in use???
405
-
406
- [ { 'point' => 'receive',
407
- 'exid' => message['exid'],
408
- 'nid' => message['nid'],
409
- 'payload' => message['payload'],
410
- 'tasker' => message['tasker'] } ]
411
- end
412
-
413
377
  def cancel(message)
414
378
 
415
379
  n = @execution['nodes'][message['nid']]
@@ -444,6 +408,11 @@ module Flor
444
408
 
445
409
  def process(message)
446
410
 
411
+ fail ArgumentError.new("incoming message has non nil or Hash payload") \
412
+ unless message['payload'] == nil || message['payload'].is_a?(Hash)
413
+ #
414
+ # weed out messages with non-conforming payloads
415
+
447
416
  begin
448
417
 
449
418
  message['m'] = counter_next('msgs') # number messages
@@ -516,6 +485,7 @@ module Flor
516
485
  # with its 'on_error' turned on.
517
486
  #
518
487
  oep.trigger_on_error
488
+ #
519
489
  else
520
490
  #
521
491
  # Simply log and don't add further messages ([]) to execute
@@ -2,10 +2,12 @@
2
2
  class Flor::Node
3
3
 
4
4
  class Payload
5
+
5
6
  def initialize(node, type=:node)
6
7
  @node = node
7
8
  @type = type
8
9
  end
10
+
9
11
  def has_key?(k)
10
12
  current.has_key?(k)
11
13
  end
@@ -33,7 +35,16 @@ class Flor::Node
33
35
  def merge(h)
34
36
  current.merge(h)
35
37
  end
38
+
39
+ def ret
40
+ self['ret']
41
+ end
42
+ def ret=(v)
43
+ self['ret'] = v
44
+ end
45
+
36
46
  protected
47
+
37
48
  def container
38
49
  @type == :node ? @node.h : @node.message
39
50
  end
@@ -71,6 +82,8 @@ class Flor::Node
71
82
  def nid; @node['nid']; end
72
83
  def parent; @node['parent']; end
73
84
 
85
+ def child_id; Flor.child_id(@node['nid']); end
86
+
74
87
  def domain; Flor.domain(@execution['exid']); end
75
88
 
76
89
  def point; @message['point']; end
@@ -78,6 +91,7 @@ class Flor::Node
78
91
 
79
92
  def cnodes; @node['cnodes']; end
80
93
  def cnodes_any?; cnodes && cnodes.any?; end
94
+ def cnodes_empty?; cnodes.nil? || cnodes.empty?; end
81
95
 
82
96
  def payload
83
97
  @message_payload ||= Payload.new(self, :message)
@@ -106,6 +120,10 @@ class Flor::Node
106
120
  Flor.dup(node_payload['ret'])
107
121
  end
108
122
 
123
+ def payload_ret
124
+ message['payload']['ret']
125
+ end
126
+
109
127
  def message_or_node_payload
110
128
  payload.current ? payload : node_payload
111
129
  end
@@ -151,89 +169,86 @@ class Flor::Node
151
169
  #
152
170
  # that might be the way...
153
171
 
154
- def lookup(name, silence_index_error=false)
172
+ def lookup_value(path)
173
+
174
+ original_path = path
155
175
 
156
- cat, mod, key_and_path = key_split(name)
157
- key, pth = key_and_path.split('.', 2)
176
+ path =
177
+ case path
178
+ when '*' then [ path ]
179
+ when String then Dense::Path.make(path).to_a
180
+ else path
181
+ end
158
182
 
159
- if [ cat, mod, key ] == [ 'v', '', 'node' ]
160
- lookup_in_node(pth)
161
- elsif cat == 'v'
162
- lookup_var(@node, mod, key, pth)
163
- elsif cat == 't'
164
- lookup_tag(mod, key)
183
+ path.unshift('v') \
184
+ if path.length < 2
185
+
186
+ case path.first
187
+ when /\Af(?:ld|ield)?\z/
188
+ lookup_field(nil, path[1..-1]) # mod -> nil...
189
+ when /\At(?:ag)?\z/
190
+ lookup_tag(nil, path[1])
191
+ when /\A([lgd]?)v(?:ar|ariable)?\z/
192
+ return @message['__head'][1] if path[1] == '__head'
193
+ lookup_var(@node, $1, path[1], path[2..-1])
194
+ when 'node'
195
+ lookup_in_node(path[1..-1])
196
+ when 'exe', 'execution'
197
+ lookup_in_execution(path[1..-1])
165
198
  else
166
- lookup_field(mod, key_and_path)
199
+ lookup_var(@node, '', path[0], path[1..-1])
167
200
  end
168
201
 
169
- rescue KeyError, TypeError
170
-
171
- raise unless silence_index_error
172
- nil
173
- end
174
-
175
- class Expander < Flor::Dollar
176
-
177
- def initialize(n); @node = n; end
178
-
179
- def lookup(k)
202
+ rescue KeyError => ke
180
203
 
181
- return @node.nid if k == 'nid'
182
- return @node.exid if k == 'exid'
183
- return Flor.domain(@node.exid) if k == 'domain'
184
- return Flor.tstamp if k == 'tstamp'
204
+ class << ke; attr_accessor :original_path, :work_path; end
205
+ ke.original_path = original_path
206
+ ke.work_path = path
185
207
 
186
- r = @node.lookup(k, true)
187
- r.is_a?(Symbol) ? nil : r
188
- end
208
+ raise
189
209
  end
190
210
 
191
- def expand(s)
211
+ # Returns the referenced tree.
212
+ # Returns nil if not found.
213
+ #
214
+ def deref(s)
192
215
 
193
- return s unless s.is_a?(String)
216
+ v = lookup_value(s)
194
217
 
195
- Expander.new(self).expand(s)
196
- end
218
+ if Flor.is_tree?(v)
197
219
 
198
- def deref(o)
220
+ ref =
221
+ case v[0]
222
+ when '_func' then true
223
+ when '_proc' then v[1]['proc'] != s
224
+ when '_tasker' then v[1]['tasker'] != s
225
+ else false
226
+ end
199
227
 
200
- return o unless o.is_a?(String)
228
+ v[1]['oref'] ||= v[1]['ref'] if ref && v[1]['ref']
229
+ v[1]['ref'] = s if ref
201
230
 
202
- v = lookup(o)
231
+ v
203
232
 
204
- return v unless Flor.is_tree?(v)
205
- return v unless v[1].is_a?(Hash)
206
-
207
- return v unless %w[ _proc _task _func ].include?(v[0])
233
+ else
208
234
 
209
- ref =
210
- case v[0]
211
- when '_func' then true
212
- when '_proc' then v[1]['proc'] != o
213
- when '_task' then v[1]['task'] != o
214
- else false
215
- end
235
+ [ '_val', v, tree[2] ]
236
+ end
216
237
 
217
- v[1]['oref'] ||= v[1]['ref'] if ref && v[1]['ref']
218
- v[1]['ref'] = o if ref
238
+ rescue KeyError => ke
219
239
 
220
- v
240
+ nil
221
241
  end
222
242
 
223
243
  def reheap(tree, heat)
224
244
 
225
- if ! heat.is_a?(Array)
226
- '_val'
227
- elsif tree && tree[1] == []
228
- '_val'
229
- elsif heat[0] == '_proc'
230
- heat[1]['proc']
231
- elsif heat[0] == '_func'
232
- 'apply'
233
- elsif heat[0] == '_task'
234
- 'task'
235
- else
236
- '_val'
245
+ case
246
+ when ! heat.is_a?(Array) then '_val'
247
+ when tree && tree[1] == [] then '_val'
248
+ when heat[0] == '_proc' then heat[1]['proc']
249
+ when heat[0] == '_func' then 'apply'
250
+ when heat[0] == '_tasker' then 'task'
251
+ else '_val'
237
252
  end
238
253
  end
239
254
 
@@ -247,17 +262,6 @@ class Flor::Node
247
262
  "#{exid}-#{nid}"
248
263
  end
249
264
 
250
- def on_error_parent
251
-
252
- oe = @node['on_error']
253
- return self if oe && oe.any?
254
-
255
- pn = parent_node
256
- return Flor::Node.new(@executor, pn, @message).on_error_parent if pn
257
-
258
- nil
259
- end
260
-
261
265
  def to_procedure_node
262
266
 
263
267
  Flor::Procedure.new(@executor, @node, @message)
@@ -279,6 +283,24 @@ class Flor::Node
279
283
  false
280
284
  end
281
285
 
286
+ def on_error_parent(skip=false)
287
+
288
+ if @node['in_on_error'] # prevent loop when error in on_error:
289
+ skip = true
290
+ end
291
+
292
+ if (@node['on_error'] || []).find { |criteria, _| match_on?(criteria) }
293
+ return self unless skip
294
+ skip = false
295
+ end
296
+
297
+ if pn = parent_node
298
+ return Flor::Node.new(@executor, pn, @message).on_error_parent(skip)
299
+ end
300
+
301
+ nil
302
+ end
303
+
282
304
  protected
283
305
 
284
306
  def subtree(tree, pnid, nid)
@@ -294,7 +316,7 @@ class Flor::Node
294
316
  return nil unless cid
295
317
  # maybe failing would be better
296
318
 
297
- cid.split('_').each { |cid| tree = tree[1][cid.to_i] }
319
+ cid.split('_').each { |id| tree = tree[1][id.to_i] }
298
320
 
299
321
  tree
300
322
  end
@@ -309,7 +331,21 @@ class Flor::Node
309
331
  lookup_tree(node['parent'])
310
332
  end
311
333
 
312
- def is_ancestor_node?(nid, node=@node)
334
+ def parent_node_procedure(node=@node)
335
+
336
+ Flor::Procedure.make(@executor, parent_node(node), @message)
337
+ end
338
+
339
+ # Returns true if the current node has the node identified with nid as
340
+ # an ancestor. Returns false else.
341
+ #
342
+ def is_ancestor_node?(node_or_nid, node=@node)
343
+
344
+ nid = node_or_nid
345
+
346
+ return false unless nid
347
+
348
+ nid = node_or_nid['nid'] unless nid.is_a?(String)
313
349
 
314
350
  return false unless node
315
351
  return true if node['nid'] == nid
@@ -321,9 +357,18 @@ class Flor::Node
321
357
  # @execution['nodes'][node['cnid']]
322
358
  #end
323
359
 
324
- def lookup_in_node(pth)
360
+ def lookup_in_node(path)
325
361
 
326
- Dense.fetch(@node, pth)
362
+ Dense.fetch(@node, path)
363
+ end
364
+
365
+ def lookup_in_execution(path)
366
+
367
+ if path == %w[ domain ]
368
+ Flor.domain(@execution['exid'])
369
+ else
370
+ Dense.fetch(@execution, path)
371
+ end
327
372
  end
328
373
 
329
374
  class PseudoVarContainer < Hash
@@ -336,7 +381,7 @@ class Flor::Node
336
381
  end
337
382
  #
338
383
  PROC_VAR_CONTAINER = PseudoVarContainer.new('proc')
339
- TASKER_VAR_CONTAINER = PseudoVarContainer.new('task')
384
+ TASKER_VAR_CONTAINER = PseudoVarContainer.new('tasker')
340
385
 
341
386
  def escape(k)
342
387
 
@@ -357,19 +402,53 @@ class Flor::Node
357
402
 
358
403
  rescue KeyError => ke
359
404
 
360
- return nil if ke.miss[4].empty?
361
-
362
405
  m = "variable #{ke.miss[3].inspect} not found"
363
406
  m += " at #{Dense::Path.make(ke.miss[1]).to_s.inspect}" if ke.miss[1].any?
364
407
 
365
408
  raise ke.relabel(m)
366
409
 
410
+ rescue IndexError => ie
411
+
412
+ m =
413
+ if ie.miss[1] == [ ie.miss[2] ]
414
+ "variable #{ie.miss[2].inspect} not found"
415
+ else
416
+ pa = Dense::Path.make(ie.miss[1]).to_s.inspect
417
+ ty = Flor.type(ie.miss[2])
418
+ ke = ie.miss[3].inspect
419
+ "variable at #{pa} is a #{ty}, it has no key #{ke}"
420
+ end
421
+
422
+ raise ie.relabel(m)
423
+
367
424
  #rescue TypeError => te # leave as is
368
425
  end
369
426
 
427
+ def var_match?(vs, key)
428
+
429
+ vs.each do |v|
430
+ return true if v == key
431
+ return true if v.is_a?(Regexp) && v =~ key
432
+ # TODO fun call
433
+ end
434
+
435
+ false
436
+ end
437
+
370
438
  def lookup_var_container(node, mod, key)
371
439
 
372
- return lookup_dvar_container(mod, key) if node == nil || mod == 'd'
440
+ return lookup_dvar_container(mod, key) \
441
+ if node == nil || mod == 'd'
442
+
443
+ return lookup_arg_container(key) \
444
+ if mod == '' && %w[ args argv argh ].include?(key)
445
+
446
+ if vwl = node['vwlist']
447
+ return lookup_dvar_container(mod, key) unless var_match?(vwl, key)
448
+ end
449
+ if vbl = node['vblist']
450
+ return lookup_dvar_container(mod, key) if var_match?(vbl, key)
451
+ end
373
452
 
374
453
  pnode = parent_node(node)
375
454
  vars = node['vars']
@@ -413,15 +492,21 @@ class Flor::Node
413
492
  {}
414
493
  end
415
494
 
416
- def lookup_var_name(node, val)
495
+ def lookup_arg_container(key)
417
496
 
418
- return nil unless node
497
+ vars = lookup_var_container(@node, '', 'arguments')
498
+ return {} unless vars
419
499
 
420
- vars = node['vars']
421
- k, _ = vars && vars.find { |k, v| v == val }
422
- return k if k
500
+ args = vars['arguments']
501
+ return {} unless args
423
502
 
424
- lookup_var_name(parent_node(node), val)
503
+ val =
504
+ case key
505
+ when 'args', 'argv' then args.collect(&:last)
506
+ else args.inject({}) { |h, (k, v)| h[k] = v if k; h }
507
+ end
508
+
509
+ { key => val }
425
510
  end
426
511
 
427
512
  def lookup_tag(mod, key)
@@ -432,28 +517,60 @@ class Flor::Node
432
517
  a
433
518
  end
434
519
 
435
- nids.empty? ? [ '_nul', nil, -1 ] : nids
520
+ nids.any? ? nids : nil
436
521
  end
437
522
 
438
523
  def lookup_field(mod, key_and_path)
439
524
 
440
525
  Dense.fetch(payload.current, key_and_path)
526
+ end
441
527
 
442
- rescue IndexError
528
+ # Return true if the current @message matches on the given array of
529
+ # criteria.
530
+ #
531
+ def match_on?(criteria)
443
532
 
444
- nil
533
+ # AND, not OR, hence the true at the bottom
534
+
535
+ criteria
536
+ .each { |c|
537
+ next if c == '*'
538
+ return false unless send("match_on_#{c[0]}?", c) }
539
+
540
+ true
445
541
  end
446
542
 
447
- def key_split(key) # => category, mode, key
543
+ def extract_on_info
544
+
545
+ kla = @message['error']['kla']
546
+ msg = @message['error']['msg']
547
+ la = kla.split('::').last
548
+
549
+ [ kla, la, msg ]
550
+ end
551
+
552
+ def match_on_class?(criterion)
553
+
554
+ c1 = criterion[1]
555
+ kla, la, _ = extract_on_info
556
+
557
+ kla == c1 || la == c1
558
+ end
559
+
560
+ def match_on_string?(criterion)
561
+
562
+ c1 = criterion[1]
563
+ kla, la, msg = extract_on_info
564
+
565
+ msg == c1 || kla == c1 || la == c1
566
+ end
448
567
 
449
- m = key.match(
450
- /\A(?:([lgd]?)((?:v|var|variable)|w|f|fld|field|t|tag)\.)?(.+)\z/)
568
+ def match_on_regex?(criterion)
451
569
 
452
- ca = (m[2] || 'v')[0, 1]
453
- mo = m[1] || ''
454
- ke = m[3]
570
+ c1 = Flor.to_regex(criterion)
571
+ kla, _, msg = extract_on_info
455
572
 
456
- [ ca, mo, ke ]
573
+ msg =~ c1 || kla =~ c1
457
574
  end
458
575
  end
459
576