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
@@ -24,7 +24,7 @@ module Pubid
24
24
  # The code is stored as flat index columns on the leaf (see
25
25
  # Identifiers::Standard) — `number` is what relaton-index keys on — so
26
26
  # the Code built below is unpacked rather than assigned.
27
- if parsed_hash[:designator]
27
+ if parsed_hash[:designator] || parsed_hash[:designator_code]
28
28
  identifier.number = build_code(parsed_hash).to_s
29
29
  elsif parsed_hash[:number]
30
30
  # A joint-published reference ("ISO/ASME 14414-2015") parses to a bare
@@ -94,9 +94,11 @@ module Pubid
94
94
  private
95
95
 
96
96
  def build_code(parsed_hash)
97
- # Handle BPVC subdivision
98
- if parsed_hash[:designator].is_a?(Hash) && parsed_hash[:designator][:bpvc_code]
99
- bpvc_data = parsed_hash[:designator][:bpvc_code]
97
+ # Handle BPVC subdivision — the composite designator arrives under
98
+ # :designator_code ((raw) wrapper); :designator is the plain
99
+ # (string) form, so the composite cannot collide with it.
100
+ if parsed_hash[:designator_code].is_a?(Hash) && parsed_hash[:designator_code][:bpvc_code]
101
+ bpvc_data = parsed_hash[:designator_code][:bpvc_code]
100
102
 
101
103
  # Build BPVC designator string
102
104
  designator_str = "BPVC"
@@ -13,10 +13,9 @@ module Pubid
13
13
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
14
14
  end
15
15
 
16
- parser = Parser.new
17
16
  builder = Builder.new
18
17
 
19
- parsed = Pubid::Parg::Backend.parse(:asme, str)
18
+ parsed = Pubid::Parg::Model.bound(:asme, str)
20
19
  builder.build(parsed)
21
20
  end
22
21
  end
