cddl 0.8.9 → 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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cddl +51 -15
  3. data/cddl.gemspec +10 -7
  4. data/data/cddl.abnf +30 -13
  5. data/lib/cbor-pp-play.rb +1 -0
  6. data/lib/cddl.rb +719 -71
  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/cat-re.cddl +4 -0
  21. data/test-data/catb64.cddl +8 -0
  22. data/test-data/complex-occ.cddl +11 -0
  23. data/test-data/coral.cddl +10 -0
  24. data/test-data/coral1.cddl +5 -0
  25. data/test-data/coral2.cddl +7 -0
  26. data/test-data/coral3.cddl +7 -0
  27. data/test-data/decimal-warn.cddl +1 -0
  28. data/test-data/decimal.cddl +1 -0
  29. data/test-data/decimal2.cddl +1 -0
  30. data/test-data/det1.cddl +12 -0
  31. data/test-data/dotplus1.cddl +3 -0
  32. data/test-data/dotplus2.cddl +3 -0
  33. data/test-data/dotplus3.cddl +3 -0
  34. data/test-data/dotplus4.cddl +3 -0
  35. data/test-data/enum.cddl +14 -0
  36. data/test-data/enum1.cddl +12 -0
  37. data/test-data/feat1.cddl +4 -0
  38. data/test-data/feature-controller.cddl +6 -0
  39. data/test-data/feature-detail.cddl +5 -0
  40. data/test-data/hex.cddl +1 -0
  41. data/test-data/hexlc.cddl +1 -0
  42. data/test-data/hexuc.cddl +1 -0
  43. data/test-data/homenet-de.cddl +12 -0
  44. data/test-data/homenet-fe.cddl +12 -0
  45. data/test-data/jim-cut-2.cddl +11 -0
  46. data/test-data/jim-cut.cddl +12 -0
  47. data/test-data/join.cddl +1 -0
  48. data/test-data/joini.cddl +1 -0
  49. data/test-data/joinx.cddl +1 -0
  50. data/test-data/json.cddl +1 -0
  51. data/test-data/json1.cddl +15 -0
  52. data/test-data/json2.cddl +1 -0
  53. data/test-data/jwt.cddl +15 -0
  54. data/test-data/lint1.cddl +9 -0
  55. data/test-data/mdl-ble.cddl +9 -0
  56. data/test-data/mdl-deve.cddl +54 -0
  57. data/test-data/mdl-dp.cddl +11 -0
  58. data/test-data/mdl-mso.cddl +20 -0
  59. data/test-data/mdl-request.cddl +26 -0
  60. data/test-data/mdl-response.cddl +66 -0
  61. data/test-data/mdl.cddl +11 -0
  62. data/test-data/mon-val.cddl +1 -0
  63. data/test-data/multipart-ct.cddl +2 -0
  64. data/test-data/non-empty.cddl +5 -0
  65. data/test-data/oid.cddl +8 -0
  66. data/test-data/oidbat.cddl +8 -0
  67. data/test-data/plus.cddl +3 -0
  68. data/test-data/prim.cddl +7 -0
  69. data/test-data/printf.cddl +2 -0
  70. data/test-data/printf0.cddl +1 -0
  71. data/test-data/printf1.cddl +2 -0
  72. data/test-data/printf2.cddl +2 -0
  73. data/test-data/printf3.cddl +2 -0
  74. data/test-data/sasl.cddl +24 -0
  75. data/test-data/sequence.cddl +1 -0
  76. data/test-data/sipos.cddl +8 -0
  77. data/test-data/star-edited.cddl +98 -0
  78. data/test-data/star.cddl +95 -0
  79. data/test-data/test-gen.cddl +3 -0
  80. data/test-data/time.cddl +11 -0
  81. data/test-data/valemb.cddl +16 -0
  82. data/test-data/yangid.cddl +1 -0
  83. data/test-data/yaron1.cddl +95 -0
  84. metadata +142 -13
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)
@@ -371,9 +442,17 @@ module CDDL
371
442
  when 3
372
443
  gen_word
373
444
  when 6
374
- 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]))
375
450
  when 7
