rng 0.3.6 → 0.3.7

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: fdcf95250d1a2a74faf391653627fb81dad5c60eba650a206ad9c9cce4a6b638
4
- data.tar.gz: 3ec79ed69ac17cf4a0c7f24c7b4d39b4deaf63c0c2d5868b8579fb29a3ca83ee
3
+ metadata.gz: 64d22a32278b59bf3f6913561453c616ac39c259f4ffded1b200efddc50461af
4
+ data.tar.gz: 1fc2453376a3f7f65aa717beb3707e54ff1bdc751ae528ce8132e3f18720cf2b
5
5
  SHA512:
6
- metadata.gz: af94bb3c3fb3c56877df25ca93a89274664a3e8649524dbc2a6d1b3775541e9c4e6060f5df5eb4851966ad8de23e7fb9e4c70ed8bbf6977bb21578ddaa19dabe
7
- data.tar.gz: e29d6b582c39eac4d0c4245a318c2cbae919e178403d44afe5c10fa271ab5391785d02e7b7819ce3beca6e4992d7ab8684e278858fa5e3dbc8326cb4ac527173
6
+ metadata.gz: dc8735cc2a8a3ae46635100a30ee39e114c762b9bace21955e2d361ad8bcbf65e0a0f10102f81085fea19a13d377ad93e1ac5313c8ae56a3237171712de7f965
7
+ data.tar.gz: 3ac117095690e1065a10921f4093072f151c84454cc0439fcd4a21764978654b8e5963f548969d92d76efb70c9ecca674984aa38fdc3a67cd3bb72c0924d394c
@@ -235,7 +235,8 @@ module Rng
235
235
  obj.map { |o| deep_dup(o) }
236
236
  when Hash
237
237
  obj.each_with_object({}) { |(k, v), h| h[deep_dup(k)] = deep_dup(v) }
238
- when NilClass, Symbol, Numeric, TrueClass, FalseClass
238
+ when NilClass, Symbol, Numeric, TrueClass, FalseClass,
239
+ Lutaml::Model::UninitializedClass # can't dup instance of singleton
239
240
  obj
240
241
  else
241
242
  obj.dup
@@ -51,8 +51,9 @@ module Rng
51
51
  end
52
52
 
53
53
  # Character escapes for strings: \", \\, \n, \r, \t, and RELAX NG class escapes: \i, \c, \d, \w
54
+ # Any other \X sequence is preserved literally (e.g., \+, \-, \. in regex patterns)
54
55
  rule(:char_escape) do
55
- str('\\') >> match('["\\\\ntricdw]').as(:char)
56
+ str('\\') >> (match('["\\\\ntricdw]') | match('[^x]')).as(:char)
56
57
  end
57
58
 
58
59
  # Identifier can contain regular chars, dots, hex escapes, or backslash escapes
@@ -407,7 +408,8 @@ module Rng
407
408
 
408
409
  # Choice is handled at content level, not as separate pattern
409
410
  rule(:content_item) do
