cddl 0.8.8 → 0.12.14

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cddl +51 -15
  3. data/cddl.gemspec +11 -7
  4. data/data/cddl.abnf +30 -13
  5. data/lib/cbor-pp-play.rb +1 -0
  6. data/lib/cddl.rb +726 -74
  7. data/test/test-cddl.rb +53 -2
  8. data/test-data/7807.cddl +14 -0
  9. data/test-data/abignum.cddl +1 -0
  10. data/test-data/abnf1.cddl +3 -0
  11. data/test-data/abnf2.cddl +37 -0
  12. data/test-data/abnf3.cddl +32 -0
  13. data/test-data/b64c-sloppy.cddl +1 -0
  14. data/test-data/b64c.cddl +1 -0
  15. data/test-data/b64u-strict.cddl +12 -0
  16. data/test-data/b64u.cddl +1 -0
  17. data/test-data/base32.cddl +1 -0
  18. data/test-data/base32hex.cddl +1 -0
  19. data/test-data/bat.cddl +16 -0
  20. data/test-data/bpv7.cddl +136 -0
  21. data/test-data/bpv7a.cddl +181 -0
  22. data/test-data/bpv7b.cddl +167 -0
  23. data/test-data/cat-re.cddl +4 -0
  24. data/test-data/catb64.cddl +8 -0
  25. data/test-data/complex-occ.cddl +11 -0
  26. data/test-data/coral.cddl +10 -0
  27. data/test-data/coral1.cddl +5 -0
  28. data/test-data/coral2.cddl +7 -0
  29. data/test-data/coral3.cddl +7 -0
  30. data/test-data/decimal-warn.cddl +1 -0
  31. data/test-data/decimal.cddl +1 -0
  32. data/test-data/decimal2.cddl +1 -0
  33. data/test-data/det1.cddl +12 -0
  34. data/test-data/dotplus1.cddl +3 -0
  35. data/test-data/dotplus2.cddl +3 -0
  36. data/test-data/dotplus3.cddl +3 -0
  37. data/test-data/dotplus4.cddl +3 -0
  38. data/test-data/enum.cddl +14 -0
  39. data/test-data/enum1.cddl +12 -0
  40. data/test-data/feat1.cddl +4 -0
  41. data/test-data/feature-controller.cddl +6 -0
  42. data/test-data/feature-detail.cddl +5 -0
  43. data/test-data/foo.cddl +1 -0
  44. data/test-data/hex.cddl +1 -0
  45. data/test-data/hexlc.cddl +1 -0
  46. data/test-data/hexuc.cddl +1 -0
  47. data/test-data/homenet-de.cddl +12 -0
  48. data/test-data/homenet-fe.cddl +12 -0
  49. data/test-data/jim-cut-2.cddl +11 -0
  50. data/test-data/jim-cut.cddl +12 -0
  51. data/test-data/join.cddl +1 -0
  52. data/test-data/joini.cddl +1 -0
  53. data/test-data/joinx.cddl +1 -0
  54. data/test-data/json.cddl +1 -0
  55. data/test-data/json1.cddl +15 -0
  56. data/test-data/json2.cddl +1 -0
  57. data/test-data/jsoniodef.cddl +719 -0
  58. data/test-data/jwt.cddl +15 -0
  59. data/test-data/lint1.cddl +9 -0
  60. data/test-data/mdl-ble.cddl +9 -0
  61. data/test-data/mdl-deve.cddl +54 -0
  62. data/test-data/mdl-dp.cddl +11 -0
  63. data/test-data/mdl-mso.cddl +20 -0
  64. data/test-data/mdl-request.cddl +26 -0
  65. data/test-data/mdl-response.cddl +66 -0
  66. data/test-data/mdl.cddl +11 -0
  67. data/test-data/mon-val.cddl +1 -0
  68. data/test-data/multipart-ct.cddl +2 -0
  69. data/test-data/named-group.cddl +7 -0
  70. data/test-data/non-empty.cddl +5 -0
  71. data/test-data/oid.cddl +8 -0
  72. data/test-data/oidbat.cddl +8 -0
  73. data/test-data/plus.cddl +3 -0
  74. data/test-data/prim.cddl +7 -0
  75. data/test-data/printf.cddl +2 -0
  76. data/test-data/printf0.cddl +1 -0
  77. data/test-data/printf1.cddl +2 -0
  78. data/test-data/printf2.cddl +2 -0
  79. data/test-data/printf3.cddl +2 -0
  80. data/test-data/sasl.cddl +24 -0
  81. data/test-data/sequence.cddl +1 -0
  82. data/test-data/sipos.cddl +8 -0
  83. data/test-data/star-edited.cddl +98 -0
  84. data/test-data/star.cddl +95 -0
  85. data/test-data/test-gen.cddl +3 -0
  86. data/test-data/time.cddl +11 -0
  87. data/test-data/valemb.cddl +16 -0
  88. data/test-data/yangid.cddl +1 -0
  89. data/test-data/yaron1.cddl +95 -0
  90. metadata +148 -14
data/lib/cddl.rb CHANGED
@@ -2,9 +2,15 @@ require 'abnc'
2
2
  require 'pp'
3
3
  require 'pathname'
4
4
  require 'cbor-pure' unless defined?(CBOR::Tagged)
5
+ require 'half'
6
+ require 'cbor-deterministic'
5
7
  require 'regexp-examples'
8
+ require 'abnftt'
6
9
  require 'colorize'
7
10
  require 'base64'
11
+ require 'base32'
12
+ require 'base45_lite'
13
+ require 'scanf'
8
14
 
9
15
  module CDDL
10
16
 
@@ -21,6 +27,21 @@ module CDDL
21
27
 
22
28
  class Parser
23
29
 
30
+ FEATURE_REJECT_RE = /\A\^/
31
+ # CDDL_FEATURE_OK=cbor,^json
32
+ CDDL_FEATURE_OK, CDDL_FEATURE_REJECT =
33
+ if ok = ENV["CDDL_FEATURE_OK"]
34
+ ok.split(/,\s*/)
35
+ .partition{|s| s[0] !~ FEATURE_REJECT_RE}
36
+ .map {|l| Hash[l.map {|feature|
37
+ [feature.sub(FEATURE_REJECT_RE, ''),
38
+ true]}]}
39
+ else
40
+ [{}, {}]
41
+ end
42
+
43
+ CDDL_UNUSED_OK = ENV["CDDL_UNUSED_OK"]
44
+
24
45
  class BackTrack < Exception; end
25
46
 
26
47
  def initialize(source_text)
