cddl 0.6.2 → 0.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e5369f34b05a1ece8d8f11f3b4a04d704928850e
4
- data.tar.gz: a35dff8b713d0a6baca7bca0554fdfad498fd490
3
+ metadata.gz: 7ed6608fc505b18aec7d8e30bac4b1faca1265da
4
+ data.tar.gz: 46a78ea14fc09cc01a56ca63320e2f8a9de175d3
5
5
  SHA512:
6
- metadata.gz: 8d21bb24a81af9f70f698e97f38e96fa3ae37bc904841f3f29abf246b013a450816a28612729e0b82c0505a376603330156c7666e943e39ad296422c581cc818
7
- data.tar.gz: 086513c70f53d7b21e7e8447f36002aa05cab7e66d1f744711600967bd0fbfad3741d318c00e4696cbbaa8b5e82ff16b440e5dd1b79129fc717c2674b5ff81f0
6
+ metadata.gz: c941909e3cf4064ae0fbcb3f94e8bb54a10da19c294f1b6a7d5152cb0dd653a7ac6652f11bddc3d2a0e30a8679e823585b235acdf3479e660a66ba33b793e069
7
+ data.tar.gz: 2dd6ad6d49ab40dc1ccba874ee722aa3e490075886f8af51255ad33b5656c0b5c009f326727689f36f0ebd2cc7a30944e69209c6340fead7277a8294dc2878ba
data/bin/cddl CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- coding: utf-8 -*-
3
+ # require_relative '../lib/cddl'
3
4
  require 'cddl'
4
5
  require 'cbor-diagnostic'
5
6
  require 'json'
@@ -28,17 +29,44 @@ def parser
28
29
  @parser ||= CDDL::Parser.new(read_arg(ARGV[0]))
29
30
  end
30
31
 
32
+ def my_pp(v)
33
+ if $annotate
34
+ CBOR::PP.pp v
35
+ else
36
+ pp v
37
+ end
38
+ end
39
+
40
+ def my_diag(v)
41
+ if $annotate
42
+ CBOR::PP.pp v
43
+ else
44
+ puts v.cbor_diagnostic
45
+ end
46
+ end
47
+
31
48
  begin
49
+ case ARGV[1]
50
+ when /\A.p/
51
+ $annotate = true
52
+ require_relative '../lib/cbor-pp'
53
+ end
32
54
  case ARGV[1]
33
55
  when /\Ar/ # secret
34
56
  $advanced = true
35
- pp parser.rules
57
+ my_pp parser.rules
36
58
  when /\Ag/
37
59
  n = 1
38
60
  n = ARGV[2].to_i if ARGV[2]
39
61
  n.times do
40
62
  g = parser.generate
41
- puts g.cbor_diagnostic
63
+ if $annotate
64
+ g = g.cbor_clone if ENV["EXPERIMENTAL_ANNOTATE"]
65
+ ann = parser.validate(g)
66
+ # my_pp ann
67
+ g.cbor_add_annotations_from(ann) rescue nil
68
+ end
69
+ my_diag(g)
42
70
  end
43
71
  when /\Aj/
44
72
  n = 1
@@ -50,7 +78,11 @@ begin
50
78
  when /\Av/
51
79
  instance = read_arg(ARGV[2])
52
80
  instance = CBOR.decode(instance.b) rescue JSON.load(instance)
53
- p parser.validate(instance)
81
+ instance = instance.cbor_clone if $annotate && ENV["EXPERIMENTAL_ANNOTATE"]
82
+ ann = parser.validate(instance)
83
+ # my_pp ann
84
+ instance.cbor_add_annotations_from(ann) rescue nil
85
+ my_diag(instance) if $annotate
54
86
  else
55
87
  usage
56
88
  end
data/cddl.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  spec = Gem::Specification.new do |s|
2
2
  s.name = 'cddl'
3
- s.version = '0.6.2'
3
+ s.version = '0.6.3'
4
4
  s.summary = "CDDL generator and validator."
5
5
  s.description = %{A parser, generator, and validator for CDDL}
6
6
  s.add_dependency('cbor-diag')
