@pubid/pubid 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/artifact/corpus.json.gz +0 -0
  2. package/dist/conformance/cli.js +5 -1
  3. package/dist/flavors/amca/grammar.d.ts +4 -0
  4. package/dist/flavors/amca/grammar.js +87 -0
  5. package/dist/flavors/amca/index.d.ts +4 -0
  6. package/dist/flavors/amca/index.js +76 -0
  7. package/dist/flavors/amca/model.d.ts +29 -0
  8. package/dist/flavors/amca/model.js +146 -0
  9. package/dist/flavors/ansi/index.d.ts +24 -0
  10. package/dist/flavors/ansi/index.js +147 -0
  11. package/dist/flavors/api/grammar.d.ts +4 -0
  12. package/dist/flavors/api/grammar.js +79 -0
  13. package/dist/flavors/api/index.d.ts +4 -0
  14. package/dist/flavors/api/index.js +71 -0
  15. package/dist/flavors/api/model.d.ts +27 -0
  16. package/dist/flavors/api/model.js +114 -0
  17. package/dist/flavors/ashrae/grammar.d.ts +2 -0
  18. package/dist/flavors/ashrae/grammar.js +667 -0
  19. package/dist/flavors/ashrae/index.d.ts +4 -0
  20. package/dist/flavors/ashrae/index.js +306 -0
  21. package/dist/flavors/ashrae/model.d.ts +87 -0
  22. package/dist/flavors/ashrae/model.js +335 -0
  23. package/dist/flavors/ashrae/preprocessor.d.ts +7 -0
  24. package/dist/flavors/ashrae/preprocessor.js +44 -0
  25. package/dist/flavors/asme/grammar.d.ts +2 -0
  26. package/dist/flavors/asme/grammar.js +200 -0
  27. package/dist/flavors/asme/index.d.ts +4 -0
  28. package/dist/flavors/asme/index.js +138 -0
  29. package/dist/flavors/asme/model.d.ts +34 -0
  30. package/dist/flavors/asme/model.js +123 -0
  31. package/dist/flavors/astm/grammar.d.ts +2 -0
  32. package/dist/flavors/astm/grammar.js +110 -0
  33. package/dist/flavors/astm/index.d.ts +2 -0
  34. package/dist/flavors/astm/index.js +127 -0
  35. package/dist/flavors/astm/model.d.ts +42 -0
  36. package/dist/flavors/astm/model.js +263 -0
  37. package/dist/flavors/bipm/index.d.ts +30 -0
  38. package/dist/flavors/bipm/index.js +434 -0
  39. package/dist/flavors/bsi/grammar.d.ts +4 -0
  40. package/dist/flavors/bsi/grammar.js +535 -0
  41. package/dist/flavors/bsi/index.d.ts +4 -0
  42. package/dist/flavors/bsi/index.js +941 -0
  43. package/dist/flavors/bsi/model.d.ts +127 -0
  44. package/dist/flavors/bsi/model.js +863 -0
  45. package/dist/flavors/calconnect/index.d.ts +21 -0
  46. package/dist/flavors/calconnect/index.js +113 -0
  47. package/dist/flavors/cen_cenelec/grammar.d.ts +4 -0
  48. package/dist/flavors/cen_cenelec/grammar.js +107 -0
  49. package/dist/flavors/cen_cenelec/index.d.ts +5 -0
  50. package/dist/flavors/cen_cenelec/index.js +263 -0
  51. package/dist/flavors/cen_cenelec/model.d.ts +45 -0
  52. package/dist/flavors/cen_cenelec/model.js +490 -0
  53. package/dist/flavors/cie/grammar.d.ts +5 -0
  54. package/dist/flavors/cie/grammar.js +127 -0
  55. package/dist/flavors/cie/index.d.ts +2 -0
  56. package/dist/flavors/cie/index.js +311 -0
  57. package/dist/flavors/cie/model.d.ts +122 -0
  58. package/dist/flavors/cie/model.js +464 -0
  59. package/dist/flavors/csa/grammar.d.ts +6 -0
  60. package/dist/flavors/csa/grammar.js +239 -0
  61. package/dist/flavors/csa/index.d.ts +2 -0
  62. package/dist/flavors/csa/index.js +634 -0
  63. package/dist/flavors/csa/model.d.ts +33 -0
  64. package/dist/flavors/csa/model.js +505 -0
  65. package/dist/flavors/doi/index.d.ts +13 -0
  66. package/dist/flavors/doi/index.js +65 -0
  67. package/dist/flavors/easc/index.d.ts +4 -0
  68. package/dist/flavors/easc/index.js +107 -0
  69. package/dist/flavors/ecma/index.d.ts +4 -0
  70. package/dist/flavors/ecma/index.js +117 -0
  71. package/dist/flavors/etsi/index.d.ts +14 -0
  72. package/dist/flavors/etsi/index.js +307 -0
  73. package/dist/flavors/evs/grammar.d.ts +2 -0
  74. package/dist/flavors/evs/grammar.js +22 -0
  75. package/dist/flavors/evs/index.d.ts +2 -0
  76. package/dist/flavors/evs/index.js +24 -0
  77. package/dist/flavors/evs/model.d.ts +19 -0
  78. package/dist/flavors/evs/model.js +36 -0
  79. package/dist/flavors/gb/index.d.ts +24 -0
  80. package/dist/flavors/gb/index.js +111 -0
  81. package/dist/flavors/gost/grammar.d.ts +2 -0
  82. package/dist/flavors/gost/grammar.js +46 -0
  83. package/dist/flavors/gost/index.d.ts +2 -0
  84. package/dist/flavors/gost/index.js +165 -0
  85. package/dist/flavors/gost/model.d.ts +61 -0
  86. package/dist/flavors/gost/model.js +147 -0
  87. package/dist/flavors/gost/prefix_routes.d.ts +15 -0
  88. package/dist/flavors/gost/prefix_routes.js +192 -0
  89. package/dist/flavors/iala/index.d.ts +32 -0
  90. package/dist/flavors/iala/index.js +300 -0
  91. package/dist/flavors/iana/index.d.ts +19 -0
  92. package/dist/flavors/iana/index.js +71 -0
  93. package/dist/flavors/idf/grammar.d.ts +2 -0
  94. package/dist/flavors/idf/grammar.js +71 -0
  95. package/dist/flavors/idf/index.d.ts +2 -0
  96. package/dist/flavors/idf/index.js +100 -0
  97. package/dist/flavors/idf/model.d.ts +54 -0
  98. package/dist/flavors/idf/model.js +359 -0
  99. package/dist/flavors/iec/grammar.d.ts +3 -0
  100. package/dist/flavors/iec/grammar.js +187 -0
  101. package/dist/flavors/iec/index.d.ts +4 -0
  102. package/dist/flavors/iec/index.js +507 -0
  103. package/dist/flavors/iec/model.d.ts +54 -0
  104. package/dist/flavors/iec/model.js +437 -0
  105. package/dist/flavors/ieee/grammar.d.ts +2 -0
  106. package/dist/flavors/ieee/grammar.js +626 -0
  107. package/dist/flavors/ieee/index.d.ts +3 -0
  108. package/dist/flavors/ieee/index.js +1186 -0
  109. package/dist/flavors/ieee/model.d.ts +142 -0
  110. package/dist/flavors/ieee/model.js +1148 -0
  111. package/dist/flavors/ieee/preprocessor.d.ts +8 -0
  112. package/dist/flavors/ieee/preprocessor.js +410 -0
  113. package/dist/flavors/ieee/update_codes.d.ts +8 -0
  114. package/dist/flavors/ieee/update_codes.js +191 -0
  115. package/dist/flavors/ietf/index.d.ts +30 -0
  116. package/dist/flavors/ietf/index.js +151 -0
  117. package/dist/flavors/iho/index.d.ts +4 -0
  118. package/dist/flavors/iho/index.js +126 -0
  119. package/dist/flavors/index.d.ts +9 -0
  120. package/dist/flavors/index.js +136 -0
  121. package/dist/flavors/isbn/index.d.ts +13 -0
  122. package/dist/flavors/isbn/index.js +97 -0
  123. package/dist/flavors/iso/grammar.d.ts +2 -0
  124. package/dist/flavors/iso/grammar.js +207 -0
  125. package/dist/flavors/iso/index.d.ts +2 -0
  126. package/dist/flavors/iso/index.js +343 -0
  127. package/dist/flavors/iso/model.d.ts +159 -0
  128. package/dist/flavors/iso/model.js +802 -0
  129. package/dist/flavors/iso/stages.generated.d.ts +13 -0
  130. package/dist/flavors/iso/stages.generated.js +391 -0
  131. package/dist/flavors/itu/grammar.d.ts +4 -0
  132. package/dist/flavors/itu/grammar.js +205 -0
  133. package/dist/flavors/itu/index.d.ts +2 -0
  134. package/dist/flavors/itu/index.js +224 -0
  135. package/dist/flavors/itu/model.d.ts +184 -0
  136. package/dist/flavors/itu/model.js +599 -0
  137. package/dist/flavors/jcgm/index.d.ts +7 -0
  138. package/dist/flavors/jcgm/index.js +300 -0
  139. package/dist/flavors/jis/index.d.ts +4 -0
  140. package/dist/flavors/jis/index.js +267 -0
  141. package/dist/flavors/nist/grammar.d.ts +2 -0
  142. package/dist/flavors/nist/grammar.js +287 -0
  143. package/dist/flavors/nist/index.d.ts +2 -0
  144. package/dist/flavors/nist/index.js +1244 -0
  145. package/dist/flavors/nist/model.d.ts +155 -0
  146. package/dist/flavors/nist/model.js +823 -0
  147. package/dist/flavors/nist/preprocessor.d.ts +11 -0
  148. package/dist/flavors/nist/preprocessor.js +281 -0
  149. package/dist/flavors/oasis/index.d.ts +23 -0
  150. package/dist/flavors/oasis/index.js +110 -0
  151. package/dist/flavors/ogc/index.d.ts +20 -0
  152. package/dist/flavors/ogc/index.js +75 -0
  153. package/dist/flavors/oiml/grammar.d.ts +2 -0
  154. package/dist/flavors/oiml/grammar.js +180 -0
  155. package/dist/flavors/oiml/implementation.d.ts +9 -0
  156. package/dist/flavors/oiml/implementation.js +16 -0
  157. package/dist/flavors/oiml/model.d.ts +4 -0
  158. package/dist/flavors/oiml/model.js +462 -0
  159. package/dist/flavors/omg/index.d.ts +14 -0
  160. package/dist/flavors/omg/index.js +73 -0
  161. package/dist/flavors/plateau/index.d.ts +4 -0
  162. package/dist/flavors/plateau/index.js +97 -0
  163. package/dist/flavors/tgpp/index.d.ts +4 -0
  164. package/dist/flavors/tgpp/index.js +122 -0
  165. package/dist/flavors/un/index.d.ts +13 -0
  166. package/dist/flavors/un/index.js +90 -0
  167. package/dist/flavors/w3c/index.d.ts +14 -0
  168. package/dist/flavors/w3c/index.js +148 -0
  169. package/dist/flavors/xsf/index.d.ts +18 -0
  170. package/dist/flavors/xsf/index.js +65 -0
  171. package/dist/grammar/engine.d.ts +78 -0
  172. package/dist/grammar/engine.js +351 -0
  173. package/dist/model/attribute.d.ts +54 -0
  174. package/dist/model/attribute.js +53 -0
  175. package/dist/model/builder.d.ts +39 -0
  176. package/dist/model/builder.js +124 -0
  177. package/dist/model/component.d.ts +70 -0
  178. package/dist/model/component.js +170 -0
  179. package/dist/model/identifier.d.ts +83 -0
  180. package/dist/model/identifier.js +270 -0
  181. package/dist/model/urn-generator.d.ts +27 -0
  182. package/dist/model/urn-generator.js +91 -0
  183. package/package.json +1 -1
