cddl 0.8.26 → 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.
data/lib/cddl.rb CHANGED
@@ -2,10 +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'
6
8
  require 'abnftt'
7
9
  require 'colorize'
8
10
  require 'base64'
11
+ require 'base32'
12
+ require 'base45_lite'
13
+ require 'scanf'
9
14
 
10
15
  module CDDL
11
16
 
@@ -22,6 +27,21 @@ module CDDL
22
27
 
23
28
  class Parser
24
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
+
25
45
  class BackTrack < Exception; end
26
46
 
27
47
  def initialize(source_text)
@@ -42,7 +62,7 @@ module CDDL
42
62
  if upto - presult < 100
43
63
  part2 = source_text[presult...upto]
44
64
  else
45
- part2 = source_text[presult, 50] + "......." + source_text[upto-50, 50]
65
+ part2 = source_text[presult, 50] + "......." + (source_text[upto-50, 50] || "")
46
66
  end
47
67
  warn "*** Look for syntax problems around the #{
48
68
  "%%%".colorize(background: :light_yellow)} markers:\n#{
@@ -99,6 +119,16 @@ module CDDL
99
119
  r.compact
100
120
  end
101
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
+
102
132
  def cname(s)
103
133
  s.to_s.gsub(/-/, "_")
104
134
  end
@@ -130,7 +160,7 @@ module CDDL
130
160
  if subtree[0] == :type1 && subtree[1..-1].all? {|x| x[0] == :int}
131
161
  if enumname = subtree.cbor_annotations rescue nil
132
162
  enumname = cname(enumname.first)
133
- subtree[1..-1].each do |x|
163
+ subtree[1..-1].each do |x|
134
164
  if memname = x.cbor_annotations
135
165
  memname = "#{enumname}_#{cname(memname.first)}"
136
166
  add.(memname, x[1].to_s)
@@ -185,7 +215,8 @@ module CDDL
185
215
  ids = g.children(:id).map(&:to_s)
186
216
  # puts ["ids", ids].inspect
187
217
  if b = @generics[n]
188
- 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)}"
189
220
  end
190
221
  @generics[n] = [rule_ast, ids]
191
222
  else
@@ -197,7 +228,7 @@ module CDDL
197
228
  if a == b
198
229
  warn "*** Warning: Identical redefinition of #{n} as #{a}"
199
230
  else
200
- fail "Duplicate rule definition #{n} as #{b} (was #{a})"
231
+ fail "Duplicate rule definition #{n} #{diff_message(b, a)}"
201
232
  end
202
233
  end
203
234
  @rules[n] = rule_ast
@@ -229,7 +260,7 @@ module CDDL
229
260
  r_process("used_in_cddl_prelude", @rules["used_in_cddl_prelude"])
230
261
  @rules.each do |n, r|
231
262
  # r_process(n, r) # debug only loop
232
- warn "*** Unused rule #{n}" unless @stage1[n]
263
+ warn "*** Unused rule #{n}" unless @stage1[n] || CDDL_UNUSED_OK
233
264
  end
234
265
  if result[0] == :grpent
235
266
  warn "Group at top -- first rule must be a type!"
@@ -252,10 +283,25 @@ module CDDL
252
283
  l.map {|l| l.sub(/^ {0,#{indent}}/, "")}.join
253
284
  end
254
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
+
255
301
  # Memoize a bit here
256
302
 
257
303
  REGEXP_FOR_STRING = Hash.new {|h, k|
258
- h[k] = Regexp.new("\\A#{k}\\z")
304
+ h[k] = Regexp.new("\\A(?:#{k})\\z")
259
305
  }
260
306
 
261
307
  ABNF_PARSER_FOR_STRING = Hash.new {|h, k|
@@ -268,6 +314,12 @@ module CDDL
268
314
  abnfb: Encoding::BINARY
269
315
  }
270
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
+
271
323
  def generate
272
324
  @recursion = 0
273
325
  generate1(rules)
@@ -390,9 +442,17 @@ module CDDL
390
442
  when 3
391
443
  gen_word
392
444
  when 6
393
- 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]))
394
450
  when 7
395
- case where[2]
451
+ w2 = where[2]
452
+ if Array === w2
453
+ w2 = generate1(w2)
454
+ end
455
+ case w2
396
456
  when nil
397
457
  Math::PI
398
458
  when 20
