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
@@ -1,7 +1,40 @@
1
1
 
2
2
  module Flor
3
3
 
4
- module Lang include Raabro
4
+ def self.parse(input, fname=nil, opts={})
5
+
6
+ opts = fname if fname.is_a?(Hash) && opts.empty?
7
+
8
+ #Raabro.pp(Flor::Parser.parse(input, debug: 2), colours: true)
9
+ #Raabro.pp(Flor::Parser.parse(input, debug: 3), colours: true)
10
+
11
+ if r = Flor::Parser.parse(input, opts)
12
+ r << fname if fname
13
+ r
14
+ else
15
+ r = Flor::Parser.parse(input, opts.merge(error: true))
16
+ fail Flor::ParseError.new(r, fname)
17
+ end
18
+ end
19
+
20
+ class ParseError < StandardError
21
+
22
+ attr_reader :line, :column, :offset, :msg, :visual, :fname
23
+
24
+ def initialize(error_array, fname)
25
+
26
+ #puts "-" * 80
27
+ #p error_array
28
+ #puts error_array.last
29
+ #puts "-" * 80
30
+ @line, @column, @offset, @msg, @visual = error_array
31
+ @fname = fname
32
+
33
+ super("syntax error at line #{@line} column #{@column}")
34
+ end
35
+ end
36
+
37
+ module Parser include Raabro
5
38
 
6
39
  # parsing
7
40
 
@@ -28,9 +61,10 @@ module Flor
28
61
  def fls(i); str(nil, i, 'false'); end
29
62
  def boolean(i); alt(:boolean, i, :tru, :fls); end
30
63
 