376
- case where[2]
451
+ w2 = where[2]
452
+ if Array === w2
453
+ w2 = generate1(w2)
454
+ end
455
+ case w2
377
456
  when nil
378
457
  Math::PI
379
458
  when 20
@@ -382,10 +461,22 @@ module CDDL
382
461
  true
383
462
  when 22
384
463
  nil
385
- when 23
386
- :undefined
387
- when 25, 26, 27
388
- 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
389
480
  end
390
481
  else
391
482
  fail "Can't generate prim #{where[1]}"
@@ -393,7 +484,7 @@ module CDDL
393
484
  when :anno
394
485
  target = where[2]
395
486
  control = where[3]
396
- case where[1]
487
+ case conop = where[1]
397
488
  when :size
398
489
  should_be_int = generate1(control)
399
490
  unless (Array === target && target[0] == :prim && [0, 2, 3].include?(target[1])) && Integer === should_be_int && should_be_int >= 0
@@ -439,6 +530,40 @@ module CDDL
439
530
  $default_warned = true
440
531
  end
441
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
442
567
  when :eq
443
568
  content = generate1(control)
444
569
  if validate1(content, where)
@@ -452,7 +577,7 @@ module CDDL
452
577
  content = Integer(content)
453
578
  case target[1]
454
579
  when 0
455
- case where[1]
580
+ case conop
456
581
  when :lt
457
582
  rand(0...content)
458
583
  when :le
@@ -466,8 +591,7 @@ module CDDL
466
591
  warn "HUH gen #{where.inspect} #{try.inspect}" unless try.nil?
467
592
  end
468
593
  end
469
- 32.times do
470
- content = generate1(target)
594
+ try_generate(target) do |content|
471
595
  if validate1(content, where)
472
596
  return content
473
597
  end
@@ -479,30 +603,117 @@ module CDDL
479
603
  fail "Don't know yet how to generate #{where}"
480
604
  end
481
605
  REGEXP_FOR_STRING[regexp].random_example(max_repeater_variance: 5)
482
- 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
483
621
  unless target == [:prim, 2]
484
622
  fail "Don't know yet how to generate #{where}"
485
623
  end
486
- content = CBOR::encode(generate1(control))
487
- 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
488
630
  # remove the first head
489
- n = case content.getbyte(0) - (4 << 5)
490
- when 0..23; 1
491
- when 24; 2
492
- when 25; 3
493
- when 26; 5
494
- when 27; 9 # unlikely :-)
495
- else fail "Generated .cborseq sequence for #{where} not an array"
496
- end
497
- content[0...n] = ''
631
+ content = unpack_array_to_sequence(content, where)
498
632
  end
499
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}"
500
711
  when :within, :and
501
- 32.times do
502
- content = generate1(target)
712
+ try_generate(target) do |content|
713
+ $features ||= Hash.new {|h, k| h[k] = {}}
503
714
  if validate1(content, control)
504
715
  return content
505
- elsif where[1] == :within
716
+ elsif conop == :within
506
717
  warn "*** #{content.inspect} meets #{target.inspect} but not #{control.inspect}"
507
718
  end
508
719
  end
@@ -527,22 +738,102 @@ module CDDL
527
738
  end
528
739
  end
529
740
 
530
- 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}
531
749
  SIMPLE_VALUE = {
532
750
  [:prim, 7, 20] => [true, false, :bool],
533
751
  [:prim, 7, 21] => [true, true, :bool],
534
752
  [:prim, 7, 22] => [true, nil, :nil],
535
- [:prim, 7, 23] => [true, :undefined, :undefined],
536
753
  }
754
+ SIMPLE_VALUE_SIMPLE = Set[23] + (0..19) + (32..255)
537
755
 
538
756
  def extract_value(t) # []
539
757
  if vt = VALUE_TYPE[t[0]]
540
758
  [true, t[1], vt]
541
- elsif v = SIMPLE_VALUE[t]
542
- 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
543
826
  else
