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
@@ -104,7 +104,10 @@ module Pubid
104
104
 
105
105
  # Check for CS variants (works for both compound "NBS CS" and simple "CS")
106
106
  if series == "CS"
107
- first_num = parsed_hash[:first_number]
107
+ # The composite branch of the split first_number wrapper (the
108
+ # grammar's v#n# branch) arrives under :first_number_composite.
109
+ first_num = parsed_hash[:first_number] ||
110
+ parsed_hash[:first_number_composite]
108
111
 
109
112
  # Check for CSM (monthly) - v#n# pattern inside first_number hash
110
113
  if first_num.is_a?(Hash) && first_num[:volume_number] && first_num[:issue_number]
@@ -30,12 +30,12 @@ module Pubid
30
30
  # and `to_s` wraps its single result. `super` is not reachable from a
31
31
  # private method, so it is handed in as a block.
32
32
  def to_s(format = :short)
33
- annotated = format.is_a?(Hash) ? format[:annotated] : nil
33
+ flags = plain_render_flags(format)
34
34
  format = format[:format] || :short if format.is_a?(Hash)
35
35
 
36
36
  rendered = render_plain(format) { super(format) }
37
37
 
38
- annotate_plain_render(rendered, annotated: annotated)
38
+ annotate_plain_render(rendered, **flags)
39
39
  end
40
40
 
41
41
  private
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Oasis
7
- # Parslet grammar for printed OASIS identifiers, e.g.
5
+ # Grammar for printed OASIS identifiers, e.g.
8
6
  # "OASIS OSLC-CoreShapes-3.0-PS01-Pt8"
9
7
  # "OASIS amqp-core"
10
8
  #
@@ -16,19 +14,7 @@ module Pubid
16
14
  # Builder with order-independent fragment classification. Keeping
17
15
  # decomposition out of the PEG avoids brittle backtracking and guarantees a
18
16
  # lossless round-trip.
19
- class Parser < ::Pubid::Parser::Grammar
20
- rule(:prefix) { str("OASIS") >> str(" ") }
21
-
22
- # The whole slug, captured verbatim (any character, incl. spaces / "]").
23
- rule(:slug) { any.repeat(1).as(:original) }
24
-
25
- rule(:identifier) { prefix >> slug }
26
-
27
- rule(:root) { identifier }
28
-
29
- def self.parse(input)
30
- new.parse(input)
31
- end
17
+ class Parser
32
18
  end
33
19
  end
34
20
  end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Ogc
7
- # Parslet grammar for OGC identifiers.
5
+ # Grammar for OGC identifiers.
8
6
  #
9
7
  # Recognizes `<yy>-<nnn>` optionally followed by a revision suffix that
10
8
  # begins with a letter (r/c/a/R…) and may carry a trailing letter
@@ -12,29 +10,7 @@ module Pubid
12
10
  # leniency, though the canonical printed form omits it.
13
11
  #
14
12
  # Examples: "25-023", "24-032r1", "01-009a", "04-095c1", "OGC 24-032r1".
15
- class Parser < ::Pubid::Parser::Grammar
16
- rule(:space) { match["\\s"].repeat(1) }
17
- rule(:digits) { match["0-9"].repeat(1) }
18
-
19
- # Optional leading publisher token, e.g. "OGC " — consumed, not captured.
20
- rule(:publisher) { (str("OGC") >> space).maybe }
21
-
22
- # Revision suffix: a run of letters/digits that starts after the numeric
23
- # part (which greedily consumes all digits, so the suffix begins with a
24
- # separator letter). Kept as a single token for the builder to normalize.
25
- rule(:revision) { match["A-Za-z0-9"].repeat(1).as(:revision) }
26
-
27
- rule(:identifier) do
28
- publisher >>
29
- digits.as(:year) >> str("-") >> digits.as(:number) >>
30
- revision.maybe >> space.maybe
31
- end
32
-
33
- root(:identifier)
34
-
35
- def self.parse(input)
36
- new.parse(input)
37
- end
13
+ class Parser
38
14
  end
39
15
  end
40
16
  end
@@ -28,6 +28,13 @@ module Pubid
28
28
  def type_string
29
29
  "CS"