64
+ def rf_sq_symbol(i); rex(nil, i, /[^.| \b\f\n\r\t"'()\[\]{}#\\]+/); end
65
+ def rf_sq_index(i); alt(nil, i, :rf_sq_symbol, :dqstring, :sqstring); end
66
+ def rf_square(i); seq(nil, i, :sbstart, :rf_sq_index, :sbend); end
31
67
  def rf_symbol(i); rex(nil, i, /[^.:;| \b\f\n\r\t"',()\[\]{}#\\]+/); end
32
- def rf_square_index(i); alt(nil, i, :rf_symbol, :dqstring, :sqstring); end
33
- def rf_square(i); seq(nil, i, :sbstart, :rf_square_index, :sbend); end
34
68
  def rf_dot(i); seq(nil, i, :dot, :rf_symbol); end
35
69
  def rf_index(i); alt(nil, i, :rf_dot, :rf_square); end
36
70
  def reference(i); seq(:ref, i, :rf_symbol, :rf_index, '*'); end
@@ -79,8 +113,12 @@ module Flor
79
113
  def comma_qmark_eol(i); seq(nil, i, :comma, '?', :eol); end
80
114
  def coll_sep(i); alt(nil, i, :comma_qmark_eol, :ws_star); end
81
115
 
82
- def ent(i); seq(:ent, i, :key, :postval, :colon, :postval, :exp, :postval); end
83
- def ent_qmark(i); rep(nil, i, :ent, 0, 1); end
116
+ def ent(i)
117
+ seq(:ent, i, :key, :postval, :colon, :postval, :exp, :postval)
118
+ end
119
+ def ent_qmark(i)
120
+ rep(nil, i, :ent, 0, 1)
121
+ end
84
122
 
85
123
  def exp_qmark(i); rep(nil, i, :exp, 0, 1); end
86
124
 
@@ -191,22 +229,26 @@ module Flor
191
229
 
192
230
  def rewrite_obj(t)
193
231
 
232
+ l = ln(t)
233
+
194
234
  cn =
195
235
  t.subgather(nil).inject([]) do |a, tt|
196
236
  a << rewrite(tt.c0.c0)
197
237
  a << rewrite(tt.c4)
198
238
  end
199
- cn = 0 if cn.empty?
239
+ cn = [ [ '_att', [ [ '_', [], l ] ], l ] ] if cn.empty?
200
240
 
201
- [ '_obj', cn, ln(t) ]
241
+ [ '_obj', cn, l ]
202
242
  end
203
243
 
204
244
  def rewrite_arr(t)
205
245
 
246
+ l = ln(t)
247
+
206
248
  cn = t.subgather(nil).collect { |n| rewrite(n) }
207
- cn = 0 if cn.empty?
249
+ cn = [ [ '_att', [ [ '_', [], l ] ], l ] ] if cn.empty?
208
250
 
209
- [ '_arr', cn, ln(t) ]
251
+ [ '_arr', cn, l ]
210
252
  end
211
253
 
212
254
  def rewrite_val(t)
@@ -236,6 +278,7 @@ fail "don't know how to invert #{operation.inspect}" # FIXME
236
278
 
237
279
  return rewrite(t.c0) if t.children.size == 1
238
280
 
281
+ #Raabro.pp(t, colours: true)
239
282
  cn = t.children.collect { |ct| ct.lookup(nil) }
240
283
 
241
284
  operation = cn.find { |ct| ct.name == :sop }.string
@@ -342,14 +385,18 @@ fail "don't know how to invert #{operation.inspect}" # FIXME
342
385
 
343
386
  def ta_rework_core(core)
344
387
 
388
+ dig = Digest::SHA256.hexdigest(JSON.dump(core))[0, 7]
389
+ tsp = Flor.tamp.gsub(/\./, '_')
390
+ h = "_head_#{dig}_#{tsp}"
391
+
345
392
  l = core[2]
346
393
 
347
394
  [ 'sequence', [
348
395
  [ 'set', [
349
- [ 'head_', [], l ],
396
+ [ h, [], l ],
350
397
  core[0]
351
398
  ], l ],
352
- [ 'head_', core[1], l ]
399
+ [ h, core[1], l ]
353
400
  ], l ]
354
401
  end
355
402
 
@@ -358,19 +405,19 @@ fail "don't know how to invert #{operation.inspect}" # FIXME
358
405
  @indent = tree.lookup(:indent).string.length
359
406
 
360
407
  ht = tree.lookup(:head)
361
- @line = Flor::Lang.line_number(ht)
408
+ @line = Flor::Parser.line_number(ht)
362
409
 
363
- @head = Flor::Lang.rewrite(ht.c0)
410
+ @head = Flor::Parser.rewrite(ht.c0)
364
411
  @head = @head[0] if @head[0].is_a?(String) && @head[1] == []
365
412
 
366
413
  atts = tree.children[2..-1]
367
414
  .inject([]) { |as, ct|
368
415
 
369
416
  kt = ct.children.size == 3 ? ct.children[1].lookup(:key) : nil
370
- v = Flor::Lang.rewrite(ct.clast)
417
+ v = Flor::Parser.rewrite(ct.clast)
371
418
 
372
419
  if kt
373
- k = Flor::Lang.rewrite(kt.c0)
420
+ k = Flor::Parser.rewrite(kt.c0)
374
421
  as << [ '_att', [ k, v ], k[2] ]
375
422
  else
376
423
  as << [ '_att', [ v ], v[2] ]
@@ -399,7 +446,7 @@ fail "don't know how to invert #{operation.inspect}" # FIXME
399
446
  elsif %w[ - + ].include?(c[0])
400
447
  @head = c[0]
401
448
  @children = c[1]
402
- @children[0] = Flor::Lang.invert('+', @children[0])
449
+ @children[0] = Flor::Parser.invert('+', @children[0])
403
450
  end
404
451
  end
405
452
 
@@ -434,20 +481,7 @@ fail "don't know how to invert #{operation.inspect}" # FIXME
434
481
  root.children.count == 1 ? root.children.first.to_a : root.to_a
435
482
  end
436
483
  alias rewrite_panode rewrite_flor
437
-
438
- def parse(input, fname=nil, opts={})
439
-
440
- opts = fname if fname.is_a?(Hash) && opts.empty?
441
-
442
- #Raabro.pp(super(input, debug: 2))
443
- #Raabro.pp(super(input, debug: 3))
444
-
445
- r = super(input, opts)
446
- r << fname if fname
447
-
448
- r
449
- end
450
- end # module Lang
484
+ end # module Parser
451
485
 
452
486
  def self.unescape_u(cs)
453
487
 
@@ -470,14 +504,14 @@ fail "don't know how to invert #{operation.inspect}" # FIXME
470
504
 
471
505
  if c == '\\'
472
506
  case cn = cs.next
473
- when 'u' then sio.print(unescape_u(cs))
474
- when '\\', '"', '\'' then sio.print(cn)
475
- when 'b' then sio.print("\b")
476
- when 'f' then sio.print("\f")
477
- when 'n' then sio.print("\n")
478
- when 'r' then sio.print("\r")
479
- when 't' then sio.print("\t")
480
- else sio.print("\\#{cn}")
507
+ when 'u' then sio.print(unescape_u(cs))
508
+ when '\\', '"', '\'' then sio.print(cn)
509
+ when 'b' then sio.print("\b")
510
+ when 'f' then sio.print("\f")
511
+ when 'n' then sio.print("\n")
512
+ when 'r' then sio.print("\r")
513
+ when 't' then sio.print("\t")
514
+ else sio.print("\\#{cn}")
481
515
  end
482
516
  else
483
517
  sio.print(c)
@@ -1,5 +1,8 @@
1
1
 
2
- class Flor::Pro::Arr < Flor::Procedure
2
+ require 'flor/pcore/_coll'
3
+
4
+
5
+ class Flor::Pro::Arr < Flor::Pro::Coll
3
6
  #
4
7
  # "_arr" is the procedure behind arrays.
5
8
  #
@@ -18,23 +21,20 @@ class Flor::Pro::Arr < Flor::Procedure
18
21
 
19
22
  name '_arr'
20
23
 
21
- def pre_execute
24
+ def receive_last_att
22
25
 
23
- @node['rets'] = []
24
- end
26
+ elts = tree[1][@ncid..-1]
25
27
 
26
- def receive
27
-
28
- return wrap_reply('ret' => []) if children == 0
28
+ return wrap_reply('ret' => elts.collect { |e| e[1] }) \
29
+ if elts.all? { |e| atomic?(e) }
29
30
 
31
+ @node['rets'] = []
30
32
  super
31
33
  end
32
34
 
33
35
  def receive_last
34
36
 
35
- payload['ret'] = @node['rets']
36
-
37
- wrap_reply
37
+ wrap_reply('ret' => @node['rets'])
38
38
  end
39
39
  end
40
40
 
@@ -67,8 +67,7 @@ class Flor::Pro::Att < Flor::Procedure
67
67
 
68
68
  if Flor.child_id(@message['from']) == 0
69
69
  ret = payload['ret']
70
- ret = ret[1]['task'] if Flor.is_task_tree?(ret)
71
- @node['key'] = k = ret
70
+ @node['key'] = k = unref(ret, :key)
72
71
  as = (parent_node || {})['atts_accepting_symbols'] || []
73
72
  execute_child(1, nil, 'accept_symbol' => as.include?(k))
74
73
  else
@@ -107,12 +106,18 @@ class Flor::Pro::Att < Flor::Procedure
107
106
  wrap_reply
108
107
  end
109
108
 
110
- # vars: { ... }, inits a scope for the parent node
109
+ # `vars: { ... }` inits a scope for the parent node
110
+ # `vars: 'copy'` copies the parent scope and use as local scope
111
+ # `vars: [ 'a', 'b' ]` inits a new scope containing vars a and b
111
112
  #
112
113
  def receive_vars
113
114
 
114
- vars = payload['ret']
115
- vars = @executor.vars(nid) if vars == 'copy' || vars == '*'
115
+ vars =
116
+ case (vs = payload['ret'])
117
+ when 'copy', '*' then copy_vars
118
+ when Array then wlist_vars(vs)
119
+ else vs
120
+ end
116
121
 
117
122
  (parent_node['vars'] ||= {}).merge!(vars)
118
123
 
@@ -121,6 +126,42 @@ class Flor::Pro::Att < Flor::Procedure
121
126
  wrap_reply
122
127
  end
123
128
 
129
+ def copy_vars
130
+
131
+ @executor.vars(nid)
132
+ # Returns a hash of all the vars known at point `nid` of the execution
133
+ end
134
+
135
+ def wlist_vars(vs)
136
+
137
+ mode =
138
+ case vs.first
139
+ when '+' then '+'
140
+ when '-', '^', '!' then '-'
141
+ #else nil
142
+ end
143
+ vs.shift if mode
144
+
145
+ vs = vs.collect { |v| Flor.is_regex_tree?(v) ? Flor.to_regex(v) : v }
146
+ vars = copy_vars
147
+
148
+ if mode == '-'
149
+ Hash[vars.map { |k, v| [ k, var_match?(vs, k) ? nil : v ] }]
150
+ else
151
+ Hash[vars.map { |k, v| [ k, var_match?(vs, k) ? v : nil ] }]
152
+ end
153
+ end
154
+
155
+ def var_match?(vs, key)
156
+
157
+ vs.each do |v|
158
+ return true if v == key
159
+ return true if v.is_a?(Regexp) && v =~ key
160
+ end
161
+
162
+ false
163
+ end
164
+
124
165
  def parent_is_trap?
125
166
 
126
167
  pt = parent_node_tree; return false unless pt
@@ -167,15 +208,9 @@ class Flor::Pro::Att < Flor::Procedure
167
208
  wrap_reply
168
209
  end
169
210
 
170
- def receive_on_error
171
-
172
- oe = payload['ret']
173
- oe[1]['on_error'] = true
174
-
175
- (parent_node['on_error'] ||= []) << oe
176
-
177
- wrap_reply
178
- end
211
+ def receive_on_error; store_on(:error); wrap_reply; end
212
+ def receive_on_cancel; store_on(:cancel); wrap_reply; end
213
+ def receive_on_timeout; store_on(:timeout); wrap_reply; end
179
214
 
180
215
  def receive_flank
181
216
 
@@ -0,0 +1,18 @@
1
+
2
+ class Flor::Pro::Coll < Flor::Procedure
3
+
4
+ protected
5
+
6
+ def atomic?(child_tree)
7
+
8
+ t0, t1 = child_tree
9
+
10
+ return false if t1.is_a?(Array)
11
+ return false if t0 == '_dqs' && t1.index('$(')
12
+ return false if t0 == '_rxs'
13
+ return false if t0 == '_func'
14
+
15
+ Flor::Pro::Atom.names.include?(t0)
16
+ end
17
+ end
18
+
@@ -1,5 +1,8 @@
1
1
 
2
- class Flor::Pro::Obj < Flor::Procedure
2
+ require 'flor/pcore/_coll'
3
+
4
+
5
+ class Flor::Pro::Obj < Flor::Pro::Coll
3
6
  #
4
7
  # "_obj" is the procedure behind objects (maps).
5
8
  #
@@ -27,8 +30,6 @@ class Flor::Pro::Obj < Flor::Procedure
27
30
 
28
31
  def execute_child(index=0, sub=nil, h=nil)
29
32
 
30
- return wrap_reply('ret' => {}) if children == 0
31
-
32
33
  return super if @node['rets'].size.odd?
33
34
 
34
35
  ct = children[index]
@@ -43,13 +44,28 @@ class Flor::Pro::Obj < Flor::Procedure
43
44
  super
44
45
  end
45
46
 
47
+ def receive_last_att
48
+
49
+ cn = tree[1][@ncid..-1]
50
+
51
+ return wrap_object(cn.collect { |c| c[1] }) \
52
+ if cn.all? { |c| atomic?(c) }
53
+
54
+ super
55
+ end
56
+
46
57
  def receive_last
47
58
 
48
- payload['ret'] = @node['rets']
49
- .each_slice(2)
50
- .inject({}) { |h, (k, v)| h[k.to_s] = v; h }
59
+ wrap_object(@node['rets'])
60
+ end
61
+
62
+ protected
63
+
64
+ def wrap_object(arr)
51
65
 
52
- wrap_reply
66
+ wrap_reply(
67
+ 'ret' =>
68
+ arr.each_slice(2).inject({}) { |h, (k, v)| h[k.to_s] = v; h })
53
69
  end
54
70
  end
55
71
 
@@ -56,7 +56,7 @@ class Flor::Pro::PatContainer < Flor::Procedure
56
56
  ct0 = ct[0]
57
57
 
58
58
  return :att if ct0 == '_att'
59
- return :pattern if /\A_pat_(arr|obj|or|guard)\z/ === ct0
59
+ return :pattern if /\A_pat_(arr|obj|or|guard|regex)\z/ === ct0
60
60
  return '_' if ct0 == '_'
61
61
  return ct0 if /\A[a-z][a-z0-9]*\z/ === ct0 && ct[1] == []
62
62
 
@@ -29,6 +29,9 @@ class Flor::Pro::PatGuard < Flor::Pro::PatContainer
29
29
  if ct == nil && @node['key'] == nil && payload['ret'].is_a?(String)
30
30
 
31
31
  k = payload['ret']
32
+
33
+ return wrap_no_match_reply if k == '_'
34
+
32
35
  m = k.match(Flor::SPLAT_REGEX)
33
36
  k = m ? k[0] : k
34
37
 
@@ -44,6 +47,11 @@ class Flor::Pro::PatGuard < Flor::Pro::PatContainer
44
47
  b = payload['_pat_binding']
45
48
  return wrap_no_match_reply unless b
46
49
 
50
+ if (k = @node['key']) && (m = b['match'])
51
+ b["#{k}__match"] = m
52
+ b["#{k}__matched"] = val
53
+ end
54
+
47
55
  @node['binding'].merge!(b)
48
56
  end
49
57
 
@@ -53,7 +53,7 @@ class Flor::Pro::PatObj < Flor::Pro::PatContainer
53
53
 
54
54
  unless key
55
55
  ret = ret.to_s
56
- return wrap_no_match_reply unless Flor.deep_has_key?(val, ret)
56
+ return wrap_no_match_reply unless Dense.has_key?(val, ret)
57
57
  @node['key'] = ret
58
58
  @node['keys'] << ret if @node['keys']
59
59
  return super
@@ -77,7 +77,7 @@ class Flor::Pro::PatObj < Flor::Pro::PatContainer
77
77
 
78
78
  @node['binding'][ct[0]] = val[@node['key']] if ct[0].length > 0
79
79
 
80
- elsif Flor.deep_get(val, key) != ret
80
+ elsif Dense.get(val, key) != ret
81
81
 
82
82
  return wrap_no_match_reply
83
83
  end
@@ -137,7 +137,7 @@ class Flor::Pro::PatObj < Flor::Pro::PatContainer
137
137
 
138
138
  def sub_val(child_index)
139
139
 
140
- [ 1, val[@node['key']] ]
140
+ [ 1, Dense.get(val, @node['key']) ]
141
141
  end
142
142
  end
143
143