cddlc 0.1.3 → 0.1.5

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
  SHA256:
3
- metadata.gz: c0fb30da3097b44521e7c3fe12d6e061f7d4f64d9faa3c1239c22a8e76b629e5
4
- data.tar.gz: 7cc5c141bd3833d11efb95ab25dd7649666ee6398efb3c01d95dc7cd8a220802
3
+ metadata.gz: aa1feed69996a1e91c43877cfbe3e68c6b935039976ae95db312fef99a0e8bbd
4
+ data.tar.gz: 746c4b175cb78c502ab731e43672841663cfb4a4159bcf387f8f69c9f34e347a
5
5
  SHA512:
6
- metadata.gz: d119b506cf51407c66e882df9bcc59e2019926c3843d8ad5acb0a5dede020f6ecb099284eded1a6cae912db42347edafabb3be79a789d4afd2153015258abe6c
7
- data.tar.gz: b30fb2251c6daaba6fe3c20a85800b64af61a1908a4ab80596891bf88687f7ec2e441649d9f05d4268e24c9a77c0818cd188a8deada0642ba20fc75961445abd
6
+ metadata.gz: c9b58614695995f0336496734a3684992ddd6f7a1be0dfcbc0451f45afde7e78eb2a2f89ce60e9c5e0c09a1c0e7e8856cc5e5658fe3fe78bc22bc08c4897c4d4
7
+ data.tar.gz: d19ca545ca39eb87af227651438755c13475dd75bd29ef11a6a6327aac42e9c4a69fe0e5322a291e7819903d89c4de1296e1605934bce9b154dcb7ef6563c151
data/bin/cddlc CHANGED
@@ -44,7 +44,7 @@ require 'ostruct'
44
44
  $options = OpenStruct.new
45
45
  begin
46
46
  op = OptionParser.new do |opts|
47
- opts.banner = "Usage: cddlc.rb [options] file.cddl"
47
+ opts.banner = "Usage: cddlc.rb [options] [file.cddl... | -]"
48
48
 
49
49
  opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
50
50
  $options.verbose = v
@@ -64,6 +64,17 @@ begin
64
64
  opts.on("-tFMT", "--to=FMT", [:basic, :neat, :json, :yaml, :enum, :cddl], "Target format") do |v|
65
65
  $options.target = v
66
66
  end
67
+ opts.on("-sRULE", "--start=RULE", String, "Start rule name") do |v|
68
+ $options.start = v
69
+ end
70
+ opts.on("-iIMPORT", "--import=IMPORT", String, "Import [namespace=]reference") do |v|
71
+ $options.import ||= []
72
+ $options.import << v
73
+ end
74
+ opts.on("-IINCLUDE", "--include=INCLUDE", String, "Include [namespace=]reference") do |v|
75
+ $options.include ||= []
76
+ $options.include << v
77
+ end
67
78
  end
68
79
  op.parse!
69
80
  rescue Exception => e
@@ -71,11 +82,40 @@ rescue Exception => e
71
82
  exit 1
72
83
  end
73
84
 
85
+ cddl_file = ""
86
+ if $options.start
87
+ cddl_file << "$.start.$ = #{$options.start}\n"
88
+ end
89
+
90
+ [[$options.include, "include"], [$options.import, "import"]].each do |arr, directive|
91
+ if arr
92
+ arr.each do |ref|
93
+ asname, refname = ref.split("=", 2)
94
+ unless refname
95
+ refname = asname
96
+ asname = nil
97
+ end
98
+ cddl_file << ";# #{directive} #{refname}"
99
+ cddl_file << " as #{asname}" if asname
100
+ cddl_file << "\n"
101
+ end
102
+ end
103
+ end
104
+
105
+ if $options.verbose
106
+ warn "::: generated from options:"
107
+ warn cddl_file
108
+ warn ":::\n\n"
109
+ end
110
+
74
111
  if ARGV == []
