cddl 0.1.0 → 0.8.8

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 (50) hide show
  1. checksums.yaml +5 -5
  2. data/bin/cddl +80 -23
  3. data/cddl.gemspec +4 -2
  4. data/data/cddl.abnf +50 -20
  5. data/data/prelude.cddl +9 -3
  6. data/lib/cbor-pp-play.rb +13 -0
  7. data/lib/cbor-pp.rb +213 -0
  8. data/lib/cddl.rb +1001 -163
  9. data/test/test-cddl.rb +1620 -93
  10. data/test-data/7071-concise-cut.cddl +48 -0
  11. data/test-data/7071-concise.cddl +1 -1
  12. data/test-data/7071-verbose.cddl +1 -1
  13. data/test-data/a.cddl +9 -0
  14. data/test-data/ambig.cddl +2 -0
  15. data/test-data/b.cddl +3 -0
  16. data/test-data/badaddr.cddl +1 -0
  17. data/test-data/basic_syntax_example.cddl +7 -0
  18. data/test-data/cdni-ct.cddl +72 -0
  19. data/test-data/cose.cddl +68 -0
  20. data/test-data/dcaf.cddl +58 -0
  21. data/test-data/dcaf1.cddl +48 -0
  22. data/test-data/dotsize.cddl +7 -0
  23. data/test-data/extractor-demo.cddl +14 -0
  24. data/test-data/grasp-01-extract.cddl +91 -0
  25. data/test-data/grasp-01-test.cddl +90 -0
  26. data/test-data/grasp-01.cddl +90 -0
  27. data/test-data/grasp-09.cddl +108 -0
  28. data/test-data/grasp-v1.cddl +97 -0
  29. data/test-data/grasp-v2X.cddl +89 -0
  30. data/test-data/ifmap-base-2.0v17.cddl +392 -0
  31. data/test-data/ifmap-base-2.2v9_fh-cabo.cddl +149 -0
  32. data/test-data/ifmap-metadata-2.2v9_fh-cabo.cddl +239 -0
  33. data/test-data/ifmap2.cddl +21 -0
  34. data/test-data/jcr-ex.cddl +42 -0
  35. data/test-data/kevin5.cddl +5 -0
  36. data/test-data/map-group.cddl +13 -0
  37. data/test-data/mapkey.cddl +2 -0
  38. data/test-data/minimal.cddl +1 -0
  39. data/test-data/patch1.cddl +13 -0
  40. data/test-data/reused_named_group.cddl +14 -0
  41. data/test-data/structure.cddl +9 -0
  42. data/test-data/toerless0.cddl +14 -0
  43. data/test-data/toerless1.cddl +11 -0
  44. data/test-data/two_anonymous_groups.cddl +13 -0
  45. data/test-data/wrong1.cddl +17 -0
  46. data/test-data/wrong1a.cddl +23 -0
  47. data/test-data/wrong2.cddl +17 -0
  48. data/test-data/wrong2a.cddl +17 -0
  49. data/test-data/xmlmig.cddl +15 -0
  50. metadata +72 -5
data/lib/cddl.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  require 'abnc'
2
2
  require 'pp'
3
3
  require 'pathname'
4
+ require 'cbor-pure' unless defined?(CBOR::Tagged)
5
+ require 'regexp-examples'
6
+ require 'colorize'
7
+ require 'base64'
4
8
 
5
9
  module CDDL
6
10
 
@@ -9,7 +13,16 @@ module CDDL
9
13
  PRELUDE = File.read("#{DATA_DIR}/prelude.cddl")
10
14
  ABNF_SPEC = File.read("#{DATA_DIR}/cddl.abnf")
11
15
 
16
+ MANY = Float::INFINITY
17
+
18
+ MAX_RECURSE = 128 # XXX
19
+
20
+ class ParseError < ArgumentError; end
21
+
12
22
  class Parser
23
+
24
+ class BackTrack < Exception; end
25
+
13
26
  def initialize(source_text)
14
27
  @abnf = Peggy::ABNF.new
15
28
  _cresult = @abnf.compile! ABNF_SPEC, ignore: :s
@@ -17,71 +30,334 @@ module CDDL
17
30
  expected_length = source_text.length + PRELUDE.length
18
31
  if expected_length != presult
19
32
  upto = @abnf.parse_results.keys.max
