cddlc 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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