pubid 2.0.1 → 2.0.3

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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/data/ieee/update_codes.yaml +2 -2
  3. data/data/parg/adobe.json +1 -1
  4. data/data/parg/aiee.json +1 -1
  5. data/data/parg/aiee.parg +99 -9
  6. data/data/parg/amca.json +1 -1
  7. data/data/parg/ansi.json +1 -1
  8. data/data/parg/api.json +1 -1
  9. data/data/parg/ashrae.json +1 -1
  10. data/data/parg/asme.json +1 -1
  11. data/data/parg/astm.json +1 -1
  12. data/data/parg/bipm.json +1 -1
  13. data/data/parg/bsi.json +1 -1
  14. data/data/parg/ccsds.json +1 -1
  15. data/data/parg/cen_cenelec.json +1 -1
  16. data/data/parg/cie.json +1 -1
  17. data/data/parg/csa.json +1 -1
  18. data/data/parg/ecma.json +1 -1
  19. data/data/parg/etsi.json +1 -1
  20. data/data/parg/evs.json +1 -1
  21. data/data/parg/gost.json +1 -1
  22. data/data/parg/iala.json +1 -1
  23. data/data/parg/iana.json +1 -1
  24. data/data/parg/idf.json +1 -1
  25. data/data/parg/iec.json +1 -1
  26. data/data/parg/ieee.json +1 -1
  27. data/data/parg/iho.json +1 -0
  28. data/data/parg/ire.json +1 -1
  29. data/data/parg/ire.parg +51 -12
  30. data/data/parg/isbn.json +1 -1
  31. data/data/parg/iso.json +1 -1
  32. data/data/parg/iso.parg +751 -0
  33. data/data/parg/itu.json +1 -1
  34. data/data/parg/jcgm.json +1 -1
  35. data/data/parg/jis.json +1 -1
  36. data/data/parg/nesc.json +1 -1
  37. data/data/parg/nesc.parg +97 -12
  38. data/data/parg/nist.json +1 -1
  39. data/data/parg/oiml.json +1 -1
  40. data/data/parg/omg.json +1 -1
  41. data/data/parg/parg.json +1 -1
  42. data/data/parg/plateau.json +1 -1
  43. data/data/parg/tgpp.json +1 -1
  44. data/data/parg/un.json +1 -1
  45. data/lib/pubid/adobe/identifier.rb +1 -1
  46. data/lib/pubid/adobe/parser.rb +2 -113
  47. data/lib/pubid/amca/identifiers/base.rb +1 -1
  48. data/lib/pubid/amca/parser.rb +1 -145
  49. data/lib/pubid/ansi/identifier.rb +1 -1
  50. data/lib/pubid/ansi/parser.rb +1 -82
  51. data/lib/pubid/api/builder.rb +32 -1
  52. data/lib/pubid/api/identifier.rb +1 -1
  53. data/lib/pubid/api/parser.rb +1 -131
  54. data/lib/pubid/ashrae/identifiers/base.rb +1 -1
  55. data/lib/pubid/ashrae/parser.rb +3 -710
  56. data/lib/pubid/asme/builder.rb +6 -4
  57. data/lib/pubid/asme/identifier.rb +1 -2
  58. data/lib/pubid/asme/parser.rb +1 -328
  59. data/lib/pubid/astm/builder.rb +12 -3
  60. data/lib/pubid/astm/identifier.rb +1 -2
  61. data/lib/pubid/astm/parser.rb +1 -227
  62. data/lib/pubid/bipm/identifier.rb +8 -1
  63. data/lib/pubid/bipm/parser.rb +2 -224
  64. data/lib/pubid/bsi/builder.rb +2 -4
  65. data/lib/pubid/bsi/parser.rb +1 -666
  66. data/lib/pubid/bsi/single_identifier.rb +38 -1
  67. data/lib/pubid/calconnect/parser.rb +2 -40
  68. data/lib/pubid/ccsds/identifier.rb +1 -1
  69. data/lib/pubid/ccsds/parser.rb +1 -66
  70. data/lib/pubid/cen_cenelec/identifier.rb +16 -1
  71. data/lib/pubid/cen_cenelec/parser.rb +3 -136
  72. data/lib/pubid/cie/identifier.rb +1 -1
  73. data/lib/pubid/cie/parser.rb +3 -378
  74. data/lib/pubid/conformance.rb +1 -1
  75. data/lib/pubid/csa/builder.rb +41 -18
  76. data/lib/pubid/csa/identifier.rb +12 -4
  77. data/lib/pubid/csa/identifiers/amendment.rb +28 -0
  78. data/lib/pubid/csa/identifiers.rb +1 -0
  79. data/lib/pubid/csa/parser.rb +16 -397
  80. data/lib/pubid/doi/parser.rb +2 -37
  81. data/lib/pubid/easc/builder.rb +1 -1
  82. data/lib/pubid/easc/parser.rb +2 -70
  83. data/lib/pubid/ecma/CLAUDE.md +8 -1
  84. data/lib/pubid/ecma/identifier.rb +13 -8
  85. data/lib/pubid/ecma/parser.rb +2 -54
  86. data/lib/pubid/ecma/renderer.rb +3 -7
  87. data/lib/pubid/errors.rb +18 -13
  88. data/lib/pubid/etsi/builder.rb +8 -5
  89. data/lib/pubid/etsi/identifiers/base.rb +1 -1
  90. data/lib/pubid/etsi/parser.rb +1 -132
  91. data/lib/pubid/evs/identifier.rb +7 -0
  92. data/lib/pubid/evs/parser.rb +1 -8
  93. data/lib/pubid/evs.rb +1 -1
  94. data/lib/pubid/gb/identifier.rb +6 -0
  95. data/lib/pubid/gb/parser.rb +2 -51
  96. data/lib/pubid/gost/identifier.rb +9 -1
  97. data/lib/pubid/gost/parser.rb +4 -81
  98. data/lib/pubid/iala/builder.rb +48 -39
  99. data/lib/pubid/iala/identifier.rb +1 -1
  100. data/lib/pubid/iala/parser.rb +2 -121
  101. data/lib/pubid/iana/identifier.rb +1 -1
  102. data/lib/pubid/iana/parser.rb +2 -24
  103. data/lib/pubid/identifier.rb +78 -1
  104. data/lib/pubid/idf/builder.rb +42 -0
  105. data/lib/pubid/idf/identifier.rb +1 -1
  106. data/lib/pubid/idf/parser.rb +1 -137
  107. data/lib/pubid/idf.rb +1 -2
  108. data/lib/pubid/iec/builder.rb +158 -50
  109. data/lib/pubid/iec/identifier.rb +1 -1
  110. data/lib/pubid/iec/parser.rb +3 -430
  111. data/lib/pubid/ieee/CLAUDE.md +2 -0
  112. data/lib/pubid/ieee/aiee/identifier.rb +2 -1
  113. data/lib/pubid/ieee/aiee.rb +0 -1
  114. data/lib/pubid/ieee/builder.rb +177 -30
  115. data/lib/pubid/ieee/components/code.rb +46 -7
  116. data/lib/pubid/ieee/identifiers/base.rb +5 -2
  117. data/lib/pubid/ieee/identifiers/joint_development.rb +7 -3
  118. data/lib/pubid/ieee/ire/identifier.rb +2 -1
  119. data/lib/pubid/ieee/ire.rb +0 -1
  120. data/lib/pubid/ieee/nesc/builder.rb +1 -1
  121. data/lib/pubid/ieee/nesc.rb +0 -1
  122. data/lib/pubid/ieee/parser.rb +1 -1257
  123. data/lib/pubid/ieee/project_renderer.rb +5 -1
  124. data/lib/pubid/ieee/renderer.rb +4 -2
  125. data/lib/pubid/ieee/urn_generator.rb +4 -1
  126. data/lib/pubid/ietf/identifiers/base.rb +17 -1
  127. data/lib/pubid/ietf/parser.rb +2 -47
  128. data/lib/pubid/ietf/renderer.rb +17 -2
  129. data/lib/pubid/iho/CLAUDE.md +98 -0
  130. data/lib/pubid/iho/builder.rb +4 -1
  131. data/lib/pubid/iho/identifiers/base.rb +28 -2
  132. data/lib/pubid/iho/renderer.rb +8 -0
  133. data/lib/pubid/iho/urn_generator.rb +14 -1
  134. data/lib/pubid/iho/urn_parser.rb +45 -32
  135. data/lib/pubid/iho.rb +0 -1
  136. data/lib/pubid/isbn/identifier.rb +1 -1
  137. data/lib/pubid/isbn/parser.rb +2 -31
  138. data/lib/pubid/iso/CLAUDE.md +66 -0
  139. data/lib/pubid/iso/builder.rb +229 -15
  140. data/lib/pubid/iso/identifier.rb +22 -12
  141. data/lib/pubid/iso/identifiers/directives.rb +6 -3
  142. data/lib/pubid/iso/identifiers/guide.rb +6 -3
  143. data/lib/pubid/iso/identifiers/international_workshop_agreement.rb +6 -3
  144. data/lib/pubid/iso/normalizer.rb +5 -4
  145. data/lib/pubid/iso/parser.rb +1 -539
  146. data/lib/pubid/iso/supplement_identifier.rb +6 -3
  147. data/lib/pubid/iso/urn_parser.rb +13 -8
  148. data/lib/pubid/iso/utilities.rb +4 -5
  149. data/lib/pubid/iso.rb +1 -19
  150. data/lib/pubid/itu/builder.rb +36 -20
  151. data/lib/pubid/itu/identifiers/base.rb +15 -2
  152. data/lib/pubid/itu/parser.rb +1 -775
  153. data/lib/pubid/jcgm/builder.rb +35 -0
  154. data/lib/pubid/jcgm/parser.rb +1 -151
  155. data/lib/pubid/jcgm.rb +1 -2
  156. data/lib/pubid/jis/CLAUDE.md +36 -0
  157. data/lib/pubid/jis/builder.rb +30 -41
  158. data/lib/pubid/jis/identifier.rb +12 -2
  159. data/lib/pubid/jis/identifiers/welding_engineering_standard.rb +35 -0
  160. data/lib/pubid/jis/identifiers.rb +2 -0
  161. data/lib/pubid/jis/parser.rb +1 -128
  162. data/lib/pubid/jis/renderer.rb +1 -1
  163. data/lib/pubid/jis/supplement_identifier.rb +5 -5
  164. data/lib/pubid/jis/urn_parser.rb +2 -1
  165. data/lib/pubid/jis.rb +3 -2
  166. data/lib/pubid/nist/builder.rb +5 -1
  167. data/lib/pubid/nist/caster.rb +13 -3
  168. data/lib/pubid/nist/identifiers/base.rb +37 -2
  169. data/lib/pubid/nist/identifiers/circular.rb +2 -2
  170. data/lib/pubid/nist/identifiers/circular_supplement.rb +2 -2
  171. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +2 -2
  172. data/lib/pubid/nist/identifiers/crpl_report.rb +2 -2
  173. data/lib/pubid/nist/identifiers/handbook.rb +2 -2
  174. data/lib/pubid/nist/identifiers/internal_report.rb +2 -2
  175. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +2 -2
  176. data/lib/pubid/nist/identifiers/monograph.rb +2 -2
  177. data/lib/pubid/nist/identifiers/report.rb +2 -2
  178. data/lib/pubid/nist/parser.rb +19 -775
  179. data/lib/pubid/nist/router.rb +4 -1
  180. data/lib/pubid/nist/supplement_identifier.rb +2 -2
  181. data/lib/pubid/oasis/parser.rb +2 -16
  182. data/lib/pubid/ogc/parser.rb +2 -26
  183. data/lib/pubid/oiml/identifiers/certification_system.rb +7 -0
  184. data/lib/pubid/oiml/identifiers/dual_published.rb +2 -2
  185. data/lib/pubid/oiml/parser.rb +1 -303
  186. data/lib/pubid/oiml.rb +1 -2
  187. data/lib/pubid/omg/identifier.rb +6 -1
  188. data/lib/pubid/omg/parser.rb +2 -76
  189. data/lib/pubid/parser.rb +5 -4
  190. data/lib/pubid/parsers/mr_string.rb +1 -1
  191. data/lib/pubid/plateau/parser.rb +1 -66
  192. data/lib/pubid/plateau.rb +1 -2
  193. data/lib/pubid/renderers/directives_renderer.rb +1 -1
  194. data/lib/pubid/renderers/iwa_renderer.rb +1 -1
  195. data/lib/pubid/renderers/publisher_stripper.rb +82 -0
  196. data/lib/pubid/renderers/supplement_renderer.rb +1 -1
  197. data/lib/pubid/renderers.rb +1 -0
  198. data/lib/pubid/sae/parser.rb +1 -48
  199. data/lib/pubid/tgpp/identifier.rb +1 -1
  200. data/lib/pubid/tgpp/parser.rb +2 -59
  201. data/lib/pubid/un/identifier.rb +1 -1
  202. data/lib/pubid/un/parser.rb +1 -18
  203. data/lib/pubid/version.rb +1 -1
  204. data/lib/pubid/w3c/parser.rb +2 -27
  205. data/lib/pubid/xsf/identifier.rb +10 -6
  206. data/lib/pubid/xsf/parser.rb +3 -31
  207. data/lib/pubid/xsf/renderer.rb +2 -4
  208. data/lib/pubid.rb +8 -9
  209. metadata +9 -23
  210. data/lib/pubid/ieee/aiee/parser.rb +0 -130
  211. data/lib/pubid/ieee/ire/parser.rb +0 -109
  212. data/lib/pubid/ieee/nesc/parser.rb +0 -155
  213. data/lib/pubid/iho/parser.rb +0 -68
  214. data/lib/pubid/parser/common_parse_methods.rb +0 -13
  215. data/lib/pubid/parser/common_parse_rules.rb +0 -56
  216. data/lib/pubid/parser/grammar.rb +0 -96