20
- puts "UPTO: #{upto}"
21
- pp @abnf.parse_results[upto]
22
- pp @abnf.parse_results[presult]
23
- p presult
24
- puts @abnf.ast?
25
- raise "parse error at #{presult} upto #{upto} of #{expected_length}"
33
+ puts "UPTO: #{upto}" if $advanced
34
+ pp @abnf.parse_results[upto] if $advanced
35
+ pp @abnf.parse_results[presult] if $advanced
36
+ puts "SO FAR: #{presult}" if $advanced
37
+ puts @abnf.ast? if $advanced
38
+ presult ||= 0
39
+ part1 = source_text[[presult - 100, 0].max...presult]
40
+ part3 = source_text[upto...[upto + 100, source_text.length].min]
41
+ if upto - presult < 100
42
+ part2 = source_text[presult...upto]
43
+ else
44
+ part2 = source_text[presult, 50] + "......." + source_text[upto-50, 50]
45
+ end
46
+ warn "*** Look for syntax problems around the #{
47
+ "%%%".colorize(background: :light_yellow)} markers:\n#{
48
+ part1}#{"%%%".colorize(color: :green, background: :light_yellow)}#{
49
+ part2}#{"%%%".colorize(color: :red, background: :light_yellow)}#{
50
+ part3}"
51
+ raise ParseError, "*** Parse error at #{presult} upto #{upto} of #{
52
+ source_text.length} (#{expected_length})."
26
53
  end
27
54
  puts @abnf.ast? if $debug_ast
28
55
  @ast = @abnf.ast?
56
+ # our little argument stack for rule processing
57
+ @insides = []
29
58
  end
30
59
 
31
60
  def apr # for debugging
32
61
  @abnf.parse_results
33
62
  end
34
63
 
64
+ def ast_debug
65
+ ast.to_s[/.*comment/m] # stop at first comment -- prelude
66
+ end
67
+
68
+ def strip_nodes(n)
69
+ [n[0], *n[1..-1].map {|e|
70
+ e._strip
71
+ }]
72
+ end
73
+
74
+ def walk(rule, anno = nil, &block)
75
+ r = []
76
+ case rule
77
+ when Array
78
+ r.concat(Array(yield rule, anno))
79
+ case rule[0]
80
+ when :type1, :array, :map
81
+ a = (rule.cbor_annotations rescue nil) if rule.size == 2
82
+ a = a.first if a
83
+ r.concat(rule[1..-1].map{|x| walk(x, a, &block)})
84
+ when :grpchoice
85
+ r.concat(rule[1..-1].map{|x| x.flat_map {|y| walk(y, &block)}})
86
+ when :member
87
+ r << walk(rule[3], &block)
88
+ r << walk(rule[4], &block)
89
+ when :anno
90
+ r << walk(rule[2], &block)
91
+ r << walk(rule[3], &block)
92
+ else
93
+ # p ["LEAF", rule[0]]
94
+ end
95
+ end
96
+ r.compact
97
+ end
98
+
99
+ def cname(s)
100
+ s.to_s.gsub(/-/, "_")
101
+ end
102
+
103
+ # Generate some simple #define lines from the value-only rules
104
+ def defines(prefix)
105
+ prefix ||= "CDDL"
106
+ if prefix =~ /%\d*\$?s/
107
+ format = prefix
108
+ format += "\n" unless format[-1] == "\n"
109
+ else
110
+ format = "#define #{prefix}_%s %s\n"
111
+ end
112
+ s = {} # keys form crude set of defines
113
+ add = proc { |*a| s[format % a] = true }
114
+ r = rules
115
+ ast.each :rule do |rule|
116
+ if rulename = rule.typename
117
+ t = rule.type.children(:type1)
118
+ if t.size == 1
119
+ if (t2 = t.first.children(:type2)) && t2.size == 1 && (v = t2.first.value)
120
+ add.(cname(rulename), v.to_s)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ # CBOR::PP.pp r
126
+ walk(r) do |subtree, anno|
127
+ if subtree[0] == :type1 && subtree[1..-1].all? {|x| x[0] == :int}
128
+ if enumname = subtree.cbor_annotations rescue nil
129
+ enumname = cname(enumname.first)
130
+ subtree[1..-1].each do |x|
131
+ if memname = x.cbor_annotations
132
+ memname = "#{enumname}_#{cname(memname.first)}"
133
+ add.(memname, x[1].to_s)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ if subtree[0] == :array
139
+ if (arrayname = subtree.cbor_annotations rescue nil) || anno
140
+ arrayname = cname(arrayname ? arrayname.first : anno)
141
+ subtree[1..-1].each_with_index do |x, i|
142
+ if x[0] == :member
143
+ if Array === x[3] && x[3][0] == :text
144
+ memname = x[3][1] # preferably use key string
145
+ elsif memname = x[4].cbor_annotations
146
+ memname = memname.first # use value annotation otherwise
147
+ end
148
+ if memname
149
+ memname = "#{arrayname}_#{cname(memname)}_index"
150
+ add.(memname, i.to_s)
151
+ end
152
+ end
153
+ if x[0] == :member && (x[1] != 1 || x[2] != 1)
154
+ break # can't give numbers if we have optionals etc.
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ s.keys.join
161
+ end
162
+
35
163
  def rules
36
164
  @rules = {}
165
+ @generics = {}
166
+ @bindings = [{}]
37
167
  ast.each :rule do |rule|
38
- # p rule
39
- if rulename = rule.groupname
40
- fail "Duplicate rule #{rulename.to_s}" if @rules[rulename.to_s]
41
- @rules[rulename.to_s] = [:grpent, *rule.group.children(:grpent)]
42
- elsif rulename = rule.typename
43
- fail "Duplicate rule #{rulename.to_s}" if @rules[rulename.to_s]
44
- @rules[rulename.to_s] = [:type1, *rule.type.children(:type1)]
168
+ rule_ast =
169
+ if rulename = rule.groupname
170
+ [:grpent, rule.grpent]
171
+ elsif rulename = rule.typename
172
+ [:type1, *rule.type.children(:type1)]
173
+ else
174
+ fail "Huh?"
175
+ end
176
+ n = rulename.to_s
177
+ asg = rule.assign.to_s
178
+ if g = rule.genericparm
179
+ if asg != "="
180
+ fail "Augment #{asg.inspect} not implemented for generics"
181
+ end
182
+ ids = g.children(:id).map(&:to_s)
183
+ # puts ["ids", ids].inspect
184
+ if b = @generics[n]
185
+ fail "Duplicate generics definition #{n} as #{rule_ast} (was #{b})"
186
+ end
187
+ @generics[n] = [rule_ast, ids]
45
188
  else
46
- fail "Huh?"
189
+ case asg
190
+ when "="
191
+ if @rules[n]
192
+ a = strip_nodes(rule_ast).inspect
193
+ b = strip_nodes(@rules[n]).inspect
194
+ if a == b
195
+ warn "*** Warning: Identical redefinition of #{n} as #{a}"
196
+ else
197
+ fail "Duplicate rule definition #{n} as #{b} (was #{a})"
198
+ end
199
+ end
200
+ @rules[n] = rule_ast
201
+ when "/="
202
+ @rules[n] ||= [:type1]
203
+ fail "Can't add #{rule_ast} to #{n}" unless rule_ast[0] == :type1
204
+ # XXX need to check existing rule as well
205
+ @rules[n].concat rule_ast[1..-1]
206
+ # puts "#{@rules[n].inspect} /="
207
+ when "//="
208
+ @rules[n] ||= [:grpchoice]
209
+ fail "Can't add #{rule_ast} to #{n}" unless rule_ast[0] == :grpent
210
+ # XXX need to check existing rule as well
211
+ if @rules[n][0] == :grpent # widen
212
+ @rules[n] = [:grpchoice, @rules[n]]
213
+ end
214
+ @rules[n] << rule_ast
215
+ # puts "#{@rules[n].inspect} //="
216
+ else
217
+ fail "Unknown assign #{asg.inspect}"
218
+ end
47
219
  end
48
220
  end
49
- # pp @rules
50
- @rootrule = @rules.keys.first
221
+ # pp @generics
222
+ @rootrule = @rules.keys.first # DRAFT: generics are ignored here.
51
223
  # now process the rules...
52
224
  @stage1 = {}
53
- r_process(@rootrule, @rules[@rootrule])
54
- # @rules.each do |n, r| # debug only loop
55
- # r_process(n, r)
225
+ result = r_process(@rootrule, @rules[@rootrule])
226
+ r_process("used_in_cddl_prelude", @rules["used_in_cddl_prelude"])
227
+ @rules.each do |n, r|
228
+ # r_process(n, r) # debug only loop
229
+ warn "*** Unused rule #{n}" unless @stage1[n]
230
+ end
231
+ if result[0] == :grpent
232
+ warn "Group at top -- first rule must be a type!"
233
+ end
56
234
  # end
57
235
  # @stage1
236
+ result
237
+ end
238
+
239
+ def lookup_recurse_grpent(name)
240
+ rule = @stage1[name]
241
+ # pp rule
242
+ fail unless rule.size == 2
243
+ [rule[0], *rule[1]]
58
244
  end
59
245
 
60
- def generate(where=rules, inmap = false)
246
+ # Memoize a bit here
247
+
248
+ REGEXP_FOR_STRING = Hash.new {|h, k|
249
+ h[k] = Regexp.new("\\A#{k}\\z")
250
+ }
251
+
252
+ def generate
253
+ @recursion = 0
254
+ generate1(rules)
255
+ end
256
+
257
+ def generate1(where, inmap = false)
61
258
  case where[0]
62
259
  when :type1
63
- generate(where[rand(where.size-1)+1])
260
+ fail BackTrack.new("Can't generate from empty type choice socket yet") unless where.size > 1
261
+ begin
262
+ chosen = where[rand(where.size-1)+1]
263
+ generate1(chosen)
264
+ rescue BackTrack
265
+ tries = where[1..-1].sample(where.size) - [chosen]
266
+ r = begin
267
+ if tries.empty?
268
+ BackTrack.new("No suitable alternative in type choice")
269
+ else
270
+ generate1(tries.pop)
271
+ end
272
+ rescue BackTrack
273
+ retry
274
+ end
275
+ fail r if BackTrack === r
276
+ r
277
+ end
278
+ when :grpchoice
279
+ fail BackTrack.new("Can't generate from empty group choice socket yet") unless where.size > 1
280
+ begin
281
+ chosen = where[rand(where.size-1)+1]
282
+ chosen.flat_map {|m| generate1(m, inmap)}
283
+ rescue BackTrack
284
+ tries = where[1..-1].sample(where.size) - [chosen]
285
+ r = begin
286
+ if tries.empty?
287
+ BackTrack.new("No suitable alternative in group choice")
288
+ else
289
+ tries.pop.flat_map {|m| generate1(m, inmap)}
290
+ end
291
+ rescue BackTrack
292
+ retry
293
+ end
294
+ fail r if BackTrack === r
295
+ r
296
+ end
64
297
  when :map
65
- Hash[where[1..-1].flat_map {|m| generate(m, true)}]
66
- when :array
67
- where[1..-1].flat_map {|m| generate(m).map{|e| e[1]}}
298
+ Hash[where[1..-1].flat_map {|m| generate1(m, true)}]
299
+ when :recurse_grpent
300
+ name = where[1]
301
+ rule = lookup_recurse_grpent(name)
302
+ if @recursion < MAX_RECURSE
303
+ @recursion += 1
304
+ #p ["recurse_grpent", *rule]
305
+ #r = generate1(rule)
306
+ r = rule[1..-1].flat_map {|m| generate1(m, inmap)}
307
+ @recursion -= 1
308
+ r
309
+ else
310
+ fail "Deep recursion into #{name}: #{@stage1[name]}, not yet implemented"
311
+ end
312
+ when :array, :grpent
313
+ r = where[1..-1].flat_map {|m| generate1(m).map{|e| e[1]}}
314
+ .flat_map {|e| Array === e && e[0] == :grpent ? e[1..-1] : [e]}
315
+ # nested grpents need to be "unpacked"
316
+ if where[0] == :grpent
317
+ [:grpent, *r]
318
+ else
319
+ r
320
+ end
68
321
  when :member
69
- s = where[1]
70
- e = where[2]
71
- e = 4 if e == -1
72
- s += rand(e + 1 - s) if e != s
322
+ st = where[1]
323
+ fudge = 4 * (1 - (@recursion / MAX_RECURSE.to_f))**3 # get less generate-happy with more recursion
324
+ en = [where[2], [st, fudge].max].min # truncate to fudge unless must be more
325
+ st += rand(en + 1 - st) if en != st
73
326
  kr = where[3]
74
327
  vr = where[4]
75
- fail "member key not given in map for #{where}" unless kr if inmap
76
- Array.new(s) { [ (generate(kr) if kr), # XXX: need error in map context
77
- generate(vr)
78
- ]}
79
- when :string, :int, :float
328
+ if inmap
329
+ unless kr
330
+ case vr[0]
331
+ when :grpent
332
+ fail "grpent in map #{vr.inspect}" unless vr.size == 2
333
+ g = Array.new(st) { generate1(vr[1], true) }.flatten(1)
334
+ # warn "GGG #{g.inspect}"
335
+ return g
336
+ when :grpchoice
337
+ g = Array.new(st) { generate1(vr, true) }.flatten(1)
338
+ # warn "GGG #{g.inspect}"
339
+ return g
340
+ else
341
+ fail "member key not given in map for #{where}" # || vr == [:grpchoice]
342
+ end
343
+ end
344
+ end
345
+ begin
346
+ Array.new(st) { [ (generate1(kr) if kr), # XXX: need error in map context
347
+ generate1(vr)
348
+ ]}
349
+ rescue BackTrack
350
+ fail BackTrack.new("Need #{where[1]}..#{where[2]} of these: #{[kr, vr].inspect}") unless where[1] == 0
351
+ []
352
+ end
353
+ when :text, :int, :float, :bytes
80
354
  where[1]
355
+ when :range
356
+ rand(where[1])
81
357
  when :prim
82
358
  case where[1]
83
359
  when nil
84
- gen_word
360
+ gen_word # XXX: maybe always returning a string is confusing
85
361
  when 0
86
362
  rand(4711)
87
363
  when 1
@@ -90,6 +366,8 @@ module CDDL
90
366
  gen_word.force_encoding(Encoding::BINARY)
91
367
  when 3
92
368
  gen_word
369
+ when 6
370
+ CBOR::Tagged.new(where[2], generate1(where[3]))
93
371
  when 7
94
372
  case where[2]
95
373
  when nil
@@ -108,22 +386,154 @@ module CDDL
108
386
  else
109
387
  fail "Can't generate prim #{where[1]}"
110
388
  end
389
+ when :anno
390
+ target = where[2]
391
+ control = where[3]
392
+ case where[1]
393
+ when :size
394
+ should_be_int = generate1(control)
395
+ unless (Array === target && target[0] == :prim && [0, 2, 3].include?(target[1])) && Integer === should_be_int && should_be_int >= 0
396
+ fail "Don't know yet how to generate #{where}"
397
+ end
398
+ s = Random.new.bytes(should_be_int)
399
+ case target[1]
400
+ when 0
401
+ # silently restrict to what we can put into a uint
402
+ s[0...8].bytes.inject(0) {|a, b| a << 8 | b }
403
+ when 2
404
+ s
405
+ when 3
406
+ Base64.urlsafe_encode64(s)[0...should_be_int]
407
+ # XXX generate word a la w = gen_word
408
+ end
409
+ when :bits
410
+ set_of_bits = Array.new(10) { generate1(control) } # XXX: ten?
411
+ # p set_of_bits
412
+ unless (target == [:prim, 0] || target == [:prim, 2]) &&
413
+ set_of_bits.all? {|x| Integer === x && x >= 0 }
414
+ fail "Don't know yet how to generate #{where}"
415
+ end
416
+ if target == [:prim, 2]
417
+ set_of_bits.inject(String.new) do |s, i|
418
+ n = i >> 3
419
+ bit = 1 << (i & 7)
420
+ if v = s.getbyte(n)
421
+ s.setbyte(n, v | bit); s
422
+ else
423
+ s << "\0" * (n - s.size) << bit.chr(Encoding::BINARY)
424
+ end
425
+ end
426
+ else # target == [:prim, 0]
427
+ set_of_bits.inject(0) do |a, v|
428
+ a |= (1 << v)
429
+ end
430
+ end
431
+ when :default
432
+ # Hmm.
433
+ unless $default_warned
434
+ warn "*** Ignoring .default for now."
435
+ $default_warned = true
436
+ end
437
+ generate1(target, inmap)
438
+ when :eq
439
+ content = generate1(control)
440
+ if validate1(content, where)
441
+ return content
442
+ end
443
+ fail "Not smart enough to generate #{where}"
444
+ when :lt, :le, :gt, :ge, :ne
445
+ if Array === target && target[0] == :prim
446
+ content = generate1(control)
447
+ try = if Numeric === content
448
+ content = Integer(content)
449
+ case target[1]
450
+ when 0
451
+ case where[1]
452
+ when :lt
453
+ rand(0...content)
454
+ when :le
455
+ rand(0..content)
456
+ end
457
+ end
458
+ end
459
+ if validate1(try, where)
460
+ return try
461
+ else
462
+ warn "HUH gen #{where.inspect} #{try.inspect}" unless try.nil?
463
+ end
464
+ end
465
+ 32.times do
466
+ content = generate1(target)
467
+ if validate1(content, where)
468
+ return content
469
+ end
470
+ end
471
+ fail "Not smart enough to generate #{where}"
472
+ when :regexp
473
+ regexp = generate1(control)
474
+ unless target == [:prim, 3] && String === regexp
475
+ fail "Don't know yet how to generate #{where}"
476
+ end
477
+ REGEXP_FOR_STRING[regexp].random_example(max_repeater_variance: 5)
478
+ when :cbor, :cborseq
479
+ unless target == [:prim, 2]
480
+ fail "Don't know yet how to generate #{where}"
481
+ end
482
+ content = CBOR::encode(generate1(control))
483
+ if where[1] == :cborseq
484
+ # remove the first head
485
+ n = case content.getbyte(0) - (4 << 5)
486
+ when 0..23; 1
487
+ when 24; 2
488
+ when 25; 3
489
+ when 26; 5
490
+ when 27; 9 # unlikely :-)
491
+ else fail "Generated .cborseq sequence for #{where} not an array"
492
+ end
493
+ content[0...n] = ''
494
+ end
495
+ content
496
+ when :within, :and
497
+ 32.times do
498
+ content = generate1(target)
499
+ if validate1(content, control)
500
+ return content
501
+ elsif where[1] == :within
502
+ warn "*** #{content.inspect} meets #{target.inspect} but not #{control.inspect}"
503
+ end
504
+ end
505
+ fail "Not smart enough to generate #{where}"
506
+ else
507
+ fail "Don't know yet how to generate from #{where}"
508
+ end
509
+ when :recurse
510
+ name = where[1]
511
+ rule = @stage1[name]
512
+ if @recursion < MAX_RECURSE
513
+ @recursion += 1
514
+ #p ["recurse", *rule]
515
+ r = generate1(rule)
516
+ @recursion -= 1
517
+ r
518
+ else
519
+ fail "Deep recursion into #{name}: #{@stage1[name]}, not yet implemented"
520
+ end
111
521
  else
112
- # fail where
522
+ fail "Don't know how to generate from #{where[0]} in #{where.inspect}"
113
523
  end
114
524
  end
115
525
 
116
- VALUE_TYPE = {string: true, int: true, float: true}
526
+ VALUE_TYPE = {text: String, int: Integer, float: Float}
117
527
  SIMPLE_VALUE = {
118
- [:prim, 7, 20] => [true, false],
119
- [:prim, 7, 21] => [true, true],
120
- [:prim, 7, 22] => [true, nil],
121
- [:prim, 7, 23] => [true, :undefined],
528
+ [:prim, 7, 20] => [true, false, :bool],
529
+ [:prim, 7, 21] => [true, true, :bool],
530
+ [:prim, 7, 22] => [true, nil, :nil],
531
+ [:prim, 7, 23] => [true, :undefined, :undefined],
122
532
  }
123
533
 
124
- def extract_value(t)
125
- if VALUE_TYPE[t[0]]
126
- [true, t[1]]
534
+ def extract_value(t) # []
535
+ if vt = VALUE_TYPE[t[0]]
536
+ [true, t[1], vt]
127
537
  elsif v = SIMPLE_VALUE[t]
128
538
  v
129
539
  else
@@ -136,10 +546,11 @@ module CDDL
136
546
  end
137
547
 
138
548
  def validate(d, warn=true)
139
- result = validate1(d)
549
+ @recursion = 0
550
+ result = validate1a(d, rules)
140
551
  unless result
141
552
  if warn
142
- warn "CDDL validation failure:"
553
+ warn "CDDL validation failure (#{result.inspect} for #{d.inspect}):"
143
554
  PP::pp(validate_diag, STDERR)
144
555
  end
145
556
  end
@@ -153,67 +564,267 @@ module CDDL
153
564
  )