30
30
  end
31
+
32
+ # `OIML-CS` names the OIML Certification System as one unit, so
33
+ # `with_publisher: false` drops it whole (`PD-05 Edition 6`) and never
34
+ # leaves a bare `CS`.
35
+ def publisher_prefixes
36
+ ["#{publisher}-#{type_string}", "#{publisher} #{type_string}"]
37
+ end
31
38
  end
32
39
  end
33
40
  end
@@ -127,7 +127,7 @@ module Pubid
127
127
  # which starts with an OIML prefix), parses each side through its
128
128
  # own flavor and returns the wrapper. Returns nil for anything else,
129
129
  # so the caller (Pubid::Oiml.parse) falls through to the ordinary
130
- # grammar, which raises the standard Parslet::ParseFailed for a
130
+ # grammar, which raises the standard Pubid::Errors::ParseError for a
131
131
  # string containing "|" (the grammar defines no rule for it).
132
132
  def self.build(identifier)
133
133
  sides = split_sides(identifier)
@@ -164,7 +164,7 @@ module Pubid
164
164
 
165
165
  def self.parse_side(str, oiml:)
166
166
  oiml ? Oiml.parse(str) : ::Pubid.parse(str)
167
- rescue Parslet::ParseFailed, Pubid::Errors::InvalidInputError
167
+ rescue Pubid::Errors::ParseError, Pubid::Errors::InvalidInputError
168
168
  nil
169
169
  end
170
170
  private_class_method :parse_side