@@ -0,0 +1,1186 @@
1
+ import { ParseFailed, parseGrammar } from "../../grammar/engine.js";
2
+ import { grammarImplementation } from "../index.js";
3
+ import { ieeeGrammar } from "./grammar.js";
4
+ import { preParse, preprocessIeee } from "./preprocessor.js";
5
+ import { applyUpdateCodes } from "./update_codes.js";
6
+ import { AdoptedStandardClass, AieeClass, AmendmentClass, ConformanceClass, CorrigendumClass, CsaDualPublishedClass, DualPublishedClass, IecIeeeCopublishedClass, IeeeCode, IeeeDraft, IeeeRelationship, IeeeTypedStage, InterpretationClass, IreClass, JointDevelopmentClass, MultiNumberedClass, NescDraftClass, NescEditionClass, NescHandbookClass, NescRedlineClass, NescStandardClass, ProjectDraftIdentifierClass, SiStandardClass, StandardClass, locateStage, } from "./model.js";
7
+ /**
8
+ * Port of lib/pubid/ieee/{builder}.rb + Identifier.parse orchestration
9
+ * (pre-parser dispatch, adopted/dual constructors, sub-builders).
10
+ */
11
+ const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
12
+ const extractValue = (value) => {
13
+ if (value === undefined || value === null)
14
+ return undefined;
15
+ if (Array.isArray(value)) {
16
+ if (value.length === 0)
17
+ return undefined;
18
+ const joined = value.join("");
19
+ return joined.length > 0 ? joined : undefined;
20
+ }
21
+ const strValue = String(value).trim();
22
+ return strValue.length > 0 ? strValue : undefined;
23
+ };
24
+ function mergeParsedArray(parsedArray) {
25
+ return Object.assign({}, ...parsedArray);
26
+ }
27
+ const asHash = (v) => (Array.isArray(v) ? mergeParsedArray(v) : v);
28
+ const SI_STAGES = [
29
+ { abbr: ["SI"], stage_code: "published", type_code: "SI" },
30
+ { abbr: ["PSI"], stage_code: "draft", type_code: "SI" },
31
+ ];
32
+ // --- Builder -----------------------------------------------------------------
33
+ class IeeeBuilder {
34
+ originalInput = "";
35
+ build(parsed) {
36
+ const d = Array.isArray(parsed) ? mergeParsedArray(parsed) : parsed;
37
+ if (d["ieee_portion"] !== undefined && d["csa_portion"] !== undefined) {
38
+ return this.buildCsaDualPublished(d);
39
+ }
40
+ if (d["s_number"] !== undefined)
41
+ return this.buildSDesignation(d);
42
+ if (d["first_aiee"] !== undefined && d["second_aiee"] !== undefined) {
43
+ return this.buildCombinedAiee(d);
44
+ }
45
+ if (d["content"] !== undefined)
46
+ return this.buildIecIeeCopublished(d);
47
+ if (d["nesc"] !== undefined)
48
+ return this.buildNesc(asHash(d["nesc"]));
49
+ if (d["aiee"] !== undefined)
50
+ return this.buildAiee(asHash(d["aiee"]));
51
+ if (d["ire"] !== undefined)
52
+ return this.buildIre(asHash(d["ire"]));
53
+ if (d["si_type"] !== undefined)
54
+ return this.buildSiPsi(d);
55
+ return this.buildSingleIdentifier(d, false);
56
+ }
57
+ // --- single identifier -----------------------------------------------------
58
+ buildSingleIdentifier(parsed, buildingSecondary) {
59
+ const parsedHash = asHash(parsed);
60
+ if (!buildingSecondary && parsedHash["primary_identifier"] !== undefined &&
61
+ (parsedHash["secondary_crossref"] !== undefined || parsedHash["secondary_joint"] !== undefined)) {
62
+ return this.buildMultiNumbered(parsedHash);
63
+ }
64
+ if (parsedHash["base"] !== undefined && parsedHash["cor_number"] !== undefined) {
65
+ return this.buildCorrigendumSupplement(parsedHash);
66
+ }
67
+ // A corrigendum capture that is a bare STRING matched the marker
68
+ // without its ordinal - never a valid complete parse (the reference
69
+ // rejects "…Corrigendum/D1").
70
+ if (typeof parsedHash["corrigendum"] === "string") {
71
+ throw new ParseFailed("corrigendum marker without ordinal", 0);
72
+ }
73
+ if (isObj(parsedHash["corrigendum"]) && parsedHash["base"] === undefined) {
74
+ // A corrigendum marker without its ordinal is not a marker - the
75
+ // reference never completes such a parse ("…Corrigendum/D1").
76
+ const cor = asHash(parsedHash["corrigendum"]);
77
+ if (cor["cor_number"] === undefined && cor["cor_year"] === undefined) {
78
+ throw new ParseFailed("corrigendum marker without ordinal", 0);
79
+ }
80
+ return this.buildFlatCorrigendum(parsedHash);
81
+ }
82
+ if (parsedHash["base"] !== undefined && parsedHash["amd_number"] !== undefined) {
83
+ return this.buildAmendmentSupplement(parsedHash);
84
+ }
85
+ if (isObj(parsedHash["amendment"]) && parsedHash["base"] === undefined) {
86
+ return this.buildFlatAmendment(parsedHash);
87
+ }
88
+ if (parsedHash["base"] !== undefined &&
89
+ (parsedHash["int_year"] !== undefined || parsedHash["interpretation"] !== undefined)) {
90
+ return this.buildInterpretationSupplement(parsedHash);
91
+ }
92
+ if (parsedHash["interpretation"] !== undefined && parsedHash["base"] === undefined) {
93
+ return this.buildFlatInterpretation(parsedHash);
94
+ }
95
+ if (parsedHash["base"] !== undefined && parsedHash["conf_number"] !== undefined) {
96
+ return this.buildConformanceSupplement(parsedHash);
97
+ }
98
+ if (parsedHash["joint_publishers"] !== undefined || parsedHash["iso_stage"] !== undefined) {
99
+ return this.buildJointDevelopment(parsedHash);
100
+ }
101
+ const attributes = this.extractAttributes(parsedHash);
102
+ if (parsedHash["relationship_type"] !== undefined || parsedHash["relationship_clause"] !== undefined) {
103
+ attributes["relationships"] = this.buildRelationships(parsedHash);
104
+ }
105
+ const klass = this.determineIdentifierClass(attributes);
106
+ // ProjectDraftIdentifier#initialize strips the leading P off the code
107
+ // string (the project marker lives in the stage, not the columns).
108
+ if (klass === ProjectDraftIdentifierClass) {
109
+ // The strip happens on the CODE STRING before Code.parse (Ruby
110
+ // ProjectDraftIdentifier#initialize), so "PIEEP62" -> prefix I.
111
+ const codeStr = attributes["__code"];
112
+ if (codeStr !== undefined && codeStr.startsWith("P")) {
113
+ const codeObj = IeeeCode.parse(codeStr.slice(1));
114
+ delete attributes["prefix"];
115
+ delete attributes["parts"];
116
+ delete attributes["separator"];
117
+ attributes["number"] = codeObj?.number;
118
+ if (codeObj?.prefix !== undefined)
119
+ attributes["prefix"] = codeObj.prefix;
120
+ if (codeObj !== undefined && codeObj.parts.length > 0)
121
+ attributes["parts"] = codeObj.parts;
122
+ if (codeObj?.originalSeparator !== undefined)
123
+ attributes["separator"] = codeObj.originalSeparator;
124
+ }
125
+ }
126
+ delete attributes["__code"];
127
+ this.renameSupplementKeys(attributes);
128
+ return new klass(attributes);
129
+ }
130
+ renameSupplementKeys(attributes) {
131
+ const ordinal = attributes["cor_number"] ?? attributes["amd_number"] ?? attributes["conf_number"];
132
+ delete attributes["cor_number"];
133
+ delete attributes["amd_number"];
134
+ delete attributes["conf_number"];
135
+ if (ordinal !== undefined)
136
+ attributes["number"] = ordinal;
137
+ const year = attributes["cor_year"] ?? attributes["amd_year"] ?? attributes["conf_year"] ?? attributes["int_year"];
138
+ delete attributes["cor_year"];
139
+ delete attributes["amd_year"];
140
+ delete attributes["conf_year"];
141
+ delete attributes["int_year"];
142
+ if (year !== undefined)
143
+ attributes["year"] = year;
144
+ }
145
+ determineIdentifierClass(attributes) {
146
+ if (attributes["publisher"] === "AIEE")
147
+ return AieeClass;
148
+ const typeCode = attributes["type"];
149
+ if (typeCode === "P")
150
+ return ProjectDraftIdentifierClass;
151
+ if (typeCode === "SI" || typeCode === "PSI")
152
+ return SiStandardClass;
153
+ if (attributes["interpretation"] === true || attributes["int_year"] !== undefined) {
154
+ return InterpretationClass;
155
+ }
156
+ if (attributes["conf_number"] !== undefined)
157
+ return ConformanceClass;
158
+ if (attributes["cor_number"] !== undefined)
159
+ return CorrigendumClass;
160
+ if (attributes["amd_number"] !== undefined)
161
+ return AmendmentClass;
162
+ if (attributes["adoption"] !== undefined)
163
+ return AdoptedStandardClass;
164
+ return StandardClass;
165
+ }
166
+ // --- extract_attributes ----------------------------------------------------
167
+ extractAttributes(parsed) {
168
+ const attributes = {};
169
+ if (parsed["publishers"] !== undefined) {
170
+ const pubData = asHash(parsed["publishers"]);
171
+ attributes["publisher"] = extractValue(pubData["publisher"]);
172
+ if (pubData["copublishers"] !== undefined) {
173
+ const copubs = Array.isArray(pubData["copublishers"])
174
+ ? pubData["copublishers"]
175
+ : [pubData["copublishers"]];
176
+ attributes["copublisher"] = copubs
177
+ .map((cp) => extractValue(asHash(cp)["copublisher"]))
178
+ .filter((cp) => cp !== undefined);
179
+ }
180
+ }
181
+ else if (parsed["publisher"] !== undefined) {
182
+ attributes["publisher"] = extractValue(parsed["publisher"]);
183
+ }
184
+ const codeParts = [];
185
+ if (parsed["part"] !== undefined)
186
+ codeParts.push(extractValue(parsed["part"]) ?? "");
187
+ if (parsed["subpart"] !== undefined) {
188
+ const subparts = Array.isArray(parsed["subpart"]) ? parsed["subpart"] : [parsed["subpart"]];
189
+ for (const sp of subparts) {
190
+ const v = extractValue(sp);
191
+ if (v !== undefined)
192
+ codeParts.push(v);
193
+ }
194
+ }
195
+ let codeStr = extractValue(parsed["number"]);
196
+ let typeValue = extractValue(parsed["type"]);
197
+ const draftStatusValue = extractValue(parsed["draft_status"]);
198
+ if (typeValue === undefined && this.originalInput !== "" && /IEEE\s+P/.test(this.originalInput)) {
199
+ typeValue = "P";
200
+ }
201
+ if (typeValue === undefined && this.originalInput !== "" && /ANSI\s+P/.test(this.originalInput)) {
202
+ typeValue = "P";
203
+ }
204
+ if (codeStr !== undefined && codeStr.startsWith("P") && this.originalInput.includes("/CSA")) {
205
+ const pubData = parsed["publishers"] !== undefined ? asHash(parsed["publishers"]) : undefined;
206
+ const hasCsaCopub = pubData?.["copublishers"] !== undefined
207
+ ? (Array.isArray(pubData["copublishers"]) ? pubData["copublishers"] : [pubData["copublishers"]])
208
+ .some((cp) => (extractValue(asHash(cp)["copublisher"]) ?? "").includes("CSA"))
209
+ : this.originalInput.includes("/CSA");
210
+ if (hasCsaCopub)
211
+ codeStr = codeStr.replace(/^P/, "");
212
+ }
213
+ if (codeStr !== undefined && codeParts.length > 0) {
214
+ codeStr += `.${codeParts.join(".")}`;
215
+ }
216
+ if (codeStr !== undefined && parsed["year"] === undefined && codeParts.length === 0) {
217
+ const m = /^(.+)-(\d{4})$/.exec(codeStr);
218
+ if (m !== null) {
219
+ const potentialYear = Number.parseInt(m[2], 10);
220
+ if (potentialYear >= 1884 && potentialYear <= 2099) {
221
+ codeStr = m[1];
222
+ parsed["year"] = m[2];
223
+ }
224
+ }
225
+ }
226
+ const codeObj = codeStr !== undefined ? IeeeCode.parse(codeStr) : undefined;
227
+ if (codeStr !== undefined)
228
+ attributes["__code"] = codeStr;
229
+ if (codeObj !== undefined) {
230
+ attributes["number"] = codeObj.number;
231
+ if (codeObj.prefix !== undefined)
232
+ attributes["prefix"] = codeObj.prefix;
233
+ if (codeObj.parts.length > 0)
234
+ attributes["parts"] = codeObj.parts;
235
+ if (codeObj.originalSeparator !== undefined)
236
+ attributes["separator"] = codeObj.originalSeparator;
237
+ }
238
+ if (parsed["year"] !== undefined) {
239
+ attributes["year"] = extractValue(parsed["year"]);
240
+ }
241
+ else if (parsed["edition_year"] !== undefined) {
242
+ attributes["year"] = extractValue(parsed["edition_year"]);
243
+ }
244
+ else if (parsed["trailing_year"] !== undefined) {
245
+ attributes["year"] = extractValue(parsed["trailing_year"]);
246
+ if (parsed["trailing_month"] !== undefined) {
247
+ attributes["month"] = extractValue(parsed["trailing_month"]);
248
+ }
249
+ }
250
+ if (parsed["edition_month"] !== undefined) {
251
+ attributes["edition_month"] = extractValue(parsed["edition_month"]);
252
+ }
253
+ // The historical "No"-prefixed spellings ("IEEE No148, April 1959")
254
+ // are plain standards - No normalizes to Std so the render prints
255
+ // "IEEE Std 148" and the stage lookup resolves. AIEE keeps its No.
256
+ if (typeValue !== undefined && /^No\.?$/.test(typeValue) &&
257
+ /^IEEE\s/.test(this.originalInput)) {
258
+ typeValue = "Std";
259
+ }
260
+ if (typeValue !== undefined)
261
+ attributes["type"] = typeValue;
262
+ const typedStageAbbr = this.determineStageAbbr(typeValue, parsed);
263
+ if (typedStageAbbr !== undefined) {
264
+ const record = locateStage(typedStageAbbr);
265
+ if (record !== undefined)
266
+ attributes["typed_stage"] = new IeeeTypedStage(record);
267
+ }
268
+ attributes["draft_status"] = draftStatusValue;
269
+ for (const key of ["edition", "revision"]) {
270
+ const value = extractValue(parsed[key]);
271
+ if (value !== undefined)
272
+ attributes[key] = value;
273
+ }
274
+ if (attributes["edition_month"] === undefined) {
275
+ const month = extractValue(parsed["month"]);
276
+ if (month !== undefined)
277
+ attributes["month"] = month;
278
+ }
279
+ const day = extractValue(parsed["day"]);
280
+ if (day !== undefined)
281
+ attributes["day"] = day;
282
+ this.handleDraft(parsed, attributes);
283
+ this.applyDefaultDraftVersion(attributes, typeValue);
284
+ this.handleKeyedHash(parsed, "corrigendum", attributes, "cor_number", "cor_year");
285
+ this.handleKeyedHash(parsed, "amendment", attributes, "amd_number", "amd_year");
286
+ this.handleKeyedHash(parsed, "conformance", attributes, "conf_number", "conf_year");
287
+ this.handleAshraeCopub(parsed, attributes);
288
+ this.handleIeeeCrossref(parsed, attributes);
289
+ this.handleReaffirmed(parsed, attributes);
290
+ this.handleParameters(parsed, attributes);
291
+ if (parsed["nickname"] !== undefined) {
292
+ attributes["nickname"] = extractValue(parsed["nickname"]);
293
+ }
294
+ if (parsed["interpretation"] !== undefined)
295
+ attributes["interpretation"] = true;
296
+ if (parsed["redline"] !== undefined)
297
+ attributes["redline"] = true;
298
+ return attributes;
299
+ }
300
+ determineStageAbbr(typeValue, parsed) {
301
+ if (parsed["draft"] !== undefined) {
302
+ let draftData = parsed["draft"];
303
+ if (Array.isArray(draftData)) {
304
+ draftData = mergeParsedArray(draftData);
305
+ }
306
+ if (isObj(draftData) && draftData["draft_version"] !== undefined) {
307
+ const dv = draftData["draft_version"];
308
+ const version = Array.isArray(dv) ? dv.map((v) => extractValue(v) ?? "").join("") : extractValue(dv);
309
+ if (version !== undefined) {
310
+ const draftAbbr = `D${version}`;
311
+ const stage = locateStage(draftAbbr);
312
+ if (stage !== undefined && stage.abbr.includes(draftAbbr))
313
+ return draftAbbr;
314
+ }
315
+ }
316
+ }
317
+ if (typeValue !== undefined) {
318
+ if (typeValue.startsWith("P"))
319
+ return "P";
320
+ if (typeValue === "Std")
321
+ return "Std";
322
+ if (typeValue === "Draft Std" && parsed["draft"] === undefined && parsed["digit_draft"] === undefined) {
323
+ return "D1";
324
+ }
325
+ if (/^No\.?$/.test(typeValue))
326
+ return typeValue;
327
+ }
328
+ return "Std";
329
+ }
330
+ handleDraft(parsed, attributes) {
331
+ let draftData = parsed["draft"] ?? parsed["digit_draft"];
332
+ if (draftData === undefined)
333
+ return;
334
+ if (Array.isArray(draftData)) {
335
+ const hashes = draftData.filter((e) => isObj(e));
336
+ const versions = hashes
337
+ .map((e) => e["draft_version"])
338
+ .filter((v) => v !== undefined);
339
+ const merged = Object.assign({}, ...hashes);
340
+ if (versions.length > 0)
341
+ merged["draft_version"] = versions;
342
+ draftData = merged;
343
+ }
344
+ let version;
345
+ let revision;
346
+ let month;
347
+ let year;
348
+ let day;
349
+ let commaBeforeMonth = false;
350
+ if (isObj(draftData)) {
351
+ if (draftData["draft_version"] !== undefined) {
352
+ const dv = draftData["draft_version"];
353
+ version = Array.isArray(dv) ? dv.map((v) => extractValue(v) ?? "").join("") : extractValue(dv);
354
+ if (version !== undefined)
355
+ version = version.replace(/^-/, "");
356
+ }
357
+ revision = extractValue(draftData["revision"]);
358
+ month = extractValue(draftData["month"]);
359
+ year = extractValue(draftData["year"]);
360
+ day = extractValue(draftData["day"]);
361
+ if (this.originalInput !== "" && month !== undefined) {
362
+ commaBeforeMonth = new RegExp(`,\\s+${month.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i")
363
+ .test(this.originalInput);
364
+ }
365
+ if (version !== undefined && this.originalInput !== "" &&
366
+ new RegExp(`\\s+/D${version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i").test(this.originalInput)) {
367
+ attributes["space_before_draft"] = true;
368
+ }
369
+ }
370
+ else {
371
+ version = extractValue(draftData);
372
+ }
373
+ if (version !== undefined) {
374
+ const draftObj = new IeeeDraft({
375
+ version,
376
+ revision,
377
+ month,
378
+ year,
379
+ day,
380
+ comma_before_month: commaBeforeMonth,
381
+ });
382
+ attributes["draft"] = draftObj.render();
383
+ }
384
+ }
385
+ applyDefaultDraftVersion(attributes, typeValue) {
386
+ if (attributes["draft"] !== undefined)
387
+ return;
388
+ if (typeValue !== "Draft Std")
389
+ return;
390
+ attributes["draft"] = new IeeeDraft({ version: "1", year: attributes["year"] }).render();
391
+ }
392
+ handleKeyedHash(parsed, key, attributes, numberKey, yearKey) {
393
+ if (isObj(parsed[key])) {
394
+ const data = parsed[key];
395
+ attributes[numberKey] = extractValue(data[numberKey]);
396
+ if (data[yearKey] !== undefined)
397
+ attributes[yearKey] = extractValue(data[yearKey]);
398
+ }
399
+ }
400
+ handleAshraeCopub(parsed, attributes) {
401
+ if (isObj(parsed["ashrae_copub"])) {
402
+ const data = parsed["ashrae_copub"];
403
+ attributes["ashrae_number"] = extractValue(data["ashrae_number"]);
404
+ if (data["ashrae_year"] !== undefined) {
405
+ attributes["ashrae_year"] = extractValue(data["ashrae_year"]);
406
+ }
407
+ }
408
+ }
409
+ handleIeeeCrossref(parsed, attributes) {
410
+ if (parsed["ieee_crossref"] !== undefined) {
411
+ const raw = extractValue(parsed["ieee_crossref"]) ?? "";
412
+ attributes["crossref"] = `/C${raw.replace(/^\//, "")}`;
413
+ }
414
+ }
415
+ handleReaffirmed(parsed, attributes) {
416
+ if (isObj(parsed["reaffirmed"])) {
417
+ attributes["reaffirmed"] = extractValue(parsed["reaffirmed"]["year"]);
418
+ }
419
+ else if (parsed["reaffirmed"] !== undefined) {
420
+ attributes["reaffirmed"] = extractValue(parsed["reaffirmed"]);
421
+ }
422
+ }
423
+ handleParameters(parsed, attributes) {
424
+ if (!isObj(parsed["parameters"]))
425
+ return;
426
+ const paramData = parsed["parameters"];
427
+ for (const key of ["parenthetical_content", "revision_of", "amendment_to", "adoption", "note"]) {
428
+ const value = extractValue(paramData[key]);
429
+ if (value !== undefined)
430
+ attributes[key] = value;
431
+ }
432
+ }
433
+ parseIdentifierList(listData) {
434
+ if (listData === undefined || listData === null)
435
+ return [];
436
+ const entries = Array.isArray(listData) ? listData : [listData];
437
+ const idStrings = [];
438
+ for (const entry of entries) {
439
+ if (isObj(entry) && entry["id"] !== undefined) {
440
+ idStrings.push(String(entry["id"]).trim());
441
+ }
442
+ }
443
+ return idStrings.map((idStr) => {
444
+ try {
445
+ return ieeeParse(idStr);
446
+ }
447
+ catch {
448
+ return new StandardClass({ parenthetical_content: idStr });
449
+ }
450
+ });
451
+ }
452
+ relationshipFrom(relData) {
453
+ if (!isObj(relData["relationship_type"]))
454
+ return undefined;
455
+ const firstKey = Object.keys(relData["relationship_type"])[0];
456
+ if (firstKey === undefined)
457
+ return undefined;
458
+ let amendments = [];
459
+ if (relData["amendments"] !== undefined && !isObj(relData["amendments"])) {
460
+ amendments = this.parseIdentifierList(relData["amendments"]);
461
+ }
462
+ return new IeeeRelationship(firstKey, this.parseIdentifierList(relData["related_ids"]), amendments, isObj(relData["amendments"]) && relData["amendments"]["approved_amendments"] !== undefined);
463
+ }
464
+ buildRelationships(parsedHash) {
465
+ const relationships = [];
466
+ if (parsedHash["relationship_type"] !== undefined || parsedHash["relationship_clause"] !== undefined) {
467
+ const rel = this.relationshipFrom(parsedHash);
468
+ if (rel !== undefined)
469
+ relationships.push(rel);
470
+ }
471
+ if (parsedHash["additional_rels"] !== undefined) {
472
+ const additional = Array.isArray(parsedHash["additional_rels"])
473
+ ? parsedHash["additional_rels"]
474
+ : [parsedHash["additional_rels"]];
475
+ for (const relData of additional) {
476
+ if (isObj(relData)) {
477
+ const rel = this.relationshipFrom(relData);
478
+ if (rel !== undefined)
479
+ relationships.push(rel);
480
+ }
481
+ }
482
+ }
483
+ return relationships;
484
+ }
485
+ // --- supplements and wrappers ----------------------------------------------
486
+ reconstructBaseString(baseData) {
487
+ const baseParts = [];
488
+ if (baseData["publishers"] !== undefined) {
489
+ const pubData = asHash(baseData["publishers"]);
490
+ let publisherStr = extractValue(pubData["publisher"]) ?? "";
491
+ if (pubData["copublishers"] !== undefined && pubData["copublishers"] !== "") {
492
+ const copubs = Array.isArray(pubData["copublishers"])
493
+ ? pubData["copublishers"]
494
+ : [pubData["copublishers"]];
495
+ const copubStrs = copubs
496
+ .map((cp) => extractValue(asHash(cp)["copublisher"]))
497
+ .filter((cp) => cp !== undefined);
498
+ if (copubStrs.length > 0)
499
+ publisherStr += `/${copubStrs.join("/")}`;
500
+ }
501
+ baseParts.push(publisherStr);
502
+ }
503
+ if (baseData["type"] !== undefined) {
504
+ baseParts.push(extractValue(baseData["type"]) ?? "");
505
+ }
506
+ let numberStr = extractValue(baseData["number"]) ?? "";
507
+ if (baseData["part"] !== undefined) {
508
+ numberStr += `.${extractValue(baseData["part"]) ?? ""}`;
509
+ }
510
+ if (baseData["subpart"] !== undefined) {
511
+ const subparts = Array.isArray(baseData["subpart"]) ? baseData["subpart"] : [baseData["subpart"]];
512
+ for (const sp of subparts) {
513
+ const v = extractValue(sp);
514
+ if (v !== undefined)
515
+ numberStr += `.${v}`;
516
+ }
517
+ }
518
+ if (baseData["year"] !== undefined) {
519
+ numberStr += `-${extractValue(baseData["year"]) ?? ""}`;
520
+ }
521
+ baseParts.push(numberStr);
522
+ return baseParts.join(" ");
523
+ }
524
+ buildCorrigendumSupplement(parsedHash) {
525
+ const base = ieeeParseSingle(this.reconstructBaseString(asHash(parsedHash["base"])));
526
+ const corNumber = extractValue(parsedHash["cor_number"]);
527
+ const corYear = extractValue(parsedHash["cor_year"]);
528
+ return new CorrigendumClass({
529
+ base, number: corNumber, year: corYear,
530
+ });
531
+ }
532
+ buildFlatCorrigendum(parsedHash) {
533
+ const corData = parsedHash["corrigendum"];
534
+ const corNumber = extractValue(corData["cor_number"]);
535
+ const corYear = corData["cor_year"] !== undefined ? extractValue(corData["cor_year"]) : undefined;
536
+ const baseHash = { ...parsedHash };
537
+ delete baseHash["corrigendum"];
538
+ const base = this.buildSingleIdentifier(baseHash, false);
539
+ return new CorrigendumClass({
540
+ base, number: corNumber, year: corYear,
541
+ });
542
+ }
543
+ buildFlatAmendment(parsedHash) {
544
+ const amdData = parsedHash["amendment"];
545
+ const amdNumber = extractValue(amdData["amd_number"]);
546
+ const amdYear = amdData["amd_year"] !== undefined ? extractValue(amdData["amd_year"]) : undefined;
547
+ const baseHash = { ...parsedHash };
548
+ delete baseHash["amendment"];
549
+ const base = this.buildSingleIdentifier(baseHash, false);
550
+ return new AmendmentClass({
551
+ base, number: amdNumber, year: amdYear,
552
+ });
553
+ }
554
+ buildFlatInterpretation(parsedHash) {
555
+ const intYear = parsedHash["trailing_year"] !== undefined
556
+ ? extractValue(parsedHash["trailing_year"])
557
+ : undefined;
558
+ const baseHash = { ...parsedHash };
559
+ delete baseHash["interpretation"];
560
+ delete baseHash["trailing_month"];
561
+ delete baseHash["trailing_year"];
562
+ const base = this.buildSingleIdentifier(baseHash, false);
563
+ return new InterpretationClass({
564
+ base, year: intYear,
565
+ });
566
+ }
567
+ buildInterpretationSupplement(parsedHash) {
568
+ const base = ieeeParseSingle(this.reconstructBaseString(asHash(parsedHash["base"])));
569
+ const intYear = extractValue(parsedHash["int_year"]);
570
+ return new InterpretationClass({
571
+ base, year: intYear,
572
+ });
573
+ }
574
+ buildAmendmentSupplement(parsedHash) {
575
+ const base = ieeeParseSingle(this.reconstructBaseString(asHash(parsedHash["base"])));
576
+ return new AmendmentClass({
577
+ base,
578
+ number: extractValue(parsedHash["amd_number"]),
579
+ year: extractValue(parsedHash["amd_year"]),
580
+ });
581
+ }
582
+ buildConformanceSupplement(parsedHash) {
583
+ const base = ieeeParseSingle(this.reconstructBaseString(asHash(parsedHash["base"])));
584
+ return new ConformanceClass({
585
+ base,
586
+ number: extractValue(parsedHash["conf_number"]),
587
+ year: extractValue(parsedHash["conf_year"]),
588
+ });
589
+ }
590
+ buildJointDevelopment(parsed) {
591
+ const attributes = {};
592
+ if (parsed["joint_publishers"] !== undefined) {
593
+ const jointPubStr = extractValue(parsed["joint_publishers"]) ?? "";
594
+ const pubs = jointPubStr.split("/");
595
+ attributes["publishers"] = pubs;
596
+ attributes["publisher"] = pubs[0];
597
+ attributes["copublisher"] = pubs.slice(1);
598
+ }
599
+ const codeParts = [];
600
+ if (parsed["part"] !== undefined)
601
+ codeParts.push(extractValue(parsed["part"]) ?? "");
602
+ let codeStr = extractValue(parsed["number"]);
603
+ if (codeStr !== undefined && codeParts.length > 0) {
604
+ codeStr += `.${codeParts.join(".")}`;
605
+ }
606
+ if (codeStr !== undefined) {
607
+ const codeObj = IeeeCode.parse(codeStr);
608
+ if (codeObj !== undefined) {
609
+ attributes["number"] = codeObj.number;
610
+ if (codeObj.prefix !== undefined)
611
+ attributes["prefix"] = codeObj.prefix;
612
+ if (codeObj.parts.length > 0)
613
+ attributes["parts"] = codeObj.parts;
614
+ if (codeObj.originalSeparator !== undefined)
615
+ attributes["separator"] = codeObj.originalSeparator;
616
+ }
617
+ }
618
+ if (parsed["year"] !== undefined) {
619
+ attributes["year"] = extractValue(parsed["year"]);
620
+ }
621
+ else if (parsed["edition_year"] !== undefined) {
622
+ attributes["year"] = extractValue(parsed["edition_year"]);
623
+ }
624
+ if (parsed["month"] !== undefined)
625
+ attributes["month"] = extractValue(parsed["month"]);
626
+ if (parsed["draft_year"] !== undefined) {
627
+ if (attributes["year"] === undefined)
628
+ attributes["year"] = extractValue(parsed["draft_year"]);
629
+ if (attributes["month"] === undefined)
630
+ attributes["month"] = extractValue(parsed["draft_month"]);
631
+ }
632
+ if (parsed["edition"] !== undefined)
633
+ attributes["edition"] = extractValue(parsed["edition"]);
634
+ if (parsed["edition_month"] !== undefined) {
635
+ attributes["edition_month"] = extractValue(parsed["edition_month"]);
636
+ }
637
+ if (parsed["draft_version"] !== undefined) {
638
+ const draftVer = (extractValue(parsed["draft_version"]) ?? "").replace(/^D/, "");
639
+ if (draftVer !== "")
640
+ attributes["ieee_draft"] = `D${draftVer}`;
641
+ }
642
+ // The catalogue-PRINTED joint form (dash-year / ", Month YYYY" /
643
+ // date-trailing-the-draft / date-less parenthetical): a Standard
644
+ // carrying publisher/copublisher renders it as printed; the colon-year
645
+ // spelling stays the ISO-style JointDevelopment. Stage-WORD drafts
646
+ // (letters, not a D-number) stay joint - their canonical renders drop
647
+ // the P and keep the joint spelling.
648
+ const numericDraft = parsed["draft_version"] !== undefined &&
649
+ /^\d/.test(extractValue(parsed["draft_version"]) ?? "");
650
+ if (parsed["iso_published"] !== undefined &&
651
+ (parsed["printed_dash_year"] !== undefined || parsed["printed_month_year"] !== undefined ||
652
+ (numericDraft && parsed["draft_month"] !== undefined) ||
653
+ (parsed["year"] === undefined && parsed["parameters"] !== undefined &&
654
+ typeof parsed["parameters"] === "object" && !Array.isArray(parsed["parameters"]) &&
655
+ parsed["parameters"]["parenthetical_content"] !== undefined))) {
656
+ const sep = parsed["part_dash"] !== undefined ? "-" : ".";
657
+ const printedCode = [extractValue(parsed["number"]), extractValue(parsed["part"])]
658
+ .filter((x) => x !== undefined).join(sep);
659
+ const printedAttrs = {
660
+ publisher: attributes["publisher"],
661
+ copublisher: attributes["copublisher"],
662
+ typed_stage: locateStage("Std") !== undefined ? new IeeeTypedStage(locateStage("Std")) : undefined,
663
+ };
664
+ let printedDraft = attributes["ieee_draft"];
665
+ let finalCode = printedCode;
666
+ if (parsed["printed_dash_year"] !== undefined && parsed["month"] !== undefined) {
667
+ // A dash-year-month date glues onto the printed code before any
668
+ // draft - the render's month slot would lose the printed form.
669
+ finalCode = `${printedCode}-${attributes["year"]}-${attributes["month"]}`;
670
+ }
671
+ else if (parsed["printed_dash_year"] !== undefined) {
672
+ // A bare dash-year is the identity year - an attribute.
673
+ printedAttrs["year"] = attributes["year"];
674
+ }
675
+ else if (parsed["printed_month_year"] !== undefined) {
676
+ printedAttrs["year"] = attributes["year"];
677
+ printedAttrs["month"] = attributes["month"];
678
+ }
679
+ else if (parsed["draft_month"] !== undefined) {
680
+ // The date trails the draft itself; keep it inside the draft so
681
+ // the code stays date-less.
682
+ printedDraft = `${printedDraft}, ${extractValue(parsed["draft_month"])} ${extractValue(parsed["draft_year"])}`;
683
+ }
684
+ const codeObj = IeeeCode.parse(finalCode);
685
+ if (codeObj !== undefined) {
686
+ printedAttrs["number"] = codeObj.number;
687
+ if (codeObj.prefix !== undefined)
688
+ printedAttrs["prefix"] = codeObj.prefix;
689
+ if (codeObj.parts.length > 0)
690
+ printedAttrs["parts"] = codeObj.parts;
691
+ if (codeObj.originalSeparator !== undefined)
692
+ printedAttrs["separator"] = codeObj.originalSeparator;
693
+ }
694
+ if (printedDraft !== undefined)
695
+ printedAttrs["draft"] = printedDraft;
696
+ if (attributes["edition"] !== undefined)
697
+ printedAttrs["edition"] = attributes["edition"];
698
+ if (attributes["edition_month"] !== undefined)
699
+ printedAttrs["edition_month"] = attributes["edition_month"];
700
+ const params = parsed["parameters"];
701
+ if (params !== undefined && typeof params === "object" && !Array.isArray(params) &&
702
+ params["parenthetical_content"] !== undefined) {
703
+ printedAttrs["parenthetical_content"] = extractValue(params["parenthetical_content"]);
704
+ }
705
+ for (const k of Object.keys(printedAttrs)) {
706
+ if (printedAttrs[k] === undefined)
707
+ delete printedAttrs[k];
708
+ }
709
+ return new StandardClass(printedAttrs);
710
+ }
711
+ if (parsed["iso_stage"] !== undefined) {
712
+ attributes["lead_party"] = "ISO";
713
+ const stageAbbr = extractValue(parsed["iso_stage"]);
714
+ attributes["iso_stage"] = stageAbbr;
715
+ if (stageAbbr !== undefined) {
716
+ const record = locateStage(stageAbbr);
717
+ if (record !== undefined)
718
+ attributes["typed_stage"] = new IeeeTypedStage(record);
719
+ }
720
+ }
721
+ else if (parsed["iso_published"] !== undefined) {
722
+ attributes["lead_party"] = "ISO";
723
+ }
724
+ else {
725
+ attributes["lead_party"] = "IEEE";
726
+ attributes["type"] = "P";
727
+ const record = locateStage("P");
728
+ if (record !== undefined)
729
+ attributes["typed_stage"] = new IeeeTypedStage(record);
730
+ }
731
+ const joint = new JointDevelopmentClass(attributes);
732
+ if (parsed["amd_number"] === undefined)
733
+ return joint;
734
+ const amdYear = parsed["amd_year"] !== undefined ? extractValue(parsed["amd_year"]) : undefined;
735
+ return new AmendmentClass({
736
+ base: joint,
737
+ number: extractValue(parsed["amd_number"]),
738
+ year: amdYear,
739
+ });
740
+ }
741
+ buildSiPsi(parsed) {
742
+ const attributes = {};
743
+ const siType = extractValue(parsed["si_type"]);
744
+ if (parsed["publishers"] !== undefined) {
745
+ const publishersStr = extractValue(parsed["publishers"]) ?? "";
746
+ const pubs = publishersStr.split("/");
747
+ attributes["publisher"] = pubs[0];
748
+ if (pubs.length > 1)
749
+ attributes["copublisher"] = pubs.slice(1);
750
+ }
751
+ const codeStr = extractValue(parsed["number"]);
752
+ if (codeStr !== undefined) {
753
+ const codeObj = IeeeCode.parse(codeStr);
754
+ if (codeObj !== undefined) {
755
+ attributes["number"] = codeObj.number;
756
+ if (codeObj.prefix !== undefined)
757
+ attributes["prefix"] = codeObj.prefix;
758
+ if (codeObj.parts.length > 0)
759
+ attributes["parts"] = codeObj.parts;
760
+ if (codeObj.originalSeparator !== undefined)
761
+ attributes["separator"] = codeObj.originalSeparator;
762
+ }
763
+ }
764
+ const stageRecord = SI_STAGES.find((ts) => siType !== undefined && ts.abbr.includes(siType));
765
+ if (stageRecord !== undefined)
766
+ attributes["typed_stage"] = new IeeeTypedStage(stageRecord);
767
+ if (siType === "PSI" && parsed["draft_version"] !== undefined) {
768
+ attributes["draft"] = new IeeeDraft({ version: extractValue(parsed["draft_version"]) ?? "" }).render();
769
+ }
770
+ if (parsed["year"] !== undefined)
771
+ attributes["year"] = extractValue(parsed["year"]);
772
+ if (parsed["month"] !== undefined)
773
+ attributes["month"] = extractValue(parsed["month"]);
774
+ if (parsed["relationship_type"] !== undefined || parsed["relationship_clause"] !== undefined) {
775
+ attributes["relationships"] = this.buildRelationships(parsed);
776
+ }
777
+ this.handleParameters(parsed, attributes);
778
+ return new SiStandardClass(attributes);
779
+ }
780
+ buildMultiNumbered(parsed) {
781
+ const primaryId = this.buildSingleIdentifier(asHash(parsed["primary_identifier"]), false);
782
+ let secondaryId;
783
+ if (parsed["secondary_crossref"] !== undefined) {
784
+ const crossref = extractValue(parsed["secondary_crossref"]) ?? "";
785
+ secondaryId = new StandardClass({
786
+ publisher: "IEEE",
787
+ number: crossref.replace(/^\//, ""),
788
+ });
789
+ }
790
+ else if (parsed["secondary_joint"] !== undefined) {
791
+ secondaryId = this.buildSingleIdentifier(asHash(parsed["secondary_joint"]), true);
792
+ }
793
+ return new MultiNumberedClass({
794
+ primary_identifier: primaryId,
795
+ secondary_identifier: secondaryId,
796
+ });
797
+ }
798
+ buildCsaDualPublished(parsed) {
799
+ const ieeeId = this.buildSingleIdentifier(asHash(parsed["ieee_portion"]), false);
800
+ // The CSA portion normalizes "No." to the catalogue's "NO." spelling
801
+ // (the Ruby CSA component uppercases it) so every alias renders the
802
+ // same canonical form.
803
+ const csaString = (extractValue(parsed["csa_portion"]) ?? "").replace(/\bNo\./, "NO.");
804
+ return new CsaDualPublishedClass({
805
+ ieee_identifier: ieeeId,
806
+ csa_string: csaString,
807
+ year: ieeeId.year,
808
+ typed_stage: ieeeId.typed_stage,
809
+ });
810
+ }
811
+ buildSDesignation(parsed) {
812
+ const attributes = {
813
+ number: extractValue(parsed["s_number"]),
814
+ publisher: extractValue(parsed["publisher"]) ?? "IEEE",
815
+ type: "Std",
816
+ };
817
+ const stdRecord = locateStage("Std");
818
+ if (stdRecord !== undefined)
819
+ attributes["typed_stage"] = new IeeeTypedStage(stdRecord);
820
+ if (parsed["ipcea_copub"] !== undefined) {
821
+ attributes["crossref"] = extractValue(parsed["ipcea_copub"]);
822
+ }
823
+ this.handleParameters(parsed, attributes);
824
+ return new StandardClass(attributes);
825
+ }
826
+ buildCombinedAiee(parsed) {
827
+ const firstId = this.buildAiee(asHash(parsed["first_aiee"]));
828
+ const secondId = this.buildAiee(asHash(parsed["second_aiee"]));
829
+ return new DualPublishedClass({
830
+ first_identifier: firstId,
831
+ second_identifier: secondId,
832
+ });
833
+ }
834
+ buildIecIeeCopublished(parsed) {
835
+ const content = extractValue(parsed["content"]);
836
+ let copublishedNumber;
837
+ let draftInfo;
838
+ let iecYear;
839
+ let dateInfo;
840
+ if (content !== undefined) {
841
+ if (content.includes("IEC:")) {
842
+ copublishedNumber = content.split(" IEC:")[0]?.trim();
843
+ }
844
+ else if (content.includes(", ")) {
845
+ copublishedNumber = content.split(", ")[0]?.trim();
846
+ }
847
+ else if (content.includes(" (")) {
848
+ copublishedNumber = content.split(" (")[0]?.trim();
849
+ }
850
+ else {
851
+ copublishedNumber = content.trim();
852
+ }
853
+ if (copublishedNumber !== undefined && copublishedNumber.includes("/D")) {
854
+ const parts = copublishedNumber.split("/D");
855
+ copublishedNumber = parts[0];
856
+ draftInfo = `/D${parts[1] ?? ""}`;
857
+ }
858
+ if (content.includes("IEC:")) {
859
+ const iecPart = content.split("IEC:")[1];
860
+ if (iecPart !== undefined)
861
+ iecYear = iecPart.split(" ")[0];
862
+ }
863
+ if (content.includes(" (")) {
864
+ const datePart = content.split(" (")[1];
865
+ if (datePart !== undefined && datePart.includes(")")) {
866
+ dateInfo = datePart.split(")")[0];
867
+ }
868
+ }
869
+ }
870
+ const attrs = {};
871
+ if (draftInfo !== undefined)
872
+ attrs["draft_info"] = draftInfo;
873
+ if (iecYear !== undefined)
874
+ attrs["iec_year"] = iecYear;
875
+ if (dateInfo !== undefined)
876
+ attrs["date_info"] = dateInfo;
877
+ Object.assign(attrs, copublishedStructured(copublishedNumber));
878
+ return new IecIeeeCopublishedClass(attrs);
879
+ }
880
+ // --- AIEE / IRE / NESC builders ---------------------------------------------
881
+ buildAiee(parsed) {
882
+ const attributes = { publisher: "AIEE" };
883
+ const typeStr = extractValue(parsed["type"]);
884
+ attributes["type"] = typeStr !== undefined && typeStr.startsWith("Standard") ? "No" : typeStr;
885
+ const numberValue = parsed["number"];
886
+ let codeStr;
887
+ if (isObj(numberValue)) {
888
+ const main = extractValue(numberValue["main_number"]);
889
+ const paren = numberValue["parenthetical"];
890
+ const alt = isObj(paren) ? extractValue(paren["alt_number"]) : undefined;
891
+ codeStr = alt !== undefined && main !== undefined ? `${main} (${alt})` : main;
892
+ }
893
+ else {
894
+ codeStr = extractValue(numberValue);
895
+ }
896
+ if (codeStr !== undefined) {
897
+ const codeObj = IeeeCode.parse(codeStr);
898
+ if (codeObj !== undefined) {
899
+ attributes["number"] = codeObj.number;
900
+ if (codeObj.prefix !== undefined)
901
+ attributes["prefix"] = codeObj.prefix;
902
+ if (codeObj.parts.length > 0)
903
+ attributes["parts"] = codeObj.parts;
904
+ if (codeObj.originalSeparator !== undefined)
905
+ attributes["separator"] = codeObj.originalSeparator;
906
+ }
907
+ }
908
+ attributes["year"] = extractValue(parsed["year"]);
909
+ const monthStr = extractValue(parsed["month"]);
910
+ if (monthStr !== undefined)
911
+ attributes["month"] = monthStr;
912
+ const separatorStr = extractValue(parsed["separator"]);
913
+ if (separatorStr !== undefined)
914
+ attributes["date_separator"] = separatorStr;
915
+ attributes["original_format"] = separatorStr !== undefined || monthStr !== undefined ? "long" : "short";
916
+ return new AieeClass(attributes);
917
+ }
918
+ buildIre(parsed) {
919
+ const attributes = {};
920
+ attributes["publisher"] = extractValue(parsed["publisher"]);
921
+ attributes["type"] = extractValue(parsed["type"]);
922
+ const numberStr = extractValue(parsed["number"]);
923
+ if (numberStr !== undefined) {
924
+ const codeObj = IeeeCode.parse(numberStr);
925
+ if (codeObj !== undefined) {
926
+ attributes["number"] = codeObj.number;
927
+ if (codeObj.prefix !== undefined)
928
+ attributes["prefix"] = codeObj.prefix;
929
+ if (codeObj.parts.length > 0)
930
+ attributes["parts"] = codeObj.parts;
931
+ if (codeObj.originalSeparator !== undefined)
932
+ attributes["separator"] = codeObj.originalSeparator;
933
+ }
934
+ }
935
+ const yearStr = extractValue(parsed["year"]);
936
+ if (yearStr !== undefined) {
937
+ const yearInt = Number.parseInt(yearStr, 10);
938
+ attributes["year"] = String(yearInt >= 12 && yearInt <= 63 ? yearInt + 1900 : yearInt);
939
+ }
940
+ const fullYearStr = extractValue(parsed["full_year"]);
941
+ if (fullYearStr !== undefined)
942
+ attributes["year"] = String(Number.parseInt(fullYearStr, 10));
943
+ return new IreClass(attributes);
944
+ }
945
+ buildNesc(parsedHash) {
946
+ // Ruby falls through the variant case on a non-matching value (the
947
+ // nested name_first variant captures as a hash) and reaches Standard
948
+ // via the code check; Edition is only the no-code fallback.
949
+ let klass = NescEditionClass;
950
+ if (parsedHash["draft"] !== undefined) {
951
+ klass = NescDraftClass;
952
+ }
953
+ else if (typeof parsedHash["variant"] === "string") {
954
+ const variant = parsedHash["variant"];
955
+ if (variant === "Handbook")
956
+ klass = NescHandbookClass;
957
+ else if (variant === "Redline")
958
+ klass = NescRedlineClass;
959
+ }
960
+ if (klass === NescEditionClass && parsedHash["code"] !== undefined) {
961
+ klass = NescStandardClass;
962
+ }
963
+ const attrs = { code: parsedHash["code"] ?? "C2" };
964
+ const codeObj = IeeeCode.parse(String(attrs["code"]));
965
+ if (codeObj !== undefined) {
966
+ attrs["number"] = codeObj.number;
967
+ if (codeObj.prefix !== undefined)
968
+ attrs["prefix"] = codeObj.prefix;
969
+ if (codeObj.parts.length > 0)
970
+ attrs["parts"] = codeObj.parts;
971
+ }
972
+ if (parsedHash["year"] !== undefined)
973
+ attrs["year"] = String(parsedHash["year"]);
974
+ if (parsedHash["variant"] !== undefined)
975
+ attrs["variant"] = String(parsedHash["variant"]);
976
+ if (parsedHash["edition"] !== undefined)
977
+ attrs["edition"] = String(parsedHash["edition"]);
978
+ if (parsedHash["month"] !== undefined)
979
+ attrs["month"] = String(parsedHash["month"]);
980
+ if (parsedHash["name_registered"] !== undefined || parsedHash["abbr_registered"] !== undefined) {
981
+ attrs["registered"] = true;
982
+ }
983
+ if (parsedHash["paren_abbr"] !== undefined) {
984
+ attrs["abbr_suffix"] = true;
985
+ if (parsedHash["paren_registered"] !== undefined)
986
+ attrs["abbr_suffix_registered"] = true;
987
+ }
988
+ return new klass(attrs);
989
+ }
990
+ }
991
+ function copublishedStructured(copublishedNumber) {
992
+ if (copublishedNumber === undefined || copublishedNumber === "")
993
+ return {};
994
+ const attrs = {};
995
+ let remainder = copublishedNumber;
996
+ let year;
997
+ let yearSep;
998
+ const yearMatch = /([-:])((?:19|20)\d\d)$/.exec(copublishedNumber);
999
+ if (yearMatch !== null) {
1000
+ yearSep = yearMatch[1];
1001
+ year = yearMatch[2];
1002
+ remainder = copublishedNumber.slice(0, copublishedNumber.length - yearSep.length - year.length);
1003
+ }
1004
+ const all = remainder.match(/^[^.\-]+|[.\-][^.\-]+/g) ?? [];
1005
+ const parts = [];
1006
+ const separators = [];
1007
+ for (const token of all.slice(1)) {
1008
+ parts.push(token.slice(1));
1009
+ separators.push(token[0] ?? "");
1010
+ }
1011
+ attrs["number"] = all[0];
1012
+ if (parts.length > 0) {
1013
+ attrs["parts"] = parts;
1014
+ attrs["separators"] = separators;
1015
+ }
1016
+ if (year !== undefined) {
1017
+ attrs["year"] = year;
1018
+ if (yearSep !== "-")
1019
+ attrs["year_sep"] = yearSep;
1020
+ }
1021
+ return attrs;
1022
+ }
1023
+ // --- parse orchestration -------------------------------------------------------
1024
+ function ieeeParseSingle(input) {
1025
+ const cleaned = preprocessIeee(applyUpdateCodes(input));
1026
+ const tree = parseGrammar(ieeeGrammar, cleaned);
1027
+ const builder = new IeeeBuilder();
1028
+ builder.originalInput = input;
1029
+ return builder.build(tree);
1030
+ }
1031
+ // The aiee_simple fallback parses WITHOUT the legacy update-codes table -
1032
+ // the Ruby reference routes "AIEE …" to the AIEE sub-parser only, so an
1033
+ // update-codes rewrite (e.g. "AIEE Nos 72 and 73 - 1932") must not make
1034
+ // a line parse that the reference rejects.
1035
+ function ieeeParseSingleNoCodes(input) {
1036
+ const tree = parseGrammar(ieeeGrammar, preprocessIeee(input));
1037
+ const builder = new IeeeBuilder();
1038
+ builder.originalInput = input;
1039
+ return builder.build(tree);
1040
+ }
1041
+ // The aiee_simple fallback parses WITHOUT the legacy update-codes table -
1042
+ // the Ruby reference routes "AIEE …" to the AIEE sub-parser only, so an
1043
+ // update-codes rewrite (e.g. "AIEE Nos 72 and 73 - 1932") must not make
1044
+ // a line parse that the reference rejects.
1045
+ function parseAieeDirect(input) {
1046
+ const builder = new IeeeBuilder();
1047
+ builder.originalInput = input;
1048
+ const tree = parseGrammar({ rules: ieeeGrammar.rules, root: "aiee_identifier" }, input);
1049
+ return builder.buildAiee(asHash(tree));
1050
+ }
1051
+ function normalizeIecAdoption(part) {
1052
+ return part
1053
+ .replace(/\s+Edition\s+([0-9.]+)\s+([0-9-]+)/, ":$2 ED$1")
1054
+ .replace(/\s+Edition\s+([0-9.]+)\s*$/, " ED$1");
1055
+ }
1056
+ function buildDual(parts) {
1057
+ const first = ieeeParseSingle(parts[0]);
1058
+ const second = ieeeParseSingle(parts[1]);
1059
+ return new DualPublishedClass({
1060
+ first_identifier: first,
1061
+ second_identifier: second,
1062
+ });
1063
+ }
1064
+ function buildReaffirmed(input, reaffirmed) {
1065
+ const parsed = ieeeParseSingle(input);
1066
+ parsed["reaffirmed"] = reaffirmed;
1067
+ return parsed;
1068
+ }
1069
+ function buildDualWithReaffirmed(parts, reaffirmed) {
1070
+ const ieeeId = ieeeParseSingle(parts[0]);
1071
+ ieeeId["reaffirmed"] = reaffirmed;
1072
+ const ireId = ieeeParseSingle(parts[1]);
1073
+ return new DualPublishedClass({
1074
+ first_identifier: ieeeId,
1075
+ second_identifier: ireId,
1076
+ });
1077
+ }
1078
+ function parseAdoptedPart(part) {
1079
+ if (part.startsWith("IEC")) {
1080
+ const impl = grammarImplementation("iec");
1081
+ if (impl === undefined)
1082
+ throw new ParseFailed("IEC implementation unavailable", 0);
1083
+ return impl.parse(normalizeIecAdoption(part));
1084
+ }
1085
+ if (part.startsWith("ANSI")) {
1086
+ const impl = grammarImplementation("ansi");
1087
+ if (impl === undefined)
1088
+ throw new ParseFailed("ANSI implementation unavailable", 0);
1089
+ return impl.parse(part);
1090
+ }
1091
+ return ieeeParseSingle(part);
1092
+ }
1093
+ function buildAdopted(parts) {
1094
+ const ieeeId = ieeeParseSingle(parts[0]);
1095
+ const adoptedParts = parts[1].split(",").map((p) => p.trim());
1096
+ const adoptedIds = adoptedParts.map(parseAdoptedPart);
1097
+ const AdoptedClass = AdoptedStandardClass;
1098
+ return new AdoptedClass({
1099
+ ieee_identifier: ieeeId,
1100
+ adopted_identifiers: adoptedIds,
1101
+ });
1102
+ }
1103
+ // A sentinel for "the reference rejects this line outright" - distinct from
1104
+ // ParseFailed so ieeeParse's blanket retry (load-bearing for other forms)
1105
+ // does not resurrect it.
1106
+ class RejectedOutright extends Error {
1107
+ }
1108
+ export function ieeeParse(input) {
1109
+ try {
1110
+ return ieeeParseDispatched(input);
1111
+ }
1112
+ catch (e) {
1113
+ if (e instanceof ParseFailed && !(e instanceof RejectedOutright))
1114
+ return ieeeParseSingle(input);
1115
+ if (e instanceof RejectedOutright)
1116
+ throw new ParseFailed(e.message, 0);
1117
+ throw e;
1118
+ }
1119
+ }
1120
+ function ieeeParseDispatched(input) {
1121
+ const result = preParse(input);
1122
+ switch (result.dispatch) {
1123
+ case "aiee_simple":
1124
+ // Ruby: Aiee::Identifier.parse, with the WHOLE parse falling back to
1125
+ // parse_single on ParseFailed (forms the AIEE sub-grammar rejects,
1126
+ // e.g. "AIEE 15-May 1928", parse through the generic bucket).
1127
+ try {
1128
+ return parseAieeDirect(result.input);
1129
+ }
1130
+ catch {
1131
+ // Ruby: Aiee::Identifier.parse with no retry - an update-codes
1132
+ // rewrite must not make an AIEE-form line parse that the
1133
+ // reference rejects ("AIEE Nos 72 and 73 - 1932").
1134
+ try {
1135
+ return ieeeParseSingleNoCodes(input);
1136
+ }
1137
+ catch {
1138
+ throw new RejectedOutright(input);
1139
+ }
1140
+ }
1141
+ case "iec_ieee_copublished":
1142
+ case "dual_semicolon":
1143
+ case "dual_space_separated":
1144
+ if (result.dispatch === "dual_semicolon" || result.dispatch === "dual_space_separated") {
1145
+ return buildDual(result.parts);
1146
+ }
1147
+ try {
1148
+ return ieeeParseSingle(result.input);
1149
+ }
1150
+ catch {
1151
+ return ieeeParseSingle(input);
1152
+ }
1153
+ case "dual_reaffirmed":
1154
+ return buildReaffirmed(result.input, result.metadata["reaffirmed"] ?? "");
1155
+ case "dual_ire":
1156
+ return buildDualWithReaffirmed(result.parts, result.metadata["reaffirmed"] ?? "");
1157
+ case "dual_and":
1158
+ case "dual_ampersand":
1159
+ return buildDual(result.parts);
1160
+ case "aiee_asa_adoption": {
1161
+ const aieeId = ieeeParseSingle(result.parts[0]);
1162
+ const asaId = ieeeParseSingle(result.parts[1]);
1163
+ const AdoptedClass = AdoptedStandardClass;
1164
+ return new AdoptedClass({
1165
+ ieee_identifier: aieeId,
1166
+ adopted_identifiers: [asaId],
1167
+ });
1168
+ }
1169
+ case "adopted":
1170
+ return buildAdopted(result.parts);
1171
+ default:
1172
+ try {
1173
+ return ieeeParseSingle(result.input);
1174
+ }
1175
+ catch {
1176
+ return ieeeParseSingle(input);
1177
+ }
1178
+ }
1179
+ }
1180
+ export function ieeeGrammarImplementation() {
1181
+ return {
1182
+ parse(input) {
1183
+ return ieeeParse(input);
1184
+ },
1185
+ };
1186
+ }