154
565
  end
155
566
 
156
- def validate1(d, where=rules)
157
- # puts "DATA: #{d.inspect}"
158
- # puts "RULE: #{where.inspect}"
567
+ def validate_forward(d, start, where)
568
+ # warn ["valforw", d, start, where].inspect
569
+ i = 0
570
+ ann = []
571
+ where[1..-1].each { |r|
572
+ t, s, e, _k, v = r # XXX
573
+ if t == :recurse_grpent
574
+ rule = lookup_recurse_grpent(s)
575
+ n, ann2 = validate_linear(d, start+i, rule)
576
+ return [false, ann] unless n
577
+ i += n
578
+ ann.concat(ann2)
579
+ elsif t == :grpchoice
580
+ return [false, ann] unless r[1..-1].any? {|cand|
581
+ n, ann2 = validate_forward(d, start+i, [:foo, *cand])
582
+ if n
583
+ i += n
584
+ ann.concat(ann2)
585
+ end}
586
+ else
587
+ fail r.inspect unless t == :member
588
+ occ = 0
589
+ while ((occ < e) && i != d.size && ((n, ann2 = validate_linear(d, start+i, v)); n))
590
+ i += n
591
+ occ += 1
592
+ ann.concat(ann2)
593
+ end
594
+ if occ < s
595
+ @last_message = "occur not reached in array #{d} for #{where}"
596
+ return [false, ann]
597
+ end
598
+ end
599
+ }
600
+ # warn ["valforw>", i].inspect
601
+ [i, ann]
602
+ end
603
+
604
+ # returns number of matches or false for breakage
605
+ def validate_linear(d, start, where)
606
+ # warn ["vallin", d, start, where].inspect
607
+ fail unless Array === d
608
+ case where[0]
609
+ when :grpent
610
+ # must be inside an array with nested occurrences
611
+ validate_forward(d, start, where)
612
+ else
613
+ (ann = validate1a(d[start], where)) ? [1, ann] : [false, ann]
614
+ end
615
+ end
616
+
617
+ def map_check(d, d_check, members)
618
+ anno = []
619
+ anno if members.all? { |r|
620
+ puts "ALL SUBRULE: #{r.inspect}" if ENV["CDDL_TRACE"]
621
+ t, s, e, k, v = r
622
+ case t
623
+ when :recurse_grpent
624
+ rule = lookup_recurse_grpent(s)
625
+ if ann2 = map_check(d, d_check, rule[1..-1])
626
+ anno.concat(ann2)
627
+ end
628
+ when :grpchoice
629
+ r[1..-1].any? {|cand|
630
+ puts "CHOICE SUBRULE: #{cand.inspect}" if ENV["CDDL_TRACE"]
631
+ cand_d_check = d_check.dup
632
+ if ann2 = map_check(d, cand_d_check, cand)
633
+ puts "CHOICE SUBRULE SUCCESS: #{cand.inspect}" if ENV["CDDL_TRACE"]
634
+ d_check.replace(cand_d_check)
635
+ anno.concat(ann2)
636
+ end
637
+ }
638
+ when :member
639
+ unless k
640
+ case v[0]
641
+ when :grpent
642
+ entries = v[1..-1]
643
+ when :grpchoice
644
+ entries = [v]
645
+ else
646
+ fail "member name not known for group entry #{r} in map"
647
+ end
648
+ d_check1 = d_check.dup
649
+ occ = 0
650
+ ann2 = []
651
+ while occ < e && (ann3 = map_check(d, d_check1, entries)) && ann3 != []
652
+ occ += 1
653
+ ann2.concat(ann3)
654
+ end
655
+ if occ >= s
656
+ d_check.replace(d_check1)
657
+ anno.concat(ann2)
658
+ puts "OCC SATISFIED: #{occ.inspect} >= #{s.inspect}" if ENV["CDDL_TRACE"]
659
+ anno
660
+ else
661
+ # leave some diagnostic breadcrumbs?
662
+ puts "OCC UNSATISFIED: #{occ.inspect} < #{s.inspect}" if ENV["CDDL_TRACE"]
663
+ false
664
+ end
665
+ else
666
+ # this is mostly quadratic; let's do the linear thing if possible
667
+ simple, simpleval = extract_value(k)
668
+ if simple
669
+ puts "SIMPLE: #{d_check.inspect} #{simpleval}" if ENV["CDDL_TRACE"]
670
+ # add occurrence check; check that val is present in the first place
671
+ actual = d.fetch(simpleval, :not_found)
672
+ if actual == :not_found
673
+ s == 0 # minimum occurrence must be 0 then
674
+ else
675
+ if (ann2 = validate1a(actual, v)) &&
676
+ d_check.delete(simpleval) {:not_found} != :not_found
677
+ anno.concat(ann2)
678
+ end
679
+ end
680
+ else
681
+ puts "COMPLEX: #{k.inspect} #{simple.inspect} #{simpleval.inspect}" if ENV["CDDL_TRACE"]
682
+ keys = d_check.keys
683
+ ta, keys = keys.partition{ |key| validate1(key, k)}
684
+ # XXX check ta.size against s/e
685
+ ta.all? { |val|
686
+ if (ann2 = validate1a(d[val], v)) &&
687
+ d_check.delete(val) {:not_found} != :not_found
688
+ anno.concat(ann2)
689
+ end
690
+ }
691
+ end
692
+ end
693
+ else
694
+ fail "Cannot validate #{t} in maps yet #{r}" # MMM
695
+ end
696
+ }
697
+ end
698
+
699
+ def validate1a(d, where)
700
+ if ann = validate1(d, where)
701
+ here = [d, where]
702
+ if Array === ann
703
+ [here, *ann]
704
+ else
705
+ [here]
706
+ end
707
+ end
708
+ end
709
+
710
+ OPERATORS = {lt: :<, le: :<=, gt: :>, ge: :>=, eq: :==, ne: :!=}
711
+
712
+ def validate1(d, where)
713
+ if ENV["CDDL_TRACE"]
714
+ puts "DATA: #{d.inspect}"
715
+ puts "RULE: #{where.inspect}"
716
+ end
717
+ # warn ["val1", d, where].inspect
159
718
  @last_data = d