@@ -1,310 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Oiml
7
- class Parser < ::Pubid::Parser::Grammar
8
- include ::Pubid::Parser::CommonParseRules
9
- include ::Pubid::Parser::CommonParseMethods
10
-
11
- root :identifier
12
-
13
- # Additional basic rules not in CommonParseRules
14
- rule(:colon) { str(":") }
15
- rule(:lparen) { str("(") }
16
- rule(:rparen) { str(")") }
17
- rule(:slash) { str("/") }
18
-
19
- # Main identifier pattern - check supplements first
20
- rule(:identifier) do
21
- amendment_identifier | amendment_short | annex_letter_identifier |
22
- annex_identifier | plus_supplement_identifier |
23
- trailing_supplement_identifier | cs_identifier |
24
- bulletin_identifier | base
25
- end
26
-
27
- # Publisher - always "OIML"
28
- rule(:publisher) { str("OIML").as(:publisher) >> space }
29
-
30
- # Document type - single letter. Strict family set: OIML publishes
31
- # R D B G E V S documents (the estate grammar's family letters);
32
- # any other letter is a rejection, not a flavor.
33
- rule(:doc_type) { match("[BDEGRSV]").as(:type) >> space }
34
-
35
- # Bulletin locator — structured form. Year optionally followed by
36
- # 2-digit issue and 2-digit sequence:
37
- # "OIML Bulletin", "OIML Bulletin 1960",
38
- # "OIML Bulletin 1960-03", "OIML Bulletin 1960-03-01"
39
- # All four shapes appear as primary docids in relaton-data-oiml.
40
- rule(:bulletin_date) do
41
- space >> year_digits.as(:year) >>
42
- (dash >> two_digits.as(:issue)).maybe >>
43
- (dash >> two_digits.as(:sequence)).maybe
44
- end
45
-
46
- # Two-digit zero-padded number (used for issue / sequence).
47
- rule(:two_digits) { match('\d').repeat(2, 2) }
48
-
49
- # Roman numeral token composed of I,V,X,L,C,D,M (uppercase, matching
50
- # OIML's print convention). Captured so the builder can cross-check
51
- # the declared volume against the year implied by the article_id.
52
- rule(:roman_numeral) { match("[IVXLCDM]").repeat(1).as(:volume_roman) }
53
-
54
- # Bulletin locator — citation form. The format OIML prints on the
55
- # article page is: "LXVII(2) 20260211" where LXVII is the volume in
56
- # roman numerals, (2) is the issue in arabic without zero padding,
57
- # and 20260211 is the 8-digit oiml.org article id (YYYYNNSS).
58
- rule(:bulletin_citation) do
59
- space >> roman_numeral >>
60
- lparen >> digits.as(:issue_arabic) >> rparen >>
61
- space >> match('\d').repeat(8, 8).as(:article_id)
62
- end
63
-
64
- # Bulletin identifier — no code; the locator (when present) is the
65
- # (year, issue, sequence) tuple drawn from either the structured
66
- # YYYY-II-SS form or the citation VOLUME(ISSUE) ARTID form. Both
67
- # decode to the same record. Tried before base because
68
- # "Bulletin" is a word that the single-letter doc_type rule cannot
69
- # match.
70
- rule(:bulletin_identifier) do
71
- publisher >> str("Bulletin").as(:type) >>
72
- (bulletin_citation | bulletin_date).maybe >>
73
- language_portion.maybe.as(:language)
74
- end
75
-
76
- # Number with optional part and subpart
77
- rule(:number_only) { digits.as(:number) }
78
-
79
- # Part number, optionally a slashed multi-part continuation captured
80
- # verbatim, e.g. "1/-2" in "OIML R 46-1/-2:2012".
81
- rule(:part_number) do
82
- dash >> (digits >> (slash >> dash >> digits).repeat).as(:part)
83
- end
84
-
85
- rule(:subpart_number) { dash >> digits.as(:subpart) }
86
-
87
- # Free-form named/lettered code suffix glued after the numeric part:
88
- # -GUM 1, -special, -sup, -erratum, -A, -ISO3930, -Amend, -Amended_2012
89
- # plus the space-separated "Brochure" label. "GUM <n>" is tried first so
90
- # its trailing number isn't lost to the bare-word alternative. The
91
- # trailing `(_?digits)*` captures joint codes ("ISO3930") and the
92
- # "Amended_2012" amendment label (the hand-off treats these as part
93
- # suffixes, preserved verbatim for round-trip).
94
- rule(:named_suffix) do
95
- (dash >> (
96
- (str("GUM") >> space >> digits) |
97
- (match("[A-Za-z]").repeat(1) >> (str("_").maybe >> digits).repeat)
98
- ).as(:code_suffix)) |
99
- (space >> str("Brochure").as(:code_suffix) >> str("").as(:space_suffix))
100
- end
101
-
102
- rule(:full_number) do
103
- (number_only >> part_number >> subpart_number >> named_suffix.maybe) |
104
- (number_only >> part_number >> named_suffix.maybe) |
105
- (number_only >> named_suffix.maybe)
106
- end
107
-
108
- # Edition number - ordinal numbers
109
- rule(:edition_number) do
110
- (
111
- str("6th") | str("5th") | str("4th") | str("3rd") |
112
- str("2nd") | str("1st") |
113
- (digits >> (str("th") | str("nd") | str("rd") | str("st")))
114
- ).as(:edition)
115
- end
116
-
117
- # Edition text - uppercase or lowercase
118
- rule(:edition_text) { str("Edition") | str("edition") }
119
-
120
- # Edition portion - handles "6th Edition 2015" or "Edition 2013" or ", edition 1992"
121
- rule(:edition_portion) do
122
- (
123
- (str(", ") | space) >>
124
- edition_number.maybe >> space.maybe >> edition_text >> space.maybe >> year_digits.as(:year)
125
- ).as(:edition_format) # Wrap entire match to capture that Edition was used
126
- end
127
-
128
- # Date - year after colon, the VIM's paren-year form ("OIML V 2-200
129
- # (2000)" — space + (YYYY) instead of :YYYY, the shape #342 flags as
130
- # the one real-world spelling that did not parse), OR edition portion
131
- # (with optional space before year)
132
- rule(:date) do
133
- edition_portion |
134
- (space.maybe >> colon >> space.maybe >> year_digits.as(:year)) |
135
- (space.maybe >> lparen >> year_digits.as(:year) >> rparen)
136
- end
137
-
138
- # Draft stage - WD or CD with optional iteration
139
- rule(:stage_iteration) do
140
- (
141
- (digits >> str(".") >> digits) | # 3.1
142
- digits # 1, 2, 3
143
- ).as(:iteration)
144
- end
145
-
146
- rule(:stage_abbr) do
147
- (str("WD") | str("CD")).as(:stage)
148
- end
149
-
150
- rule(:draft_stage) do
151
- space >> stage_iteration.maybe >> stage_abbr
152
- end
153
-
154
- # Language codes
155
- #
156
- # relaton-data-oiml encodes the publication language with its own code
157
- # set (lib/oiml_fetcher.rb DOCID_LANG_CODE): single uppercase letters
158
- # E F D R S C A U X, plus two-letter PO/PT/PE/SR. The two-letter OIML
159
- # codes must be tried before the single-letter rule so e.g. "PE" isn't
160
- # left with a dangling "E".
161
- rule(:lang_single) do
162
- match("[EFRXDSCAU]")
163
- end
164
-
165
- rule(:lang_multi_oiml) do
166
- str("PO") | str("PT") | str("PE") | str("SR")
167
- end
168
-
169
- rule(:lang_multi) do
170
- match("[a-z]").repeat(2, 2) # Two letters: en, fr, etc.
171
- end
172
-
173
- # Full-word language markers as OIML prints them ("(Fra)", "(Eng)"),
174
- # three or more letters, any case. Kept verbatim on the identifier;
175
- # the URN lowercases.
176
- rule(:lang_word) do
177
- match("[A-Za-z]").repeat(3)
178
- end
179
-
180
- rule(:language_code) do
181
- (
182
- (lang_single >> slash >> lang_single) | # E/F
183
- lang_multi_oiml | # PO, PT, PE, SR
184
- lang_word | # Fra, eng, rus — before
185
- # the letter rules: a
186
- # committed "F" of "(Fra)"
187
- # or "fr" of "(fra)" would
188
- # never fall through
189
- lang_single | # E, F, D, R, S, C, A, U, X
190
- lang_multi # en, fr
191
- ).as(:language)
192
- end
193
-
194
- rule(:language_with_space) do
195
- space >> lparen >> language_code >> rparen >> str("").as(:space_before_lang)
196
- end
197
-
198
- rule(:language_without_space) do
199
- lparen >> language_code >> rparen
200
- end
201
-
202
- rule(:language_portion) do
203
- language_with_space | language_without_space
204
- end
205
-
206
- # Amendment identifier - "Amendment (YYYY) to BASE"
207
- rule(:amendment_identifier) do
208
- str("Amendment") >> space >> lparen >> year_digits.as(:year) >> rparen >>
209
- space >> str("to") >> space >>
210
- base_without_language.as(:base) >>
211
- language_portion.maybe.as(:language)
212
- end
213
-
214
- # Short amendment forms - "OIML TYPE NUMBER Amendment Edition YYYY" or "Amendment: YYYY"
215
- rule(:amendment_short) do
216
- publisher >>
217
- doc_type >>
218
- full_number.as(:base_code) >>
219
- space >> str("Amendment").as(:amd_marker) >>
220
- (
221
- (space >> edition_text >> space.maybe >> year_digits.as(:year)).as(:edition_format) |
222
- (colon >> space.maybe >> year_digits.as(:year))
223
- ) >>
224
- language_portion.maybe.as(:language)
225
- end
226
-
227
- # Trailing supplement word - "BASE Amendment" / "BASE Errata" where the
228
- # publication year stays on the base (e.g. "OIML R 138:2009 Amendment").
229
- # amendment_short is tried first; it only matches when a year follows the
230
- # word, so the no-year trailing form falls through to here.
231
- rule(:trailing_supplement_identifier) do
232
- base_without_language.as(:base) >>
233
- space >> (str("Amendment") | str("Errata")).as(:trailing_marker) >>
234
- (space >> digits.as(:number)).maybe >>
235
- language_portion.maybe.as(:language)
236
- end
237
-
238
- # OIML-CS certification-system documents. Two head spellings
239
- # ("OIML-CS" / "OIML CS") and two family-number separators
240
- # ("PD-05" / "PD 05"), an "Edition N" instead of a year, and an
241
- # optional parenthesized trailing amendment - "(Amendment 1)".
242
- rule(:cs_identifier) do
243
- str("OIML").as(:publisher) >>
244
- (dash >> str("CS") | space >> str("CS")).as(:cs_series) >> space >>
245
- (str("PD") | str("OD") | str("CID")).as(:cs_family) >>
246
- (dash | space).as(:cs_separator) >>
247
- digits.as(:number) >>
248
- space >> str("Edition") >> space >> digits.as(:edition) >>
249
- (space >> lparen >> str("Amendment") >> space >>
250
- digits.as(:cs_amendment) >> rparen).maybe
251
- end
252
-
253
- # Plus-joined supplement - "BASE:YEAR+Supplement:YEAR" form where both
254
- # the base and the supplement carry their own year. Used for amendments
255
- # and errata to dated bases (e.g. "OIML B 10:2011+Amendment:2012").
256
- # Annexes already encode the year-on-base intent via their own model.
257
- rule(:plus_supplement_identifier) do
258
- base_without_language.as(:base) >>
259
- str("+") >>
260
- (str("Amendment") | str("Errata")).as(:plus_marker) >>
261
- (colon >> year_digits.as(:year)).maybe >>
262
- language_portion.maybe.as(:language)
263
- end
264
-
265
- # Annex identifier - "BASE Annexes Edition YYYY" / "BASE Annexes:YYYY" /
266
- # "BASE:YYYY Annexes" (year on the base, no annex year).
267
- rule(:annex_identifier) do
268
- base_without_language.as(:base) >>
269
- space >> str("Annexes").as(:annex_marker) >>
270
- (
271
- (space >> edition_text >> space >> year_digits.as(:year)).as(:edition_format) |
272
- (colon >> year_digits.as(:year))
273
- ).maybe >>
274
- language_portion.maybe.as(:language)
275
- end
276
-
277
- # Annex letter or letter range, e.g. "A" or "B-C".
278
- rule(:annex_letter_value) do
279
- (match("[A-Z]") >> (dash >> match("[A-Z]")).maybe).as(:annex_letter)
280
- end
281
-
282
- # Annex with letter - "BASE Annex A Edition YYYY" / "BASE:YYYY Annex B-C"
283
- rule(:annex_letter_identifier) do
284
- base_without_language.as(:base) >>
285
- space >> str("Annex") >> space >> annex_letter_value >>
286
- ((space >> edition_text >> space >> year_digits.as(:year)) | (colon >> year_digits.as(:year))).maybe >>
287
- language_portion.maybe.as(:language)
288
- end
289
-
290
- # Base identifier without language (for use in supplements)
291
- rule(:base_without_language) do
292
- publisher >>
293
- doc_type >>
294
- full_number >>
295
- date.maybe >>
296
- draft_stage.maybe
297
- end
298
-
299
- # Base identifier for recursion and standalone parsing
300
- rule(:base) do
301
- publisher >>
302
- doc_type >>
303
- full_number >>
304
- date.maybe >>
305
- draft_stage.maybe >>
306
- language_portion.maybe
307
- end
5
+ class Parser
308
6
  end