@@ -1,335 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Asme
7
- class Parser < ::Pubid::Parser::Grammar
8
- # Basic building blocks
9
- rule(:space) { str(" ") }
10
- # Regular dash, en-dash, em-dash
11
- rule(:dash) do
12
- str("-") | str("–") | str("—")
13
- end
14
- rule(:slash) { str("/") }
15
- rule(:dot) { str(".") }
16
- rule(:digit) { match("[0-9]") }
17
- rule(:digits) { digit.repeat(1) }
18
- rule(:letter) { match("[A-Z]") }
19
- rule(:letters) { letter.repeat(1) }
20
- rule(:underscore) { str("_") }
21
-
22
- # Roman numerals (longest first for proper matching - added X)
23
- rule(:roman_numeral) do
24
- str("XIII") | str("XII") | str("XI") | str("VIII") |
25
- str("VII") | str("VI") | str("IV") | str("IX") | str("X") |
26
- str("III") | str("II") | str("V") | str("I")
27
- end
28
-
29
- # BPVC special letter codes
30
- rule(:bpvc_letter_code) do
31
- str("NCA") | str("NCD") | str("SSC") | str("BPV") | str("NUC") |
32
- str("NB") | str("NC") | str("ND") | str("NE") | str("NF") | str("NG") |
33
- letter # Single letter for A, B, C, D, M
34
- end
35
-
36
- # BPVC complete subdivision
37
- rule(:bpvc_subdivision) do
38
- str("BPVC") >>
39
- (
40
- # Special: COMPLETE CODE BIND
41
- (space >> str("COMPLETE CODE BIND")).as(:special) |
42
- # Dash notation: BPVC-CC-BPV
43
- (dash >> str("CC") >> dash >> bpvc_letter_code.as(:case_code)) |
44
- # Standard dotted notation
45
- (
46
- dot >>
47
- (
48
- # SSC with complex subdivision: BPVC.SSC.XI.II.V.IX,
49
- # or the bare catalogue form BPVC.SSC.
50
- (str("SSC") >>
51
- (dot >>
52
- (roman_numeral >> (dot >> roman_numeral).repeat)
53
- .as(:ssc_sections)).maybe >>
54
- dot.maybe).as(:ssc_code) |
55
- # CC = Case Code: BPVC.CC.BPV or BPVC.CC.NC.XI; the
56
- # catalogue prints the sub-code with its own leading dot
57
- # (BPVC.CC.BPV..I)
58
- (str("CC") >> dot >> bpvc_letter_code.as(:case_code) >>
59
- (dot >> dot.maybe >>
60
- (roman_numeral | bpvc_letter_code).as(:case_sub)).maybe) |
61
- # Standard roman numeral subdivision: BPVC.I or BPVC.III.1.NB
62
- (roman_numeral.as(:section) >>
63
- (dot >> (digits | bpvc_letter_code).as(:subsection)).maybe >>
64
- (dot >> bpvc_letter_code.as(:sub_subsection)).maybe >>
65
- # Language suffix with underscore: _ES
66
- (underscore >> letters.as(:lang_suffix)).maybe)
67
- )
68
- ).as(:subdivision)
69
- ).as(:bpvc_code)
70
- end
71
-
72
- # Multi-character designator codes (updated with EA)
73
- rule(:multi_char_code) do
74
- str("PVHO") | str("PASE") | str("PTC") | str("PTB") | str("PDS") | str("PCC") |
75
- str("V&V") | str("V V") | # Both ampersand and space variants
76
- str("VVUQ") | # Additional V&V variant
77
- str("TDP") | str("RTP") | str("RT") |
78
- str("RA-S") | # New: RA-S with dash
79
- str("RA") |
80
- str("QME") | str("QAI") | str("QEI") |
81
- str("NUM") | str("NQA") | str("NML") |
82
- str("OM") |
83
- str("HST") | str("HRT") |
84
- str("FFS") |
85
- str("TES") |
86
- str("TR") | # Keep TR separate for space-separated pattern
87
- str("STS") | # New: STS-1
88
- str("CSD") | str("CA") |
89
- str("BTH") | str("BPE") |
90
- str("ANDE") | str("AED") |
91
- str("AG") |
92
- str("NM") | # New: NM.1, NM.2, NM.3
93
- str("EA") # New: EA-3
94
- end
95
-
96
- # Publisher patterns
97
- rule(:asme_publisher) { str("ASME") }
98
- rule(:csa_publisher) { str("CSA") }
99
- rule(:api_publisher) { str("API") }
100
- rule(:iso_publisher) { str("ISO") }
101
- rule(:ans_publisher) { str("ANS") }
102
-
103
- # Joint publisher patterns
104
- rule(:iso_asme_publisher) do
105
- str("ISO/ASME").as(:joint_publisher) >> space.maybe
106
- end
107
-
108
- rule(:asme_ans_publisher) do
109
- str("ASME/ANS").as(:joint_publisher) >> space
110
- end
111
-
112
- rule(:csa_asme_publisher) do
113
- csa_publisher.as(:first_publisher) >> space >>
114
- match("[A-Z0-9.]").repeat(1).as(:first_code) >>
115
- space.maybe >> slash >> asme_publisher.as(:second_publisher) >> space
116
- end
117
-
118
- rule(:api_asme_publisher) do
119
- api_publisher.as(:first_publisher) >> space >>
120
- match("[0-9-]").repeat(1).as(:first_code) >>
121
- space.maybe >> slash >> asme_publisher.as(:second_publisher) >> space
122
- end
123
-
124
- # Standard ASME publisher
125
- rule(:publisher) { asme_publisher >> space }
126
-
127
- # Designator (longest match first!)
128
- rule(:designator) do
129
- (
130
- bpvc_subdivision | # Try BPVC patterns first
131
- multi_char_code | # Multi-char codes BEFORE letters
132
- str("ISO") |
133
- str("CSA") |
134
- str("API") |
135
- str("ANS") |
136
- letters # Fallback for single-letter
137
- ).as(:designator)
138
- end
139
-
140
- # A trailing edition year ("-2021", "-20XX") and nothing after it.
141
- # `number_part` must not read it as a dash-separated number: a
142
- # designator with no number ("BPVC.I-2021", "BPE-2012") then stored the
143
- # year as its number, and the year itself was lost.
144
- #
145
- # The grammar cannot tell a year from a final 4-digit number: a code
146
- # whose whole number is "-1234" would parse as year 1234. No ASME
147
- # corpus id has that shape; spec/pubid/asme/root_number_spec.rb pins
148
- # the choice.
149
- rule(:trailing_year) do
150
- dash >> (str("20XX") | str("202X") | (str("20") >> digit >> str("X")) |
151
- digit.repeat(4, 4)) >> match("[0-9A-Z.]").absent?
152
- end
153
-
154
- # Number part - can start with dot (NM.1), be dotted (16.5), OR dash-separated (BTH-1)
155
- rule(:number_part) do
156
- (
157
- # Starting with dot (for NM.1, VVUQ 20.1, etc.)
158
- (dot >> match("[0-9A-Z]").repeat(1) >>
159
- (dot >> match("[0-9A-Z]").repeat(1)).repeat) |
160
- # Dash-separated first (for BTH-1, CA-1 patterns)
161
- (trailing_year.absent? >> dash >> match("[0-9A-Z]").repeat(1) >>
162
- (dot >> match("[0-9A-Z]").repeat(1)).repeat) |
163
- # Regular dotted numbers
164
- (match("[0-9A-Z]").repeat(1) >>
165
- (dot >> match("[0-9A-Z]").repeat(1)).repeat)
166
- ).as(:number)
167
- end
168
-
169
- # PTC special: space-separated number with optional suffix; the
170
- # renderer glues the designator to the number (PTC 19.3 TW and
171
- # PTC19.3 TW are the same document)
172
- rule(:ptc_number) do
173
- space.maybe >>
174
- (
175
- match("[0-9]").repeat(1) >>
176
- (dot >> match("[0-9]").repeat(1)).repeat
177
- ).as(:number) >>
178
- # Optional space + suffix (like "TW" or "PM")
179
- (space >> letters.as(:ptc_suffix)).maybe
180
- end
181
-
182
- # TR special: space-separated number (like "ASME TR A17.1-8.4-2013");
183
- # the rendered form glues the designator (TRA17.1-8.4)
184
- rule(:tr_number) do
185
- space.maybe >>
186
- (
187
- match("[A-Z0-9]").repeat(1) >>
188
- (dot >> match("[0-9A-Z]").repeat(1)).repeat >>
189
- # Allow dash for patterns like A17.1-8.4
190
- (dash >> match("[0-9]").repeat(1) >>
191
- (dot >> match("[0-9]").repeat(1)).repeat).maybe
192
- ).as(:number)
193
- end
194
-
195
- # Number with optional preceding space (for V V 10, V&V 10, VVUQ 20.1, etc.)
196
- rule(:spaced_number_part) do
197
- space.maybe >> number_part
198
- end
199
-
200
- # Year (4-digit)
201
- rule(:year_4digit) { digit.repeat(4, 4).as(:year) }
202
-
203
- # Draft year patterns: 20XX, 202X
204
- rule(:draft_year) do
205
- (str("20XX") | str("202X") | (str("20") >> digit >> str("X"))).as(:draft_year)
206
- end
207
-
208
- # Reaffirmation notation
209
- rule(:reaffirmation) do
210
- space >> str("(R") >> year_4digit.as(:reaffirmation) >> str(")")
211
- end
212
-
213
- # CSA dual-published number (for ASME/CSA format)
214
- rule(:csa_portion) do
215
- slash >> str("CSA") >> space >>
216
- (letter >> match("[0-9.]").repeat(1)).as(:csa_number)
217
- end
218
-
219
- # Parenthetical revision note after year - different from [Draft ...] notation
220
- rule(:parenthetical_revision) do
221
- space >> str("(") >>
222
- (str("Proposed revision of") | str("Revision of")) >>
223
- space >> match("[^)]").repeat(1).as(:ref_standard) >>
224
- str(")")
225
- end
226
-
227
- # Revision note [Draft Proposed Revision of ...]
228
- rule(:revision_note) do
229
- space >> str("[") >> match("[^\\]]").repeat(1).as(:revision_note) >> str("]")
230
- end
231
-
232
- # Language notation - support both before and after year
233
- rule(:language) do
234
- space >> str("(") >> letters.as(:language) >> str(")")
235
- end
236
-
237
- # Handbook keyword
238
- rule(:handbook_keyword) do
239
- (space >> str("Handbook")).as(:handbook)
240
- end
241
-
242
- # Standard identifier components
243
- rule(:standard_components) do
244
- publisher >>
245
- designator >>
246
- number_part.maybe >>
247
- (csa_portion | handbook_keyword).maybe >>
248
- # Language can come before or after year
249
- language.maybe >>
250
- (
251
- dash >> (draft_year | year_4digit)
252
- ).maybe >>
253
- language.maybe >>
254
- reaffirmation.maybe >>
255
- revision_note.maybe
256
- end
257
-
258
- # Joint published identifier - CSA first
259
- rule(:csa_asme_identifier) do
260
- csa_asme_publisher >>
261
- designator >>
262
- number_part.maybe >>
263
- (dash >> (draft_year | year_4digit)).maybe >>
264
- language.maybe >>
265
- reaffirmation.maybe
266
- end
267
-
268
- # Joint published identifier - API first
269
- rule(:api_asme_identifier) do
270
- api_asme_publisher >>
271
- designator >>
272
- number_part.maybe >>
273
- (dash >> (draft_year | year_4digit)).maybe >>
274
- language.maybe >>
275
- reaffirmation.maybe
276
- end
277
-
278
- # Joint published identifier - ISO/ASME. The catalogue also lists
279
- # the numberless series identity "ISO/ASME-2015" beside the
280
- # numbered adoptions.
281
- rule(:iso_asme_identifier) do
282
- iso_asme_publisher >>
283
- number_part.maybe >>
284
- (dash >> (draft_year | year_4digit)).maybe >>
285
- language.maybe >>
286
- reaffirmation.maybe
287
- end
288
-
289
- # Joint published identifier - ASME/ANS
290
- rule(:asme_ans_identifier) do
291
- asme_ans_publisher >>
292
- designator >>
293
- number_part.maybe >>
294
- (dash >> (draft_year | year_4digit)).maybe >>
295
- language.maybe >>
296
- reaffirmation.maybe
297
- end
298
-
299
- # Standard ASME identifier
300
- rule(:standard) do
301
- publisher >>
302
- (
303
- # Special case for PTC with space-separated number
304
- (str("PTC").as(:designator) >> ptc_number >>
305
- (dash >> (draft_year | year_4digit)).maybe) |
306
- # Special case for TR with space-separated number
307
- (str("TR").as(:designator) >> tr_number >>
308
- (dash >> (draft_year | year_4digit)).maybe) |
309
- # Regular pattern with designator + optional spaced number
310
- (designator >> spaced_number_part.maybe >>
311
- # CSA portion can be followed by handbook keyword
312
- ((csa_portion >> handbook_keyword.maybe) | handbook_keyword).maybe >>
313
- # Language can come before or after year
314
- language.maybe >>
315
- (dash >> (draft_year | year_4digit)).maybe >>
316
- language.maybe)
317
- ) >>
318
- # Parenthetical revision note (after year)
319
- parenthetical_revision.maybe >>
320
- reaffirmation.maybe >>
321
- revision_note.maybe
322
- end
323
-
324
- rule(:identifier) do
325
- csa_asme_identifier |
326
- api_asme_identifier |
327
- iso_asme_identifier |
328
- asme_ans_identifier |
329
- standard
330
- end
331
-
332
- root(:identifier)
5
+ class Parser
333
6
  end