160
719
  @last_rule = where
720
+ ann = nil
161
721
  case where[0]
162
722
  when :type1
163
- where[1..-1].any? {|r| validate1(d, r)}
723
+ if where[1..-1].any? {|r| ann = validate1a(d, r)}
724
+ ann
725
+ end
164
726
  when :map
165
727
  if Hash === d
166
728
  d_check = d.dup
167
- where[1..-1].all? { |r|
168
- # puts "SUBRULE: #{r.inspect}"
169
- t, s, _e, k, v = r
170
- fail unless t == :member
171
- # this is mostly quadratic; let's do the linear thing if possible
172
- fail "member name not known for group entry #{r} in map" unless k
173
- simple, simpleval = extract_value(k)
174
- if simple
175
- # puts "SIMPLE: #{d_check.inspect} #{simpleval}"
176
- # add occurrence check; check that val is present in the first place
177
- actual = d.fetch(simpleval, :not_found)
178
- if actual == :not_found
179
- s == 0 # minimum occurrence must be 0 then
180
- else
181
- validate1(actual, v) && d_check.delete(simpleval)
182
- end
183
- else
184
- # puts "COMPLEX: #{k.inspect} #{simple.inspect} #{simpleval.inspect}"
185
- keys = d_check.keys
186
- ta, keys = keys.partition{ |key| validate1(key, k)}
187
- # XXX check ta.size against s/e
188
- ta.all? { |val|
189
- validate1(d[val], v) && d_check.delete(val)
190
- }
729
+ if (ann = map_check(d, d_check, where[1..-1])) && d_check == {}
730
+ ann
731
+ else
732
+ if ENV["CDDL_TRACE"]
733
+ puts "MAP RESIDUAL: #{d_check.inspect} for #{where[1..-1]} and #{d.inspect}"
191
734
  end
192
- } && d_check == {}
735
+ end
193
736
  end