data/data/prelude.cddl CHANGED
@@ -11,7 +11,7 @@ bytes = bstr
11
11
  tstr = #3
12
12
  text = tstr
13
13
 
14
- tdate = #6.0(tstr)
14
+ tdate = #6.0(tstr .regexp "\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2]\\d|3[0-1])[Tt]([0-1]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)(\\.\\d+)?([Zz]|[+\\-](?:[0-1]\\d|2[0-3]):[0-5]\\d)")
15
15
  time = #6.1(number)
16
16
  number = int / float
17
17
  biguint = #6.2(bstr)
@@ -0,0 +1,13 @@
1
+ require_relative "cbor-pp"
2
+
3
+ a = [1, {foo: "bar", busel: "basel", baz: "bass", ant: "cat", bat: "dog", eel: "fox"},
4
+ 2, 3, 4, [3]*55, "foo".b, 0.00006103515625, 0.0000099, 1e-7, nil]
5
+ a[1].cbor_annotation_add "fasel"
6
+ CBOR::PP.pp a
7
+
8
+ a = [1, "baz", 3]
9
+ a.cbor_annotation_add "foo"
10
+ a[1].cbor_annotation_add "bar"
11
+ CBOR::PP.pp(a)
12
+
13
+ p 1.cbor_annotation_format
data/lib/cbor-pp.rb ADDED
@@ -0,0 +1,180 @@
1
+ require 'prettyprint'
2
+ require 'cbor-diagnostic'
3
+ require 'delegate'
4
+
5
+ class CBOR::PP < PrettyPrint
6
+ # Outputs +obj+ to +out+ in pretty printed format of
7
+ # +width+ columns in width.
8
+ #
9
+ # If +out+ is omitted, <code>$></code> is assumed.
10
+ # If +width+ is omitted, 79 is assumed.
11
+ #
12
+ # CBOR::PP.pp returns +out+.
13
+ def self.pp(obj, out=$>, width=79)
14
+ q = new(out, width)
15
+ q.pp obj
16
+ q.flush
17
+ #$pp = q
18
+ out << "\n"
19
+ end
20
+
21
+ module PPMethods
22
+ # Adds +obj+ to the pretty printing buffer
23
+ # using Object#cbor_pp.
24
+ def pp(obj)
25
+ group {obj.cbor_pp self}
26
+ end
27
+
28
+ # XXX: a nested group that is broken should not have things added at the end
29
+ def comma_breakable
30
+ text ','
31
+ fill_breakable
32
+ end
33
+
34
+ def seplist(list, sep=nil, iter_method=:each) # :yield: element
35
+ sep ||= lambda { comma_breakable }
36
+ first = true
37
+ list.__send__(iter_method) {|*v|
38
+ if first
39
+ first = false
40
+ else
41
+ sep.call
42
+ end
43
+ yield(*v)
44
+ }
45
+ end
46
+
47
+ # A pretty print for a Hash
48
+ def pp_hash(obj, anno)
49
+ s = "#{anno}{"
50
+ group(1, s, '}') {
51
+ seplist(obj, nil, :each_pair) {|k, v|
52
+ group {
53
+ pp k
54
+ text ':'
55
+ group(1) {
56
+ breakable ' '
57
+ pp v
58
+ }
59
+ }
60
+ }
61
+ }
62
+ end
63
+ end
64
+
65
+ include PPMethods
66
+
67
+ module ObjectMixin # :nodoc:
68
+ def cbor_pp(q)
69
+ if @cbor_annotation
70
+ q.text cbor_annotation_format
71
+ end
72
+ q.text cbor_diagnostic
73
+ end
74
+ def cbor_annotation_add(v)
75
+ unless frozen?
76
+ @cbor_annotation ||= []
77
+ @cbor_annotation << v unless @cbor_annotation.include? v
78
+ end
79
+ end
80
+ def cbor_annotation_replace(v)
81
+ @cbor_annotation = [v]
82
+ end
83
+ def cbor_annotations
84
+ @cbor_annotation
85
+ end
86
+ def cbor_annotation_format
87
+ if @cbor_annotation
88
+ "/" << @cbor_annotation.join(", ") << "/ "
89
+ end
90
+ end
91
+ def cbor_add_annotations_from(ann_list)
92
+ _data, anno = ann_list.find{|data, _anno| equal?(data)}
93
+ f = anno.cbor_annotations
94
+ f.each {|a| cbor_annotation_add(a)} if f
95
+ end
96
+ def cbor_clone
97
+ if frozen?
98
+ CBOR::PP::Cloak.new(self)
99
+ else
100
+ self
101
+ end
102
+ end
103
+ def eql?(other)
104
+ if other.respond_to? :__getobj__
105
+ eql? other.__getobj__
106
+ else
107
+ super
108
+ end
109
+ end
110
+ end
111
+
112
+ def self.add_annotations(tree, ann_list)
113
+ tree.cbor_add_annotations_from(ann_list)
114
+ end
115
+
116
+ class Cloak < ::SimpleDelegator
117
+ def class
118
+ @delegate_sd_obj.class
119
+ end
120
+ include CBOR::PP::ObjectMixin
121
+ end
122
+ end
123
+
124
+ class Class
125
+ def ===(other)
126
+ if other.respond_to? :__getobj__
127
+ other.__getobj__.kind_of? self
128
+ else
129
+ other.kind_of? self
130
+ end
131
+ end
132
+ end
133
+
134
+ class Array # :nodoc:
135
+ def cbor_pp(q) # :nodoc:
136
+ s = "#{cbor_annotation_format}["
137
+ q.group(1, s, ']') {
138
+ q.seplist(self) {|v|
139
+ q.pp v
140
+ }
141
+ }
142
+ end
143
+ def cbor_add_annotations_from(ann_list)
144
+ super
145
+ each {|m| m.cbor_add_annotations_from(ann_list)}
146
+ end
147
+ def cbor_clone
148
+ map(&:cbor_clone)
149
+ end
150
+ end
151
+
152
+ class Hash # :nodoc:
153
+ def cbor_pp(q) # :nodoc:
154
+ q.pp_hash self, cbor_annotation_format
155
+ end
156
+ def cbor_add_annotations_from(ann_list)
157
+ super
158
+ each {|k, v|
159
+ # k.cbor_add_annotations_from(ann_list)
160
+ v.cbor_add_annotations_from(ann_list)
161
+ }
162
+ end
163
+ def cbor_clone
164
+ to_a.cbor_clone.to_h
165
+ end
166
+ end
167
+
168
+ class Numeric
169
+ def eql?(other)
170
+ if other.respond_to? :__getobj__
171
+ eql? other.__getobj__
172
+ else
173
+ super
174
+ end
175
+ end
176
+ end
177
+
178
+ class Object < BasicObject # :nodoc:
179
+ include CBOR::PP::ObjectMixin
180
+ end
data/lib/cddl.rb CHANGED
@@ -377,10 +377,10 @@ module CDDL
377
377
 