75
- puts op
76
- exit 1
112
+ if cddl_file == ""
113
+ puts op
114
+ exit 1
115
+ end
116
+ else
117
+ cddl_file << ARGF.read
77
118
  end
78
- cddl_file = ARGF.read
79
119
 
80
120
  cddl = CDDL.from_cddl(cddl_file)
81
121
  result = if $options.rules
data/cddlc.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "cddlc"
3
- s.version = "0.1.3"
3
+ s.version = "0.1.5"
4
4
  s.summary = "CDDL (Concise Data Definition Language) converters and miscellaneous tools"
5
5
  s.description = %q{cddlc implements converters and miscellaneous tools for CDDL, RFC 8610}
6
6
  s.author = "Carsten Bormann"
@@ -15,6 +15,6 @@ Gem::Specification.new do |s|
15
15
 
16
16
  s.add_development_dependency 'bundler', '~>1'
17
17
  s.add_dependency 'treetop', '~>1'
18
- s.add_dependency 'json'
19
- s.add_dependency 'neatjson'
18
+ s.add_dependency 'json', '~>2'
19
+ s.add_dependency 'neatjson', '~>0.10'
20
20
  end
@@ -0,0 +1,213 @@
1
+
2
+ grasp-message = (message .within message-structure) / noop-message
3
+
4
+ message-structure = [MESSAGE_TYPE, session-id, ?initiator,
5
+ *grasp-option]
6
+
7
+ MESSAGE_TYPE = 0..255
8
+ session-id = 0..4294967295 ; up to 32 bits
9
+ grasp-option = any
10
+
11
+
12
+ discovery-message = [M_DISCOVERY, session-id, initiator, objective]
13
+
14
+
15
+ response-message = [M_RESPONSE, session-id, initiator, ttl,
16
+ (+locator-option // divert-option), ?objective]
17
+
18
+ ttl = 0..4294967295 ; in milliseconds
19
+
20
+
21
+ request-negotiation-message = [M_REQ_NEG, session-id, objective]
22
+
23
+ request-synchronization-message = [M_REQ_SYN, session-id, objective]
24
+
25
+
26
+ negotiation-message = [M_NEGOTIATE, session-id, objective]
27
+
28
+
29
+ end-message = [M_END, session-id, accept-option / decline-option]
30
+
31
+
32
+ wait-message = [M_WAIT, session-id, waiting-time]
33
+ waiting-time = 0..4294967295 ; in milliseconds
34
+
35
+
36
+ synch-message = [M_SYNCH, session-id, objective]
37
+
38
+
39
+ flood-message = [M_FLOOD, session-id, initiator, ttl,
40
+ +[objective, (locator-option / [])]]
41
+
42
+ ttl = 0..4294967295 ; in milliseconds
43
+
44
+
45
+ invalid-message = [M_INVALID, session-id, ?any]
46
+
47
+
48
+ noop-message = [M_NOOP]
49
+
50
+
51
+ divert-option = [O_DIVERT, +locator-option]
52
+
53
+
54
+ accept-option = [O_ACCEPT]
55
+
56
+
57
+ decline-option = [O_DECLINE, ?reason]
58
+ reason = text ; optional UTF-8 error message
59
+
60
+
61
+ ipv6-locator-option = [O_IPv6_LOCATOR, ipv6-address,
62
+ transport-proto, port-number]
63
+ ipv6-address = bytes .size 16
64
+
65
+ transport-proto = IPPROTO_TCP / IPPROTO_UDP
66
+ IPPROTO_TCP = 6
67
+ IPPROTO_UDP = 17
68
+ port-number = 0..65535
69
+
70
+
71
+ ipv4-locator-option = [O_IPv4_LOCATOR, ipv4-address,
72
+ transport-proto, port-number]
73
+ ipv4-address = bytes .size 4
74
+
75
+
76
+ fqdn-locator-option = [O_FQDN_LOCATOR, text,
77
+ transport-proto, port-number]
78
+
79
+
80
+ uri-locator-option = [O_URI_LOCATOR, text,
81
+ transport-proto / null, port-number / null]
82
+
83
+
84
+ objective = [objective-name, objective-flags,
85
+ loop-count, ?objective-value]
86
+
87
+ objective-name = text
88
+ objective-value = any
89
+ loop-count = 0..255
90
+
91
+
92
+ objective-flags = uint .bits objective-flag
93
+ objective-flag = &(
94
+ F_DISC: 0 ; valid for discovery
95
+ F_NEG: 1 ; valid for negotiation
96
+ F_SYNCH: 2 ; valid for synchronization
97
+ F_NEG_DRY: 3 ; negotiation is a dry run
98
+ )
99
+
100
+
101
+ grasp-message = (message .within message-structure) / noop-message
102
+
103
+ message-structure = [MESSAGE_TYPE, session-id, ?initiator,
104
+ *grasp-option]
105
+
106
+ MESSAGE_TYPE = 0..255
107
+ session-id = 0..4294967295 ; up to 32 bits
108
+ grasp-option = any
109
+
110
+ message /= discovery-message
111
+ discovery-message = [M_DISCOVERY, session-id, initiator, objective]
112
+
113
+ message /= response-message ; response to Discovery
114
+ response-message = [M_RESPONSE, session-id, initiator, ttl,
115
+ (+locator-option // divert-option), ?objective]
116
+
117
+ message /= synch-message ; response to Synchronization request
118
+ synch-message = [M_SYNCH, session-id, objective]
119
+
120
+ message /= flood-message
121
+ flood-message = [M_FLOOD, session-id, initiator, ttl,
122
+ +[objective, (locator-option / [])]]
123
+
124
+ message /= request-negotiation-message
125
+ request-negotiation-message = [M_REQ_NEG, session-id, objective]
126
+
127
+ message /= request-synchronization-message
128
+ request-synchronization-message = [M_REQ_SYN, session-id, objective]
129
+
130
+ message /= negotiation-message
131
+ negotiation-message = [M_NEGOTIATE, session-id, objective]
132
+
133
+ message /= end-message
134
+ end-message = [M_END, session-id, accept-option / decline-option]
135
+
136
+ message /= wait-message
137
+ wait-message = [M_WAIT, session-id, waiting-time]
138
+
139
+ message /= invalid-message
140
+ invalid-message = [M_INVALID, session-id, ?any]
141
+
142
+ noop-message = [M_NOOP]
143
+
144
+ divert-option = [O_DIVERT, +locator-option]
145
+
146
+ accept-option = [O_ACCEPT]
147
+
148
+ decline-option = [O_DECLINE, ?reason]
149
+ reason = text ; optional UTF-8 error message
150
+
151
+ waiting-time = 0..4294967295 ; in milliseconds
152
+ ttl = 0..4294967295 ; in milliseconds
153
+
154
+ locator-option /= [O_IPv4_LOCATOR, ipv4-address,
155
+ transport-proto, port-number]
156
+ ipv4-address = bytes .size 4
157
+
158
+ locator-option /= [O_IPv6_LOCATOR, ipv6-address,
159
+ transport-proto, port-number]
160
+ ipv6-address = bytes .size 16
161
+
162
+ locator-option /= [O_FQDN_LOCATOR, text, transport-proto,
163
+ port-number]
164
+
165
+ locator-option /= [O_URI_LOCATOR, text,
166
+ transport-proto / null, port-number / null]
167
+
168
+ transport-proto = IPPROTO_TCP / IPPROTO_UDP
169
+ IPPROTO_TCP = 6
170
+ IPPROTO_UDP = 17
171
+ port-number = 0..65535
172
+
173
+ initiator = ipv4-address / ipv6-address
174
+
175
+ objective-flags = uint .bits objective-flag
176
+
177
+ objective-flag = &(
178
+ F_DISC: 0 ; valid for discovery
179
+ F_NEG: 1 ; valid for negotiation
180
+ F_SYNCH: 2 ; valid for synchronization
181
+ F_NEG_DRY: 3 ; negotiation is a dry run
182
+ )
183
+
184
+ objective = [objective-name, objective-flags,
185
+ loop-count, ?objective-value]
186
+
187
+ objective-name = text ; see section "Format of Objective Options"
188
+
189
+ objective-value = any
190
+
191
+ loop-count = 0..255
192
+
193
+ ; Constants for message types and option types
194
+
195
+ M_NOOP = 0
196
+ M_DISCOVERY = 1
197
+ M_RESPONSE = 2
198
+ M_REQ_NEG = 3
199
+ M_REQ_SYN = 4
200
+ M_NEGOTIATE = 5
201
+ M_END = 6
202
+ M_WAIT = 7
203
+ M_SYNCH = 8
204
+ M_FLOOD = 9
205
+ M_INVALID = 99
206
+
207
+ O_DIVERT = 100
208
+ O_ACCEPT = 101
209
+ O_DECLINE = 102
210
+ O_IPv6_LOCATOR = 103
211
+ O_IPv4_LOCATOR = 104
212
+ O_FQDN_LOCATOR = 105
213
+ O_URI_LOCATOR = 106
data/lib/cddlc.rb CHANGED
@@ -39,7 +39,22 @@ class CDDL
39
39
  end])
40
40
  end
41
41
 
42
+ def self.read_from_include_path(fn)
43
+ io = nil
44
+ CDDL::cddl_include_path.each do |path|
45
+ begin
46
+ io = (path + fn).open
47
+ break
48
+ rescue Errno::ENOENT
49
+ next
50
+ end
51
+ end
52
+ io
53
+ end
54
+
42
55
  SAFE_FN = /\A[-._a-zA-Z0-9]+\z/
56
+ IMPINC = /\A(?:import|include)\z/
57
+ IDENTIFIER_RE = /\A[A-Za-z@_$]([-.]*[A-Za-z@_$0-9])*\z/
43
58
 
44
59
  def self.from_cddl(s)
45
60
  ast = @@parser.parse s
@@ -50,92 +65,146 @@ class CDDL
50
65
  directives = s.lines.grep(/^;# /).map(&:chomp).map{|l| l.sub(/^;#\s+/, '').split(/\s+/)}
51
66
  # puts directives.to_yaml
52
67
  end
68
+
53
69
  ret = CDDL.new(ast, directives)
54
70
 
55
71
  if $options.cddl2
56
- ret.directives.each do |di|
57
- preferred_tag = nil
58
- case di
59
- in ["include" => dir, docref]
60
- in ["include" => dir, docref, "as", preferred_tag]
61
- in ["import" => dir, docref]
62
- in ["import" => dir, docref, "as", preferred_tag]
63
- else
64
- warn "** Can't parse include directive #{di.inspect}"
65
- next
66
- end
67
- unless docref =~ SAFE_FN
68
- warn "** skipping unsafe filename #{docref}"
69
- next
70
- end
71
- puts "PREFERRED_TAG #{preferred_tag}" if $options.verbose
72
- puts "DOCREF #{docref}" if $options.verbose
73
- fn = docref.downcase << ".cddl"
74
-
75
- io = nil
76
- CDDL::cddl_include_path.each do |path|
77
- begin
78
- io = (path + fn).open
79
- break
80
- rescue Errno::ENOENT
81
- next
72
+ ret.perform_directives
73
+ end
74
+ ret
75
+ end
76
+
77
+ def perform_directives
78
+ directives.each do |di|
79
+ preferred_tag = nil
80
+ from = nil
81
+ case di
82
+ in [IMPINC => dir, SAFE_FN => docref, "as", SAFE_FN => preferred_tag]
83
+ in [IMPINC => dir, SAFE_FN => docref]
84
+ in [IMPINC => dir, *from, "from", SAFE_FN => docref, "as", SAFE_FN => preferred_tag]
85
+ in [IMPINC => dir, *from, "from", SAFE_FN => docref]
86
+ else
87
+ warn "** Can't parse directive »#{di.join(" ")}«"
88
+ next
89
+ end
90
+ if from
91
+ from = from.map do |name|
92
+ name.chomp!(",")
93
+ if IDENTIFIER_RE === name
94
+ name
95
+ else
96
+ warn "*** invalid identifier #{name.inspect} ignored in »#{di.join(' ')}«"
97
+ nil
82
98
  end
83
- end
84
- unless io
85
- warn "** include file #{fn} not found in #{CDDL::cddl_include_path.map(&:to_s)}"
86
- next
87
- end
99
+ end.compact
100
+ end
101
+ puts "FROM #{from.inspect}" if $options.verbose
102
+ puts "PREFERRED_TAG #{preferred_tag.inspect}" if $options.verbose
103
+ puts "DOCREF #{docref.inspect}" if $options.verbose
88
104
 
89
- include_file = io.read
90
- included_cddl = CDDL.from_cddl(include_file)
91
- if preferred_tag
92
- included_cddl = included_cddl.deep_clone # needed?
93
- renamed_names = included_cddl.rules.keys
94
- name_rename = Hash[
95
- renamed_names.map { |o|
96
- n = "#{preferred_tag}.#{o}"
97
- warn "** Warning: renamed name #{n} already in #{fn}" if included_cddl.rules[n]
98
- [o, n]}]
99
- included_cddl.rename(name_rename)
100
- end
105
+ fn = docref.downcase << ".cddl"
106
+ io = CDDL.read_from_include_path(fn)
107
+ unless io
108
+ warn "** #{dir} file #{fn} not found in #{CDDL::cddl_include_path.map(&:to_s)}"
109
+ next
110
+ end
101
111
 
102
- case dir
103
- in "import"
104
- warn "** IMPORTING #{fn}" if $options.verbose
105
- undef_rule = nil
106
- loop do
107
- undef_rule = ret.cddl_undefined # XXX square...
108
- # p undef_rule
109
- got_more = false
110
- undef_rule.each do |name|
111
- if rule = included_cddl.rules[name]
112
- ret.rules[name] = rule
113
- warn "IMPORTED #{name} from #{fn}" if $options.verbose
114
- got_more = true
115
- end
112
+ include_file = io.read
113
+ included_cddl = CDDL.from_cddl(include_file)
114
+ if preferred_tag
115
+ included_cddl = included_cddl.deep_clone # needed?
116
+ renamed_names = included_cddl.rules.keys
117
+ name_rename = Hash[
118
+ renamed_names.map { |o|
119
+ n = "#{preferred_tag}.#{o}"
120
+ warn "** Warning: renamed name #{n} already in #{fn}" if included_cddl.rules[n]
121
+ [o, n]}]
122
+ included_cddl.rename(name_rename)
123
+ end
124
+
125
+ case dir
126
+ in "import"
127
+ warn "** IMPORTING #{from && from.join(", ") << " from "}#{fn}" if $options.verbose
128
+
129
+ must_be_found = false
130
+ to_be_imported = if from
131
+ if preferred_tag
132
+ from.map{ |k|
133
+ if k =~ /\A#{preferred_tag}[.]/
134
+ k
135
+ elsif nn = name_rename[k]
136
+ if old = store_rule(k, v = ["name", nn])
137
+ warn "*** autogenerated rule #{k} = #{v} would overwrite #{old}"
138
+ end
139
+ nn
140
+ else
141
+ warn_error "*** imported rule #{k.inspect} not in #{fn}"
142
+ end
143
+ }.compact
144
+ else
145
+ must_be_found = true
146
+ from
147
+ end
148
+ else
149
+ cddl_undefined
150
+ end
151
+ loop do
152
+ # p to_be_imported
153
+ got_more = false
154
+ to_be_imported.each do |name|
155
+ if rule = included_cddl.rules[name]
156
+ rules[name] = rule
157
+ warn "IMPORTED #{name} from #{fn}" if $options.verbose
158
+ got_more = true
159
+ elsif must_be_found
160
+ warn_error "*** imported rule #{name.inspect} not in #{fn}"
116
161
  end
117
- break unless got_more
118
162
  end
119
- if preferred_tag
120
- undef_rule.each do |name|
121
- warn "** Warning: undefined reference #{name} without namespace prefix is defined in namespaced imported module #{fn}" if name_rename[name]
122
- end
163
+ break unless got_more
164
+ to_be_imported = cddl_undefined # XXX square...
165
+ must_be_found = false
166
+ end
167
+ if preferred_tag
168
+ to_be_imported.each do |name|
169
+ warn "** Warning: undefined reference #{name} without namespace prefix is defined in namespaced imported module #{fn}" if name_rename[name]
170
+ ### instead just add rule...
123
171
  end
124
- in "include"
125
- warn "** INCLUDING #{fn}" if $options.verbose
126
- included_cddl.rules.each do |k, v|
127
- if old = ret.rules[k]
128
- if old != v
129
- warn "** included rule #{k} = #{v} would overwrite #{old}"
130
- end
131
- else
132
- ret.rules[k] = v
133
- end
172
+ end
173
+ in "include"
174
+ if from
175
+ from = Set[*from]
176
+ end
177
+ warn "** INCLUDING #{from && from.join(", ") << " from "}#{fn}" if $options.verbose
178
+ included_cddl.rules.each do |k, v|
179
+ if from
180
+ next unless from.include?(k)
181
+ from.delete(k)
134
182
  end
183
+ if old = store_rule(k, v)
184
+ warn "*** included rule #{k} = #{v} would overwrite #{old}"
185
+ end
186
+ end
187
+ if from && !from.empty?
188
+ warn "*** could not find #{from.join(", ")} in #{fn}"
135
189
  end
136
190
  end
137
191
  end
138
- ret
192
+ end
193
+
194
+ def store_rule(k, v)
195
+ if old = rules[k]
196
+ if old != v
197
+ return old # error
198
+ end
199
+ else
200
+ rules[k] = v
201
+ end
202
+ nil # OK
203
+ end
204
+
205
+ def warn_error(s)
206
+ warn s
207
+ @error = true
139
208
  end
140
209
 
141
210
  attr_accessor :ast, :tree, :directives
@@ -69,12 +69,7 @@ module CDDLGRAMMAR
69
69
  break
70
70
  end
71
71
  end
72
- if s2.empty?
73
- @index = i2
74
- r2 = nil
75
- else
76
- r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
77
- end
72
+ r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
78
73
  s0 << r2
79
74
  end
80
75
  if s0.last
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cddlc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carsten Bormann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-06 00:00:00.000000000 Z
11
+ date: 2023-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -42,30 +42,30 @@ dependencies:
42
42
  name: json
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '2'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '2'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: neatjson
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '0.10'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '0.10'
69
69
  description: cddlc implements converters and miscellaneous tools for CDDL, RFC 8610
70
70
  email: cabo@tzi.org
71
71
  executables:
@@ -78,6 +78,7 @@ files:
78
78
  - data/prelude.cddl
79
79
  - data/rfc8727.cddl
80
80
  - data/rfc8927.cddl
81
+ - data/rfc8990-cleaned.cddl
81
82
  - data/rfc8990.cddl
82
83
  - data/rfc9052.cddl
83
84
  - data/rfc9053.cddl
@@ -122,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
123
  - !ruby/object:Gem::Version
123
124
  version: '0'
124
125
  requirements: []
125
- rubygems_version: 3.4.2
126
+ rubygems_version: 3.4.6
126
127
  signing_key:
127
128
  specification_version: 4
128
129
  summary: CDDL (Concise Data Definition Language) converters and miscellaneous tools