309
7
  end
310
8
  end
data/lib/pubid/oiml.rb CHANGED
@@ -34,10 +34,9 @@ module Pubid
34
34
  return dual if dual
35
35
  end
36
36
 
37
- parser = Parser.new
38
37
  builder = Builder.new
39
38
 
40
- parsed = Pubid::Parg::Backend.parse(:oiml, identifier)
39
+ parsed = Pubid::Parg::Model.bound(:oiml, identifier)
41
40
  builder.build(parsed)
42
41
  end
43
42
 
@@ -43,6 +43,11 @@ module Pubid
43
43
  render(format: :human, **opts)
44
44
  end
45
45
 
46
+ # OMG keeps its publisher in the PUBLISHER constant, not the attribute.
47
+ def publisher_prefixes
48
+ [PUBLISHER]
49
+ end
50
+
46
51
  def self.parse(identifier)
47
52
  unless identifier.is_a?(String)
48
53
  raise Pubid::Errors::InvalidInputError,
@@ -53,7 +58,7 @@ module Pubid
53
58
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
54
59
  end
55
60
 
56
- parsed = Pubid::Parg::Backend.parse(:omg, identifier)
61
+ parsed = Pubid::Parg::Model.bound(:omg, identifier)
57
62
  Builder.build(parsed)