@@ -41,7 +62,7 @@ module CDDL
41
62
  if upto - presult < 100
42
63
  part2 = source_text[presult...upto]
43
64
  else
44
- part2 = source_text[presult, 50] + "......." + source_text[upto-50, 50]
65
+ part2 = source_text[presult, 50] + "......." + (source_text[upto-50, 50] || "")
45
66
  end
46
67
  warn "*** Look for syntax problems around the #{
47
68
  "%%%".colorize(background: :light_yellow)} markers:\n#{
@@ -55,6 +76,8 @@ module CDDL
55
76
  @ast = @abnf.ast?
56
77
  # our little argument stack for rule processing
57
78
  @insides = []
79
+ # collect error information
80
+ @last_message = ""
58
81
  end
59
82
 
60
83
  def apr # for debugging
@@ -96,6 +119,16 @@ module CDDL
96
119
  r.compact
97
120
  end
98
121
 
122
+ def longest_initial_match(old, now)
123
+ md = /\A(.*)(.*)\n\1(.*)\z/.match([old,now].join("\n"))
124
+ md[1..3]
125
+ end
126
+
127
+ def diff_message(old, now)
128
+ c, l, r = longest_initial_match(old, now)
129
+ "-- common: >#{c}< -- old: >#{l}< -- new: >#{r}<"
130
+ end
131
+
99
132
  def cname(s)
100
133
  s.to_s.gsub(/-/, "_")
101
134
  end
@@ -127,7 +160,7 @@ module CDDL
127
160
  if subtree[0] == :type1 && subtree[1..-1].all? {|x| x[0] == :int}
128
161
  if enumname = subtree.cbor_annotations rescue nil
129
162
  enumname = cname(enumname.first)
130
- subtree[1..-1].each do |x|
163
+ subtree[1..-1].each do |x|
131
164
  if memname = x.cbor_annotations
132
165
  memname = "#{enumname}_#{cname(memname.first)}"
133
166
  add.(memname, x[1].to_s)
@@ -182,7 +215,8 @@ module CDDL
182
215
  ids = g.children(:id).map(&:to_s)
183
216
  # puts ["ids", ids].inspect
184
217
  if b = @generics[n]
185
- fail "Duplicate generics definition #{n} as #{rule_ast} (was #{b})"
218
+ fail "Duplicate generics definition #{n} #{
219
+ diff_message(strip_nodes(b[0]).inspect, strip_nodes(rule_ast).inspect)}"
186
220
  end
187
221
  @generics[n] = [rule_ast, ids]
188
222
  else
@@ -194,7 +228,7 @@ module CDDL
194
228
  if a == b
195
229
  warn "*** Warning: Identical redefinition of #{n} as #{a}"
196
230
  else
197
- fail "Duplicate rule definition #{n} as #{b} (was #{a})"
231
+ fail "Duplicate rule definition #{n} #{diff_message(b, a)}"
198
232
  end
199
233
  end
200
234
  @rules[n] = rule_ast
@@ -226,7 +260,7 @@ module CDDL
226
260
  r_process("used_in_cddl_prelude", @rules["used_in_cddl_prelude"])
227
261
  @rules.each do |n, r|
228
262
  # r_process(n, r) # debug only loop
229
- warn "*** Unused rule #{n}" unless @stage1[n]
263
+ warn "*** Unused rule #{n}" unless @stage1[n] || CDDL_UNUSED_OK
230
264
  end
231
265
  if result[0] == :grpent
232
266
  warn "Group at top -- first rule must be a type!"
@@ -243,12 +277,49 @@ module CDDL
243
277
  [rule[0], *rule[1]]
244
278
  end
245
279
 