194
737
  when :array
738
+ # warn ["valarr", d, where].inspect
195
739
  if Array === d
196
740
  # validate1 against the record
197
- i = 0
198
- where[1..-1].each { |r|
199
- t, s, e, _k, v = r # XXX
200
- fail unless t == :member
201
- occ = 0
202
- while ((e == -1 || occ < e) && i != d.size && validate1(d[i], v))
203
- i += 1
204
- occ += 1
741
+ idx, ann = validate_forward(d, 0, where)
742
+ ann if validate_result(idx == d.size) { "#{validate_diag.inspect} -- cannot complete (#{idx}, #{d.size}) array #{d} for #{where}" }
743
+ end
744
+ when :text, :int, :float, :bytes
745
+ _, v = extract_value(where)
746
+ [] if d == v
747
+ when :range
748
+ [] if where[2] === d && where[1].include?(d)
749
+ when :anno
750
+ target = where[2]
751
+ if ann = validate1a(d, target)
752
+ control = where[3]
753
+ case where[1]
754
+ when :size
755
+ case d
756
+ when Integer
757
+ ok, v, vt = extract_value(control)
758
+ if ok && vt == Integer
759
+ ann if (d >> (8*v)) == 0
760
+ end
761
+ when String
762
+ ann if validate1(d.bytesize, control)
205
763
  end
206
- if occ < s
207
- @last_message = "occur not reached in array #{d} for #{where}"
208
- return false
764
+ when :bits
765
+ if String === d
766
+ d.each_byte.with_index.all? { |b, i|
767
+ bit = i << 3
768
+ ann if 8.times.all? { |nb|
769
+ b[nb] == 0 || validate1(bit+nb, control)
770
+ }
771
+ }
772
+ elsif Integer === d
773
+ if d >= 0
774
+ ok = true
775
+ i = 0
776
+ while ok && d > 0
777
+ if d.odd?
778
+ ok &&= validate1(i, control)
779
+ end
780
+ d >>= 1; i += 1
781
+ end
782
+ ann if ok
783
+ end
209
784
  end
210
- }
211
- validate_result(i == d.size) { "cannot complete array #{d} for #{where}" }
785
+ when :default
786
+ # Hmm.
787
+ unless $default_warned
788
+ warn "*** Ignoring .default for now."
789
+ $default_warned = true
790
+ end
791
+ ann
792
+ when :lt, :le, :gt, :ge, :eq, :ne
793
+ op = OPERATORS[where[1]]
794
+ ok, v, _vt = extract_value(control)
795
+ if ok
796
+ ann if d.send(op, v) rescue nil # XXX Focus ArgumentError
797
+ end
798
+ when :regexp
799
+ ann if (
800
+ if String === d
801
+ ok, v, vt = extract_value(control)
802
+ if ok && vt == String
803
+ re = REGEXP_FOR_STRING[v]
804
+ # pp re
805
+ d.match(re)
806
+ end
807
+ end
808
+ )
809
+ when :cbor
810
+ ann if validate1((CBOR::decode(d) rescue :BAD_CBOR), control)
811
+ when :cborseq
812
+ ann if validate1((CBOR::decode("\x9f".b << d << "\xff".b) rescue :BAD_CBOR), control)
813
+ when :within
814
+ if validate1(d, control)
815
+ ann
816
+ else
817
+ warn "*** #{d.inspect} meets #{target} but not #{control}"
818
+ nil
819
+ end
820
+ when :and
821
+ ann if validate1(d, control)
822
+ else
823
+ fail "Don't know yet how to validate against #{where}"
824
+ end
212
825
  end
