pubid 2.0.1 → 2.0.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.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/data/parg/adobe.json +1 -1
  3. data/data/parg/amca.json +1 -1
  4. data/data/parg/ansi.json +1 -1
  5. data/data/parg/api.json +1 -1
  6. data/data/parg/ashrae.json +1 -1
  7. data/data/parg/asme.json +1 -1
  8. data/data/parg/astm.json +1 -1
  9. data/data/parg/bipm.json +1 -1
  10. data/data/parg/bsi.json +1 -1
  11. data/data/parg/ccsds.json +1 -1
  12. data/data/parg/cen_cenelec.json +1 -1
  13. data/data/parg/cie.json +1 -1
  14. data/data/parg/csa.json +1 -1
  15. data/data/parg/ecma.json +1 -1
  16. data/data/parg/etsi.json +1 -1
  17. data/data/parg/evs.json +1 -1
  18. data/data/parg/gost.json +1 -1
  19. data/data/parg/iana.json +1 -1
  20. data/data/parg/idf.json +1 -1
  21. data/data/parg/iec.json +1 -1
  22. data/data/parg/iho.json +1 -0
  23. data/data/parg/isbn.json +1 -1
  24. data/data/parg/itu.json +1 -1
  25. data/data/parg/jcgm.json +1 -1
  26. data/data/parg/jis.json +1 -1
  27. data/data/parg/nist.json +1 -1
  28. data/data/parg/oiml.json +1 -1
  29. data/data/parg/omg.json +1 -1
  30. data/data/parg/plateau.json +1 -1
  31. data/data/parg/tgpp.json +1 -1
  32. data/data/parg/un.json +1 -1
  33. data/lib/pubid/adobe/identifier.rb +1 -1
  34. data/lib/pubid/adobe/parser.rb +2 -113
  35. data/lib/pubid/amca/identifiers/base.rb +1 -1
  36. data/lib/pubid/amca/parser.rb +1 -145
  37. data/lib/pubid/ansi/identifier.rb +1 -1
  38. data/lib/pubid/ansi/parser.rb +1 -82
  39. data/lib/pubid/api/builder.rb +32 -1
  40. data/lib/pubid/api/identifier.rb +1 -1
  41. data/lib/pubid/api/parser.rb +1 -131
  42. data/lib/pubid/ashrae/identifiers/base.rb +1 -1
  43. data/lib/pubid/ashrae/parser.rb +3 -710
  44. data/lib/pubid/asme/builder.rb +6 -4
  45. data/lib/pubid/asme/identifier.rb +1 -2
  46. data/lib/pubid/asme/parser.rb +1 -328
  47. data/lib/pubid/astm/builder.rb +12 -3
  48. data/lib/pubid/astm/identifier.rb +1 -2
  49. data/lib/pubid/astm/parser.rb +1 -227
  50. data/lib/pubid/bipm/identifier.rb +8 -1
  51. data/lib/pubid/bipm/parser.rb +2 -224
  52. data/lib/pubid/bsi/builder.rb +2 -4
  53. data/lib/pubid/bsi/parser.rb +1 -666
  54. data/lib/pubid/bsi/single_identifier.rb +38 -1
  55. data/lib/pubid/calconnect/parser.rb +2 -40
  56. data/lib/pubid/ccsds/identifier.rb +1 -1
  57. data/lib/pubid/ccsds/parser.rb +1 -66
  58. data/lib/pubid/cen_cenelec/identifier.rb +16 -1
  59. data/lib/pubid/cen_cenelec/parser.rb +3 -136
  60. data/lib/pubid/cie/identifier.rb +1 -1
  61. data/lib/pubid/cie/parser.rb +3 -378
  62. data/lib/pubid/conformance.rb +1 -1
  63. data/lib/pubid/csa/builder.rb +41 -18
  64. data/lib/pubid/csa/identifier.rb +12 -4
  65. data/lib/pubid/csa/identifiers/amendment.rb +28 -0
  66. data/lib/pubid/csa/identifiers.rb +1 -0
  67. data/lib/pubid/csa/parser.rb +16 -397
  68. data/lib/pubid/doi/parser.rb +2 -37
  69. data/lib/pubid/easc/builder.rb +1 -1
  70. data/lib/pubid/easc/parser.rb +2 -70
  71. data/lib/pubid/ecma/CLAUDE.md +8 -1
  72. data/lib/pubid/ecma/identifier.rb +13 -8
  73. data/lib/pubid/ecma/parser.rb +2 -54
  74. data/lib/pubid/ecma/renderer.rb +3 -7
  75. data/lib/pubid/errors.rb +18 -13
  76. data/lib/pubid/etsi/builder.rb +8 -5
  77. data/lib/pubid/etsi/identifiers/base.rb +1 -1
  78. data/lib/pubid/etsi/parser.rb +1 -132
  79. data/lib/pubid/evs/identifier.rb +7 -0
  80. data/lib/pubid/evs/parser.rb +1 -8
  81. data/lib/pubid/evs.rb +1 -1
  82. data/lib/pubid/gb/identifier.rb +6 -0
  83. data/lib/pubid/gb/parser.rb +2 -51
  84. data/lib/pubid/gost/identifier.rb +9 -1
  85. data/lib/pubid/gost/parser.rb +4 -81
  86. data/lib/pubid/iala/parser.rb +2 -121
  87. data/lib/pubid/iana/identifier.rb +1 -1
  88. data/lib/pubid/iana/parser.rb +2 -24
  89. data/lib/pubid/identifier.rb +78 -1
  90. data/lib/pubid/idf/builder.rb +42 -0
  91. data/lib/pubid/idf/identifier.rb +1 -1
  92. data/lib/pubid/idf/parser.rb +1 -137
  93. data/lib/pubid/idf.rb +1 -2
  94. data/lib/pubid/iec/builder.rb +158 -50
  95. data/lib/pubid/iec/identifier.rb +1 -1
  96. data/lib/pubid/iec/parser.rb +3 -430
  97. data/lib/pubid/ieee/aiee/identifier.rb +2 -1
  98. data/lib/pubid/ieee/aiee.rb +0 -1
  99. data/lib/pubid/ieee/builder.rb +2 -2
  100. data/lib/pubid/ieee/identifiers/base.rb +1 -1
  101. data/lib/pubid/ieee/ire/identifier.rb +2 -1
  102. data/lib/pubid/ieee/ire.rb +0 -1
  103. data/lib/pubid/ieee/nesc.rb +0 -1
  104. data/lib/pubid/ieee/parser.rb +1 -1257
  105. data/lib/pubid/ietf/identifiers/base.rb +17 -1
  106. data/lib/pubid/ietf/parser.rb +2 -47
  107. data/lib/pubid/ietf/renderer.rb +17 -2
  108. data/lib/pubid/iho/CLAUDE.md +89 -0
  109. data/lib/pubid/iho/builder.rb +4 -1
  110. data/lib/pubid/iho/identifiers/base.rb +28 -2
  111. data/lib/pubid/iho/renderer.rb +8 -0
  112. data/lib/pubid/iho/urn_generator.rb +11 -1
  113. data/lib/pubid/iho/urn_parser.rb +21 -11
  114. data/lib/pubid/iho.rb +0 -1
  115. data/lib/pubid/isbn/identifier.rb +1 -1
  116. data/lib/pubid/isbn/parser.rb +2 -31
  117. data/lib/pubid/iso/builder.rb +1 -1
  118. data/lib/pubid/iso/identifiers/directives.rb +6 -3
  119. data/lib/pubid/iso/identifiers/guide.rb +6 -3
  120. data/lib/pubid/iso/identifiers/international_workshop_agreement.rb +6 -3
  121. data/lib/pubid/iso/normalizer.rb +1 -1
  122. data/lib/pubid/iso/parser.rb +1 -539
  123. data/lib/pubid/iso/supplement_identifier.rb +6 -3
  124. data/lib/pubid/iso/utilities.rb +4 -5
  125. data/lib/pubid/iso.rb +1 -19
  126. data/lib/pubid/itu/builder.rb +36 -20
  127. data/lib/pubid/itu/identifiers/base.rb +15 -2
  128. data/lib/pubid/itu/parser.rb +1 -775
  129. data/lib/pubid/jcgm/builder.rb +35 -0
  130. data/lib/pubid/jcgm/parser.rb +1 -151
  131. data/lib/pubid/jcgm.rb +1 -2
  132. data/lib/pubid/jis/builder.rb +18 -29
  133. data/lib/pubid/jis/identifier.rb +1 -1
  134. data/lib/pubid/jis/parser.rb +1 -128
  135. data/lib/pubid/nist/builder.rb +5 -1
  136. data/lib/pubid/nist/caster.rb +13 -3
  137. data/lib/pubid/nist/identifiers/base.rb +37 -2
  138. data/lib/pubid/nist/identifiers/circular.rb +2 -2
  139. data/lib/pubid/nist/identifiers/circular_supplement.rb +2 -2
  140. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +2 -2
  141. data/lib/pubid/nist/identifiers/crpl_report.rb +2 -2
  142. data/lib/pubid/nist/identifiers/handbook.rb +2 -2
  143. data/lib/pubid/nist/identifiers/internal_report.rb +2 -2
  144. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +2 -2
  145. data/lib/pubid/nist/identifiers/monograph.rb +2 -2
  146. data/lib/pubid/nist/identifiers/report.rb +2 -2
  147. data/lib/pubid/nist/parser.rb +19 -775
  148. data/lib/pubid/nist/router.rb +4 -1
  149. data/lib/pubid/nist/supplement_identifier.rb +2 -2
  150. data/lib/pubid/oasis/parser.rb +2 -16
  151. data/lib/pubid/ogc/parser.rb +2 -26
  152. data/lib/pubid/oiml/identifiers/certification_system.rb +7 -0
  153. data/lib/pubid/oiml/identifiers/dual_published.rb +2 -2
  154. data/lib/pubid/oiml/parser.rb +1 -303
  155. data/lib/pubid/oiml.rb +1 -2
  156. data/lib/pubid/omg/identifier.rb +6 -1
  157. data/lib/pubid/omg/parser.rb +2 -76
  158. data/lib/pubid/parser.rb +5 -4
  159. data/lib/pubid/parsers/mr_string.rb +1 -1
  160. data/lib/pubid/plateau/parser.rb +1 -66
  161. data/lib/pubid/plateau.rb +1 -2
  162. data/lib/pubid/renderers/directives_renderer.rb +1 -1
  163. data/lib/pubid/renderers/iwa_renderer.rb +1 -1
  164. data/lib/pubid/renderers/publisher_stripper.rb +82 -0
  165. data/lib/pubid/renderers/supplement_renderer.rb +1 -1
  166. data/lib/pubid/renderers.rb +1 -0
  167. data/lib/pubid/sae/parser.rb +1 -48
  168. data/lib/pubid/tgpp/identifier.rb +1 -1
  169. data/lib/pubid/tgpp/parser.rb +2 -59
  170. data/lib/pubid/un/identifier.rb +1 -1
  171. data/lib/pubid/un/parser.rb +1 -18
  172. data/lib/pubid/version.rb +1 -1
  173. data/lib/pubid/w3c/parser.rb +2 -27
  174. data/lib/pubid/xsf/identifier.rb +10 -6
  175. data/lib/pubid/xsf/parser.rb +3 -31
  176. data/lib/pubid/xsf/renderer.rb +2 -4
  177. data/lib/pubid.rb +8 -9
  178. metadata +6 -23
  179. data/lib/pubid/ieee/aiee/parser.rb +0 -130
  180. data/lib/pubid/ieee/ire/parser.rb +0 -109
  181. data/lib/pubid/ieee/nesc/parser.rb +0 -155
  182. data/lib/pubid/iho/parser.rb +0 -68
  183. data/lib/pubid/parser/common_parse_methods.rb +0 -13
  184. data/lib/pubid/parser/common_parse_rules.rb +0 -56
  185. data/lib/pubid/parser/grammar.rb +0 -96