334
7
  end
335
8
  end
@@ -109,14 +109,18 @@ module Pubid
109
109
 
110
110
  # Manual
111
111
  if identifier.is_a?(Identifiers::Manual)
112
- identifier.edition = parsed_hash[:edition].to_s if parsed_hash[:edition]
112
+ identifier.edition = parsed_hash[:edition]&.to_s
113
+ identifier.edition ||= parsed_hash[:edition_ordinal].to_s if parsed_hash[:edition_ordinal]
114
+ identifier.edition ||= parsed_hash[:edition_number].to_s if parsed_hash[:edition_number]
113
115
  identifier.supplement = true if parsed_hash[:supplement]
114
116
  identifier.tp_designation = parsed_hash[:tp_designation].to_s if parsed_hash[:tp_designation]
115
117
  end
116
118
 
117
119
  # Monograph
118
- if identifier.is_a?(Identifiers::Monograph) && parsed_hash[:edition]
119
- identifier.edition = parsed_hash[:edition].to_s
120
+ if identifier.is_a?(Identifiers::Monograph)
121
+ identifier.edition = parsed_hash[:edition]&.to_s
122
+ identifier.edition ||= parsed_hash[:edition_ordinal].to_s if parsed_hash[:edition_ordinal]
123
+ identifier.edition ||= parsed_hash[:edition_number].to_s if parsed_hash[:edition_number]
120
124
  end