@@ -1,706 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Ashrae
7
5
  # Parser class for ASHRAE identifiers
8
6
  # Single Responsibility: Parsing ASHRAE identifier syntax
9
- class Parser < ::Pubid::Parser::Grammar
10
- # Basic building blocks
11
- rule(:space) { str(" ") }
12
- rule(:space?) { space.maybe }
13
- rule(:dash) { str("-") }
14
- rule(:dot) { str(".") }
15
- rule(:slash) { str("/") }
16
- rule(:lparen) { str("(") }
17
- rule(:rparen) { str(")") }
18
- rule(:comma) { str(",") }
19
- rule(:colon) { str(":") }
20
-
21
- rule(:digit) { match("[0-9]") }
22
- rule(:digits) { digit.repeat(1) }
23
- rule(:letter) { match("[A-Za-z]") }
24
-
25
- # Year pattern (4 digits starting with 19 or 20)
26
- rule(:year_digits) do
27
- (str("19") | str("20")) >> digit.repeat(2, 2)
28
- end
29
-
30
- # Reaffirmation year (2 or 4 digits)
31
- rule(:reaffirmation_year) do
32
- year_digits | digit.repeat(2, 2)
33
- end
34
-
35
- # Publisher
36
- rule(:publisher) { str("ASHRAE") }
37
-
38
- # Type (Guideline or Standard)
39
- rule(:type) do
40
- str("Guideline") | str("Standard")
41
- end
42
-
43
- # Code pattern (e.g., 15, 90.1, 41.10, 118.1, 90A,B,C)
44
- rule(:code) do
45
- (
46
- digits >> # First part (e.g., 15, 90)
47
- (dot >> digits).repeat(0, 2) >> # Optional dotted parts (e.g., .1, .10)
48
- # Special pattern for codes like "90A,B,C" (letter + comma-separated letters)
49
- (
50
- letter >> # Single letter after digits (e.g., "A" in "90A")
51
- (comma >> letter).repeat(0, 10) # Optional comma-separated letters (e.g., ",B,C")
52
- ).maybe # This entire letter+commas pattern is optional
53
- ).as(:code)
54
- end
55
-
56
- # Code with year pattern (e.g., 34-2024, 62.1-2022, 90A-2010) - used when type is missing
57
- #
58
- # The code portion MUST be captured with `.as(:code)`. Without it the
59
- # digits were matched and then discarded — only the year was named — so
60
- # every identifier reaching this rule lost its document number: 48 corpus
61
- # ids rendered as "ANSI/ASHRAE Addendum a to ASHRAE Standard -2024",
62
- # number gone and a dangling dash left behind. Builder#extract_base_attributes
63
- # already read `code_with_year[:code]`; it was waiting for a value the
64
- # grammar never produced.
65
- rule(:code_with_year) do
66
- (
67
- (
68
- digits >> # First part (e.g., 34, 62)
69
- (dot >> digits).repeat(0, 2) >> # Optional dotted parts (e.g., .1)
70
- # Special pattern for codes like "90A,B,C-2010"
71
- (
72
- letter >> # Single letter after digits
73
- (comma >> letter).repeat(0, 10) # Optional comma-separated letters
74
- ).maybe
75
- ).as(:code) >>
76
- space?.maybe >> dash >>
77
- year_digits.as(:year)
78
- ).as(:code_with_year)
79
- end
80
-
81
- # Suffix (R for revision, P for proposed)
82
- rule(:suffix) do
83
- (str("R") | str("P")).as(:suffix)
84
- end
85
-
86
- # Additional copublisher pattern (e.g., /AMCA 210-99 or (ANSI/AMCA 330-97))
87
- rule(:additional_copublisher) do
88
- (slash >> (letter.repeat(4,
89
- 10) >> space >> (digits >> (dash >> digits).maybe).maybe).as(:additional_copublisher)) |
90
- (lparen >> (str("ANSI") >> slash >> (letter.repeat(2,
91
- 10) >> (slash >> letter.repeat(2, 10)).repeat(0,
92
- 2))).as(:additional_copublisher) >> space >> (digits >> (dash >> digits).maybe).maybe >> rparen)
93
- end
94
-
95
- # Optional suffix in parentheses (PDF), (I-P and SI versions), long descriptions, etc.
96
- rule(:optional_suffix) do
97
- space.maybe >> lparen >> (
98
- str("PDF") | # Special case for PDF
99
- (letter.repeat(1, 20) >> (space | digit | comma | dash | dot | letter | str("–")).repeat(0, 500)) # General case including long descriptions
100
- ) >> rparen
101
- end
102
-
103
- # Reaffirmation pattern (RA YEAR or RA-YEAR)
104
- rule(:reaffirmed) do
105
- (space.maybe >> lparen >> str("RA") >> space? >> reaffirmation_year.as(:reaffirmed) >> rparen) |
106
- (space.maybe >> lparen >> str("RA") >> dash >> reaffirmation_year.as(:reaffirmed) >> rparen) |
107
- (space >> str("RA") >> space >> reaffirmation_year.as(:reaffirmed)) |
108
- (space >> str("RA") >> dash >> reaffirmation_year.as(:reaffirmed))
109
- end
110
-
111
- # Addendum code (single letter, multi-letter, or numeric-prefixed like 62o)
112
- rule(:addendum_code) do
113
- (digits.maybe >> letter.repeat(1, 3)).as(:addendum_code)
114
- end
115
-
116
- # Addendum date pattern (Month Day, Year - e.g., "January 22, 2019")
117
- rule(:month_name) do
118
- str("January") | str("February") | str("March") | str("April") | str("May") |
119
- str("June") | str("July") | str("August") | str("September") | str("October") |
120
- str("November") | str("December")
121
- end
122
-
123
- # The date is what tells two errata of one standard apart, so every part
124
- # of it is named. The leading space is part of the rule: without it the
125
- # date never matched after "Errata", and optional_suffix took it instead.
126
- rule(:errata_date) do
127
- # Full date: (Month Day, Year)
128
- (space.maybe >> lparen >> month_name.as(:month) >> space >>
129
- digit.repeat(1, 2).as(:day) >> comma.maybe >> (space | comma.maybe) >>
130
- year_digits.as(:errata_year) >> rparen).as(:errata_date) |
131
- # Month+day without year: (August 27)
132
- (space.maybe >> lparen >> month_name.as(:month) >> space >>
133
- digit.repeat(1, 2).as(:day) >> rparen).as(:errata_date) |
134
- # Numeric date with dash: (7-17- 2003) or (7-17-2003)
135
- (space.maybe >> lparen >>
136
- (digit.repeat(1, 2) >> dash >> digit.repeat(1, 2) >> dash >>
137
- space.maybe >> year_digits).as(:numeric_date) >>
138
- rparen).as(:errata_date)
139
- end
140
-
141
- # Errata suffix pattern - handles descriptive text like "– Spanish Edition" after "Errata"
142
- rule(:errata_suffix) do
143
- # Regular dash or em dash
144
- (space >> (str("-") | str("–")) >> space >> (letter >> (space | letter | digit | comma).repeat(0, 50))).repeat(
145
- 0, 3
146
- )
147
- end
148
-
149
- # The date and the descriptive suffix after "Errata", in either order. All
150
- # three errata_identifier branches share this rule: the publisher branch had
151
- # only the date-first order, so "Errata – Spanish Edition (June 2, 2010)"
152
- # lost its date to optional_suffix.
153
- rule(:errata_date_and_suffix) do
154
- (errata_suffix >> errata_date) |
155
- (errata_date >> errata_suffix) |
156
- errata_date |
157
- errata_suffix
158
- end
159
-
160
- # Errata suffix on addendum (e.g., "ASHRAE Addendum a to Standard 15-2001 Errata (July 6, 2021)")
161
- rule(:errata_suffix_on_addendum) do
162
- space >> str("Errata").as(:errata_keyword) >>
163
- (
164
- # Full date: (Month Day, Year)
165
- (space >> lparen >> month_name >> space >> digit.repeat(1,
166
- 2) >> comma.maybe >> (space | comma.maybe) >> year_digits >> rparen) |
167
- # Month+day without year: (August 27)
168
- (space >> lparen >> month_name >> space >> digit.repeat(1,
169
- 2) >> rparen) |
170
- # Numeric date with dash: (7-17-2003)
171
- (space >> lparen >> digit.repeat(1,
172
- 2) >> dash >> digit.repeat(1,
173
- 2) >> dash >> space.maybe >> year_digits >> rparen) |
174
- # No date at all
175
- str("")
176
- )
177
- end
178
-
179
- # Errata pattern (base identifier + " Errata (date)")
180
- rule(:errata_identifier) do
181
- # Format with copublisher: ANSI/ASHRAE Standard 105-2014 Errata (May 23, 2014)
182
- ((
183
- (
184
- (str("ANSI") >> dash >> str("ASHRAE")) |
185
- (str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
186
- 0, 10
187
- ))
188
- ).as(:copublisher) >> space >>
189
- type.as(:type) >> space >>
190
- code >>
191
- (space?.maybe >> dash >> year_digits.as(:year)).maybe >>
192
- (space >> additional_copublisher).maybe >>
193
- suffix.maybe >>
194
- reaffirmed.maybe
195
- ).as(:base) >>
196
- space >>
197
- str("Errata").as(:errata_keyword) >>
198
- errata_date_and_suffix.maybe >>
199
- optional_suffix.repeat(0, 2).as(:optional_suffixes)) |
200
- # Format with copublisher, missing type: ANSI/ASHRAE 51-1999 Errata (May 23, 2014)
201
- ((
202
- (
203
- (str("ANSI") >> dash >> str("ASHRAE")) |
204
- (str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
205
- 0, 10
206
- ))
207
- ).as(:copublisher) >> space >>
208
- code_with_year >>
209
- (space >> additional_copublisher).maybe >>
210
- suffix.maybe >>
211
- reaffirmed.maybe
212
- ).as(:base) >>
213
- space >>
214
- str("Errata").as(:errata_keyword) >>
215
- errata_date_and_suffix.maybe >>
216
- optional_suffix.repeat(0, 2).as(:optional_suffixes)) |
217
- # Format with publisher: ASHRAE Guideline 0-2005 Errata (September 28, 2011)
218
- ((
219
- publisher.as(:publisher) >> space >>
220
- type.as(:type) >> space >>
221
- code >>
222
- (space?.maybe >> dash >> year_digits.as(:year)).maybe >>
223
- (space >> additional_copublisher).maybe >>
224
- suffix.maybe >>
225
- reaffirmed.maybe
226
- ).as(:base) >>
227
- space >>
228
- str("Errata").as(:errata_keyword) >>
229
- errata_date_and_suffix.maybe >>
230
- optional_suffix.repeat(0, 2).as(:optional_suffixes))
231
- end
232
-
233
- # Interpretation pattern ("Interpretations for Standard X-YYYY")
234
- #
235
- # The `.as(:interpretation_identifier)` tag is what Builder#build
236
- # dispatches on. Without it the tree is a bare `{base: …}`, the builder
237
- # takes the plain-identifier path, and the interpretation becomes the
238
- # standard it interprets.
239
- rule(:interpretation_identifier) do
240
- (str("Interpretations") >> space >>
241
- str("for") >> space >>
242
- (type.as(:type) >> space >> code >>
243
- (dash >> year_digits.as(:year)).maybe).as(:base))
244
- .as(:interpretation_identifier)
245
- end
246
-
247
- # Combined Addenda pattern (multiple addendums grouped together)
248
- rule(:combined_addenda_identifier) do
249
- # Format 1: ASHRAE Addenda X, Y and Z to Standard/Guideline X-YYYY
250
- (
251
- publisher.as(:publisher) >> space >>
252
- str("Addenda") >> space >>
253
- addendum_code >> # First addendum code
254
- (
255
- (comma >> space >> str("and") >> space >> addendum_code) | # ", and b"
256
- (space >> str("and") >> space >> addendum_code) | # "and b"
257
- (comma >> space >> addendum_code).repeat(1, 50) | # comma-separated: "a, b, c"
258
- (comma >> addendum_code).repeat(1, 50) # comma without space: "a,b,c"
259
- ).repeat(0, 20).as(:additional_codes) >> # Multiple groups of codes
260
- space >>
261
- (str("to") | str("for")) >> space >>
262
- ((str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
263
- 0, 10
264
- )).as(:copublisher) >> space).maybe >>
265
- type.as(:type) >> space >>
266
- code >>
267
- (dash >> year_digits.as(:year)).maybe >>
268
- (space >> additional_copublisher).maybe >>
269
- addendum_date_suffix.maybe >>
270
- optional_suffix.repeat(0, 2)
271
- ).as(:combined_addenda) |
272
- # Format 2: ASHRAE Standard X-YYYY: Addenda a, b, c (colon separator)
273
- (
274
- publisher.as(:publisher) >> space >>
275
- type.as(:type) >> space >>
276
- code >>
277
- (dash >> year_digits.as(:year)) >>
278
- ((colon >> space) | space) >>
279
- str("Addenda") >> space >>
280
- addendum_code >> # First addendum code
281
- (
282
- # Just match all comma-separated values (including "and" as a code)
283
- (comma >> space >> addendum_code).repeat(1, 50) |
284
- # Or comma without space
285
- (comma >> addendum_code).repeat(1, 50)
286
- ).as(:additional_codes).maybe >> # Entire additional_codes is optional
287
- optional_suffix.repeat(0, 2)
288
- ).as(:combined_addenda) |
289
- # Format 3: ASHRAE Addenda a, b, c to Standard/Guideline X-YYYY (without publisher at start)
290
- (
291
- str("Addenda") >> space >>
292
- addendum_code >> # First addendum code
293
- (
294
- (comma >> space >> str("and") >> space >> addendum_code) | # ", and b"
295
- (space >> str("and") >> space >> addendum_code) | # "and b"
296
- (comma >> space >> addendum_code).repeat(1, 20) | # comma-separated
297
- (comma >> addendum_code).repeat(1, 20) # comma without space
298
- ).repeat(0, 50).as(:additional_codes) >> # Allow 0 for single addendum
299
- space >>
300
- (str("to") | str("for")) >> space >>
301
- ((str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
302
- 0, 10
303
- )).as(:copublisher) >> space).maybe >>
304
- type.as(:type) >> space >>
305
- code >>
306
- (dash >> year_digits.as(:year)).maybe >>
307
- optional_suffix.repeat(0, 2)
308
- ).as(:combined_addenda) |
309
- # Format 4: ASHRAE Addenda to Standard/Guideline X-YYYY (without specific codes)
310
- (
311
- publisher.as(:publisher) >> space >>
312
- str("Addenda") >> space >>
313
- (str("to") | str("for")) >> space >>
314
- type.as(:type) >> space >>
315
- code >>
316
- (dash >> year_digits.as(:year)).maybe >>
317
- optional_suffix.repeat(0, 2)
318
- ).as(:combined_addenda) |
319
- # Format 5: ASHRAE Addenda a, b, c to 105-2007 (missing type keyword)
320
- (
321
- publisher.as(:publisher) >> space >>
322
- str("Addenda") >> space >>
323
- addendum_code >>
324
- (
325
- (comma >> space >> str("and") >> space >> addendum_code) | # ", and b"
326
- (space >> str("and") >> space >> addendum_code) | # "and b"
327
- (comma >> space >> addendum_code).repeat(1, 20) | # comma-separated
328
- (comma >> addendum_code).repeat(1, 20) # comma without space
329
- ).repeat(0, 50).as(:additional_codes) >> # Allow 0 for single addendum
330
- space >>
331
- (str("to") | str("for")) >> space >>
332
- code_with_year >>
333
- optional_suffix.repeat(0, 2)
334
- ).as(:combined_addenda) |
335
- # Format 6: Addenda a, b, c to 105-2007 (without publisher, missing type)
336
- (
337
- str("Addenda") >> space >>
338
- addendum_code >>
339
- (
340
- (comma >> space >> str("and") >> space >> addendum_code) | # ", and b"
341
- (space >> str("and") >> space >> addendum_code) | # "and b"
342
- (comma >> space >> addendum_code).repeat(1, 20) | # comma-separated
343
- (comma >> addendum_code).repeat(1, 20) # comma without space
344
- ).repeat(0, 50).as(:additional_codes) >> # Allow 0 for single addendum
345
- space >>
346
- (str("to") | str("for")) >> space >>
347
- code_with_year >>
348
- optional_suffix.repeat(0, 2)
349
- ).as(:combined_addenda) |
350
- # Format 7: ASHRAE Standard X-YYYY: Addenda a, b, c (colon or space separator)
351
- (
352
- publisher.as(:publisher) >> space >>
353
- type.as(:type) >> space >>
354
- code >>
355
- (dash >> year_digits.as(:year)).maybe >>
356
- ((colon >> space) | space) >>
357
- str("Addenda") >> space >>
358
- addendum_code >> # First addendum code
359
- (
360
- # Range pattern: "a through z"
361
- (space >> str("through") >> space >> addendum_code) |
362
- # Space or comma separated codes (handles typos like "bl bq")
363
- ((space | (comma >> space)) >> addendum_code).repeat(1, 200) |
364
- # Just match all comma-separated values
365
- (comma >> space >> addendum_code).repeat(1, 200) |
366
- # Or comma without space
367
- (comma >> addendum_code).repeat(1, 200)
368
- ).as(:additional_codes).maybe >>
369
- (space >> additional_copublisher).maybe >>
370
- addendum_date_suffix.maybe >>
371
- optional_suffix.repeat(0, 2)
372
- ).as(:combined_addenda)
373
- end
374
-
375
- # Year-first Addenda Package pattern
376
- rule(:year_first_addenda_package_identifier) do
377
- (
378
- year_digits.as(:package_year) >> space >>
379
- (
380
- # Format 1: "YYYY Addenda Supplement Package to Standard X-YYYY"
381
- (str("Addenda") >> space >> str("Supplement") >> (space >> str("Package")).maybe >> space >> str("to") >> space >>
382
- ((str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
383
- 0, 10
384
- )).as(:copublisher) >> space).maybe >>
385
- type.as(:type) >> space >> code >> (dash >> year_digits.as(:year)).maybe) |
386
- # Format 2: "YYYY Supplement Addenda a, b, c to Standard X-YYYY"
387
- (str("Supplement") >> space >> str("Addenda") >> space >>
388
- addendum_code >>
389
- (
390
- (comma >> space >> addendum_code).repeat(1, 50) |
391
- (comma >> addendum_code).repeat(1, 50)
392
- ).as(:additional_codes).maybe >>
393
- space >> str("to") >> space >>
394
- ((str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
395
- 0, 10
396
- )).as(:copublisher) >> space).maybe >>
397
- type.as(:type) >> space >> code >> (dash >> year_digits.as(:year)).maybe
398
- ) |
399
- # Format 3: "YYYY Addenda Supplement to Standard X-YYYY" (without "Package")
400
- (str("Addenda") >> space >> str("Supplement") >> space >> str("to") >> space >>
401
- ((str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
402
- 0, 10
403
- )).as(:copublisher) >> space).maybe >>
404
- type.as(:type) >> space >> code >> (dash >> year_digits.as(:year)).maybe) |
405
- # Format 4: "YYYY Supplement to ANSI/ASHRAE/... Standard X-YYYY Errata (date)"
406
- (str("Supplement") >> space >> str("to") >> space >>
407
- ((str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
408
- 0, 10
409
- )).as(:copublisher) >> space).maybe >>
410
- type.as(:type) >> space >> code >> (dash >> year_digits.as(:year)).maybe >>
411
- errata_suffix_on_addendum.maybe)
412
- ) >>
413
- optional_suffix.repeat(0, 2)
414
- ).as(:addenda_package)
415
- end
416
-
417
- # Addenda Package pattern (collection packages)
418
- rule(:addenda_package_identifier) do
419
- # Format with copublisher: ANSI/ASHRAE Standard 34-2013 Addenda Supplement Package (...)
420
- (
421
- (str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
422
- 0, 10
423
- )).as(:copublisher) >> space >>
424
- type.as(:type) >> space >>
425
- code >>
426
- (dash >> year_digits.as(:year)).maybe >>
427
- ((colon >> space) | space) >>
428
- str("Addenda") >> space >>
429
- ((str("Supplement") >> (space >> str("Package")).maybe) | str("Package")).as(:package_description) >>
430
- (space >> str("for") >> space >> year_digits.as(:target_year)).maybe >>
431
- optional_suffix.repeat(0, 3)
432
- ).as(:addenda_package) |
433
- # Format with publisher + comma Addenda YEAR Supplement: ASHRAE Standard 90.1-2010, Addenda 2012 Supplement: Addenda Supplement Package
434
- (
435
- publisher.as(:publisher) >> space >>
436
- type.as(:type) >> space >>
437
- code >>
438
- (dash >> year_digits.as(:year)).maybe >>
439
- comma >> space >> str("Addenda") >> space >> year_digits.as(:package_year) >> space >> str("Supplement") >> colon >> space >>
440
- str("Addenda") >> space >>
441
- ((str("Supplement") >> (space >> str("Package")).maybe) | str("Package")).as(:package_description) >>
442
- optional_suffix.repeat(0, 3)
443
- ).as(:addenda_package) |
444
- # Standard format with publisher: ASHRAE Standard X-YYYY Addenda Supplement Package
445
- (
446
- publisher.as(:publisher) >> space >>
447
- type.as(:type) >> space >>
448
- code >>
449
- (dash >> year_digits.as(:year)).maybe >>
450
- (space >> year_digits.as(:package_year)).maybe >>
451
- additional_copublisher.maybe >>
452
- suffix.maybe >>
453
- reaffirmed.maybe >>
454
- ((colon >> space) | space) >>
455
- str("Addenda") >> space >>
456
- ((str("Supplement") >> (space >> str("Package")).maybe) | str("Package")).as(:package_description) >>
457
- (space >> str("for") >> space >> year_digits.as(:target_year)).maybe >>
458
- optional_suffix.repeat(0, 3)
459
- ).as(:addenda_package)
460
- end
461
-
462
- # Addendum date pattern (Month Day, Year - e.g., "January 22, 2019")
463
- rule(:addendum_date_suffix) do
464
- space >> lparen >> month_name >> space >> digit.repeat(1,
465
- 2) >> comma.maybe >> (space | comma.maybe) >> year_digits >> rparen
466
- end
467
-
468
- # Addendum pattern (multiple formats):
469
- rule(:addendum_identifier) do
470
- # Format: ASHRAE Addendum X to Standard/Guideline X-YYYY
471
- (
472
- publisher.as(:publisher) >> space >>
473
- str("Addendum") >> space >>
474
- addendum_code >>
475
- space >>
476
- (str("to") | str("for")) >> space >>
477
- type.as(:type) >> space >>
478
- code >>
479
- (dash >> year_digits.as(:year)).maybe >>
480
- additional_copublisher.maybe >>
481
- addendum_date_suffix.maybe >>
482
- errata_suffix_on_addendum.maybe >>
483
- optional_suffix.repeat(0, 2)
484
- ).as(:publisher_addendum) |
485
- # Format: ASHRAE Addendum X to ASHRAE Standard/Guideline X-YYYY (same publisher repeated)
486
- (
487
- publisher.as(:publisher) >> space >>
488
- str("Addendum") >> space >>
489
- addendum_code >>
490
- space >>
491
- (str("to") | str("for")) >> space >>
492
- publisher.as(:base_publisher) >> space >>
493
- type.as(:type) >> space >>
494
- code >>
495
- (dash >> year_digits.as(:year)).maybe >>
496
- additional_copublisher.maybe >>
497
- addendum_date_suffix.maybe >>
498
- errata_suffix_on_addendum.maybe >>
499
- optional_suffix.repeat(0, 2)
500
- ).as(:publisher_base_addendum) |
501
- # Format: [ANSI/ASHRAE] Addendum X to/for ASHRAE Standard/Guideline
502
- # X-YYYY (leading copublisher + publisher-led base)
503
- (
504
- (
505
- (str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
506
- 0, 10
507
- )).as(:copublisher) >>
508
- space
509
- ).maybe >>
510
- str("Addendum") >> space >>
511
- addendum_code >>
512
- space >>
513
- (str("to") | str("for")) >> space >>
514
- publisher.as(:base_publisher) >> space >>
515
- type.as(:type) >> space >>
516
- code >>
517
- (dash >> year_digits.as(:year)).maybe >>
518
- additional_copublisher.maybe >>
519
- addendum_date_suffix.maybe >>
520
- errata_suffix_on_addendum.maybe >>
521
- optional_suffix.repeat(0, 2)
522
- ).as(:publisher_base_addendum) |
523
- # Format: ASHRAE Addendum X to ANSI/ASHRAE Standard/Guideline X-YYYY (ASHRAE publisher + copublisher base)
524
- (
525
- publisher.as(:publisher) >> space >>
526
- str("Addendum") >> space >>
527
- addendum_code >>
528
- space >>
529
- (str("to") | str("for")) >> space >>
530
- (
531
- (str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
532
- 0, 10
533
- )).as(:copublisher) >>
534
- space
535
- ) >>
536
- type.as(:type) >> space >>
537
- code >>
538
- (dash >> year_digits.as(:year)).maybe >>
539
- addendum_date_suffix.maybe >>
540
- errata_suffix_on_addendum.maybe >>
541
- optional_suffix.repeat(0, 2)
542
- ).as(:publisher_addendum_copublisher) |
543
- # Format: ASHRAE Addendum X to ANSI/ASHRAE 62.1-2022 (publisher + copublisher, no type)
544
- (
545
- publisher.as(:publisher) >> space >>
546
- str("Addendum") >> space >>
547
- addendum_code >>
548
- space >>
549
- (str("to") | str("for")) >> space >>
550
- (
551
- (str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
552
- 0, 10
553
- )).as(:copublisher) >>
554
- space
555
- ) >>
556
- code_with_year >>
557
- additional_copublisher.maybe >>
558
- addendum_date_suffix.maybe >>
559
- errata_suffix_on_addendum.maybe >>
560
- optional_suffix.repeat(0, 2)
561
- ).as(:publisher_addendum_copublisher_no_type) |
562
- # Format: [ANSI/ASHRAE] Addendum X to/for [ANSI/ASHRAE] 34-2024 (no type)
563
- (
564
- (
565
- (str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
566
- 0, 10
567
- )).as(:copublisher) >>
568
- space
569
- ).maybe >>
570
- str("Addendum") >> space >>
571
- addendum_code >>
572
- space >>
573
- (str("to") | str("for")) >> space >>
574
- (
575
- (str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
576
- 0, 10
577
- )).as(:copublisher) >>
578
- space
579
- ).maybe >> # Make second copublisher optional
580
- code_with_year >>
581
- additional_copublisher.maybe >>
582
- addendum_date_suffix.maybe >>
583
- errata_suffix_on_addendum.maybe >>
584
- optional_suffix.repeat(0, 2)
585
- ).as(:addendum_no_type) |
586
- # Format: [ANSI/ASHRAE] Addendum X to/for [ANSI/ASHRAE] Standard/Guideline X-YYYY
587
- (
588
- (
589
- (str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
590
- 0, 10
591
- )).as(:copublisher) >>
592
- space
593
- ).maybe >>
594
- str("Addendum") >> space >>
595
- addendum_code >>
596
- space >>
597
- (str("to") | str("for")) >> space >>
598
- (
599
- (str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
600
- 0, 10
601
- )).as(:copublisher) >>
602
- space
603
- ).maybe >>
604
- type.as(:type) >> space >>
605
- code >>
606
- (dash >> year_digits.as(:year)).maybe >>
607
- additional_copublisher.maybe >>
608
- addendum_date_suffix.maybe >>
609
- errata_suffix_on_addendum.maybe >>
610
- optional_suffix.repeat(0, 2)
611
- ).as(:addendum) |
612
- # Format: ASHRAE Standard 15-2013 Addendum a
613
- (
614
- publisher.as(:publisher) >> space >>
615
- type.as(:type) >> space >>
616
- code >>
617
- (dash >> year_digits.as(:year)).maybe >>
618
- additional_copublisher.maybe >>
619
- suffix.maybe >>
620
- reaffirmed.maybe >>
621
- ((colon >> space) | space) >>
622
- (str("Addendum") | str("addendum")) >> space >>
623
- addendum_code >>
624
- addendum_date_suffix.maybe >>
625
- errata_suffix_on_addendum.maybe >>
626
- optional_suffix.repeat(0, 2)
627
- ).as(:standard_addendum) |
628
- # Format: ASHRAE Standard 15-2013 Addendum a (with lowercase and space only)
629
- (
630
- publisher.as(:publisher) >> space >>
631
- type.as(:type) >> space >>
632
- code >>
633
- (dash >> year_digits.as(:year)).maybe >>
634
- additional_copublisher.maybe >>
635
- suffix.maybe >>
636
- reaffirmed.maybe >>
637
- space >>
638
- (str("Addendum") | str("addendum")) >> space >>
639
- addendum_code >>
640
- addendum_date_suffix.maybe >>
641
- errata_suffix_on_addendum.maybe >>
642
- optional_suffix.repeat(0, 2)
643
- ).as(:standard_addendum)
644
- end
645
-
646
- # Main identifier pattern
647
- rule(:identifier) do
648
- errata_identifier |
649
- interpretation_identifier |
650
- combined_addenda_identifier |
651
- year_first_addenda_package_identifier |
652
- addenda_package_identifier |
653
- addendum_identifier |
654
- # Standalone copublisher pattern (ANSI/AMCA 210-99, AMCA 210-99).
655
- # The literal "ASHRAE" is excluded here so a bare "ASHRAE <code>-<year>"
656
- # is not swallowed as a copublisher and instead falls through to the
657
- # ASHRAE-publisher branch below.
658
- (((str("ANSI") >> slash).maybe >> (str("ASHRAE").absent? >> letter).repeat(2,
659
- 10)).as(:copublisher) >>
660
- space >>
661
- code_with_year >>
662
- optional_suffix.repeat(0, 2)) |
663
- # Copublished patterns without type (ANSI/ASHRAE 34-2024)
664
- ((str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
665
- 0, 10
666
- )).as(:copublisher) >>
667
- space >>
668
- code_with_year >>
669
- (space >> additional_copublisher).maybe >>
670
- suffix.maybe >>
671
- reaffirmed.maybe >>
672
- optional_suffix.repeat(0, 2)) |
673
- # Copublished patterns with type (ANSI/ASHRAE, ANSI/ASHRAE/ACCA, ANSI/ASHRAE/ASHE/ICC, etc.)
674
- ((str("ANSI") >> slash >> str("ASHRAE") >> (slash >> letter.repeat(3, 10)).repeat(
675
- 0, 10
676
- )).as(:copublisher) >>
677
- space >>
678
- type.as(:type).maybe >> space.maybe >>
679
- code >>
680
- (space?.maybe >> dash >> year_digits.as(:year)).maybe >>
681
- (space >> additional_copublisher).maybe >>
682
- suffix.maybe >>
683
- reaffirmed.maybe >>
684
- optional_suffix.repeat(0, 2)) |
685
- # Standard ASHRAE pattern. The type keyword is optional so a bare
686
- # partial reference "ASHRAE <code>" (no "Standard"/"Guideline") parses;
687
- # the builder defaults the absent type to "Standard".
688
- (publisher.as(:publisher) >> space >>
689
- (type.as(:type) >> space).maybe >>
690
- code >>
691
- (space?.maybe >> dash >> year_digits.as(:year)).maybe >>
692
- (space >> additional_copublisher).maybe >>
693
- suffix.maybe >>
694
- reaffirmed.maybe >>
695
- optional_suffix.repeat(0, 2)
696
- ).as(:base)
697
- end
698
-
699
- root(:identifier)
700
-
701
- # Pre-parse ingestion normalizations (R2): every parse path —
702
- # parslet and PG artifact alike — feeds the grammar the same
703
- # normalized string.
7
+ class Parser
8
+ # Pre-parse ingestion normalizations (R2): every parse path feeds
9
+ # the grammar the same normalized string.
704
10
  def self.normalize_input(string)
705
11
  # Strip leading/trailing whitespace
706
12
  cleaned = string.strip
@@ -771,19 +77,6 @@ module Pubid
771
77
 
772
78
  cleaned
773
79
  end
774
-
775
- def self.parse(string)
776
- unless string.is_a?(String)
777
- raise Pubid::Errors::InvalidInputError,
778
- Pubid::INPUT_NOT_A_STRING_MESSAGE
779
- end
780
-
781
- if string.length > Pubid::MAX_INPUT_LENGTH
782
- raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
783
- end
784
-
785
- new.parse(normalize_input(string))
786
- end
787
80
  end
788
81
  end
789
82
  end