544
- [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
545
835
  end
836
+ [nm, det]
546
837
  end
547
838
 
548
839
  def validate_diag
@@ -551,11 +842,19 @@ module CDDL
551
842
 
552
843
  def validate(d, warn=true)
553
844
  @recursion = 0
845
+ $features = Hash.new {|h, k| h[k] = {}}
554
846
  result = validate1a(d, rules)
555
- unless result
556
- 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
557
855
  warn "CDDL validation failure (#{result.inspect} for #{d.inspect}):"
558
856
  PP::pp(validate_diag, STDERR)
857
+ puts(validate_diag.cbor_diagnostic)
559
858
  end
560
859
  end
561
860
  result
@@ -563,7 +862,7 @@ module CDDL
563
862
 
564
863
  def validate_result(check)
565
864
  check || (
566
- @last_message = yield
865
+ @last_message << yield
567
866
  false
568
867
  )
569
868
  end
@@ -596,7 +895,9 @@ module CDDL
596
895
  ann.concat(ann2)
597
896
  end
598
897
  if occ < s
599
- @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}"
600
901
  return [false, ann]
601
902
  end
602
903
  end
@@ -685,13 +986,17 @@ module CDDL
685
986
  puts "COMPLEX: #{k.inspect} #{simple.inspect} #{simpleval.inspect}" if ENV["CDDL_TRACE"]
686
987
  keys = d_check.keys
687
988
  ta, keys = keys.partition{ |key| validate1(key, k)}
688
- # XXX check ta.size against s/e
689
- ta.all? { |val|
690
- if (ann2 = validate1a(d[val], v)) &&
691
- d_check.delete(val) {:not_found} != :not_found
692
- anno.concat(ann2)
693
- end
694
- }
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}" }
695
1000
  end
696
1001
  end
697
1002
  else
@@ -700,6 +1005,19 @@ module CDDL
700
1005
  }
701
1006
  end
702
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
+
703
1021
  def validate1a(d, where)
704
1022
  if ann = validate1(d, where)
705
1023
  here = [d, where]
@@ -712,6 +1030,8 @@ module CDDL
712
1030
  end
713
1031
 
714
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}
715
1035
 
716
1036
  def validate1(d, where)
717
1037
  if ENV["CDDL_TRACE"]
@@ -722,6 +1042,9 @@ module CDDL
722
1042
  @last_data = d
723
1043
  @last_rule = where
724
1044
  ann = nil
1045
+ if CBOR::Tagged === d && d.tag === 888 && ENV["CDDL_888"]
1046
+ return []
1047
+ end
725
1048
  case where[0]
726
1049
  when :type1
727
1050
  if where[1..-1].any? {|r| ann = validate1a(d, r)}
@@ -745,16 +1068,55 @@ module CDDL
745
1068
  idx, ann = validate_forward(d, 0, where)
746
1069
  ann if validate_result(idx == d.size) { "#{validate_diag.inspect} -- cannot complete (#{idx}, #{d.size}) array #{d} for #{where}" }
747
1070
  end
748
- when :text, :int, :float, :bytes
1071
+ when :int, :float
749
1072
  _, v = extract_value(where)
750
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))
751
1077
  when :range
752
1078
  [] if where[2] === d && where[1].include?(d)
753
1079
  when :anno
754
- target = where[2]
755
- if ann = validate1a(d, target)
756
- control = where[3]
757
- 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
758
1120
  when :size
759
1121
  case d
760
1122
  when Integer
@@ -793,11 +1155,20 @@ module CDDL
793
1155
  $default_warned = true
794
1156
  end
795
1157
  ann
1158
+ when :feature
1159
+ nm, det = extract_feature(control, d)
1160
+ $features[nm][det] = true
1161
+ ann if !CDDL_FEATURE_REJECT[nm]
796
1162
  when :lt, :le, :gt, :ge, :eq, :ne
797
- op = OPERATORS[where[1]]
1163
+ op = OPERATORS[conop]
798
1164
  ok, v, _vt = extract_value(control)
799
1165
  if ok
800
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
801
1172
  end
802
1173
  when :regexp
803
1174
  ann if (
@@ -810,10 +1181,181 @@ module CDDL
810
1181
  end
811
1182
  end
812
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
+ )
813
1202
  when :cbor
814
1203
  ann if validate1((CBOR::decode(d) rescue :BAD_CBOR), control)
