pubid 2.0.0.pre.alpha.12 → 2.0.0.pre.alpha.13
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/README.adoc +43 -1
- data/data/ieee/update_codes.yaml +17 -4
- data/data/nist/update_codes.yaml +7 -3
- data/lib/pubid/adobe/builder.rb +2 -0
- data/lib/pubid/all_parts.rb +201 -0
- data/lib/pubid/all_parts_identifier.rb +19 -0
- data/lib/pubid/amca/CLAUDE.md +47 -0
- data/lib/pubid/amca/builder.rb +3 -5
- data/lib/pubid/amca/identifiers/base.rb +10 -0
- data/lib/pubid/amca/identifiers/publication.rb +13 -0
- data/lib/pubid/amca/parser.rb +2 -1
- data/lib/pubid/amca/renderer.rb +22 -33
- data/lib/pubid/amca/urn_generator.rb +21 -2
- data/lib/pubid/amca/urn_parser.rb +36 -10
- data/lib/pubid/ansi/builder.rb +6 -0
- data/lib/pubid/api/CLAUDE.md +23 -0
- data/lib/pubid/api/builder.rb +2 -0
- data/lib/pubid/ashrae/CLAUDE.md +13 -0
- data/lib/pubid/ashrae/builder.rb +58 -14
- data/lib/pubid/ashrae/identifiers/errata.rb +14 -2
- data/lib/pubid/ashrae/identifiers/interpretation.rb +2 -10
- data/lib/pubid/ashrae/parser.rb +62 -28
- data/lib/pubid/ashrae/renderer.rb +32 -1
- data/lib/pubid/ashrae/urn_generator.rb +32 -9
- data/lib/pubid/asme/CLAUDE.md +25 -0
- data/lib/pubid/asme/builder.rb +16 -9
- data/lib/pubid/asme/components/code.rb +2 -0
- data/lib/pubid/asme/identifiers/standard.rb +6 -1
- data/lib/pubid/asme/parser.rb +41 -14
- data/lib/pubid/astm/CLAUDE.md +9 -0
- data/lib/pubid/astm/builder.rb +2 -0
- data/lib/pubid/astm/components/code.rb +2 -0
- data/lib/pubid/astm/parser.rb +4 -1
- data/lib/pubid/bipm/CLAUDE.md +11 -0
- data/lib/pubid/bipm/builder.rb +2 -0
- data/lib/pubid/bsi/CLAUDE.md +93 -0
- data/lib/pubid/bsi/builder.rb +13 -11
- data/lib/pubid/bsi/identifiers/addendum_document.rb +2 -0
- data/lib/pubid/bsi/identifiers/adopted_european_norm.rb +6 -54
- data/lib/pubid/bsi/identifiers/adopted_international_standard.rb +5 -22
- data/lib/pubid/bsi/identifiers/amendment.rb +36 -12
- data/lib/pubid/bsi/identifiers/bundled_identifier.rb +2 -0
- data/lib/pubid/bsi/identifiers/consolidated_identifier.rb +23 -26
- data/lib/pubid/bsi/identifiers/corrigendum.rb +29 -12
- data/lib/pubid/bsi/identifiers/expert_commentary.rb +6 -7
- data/lib/pubid/bsi/identifiers/national_annex.rb +18 -20
- data/lib/pubid/bsi/identifiers/root_identity.rb +31 -0
- data/lib/pubid/bsi/identifiers/set.rb +2 -0
- data/lib/pubid/bsi/identifiers/supplement_document.rb +2 -0
- data/lib/pubid/bsi/identifiers.rb +1 -0
- data/lib/pubid/bsi/parser.rb +8 -8
- data/lib/pubid/bsi/renderer.rb +20 -20
- data/lib/pubid/bsi/urn_generator.rb +28 -18
- data/lib/pubid/builder/base.rb +27 -0
- data/lib/pubid/calconnect/builder.rb +2 -0
- data/lib/pubid/calconnect/identifier.rb +4 -0
- data/lib/pubid/ccsds/builder.rb +2 -0
- data/lib/pubid/ccsds/identifier.rb +8 -0
- data/lib/pubid/cen_cenelec/CLAUDE.md +59 -0
- data/lib/pubid/cen_cenelec/builder.rb +6 -1
- data/lib/pubid/cen_cenelec/identifier.rb +10 -27
- data/lib/pubid/cen_cenelec/identifiers/amendment.rb +3 -10
- data/lib/pubid/cen_cenelec/identifiers/corrigendum.rb +3 -10
- data/lib/pubid/cen_cenelec/parser.rb +11 -3
- data/lib/pubid/cie/CLAUDE.md +58 -0
- data/lib/pubid/cie/builder.rb +2 -0
- data/lib/pubid/cie/components/language.rb +2 -0
- data/lib/pubid/components/adoption.rb +2 -0
- data/lib/pubid/components/code.rb +2 -0
- data/lib/pubid/components/date.rb +8 -6
- data/lib/pubid/components/edition.rb +2 -0
- data/lib/pubid/components/iteration.rb +2 -0
- data/lib/pubid/components/language.rb +2 -0
- data/lib/pubid/components/locality.rb +2 -0
- data/lib/pubid/components/publisher.rb +2 -0
- data/lib/pubid/components/relationship.rb +2 -0
- data/lib/pubid/components/stage.rb +2 -0
- data/lib/pubid/components/supplement.rb +2 -0
- data/lib/pubid/components/type.rb +2 -0
- data/lib/pubid/components/typed_stage.rb +8 -0
- data/lib/pubid/csa/CLAUDE.md +41 -0
- data/lib/pubid/csa/builder.rb +2 -0
- data/lib/pubid/csa/identifier.rb +13 -1
- data/lib/pubid/csa/renderer.rb +12 -12
- data/lib/pubid/csa/single_identifier.rb +17 -0
- data/lib/pubid/doi/builder.rb +2 -0
- data/lib/pubid/easc/builder.rb +2 -0
- data/lib/pubid/ecma/CLAUDE.md +28 -0
- data/lib/pubid/ecma/builder.rb +2 -0
- data/lib/pubid/ecma/identifier.rb +7 -0
- data/lib/pubid/etsi/CLAUDE.md +34 -0
- data/lib/pubid/etsi/builder.rb +2 -0
- data/lib/pubid/etsi/components/code.rb +6 -0
- data/lib/pubid/etsi/components/version.rb +2 -0
- data/lib/pubid/etsi/identifiers/etsi_standard.rb +7 -0
- data/lib/pubid/evs/CLAUDE.md +58 -0
- data/lib/pubid/evs/builder.rb +2 -0
- data/lib/pubid/gb/CLAUDE.md +140 -0
- data/lib/pubid/gb/builder.rb +7 -2
- data/lib/pubid/gb/identifier.rb +5 -3
- data/lib/pubid/gb/identifiers/all_parts.rb +17 -0
- data/lib/pubid/gb/identifiers.rb +1 -0
- data/lib/pubid/gb/renderer.rb +0 -1
- data/lib/pubid/gost/CLAUDE.md +64 -0
- data/lib/pubid/gost/builder.rb +3 -1
- data/lib/pubid/gost/identifier.rb +5 -0
- data/lib/pubid/iala/CLAUDE.md +82 -0
- data/lib/pubid/iala/builder.rb +2 -0
- data/lib/pubid/iana/CLAUDE.md +7 -0
- data/lib/pubid/iana/builder.rb +2 -0
- data/lib/pubid/identifier.rb +161 -17
- data/lib/pubid/idf/builder.rb +6 -1
- data/lib/pubid/idf/identifier.rb +5 -0
- data/lib/pubid/idf/identifiers/all_parts.rb +17 -0
- data/lib/pubid/idf/identifiers.rb +1 -0
- data/lib/pubid/iec/CLAUDE.md +31 -0
- data/lib/pubid/iec/builder.rb +7 -1
- data/lib/pubid/iec/components/consolidated_amendment.rb +4 -0
- data/lib/pubid/iec/components/sheet.rb +2 -0
- data/lib/pubid/iec/components/trf_info.rb +2 -0
- data/lib/pubid/iec/components/vap_suffix.rb +2 -0
- data/lib/pubid/iec/identifier.rb +7 -2
- data/lib/pubid/iec/identifiers/all_parts.rb +19 -0
- data/lib/pubid/iec/identifiers.rb +1 -0
- data/lib/pubid/iec/renderer.rb +0 -1
- data/lib/pubid/iec/urn_generator.rb +9 -1
- data/lib/pubid/iec/urn_parser.rb +3 -2
- data/lib/pubid/ieee/CLAUDE.md +97 -0
- data/lib/pubid/ieee/builder.rb +134 -5
- data/lib/pubid/ieee/components/code.rb +2 -0
- data/lib/pubid/ieee/components/draft.rb +35 -2
- data/lib/pubid/ieee/components/typed_stage.rb +2 -0
- data/lib/pubid/ieee/identifiers/base.rb +20 -0
- data/lib/pubid/ieee/identifiers/iec_ieee_copublished.rb +9 -0
- data/lib/pubid/ieee/identifiers/joint_development.rb +17 -10
- data/lib/pubid/ieee/identifiers/project_draft_identifier.rb +8 -1
- data/lib/pubid/ieee/parser.rb +134 -21
- data/lib/pubid/ieee/renderer.rb +40 -7
- data/lib/pubid/ieee/urn_generator.rb +31 -0
- data/lib/pubid/ietf/CLAUDE.md +7 -0
- data/lib/pubid/ietf/builder.rb +2 -0
- data/lib/pubid/iho/builder.rb +2 -0
- data/lib/pubid/isbn/builder.rb +2 -0
- data/lib/pubid/iso/CLAUDE.md +47 -0
- data/lib/pubid/iso/builder.rb +19 -5
- data/lib/pubid/iso/components/publisher.rb +2 -0
- data/lib/pubid/iso/identifier.rb +6 -14
- data/lib/pubid/iso/identifiers/all_parts.rb +19 -0
- data/lib/pubid/iso/identifiers/directives_supplement.rb +4 -2
- data/lib/pubid/iso/identifiers.rb +1 -0
- data/lib/pubid/iso/rendering_style.rb +0 -1
- data/lib/pubid/itu/CLAUDE.md +69 -0
- data/lib/pubid/itu/builder.rb +2 -0
- data/lib/pubid/itu/components/code.rb +2 -0
- data/lib/pubid/itu/components/designation.rb +2 -0
- data/lib/pubid/itu/components/sector.rb +2 -0
- data/lib/pubid/itu/components/series.rb +2 -0
- data/lib/pubid/itu/identifiers/supplement.rb +15 -0
- data/lib/pubid/jcgm/CLAUDE.md +7 -0
- data/lib/pubid/jcgm/builder.rb +2 -0
- data/lib/pubid/jcgm/components/publisher.rb +2 -0
- data/lib/pubid/jis/builder.rb +5 -1
- data/lib/pubid/jis/identifier.rb +5 -17
- data/lib/pubid/jis/identifiers/all_parts.rb +19 -0
- data/lib/pubid/jis/identifiers.rb +1 -0
- data/lib/pubid/jis/renderer.rb +0 -2
- data/lib/pubid/jis/urn_generator.rb +0 -1
- data/lib/pubid/nist/CLAUDE.md +56 -0
- data/lib/pubid/nist/builder.rb +2 -0
- data/lib/pubid/nist/components/edition.rb +2 -0
- data/lib/pubid/nist/components/issue_number.rb +2 -0
- data/lib/pubid/nist/components/part.rb +2 -0
- data/lib/pubid/nist/components/stage.rb +2 -0
- data/lib/pubid/nist/components/supplement.rb +2 -0
- data/lib/pubid/nist/components/translation.rb +2 -0
- data/lib/pubid/nist/components/update.rb +2 -0
- data/lib/pubid/nist/components/version.rb +2 -0
- data/lib/pubid/nist/components/volume.rb +2 -0
- data/lib/pubid/nist/identifiers/base.rb +24 -3
- data/lib/pubid/nist/parser.rb +15 -2
- data/lib/pubid/nist/preprocessor.rb +53 -2
- data/lib/pubid/oasis/CLAUDE.md +19 -0
- data/lib/pubid/oasis/builder.rb +2 -0
- data/lib/pubid/oasis/identifier.rb +19 -0
- data/lib/pubid/ogc/CLAUDE.md +34 -0
- data/lib/pubid/ogc/builder.rb +2 -0
- data/lib/pubid/ogc/identifier.rb +11 -0
- data/lib/pubid/oiml/CLAUDE.md +189 -0
- data/lib/pubid/oiml/builder.rb +20 -0
- data/lib/pubid/oiml/components/code.rb +6 -0
- data/lib/pubid/oiml/identifier.rb +13 -0
- data/lib/pubid/oiml/identifiers/annex.rb +4 -0
- data/lib/pubid/oiml/identifiers/certification_system.rb +34 -0
- data/lib/pubid/oiml/identifiers/code_number.rb +8 -0
- data/lib/pubid/oiml/identifiers/dual_published.rb +174 -0
- data/lib/pubid/oiml/identifiers.rb +2 -0
- data/lib/pubid/oiml/parser.rb +35 -4
- data/lib/pubid/oiml/renderer.rb +23 -1
- data/lib/pubid/oiml/single_identifier.rb +4 -0
- data/lib/pubid/oiml/supplement_identifier.rb +7 -0
- data/lib/pubid/oiml/urn_generator.rb +28 -0
- data/lib/pubid/oiml.rb +5 -0
- data/lib/pubid/omg/CLAUDE.md +15 -0
- data/lib/pubid/omg/builder.rb +2 -0
- data/lib/pubid/parser/grammar.rb +23 -0
- data/lib/pubid/plateau/builder.rb +2 -0
- data/lib/pubid/plateau/identifiers/base.rb +4 -0
- data/lib/pubid/plateau/supplement_identifier.rb +14 -2
- data/lib/pubid/plateau/urn_generator.rb +7 -1
- data/lib/pubid/renderers/human_readable.rb +0 -1
- data/lib/pubid/sae/builder.rb +2 -0
- data/lib/pubid/sae/components/date.rb +2 -0
- data/lib/pubid/sae/components/type.rb +2 -0
- data/lib/pubid/subset_match.rb +197 -0
- data/lib/pubid/tgpp/CLAUDE.md +43 -0
- data/lib/pubid/tgpp/builder.rb +2 -0
- data/lib/pubid/tgpp/identifier.rb +14 -0
- data/lib/pubid/type_resolver.rb +14 -2
- data/lib/pubid/un/builder.rb +2 -0
- data/lib/pubid/version.rb +1 -1
- data/lib/pubid/w3c/CLAUDE.md +7 -0
- data/lib/pubid/w3c/builder.rb +2 -0
- data/lib/pubid/xsf/CLAUDE.md +11 -0
- data/lib/pubid/xsf/builder.rb +2 -0
- data/lib/pubid.rb +16 -3
- metadata +44 -2
|
@@ -201,6 +201,18 @@ module Pubid
|
|
|
201
201
|
hash
|
|
202
202
|
end
|
|
203
203
|
|
|
204
|
+
# Three more IEEE discriminators the default %i[date year edition
|
|
205
|
+
# version] list misses, alongside `edition`/`year`: `revision` (e.g.
|
|
206
|
+
# "2" in "P802.16Rev2"); `reaffirmed` (the "(R2010)" year, read by
|
|
207
|
+
# renderer.rb and urn_generator.rb — two reaffirmations of the same
|
|
208
|
+
# standard failed to collapse under #to_all_parts/#===); and
|
|
209
|
+
# `edition_month`, the month half of "Edition N YYYY-MM"
|
|
210
|
+
# (renderer.rb's `"Edition #{edition} #{year}-#{edition_month}"`),
|
|
211
|
+
# which survived even though its sibling `year` is already stripped.
|
|
212
|
+
def self.all_parts_edition_keys
|
|
213
|
+
super + %i[revision reaffirmed edition_month]
|
|
214
|
+
end
|
|
215
|
+
|
|
204
216
|
# Inverse of the to_hash compaction: expand a scalar `stage` back into the
|
|
205
217
|
# `typed_stage` sub-hash (on a deep copy, recursively) before lutaml
|
|
206
218
|
# deserializes, so nested bases rebuild their component too. `draft` needs
|
|
@@ -351,6 +363,14 @@ module Pubid
|
|
|
351
363
|
def self.parse_single(input)
|
|
352
364
|
# Apply legacy update_codes normalization first, before Parser's extensive preprocessing
|
|
353
365
|
normalized = Core::UpdateCodes.apply(input, :ieee)
|
|
366
|
+
# UpdateCodes may introduce the "; " double-label separator
|
|
367
|
+
# ("ISO/IEC13210: 1994 (E) ANSI/IEEE Std ..." → the ISO/IEC and
|
|
368
|
+
# IEEE labels of one document), but the dispatch ran on the raw
|
|
369
|
+
# input - re-dispatch so the dual construction sees it.
|
|
370
|
+
if normalized != input && normalized.include?("; ")
|
|
371
|
+
result = PreParser.preprocess(normalized)
|
|
372
|
+
return build_dual(result.parts) if result.dispatch == :dual_semicolon
|
|
373
|
+
end
|
|
354
374
|
parsed = Parser.parse(normalized) # Use class method for preprocessing
|
|
355
375
|
builder = Builder.new(Identifier)
|
|
356
376
|
# Pass the original input string to builder for context
|
|
@@ -26,6 +26,15 @@ module Pubid
|
|
|
26
26
|
attribute :iec_year, :string # IEC year like "2013"
|
|
27
27
|
attribute :date_info, :string # Date information like "(10/07)"
|
|
28
28
|
|
|
29
|
+
# `year_sep` only describes how a year is attached. When the reference
|
|
30
|
+
# omits the year, a subset match skips it: its default "-" would
|
|
31
|
+
# otherwise refuse a candidate printed with ":".
|
|
32
|
+
def subset_attribute_match?(name, mine, theirs)
|
|
33
|
+
return true if name == :year_sep && year.nil?
|
|
34
|
+
|
|
35
|
+
super
|
|
36
|
+
end
|
|
37
|
+
|
|
29
38
|
# The printed IEC/IEEE number, rebuilt losslessly from the split
|
|
30
39
|
# columns (the renderer/urn read this). Not stored — number/parts/
|
|
31
40
|
# separators/year fully describe it, so the verbatim string never
|
|
@@ -42,6 +42,8 @@ module Pubid
|
|
|
42
42
|
attribute :year, :string
|
|
43
43
|
attribute :iso_stage, :string # For ISO stage if present
|
|
44
44
|
attribute :ieee_draft, :string # For IEEE P/D notation if present
|
|
45
|
+
attribute :parenthetical_content, :string # e.g. the "(E)" edition
|
|
46
|
+
# marker of an ISO/IEC label
|
|
45
47
|
|
|
46
48
|
# Accepts keyword args or a single positional hash (the base
|
|
47
49
|
# #exclude/matching rebuild passes the latter) — see
|
|
@@ -138,14 +140,23 @@ module Pubid
|
|
|
138
140
|
end
|
|
139
141
|
end
|
|
140
142
|
|
|
141
|
-
#
|
|
142
|
-
|
|
143
|
+
# IEEE semantics: P = project (a draft); no P = a standard. The
|
|
144
|
+
# P-state is identity-bearing and prints as spelled — it is never
|
|
145
|
+
# added or stripped here.
|
|
146
|
+
code_str = code.to_s
|
|
143
147
|
code_str += mark unless code_str.empty?
|
|
144
148
|
parts << code_str if code_str && !code_str.empty?
|
|
145
149
|
|
|
146
|
-
# Join with space and add year with colon
|
|
150
|
+
# Join with space and add year with colon; the joint stage-draft
|
|
151
|
+
# clause ("D=WD.5") rides after the year in the ISO-led print.
|
|
147
152
|
result = parts.join(" ")
|
|
148
153
|
result += ":#{year}" if year
|
|
154
|
+
result += "/#{ieee_draft}" if ieee_draft && ieee_draft.start_with?("D=")
|
|
155
|
+
# Only the language/edition marker ("(E)", "(E/F)") prints; a
|
|
156
|
+
# trailing relationship narrative is metadata, not identity.
|
|
157
|
+
if parenthetical_content&.match?(%r{\A[A-Z](?:\s*[/&]\s*[A-Z])*\z})
|
|
158
|
+
result += " (#{parenthetical_content})"
|
|
159
|
+
end
|
|
149
160
|
|
|
150
161
|
result
|
|
151
162
|
end
|
|
@@ -159,13 +170,9 @@ module Pubid
|
|
|
159
170
|
# Publishers (slash-separated)
|
|
160
171
|
parts << publishers.join("/") if publishers && !publishers.empty?
|
|
161
172
|
|
|
162
|
-
# Build code part
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
# Add P prefix for projects (IEEE format always shows P for drafts)
|
|
166
|
-
if typed_stage&.project_status || type == "P"
|
|
167
|
-
code_str = "P#{code_str}"
|
|
168
|
-
end
|
|
173
|
+
# Build code part — the P-state prints as spelled (P = project
|
|
174
|
+
# draft; no P = standard). Never added or stripped.
|
|
175
|
+
code_str = code.to_s
|
|
169
176
|
|
|
170
177
|
# Mark after the number, before the draft and the year
|
|
171
178
|
code_str += mark unless code_str.empty?
|
|
@@ -33,10 +33,17 @@ module Pubid
|
|
|
33
33
|
# object. The "P" is reflected in the typed_stage, not in the Code
|
|
34
34
|
# component itself. Accepts keyword args or a single positional hash (the
|
|
35
35
|
# base #exclude/matching rebuild passes the latter).
|
|
36
|
+
attribute :project_marker, :boolean, default: -> { false }
|
|
37
|
+
|
|
36
38
|
def initialize(args = {}, **kwargs)
|
|
37
39
|
args = kwargs.empty? ? args.dup : args.merge(kwargs)
|
|
38
|
-
# If code is provided with "P" prefix (e.g., "P1234"), strip it
|
|
40
|
+
# If code is provided with "P" prefix (e.g., "P1234"), strip it.
|
|
41
|
+
# P = project (the document is a draft): the marker is
|
|
42
|
+
# identity-bearing, so its presence is remembered for the render —
|
|
43
|
+
# stripping the letter must not strip the fact (docs/
|
|
44
|
+
# IEEE-DRAFT-STAGES.md §1.1).
|
|
39
45
|
if args[:code].is_a?(String) && args[:code].start_with?("P")
|
|
46
|
+
args[:project_marker] = true unless args.key?(:project_marker)
|
|
40
47
|
args[:code] = args[:code][1..] # Strip leading "P"
|
|
41
48
|
end
|
|
42
49
|
|
data/lib/pubid/ieee/parser.rb
CHANGED
|
@@ -269,6 +269,10 @@ module Pubid
|
|
|
269
269
|
rule(:draft_date) do
|
|
270
270
|
# Enhanced to handle: ", Sept 2008" or " Sept 2008" or ", Month Year"
|
|
271
271
|
((comma | space) >> month_name.as(:month) >> space >> year_digits.as(:year)) |
|
|
272
|
+
# Space-separated bare year after the draft designator
|
|
273
|
+
# ("IEEE Draft Std P14764/D1 2004, Nov 2004" - the "D1 2004" draft
|
|
274
|
+
# carries its own year, with the print date still trailing).
|
|
275
|
+
(space >> year_digits.as(:year)) |
|
|
272
276
|
# Numeric-month form ", 05 2007" / " 05 2007" — no text month name. The
|
|
273
277
|
# year separator may be a dash: preprocessing (parser.rb ~1278) rewrites
|
|
274
278
|
# a trailing " <digits> <year>" to "<digits>-<year>", so "05 2007"
|
|
@@ -302,6 +306,15 @@ module Pubid
|
|
|
302
306
|
rule(:draft) do
|
|
303
307
|
(draft_prefix >> draft_version.repeat(1, 2) >>
|
|
304
308
|
(dot >> digits.as(:revision)).maybe >>
|
|
309
|
+
# The compound both-systems form (docs/IEEE-DRAFT-STAGES.md
|
|
310
|
+
# §1.3): "=DDIS.3" - IEEE draft ordinal = draft of the ISO/IEC
|
|
311
|
+
# stage, with its iteration. The glued "=DDIS3" / "=DDIS-3"
|
|
312
|
+
# spellings are accepted aliases of "=DDIS.3".
|
|
313
|
+
(str("=") >> (str("D") >> (str("FDIS") | str("CDV") | str("PWI") |
|
|
314
|
+
str("DIS") | str("WD") | str("NP") | str("CD")) |
|
|
315
|
+
(str("FDIS") | str("CDV") | str("PWI") | str("DIS") | str("WD") |
|
|
316
|
+
str("NP") | str("CD"))).as(:draft_iso_stage) >>
|
|
317
|
+
(dot | dash).maybe >> digits.as(:draft_iso_iteration).maybe).maybe >>
|
|
305
318
|
draft_date.maybe).as(:draft)
|
|
306
319
|
end
|
|
307
320
|
|
|
@@ -532,7 +545,9 @@ module Pubid
|
|
|
532
545
|
|
|
533
546
|
# Additional parameters (inside parentheses)
|
|
534
547
|
rule(:additional_parameters) do
|
|
535
|
-
(space.maybe >> str("(") >> #
|
|
548
|
+
(space.maybe >> str("(") >> space.maybe >> # Space before/after '(' optional
|
|
549
|
+
# ("( Revision of …)" - a crawl space inside the paren must not push
|
|
550
|
+
# the narrative to the catch-all and into the render).
|
|
536
551
|
(reaffirmed |
|
|
537
552
|
# Handle "Revision of IEEE Std ..." with optional space after Std
|
|
538
553
|
(str("Revision of IEEE Std ") >> space.maybe >> match("[^)]").repeat(1).as(:revision_of)) |
|
|
@@ -582,15 +597,32 @@ module Pubid
|
|
|
582
597
|
# ALSO handle: IEEE/CSA P844.1/293.1/D2 (CSA dual numbering)
|
|
583
598
|
(str("ISO/IEC/IEEE") | str("ISO/IEEE") | str("IEC/IEEE") | str("IEEE/CSA")).as(:joint_publishers) >>
|
|
584
599
|
space >>
|
|
585
|
-
|
|
600
|
+
# P = project (the document is a draft): identity-bearing, so it
|
|
601
|
+
# is captured and preserved, never silently consumed.
|
|
602
|
+
str("P").as(:project_marker) >>
|
|
586
603
|
digits.as(:number) >>
|
|
587
604
|
((dot | dash) >> digits.as(:part)).maybe >> # Optional part like .1 or -1
|
|
588
605
|
# CSA dual numbering: /293.1 (second number)
|
|
589
606
|
(slash >> digits >> (dot >> digits).maybe >> (dash >> digits.as(:draft_version)).maybe).maybe >>
|
|
590
607
|
(
|
|
591
|
-
# Variant
|
|
592
|
-
|
|
593
|
-
#
|
|
608
|
+
# Variant 1b: the ordinal-less stage draft "D=CDV[:2020]" -
|
|
609
|
+
# D (draft) = CDV (the IEC stage it drafts). The year rides in
|
|
610
|
+
# the draft clause (a distinct key, so the builder keeps the
|
|
611
|
+
# date inside the designator).
|
|
612
|
+
(slash >> str("D") >> str("=") >>
|
|
613
|
+
(str("CDV") | str("FDIS") | str("PWI") | str("WD") |
|
|
614
|
+
str("NP") | str("DIS") | str("CD")).as(:draft_iso_stage) >>
|
|
615
|
+
(str(":") >> year_digits.as(:draft_stage_year)).maybe) |
|
|
616
|
+
# Variant 1: /D8 notation (original), with the compound
|
|
617
|
+
# both-systems suffix "=DDIS.3" (docs/IEEE-DRAFT-STAGES.md §1.3)
|
|
618
|
+
(slash >> str("D") >> digits.as(:draft_version) >>
|
|
619
|
+
(str("=") >> (str("D") >> (str("FDIS") | str("CDV") | str("PWI") |
|
|
620
|
+
str("DIS") | str("WD") | str("NP") | str("CD")) |
|
|
621
|
+
(str("FDIS") | str("CDV") | str("PWI") | str("DIS") | str("WD") |
|
|
622
|
+
str("NP") | str("CD"))).as(:draft_iso_stage) >>
|
|
623
|
+
(dot | dash).maybe >> digits.as(:draft_iso_iteration).maybe).maybe) |
|
|
624
|
+
# Variant 2: , CDV1 notation (comma before stage code) —
|
|
625
|
+
# the stage draft of the named ISO/IEC stage, its iteration
|
|
594
626
|
(comma >> (str("CDV") | str("FDIS") | str("CD") | str("DIS")).as(:iec_stage) >> digits.maybe.as(:stage_iteration))
|
|
595
627
|
).maybe >>
|
|
596
628
|
# Optional edition, from relaton's "/E-<n>" suffix normalized to
|
|
@@ -636,18 +668,30 @@ module Pubid
|
|
|
636
668
|
str("").as(:iso_published) >> space.maybe)) |
|
|
637
669
|
(staged_only.as(:joint_publishers) >> space >>
|
|
638
670
|
iso_stage >> std_noise >> space)) >>
|
|
639
|
-
str("P").maybe >> #
|
|
671
|
+
str("P").as(:project_marker).maybe >> # project marker (P =
|
|
672
|
+
# project/draft; its
|
|
673
|
+
# presence is identity)
|
|
640
674
|
digits.as(:number) >>
|
|
641
|
-
# part must not swallow the trailing year (year_digits.absent?)
|
|
642
|
-
|
|
675
|
+
# part must not swallow the trailing year (year_digits.absent?). A
|
|
676
|
+
# DASH-joined part is tagged (:part_dash) - the catalogue-printed
|
|
677
|
+
# joint form spells the part with a dash ("21451-7") where the ISO
|
|
678
|
+
# form uses the dot.
|
|
679
|
+
((dot >> year_digits.absent? >> digits.as(:part)) |
|
|
680
|
+
(dash >> str("").as(:part_dash) >> year_digits.absent? >>
|
|
681
|
+
digits.as(:part))).maybe >>
|
|
643
682
|
(
|
|
644
683
|
(str(":") >> year_digits.as(:year)) |
|
|
684
|
+
# The dash-year (and ", Month YYYY" text date below) spellings are
|
|
685
|
+
# the catalogue-PRINTED joint form; tag them (:printed_form) so the
|
|
686
|
+
# builder routes to a Standard instead of the ISO joint reference.
|
|
645
687
|
(dash >> year_digits.as(:year) >>
|
|
646
|
-
(dash >> month_numeric.as(:month)).maybe
|
|
688
|
+
(dash >> month_numeric.as(:month)).maybe >>
|
|
689
|
+
str("").as(:printed_dash_year)) |
|
|
647
690
|
# Trailing text date ", April 2015" / " April 2015" (build_joint_development
|
|
648
691
|
# already reads :month/:year). No :year collision — the iso rule has no
|
|
649
692
|
# other :year capture (edition uses :edition_year).
|
|
650
|
-
((comma | space) >> month_name.as(:month) >> space >>
|
|
693
|
+
((comma | space) >> month_name.as(:month) >> space >>
|
|
694
|
+
year_digits.as(:year) >> str("").as(:printed_month_year))
|
|
651
695
|
).maybe >>
|
|
652
696
|
# Optional /D<draft> tail. normalize_relaton_suffixes repositions the
|
|
653
697
|
# historical "…/D-3-2017" onto the number as "…-2017/D3", so by the
|
|
@@ -658,11 +702,26 @@ module Pubid
|
|
|
658
702
|
# keys (:draft_month/:draft_year), because a plain :month/:year
|
|
659
703
|
# here collides with the date clause above and parslet drops the
|
|
660
704
|
# subtree with a "Duplicate subtrees" warning.
|
|
705
|
+
# The ISO/IEC edition marker may sit between the year and an
|
|
706
|
+
# amendment/draft tail: "8802.11:2012 (E)/Amd 1-2014". A distinct
|
|
707
|
+
# key - a second :parameters capture here collides with the
|
|
708
|
+
# trailing parenthetical slot (parslet drops duplicate subtrees).
|
|
709
|
+
(space.maybe >> str("(") >>
|
|
710
|
+
match("[^)]").repeat(1).as(:edition_marker) >> str(")")).maybe >>
|
|
711
|
+
# The stage-draft clause of the ISO-led print: "/D=WD.5" -
|
|
712
|
+
# D (draft) = the ISO/IEC stage it drafts, with its iteration
|
|
713
|
+
# (docs/IEEE-DRAFT-STAGES.md §1.2).
|
|
714
|
+
((slash >> str("D") >> str("=") >>
|
|
715
|
+
(str("FDIS") | str("FCD") | str("CDV") |
|
|
716
|
+
(str("DIS") >> digit.maybe) |
|
|
717
|
+
(str("CD") >> digit.maybe) |
|
|
718
|
+
str("WD") | str("PWI") | str("NP")).as(:draft_iso_stage) >>
|
|
719
|
+
(dot >> (digits >> match("[A-Za-z]").repeat(0, 1)).as(:draft_iso_iteration)).maybe) |
|
|
661
720
|
(slash >> str("D") >> dash.maybe >>
|
|
662
721
|
match('[0-9.]').repeat(1).as(:draft_version) >>
|
|
663
722
|
(((comma | space) >> month_name.as(:draft_month) >> space >>
|
|
664
723
|
year_digits.as(:draft_year)) |
|
|
665
|
-
(comma >> year_digits.as(:draft_year))).maybe).maybe >>
|
|
724
|
+
(comma >> year_digits.as(:draft_year))).maybe).maybe).maybe >>
|
|
666
725
|
# Optional amendment tail (pubid#317:
|
|
667
726
|
# "8802-11:2012/Amd.1:2014(E)") - the flat tree keys reuse
|
|
668
727
|
# build_flat_amendment.
|
|
@@ -678,7 +737,6 @@ module Pubid
|
|
|
678
737
|
# The crawl sometimes spaces it ("…(E), January 2017" family,
|
|
679
738
|
# pubid#216), and a relationship parenthetical may follow
|
|
680
739
|
# ("…CD2 P15288-2013-09 (Revision of …)").
|
|
681
|
-
space.maybe >> (str("(E)") | str("(F)")).maybe >>
|
|
682
740
|
parenthetical.maybe
|
|
683
741
|
end
|
|
684
742
|
|
|
@@ -692,6 +750,28 @@ module Pubid
|
|
|
692
750
|
# the trailing `.FDIS` is the ISO stage, NOT a second part. Routes through
|
|
693
751
|
# the same build_joint_development (via :joint_publishers/:iso_stage), so the
|
|
694
752
|
# stage is modeled correctly and the numeric part stays separate.
|
|
753
|
+
# The stage-less ISO/IEC label with a colon year and the ISO/IEC
|
|
754
|
+
# edition marker: "ISO/IEC 13210:1994 (E)" - the ISO/IEC portion of a
|
|
755
|
+
# double-labeled standard. The (E) parenthetical is REQUIRED: without
|
|
756
|
+
# it this spelling is the ISO flavor's own form and must not be
|
|
757
|
+
# stolen. Routes through build_joint_development like every ISO-led
|
|
758
|
+
# joint reference.
|
|
759
|
+
rule(:joint_development_iso_iec_edition) do
|
|
760
|
+
str("ISO/IEC").as(:joint_publishers) >> space >>
|
|
761
|
+
str("").as(:iso_published) >>
|
|
762
|
+
str("P").as(:project_marker).maybe >>
|
|
763
|
+
digits.as(:number) >>
|
|
764
|
+
# The part may be dot- or dash-joined ("8802-9"); a dash-year
|
|
765
|
+
# ("1234-2012") is not a part.
|
|
766
|
+
# A distinct key: the iso-format route's :part_dash prints as a
|
|
767
|
+
# dot in the joint code (the "P24748.2:2018" convention); only
|
|
768
|
+
# the ISO/IEC LABEL keeps its printed dash.
|
|
769
|
+
((dot | (dash >> str("").as(:iec_label_dash) >> year_digits.absent?)) >>
|
|
770
|
+
digits.as(:part)).maybe >>
|
|
771
|
+
(str(":") >> space.maybe >> year_digits.as(:year)) >>
|
|
772
|
+
space.maybe >> parenthetical
|
|
773
|
+
end
|
|
774
|
+
|
|
695
775
|
rule(:joint_development_embedded_stage) do
|
|
696
776
|
# publisher set mirrors joint_development_iso_format (incl. bare IEEE);
|
|
697
777
|
# longest token first.
|
|
@@ -699,7 +779,9 @@ module Pubid
|
|
|
699
779
|
str("ISO/IEEE") | str("IEC/IEEE") | str("IEEE/IEC") | str("ISO/IEC") |
|
|
700
780
|
str("IEEE")).as(:joint_publishers) >>
|
|
701
781
|
space >>
|
|
702
|
-
str("P").maybe >> #
|
|
782
|
+
str("P").as(:project_marker).maybe >> # project marker (P =
|
|
783
|
+
# project/draft; its
|
|
784
|
+
# presence is identity)
|
|
703
785
|
digits.as(:number) >>
|
|
704
786
|
# optional numeric part (dot or dash); must not swallow a year
|
|
705
787
|
((dot | dash) >> year_digits.absent? >> digits.as(:part)).maybe >>
|
|
@@ -814,7 +896,17 @@ module Pubid
|
|
|
814
896
|
((space >> month_name.as(:trailing_month) >> space >> year_digits.as(:trailing_year)) |
|
|
815
897
|
(space >> month_numeric.as(:trailing_month) >> (space | dash) >> year_digits.as(:trailing_year))).maybe >>
|
|
816
898
|
draft.maybe >>
|
|
899
|
+
# A print date may trail a draft that carries its own date
|
|
900
|
+
# ("IEEE Unapproved Draft 24765/D1 2009, Oct 2009" - the draft's
|
|
901
|
+
# year is "2009", the print date is ", Oct 2009").
|
|
902
|
+
(comma >> space? >> month_name.as(:trailing_month) >> space >>
|
|
903
|
+
year_digits.as(:trailing_year)).maybe >>
|
|
817
904
|
revision_suffix.maybe >>
|
|
905
|
+
# …and the print date may also trail the repositioned revision
|
|
906
|
+
# ("IEEE Unapproved Draft P802.16Rev2/D9a, March 2009" reaches the
|
|
907
|
+
# grammar as "…/D9a/R-2, March 2009" via normalize_revision_notation).
|
|
908
|
+
(comma >> space? >> month_name.as(:trailing_month) >> space >>
|
|
909
|
+
year_digits.as(:trailing_year)).maybe >>
|
|
818
910
|
# Trailing corrigendum after the draft ("…/D2.0/Cor. 1", or
|
|
819
911
|
# "…/D1.0, Dec 2007/Cor. 1" where the draft's own draft_date consumes
|
|
820
912
|
# the date, leaving "/Cor. N"). The flat corrigendum+draft tree (no
|
|
@@ -899,10 +991,17 @@ module Pubid
|
|
|
899
991
|
# Draft NESC pattern
|
|
900
992
|
(str("Draft") >> space >> (str("NESC") | str("National Electrical Safety Code"))) |
|
|
901
993
|
# Name-first pattern (NEW)
|
|
902
|
-
(str("National Electrical Safety Code") >> str(",") >> space >> str("C2-"))
|
|
994
|
+
(str("National Electrical Safety Code") >> str(",") >> space >> str("C2-")) |
|
|
995
|
+
# Catalogue form: "IEEE Std YYYY NESC ..." / "IEEE Std YYYY National
|
|
996
|
+
# Electrical Safety Code" - the prefix is consumed here (the NESC
|
|
997
|
+
# renderer prepends "IEEE Std" itself) but must not fall through to
|
|
998
|
+
# the generic standard grammar, which would read YYYY as a number.
|
|
999
|
+
(str("IEEE") >> space >> str("Std") >> space >> year_digits >> space >>
|
|
1000
|
+
(str("NESC") | str("National Electrical Safety Code")))
|
|
903
1001
|
).present? >>
|
|
904
1002
|
# Delegate to NESC parser if pattern detected
|
|
905
|
-
|
|
1003
|
+
((str("IEEE") >> space >> str("Std") >> space).maybe >>
|
|
1004
|
+
Nesc::Parser.new.nesc_identifier).as(:nesc)
|
|
906
1005
|
end
|
|
907
1006
|
|
|
908
1007
|
# Draft notation for PSI (e.g., /D2, /D3, rawbib's /D-2)
|
|
@@ -1056,13 +1155,13 @@ module Pubid
|
|
|
1056
1155
|
str("CSA") >> space >>
|
|
1057
1156
|
# CSA number formats (various patterns observed)
|
|
1058
1157
|
(
|
|
1059
|
-
# Format 1: C22.2 No. 293.1-17 (with NO
|
|
1060
|
-
(str("C") >> digit.repeat(2) >> dot >> digit >> space >> str("No") >> dot >> space >>
|
|
1158
|
+
# Format 1: C22.2 No. 293.1-17 (with NO., either case)
|
|
1159
|
+
(str("C") >> digit.repeat(2) >> dot >> digit >> space >> (str("No") | str("NO")) >> dot >> space >>
|
|
1061
1160
|
match("[0-9.]").repeat(1) >> (dash | str(":")) >> digit.repeat(2)) |
|
|
1062
1161
|
# Format 2: C293.2-17 (without NO., dash year)
|
|
1063
1162
|
(str("C") >> match("[0-9.]").repeat(1) >> dash >> digit.repeat(2)) |
|
|
1064
|
-
# Format 3: C22.2 No. 293.3:19 (with NO., colon year)
|
|
1065
|
-
(str("C") >> digit.repeat(2) >> dot >> digit >> space >> str("No") >> dot >> space >>
|
|
1163
|
+
# Format 3: C22.2 No. 293.3:19 (with NO., either case, colon year)
|
|
1164
|
+
(str("C") >> digit.repeat(2) >> dot >> digit >> space >> (str("No") | str("NO")) >> dot >> space >>
|
|
1066
1165
|
match("[0-9.]").repeat(1) >> str(":") >> digit.repeat(2)) |
|
|
1067
1166
|
# Format 4: C293.4:19 (without NO., colon year)
|
|
1068
1167
|
(str("C") >> match("[0-9.]").repeat(1) >> str(":") >> digit.repeat(2))
|
|
@@ -1085,6 +1184,8 @@ module Pubid
|
|
|
1085
1184
|
conformance_identifier | # NEW: Try conformance identifier before generic patterns
|
|
1086
1185
|
joint_development_ieee_format |
|
|
1087
1186
|
joint_development_iso_format |
|
|
1187
|
+
joint_development_iso_iec_edition | # stage-less ISO/IEC label with (E) - double-label portion
|
|
1188
|
+
|
|
1088
1189
|
joint_development_embedded_stage | # stage-LAST embedded form (before generic)
|
|
1089
1190
|
iec_ieee_copublished |
|
|
1090
1191
|
number_first_identifier |
|
|
@@ -1110,6 +1211,15 @@ module Pubid
|
|
|
1110
1211
|
# never collides with the base -YYYY identity year (see
|
|
1111
1212
|
# ieee_p_identifier). The builder promotes it only when no base year.
|
|
1112
1213
|
trailing_month_year.maybe >>
|
|
1214
|
+
# The ASHRAE joint suffix may also trail a draft+date
|
|
1215
|
+
# ("IEEE P1635/D13, December, 2017/ASHRAE Guideline 21"), so a
|
|
1216
|
+
# second slot catches it after the date.
|
|
1217
|
+
ashrae_copub.maybe >>
|
|
1218
|
+
# A bracketed narrative may sit between the base and a trailing
|
|
1219
|
+
# corrigendum: "IEEE Std 671-1985 [Corrigendum to IEEE Std 671-1985
|
|
1220
|
+
# (Reaff 2008)]/Cor. 1-2010" captures the bracket as the base's
|
|
1221
|
+
# nickname before the wrapper is parsed.
|
|
1222
|
+
book_nickname.maybe >>
|
|
1113
1223
|
# Trailing corrigendum after the draft+date ("IEEE Approved P1015/D1,
|
|
1114
1224
|
# Jan 2007/Cor. 1"): the generic bucket is the only path a status-word
|
|
1115
1225
|
# form reaches. Routes via build_flat_corrigendum (disjoint from the
|
|
@@ -1333,9 +1443,12 @@ module Pubid
|
|
|
1333
1443
|
# Slash-stage carrying a dash-date ("…P15288/CD2-2013-09 …" — the
|
|
1334
1444
|
# dash-year tail is what the joint grammar reads after the number, so
|
|
1335
1445
|
# this cannot steal the plain "/FDIS, June 2021" spellings
|
|
1336
|
-
# ieee_p_identifier finishes): → "…CD2 P15288-2013-09 …"
|
|
1446
|
+
# ieee_p_identifier finishes): → "…CD2 P15288-2013-09 …". The date
|
|
1447
|
+
# must be a plausible year — a 4-digit monthcode (YYMM, e.g.
|
|
1448
|
+
# "…P24748-4/DIS-1404" = April 2014) is a DRAFT designator, not a
|
|
1449
|
+
# dash-date, and rewriting it onto the number breaks the parse.
|
|
1337
1450
|
cleaned = cleaned.sub(
|
|
1338
|
-
%r{\A#{pubs}#{num}/ ?#{stage}(
|
|
1451
|
+
%r{\A#{pubs}#{num}/ ?#{stage}(-(?:19|20)\d\d(?:-\d\d)?)},
|
|
1339
1452
|
'\1\3 \2\4',
|
|
1340
1453
|
)
|
|
1341
1454
|
|
data/lib/pubid/ieee/renderer.rb
CHANGED
|
@@ -128,12 +128,20 @@ module Pubid
|
|
|
128
128
|
parts << type_str unless type_str.strip.empty?
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
# Code - with P prefix for projects (concatenated, not separated)
|
|
131
|
+
# Code - with P prefix for projects (concatenated, not separated).
|
|
132
|
+
# IEEE semantics (normative): P = project = the document is a
|
|
133
|
+
# draft; no P = it has become a standard. The P-state is
|
|
134
|
+
# identity-bearing, so the renderer never adds or drops it —
|
|
135
|
+
# whatever the source spelling carries round-trips.
|
|
132
136
|
if id.code_obj
|
|
133
137
|
result = id.code_obj.to_s
|
|
134
138
|
|
|
135
|
-
# Prepend P if this is a project AND code doesn't already have P
|
|
136
|
-
|
|
139
|
+
# Prepend P if this is a project AND code doesn't already have P.
|
|
140
|
+
# A recorded project marker (the source spelled the P on a
|
|
141
|
+
# non-IEEE-led publisher) prints regardless of the publisher.
|
|
142
|
+
if (id.typed_stage&.project_status && should_render_type ||
|
|
143
|
+
id.is_a?(Identifiers::ProjectDraftIdentifier) &&
|
|
144
|
+
id.project_marker) && !result.start_with?("P")
|
|
137
145
|
result = "P#{result}"
|
|
138
146
|
end
|
|
139
147
|
|
|
@@ -142,6 +150,7 @@ module Pubid
|
|
|
142
150
|
result += mark(id.code_obj.number, id.code_obj.prefix,
|
|
143
151
|
publishers: publishers_of(id))
|
|
144
152
|
|
|
153
|
+
|
|
145
154
|
# Only attach year to code if there's no edition, no month, and no draft
|
|
146
155
|
result += "-#{id.year}" if id.year && !id.draft_obj && !id.edition && !id.month
|
|
147
156
|
|
|
@@ -150,9 +159,29 @@ module Pubid
|
|
|
150
159
|
# ("P802.16Rev2/D3"). Keeps a revision distinct from its base standard.
|
|
151
160
|
result += "Rev#{id.revision}" if id.revision
|
|
152
161
|
|
|
153
|
-
# Append draft to code - with or without space based on original
|
|
162
|
+
# Append draft to code - with or without space based on original
|
|
163
|
+
# format. An exactly-IEC/IEEE co-published reference prints only the
|
|
164
|
+
# draft DESIGNATOR - a comma-separated project date ("IEC/IEEE
|
|
165
|
+
# 61886-1/D2, May 2020" prints as "…/D2") is catalogue metadata, not
|
|
166
|
+
# identity. Wider sets ("IEC/ISO/IEEE P82079-1/D1, June 2016") print
|
|
167
|
+
# the date; a space-separated date always stays ("…/DCDV July 2015").
|
|
168
|
+
# IEEE-published drafts normalize the comma date to the long form
|
|
169
|
+
# ("D08, September, 2018").
|
|
154
170
|
if id.draft_obj
|
|
155
|
-
|
|
171
|
+
printed = id.draft_obj.to_s
|
|
172
|
+
if id.publisher == "IEC" && id.copublisher == ["IEEE"]
|
|
173
|
+
printed = printed.split(", ").first
|
|
174
|
+
elsif id.publisher == "IEEE" && id.draft_status.to_s.empty?
|
|
175
|
+
# The long comma form is for dated project drafts; an
|
|
176
|
+
# unapproved-draft render keeps its pinned single-comma form
|
|
177
|
+
# (pubid#318 idempotence).
|
|
178
|
+
printed = printed.sub(/, ([A-Z][a-z]+) (\d{4})\z/, ', \1, \2')
|
|
179
|
+
end
|
|
180
|
+
# A space-separated draft year with no print date trailing is
|
|
181
|
+
# IEEE's dash form ("…/D1-2006"); with a trailing print date the
|
|
182
|
+
# space stays ("…/D1 2004, Nov 2004").
|
|
183
|
+
printed = printed.sub(/\A(D\d+) (\d{4})\z/, '\1-\2')
|
|
184
|
+
result += id.space_before_draft ? " #{printed}" : printed
|
|
156
185
|
end
|
|
157
186
|
|
|
158
187
|
# Append interpretation notation (/INT)
|
|
@@ -190,8 +219,12 @@ module Pubid
|
|
|
190
219
|
# Build the main identifier (without month yet)
|
|
191
220
|
result = parts.join(" ")
|
|
192
221
|
|
|
193
|
-
|
|
194
|
-
|
|
222
|
+
|
|
223
|
+
# Month/Day - append directly to avoid extra space before comma.
|
|
224
|
+
# An exactly-IEC/IEEE co-published reference drops the comma date
|
|
225
|
+
# entirely (same catalogue-metadata rule as the draft date above):
|
|
226
|
+
# "IEC/IEEE P60076-16, May 2016" prints as "IEC/IEEE 60076-16".
|
|
227
|
+
if id.month && !(id.publisher == "IEC" && id.copublisher == ["IEEE"])
|
|
195
228
|
result += ", #{id.month}"
|
|
196
229
|
result += " #{id.day}" if id.day
|
|
197
230
|
if id.year && !id.edition
|
|
@@ -127,6 +127,10 @@ module Pubid
|
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
def publisher_component
|
|
130
|
+
# The IEC/IEEE co-published pair is definitional to the class and
|
|
131
|
+
# was previously lost ("urn:ieee:ieee" — no number, no draft).
|
|
132
|
+
return "iec-ieee" if identifier.is_a?(Identifiers::IecIeeeCopublished)
|
|
133
|
+
|
|
130
134
|
pub = normalized_publisher
|
|
131
135
|
|
|
132
136
|
if identifier.copublisher&.any?
|
|
@@ -150,6 +154,14 @@ module Pubid
|
|
|
150
154
|
end
|
|
151
155
|
|
|
152
156
|
def type_component
|
|
157
|
+
# A joint stage draft carries the stage in its draft clause and the
|
|
158
|
+
# project P inside the code — a separate type segment would
|
|
159
|
+
# duplicate both.
|
|
160
|
+
if identifier.is_a?(Identifiers::JointDevelopment) &&
|
|
161
|
+
identifier.ieee_draft.to_s.start_with?("D=")
|
|
162
|
+
return nil
|
|
163
|
+
end
|
|
164
|
+
|
|
153
165
|
return nil unless identifier.type
|
|
154
166
|
|
|
155
167
|
type = identifier.type
|
|
@@ -159,6 +171,16 @@ module Pubid
|
|
|
159
171
|
end
|
|
160
172
|
|
|
161
173
|
def code_component
|
|
174
|
+
# The co-published code is number+parts+separators (no year — its
|
|
175
|
+
# own segment), e.g. "61886-1".
|
|
176
|
+
if identifier.is_a?(Identifiers::IecIeeeCopublished)
|
|
177
|
+
return nil if identifier.number.to_s.empty?
|
|
178
|
+
|
|
179
|
+
# Year-less: the publication year is its own URN segment (the
|
|
180
|
+
# rebuilt copublished_number would double-emit it).
|
|
181
|
+
return "#{identifier.number}#{identifier.parts_suffix}"
|
|
182
|
+
end
|
|
183
|
+
|
|
162
184
|
return nil unless identifier.code_obj
|
|
163
185
|
|
|
164
186
|
identifier.code_obj.to_s
|
|
@@ -182,6 +204,15 @@ module Pubid
|
|
|
182
204
|
end
|
|
183
205
|
|
|
184
206
|
def draft_component
|
|
207
|
+
if identifier.is_a?(Identifiers::JointDevelopment) &&
|
|
208
|
+
identifier.ieee_draft.to_s.start_with?("D=")
|
|
209
|
+
return "draft.#{identifier.ieee_draft}"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
if identifier.is_a?(Identifiers::IecIeeeCopublished)
|
|
213
|
+
return identifier.draft_info ? "draft.#{identifier.draft_info}" : nil
|
|
214
|
+
end
|
|
215
|
+
|
|
185
216
|
return nil unless identifier.draft_obj
|
|
186
217
|
|
|
187
218
|
"draft.#{identifier.draft_obj}"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# IETF flavor notes
|
|
2
|
+
|
|
3
|
+
IETF readiness for the unified relaton index.
|
|
4
|
+
|
|
5
|
+
These notes were part of the root `CLAUDE.md`. Read them before you change `lib/pubid/ietf/` or `spec/pubid/ietf/`. The root file keeps the cross-flavor contract that every flavor obeys.
|
|
6
|
+
|
|
7
|
+
- **IETF readiness for the unified relaton index (`relaton-data-ietf`)**: relaton is collapsing RFCs, the RFC sub-series (BCP/STD/FYI) and Internet-Drafts into **one** index keyed on the structured pubid hash (`pubid_class: ::Pubid::Ietf::Identifier`). That index is **all-or-nothing** — `Relaton::Index::FileIO#deserialize_id` raises on the *first* unparseable row and `#load_index` then rejects the whole index — so one bad id out of 176,862 means no consumer resolves *any* IETF reference. Four changes, all verified against the published corpora (`relaton-data-{rfcs,rfcsubseries,ids}`, branch `v2`). **(1) `draft_rest` accepts `.` and `A-Z`** (`match['a-zA-Z0-9+_.\-']`): exactly 50 of the 166,740 published draft ids failed without them (21 dotted `draft-ietf-pilc-2.5g3g-12`, 29 uppercase `draft-chapin-clnp-ISO8473-00`, 0 both), and a character census confirms the class now covers the corpus **exactly** — the only chars outside `[a-z0-9-]` anywhere in it are `. _ +` and `A-Z`. Widening is safe because the top-level alternation is keyed on the leading token (`RFC`/`BCP|STD|FYI`/`draft-`), so a wider draft tail can never make another branch match; `Builder#split_draft_version` needed no change (it inspects only the last three characters). The set stays **closed** — a space, `/` or `&` in a slug is crawler junk and still fails (pinned by negative examples). **(2) Zero-padded ids parse, grammar-level**: `rfc-index.xml` writes sub-series membership as `<is-also><doc-id>STD0066</doc-id></is-also>` (369 padded doc-ids) and RFCs as `RFC0001`, and relaton#109 forbids relaton normalizing them itself. The space is `.maybe` on both the `rfc` and `subseries` rules and `Builder#unpad` strips the pad (`/\A0+(?=\d)/`, a look-ahead so an all-zero number keeps its last digit), so `STD0066`/`STD 0066`/`STD66` all render the canonical `STD 66`. `unpad` is applied unconditionally, so the **spaced** padded form is normalized too — `RFC 0001` previously parsed and rendered back verbatim, and now renders `RFC 1` with a correspondingly changed hash and URN. That is deliberate (one canonical spelling per document) and has **zero** effect on the published corpora, which contain no padded row. **This is a normalizing parse, so the padded spellings must NEVER go into the byte-exact `spec/fixtures/ietf/identifiers/pass/`** — they live in `spec/pubid/ietf/zero_padded_spec.rb`, the BIPM `CIPM/2005-06(REV)` precedent. **(3) The Internet-Draft slug moved from `name` into `number`, and `series` became derived**: relaton bsearches on `id.root.number.to_s`, so with the slug in `name` all 166,740 drafts keyed to `""` and the narrowing bought nothing (with the slug: 43,564 buckets, median 2, worst 101). `name` is **dropped entirely** (no alias); the slug — leading `draft-` included — is the key, deliberately shared by a draft's unversioned aggregator row and every one of its versions so a document and its variants cluster. `series` is likewise **not stored**: `_type: pubid:ietf:std` already encodes it, so `Bcp`/`Std`/`Fyi` expose a `SERIES` constant + a plain `#series` reader (safe — `::Pubid::Identifier` declares no `series` attribute and nothing generic reads `.series`) and `build_subseries` stops passing it. The whole serialized vocabulary is now **three keys**: `_type` + `number` always, `version` only on a versioned draft — a sub-series row (`{_type, number}`) has the same shape as an RFC's. **The attributes live on the five LEAVES, not the shared `Pubid::Ietf::Identifier`**, via the `Identifiers::Serialization` mixin (`number` + the `key_value` block; `InternetDraft` merges its own `version` mapping on top — lutaml combines a mixin-installed block with a second in-class one, the ITU precedent). This is the IEEE `number` determinism landmine: a `:string` redefinition of the parent's `Components::Code number` on a class the leaves *inherit* from resolves nondeterministically under multi-flavor load. It was invisible while drafts left `number` nil; it is load-bearing now. The base deliberately carries **no** `key_value` block — one there would be inherited-and-merged by every leaf. `spec/pubid/ietf/root_number_spec.rb` is the tripwire (asserts `number` is a `String`, the exact keys, the exact `to_hash` key sets) and **must run under the full `rake` suite** to mean anything. **(4) Round-trip is validated here because relaton structurally cannot**: `FileIO#id_supported?` **skips** its `to_hash`/`from_hash` check whenever the object is a concrete subclass (`return true unless obj.instance_of?(@pubid_class)`), and *every* IETF id is a subclass — so a lossy round-trip would pass validation silently and produce wrong lookups. Three layers: `fixtures_spec.rb` is now **zero-failure** (was a 90% threshold — wrong for an all-or-nothing index) and checks `to_s` byte-exactness, `from_hash(to_hash)` fidelity in both `to_s` and `to_hash`, **and** a non-empty `root.number` for every fixture line; the fixtures grew to ~2,500 lines (the complete 365-row sub-series corpus, a 139-id structural edge set, a deterministic 1-in-84 draft sample — each file's header records the regeneration command); and `spec/pubid/ietf/corpus_round_trip_spec.rb` runs the same four checks over all 176,862 published ids, **opt-in** via `PUBID_IETF_CORPUS=<dir with the three relaton-data checkouts> bundle exec rake test:corpus_ietf` (self-skips otherwise; ~100s; verified 0 failures). **relaton note — the indexes must be regenerated, there is no alias**: `name` and `series` are gone as serialized keys, so a pre-`number` row (`{_type, name, version}`) deserializes with **`number` nil** and no error at all — lutaml ignores unknown keys, and relaton's subclass skip means `id_supported?` won't catch it either, leaving every draft keyed under `""`. Rendering is therefore the loud failure: `Renderer#require_number!` raises `ArgumentError` on a nil/empty `number` rather than returning `nil` or `"-02"`, and the renderer's sub-series arm is an explicit `when Bcp, Std, Fyi` with an `else raise` instead of a catch-all (a bare `Pubid::Ietf::Identifier` has no `series` reader). Pinned by the "legacy `name`-shaped hash" block in `root_number_spec.rb`. **Pre-existing bug fixed in passing**: `UrnParser`'s bare `Errors::ParseError` does not resolve — `Errors` lives under `Pubid::UrnParser` (the module), which is neither in the lexical scope of `Pubid::Ietf::UrnParser` nor an ancestor of `Base` — so an invalid URN raised `NameError` instead, and the spec's `raise_error(StandardError)` accepted it. Now fully qualified as `Pubid::UrnParser::Errors::ParseError` (the adobe/easc/gost/iala convention) with the spec asserting that exact class. **`Pubid::Iec::UrnParser` has the identical bug and is untouched here.** Also: `Identifier.parse` gained the inline `MAX_INPUT_LENGTH` guard — relaton reaches that class-level funnel directly through `pubid_class:`, bypassing `Pubid::Ietf.parse` — and IETF was added to the three cross-flavor tables it was missing from (`identifier_roundtrip_spec`, `uniform_identifier_handle_spec`, `redos_guard_spec`), which is what exercises the leaf `number` under real multi-flavor load. (hand-off: ietf-index-readiness.)
|
data/lib/pubid/ietf/builder.rb
CHANGED
data/lib/pubid/iho/builder.rb
CHANGED
data/lib/pubid/isbn/builder.rb
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# ISO flavor notes
|
|
2
|
+
|
|
3
|
+
- **No identifier attribute holds a `Components::Code` any more**: `number`,
|
|
4
|
+
`part` and `subpart` on `Pubid::Iso::Identifier`, the committee structure on
|
|
5
|
+
`Identifiers::TcDocument` (`tc_type`/`tc_number`/`sc_type`/`sc_number`/
|
|
6
|
+
`wg_type`/`wg_number`), `Identifiers::Directives#subgroup` and the
|
|
7
|
+
`edition.number` the builder writes are all plain strings, and
|
|
8
|
+
`Pubid::Iso::Components::Code` is deleted.
|
|
9
|
+
**The component was degenerate everywhere it was used.** Measured over the
|
|
10
|
+
whole 7,613-id pass corpus: no ISO Code carries `prefix`, `part`, `subpart`
|
|
11
|
+
or `parts`, `parts` is never written anywhere in `lib/pubid/iso`, and no Code
|
|
12
|
+
renders differently from its own `value`. ISO's part and subpart are sibling
|
|
13
|
+
attributes of the identifier, not fields of the number — the `-` join in
|
|
14
|
+
`Code#to_s` was unreachable.
|
|
15
|
+
**`edition.number` was worse than degenerate: it leaked a live object.**
|
|
16
|
+
`Components::Edition#number` is typed `Lutaml::Model::Type::Value`, so lutaml
|
|
17
|
+
never serialized it — `to_hash` returned the `Components::Code` instance
|
|
18
|
+
itself, and `to_yaml` emitted `!ruby/object:Pubid::Components::Code` with its
|
|
19
|
+
internal ivars, which `YAML.safe_load` refuses. An edition now serializes
|
|
20
|
+
`{"number" => "13", "original_text" => "Ed 13"}`.
|
|
21
|
+
**The TC converters are gone with it**: twelve `*_to_kv`/`*_from_kv` helpers
|
|
22
|
+
were replaced by plain `map "tc_type", to: :tc_type` declarations (the
|
|
23
|
+
ETSI/OIML shape), because a String needs no converter to flatten.
|
|
24
|
+
**Two shared surfaces needed a change, and both are the documented shims**:
|
|
25
|
+
`Renderers::DirectivesRenderer` called `subgroup.render(context:)` directly
|
|
26
|
+
and now reads through `render_component`; `Pubid::Iso.build_code` returned a
|
|
27
|
+
Code and now returns the string (ASTM's `to_iso_identifier` goes through it,
|
|
28
|
+
which is how one ASTM example caught the `NameError` after the class was
|
|
29
|
+
deleted).
|
|
30
|
+
**Verification — replay a baseline, do not trust the suite alone.** Against
|
|
31
|
+
all 7,621 pass-fixture ids captured on the parent commit, `to_s`, `to_urn`,
|
|
32
|
+
`to_mr_string`, `root.number` and the identifier class are **byte-identical**,
|
|
33
|
+
and `to_hash` differs for exactly **12** ids: the 9 `subgroup` rows and the 3
|
|
34
|
+
editions below. The first replay also caught a real regression the specs did
|
|
35
|
+
not — `directives.rb` still called `part.value.downcase`, so 4 Directives
|
|
36
|
+
URNs raised.
|
|
37
|
+
**relaton note — `relaton-data-iso` needs a re-crawl, and only for one key.**
|
|
38
|
+
`subgroup` flattens from `{"value" => "JTC 1"}` to `"JTC 1"`, and the
|
|
39
|
+
published `index-v2.yaml` (branch `v2`) carries **5 such rows**, all ISO/IEC
|
|
40
|
+
JTC 1 directives. **No compatibility shim is kept** — a stored nested
|
|
41
|
+
`subgroup` will not deserialize. The edition change reaches nothing stored:
|
|
42
|
+
`grep -c edition` over that index returns **0**.
|
|
43
|
+
**Spec fallout is the shape to expect for the remaining flavors**: 592
|
|
44
|
+
assertions across 25 files read `.number.value` / `.part.value`. The rewrite
|
|
45
|
+
needs a lookbehind, because `tc_number.value` and `edition.number.value` are
|
|
46
|
+
different attributes — the first pass stripped `edition.number.value` too,
|
|
47
|
+
and two corrigendum examples caught it.
|