58
63
  end
59
64
  end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Omg
7
- # Parslet grammar for OMG specification identifiers.
5
+ # Grammar for OMG specification identifiers.
8
6
  #
9
7
  # Accepts:
10
8
  # OMG {ACRONYM}[ {VERSION}][ {PART}]
@@ -15,79 +13,7 @@ module Pubid
15
13
  # "2.5.1", "5 beta 3", "2.5 beta".
16
14
  # PART is the volume or format segment, e.g. "Superstructure", "PDF". A
17
15
  # space or a slash separates it.
18
- class Parser < ::Pubid::Parser::Grammar
19
- rule(:space) { str(" ") }
20
-
21
- # Acronym: the URL segment OMG gives the specification, kept verbatim,
22
- # because a consumer builds the URL from it. It starts with a letter,
23
- # which may be lower case ("smartant"). Letters, digits and "+" follow
24
- # ("SysML", "AMI4CCM", "VSIPL++"). A hyphen or a slash joins further
25
- # segments ("DDS-PSM-Cxx", "EDMC-FIBO/BE"). Each segment is non-empty,
26
- # so a hyphen or a slash never ends the acronym.
27
- #
28
- # The slash in "EDMC-FIBO/BE" belongs to the acronym: OMG serves the
29
- # document at /spec/EDMC-FIBO/BE/. A slash after the version separates
30
- # the document part instead (see the identifier rule).
31
- rule(:acronym_char) { match("[A-Za-z0-9+]") }
32
-
33
- rule(:acronym) do
34
- (match("[A-Za-z]") >> acronym_char.repeat >>
35
- (match("[-/]") >> acronym_char.repeat(1)).repeat).as(:acronym)
36
- end
37
-
38
- # Version: digits with optional dots, optionally followed by " beta" and
39
- # an optional beta number. OMG writes the label both ways: the document
40
- # at /spec/UML/2.5/Beta1 gives its own version as "2.5 beta", and DDS 1.4
41
- # supersedes /spec/DDS/1.4/Beta2.
42
- #
43
- # The beta number must stay optional here. The document part below would
44
- # otherwise swallow a bare "beta" and report the version as "2.5" — a
45
- # silent wrong answer, not a parse failure.
46
- #
47
- # Both halves of the beta label end at a word boundary. Parslet never
48
- # backtracks into a `.maybe` that already succeeded, so an unanchored
49
- # literal makes the version eat the front of a document part and then
50
- # reject the whole identifier: "OMG DDS 1.4 beta2" and
51
- # "OMG DDS 1.4 betawave" would raise instead of reading the part.
52
- rule(:word_boundary) { match("[A-Za-z0-9]").absent? }
53
-
54
- rule(:beta) do
55
- str(" beta") >> word_boundary >>
56
- (space >> match("[0-9]").repeat(1) >> word_boundary).maybe
57
- end
58
-
59
- rule(:version) do
60
- (match("[0-9]").repeat(1) >>
61
- (str(".") >> match("[0-9]").repeat(1)).repeat >>
62
- beta.maybe).as(:version)
63
- end
64
-
65
- # After the version, OMG separates the document part with either a
66
- # space or a slash. The renderer prints a space, so the two spellings of
67
- # one document stay equal.
68
- rule(:part_separator) { space | str("/") }
69
-
70
- # Document part: the volume or format segment OMG puts after the
71
- # version. UML 2.1.1 is two documents, Superstructure and
72
- # Infrastructure, and the URL carries the same segment
73
- # (/spec/UML/2.1.1/Superstructure). A format name (/spec/DDS/1.4/PDF)
74
- # occupies the same position.
75
- rule(:part) { match("[A-Za-z0-9]").repeat(1).as(:part) }
76
-
77
- # Only a space separates a part that follows the acronym directly. The
78
- # acronym rule takes a slash there, so a slash separator is legal only
79
- # after a version.
80
- rule(:identifier) do
81
- str("OMG") >> space >> acronym >>
82
- ((space >> version >> (part_separator >> part).maybe) |
83
- (space >> part)).maybe
84
- end
85
-
86
- rule(:root) { identifier }
87
-
88
- def self.parse(input)
89
- new.parse(input)
90
- end
16
+ class Parser
91
17
  end
