@pubid/pubid 0.1.30 → 0.1.32
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.
- package/artifact/corpus.json.gz +0 -0
- package/dist/flavors/iec/index.js +12 -5
- package/dist/flavors/ieee/index.js +321 -113
- package/dist/flavors/ieee/model.d.ts +18 -0
- package/dist/flavors/ieee/model.js +62 -8
- package/dist/flavors/ieee/preprocessor.js +3 -0
- package/dist/flavors/ieee/update_codes.js +1 -1
- package/dist/flavors/iho/index.js +198 -35
- package/dist/flavors/jis/index.js +74 -2
- package/dist/flavors/nist/grammar.js +44 -16
- package/dist/flavors/nist/index.js +17 -2
- package/dist/model/identifier.d.ts +9 -0
- package/dist/model/identifier.js +50 -0
- package/dist/urn/parsers.js +57 -20
- package/package.json +2 -2
- package/vendor/parg/adobe.json +1 -1
- package/vendor/parg/aiee.json +1 -1
- package/vendor/parg/amca.json +1 -1
- package/vendor/parg/ansi.json +1 -1
- package/vendor/parg/api.json +1 -1
- package/vendor/parg/ashrae.json +1 -1
- package/vendor/parg/asme.json +1 -1
- package/vendor/parg/astm.json +1 -1
- package/vendor/parg/bipm.json +1 -1
- package/vendor/parg/bsi.json +1 -1
- package/vendor/parg/calconnect.json +1 -1
- package/vendor/parg/ccsds.json +1 -1
- package/vendor/parg/cen_cenelec.json +1 -1
- package/vendor/parg/cie.json +1 -1
- package/vendor/parg/csa.json +1 -1
- package/vendor/parg/doi.json +1 -1
- package/vendor/parg/easc.json +1 -1
- package/vendor/parg/ecma.json +1 -1
- package/vendor/parg/etsi.json +1 -1
- package/vendor/parg/evs.json +1 -1
- package/vendor/parg/gb.json +1 -1
- package/vendor/parg/gost.json +1 -1
- package/vendor/parg/iala.json +1 -1
- package/vendor/parg/iana.json +1 -1
- package/vendor/parg/idf.json +1 -1
- package/vendor/parg/iec.json +1 -1
- package/vendor/parg/ieee.json +1 -29571
- package/vendor/parg/ietf.json +1 -1
- package/vendor/parg/iho.json +1 -0
- package/vendor/parg/ire.json +1 -1
- package/vendor/parg/isbn.json +1 -1
- package/vendor/parg/iso.json +1 -8510
- package/vendor/parg/itu.json +1 -1
- package/vendor/parg/jcgm.json +1 -1
- package/vendor/parg/jis.json +1 -1
- package/vendor/parg/nesc.json +1 -1
- package/vendor/parg/nist.json +1 -9588
- package/vendor/parg/oasis.json +1 -1
- package/vendor/parg/ogc.json +1 -1
- package/vendor/parg/oiml.json +1 -1
- package/vendor/parg/omg.json +1 -1
- package/vendor/parg/parg.json +1 -1
- package/vendor/parg/plateau.json +1 -1
- package/vendor/parg/sae.json +1 -1
- package/vendor/parg/tgpp.json +1 -1
- package/vendor/parg/un.json +1 -1
- package/vendor/parg/w3c.json +1 -1
- package/vendor/parg/xsf.json +1 -1
|
@@ -36,7 +36,6 @@ class IeeeBuilder {
|
|
|
36
36
|
originalInput = "";
|
|
37
37
|
build(parsed) {
|
|
38
38
|
const d = Array.isArray(parsed) ? mergeParsedArray(parsed) : parsed;
|
|
39
|
-
this.normalizeDraftDashYear(d);
|
|
40
39
|
if (d["ieee_portion"] !== undefined && d["csa_portion"] !== undefined) {
|
|
41
40
|
return this.buildCsaDualPublished(d);
|
|
42
41
|
}
|
|
@@ -58,119 +57,236 @@ class IeeeBuilder {
|
|
|
58
57
|
return this.buildSingleIdentifier(d, false);
|
|
59
58
|
}
|
|
60
59
|
// --- single identifier -----------------------------------------------------
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
// Deep-first collection of every :subpart node under a raw
|
|
61
|
+
// part_subpart_year wrapper (hash or array, any nesting).
|
|
62
|
+
collectSubparts(node, out = []) {
|
|
63
|
+
if (isObj(node)) {
|
|
64
|
+
for (const [key, value] of Object.entries(node)) {
|
|
65
|
+
if (key === "subpart")
|
|
66
|
+
out.push(value);
|
|
67
|
+
else
|
|
68
|
+
this.collectSubparts(value, out);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (Array.isArray(node)) {
|
|
72
|
+
for (const item of node)
|
|
73
|
+
this.collectSubparts(item, out);
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
// The part/subpart/year captures ride the raw part_subpart_year wrapper
|
|
78
|
+
// (parsanol-ruby#399: the repeated-subpart flat spread clobbers to the
|
|
79
|
+
// last value, so the run does not bind top-level). Hoist the wrapper's
|
|
80
|
+
// part/year and deep-collect its interior subparts, so every consumer —
|
|
81
|
+
// and every raw subtree built through here (the CSA dual's
|
|
82
|
+
// ieee_portion, the multi-numbered primary, the supplement bases) —
|
|
83
|
+
// sees the flat keys it has always read (the reference's
|
|
84
|
+
// unwrap_part_subpart_year).
|
|
85
|
+
unwrapPartSubpartYear(node) {
|
|
86
|
+
const run = node["part_subpart_year"];
|
|
87
|
+
if (run === undefined)
|
|
67
88
|
return;
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
const nodes = Array.isArray(run) ? run : [run];
|
|
90
|
+
const flat = {};
|
|
91
|
+
for (const item of nodes) {
|
|
92
|
+
if (isObj(item))
|
|
93
|
+
Object.assign(flat, item);
|
|
94
|
+
}
|
|
95
|
+
if (flat["part"] !== undefined)
|
|
96
|
+
node["part"] = flat["part"];
|
|
97
|
+
if (flat["year"] !== undefined)
|
|
98
|
+
node["year"] = flat["year"];
|
|
99
|
+
if (node["subpart"] === undefined) {
|
|
100
|
+
const collected = this.collectSubparts(run);
|
|
101
|
+
if (collected.length > 0)
|
|
102
|
+
node["subpart"] = collected;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// The edition-2 wasm tree → the native engine's flat shape — the
|
|
106
|
+
// normalization prelude the reference's build_single_identifier runs
|
|
107
|
+
// before every dispatch (builder.rb: the scalar surfacing, the
|
|
108
|
+
// composite rename, the wrapper unwrap, the draft-array repairs and
|
|
109
|
+
// the dash-date hoist).
|
|
110
|
+
normalizeParsedHash(parsedHash) {
|
|
111
|
+
// The wasm bound map captures the optional rev_sep for every printed
|
|
112
|
+
// boundary; the reference's native engine binds it on the bare-REV
|
|
113
|
+
// ("-REV/D1.7" keeps "-") and lettered-revision branches
|
|
114
|
+
// ("-REVmc" keeps "-") and declines it on every numeric revision
|
|
115
|
+
// spelling (".Rev 3", "/rev43", "-rev1", "_Rev2", " Rev 8" — the
|
|
116
|
+
// rev_numbered route, parsanol-ruby#394 family), so the render glues
|
|
117
|
+
// the numeric lettered revisions on those rows.
|
|
118
|
+
if (parsedHash["rev_sep"] !== undefined) {
|
|
119
|
+
const revision = extractValue(parsedHash["revision"]) ?? "";
|
|
120
|
+
if (/^[0-9]+$/.test(revision)) {
|
|
121
|
+
delete parsedHash["rev_sep"];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// The grammar binds the bare top-level draft clause as
|
|
125
|
+
// draft_version_scalar (string) — surface it under the key the rest
|
|
126
|
+
// of the builder has always read.
|
|
127
|
+
if (parsedHash["draft_version"] === undefined && parsedHash["draft_version_scalar"] !== undefined) {
|
|
128
|
+
parsedHash["draft_version"] = parsedHash["draft_version_scalar"];
|
|
129
|
+
delete parsedHash["draft_version_scalar"];
|
|
130
|
+
}
|
|
131
|
+
// The composite draft rule binds draft_version_composite (raw)
|
|
132
|
+
// inside the draft elements; rename it under the interior key the
|
|
133
|
+
// draft-element walks read.
|
|
134
|
+
const draftElems0 = Array.isArray(parsedHash["draft"])
|
|
135
|
+
? parsedHash["draft"]
|
|
136
|
+
: [parsedHash["draft"]];
|
|
137
|
+
for (const e of draftElems0) {
|
|
138
|
+
if (isObj(e) && e["draft_version"] === undefined && e["draft_version_composite"] !== undefined) {
|
|
139
|
+
e["draft_version"] = e["draft_version_composite"];
|
|
140
|
+
delete e["draft_version_composite"];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
this.unwrapPartSubpartYear(parsedHash);
|
|
144
|
+
// A revision element INSIDE the draft array is the draft rule's own
|
|
145
|
+
// split artifact (the version's trailing ".1" segment) — the spine
|
|
146
|
+
// captured the same value; drop it.
|
|
147
|
+
if (Array.isArray(parsedHash["draft"]) && parsedHash["revision"] !== undefined) {
|
|
148
|
+
const artifact = parsedHash["draft"].some((e) => isObj(e) && e["revision"] !== undefined &&
|
|
149
|
+
String(extractValue(e["revision"]) ?? "") === String(extractValue(parsedHash["revision"]) ?? ""));
|
|
150
|
+
if (artifact)
|
|
151
|
+
delete parsedHash["revision"];
|
|
152
|
+
}
|
|
153
|
+
// The edition-2 tree materializes a multi-segment draft version
|
|
154
|
+
// ("D2.1.1") as per-token nodes carrying no draft_version key (the
|
|
155
|
+
// v1 tree degraded such a clause to a whole-clause LEAF string when
|
|
156
|
+
// no draft date follows, and to a date-only hash when it does).
|
|
157
|
+
// Normalize to the v1 shape before the stage derivation and draft
|
|
158
|
+
// handling read it.
|
|
159
|
+
if (Array.isArray(parsedHash["draft"])) {
|
|
160
|
+
const elems = parsedHash["draft"].filter(isObj);
|
|
161
|
+
if (elems.length > 0 && elems.every((e) => e["draft_version"] === undefined)) {
|
|
162
|
+
const hasDate = elems.some((e) => e["draft_date_month"] !== undefined || e["draft_date_year"] !== undefined);
|
|
163
|
+
if (hasDate) {
|
|
164
|
+
const dateElems = elems.filter((e) => e["draft_date_month"] !== undefined || e["draft_date_year"] !== undefined || e["draft_date_day"] !== undefined);
|
|
165
|
+
parsedHash["draft"] = Object.assign({}, ...dateElems);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
parsedHash["draft"] = elems
|
|
169
|
+
.flatMap((e) => Object.values(e))
|
|
170
|
+
.map((v) => String(extractValue(v) ?? ""))
|
|
171
|
+
.join("");
|
|
79
172
|
}
|
|
80
173
|
}
|
|
81
174
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
175
|
+
const drafts = Array.isArray(parsedHash["draft"])
|
|
176
|
+
? parsedHash["draft"]
|
|
177
|
+
: [parsedHash["draft"]].filter((v) => v !== undefined);
|
|
178
|
+
// The edition-2 grammar renames the nested draft-version dash
|
|
179
|
+
// captures (draft_version_dash_year/month); hoist them to the
|
|
180
|
+
// element level the dash walk reads, and drop the wrapper key.
|
|
181
|
+
for (const d of drafts) {
|
|
182
|
+
if (!isObj(d) || !isObj(d["draft_version"]))
|
|
87
183
|
continue;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
184
|
+
const dv = d["draft_version"];
|
|
185
|
+
if (dv["draft_version_dash_year"] !== undefined || dv["dash_year"] !== undefined) {
|
|
186
|
+
d["dash_year"] = dv["draft_version_dash_year"] ?? dv["dash_year"];
|
|
187
|
+
}
|
|
188
|
+
if (dv["draft_version_dash_month"] !== undefined || dv["dash_month"] !== undefined) {
|
|
189
|
+
d["dash_month"] = dv["draft_version_dash_month"] ?? dv["dash_month"];
|
|
190
|
+
}
|
|
191
|
+
delete d["draft_version"];
|
|
192
|
+
}
|
|
193
|
+
// The dash host: the top-level spine, else a draft element carrying
|
|
194
|
+
// its own dash date (draft_date_dash_year/month on the edition-2
|
|
195
|
+
// grammar, dash_year/month on v1 — read both spellings).
|
|
196
|
+
let dash = parsedHash["dash_year"] !== undefined ? parsedHash : undefined;
|
|
197
|
+
if (dash === undefined) {
|
|
198
|
+
for (const d of drafts) {
|
|
199
|
+
if (isObj(d) && (d["draft_date_dash_year"] !== undefined || d["dash_year"] !== undefined)) {
|
|
200
|
+
dash = d;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// A numeric month trailing a draft is the draft's own date: it
|
|
206
|
+
// embeds in the designator ("D09, 04 2011" -> "D09-2011-04") rather
|
|
207
|
+
// than printing the long-format comma face — pubid ruling
|
|
208
|
+
// 2026-10-13.
|
|
209
|
+
if (dash === undefined && parsedHash["draft"] !== undefined) {
|
|
210
|
+
const mVal = extractValue((parsedHash["month"] ?? parsedHash["trailing_month"]));
|
|
211
|
+
const yVal = extractValue((parsedHash["year"] ?? parsedHash["trailing_year"]));
|
|
212
|
+
if (mVal !== undefined && /^\d{1,2}$/.test(mVal) && yVal !== undefined) {
|
|
213
|
+
const host = drafts.find((d) => isObj(d) && d["draft_version"] !== undefined);
|
|
105
214
|
if (host !== undefined && isObj(host)) {
|
|
106
215
|
const hv = host["draft_version"];
|
|
107
216
|
const joined = Array.isArray(hv)
|
|
108
|
-
? hv.map((v) => (
|
|
217
|
+
? hv.map((v) => extractValue(v) ?? "").join("")
|
|
109
218
|
: String(extractValue(hv) ?? "");
|
|
110
|
-
host["draft_version"] = [joined,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
delete
|
|
114
|
-
delete
|
|
115
|
-
delete d["trailing_month"];
|
|
116
|
-
delete d["trailing_year"];
|
|
219
|
+
host["draft_version"] = [joined, yVal, mVal].filter((s) => s !== "").join("-");
|
|
220
|
+
delete parsedHash["month"];
|
|
221
|
+
delete parsedHash["year"];
|
|
222
|
+
delete parsedHash["trailing_month"];
|
|
223
|
+
delete parsedHash["trailing_year"];
|
|
117
224
|
}
|
|
118
225
|
}
|
|
119
226
|
}
|
|
120
|
-
const dash = drafts.find((e) => isObj(e) && e["dash_year"] !== undefined) ??
|
|
121
|
-
(d["dash_year"] !== undefined ? d : undefined);
|
|
122
227
|
if (dash === undefined)
|
|
123
228
|
return;
|
|
229
|
+
// The draft's own dash date embeds onto the draft even when a base
|
|
230
|
+
// identity year is present ("…-2011/D4-2015-07"); only the root-year
|
|
231
|
+
// assignment waits for an unoccupied year slot.
|
|
124
232
|
const objs = drafts.filter(isObj);
|
|
125
233
|
const joined = objs
|
|
126
234
|
.map((e) => e["draft_version"])
|
|
127
235
|
.filter((v) => v !== undefined)
|
|
128
|
-
.map((v) => (
|
|
236
|
+
.map((v) => (Array.isArray(v) ? v.map((x) => extractValue(x) ?? "").join("") : String(extractValue(v) ?? "")))
|
|
129
237
|
.join("");
|
|
130
|
-
const
|
|
131
|
-
|
|
238
|
+
const dashY = extractValue((dash["dash_year"] ?? dash["draft_date_dash_year"]));
|
|
239
|
+
const dashM = extractValue((dash["dash_month"] ?? dash["draft_date_dash_month"]));
|
|
240
|
+
const clearDash = () => {
|
|
241
|
+
delete dash["dash_year"];
|
|
242
|
+
delete dash["dash_month"];
|
|
243
|
+
delete dash["draft_date_dash_year"];
|
|
244
|
+
delete dash["draft_date_dash_month"];
|
|
245
|
+
};
|
|
246
|
+
const embed = () => {
|
|
247
|
+
const host = drafts.find((d) => isObj(d) && d["draft_version"] !== undefined);
|
|
248
|
+
if (host === undefined || !isObj(host))
|
|
249
|
+
return undefined;
|
|
250
|
+
const hv = host["draft_version"];
|
|
251
|
+
const vJoined = Array.isArray(hv)
|
|
252
|
+
? hv.map((v) => extractValue(v) ?? "").join("")
|
|
253
|
+
: String(extractValue(hv) ?? "");
|
|
254
|
+
host["draft_version"] = [vJoined, dashY, dashM]
|
|
255
|
+
.filter((s) => s !== undefined)
|
|
256
|
+
.join("-");
|
|
257
|
+
return host;
|
|
258
|
+
};
|
|
259
|
+
const bareJointDraft = parsedHash["joint_publishers"] !== undefined &&
|
|
260
|
+
parsedHash["part"] === undefined && parsedHash["iso_stage"] === undefined &&
|
|
132
261
|
objs.some((e) => e["draft_version"] !== undefined);
|
|
133
262
|
const dashLed = joined.startsWith("-");
|
|
134
|
-
const dashYear = extractValue(dash["dash_year"]);
|
|
135
|
-
const dashMonth = dash["dash_month"];
|
|
136
263
|
if (bareJointDraft && dashLed) {
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
else if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
delete
|
|
152
|
-
delete dash["dash_month"];
|
|
153
|
-
}
|
|
154
|
-
else if (d["year"] === undefined) {
|
|
155
|
-
d["year"] = dashYear;
|
|
156
|
-
d["month"] = dm;
|
|
157
|
-
}
|
|
264
|
+
parsedHash["part"] = { value: dashY };
|
|
265
|
+
}
|
|
266
|
+
else if (dashM !== undefined && embed() !== undefined) {
|
|
267
|
+
clearDash();
|
|
268
|
+
}
|
|
269
|
+
else if ((dashM !== undefined || dashLed) && parsedHash["year"] === undefined) {
|
|
270
|
+
parsedHash["year"] = dashY;
|
|
271
|
+
parsedHash["month"] = dashM;
|
|
272
|
+
}
|
|
273
|
+
else if (embed() !== undefined) {
|
|
274
|
+
clearDash();
|
|
275
|
+
// the hyphenated "/D-2-2007" spelling's year the spine captured at
|
|
276
|
+
// top level is the draft's own — vacate it
|
|
277
|
+
if (extractValue(parsedHash["year"]) === dashY)
|
|
278
|
+
delete parsedHash["year"];
|
|
158
279
|
}
|
|
159
280
|
else {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
host["draft_version"] = `${joined}-${dashYear}`;
|
|
163
|
-
delete dash["dash_year"];
|
|
164
|
-
delete dash["dash_month"];
|
|
165
|
-
}
|
|
166
|
-
else if (d["year"] === undefined) {
|
|
167
|
-
d["year"] = dashYear;
|
|
168
|
-
d["month"] = extractValue(dashMonth);
|
|
169
|
-
}
|
|
281
|
+
parsedHash["year"] = dashY;
|
|
282
|
+
parsedHash["month"] = dashM;
|
|
170
283
|
}
|
|
171
284
|
}
|
|
172
285
|
buildSingleIdentifier(parsed, buildingSecondary) {
|
|
173
286
|
const parsedHash = asHash(parsed);
|
|
287
|
+
// The edition-2 wasm tree → the native engine's flat shape, before
|
|
288
|
+
// every dispatch reads it (the reference's prelude).
|
|
289
|
+
this.normalizeParsedHash(parsedHash);
|
|
174
290
|
if (!buildingSecondary && parsedHash["primary_identifier"] !== undefined &&
|
|
175
291
|
(parsedHash["secondary_crossref"] !== undefined || parsedHash["secondary_joint"] !== undefined)) {
|
|
176
292
|
return this.buildMultiNumbered(parsedHash);
|
|
@@ -256,6 +372,8 @@ class IeeeBuilder {
|
|
|
256
372
|
attributes["parts"] = codeObj.parts;
|
|
257
373
|
if (codeObj?.originalSeparator !== undefined)
|
|
258
374
|
attributes["separator"] = codeObj.originalSeparator;
|
|
375
|
+
if (codeObj !== undefined)
|
|
376
|
+
attributes["__code_obj"] = codeObj;
|
|
259
377
|
// P = project (the document is a draft): the marker is
|
|
260
378
|
// identity-bearing, so stripping the letter must not strip the
|
|
261
379
|
// fact (docs/IEEE-DRAFT-STAGES.md §1.1).
|
|
@@ -264,7 +382,15 @@ class IeeeBuilder {
|
|
|
264
382
|
}
|
|
265
383
|
delete attributes["__code"];
|
|
266
384
|
this.renameSupplementKeys(attributes);
|
|
267
|
-
|
|
385
|
+
// The parse path carries the freshly parsed Code (per-part
|
|
386
|
+
// separators — the reference's `code:` string → Components::Code.parse
|
|
387
|
+
// in base #initialize); the column-only rebuild stays the from_hash
|
|
388
|
+
// face.
|
|
389
|
+
const built = new klass(attributes);
|
|
390
|
+
if (attributes["__code_obj"] instanceof IeeeCode) {
|
|
391
|
+
built["__code_obj"] = attributes["__code_obj"];
|
|
392
|
+
}
|
|
393
|
+
return built;
|
|
268
394
|
}
|
|
269
395
|
renameSupplementKeys(attributes) {
|
|
270
396
|
const ordinal = attributes["cor_number"] ?? attributes["amd_number"] ?? attributes["conf_number"];
|
|
@@ -368,11 +494,10 @@ class IeeeBuilder {
|
|
|
368
494
|
}
|
|
369
495
|
if (codeStr !== undefined && codeParts.length > 0) {
|
|
370
496
|
// Captures carry their own separator when present (the artifact's
|
|
371
|
-
// part/subpart bind ".18" / "-10");
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
|
|
375
|
-
codeStr += codeParts.map((cp) => /^[._-]/.test(cp) ? cp : `${partSep}${cp}`).join("");
|
|
497
|
+
// part/subpart bind ".18" / "-10"); join verbatim like the
|
|
498
|
+
// reference's extract_attributes — a dashed "62271-37-082" stays
|
|
499
|
+
// dashed.
|
|
500
|
+
codeStr += codeParts.join("");
|
|
376
501
|
}
|
|
377
502
|
if (codeStr !== undefined && parsed["year"] === undefined && codeParts.length === 0) {
|
|
378
503
|
const m = /^(.+)-(\d{4})$/.exec(codeStr);
|
|
@@ -388,6 +513,7 @@ class IeeeBuilder {
|
|
|
388
513
|
if (codeStr !== undefined)
|
|
389
514
|
attributes["__code"] = codeStr;
|
|
390
515
|
if (codeObj !== undefined) {
|
|
516
|
+
attributes["__code_obj"] = codeObj;
|
|
391
517
|
attributes["number"] = codeObj.number;
|
|
392
518
|
if (codeObj.prefix !== undefined)
|
|
393
519
|
attributes["prefix"] = codeObj.prefix;
|
|
@@ -460,8 +586,11 @@ class IeeeBuilder {
|
|
|
460
586
|
if (fdraft !== undefined && typeof fdraft === "object" && !Array.isArray(fdraft)) {
|
|
461
587
|
const fdStage = extractValue(fdraft["fdraft_stage"]);
|
|
462
588
|
if (fdStage !== undefined) {
|
|
463
|
-
|
|
464
|
-
|
|
589
|
+
// fdraft's month/year rename with the fdraft_ prefix on the
|
|
590
|
+
// edition-2 grammar (nested-only); the v1 spellings stay as the
|
|
591
|
+
// fallback.
|
|
592
|
+
const fdYear = extractValue((fdraft["fdraft_year"] ?? fdraft["year"]));
|
|
593
|
+
const fdMonth = extractValue((fdraft["fdraft_month"] ?? fdraft["month"]));
|
|
465
594
|
let date = "";
|
|
466
595
|
if (fdYear !== undefined) {
|
|
467
596
|
date = `-${fdYear}`;
|
|
@@ -597,10 +726,16 @@ class IeeeBuilder {
|
|
|
597
726
|
if (version !== undefined)
|
|
598
727
|
version = version.replace(/^-/, "").replace(/^0(?=\d+\.\d)/, "");
|
|
599
728
|
}
|
|
600
|
-
// The
|
|
601
|
-
//
|
|
602
|
-
//
|
|
603
|
-
|
|
729
|
+
// The dotted ordinal stays part of the designator (the #601
|
|
730
|
+
// convergence): a split field would make the parse and from_hash
|
|
731
|
+
// paths build different Draft objects on wire-identical rows.
|
|
732
|
+
// #to_s renders version + revision identically, so the whole tail
|
|
733
|
+
// rides the version on every route.
|
|
734
|
+
if (draftData["revision"] !== undefined && version !== undefined) {
|
|
735
|
+
const tail = extractValue(draftData["revision"])?.replace(/^\./, "");
|
|
736
|
+
if (tail !== undefined && tail !== "")
|
|
737
|
+
version = `${version}.${tail}`;
|
|
738
|
+
}
|
|
604
739
|
isoStage = extractValue(draftData["draft_iso_stage"]);
|
|
605
740
|
isoIteration = extractValue(draftData["draft_iso_iteration"]);
|
|
606
741
|
// FDIS/CDV/… are ISO/IEC stage tokens, not draft ordinals (C1
|
|
@@ -610,8 +745,15 @@ class IeeeBuilder {
|
|
|
610
745
|
// Draft#to_s renders as the D= designator face ("…/D=DIS-2020").
|
|
611
746
|
// The glued month-name date ("-June2010", the 2026-10-19 FCD
|
|
612
747
|
// ruling) rides the designator's date slot with the month numeric.
|
|
613
|
-
|
|
614
|
-
|
|
748
|
+
// The edition-2 grammar renames draft_date's nested month/year/day
|
|
749
|
+
// captures with the draft_date_ prefix (kept off the identity
|
|
750
|
+
// keys); the v1 spellings stay as the fallback.
|
|
751
|
+
const monthSlice = draftData["draft_date_month"] ?? draftData["month"];
|
|
752
|
+
month = extractValue(monthSlice);
|
|
753
|
+
const yearSlice = draftData["draft_date_year"] ?? draftData["year"];
|
|
754
|
+
year = yearSlice !== undefined ? extractValue(yearSlice) : undefined;
|
|
755
|
+
const daySlice = draftData["draft_date_day"] ?? draftData["day"];
|
|
756
|
+
day = daySlice !== undefined ? extractValue(daySlice) : undefined;
|
|
615
757
|
if (version !== undefined) {
|
|
616
758
|
// The stage iteration rides glued onto the word ("DIS2" — the
|
|
617
759
|
// lossless face, pubid#477; the 2026-10-19 DIS2 ruling).
|
|
@@ -631,7 +773,6 @@ class IeeeBuilder {
|
|
|
631
773
|
if (year !== undefined && /^\d{2}$/.test(year)) {
|
|
632
774
|
year = `${Number(year) < 30 ? 20 : 19}${year}`;
|
|
633
775
|
}
|
|
634
|
-
day = extractValue(draftData["day"]);
|
|
635
776
|
if (this.originalInput !== "" && month !== undefined) {
|
|
636
777
|
commaBeforeMonth = new RegExp(`,\\s+${month.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i")
|
|
637
778
|
.test(this.originalInput);
|
|
@@ -656,7 +797,8 @@ class IeeeBuilder {
|
|
|
656
797
|
// with the month canonicalized numeric (pubid ruling 2026-10-14):
|
|
657
798
|
// "P802.11-REVmc/D8.0, Aug 2016" renders ".../D8.0-2016-08".
|
|
658
799
|
// Drafts without a REV keep the comma face.
|
|
659
|
-
if ((attributes["revision"] !== undefined || attributes["rev_bare"] === true
|
|
800
|
+
if ((attributes["revision"] !== undefined || attributes["rev_bare"] === true ||
|
|
801
|
+
attributes["cor_number"] !== undefined || attributes["cor_year"] !== undefined) &&
|
|
660
802
|
month !== undefined && year !== undefined && version !== undefined) {
|
|
661
803
|
const monthNumber = DRAFT_MONTH_NAMES[String(month).replace(/\.$/, "")];
|
|
662
804
|
if (monthNumber !== undefined) {
|
|
@@ -753,7 +895,13 @@ class IeeeBuilder {
|
|
|
753
895
|
}
|
|
754
896
|
handleReaffirmed(parsed, attributes) {
|
|
755
897
|
if (isObj(parsed["reaffirmed"])) {
|
|
756
|
-
|
|
898
|
+
// The edition-2 grammar renames the nested year capture
|
|
899
|
+
// (reaffirmed_year) so the reaffirmation year cannot leak onto the
|
|
900
|
+
// identity year key; the v1 spelling stays as the fallback.
|
|
901
|
+
const reaf = parsed["reaffirmed"];
|
|
902
|
+
const value = reaf["reaffirmed_year"] ?? reaf["year"];
|
|
903
|
+
if (value !== undefined)
|
|
904
|
+
attributes["reaffirmed"] = extractValue(value);
|
|
757
905
|
}
|
|
758
906
|
else if (parsed["reaffirmed"] !== undefined) {
|
|
759
907
|
attributes["reaffirmed"] = extractValue(parsed["reaffirmed"]);
|
|
@@ -823,6 +971,9 @@ class IeeeBuilder {
|
|
|
823
971
|
}
|
|
824
972
|
// --- supplements and wrappers ----------------------------------------------
|
|
825
973
|
reconstructBaseString(baseData) {
|
|
974
|
+
// The part/subpart/year captures sit one level down, inside the raw
|
|
975
|
+
// part_subpart_year wrapper — hoist them before the flat reads.
|
|
976
|
+
this.unwrapPartSubpartYear(baseData);
|
|
826
977
|
const baseParts = [];
|
|
827
978
|
if (baseData["publishers"] !== undefined) {
|
|
828
979
|
const pubData = asHash(baseData["publishers"]);
|
|
@@ -981,6 +1132,7 @@ class IeeeBuilder {
|
|
|
981
1132
|
if (codeStr !== undefined) {
|
|
982
1133
|
const codeObj = IeeeCode.parse(codeStr);
|
|
983
1134
|
if (codeObj !== undefined) {
|
|
1135
|
+
attributes["__code_obj"] = codeObj;
|
|
984
1136
|
attributes["number"] = codeObj.number;
|
|
985
1137
|
if (codeObj.prefix !== undefined)
|
|
986
1138
|
attributes["prefix"] = codeObj.prefix;
|
|
@@ -1208,7 +1360,11 @@ class IeeeBuilder {
|
|
|
1208
1360
|
attributes["project_marker"] = true;
|
|
1209
1361
|
parsed["project_marker"] = "P";
|
|
1210
1362
|
}
|
|
1211
|
-
if (
|
|
1363
|
+
if (
|
|
1364
|
+
// The bound tree spreads the raw iso_published interior flat, so a
|
|
1365
|
+
// stage-first joint row carries a top-level iso_stage — the stage
|
|
1366
|
+
// word keeps the joint ISO face (the reference's iso_stage.nil? gate).
|
|
1367
|
+
parsed["iso_published"] !== undefined && parsed["iso_stage"] === undefined && !pWithDraft &&
|
|
1212
1368
|
(parsed["printed_dash_year"] !== undefined || parsed["printed_month_year"] !== undefined ||
|
|
1213
1369
|
(numericDraft && parsed["draft_month"] !== undefined) ||
|
|
1214
1370
|
(parsed["year"] === undefined && parsed["parameters"] !== undefined &&
|
|
@@ -1260,6 +1416,7 @@ class IeeeBuilder {
|
|
|
1260
1416
|
}
|
|
1261
1417
|
const codeObj = IeeeCode.parse(finalCode);
|
|
1262
1418
|
if (codeObj !== undefined) {
|
|
1419
|
+
printedAttrs["__code_obj"] = codeObj;
|
|
1263
1420
|
printedAttrs["number"] = codeObj.number;
|
|
1264
1421
|
if (codeObj.prefix !== undefined)
|
|
1265
1422
|
printedAttrs["prefix"] = codeObj.prefix;
|
|
@@ -1283,7 +1440,11 @@ class IeeeBuilder {
|
|
|
1283
1440
|
if (printedAttrs[k] === undefined)
|
|
1284
1441
|
delete printedAttrs[k];
|
|
1285
1442
|
}
|
|
1286
|
-
|
|
1443
|
+
const printed = new StandardClass(printedAttrs);
|
|
1444
|
+
if (printedAttrs["__code_obj"] instanceof IeeeCode) {
|
|
1445
|
+
printed["__code_obj"] = printedAttrs["__code_obj"];
|
|
1446
|
+
}
|
|
1447
|
+
return printed;
|
|
1287
1448
|
}
|
|
1288
1449
|
// A stage word with a glued year tail ("/D=CD2-201802") rides the
|
|
1289
1450
|
// D= designator with the IEEE lead (the reference's builder, the
|
|
@@ -1434,7 +1595,21 @@ class IeeeBuilder {
|
|
|
1434
1595
|
if (attributes["draft_status"] !== undefined) {
|
|
1435
1596
|
projectAttrs["draft_status"] = attributes["draft_status"];
|
|
1436
1597
|
}
|
|
1437
|
-
|
|
1598
|
+
// Ruby's draft_attrs[:code] = "P" + code re-strips in
|
|
1599
|
+
// ProjectDraftIdentifier#initialize and parses the stripped string —
|
|
1600
|
+
// rebuild the runtime code obj from the P-less columns the same way.
|
|
1601
|
+
const strippedCode = new IeeeCode({
|
|
1602
|
+
number: projectAttrs["number"],
|
|
1603
|
+
prefix: projectAttrs["prefix"],
|
|
1604
|
+
parts: projectAttrs["parts"],
|
|
1605
|
+
original_separator: projectAttrs["separator"],
|
|
1606
|
+
}).render();
|
|
1607
|
+
const strippedObj = IeeeCode.parse(strippedCode);
|
|
1608
|
+
if (strippedObj !== undefined)
|
|
1609
|
+
projectAttrs["__code_obj"] = strippedObj;
|
|
1610
|
+
const projectBuilt = new ProjectDraftIdentifierClass(projectAttrs);
|
|
1611
|
+
projectBuilt["__code_obj"] = strippedObj;
|
|
1612
|
+
return projectBuilt;
|
|
1438
1613
|
}
|
|
1439
1614
|
// The iso_published joint rows normalize the publisher order to
|
|
1440
1615
|
// ISO-first (the reference: "IEEE/ISO/IEC 8802-3:2021/Amd 4-2021"
|
|
@@ -1458,11 +1633,15 @@ class IeeeBuilder {
|
|
|
1458
1633
|
attributes["copublisher"] = attributes["publishers"].slice(1);
|
|
1459
1634
|
}
|
|
1460
1635
|
const joint = new JointDevelopmentClass(attributes);
|
|
1636
|
+
if (attributes["__code_obj"] instanceof IeeeCode) {
|
|
1637
|
+
joint["__code_obj"] = attributes["__code_obj"];
|
|
1638
|
+
}
|
|
1639
|
+
const jointIdentifier = joint;
|
|
1461
1640
|
if (parsed["amd_number"] === undefined)
|
|
1462
|
-
return
|
|
1641
|
+
return jointIdentifier;
|
|
1463
1642
|
const amdYear = parsed["amd_year"] !== undefined ? extractValue(parsed["amd_year"]) : undefined;
|
|
1464
1643
|
return new AmendmentClass({
|
|
1465
|
-
base:
|
|
1644
|
+
base: jointIdentifier,
|
|
1466
1645
|
number: extractValue(parsed["amd_number"]),
|
|
1467
1646
|
year: amdYear,
|
|
1468
1647
|
});
|
|
@@ -1470,8 +1649,19 @@ class IeeeBuilder {
|
|
|
1470
1649
|
buildSiPsi(parsed) {
|
|
1471
1650
|
const attributes = {};
|
|
1472
1651
|
const siType = extractValue(parsed["si_type"]);
|
|
1473
|
-
|
|
1474
|
-
|
|
1652
|
+
// The bare top-level draft clause binds as draft_version_scalar
|
|
1653
|
+
// (string) — surface it under the key this builder reads (the
|
|
1654
|
+
// reference's build_si_psi_identifier).
|
|
1655
|
+
if (parsed["draft_version"] === undefined && parsed["draft_version_scalar"] !== undefined) {
|
|
1656
|
+
parsed["draft_version"] = parsed["draft_version_scalar"];
|
|
1657
|
+
delete parsed["draft_version_scalar"];
|
|
1658
|
+
}
|
|
1659
|
+
// Extract publishers (should be "IEEE/ASTM") — the SI rule's bare
|
|
1660
|
+
// publishers clause binds as publishers_scalar (string); the
|
|
1661
|
+
// composite joint rows deliver publishers (raw).
|
|
1662
|
+
const pubSrc = parsed["publishers"] ?? parsed["publishers_scalar"];
|
|
1663
|
+
if (pubSrc !== undefined) {
|
|
1664
|
+
const publishersStr = extractValue(pubSrc) ?? "";
|
|
1475
1665
|
const pubs = publishersStr.split("/");
|
|
1476
1666
|
attributes["publisher"] = pubs[0];
|
|
1477
1667
|
if (pubs.length > 1)
|
|
@@ -1481,6 +1671,7 @@ class IeeeBuilder {
|
|
|
1481
1671
|
if (codeStr !== undefined) {
|
|
1482
1672
|
const codeObj = IeeeCode.parse(codeStr);
|
|
1483
1673
|
if (codeObj !== undefined) {
|
|
1674
|
+
attributes["__code_obj"] = codeObj;
|
|
1484
1675
|
attributes["number"] = codeObj.number;
|
|
1485
1676
|
if (codeObj.prefix !== undefined)
|
|
1486
1677
|
attributes["prefix"] = codeObj.prefix;
|
|
@@ -1504,7 +1695,11 @@ class IeeeBuilder {
|
|
|
1504
1695
|
attributes["relationships"] = this.buildRelationships(parsed);
|
|
1505
1696
|
}
|
|
1506
1697
|
this.handleParameters(parsed, attributes);
|
|
1507
|
-
|
|
1698
|
+
const siBuilt = new SiStandardClass(attributes);
|
|
1699
|
+
if (attributes["__code_obj"] instanceof IeeeCode) {
|
|
1700
|
+
siBuilt["__code_obj"] = attributes["__code_obj"];
|
|
1701
|
+
}
|
|
1702
|
+
return siBuilt;
|
|
1508
1703
|
}
|
|
1509
1704
|
buildMultiNumbered(parsed) {
|
|
1510
1705
|
const primaryId = this.buildSingleIdentifier(asHash(parsed["primary_identifier"]), false);
|
|
@@ -1661,6 +1856,7 @@ class IeeeBuilder {
|
|
|
1661
1856
|
if (codeStr !== undefined) {
|
|
1662
1857
|
const codeObj = IeeeCode.parse(codeStr);
|
|
1663
1858
|
if (codeObj !== undefined) {
|
|
1859
|
+
attributes["__code_obj"] = codeObj;
|
|
1664
1860
|
attributes["number"] = codeObj.number;
|
|
1665
1861
|
if (codeObj.prefix !== undefined)
|
|
1666
1862
|
attributes["prefix"] = codeObj.prefix;
|
|
@@ -1678,7 +1874,11 @@ class IeeeBuilder {
|
|
|
1678
1874
|
if (separatorStr !== undefined)
|
|
1679
1875
|
attributes["date_separator"] = separatorStr;
|
|
1680
1876
|
attributes["original_format"] = separatorStr !== undefined || monthStr !== undefined ? "long" : "short";
|
|
1681
|
-
|
|
1877
|
+
const aieeBuilt = new AieeClass(attributes);
|
|
1878
|
+
if (attributes["__code_obj"] instanceof IeeeCode) {
|
|
1879
|
+
aieeBuilt["__code_obj"] = attributes["__code_obj"];
|
|
1880
|
+
}
|
|
1881
|
+
return aieeBuilt;
|
|
1682
1882
|
}
|
|
1683
1883
|
buildIre(parsed) {
|
|
1684
1884
|
const attributes = {};
|
|
@@ -1800,7 +2000,15 @@ let cachedArtifactRuntime;
|
|
|
1800
2000
|
function parseSource(input) {
|
|
1801
2001
|
cachedArtifactRuntime ??= PubidRuntime.load("ieee");
|
|
1802
2002
|
try {
|
|
1803
|
-
|
|
2003
|
+
// The edition-2 artifact's bindings block produces the builder-ready
|
|
2004
|
+
// hash at the engine (typed scalars + raw wrappers) — the same bound
|
|
2005
|
+
// tree the reference's Pubid::Parg::Model.bound hands its builder.
|
|
2006
|
+
const bound = cachedArtifactRuntime.parseAndBind(input);
|
|
2007
|
+
// A rejected input binds to null (the parseShape contract threw) —
|
|
2008
|
+
// keep the throw so the reference's ParseFailed-keyed fallbacks fire.
|
|
2009
|
+
if (bound === null || bound === undefined)
|
|
2010
|
+
throw new ParseFailed("no match", 0);
|
|
2011
|
+
return artifactToBuilderTree(bound);
|
|
1804
2012
|
}
|
|
1805
2013
|
catch (e) {
|
|
1806
2014
|
// The engine throws its own error class; the reference's fallback
|