cddlc 0.4.1 → 0.4.2

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: 0cf26993bc0f0556758da6e5567849461b1b6e2779c02e7a1bf8be89f3466747
4
- data.tar.gz: 6d3a7a675a37815fc23fe4cbbe786698fc996cb6b7bb9cbfec5572bb74d50520
3
+ metadata.gz: f2a8aaeed392227b1d7650ddc62d010a577360f06c3bd07f879dd22d6a2e3ce9
4
+ data.tar.gz: e0db7efbdb77e5cc3f770e6a586ce710414e82419127f7b08302248943f1e045
5
5
  SHA512:
6
- metadata.gz: cbbbd25e3cbd37740b856de841a844ce644ef5efc9a97e9966714810db7fb3b070821774a7da17a0d8e20c2047c591de0579d16ba5a12bf8b399578275862a21
7
- data.tar.gz: 2fb8b0946855f67c1e6a17f13837cf6484bdc69e01e5f353c8815ceb9ca09f1fc42a5ac02c8449d79973ccac7b205b845716c48314668da9f76bbff986a63b16
6
+ metadata.gz: 2b95180d14a2cab74e89e76a753a8519f3aa2b17abc7809ed5b9fd1bbbaf20e2dee87a6635c279b35d89cb754b2e9ac4b9051c4eb15a628ac6accb2ad5f3ccb1
7
+ data.tar.gz: 1ad97e5fc8ac2b5242dacedb9a8521b4ea376c79f54669370d7a1d1156d10ce6ca2b3d9ce2075844d5991dee8d1fa36e17fd9d5a175317f14516e92314396e89
data/cddlc.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "cddlc"
3
- s.version = "0.4.1"
3
+ s.version = "0.4.2"
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"
data/lib/cddlc.rb CHANGED
@@ -321,4 +321,51 @@ class CDDL
321
321
  end
322
322
  @prelude
323
323
  end