378
378
  def validate(d, warn=true)
379
379
  @recursion = 0
380
- result = validate1(d)
380
+ result = validate1a(d, rules)
381
381
  unless result
382
382
  if warn
383
- warn "CDDL validation failure:"
383
+ warn "CDDL validation failure (#{result.inspect} for #{d.inspect}):"
384
384
  PP::pp(validate_diag, STDERR)
385
385
  end
386
386
  end
@@ -395,60 +395,72 @@ module CDDL
395
395
  end
396
396
 
397
397
  def validate_forward(d, start, where)
398
+ # warn ["valforw", d, start, where].inspect
398
399
  i = 0
400
+ ann = []
399
401
  where[1..-1].each { |r|
400
402
  t, s, e, _k, v = r # XXX
401
403
  if t == :recurse_grpent
402
404
  rule = lookup_recurse_grpent(s)
403
- n = validate_linear(d, start+i, rule)
404
- return false unless n
405
+ n, ann2 = validate_linear(d, start+i, rule)
406
+ return [false, ann] unless n
405
407
  i += n
408
+ ann.concat(ann2)
406
409
  elsif t == :grpchoice
407
- return false unless r[1..-1].any? {|cand|
408
- if n = validate_forward(d, start+i, [:foo, *cand])
410
+ return [false, ann] unless r[1..-1].any? {|cand|
411
+ n, ann2 = validate_forward(d, start+i, [:foo, *cand])
412
+ if n
409
413
  i += n
414
+ ann.concat(ann2)
410
415
  end}
411
416
  else
412
417
  fail r.inspect unless t == :member
413
418
  occ = 0
414
- while ((occ < e) && i != d.size && (n = validate_linear(d, start+i, v)))
419
+ while ((occ < e) && i != d.size && ((n, ann2 = validate_linear(d, start+i, v)); n))
415
420
  i += n
416
421
  occ += 1
422
+ ann.concat(ann2)
417
423
  end
418
424
  if occ < s
419
425
  @last_message = "occur not reached in array #{d} for #{where}"
420
- return false
426
+ return [false, ann]
421
427
  end
422
428
  end
423
429
  }
424
- i
430
+ # warn ["valforw>", i].inspect
431
+ [i, ann]
425
432
  end
426
433
 
427
434
  # returns number of matches or false for breakage
428
435
  def validate_linear(d, start, where)
436
+ # warn ["vallin", d, start, where].inspect
429
437
  fail unless Array === d
430
438
  case where[0]
431
439
  when :grpent
432
440
  # must be inside an array with nested occurrences
433
441
  validate_forward(d, start, where)
434
442
  else
435
- validate1(d[start], where) ? 1 : false
443
+ (ann = validate1a(d[start], where)) ? [1, ann] : [false, ann]
436
444
  end
437
445
  end
438
446
 
439
447
  def map_check(d, d_check, members)
440
- members.all? { |r|
448
+ anno = []
449
+ anno if members.all? { |r|
441
450
  # puts "SUBRULE: #{r.inspect}"
442
- t, s, _e, k, v = r
451
+ t, s, e, k, v = r
443
452
  case t
444
453
  when :recurse_grpent
445
454
  rule = lookup_recurse_grpent(s)
446
- map_check(d, d_check, rule[1..-1])
455
+ if ann2 = map_check(d, d_check, rule[1..-1])
456
+ anno.concat(ann2)
457
+ end
447
458
  when :grpchoice
448
459
  r[1..-1].any? {|cand|
449
460
  cand_d_check = d_check.dup
450
- if map_check(d, cand_d_check, cand)
461
+ if ann2 = map_check(d, cand_d_check, cand)
451
462
  d_check.replace(cand_d_check)
463
+ anno.concat(ann2)
452
464
  end
453
465
  }
454
466
  when :member
@@ -462,8 +474,20 @@ module CDDL
462
474
  fail "member name not known for group entry #{r} in map"
463
475
  end
464
476
  d_check1 = d_check.dup
465
- # XXX this is ignoring occur = [s, _e] FIXME!
466
- return map_check(d, d_check1, entries) && d_check.replace(d_check1)
477
+ occ = 0
478
+ ann2 = []
479
+ while occ < e && (ann3 = map_check(d, d_check1, entries))
480
+ occ += 1
481
+ ann2.concat(ann3)
482
+ end
483
+ if occ >= s
484
+ d_check.replace(d_check1)
485
+ anno.concat(ann2)
486
+ return anno
487
+ else
488
+ # leave some diagnostic breadcrumbs?
489
+ return false
490
+ end
467
491
  end
468
492
  # this is mostly quadratic; let's do the linear thing if possible
469
493
  simple, simpleval = extract_value(k)
@@ -474,7 +498,10 @@ module CDDL
474
498
  if actual == :not_found
475
499
  s == 0 # minimum occurrence must be 0 then
476
500
  else
477
- validate1(actual, v) && d_check.delete(simpleval)
501
+ if (ann2 = validate1a(actual, v)) &&
502
+ d_check.delete(simpleval) {:not_found} != :not_found
503
+ anno.concat(ann2)
504
+ end
478
505
  end
479
506
  else
480
507
  # puts "COMPLEX: #{k.inspect} #{simple.inspect} #{simpleval.inspect}"
@@ -482,7 +509,10 @@ module CDDL
482
509
  ta, keys = keys.partition{ |key| validate1(key, k)}
483
510
  # XXX check ta.size against s/e
484
511
  ta.all? { |val|
485
- validate1(d[val], v) && d_check.delete(val)
512
+ if (ann2 = validate1a(d[val], v)) &&
513
+ d_check.delete(val) {:not_found} != :not_found
514
+ anno.concat(ann2)
515
+ end
486
516
  }
487
517
  end
488
518
  else
@@ -491,42 +521,60 @@ module CDDL
491
521
  }
492
522
  end
493
523
 
494
- def validate1(d, where=rules)
524
+ def validate1a(d, where)
525
+ if ann = validate1(d, where)
526
+ here = [d, where]
527
+ if Array === ann
528
+ [here, *ann]
529
+ else
530
+ [here]
531
+ end
532
+ end
533
+ end
534
+
535
+ def validate1(d, where)
495
536
  # puts "DATA: #{d.inspect}"
496
537
  # puts "RULE: #{where.inspect}"
538
+ # warn ["val1", d, where].inspect
497
539
  @last_data = d
498
540
  @last_rule = where
541
+ ann = nil
499
542
  case where[0]
500
543
  when :type1
501
- where[1..-1].any? {|r| validate1(d, r)}
544
+ if where[1..-1].any? {|r| ann = validate1a(d, r)}
545
+ ann
546
+ end
502
547
  when :map
503
548
  if Hash === d
504
549
  d_check = d.dup
505
- map_check(d, d_check, where[1..-1]) && d_check == {}
550
+ if (ann = map_check(d, d_check, where[1..-1])) && d_check == {}
551
+ ann
552
+ end
506
553
  end
507
554
  when :array
555
+ # warn ["valarr", d, where].inspect
508
556
  if Array === d
509
557
  # validate1 against the record
510
- idx = validate_forward(d, 0, where)
511
- validate_result(idx == d.size) { "cannot complete array #{d} for #{where}" }
558
+ idx, ann = validate_forward(d, 0, where)
559
+ ann if validate_result(idx == d.size) { "cannot complete array #{d} for #{where}" }
512
560
  end
513
561
  when :string, :int, :float
514
562
  _, v = extract_value(where)
515
- d == v
563
+ [] if d == v
516
564
  when :range
517
- where[2] === d && where[1].include?(d)
565
+ [] if where[2] === d && where[1].include?(d)
518
566
  when :anno
519
567
  target = where[2]
520
- if validate1(d, target)
568
+ if ann = validate1a(d, target)
521
569
  control = where[3]
522
570
  case where[1]
523
571
  when :size
524
- validate1(d.bytesize, control)
572
+ ann if validate1(d.bytesize, control)
525
573
  when :bits
526
574
  if String === d
527
575
  d.each_byte.with_index.all? { |b, i|
528
576
  bit = i << 3
529
- 8.times.all? { |nb|
577
+ ann if 8.times.all? { |nb|
530
578
  b[nb] == 0 || validate1(bit+nb, control)
531
579
  }
532
580
  }
@@ -540,10 +588,11 @@ module CDDL
540
588
  end
541
589
  d >>= 1; i += 1
542
590
  end
543
- ok
591
+ ann if ok
544
592
  end
545
593
  end
546
594
  when :regexp
595
+ ann if (
547
596
  if String === d
548
597
  ok, v, vt = extract_value(control)
549
598
  if ok && vt == String
@@ -552,6 +601,7 @@ module CDDL
552
601
  d.match(re)
553
602
  end
554
603
  end
604
+ )
555
605
  else
556
606
  fail "Don't know yet how to validate against #{where}"
557
607
  end
@@ -570,7 +620,7 @@ module CDDL
570
620
  when 3
571
621
  String === d && d.encoding != Encoding::BINARY # cheat
572
622
  when 6
573
- CBOR::Tagged === d && d.tag == where[2] && validate1(d.data, where[3])
623
+ CBOR::Tagged === d && d.tag == where[2] && validate1a(d.data, where[3])
574
624
  when 7
575
625
  t, v = extract_value(where)
576
626
  if t
@@ -594,13 +644,15 @@ module CDDL
594
644
  rule = @stage1[name]
595
645
  if @recursion < MAX_RECURSE
596
646
  @recursion += 1
597
- r = validate1(d, rule)
647
+ r = validate1a(d, rule)
598
648
  @recursion -= 1
599
649
  r
600
650
  else
601
651
  fail "Deep recursion into #{name}: #{rule}, not yet implemented"
602
652
  end
603
653
  else
654
+ @last_message = "Don't know how to validate #{where}"
655
+ false
604
656
  # fail where
605
657
  end
606
658
  end
@@ -671,6 +723,7 @@ module CDDL
671
723
  fail t
672
724
  end}]
673
725
  @bindings.pop
726
+ r.cbor_annotation_add(n) rescue nil # AAA
674
727
  r
675
728
  end
676
729
  end
@@ -729,7 +782,10 @@ module CDDL
729
782
  else
730
783
  t = if nbw = nt.bareword
731
784
  t1 = nbw.type1 # || n.bareword.s.type.type1 # workaround
732
- type1(t1, true)
785
+ rest = type_collect(nt, true)
786
+ s = [:type1, type1(t1, true), *rest]
787
+ # warn "T2: #{s.size} #{s}" -- maybe this should have a parenthesis warning?
788
+ s.size == 2 ? s[1] : s # decapsulate single-element choice
733
789
  else
734
790
  type(nt, true) # type can actually be a group here!
735
791
  end
@@ -767,6 +823,9 @@ module CDDL
767
823
  if occ[0] == 0 && t == [:grpchoice]
768
824
  [] # we won't be able to generate any of those
769
825
  else
826
+ if t[0] == :grpchoice # FIXME: need to package grpchoice into grpent in a member
827
+ t = [:grpent, t]
828
+ end
770
829
  [[:member, *occ, nil, t]]
771
830
  end
772
831
  end
@@ -790,7 +849,7 @@ module CDDL
790
849
  elsif !genericargs && (t = rule_lookup(name, canbegroup))
791
850
  t
792
851
  else
793
- fail "Unknown type #{name} #{genericargs.inspect} #{@bindings} #{@abnf.ast?}"
852
+ fail "Unknown type #{name} #{genericargs.inspect} #{@bindings}" #{@abnf.ast?}"
794
853
  end
795
854
  end
796
855
 
@@ -801,7 +860,7 @@ module CDDL
801
860
  g[1..-1]
802
861
  elsif !genericargs && (g = rule_lookup(name, true))
803
862
  fail "#{name} not a group" unless g[0] == :grpent
804
- g[1..-1]
863
+ g[1..-1] # AAA
805
864
  else
806
865
  fail "Unknown group #{name}"
807
866
  end
@@ -829,6 +888,8 @@ module CDDL
829
888
 
830
889
  end
831
890
  end # XXX should flatten the thing, too
891
+ t = t.dup
892
+ t.cbor_annotation_replace(v.to_s) rescue nil # AAA
832
893
  t
833
894
  else
834
895
  fail [n, n.children].inspect
@@ -911,6 +972,7 @@ module CDDL
911
972
  [:type1, *s.map {|mem|
912
973
  t, _s, _e, _k, v = mem
913
974
  fail "enum #{t.inspect}" unless t == :member
975
+ v.cbor_annotation_add(generate1(_k)) rescue nil # AAA (XXX: what if more than one?)
914
976
  v
915
977
  }
916
978
  ]
@@ -920,9 +982,13 @@ module CDDL
920
982
  end
921
983
  end
922
984
 
985
+ def type_collect(n, canbegroup)
986
+ n.children(:type1).map {|ch| type1(ch, canbegroup)}
987
+ end
988
+
923
989
  def type(n, canbegroup = false)
924
990
  # pp ["nch", n.children]
925
- s = n.children(:type1).map {|ch| type1(ch, canbegroup)}
991
+ s = type_collect(n, canbegroup)
926
992
  if s.size == 1
927
993
  s.first
928
994
  else