cddl 0.8.8 → 0.10.3

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cddl +35 -15
  3. data/cddl.gemspec +5 -2
  4. data/data/cddl.abnf +2 -1
  5. data/lib/base45_lite.rb +71 -0
  6. data/lib/cbor-pp-play.rb +1 -0
  7. data/lib/cddl.rb +406 -50
  8. data/test/test-cddl.rb +53 -2
  9. data/test-data/7807.cddl +14 -0
  10. data/test-data/abignum.cddl +1 -0
  11. data/test-data/abnf1.cddl +3 -0
  12. data/test-data/abnf2.cddl +37 -0
  13. data/test-data/abnf3.cddl +32 -0
  14. data/test-data/b64c-sloppy.cddl +1 -0
  15. data/test-data/b64c.cddl +1 -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 +2 -0
  24. data/test-data/complex-occ.cddl +11 -0
  25. data/test-data/coral.cddl +10 -0
  26. data/test-data/coral1.cddl +5 -0
  27. data/test-data/coral2.cddl +7 -0
  28. data/test-data/coral3.cddl +7 -0
  29. data/test-data/decimal.cddl +1 -0
  30. data/test-data/decimal2.cddl +1 -0
  31. data/test-data/det1.cddl +12 -0
  32. data/test-data/feat1.cddl +4 -0
  33. data/test-data/feature-controller.cddl +6 -0
  34. data/test-data/feature-detail.cddl +5 -0
  35. data/test-data/foo.cddl +1 -0
  36. data/test-data/hex.cddl +1 -0
  37. data/test-data/hexlc.cddl +1 -0
  38. data/test-data/hexuc.cddl +1 -0
  39. data/test-data/homenet-de.cddl +12 -0
  40. data/test-data/homenet-fe.cddl +12 -0
  41. data/test-data/jim-cut-2.cddl +11 -0
  42. data/test-data/jim-cut.cddl +12 -0
  43. data/test-data/join.cddl +1 -0
  44. data/test-data/joini.cddl +1 -0
  45. data/test-data/joinx.cddl +1 -0
  46. data/test-data/json.cddl +1 -0
  47. data/test-data/json2.cddl +1 -0
  48. data/test-data/jsoniodef.cddl +719 -0
  49. data/test-data/lint1.cddl +9 -0
  50. data/test-data/mdl-ble.cddl +9 -0
  51. data/test-data/mdl-deve.cddl +54 -0
  52. data/test-data/mdl-dp.cddl +11 -0
  53. data/test-data/mdl-mso.cddl +20 -0
  54. data/test-data/mdl-request.cddl +26 -0
  55. data/test-data/mdl-response.cddl +66 -0
  56. data/test-data/mdl.cddl +11 -0
  57. data/test-data/mon-val.cddl +1 -0
  58. data/test-data/multipart-ct.cddl +2 -0
  59. data/test-data/named-group.cddl +7 -0
  60. data/test-data/non-empty.cddl +5 -0
  61. data/test-data/oid.cddl +8 -0
  62. data/test-data/oidbat.cddl +8 -0
  63. data/test-data/plus.cddl +3 -0
  64. data/test-data/sasl.cddl +24 -0
  65. data/test-data/sequence.cddl +1 -0
  66. data/test-data/sipos.cddl +8 -0
  67. data/test-data/star-edited.cddl +98 -0
  68. data/test-data/star.cddl +95 -0
  69. data/test-data/test-gen.cddl +3 -0
  70. data/test-data/time.cddl +11 -0
  71. data/test-data/yangid.cddl +1 -0
  72. data/test-data/yaron1.cddl +95 -0
  73. metadata +100 -8
data/lib/cddl.rb CHANGED
@@ -2,9 +2,13 @@ require 'abnc'
2
2
  require 'pp'
3
3
  require 'pathname'
4
4
  require 'cbor-pure' unless defined?(CBOR::Tagged)
5
+ require 'cbor-deterministic'
5
6
  require 'regexp-examples'
7
+ require 'abnftt'
6
8
  require 'colorize'
7
9
  require 'base64'