@@ -401,10 +461,22 @@ module CDDL
401
461
  true
402
462
  when 22
403
463
  nil
404
- when 23
405
- :undefined
406
- when 25, 26, 27
407
- 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
408
480
  end
409
481
  else
410
482
  fail "Can't generate prim #{where[1]}"
@@ -459,6 +531,11 @@ module CDDL
459
531
  end
460
532
  generate1(target, inmap)
461
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
462
539
  generate1(target, inmap)
463
540
  when :cat, :det
464
541
  lhs = generate1(target, inmap)
@@ -514,8 +591,7 @@ module CDDL
514
591
  warn "HUH gen #{where.inspect} #{try.inspect}" unless try.nil?
515
592
  end
516
593
  end
517
- 32.times do
518
- content = generate1(target)
594
+ try_generate(target) do |content|
519
595
  if validate1(content, where)
520
596
  return content
521
597
  end
@@ -534,33 +610,107 @@ module CDDL
534
610
  unless !bytes.nil? && String === grammar
535
611
  fail "Don't know yet how to generate #{where}"
536
612
  end
613
+ grammar << "\n" unless grammar =~ /\n\z/
537
614
  out = ABNF_PARSER_FOR_STRING[grammar].generate
538
615
  if conop == :abnfb
539
616
  out = out.codepoints.pack("C*")
540
617
  end
541
618
  enc = bytes ? Encoding::BINARY : Encoding::UTF_8
542
619
  out.force_encoding(enc)
543
- when :cbor, :cborseq
620
+ when :cbor, :cborseq, :cbordet, :cborseqdet
544
621
  unless target == [:prim, 2]
545
622
  fail "Don't know yet how to generate #{where}"
546
623
  end
547
- content = CBOR::encode(generate1(control))
548
- if conop == :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
549
630
  # remove the first head
550
- n = case content.getbyte(0) - (4 << 5)
551
- when 0..23; 1
552
- when 24; 2
553
- when 25; 3
554
- when 26; 5
555
- when 27; 9 # unlikely :-)
556
- else fail "Generated .cborseq sequence for #{where} not an array"
557
- end
558
- content[0...n] = ''
631
+ content = unpack_array_to_sequence(content, where)
632
+ end
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}"
559
649
  end
650
+ content = Integer(generate1(control)).to_s
560
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}"
561
711
  when :within, :and
562
- 32.times do
563
- content = generate1(target)
712
+ try_generate(target) do |content|
713
+ $features ||= Hash.new {|h, k| h[k] = {}}
564
714
  if validate1(content, control)
565
715
  return content
566
716
  elsif conop == :within
@@ -588,22 +738,41 @@ module CDDL
588
738
  end
589
739
  end
590
740
 
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
+
591
748
  VALUE_TYPE = {text: String, bytes: String, int: Integer, float: Float}
592
749
  SIMPLE_VALUE = {
593
750
  [:prim, 7, 20] => [true, false, :bool],
594
751
  [:prim, 7, 21] => [true, true, :bool],
595
752
  [:prim, 7, 22] => [true, nil, :nil],
596
- [:prim, 7, 23] => [true, :undefined, :undefined],
597
753
  }
754
+ SIMPLE_VALUE_SIMPLE = Set[23] + (0..19) + (32..255)
598
755
 
599
756
  def extract_value(t) # []
600
757
  if vt = VALUE_TYPE[t[0]]
601
758
  [true, t[1], vt]
602
- elsif v = SIMPLE_VALUE[t]
603
- 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
604
773
  elsif t[0] == :anno
605
774
  _, conop, target, control = t
606
- # warn ["EXV0", conop, target, control].inspect
775
+ warn ["EXV0", conop, target, control].inspect if ENV["CDDL_TRACE"]
607
776
  if conop == :cat || conop == :plus || conop == :det
608
777
  ok1, v1, vt1 = extract_value(target)
609
778
  ok2, v2, vt2 = extract_value(control)
@@ -635,6 +804,38 @@ module CDDL
635
804
  }]
636
805
  end
637
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
826
+ else
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
835
+ end
836
+ [nm, det]
837
+ end
838
+
638
839
  def validate_diag
639
840
  [@last_data, @last_rule, @last_message]
640
841
  end
@@ -646,9 +847,8 @@ module CDDL
646
847
  if warn
647
848
  if result