815
1204
  when :cborseq
816
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
+ )
817
1359
  when :within
818
1360
  if validate1(d, control)
819
1361
  ann
@@ -841,20 +1383,43 @@ module CDDL
841
1383
  when 3
842
1384
  String === d && d.encoding != Encoding::BINARY # cheat
843
1385
  when 6
844
- 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])
845
1397
  when 7
846
1398
  t, v = extract_value(where)
847
1399
  if t
848
1400
  v.eql? d
849
1401
  else
850
- case where[2]
1402
+ case w2 = where[2]
851
1403
  when nil
852
- # XXX
853
- 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)
854
1419
  when 25, 26, 27
855
- Float === d
1420
+ Float === d && FLOAT_AI_FROM_SIZE[d.to_cbor.size] == w2
856
1421
  else
857
- fail
1422
+ fail [:val7, d, where].inspect
858
1423
  end
859
1424
  end
860
1425
  else
@@ -904,6 +1469,10 @@ module CDDL
904
1469
  else
905
1470
  [:type1]
906
1471
  end
1472
+ elsif s = ENV["CDDL_INVENT"]
1473
+ s = "_" if s == ""
1474
+ r = [:type1, [:text, "#{s}-#{name}"]]
1475
+ return r
907
1476
  end
908
1477
  end
909
1478
  if r
@@ -949,29 +1518,69 @@ module CDDL
949
1518
  end
950
1519
  end
951
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
+
952
1533
  def value(n)
953
1534
  # cheat:
954
1535
  # warn n
955
1536
  s = n.to_s
956
1537
  if s[-1] == "'"
957
- parts = s.split("'", 3)
958
- if parts[2] != "" || parts[1] =~ /\\/
959
- 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}"
960
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
+ }
961
1556
  [:bytes,
962
- case parts[0]
1557
+ case bsqual
963
1558
  when ""
964
- parts[1].b
1559
+ bsval.b
965
1560
  when "h"
966
- 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
967
1562
  when "b64"
968
- Base64.urlsafe_decode64(parts[1])
1563
+ Base64.urlsafe_decode64(bsval)
969
1564
  else
970
- warn "*** Can't handle byte string type #{parts[0].inspect} yet"
1565
+ warn "*** Can't handle byte string type #{bsqual.inspect} yet"
971
1566
  end
972
1567
  ]
973
1568
  else
974
- 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
975
1584
  # warn val
976
1585
  case val
977
1586
  when Integer; [:int, val]
@@ -1077,9 +1686,13 @@ module CDDL
1077
1686
  def g_process(name, g, genericargs)
1078
1687
  r, ids = g
1079
1688
  args = genericargs.children(:type1).map {|x| type1(x, true)}
1689
+ # warn "** args #{args.inspect}"
1080
1690
  fail "number of args #{name} #{ids.size} #{args.size}" if ids.size != args.size
1081
1691
  bindings = Hash[ids.zip(args)]
1082
- 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)
1083
1696
  end
1084
1697
 
1085
1698
  def type_recall(name, canbegroup, genericargs)
@@ -1155,8 +1768,16 @@ module CDDL
1155
1768
 
1156
1769
  BRACE = {"{" => :map, "[" => :array}
1157
1770
  RANGE_EXCLUDE_END = {".." => false, "..." => true}
1158
- SUPPORTED_ANNOTATIONS = [:bits, :size, :regexp, :cbor, :cborseq, :within, :and,
1159
- :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
+ ]
1160
1781
 
1161
1782
  def type1(n, canbegroup = false)
1162
1783
  # puts "NVALUE #{n.value.inspect}"
@@ -1188,6 +1809,13 @@ module CDDL
1188
1809
  when /\A#(\d+)/
1189
1810
  maj = $1.to_i
1190
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
1191
1819
  if tagged_type = n.type
1192
1820
  s << type(tagged_type)
1193
1821
  end
@@ -1218,6 +1846,26 @@ module CDDL
1218
1846
  v
1219
1847
  }
1220
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
1221
1869
  else
1222
1870
  "unimplemented #{n}"
1223
1871
  end