121
125
 
122
126
  # Data Series
@@ -146,6 +150,11 @@ module Pubid
146
150
  # Ordinal notation: "1ST"
147
151
  identifier.edition = edition_data.to_s
148
152
  end
153
+ elsif parsed_hash[:edition_number] || parsed_hash[:edition_ordinal]
154
+ # The bindings carry the edition flat: the e-notation form
155
+ # binds its digits (:edition_number) and the ordinals bind
156
+ # whole (:edition_ordinal); the :edition capture is gone.
157
+ identifier.edition = (parsed_hash[:edition_number] || parsed_hash[:edition_ordinal]).to_s
149
158
  end
150
159
  end
151
160
  end
@@ -13,10 +13,9 @@ module Pubid
13
13
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
14
14
  end
15
15
 
16
- parser = Parser.new
17
16
  builder = Builder.new
18
17
 
19
- parsed = Pubid::Parg::Backend.parse(:astm, str)
18
+ parsed = Pubid::Parg::Model.bound(:astm, str)
20
19
  builder.build(parsed)
21
20
  end
22
21
  end
@@ -1,186 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Astm
7
- class Parser < ::Pubid::Parser::Grammar
8
- # Basic building blocks
9
- rule(:space) { str(" ") }
10
- rule(:dash) { str("-") }
11
- rule(:slash) { str("/") }
12
- rule(:colon) { str(":") }
13
- rule(:dot) { str(".") }
14
- rule(:digit) { match("[0-9]") }
15
- rule(:digits) { digit.repeat(1) }
16
- rule(:letter) { match("[A-Z]") }
17
- rule(:letters) { letter.repeat(1) }
18
-
19
- # Publisher
20
- rule(:publisher) { str("ASTM").as(:publisher) >> space.maybe }
21
-
22
- # Letters A-G for standard types
23
- rule(:standard_letter) { match("[A-G]").as(:letter) }
24
-
25
- # Year patterns (2-digit)
26
- rule(:year_2digit) { digit.repeat(2, 2).as(:year) }
27
-
28
- # Reapproval notation (may have comment after)
29
- rule(:reapproval) do
30
- str("(") >> digit.repeat(4, 4).as(:reapproval) >> str(")")
31
- end
32
-
33
- # Comment portion (anything after #)
34
- rule(:comment) { space.maybe >> str("#") >> match("[^#]").repeat }
35
-
36
- # Edition notations - supports both ordinal (1ST, 2ND) and e-notation (e1, e2)
37
- # For e-notation, capture just the digits (not the 'e' prefix)
38
- # For ordinal notation, capture the whole string
39
- rule(:edition) do
40
- (
41
- (str("e") >> digits.as(:edition_number)) |
42
- str("9TH") | str("8TH") | str("7TH") | str("6TH") |
43
- str("5TH") | str("4TH") | str("3RD") | str("2ND") |
44
- str("1ST")
45
- ).as(:edition)
46
- end
47
-
48
- # Sub-year notation (a, b, c)
49
- rule(:sub_year) { match("[a-c]").as(:sub_year) }
50
-
51
- # Format suffix
52
- rule(:format_suffix) { dash >> str("EB").as(:format_suffix) }
53
-
54
- # Format suffix without dash (for use after supplement which ends with dash)
55
- rule(:format_suffix_no_dash) { str("EB").as(:format_suffix) }
56
-
57
- # Supplement notation (ends with dash)
58
- rule(:supplement) { dash >> str("SUP").as(:supplement) >> dash }
59
-
60
- # ========================================
61
- # Research Report - MOST SPECIFIC (has colon)
62
- # ========================================
63
- rule(:research_report) do
64
- publisher.maybe >>
65
- str("RR").as(:type) >>
66
- colon >>
67
- (letter >> digit.repeat(2, 2)).as(:committee) >>
68
- dash >>
69
- digits.as(:number)
70
- end
71
-
72
- # ========================================
73
- # Manual
74
- # ========================================
75
- rule(:manual) do
76
- publisher.maybe >>
77
- str("MNL").as(:type) >>
78
- str("TP").as(:tp_designation).maybe >>
79
- digits.as(:number) >>
80
- (dash >> edition).maybe >>
81
- (
82
- (supplement >> format_suffix_no_dash.maybe) |
83
- format_suffix.maybe
84
- )
85
- end
86
-
87
- # ========================================
88
- # Monograph
89
- # ========================================
90
- rule(:monograph) do
91
- publisher.maybe >>
92
- str("MONO").as(:type) >>
93
- digits.as(:number) >>
94
- (dash >> edition).maybe >>
95
- format_suffix.maybe
96
- end
97
-
98
- # ========================================
99
- # Data Series
100
- # ========================================
101
- rule(:data_series_suffix) { match("[A-Z]").as(:suffix) }
102
- # Subseries can be: -S4 OR S4 (when following a letter suffix)
103
- rule(:data_series_subseries_with_dash) do
104
- dash >> str("S") >> digits.as(:subseries)
105
- end
106
- rule(:data_series_subseries_no_dash) do
107
- digits.as(:subseries) # Directly after letter suffix
108
- end
109
- rule(:data_series_code) do
110
- digits.as(:number) >>
111
- (
112
- str("HOL").as(:hol_suffix) |
113
- # A letter suffix may carry its subseries with the dash
114
- # spelled out too ("DS55S-S1"), not only glued ("DS55S1").
115
- (data_series_suffix >>
116
- (data_series_subseries_no_dash | data_series_subseries_with_dash).maybe) |
117
- data_series_subseries_with_dash
118
- ).maybe
119
- end
120
-
121
- rule(:data_series) do
122
- publisher.maybe >>
123
- str("DS").as(:type) >>
124
- data_series_code >>
125
- format_suffix.maybe
126
- end
127
-
128
- # ========================================
129
- # Work in Progress
130
- # ========================================
131
- rule(:work_in_progress) do
132
- publisher.maybe >>
133
- str("WK").as(:type) >>
134
- digits.as(:number)
135
- end
136
-
137
- # ========================================
138
- # Adjunct
139
- # ========================================
140
- # SEMANTIC NOTE: Adjunct identifiers reference a base standard
141
- # Structure: ADJ + base_standard_code + adjunct_designation
142
- # Examples:
143
- # ADJF3504-EA: Adjunct to F3504, EA = Excel file format
144
- # ADJC033501: Adjunct "01" to C335
145
- # ADJC0450A: Adjunct format "A" to C450
146
- # ADJE11211T: Adjunct "11T" to E112
147
- # ADJDQCALC: Adjunct with text designation (not to numbered standard)
148
- # Adjunct formats: EA (Excel), E-PDF, A1A2 (arbitrary identifiers)
149
- # One standard can have multiple adjuncts
150
- rule(:adjunct) do
151
- publisher.maybe >>
152
- str("ADJ").as(:type) >>
153
- ((letter >> digits) | letters | digits).as(:designation) >>
154
- (dash >> str("EA")).maybe.as(:ea_suffix) >>
155
- str("DVD").maybe.as(:dvd_suffix)
156
- end
157
-
158
- # ========================================
159
- # Technical Report
160
- # ========================================
161
- rule(:technical_report_iso_astm) do
162
- str("ISO/ASTMTR").as(:type) >>
163
- digits.as(:number) >>
164
- format_suffix.maybe >>
165
- comment.maybe
166
- end
167
-
168
- rule(:technical_report_simple) do
169
- publisher.maybe >>
170
- str("TR").as(:type) >>
171
- digits.as(:number) >>
172
- format_suffix.maybe >>
173
- comment.maybe
174
- end
175
-
176
- rule(:technical_report) do
177
- technical_report_iso_astm | technical_report_simple
178
- end
179
-
180
- # ========================================
181
- # Standard (DEFAULT - handles A-G prefix AND digit-only E-standards)
182
- # ========================================
183
-
5
+ class Parser
184
6
  # SEMANTIC NOTE: 5xxxx digit-only standards are ISO/ASTM dual-published
