@pubid/pubid 0.1.8 → 0.1.10
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/cen_cenelec/grammar.js +10 -4
- package/dist/flavors/cen_cenelec/index.js +20 -1
- package/dist/flavors/cen_cenelec/model.d.ts +1 -0
- package/dist/flavors/cen_cenelec/model.js +4 -1
- package/dist/flavors/evs/grammar.js +53 -7
- package/dist/flavors/evs/index.js +74 -7
- package/dist/flavors/evs/model.d.ts +37 -1
- package/dist/flavors/evs/model.js +173 -11
- package/dist/flavors/ieee/artifact_tree.d.ts +2 -0
- package/dist/flavors/ieee/artifact_tree.js +36 -0
- package/dist/flavors/ieee/grammar.js +99 -19
- package/dist/flavors/ieee/index.js +353 -18
- package/dist/flavors/ieee/model.d.ts +5 -0
- package/dist/flavors/ieee/model.js +132 -20
- package/dist/flavors/ieee/preprocessor.js +28 -6
- package/dist/flavors/nist/grammar.js +18 -9
- package/dist/flavors/nist/index.js +16 -7
- package/dist/flavors/nist/preprocessor.js +9 -1
- package/dist/parg/runtime.d.ts +25 -0
- package/dist/parg/runtime.js +76 -0
- package/dist/parg/suite.d.ts +4 -0
- package/dist/parg/suite.js +14 -0
- package/dist/urn/parsers.js +7 -0
- package/package.json +6 -4
- package/vendor/parg/adobe.json +1 -0
- package/vendor/parg/aiee.json +1 -0
- package/vendor/parg/amca.json +1 -0
- package/vendor/parg/ansi.json +1 -0
- package/vendor/parg/api.json +1 -0
- package/vendor/parg/ashrae.json +1 -0
- package/vendor/parg/asme.json +1 -0
- package/vendor/parg/astm.json +1 -0
- package/vendor/parg/bipm.json +1 -0
- package/vendor/parg/bsi.json +1 -0
- package/vendor/parg/calconnect.json +1 -0
- package/vendor/parg/ccsds.json +1 -0
- package/vendor/parg/cen_cenelec.json +1 -0
- package/vendor/parg/cie.json +1 -0
- package/vendor/parg/csa.json +1 -0
- package/vendor/parg/doi.json +1 -0
- package/vendor/parg/easc.json +1 -0
- package/vendor/parg/ecma.json +1 -0
- package/vendor/parg/etsi.json +1 -0
- package/vendor/parg/evs.json +1 -0
- package/vendor/parg/gb.json +1 -0
- package/vendor/parg/gost.json +1 -0
- package/vendor/parg/iala.json +1 -0
- package/vendor/parg/iana.json +1 -0
- package/vendor/parg/idf.json +1 -0
- package/vendor/parg/iec.json +1 -0
- package/vendor/parg/ieee.json +1 -0
- package/vendor/parg/ietf.json +1 -0
- package/vendor/parg/ire.json +1 -0
- package/vendor/parg/isbn.json +1 -0
- package/vendor/parg/iso.json +1 -0
- package/vendor/parg/itu.json +1 -0
- package/vendor/parg/jcgm.json +1 -0
- package/vendor/parg/jis.json +1 -0
- package/vendor/parg/nesc.json +1 -0
- package/vendor/parg/nist.json +1 -0
- package/vendor/parg/oasis.json +1 -0
- package/vendor/parg/ogc.json +1 -0
- package/vendor/parg/oiml.json +1 -0
- package/vendor/parg/omg.json +1 -0
- package/vendor/parg/parg.json +1 -0
- package/vendor/parg/plateau.json +1 -0
- package/vendor/parg/sae.json +1 -0
- package/vendor/parg/tgpp.json +1 -0
- package/vendor/parg/un.json +1 -0
- package/vendor/parg/w3c.json +1 -0
- package/vendor/parg/xsf.json +1 -0
- package/dist/pg/runtime.d.ts +0 -85
- package/dist/pg/runtime.js +0 -170
- package/dist/pg/suite.d.ts +0 -20
- package/dist/pg/suite.js +0 -90
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { ParseFailed, parseGrammar } from "../../grammar/engine.js";
|
|
2
|
+
import { PubidRuntime } from "../../parg/runtime.js";
|
|
3
|
+
import { artifactToBuilderTree } from "./artifact_tree.js";
|
|
2
4
|
import { grammarImplementation } from "../index.js";
|
|
3
5
|
import { ieeeGrammar } from "./grammar.js";
|
|
4
6
|
import { preParse, preprocessIeee } from "./preprocessor.js";
|
|
5
7
|
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";
|
|
8
|
+
import { AdoptedStandardClass, AieeClass, AmendmentClass, ConformanceClass, CorrigendumClass, CsaDualPublishedClass, DualPublishedClass, IecIeeeCopublishedClass, IeeeCode, IeeeDraft, IeeeRelationship, IeeeTypedStage, InterpretationClass, IreClass, JointDevelopmentClass, MultiNumberedClass, NescDraftClass, NescEditionClass, NescHandbookClass, NescPreprintClass, NescRedlineClass, NescStandardClass, DRAFT_MONTH_NAMES, ProjectDraftIdentifierClass, SiStandardClass, StandardClass, locateStage, } from "./model.js";
|
|
7
9
|
/**
|
|
8
10
|
* Port of lib/pubid/ieee/{builder}.rb + Identifier.parse orchestration
|
|
9
11
|
* (pre-parser dispatch, adopted/dual constructors, sub-builders).
|
|
@@ -34,6 +36,7 @@ class IeeeBuilder {
|
|
|
34
36
|
originalInput = "";
|
|
35
37
|
build(parsed) {
|
|
36
38
|
const d = Array.isArray(parsed) ? mergeParsedArray(parsed) : parsed;
|
|
39
|
+
this.normalizeDraftDashYear(d);
|
|
37
40
|
if (d["ieee_portion"] !== undefined && d["csa_portion"] !== undefined) {
|
|
38
41
|
return this.buildCsaDualPublished(d);
|
|
39
42
|
}
|
|
@@ -55,6 +58,117 @@ class IeeeBuilder {
|
|
|
55
58
|
return this.buildSingleIdentifier(d, false);
|
|
56
59
|
}
|
|
57
60
|
// --- single identifier -----------------------------------------------------
|
|
61
|
+
// The artifact binds a dash-year draft tail as a second draft element
|
|
62
|
+
// ({draft_version: {dash_year: ...}}); the reference's builder hoists
|
|
63
|
+
// the dash fields and rejoins the version ("D9-2006", "/DPub2-2012").
|
|
64
|
+
normalizeDraftDashYear(d) {
|
|
65
|
+
const raw = d["draft"];
|
|
66
|
+
if (raw === undefined)
|
|
67
|
+
return;
|
|
68
|
+
const drafts = Array.isArray(raw) ? raw : [raw];
|
|
69
|
+
// The compound-stage captures nest inside the draft elements (the
|
|
70
|
+
// artifact's binding); the hand mirror bound them top-level, so the
|
|
71
|
+
// joint-designator block reads them from `parsed` directly.
|
|
72
|
+
for (const key of ["draft_iso_stage", "draft_iso_iteration", "draft_stage_d", "draft_stage_year"]) {
|
|
73
|
+
if (d[key] === undefined) {
|
|
74
|
+
for (const e of drafts) {
|
|
75
|
+
if (isObj(e) && e[key] !== undefined) {
|
|
76
|
+
d[key] = e[key];
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (const e of drafts) {
|
|
83
|
+
if (!isObj(e))
|
|
84
|
+
continue;
|
|
85
|
+
const dv = e["draft_version"];
|
|
86
|
+
if (!isObj(dv))
|
|
87
|
+
continue;
|
|
88
|
+
if (dv["dash_year"] !== undefined)
|
|
89
|
+
e["dash_year"] = dv["dash_year"];
|
|
90
|
+
if (dv["dash_month"] !== undefined)
|
|
91
|
+
e["dash_month"] = dv["dash_month"];
|
|
92
|
+
delete e["draft_version"];
|
|
93
|
+
}
|
|
94
|
+
// A numeric month trailing a draft is the draft's own date: it embeds
|
|
95
|
+
// in the designator ("D09, 04 2011" -> "D09-2011-04") rather than
|
|
96
|
+
// printing the long-format comma face (a month name keeps the comma,
|
|
97
|
+
// "D7.2, Nov 2006") - pubid ruling 2026-10-13.
|
|
98
|
+
if (d["dash_year"] === undefined && d["draft"] !== undefined) {
|
|
99
|
+
const m = d["month"] ?? d["trailing_month"];
|
|
100
|
+
const y = d["year"] ?? d["trailing_year"];
|
|
101
|
+
const mVal = extractValue(m);
|
|
102
|
+
const yVal = extractValue(y);
|
|
103
|
+
if (mVal !== undefined && /^\d{1,2}$/.test(String(mVal)) && yVal !== undefined) {
|
|
104
|
+
const host = drafts.find((e) => isObj(e) && e["draft_version"] !== undefined);
|
|
105
|
+
if (host !== undefined && isObj(host)) {
|
|
106
|
+
const hv = host["draft_version"];
|
|
107
|
+
const joined = Array.isArray(hv)
|
|
108
|
+
? hv.map((v) => (isObj(v) ? (v["value"] ?? "") : String(v))).join("")
|
|
109
|
+
: String(extractValue(hv) ?? "");
|
|
110
|
+
host["draft_version"] = [joined, String(yVal), String(mVal)]
|
|
111
|
+
.filter((s) => s !== "")
|
|
112
|
+
.join("-");
|
|
113
|
+
delete d["month"];
|
|
114
|
+
delete d["year"];
|
|
115
|
+
delete d["trailing_month"];
|
|
116
|
+
delete d["trailing_year"];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const dash = drafts.find((e) => isObj(e) && e["dash_year"] !== undefined) ??
|
|
121
|
+
(d["dash_year"] !== undefined ? d : undefined);
|
|
122
|
+
if (dash === undefined)
|
|
123
|
+
return;
|
|
124
|
+
const objs = drafts.filter(isObj);
|
|
125
|
+
const joined = objs
|
|
126
|
+
.map((e) => e["draft_version"])
|
|
127
|
+
.filter((v) => v !== undefined)
|
|
128
|
+
.map((v) => (isObj(v) ? (v["value"] ?? "") : String(v)))
|
|
129
|
+
.join("");
|
|
130
|
+
const bareJointDraft = d["joint_publishers"] !== undefined &&
|
|
131
|
+
d["part"] === undefined && d["iso_stage"] === undefined &&
|
|
132
|
+
objs.some((e) => e["draft_version"] !== undefined);
|
|
133
|
+
const dashLed = joined.startsWith("-");
|
|
134
|
+
const dashYear = extractValue(dash["dash_year"]);
|
|
135
|
+
const dashMonth = dash["dash_month"];
|
|
136
|
+
if (bareJointDraft && dashLed) {
|
|
137
|
+
d["part"] = dashYear;
|
|
138
|
+
}
|
|
139
|
+
else if (dashMonth !== undefined || dashLed) {
|
|
140
|
+
// A dash_month beside a draft host is the draft's own date
|
|
141
|
+
// ("/R-i-2011-04" -> "D09-2011-04"); with no host it is the
|
|
142
|
+
// identifier's year/month.
|
|
143
|
+
const host = objs.find((e) => e["draft_version"] !== undefined);
|
|
144
|
+
const dm = extractValue(dashMonth);
|
|
145
|
+
if (dm !== undefined && host !== undefined) {
|
|
146
|
+
const hv = host["draft_version"];
|
|
147
|
+
const joined = Array.isArray(hv)
|
|
148
|
+
? hv.map((v) => (isObj(v) ? (v["value"] ?? "") : String(v))).join("")
|
|
149
|
+
: String(extractValue(hv) ?? "");
|
|
150
|
+
host["draft_version"] = [joined, dashYear, String(dm)].join("-");
|
|
151
|
+
delete dash["dash_year"];
|
|
152
|
+
delete dash["dash_month"];
|
|
153
|
+
}
|
|
154
|
+
else if (d["year"] === undefined) {
|
|
155
|
+
d["year"] = dashYear;
|
|
156
|
+
d["month"] = dm;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const host = objs.find((e) => e["draft_version"] !== undefined);
|
|
161
|
+
if (host !== undefined) {
|
|
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
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
58
172
|
buildSingleIdentifier(parsed, buildingSecondary) {
|
|
59
173
|
const parsedHash = asHash(parsed);
|
|
60
174
|
if (!buildingSecondary && parsedHash["primary_identifier"] !== undefined &&
|
|
@@ -98,6 +212,27 @@ class IeeeBuilder {
|
|
|
98
212
|
if (parsedHash["joint_publishers"] !== undefined || parsedHash["iso_stage"] !== undefined) {
|
|
99
213
|
return this.buildJointDevelopment(parsedHash);
|
|
100
214
|
}
|
|
215
|
+
// The ORDINAL-LESS D= designator on a multi-publisher row is the
|
|
216
|
+
// joint compound form ("ISO/IEC/IEEE P16326:2017/D=WD.4a"); the
|
|
217
|
+
// ordinal compound ("D8=DIS.3") is a project draft whatever the
|
|
218
|
+
// publishers — the D-ordinal is the draft, the stage the target.
|
|
219
|
+
if (parsedHash["draft_iso_stage"] !== undefined) {
|
|
220
|
+
const draftHash = parsedHash["draft"] !== undefined
|
|
221
|
+
? asHash(parsedHash["draft"])
|
|
222
|
+
: undefined;
|
|
223
|
+
const hasOrdinal = extractValue(parsedHash["draft_version"]) !== undefined ||
|
|
224
|
+
extractValue(draftHash?.["draft_version"]) !== undefined;
|
|
225
|
+
if (!hasOrdinal) {
|
|
226
|
+
const pubData = parsedHash["publishers"] !== undefined
|
|
227
|
+
? asHash(parsedHash["publishers"])
|
|
228
|
+
: undefined;
|
|
229
|
+
const copubs = pubData?.["copublishers"] !== undefined
|
|
230
|
+
? (Array.isArray(pubData["copublishers"]) ? pubData["copublishers"] : [pubData["copublishers"]])
|
|
231
|
+
: [];
|
|
232
|
+
if (copubs.length > 0)
|
|
233
|
+
return this.buildJointDevelopment(parsedHash);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
101
236
|
const attributes = this.extractAttributes(parsedHash);
|
|
102
237
|
if (parsedHash["relationship_type"] !== undefined || parsedHash["relationship_clause"] !== undefined) {
|
|
103
238
|
attributes["relationships"] = this.buildRelationships(parsedHash);
|
|
@@ -215,7 +350,12 @@ class IeeeBuilder {
|
|
|
215
350
|
codeStr = codeStr.replace(/^P/, "");
|
|
216
351
|
}
|
|
217
352
|
if (codeStr !== undefined && codeParts.length > 0) {
|
|
218
|
-
|
|
353
|
+
// Captures carry their own separator when present (the artifact's
|
|
354
|
+
// part/subpart bind ".18" / "-10"); bare tokens fall back to the
|
|
355
|
+
// hand grammar's part_sep. Joins verbatim like the reference's
|
|
356
|
+
// extract_attributes — a dashed "62271-37-082" stays dashed.
|
|
357
|
+
const partSep = extractValue(parsed["part_sep"]) ?? ".";
|
|
358
|
+
codeStr += codeParts.map((cp) => /^[._-]/.test(cp) ? cp : `${partSep}${cp}`).join("");
|
|
219
359
|
}
|
|
220
360
|
if (codeStr !== undefined && parsed["year"] === undefined && codeParts.length === 0) {
|
|
221
361
|
const m = /^(.+)-(\d{4})$/.exec(codeStr);
|
|
@@ -261,7 +401,15 @@ class IeeeBuilder {
|
|
|
261
401
|
/^IEEE\s/.test(this.originalInput)) {
|
|
262
402
|
typeValue = "Std";
|
|
263
403
|
}
|
|
264
|
-
|
|
404
|
+
// The canonical type word is the short form (C2 ruling):
|
|
405
|
+
// "IEEE Standard 495-2007" and "IEEE Std 495-2007" are the same
|
|
406
|
+
// identifier. A DRAFT type word never reaches the wire (the
|
|
407
|
+
// draft-ness rides draft_status + the draft component; "Draft Std"
|
|
408
|
+
// is banned from canonical output) - it still feeds
|
|
409
|
+
// determineStageAbbr below (the version-less D1 fabrication).
|
|
410
|
+
if (typeValue === "Standard")
|
|
411
|
+
typeValue = "Std";
|
|
412
|
+
if (typeValue !== undefined && !/^Draft/.test(typeValue))
|
|
265
413
|
attributes["type"] = typeValue;
|
|
266
414
|
const typedStageAbbr = this.determineStageAbbr(typeValue, parsed);
|
|
267
415
|
if (typedStageAbbr !== undefined) {
|
|
@@ -270,11 +418,13 @@ class IeeeBuilder {
|
|
|
270
418
|
attributes["typed_stage"] = new IeeeTypedStage(record);
|
|
271
419
|
}
|
|
272
420
|
attributes["draft_status"] = draftStatusValue;
|
|
273
|
-
for (const key of ["edition", "revision"]) {
|
|
421
|
+
for (const key of ["edition", "revision", "rev_sep"]) {
|
|
274
422
|
const value = extractValue(parsed[key]);
|
|
275
423
|
if (value !== undefined)
|
|
276
424
|
attributes[key] = value;
|
|
277
425
|
}
|
|
426
|
+
if (parsed["rev_bare"] !== undefined)
|
|
427
|
+
attributes["rev_bare"] = true;
|
|
278
428
|
if (attributes["edition_month"] === undefined) {
|
|
279
429
|
const month = extractValue(parsed["month"]);
|
|
280
430
|
if (month !== undefined)
|
|
@@ -299,6 +449,13 @@ class IeeeBuilder {
|
|
|
299
449
|
attributes["interpretation"] = true;
|
|
300
450
|
if (parsed["redline"] !== undefined)
|
|
301
451
|
attributes["redline"] = true;
|
|
452
|
+
// Glued identity year-month provenance (gem#520): the input tail is
|
|
453
|
+
// the glued "-YYYY-MM" on a draft-free, revision-free identity.
|
|
454
|
+
if (attributes["month"] !== undefined && attributes["year"] !== undefined &&
|
|
455
|
+
attributes["draft"] === undefined && attributes["revision"] === undefined &&
|
|
456
|
+
/-\d{4}-\d{2}$/.test(this.originalInput ?? "")) {
|
|
457
|
+
attributes["month_glued"] = true;
|
|
458
|
+
}
|
|
302
459
|
return attributes;
|
|
303
460
|
}
|
|
304
461
|
determineStageAbbr(typeValue, parsed) {
|
|
@@ -377,6 +534,12 @@ class IeeeBuilder {
|
|
|
377
534
|
isoIteration = extractValue(draftData["draft_iso_iteration"]);
|
|
378
535
|
month = extractValue(draftData["month"]);
|
|
379
536
|
year = extractValue(draftData["year"]);
|
|
537
|
+
// The apostrophe-year catalogue convention ("P1484.20.1/D7,
|
|
538
|
+
// May 07"): the 2-digit year expands with the <30 pivot so the
|
|
539
|
+
// hash carries an unambiguous year (the reference's builder).
|
|
540
|
+
if (year !== undefined && /^\d{2}$/.test(year)) {
|
|
541
|
+
year = `${Number(year) < 30 ? 20 : 19}${year}`;
|
|
542
|
+
}
|
|
380
543
|
day = extractValue(draftData["day"]);
|
|
381
544
|
if (this.originalInput !== "" && month !== undefined) {
|
|
382
545
|
commaBeforeMonth = new RegExp(`,\\s+${month.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i")
|
|
@@ -390,6 +553,20 @@ class IeeeBuilder {
|
|
|
390
553
|
else {
|
|
391
554
|
version = extractValue(draftData);
|
|
392
555
|
}
|
|
556
|
+
// A REV-carrying draft embeds its print date into the designator
|
|
557
|
+
// with the month canonicalized numeric (pubid ruling 2026-10-14):
|
|
558
|
+
// "P802.11-REVmc/D8.0, Aug 2016" renders ".../D8.0-2016-08".
|
|
559
|
+
// Drafts without a REV keep the comma face.
|
|
560
|
+
if ((attributes["revision"] !== undefined || attributes["rev_bare"] === true) &&
|
|
561
|
+
month !== undefined && year !== undefined && version !== undefined) {
|
|
562
|
+
const monthNumber = DRAFT_MONTH_NAMES[String(month).replace(/\.$/, "")];
|
|
563
|
+
if (monthNumber !== undefined) {
|
|
564
|
+
// The canonical face zero-pads ("D8.0-2016-08"), matching the
|
|
565
|
+
// reference's REV_MONTH_NUMBERS.
|
|
566
|
+
version = `${version}-${year}-${monthNumber.padStart(2, "0")}`;
|
|
567
|
+
month = year = day = undefined;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
393
570
|
if (version !== undefined || (isObj(draftData) && draftData["draft_iso_stage"] !== undefined)) {
|
|
394
571
|
const draftObj = new IeeeDraft({
|
|
395
572
|
version,
|
|
@@ -409,7 +586,11 @@ class IeeeBuilder {
|
|
|
409
586
|
return;
|
|
410
587
|
if (typeValue !== "Draft Std")
|
|
411
588
|
return;
|
|
412
|
-
|
|
589
|
+
// The reference stores the fabricated designator comma-joined with the
|
|
590
|
+
// identity year ("D1, 2012" — the gem's Draft#to_s), not space-joined.
|
|
591
|
+
attributes["draft"] = attributes["year"] === undefined
|
|
592
|
+
? "D1"
|
|
593
|
+
: `D1, ${attributes["year"]}`;
|
|
413
594
|
}
|
|
414
595
|
handleKeyedHash(parsed, key, attributes, numberKey, yearKey) {
|
|
415
596
|
if (isObj(parsed[key])) {
|
|
@@ -526,15 +707,17 @@ class IeeeBuilder {
|
|
|
526
707
|
baseParts.push(extractValue(baseData["type"]) ?? "");
|
|
527
708
|
}
|
|
528
709
|
let numberStr = extractValue(baseData["number"]) ?? "";
|
|
710
|
+
const baseSep = extractValue(baseData["part_sep"]) ?? ".";
|
|
529
711
|
if (baseData["part"] !== undefined) {
|
|
530
|
-
|
|
712
|
+
const pv = extractValue(baseData["part"]) ?? "";
|
|
713
|
+
numberStr += /^[._-]/.test(pv) ? pv : `${baseSep}${pv}`;
|
|
531
714
|
}
|
|
532
715
|
if (baseData["subpart"] !== undefined) {
|
|
533
716
|
const subparts = Array.isArray(baseData["subpart"]) ? baseData["subpart"] : [baseData["subpart"]];
|
|
534
717
|
for (const sp of subparts) {
|
|
535
718
|
const v = extractValue(sp);
|
|
536
719
|
if (v !== undefined)
|
|
537
|
-
numberStr +=
|
|
720
|
+
numberStr += /^[._-]/.test(v) ? v : `${baseSep}${v}`;
|
|
538
721
|
}
|
|
539
722
|
}
|
|
540
723
|
if (baseData["year"] !== undefined) {
|
|
@@ -618,15 +801,33 @@ class IeeeBuilder {
|
|
|
618
801
|
attributes["publisher"] = pubs[0];
|
|
619
802
|
attributes["copublisher"] = pubs.slice(1);
|
|
620
803
|
}
|
|
804
|
+
else if (parsed["publishers"] !== undefined) {
|
|
805
|
+
// Stage-bearing rows the artifact routes without a
|
|
806
|
+
// joint_publishers capture ("ISO/IEC/IEEE P26511/D8=DDIS.3"):
|
|
807
|
+
// the publishers hash carries the same information.
|
|
808
|
+
const pubData = asHash(parsed["publishers"]);
|
|
809
|
+
const lead = extractValue(pubData["publisher"]) ?? "";
|
|
810
|
+
const copubs = pubData["copublishers"] !== undefined
|
|
811
|
+
? (Array.isArray(pubData["copublishers"])
|
|
812
|
+
? pubData["copublishers"]
|
|
813
|
+
: [pubData["copublishers"]])
|
|
814
|
+
.map((cp) => extractValue(asHash(cp)["copublisher"]))
|
|
815
|
+
.filter((cp) => cp !== undefined)
|
|
816
|
+
: [];
|
|
817
|
+
attributes["publishers"] = [lead, ...copubs];
|
|
818
|
+
attributes["publisher"] = lead;
|
|
819
|
+
attributes["copublisher"] = copubs;
|
|
820
|
+
}
|
|
621
821
|
const codeParts = [];
|
|
622
822
|
if (parsed["part"] !== undefined)
|
|
623
823
|
codeParts.push(extractValue(parsed["part"]) ?? "");
|
|
624
824
|
let codeStr = extractValue(parsed["number"]);
|
|
625
825
|
if (codeStr !== undefined && codeParts.length > 0) {
|
|
626
826
|
// The ISO/IEC label's part may be dash-joined ("8802-9") - keep the
|
|
627
|
-
// printed separator.
|
|
827
|
+
// printed separator. Separator-carrying captures (the artifact's
|
|
828
|
+
// part binds ".2") join verbatim.
|
|
628
829
|
const sep = parsed["iec_label_dash"] !== undefined ? "-" : ".";
|
|
629
|
-
codeStr += `${sep}${
|
|
830
|
+
codeStr += codeParts.map((cp) => /^[._-]/.test(cp) ? cp : `${sep}${cp}`).join("");
|
|
630
831
|
}
|
|
631
832
|
// P = project (a draft): identity-bearing, preserved as spelled.
|
|
632
833
|
if (parsed["project_marker"] !== undefined && codeStr !== undefined) {
|
|
@@ -665,8 +866,69 @@ class IeeeBuilder {
|
|
|
665
866
|
}
|
|
666
867
|
if (parsed["draft_version"] !== undefined) {
|
|
667
868
|
const draftVer = (extractValue(parsed["draft_version"]) ?? "").replace(/^D/, "");
|
|
668
|
-
if (draftVer !== "")
|
|
669
|
-
|
|
869
|
+
if (draftVer !== "") {
|
|
870
|
+
// A date trailing the draft (", June 2010") belongs to the draft
|
|
871
|
+
// component (canonical month, comma form); the identity
|
|
872
|
+
// year/month the promotion filled from the same captures are
|
|
873
|
+
// vacated. Stage-first ISO rows are exempt: their canonical is
|
|
874
|
+
// the ISO face, which prints the date from the identity slots
|
|
875
|
+
// (", March 2017", pubid#216).
|
|
876
|
+
if (parsed["draft_month"] !== undefined && parsed["iso_stage"] === undefined) {
|
|
877
|
+
const dm = extractValue(parsed["draft_month"]) ?? "";
|
|
878
|
+
const dy = extractValue(parsed["draft_year"]) ?? "";
|
|
879
|
+
const monthNum = DRAFT_MONTH_NAMES[dm.replace(/\.$/, "")];
|
|
880
|
+
if (JSON.stringify(attributes["publishers"]) === JSON.stringify(["IEEE", "CSA"]) &&
|
|
881
|
+
monthNum !== undefined && dy !== "") {
|
|
882
|
+
// The joint IEEE/CSA drafts glue their date into the
|
|
883
|
+
// designator with the month numeric (pubid ruling
|
|
884
|
+
// 2026-10-14: no comma dates on these rows).
|
|
885
|
+
const glued = new IeeeDraft({
|
|
886
|
+
version: `${draftVer}-${dy}-${monthNum.padStart(2, "0")}`,
|
|
887
|
+
});
|
|
888
|
+
attributes["draft"] = glued.render();
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
const draftObj = new IeeeDraft({
|
|
892
|
+
version: draftVer,
|
|
893
|
+
month: dm === "" ? undefined : dm,
|
|
894
|
+
year: dy === "" ? undefined : dy,
|
|
895
|
+
comma_before_month: true,
|
|
896
|
+
});
|
|
897
|
+
attributes["draft"] = draftObj.render();
|
|
898
|
+
}
|
|
899
|
+
if (parsed["year"] === undefined)
|
|
900
|
+
delete attributes["year"];
|
|
901
|
+
if (parsed["month"] === undefined)
|
|
902
|
+
delete attributes["month"];
|
|
903
|
+
}
|
|
904
|
+
else if (parsed["draft_year"] !== undefined && parsed["iso_stage"] === undefined) {
|
|
905
|
+
// The comma-year tail captures its separator (", 2013") —
|
|
906
|
+
// strip it (the #471 separator-in-capture rule). The stored
|
|
907
|
+
// form keeps the comma so it re-parses through the same tail.
|
|
908
|
+
const draftYear = (extractValue(parsed["draft_year"]) ?? "").replace(/^[, ]+/, "");
|
|
909
|
+
attributes["draft"] = `/D${draftVer}, ${draftYear}`;
|
|
910
|
+
if (parsed["year"] === undefined)
|
|
911
|
+
delete attributes["year"];
|
|
912
|
+
}
|
|
913
|
+
else if (JSON.stringify(attributes["publishers"]) === JSON.stringify(["IEEE", "CSA"]) &&
|
|
914
|
+
parsed["iso_stage"] === undefined &&
|
|
915
|
+
/^(19|20)\d\d$/.test(extractValue(parsed["year"]) ?? "") &&
|
|
916
|
+
/^\d{1,2}$/.test(extractValue(parsed["month"]) ?? "")) {
|
|
917
|
+
// The glued face re-parses with the date in the identity
|
|
918
|
+
// slots ("D2-2016-09" -> year/month + a bare D2): it
|
|
919
|
+
// normalizes back onto the draft-embedded date so the
|
|
920
|
+
// round trip closes on the CSA canonical.
|
|
921
|
+
const glued = new IeeeDraft({
|
|
922
|
+
version: `${draftVer}-${extractValue(parsed["year"])}-${extractValue(parsed["month"])}`,
|
|
923
|
+
});
|
|
924
|
+
attributes["draft"] = glued.render();
|
|
925
|
+
delete attributes["year"];
|
|
926
|
+
delete attributes["month"];
|
|
927
|
+
}
|
|
928
|
+
else {
|
|
929
|
+
attributes["ieee_draft"] = `D${draftVer}`;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
670
932
|
}
|
|
671
933
|
// The "(E)" edition marker of an ISO/IEC label rides on the joint
|
|
672
934
|
// reference as parenthetical_content - either from the trailing
|
|
@@ -681,21 +943,37 @@ class IeeeBuilder {
|
|
|
681
943
|
if (attributes["parenthetical_content"] === undefined && parsed["edition_marker"] !== undefined) {
|
|
682
944
|
attributes["parenthetical_content"] = extractValue(parsed["edition_marker"]);
|
|
683
945
|
}
|
|
946
|
+
// A joint ISO-print redline is a distinct document of the same standard
|
|
947
|
+
// (pubid#215) - same flag semantics as the generic path.
|
|
948
|
+
if (parsed["redline"] !== undefined)
|
|
949
|
+
attributes["redline"] = true;
|
|
684
950
|
// The joint stage-draft clause (docs/IEEE-DRAFT-STAGES.md §1.3):
|
|
685
951
|
// variant 1's compound tail composes onto the IEEE ordinal
|
|
686
952
|
// ("D5=DIS.3"); variant 1b is the ordinal-less stage draft whose
|
|
687
953
|
// date rides inside the designator ("D=CDV:2020"). The doubled-D
|
|
688
954
|
// alias spelling echoes as spelled.
|
|
689
955
|
if (parsed["draft_iso_stage"] !== undefined) {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
956
|
+
// The doubled-D alias ("=DDIS") is notation: the designator carries
|
|
957
|
+
// its own D-ordinal, the stage is the bare ISO/IEC token.
|
|
958
|
+
const rawStage = `${extractValue(parsed["draft_stage_d"]) ?? ""}${extractValue(parsed["draft_iso_stage"]) ?? ""}`;
|
|
959
|
+
const stageText = rawStage.startsWith("D") &&
|
|
960
|
+
["PWI", "NP", "WD", "CD", "CDV", "DIS", "FDIS"].includes(rawStage.slice(1))
|
|
961
|
+
? rawStage.slice(1)
|
|
962
|
+
: rawStage;
|
|
963
|
+
const jointVersion = extractValue(parsed["draft_version"]) ??
|
|
964
|
+
(parsed["draft"] !== undefined
|
|
965
|
+
? extractValue(asHash(parsed["draft"])["draft_version"])
|
|
966
|
+
: undefined);
|
|
967
|
+
let jointDraft = jointVersion !== undefined
|
|
968
|
+
? `D${jointVersion.replace(/^D/, "")}=${stageText}`
|
|
693
969
|
: `D=${stageText}`;
|
|
694
970
|
if (parsed["draft_iso_iteration"] !== undefined) {
|
|
695
971
|
jointDraft += `.${extractValue(parsed["draft_iso_iteration"])}`;
|
|
696
972
|
}
|
|
697
973
|
if (parsed["draft_stage_year"] !== undefined) {
|
|
698
|
-
|
|
974
|
+
// The capture carries its separator (":2020") — strip it (the
|
|
975
|
+
// #471 separator-in-capture rule).
|
|
976
|
+
jointDraft += `:${(extractValue(parsed["draft_stage_year"]) ?? "").replace(/^[\-:]/, "")}`;
|
|
699
977
|
}
|
|
700
978
|
attributes["ieee_draft"] = jointDraft;
|
|
701
979
|
}
|
|
@@ -791,6 +1069,40 @@ class IeeeBuilder {
|
|
|
791
1069
|
if (record !== undefined)
|
|
792
1070
|
attributes["typed_stage"] = new IeeeTypedStage(record);
|
|
793
1071
|
}
|
|
1072
|
+
// A plain IEEE-designator draft on joint publishers ("/D8, June
|
|
1073
|
+
// 2010") carries no ISO stage machinery: it builds as the project
|
|
1074
|
+
// draft its bare-IEEE twin and the D= designator rows already pin
|
|
1075
|
+
// (pubid#430) rather than the joint-development model.
|
|
1076
|
+
if (attributes["draft"] !== undefined &&
|
|
1077
|
+
parsed["iso_stage"] === undefined &&
|
|
1078
|
+
parsed["iec_stage"] === undefined &&
|
|
1079
|
+
parsed["amd_number"] === undefined) {
|
|
1080
|
+
const pubs = (attributes["publishers"] ?? []);
|
|
1081
|
+
const projectAttrs = {
|
|
1082
|
+
draft: attributes["draft"],
|
|
1083
|
+
};
|
|
1084
|
+
// The code columns (number/parts/separator/prefix) are the
|
|
1085
|
+
// ProjectDraft render/hash face; the spelled P is the project
|
|
1086
|
+
// marker (identity), whatever the lead publisher.
|
|
1087
|
+
for (const k of ["number", "prefix", "parts", "separator"]) {
|
|
1088
|
+
// A "P" prefix is the project marker itself (the generic path
|
|
1089
|
+
// deletes it on the strip) - it must not ride the code columns.
|
|
1090
|
+
if (attributes[k] !== undefined && !(k === "prefix" && attributes[k] === "P")) {
|
|
1091
|
+
projectAttrs[k] = attributes[k];
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
if (parsed["project_marker"] !== undefined)
|
|
1095
|
+
projectAttrs["project_marker"] = true;
|
|
1096
|
+
if (pubs.length > 0) {
|
|
1097
|
+
projectAttrs["publisher"] = pubs[0];
|
|
1098
|
+
if (pubs.length > 1)
|
|
1099
|
+
projectAttrs["copublisher"] = pubs.slice(1);
|
|
1100
|
+
}
|
|
1101
|
+
if (attributes["draft_status"] !== undefined) {
|
|
1102
|
+
projectAttrs["draft_status"] = attributes["draft_status"];
|
|
1103
|
+
}
|
|
1104
|
+
return new ProjectDraftIdentifierClass(projectAttrs);
|
|
1105
|
+
}
|
|
794
1106
|
const joint = new JointDevelopmentClass(attributes);
|
|
795
1107
|
if (parsed["amd_number"] === undefined)
|
|
796
1108
|
return joint;
|
|
@@ -1046,7 +1358,12 @@ class IeeeBuilder {
|
|
|
1046
1358
|
// nested name_first variant captures as a hash) and reaches Standard
|
|
1047
1359
|
// via the code check; Edition is only the no-code fallback.
|
|
1048
1360
|
let klass = NescEditionClass;
|
|
1049
|
-
if (parsedHash["
|
|
1361
|
+
if (parsedHash["preprint"] !== undefined) {
|
|
1362
|
+
// Preprint is a draft STAGE of the C2 code — checked before the
|
|
1363
|
+
// Standard route: the c2-preprint tree carries :code "C2" too.
|
|
1364
|
+
klass = NescPreprintClass;
|
|
1365
|
+
}
|
|
1366
|
+
else if (parsedHash["draft"] !== undefined) {
|
|
1050
1367
|
klass = NescDraftClass;
|
|
1051
1368
|
}
|
|
1052
1369
|
else if (typeof parsedHash["variant"] === "string") {
|
|
@@ -1120,6 +1437,24 @@ function copublishedStructured(copublishedNumber) {
|
|
|
1120
1437
|
return attrs;
|
|
1121
1438
|
}
|
|
1122
1439
|
// --- parse orchestration -------------------------------------------------------
|
|
1440
|
+
// The grammar runtime is the baked PARG artifact (pubid-grammar
|
|
1441
|
+
// artifacts/ieee.json via the parsanol engine) — the same artifact the
|
|
1442
|
+
// gem runs, so grammar changes land once and this port cannot drift.
|
|
1443
|
+
// Only the preprocessor, builder, model and the AIEE sub-grammar root
|
|
1444
|
+
// (parseAieeDirect) remain hand-written.
|
|
1445
|
+
let cachedArtifactRuntime;
|
|
1446
|
+
function parseSource(input) {
|
|
1447
|
+
cachedArtifactRuntime ??= PubidRuntime.load("ieee");
|
|
1448
|
+
try {
|
|
1449
|
+
return artifactToBuilderTree(cachedArtifactRuntime.parseShape(input));
|
|
1450
|
+
}
|
|
1451
|
+
catch (e) {
|
|
1452
|
+
// The engine throws its own error class; the reference's fallback
|
|
1453
|
+
// logic keys on Parslet::ParseFailed, so normalize (the messageless
|
|
1454
|
+
// wasm throws included).
|
|
1455
|
+
throw new ParseFailed(e instanceof Error && e.message !== "" ? e.message : String(e), 0);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1123
1458
|
function ieeeParseSingle(input) {
|
|
1124
1459
|
const normalized = applyUpdateCodes(input);
|
|
1125
1460
|
// UpdateCodes may introduce the "; " double-label separator (the
|
|
@@ -1131,7 +1466,7 @@ function ieeeParseSingle(input) {
|
|
|
1131
1466
|
return buildDual(re.parts);
|
|
1132
1467
|
}
|
|
1133
1468
|
const cleaned = preprocessIeee(normalized);
|
|
1134
|
-
const tree =
|
|
1469
|
+
const tree = parseSource(cleaned);
|
|
1135
1470
|
const builder = new IeeeBuilder();
|
|
1136
1471
|
builder.originalInput = input;
|
|
1137
1472
|
return builder.build(tree);
|
|
@@ -1141,7 +1476,7 @@ function ieeeParseSingle(input) {
|
|
|
1141
1476
|
// update-codes rewrite (e.g. "AIEE Nos 72 and 73 - 1932") must not make
|
|
1142
1477
|
// a line parse that the reference rejects.
|
|
1143
1478
|
function ieeeParseSingleNoCodes(input) {
|
|
1144
|
-
const tree =
|
|
1479
|
+
const tree = parseSource(preprocessIeee(input));
|
|
1145
1480
|
const builder = new IeeeBuilder();
|
|
1146
1481
|
builder.originalInput = input;
|
|
1147
1482
|
return builder.build(tree);
|
|
@@ -40,6 +40,7 @@ export declare class IeeeCode {
|
|
|
40
40
|
static parse(codeStr: string): IeeeCode | undefined;
|
|
41
41
|
render(): string;
|
|
42
42
|
}
|
|
43
|
+
export declare const DRAFT_MONTH_NAMES: Record<string, string>;
|
|
43
44
|
export declare class IeeeDraft {
|
|
44
45
|
readonly version: string;
|
|
45
46
|
readonly revision: string | undefined;
|
|
@@ -83,10 +84,13 @@ export declare abstract class IeeeIdentifier extends BaseIdentifier {
|
|
|
83
84
|
readonly draft_status: string | undefined;
|
|
84
85
|
readonly draft: string | undefined;
|
|
85
86
|
readonly revision: string | undefined;
|
|
87
|
+
readonly rev_sep: string | undefined;
|
|
88
|
+
readonly rev_bare: boolean | undefined;
|
|
86
89
|
readonly edition: string | undefined;
|
|
87
90
|
readonly month: string | undefined;
|
|
88
91
|
readonly day: string | undefined;
|
|
89
92
|
readonly redline: boolean | undefined;
|
|
93
|
+
readonly month_glued: boolean | undefined;
|
|
90
94
|
readonly edition_month: string | undefined;
|
|
91
95
|
readonly space_before_draft: boolean | undefined;
|
|
92
96
|
readonly typed_stage: IeeeTypedStage | undefined;
|
|
@@ -142,3 +146,4 @@ export declare const NescHandbookClass: IdentifierStatic;
|
|
|
142
146
|
export declare const NescRedlineClass: IdentifierStatic;
|
|
143
147
|
export declare const NescStandardClass: IdentifierStatic;
|
|
144
148
|
export declare const NescDraftClass: IdentifierStatic;
|
|
149
|
+
export declare const NescPreprintClass: IdentifierStatic;
|