648
849
  if $features != {}
649
- ok = ENV["CDDL_FEATURE_OK"] and ok = ok.split(/,\s*/) or ok = []
650
- # warn([:OK, ok, $features].inspect)
651
- features = $features.reject {|k, v| ok.include? k.to_s }
850
+ features = $features.reject {|k, v| CDDL_FEATURE_OK[k.to_s] }
851
+ # warn "@@@ FEAT #{CDDL_FEATURE_OK.inspect} #{CDDL_FEATURE_REJECT.inspect}"
652
852
  warn "** Features potentially used (#$fn): #{features.map {|k, v| "#{k}: #{v.keys}"}.join(", ")}" if features != {}
653
853
  end
654
854
  else
@@ -805,6 +1005,19 @@ module CDDL
805
1005
  }
806
1006
  end
807
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
+
808
1021
  def validate1a(d, where)
809
1022
  if ann = validate1(d, where)
810
1023
  here = [d, where]
@@ -817,6 +1030,8 @@ module CDDL
817
1030
  end
818
1031
 
819
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}
820
1035
 
821
1036
  def validate1(d, where)
822
1037
  if ENV["CDDL_TRACE"]
@@ -827,6 +1042,9 @@ module CDDL
827
1042
  @last_data = d
828
1043
  @last_rule = where
829
1044
  ann = nil
1045
+ if CBOR::Tagged === d && d.tag === 888 && ENV["CDDL_888"]
1046
+ return []
1047
+ end
830
1048
  case where[0]
831
1049
  when :type1
832
1050
  if where[1..-1].any? {|r| ann = validate1a(d, r)}
@@ -863,7 +1081,7 @@ module CDDL
863
1081
  if conop == :cat || conop == :plus || conop == :det
864
1082
  ok1, v1, vt1 = extract_value(target)
865
1083
  ok2, v2, vt2 = extract_value(control)
866
- # warn ["ANNO0", ok1, v1, vt1, ok2, v2, vt2, d].inspect
1084
+ warn ["ANNO0", ok1, v1, vt1, ok2, v2, vt2, d].inspect if ENV["CDDL_TRACE"]
867
1085
  if ok1 && ok2
868
1086
  v2 = Integer(v2) if vt1 == Integer
869
1087
  if conop == :det
@@ -872,6 +1090,30 @@ module CDDL
872
1090
  end
873
1091
  # warn ["ANNO", ok1, v1, vt1, ok2, v2, vt2, d].inspect
874
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
875
1117
  end
876
1118
  elsif ann = validate1a(d, target)
877
1119
  case conop
@@ -914,28 +1156,19 @@ module CDDL
914
1156
  end
915
1157
  ann
916
1158
  when :feature
917
- ok, v, vt = extract_value(control)
918
- if ok
919
- nm = v
920
- det = d
921
- warn "*** feature controller should be a string: #{control.inspect}" unless String == vt
922
- else
923
- ok, *v = extract_array(control)
924
- if ok && v.size == 2
925
- nm = v[0][0]
926
- det = v[1][0]
927
- warn "*** first element of feature controller should be a string: #{control.inspect}" unless String === nm
928
- else
929
- warn "*** feature controller not implemented: #{control.inspect}"
930
- end
931
- end
1159
+ nm, det = extract_feature(control, d)
932
1160
  $features[nm][det] = true
933
- ann
1161
+ ann if !CDDL_FEATURE_REJECT[nm]
934
1162
  when :lt, :le, :gt, :ge, :eq, :ne
935
1163
  op = OPERATORS[conop]
936
1164
  ok, v, _vt = extract_value(control)
937
1165
  if ok
938
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
939
1172
  end
940
1173
  when :regexp
941
1174
  ann if (
@@ -970,6 +1203,159 @@ module CDDL
970
1203
  ann if validate1((CBOR::decode(d) rescue :BAD_CBOR), control)
971
1204
  when :cborseq
972
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
+ )
973
1359
  when :within
974
1360
  if validate1(d, control)
975
1361
  ann
@@ -1005,20 +1391,35 @@ module CDDL
1005
1391
  end
1006
1392
  d = CBOR::Tagged.new(t, d == 0 ? "".b : d.digits(256).reverse!.pack("C*"))
1007
1393
  end