185
7
  # Examples:
186
8
  # ASTM 52303-24e1: ASTM's version (e1 = edition 1, not "E" prefix)
@@ -194,54 +16,6 @@ module Pubid
194
16
  # RRE015501: Reference radiograph, volume 1
195
17
  # RRE2669CS: Adjunct to E2669 reference radiograph
196
18
  # These require separate ReferenceRadiograph identifier class
197
-
198
- # Dual unit pattern: F1862/F1862M
199
- rule(:dual_unit) do
200
- slash >>
201
- standard_letter >>
202
- digits >>
203
- str("M").as(:dual_m)
204
- end
205
-
206
- rule(:standard_code_with_letter) do
207
- standard_letter >>
208
- digits.as(:number) >>
209
- dual_unit.maybe
210
- end
211
-
212
- # Digit-only standards (primarily 5xxxx ISO/ASTM dual-published)
213
- # Parsed as digit-only, semantic meaning is dual-published with ISO
214
- rule(:standard_code_digit_only) do
215
- digits.as(:number) # Accept digit-only, no implicit E prefix
216
- end
217
-
218
- rule(:standard) do
219
- publisher.maybe >>
220
- (standard_code_with_letter | standard_code_digit_only) >>
221
- (
222
- dash >> year_2digit >>
223
- sub_year.maybe >>
224
- reapproval.maybe >>
225
- edition.maybe
226
- ).maybe >>
227
- comment.maybe
228
- end
229
-
230
- # ========================================
231
- # Main identifier rule - ORDER MATTERS!
232
- # ========================================
233
- rule(:identifier) do
234
- research_report | # Most specific (has colon)
235
- technical_report | # Has TR prefix
236
- manual | # Has MNL prefix
237
- monograph | # Has MONO prefix
238
- data_series | # Has DS prefix
239
- work_in_progress | # Has WK prefix
240
- adjunct | # Has ADJ prefix
241
- standard # DEFAULT (A-G letter)
242
- end
243
-
244
- root(:identifier)
245
19
  end
246
20
  end
247
21
  end
@@ -185,6 +185,13 @@ module Pubid
185
185
  render(format: :human, **opts)
186
186
  end
187
187
 
188
+ # The SI Brochure and the full MEP / guide content forms print a leading
189
+ # `BIPM ` from the renderer; `with_publisher: false` drops it. Committee,
190
+ # meeting and Metrologia forms never print it, so they are unchanged.
191
+ def publisher_prefixes
192
+ [PUBLISHER]
193
+ end
194
+
188
195
  # --- MR string / slug -------------------------------------------------
189
196
  #
190
197
  # `to_slug` is what consumers use as an output FILENAME, so every BIPM
@@ -277,7 +284,7 @@ module Pubid
277
284
  # Normalize legacy/docnumber-style spellings before parsing
278
285
  # (data/bipm/update_codes.yaml), mirroring Pubid::Ccsds / Pubid::Plateau.
279
286
  normalized = Core::UpdateCodes.apply(identifier, :bipm)
280
- parsed = Pubid::Parg::Backend.parse(:bipm, normalized)
287
+ parsed = Pubid::Parg::Model.bound(:bipm, normalized)
281
288
  Builder.build(parsed)
282
289
  end
283
290
  end