pubid 2.0.0.pre.alpha.27 → 2.0.0.pre.alpha.29
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 +6 -3
- data/data/parg/iec.json +1 -1
- data/data/parg/ieee.json +1 -1
- data/data/parg/nesc.parg +17 -2
- data/data/parg/nist.json +1 -1
- data/lib/pubid/ieee/aiee/builder.rb +5 -2
- data/lib/pubid/ieee/aiee/identifier.rb +18 -13
- data/lib/pubid/ieee/builder.rb +164 -15
- data/lib/pubid/ieee/components/draft.rb +29 -3
- data/lib/pubid/ieee/identifiers/base.rb +7 -0
- data/lib/pubid/ieee/identifiers/joint_development.rb +15 -2
- data/lib/pubid/ieee/identifiers/nesc/base.rb +1 -1
- data/lib/pubid/ieee/identifiers/nesc/preprint.rb +41 -0
- data/lib/pubid/ieee/identifiers/nesc.rb +1 -0
- data/lib/pubid/ieee/long_renderer.rb +18 -0
- data/lib/pubid/ieee/nesc/builder.rb +5 -0
- data/lib/pubid/ieee/parser.rb +9 -4
- data/lib/pubid/ieee/renderer.rb +58 -4
- data/lib/pubid/ieee/urn_generator.rb +7 -0
- data/lib/pubid/ieee.rb +2 -0
- data/lib/pubid/nist/components/edition.rb +22 -1
- data/lib/pubid/nist/components/translation.rb +4 -2
- data/lib/pubid/nist/identifiers/base.rb +55 -22
- data/lib/pubid/nist/parser.rb +24 -8
- data/lib/pubid/nist/preprocessor.rb +15 -0
- data/lib/pubid/version.rb +1 -1
- metadata +8 -6
data/lib/pubid/ieee/renderer.rb
CHANGED
|
@@ -24,7 +24,29 @@ module Pubid
|
|
|
24
24
|
# (Pubid::Identifier#render), so the flag is safe as instance state.
|
|
25
25
|
@trademark = opts[:trademark]
|
|
26
26
|
|
|
27
|
-
render_id(@id)
|
|
27
|
+
result = render_id(@id)
|
|
28
|
+
# The long face spells draft months out ("Aug" → "August") over the
|
|
29
|
+
# bounded identifier — no title prose is ever in the string.
|
|
30
|
+
result = expand_months(result) if @long
|
|
31
|
+
result
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The long-form month names, longest key first ("Sept" before "Sep").
|
|
35
|
+
LONG_MONTHS = {
|
|
36
|
+
"Sept" => "September", "Jan" => "January", "Feb" => "February",
|
|
37
|
+
"Mar" => "March", "Apr" => "April", "Jun" => "June",
|
|
38
|
+
"Jul" => "July", "Aug" => "August", "Sep" => "September",
|
|
39
|
+
"Oct" => "October", "Nov" => "November", "Dec" => "December",
|
|
40
|
+
}.freeze
|
|
41
|
+
private_constant :LONG_MONTHS
|
|
42
|
+
|
|
43
|
+
# Expand abbreviated draft months to their long names (\b-bounded, so
|
|
44
|
+
# only whole month tokens move).
|
|
45
|
+
def expand_months(str)
|
|
46
|
+
LONG_MONTHS.each do |abbr, full|
|
|
47
|
+
str = str.gsub(/\b#{abbr}\b/, full)
|
|
48
|
+
end
|
|
49
|
+
str
|
|
28
50
|
end
|
|
29
51
|
|
|
30
52
|
private
|
|
@@ -125,6 +147,15 @@ module Pubid
|
|
|
125
147
|
!id.draft_status.to_s.match?(/unapproved/i)
|
|
126
148
|
type_str = id.type.dup
|
|
127
149
|
type_str = type_str.sub(/^P/, "") if type_str.start_with?("P")
|
|
150
|
+
# A draft carries the word Draft alone in every face (the C2
|
|
151
|
+
# ruling): Std/Standard never renders on a draft, whatever
|
|
152
|
+
# spelling the source used — a status-less draft ("IEEE Draft
|
|
153
|
+
# Std P802.3/D2.0") renders "IEEE Draft P802.3/D2.0".
|
|
154
|
+
type_str = "Draft" if id.draft_obj && !id.draft_status
|
|
155
|
+
# The long face expands the type word ("IEEE Standard 495-2007");
|
|
156
|
+
# a draft never takes Std/Standard in any face, so the expansion
|
|
157
|
+
# only ever sees the published word.
|
|
158
|
+
type_str = "Standard" if @long && type_str == "Std"
|
|
128
159
|
parts << type_str unless type_str.strip.empty?
|
|
129
160
|
end
|
|
130
161
|
|
|
@@ -151,8 +182,22 @@ module Pubid
|
|
|
151
182
|
publishers: publishers_of(id))
|
|
152
183
|
|
|
153
184
|
|
|
154
|
-
#
|
|
155
|
-
|
|
185
|
+
# A base year dash-attaches to the code when nothing else
|
|
186
|
+
# carries it: no edition, no identity-month date - and either
|
|
187
|
+
# no draft at all, or a draft that carries its OWN date (the
|
|
188
|
+
# draft+corrigendum family: "PC37.09-1999/D7.2, Nov 2006" -
|
|
189
|
+
# the 1999 is the base's year, the Nov 2006 the draft's).
|
|
190
|
+
# A numeric identity month is the SHORT format: it keeps the
|
|
191
|
+
# year on the code and appends itself dash-joined, whatever the
|
|
192
|
+
# input spelling ("ANSI/IEEE 21-1976-11" and "IEEE P15026.1,
|
|
193
|
+
# 05 2014" both render glued - pubid#517). The comma face is
|
|
194
|
+
# the LONG format, a month name ("..., May 2014").
|
|
195
|
+
glued_month = id.month.to_s.match?(/\A\d{1,2}\z/)
|
|
196
|
+
year_on_code = id.year && !id.edition &&
|
|
197
|
+
(!id.month || glued_month) &&
|
|
198
|
+
(!id.draft_obj || id.draft_obj&.year)
|
|
199
|
+
result += "-#{id.year}" if year_on_code
|
|
200
|
+
result += "-#{id.month}" if glued_month
|
|
156
201
|
|
|
157
202
|
# Append the numbered/lettered revision inline, in IEEE's native
|
|
158
203
|
# position — right after the code number and before the draft
|
|
@@ -226,12 +271,21 @@ module Pubid
|
|
|
226
271
|
# An exactly-IEC/IEEE co-published reference drops the comma date
|
|
227
272
|
# entirely (same catalogue-metadata rule as the draft date above):
|
|
228
273
|
# "IEC/IEEE P60076-16, May 2016" prints as "IEC/IEEE 60076-16".
|
|
229
|
-
if id.month && !
|
|
274
|
+
if id.month && !glued_month &&
|
|
275
|
+
!(id.publisher == "IEC" && id.copublisher == ["IEEE"])
|
|
230
276
|
result += ", #{id.month}"
|
|
231
277
|
result += " #{id.day}" if id.day
|
|
232
278
|
if id.year && !id.edition
|
|
233
279
|
result += " #{id.year}"
|
|
234
280
|
end
|
|
281
|
+
elsif id.year && id.draft_obj && !id.edition && !year_on_code &&
|
|
282
|
+
!(id.publisher == "IEC" && id.copublisher == ["IEEE"])
|
|
283
|
+
# A year-only date trailing the draft ("ANSI N42.34/D9a,
|
|
284
|
+
# 2015") keeps its comma form - the grammar's comma-year
|
|
285
|
+
# draft tail - instead of being silently dropped. A year
|
|
286
|
+
# that dash-attached to the code above (the base year of a
|
|
287
|
+
# dated-draft family) does not repeat here.
|
|
288
|
+
result += ", #{id.year}"
|
|
235
289
|
end
|
|
236
290
|
|
|
237
291
|
# Bounded parenthetical markers only. The unbounded relationship/
|
|
@@ -40,6 +40,13 @@ module Pubid
|
|
|
40
40
|
parts << "int"
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
# A NESC preprint is a draft-stage document distinct from the
|
|
44
|
+
# published edition it precedes — without this component it would
|
|
45
|
+
# share the edition's URN (urn:ieee:ieee:C2:2012).
|
|
46
|
+
if identifier.is_a?(Identifiers::Nesc::Preprint)
|
|
47
|
+
parts << "preprint"
|
|
48
|
+
end
|
|
49
|
+
|
|
43
50
|
# Conformance is always a ConformanceIdentifier wrapper (special_type
|
|
44
51
|
# "conformance"), never inline on a base standard — the base no longer
|
|
45
52
|
# carries a conf_number attribute, so there is no inline conf part.
|
data/lib/pubid/ieee.rb
CHANGED
|
@@ -21,6 +21,7 @@ module Pubid
|
|
|
21
21
|
autoload :PreParser, "#{__dir__}/ieee/pre_parser"
|
|
22
22
|
autoload :Compaction, "#{__dir__}/ieee/compaction"
|
|
23
23
|
autoload :ProjectRenderer, "#{__dir__}/ieee/project_renderer"
|
|
24
|
+
autoload :LongRenderer, "#{__dir__}/ieee/long_renderer"
|
|
24
25
|
autoload :Renderer, "#{__dir__}/ieee/renderer"
|
|
25
26
|
autoload :TypedStages, "#{__dir__}/ieee/typed_stages"
|
|
26
27
|
autoload :UrnGenerator, "#{__dir__}/ieee/urn_generator"
|
|
@@ -164,3 +165,4 @@ Pubid::Registry.register(:ieee, Pubid::Ieee)
|
|
|
164
165
|
Pubid::Ieee::Identifier.format_registry = Pubid::FormatRegistry.new(parent: Pubid::Identifier.format_registry)
|
|
165
166
|
Pubid::Ieee::Identifier.format_registry.register(:human, renderer: Pubid::Ieee::Renderer)
|
|
166
167
|
Pubid::Ieee::Identifier.format_registry.register(:project, renderer: Pubid::Ieee::ProjectRenderer)
|
|
168
|
+
Pubid::Ieee::Identifier.format_registry.register(:long, renderer: Pubid::Ieee::LongRenderer)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "date"
|
|
3
4
|
require "lutaml/model"
|
|
4
5
|
|
|
5
6
|
module Pubid
|
|
@@ -81,6 +82,10 @@ module Pubid
|
|
|
81
82
|
# For letter suffixes, append uppercase letter directly: " r5A" or "r5A"
|
|
82
83
|
if additional_text.match?(/^[A-Za-z]$/)
|
|
83
84
|
result += additional_text.upcase
|
|
85
|
+
elsif additional_text.match?(/\Ar\d+\z/)
|
|
86
|
+
# A revision marker rides attached ("e2001r3"), the 1.x
|
|
87
|
+
# corpus form
|
|
88
|
+
result += additional_text
|
|
84
89
|
else
|
|
85
90
|
# For other additional text (years, etc.), use dot separator
|
|
86
91
|
result += ".#{additional_text}"
|
|
@@ -106,6 +111,10 @@ module Pubid
|
|
|
106
111
|
# Use UPPERCASE for consistency: "r5A" for SP patterns like 800-53r5a
|
|
107
112
|
elsif additional_text.match?(/^[A-Za-z]$/)
|
|
108
113
|
result += additional_text.upcase # Normalize to UPPERCASE: r5A
|
|
114
|
+
elsif additional_text.match?(/\Ar\d+\z/)
|
|
115
|
+
# A revision marker rides attached ("e2001r3"), the 1.x
|
|
116
|
+
# corpus form
|
|
117
|
+
result += additional_text
|
|
109
118
|
else
|
|
110
119
|
# For ALL years (2-digit or 4-digit) and month+year, use DOT separator
|
|
111
120
|
# Examples: e2.50, e2.1915, e2.June1908
|
|
@@ -119,8 +128,20 @@ module Pubid
|
|
|
119
128
|
# Build long format: "Edition 2021", "Revision 5", etc.
|
|
120
129
|
def build_long_format
|
|
121
130
|
case type
|
|
131
|
+
# An edition date renders as the parenthesised date
|
|
132
|
+
# ("1000e2021" long: "... (2021)", "ABCe20130101" long:
|
|
133
|
+
# "... (January 01, 2013)"), the 1.x corpus form; a numbered
|
|
134
|
+
# edition keeps its worded form ("Edition 2")
|
|
122
135
|
when "e"
|
|
123
|
-
|
|
136
|
+
case id.to_s
|
|
137
|
+
when /\A\d{4}\z/ then "(#{id})"
|
|
138
|
+
when /\A(\d{4})(\d{2})(\d{2})\z/
|
|
139
|
+
"(#{::Date.new(Regexp.last_match[1].to_i,
|
|
140
|
+
Regexp.last_match[2].to_i,
|
|
141
|
+
Regexp.last_match[3].to_i)
|
|
142
|
+
.strftime('%B %d, %Y')})"
|
|
143
|
+
else "Edition #{id}"
|
|
144
|
+
end
|
|
124
145
|
when "r"
|
|
125
146
|
"Revision #{id}"
|
|
126
147
|
when "-"
|
|
@@ -31,11 +31,13 @@ module Pubid
|
|
|
31
31
|
|
|
32
32
|
case format
|
|
33
33
|
when :short, :long
|
|
34
|
-
|
|
34
|
+
# 1.x corpus shape: the trailing parenthesised suffix
|
|
35
|
+
# "(en)" (pubid#497), not a leading segment.
|
|
36
|
+
"(#{code})"
|
|
35
37
|
when :mr
|
|
36
38
|
".#{code}"
|
|
37
39
|
else
|
|
38
|
-
"
|
|
40
|
+
"(#{code})"
|
|
39
41
|
end
|
|
40
42
|
end
|
|
41
43
|
end
|
|
@@ -438,32 +438,42 @@ module Pubid
|
|
|
438
438
|
end
|
|
439
439
|
|
|
440
440
|
def to_full_style
|
|
441
|
-
# "National Institute of Standards and Technology Special
|
|
441
|
+
# "National Institute of Standards and Technology Special
|
|
442
|
+
# Publication 1000 Part 1 Supplement 3 Section 2 Index Update 5
|
|
443
|
+
# (2021)(en)" - the edition renders its own long form ("Revision
|
|
444
|
+
# 5" / "(2021)"), never the derived revision view alongside it
|
|
442
445
|
result = publisher_full_name
|
|
443
446
|
result += " #{series_full_name}" if series
|
|
444
447
|
result += " #{number}" if number
|
|
445
448
|
result += parts.map { |p| "-#{p}" }.join if parts&.any?
|
|
446
449
|
|
|
447
|
-
#
|
|
450
|
+
# The 1.x corpus order: stage, volume, part, revision, version,
|
|
451
|
+
# supplement, section, update; an edition date trails as the
|
|
452
|
+
# parenthesised date before the translation
|
|
453
|
+
result += " #{stage.to_s(:long)}" if stage
|
|
454
|
+
|
|
455
|
+
# Volume and issue number in long form: "Volume 6, No. 12" - the
|
|
456
|
+
# component's own v-notation must not leak into the worded form
|
|
448
457
|
if volume && issue_number
|
|
449
|
-
result += "
|
|
458
|
+
result += " Volume #{volume.value}, #{issue_number.to_s(:long)}"
|
|
450
459
|
elsif volume
|
|
451
|
-
result += "
|
|
460
|
+
result += " Volume #{volume.value}"
|
|
452
461
|
end
|
|
453
462
|
|
|
454
|
-
|
|
455
|
-
result += " #{edition.to_s(:long)}" if edition
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
463
|
+
result += " Part #{part.value}" if part
|
|
464
|
+
result += " #{edition.to_s(:long)}" if edition && !edition_year?
|
|
465
|
+
# A revision riding on a date edition composes at the revision
|
|
466
|
+
# slot ("... Part 2, Revision 3 ... (2001)")
|
|
467
|
+
if edition&.additional_text.to_s =~ /\Ar(\d+)\z/
|
|
468
|
+
result += " Revision #{Regexp.last_match[1]}"
|
|
469
|
+
end
|
|
460
470
|
result += " #{version_component.to_s(:long)}" if version_component
|
|
461
|
-
|
|
462
|
-
#
|
|
471
|
+
result += " #{supplement.to_s(:long)}" if supplement
|
|
472
|
+
result += " Section #{section}" if section
|
|
473
|
+
result += " Index" if index
|
|
463
474
|
result += " #{update_component.to_s(:long)}" if update_component
|
|
464
475
|
|
|
465
|
-
#
|
|
466
|
-
result += " #{stage.to_s(:long)}" if stage
|
|
476
|
+
result += " #{edition.to_s(:long)}" if edition && edition_year?
|
|
467
477
|
|
|
468
478
|
# V2: Use translation_component (already includes space)
|
|
469
479
|
result += translation_component.to_s(:long) if translation_component
|
|
@@ -478,10 +488,9 @@ module Pubid
|
|
|
478
488
|
result += " #{number}" if number
|
|
479
489
|
result += " Part #{parts.first}" if parts&.any?
|
|
480
490
|
|
|
481
|
-
#
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
result += ", Revision #{revision}" if revision
|
|
491
|
+
# The edition renders its own form ("Revision 5" / "(2021)");
|
|
492
|
+
# the derived revision view never renders alongside it
|
|
493
|
+
result += " #{edition.to_s(:long)}" if edition
|
|
485
494
|
|
|
486
495
|
# V2: Use version_component
|
|
487
496
|
result += " #{version_component.to_s(:abbrev)}" if version_component
|
|
@@ -565,7 +574,8 @@ module Pubid
|
|
|
565
574
|
|
|
566
575
|
# Volume and Part components (v6n1 notation for CSM, pt1 for SP)
|
|
567
576
|
if volume.is_a?(Components::Volume) && effective_part.is_a?(Components::Part)
|
|
568
|
-
#
|
|
577
|
+
# the volume keeps its leading space before the part
|
|
578
|
+
# ("535 v1pt1"), the corpus form (testsuite 10491/10492)
|
|
569
579
|
result += " #{volume}#{effective_part}"
|
|
570
580
|
elsif effective_part.is_a?(Components::Part)
|
|
571
581
|
# SP and other series: use Part.type to determine format
|
|
@@ -578,6 +588,11 @@ module Pubid
|
|
|
578
588
|
# Render volume and issue number in short form: "v6n12"
|
|
579
589
|
vol_str = volume.is_a?(Components::Volume) ? volume.to_s : "v#{volume}"
|
|
580
590
|
result += "#{vol_str}n#{issue_number.number}"
|
|
591
|
+
elsif issue_number
|
|
592
|
+
# A standalone issue renders its own short form (pubid#497 -
|
|
593
|
+
# the metanorma-nist port passes the index here; n4 must not
|
|
594
|
+
# silently drop).
|
|
595
|
+
result += issue_number.to_s(:short)
|
|
581
596
|
end
|
|
582
597
|
|
|
583
598
|
# Use edition component properly (e2, e2021, r5, -3)
|
|
@@ -615,9 +630,12 @@ module Pubid
|
|
|
615
630
|
|
|
616
631
|
# Add other attributes
|
|
617
632
|
result += errata.to_s if errata
|
|
618
|
-
result += "index" if index
|
|
619
633
|
result += "insert" if insert
|
|
634
|
+
# The 1.x order is section before index ("sup3sec2indx"); the index
|
|
635
|
+
# spelling is the abbreviated indx (pubid#497) — the full "index"
|
|
636
|
+
# input spelling canonicalizes onto it.
|
|
620
637
|
result += "sec#{section}" if section
|
|
638
|
+
result += "indx" if index
|
|
621
639
|
result += "app" if appendix
|
|
622
640
|
|
|
623
641
|
# Add addendum - render as " Add." suffix
|
|
@@ -694,6 +712,9 @@ module Pubid
|
|
|
694
712
|
elsif volume && issue_number
|
|
695
713
|
vol_str = volume.is_a?(Components::Volume) ? volume.to_s : "v#{volume}"
|
|
696
714
|
result += "#{vol_str}n#{issue_number.number}"
|
|
715
|
+
elsif issue_number
|
|
716
|
+
# A standalone issue renders its own short form (pubid#497)
|
|
717
|
+
result += issue_number.to_s(:mr)
|
|
697
718
|
end
|
|
698
719
|
|
|
699
720
|
# With a number, the edition glues to it per the NIST spec
|
|
@@ -727,19 +748,31 @@ module Pubid
|
|
|
727
748
|
result += ".Add."
|
|
728
749
|
end
|
|
729
750
|
|
|
751
|
+
# Section and index travel with the identifier in MR form too
|
|
752
|
+
# (pubid#497 - sec<N> / indx must not drop)
|
|
753
|
+
result += ".sec#{section}" if section
|
|
754
|
+
result += ".indx" if index
|
|
755
|
+
|
|
730
756
|
# Use translation_component
|
|
731
757
|
result += translation_component.to_s(:mr) if translation_component
|
|
732
758
|
|
|
733
759
|
result
|
|
734
760
|
end
|
|
735
761
|
|
|
762
|
+
# An e-type edition whose id is a date (year or full date)
|
|
763
|
+
# composes as the trailing parenthesised date
|
|
764
|
+
def edition_year?
|
|
765
|
+
edition&.type == "e" && edition.id.to_s =~ /\A\d{4}(\d{2}(\d{2})?)?\z/
|
|
766
|
+
end
|
|
767
|
+
|
|
736
768
|
def series_full_name
|
|
737
769
|
{
|
|
738
770
|
"SP" => "Special Publication",
|
|
739
|
-
"FIPS" => "Federal Information Processing Standards",
|
|
771
|
+
"FIPS" => "Federal Information Processing Standards Publication",
|
|
740
772
|
"IR" => "Interagency Report",
|
|
741
773
|
"TN" => "Technical Note",
|
|
742
|
-
|
|
774
|
+
"CSWP" => "Cybersecurity White Papers",
|
|
775
|
+
}[series.to_s] || series.to_s
|
|
743
776
|
end
|
|
744
777
|
|
|
745
778
|
def series_abbreviated_name
|
data/lib/pubid/nist/parser.rb
CHANGED
|
@@ -138,10 +138,15 @@ module Pubid
|
|
|
138
138
|
# Two-letter suffixes: Ur (Unclassified Revised), Ua (Unclassified Amended), Ub-Uj (series variants)
|
|
139
139
|
# Single letter: any letter not followed by excluded keywords
|
|
140
140
|
rule(:number_suffix) do
|
|
141
|
-
(str("U") >> lower_letter) |
|
|
141
|
+
(str("U") >> lower_letter) |
|
|
142
|
+
# An "r" directly followed by a digit is the revision marker (pubid#170),
|
|
143
|
+
# not a suffix — leave it for the second_number/edition rules.
|
|
144
|
+
# The guard sits before the letter so it tests the suffix start itself.
|
|
145
|
+
((str("r") >> digit).absent? >> match("[a-zA-Z]") >> (
|
|
142
146
|
# Match suffixes
|
|
143
147
|
str("ec") |
|
|
144
148
|
str("ndex") |
|
|
149
|
+
str("ndx") |
|
|
145
150
|
str("nsert") |
|
|
146
151
|
str("rrata") |
|
|
147
152
|
str("raft") | # NEW: Exclude "draft" from number suffix matching
|
|
@@ -264,7 +269,9 @@ module Pubid
|
|
|
264
269
|
(digits >> str("r") >> digits >> match("[a-zA-Z]")) |
|
|
265
270
|
# NEW: Revision pattern with year (e.g., "126r2013")
|
|
266
271
|
# This handles SP revision format where revision is attached to second_number
|
|
267
|
-
(
|
|
272
|
+
# Dotted minor accepted atomically (pubid#170): "53r4.1" binds id "4.1"
|
|
273
|
+
(digits.as(:number_only) >> str("r") >>
|
|
274
|
+
(digits >> (str(".") >> digits).maybe).as(:edition_id)) |
|
|
268
275
|
# CRPL range with underscore (e.g., "2_3-1A")
|
|
269
276
|
(digits >> str("_") >> digits >> dash >> digits >> upper_letter.maybe) |
|
|
270
277
|
# Letter followed by dash and digits (e.g., "m-5")
|
|
@@ -282,7 +289,9 @@ module Pubid
|
|
|
282
289
|
# For patterns like NIST SP 800-27ra where revision 'ra' is attached directly to number
|
|
283
290
|
(str("r") >> match("[a-zA-Z]")).as(:revision_letter_suffix) |
|
|
284
291
|
# NEW: Simple revision pattern r followed by digits (e.g., "r1", "r2") for trailing revision
|
|
285
|
-
(
|
|
292
|
+
# Dotted minor accepted atomically (pubid#170): "r4.1" binds id "4.1"
|
|
293
|
+
(str("r") >>
|
|
294
|
+
(digits >> (str(".") >> digits).maybe).as(:edition_id)).as(:revision_simple) |
|
|
286
295
|
# Special patterns like "NCNR", "PERMIS", "BFRL"
|
|
287
296
|
str("NCNR") | str("PERMIS") | str("BFRL") |
|
|
288
297
|
# Just capital letters (e.g., "A", "B", "C") - standalone
|
|
@@ -302,15 +311,22 @@ module Pubid
|
|
|
302
311
|
# Enhanced: Support space-separated format from preprocessing (r1 separated from number)
|
|
303
312
|
rule(:edition) do
|
|
304
313
|
# Edition with "e" prefix: e2, e3, e2021 (1-4 digits for ID)
|
|
305
|
-
|
|
314
|
+
# Dotted minor accepted atomically (pubid#170): "e2006.1" binds id "2006.1"
|
|
315
|
+
(space.maybe >> str("e") >>
|
|
316
|
+
(digits >> (str(".") >> digits).maybe).as(:edition_id)).as(:edition_e) |
|
|
306
317
|
# Revision with "r" prefix and SPACE, with letter: r 5A (preserve format)
|
|
307
|
-
|
|
318
|
+
# Dotted minor accepted atomically (pubid#170)
|
|
319
|
+
(space >> str("r") >> (digits >> (str(".") >> digits).maybe).as(:edition_id) >>
|
|
320
|
+
match("[a-zA-Z]").as(:edition_letter)).as(:edition_r_with_space_letter) |
|
|
308
321
|
# Revision with "r" prefix and SPACE: r 5 (preserve format)
|
|
309
|
-
(space >> str("r") >>
|
|
322
|
+
(space >> str("r") >>
|
|
323
|
+
(digits >> (str(".") >> digits).maybe).as(:edition_id)).as(:edition_r_with_space) |
|
|
310
324
|
# Revision with "r" prefix NO space, with letter: r5A (compact format)
|
|
311
|
-
(str("r") >> digits
|
|
325
|
+
(str("r") >> (digits >> (str(".") >> digits).maybe).as(:edition_id) >>
|
|
326
|
+
match("[a-zA-Z]").as(:edition_letter)).as(:edition_r_no_space_letter) |
|
|
312
327
|
# Revision with "r" prefix NO space: r5 (compact format)
|
|
313
|
-
(str("r") >>
|
|
328
|
+
(str("r") >>
|
|
329
|
+
(digits >> (str(".") >> digits).maybe).as(:edition_id)).as(:edition_r_no_space) |
|
|
314
330
|
# Revision with "rev" prefix (verbose): rev2013, rev 2013
|
|
315
331
|
(space.maybe >> str("rev") >> space.maybe >> digits.as(:edition_id)).as(:edition_rev) |
|
|
316
332
|
# Historical with "-" prefix: -2, -3 (ONLY if followed by non-digit or end)
|
|
@@ -76,6 +76,7 @@ module Pubid
|
|
|
76
76
|
normalize_supplement_and_part!
|
|
77
77
|
normalize_version_notation!
|
|
78
78
|
normalize_edition_year_suffix!
|
|
79
|
+
normalize_glued_dotted_edition!
|
|
79
80
|
normalize_revision_with_letter!
|
|
80
81
|
normalize_version_dotted_spaces!
|
|
81
82
|
normalize_update_markers!
|
|
@@ -264,10 +265,24 @@ module Pubid
|
|
|
264
265
|
end
|
|
265
266
|
|
|
266
267
|
# Edition year suffix shorthand: "2006ed." → "e2006".
|
|
268
|
+
# A dotted minor after the dot stays attached (pubid#170): "2006ed.1" → "e2006.1",
|
|
269
|
+
# so the edition id binds atomically instead of corrupting into "e20061".
|
|
267
270
|
def normalize_edition_year_suffix!
|
|
271
|
+
@cleaned = @cleaned.gsub(/(\d{4})ed\.(\d+)/, 'e\1.\2')
|
|
268
272
|
@cleaned = @cleaned.gsub(/(\d{4})ed\./, 'e\1')
|
|
269
273
|
end
|
|
270
274
|
|
|
275
|
+
# A glued dotted e-edition needs the space the spaced form has (pubid#170):
|
|
276
|
+
# "800-53e2.1" → "800-53 e2.1", so the edition rule binds "2.1" atomically.
|
|
277
|
+
# The minor is 1-2 digits — a 4-digit tail is the additional-text year of
|
|
278
|
+
# forms like "NBS CIRC 11e2.1915", which must keep its existing tree.
|
|
279
|
+
# Only dotted e-editions are unglued; the dot-less glued form "800-53e2"
|
|
280
|
+
# keeps its number-bound tree. Mirrors the "v1.1" unglue in
|
|
281
|
+
# normalize_draft_and_volume!.
|
|
282
|
+
def normalize_glued_dotted_edition!
|
|
283
|
+
@cleaned = @cleaned.gsub(/(\d)e(\d+\.\d{1,2})(?!\d)/, '\1 e\2')
|
|
284
|
+
end
|
|
285
|
+
|
|
271
286
|
# Revision attached to a number with optional letter suffix. When
|
|
272
287
|
# a letter suffix is present, keep them together for the
|
|
273
288
|
# second_number grammar rule; otherwise insert a space before
|
data/lib/pubid/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pubid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.0.pre.alpha.
|
|
4
|
+
version: 2.0.0.pre.alpha.29
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0'
|
|
19
|
+
version: '0.8'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- - "
|
|
24
|
+
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0'
|
|
26
|
+
version: '0.8'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: parslet
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -640,6 +640,7 @@ files:
|
|
|
640
640
|
- lib/pubid/ieee/identifiers/nesc/draft.rb
|
|
641
641
|
- lib/pubid/ieee/identifiers/nesc/edition.rb
|
|
642
642
|
- lib/pubid/ieee/identifiers/nesc/handbook.rb
|
|
643
|
+
- lib/pubid/ieee/identifiers/nesc/preprint.rb
|
|
643
644
|
- lib/pubid/ieee/identifiers/nesc/redline.rb
|
|
644
645
|
- lib/pubid/ieee/identifiers/nesc/standard.rb
|
|
645
646
|
- lib/pubid/ieee/identifiers/parenthetical_identifier.rb
|
|
@@ -652,6 +653,7 @@ files:
|
|
|
652
653
|
- lib/pubid/ieee/ire/builder.rb
|
|
653
654
|
- lib/pubid/ieee/ire/identifier.rb
|
|
654
655
|
- lib/pubid/ieee/ire/parser.rb
|
|
656
|
+
- lib/pubid/ieee/long_renderer.rb
|
|
655
657
|
- lib/pubid/ieee/nesc.rb
|
|
656
658
|
- lib/pubid/ieee/nesc/builder.rb
|
|
657
659
|
- lib/pubid/ieee/nesc/parser.rb
|