324
+
325
+ # In each specific case, use the appropriate subset
326
+ ESCAPED_STRING_MAPPINGS = Hash[[
327
+ ["\x08", "b"],
328
+ ["\x09", "t"],
329
+ ["\x0A", "n"],
330
+ ["\x0C", "f"],
331
+ ["\x0D", "r"],
332
+ ["\x22", "\""],
333
+ ["\x27", "'"],
334
+ ["\x2F", "/"],
335
+ ["\x5C", "\\"],
336
+ ]]
337
+
338
+ # TODO: Enable selecting Unicode-friendly variants
339
+ def escape_string(s, regexp = /[^\n !#-\[\]-~]/)
340
+ s.gsub(regexp) {|ch|
341
+ if m = ESCAPED_STRING_MAPPINGS[ch]
342
+ "\\#{m}"
343
+ elsif (o = ch.ord) < 0x10000
344
+ "\\u#{"%04x" % o}"
345
+ else
346
+ "\\u{#{"%x" % o}}"
347
+ end
348
+ }
349
+ end
350
+
351
+ def escape_byte_string(s)
352
+ escape_string(s, /[^\n -&(-\[\]-~]/)
353
+ end
354
+
355
+ def bytes_escaped(tesc, t)
356
+ if tesc.nil?
357
+ case t
358
+ in ["", text]
359
+ "'" + escape_byte_string(text)
360
+ in [/\Ah\z/i, text]
361
+ "h'" + escape_byte_string(text)
362
+ in [/\Ab64\z/i, text]
363
+ "b64'" + escape_byte_string(text)
364
+ # else error
365
+ end + "'"
366
+ else
367
+ tesc
368
+ end
369
+ end
370
+
324
371
  end
@@ -4166,7 +4166,9 @@ module CDDLGRAMMAR
4166
4166
  end
4167
4167
 
4168
4168
  module Bytes1
4169
- def ast() ["bytes", text_value] end
4169
+ def ast()
4170
+ ["bytes", [elements[0].text_value, elements[2].elements.map(&:ast).join], text_value]
4171
+ end
4170
4172
  end
4171
4173
 
4172
4174
  def _nt_bytes
@@ -4235,6 +4237,30 @@ module CDDLGRAMMAR
4235
4237
  r0
4236
4238
  end
4237
4239
 
4240
+ module BCHAR0
4241
+ def ast() text_value end
4242
+ end
4243
+
4244
+ module BCHAR1
4245
+ def ast() text_value end
4246
+ end
4247
+
4248
+ module BCHAR2
4249
+ def ast() text_value end
4250
+ end
4251
+
4252
+ module BCHAR3
4253
+ def ast() text_value end
4254
+ end
4255
+
4256
+ module BCHAR4
4257
+ def ast() "'" end
4258
+ end
4259
+
4260
+ module BCHAR5
4261
+ def ast() "\n" end
4262
+ end
4263
+
4238
4264
  def _nt_BCHAR
4239
4265
  start_index = index
4240
4266
  if node_cache[:BCHAR].has_key?(index)
@@ -4248,7 +4274,9 @@ module CDDLGRAMMAR
4248
4274
 
4249
4275
  i0 = index
4250
4276
  if has_terminal?(@regexps[gr = '\A[\\ -&]'] ||= Regexp.new(gr), :regexp, index)
4251
- r1 = true
4277
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
4278
+ r1.extend(BCHAR0)
4279
+ r1.extend(BCHAR0)
4252
4280
  @index += 1
4253
4281
  else
4254
4282
  terminal_parse_failure('[\\ -&]')
@@ -4259,7 +4287,9 @@ module CDDLGRAMMAR
4259
4287
  r0 = r1
4260
4288
  else
4261
4289
  if has_terminal?(@regexps[gr = '\A[\\(-\\[]'] ||= Regexp.new(gr), :regexp, index)
4262
- r2 = true
4290
+ r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
4291
+ r2.extend(BCHAR1)
4292
+ r2.extend(BCHAR1)
4263
4293
  @index += 1
4264
4294
  else
4265
4295
  terminal_parse_failure('[\\(-\\[]')
@@ -4270,7 +4300,9 @@ module CDDLGRAMMAR
4270
4300
  r0 = r2
4271
4301
  else
4272
4302
  if has_terminal?(@regexps[gr = '\A[\\]-~]'] ||= Regexp.new(gr), :regexp, index)
4273
- r3 = true
4303
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
4304
+ r3.extend(BCHAR2)
4305
+ r3.extend(BCHAR2)
4274
4306
  @index += 1
4275
4307
  else
4276
4308
  terminal_parse_failure('[\\]-~]')
@@ -4281,6 +4313,8 @@ module CDDLGRAMMAR
4281
4313
  r0 = r3
4282
4314
  else
4283
4315
  r4 = _nt_NONASCII
4316
+ r4.extend(BCHAR3)
4317
+ r4.extend(BCHAR3)
4284
4318
  if r4
4285
4319
  r4 = SyntaxNode.new(input, (index-1)...index) if r4 == true
4286
4320
  r0 = r4
@@ -4292,6 +4326,7 @@ module CDDLGRAMMAR
4292
4326
  else
4293
4327
  if (match_len = has_terminal?("\\'", false, index))
4294
4328
  r6 = instantiate_node(SyntaxNode,input, index...(index + match_len))
4329
+ r6.extend(BCHAR4)
4295
4330
  @index += match_len
4296
4331
  else
4297
4332
  terminal_parse_failure('"\\\\\'"')
@@ -4302,6 +4337,8 @@ module CDDLGRAMMAR
4302
4337
  r0 = r6
4303
4338
  else
4304
4339
  r7 = _nt_CRLF
4340
+ r7.extend(BCHAR5)
4341
+ r7.extend(BCHAR5)
4305
4342
  if r7
4306
4343
  r7 = SyntaxNode.new(input, (index-1)...index) if r7 == true
4307
4344
  r0 = r7
@@ -5148,9 +5185,11 @@ module CDDLGRAMMAR
5148
5185
  r0
5149
5186
  end
5150
5187
 
5151
- end
5152
5188
 
5153
- class CDDLGRAMMARParser < Treetop::Runtime::CompiledParser
5154
- include CDDLGRAMMAR
5189
+ class Parser < Treetop::Runtime::CompiledParser
5190
+ include CDDLGRAMMAR
5191
+ end
5155
5192
  end
5156
5193
 
5194
+ CDDLGRAMMARParser = CDDLGRAMMAR::Parser
5195
+
@@ -28,6 +28,34 @@ class CDDL
28
28
  CDDLC_INVENT = ENV["CDDLC_INVENT"]
29
29
  CDDLC_DEBUG = ENV["CDDLC_DEBUG"]
30
30
 
31
+ FEATURE_REJECT_RE = /\A\^/
32
+ # CDDLC_FEATURE_OK=cbor,^json
33
+ CDDLC_FEATURE_OK, CDDLC_FEATURE_REJECT =
34
+ if ok = ENV["CDDLC_FEATURE_OK"]
35
+ ok.split(/,\s*/)
36
+ .partition{|s| s[0] !~ FEATURE_REJECT_RE}
37
+ .map {|l| Hash[l.map {|feature|
38
+ [feature.sub(FEATURE_REJECT_RE, ''),
39
+ true]}]}
40
+ else
41
+ [{}, {}]
42
+ end
43
+
44
+ REGEXP_FOR_STRING = Hash.new {|h, k|
45
+ h[k] = Regexp.new("\\A(?:#{k})\\z")
46
+ }
47
+
48
+ ABNF_PARSER_FOR_STRING = Hash.new {|h, k|
49
+ grammar = "cddl-t0p--1eve1-f0r--abnf = " << k # XXX
50
+ h[k] = ABNF.from_abnf(grammar)
51
+ }
52
+
53
+ ABNF_ENCODING_FOR_CONOP = {
54
+ ".abnf" => Encoding::UTF_8,
55
+ ".abnfb" => Encoding::BINARY
56
+ }
57
+
58
+
31
59
  # library:
32
60
 
33
61
  def remove_indentation(s)
@@ -185,47 +213,14 @@ class CDDL
185
213
  item, where, :out_of_range)
186
214
  end
187
215
  end
188
- in ".cat" | ".det"
189
- lhss, lhsv, lhst = extract_value(lhs)
190
- rhss, rhsv, rhst = extract_value(rhs)
191
- if !lhss || !rhss
192
- [false, item, {UNSPECIFIC_STRING_CAT: [op, lhs, rhs]}, []]
193
- elsif [lhsv, rhsv].any? {!(String === _1)}
194
- [false, item, {NON_STRING_CAT: [op, lhs, rhs]}, []]
216
+ in ".cat" | ".det" | ".plus"
217
+ s, v, t = extract_value(where)
218
+ pp [:CAT_DET_PLUS, s, v, t, item, scalar_type(item)] if CDDLC_DEBUG
219
+ if s
220
+ simple_result(s && scalar_type(item) == t && item == v,
221
+ item, where, :no_match)
195
222
  else
196
- if op == ".det"
197
- lhsv = remove_indentation(lhsv)
198
- rhsv = remove_indentation(rhsv)
199
- end
200
- case [lhst, rhst]
201
- in [:text, :text] | [:bytes, :bytes]
202
- result = lhsv + rhsv
203
- in [:bytes, :text]
204
- result = (lhsv + rhsv.b).b
205
- in [:text, :bytes]
206
- result = lhsv + rhsv.force_encoding(Encoding::UTF_8)
207
- unless result.valid_encoding?
208
- return [false, item, {text_encoding_error: [lhst, lhsv, rhst, rhsv]}, []]
209
- end
210
- end
211
- simple_result(scalar_type(item) == lhst && item == result,
212
- item, where, :no_match)
213
- end
214
- in ".plus"
215
- lhss, lhsv, lhst = extract_value(lhs)
216
- rhss, rhsv, rhst = extract_value(rhs)
217
- if !lhss || !rhss
218
- [false, item, {UNSPECIFIC_PLUS: [op, lhs, rhs]}, []]
219
- elsif [lhsv, rhsv].any? {!(Numeric === _1)}
220
- [false, item, {NON_NUMERIC_PLUS: [op, lhs, rhs]}, []]
221
- else
222
- if lhst == :int
223
- result = lhsv + Integer(rhsv)
224
- elsif lhst == :float
225
- result = rbv + rhsv
226
- end
227
- simple_result(item.eql?(result),
228
- item, where, :no_match)
223
+ [false, item, v, []]
229
224
  end
230
225
  in ".size"
231
226
  anno = :lhs
@@ -245,6 +240,7 @@ class CDDL
245
240
  end
246
241
  end
247
242
  in ".bits"
243
+ anno = :lhs
248
244
  r = validate1(item, lhs)
249
245
  if r[0]
250
246
  if String === item
@@ -272,6 +268,60 @@ class CDDL
272
268
  end
273
269
  end
274
270
  end
271
+ in ".default"
272
+ # anno = :lhs
273
+ r = validate1(item, lhs)
274
+ # TO DO
275
+ unless @default_warned
276
+ warn "*** Ignoring .default for now."
277
+ @default_warned = true
278
+ end
279
+ r
280
+ in ".feature"
281
+ r = validate1(item, lhs)
282
+ if r[0]
283
+ nm, det = extract_feature(rhs, d)
284
+ if CDDLC_FEATURE_REJECT[nm]
285
+ [false, item, {:rejected_feature => [nm, det]}, []]
286
+ else
287
+ [true, item, {:accepted_feature => [nm, det]}, []]
288
+ end
289
+ end
290
+
291
+ in ".regexp"
292
+ anno = :lhs
293
+ r = validate1(item, lhs)
294
+ if r[0]
295
+ if String === item
296
+ ok, v, vt = extract_value(rhs)
297
+ if ok && :text == vt
298
+ re = REGEXP_FOR_STRING[v]
299
+ # pp re if CDDLC_DEBUG
300
+ simple_result(item.match(re),
301
+ item, where, :regexp_not_matched)
302
+ end
303
+ end
304
+ end
305
+ in ".abnf" | ".abnfb"
306
+ anno = :lhs
307
+ r = validate1(item, lhs)
308
+ if r[0]
309
+ if String === item
310
+ ok, v, vt = extract_value(rhs)
311
+ # pp [:abnfex, rhs, ok, v, vt] if CDDLC_DEBUG
312
+ if ok && (:text == vt || :bytes == vt)
313
+ begin
314
+ ABNF_PARSER_FOR_STRING[v].validate(
315
+ item.dup.force_encoding(ABNF_ENCODING_FOR_CONOP[op]).codepoints.pack("U*"))
316
+ [true, item, {abnf: [v]}, [r]]
317
+ rescue => e
318
+ # warn "*** #{e}" # XXX
319
+ [false, item, {abnf_not_matched: [v, e.to_s.force_encoding(Encoding::UTF_8)] }, [r]]
320
+ end
321
+ end
322
+ end
323
+ end
324
+
275
325
  else
276
326
  fail [:CONTROL_UNIMPLEMENTED, op, item, where].inspect
277
327
  end
@@ -280,7 +330,6 @@ class CDDL
280
330
  exit 1
281
331
  end || [false, item, {anno => [item, where]}, []]
282
332
  end
283
- end
284
333
 
285
334
  def scalar_type(item)
286
335
  case item
@@ -327,7 +376,27 @@ def biggify(d) # stand-in for real stand-ins
327
376
  CBOR::Tagged.new(t, d == 0 ? "".b : d.digits(256).reverse!.pack("C*"))
328
377
  end
329
378
 
330
- def extract_value(wh) # XXX complete: .cat/plus/det
379
+ def extract_bytes(bsqual, bsval)
380
+ if bsqual == ""
381
+ bsval.b
382
+ else
383
+ bsclean = bsval.gsub(/\s/, "")
384
+ case bsqual
385
+ in /\Ah\z/i
386
+ bsclean.chars.each_slice(2).map{ |x| Integer(x.join, 16).chr("BINARY") }.join.b
387
+ in /\Ab64\z/i
388
+ begin
389
+ Base64.urlsafe_decode64(bsclean)
390
+ rescue ArgumentError => e
391
+ {base64_error: [bsclean, e.to_s]}
392
+ end
393
+ else
394
+ warn "*** Can't handle byte string type #{bsqual.inspect} yet"
395
+ end
396
+ end
397
+ end
398
+
399
+ def extract_value(wh)
331
400
  case wh
332
401
  in x if a = SIMPLE_VALUE[x]
333
402
  SIMPLE_VALUE[x]
@@ -335,9 +404,86 @@ def extract_value(wh) # XXX complete: .cat/plus/det
335
404
  [true, Integer(num), :int] rescue [true, Float(num), :float]
336
405
  in ["text", val]
337
406
  [true, val, :text]
338
- in ["bytes", val] # TODO
339
- [true, val, :bytes]
407
+ in ["bytes", val, _orig]
408
+ eb = extract_bytes(*val)
409
+ if String === eb
410
+ [true, extract_bytes(*val), :bytes]
411
+ else
412
+ [false, eb]
413
+ end
414
+ in ["op", ".cat" | ".det" | ".plus", lhs, rhs]
415
+ op = wh[1]
416
+ expected_type = op == ".plus" ? Numeric : String;
417
+ lhss, lhsv, lhst = extract_value(lhs)
418
+ rhss, rhsv, rhst = extract_value(rhs)
419
+ if !lhss || !rhss
420
+ [false, {%i'UNSPECIFIC_#{op}' => [op, lhs, rhs]}]
421
+ elsif [lhsv, rhsv].any? {!(expected_type === _1)}
422
+ [false, {%i'BAD_TYPES_#{op}' => [op, lhs, rhs]}]
423
+ else
424
+ if op == ".det"
425
+ lhsv = remove_indentation(lhsv)
426
+ rhsv = remove_indentation(rhsv)
427
+ end
428
+ case [lhst, rhst]
429
+ in [:text, :text] | [:bytes, :bytes]
430
+ [true, lhsv + rhsv]
431
+ in [:bytes, :text]
432
+ [true, (lhsv + rhsv.b).b]
433
+ in [:text, :bytes]
434
+ result = lhsv + rhsv.force_encoding(Encoding::UTF_8)
435
+ if result.valid_encoding?
436
+ [true, result]
437
+ else
438
+ [false, {text_encoding_not_utf8: [lhst, lhsv, rhst, rhsv]}]
439
+ end
440
+ in [:int, _]
441
+ [true, lhsv + Integer(rhsv)]
442
+ in [:float, _]
443
+ [true, lhsv + rhsv]
444
+ end << lhst
445
+ end
340
446
  else
341
447
  [false]
342
448
  end
343
449
  end
450
+
451
+ def extract_array(t)
452
+ case t
453
+ in ["ary", ["seq", *members]]
454
+ else
455
+ return [false]
456
+ end
457
+ [true, *members.map { |el|
458
+ case el
459
+ in ["mem", _cut, _any, el4]
460
+ ok, v, vt = extract_value(el4)
461
+ return [false] unless ok
462
+ [v, vt]
463
+ else
464
+ return [false]
465
+ end
466
+ }]
467
+ end
468
+
469
+
470
+ def extract_feature(control, d)
471
+ ok, v, vt = extract_value(control)
472
+ if ok
473
+ nm = v
474
+ det = d
475
+ warn "*** feature controller should be a string: #{control.inspect}" unless :text == vt || :bytes == vt
476
+ else
477
+ ok, *v = extract_array(control)
478
+ if ok && v.size == 2
479
+ nm = v[0][0]
480
+ det = v[1][0]
481
+ warn "*** first element of feature controller should be a string: #{control.inspect}" unless String === nm
482
+ else
483
+ warn "*** feature controller not implemented: #{control.inspect}"
484
+ end
485
+ end
486
+ [nm, det]
487
+ end
488
+
489
+ end
@@ -2,32 +2,6 @@ require_relative "../cddlc.rb"
2
2
 
3
3
  class CDDL
4
4
 
5
- # In each specific case, use the appropriate subset
6
- ESCAPED_STRING_MAPPINGS = Hash[[
7
- ["\x08", "b"],
8
- ["\x09", "t"],
9
- ["\x0A", "n"],
10
- ["\x0C", "f"],
11
- ["\x0D", "r"],
12
- ["\x22", "\""],
13
- ["\x27", "'"],
14
- ["\x2F", "/"],
15
- ["\x5C", "\\"],
16
- ]]
17
-
18
- # TODO: Enable selecting Unicode-friendly variants
19
- def escape_string(s)
20
- s.gsub(/[^\n !#-\[\]-~]/) {|ch|
21
- if m = ESCAPED_STRING_MAPPINGS[ch]
22
- "\\#{m}"
23
- elsif (o = ch.ord) < 0x10000
24
- "\\u#{"%04x" % o}"
25
- else
26
- "\\u{#{"%x" % o}}"
27
- end
28
- }
29
- end
30
-
31
5
  def write_lhs(k, parmnames)
32
6
  if parmnames
33
7
  "#{k}<#{parmnames.join(", ")}>"
@@ -127,8 +101,8 @@ class CDDL
127
101
  in ["mem", cut, t1, t2]
128
102
  [2, "#{write_rhs(t1, 3, indent, pn)} #{cut ? "^" : ""}=> #{write_rhs(t2, 2, indent, pn)}"]
129
103
  # 2->3: work around cddl tool limitation
130
- in ["bytes", t]
131
- [4, t] # XXX not very clean
104
+ in ["bytes", t, tesc]
105
+ [4, bytes_escaped(tesc, t)]
132
106
  in ["text", t]
133
107
  [4, "\"#{escape_string(t)}\""]
134
108
  in ["number", t]
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cddlc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carsten Bormann
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-02-10 00:00:00.000000000 Z
10
+ date: 2025-02-16 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: bundler