10
+ require 'base32'
11
+ require 'base45_lite'
8
12
 
9
13
  module CDDL
10
14
 
@@ -21,6 +25,20 @@ module CDDL
21
25
 
22
26
  class Parser
23
27
 
28
+ FEATURE_REJECT_RE = /\A\^/
29
+ # CDDL_FEATURE_OK=cbor,^json
30
+ CDDL_FEATURE_OK, CDDL_FEATURE_REJECT =
31
+ if ok = ENV["CDDL_FEATURE_OK"]
32
+ ok.split(/,\s*/)
33
+ .partition{|s| s[0] !~ FEATURE_REJECT_RE}
34
+ .map {|l| Hash[l.map {|feature|
35
+ [feature.sub(FEATURE_REJECT_RE, ''),
36
+ true]}]}
37
+ else
38
+ [{}, {}]
39
+ end
40
+
41
+
24
42
  class BackTrack < Exception; end
25
43
 
26
44
  def initialize(source_text)
@@ -55,6 +73,8 @@ module CDDL
55
73
  @ast = @abnf.ast?
56
74
  # our little argument stack for rule processing
57
75
  @insides = []
76
+ # collect error information
77
+ @last_message = ""
58
78
  end
59
79
 
60
80
  def apr # for debugging
@@ -127,7 +147,7 @@ module CDDL
127
147
  if subtree[0] == :type1 && subtree[1..-1].all? {|x| x[0] == :int}
128
148
  if enumname = subtree.cbor_annotations rescue nil
129
149
  enumname = cname(enumname.first)
130
- subtree[1..-1].each do |x|
150
+ subtree[1..-1].each do |x|
131
151
  if memname = x.cbor_annotations
132
152
  memname = "#{enumname}_#{cname(memname.first)}"
133
153
  add.(memname, x[1].to_s)
@@ -243,10 +263,41 @@ module CDDL
243
263
  [rule[0], *rule[1]]
244
264
  end
245
265
 
