flor 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. data/CHANGELOG.md +8 -0
  2. data/lib/flor.rb +1 -1
  3. data/lib/flor/core.rb +7 -0
  4. data/lib/flor/core/executor.rb +43 -8
  5. data/lib/flor/core/node.rb +84 -54
  6. data/lib/flor/core/procedure.rb +36 -19
  7. data/lib/flor/core/texecutor.rb +4 -1
  8. data/lib/flor/deep.rb +43 -51
  9. data/lib/flor/dollar.rb +2 -3
  10. data/lib/flor/flor.rb +38 -9
  11. data/lib/flor/parser.rb +123 -72
  12. data/lib/flor/pcore/_arr.rb +15 -0
  13. data/lib/flor/pcore/_atom.rb +6 -5
  14. data/lib/flor/pcore/_att.rb +9 -4
  15. data/lib/flor/pcore/_obj.rb +28 -34
  16. data/lib/flor/pcore/_pat_.rb +77 -0
  17. data/lib/flor/pcore/_pat_arr.rb +131 -0
  18. data/lib/flor/pcore/_pat_guard.rb +70 -0
  19. data/lib/flor/pcore/_pat_obj.rb +143 -0
  20. data/lib/flor/pcore/_pat_or.rb +46 -0
  21. data/lib/flor/pcore/_val.rb +20 -0
  22. data/lib/flor/pcore/arith.rb +7 -1
  23. data/lib/flor/pcore/case.rb +46 -69
  24. data/lib/flor/pcore/cursor.rb +24 -24
  25. data/lib/flor/pcore/define.rb +5 -1
  26. data/lib/flor/pcore/do_return.rb +32 -0
  27. data/lib/flor/pcore/length.rb +18 -0
  28. data/lib/flor/pcore/logo.rb +47 -0
  29. data/lib/flor/pcore/map.rb +12 -10
  30. data/lib/flor/pcore/match.rb +276 -0
  31. data/lib/flor/pcore/not.rb +13 -0
  32. data/lib/flor/pcore/push.rb +52 -13
  33. data/lib/flor/pcore/range.rb +69 -0
  34. data/lib/flor/pcore/set.rb +82 -24
  35. data/lib/flor/unit/hook.rb +28 -0
  36. data/lib/flor/unit/hooker.rb +5 -0
  37. data/lib/flor/unit/loader.rb +4 -1
  38. data/lib/flor/unit/storage.rb +5 -3
  39. data/lib/flor/unit/waiter.rb +12 -2
  40. data/lib/flor/unit/wlist.rb +4 -3
  41. data/match.md +22 -0
  42. metadata +15 -5
  43. data/lib/flor/pcore/_happly.rb +0 -49
  44. data/lib/flor/pcore/val.rb +0 -16
  45. data/t.txt +0 -4
@@ -2,6 +2,14 @@
2
2
  # flor CHANGELOG.md
3
3
 
4
4
 
5
+ ## flor 0.14.0 released 2017-06-13
6
+
7
+ - Implement "match"
8
+ - Implement "range"
9
+ - Implement "for-each"
10
+ - Merge enhancements by @jfrioux
11
+
12
+
5
13
  ## flor 0.13.0 released 2017-04-24
6
14
 
7
15
  - Simplify "trace" implementation
@@ -12,7 +12,7 @@ require 'raabro'
12
12
 
13
13
  module Flor
14
14
 
15
- VERSION = '0.13.0'
15
+ VERSION = '0.14.0'
16
16
  end
17
17
 
18
18
  require 'flor/colours'
@@ -44,6 +44,13 @@ module Flor
44
44
  pl = opts[:payload] || opts[:fields] || {}
45
45
  vs = opts[:variables] || opts[:vars] || {}
46
46
 
47
+ fail ArgumentError.new(
48
+ "given launch payload should be a Hash, but it's a #{pl.class}"
49
+ ) unless pl.is_a?(Hash)
50
+ fail ArgumentError.new(
51
+ "given launch variables should come in a Hash, but it's a #{vs.class}"
52
+ ) unless vs.is_a?(Hash)
53
+
47
54
  msg =