213
- when :string, :int, :float
214
- _, v = extract_value(where)
215
- d == v
216
826
  when :prim
827
+ # warn "validate prim WI #{where.inspect} #{d.inspect}"
217
828
  case where[1]
218
829
  when nil
219
830
  true
@@ -225,10 +836,12 @@ module CDDL
225
836
  String === d && d.encoding == Encoding::BINARY
226
837
  when 3
227
838
  String === d && d.encoding != Encoding::BINARY # cheat
839
+ when 6
840
+ CBOR::Tagged === d && d.tag == where[2] && validate1a(d.data, where[3])
228
841
  when 7
229
842
  t, v = extract_value(where)
230
843
  if t
231
- v == d
844
+ v.eql? d
232
845
  else
233
846
  case where[2]
234
847
  when nil
@@ -240,11 +853,23 @@ module CDDL
240
853
  fail
241
854
  end
242
855
  end
243
-
244
856
  else
245
- fail "Can't generate prim #{where[1]}"
857
+ fail "Can't validate prim #{where[1]} yet"
858
+ end
859
+ when :recurse
860
+ name = where[1]
861
+ rule = @stage1[name]
862
+ if @recursion < MAX_RECURSE
863
+ @recursion += 1
864
+ r = validate1a(d, rule)
865
+ @recursion -= 1
866
+ r
867
+ else
868
+ fail "Deep recursion into #{name}: #{rule}, not yet implemented"
246
869
  end
247
870
  else
871
+ @last_message = "Don't know how to validate #{where}"
872
+ false
248
873
  # fail where
249
874
  end
250
875
  end
@@ -255,7 +880,7 @@ module CDDL
255
880
  private
256
881
 
257
882
  def gen_word
258
- @words ||= File.read("/usr/share/dict/words").lines.shuffle
883
+ @words ||= (File.read("/usr/share/dict/words").lines.shuffle rescue %w{tic tac toe})
259
884
  @wordptr ||= 0
260
885
  @wordptr = 0 if @wordptr == @words.size
261
886
  w = @words[@wordptr].chomp
@@ -263,99 +888,229 @@ module CDDL
263
888
  w
264
889
  end
265
890
 
266
- # XXX needs serious memoizing
891
+ def rule_lookup(name, canbegroup)
892
+ if b = @bindings.last[name]
893
+ b
894
+ else
895
+ r = @rules[name]
896
+ unless r
897
+ if name[0] == "$"
898
+ r = @rules[name] = if name[1] == "$"
899
+ [:grpchoice]
900
+ else
901
+ [:type1]
902
+ end
903
+ end
904
+ end
905
+ if r
906
+ t = r_process(name, r)
907
+ unless t[0] == :type1
908
+ fail "#{name} not a type #{t}" unless canbegroup && (t[0] == :grpent || t[0] == :grpchoice)
909
+ end
910
+ t
911
+ end
912
+ end
913
+ end
914
+
915
+ RECURSE_TYPE = {grpent: :recurse_grpent, grpchoice: :recurse_grpent,
916
+ type1: :recurse} # MMM
917
+ RECURSE_TYPE.default_proc = proc do |a|
918
+ fail a.inspect
919
+ end
267
920
 
268
- def r_process(n, r)
921
+ def r_process(n, r, bindings = {})
269
922
  t = r[0]
270
923
  # puts "Processing rule #{n} = #{t}"
271
- @stage1[n] ||= [t, *r[1..-1].map {|e| send(t, e)}]
924
+ @stage1[n] ||= begin
925
+ @stage1[n] = [t, [RECURSE_TYPE[t], n]]
926
+ @bindings.push(bindings)
927
+ r = [t, *r[1..-1].map {|e|
928
+ case t
929
+ when :grpchoice
930
+ fail e[0].inspect unless e[0] == :grpent
931
+ fail e unless e.size == 2
932
+ res = grpent(e[1])
933
+ # warn "RES #{res.inspect}"
934
+ res
935
+ when :grpent
936
+ grpent(e)
937
+ when :type1
938
+ type1(e, r.size == 2) # a single type1 could be a group
939
+ else
940
+ fail t
941
+ end}]
942
+ @bindings.pop
943
+ r.cbor_annotation_add(n) rescue nil # AAA
944
+ r
945
+ end
272
946
  end
273
947
 
274
948
  def value(n)
275
949
  # cheat:
276
- val = eval(n.to_s)
277
- [case val
278
- when Integer; :int
279
- when Numeric; :float
280
- when String; :string
281
- else fail "huh? value #{val.inspect}"
282
- end, val]
950
+ # warn n
951
+ s = n.to_s
952
+ if s[-1] == "'"
953
+ parts = s.split("'", 3)
954
+ if parts[2] != "" || parts[1] =~ /\\/
955
+ warn "*** Can't handle backslashes in #{s.inspect} yet"
956
+ end
957
+ [:bytes,
958
+ case parts[0]
959
+ when ""
960
+ parts[1].b
961
+ when "h"
962
+ parts[1].gsub(/\s/, "").chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join
963
+ when "b64"
964
+ Base64.urlsafe_decode64(parts[1])
965
+ else
966
+ warn "*** Can't handle byte string type #{parts[0].inspect} yet"
967
+ end
968
+ ]
969
+ else
970
+ val = eval(n.to_s)
971
+ # warn val
972
+ case val
973
+ when Integer; [:int, val]
974
+ when Numeric; [:float, val]
975
+ when String; [:text, val.force_encoding(Encoding::UTF_8)]
976
+ else fail "huh? value #{val.inspect}"
977
+ end
978
+ end
979
+ end
980
+
981
+ def workaround1(n)
982
+ n.children.each do |ch|
983
+ if ch.to_s == ")"
984
+ # warn "W1 #{ch.inspect} -> #{ch.group.inspect}"
985
+ return ch.group
986
+ end
987
+ end
988
+ nil
283
989
  end