266
+ def remove_indentation(s)
267
+ l = s.lines
268
+ indent = l.grep(/\S/).map {|l| l[/^\s*/].size}.min
269
+ l.map {|l| l.sub(/^ {0,#{indent}}/, "")}.join
270
+ end
271
+
272
+ def unpack_array_to_sequence(content, where)
273
+ # remove the first head
274
+ n = case content.getbyte(0) - (4 << 5)
275
+ when 0..23; 1
276
+ when 24; 2
277
+ when 25; 3
278
+ when 26; 5
279
+ when 27; 9 # unlikely :-)
280
+ else fail ".cborseq sequence for #{where} not an array"
281
+ end
282
+ content[0...n] = ''
283
+ content
284
+ end
285
+
286
+
246
287
  # Memoize a bit here
247
288
 
248
289
  REGEXP_FOR_STRING = Hash.new {|h, k|
249
- h[k] = Regexp.new("\\A#{k}\\z")
290
+ h[k] = Regexp.new("\\A(?:#{k})\\z")
291
+ }
292
+
293
+ ABNF_PARSER_FOR_STRING = Hash.new {|h, k|
294
+ grammar = "cddl-t0p--1eve1-f0r--abnf = " << k # XXX
295
+ h[k] = ABNF.from_abnf(grammar)
296
+ }
297
+
298
+ ABNF_ENCODING_FOR_CONOP = {
299
+ abnf: Encoding::UTF_8,
300
+ abnfb: Encoding::BINARY
250
301
  }
251
302
 
252
303
  def generate
@@ -329,8 +380,12 @@ module CDDL
329
380
  unless kr
330
381
  case vr[0]
331
382
  when :grpent
332
- fail "grpent in map #{vr.inspect}" unless vr.size == 2
333
- g = Array.new(st) { generate1(vr[1], true) }.flatten(1)
383
+ # fail "grpent in map #{vr.inspect}" unless vr.size == 2
384
+ g = Array.new(st) {
385
+ vr[1..-1].map{ |vrx|
386
+ generate1(vrx, true)
387
+ }.flatten(1)
388
+ }.flatten(1)
334
389
  # warn "GGG #{g.inspect}"
335
390
  return g
336
391
  when :grpchoice
@@ -389,7 +444,7 @@ module CDDL
389
444
  when :anno
390
445
  target = where[2]
391
446
  control = where[3]
392
- case where[1]
447
+ case conop = where[1]
393
448
  when :size
394
449
  should_be_int = generate1(control)
395
450
  unless (Array === target && target[0] == :prim && [0, 2, 3].include?(target[1])) && Integer === should_be_int && should_be_int >= 0
@@ -435,6 +490,40 @@ module CDDL
435
490
  $default_warned = true
436
491
  end
437
492
  generate1(target, inmap)
493
+ when :feature
494
+ feature, = extract_feature(control, nil)
495
+ # warn "@@@ GENFEAT #{feature.inspect} #{CDDL_FEATURE_REJECT[feature].inspect}"
496
+ if CDDL_FEATURE_REJECT[feature]
497
+ fail BackTrack.new("Feature '#{feature}' rejected")
498
+ end
499
+ generate1(target, inmap)
500
+ when :cat, :det
501
+ lhs = generate1(target, inmap)
502
+ rhs = generate1(control)
503
+ if conop == :det
504
+ lhs = remove_indentation(lhs)
505
+ rhs = remove_indentation(rhs)
506
+ end
507
+ begin
508
+ lhs + rhs
509
+ rescue Exception => e
510
+ fail "Can't #{lhs.inspect} .cat #{rhs.inspect}: #{e.message}"
511
+ end
512
+ when :plus
513
+ lhs = generate1(target, inmap)
514
+ rhs = generate1(control)
515
+ begin
516
+ case lhs
517
+ when Integer
518
+ lhs + Integer(rhs)
519
+ when Numeric
520
+ lhs + rhs
521
+ else
522
+ fail "#{lhs.inspect}: Not a number"
523
+ end
524
+ rescue Exception => e
525
+ fail "Can't #{lhs.inspect} .plus #{rhs.inspect}: #{e.message}"
526
+ end
438
527
  when :eq
439
528
  content = generate1(control)
440
529
  if validate1(content, where)
@@ -448,7 +537,7 @@ module CDDL
448
537
  content = Integer(content)
449
538
  case target[1]
450
539
  when 0
451
- case where[1]
540
+ case conop
452
541
  when :lt
453
542
  rand(0...content)
454
543
  when :le
@@ -475,30 +564,88 @@ module CDDL
475
564
  fail "Don't know yet how to generate #{where}"
476
565
  end
477
566
  REGEXP_FOR_STRING[regexp].random_example(max_repeater_variance: 5)
478
- when :cbor, :cborseq
567
+ when :abnf, :abnfb
568
+ grammar = generate1(control)
569
+ bytes = true if target == [:prim, 2]
570
+ bytes = false if target == [:prim, 3]
571
+ unless !bytes.nil? && String === grammar
572
+ fail "Don't know yet how to generate #{where}"
573
+ end
574
+ grammar << "\n" unless grammar =~ /\n\z/
575
+ out = ABNF_PARSER_FOR_STRING[grammar].generate
576
+ if conop == :abnfb
577
+ out = out.codepoints.pack("C*")
578
+ end
579
+ enc = bytes ? Encoding::BINARY : Encoding::UTF_8
580
+ out.force_encoding(enc)
581
+ when :cbor, :cborseq, :cbordet, :cborseqdet
479
582
  unless target == [:prim, 2]
480
583
  fail "Don't know yet how to generate #{where}"
481
584
  end
482
- content = CBOR::encode(generate1(control))
483
- if where[1] == :cborseq
585
+ input = generate1(control)
586
+ if conop == :cbordet || conop == :cborseqdet
587
+ input = input.cbor_prepare_deterministic
588
+ end
589
+ content = CBOR::encode(input)
590
+ if conop == :cborseq || conop == :cborseqdet
484
591
  # 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] = ''
592
+ content = unpack_array_to_sequence(content, where)
593
+ end
594
+ content
595
+ when :json
596
+ unless target == [:prim, 3]
597
+ fail "Don't know yet how to generate #{where}"
494
598
  end
599
+ content = JSON::dump(generate1(control))
600
+ content
601
+ when :decimal
602
+ unless target == [:prim, 3]
603
+ fail "Don't know yet how to generate #{where}"
604
+ end
605
+ content = Integer(generate1(control)).to_s
606
+ content
607
+ when :join
608
+ content = generate1(control)
609
+ unless Array === content &&
610
+ content.all? {|x| String === x &&
611
+ ((target == [:prim, 2] && x.encoding == Encoding::BINARY) ||
612
+ (target == [:prim, 3] && x.encoding != Encoding::BINARY))}
613
+ fail "Don't know yet how to generate #{where}"
614
+ end
615
+ content = content.join
616
+ content
617
+ when :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy",
618
+ :b45, :b32, :h32, :hex, :hexlc, :hexuc
619
+ content = generate1(control)
620
+ unless String === content
621
+ fail "Don't know yet how to generate #{where}"
622
+ end
623
+ content = case conop
624
+ when :b64u, :"b64u-sloppy"
625
+ Base64.urlsafe_encode64(content, padding: false)
626
+ when :b64c, :"b64c-sloppy"
627
+ Base64.strict_encode64(content)
628
+ when :b45
629
+ Base45Lite.encode(content)
630
+ when :b32
631
+ Base32.table = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
632
+ Base32.encode(content).gsub("=", "")
633
+ when :h32
634
+ Base32.table = "0123456789ABCDEFGHIJKLMNOPQRSTUV"
635
+ Base32.encode(content).gsub("=", "")
636
+ when :hex, :hexuc
637
+ content.unpack("H*")[0].upcase
638
+ when :hexlc
639
+ content.unpack("H*")[0]
640
+ else fail "Cannot happen"
641
+ end
495
642
  content
496
643
  when :within, :and
497
644
  32.times do
498
645
  content = generate1(target)
499
646
  if validate1(content, control)
500
647
  return content
501
- elsif where[1] == :within
648
+ elsif conop == :within
502
649
  warn "*** #{content.inspect} meets #{target.inspect} but not #{control.inspect}"
503
650
  end
504
651
  end
@@ -523,7 +670,7 @@ module CDDL
523
670
  end
524
671
  end
525
672
 
526
- VALUE_TYPE = {text: String, int: Integer, float: Float}
673
+ VALUE_TYPE = {text: String, bytes: String, int: Integer, float: Float}
527
674
  SIMPLE_VALUE = {
528
675
  [:prim, 7, 20] => [true, false, :bool],
529
676
  [:prim, 7, 21] => [true, true, :bool],
@@ -536,9 +683,57 @@ module CDDL
536
683
  [true, t[1], vt]
537
684
  elsif v = SIMPLE_VALUE[t]
538
685
  v
686
+ elsif t[0] == :anno
687
+ _, conop, target, control = t
688
+ # warn ["EXV0", conop, target, control].inspect
689
+ if conop == :cat || conop == :plus || conop == :det
690
+ ok1, v1, vt1 = extract_value(target)
691
+ ok2, v2, vt2 = extract_value(control)
692
+ # warn ["EXV", ok1, v1, vt1, ok2, v2, vt2].inspect
693
+ if ok1 && ok2
694
+ if vt1 == Integer
695
+ [true, v1 + Integer(v2), Integer] if vt2 == Integer || vt2 == Float
696
+ elsif vt1 == Float
697
+ [true, v1 + v2, vt1] if vt2 == Integer || vt2 == Float
698
+ else
699
+ if conop == :det
700
+ v1 = remove_indentation(v1)
701
+ v2 = remove_indentation(v2)
702
+ end
703
+ [true, v1 + v2, vt1] if vt1 == vt2
704
+ end
705
+ end rescue nil
706
+ end
707
+ end || [false]
708
+ end
709
+
710
+ def extract_array(t)
711
+ return [false] unless t[0] == :array
712
+ [true, *t[1..-1].map { |el|
713
+ return [false] unless el[0..3] == [:member, 1, 1, nil]
714
+ ok, v, vt = extract_value(el[4])
715
+ return [false] unless ok
716
+ [v, vt]
717
+ }]
718
+ end
719
+
720
+ def extract_feature(control, d)
721
+ ok, v, vt = extract_value(control)
722
+ if ok
723
+ nm = v
724
+ det = d
725
+ warn "*** feature controller should be a string: #{control.inspect}" unless String == vt
539
726
  else
540
- [false]
727
+ ok, *v = extract_array(control)
728
+ if ok && v.size == 2
729
+ nm = v[0][0]
730
+ det = v[1][0]
731
+ warn "*** first element of feature controller should be a string: #{control.inspect}" unless String === nm
732
+ else
733
+ warn "*** feature controller not implemented: #{control.inspect}"
734
+ end
541
735
  end
736
+ [nm, det]
542
737
  end
543
738
 
544
739
  def validate_diag
@@ -547,11 +742,19 @@ module CDDL
547
742
 
548
743
  def validate(d, warn=true)
549
744
  @recursion = 0
745
+ $features = Hash.new {|h, k| h[k] = {}}
550
746
  result = validate1a(d, rules)
551
- unless result
552
- if warn
747
+ if warn
748
+ if result
749
+ if $features != {}
750
+ features = $features.reject {|k, v| CDDL_FEATURE_OK[k.to_s] }
751
+ # warn "@@@ FEAT #{CDDL_FEATURE_OK.inspect} #{CDDL_FEATURE_REJECT.inspect}"
752
+ warn "** Features potentially used (#$fn): #{features.map {|k, v| "#{k}: #{v.keys}"}.join(", ")}" if features != {}
753
+ end
754
+ else
553
755
  warn "CDDL validation failure (#{result.inspect} for #{d.inspect}):"
554
756
  PP::pp(validate_diag, STDERR)
757
+ puts(validate_diag.cbor_diagnostic)
555
758
  end
556
759
  end
557
760
  result
@@ -559,7 +762,7 @@ module CDDL
559
762
 
560
763
  def validate_result(check)
561
764
  check || (
562
- @last_message = yield
765
+ @last_message << yield
563
766
  false
564
767
  )
565
768
  end
@@ -592,7 +795,9 @@ module CDDL
592
795
  ann.concat(ann2)
593
796
  end
594
797
  if occ < s
595
- @last_message = "occur not reached in array #{d} for #{where}"
798
+ # warn "*** lme #{@last_message.encoding} #{@last_message}"
799
+ # warn "*** #{"\noccur #{occ} < #{s}, not reached at #{i} in array #{d} for #{where}".encoding}"
800
+ @last_message << "\noccur #{occ} < #{s}, not reached at #{i} in array #{d} for #{where}"
596
801
  return [false, ann]
597
802
  end
598
803
  end
@@ -681,13 +886,17 @@ module CDDL
681
886
  puts "COMPLEX: #{k.inspect} #{simple.inspect} #{simpleval.inspect}" if ENV["CDDL_TRACE"]
682
887
  keys = d_check.keys
683
888
  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
- }
889
+ count = 0
890
+ catch :enough do
891
+ ta.all? { |val|
892
+ if (ann2 = validate1a(d[val], v)) && # XXX check cut or not!
893
+ d_check.delete(val) {:not_found} != :not_found
894
+ anno.concat(ann2)
895
+ throw :enough, true if (count += 1) == e
896
+ true
897
+ end
898
+ }
899
+ end and validate_result(count >= s) { "not enough #{ta.inspect} for #{r.inspect}" }
691
900
  end
692
901
  end
693
902
  else
@@ -741,16 +950,31 @@ module CDDL
741
950
  idx, ann = validate_forward(d, 0, where)
742
951
  ann if validate_result(idx == d.size) { "#{validate_diag.inspect} -- cannot complete (#{idx}, #{d.size}) array #{d} for #{where}" }
743
952
  end
744
- when :text, :int, :float, :bytes
953
+ when :int, :float
745
954
  _, v = extract_value(where)
746
955
  [] if d == v
956
+ when :text, :bytes
957
+ _, v = extract_value(where)
958
+ [] if d == v && ((d.encoding == Encoding::BINARY) == (v.encoding == Encoding::BINARY))
747
959
  when :range
748
960
  [] if where[2] === d && where[1].include?(d)
749
961
  when :anno
750
- target = where[2]
751
- if ann = validate1a(d, target)
752
- control = where[3]
753
- case where[1]
962
+ _, conop, target, control = where
963
+ if conop == :cat || conop == :plus || conop == :det
964
+ ok1, v1, vt1 = extract_value(target)
965
+ ok2, v2, vt2 = extract_value(control)
966
+ # warn ["ANNO0", ok1, v1, vt1, ok2, v2, vt2, d].inspect
967
+ if ok1 && ok2
968
+ v2 = Integer(v2) if vt1 == Integer
969
+ if conop == :det
970
+ v1 = remove_indentation(v1)
971
+ v2 = remove_indentation(v2)
972
+ end
973
+ # warn ["ANNO", ok1, v1, vt1, ok2, v2, vt2, d].inspect
974
+ [] if d == v1 + v2 # XXX Focus ArgumentError
975
+ end
976
+ elsif ann = validate1a(d, target)
977
+ case conop
754
978
  when :size
755
979
  case d
756
980
  when Integer
@@ -789,8 +1013,12 @@ module CDDL
789
1013
  $default_warned = true
790
1014
  end
791
1015
  ann
1016
+ when :feature
1017
+ nm, det = extract_feature(control, d)
1018
+ $features[nm][det] = true
1019
+ ann if !CDDL_FEATURE_REJECT[nm]
792
1020
  when :lt, :le, :gt, :ge, :eq, :ne
793
- op = OPERATORS[where[1]]
1021
+ op = OPERATORS[conop]
794
1022
  ok, v, _vt = extract_value(control)
795
1023
  if ok
796
1024
  ann if d.send(op, v) rescue nil # XXX Focus ArgumentError
@@ -806,10 +1034,98 @@ module CDDL
806
1034
  end
807
1035
  end
808
1036
  )
1037
+ when :abnf, :abnfb
1038
+ ann if (
1039
+ if String === d
1040
+ ok, v, vt = extract_value(control)
1041
+ if ok && vt == String
1042
+ begin
1043
+ ABNF_PARSER_FOR_STRING[v].validate(
1044
+ d.dup.force_encoding(ABNF_ENCODING_FOR_CONOP[conop]).codepoints.pack("U*")
1045
+ )
1046
+ true
1047
+ rescue => e
1048
+ # warn "*** #{e}" # XXX
1049
+ @last_message = e.to_s.force_encoding(Encoding::UTF_8)
1050
+ nil
1051
+ end
1052
+ end
1053
+ end
1054
+ )
809
1055
  when :cbor
810
1056
  ann if validate1((CBOR::decode(d) rescue :BAD_CBOR), control)
811
1057
  when :cborseq
812
1058
  ann if validate1((CBOR::decode("\x9f".b << d << "\xff".b) rescue :BAD_CBOR), control)
1059
+ when :cbordet
1060
+ decoded = CBOR::decode(d) rescue :BAD_CBOR
1061
+ if d != decoded.to_deterministic_cbor
1062
+ @last_message = "CBOR #{d.inspect} not deterministically encoded"
1063
+ nil
1064
+ else
1065
+ ann if validate1(decoded, control)
1066
+ end
1067
+ when :cborseqdet
1068
+ decoded = CBOR::decode("\x9f".b << d << "\xff".b) rescue :BAD_CBOR
1069
+ if d != unpack_array_to_sequence(decoded.to_deterministic_cbor, d.inspect)
1070
+ @last_message = "CBOR Sequence #{d.inspect} not deterministically encoded"
1071
+ nil
1072
+ else
1073
+ ann if validate1(decoded, control)
1074
+ end
1075
+ when :json
1076
+ ann if validate1((JSON::load(d) rescue :BAD_JSON), control)
1077
+ when :decimal
1078
+ ann if (
1079
+ if /\A0|-?[1-9][0-9]*\z/ === d
1080
+ validate1((d.to_i rescue :BAD_JSON), control)
1081
+ end
1082
+ )
1083
+ when :join
1084
+ ok, *v = extract_array(control)
1085
+ # warn "@@@JJJ@@@ #{ok.inspect} #{v.inspect}"
1086
+ if ok
1087
+ expected = v.map {|ve|
1088
+ fail "Not a string in #{where}" unless String === ve[0]
1089
+ ve[0]
1090
+ }.join
1091
+ ann if d == expected
1092
+ else
1093
+ fail "Don't know yet how to validate against #{where}"
1094
+ end
1095
+ when :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy", :b45, :b32, :h32, :hex, :hexlc, :hexuc
1096
+ ann if (
1097
+ String === d && (
1098
+ decoded = case conop
1099
+ when :b64u
1100
+ /=/ !~ d &&
1101
+ Base64.urlsafe_decode64(d)
1102
+ when :"b64u-sloppy"
1103
+ /[-_=]/ !~ d &&
1104
+ Base64.decode64(d.tr("+/", "-_"))
1105
+ when :b64c
1106
+ Base64.strict_decode64(d)
1107
+ when :"b64c-sloppy"
1108
+ Base64.decode64(d)
1109
+ when :b32
1110
+ /=/ !~ d &&
1111
+ (Base32.table = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") &&
1112
+ Base32.decode(d)
1113
+ when :b45
1114
+ Base45Lite.decode(d)
1115
+ when :h32
1116
+ /=/ !~ d &&
1117
+ (Base32.table = "0123456789ABCDEFGHIJKLMNOPQRSTUV") &&
1118
+ Base32.decode(d)
1119
+ when :hex
1120
+ /\A[0-9a-fA-F]*\z/ =~ d && [d].pack("H*")
1121
+ when :hexuc
1122
+ /\A[0-9A-F]*\z/ =~ d && [d].pack("H*")
1123
+ when :hexlc
1124
+ /\A[0-9a-f]*\z/ =~ d && [d].pack("H*")
1125
+ else fail "Cannot happen"
1126
+ end
1127
+ ) && validate1(decoded.b, control)
1128
+ )
813
1129
  when :within
814
1130
  if validate1(d, control)
815
1131
  ann
@@ -837,6 +1153,14 @@ module CDDL
837
1153
  when 3
838
1154
  String === d && d.encoding != Encoding::BINARY # cheat
839
1155
  when 6
1156
+ if Integer === d
1157
+ t = 2 # assuming only 2/3 match an Integer
1158
+ if d < 0
1159
+ d = ~d
1160
+ t = 3
1161
+ end
1162
+ d = CBOR::Tagged.new(t, d == 0 ? "".b : d.digits(256).reverse!.pack("C*"))
1163
+ end
840
1164
  CBOR::Tagged === d && d.tag == where[2] && validate1a(d.data, where[3])
841
1165
  when 7
842
1166
  t, v = extract_value(where)
@@ -950,20 +1274,21 @@ module CDDL
950
1274
  # warn n
951
1275
  s = n.to_s
952
1276
  if s[-1] == "'"
953
- parts = s.split("'", 3)
954
- if parts[2] != "" || parts[1] =~ /\\/
955
- warn "*** Can't handle backslashes in #{s.inspect} yet"
1277
+ bsqual, *parts = s.split("'", -1)
1278
+ if parts[-1] != ""
1279
+ warn "*** Problem decoding byte string #{s.inspect}"
956
1280
  end
1281
+ bsval = parts[0...-1].join("'").gsub(/\\(.)/){$1}
957
1282
  [:bytes,
958
- case parts[0]
1283
+ case bsqual
959
1284
  when ""
960
- parts[1].b
1285
+ bsval.b
961
1286
  when "h"
962
- parts[1].gsub(/\s/, "").chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join
1287
+ bsval.gsub(/\s/, "").chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join.b
963
1288
  when "b64"
964
- Base64.urlsafe_decode64(parts[1])
1289
+ Base64.urlsafe_decode64(bsval)
965
1290
  else
966
- warn "*** Can't handle byte string type #{parts[0].inspect} yet"
1291
+ warn "*** Can't handle byte string type #{bsqual.inspect} yet"
967
1292
  end
968
1293
  ]
969
1294
  else
@@ -1073,9 +1398,13 @@ module CDDL
1073
1398
  def g_process(name, g, genericargs)
1074
1399
  r, ids = g
1075
1400
  args = genericargs.children(:type1).map {|x| type1(x, true)}
1401
+ # warn "** args #{args.inspect}"
1076
1402
  fail "number of args #{name} #{ids.size} #{args.size}" if ids.size != args.size
1077
1403
  bindings = Hash[ids.zip(args)]
1078
- r_process("#{name}<#{genericargs._strip}>", r, bindings)
1404
+ # warn "** bindings: #{bindings.inspect}"
1405
+ genname = "#{name}<#{args.inspect}>" # XXX this works up to a RECURSE...
1406
+ # warn "** genname #{genname}"
1407
+ r_process(genname, r, bindings)
1079
1408
  end
1080
1409
 
1081
1410
  def type_recall(name, canbegroup, genericargs)
@@ -1142,7 +1471,7 @@ module CDDL
1142
1471
  end
1143
1472
  when :recurse_grpent # XXX we don't have the entry yet
1144
1473
  when :member
1145
- fail "map entry without member key given: #{member}" unless member[3] || member[4][0] == :grpent || member[4][0] == :grpchoice
1474
+ fail "map entry #{member.inspect} without member key given: #{member}" unless member[3] || member[4][0] == :grpent || member[4][0] == :grpchoice
1146
1475
  else
1147
1476
  fail ["type1 member", member].inspect unless member[0] == :member
1148
1477
  end
@@ -1151,8 +1480,15 @@ module CDDL
1151
1480
 
1152
1481
  BRACE = {"{" => :map, "[" => :array}
1153
1482
  RANGE_EXCLUDE_END = {".." => false, "..." => true}
1154
- SUPPORTED_ANNOTATIONS = [:bits, :size, :regexp, :cbor, :cborseq, :within, :and,
1155
- :default, :lt, :le, :gt, :ge, :eq, :ne]
1483
+ SUPPORTED_ANNOTATIONS = [:bits, :size, :regexp,
1484
+ :cbor, :cborseq, :cbordet, :cborseqdet,
1485
+ :within, :and,
1486
+ :default, :lt, :le, :gt, :ge, :eq, :ne,
1487
+ :feature, :abnf, :abnfb, :det, :cat, :plus,
1488
+ :json, :decimal, :join,
1489
+ :b64u, :"b64u-sloppy", :b64c, :"b64c-sloppy",
1490
+ :b45, :b32, :h32, :hex, :hexlc, :hexuc,
1491
+ ]
1156
1492
 
1157
1493
  def type1(n, canbegroup = false)
1158
1494
  # puts "NVALUE #{n.value.inspect}"
@@ -1214,6 +1550,26 @@ module CDDL
1214
1550
  v
1215
1551
  }
1216
1552
  ]
1553
+ when /\A~/
1554
+ if tn = n.typename
1555
+ s = type_recall(tn.to_s, false, n.genericarg).flatten(1)
1556
+ else
1557
+ fail "No typename in unwrap #{n}"
1558
+ end
1559
+ if s[0] != :type1
1560
+ fail [:UNWRAPs0, s].inspect
1561
+ end
1562
+ case s[1]
1563
+ when :map, :array
1564
+ [:grpent, s[2..-1]]
1565
+ when :prim
1566
+ if s[2] != 6
1567
+ fail [:UNWRAPs2, s].inspect
1568
+ end
1569
+ s[4] # check :type1?
1570
+ else
1571
+ fail [:UNWRAPs1, s].inspect
1572
+ end
1217
1573
  else
1218
1574
  "unimplemented #{n}"
1219
1575
  end