1008
- CBOR::Tagged === d && d.tag == where[2] && validate1a(d.data, where[3])
1394
+ CBOR::Tagged === d && (
1395
+ Integer === where[2] ? d.tag == where[2] : validate1a(d.tag, where[2])
1396
+ ) && validate1a(d.data, where[3])
1009
1397
  when 7
1010
1398
  t, v = extract_value(where)
1011
1399
  if t
1012
1400
  v.eql? d
1013
1401
  else
1014
- case where[2]
1402
+ case w2 = where[2]
1015
1403
  when nil
1016
- # XXX
1017
- 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)
1018
1419
  when 25, 26, 27
1019
- Float === d
1420
+ Float === d && FLOAT_AI_FROM_SIZE[d.to_cbor.size] == w2
1020
1421
  else
1021
- fail
1422
+ fail [:val7, d, where].inspect
1022
1423
  end
1023
1424
  end
1024
1425
  else
@@ -1068,6 +1469,10 @@ module CDDL
1068
1469
  else
1069
1470
  [:type1]
1070
1471
  end
1472
+ elsif s = ENV["CDDL_INVENT"]
1473
+ s = "_" if s == ""
1474
+ r = [:type1, [:text, "#{s}-#{name}"]]
1475
+ return r
1071
1476
  end
1072
1477
  end
1073
1478
  if r
@@ -1113,29 +1518,69 @@ module CDDL
1113
1518
  end
1114
1519
  end
1115
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
+
1116
1533
  def value(n)
1117
1534
  # cheat:
1118
1535
  # warn n
1119
1536
  s = n.to_s
1120
1537
  if s[-1] == "'"
1121
- parts = s.split("'", 3)
1122
- if parts[2] != "" || parts[1] =~ /\\/
1123
- 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}"
1124
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
+ }
1125
1556
  [:bytes,
1126
- case parts[0]
1557
+ case bsqual
1127
1558
  when ""
1128
- parts[1].b
1559
+ bsval.b
1129
1560
  when "h"
1130
- parts[1].gsub(/\s/, "").chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join.b
1561
+ bsval.gsub(/\s/, "").chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join.b
1131
1562
  when "b64"
1132
- Base64.urlsafe_decode64(parts[1])
1563
+ Base64.urlsafe_decode64(bsval)
1133
1564
  else
1134
- warn "*** Can't handle byte string type #{parts[0].inspect} yet"
1565
+ warn "*** Can't handle byte string type #{bsqual.inspect} yet"
1135
1566
  end
1136
1567
  ]
1137
1568
  else
1138
- 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
1139
1584
  # warn val
1140
1585
  case val
1141
1586
  when Integer; [:int, val]
@@ -1241,9 +1686,13 @@ module CDDL
1241
1686
  def g_process(name, g, genericargs)
1242
1687
  r, ids = g
1243
1688
  args = genericargs.children(:type1).map {|x| type1(x, true)}
1689
+ # warn "** args #{args.inspect}"
1244
1690
  fail "number of args #{name} #{ids.size} #{args.size}" if ids.size != args.size
1245
1691
  bindings = Hash[ids.zip(args)]
1246
- 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)
1247
1696
  end
1248
1697
 
1249
1698
  def type_recall(name, canbegroup, genericargs)
@@ -1319,9 +1768,16 @@ module CDDL
1319
1768
 
1320
1769
  BRACE = {"{" => :map, "[" => :array}
1321
1770
  RANGE_EXCLUDE_END = {".." => false, "..." => true}
1322
- SUPPORTED_ANNOTATIONS = [:bits, :size, :regexp, :cbor, :cborseq, :within, :and,
1771
+ SUPPORTED_ANNOTATIONS = [:bits, :size, :regexp,
1772
+ :cbor, :cborseq, :cbordet, :cborseqdet,
1773
+ :within, :and,
1323
1774
  :default, :lt, :le, :gt, :ge, :eq, :ne,
1324
- :feature, :abnf, :abnfb, :det, :cat, :plus]
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
+ ]
1325
1781
 
1326
1782
  def type1(n, canbegroup = false)
1327
1783
  # puts "NVALUE #{n.value.inspect}"
@@ -1353,6 +1809,13 @@ module CDDL
1353
1809
  when /\A#(\d+)/
1354
1810
  maj = $1.to_i
1355
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
1356
1819
  if tagged_type = n.type
1357
1820
  s << type(tagged_type)
1358
1821
  end