284
990
 
285
991
  def grpent(n) # returns array of entries
286
992
  occ = occur(n.occur)
287
- if mk = n.memberkey # work around unclear bug in ast generation
288
- [[:member, *occ, label(mk.membername), type1(n.type1)]]
289
- else
290
- # fail n.inspect unless n.type1
291
- t = if n.membername # work around unclear bug, part 2
292
- type1(n.membername.type1, true)
993
+ if g = n.group || workaround1(n)
994
+ gr = group(g)
995
+ if occ != [1, 1]
996
+ gr = [[:member, *occ, nil, [:grpent, *gr]]]
997
+ end
998
+ return gr
999
+ end
1000
+ nt = n.type || (n.s && n.s.type) || (n.bareword && n.bareword.s.type) # workarounds
1001
+ unless nt
1002
+ warn "NO NT"
1003
+ warn @abnf.ast?
1004
+ fail ["ntype", n, n.children].inspect
1005
+ end
1006
+ if mk = n.memberkey # work around unclear bug in ast generation below
1007
+ if (t1 = mk.type1 || t1 = mk.value ||
1008
+ ((t1 = mk.s) && (t1 = t1.type) && (t1 = t1.type1))) # workaround
1009
+ [[:member, *occ, type1(t1), type(nt)]]
293
1010
  else
294
- type1(n.type1, true) # type1 can be a group here!
1011
+ bw = mk.bareword
1012
+ unless bw
1013
+ warn @abnf.ast?
1014
+ fail [n, n.children, mk, mk.children].inspect
1015
+ end
1016
+ name = bw.to_s
1017
+ [[:member, *occ, [:text, name.force_encoding(Encoding::UTF_8)], type(nt)]]
295
1018
  end
296
- if t[0] == :grpent
297
- # XXX go through the members here and multiply the occs
298
- t1 = t[1..-1].flatten(1)
299
- t1.flat_map {|t2|
300
- if t2[0] == :member
301
- [t2]
1019
+ else
1020
+ t = if nbw = nt.bareword
1021
+ t1 = nbw.type1 # || n.bareword.s.type.type1 # workaround
1022
+ rest = type_collect(nt, true)
1023
+ s = [:type1, type1(t1, true), *rest]
1024
+ # warn "T2: #{s.size} #{s}" -- maybe this should have a parenthesis warning?
1025
+ s.size == 2 ? s[1] : s # decapsulate single-element choice
302
1026
  else
303
- fail unless t2[0] == :grpent
304
- t2[1..-1]
1027
+ type(nt, true) # type can actually be a group here!
305
1028
  end
306
- }.map {|mem|
307
- # p mem
308
- mem[1] *= occ[0]
309
- mem[2] *= [occ[1], -1].max
310
- # p mem
311
- mem
312
- }
1029
+ pp ["t is", t] if ENV["CDDL_TRACE"]
1030
+ if t[0] == :grpent && (occ == [1, 1])
1031
+ # go through the members here and multiply the occs
1032
+ t1 = t[1..-1].flatten(1)
1033
+ if t1[0] == :recurse_grpent
1034
+ [t1]
1035
+ elsif Array === t1[0] && t1[0][0] == :grpchoice
1036
+ fail t1.inspect unless t1.size == 1
1037
+ t1
1038
+ else
1039
+ t1.flat_map {|t2|
1040
+ if t2[0] == :member
1041
+ [t2]
1042
+ else
1043
+ fail t1.inspect unless t2[0] == :grpent # XXX Where is the nested multiplication here?
1044
+ t2[1..-1]
1045
+ end
1046
+ }.map {|mem|
1047
+ mem[1] *= occ[0]
1048
+ mem[2] *= occ[1] # Does all the infinity magic
1049
+ # p mem
1050
+ mem
1051
+ }
1052
+ end
313
1053
  else
314
- [[:member, *occ, nil, t]]
1054
+ if t[0] == :grpchoice && occ == [1, 1]
1055
+ [t]
1056
+ else
1057
+ if t[0] == :grpent
1058
+ t = [t[0], *t[1..-1].flatten(1)]
1059
+ end
1060
+ if occ[0] == 0 && t == [:grpchoice]
1061
+ [] # we won't be able to generate any of those
1062
+ else
1063
+ if t[0] == :grpchoice # FIXME: need to package grpchoice into grpent in a member
1064
+ t = [:grpent, t]
1065
+ end
1066
+ [[:member, *occ, nil, t]]
1067
+ end
1068
+ end
315
1069
  end
316
1070
  end
317
1071
  end
318
1072
 
319
- def label(n)
320
- # if this is not a rulename, it's a bareword
321
- name = n.to_s
322
- if r = @rules[name]
323
- r_process(name, r)
324
- else
325
- [:string, name]
326
- end
1073
+ def g_process(name, g, genericargs)
1074
+ r, ids = g
1075
+ args = genericargs.children(:type1).map {|x| type1(x, true)}
1076
+ fail "number of args #{name} #{ids.size} #{args.size}" if ids.size != args.size
1077
+ bindings = Hash[ids.zip(args)]
1078
+ r_process("#{name}<#{genericargs._strip}>", r, bindings)
327
1079
  end
328
1080
 
329
- def type_recall(name, canbegroup)
330
- if r = @rules[name]
331
- t = r_process(name, r)
332
- unless t[0] == :type1
333
- fail "#{name} not a type" unless canbegroup && t[0] == :grpent
334
- end
1081
+ def type_recall(name, canbegroup, genericargs)
1082
+ # p genericargs, "GENERIC"
1083
+ if genericargs && (generic = @generics[name])
1084
+ t = g_process(name, generic, genericargs)
1085
+ t
1086
+ elsif !genericargs && (t = rule_lookup(name, canbegroup))
335
1087
  t
336
1088
  else
337
- fail "Unknown type #{name}"
1089
+ fail "Unknown type #{name} #{genericargs.inspect} #{@bindings}" #{@abnf.ast?}"
338
1090
  end
339
1091
  end
340
1092
 
341
- def group_recall(name)
342
- if r = @rules[name]
343
- g = r_process(name, r)
1093
+ def group_recall(name, genericargs)
1094
+ if genericargs && (generic = @generics[name])
1095
+ g = g_process(name, generic, genericargs)
344
1096
  fail "#{name} not a group" unless g[0] == :grpent
345
1097
  g[1..-1]
