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.
- checksums.yaml +4 -4
- data/data/ieee/update_codes.yaml +2 -2
- data/data/parg/adobe.json +1 -1
- data/data/parg/aiee.json +1 -1
- data/data/parg/aiee.parg +99 -9
- data/data/parg/amca.json +1 -1
- data/data/parg/ansi.json +1 -1
- data/data/parg/api.json +1 -1
- data/data/parg/ashrae.json +1 -1
- data/data/parg/asme.json +1 -1
- data/data/parg/astm.json +1 -1
- data/data/parg/bipm.json +1 -1
- data/data/parg/bsi.json +1 -1
- data/data/parg/ccsds.json +1 -1
- data/data/parg/cen_cenelec.json +1 -1
- data/data/parg/cie.json +1 -1
- data/data/parg/csa.json +1 -1
- data/data/parg/ecma.json +1 -1
- data/data/parg/etsi.json +1 -1
- data/data/parg/evs.json +1 -1
- data/data/parg/gost.json +1 -1
- data/data/parg/iala.json +1 -1
- data/data/parg/iana.json +1 -1
- data/data/parg/idf.json +1 -1
- data/data/parg/iec.json +1 -1
- data/data/parg/ieee.json +1 -1
- data/data/parg/iho.json +1 -0
- data/data/parg/ire.json +1 -1
- data/data/parg/ire.parg +51 -12
- data/data/parg/isbn.json +1 -1
- data/data/parg/iso.json +1 -1
- data/data/parg/iso.parg +751 -0
- data/data/parg/itu.json +1 -1
- data/data/parg/jcgm.json +1 -1
- data/data/parg/jis.json +1 -1
- data/data/parg/nesc.json +1 -1
- data/data/parg/nesc.parg +97 -12
- data/data/parg/nist.json +1 -1
- data/data/parg/oiml.json +1 -1
- data/data/parg/omg.json +1 -1
- data/data/parg/parg.json +1 -1
- data/data/parg/plateau.json +1 -1
- data/data/parg/tgpp.json +1 -1
- data/data/parg/un.json +1 -1
- data/lib/pubid/adobe/identifier.rb +1 -1
- data/lib/pubid/adobe/parser.rb +2 -113
- data/lib/pubid/amca/identifiers/base.rb +1 -1
- data/lib/pubid/amca/parser.rb +1 -145
- data/lib/pubid/ansi/identifier.rb +1 -1
- data/lib/pubid/ansi/parser.rb +1 -82
- data/lib/pubid/api/builder.rb +32 -1
- data/lib/pubid/api/identifier.rb +1 -1
- data/lib/pubid/api/parser.rb +1 -131
- data/lib/pubid/ashrae/identifiers/base.rb +1 -1
- data/lib/pubid/ashrae/parser.rb +3 -710
- data/lib/pubid/asme/builder.rb +6 -4
- data/lib/pubid/asme/identifier.rb +1 -2
- data/lib/pubid/asme/parser.rb +1 -328
- data/lib/pubid/astm/builder.rb +12 -3
- data/lib/pubid/astm/identifier.rb +1 -2
- data/lib/pubid/astm/parser.rb +1 -227
- data/lib/pubid/bipm/identifier.rb +8 -1
- data/lib/pubid/bipm/parser.rb +2 -224
- data/lib/pubid/bsi/builder.rb +2 -4
- data/lib/pubid/bsi/parser.rb +1 -666
- data/lib/pubid/bsi/single_identifier.rb +38 -1
- data/lib/pubid/calconnect/parser.rb +2 -40
- data/lib/pubid/ccsds/identifier.rb +1 -1
- data/lib/pubid/ccsds/parser.rb +1 -66
- data/lib/pubid/cen_cenelec/identifier.rb +16 -1
- data/lib/pubid/cen_cenelec/parser.rb +3 -136
- data/lib/pubid/cie/identifier.rb +1 -1
- data/lib/pubid/cie/parser.rb +3 -378
- data/lib/pubid/conformance.rb +1 -1
- data/lib/pubid/csa/builder.rb +41 -18
- data/lib/pubid/csa/identifier.rb +12 -4
- data/lib/pubid/csa/identifiers/amendment.rb +28 -0
- data/lib/pubid/csa/identifiers.rb +1 -0
- data/lib/pubid/csa/parser.rb +16 -397
- data/lib/pubid/doi/parser.rb +2 -37
- data/lib/pubid/easc/builder.rb +1 -1
- data/lib/pubid/easc/parser.rb +2 -70
- data/lib/pubid/ecma/CLAUDE.md +8 -1
- data/lib/pubid/ecma/identifier.rb +13 -8
- data/lib/pubid/ecma/parser.rb +2 -54
- data/lib/pubid/ecma/renderer.rb +3 -7
- data/lib/pubid/errors.rb +18 -13
- data/lib/pubid/etsi/builder.rb +8 -5
- data/lib/pubid/etsi/identifiers/base.rb +1 -1
- data/lib/pubid/etsi/parser.rb +1 -132
- data/lib/pubid/evs/identifier.rb +7 -0
- data/lib/pubid/evs/parser.rb +1 -8
- data/lib/pubid/evs.rb +1 -1
- data/lib/pubid/gb/identifier.rb +6 -0
- data/lib/pubid/gb/parser.rb +2 -51
- data/lib/pubid/gost/identifier.rb +9 -1
- data/lib/pubid/gost/parser.rb +4 -81
- data/lib/pubid/iala/builder.rb +48 -39
- data/lib/pubid/iala/identifier.rb +1 -1
- data/lib/pubid/iala/parser.rb +2 -121
- data/lib/pubid/iana/identifier.rb +1 -1
- data/lib/pubid/iana/parser.rb +2 -24
- data/lib/pubid/identifier.rb +78 -1
- data/lib/pubid/idf/builder.rb +42 -0
- data/lib/pubid/idf/identifier.rb +1 -1
- data/lib/pubid/idf/parser.rb +1 -137
- data/lib/pubid/idf.rb +1 -2
- data/lib/pubid/iec/builder.rb +158 -50
- data/lib/pubid/iec/identifier.rb +1 -1
- data/lib/pubid/iec/parser.rb +3 -430
- data/lib/pubid/ieee/CLAUDE.md +2 -0
- data/lib/pubid/ieee/aiee/identifier.rb +2 -1
- data/lib/pubid/ieee/aiee.rb +0 -1
- data/lib/pubid/ieee/builder.rb +177 -30
- data/lib/pubid/ieee/components/code.rb +46 -7
- data/lib/pubid/ieee/identifiers/base.rb +5 -2
- data/lib/pubid/ieee/identifiers/joint_development.rb +7 -3
- data/lib/pubid/ieee/ire/identifier.rb +2 -1
- data/lib/pubid/ieee/ire.rb +0 -1
- data/lib/pubid/ieee/nesc/builder.rb +1 -1
- data/lib/pubid/ieee/nesc.rb +0 -1
- data/lib/pubid/ieee/parser.rb +1 -1257
- data/lib/pubid/ieee/project_renderer.rb +5 -1
- data/lib/pubid/ieee/renderer.rb +4 -2
- data/lib/pubid/ieee/urn_generator.rb +4 -1
- data/lib/pubid/ietf/identifiers/base.rb +17 -1
- data/lib/pubid/ietf/parser.rb +2 -47
- data/lib/pubid/ietf/renderer.rb +17 -2
- data/lib/pubid/iho/CLAUDE.md +98 -0
- data/lib/pubid/iho/builder.rb +4 -1
- data/lib/pubid/iho/identifiers/base.rb +28 -2
- data/lib/pubid/iho/renderer.rb +8 -0
- data/lib/pubid/iho/urn_generator.rb +14 -1
- data/lib/pubid/iho/urn_parser.rb +45 -32
- data/lib/pubid/iho.rb +0 -1
- data/lib/pubid/isbn/identifier.rb +1 -1
- data/lib/pubid/isbn/parser.rb +2 -31
- data/lib/pubid/iso/CLAUDE.md +66 -0
- data/lib/pubid/iso/builder.rb +229 -15
- data/lib/pubid/iso/identifier.rb +22 -12
- data/lib/pubid/iso/identifiers/directives.rb +6 -3
- data/lib/pubid/iso/identifiers/guide.rb +6 -3
- data/lib/pubid/iso/identifiers/international_workshop_agreement.rb +6 -3
- data/lib/pubid/iso/normalizer.rb +5 -4
- data/lib/pubid/iso/parser.rb +1 -539
- data/lib/pubid/iso/supplement_identifier.rb +6 -3
- data/lib/pubid/iso/urn_parser.rb +13 -8
- data/lib/pubid/iso/utilities.rb +4 -5
- data/lib/pubid/iso.rb +1 -19
- data/lib/pubid/itu/builder.rb +36 -20
- data/lib/pubid/itu/identifiers/base.rb +15 -2
- data/lib/pubid/itu/parser.rb +1 -775
- data/lib/pubid/jcgm/builder.rb +35 -0
- data/lib/pubid/jcgm/parser.rb +1 -151
- data/lib/pubid/jcgm.rb +1 -2
- data/lib/pubid/jis/CLAUDE.md +36 -0
- data/lib/pubid/jis/builder.rb +30 -41
- data/lib/pubid/jis/identifier.rb +12 -2
- data/lib/pubid/jis/identifiers/welding_engineering_standard.rb +35 -0
- data/lib/pubid/jis/identifiers.rb +2 -0
- data/lib/pubid/jis/parser.rb +1 -128
- data/lib/pubid/jis/renderer.rb +1 -1
- data/lib/pubid/jis/supplement_identifier.rb +5 -5
- data/lib/pubid/jis/urn_parser.rb +2 -1
- data/lib/pubid/jis.rb +3 -2
- data/lib/pubid/nist/builder.rb +5 -1
- data/lib/pubid/nist/caster.rb +13 -3
- data/lib/pubid/nist/identifiers/base.rb +37 -2
- data/lib/pubid/nist/identifiers/circular.rb +2 -2
- data/lib/pubid/nist/identifiers/circular_supplement.rb +2 -2
- data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +2 -2
- data/lib/pubid/nist/identifiers/crpl_report.rb +2 -2
- data/lib/pubid/nist/identifiers/handbook.rb +2 -2
- data/lib/pubid/nist/identifiers/internal_report.rb +2 -2
- data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +2 -2
- data/lib/pubid/nist/identifiers/monograph.rb +2 -2
- data/lib/pubid/nist/identifiers/report.rb +2 -2
- data/lib/pubid/nist/parser.rb +19 -775
- data/lib/pubid/nist/router.rb +4 -1
- data/lib/pubid/nist/supplement_identifier.rb +2 -2
- data/lib/pubid/oasis/parser.rb +2 -16
- data/lib/pubid/ogc/parser.rb +2 -26
- data/lib/pubid/oiml/identifiers/certification_system.rb +7 -0
- data/lib/pubid/oiml/identifiers/dual_published.rb +2 -2
- data/lib/pubid/oiml/parser.rb +1 -303
- data/lib/pubid/oiml.rb +1 -2
- data/lib/pubid/omg/identifier.rb +6 -1
- data/lib/pubid/omg/parser.rb +2 -76
- data/lib/pubid/parser.rb +5 -4
- data/lib/pubid/parsers/mr_string.rb +1 -1
- data/lib/pubid/plateau/parser.rb +1 -66
- data/lib/pubid/plateau.rb +1 -2
- data/lib/pubid/renderers/directives_renderer.rb +1 -1
- data/lib/pubid/renderers/iwa_renderer.rb +1 -1
- data/lib/pubid/renderers/publisher_stripper.rb +82 -0
- data/lib/pubid/renderers/supplement_renderer.rb +1 -1
- data/lib/pubid/renderers.rb +1 -0
- data/lib/pubid/sae/parser.rb +1 -48
- data/lib/pubid/tgpp/identifier.rb +1 -1
- data/lib/pubid/tgpp/parser.rb +2 -59
- data/lib/pubid/un/identifier.rb +1 -1
- data/lib/pubid/un/parser.rb +1 -18
- data/lib/pubid/version.rb +1 -1
- data/lib/pubid/w3c/parser.rb +2 -27
- data/lib/pubid/xsf/identifier.rb +10 -6
- data/lib/pubid/xsf/parser.rb +3 -31
- data/lib/pubid/xsf/renderer.rb +2 -4
- data/lib/pubid.rb +8 -9
- metadata +9 -23
- data/lib/pubid/ieee/aiee/parser.rb +0 -130
- data/lib/pubid/ieee/ire/parser.rb +0 -109
- data/lib/pubid/ieee/nesc/parser.rb +0 -155
- data/lib/pubid/iho/parser.rb +0 -68
- data/lib/pubid/parser/common_parse_methods.rb +0 -13
- data/lib/pubid/parser/common_parse_rules.rb +0 -56
- data/lib/pubid/parser/grammar.rb +0 -96
data/lib/pubid/cie/parser.rb
CHANGED
|
@@ -1,383 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "parslet"
|
|
4
|
-
|
|
5
3
|
module Pubid
|
|
6
4
|
module Cie
|
|
7
5
|
# Parser for CIE identifiers
|
|
8
6
|
# Handles dual-style system (legacy vs current)
|
|
9
|
-
class Parser
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
rule(:space?) { space.maybe }
|
|
13
|
-
rule(:dash) { str("-") }
|
|
14
|
-
rule(:colon) { str(":") }
|
|
15
|
-
rule(:dot) { str(".") }
|
|
16
|
-
rule(:slash) { str("/") }
|
|
17
|
-
rule(:comma) { str(",") }
|
|
18
|
-
rule(:digit) { match("[0-9]") }
|
|
19
|
-
rule(:digits) { digit.repeat(1) }
|
|
20
|
-
rule(:upper) { match("[A-Z]") }
|
|
21
|
-
rule(:lower) { match("[a-z]") }
|
|
22
|
-
|
|
23
|
-
# Year (4 digits: 1900-2099)
|
|
24
|
-
rule(:year_digits) do
|
|
25
|
-
(str("19") | str("20")) >> digit.repeat(2, 2)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Date with separator detection (CRITICAL for style)
|
|
29
|
-
# Legacy: dash "-" (pre-2001)
|
|
30
|
-
# Current: colon ":" (2001+)
|
|
31
|
-
rule(:legacy_date) do
|
|
32
|
-
(dash >> year_digits.as(:year)).as(:legacy_date)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
rule(:current_date) do
|
|
36
|
-
(colon >> year_digits.as(:year)).as(:current_date)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
rule(:date) do
|
|
40
|
-
current_date | legacy_date
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Legacy format: NUMBER-YEAR (e.g., 001-1980)
|
|
44
|
-
# This is BEFORE 2001, using dash as year separator (NOT part)
|
|
45
|
-
# Can optionally have trailing language like "(RU-2020)"
|
|
46
|
-
rule(:legacy_code_with_year) do
|
|
47
|
-
str("CIE") >> space >> digits.as(:number) >> dash >> year_digits.as(:year) >>
|
|
48
|
-
(language_paren_year | str("")).as(:trailing_lang)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Code patterns
|
|
52
|
-
# Pattern 1: "006.1" - iteration (leading zero + dot)
|
|
53
|
-
rule(:code_with_iteration) do
|
|
54
|
-
digits.as(:number) >> dot >> digits.as(:iteration)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Pattern 1b: "19/2.1" - part with slash THEN iteration with dot (complex)
|
|
58
|
-
rule(:code_with_part_and_iteration_slash) do
|
|
59
|
-
digits.as(:number) >> slash >> digits.as(:part) >> dot >> digits.as(:iteration)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Pattern 1c: "19-2.1" - part with dash THEN iteration with dot (complex)
|
|
63
|
-
rule(:code_with_part_and_iteration_dash) do
|
|
64
|
-
digits.as(:number) >> dash >> digits.as(:part) >> dot >> digits.as(:iteration) >>
|
|
65
|
-
dash.absent? # Don't consume date dash
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Pattern 2: "170-1" - part with dash (current style)
|
|
69
|
-
# NOTE: Part must be 1-3 digits, and NOT followed by a 4-digit year
|
|
70
|
-
rule(:code_with_part_dash) do
|
|
71
|
-
digits.as(:number) >> dash >> match("[0-9]").repeat(1, 3).as(:part) >>
|
|
72
|
-
str("").as(:dash_sep) >> # Marker for dash separator
|
|
73
|
-
# Negative lookahead: don't consume if followed by 4-digit year
|
|
74
|
-
(str("19") | str("20")).absent? >>
|
|
75
|
-
(dot | dash).absent? # Don't consume dot or date dash
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Pattern 3: "170/1" - part with slash (legacy style)
|
|
79
|
-
rule(:code_with_part_slash) do
|
|
80
|
-
digits.as(:number) >> slash >> digits.as(:part) >>
|
|
81
|
-
str("").as(:slash_sep) >> # Marker for slash separator
|
|
82
|
-
dot.absent? # Don't consume iteration dot
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Pattern 4: Simple number
|
|
86
|
-
rule(:code_simple) do
|
|
87
|
-
digits.as(:number)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Code rule (longest match first - complex patterns before simple)
|
|
91
|
-
rule(:code) do
|
|
92
|
-
code_with_part_and_iteration_slash |
|
|
93
|
-
code_with_part_and_iteration_dash |
|
|
94
|
-
code_with_iteration |
|
|
95
|
-
code_with_part_slash |
|
|
96
|
-
code_with_part_dash |
|
|
97
|
-
code_simple
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Language patterns
|
|
101
|
-
# Format 1: /E, /F, /G (slash-prefix, legacy)
|
|
102
|
-
rule(:language_slash) do
|
|
103
|
-
(slash >> upper.as(:lang_code)).as(:language_slash)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Format 2: (DE), (ES), (CN), (en), (E), (F), (G)
|
|
107
|
-
rule(:language_paren) do
|
|
108
|
-
(str("(") >> match("[A-Za-z]").repeat(1,
|
|
109
|
-
3).as(:lang_code) >> str(")")).as(:language_paren)
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Format 3: (RU-2021) - with translation year
|
|
113
|
-
# NOTE: The space before parenthesis is OPTIONAL (e.g., "2003 (RU-2021)" or "2003(RU-2021)")
|
|
114
|
-
rule(:language_paren_year) do
|
|
115
|
-
(space.maybe >> str("(") >> upper.repeat(2).as(:lang_code) >>
|
|
116
|
-
dash >> year_digits.as(:trans_year) >> str(")")).as(:language_paren_year)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
rule(:language) do
|
|
120
|
-
language_paren_year | language_paren | language_slash
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
# S prefix (optional "Standard" marker)
|
|
124
|
-
rule(:s_prefix) do
|
|
125
|
-
str("S") >> space
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Conference x-prefix
|
|
129
|
-
rule(:x_prefix) do
|
|
130
|
-
str("x")
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# Stage codes (DIS, DS, FDIS, etc.)
|
|
134
|
-
rule(:stage_code) do
|
|
135
|
-
(str("FDIS") | str("DIS") | str("DS")) >> space
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Document type (TR for Technical Report, TS for Technical Specification)
|
|
139
|
-
rule(:doc_type) do
|
|
140
|
-
(str("TR") | str("TS")) # Don't consume trailing space
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# Proceedings paper code: 1+ uppercase letters (OP, PO, PP, WP, IP, P, ...)
|
|
144
|
-
# Deliberately generic (open set); greedy letters then digits.
|
|
145
|
-
rule(:paper_code) do
|
|
146
|
-
match("[A-Z]").repeat(1).as(:paper_code)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# Techstreet opaque conference variant: /<lowercase-alnum slug>
|
|
150
|
-
# Charset kept lowercase-alnum so it can't collide with the uppercase
|
|
151
|
-
# /E, /Cor, /IEC tails or the self.parse language gsub.
|
|
152
|
-
rule(:variant_slug) do
|
|
153
|
-
slash >> match("[0-9a-z]").repeat(1).as(:variant)
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# Conference proceedings paper (x-prefixed): CIE x043-OP01
|
|
157
|
-
rule(:conf_proceedings) do
|
|
158
|
-
str("CIE") >> space >> str("x") >> digits.as(:conf_number) >>
|
|
159
|
-
dash >> paper_code >> digits.as(:paper_number)
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
# Standalone proceedings paper: CIE OP02 1-5
|
|
163
|
-
rule(:standalone_proceedings) do
|
|
164
|
-
str("CIE") >> space >> paper_code >> digits.as(:paper_number) >>
|
|
165
|
-
space >> (digits >> dash >> digits).as(:page_range)
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# D-series: CIE D001-2006 / CIE D002:2004
|
|
169
|
-
rule(:d_series_identifier) do
|
|
170
|
-
str("CIE") >> space >> str("D").as(:d_prefix) >> digits.as(:number) >> date
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
# Standard with bare slash-year (no language letter): CIE S 007/1998
|
|
174
|
-
# Uses a narrow number(+iteration) pattern rather than the full code rule
|
|
175
|
-
# so code_with_part_slash doesn't greedily consume the /year.
|
|
176
|
-
rule(:standard_with_slash_year) do
|
|
177
|
-
str("CIE") >> space >>
|
|
178
|
-
stage_code.maybe.as(:stage) >>
|
|
179
|
-
s_prefix.maybe.as(:s_prefix) >>
|
|
180
|
-
(digits.as(:number) >> (dot >> digits.as(:iteration)).maybe) >>
|
|
181
|
-
slash >> year_digits.as(:slash_year)
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
# DIS stage with supplement pattern (e.g., DIS 025-SP1/E:2019)
|
|
185
|
-
rule(:dis_with_supplement) do
|
|
186
|
-
str("CIE") >> space >>
|
|
187
|
-
stage_code.as(:stage) >>
|
|
188
|
-
s_prefix.maybe.as(:s_prefix) >>
|
|
189
|
-
digits.as(:base_number) >>
|
|
190
|
-
dash >> str("SP") >> digits.as(:supplement_number) >>
|
|
191
|
-
(dot >> digits.as(:supplement_part)).maybe >>
|
|
192
|
-
slash >> upper.as(:lang_code) >> colon.as(:lang_colon) >> year_digits.as(:year)
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
# Standard identifier (most common) - language can come AFTER date
|
|
196
|
-
rule(:standard_identifier) do
|
|
197
|
-
str("CIE") >> space >>
|
|
198
|
-
stage_code.maybe.as(:stage) >>
|
|
199
|
-
s_prefix.maybe.as(:s_prefix) >>
|
|
200
|
-
code >>
|
|
201
|
-
# Language can come BEFORE date, AFTER date, or both.
|
|
202
|
-
# The whole date group is optional so a bare "CIE 015" (no year)
|
|
203
|
-
# parses with year left nil (partial reference for relaton matching).
|
|
204
|
-
(
|
|
205
|
-
(language >> date).as(:lang_before) |
|
|
206
|
-
(date >> language.maybe).as(:date_then_lang) |
|
|
207
|
-
date
|
|
208
|
-
).maybe
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# Standard identifier without ISO reference but with language-year (CIE S 014-4/E:2007)
|
|
212
|
-
rule(:standard_with_language_year) do
|
|
213
|
-
str("CIE") >> space >>
|
|
214
|
-
stage_code.maybe.as(:stage) >>
|
|
215
|
-
s_prefix.maybe.as(:s_prefix) >>
|
|
216
|
-
code >>
|
|
217
|
-
slash >> upper.as(:lang_code) >> colon.as(:lang_colon) >> year_digits.as(:year)
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
# Conference identifier (x-prefix)
|
|
221
|
-
rule(:conference_identifier) do
|
|
222
|
-
str("CIE") >> space >>
|
|
223
|
-
x_prefix.as(:conference) >>
|
|
224
|
-
digits.as(:conf_number) >>
|
|
225
|
-
date >>
|
|
226
|
-
# Optional amendment
|
|
227
|
-
(space >> str("Amendment") >> space >> digits.as(:amd_number)).maybe >>
|
|
228
|
-
# Optional techstreet opaque /slug variant
|
|
229
|
-
variant_slug.maybe
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
# Joint published with ISO
|
|
233
|
-
rule(:joint_with_iso) do
|
|
234
|
-
str("CIE") >> space >>
|
|
235
|
-
str("ISO").as(:copublisher) >> # Match and mark once
|
|
236
|
-
(slash >> str("CIE")).absent? >> # Make sure it's not "ISO/CIE"
|
|
237
|
-
space >>
|
|
238
|
-
(doc_type >> space).maybe.as(:doc_type) >> # Consume space here
|
|
239
|
-
stage_code.maybe.as(:stage) >>
|
|
240
|
-
digits.as(:number) >>
|
|
241
|
-
(dash >> digits.as(:part)).maybe >>
|
|
242
|
-
((colon >> year_digits.as(:year)) | (dash >> year_digits.as(:year))).maybe >>
|
|
243
|
-
language.maybe
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
# Joint with ISO/CIE (special case)
|
|
247
|
-
rule(:joint_with_iso_cie) do
|
|
248
|
-
str("CIE") >> space >>
|
|
249
|
-
str("ISO/CIE").as(:copublisher) >> # Match and mark once
|
|
250
|
-
space >>
|
|
251
|
-
(doc_type >> space).maybe.as(:doc_type) >> # Consume space here
|
|
252
|
-
digits.as(:number) >>
|
|
253
|
-
(colon >> year_digits.as(:year)).maybe >>
|
|
254
|
-
language.maybe
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
# Joint published with IEC
|
|
258
|
-
rule(:joint_with_iec) do
|
|
259
|
-
str("CIE") >> space >>
|
|
260
|
-
str("IEC").as(:copublisher) >> # Match and mark once
|
|
261
|
-
space >>
|
|
262
|
-
digits.as(:number) >>
|
|
263
|
-
dot >> digits.as(:part) >> # IEC always uses dot
|
|
264
|
-
dash >> year_digits.as(:year)
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
# Identical with ISO (parenthetical reference)
|
|
268
|
-
# Patterns:
|
|
269
|
-
# "CIE S 006.1/1998 (ISO 16508:1999)" - iteration with dot
|
|
270
|
-
# "CIE S 014-4/E2007" - part with dash + language without colon (needs colon insertion)
|
|
271
|
-
# "CIE S 008/E:2001 (ISO 8995-1:2002(E))" - language with colon year
|
|
272
|
-
rule(:identical_with_iso) do
|
|
273
|
-
str("CIE") >> space >>
|
|
274
|
-
s_prefix.maybe.as(:s_prefix) >>
|
|
275
|
-
digits.as(:number) >>
|
|
276
|
-
# Can have iteration (.1) OR part (-4)
|
|
277
|
-
(
|
|
278
|
-
(dot >> digits.as(:iteration)) |
|
|
279
|
-
(dash >> digits.as(:part))
|
|
280
|
-
).maybe >>
|
|
281
|
-
# Slash handling - three mutually exclusive patterns:
|
|
282
|
-
(slash >>
|
|
283
|
-
(
|
|
284
|
-
# /E:2001 - language code WITH colon year
|
|
285
|
-
(upper.as(:lang_code) >> colon.as(:lang_colon) >> year_digits.as(:year)) |
|
|
286
|
-
# /E2007 - language code WITHOUT colon + year (needs colon insertion)
|
|
287
|
-
(upper.as(:lang_code) >> year_digits.as(:year)) |
|
|
288
|
-
# /1998 - just legacy slash-year (no language)
|
|
289
|
-
year_digits.as(:slash_year)
|
|
290
|
-
)
|
|
291
|
-
).maybe >>
|
|
292
|
-
# ISO reference - handle nested parentheses for language codes
|
|
293
|
-
space >> str("(ISO") >> space >>
|
|
294
|
-
# Match ISO identifier: digits, colons, dashes, slashes, dots
|
|
295
|
-
# Plus optional language code in parentheses like (E), (F)
|
|
296
|
-
(
|
|
297
|
-
match("[0-9A-Z:/.\\-]").repeat(1) >> # Main ISO identifier
|
|
298
|
-
(str("(") >> match("[A-Z]").repeat(1) >> str(")")).maybe # Optional (E), (F), etc.
|
|
299
|
-
).as(:iso_reference) >>
|
|
300
|
-
str(")")
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
# Dual published with IEC (slash separator between full identifiers)
|
|
304
|
-
# Pattern: "CIE S 009:2002/IEC 62471:2006"
|
|
305
|
-
rule(:dual_with_iec) do
|
|
306
|
-
str("CIE") >> space >>
|
|
307
|
-
s_prefix.maybe.as(:s_prefix) >>
|
|
308
|
-
digits.as(:number) >>
|
|
309
|
-
colon >> year_digits.as(:year) >>
|
|
310
|
-
slash >> str("IEC") >> space >>
|
|
311
|
-
match("[^)\\n]").repeat(1).as(:iec_identifier)
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
# Supplement identifier (-SPN notation)
|
|
315
|
-
rule(:supplement_identifier) do
|
|
316
|
-
str("CIE") >> space >>
|
|
317
|
-
digits.as(:base_number) >>
|
|
318
|
-
dash >> str("SP") >> digits.as(:supplement_number) >>
|
|
319
|
-
(dot >> digits.as(:supplement_part)).maybe >>
|
|
320
|
-
colon >> year_digits.as(:year)
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
# Corrigendum identifier (/CorN notation)
|
|
324
|
-
rule(:corrigendum_identifier) do
|
|
325
|
-
# Parse base identifier first (could be supplement or standard)
|
|
326
|
-
str("CIE") >> space >>
|
|
327
|
-
digits.as(:base_number) >>
|
|
328
|
-
# Could have supplement notation before corrigendum
|
|
329
|
-
(dash >> str("SP") >> digits.as(:base_supplement) >>
|
|
330
|
-
(dot >> digits.as(:base_supplement_part)).maybe).maybe >>
|
|
331
|
-
colon >> year_digits.as(:base_year) >>
|
|
332
|
-
# Corrigendum portion
|
|
333
|
-
slash >> str("Cor") >> digits.as(:cor_number) >>
|
|
334
|
-
colon >> year_digits.as(:cor_year)
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
# Bundle identifier (comma-separated list)
|
|
338
|
-
rule(:bundle_identifier) do
|
|
339
|
-
str("CIE") >> space >>
|
|
340
|
-
digits.as(:first_number) >>
|
|
341
|
-
dash >> str("SP") >> digits >> dot >> digits >>
|
|
342
|
-
colon >> year_digits >>
|
|
343
|
-
(comma >> digits.as(:next_number) >>
|
|
344
|
-
dash >> str("SP") >> digits >> dot >> digits >>
|
|
345
|
-
colon >> year_digits).repeat(1).as(:bundle_items)
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
# Tutorial Bundle
|
|
349
|
-
rule(:tutorial_bundle) do
|
|
350
|
-
str("CIE Tutorials Bundle") >> space >> digits.as(:bundle_number)
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
# Main identifier rule (longest/most specific first)
|
|
354
|
-
root(:identifier)
|
|
355
|
-
|
|
356
|
-
rule(:identifier) do
|
|
357
|
-
tutorial_bundle |
|
|
358
|
-
bundle_identifier |
|
|
359
|
-
joint_with_iso_cie |
|
|
360
|
-
joint_with_iso |
|
|
361
|
-
joint_with_iec |
|
|
362
|
-
identical_with_iso |
|
|
363
|
-
dis_with_supplement |
|
|
364
|
-
dual_with_iec |
|
|
365
|
-
corrigendum_identifier |
|
|
366
|
-
supplement_identifier |
|
|
367
|
-
standard_with_language_year |
|
|
368
|
-
standard_with_slash_year |
|
|
369
|
-
d_series_identifier |
|
|
370
|
-
conf_proceedings |
|
|
371
|
-
standalone_proceedings |
|
|
372
|
-
legacy_code_with_year | # Before standard to catch 001-1980 pattern
|
|
373
|
-
conference_identifier |
|
|
374
|
-
standard_identifier
|
|
375
|
-
end
|
|
376
|
-
|
|
377
|
-
# Class method for parsing with preprocessing
|
|
378
|
-
# Pre-parse ingestion normalizations (R2): every parse path —
|
|
379
|
-
# parslet and PG artifact alike — feeds the grammar the same
|
|
380
|
-
# normalized string.
|
|
7
|
+
class Parser
|
|
8
|
+
# Pre-parse ingestion normalizations (R2): every parse path feeds
|
|
9
|
+
# the grammar the same normalized string.
|
|
381
10
|
def self.normalize_input(string)
|
|
382
11
|
# Minimal preprocessing for data quality
|
|
383
12
|
cleaned = string.strip
|
|
@@ -394,10 +23,6 @@ module Pubid
|
|
|
394
23
|
|
|
395
24
|
cleaned
|
|
396
25
|
end
|
|
397
|
-
|
|
398
|
-
def self.parse(string)
|
|
399
|
-
new.parse(normalize_input(string))
|
|
400
|
-
end
|
|
401
26
|
end
|
|
402
27
|
end
|
|
403
28
|
end
|
data/lib/pubid/conformance.rb
CHANGED
|
@@ -20,7 +20,7 @@ module Pubid
|
|
|
20
20
|
# Neutral error vocabulary. Each implementation maps its native
|
|
21
21
|
# exceptions onto these codes; the corpus never records Ruby classes.
|
|
22
22
|
ERROR_CODES = {
|
|
23
|
-
"
|
|
23
|
+
"Pubid::Errors::ParseError" => "parse_failed",
|
|
24
24
|
"ArgumentError" => "invalid_argument",
|
|
25
25
|
}.freeze
|
|
26
26
|
|
data/lib/pubid/csa/builder.rb
CHANGED
|
@@ -33,12 +33,12 @@ module Pubid
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
# Handle bundled identifiers (with + notation)
|
|
36
|
-
if parsed_hash
|
|
36
|
+
if parsed_hash[:plus_separator]
|
|
37
37
|
return build_bundled(parsed_hash)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# Handle combined identifiers (with / notation)
|
|
41
|
-
if parsed_hash[:
|
|
41
|
+
if parsed_hash[:csa_code_part] && !parsed_hash[:plus_separator]
|
|
42
42
|
return build_combined(parsed_hash)
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -101,7 +101,7 @@ module Pubid
|
|
|
101
101
|
else
|
|
102
102
|
begin
|
|
103
103
|
Pubid::Iso.parse(base_str)
|
|
104
|
-
rescue
|
|
104
|
+
rescue Pubid::Errors::ParseError
|
|
105
105
|
Pubid::Iec.parse(base_str)
|
|
106
106
|
end
|
|
107
107
|
end
|
|
@@ -135,7 +135,7 @@ module Pubid
|
|
|
135
135
|
else
|
|
136
136
|
begin
|
|
137
137
|
Pubid::Iso.parse(amendment_str)
|
|
138
|
-
rescue
|
|
138
|
+
rescue Pubid::Errors::ParseError
|
|
139
139
|
Pubid::Iec.parse(amendment_str)
|
|
140
140
|
end
|
|
141
141
|
end
|
|
@@ -155,7 +155,7 @@ module Pubid
|
|
|
155
155
|
else
|
|
156
156
|
begin
|
|
157
157
|
Pubid::Iso.parse(wrapped_id_str)
|
|
158
|
-
rescue
|
|
158
|
+
rescue Pubid::Errors::ParseError
|
|
159
159
|
Pubid::Iec.parse(wrapped_id_str)
|
|
160
160
|
end
|
|
161
161
|
end
|
|
@@ -417,17 +417,11 @@ module Pubid
|
|
|
417
417
|
bundled = Identifiers::Bundled.new
|
|
418
418
|
|
|
419
419
|
# Build base identifier
|
|
420
|
-
|
|
420
|
+
parts = parsed_hash[:csa_code_part]
|
|
421
|
+
bundled.base = build_single(strip_part_prefix(parts.first))
|
|
421
422
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
bundled_portions = [parsed_hash[:bundled_first]]
|
|
425
|
-
if parsed_hash[:bundled_rest] && !parsed_hash[:bundled_rest].empty?
|
|
426
|
-
bundled_portions += parsed_hash[:bundled_rest]
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
bundled.bundled_with = bundled_portions.map do |portion|
|
|
430
|
-
build_single(portion)
|
|
423
|
+
bundled.bundled_with = parts[1..].map do |portion|
|
|
424
|
+
build_single(strip_part_prefix(portion))
|
|
431
425
|
end
|
|
432
426
|
|
|
433
427
|
# Reaffirmation - supports both 2-digit and 4-digit years
|
|
@@ -450,13 +444,33 @@ module Pubid
|
|
|
450
444
|
bundled
|
|
451
445
|
end
|
|
452
446
|
|
|
447
|
+
|
|
448
|
+
# Strips the part_-prefix the combined/bundled array group carries,
|
|
449
|
+
# leaving the plain field names build_single reads.
|
|
450
|
+
def strip_part_prefix(part)
|
|
451
|
+
part.each_with_object({}) do |(key, value), acc|
|
|
452
|
+
acc[key.to_s.delete_prefix("part_").to_sym] = value
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
453
456
|
def build_combined(parsed_hash)
|
|
454
457
|
combined = Identifiers::Combined.new
|
|
455
458
|
|
|
459
|
+
parts = parsed_hash[:csa_code_part].map(&:dup)
|
|
460
|
+
# SERIES and series_prefix bind as top-level scalars: the capture
|
|
461
|
+
# only fires in the parts that carry the keyword, and array
|
|
462
|
+
# bindings pair occurrences by index, so a sparse capture would
|
|
463
|
+
# land on the wrong part. The keyword qualifies the designation it
|
|
464
|
+
# follows — the last part in every combined form.
|
|
465
|
+
if parsed_hash[:part_series]
|
|
466
|
+
parts.last[:part_series] = parsed_hash[:part_series]
|
|
467
|
+
parts.last[:part_series_prefix] = parsed_hash[:part_series_prefix] if
|
|
468
|
+
parsed_hash[:part_series_prefix]
|
|
469
|
+
end
|
|
470
|
+
|
|
456
471
|
# Build each designation, in printed order, into one collection.
|
|
457
|
-
combined.identifiers =
|
|
458
|
-
.
|
|
459
|
-
.map { |part| build_single(part) }
|
|
472
|
+
combined.identifiers = parts
|
|
473
|
+
.map { |part| build_single(strip_part_prefix(part)) }
|
|
460
474
|
|
|
461
475
|
# Detect separator from parser marker
|
|
462
476
|
combined.separator = if parsed_hash[:comma_separator]
|
|
@@ -627,6 +641,15 @@ module Pubid
|
|
|
627
641
|
identifier.package = data[:package_portion].to_s
|
|
628
642
|
end
|
|
629
643
|
|
|
644
|
+
# Amendment riding on the identifier ("/A1:15" after the base)
|
|
645
|
+
if data[:amendment_number]
|
|
646
|
+
return Identifiers::Amendment.new(
|
|
647
|
+
base: identifier,
|
|
648
|
+
amendment_number: data[:amendment_number].to_s,
|
|
649
|
+
amendment_year: data[:amendment_year]&.to_s,
|
|
650
|
+
)
|
|
651
|
+
end
|
|
652
|
+
|
|
630
653
|
identifier
|
|
631
654
|
end
|
|
632
655
|
|
data/lib/pubid/csa/identifier.rb
CHANGED
|
@@ -61,7 +61,7 @@ module Pubid
|
|
|
61
61
|
normalized = normalized.gsub(/\s+/, " ").strip
|
|
62
62
|
|
|
63
63
|
# Parse normally (will create Bundled or Combined identifier)
|
|
64
|
-
tree = Pubid::Parg::
|
|
64
|
+
tree = Pubid::Parg::Model.bound(:csa, Parser.normalize_input(normalized))
|
|
65
65
|
prefix = Parser.publisher_prefix_for(normalized)
|
|
66
66
|
Parser.inject_publisher_prefix(tree, prefix) if prefix && tree.is_a?(Hash)
|
|
67
67
|
result = build!(tree, input)
|
|
@@ -421,7 +421,7 @@ module Pubid
|
|
|
421
421
|
# Normalize CAN3- to CSA (historical prefix)
|
|
422
422
|
normalized = normalized.gsub("CAN3-", "CSA ")
|
|
423
423
|
|
|
424
|
-
tree = Pubid::Parg::
|
|
424
|
+
tree = Pubid::Parg::Model.bound(:csa, Parser.normalize_input(normalized))
|
|
425
425
|
prefix = Parser.publisher_prefix_for(normalized)
|
|
426
426
|
Parser.inject_publisher_prefix(tree, prefix) if prefix && tree.is_a?(Hash)
|
|
427
427
|
result = build!(tree, input)
|
|
@@ -433,7 +433,7 @@ module Pubid
|
|
|
433
433
|
result.year_format = "dash" if has_dash_year && result.year_format.nil?
|
|
434
434
|
|
|
435
435
|
result
|
|
436
|
-
rescue
|
|
436
|
+
rescue Pubid::Errors::ParseError => e
|
|
437
437
|
raise e
|
|
438
438
|
end
|
|
439
439
|
|
|
@@ -459,7 +459,7 @@ module Pubid
|
|
|
459
459
|
# it is the one caller that genuinely wants a nil rather than a raise.
|
|
460
460
|
def self.try_parse(input)
|
|
461
461
|
parse(input)
|
|
462
|
-
rescue
|
|
462
|
+
rescue Pubid::Errors::ParseError, ArgumentError
|
|
463
463
|
nil
|
|
464
464
|
end
|
|
465
465
|
|
|
@@ -523,6 +523,14 @@ module Pubid
|
|
|
523
523
|
nil
|
|
524
524
|
end
|
|
525
525
|
|
|
526
|
+
# The leading publisher prefixes a CSA rendering can carry
|
|
527
|
+
# (`publisher_prefix` records which one the input used). The national
|
|
528
|
+
# forms `CAN/CSA-` and `CAN3-` are dropped whole, so
|
|
529
|
+
# `with_publisher: false` gives `C22.2 NO. 60601-1:14`, not `CSA-C22.2…`.
|
|
530
|
+
def publisher_prefixes
|
|
531
|
+
["CAN/CSA", "CAN3", "CSA"]
|
|
532
|
+
end
|
|
533
|
+
|
|
526
534
|
# CSA stores the publication year in its own plain `year` string
|
|
527
535
|
# attribute (not a `Components::Date`), so the base `#exclude`'s
|
|
528
536
|
# `:year`->`:date` remap nils the unused inherited `date` and leaves
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Csa
|
|
5
|
+
module Identifiers
|
|
6
|
+
# An amendment riding on a CSA identifier: "/A1:15" after the base
|
|
7
|
+
# designation. The grammar binds amendment_number and
|
|
8
|
+
# amendment_year; the builder wraps the base identifier in this
|
|
9
|
+
# class when they are present.
|
|
10
|
+
class Amendment < Identifier
|
|
11
|
+
attribute :base, ::Pubid::Identifier
|
|
12
|
+
attribute :amendment_number, :string
|
|
13
|
+
attribute :amendment_year, :string
|
|
14
|
+
|
|
15
|
+
def initialize(base:, amendment_number:, amendment_year: nil, **opts)
|
|
16
|
+
super(base: base, amendment_number: amendment_number,
|
|
17
|
+
amendment_year: amendment_year, **opts)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_s(**opts)
|
|
21
|
+
suffix = "/A#{amendment_number}"
|
|
22
|
+
suffix += ":#{amendment_year}" if amendment_year
|
|
23
|
+
"#{base.to_s(**opts)}#{suffix}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -4,6 +4,7 @@ module Pubid
|
|
|
4
4
|
module Csa
|
|
5
5
|
module Identifiers
|
|
6
6
|
autoload :Base, "#{__dir__}/identifiers/base"
|
|
7
|
+
autoload :Amendment, "#{__dir__}/identifiers/amendment"
|
|
7
8
|
autoload :Bundled, "#{__dir__}/identifiers/bundled"
|
|
8
9
|
autoload :CanadianAdopted, "#{__dir__}/identifiers/canadian_adopted"
|
|
9
10
|
autoload :Cec, "#{__dir__}/identifiers/cec"
|