92
18
  end
93
19
  end
data/lib/pubid/parser.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "pubid"
4
3
  module Pubid
5
4
  module Parser
6
- autoload :Grammar, "pubid/parser/grammar"
7
- autoload :CommonParseMethods, "pubid/parser/common_parse_methods"
8
- autoload :CommonParseRules, "pubid/parser/common_parse_rules"
5
+ # A trailing "(all parts)" marks the reference as the whole document.
6
+ # NIST's parse path strips it before preprocessing and carries the
7
+ # marker into the tree (:all_parts) for the builder to wrap; the
8
+ # shared Builder::Base reads the same marker for every other flavor.
9
+ ALL_PARTS_SUFFIX = "(all parts)".freeze
9
10
  end
10
11
  end
@@ -116,7 +116,7 @@ module Pubid
116
116
  # failure — the class the cross-flavor contract requires, and the one
117
117
  # `Pubid.parse` treats as "try the next flavor".
118
118
  if identifier_string == mr_string && flavor_module == Pubid::Iso
119
- raise Parslet::ParseFailed,
119
+ raise Pubid::Errors::ParseError,
120
120
  "#{mr_string.inspect} is not an MR string"
121
121
  end
122
122
 
@@ -2,72 +2,7 @@
2
2
 
3
3
  module Pubid