1098
+ elsif !genericargs && (g = rule_lookup(name, true))
1099
+ fail "#{name} not a group" unless g[0] == :grpent
1100
+ g[1..-1] # AAA
346
1101
  else
347
1102
  fail "Unknown group #{name}"
348
1103
  end
349
1104
  end
350
1105
 
351
- BRACE = {"{" => :map, "[" => :array}
352
-
353
- def type1(n, canbegroup = false)
354
- # puts "NVALUE #{n.value.inspect}"
355
- if v = n.value
1106
+ def type2(n, canbegroup = false)
1107
+ if v = n.type
1108
+ type(n.type)
1109
+ elsif v = n.value
356
1110
  value(n)
357
1111
  elsif v = n.typename
358
- t = type_recall(v.to_s, canbegroup)
1112
+ ga = n.genericarg || (n.s && n.s.genericarg) # workaround
1113
+ t = type_recall(v.to_s, canbegroup, ga)
359
1114
  if t[0] == :type1
360
1115
  if t.size == 2
361
1116
  t = t[1]
@@ -370,7 +1125,58 @@ module CDDL
370
1125
 
371
1126
  end
372
1127
  end # XXX should flatten the thing, too
1128
+ t = t.dup
1129
+ t.cbor_annotation_replace(v.to_s) rescue nil # AAA
373
1130
  t
1131
+ else
1132
+ fail [n, n.children].inspect
1133
+ end
1134
+ end
1135
+
1136
+ def memberkey_check(s)
1137
+ s.each do |member|
1138
+ case member[0]
1139
+ when :grpchoice
1140
+ member[1..-1].each do |m|
1141
+ memberkey_check(m)
1142
+ end
1143
+ when :recurse_grpent # XXX we don't have the entry yet
1144
+ when :member
1145
+ fail "map entry without member key given: #{member}" unless member[3] || member[4][0] == :grpent || member[4][0] == :grpchoice
1146
+ else
1147
+ fail ["type1 member", member].inspect unless member[0] == :member
1148
+ end
1149
+ end
1150
+ end
1151
+
1152
+ BRACE = {"{" => :map, "[" => :array}
1153
+ RANGE_EXCLUDE_END = {".." => false, "..." => true}
1154
+ SUPPORTED_ANNOTATIONS = [:bits, :size, :regexp, :cbor, :cborseq, :within, :and,
1155
+ :default, :lt, :le, :gt, :ge, :eq, :ne]
1156
+
1157
+ def type1(n, canbegroup = false)
1158
+ # puts "NVALUE #{n.value.inspect}"
1159
+ if v = n.type2
1160
+ ch = n.children(:type2)
1161
+ if ro = n.rangeop
1162
+ cats = []
1163
+ [:range, Range.new(*ch.map {|l|
1164
+ ok, val, cat = extract_value(type2(l))
1165
+ fail "Can't have range with #{l}" unless ok
1166
+ # XXX really should be checking type coherence of range
1167
+ cats << cat
1168
+ val
1169
+ }, RANGE_EXCLUDE_END[ro.to_s]),
1170
+ cats[0] == cats[1] ? cats[0] : fail("Incompatible range #{cats}")]
1171
+ elsif anno = n.annotator
1172
+ annotyp = anno.id.to_s.intern
1173
+ unless SUPPORTED_ANNOTATIONS.include?(annotyp)
1174
+ fail "Unsupported annotation .#{annotyp}"
1175
+ end
1176
+ [:anno, annotyp, *ch.map {|t| type2(t)}]
1177
+ else
1178
+ type2(v, canbegroup)
1179
+ end
374
1180
  else
375
1181
  case str = n.to_s
376
1182
  when "#"
@@ -382,26 +1188,45 @@ module CDDL
382
1188
  s << type(tagged_type)
383
1189
  end
384
1190
  s
385
- when /\A\(/
386
- type(n.type)
387
1191
  when /\A[\[{]/
388
- s = n.children(:group).flat_map {|ch| group(ch)}
389
1192
  type = BRACE[str[0]]
1193
+ @insides << type
1194
+ s = n.children(:group).flat_map {|g| group(g)}
1195
+ @insides.pop
390
1196
  if type == :map
391
- s.each do |member|
392
- fail unless member[0] == :member
393
- fail "map entry without member key given: #{member}" unless member[3]
394
- end
1197
+ memberkey_check(s)
1198
+ # XXX could do the occurrence multiplier here
395
1199
  end
1200
+ # warn s.inspect
396
1201
  [type, *s]
1202
+ when /\A&/
1203
+ if gn = n.groupname
1204
+ s = group_recall(gn.to_s, n.genericarg).flatten(1)
1205
+ else
1206
+ @insides << :enum
1207
+ s = n.children(:group).flat_map {|g| group(g)}
1208
+ @insides.pop
1209
+ end
1210
+ [:type1, *s.map {|mem|
1211
+ t, _s, _e, _k, v = mem
1212
+ fail "enum #{t.inspect}" unless t == :member
1213
+ v.cbor_annotation_add(generate1(_k)) rescue nil # AAA (XXX: what if more than one?)
1214
+ v
1215
+ }
1216
+ ]
397
1217
  else
398
1218
  "unimplemented #{n}"
399
1219
  end
400
1220
  end
401
1221
  end
402
1222
 
403
- def type(n)
404
- s = n.children(:type1).map {|ch| type1(ch)}
1223
+ def type_collect(n, canbegroup)
1224
+ n.children(:type1).map {|ch| type1(ch, canbegroup)}
1225
+ end
1226
+
1227
+ def type(n, canbegroup = false)
1228
+ # pp ["nch", n.children]
1229
+ s = type_collect(n, canbegroup)
405
1230
  if s.size == 1
406
1231
  s.first
407
1232
  else
@@ -410,7 +1235,20 @@ module CDDL
410
1235
  end
411
1236
 
412
1237
  def group(n) # returns array
413
- n.children(:grpent).flat_map {|ch| grpent(ch)}
1238
+ choices = n.children(:grpchoice)
1239
+ if choices == [] # "cannot happen", workaround
1240
+ choices = n.s.children(:grpchoice)
1241
+ # warn "W2: #{choices.inspect}"
1242
+ end
1243
+ choices = choices.map {|choice|
1244
+ choice.children(:grpent).flat_map {|ch| grpent(ch)}
1245
+ }
1246
+ case choices.size
1247
+ when 1
1248
+ choices.first
1249
+ else
1250
+ [[:grpchoice, *choices]]
1251
+ end
414
1252
  end
415
1253
 
416
1254
  def occur(n)
@@ -418,7 +1256,7 @@ module CDDL
418
1256
  when ""
419
1257
  [1, 1]
420
1258
  when "+"
421
- [1, -1]
1259
+ [1, MANY]
422
1260
  when "?"
423
1261
  [0, 1]
424
1262
  when /\A(\d*)\*(\d*)\z/
@@ -428,7 +1266,7 @@ module CDDL
428
1266
  s = s.to_i
429
1267
  end
430
1268
  if (e = $2) == ""
431
- e = -1
1269
+ e = MANY
432
1270
  else
433
1271
  e = e.to_i
434
1272
  end