data/lib/pubid/plateau.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "pubid"
4
- require "parslet"
5
4
 
6
5
  module Pubid
7
6
  module Plateau
@@ -31,7 +30,7 @@ module Pubid
31
30
 
32
31
  # Apply legacy update_codes normalization first
33
32
  normalized = Core::UpdateCodes.apply(input, :plateau)
34
- parsed = Pubid::Parg::Backend.parse(:plateau, normalized)
33
+ parsed = Pubid::Parg::Model.bound(:plateau, normalized)
35
34
  Builder.build(parsed)
36
35
  end
37
36
 
@@ -3,7 +3,7 @@
3
3
  module Pubid
4
4
  module Renderers
5
5
  class DirectivesRenderer < HumanReadable
6
- def render(context:, with_edition: false)
6
+ def render(context:, with_edition: false, **_opts)
7
7
  parts = []
8
8
  parts << render_publisher_portion(context)
9
9
  num_port = render_number_portion(context)
@@ -3,7 +3,7 @@
3
3
  module Pubid
4
4
  module Renderers
5
5
  class IwaRenderer < HumanReadable
6
- def render(context:, with_edition: false)
6
+ def render(context:, with_edition: false, **_opts)
7
7
  parts = []
8
8
  if @id.typed_stage
9
9
  parts << annotate(@id.typed_stage.render(context:),
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pubid
4
+ module Renderers
5
+ # Removes the leading publisher token from an already-rendered identifier,
6
+ # for `to_s(with_publisher: false)`.
7
+ #
8
+ # relaton#49 asks for an internal form of every identifier: the form an SDO
9
+ # uses when it cites its own documents, with the publisher prefix dropped
10
+ # (`NIST SP 800-113` -> `SP 800-113`). Teaching 40-odd flavor renderers to
11
+ # omit the publisher would mean 40 chances to drift, and a flavor added
12
+ # tomorrow would get nothing. This class works from the OUTSIDE instead,
13
+ # the way {Annotator} does: the identifier names the prefixes its rendering
14
+ # can start with (`Pubid::Identifier#publisher_prefixes`), and the prefix
15
+ # is cut off together with the one separator that follows it.
16
+ #
17
+ # Deliberate limits:
18
+ #
19
+ # * A prefix must be followed by a separator (space, `-`, `/` or `.`), so
20
+ # `ISO` never cuts into `ISOFIX`.
21
+ # * When no prefix matches, or cutting it would leave nothing, the input
22
+ # comes back unchanged: the option is ignored rather than producing a
23
+ # broken identifier.
24
+ class PublisherStripper
25
+ SEPARATOR = %r{\A(?:\s+|[-/.]\s*)}
26
+
27
+ def initialize(identifier)
28
+ @id = identifier
29
+ end
30
+
31
+ # A co-published group is not always joined by `/`: CIE prints
32
+ # `CIE ISO 10916:2024`. The cut therefore repeats while a publisher
33
+ # token still leads, so the group drops whole whatever its joiner.
34
+ # The cap only guards against a cycle; no corpus id has more than three.
35
+ MAX_CUTS = 4
36
+
37
+ # @param rendered [String] the default human rendering of the identifier
38
+ # @return [String] the rendering without its leading publisher token
39
+ def strip(rendered)
40
+ return rendered unless rendered.is_a?(String)
41
+
42
+ candidates = prefixes
43
+ MAX_CUTS.times do
44
+ stripped = cut_once(rendered, candidates)
45
+ break if stripped.equal?(rendered)
46
+
47
+ rendered = stripped
48
+ end
49
+ rendered
50
+ end
51
+
52
+ private
53
+
54
+ def cut_once(rendered, candidates)
55
+ candidates.each do |prefix|
56
+ next unless rendered.start_with?(prefix)
57
+
58
+ rest = rendered[prefix.length..]
59
+ next unless rest.match?(SEPARATOR)
60
+
61
+ stripped = rest.sub(SEPARATOR, "")
62
+ return stripped unless stripped.empty?
63
+ end
64
+
65
+ rendered
66
+ end
67
+
68
+ # Longest first, so `ISO/IEC` wins over `ISO`. Each prefix also comes in
69
+ # its annotated spelling: ISO annotates inside its own renderer, so its
70
+ # string already reads `<span class="publisher">ISO</span> …` when it
71
+ # reaches this class.
72
+ def prefixes
73
+ Array(@id.publisher_prefixes)
74
+ .map { |p| p.to_s.strip }
75
+ .reject(&:empty?)
76
+ .flat_map { |p| [%(<span class="publisher">#{p}</span>), p] }
77
+ .uniq
78
+ .sort_by { |p| -p.length }
79
+ end
80
+ end
81
+ end
82
+ end
@@ -3,7 +3,7 @@
3
3
  module Pubid
4
4
  module Renderers
5
5
  class SupplementRenderer < HumanReadable
6
- def render(context:, with_edition: false)
6
+ def render(context:, with_edition: false, **_opts)
7
7
  parts = []
8
8
 
9
9
  base_str = @id.base.to_s(
@@ -3,6 +3,7 @@
3
3
  module Pubid
4
4
  module Renderers
5
5
  autoload :Annotator, "pubid/renderers/annotator"
6
+ autoload :PublisherStripper, "pubid/renderers/publisher_stripper"
6
7
  autoload :Base, "pubid/renderers/base"
7
8
  autoload :DirectivesRenderer, "pubid/renderers/directives_renderer"
8
9
  autoload :GuideRenderer, "pubid/renderers/guide_renderer"
@@ -1,55 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Sae
7
- class Parser < ::Pubid::Parser::Grammar
8
- rule(:space) { str(" ") }
9
- rule(:digit) { match["0-9"] }
10
- rule(:digits) { digit.repeat(1) }
11
- rule(:letter) { match["A-Z"] }
12
- rule(:colon) { str(":") }
13
-
14
- # Publisher
15
- rule(:publisher) { str("SAE").as(:publisher) }
16
-
17
- # Document types with optional number suffix (e.g., J300, J308A)
18
- rule(:doc_type) do
19
- (
20
- str("AMS") | # Aerospace Material Specification
21
- str("AIR") | # Aerospace Information Report
22
- str("ARP") | # Aerospace Recommended Practice
23
- str("AS") | # Aerospace Standard
24
- str("J") | # SAE Standard (e.g., J300)
25
- str("MA") # Material Advisory
26
- ).as(:type)
27
- end
28
-
29
- # Number with optional letter prefix or suffix
30
- rule(:number) do
31
- ((letter.repeat(1,
32
- 1) >> digits) | (digits >> letter.repeat(0,
33
- 1))).as(:number)
34
- end
35
-
36
- # Year
37
- rule(:year) { digit.repeat(4, 4).as(:year) }
38
-
39
- # Date
40
- rule(:date) { colon >> year }
41
-
42
- # Main identifier
43
- rule(:identifier) do
44
- publisher >> space >> doc_type >> space.maybe >>
45
- number >> date.maybe
46
- end
47
-
48
- root(:identifier)
49
-
50
- def self.parse(input)
51
- new.parse(input)
52
- end
5
+ class Parser
53
6
  end
54
7
  end
55
8
  end
@@ -113,7 +113,7 @@ module Pubid
113
113
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
114
114
  end
115
115
 
116
- parsed = Pubid::Parg::Backend.parse(:tgpp, identifier)
116
+ parsed = Pubid::Parg::Model.bound(:tgpp, identifier)
117
117
  Builder.build(parsed)
118
118
  end
119
119
  end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Tgpp
7
- # Parslet grammar for 3GPP identifiers.
5
+ # Grammar for 3GPP identifiers.
8
6
  #
9
7
  # Form: `[3GPP ]<TR|TS> <NN.NNN>[suffix][-part…][:<release>][/<version>]`
10
8
  # TS 23.207:REL-4/2.0.0
@@ -17,62 +15,7 @@ module Pubid
17
15
  # document alone ("3GPP TS 23.207"), and relaton parses that bare form to
18
16
  # search the index. Only the type and the dotted number core are required,
19
17
  # so "TS" and "TS foo" are still rejected.
20
- class Parser < ::Pubid::Parser::Grammar
21
- rule(:digit) { match["0-9"] }
22
- rule(:digits) { digit.repeat(1) }
23
- rule(:space) { str(" ") }
24
-
25
- # Optional leading publisher token.
26
- rule(:publisher_prefix) { str("3GPP") >> space }
27
-
28
- # Document type.
29
- rule(:type) { (str("TR") | str("TS")).as(:type) }
30
-
31
- # Dotted number core, kept as a string (preserves leading zeros).
32
- rule(:number_core) { (digits >> str(".") >> digits).as(:number) }
33
-
34
- # Letter suffix directly after the number ("U"/"dcs"/"ext"…). Generic so
35
- # future controlled-vocabulary values keep round-tripping.
36
- rule(:suffix) { match["A-Za-z"].repeat(1).as(:suffix) }
37
-
38
- # Hyphen parts.
39
- rule(:part) { str("-") >> digits.as(:part) }
40
- rule(:parts) { part.repeat(0).as(:parts) }
41
-
42
- # Release token, stored verbatim: everything up to the version separator.
43
- # Excludes ":" (as well as "/") so the ":"-delimited URN scheme can always
44
- # encode it — every real release ("REL-4", "Ph1", "UMTS", "Release 2000")
45
- # is colon-free.
46
- #
47
- # A trailing segment shaped exactly like a version is a mistyped "/"
48
- # separator ("TS 23.207:2.0.0"), not a release: no release in the 88,464
49
- # published index rows contains a dot at all. Reject it rather than
50
- # silently misfiling the version as the release — which only became
51
- # reachable once the version segment was made optional.
52
- rule(:release) do
53
- (version_core >> any.absent?).absent? >>
54
- match["^/:"].repeat(1).as(:release)
55
- end
56
-
57
- # Three-part version, kept as a string.
58
- rule(:version_core) do
59
- digits >> str(".") >> digits >> str(".") >> digits
60
- end
61
- rule(:version) { version_core.as(:version) }
62
-
63
- rule(:identifier) do
64
- publisher_prefix.maybe >>
65
- type >> space >>
66
- number_core >> suffix.maybe >> parts >>
67
- (str(":") >> release).maybe >>
68
- (str("/") >> version).maybe
69
- end
70
-
71
- rule(:root) { identifier }
72
-
73
- def self.parse(input)
74
- new.parse(input)
75
- end
18
+ class Parser
76
19
  end
77
20
  end
78
21
  end
@@ -32,7 +32,7 @@ module Pubid
32
32
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
33
33
  end
34
34
 
35
- parsed = Pubid::Parg::Backend.parse(:un, identifier, merge_top_sequence: false)
35
+ parsed = Pubid::Parg::Model.bound(:un, identifier)
36
36
  Builder.build(parsed)
37
37
  end
38
38
  end
@@ -1,25 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Un
7
- class Parser < ::Pubid::Parser::Grammar
8
- rule(:slash) { str("/") }
9
- rule(:un_prefix) { (str("UN") >> str(" ")).maybe }
10
- rule(:token) { match("[A-Z0-9.]").repeat(1) }
11
-
12
- rule(:identifier) do
13
- un_prefix >>
14
- token.as(:token) >>
15
- (slash >> token.as(:token)).repeat(1)
16
- end
17
-
18
- rule(:root) { identifier }
19
-
20
- def self.parse(input)
21
- new.parse(input)
22
- end
5
+ class Parser
23
6
  end
24
7
  end
25
8
  end
data/lib/pubid/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pubid
4
- VERSION = "2.0.1"
4
+ VERSION = "2.0.2"
5
5
  end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module W3c
7
- # Parslet grammar for printed W3C identifiers, e.g.
5
+ # Grammar for printed W3C identifiers, e.g.
8
6
  # "W3C WD-charmod-19991129" (type + code + date)
9
7
  # "W3C NOTE-xml-names" (type + code)
10
8
  # "W3C 2dcontext" (bare code)
@@ -14,30 +12,7 @@ module Pubid
14
12
  # greedily consume the trailing date and make a `.maybe` date rule
15
13
  # unreliable. This is safe: no real W3C code ends in a same-width (4/6/8)
16
14
  # digit run, so the Builder's fixed-width trailing-date test never collides.
17
- class Parser < ::Pubid::Parser::Grammar
18
- rule(:prefix) { str("W3C") >> str(" ") }
19
-
20
- # Known W3C maturity tokens. Order matters for shared prefixes: CRD before
21
- # CR, PER before PR, DNOTE before NOTE.
22
- rule(:type_token) do
23
- str("DNOTE") | str("NOTE") | str("WD") | str("CRD") | str("CR") |
24
- str("REC") | str("PER") | str("PR") | str("SPSD") | str("OBSL")
25
- end
26
-
27
- # A leading token is only a type when immediately followed by "-".
28
- rule(:type_part) { type_token.as(:type) >> str("-") }
29
-
30
- # The remainder: code plus an optional trailing date. Captured whole and
31
- # split in the Builder.
32
- rule(:rest) { any.repeat(1).as(:rest) }
33
-
34
- rule(:identifier) { prefix >> type_part.maybe >> rest }
35
-
36
- rule(:root) { identifier }
37
-
38
- def self.parse(input)
39
- new.parse(input)
40
- end
15
+ class Parser
41
16
  end
42
17
  end
43
18
  end
@@ -30,15 +30,19 @@ module Pubid
30
30
  # publisher name in an identifier — the leading token is "XEP".
31
31
  PUBLISHER = "XSF"
32
32
 
33
- attr_reader :with_publisher
34
-
35
- # Basic string representation. Delegates to renderer; `with_publisher:
36
- # false` yields the bare number.
37
- def to_s(with_publisher: true, **opts)
38
- @with_publisher = with_publisher
33
+ # Basic string representation. Delegates to renderer.
34
+ #
35
+ # `with_publisher:` is accepted and has no effect: `XEP` names the
36
+ # series, and XSF itself cites `XEP 0001`, so the internal form is the
37
+ # external one (see {#publisher_prefixes}).
38
+ def to_s(**opts)
39
39
  render(format: :human, **opts)
40
40
  end
41
41
 
42
+ def publisher_prefixes
43
+ []
44
+ end
45
+
42
46
  # from_hash is the shared polymorphic dispatch on Pubid::Identifier;
43
47
  # XSF_TYPE_MAP remains as the key_value polymorphic_map.
44
48
 
@@ -1,38 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Xsf
7
- # Parslet grammar for XSF identifiers. Two shapes: "XEP NNNN", and the two
8
- # named documents in SPECIAL_NUMBERS.
9
- class Parser < ::Pubid::Parser::Grammar
10
- # The XMPP extensions repository publishes two documents whose name is
11
- # not a number: the editor README and the `xep-xxxx` template. Both reach
12
- # pubid as real primary docids ("XEP README", "XEP xxxx"), so the number
13
- # rule admits these two literals beside the digits. Exact case, and no
14
- # other word: the flavor still rejects "XEP foo".
15
- SPECIAL_NUMBERS = %w[README xxxx].freeze
16
-
17
- rule(:digit) { match["0-9"] }
18
- rule(:digits) { digit.repeat(1) }
19
-
20
- rule(:special_number) do
21
- SPECIAL_NUMBERS.map { |name| str(name) }.reduce(:|)
22
- end
23
-
24
- # "XEP 0001" — publisher token, a single space, then the number kept as
25
- # a string of digits (leading zeros preserved by the builder), or the
26
- # name of one of the two named documents.
27
- rule(:identifier) do
28
- str("XEP") >> str(" ") >> (digits | special_number).as(:number)
29
- end
30
-
31
- rule(:root) { identifier }
32
-
33
- def self.parse(input)
34
- new.parse(input)
35
- end
5
+ # Grammar for XSF identifiers. Two shapes: "XEP NNNN", and the two
6
+ # named documents (README and the four-x placeholder).
7
+ class Parser
36
8
  end
37
9
  end
38
10
  end
@@ -4,8 +4,8 @@ module Pubid
4
4
  module Xsf
5
5
  # Human-readable renderer for XSF identifiers.
6
6
  #
7
- # Produces strings like "XEP 0001". With `with_publisher: false` it emits
8
- # just the bare number ("0001").
7
+ # Produces strings like "XEP 0001". The `XEP` token is the series and is
8
+ # always rendered.
9
9
  #
10
10
  # Registered as the `:human` format in the XSF format registry and invoked
11
11
  # via `render(format: :human)`.
@@ -14,8 +14,6 @@ module Pubid
14
14
 
15
15
  def render(context: nil, **opts)
16
16
  id = @id
17
- return id.number.to_s if id.with_publisher == false
18
-
19
17
  "#{PUBLISHER} #{id.number}"
20
18
  end
21
19
  end
data/lib/pubid.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require "parslet"
5
4
 
6
5
  # Eagerly load the Lutaml::Model patch (disables reference store to avoid
7
6
  # unbounded memory growth during bulk parsing, and adds a default +to_urn+
@@ -114,7 +113,7 @@ module Pubid
114
113
  #
115
114
  # @param string [String] the identifier string
116
115
  # @return [Pubid::Identifier]
117
- # @raise [Parslet::ParseFailed] when no flavor can parse it
116
+ # @raise [Pubid::Errors::ParseError] when no flavor can parse it
118
117
  def parse(string, **opts)
119
118
  Pubid.parse(string, **opts)
120
119
  end
@@ -238,7 +237,7 @@ module Pubid
238
237
  # @param format [Symbol] :auto, :human, :mr_string, or :urn
239
238
  # @return [Identifier] The parsed identifier
240
239
  # @raise [ArgumentError] for a non-String, or one over {MAX_INPUT_LENGTH}
241
- # @raise [Parslet::ParseFailed] when no flavor can parse the string — the
240
+ # @raise [Pubid::Errors::ParseError] when no flavor can parse the string — the
242
241
  # class every flavor's own +parse+ raises (see the parse-failure contract)
243
242
  def self.parse(string, format: :auto)
244
243
  unless string.is_a?(String)
@@ -259,13 +258,13 @@ module Pubid
259
258
  # standing example. Fall through to prefix routing when the MR parse
260
259
  # fails, rather than reporting an MR error for a string that was never
261
260
  # an MR string. A genuine MR failure still surfaces, from the second
262
- # attempt, as Parslet::ParseFailed.
261
+ # attempt, as Pubid::Errors::ParseError.
263
262
  #
264
263
  # Narrow, for the reason spelled out on {parse_by_prefix}: only a parse
265
264
  # failure means "wrong shape". Anything else is a defect and propagates.
266
265
  begin
267
266
  Parsers::MrString.parse(string)
268
- rescue Parslet::ParseFailed
267
+ rescue Pubid::Errors::ParseError
269
268
  parse_by_prefix(string)
270
269
  end
271
270
  when :urn
@@ -358,7 +357,7 @@ module Pubid
358
357
  #
359
358
  # @param string [String] a human-readable identifier
360
359
  # @return [Identifier]
361
- # @raise [Parslet::ParseFailed] when no flavor accepts the string
360
+ # @raise [Pubid::Errors::ParseError] when no flavor accepts the string
362
361
  # @api private
363
362
  def self.parse_by_prefix(string)
364
363
  eager_load_flavors!
@@ -370,7 +369,7 @@ module Pubid
370
369
  (prefix_owners + other_candidates(prefix_owners)).each do |mod|
371
370
  begin
372
371
  parsed = mod.parse(string)
373
- rescue Parslet::ParseFailed => e
372
+ rescue Pubid::Errors::ParseError => e
374
373
  # ONLY a parse failure means "not this flavor's identifier". Every
375
374
  # other exception is a defect in that flavor and must propagate.
376
375
  #
@@ -411,7 +410,7 @@ module Pubid
411
410
  return fallback if fallback
412
411
  raise first_error if first_error
413
412
 
414
- raise Parslet::ParseFailed,
413
+ raise Pubid::Errors::ParseError,
415
414
  "no registered flavor could parse #{string.inspect}"
416
415
  end
417
416
  private_class_method :parse_by_prefix
@@ -474,7 +473,7 @@ module Pubid
474
473
  begin
475
474
  mod.parse(ROUTING_PROBE)
476
475
  true
477
- rescue StandardError, Parslet::ParseFailed
476
+ rescue StandardError, Pubid::Errors::ParseError
478
477
  false
479
478
  end
480
479
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pubid
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-29 00:00:00.000000000 Z
11
+ date: 2026-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.8'
27
- - !ruby/object:Gem::Dependency
28
- name: parslet
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: parsanol
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -112,6 +98,7 @@ files:
112
98
  - data/parg/iec.json
113
99
  - data/parg/ieee.json
114
100
  - data/parg/ietf.json
101
+ - data/parg/iho.json
115
102
  - data/parg/ire.json
116
103
  - data/parg/ire.parg
117
104
  - data/parg/isbn.json
@@ -429,6 +416,7 @@ files:
429
416
  - lib/pubid/csa/composite_identifier.rb
430
417
  - lib/pubid/csa/identifier.rb
431
418
  - lib/pubid/csa/identifiers.rb
419
+ - lib/pubid/csa/identifiers/amendment.rb
432
420
  - lib/pubid/csa/identifiers/base.rb
433
421
  - lib/pubid/csa/identifiers/bundled.rb
434
422
  - lib/pubid/csa/identifiers/canadian_adopted.rb
@@ -627,7 +615,6 @@ files:
627
615
  - lib/pubid/ieee/aiee.rb
628
616
  - lib/pubid/ieee/aiee/builder.rb
629
617
  - lib/pubid/ieee/aiee/identifier.rb
630
- - lib/pubid/ieee/aiee/parser.rb
631
618
  - lib/pubid/ieee/builder.rb
632
619
  - lib/pubid/ieee/compaction.rb
633
620
  - lib/pubid/ieee/components/code.rb
@@ -666,11 +653,9 @@ files:
666
653
  - lib/pubid/ieee/ire.rb
667
654
  - lib/pubid/ieee/ire/builder.rb
668
655
  - lib/pubid/ieee/ire/identifier.rb
669
- - lib/pubid/ieee/ire/parser.rb
670
656
  - lib/pubid/ieee/long_renderer.rb
671
657
  - lib/pubid/ieee/nesc.rb
672
658
  - lib/pubid/ieee/nesc/builder.rb
673
- - lib/pubid/ieee/nesc/parser.rb
674
659
  - lib/pubid/ieee/parser.rb
675
660
  - lib/pubid/ieee/pre_parser.rb
676
661
  - lib/pubid/ieee/project_renderer.rb
@@ -696,6 +681,7 @@ files:
696
681
  - lib/pubid/ietf/urn_generator.rb
697
682
  - lib/pubid/ietf/urn_parser.rb
698
683
  - lib/pubid/iho.rb
684
+ - lib/pubid/iho/CLAUDE.md
699
685
  - lib/pubid/iho/builder.rb
700
686
  - lib/pubid/iho/identifier.rb
701
687
  - lib/pubid/iho/identifiers.rb
@@ -705,7 +691,6 @@ files:
705
691
  - lib/pubid/iho/identifiers/miscellaneous.rb
706
692
  - lib/pubid/iho/identifiers/publication.rb
707
693
  - lib/pubid/iho/identifiers/standard.rb
708
- - lib/pubid/iho/parser.rb
709
694
  - lib/pubid/iho/renderer.rb
710
695
  - lib/pubid/iho/urn_generator.rb
711
696
  - lib/pubid/iho/urn_parser.rb
@@ -941,9 +926,6 @@ files:
941
926
  - lib/pubid/parg/backend.rb
942
927
  - lib/pubid/parg/model.rb
943
928
  - lib/pubid/parser.rb
944
- - lib/pubid/parser/common_parse_methods.rb
945
- - lib/pubid/parser/common_parse_rules.rb
946
- - lib/pubid/parser/grammar.rb
947
929
  - lib/pubid/parsers/base.rb
948
930
  - lib/pubid/parsers/mr_string.rb
949
931
  - lib/pubid/parsers/urn.rb
@@ -970,6 +952,7 @@ files:
970
952
  - lib/pubid/renderers/human_readable.rb
971
953
  - lib/pubid/renderers/iwa_renderer.rb
972
954
  - lib/pubid/renderers/mr_string.rb
955
+ - lib/pubid/renderers/publisher_stripper.rb
973
956
  - lib/pubid/renderers/supplement_renderer.rb
974
957
  - lib/pubid/renderers/urn.rb
975
958
  - lib/pubid/rendering.rb