48
55
  { 'point' => 'execute',
49
56
  'exid' => exid,
@@ -182,18 +182,23 @@ module Flor
182
182
 
183
183
  # "exceptions"
184
184
 
185
- # TODO could those two ifs go upstream (top of this method)
186
- # and thus become smaller
187
- #
188
- if message['accept_symbol'] && node['heat'] == nil
185
+ if heat == nil && tree[0].index('.') && tree[1].empty?
186
+ #
187
+ # a field reference that points to nothing returns null
188
+
189
+ node['heat0'] = '_nul'
190
+ node['heat'] = '_nul'
191
+ node['heap'] = '_nul'
192
+
193
+ elsif message['accept_symbol'] && heat == nil
189
194
  #
190
195
  # tag: et al
191
196
 
192
- tree = node['tree'] = message['tree'] = [ '_dqs', tree[0], tree[2] ]
197
+ node['tree'] = message['tree'] = t = [ '_dqs', tree[0], tree[2] ]
193
198
 
194
- node['heat0'] = tree[0]
195
- node['heat'] = heat = n.deref(tree[0])
196
- node['heap'] = heap = n.reheap(tree, heat)
199
+ node['heat0'] = t[0]
200
+ node['heat'] = h = n.deref(t[0])
201
+ node['heap'] = n.reheap(t, h)
197
202
 
198
203
  elsif heap == 'task' && heat[0] == '_task'
199
204
  #
@@ -296,11 +301,41 @@ module Flor
296
301
  @unit.archive_node(message['exid'], node)
297
302
  # archiving is only active during testing
298
303
 
304
+ #update_parent_node_tree(node)
305
+
299
306
  @execution['nodes'].delete(nid)
300
307
 
301
308
  cancels
302
309
  end
303
310
 
311
+ # This saves the modified trees in the parent when the node is removed
312
+ # it works ok except for 3 (2017-05-9) failing specs.
313
+ #
314
+ # Introducing 3 exceptions for this is not interesting.
315
+ #
316
+ # def update_parent_node_tree(node)
317
+ #
318
+ # t = node['tree']; return unless t
319
+ ##return if t[0] == '_apply'
320
+ # pnode = @execution['nodes'][node['parent']]; return unless pnode
321
+ #
322
+ # pt =
323
+ # pnode['tree'] ||
324
+ # Flor::Node.new(self, pnode, nil).lookup_tree(pnode['nid'])
325
+ # cid =
326
+ # Flor.child_id(node['nid'])
327
+ #
328
+ # if cid == pt[1].size # head "exception"
329
+ # return if pt[0] == t
330
+ # pt[0] = t
331
+ # pnode['tree'] = pt
332
+ # return
333
+ # end
334
+ #
335
+ # pt[1][cid] = t
336
+ # pnode['tree'] = pt
337
+ # end
338
+
304
339
  def leave_tags(message, node)
305
340
 
306
341
  ts = node['tags']; return [] unless ts && ts.any?
@@ -145,27 +145,25 @@ class Flor::Node
145
145
  #
146
146
  # that might be the way...
147
147
 
148
- def lookup(name)
149
-
150
- cat, mod, key = key_split(name)
151
- key, pth = key.split('.', 2)
152
-
153
- val =
154
- if [ cat, mod, key ] == [ 'v', '', 'node' ]
155
- @node
156
- elsif cat == 'v'
157
- lookup_var(@node, mod, key)
158
- elsif cat == 't'
159
- lookup_tag(mod, key)
160
- else
161
- lookup_field(mod, key)
162
- end
148
+ def lookup(name, silence_index_error=false)
149
+
150
+ cat, mod, key_and_path = key_split(name)
151
+ key, pth = key_and_path.split('.', 2)
163
152
 
164
- if pth
165
- Flor.deep_get(val, pth)[1]
153
+ if [ cat, mod, key ] == [ 'v', '', 'node' ]
154
+ lookup_in_node(pth)
155
+ elsif cat == 'v'
156
+ lookup_var(@node, mod, key, pth)
157
+ elsif cat == 't'
158
+ lookup_tag(mod, key)
166
159
  else
167
- val
160
+ lookup_field(mod, key_and_path)
168
161
  end
162
+
163
+ rescue IndexError
164
+
165
+ raise unless silence_index_error
166
+ nil
169
167
  end
170
168
 
171
169
  class Expander < Flor::Dollar
@@ -178,7 +176,8 @@ class Flor::Node
178
176
  return @node.exid if k == 'exid'
179
177
  return Flor.tstamp if k == 'tstamp'
180
178
 
181
- @node.lookup(k)
179
+ r = @node.lookup(k, true)
180
+ r.is_a?(Symbol) ? nil : r
182
181
  end
183
182
  end
184
183
 
@@ -202,10 +201,10 @@ class Flor::Node
202
201
 
203
202
  ref =
204
203
  case v[0]
205
- when '_func' then true
206
- when '_proc' then v[1]['proc'] != o
207
- when '_task' then v[1]['task'] != o
208
- else false
204
+ when '_func' then true
205
+ when '_proc' then v[1]['proc'] != o
206
+ when '_task' then v[1]['task'] != o
207
+ else false
209
208
  end
210
209
 
211
210
  v[1]['oref'] ||= v[1]['ref'] if ref && v[1]['ref']
@@ -226,8 +225,6 @@ class Flor::Node
226
225
  'apply'
227
226
  elsif heat[0] == '_task'
228
227
  'task'
229
- elsif Flor.is_tree_head_tree?(tree)
230
- '_happly'
231
228
  else
232
229
  '_val'
233
230
  end
@@ -317,54 +314,86 @@ class Flor::Node
317
314
  # @execution['nodes'][node['cnid']]
318
315
  #end
319
316
 
320
- def lookup_dvar(mod, key)
317
+ def lookup_in_node(pth)
321
318
 
322
- if mod != 'd' && Flor::Procedure[key]
323
- return [ '_proc', { 'proc' => key }, -1 ]
324
- end
319
+ Flor.deep_get(@node, pth)
320
+ end
325
321
 
326
- l = @executor.unit.loader
327
- vdomain = @node['vdomain']
328
- #
329
- if l && vdomain != false
330
- v = l.variables(vdomain || domain).fetch(key) { :no }
331
- return v unless v == :no
332
- end
322
+ class PseudoVarContainer < Hash
323
+ #
324
+ # inherit from Hash so that deep.rb is quietly mislead
325
+ #
326
+ def initialize(type); @type = type; end
327
+ #def has_key?(key); true; end
328
+ def [](key); [ "_#{@type}", { @type => key }, -1 ]; end
329
+ end
330
+ #
331
+ PROC_VAR_CONTAINER = PseudoVarContainer.new('proc')
332
+ TASKER_VAR_CONTAINER = PseudoVarContainer.new('task')
333
333
 
334
- if mod != 'd' && @executor.unit.has_tasker?(@executor.exid, key)
335
- return [ '_task', { 'task' => key }, -1 ]
336
- end
334
+ def lookup_var(node, mod, key, pth)
337
335
 
338
- nil
336
+ c = lookup_var_container(node, mod, key)
337
+
338
+ kp = [ key, pth ].reject { |x| x == nil || x.size < 1 }.join('.')
339
+
340
+ v = Flor.deep_get(c, kp)
341
+ #p [ mod, key, pth, '->', v ]
342
+
343
+ return v unless v.is_a?(Symbol)
344
+
345
+ vs = v.to_s.split('.')
346
+ tail = vs.pop
347
+ vs = vs.join('.')
348
+
349
+ fail IndexError.new("variable #{tail.inspect} not found") if vs.empty?
350
+ fail IndexError.new("no key #{tail.inspect} in variable #{vs.inspect}")
339
351
  end
340
352
 
341
- def lookup_var(node, mod, key)
353
+ def lookup_var_container(node, mod, key)
342
354
 
343
- return lookup_dvar(mod, key) if node == nil || mod == 'd'
355
+ return lookup_dvar_container(mod, key) if node == nil || mod == 'd'
344
356
 
345
357
  pnode = parent_node(node)
346
- #cnode = closure_node(node)
358
+ vars = node['vars']
347
359
 
348
360
  if mod == 'g'
349
- vars = node['vars']
350
- return lookup_var(pnode, mod, key) if pnode
351
- return vars[key] if vars
352
- #return lookup_var(cnode, mod, key) if cnode
361
+ return lookup_var_container(pnode, mod, key) if pnode
362
+ return vars if vars
353
363
  fail "node #{node['nid']} has no vars and no parent"
354
364
  end
355
365
 
356
- vars = node['vars']
357
-
358
- return vars[key] if vars && vars.has_key?(key)
366
+ return vars if vars && vars.has_key?(key)
359
367
 
360
368
  if cnid = node['cnid']
361
369
  cvars = (@execution['nodes'][cnid] || {})['vars']
362
- return cvars[key] if cvars && cvars.has_key?(key)
370
+ return cvars if cvars && cvars.has_key?(key)
363
371
  end
364
372
  #
365
373
  # look into closure, just one level deep...
366
374
 
367
- lookup_var(pnode, mod, key)
375
+ lookup_var_container(pnode, mod, key)
376
+ end
377
+
378
+ def lookup_dvar_container(mod, key)
379
+
380
+ if mod != 'd' && Flor::Procedure[key]
381
+ return PROC_VAR_CONTAINER
382
+ end
383
+
384
+ l = @executor.unit.loader
385
+ vdomain = @node['vdomain']
386
+ #
387
+ if l && vdomain != false
388
+ vars = l.variables(vdomain || domain)
389
+ return vars if vars.has_key?(key)
390
+ end
391
+
392
+ if mod != 'd' && @executor.unit.has_tasker?(@executor.exid, key)
393
+ return TASKER_VAR_CONTAINER
394
+ end
395
+
396
+ {}
368
397
  end
369
398
 
370
399
  def lookup_var_name(node, val)
@@ -389,9 +418,10 @@ class Flor::Node
389
418
  nids.empty? ? [ '_nul', nil, -1 ] : nids
390
419
  end
391
420
 
392
- def lookup_field(mod, key)
421
+ def lookup_field(mod, key_and_path)
393
422
 
394
- Flor.deep_get(payload.current, key)[1]
423
+ r = Flor.deep_get(payload.current, key_and_path)
424
+ r.is_a?(Symbol) ? nil : r
395
425
  end
396
426
 
397
427
  def key_split(key) # => category, mode, key
@@ -17,12 +17,15 @@ class Flor::Procedure < Flor::Node
17
17
 
18
18
  names = names.flatten
19
19
  @names = names if names.any?
20
+ @core = !! caller.find { |l| l.match(/flor\/pcore/) } if names.any?
20
21
 
21
22
  @names
22
23
  end
23
24
 
24
25
  alias :name :names
25
26
 
27
+ def core?; @core; end
28
+
26
29
  def make(executor, node, message)
27
30
 
28
31
  heap = node['heat'] ? node['heap'] : nil
@@ -234,9 +237,9 @@ class Flor::Procedure < Flor::Node
234
237
  unatt_unkeyed_children(true)
235
238
  end
236
239
 
237
- def stringify_first_child
240
+ def stringify_child(non_att_index)
238
241
 
239
- c = non_att_children.first
242
+ c = non_att_children[non_att_index]
240
243
  return unless c
241
244
  return unless c[1] == [] && c[0].is_a?(String)
242
245
 
@@ -247,6 +250,11 @@ class Flor::Procedure < Flor::Node
247
250
  @node['tree'] = [ tree[0], cn, tree[2] ]
248
251
  end
249
252
 
253
+ def stringify_first_child
254
+
255
+ stringify_child(0)
256
+ end
257
+
250
258
  def do_execute
251
259
 
252
260
  pre_execute
@@ -321,31 +329,37 @@ class Flor::Procedure < Flor::Node
321
329
  []
322
330
  end
323
331
 
324
- def receive
332
+ def determine_fcid_and_ncid
325
333
 
326
334
  @fcid = point == 'receive' ? Flor.child_id(from) : nil
327
335
  @ncid = (@fcid || -1) + 1
336
+ end
328
337
 
329
- return receive_first if @fcid == nil
338
+ def from_att?
339
+
340
+ @fcid && (c = children[@fcid]) && c[0] == '_att'
341
+ end
330
342
 
331
- child = children[@fcid]
343
+ def receive
332
344
 
333
- return receive_att if child && child[0] == '_att'
345
+ determine_fcid_and_ncid
334
346
 
347
+ return receive_first if @fcid == nil
348
+ return receive_att if from_att?
335
349
  receive_non_att
336
350
  end
337
351
 
338
352
  def receive_first
339
353
 
340
- return receive_last_att if children[0] && children[0][0] != '_att'
354
+ return receive_last_att if (c = children[0]) && c[0] != '_att'
341
355
  execute_child(@ncid)
342
356
  end
343
357
 
344
358
  def receive_att
345
359
 
346
- nctree = children[@ncid]
360
+ nt = children[@ncid]
347
361
 
348
- return receive_last_att if nctree == nil || nctree[0] != '_att'
362
+ return receive_last_att if nt == nil || nt[0] != '_att'
349
363
  execute_child(@ncid)
350
364
  end
351
365
 
@@ -477,9 +491,9 @@ class Flor::Procedure < Flor::Node
477
491
 
478
492
  if node
479
493
 
480
- b, v = Flor.deep_set(node['vars'], k, v)
494
+ v = Flor.deep_set(node['vars'], k, v)
481
495
 
482
- return v if b
496
+ return v unless v.is_a?(Symbol)
483
497
  end
484
498
 
485
499
  fail IndexError.new("couldn't set var #{mode}v.#{k}")
@@ -487,9 +501,9 @@ class Flor::Procedure < Flor::Node
487
501
 
488
502
  def set_field(k, v)
489
503
 
490
- success, value = Flor.deep_set(payload.copy, k, v)
504
+ value = Flor.deep_set(payload.copy, k, v)
491
505
 
492
- fail IndexError.new("couldn't set field #{k}") unless success
506
+ fail IndexError.new("couldn't set field #{k}") if value.is_a?(Symbol)
493
507
 
494
508
  value
495
509
  end
@@ -501,10 +515,10 @@ class Flor::Procedure < Flor::Node
501
515
  cat, mod, key = key_split(k)
502
516
 
503
517
  case cat[0, 1]
504
- when 'f' then set_field(key, v)
505
- when 'v' then set_var(mod, key, v)
506
- #when 'w' then set_war(key, v)
507
- else fail IndexError.new("don't know how to set #{k.inspect}")
518
+ when 'f' then set_field(key, v)
519
+ when 'v' then set_var(mod, key, v)
520
+ #when 'w' then set_war(key, v)
521
+ else fail IndexError.new("don't know how to set #{k.inspect}")
508
522
  end
509
523
  end
510
524
 
@@ -516,10 +530,12 @@ class Flor::Procedure < Flor::Node
516
530
 
517
531
  cni = fun[1]['cnid'] # closure nid
518
532
 
519
- t = lookup_tree(fni)
533
+ t = fun[1]['tree']
534
+ t = t || lookup_tree(fni) # TODO when fun[1]['tree'] is settled, drop me
520
535
  fail ArgumentError.new("couldn't find function at #{fni}") unless t
521
536
 
522
- t = t[0] if fun[1]['head']
537
+ t = t[0] if t[0].is_a?(Array)
538
+ t = t[1][0] if t[0] == '_att'
523
539
 
524
540
  sig = t[1].select { |c| c[0] == '_att' }
525
541
  sig = sig.drop(1) if t[0] == 'define'
@@ -629,6 +645,7 @@ class Flor::Macro < Flor::Procedure
629
645
  def rewrite
630
646
 
631
647
  t = rewrite_tree
648
+ #Flor.print_tree(t, nid)
632
649
 
633
650
  m = @message.dup
634
651
  m['tree'] = t