pubid 2.0.0.pre.alpha.27 → 2.0.0.pre.alpha.28
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 +1 -1
- 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 +132 -14
- data/lib/pubid/ieee/components/draft.rb +29 -3
- data/lib/pubid/ieee/identifiers/joint_development.rb +11 -2
- data/lib/pubid/ieee/long_renderer.rb +18 -0
- data/lib/pubid/ieee/renderer.rb +48 -3
- 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 +1 -0
- data/lib/pubid/version.rb +1 -1
- metadata +3 -2
|
@@ -38,9 +38,12 @@ module Pubid
|
|
|
38
38
|
|
|
39
39
|
# Extract the long-form date separator ("," or ".") for rendering.
|
|
40
40
|
# Named `date_separator` on the identifier to avoid colliding with the
|
|
41
|
-
# CodeNumber code-part `separator`.
|
|
41
|
+
# CodeNumber code-part `separator`. The dash-glued form ("-1928-05")
|
|
42
|
+
# carries no separator capture (its tail binds as the year/month
|
|
43
|
+
# clause itself), so a month without a separator capture IS the
|
|
44
|
+
# dash form.
|
|
42
45
|
separator_str = extract_value(parsed[:separator])
|
|
43
|
-
attributes[:date_separator] = separator_str if separator_str
|
|
46
|
+
attributes[:date_separator] = separator_str || "-" if separator_str || month_str
|
|
44
47
|
|
|
45
48
|
# Set original format based on parsed data
|
|
46
49
|
attributes[:original_format] = if separator_str || month_str
|
|
@@ -59,20 +59,25 @@ module Pubid
|
|
|
59
59
|
format ||= month || date_separator ? "long" : "short"
|
|
60
60
|
|
|
61
61
|
if year
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
base += "-#{year}"
|
|
65
|
-
when "long", :long
|
|
66
|
-
sep = date_separator || "," # Default to comma for long form
|
|
67
|
-
base += "#{sep} #{"#{month} " if month}#{year}"
|
|
62
|
+
if date_separator == "-" && month
|
|
63
|
+
# The dash-glued numeric form renders whole ("No 15-1928-05")
|
|
64
|
+
base += "-#{year}-#{month}"
|
|
68
65
|
else
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
case format
|
|
67
|
+
when "short", :short
|
|
68
|
+
base += "-#{year}"
|
|
69
|
+
when "long", :long
|
|
70
|
+
sep = date_separator || "," # Default to comma for long form
|
|
71
|
+
base += "#{sep} #{"#{month} " if month}#{year}"
|
|
72
|
+
else
|
|
73
|
+
base += if date_separator
|
|
74
|
+
"#{date_separator} #{"#{month} " if month}#{year}"
|
|
75
|
+
elsif month
|
|
76
|
+
", #{month} #{year}"
|
|
77
|
+
else
|
|
78
|
+
"-#{year}"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
76
81
|
end
|
|
77
82
|
end
|
|
78
83
|
|
data/lib/pubid/ieee/builder.rb
CHANGED
|
@@ -771,6 +771,11 @@ module Pubid
|
|
|
771
771
|
def build_joint_development(parsed)
|
|
772
772
|
attributes = {}
|
|
773
773
|
|
|
774
|
+
# The status-draft spelling ("IEEE Unapproved P15288/DFDIS") keeps
|
|
775
|
+
# its status word on the designator face (pubid#318: the word marks
|
|
776
|
+
# the draft).
|
|
777
|
+
attributes[:draft_status] = extract_value(parsed[:draft_status]) if parsed[:draft_status]
|
|
778
|
+
|
|
774
779
|
# Extract publishers from joint_publishers
|
|
775
780
|
if parsed[:joint_publishers]
|
|
776
781
|
joint_pub_str = extract_value(parsed[:joint_publishers])
|
|
@@ -788,6 +793,17 @@ module Pubid
|
|
|
788
793
|
# date ("P42010/D-4-2019") rides the code as a year-part
|
|
789
794
|
# ("P42010.2019/D4") — the face the relaton fixtures pin — while
|
|
790
795
|
# stage-first joints keep the top-level year ("FDIS P15289:2017").
|
|
796
|
+
# A part whose whole value is an ISO/IEC stage token is the
|
|
797
|
+
# stage-last spelling ("29119.FDIS"), not a part: promote it to
|
|
798
|
+
# the stage slot (the #311 embedded rule covers "29119.4.FDIS";
|
|
799
|
+
# this covers the part-less form the shared part capture claims
|
|
800
|
+
# first).
|
|
801
|
+
if parsed[:iso_stage].nil? && parsed[:part] &&
|
|
802
|
+
extract_value(parsed[:part]).to_s.match?(/\A[._-]?(FDIS|FCD|CDV|DIS|CD\d?|WD|PWI|NP)\z/)
|
|
803
|
+
# the stage is the bare token; the capture carries its separator
|
|
804
|
+
parsed[:iso_stage] = extract_value(parsed[:part]).to_s.sub(/\A[._-]/, "")
|
|
805
|
+
parsed[:part] = nil
|
|
806
|
+
end
|
|
791
807
|
code_parts = []
|
|
792
808
|
if parsed[:part].nil? && parsed[:iso_stage].nil? && parsed[:draft_version] &&
|
|
793
809
|
parsed[:printed_dash_year] && parsed[:year] &&
|
|
@@ -805,15 +821,23 @@ module Pubid
|
|
|
805
821
|
"#{extract_value(parsed[:draft_version])}-#{extract_value(parsed[:year])}"
|
|
806
822
|
end
|
|
807
823
|
end
|
|
808
|
-
|
|
824
|
+
# The part/subpart captures carry their printed separator
|
|
825
|
+
# (C1: the part binds before the draft status and the year,
|
|
826
|
+
# separator-in-capture), so they concatenate verbatim - a
|
|
827
|
+
# synthetic separator here is what produced "61850..9".
|
|
828
|
+
# The 8802-family IEC label binds its part with a dash the
|
|
829
|
+
# grammar consumes separately (iec_label_dash): "8802-9", not
|
|
830
|
+
# the dot join a bare part would get.
|
|
831
|
+
if parsed[:part] && parsed[:iec_label_dash]
|
|
832
|
+
code_parts << "-#{extract_value(parsed[:part])}"
|
|
833
|
+
elsif parsed[:part]
|
|
834
|
+
code_parts << extract_value(parsed[:part])
|
|
835
|
+
end
|
|
836
|
+
code_parts << extract_value(parsed[:subpart]) if parsed[:subpart]
|
|
809
837
|
|
|
810
838
|
code_str = extract_value(parsed[:number])
|
|
811
839
|
if code_str && !code_parts.empty?
|
|
812
|
-
|
|
813
|
-
# the printed separator (the iso-format route's :part_dash
|
|
814
|
-
# prints as a dot in the joint code).
|
|
815
|
-
sep = parsed[:iec_label_dash] ? "-" : "."
|
|
816
|
-
code_str += "#{sep}#{code_parts.join(sep)}"
|
|
840
|
+
code_str += code_parts.map { |p| p.to_s.start_with?(".", "-", "_") ? p : ".#{p}" }.join
|
|
817
841
|
end
|
|
818
842
|
# P = project (a draft): identity-bearing, preserved as spelled.
|
|
819
843
|
code_str = "P#{code_str}" if parsed[:project_marker] && code_str
|
|
@@ -867,7 +891,34 @@ module Pubid
|
|
|
867
891
|
draft_ver = extract_value(parsed[:draft_version])
|
|
868
892
|
# Remove leading 'D' if present since draft_version already has it
|
|
869
893
|
draft_ver = draft_ver.sub(/^D/, "") if draft_ver
|
|
870
|
-
|
|
894
|
+
if draft_ver
|
|
895
|
+
# A date trailing the draft ("/D8, June 2010") belongs to the
|
|
896
|
+
# draft component (canonical month, comma form), and the
|
|
897
|
+
# identity year/month the earlier promotion filled from the
|
|
898
|
+
# same captures are vacated. Stage-first ISO rows are
|
|
899
|
+
# exempt: their canonical is the ISO face, which prints the
|
|
900
|
+
# date from the identity slots (", March 2017", pubid#216).
|
|
901
|
+
if parsed[:draft_month] && parsed[:iso_stage].nil?
|
|
902
|
+
draft = Components::Draft.new(
|
|
903
|
+
version: draft_ver,
|
|
904
|
+
month: extract_value(parsed[:draft_month]),
|
|
905
|
+
year: extract_value(parsed[:draft_year]))
|
|
906
|
+
draft.comma_before_month = true
|
|
907
|
+
attributes[:draft] = draft.to_s.sub(%r{\A/}, "")
|
|
908
|
+
attributes.delete(:year) if parsed[:year].nil?
|
|
909
|
+
attributes.delete(:month) if parsed[:month].nil?
|
|
910
|
+
elsif parsed[:draft_year] && parsed[:iso_stage].nil?
|
|
911
|
+
# The artifact's comma-year tail captures its separator
|
|
912
|
+
# with the value (", 2015") — strip it (the #471
|
|
913
|
+
# separator-in-capture rule: every capture a renderer
|
|
914
|
+
# re-joins gets checked).
|
|
915
|
+
draft_year = extract_value(parsed[:draft_year]).to_s.sub(/\A[, ]+/, "")
|
|
916
|
+
attributes[:draft] = "D#{draft_ver}, #{draft_year}"
|
|
917
|
+
attributes.delete(:year) if parsed[:year].nil?
|
|
918
|
+
else
|
|
919
|
+
attributes[:ieee_draft] = "D#{draft_ver}"
|
|
920
|
+
end
|
|
921
|
+
end
|
|
871
922
|
end
|
|
872
923
|
|
|
873
924
|
# The joint stage-draft clause (docs/IEEE-DRAFT-STAGES.md §1.3):
|
|
@@ -909,7 +960,9 @@ module Pubid
|
|
|
909
960
|
# must not become a printed Standard (which keeps the code's P).
|
|
910
961
|
numeric_draft = parsed[:draft_version] &&
|
|
911
962
|
extract_value(parsed[:draft_version]).to_s.match?(/\A\d/)
|
|
912
|
-
|
|
963
|
+
# A promoted stage ("29119.FDIS") is printed identity - the joint
|
|
964
|
+
# model carries it, never the printed Standard face.
|
|
965
|
+
printed_joint = parsed[:iso_published] && parsed[:iso_stage].nil? &&
|
|
913
966
|
(parsed[:printed_dash_year] || parsed[:printed_month_year] ||
|
|
914
967
|
(numeric_draft && parsed[:draft_month]) ||
|
|
915
968
|
# A date-less stage-less joint reference carrying only a
|
|
@@ -919,9 +972,18 @@ module Pubid
|
|
|
919
972
|
(parsed[:year].nil? && parsed[:parameters].is_a?(Hash) &&
|
|
920
973
|
parsed[:parameters][:parenthetical_content]))
|
|
921
974
|
if printed_joint
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
975
|
+
# The part capture carries its own separator (C1
|
|
976
|
+
# separator-in-capture); join verbatim, synthesizing one only
|
|
977
|
+
# for a bare capture.
|
|
978
|
+
part_val = extract_value(parsed[:part])
|
|
979
|
+
printed_code = extract_value(parsed[:number]).to_s
|
|
980
|
+
if part_val
|
|
981
|
+
printed_code += if part_val.start_with?(".", "-", "_")
|
|
982
|
+
part_val
|
|
983
|
+
else
|
|
984
|
+
"#{parsed[:part_dash] ? "-" : "."}#{part_val}"
|
|
985
|
+
end
|
|
986
|
+
end
|
|
925
987
|
# P = project: the marker is identity, preserved as spelled.
|
|
926
988
|
printed_code = "P#{printed_code}" if parsed[:project_marker]
|
|
927
989
|
printed_attrs = { publisher: attributes[:publisher],
|
|
@@ -1049,6 +1111,24 @@ module Pubid
|
|
|
1049
1111
|
Pubid::Ieee.locate_stage("P")
|
|
1050
1112
|
end
|
|
1051
1113
|
|
|
1114
|
+
# A plain IEEE-designator draft on joint publishers ("/D8,
|
|
1115
|
+
# June 2010") carries no ISO stage machinery: it is the same
|
|
1116
|
+
# document family as its bare-IEEE twin and the D= designator
|
|
1117
|
+
# rows, so it builds as the project draft they pin (pubid#430)
|
|
1118
|
+
# rather than the joint-development model.
|
|
1119
|
+
if attributes[:draft] && parsed[:iso_stage].nil? &&
|
|
1120
|
+
parsed[:iec_stage].nil? && parsed[:amd_number].nil?
|
|
1121
|
+
draft_attrs = { code: attributes[:code],
|
|
1122
|
+
draft: attributes[:draft] }
|
|
1123
|
+
if attributes[:publishers]
|
|
1124
|
+
draft_attrs[:publisher] = attributes[:publishers].first
|
|
1125
|
+
rest = attributes[:publishers][1..]
|
|
1126
|
+
draft_attrs[:copublisher] = rest if rest
|
|
1127
|
+
end
|
|
1128
|
+
draft_attrs[:draft_status] = attributes[:draft_status] if attributes[:draft_status]
|
|
1129
|
+
return Identifiers::ProjectDraftIdentifier.new(**draft_attrs)
|
|
1130
|
+
end
|
|
1131
|
+
|
|
1052
1132
|
joint = Identifiers::JointDevelopment.new(**attributes)
|
|
1053
1133
|
|
|
1054
1134
|
# Amendment tail on the joint ISO-format form (pubid#317:
|
|
@@ -1322,6 +1402,11 @@ module Pubid
|
|
|
1322
1402
|
# standards ("IEEE No148" prints "IEEE Std 148").
|
|
1323
1403
|
type_value = "Std" if type_value&.match?(/\ANo\.?\z/) &&
|
|
1324
1404
|
original_input.to_s.match?(/\AIEEE\s/)
|
|
1405
|
+
# The canonical type word is the short form (C2 ruling):
|
|
1406
|
+
# "IEEE Standard 495-2007" and "IEEE Std 495-2007" are the same
|
|
1407
|
+
# identifier, so the model normalizes to Std and matches? unifies
|
|
1408
|
+
# the spellings; the long word is a render option (:long).
|
|
1409
|
+
type_value = "Std" if type_value == "Standard"
|
|
1325
1410
|
draft_status_value = extract_value(parsed[:draft_status])
|
|
1326
1411
|
|
|
1327
1412
|
# Handle case where parser captured number without "P" prefix
|
|
@@ -1335,6 +1420,10 @@ module Pubid
|
|
|
1335
1420
|
if !type_value && original_input&.match?(/ANSI\s+P/)
|
|
1336
1421
|
# Extract P as type since it was in the original but parser stripped it
|
|
1337
1422
|
type_value = "P"
|
|
1423
|
+
# The P is identity on a non-IEEE-led publisher too (the C1
|
|
1424
|
+
# ruling): record the marker so the render prepends it —
|
|
1425
|
+
# "ANSI PN42.34/D9a, 2015" round-trips.
|
|
1426
|
+
attributes[:project_marker] = true
|
|
1338
1427
|
end
|
|
1339
1428
|
|
|
1340
1429
|
# NOTE: "P" is a project/draft stage indicator, NOT a code prefix
|
|
@@ -1388,6 +1477,15 @@ module Pubid
|
|
|
1388
1477
|
if parsed[:year]
|
|
1389
1478
|
year_str = extract_value(parsed[:year])
|
|
1390
1479
|
attributes[:year] = year_str
|
|
1480
|
+
# AIEE dash-glued date ("No 15-1928-05"): the tail binds as the
|
|
1481
|
+
# year/month clause with no separator capture, and the AIEE
|
|
1482
|
+
# long-form render is the dash form (a plain numeric month alone
|
|
1483
|
+
# would render ", 05 1928").
|
|
1484
|
+
if attributes[:publisher] == "AIEE" &&
|
|
1485
|
+
original_input.to_s.match?(/-\d{4}(?:-\d{2})?\z/) &&
|
|
1486
|
+
parsed[:month] && year_str.match?(/\A(19|20)\d\d\z/)
|
|
1487
|
+
attributes[:date_separator] = "-"
|
|
1488
|
+
end
|
|
1391
1489
|
elsif parsed[:edition_year]
|
|
1392
1490
|
# The `edition` rule captures its own year under :edition_year (not
|
|
1393
1491
|
# :year) so it never collides with the base -YYYY (see parser.rb).
|
|
@@ -1418,7 +1516,13 @@ module Pubid
|
|
|
1418
1516
|
# Set type attribute for backward compatibility (after P extraction)
|
|
1419
1517
|
# NOTE: "P" IS a type code that maps to ProjectDraftIdentifier via flavor module
|
|
1420
1518
|
# Type can be: "Std", "No", "P" (project draft), etc.
|
|
1421
|
-
|
|
1519
|
+
# A DRAFT type word never reaches the hash: the draft-ness is
|
|
1520
|
+
# carried by draft_status and the draft component (the C2 ruling
|
|
1521
|
+
# - a draft is a Draft, "Draft Std" is banned from canonical
|
|
1522
|
+
# output), so an alias spelling and its canonical hash equal.
|
|
1523
|
+
# The value still feeds determine_stage_abbr below (the
|
|
1524
|
+
# version-less D1 fabrication, pubid#318).
|
|
1525
|
+
attributes[:type] = type_value if type_value && type_value !~ /\ADraft/
|
|
1422
1526
|
|
|
1423
1527
|
# Lookup typed_stage from registry
|
|
1424
1528
|
typed_stage_abbr = determine_stage_abbr(type_value, draft_status_value,
|
|
@@ -1638,6 +1742,17 @@ module Pubid
|
|
|
1638
1742
|
# Normalize a leading hyphen ("/D-3.0" -> "3.0"); the draft
|
|
1639
1743
|
# designator is otherwise held verbatim so no digit is lost.
|
|
1640
1744
|
version = version.sub(/\A-/, "") if version
|
|
1745
|
+
|
|
1746
|
+
# FDIS/CDV/… are ISO/IEC stage tokens, not draft ordinals (C1
|
|
1747
|
+
# ruling, testsuite#2). "/DDIS" and "/DFDIS" are hallucinated
|
|
1748
|
+
# concatenations — the designator D does not merge onto a
|
|
1749
|
+
# stage word, and IEEE speak has no such token. The draft of
|
|
1750
|
+
# a stage rides the iso_stage slot, which Draft#to_s renders
|
|
1751
|
+
# as the D= designator face ("…/D=DIS-2020").
|
|
1752
|
+
if version.match?(/\A(FDIS|FCD|CDV|DIS|CD|WD|PWI|NP)\z/)
|
|
1753
|
+
iso_stage = version
|
|
1754
|
+
version = nil
|
|
1755
|
+
end
|
|
1641
1756
|
end
|
|
1642
1757
|
|
|
1643
1758
|
# The PARG capture carries its separator (".12", the #471
|
|
@@ -1649,7 +1764,7 @@ module Pubid
|
|
|
1649
1764
|
|
|
1650
1765
|
# The compound both-systems form (docs/IEEE-DRAFT-STAGES.md
|
|
1651
1766
|
# §1.3): the stage half of "=DDIS.3".
|
|
1652
|
-
iso_stage
|
|
1767
|
+
iso_stage ||= extract_value(draft_data[:draft_iso_stage]) if draft_data[:draft_iso_stage]
|
|
1653
1768
|
iso_iteration = extract_value(draft_data[:draft_iso_iteration]) if draft_data[:draft_iso_iteration]
|
|
1654
1769
|
|
|
1655
1770
|
# Extract date information from draft data
|
|
@@ -1674,9 +1789,12 @@ module Pubid
|
|
|
1674
1789
|
version = extract_value(draft_data)
|
|
1675
1790
|
end
|
|
1676
1791
|
|
|
1792
|
+
# A stage-token draft's month rides the D= designator zero-padded
|
|
1793
|
+
# ("-202007", the pubid#203 convention) — it must not drop.
|
|
1794
|
+
|
|
1677
1795
|
# Create Draft component object if we have version info (or a
|
|
1678
1796
|
# compound stage half without an ordinal, "D=CDV:2020")
|
|
1679
|
-
if version || draft_data.is_a?(Hash) && draft_data[:draft_iso_stage]
|
|
1797
|
+
if version || iso_stage || draft_data.is_a?(Hash) && draft_data[:draft_iso_stage]
|
|
1680
1798
|
draft_obj = Components::Draft.new(
|
|
1681
1799
|
version: version,
|
|
1682
1800
|
revision: revision,
|
|
@@ -44,7 +44,11 @@ day: nil, iso_stage: nil, iso_iteration: nil)
|
|
|
44
44
|
self.version = version
|
|
45
45
|
self.revision = revision
|
|
46
46
|
self.year = year
|
|
47
|
-
|
|
47
|
+
# The canonical month form is the IEEE crawl abbreviation
|
|
48
|
+
# (C2 ruling: "Aug 2007" and "August 2007" are the same
|
|
49
|
+
# document, so the model normalizes and matches? unifies the
|
|
50
|
+
# spellings; the long face spells the name back out).
|
|
51
|
+
self.original_month = month && CANONICAL_ABBREVIATIONS[month.sub(/\.\z/, "")] || month
|
|
48
52
|
self.month = convert_month(month)
|
|
49
53
|
self.day = day
|
|
50
54
|
# The grammar's ordered alternation captures the stage WITH its
|
|
@@ -58,6 +62,16 @@ day: nil, iso_stage: nil, iso_iteration: nil)
|
|
|
58
62
|
self.iso_iteration = iso_iteration
|
|
59
63
|
end
|
|
60
64
|
|
|
65
|
+
# Canonical month spelling: the crawl abbreviation. Full names
|
|
66
|
+
# normalize onto these ("August" → "Aug"); "September" keeps its
|
|
67
|
+
# IEEE "Sept" form, so the 3-letter "Sep" folds onto it too.
|
|
68
|
+
CANONICAL_ABBREVIATIONS = {
|
|
69
|
+
"January" => "Jan", "February" => "Feb", "March" => "Mar",
|
|
70
|
+
"April" => "Apr", "June" => "Jun", "July" => "Jul",
|
|
71
|
+
"August" => "Aug", "September" => "Sept", "October" => "Oct",
|
|
72
|
+
"November" => "Nov", "December" => "Dec", "Sep" => "Sept",
|
|
73
|
+
}.freeze
|
|
74
|
+
|
|
61
75
|
# Month names as an alternation, longest-first so "September" wins over
|
|
62
76
|
# "Sep"; each may carry a trailing period ("Sept."). Used to split a
|
|
63
77
|
# rendered draft's trailing date back out.
|
|
@@ -67,7 +81,7 @@ day: nil, iso_stage: nil, iso_iteration: nil)
|
|
|
67
81
|
private_constant :MONTH_ALTERNATION
|
|
68
82
|
|
|
69
83
|
DATE_SUFFIX =
|
|
70
|
-
/(?:(, | )(#{MONTH_ALTERNATION})(?: (\d{1,2}))?(?:, | )| )((?:19|20)\d{2}[a-z]{0,2})\z/
|
|
84
|
+
/(?:(, | )(#{MONTH_ALTERNATION})(?: (\d{1,2}))?(?:, | )|, | )((?:19|20)\d{2}[a-z]{0,2})\z/
|
|
71
85
|
private_constant :DATE_SUFFIX
|
|
72
86
|
|
|
73
87
|
# Parse a rendered draft such as "/D3", "/D3.4", "/D7 Jul 2019", or a
|
|
@@ -151,6 +165,15 @@ day: nil, iso_stage: nil, iso_iteration: nil)
|
|
|
151
165
|
end if revision
|
|
152
166
|
|
|
153
167
|
if year
|
|
168
|
+
if iso_stage
|
|
169
|
+
# The D= designator face dash-joins its date in IEEE's
|
|
170
|
+
# convention: the year, and a draft month rides the
|
|
171
|
+
# designator zero-padded ("D=FDIS-201008", pubid#203).
|
|
172
|
+
result += "-#{year}"
|
|
173
|
+
result += format("%02d", month.to_i) if month
|
|
174
|
+
return result
|
|
175
|
+
end
|
|
176
|
+
|
|
154
177
|
# Use original month format to preserve abbreviated vs full names
|
|
155
178
|
display_month = original_month
|
|
156
179
|
|
|
@@ -173,7 +196,10 @@ day: nil, iso_stage: nil, iso_iteration: nil)
|
|
|
173
196
|
", #{year}"
|
|
174
197
|
end
|
|
175
198
|
else
|
|
176
|
-
|
|
199
|
+
# The year-only date joins with its comma - the grammar's
|
|
200
|
+
# comma-year draft tail (pubid-grammar 17244fc) - so the
|
|
201
|
+
# render re-parses through the same clause.
|
|
202
|
+
result += ", #{year}"
|
|
177
203
|
end
|
|
178
204
|
end
|
|
179
205
|
|
|
@@ -159,8 +159,13 @@ module Pubid
|
|
|
159
159
|
|
|
160
160
|
# The P project marker is identity-bearing and prints on both
|
|
161
161
|
# faces (the standing trademark_leaf_to_s_spec contract:
|
|
162
|
-
# "ISO/IEC/IEEE P26511:2018").
|
|
162
|
+
# "ISO/IEC/IEEE P26511:2018"). A P-prefixed joint project
|
|
163
|
+
# prints its parts dot-joined on the ISO face ("ISO/IEEE DIS
|
|
164
|
+
# P11073.10418", #216); a plain ISO/IEC number keeps the
|
|
165
|
+
# printed separator ("IEC/IEEE FDIS 60079-30-2", "ISO/IEC
|
|
166
|
+
# 8802-3").
|
|
163
167
|
code_str = code.to_s
|
|
168
|
+
code_str = code_str.tr("-", ".") if code_str.start_with?("P")
|
|
164
169
|
code_str += mark unless code_str.empty?
|
|
165
170
|
parts << code_str if code_str && !code_str.empty?
|
|
166
171
|
|
|
@@ -200,6 +205,9 @@ module Pubid
|
|
|
200
205
|
# Publishers (slash-separated)
|
|
201
206
|
parts << publishers.join("/") if publishers && !publishers.empty?
|
|
202
207
|
|
|
208
|
+
# A status word ("Unapproved") marks the draft (pubid#318)
|
|
209
|
+
parts << draft_status if draft_status
|
|
210
|
+
|
|
203
211
|
# Build code part — the P-state prints as spelled (P = project
|
|
204
212
|
# draft; no P = standard). Never added or stripped.
|
|
205
213
|
code_str = code.to_s
|
|
@@ -230,7 +238,8 @@ module Pubid
|
|
|
230
238
|
# ("D=CD.2" — docs/IEEE-DRAFT-STAGES.md §1.3).
|
|
231
239
|
stage = iso_stage.match(/\A([A-Z]+?)(\d+)\z/)
|
|
232
240
|
code_str += stage ? "/D=#{stage[1]}.#{stage[2]}" : "/D=#{iso_stage}"
|
|
233
|
-
elsif typed_stage&.ieee_draft_equivalent
|
|
241
|
+
elsif typed_stage&.ieee_draft_equivalent &&
|
|
242
|
+
!code_str.start_with?(typed_stage.ieee_draft_equivalent)
|
|
234
243
|
code_str += "/#{typed_stage.ieee_draft_equivalent}"
|
|
235
244
|
end
|
|
236
245
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Ieee
|
|
5
|
+
# The long face (C2 ruling): one uniform format dimension for every
|
|
6
|
+
# IEEE identifier. Expands the type words an identifier actually
|
|
7
|
+
# carries — Std → Standard on a published standard — and spells
|
|
8
|
+
# draft months out ("August 2007"). A draft carries no Std/Standard
|
|
9
|
+
# word, so both faces render `Draft`; there are no draft-specific
|
|
10
|
+
# render rules.
|
|
11
|
+
class LongRenderer < Renderer
|
|
12
|
+
def render(context: nil, **opts)
|
|
13
|
+
@long = true
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
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,14 @@ 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
|
+
year_on_code = id.year && !id.edition && !id.month &&
|
|
191
|
+
(!id.draft_obj || id.draft_obj&.year)
|
|
192
|
+
result += "-#{id.year}" if year_on_code
|
|
156
193
|
|
|
157
194
|
# Append the numbered/lettered revision inline, in IEEE's native
|
|
158
195
|
# position — right after the code number and before the draft
|
|
@@ -232,6 +269,14 @@ module Pubid
|
|
|
232
269
|
if id.year && !id.edition
|
|
233
270
|
result += " #{id.year}"
|
|
234
271
|
end
|
|
272
|
+
elsif id.year && id.draft_obj && !id.edition && !year_on_code &&
|
|
273
|
+
!(id.publisher == "IEC" && id.copublisher == ["IEEE"])
|
|
274
|
+
# A year-only date trailing the draft ("ANSI N42.34/D9a,
|
|
275
|
+
# 2015") keeps its comma form - the grammar's comma-year
|
|
276
|
+
# draft tail - instead of being silently dropped. A year
|
|
277
|
+
# that dash-attached to the code above (the base year of a
|
|
278
|
+
# dated-draft family) does not repeat here.
|
|
279
|
+
result += ", #{id.year}"
|
|
235
280
|
end
|
|
236
281
|
|
|
237
282
|
# Bounded parenthetical markers only. The unbounded relationship/
|
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
|