410
- annotations.maybe >>
411
+ (doc_comments >> whitespace).maybe >>
412
+ annotations.maybe >>
411
413
  (element_def | attribute_def |
412
414
  # Datatype subtraction: identifier - ( value|identifier|choice|annotated )
413
415
  (identifier.as(:datatype_name) >>
@@ -394,7 +394,7 @@ module Rng
394
394
  elsif part[:char_escape]
395
395
  # Map character escape
396
396
  char = extract_string(part[:char_escape][:char])
397
- CHAR_ESCAPE_MAP[char] || char
397
+ CHAR_ESCAPE_MAP[char] || "\\#{char}"
398
398
  elsif part[:backslash_escape]
399
399
  # Backslash escape in identifier: \x -> x
400
400
  if part[:backslash_escape][:escaped_backslash]
data/lib/rng/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rng
4
- VERSION = '0.3.6'
4
+ VERSION = '0.3.7'
5
5
  end
@@ -415,6 +415,103 @@ RSpec.describe Rng::RncParser do
415
415
  end
416
416
  end
417
417
 
418
+ context 'with named pattern after ## comment' do
419
+ let(:input) do
420
+ <<~RNC
421
+ document =
422
+ element document {
423
+ ## test 1
424
+ test
425
+ }
426
+
427
+ test =
428
+ element sections {
429
+ attribute test { text }?
430
+ }
431
+ RNC
432
+ end
433
+
434
+ let(:grammar) { described_class.parse(input) }
435
+
436
+ it 'parses without error' do
437
+ expect { grammar }.not_to raise_error
438
+ end
439
+
440
+ it 'contains the element document' do
441
+ expect(grammar.define.map(&:name)).to include('document')
442
+ document_def = grammar.define.find { |d| d.name == 'document' }
443
+ expect(document_def.element[0].attr_name).to eq('document')
444
+ end
445
+
446
+ it 'contains the element sections' do
447
+ expect(grammar.define.map(&:name)).to include('test')
448
+ test_def = grammar.define.find { |d| d.name == 'test' }
449
+ expect(test_def.element[0].attr_name).to eq('sections')
450
+ end
451
+ end
452
+
453
+ context 'with escape sequences in string literals' do
454
+ it 'parses known escapes (\\n, \\t, \\r, \\", \\\\)' do
455
+ input = <<~'RNC'
456
+ start = element value {
457
+ xsd:string { pattern = "hello\nworld\ttab\r\n" }
458
+ }
459
+ RNC
460
+ expect { described_class.parse(input) }.not_to raise_error
461
+ end
462
+
463
+ it 'parses RELAX NG class escapes (\\i, \\c, \\d, \\w)' do
464
+ input = <<~'RNC'
465
+ start = element value {
466
+ xsd:string { pattern = "\\i\\c\\d\\w" }
467
+ }
468
+ RNC
469
+ expect { described_class.parse(input) }.not_to raise_error
470
+ end
471
+
472
+ it 'parses unknown escape sequences (\\+, \\-, \\s, \\., \\,) preserving backslash' do
473
+ input = <<~'RNC'
474
+ start = element value {
475
+ xsd:string { pattern = "[\\+\\-]?\\d{4}" }
476
+ }
477
+ RNC
478
+ expect { described_class.parse(input) }.not_to raise_error
479
+ end
480
+
481
+ it 'parses ISO 8601 regex pattern with multiple escape sequences' do
482
+ input = <<~'RNC'
483
+ ISO8601DateTime = xsd:string { pattern = "([\\+\\-]?\\d{4})((-?)((0[1-9]|1[0-2])((-?)([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24:?00)([\\.,]\\d+)?)?((:?)[0-5]\\d([.,]\\d+)?)?([zZ]|([\\+\\-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?)?)?" }
484
+ elem = element elem { ISO8601DateTime }
485
+ RNC
486
+ expect { described_class.parse(input) }.not_to raise_error
487
+ end
488
+
489
+ it 'rejects invalid hex escape \\x{}' do
490
+ # Single-quoted heredoc: \x{} is literal \x{} (one backslash)
491
+ input = <<~'RNC'
492
+ start = element value {
493
+ xsd:string { pattern = "\x{}" }
494
+ }
495
+ RNC
496
+ expect do
497
+ described_class.parse(input)
498
+ end.to raise_error(Parslet::ParseFailed)
499
+ end
500
+
501
+ it 'round-trips unknown escape sequences preserving backslash' do
502
+ rnc_input = <<~'RNC'
503
+ start = element value {
504
+ xsd:string { pattern = "[\\+\\-]?\\d{4}" }
505
+ }
506
+ RNC
507
+ grammar = described_class.parse(rnc_input)
508
+ rnc_output = described_class.to_rnc(grammar)
509
+ expect(rnc_output).to include('\\+')
510
+ expect(rnc_output).to include('\\-')
511
+ expect(rnc_output).to include('\\d')
512
+ end
513
+ end
514
+
418
515
  context 'with except patterns in anyName' do
419
516
  let(:input) do
420
517
  <<~RNC
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rng
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-15 00:00:00.000000000 Z
11
+ date: 2026-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model