280
+ def remove_indentation(s)
281
+ l = s.lines
282
+ indent = l.grep(/\S/).map {|l| l[/^\s*/].size}.min
283
+ l.map {|l| l.sub(/^ {0,#{indent}}/, "")}.join
284
+ end
285
+
286
+ def unpack_array_to_sequence(content, where)
287
+ # remove the first head
288
+ n = case content.getbyte(0) - (4 << 5)
289
+ when 0..23; 1
290
+ when 24; 2
291
+ when 25; 3
292
+ when 26; 5
293
+ when 27; 9 # unlikely :-)
294
+ else fail ".cborseq sequence for #{where} not an array"
295
+ end
296
+ content[0...n] = ''
297
+ content
298
+ end
299
+
300
+
246
301
  # Memoize a bit here
247
302
 
248
303
  REGEXP_FOR_STRING = Hash.new {|h, k|
249
- h[k] = Regexp.new("\\A#{k}\\z")
304
+ h[k] = Regexp.new("\\A(?:#{k})\\z")
305
+ }
306
+
307
+ ABNF_PARSER_FOR_STRING = Hash.new {|h, k|
308
+ grammar = "cddl-t0p--1eve1-f0r--abnf = " << k # XXX
309
+ h[k] = ABNF.from_abnf(grammar)
310
+ }
311
+
312
+ ABNF_ENCODING_FOR_CONOP = {
313
+ abnf: Encoding::UTF_8,
314
+ abnfb: Encoding::BINARY
250
315
  }
251
316
 
317
+ def myfrand(exp)
318
+ exprange = 1 << exp
319
+ expoffset= exprange >> 1
320
+ (rand(2)*2-1) * rand() * 2.0**(rand(exprange)-expoffset)
321
+ end
322
+
252
323
  def generate
253
324
  @recursion = 0
254
325
  generate1(rules)
@@ -329,8 +400,12 @@ module CDDL
329
400
  unless kr
330
401
  case vr[0]
331
402
  when :grpent
332
- fail "grpent in map #{vr.inspect}" unless vr.size == 2
333
- g = Array.new(st) { generate1(vr[1], true) }.flatten(1)
403
+ # fail "grpent in map #{vr.inspect}" unless vr.size == 2
404
+ g = Array.new(st) {
405
+ vr[1..-1].map{ |vrx|
406
+ generate1(vrx, true)
407
+ }.flatten(1)
408
+ }.flatten(1)
334
409
  # warn "GGG #{g.inspect}"
335
410
  return g
336
411
  when :grpchoice
@@ -367,9 +442,17 @@ module CDDL
367
442
  when 3
368
443
  gen_word
369
444
  when 6
370
- CBOR::Tagged.new(where[2], generate1(where[3]))
445
+ tn = Integer === where[2] ? where[2] : generate1(where[2])
446
+ unless Integer === tn && tn >= 0
447
+ fail "Can't generate a tag number out of #{where[2]}"
448
+ end
449
+ CBOR::Tagged.new(tn, generate1(where[3]))
371
450
  when 7
372
- case where[2]
451
+ w2 = where[2]
452
+ if Array === w2
453
+ w2 = generate1(w2)
454
+ end
455
+ case w2
373
456
  when nil
374
457
  Math::PI
375
458
  when 20
@@ -378,10 +461,22 @@ module CDDL
378
461
  true
379
462
  when 22
380
463
  nil
381
- when 23
382
- :undefined
383
- when 25, 26, 27
384
- rand()
464
+ when 0..19, 23, 32..255
465
+ CBOR::Simple.new(w2)
466
+ when 25
467
+ while !(hs = Half.encode_from_single_bytes([myfrand(5)].pack("g")))
468
+ end
469
+ Half.decode(hs)
470
+ when 26
471
+ while (a = [myfrand(8)].pack("g").unpack("g").first).to_cbor.size != 5
472
+ end
473
+ a
474
+ when 27
475
+ while (a = myfrand(11)).to_cbor.size != 9
476
+ end
477
+ a
478
+ else
479
+ fail "Can't generate prim #7.#{where[2].inspect}" # XXX retry array generate
385
480
  end
386
481
  else
387
482
  fail "Can't generate prim #{where[1]}"
@@ -389,7 +484,7 @@ module CDDL
389
484
  when :anno
390
485
  target = where[2]
391
486
  control = where[3]
392
- case where[1]
487
+ case conop = where[1]
393
488
  when :size
394
489
  should_be_int = generate1(control)
395
490
  unless (Array === target && target[0] == :prim && [0, 2, 3].include?(target[1])) && Integer === should_be_int && should_be_int >= 0
@@ -435,6 +530,40 @@ module CDDL
435
530
  $default_warned = true
436
531
  end
437
532
  generate1(target, inmap)
533
+ when :feature
534
+ feature, = extract_feature(control, nil)
535
+ # warn "@@@ GENFEAT #{feature.inspect} #{CDDL_FEATURE_REJECT[feature].inspect}"
536
+ if CDDL_FEATURE_REJECT[feature]
537
+ fail BackTrack.new("Feature '#{feature}' rejected")
538
+ end
539
+ generate1(target, inmap)
540
+ when :cat, :det
541
+ lhs = generate1(target, inmap)
542
+ rhs = generate1(control)
543
+ if conop == :det
544
+ lhs = remove_indentation(lhs)
545
+ rhs = remove_indentation(rhs)
546
+ end
547
+ begin
548
+ lhs + rhs
549
+ rescue Exception => e
550
+ fail "Can't #{lhs.inspect} .cat #{rhs.inspect}: #{e.message}"
551
+ end
552
+ when :plus
553
+ lhs = generate1(target, inmap)
554
+ rhs = generate1(control)
555
+ begin
556
+ case lhs
557
+ when Integer
558
+ lhs + Integer(rhs)
559
+ when Numeric
560
+ lhs + rhs
561
+ else
562
+ fail "#{lhs.inspect}: Not a number"
563
+ end
564
+ rescue Exception => e
565
+ fail "Can't #{lhs.inspect} .plus #{rhs.inspect}: #{e.message}"
566
+ end
438
567
  when :eq
439
568
  content = generate1(control)
440
569
  if validate1(content, where)
@@ -448,7 +577,7 @@ module CDDL
448
577
  content = Integer(content)
449
578
  case target[1]
450
579
  when 0
451
- case where[1]
580
+ case conop
452
581
  when :lt
453
582
  rand(0...content)
454
583
  when :le
@@ -462,8 +591,7 @@ module CDDL
462
591
  warn "HUH gen #{where.inspect} #{try.inspect}" unless try.nil?
463
592
  end
464
593
  end
465
- 32.times do
466
- content = generate1(target)
594
+ try_generate(target) do |content|
467
595
  if validate1(content, where)
468
596
  return content
469
597
  end
@@ -475,30 +603,117 @@ module CDDL
475
603
  fail "Don't know yet how to generate #{where}"
476
604
  end
477
605
  REGEXP_FOR_STRING[regexp].random_example(max_repeater_variance: 5)
478
- when :cbor, :cborseq
606
+ when :abnf, :abnfb
607
+ grammar = generate1(control)
608
+ bytes = true if target == [:prim, 2]
609
+ bytes = false if target == [:prim, 3]
610
+ unless !bytes.nil? && String === grammar
611
+ fail "Don't know yet how to generate #{where}"
612
+ end
613
+ grammar << "\n" unless grammar =~ /\n\z/
614
+ out = ABNF_PARSER_FOR_STRING[grammar].generate
615
+ if conop == :abnfb
616
+ out = out.codepoints.pack("C*")
617
+ end
618
+ enc = bytes ? Encoding::BINARY : Encoding::UTF_8
619
+ out.force_encoding(enc)
620
+ when :cbor, :cborseq, :cbordet, :cborseqdet
479
621
  unless target == [:prim, 2]
480
622
  fail "Don't know yet how to generate #{where}"
481
623
  end
482
- content = CBOR::encode(generate1(control))
483
- if where[1] == :cborseq
624
+ input = generate1(control)
625
+ if conop == :cbordet || conop == :cborseqdet
626
+ input = input.cbor_prepare_deterministic
627
+ end
628
+ content = CBOR::encode(input)
629
+ if conop == :cborseq || conop == :cborseqdet
484
630
  # 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] = ''
631
+ content = unpack_array_to_sequence(content, where)
494
632
  end
495
633
  content
634
+ when :json
635
+ unless target == [:prim, 3]
636
+ fail "Don't know yet how to generate #{where}"
637
+ end
638
+ content = JSON::dump(generate1(control))
639
+ content
640
+ when :decimal, :base10
641
+ if conop == :decimal
642
+ unless @decimal_warned
643
+ warn "** .decimal control operator is now called .base10"
644
+ @decimal_warned = true
645
+ end
646
+ end
647
+ unless target == [:prim, 3]
648
+ fail "Don't know yet how to generate #{where}"
649
+ end
650
+ content = Integer(generate1(control)).to_s
651
+ content
652
+ when :join
653
+ try_generate(control) do |content|
654
+ if Array === content &&
655
+ content.all? {|x| String === x} &&
656
+ Set[content.map {|x| x.encoding}].size == 1
657
+ content = content.join
658
+ if validate1(content, target)
659
+ return content
660
+ end
661
+ end
662
+ end
663
+ fail "Don't know yet how to generate #{where}"
664
+ when :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy",
665
+ :b45, :b32, :h32, :hex, :hexlc, :hexuc
666
+ try_generate(control) do |content|
667
+ if String === content
668
+ content = case conop
669
+ when :b64u, :"b64u-sloppy"
670
+ Base64.urlsafe_encode64(content, padding: false)
671
+ when :b64c, :"b64c-sloppy"
672
+ Base64.strict_encode64(content)
673
+ when :b45
674
+ Base45Lite.encode(content)
675
+ when :b32
676
+ Base32.table = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
677
+ Base32.encode(content).gsub("=", "")
678
+ when :h32
679
+ Base32.table = "0123456789ABCDEFGHIJKLMNOPQRSTUV"
680
+ Base32.encode(content).gsub("=", "")
681
+ when :hex, :hexuc
682
+ content.unpack("H*")[0].upcase
683
+ when :hexlc
684
+ content.unpack("H*")[0]
685
+ else fail "Cannot happen"
686
+ end
687
+ if validate1(content, target)
688
+ return content
689
+ end
690
+ end
691
+ end
692
+ fail "Not smart enough to generate #{where}"
693
+ when :printf
694
+ try_generate(control) do |content|
695
+ if Array === content && content.size >= 1
696
+ fmt, *data = content
697
+ if String === fmt
698
+ begin
699
+ content = fmt % data
700
+ if validate1(content, target)
701
+ return content
702
+ end
703
+ rescue ArgumentError => e
704
+ # be verbose about mismatches here
705
+ @last_message << "\n** #{fmt.inspect} ArgumentError #{e}"
706
+ end
707
+ end
708
+ end
709
+ end
710
+ fail "Not smart enough to generate #{where}#{@last_message}"
496
711
  when :within, :and
497
- 32.times do
498
- content = generate1(target)
712
+ try_generate(target) do |content|
713
+ $features ||= Hash.new {|h, k| h[k] = {}}
499
714
  if validate1(content, control)
500
715
  return content
501
- elsif where[1] == :within
716
+ elsif conop == :within
502
717
  warn "*** #{content.inspect} meets #{target.inspect} but not #{control.inspect}"
503
718
  end
504
719
  end
@@ -523,22 +738,102 @@ module CDDL
523
738
  end
524
739
  end
525
740
 
526
- VALUE_TYPE = {text: String, int: Integer, float: Float}
741
+ def try_generate(target)
742
+ 32.times do
743
+ content = generate1(target)
744
+ yield content # should return if success
745
+ end
746
+ end
747
+
748
+ VALUE_TYPE = {text: String, bytes: String, int: Integer, float: Float}
527
749
  SIMPLE_VALUE = {
528
750
  [:prim, 7, 20] => [true, false, :bool],
529
751
  [:prim, 7, 21] => [true, true, :bool],
530
752
  [:prim, 7, 22] => [true, nil, :nil],
531
- [:prim, 7, 23] => [true, :undefined, :undefined],
532
753
  }
754
+ SIMPLE_VALUE_SIMPLE = Set[23] + (0..19) + (32..255)
533
755
 
534
756
  def extract_value(t) # []
535
757
  if vt = VALUE_TYPE[t[0]]
536
758
  [true, t[1], vt]
537
- elsif v = SIMPLE_VALUE[t]
538
- v
759
+ elsif t[0] == :prim && t[1] == 7
760
+ case t2 = t[2]
761
+ when Integer
762
+ when Array
763
+ a, b, c = extract_value(t2)
764
+ if a && c == Integer
765
+ t2 = b
766
+ end
767
+ end
768
+ if v = SIMPLE_VALUE[[:prim, 7, t2]]
769
+ v
770
+ elsif SIMPLE_VALUE_SIMPLE === t2
771
+ [true, CBOR::Simple.new(t2), :simple]
772
+ end
773
+ elsif t[0] == :anno
774
+ _, conop, target, control = t
775
+ warn ["EXV0", conop, target, control].inspect if ENV["CDDL_TRACE"]
776
+ if conop == :cat || conop == :plus || conop == :det
777
+ ok1, v1, vt1 = extract_value(target)
778
+ ok2, v2, vt2 = extract_value(control)
779
+ # warn ["EXV", ok1, v1, vt1, ok2, v2, vt2].inspect
780
+ if ok1 && ok2
781
+ if vt1 == Integer
782
+ [true, v1 + Integer(v2), Integer] if vt2 == Integer || vt2 == Float
783
+ elsif vt1 == Float
784
+ [true, v1 + v2, vt1] if vt2 == Integer || vt2 == Float
785
+ else
786
+ if conop == :det
787
+ v1 = remove_indentation(v1)
788
+ v2 = remove_indentation(v2)
789
+ end
790
+ [true, v1 + v2, vt1] if vt1 == vt2
791
+ end
792
+ end rescue nil
793
+ end
794
+ end || [false]
795
+ end
796
+
797
+ def extract_array(t)
798
+ return [false] unless t[0] == :array
799
+ [true, *t[1..-1].map { |el|
800
+ return [false] unless el[0..3] == [:member, 1, 1, nil]
801
+ ok, v, vt = extract_value(el[4])
802
+ return [false] unless ok
803
+ [v, vt]
804
+ }]
805
+ end
806
+
807
+
808
+ def extract_arg0(t)
809
+ return [false] unless t[0] == :array
810
+ [true,
811
+ (el = t[1]
812
+ return [false] unless el[0..3] == [:member, 1, 1, nil]
813
+ ok, v, vt = extract_value(el[4])
814
+ return [false] unless ok
815
+ [v, vt]
816
+ ),
817
+ *t[2..-1]]
818
+ end
819
+
820
+ def extract_feature(control, d)
821
+ ok, v, vt = extract_value(control)
822
+ if ok
823
+ nm = v
824
+ det = d
825
+ warn "*** feature controller should be a string: #{control.inspect}" unless String == vt
539
826
  else
540
- [false]
827
+ ok, *v = extract_array(control)
828
+ if ok && v.size == 2
829
+ nm = v[0][0]
830
+ det = v[1][0]
831
+ warn "*** first element of feature controller should be a string: #{control.inspect}" unless String === nm
832
+ else
833
+ warn "*** feature controller not implemented: #{control.inspect}"
834
+ end
541
835
  end
836
+ [nm, det]
542
837
  end
543
838
 
544
839
  def validate_diag
@@ -547,11 +842,19 @@ module CDDL
547
842
 
548
843
  def validate(d, warn=true)
549
844
  @recursion = 0
845
+ $features = Hash.new {|h, k| h[k] = {}}
550
846
  result = validate1a(d, rules)
551
- unless result
552
- if warn
847
+ if warn
848
+ if result
849
+ if $features != {}
850
+ features = $features.reject {|k, v| CDDL_FEATURE_OK[k.to_s] }
851
+ # warn "@@@ FEAT #{CDDL_FEATURE_OK.inspect} #{CDDL_FEATURE_REJECT.inspect}"
852
+ warn "** Features potentially used (#$fn): #{features.map {|k, v| "#{k}: #{v.keys}"}.join(", ")}" if features != {}
853
+ end
854
+ else
553
855
  warn "CDDL validation failure (#{result.inspect} for #{d.inspect}):"
554
856
  PP::pp(validate_diag, STDERR)
857
+ puts(validate_diag.cbor_diagnostic)
555
858
  end
556
859
  end
557
860
  result
@@ -559,7 +862,7 @@ module CDDL
559
862
 
560
863
  def validate_result(check)
561
864
  check || (
562
- @last_message = yield
865
+ @last_message << yield
563
866
  false
564
867
  )
565
868
  end
@@ -592,7 +895,9 @@ module CDDL
592
895
  ann.concat(ann2)
593
896
  end
594
897
  if occ < s
595
- @last_message = "occur not reached in array #{d} for #{where}"
898
+ # warn "*** lme #{@last_message.encoding} #{@last_message}"
899
+ # warn "*** #{"\noccur #{occ} < #{s}, not reached at #{i} in array #{d} for #{where}".encoding}"
900
+ @last_message << "\noccur #{occ} < #{s}, not reached at #{i} in array #{d} for #{where}"
596
901
  return [false, ann]
597
902
  end
598
903
  end
@@ -681,13 +986,17 @@ module CDDL
681
986
  puts "COMPLEX: #{k.inspect} #{simple.inspect} #{simpleval.inspect}" if ENV["CDDL_TRACE"]
682
987
  keys = d_check.keys
683
988
  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
- }
989
+ count = 0
990
+ catch :enough do
991
+ ta.all? { |val|
992
+ if (ann2 = validate1a(d[val], v)) && # XXX check cut or not!
993
+ d_check.delete(val) {:not_found} != :not_found
994
+ anno.concat(ann2)
995
+ throw :enough, true if (count += 1) == e
996
+ true
997
+ end
998
+ }
999
+ end and validate_result(count >= s) { "not enough #{ta.inspect} for #{r.inspect}" }
691
1000
  end
692
1001
  end
693
1002
  else
@@ -696,6 +1005,19 @@ module CDDL
696
1005
  }
697
1006
  end
698
1007
 
1008
+ def validate1a_ignore_encoding(d, where)
1009
+ if String === d
1010
+ validate1a(d.force_encoding(Encoding::BINARY), where) or (
1011
+ text = d.force_encoding(Encoding::UTF_8).scrub {
1012
+ fail "can't use bytes as text"
1013
+ } rescue :NOT_A_TEXT_STRING
1014
+ validate1a(text, where)
1015
+ )
1016
+ else
1017
+ validate1a(d, where)
1018
+ end
1019
+ end
1020
+
699
1021
  def validate1a(d, where)
700
1022
  if ann = validate1(d, where)
701
1023
  here = [d, where]
@@ -708,6 +1030,8 @@ module CDDL
708
1030
  end
709
1031
 
710
1032
  OPERATORS = {lt: :<, le: :<=, gt: :>, ge: :>=, eq: :==, ne: :!=}
1033
+ OPERATORS_MATCH = {eq: true, ne: false}
1034
+ FLOAT_AI_FROM_SIZE = {3 => 25, 5 => 26, 9 => 27}
711
1035
 
712
1036
  def validate1(d, where)
713
1037
  if ENV["CDDL_TRACE"]
@@ -718,6 +1042,9 @@ module CDDL
718
1042
  @last_data = d
719
1043
  @last_rule = where
720
1044
  ann = nil
1045
+ if CBOR::Tagged === d && d.tag === 888 && ENV["CDDL_888"]
1046
+ return []
1047
+ end
721
1048
  case where[0]
722
1049
  when :type1
723
1050
  if where[1..-1].any? {|r| ann = validate1a(d, r)}
@@ -741,16 +1068,55 @@ module CDDL
741
1068
  idx, ann = validate_forward(d, 0, where)
742
1069
  ann if validate_result(idx == d.size) { "#{validate_diag.inspect} -- cannot complete (#{idx}, #{d.size}) array #{d} for #{where}" }
743
1070
  end
744
- when :text, :int, :float, :bytes
1071
+ when :int, :float
745
1072
  _, v = extract_value(where)
746
1073
  [] if d == v
1074
+ when :text, :bytes
1075
+ _, v = extract_value(where)
1076
+ [] if d == v && ((d.encoding == Encoding::BINARY) == (v.encoding == Encoding::BINARY))
747
1077
  when :range
748
1078
  [] if where[2] === d && where[1].include?(d)
749
1079
  when :anno
750
- target = where[2]
751
- if ann = validate1a(d, target)
752
- control = where[3]
753
- case where[1]
1080
+ _, conop, target, control = where
1081
+ if conop == :cat || conop == :plus || conop == :det
1082
+ ok1, v1, vt1 = extract_value(target)
1083
+ ok2, v2, vt2 = extract_value(control)
1084
+ warn ["ANNO0", ok1, v1, vt1, ok2, v2, vt2, d].inspect if ENV["CDDL_TRACE"]
1085
+ if ok1 && ok2
1086
+ v2 = Integer(v2) if vt1 == Integer
1087
+ if conop == :det
1088
+ v1 = remove_indentation(v1)
1089
+ v2 = remove_indentation(v2)
1090
+ end
1091
+ # warn ["ANNO", ok1, v1, vt1, ok2, v2, vt2, d].inspect
1092
+ [] if d == v1 + v2 # XXX Focus ArgumentError
1093
+ elsif conop == :cat && String === d
1094
+ warn ["CAT-L", ok1, v1, vt1, ok2, v2, vt2, d].inspect if ENV["CDDL_TRACE"]
1095
+ if ok1 && vt1 == String
1096
+ # d and lhs (v1) need to agree in encoding
1097
+ if d.encoding == v1.encoding
1098
+ if d[0...v1.length] == v1
1099
+ d2 = d[v1.length..-1]
1100
+ warn ["CAT-L1", d2, d2.encoding, control].inspect if ENV["CDDL_TRACE"]
1101
+ validate1a_ignore_encoding(d2, control)
1102
+ end
1103
+ end
1104
+ elsif ok2 && vt2 == String
1105
+ warn ["CAT-R", ok1, v1, vt1, ok2, v2, vt2, d].inspect if ENV["CDDL_TRACE"]
1106
+ if d[-v2.length..-1] == v2
1107
+ d1 = d[0...-v2.length]
1108
+ validate1a(d1, control)
1109
+ end
1110
+ end
1111
+ elsif conop == :plus && Integer === d
1112
+ if ok1 && vt1 == Integer
1113
+ validate1a(d - Integer(v1), control)
1114
+ elsif ok2 && vt2 == Integer
1115
+ validate1a(d - Integer(v2), target)
1116
+ end
1117
+ end
1118
+ elsif ann = validate1a(d, target)
1119
+ case conop
754
1120
  when :size
755
1121
  case d
756
1122
  when Integer
@@ -789,11 +1155,20 @@ module CDDL
789
1155
  $default_warned = true
790
1156
  end
791
1157
  ann
1158
+ when :feature
1159
+ nm, det = extract_feature(control, d)
1160
+ $features[nm][det] = true
1161
+ ann if !CDDL_FEATURE_REJECT[nm]
792
1162
  when :lt, :le, :gt, :ge, :eq, :ne
793
- op = OPERATORS[where[1]]
1163
+ op = OPERATORS[conop]
794
1164
  ok, v, _vt = extract_value(control)
795
1165
  if ok
796
1166
  ann if d.send(op, v) rescue nil # XXX Focus ArgumentError
1167
+ else
1168
+ needs_to_match = OPERATORS_MATCH[conop]
1169
+ unless needs_to_match.nil?
1170
+ ann if !!validate1(d, control) == needs_to_match
1171
+ end
797
1172
  end
798
1173
  when :regexp
799
1174
  ann if (
@@ -806,10 +1181,181 @@ module CDDL
806
1181
  end
807
1182
  end
808
1183
  )
1184
+ when :abnf, :abnfb
1185
+ ann if (
1186
+ if String === d
1187
+ ok, v, vt = extract_value(control)
1188
+ if ok && vt == String
1189
+ begin
1190
+ ABNF_PARSER_FOR_STRING[v].validate(
1191
+ d.dup.force_encoding(ABNF_ENCODING_FOR_CONOP[conop]).codepoints.pack("U*")
1192
+ )
1193
+ true
1194
+ rescue => e
1195
+ # warn "*** #{e}" # XXX
1196
+ @last_message = e.to_s.force_encoding(Encoding::UTF_8)
1197
+ nil
1198
+ end
1199
+ end
1200
+ end
1201
+ )
809
1202
  when :cbor
810
1203
  ann if validate1((CBOR::decode(d) rescue :BAD_CBOR), control)
811
1204
  when :cborseq
812
1205
  ann if validate1((CBOR::decode("\x9f".b << d << "\xff".b) rescue :BAD_CBOR), control)
1206
+ when :cbordet
1207
+ decoded = CBOR::decode(d) rescue :BAD_CBOR
1208
+ if d != decoded.to_deterministic_cbor
1209
+ @last_message = "CBOR #{d.inspect} not deterministically encoded"
1210
+ nil
1211
+ else
1212
+ ann if validate1(decoded, control)
1213
+ end
1214
+ when :cborseqdet
1215
+ decoded = CBOR::decode("\x9f".b << d << "\xff".b) rescue :BAD_CBOR
1216
+ if d != unpack_array_to_sequence(decoded.to_deterministic_cbor, d.inspect)
1217
+ @last_message = "CBOR Sequence #{d.inspect} not deterministically encoded"
1218
+ nil
1219
+ else
1220
+ ann if validate1(decoded, control)
1221
+ end
1222
+ when :json
1223
+ ann if validate1((JSON::load(d) rescue :BAD_JSON), control)
1224
+ when :decimal, :base10
1225
+ if conop == :decimal
1226
+ unless @decimal_warned
1227
+ warn "** .decimal control operator is now called .base10"
1228
+ @decimal_warned = true
1229
+ end
1230
+ end
1231
+
1232
+ ann if (
1233
+ if /\A0|-?[1-9][0-9]*\z/ === d
1234
+ validate1((d.to_i rescue :BAD_JSON), control)
1235
+ end
1236
+ )
1237
+ when :join
1238
+ t = control
1239
+ v = if t[0] == :array
1240
+ t[1..-1].map { |el|
1241
+ if el[0..2] == [:member, 1, 1]
1242
+ ok, v, vt = extract_value(el[4])
1243
+ if ok
1244
+ [true, v, vt]
1245
+ else
1246
+ [false, el[4]]
1247
+ end
1248
+ end
1249
+ }
1250
+ end
1251
+ warn "@@@JOIN@@@ #{v.inspect}" if ENV["CDDL_TRACE"]
1252
+ ok = true
1253
+ if v
1254
+ ix = 0
1255
+ rest = d.dup
1256
+ while ix < v.length
1257
+ if left = v[ix]
1258
+ if left[0] # match constant value first
1259
+ fail "Not a string for #{left.inspect} in #{where}" unless String == left[2]
1260
+ want = left[1]
1261
+ have = rest[0...left[1].length]
1262
+ if want == have
1263
+ rest[0...left[1].length] = ''
1264
+ ix += 1
1265
+ next
1266
+ else
1267
+ fail ".join: want #{want.inspect}, have #{have.inspect}"
1268
+ end
1269
+ else
1270
+ ix += 1
1271
+ if ix == v.length # match remaining
1272
+ warn "@@@JOIN ok in #{ok.inspect} rest #{rest.inspect}" if ENV["CDDL_TRACE"]
1273
+ ok &&= validate1(rest, left[1])
1274
+ warn "@@@JOIN ok out #{ok.inspect}" if ENV["CDDL_TRACE"]
1275
+ # more diag
1276
+ rest = ''
1277
+ next
1278
+ else
1279
+ if mid = v[ix]
1280
+ if mid[0] # have constant value to split over
1281
+ fail "Not a string for #{mid} in #{where}" unless String == mid[2]
1282
+ rest1, rest2 = rest.split(mid[1], 2)
1283
+ if rest2
1284
+ warn "@@@JOIN ok in #{ok.inspect} rest1 #{rest1.inspect}" if ENV["CDDL_TRACE"]
1285
+ ok &&= validate1(rest1, left[1])
1286
+ warn "@@@JOIN ok out #{ok.inspect}" if ENV["CDDL_TRACE"]
1287
+ rest = rest2
1288
+ ix += 1
1289
+ next
1290
+ else
1291
+ fail "Can't find #{mid[1].inspect} in #{rest.inspect}"
1292
+ end
1293
+ else
1294
+ fail "Cannot validate consecutive non-constant members of .join in #{where}"
1295
+ end
1296
+ else
1297
+ fail "Cannot handle .join over #{t[ix+1]} in #{where}"
1298
+ end
1299
+ end
1300
+ end
1301
+ else
1302
+ fail "Cannot handle .join over #{t[ix+1]} in #{where}"
1303
+ end
1304
+ end
1305
+ fail "Can't match #{rest.inspect} for .join in #{where}" if rest != ''
1306
+ ann if ok
1307
+ else
1308
+ fail "Don't know yet how to validate against #{where}"
1309
+ end
1310
+ when :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy", :b45, :b32, :h32, :hex, :hexlc, :hexuc
1311
+ ann if (
1312
+ String === d && (
1313
+ decoded = case conop
1314
+ when :b64u
1315
+ /[+\/=]/ !~ d &&
1316
+ Base64.urlsafe_decode64(d)
1317
+ when :"b64u-sloppy"
1318
+ /[-_=]/ !~ d &&
1319
+ Base64.decode64(d.tr("+/", "-_"))
1320
+ when :b64c
1321
+ Base64.strict_decode64(d)
1322
+ when :"b64c-sloppy"
1323
+ Base64.decode64(d)
1324
+ when :b32
1325
+ /=/ !~ d &&
1326
+ (Base32.table = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") &&
1327
+ Base32.decode(d)
1328
+ when :b45
1329
+ Base45Lite.decode(d)
1330
+ when :h32
1331
+ /=/ !~ d &&
1332
+ (Base32.table = "0123456789ABCDEFGHIJKLMNOPQRSTUV") &&
1333
+ Base32.decode(d)
1334
+ when :hex
1335
+ /\A[0-9a-fA-F]*\z/ =~ d && [d].pack("H*")
1336
+ when :hexuc
1337
+ /\A[0-9A-F]*\z/ =~ d && [d].pack("H*")
1338
+ when :hexlc
1339
+ /\A[0-9a-f]*\z/ =~ d && [d].pack("H*")
1340
+ else fail "Cannot happen"
1341
+ end
1342
+ ) && validate1(decoded.b, control)
1343
+ )
1344
+ when :printf
1345
+ ann if String === d && (
1346
+ ok, fmt, *v = extract_arg0(control)
1347
+ if ok && String == fmt[1]
1348
+ fmt = fmt[0]
1349
+ # warn "** ok #{ok.inspect} fmt #{fmt.inspect} v #{v.inspect}"
1350
+ decoded = d.scanf(fmt) # this is a bit too lenient, so let's do:
1351
+ encode_again = fmt % decoded
1352
+ if encode_again != d
1353
+ warn "** fmt #{fmt.inspect} d #{d.inspect} decoded #{decoded.inspect} encode_again #{encode_again.inspect}"
1354
+ else
1355
+ validate1(decoded, [:array, *v])
1356
+ end
1357
+ end
1358
+ )
813
1359
  when :within
814
1360
  if validate1(d, control)
815
1361
  ann
@@ -837,20 +1383,43 @@ module CDDL
837
1383
  when 3
838
1384
  String === d && d.encoding != Encoding::BINARY # cheat
839
1385
  when 6
840
- CBOR::Tagged === d && d.tag == where[2] && validate1a(d.data, where[3])
1386
+ if Integer === d
1387
+ t = 2 # assuming only 2/3 match an Integer
1388
+ if d < 0
1389
+ d = ~d
1390
+ t = 3
1391
+ end
1392
+ d = CBOR::Tagged.new(t, d == 0 ? "".b : d.digits(256).reverse!.pack("C*"))
1393
+ end
1394
+ CBOR::Tagged === d && (
1395
+ Integer === where[2] ? d.tag == where[2] : validate1a(d.tag, where[2])
1396
+ ) && validate1a(d.data, where[3])
841
1397
  when 7
842
1398
  t, v = extract_value(where)
843
1399
  if t
844
1400
  v.eql? d
845
1401
  else
846
- case where[2]
1402
+ case w2 = where[2]
847
1403
  when nil
848
- # XXX
849
- fail
1404
+ Float === d || CBOR::Simple === d || [false, true, nil].include?(d)
1405
+ when Array
1406
+ headnum = case d
1407
+ when Float
1408
+ FLOAT_AI_FROM_SIZE[d.to_cbor.size]
1409
+ when false
1410
+ 20
1411
+ when true
1412
+ 21
1413
+ when nil
1414
+ 22
1415
+ when CBOR::Simple
1416
+ d.value
1417
+ end
1418
+ validate1a(headnum, w2)
850
1419
  when 25, 26, 27
851
- Float === d
1420
+ Float === d && FLOAT_AI_FROM_SIZE[d.to_cbor.size] == w2
852
1421
  else
853
- fail
1422
+ fail [:val7, d, where].inspect
854
1423
  end
855
1424
  end
856
1425
  else
@@ -900,6 +1469,10 @@ module CDDL
900
1469
  else
901
1470
  [:type1]
902
1471
  end
1472
+ elsif s = ENV["CDDL_INVENT"]
1473
+ s = "_" if s == ""
1474
+ r = [:type1, [:text, "#{s}-#{name}"]]
1475
+ return r
903
1476
  end
904
1477
  end
905
1478
  if r
@@ -945,29 +1518,69 @@ module CDDL
945
1518
  end
946
1519
  end
947
1520
 
1521
+ STRING_ESCAPES = {
1522
+ "\"" => "\"",
1523
+ "/" => "/",
1524
+ "\\" => "\\",
1525
+ "'" => "'",
1526
+ "b" => "\b",
1527
+ "f" => "\f",
1528
+ "n" => "\n",
1529
+ "r" => "\r",
1530
+ "t" => "\t",
1531
+ }
1532
+
948
1533
  def value(n)
949
1534
  # cheat:
950
1535
  # warn n
951
1536
  s = n.to_s
952
1537
  if s[-1] == "'"
953
- parts = s.split("'", 3)
954
- if parts[2] != "" || parts[1] =~ /\\/
955
- warn "*** Can't handle backslashes in #{s.inspect} yet"
1538
+ bsqual, *parts = s.split("'", -1)
1539
+ if parts[-1] != ""
1540
+ warn "*** Problem decoding byte string #{s.inspect}"
956
1541
  end
1542
+ bsval = parts[0...-1].join("'").gsub(/\\u([Dd][89AaBb][0-9a-zA-Z]{2})\\u([Dd][CcDdEeFf][0-9a-zA-Z]{2})|\\u([0-9a-zA-Z]{4})|\\u\{([0-9a-zA-Z]+)\}|\r?(\n)|\\([^u])/) {
1543
+ if hex = $3 || $4
1544
+ hex.to_i(16).chr(Encoding::UTF_8)
1545
+ elsif lf = $5
1546
+ lf
1547
+ elsif escaped = $6
1548
+ STRING_ESCAPES[$6] or (
1549
+ fail "Invalid String Escape #{escaped.inspect} in #{s.inspect}"
1550
+ )
1551
+ else
1552
+ ((($1.to_i(16) & 0x3ff) << 10) +
1553
+ ($2.to_i(16) & 0x3ff) + 0x10000).chr(Encoding::UTF_8)
1554
+ end
1555
+ }
957
1556
  [:bytes,
958
- case parts[0]
1557
+ case bsqual
959
1558
  when ""
960
- parts[1].b
1559
+ bsval.b
961
1560
  when "h"
962
- parts[1].gsub(/\s/, "").chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join
1561
+ bsval.gsub(/\s/, "").chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join.b
963
1562
  when "b64"
964
- Base64.urlsafe_decode64(parts[1])
1563
+ Base64.urlsafe_decode64(bsval)
965
1564
  else
966
- warn "*** Can't handle byte string type #{parts[0].inspect} yet"
1565
+ warn "*** Can't handle byte string type #{bsqual.inspect} yet"
967
1566
  end
968
1567
  ]
969
1568
  else
970
- val = eval(n.to_s)
1569
+ if s[-1] == '"'
1570
+ s.gsub!(/\\u([Dd][89AaBb][0-9a-zA-Z]{2})\\u([Dd][CcDdEeFf][0-9a-zA-Z]{2})|\\([^u]|u[0-9a-zA-Z]{4})/) {
1571
+ if $3
1572
+ "\\#$3" # skip this, use eval parser
1573
+ else
1574
+ ((($1.to_i(16) & 0x3ff) << 10) +
1575
+ ($2.to_i(16) & 0x3ff) + 0x10000).chr(Encoding::UTF_8)
1576
+ end
1577
+ }
1578
+ end
1579
+ if s =~ /p.*\d\z/ # hexfloat
1580
+ val = s.scanf("%a").first
1581
+ else
1582
+ val = eval(s)
1583
+ end
971
1584
  # warn val
972
1585
  case val
973
1586
  when Integer; [:int, val]
@@ -1073,9 +1686,13 @@ module CDDL
1073
1686
  def g_process(name, g, genericargs)
1074
1687
  r, ids = g
1075
1688
  args = genericargs.children(:type1).map {|x| type1(x, true)}
1689
+ # warn "** args #{args.inspect}"
1076
1690
  fail "number of args #{name} #{ids.size} #{args.size}" if ids.size != args.size
1077
1691
  bindings = Hash[ids.zip(args)]
1078
- r_process("#{name}<#{genericargs._strip}>", r, bindings)
1692
+ # warn "** bindings: #{bindings.inspect}"
1693
+ genname = "#{name}<#{args.inspect}>" # XXX this works up to a RECURSE...
1694
+ # warn "** genname #{genname}"
1695
+ r_process(genname, r, bindings)
1079
1696
  end
1080
1697
 
1081
1698
  def type_recall(name, canbegroup, genericargs)
@@ -1142,7 +1759,7 @@ module CDDL
1142
1759
  end
1143
1760
  when :recurse_grpent # XXX we don't have the entry yet
1144
1761
  when :member
1145
- fail "map entry without member key given: #{member}" unless member[3] || member[4][0] == :grpent || member[4][0] == :grpchoice
1762
+ fail "map entry #{member.inspect} without member key given: #{member}" unless member[3] || member[4][0] == :grpent || member[4][0] == :grpchoice
1146
1763
  else
1147
1764
  fail ["type1 member", member].inspect unless member[0] == :member
1148
1765
  end
@@ -1151,8 +1768,16 @@ module CDDL
1151
1768
 
1152
1769
  BRACE = {"{" => :map, "[" => :array}
1153
1770
  RANGE_EXCLUDE_END = {".." => false, "..." => true}
1154
- SUPPORTED_ANNOTATIONS = [:bits, :size, :regexp, :cbor, :cborseq, :within, :and,
1155
- :default, :lt, :le, :gt, :ge, :eq, :ne]
1771
+ SUPPORTED_ANNOTATIONS = [:bits, :size, :regexp,
1772
+ :cbor, :cborseq, :cbordet, :cborseqdet,
1773
+ :within, :and,
1774
+ :default, :lt, :le, :gt, :ge, :eq, :ne,
1775
+ :feature, :abnf, :abnfb, :det, :cat, :plus,
1776
+ :json, :decimal, :base10, :join,
1777
+ :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy",
1778
+ :b45, :b32, :h32, :hex, :hexlc, :hexuc,
1779
+ :printf,
1780
+ ]
1156
1781
 
1157
1782
  def type1(n, canbegroup = false)
1158
1783
  # puts "NVALUE #{n.value.inspect}"
@@ -1184,6 +1809,13 @@ module CDDL
1184
1809
  when /\A#(\d+)/
1185
1810
  maj = $1.to_i
1186
1811
  s = [:prim, maj, *n.children(:uint).map(&:to_s).map(&:to_i)]
1812
+ if tn = n.headnumber
1813
+ if ui = tn.uint
1814
+ s << ui.to_s.to_i
1815
+ elsif tt = tn.type
1816
+ s << type(tt)
1817
+ end
1818
+ end
1187
1819
  if tagged_type = n.type
1188
1820
  s << type(tagged_type)
1189
1821
  end
@@ -1214,6 +1846,26 @@ module CDDL
1214
1846
  v
1215
1847
  }
1216
1848
  ]
1849
+ when /\A~/
1850
+ if tn = n.typename
1851
+ s = type_recall(tn.to_s, false, n.genericarg).flatten(1)
1852
+ else
1853
+ fail "No typename in unwrap #{n}"
1854
+ end
1855
+ if s[0] != :type1
1856
+ fail [:UNWRAPs0, s].inspect
1857
+ end
1858
+ case s[1]
1859
+ when :map, :array
1860
+ [:grpent, s[2..-1]]
1861
+ when :prim
1862
+ if s[2] != 6
1863
+ fail [:UNWRAPs2, s].inspect
1864
+ end
1865
+ s[4] # check :type1?
1866
+ else
1867
+ fail [:UNWRAPs1, s].inspect
1868
+ end
1217
1869
  else
1218
1870
  "unimplemented #{n}"
1219
1871
  end