4
4
  module Plateau
5
- class Parser < ::Pubid::Parser::Grammar
6
- rule(:space) { str(" ") }
7
- rule(:dash) { str("-") }
8
- rule(:hash) { str("#") }
9
-
10
- rule(:digit) { match["0-9"] }
11
- rule(:digits) { digit.repeat(1) }
12
-
13
- # PLATEAU
14
- rule(:publisher) { str("PLATEAU") }
15
-
16
- # Handbook or Technical Report
17
- rule(:doc_type) do
18
- (str("Handbook") | str("Technical Report")).as(:type)
19
- end
20
-
21
- # Number: #00, #01, #46, etc.
22
- rule(:number) { hash >> digits.as(:number) }
23
-
24
- # Annex: -1, -2, etc. Legacy Latin references use "_" as the sub-number
25
- # separator (e.g. "#46_1"); both render as the canonical dash.
26
- rule(:annex) { (dash | str("_")) >> digits.as(:annex) }
27
-
28
- # Edition (Handbook only). Canonical form is 第X.Y版; legacy Latin
29
- # references write the bare X.Y (e.g. "PLATEAU Handbook #00 1.0"). Both
30
- # capture the same numeric :edition slice, so they build the same object
31
- # and render to the canonical 第X.Y版.
32
- rule(:edition_part) do
33
- str("第") >>
34
- (digits >> str(".") >> digits).as(:edition) >>
35
- str("版")
36
- end
37
-
38
- rule(:edition_latin) { (digits >> str(".") >> digits).as(:edition) }
39
-
40
- rule(:edition) { space >> (edition_part | edition_latin) }
41
-
42
- # Annex supplement: "Annex A", "Annex B", etc.
43
- rule(:annex_letter) { match["A-Z"].as(:annex_letter) }
44
- rule(:annex_supplement) do
45
- space >> str("Annex") >> space >> annex_letter
46
- end
47
-
48
- # Full identifier patterns
49
- rule(:handbook) do
50
- publisher >> space >> doc_type >> space >>
51
- number >> annex.maybe >> edition.maybe
52
- end
53
-
54
- # Technical Report has no edition. A legacy Latin TR reference carrying a
55
- # trailing edition (e.g. "#00 1.0") is matched by the `handbook` rule
56
- # (doc_type matches either keyword, tried first); the builder dispatches
57
- # on :type and drops the captured edition for Technical Report.
58
- rule(:technical_report) do
59
- publisher >> space >> doc_type >> space >>
60
- number >> annex.maybe
61
- end
62
-
63
- # Annex identifier (supplement)
64
- rule(:annex_identifier) do
65
- (handbook | technical_report).as(:base) >> annex_supplement
66
- end
67
-
68
- rule(:identifier) { annex_identifier | handbook | technical_report }
69
-
70
- rule(:root) { identifier }
5
+ class Parser
71
6
  end
72
7
  end
73
8
  end