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,782 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Itu
7
- class Parser < ::Pubid::Parser::Grammar
8
- # Basic elements
9
- rule(:digit) { match["0-9"] }
10
- rule(:digits) { digit.repeat(1) }
11
- rule(:letter) { match["A-Z"] }
12
- rule(:space) { str(" ") }
13
- rule(:dash) { str("-") }
14
- rule(:dot) { str(".") }
15
-
16
- # ITU prefix — accepts an optional leading "Recommendation " long-form
17
- # prefix that appears in ITU's own citation style (e.g. the twin-text
18
- # and common-text forms in issues #233 / #234).
19
- rule(:itu_prefix) do
20
- (str("Recommendation ") >> str("ITU") >> (dash | space)) |
21
- (str("ITU") >> (dash | space))
22
- end
23
-
24
- # Sector (R, T, or D)
25
- rule(:sector) do
26
- (str("R") | str("T") | str("D")).as(:sector)
27
- end
28
-
29
- # Series (BO, V, X, etc.)
30
- rule(:series) do
31
- (
32
- # Study groups: SG1, SG12
33
- (str("SG") >> digits) |
34
- # Regular series: BO, V, X, etc.
35
- letter.repeat(1, 3)
36
- ).as(:series)
37
- end
38
-
39
- # A numeric series GROUP rather than a single document — "E-100",
40
- # "E100-300", "G-100", "Q-500". Deliberately capped at ONE leading
41
- # letter: two or more letters before the dash is a series-code DOCUMENT
42
- # (EMC-5, MES-2, IMPL-8, SEC-QKD), handled by series_code_body, and the
43
- # two must never shadow each other. That split is a corpus regularity,
44
- # not an ITU rule — see the boundary spec.
45
- rule(:series_group) do
46
- (letter >> digits.maybe >> dash >> digits).as(:series)
47
- end
48
-
49
- # The literal word "series" — "ITU-T E-300 series Suppl. 1",
50
- # "ITU-T E.1100 series Suppl. 1". It is NOT part of the series token (it
51
- # also follows a dotted code, where folding it in would corrupt the value
52
- # every index row is keyed on), so it is carried as its own marker.
53
- rule(:series_word) { space >> str("series").as(:series_word) }
54
-
55
- # Number
56
- rule(:number) do
57
- digits.as(:number)
58
- end
59
-
60
- rule(:subseries) do
61
- dot >> digits.as(:subseries)
62
- end
63
-
64
- # Edition words. "bis" = 2nd edition, "ter" = 3rd, "quater" = 4th.
65
- # "quarter" is a real misspelling in ITU's own data, kept verbatim so the
66
- # two records it appears on stay distinct from their "quater" siblings.
67
- rule(:series_suffix_word) do
68
- str("bis") | str("ter") | str("quater") | str("quarter")
69
- end
70
-
71
- # Edition suffix attached directly to the recommendation number.
72
- # Example: X.50bis, V.8bis, V.31bis.
73
- rule(:series_suffix) { series_suffix_word.as(:series_suffix) }
74
-
75
- # The same word separated by a space — "ITU-T E.250 bis (12/1972)",
76
- # "ITU-T V.25 ter". The captured space IS the marker: its presence sets
77
- # Code#series_suffix_spaced, so the glued form above still round-trips
78
- # byte-exactly (locked by recommendation_spec.rb's issue-#231 block).
79
- rule(:series_suffix_spaced) do
80
- space.as(:series_suffix_spaced) >> series_suffix_word.as(:series_suffix)
81
- end
82
-
83
- # A single trailing letter distinguishing variants of one Recommendation:
84
- # "ITU-T D.200 R", "ITU-T Q.2931 B", "ITU-T R.38 A", the glued
85
- # "ITU-T D.502R" and the lowercase "ITU-T I.256.2a".
86
- #
87
- # TWO guards make this safe, and both are load-bearing:
88
- # * the A-R cap keeps it clear of the "S" of " Suppl." and the "V" of a
89
- # bare version marker (" V2");
90
- # * the trailing absent? forbids any further letter/digit, so " Amd."
91
- # (A then "m"), " Add.", " Annex A" (A then "n"), " App." , " Cor."
92
- # (C then "o") and " Err." (E then "r") can never be mistaken for a
93
- # qualifier.
94
- # It cannot collide with `language` (dash-attached, "-F") nor with
95
- # date_part/version_part, which both need "(" after the space.
96
- rule(:qualifier_letter) do
97
- (match["A-R"] | match["a-r"]).as(:qualifier) >>
98
- match["A-Za-z0-9"].absent?
99
- end
100
-
101
- rule(:qualifier_glued) { qualifier_letter }
102
- rule(:qualifier_spaced) do
103
- space.as(:qualifier_spaced) >> qualifier_letter
104
- end
105
-
106
- # Print-form suffixes that may trail a code with a separating space. They
107
- # cannot live inside `code` itself, because the space would then also be
108
- # offered to " Suppl." / " Annex" and would have to backtrack out of a
109
- # rule the wrappers depend on. Spliced after `code` and BEFORE
110
- # combined_suffixes, since the corpus attaches the primary designation's
111
- # suffix before the "/" ("ITU-T D.301 R/F.66").
112
- rule(:code_suffixes) do
113
- series_suffix_spaced.maybe >> qualifier_spaced.maybe
114
- end
115
-
116
- # "ITU-T H.350 attachment (08/2003)" — the machine-readable attachment
117
- # published alongside a Recommendation, a distinct record in ITU's
118
- # catalogue. A bare marker word: the flag alone reconstructs it.
119
- rule(:attachment) { space >> str("attachment").as(:attachment) }
120
-
121
- # A range of Recommendations published as one document —
122
- # "ITU-T Q.120-Q.139 (11/1988)". No conflict with `parts`, which requires
123
- # digits after the dash and so cannot match "-Q.139".
124
- rule(:range_end) do
125
- dash >> (letter.repeat(1, 3) >> dot >> digits).as(:range_end)
126
- end
127
-
128
- # A "-YYYYMM" approval date — the "200307" of "T-REC-T.4-200307-I" and
129
- # "ITU-T T.4-200307". ITU's own edition suffix is short ("-5"), so six
130
- # digits that read as a plausible year (19xx/20xx) and month (01-12) are
131
- # the date, never a part. A six-digit run that fails either test
132
- # ("-200313", "-180001") is still a part, as it was before.
133
- rule(:yyyymm_year) { (str("19") | str("20")) >> digit >> digit }
134
- rule(:yyyymm_month) do
135
- (str("0") >> match["1-9"]) | (str("1") >> match["0-2"])
136
- end
137
- rule(:yyyymm_shape) { yyyymm_year >> yyyymm_month >> digit.absent? }
138
-
139
- # The status letter that trails the date in a publication id — "I" (in
140
- # force) or "S" (superseded). It names the state of the edition, not the
141
- # edition, so it is parsed and dropped. "S" is also the Spanish language
142
- # suffix, so it is a status ONLY in the full "T-REC-…" id, where ITU
143
- # always writes one; after an "ITU-T …-YYYYMM" print form only "I" is,
144
- # and "-S" stays the language ("ITU-T Z.100-199911-S").
145
- rule(:id_status) do
146
- dash >> match["IS"] >> match["A-Za-z0-9"].absent?
147
- end
148
-
149
- rule(:print_id_status) do
150
- dash >> str("I") >> match["A-Za-z0-9"].absent?
151
- end
152
-
153
- rule(:yyyymm_date) do
154
- dash >> yyyymm_year.as(:year) >> yyyymm_month.as(:month) >>
155
- digit.absent?
156
- end
157
-
158
- rule(:id_date) { yyyymm_date >> print_id_status.maybe }
159
-
160
- # Either date spelling of a Recommendation.
161
- rule(:document_date) { date_part | id_date }
162
-
163
- # "ITU-T REC T.4", "ITU-T REC-T.4" — the redundant type word of ITU's
164
- # own URLs. Not captured: a Recommendation is the default type.
165
- rule(:rec_word) { str("REC") >> (space | dash) }
166
-
167
- # Parts. The yyyymm guard keeps the approval date out of the part list.
168
- rule(:part) do
169
- dash >> yyyymm_shape.absent? >> digits.as(:part)
170
- end
171
-
172
- rule(:parts) { part.repeat(0).as(:parts) }
173
-
174
- # Code = imp-prefixed code (Implementers' Guide) or standard code.
175
- # The imp_code alternative is tried first because the standard code's
176
- # number rule requires digits, which "Imp712" / "ImpOSI" would not
177
- # satisfy.
178
- rule(:code) do
179
- imp_code | standard_code
180
- end
181
-
182
- # The glued qualifier sits last, after parts, so "BO.600-1" still parses
183
- # and "D.502R" / "I.256.2a" newly do.
184
- rule(:standard_code) do
185
- number >> series_suffix.maybe >> subseries.maybe >> parts >>
186
- qualifier_glued.maybe
187
- end
188
-
189
- # Implementers' Guide code: "Imp" prefix followed by digits or a
190
- # short letter string. Examples: Imp712 (G.Imp712), ImpOSI (X.ImpOSI).
191
- rule(:imp_code) do
192
- str("Imp").as(:imp_marker) >>
193
- (digits | letter.repeat(1, 3)).as(:number) >>
194
- subseries.maybe >>
195
- parts
196
- end
197
-
198
- # Date
199
- rule(:date_part) do
200
- space >> str("(") >>
201
- (digits.as(:month) >> str("/")).maybe >>
202
- digit.repeat(4, 4).as(:year) >>
203
- str(")")
204
- end
205
-
206
- # Version marker — "(V14)" in "ITU-T H.264 (V14) (08/2021)". Always sits
207
- # between the code and the publication date, so it is spliced in before
208
- # `date_part.maybe` in every document rule. `date_part` requires digits
209
- # after "(", so it fails cleanly on "(V…)" and the two never compete.
210
- # ITU emits four spellings of the same marker: the canonical
211
- # parenthesised "(V14)" plus the bare "V2", "v10" and "v.1". All land in
212
- # the same `version` attribute and re-render canonically as " (V##)" —
213
- # the one deliberate non-byte-exact normalisation in this grammar.
214
- #
215
- # The bare branch requires digits immediately after V/v so it cannot fire
216
- # on a stray word, and it is unreachable by the qualifier_letter rule
217
- # below, whose A-R cap deliberately excludes "V".
218
- rule(:version_part) do
219
- space >>
220
- (
221
- (str("(V") >> digits.as(:version) >> str(")")) |
222
- ((str("V") | str("v")) >> dot.maybe >> digits.as(:version))
223
- )
224
- end
225
-
226
- # Language
227
- rule(:language) do
228
- dash >> match["EFASCR"].as(:language)
229
- end
230
-
231
- # Combined (joint) recommendation — one additional "/SERIES.CODE"
232
- # designation, e.g. the "/Y.1351" of "G.780/Y.1351". Repeatable, so
233
- # triple-joint forms ("G.780/Y.1351/Z.1362") are captured as a list.
234
- # Each designation is a self-contained subtree under :designation; the
235
- # repeated list is collected under :combined.
236
- # A designation may carry the same print-form suffixes as the primary
237
- # code — "ITU-T D.300 R/E.282 R" (both halves qualified),
238
- # "ITU-T D.81/F.80 bis" and "ITU-T E.211/Q.11 quater" (only the last).
239
- # The suffix is stored on the designation it printed against; no attempt
240
- # is made to decide whether it semantically qualifies the joint document.
241
- #
242
- # These slots sit INSIDE the .as(:designation) subtree, so they consume
243
- # " bis" before the outer code_suffixes (which precedes combined_suffixes)
244
- # can see it — that is what makes "trails the last designation" work
245
- # without ambiguity.
246
- rule(:combined_designation) do
247
- str("/") >>
248
- (
249
- series >> dot >> digits.as(:number) >>
250
- subseries.maybe >> parts >>
251
- qualifier_glued.maybe >>
252
- series_suffix_spaced.maybe >>
253
- qualifier_spaced.maybe
254
- ).as(:designation)
255
- end
256
-
257
- rule(:combined_suffixes) do
258
- combined_designation.repeat(1).as(:combined)
259
- end
260
-
261
- # The "Technical" qualifier of a Corrigendum — "ITU-T H.222.0 (1995)
262
- # Technical Cor. 1 (02/1998)". It is captured as its OWN marker key
263
- # rather than folded into the supplement_type alternation, so the type
264
- # token stays the plain "Cor." that Builder#build_supplement's case maps.
265
- # It is bound to the Cor. branch alone, NOT to the head of
266
- # supplement_type: only a corrigendum is ever printed "Technical", and
267
- # only Corrigendum carries the flag. Offered before any token, the
268
- # marker would be accepted on "Technical Err. 1" / "Technical Amd. 1"
269
- # and then silently dropped by the builder, collapsing those onto a
270
- # different document's identifier.
271
- rule(:technical_marker) do
272
- str("Technical").as(:technical) >> space
273
- end
274
-
275
- # Supplement types. The Cor. branch is its own alternative so the
276
- # optional "Technical" can precede it without landing inside the
277
- # .as(:supplement_type) capture — the builder maps that token by string
278
- # equality. It goes last, which is free: no other branch can match a
279
- # leading "Technical" or "Cor".
280
- rule(:supplement_type) do
281
- (
282
- str("Suppl.") | str("Suppl") |
283
- str("Amd.") | str("Amd") |
284
- str("Add.") | str("Add") |
285
- str("Err.") | str("Err")
286
- ).as(:supplement_type) |
287
- (technical_marker.maybe >>
288
- (str("Cor.") | str("Cor")).as(:supplement_type))
289
- end
290
-
291
- # Supplement number. ITU omits the space before the ordinal on some
292
- # listings ("ITU-T E Suppl.1", "ITU-T D.211 Suppl.1") and the ordinal is
293
- # itself dotted on the M/O series ("ITU-T M Suppl. 1.1"). The absent
294
- # space is captured as a marker so `to_s` reproduces it; the dotted
295
- # ordinal lands whole in the :string `number` and cannot run into a
296
- # following date, because (dot >> digits) stops at the space before "(".
297
- rule(:supplement_number) do
298
- space.as(:supplement_space).maybe >>
299
- (digits >> (dot >> digits).repeat(0)).as(:supplement_number)
300
- end
301
-
302
- # Supplement date (separate from base date)
303
- rule(:supplement_date) do
304
- space >> str("(") >>
305
- (digits.as(:supplement_month) >> str("/")).maybe >>
306
- digit.repeat(4, 4).as(:supplement_year) >>
307
- str(")")
308
- end
309
-
310
- # Base identifier for supplements
311
- rule(:base_with_series) do
312
- itu_prefix >>
313
- sector >>
314
- space >>
315
- rec_word.maybe >>
316
- series >> dot >>
317
- code >>
318
- range_end.maybe >>
319
- code_suffixes >>
320
- combined_suffixes.maybe >>
321
- series_word.maybe >>
322
- attachment.maybe >>
323
- version_part.maybe >>
324
- document_date.maybe
325
- end
326
-
327
- rule(:base_without_series) do
328
- itu_prefix >>
329
- sector >>
330
- space >>
331
- code >>
332
- code_suffixes >>
333
- attachment.maybe >>
334
- version_part.maybe >>
335
- document_date.maybe
336
- end
337
-
338
- # A series-code document — "EMC-5", "MES-2", "QOS-2", "IMPL-8",
339
- # "SEC-QKD": a multi-letter mnemonic series dash-joined to a numeric or
340
- # alphabetic document code. TWO letters minimum is the discriminator
341
- # against series_group ("E-100", "G-100"), which has exactly one.
342
- #
343
- # This rule carries its own letter repeat, so the 1..3 cap on `series`
344
- # (which stops the series token eating a following word) is untouched
345
- # and "IMPL" still parses. It is deliberately a flat two-token shape
346
- # (series + dash + alphanumeric number) rather than an optional middle
347
- # segment: a greedy `letter.repeat(2)` would take "SEC", an optional
348
- # "-QKD" would follow, and the required dash would then fail at
349
- # end-of-input with no backtracking into the satisfied `.maybe`.
350
- #
351
- # The number stays in `code.number`, so `root.number` — the field
352
- # relaton-index bsearches on — is "5" for EMC-5 and "QKD" for SEC-QKD
353
- # rather than nil.
354
- # The OB guard keeps "ITU-T OB-1" a clean parse failure rather than a
355
- # Recommendation of a series "OB" — the Operational Bulletin's series
356
- # name. It guards "OB" + dash specifically, so a genuine two-letter
357
- # mnemonic starting "OB" would still parse.
358
- rule(:series_code_body) do
359
- (str("OB") >> dash).absent? >>
360
- letter.repeat(2).as(:series) >> dash.as(:series_dash) >>
361
- (digits | letter.repeat(1)).as(:number) >> parts
362
- end
363
-
364
- # ITU Contribution (Temporary Document) — "ITU-R SG17-C1000" (pubid#340).
365
- # The "-C" marker before the number distinguishes it from every
366
- # neighbouring shape: a series-code document's post-dash number starts
367
- # with digits or is all letters (never "C"+digits), and with_series
368
- # requires a dot after the series.
369
- rule(:contribution) do
370
- itu_prefix >>
371
- sector >>
372
- space >>
373
- series >>
374
- str("-C").as(:contribution_marker) >>
375
- number >>
376
- parts >>
377
- language.maybe
378
- end
379
-
380
- rule(:base_series_code) do
381
- itu_prefix >>
382
- sector >>
383
- space >>
384
- series_code_body >>
385
- version_part.maybe >>
386
- date_part.maybe
387
- end
388
-
389
- rule(:series_code_identifier) { base_series_code >> language.maybe }
390
-
391
- # ITU-R Report — "Report ITU-R BT.2020-1 (2000)". Reports number
392
- # independently of Recommendations, so the leading word is the ONLY thing
393
- # distinguishing two real, different documents that share a
394
- # series/number/edition. It is captured (unlike the decorative
395
- # "Recommendation " of `itu_prefix`) so it reaches the built object.
396
- rule(:report_word) { str("Report").as(:report_marker) }
397
-
398
- # One body for both the with- and without-series shapes: `(series >> dot)`
399
- # backtracks as a unit, so a numeric code ("Report ITU-R 2205") simply
400
- # skips it.
401
- #
402
- # Deliberately NO combined_suffixes: a joint "Report X/Y" would reach
403
- # Builder#build's `combined` branch, which builds a CombinedIdentifier and
404
- # would silently drop the marker — a clean parse failure is better than a
405
- # Report that comes back as a Recommendation. The OB guard mirrors
406
- # series_code_body's: "Report ITU-T OB.1" would otherwise route to
407
- # SpecialPublication with the marker dropped.
408
- rule(:report_body) do
409
- (str("OB") >> dot).absent? >>
410
- (series >> dot).maybe >>
411
- code >>
412
- range_end.maybe >>
413
- code_suffixes >>
414
- series_word.maybe >>
415
- attachment.maybe >>
416
- version_part.maybe >>
417
- date_part.maybe
418
- end
419
-
420
- # Both spellings ITU and downstream bibliographies use. `to_s` renders
421
- # the leading (ITU's own) form for either.
422
- rule(:base_report) do
423
- (report_word >> space >> itu_prefix >> sector >> space >> report_body) |
424
- (itu_prefix >> sector >> space >> report_word >> space >> report_body)
425
- end
426
-
427
- # Slot in `rule(:identifier)`: AFTER supplement/annex/appendix (all of
428
- # which reach a Report through `base` and match the longer wrapped form
429
- # "Report ITU-R BT.2020-1 Suppl. 1"), and BEFORE with_series, which would
430
- # match the infix spelling's "ITU-R" prefix and then fail on the
431
- # unconsumed " Report ...".
432
- rule(:report_identifier) { base_report >> language.maybe }
433
-
434
- # base_series_code goes LAST: it cannot shadow base_with_series (which
435
- # needs series >> dot — "EMC" then "." fails on "-", and the greedy
436
- # "IMP" of "IMPL" then fails on "L") nor base_without_series (whose code
437
- # needs digits or "Imp"), so the last slot is both safe and the smallest
438
- # blast radius.
439
- # base_report goes FIRST and is order-free: `itu_prefix` admits only
440
- # "Recommendation " or "ITU", and `letter` is [A-Z], so neither `series`
441
- # (of base_with_series) nor `series_code_body` can consume the "Report"
442
- # token of either spelling. Its presence is what lets a supplement, annex
443
- # or appendix wrap a Report.
444
- rule(:base) do
445
- base_report | base_with_series | base_without_series | base_series_code
446
- end
447
-
448
- # Annex label — one series letter, optionally a number ("F3") and/or a
449
- # "+" ("C+", ITU-T G.729 Annex C+).
450
- rule(:annex_label) do
451
- letter >> digits.maybe >> str("+").maybe
452
- end
453
-
454
- # A labelled annex of a Recommendation — "ITU-T A.23 Annex A (06/2014)".
455
- # The annexed document is wrapped in :base so its own date (the leading
456
- # "(2002)" of "ITU-T X.692 (2002) Annex E (03/2002)") lands one level down
457
- # and cannot collide with the annex's date — Parslet silently keeps only
458
- # the last of two same-named keys in one flattened hash.
459
- rule(:annex_body) do
460
- base.as(:base) >>
461
- space >> str("Annex") >> space >>
462
- annex_label.as(:annex_number) >>
463
- date_part.maybe
464
- end
465
-
466
- rule(:annex_identifier) { annex_body >> language.maybe }
467
-
468
- # Appendix label — a Roman numeral ("I", "II", "IV", "VI").
469
- rule(:roman) { match["IVX"].repeat(1) }
470
-
471
- # A labelled appendix of a Recommendation — "ITU-T G.101 App. I
472
- # (05/2000)", "ITU-T O.9 App.I (1998)". Structurally identical to
473
- # annex_body, and for the same reason: the appendixed document is wrapped
474
- # in :base so its own date (the leading "(1988)" of "ITU-T G.722 (1988)
475
- # App. IV (11/2006)") lands one level down and cannot collide with the
476
- # appendix's — Parslet silently keeps only the last of two same-named
477
- # keys in one flattened hash. The space after "App." is optional in ITU's
478
- # listings; rendering normalises it back to the spaced spelling.
479
- rule(:appendix_body) do
480
- base.as(:base) >>
481
- space >> str("App.") >> space.maybe >>
482
- roman.as(:appendix_number) >>
483
- appendix_material.maybe >>
484
- date_part.maybe
485
- end
486
-
487
- # The companion artefact of an appendix, published as its own record —
488
- # "ITU-T G.726 App. II test vectors (03/1991)",
489
- # "ITU-T G.728 App. I Software (07/1995)". A closed set of two literals,
490
- # not free text, so it cannot swallow an unrelated tail.
491
- rule(:appendix_material) do
492
- space >> (str("test vectors") | str("Software")).as(:appendix_material)
493
- end
494
-
495
- rule(:appendix_identifier) { appendix_body >> language.maybe }
496
-
497
- # Supplement with base identifier (has recommendation number).
498
- # The base may itself be an annex ("... Annex B (1996) Cor. 3 (03/2001)");
499
- # annex_body is tried first because it is the longer match. When it fails
500
- # (an ordinary supplement, where " Suppl." follows the code instead of
501
- # " Annex") the alternation falls through to the plain base.
502
- rule(:supplement_with_base) do
503
- (annex_body.as(:base) | appendix_body.as(:base) | base.as(:base)) >>
504
- space >>
505
- supplement_type >>
506
- supplement_number >>
507
- supplement_date.maybe >>
508
- language.maybe
509
- end
510
-
511
- # A supplement whose base is itself a supplement — e.g. Errata on an
512
- # Amendment ("G.9701 (2014) Amd. 3 Err. 1 (12/2017)") or Errata on a
513
- # Corrigendum. The parse tree nests base-inside-base so the builder
514
- # produces a Supplement whose base is another Supplement.
515
- # The inner supplement may itself be the base-less, series-only form —
516
- # "ITU-T G Suppl. 39 (2006) Err. 1 (08/2006)". supplement_with_base is
517
- # tried first, being the longer match.
518
- rule(:chained_supplement) do
519
- (supplement_with_base | supplement_series_only).as(:base) >>
520
- space >>
521
- supplement_type >>
522
- supplement_number >>
523
- supplement_date.maybe >>
524
- language.maybe
525
- end
526
-
527
- # Supplement without base (series-only, like "ITU-T H Suppl. 1")
528
- # series_group is tried FIRST: `series` is a greedy, non-backtracking
529
- # letter.repeat(1,3), so it would match the "E" of "E-100" and then fail
530
- # on the required space, with no retry at a shorter length.
531
- rule(:supplement_series_only) do
532
- itu_prefix >>
533
- sector >>
534
- space >>
535
- (series_group | series) >>
536
- series_word.maybe >>
537
- space >>
538
- supplement_type >>
539
- supplement_number >>
540
- supplement_date.maybe >>
541
- language.maybe
542
- end
543
-
544
- # A supplement joined to its predecessor by "/" rather than a space —
545
- # "ITU-T X.680 (1994) Amd. 1/Technical Cor. 1 (12/1997)": one Technical
546
- # Corrigendum issued against Amendment 1, sharing the trailing date.
547
- # Modelled exactly like chained_supplement (base-inside-base) with a
548
- # :supplement_slash marker so `to_s` reproduces the "/".
549
- rule(:slash_chained_supplement) do
550
- supplement_with_base.as(:base) >>
551
- str("/").as(:supplement_slash) >>
552
- supplement_type >>
553
- supplement_number >>
554
- supplement_date.maybe >>
555
- language.maybe
556
- end
557
-
558
- # Complete supplement identifier. Both chained forms must precede
559
- # supplement_with_base: it alone matches "ITU-T X.680 (1994) Amd. 1" and
560
- # would leave "/Technical Cor. 1 (12/1997)" unconsumed, which fails the
561
- # whole parse — PEG ordered choice never re-enters the alternation once
562
- # an alternative succeeds. Space- and slash-joined are disjoint at that
563
- # position, so their relative order is free.
564
- rule(:supplement_identifier) do
565
- chained_supplement | slash_chained_supplement |
566
- supplement_with_base | supplement_series_only
567
- end
568
-
569
- # With series: ITU-R BO.600-1
570
- rule(:with_series) do
571
- itu_prefix >>
572
- sector >>
573
- space >>
574
- rec_word.maybe >>
575
- series >> dot >>
576
- code >>
577
- range_end.maybe >>
578
- code_suffixes >>
579
- combined_suffixes.maybe >>
580
- series_word.maybe >>
581
- attachment.maybe >>
582
- version_part.maybe >>
583
- document_date.maybe >>
584
- language.maybe
585
- end
586
-
587
- # Without series: ITU-R 20-200
588
- rule(:without_series) do
589
- itu_prefix >>
590
- sector >>
591
- space >>
592
- code >>
593
- code_suffixes >>
594
- attachment.maybe >>
595
- version_part.maybe >>
596
- document_date.maybe >>
597
- language.maybe
598
- end
599
-
600
- # ITU's publication id — "T-REC-T.4-200307-I",
601
- # "R-REC-BO.1130-5-202602-I": <sector>-REC-<number>[-<edition>]-<YYYYMM>
602
- # [-<status>], the name ITU gives each edition in its URLs and PDF
603
- # files. It builds the plain Recommendation it names and renders in the
604
- # print form ("ITU-T T.4 (07/2003)"). The date is required: without it
605
- # the string names no edition. No other rule starts with a bare sector
606
- # letter, so the slot is free.
607
- rule(:publication_id) do
608
- sector >> dash >> str("REC") >> dash >>
609
- series >> dot >> code >> yyyymm_date >> id_status.maybe >>
610
- language.maybe
611
- end
612
-
613
- # The Radio Regulations — "ITU-R RR", "ITU-R RR (2020)", and the URL
614
- # spelling "ITU-R RR-2020". Always ITU-R. The trailing any.absent? is
615
- # load-bearing: PEG ordered choice never re-enters the alternation once
616
- # an alternative succeeds, so a partial match on "ITU-R RR.1" must fail
617
- # here and fall through to with_series.
618
- rule(:radio_regulations) do
619
- itu_prefix >> str("R").as(:sector) >> space >>
620
- str("RR").as(:radio_regulations) >>
621
- (date_part | (dash >> digit.repeat(4, 4).as(:year))).maybe >>
622
- language.maybe >> any.absent?
623
- end
624
-
625
- # OB (Operational Bulletin) — Special Publication.
626
- # OB is a cross-bureau ITU publication. The TSB spelling carries a
627
- # sector ("ITU-T OB.1096 (2016)"); the builder keeps it, and it renders
628
- # back, but it is not part of the bulletin's identity.
629
- rule(:ob_series) { str("OB").as(:series) }
630
-
631
- rule(:ob_dot_body) { dot >> number }
632
- rule(:ob_no_body) { space >> str("No.") >> space >> number }
633
- # "ITU OB 1000" — metanorma-itu's docidentifier ("Annex to ITU OB %").
634
- # Accepted as an input spelling only; it renders "ITU OB No. 1000", the
635
- # form ITU's own bulletin site uses.
636
- rule(:ob_bare_body) { space >> number }
637
-
638
- # The date as a bulletin prints it — "ITU-T OB.1096 - 15.III.2016": day,
639
- # Roman month, year. The months are tried longest first, because PEG
640
- # takes the first alternative that matches and "I" would otherwise win
641
- # on "III"; "XIII" matches "XII", then fails on the required dot.
642
- rule(:roman_month) do
643
- %w[XII XI X IX VIII VII VI V IV III II I]
644
- .map { |m| str(m) }.reduce(:|)
645
- end
646
-
647
- rule(:ob_roman_date) do
648
- str(" - ") >> digit.repeat(2, 2).as(:day) >> dot >>
649
- roman_month.as(:roman_month) >> dot >>
650
- digit.repeat(4, 4).as(:year)
651
- end
652
-
653
- rule(:ob_date) { date_part | ob_roman_date }
654
-
655
- rule(:ob_with_sector) do
656
- itu_prefix >>
657
- (sector >> space).maybe >>
658
- ob_series >>
659
- (ob_dot_body | ob_no_body | ob_bare_body) >>
660
- ob_date.maybe >>
661
- language.maybe
662
- end
663
-
664
- # Legacy long form: "ITU-T Operational Bulletin No. 1096".
665
- # The :_op_bull marker tells the builder to set series=OB.
666
- rule(:ob_long_form) do
667
- itu_prefix >>
668
- (sector >> space).maybe >>
669
- str("Operational Bulletin").as(:_op_bull) >>
670
- space >> str("No.") >> space >>
671
- number >>
672
- ob_date.maybe >>
673
- language.maybe
674
- end
675
-
676
- rule(:special_publication) { ob_with_sector | ob_long_form }
677
-
678
- # "Annex to ..." — the document IS the annex of a Special Publication
679
- # (no annex number). The :annex_to wrapping signals to the builder.
680
- rule(:annex_to_identifier) do
681
- str("Annex to") >> space >> special_publication.as(:annex_to)
682
- end
683
-
684
- # Handbook — "ITU-R 42.HDB". The ".HDB" literal marks a Handbook; it does
685
- # NOT route through subseries (which is digits-only). The :handbook_marker
686
- # key tells the builder to build Identifiers::Handbook.
687
- rule(:handbook) do
688
- itu_prefix >>
689
- sector >>
690
- space >>
691
- number >>
692
- dot >> str("HDB").as(:handbook_marker) >>
693
- date_part.maybe
694
- end
695
-
696
- # Numeric Question — "ITU-R 234-1/7:", "ITU-R 237/3:".
697
- # number(-part)/study-group, always with a trailing ":".
698
- rule(:numeric_question) do
699
- itu_prefix >>
700
- sector >>
701
- space >>
702
- number >>
703
- parts >>
704
- str("/") >> digits.as(:study_group) >>
705
- str(":").as(:question_colon).maybe
706
- end
707
-
708
- # One "number(/BL)?/study-group" body of a letter-series Question, shared
709
- # by the bracketed and bare forms.
710
- rule(:question_tail) do
711
- number >>
712
- str("/BL").as(:has_bl).maybe >>
713
- str("/") >> digits.as(:study_group)
714
- end
715
-
716
- # Letter-series Question — "ITU-R P.3/BL/7", "ITU-R SM.1/30",
717
- # "ITU-R S.[4/BL/2]:". series.number(/BL)?/study-group, optionally
718
- # bracketed and/or with a trailing ":".
719
- rule(:letter_question) do
720
- itu_prefix >>
721
- sector >>
722
- space >>
723
- series >> dot >>
724
- (
725
- (str("[").as(:bracketed) >> question_tail >> str("]")) |
726
- question_tail
727
- ) >>
728
- str(":").as(:question_colon).maybe
729
- end
730
-
731
- # Common-text suffix: "| ISO/IEC ..." or "| ISO ..." — the same
732
- # document published jointly by ITU and ISO/IEC. Captured as a raw
733
- # string; the builder parses it with the appropriate flavor.
734
- rule(:common_text_twin) do
735
- (space >> str("|") >> space >> match("[^|]").repeat(1)).as(:common_text_twin)
736
- end
737
-
738
- rule(:identifier) do
739
- annex_to_identifier |
740
- special_publication |
741
- supplement_identifier |
742
- # Both neighbours are load-bearing (PEG ordered choice never re-enters
743
- # the alternation once an alternative succeeds): AFTER
744
- # supplement_identifier, which matches the longer "<annex> Cor. 3"
745
- # form, and BEFORE with_series, which would match the "ITU-T A.23"
746
- # prefix of "ITU-T A.23 Annex A" and then fail on the unconsumed tail.
747
- annex_identifier |
748
- # Same slot reasoning as annex_identifier: AFTER supplement_identifier
749
- # (an appendix can carry a trailing Amd./Err.) and BEFORE with_series
750
- # (which would match the "ITU-T G.101" prefix and then fail on the
751
- # unconsumed " App. I").
752
- appendix_identifier |
753
- # Slot reasoning at the rule itself.
754
- report_identifier |
755
- handbook |
756
- numeric_question |
757
- letter_question |
758
- radio_regulations |
759
- with_series |
760
- contribution |
761
- # Unreachable earlier: special_publication needs the literal "OB",
762
- # handbook/numeric_question need leading digits, letter_question
763
- # needs series >> dot, and contribution needs "-C" after the series.
764
- series_code_identifier |
765
- without_series |
766
- publication_id
767
- end
768
-
769
- # Common-text form: an ITU identifier followed by "| ISO/IEC ...".
770
- # Tried last so it doesn't shadow the simpler single-identifier forms.
771
- rule(:common_text_identifier) do
772
- identifier >> common_text_twin
773
- end
774
-
775
- rule(:root) { common_text_identifier | identifier }
776
-
777
- def self.parse(input)
778
- new.parse(input)
779
- end
5
+ class Parser
780
6
  end
781
7
  end
782
8
  end