@pubid/pubid 0.1.3 → 0.1.5
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/conformance/cli.js +5 -1
- package/dist/flavors/amca/grammar.d.ts +4 -0
- package/dist/flavors/amca/grammar.js +87 -0
- package/dist/flavors/amca/index.d.ts +4 -0
- package/dist/flavors/amca/index.js +76 -0
- package/dist/flavors/amca/model.d.ts +29 -0
- package/dist/flavors/amca/model.js +146 -0
- package/dist/flavors/ansi/index.d.ts +24 -0
- package/dist/flavors/ansi/index.js +147 -0
- package/dist/flavors/api/grammar.d.ts +4 -0
- package/dist/flavors/api/grammar.js +79 -0
- package/dist/flavors/api/index.d.ts +4 -0
- package/dist/flavors/api/index.js +71 -0
- package/dist/flavors/api/model.d.ts +27 -0
- package/dist/flavors/api/model.js +114 -0
- package/dist/flavors/ashrae/grammar.d.ts +2 -0
- package/dist/flavors/ashrae/grammar.js +667 -0
- package/dist/flavors/ashrae/index.d.ts +4 -0
- package/dist/flavors/ashrae/index.js +306 -0
- package/dist/flavors/ashrae/model.d.ts +87 -0
- package/dist/flavors/ashrae/model.js +335 -0
- package/dist/flavors/ashrae/preprocessor.d.ts +7 -0
- package/dist/flavors/ashrae/preprocessor.js +44 -0
- package/dist/flavors/asme/grammar.d.ts +2 -0
- package/dist/flavors/asme/grammar.js +200 -0
- package/dist/flavors/asme/index.d.ts +4 -0
- package/dist/flavors/asme/index.js +138 -0
- package/dist/flavors/asme/model.d.ts +34 -0
- package/dist/flavors/asme/model.js +123 -0
- package/dist/flavors/astm/grammar.d.ts +2 -0
- package/dist/flavors/astm/grammar.js +110 -0
- package/dist/flavors/astm/index.d.ts +2 -0
- package/dist/flavors/astm/index.js +127 -0
- package/dist/flavors/astm/model.d.ts +42 -0
- package/dist/flavors/astm/model.js +263 -0
- package/dist/flavors/bipm/index.d.ts +30 -0
- package/dist/flavors/bipm/index.js +434 -0
- package/dist/flavors/bsi/grammar.d.ts +4 -0
- package/dist/flavors/bsi/grammar.js +535 -0
- package/dist/flavors/bsi/index.d.ts +4 -0
- package/dist/flavors/bsi/index.js +941 -0
- package/dist/flavors/bsi/model.d.ts +127 -0
- package/dist/flavors/bsi/model.js +863 -0
- package/dist/flavors/calconnect/index.d.ts +21 -0
- package/dist/flavors/calconnect/index.js +113 -0
- package/dist/flavors/cen_cenelec/grammar.d.ts +4 -0
- package/dist/flavors/cen_cenelec/grammar.js +107 -0
- package/dist/flavors/cen_cenelec/index.d.ts +5 -0
- package/dist/flavors/cen_cenelec/index.js +263 -0
- package/dist/flavors/cen_cenelec/model.d.ts +45 -0
- package/dist/flavors/cen_cenelec/model.js +490 -0
- package/dist/flavors/cie/grammar.d.ts +5 -0
- package/dist/flavors/cie/grammar.js +127 -0
- package/dist/flavors/cie/index.d.ts +2 -0
- package/dist/flavors/cie/index.js +311 -0
- package/dist/flavors/cie/model.d.ts +122 -0
- package/dist/flavors/cie/model.js +464 -0
- package/dist/flavors/csa/grammar.d.ts +6 -0
- package/dist/flavors/csa/grammar.js +239 -0
- package/dist/flavors/csa/index.d.ts +2 -0
- package/dist/flavors/csa/index.js +634 -0
- package/dist/flavors/csa/model.d.ts +33 -0
- package/dist/flavors/csa/model.js +505 -0
- package/dist/flavors/doi/index.d.ts +13 -0
- package/dist/flavors/doi/index.js +65 -0
- package/dist/flavors/easc/index.d.ts +4 -0
- package/dist/flavors/easc/index.js +107 -0
- package/dist/flavors/ecma/index.d.ts +4 -0
- package/dist/flavors/ecma/index.js +117 -0
- package/dist/flavors/etsi/index.d.ts +14 -0
- package/dist/flavors/etsi/index.js +307 -0
- package/dist/flavors/evs/grammar.d.ts +2 -0
- package/dist/flavors/evs/grammar.js +22 -0
- package/dist/flavors/evs/index.d.ts +2 -0
- package/dist/flavors/evs/index.js +24 -0
- package/dist/flavors/evs/model.d.ts +19 -0
- package/dist/flavors/evs/model.js +36 -0
- package/dist/flavors/gb/index.d.ts +31 -0
- package/dist/flavors/gb/index.js +159 -0
- package/dist/flavors/gost/grammar.d.ts +2 -0
- package/dist/flavors/gost/grammar.js +46 -0
- package/dist/flavors/gost/index.d.ts +2 -0
- package/dist/flavors/gost/index.js +165 -0
- package/dist/flavors/gost/model.d.ts +61 -0
- package/dist/flavors/gost/model.js +147 -0
- package/dist/flavors/gost/prefix_routes.d.ts +15 -0
- package/dist/flavors/gost/prefix_routes.js +192 -0
- package/dist/flavors/iala/index.d.ts +32 -0
- package/dist/flavors/iala/index.js +300 -0
- package/dist/flavors/iana/index.d.ts +19 -0
- package/dist/flavors/iana/index.js +71 -0
- package/dist/flavors/idf/grammar.d.ts +2 -0
- package/dist/flavors/idf/grammar.js +71 -0
- package/dist/flavors/idf/index.d.ts +2 -0
- package/dist/flavors/idf/index.js +100 -0
- package/dist/flavors/idf/model.d.ts +54 -0
- package/dist/flavors/idf/model.js +359 -0
- package/dist/flavors/iec/grammar.d.ts +3 -0
- package/dist/flavors/iec/grammar.js +187 -0
- package/dist/flavors/iec/index.d.ts +4 -0
- package/dist/flavors/iec/index.js +507 -0
- package/dist/flavors/iec/model.d.ts +54 -0
- package/dist/flavors/iec/model.js +437 -0
- package/dist/flavors/ieee/grammar.d.ts +2 -0
- package/dist/flavors/ieee/grammar.js +697 -0
- package/dist/flavors/ieee/index.d.ts +3 -0
- package/dist/flavors/ieee/index.js +1258 -0
- package/dist/flavors/ieee/model.d.ts +144 -0
- package/dist/flavors/ieee/model.js +1237 -0
- package/dist/flavors/ieee/preprocessor.d.ts +8 -0
- package/dist/flavors/ieee/preprocessor.js +410 -0
- package/dist/flavors/ieee/update_codes.d.ts +8 -0
- package/dist/flavors/ieee/update_codes.js +197 -0
- package/dist/flavors/ietf/index.d.ts +30 -0
- package/dist/flavors/ietf/index.js +151 -0
- package/dist/flavors/iho/index.d.ts +4 -0
- package/dist/flavors/iho/index.js +126 -0
- package/dist/flavors/index.d.ts +9 -0
- package/dist/flavors/index.js +136 -0
- package/dist/flavors/isbn/index.d.ts +13 -0
- package/dist/flavors/isbn/index.js +97 -0
- package/dist/flavors/iso/grammar.d.ts +2 -0
- package/dist/flavors/iso/grammar.js +207 -0
- package/dist/flavors/iso/index.d.ts +2 -0
- package/dist/flavors/iso/index.js +343 -0
- package/dist/flavors/iso/model.d.ts +159 -0
- package/dist/flavors/iso/model.js +802 -0
- package/dist/flavors/iso/stages.generated.d.ts +13 -0
- package/dist/flavors/iso/stages.generated.js +391 -0
- package/dist/flavors/itu/grammar.d.ts +4 -0
- package/dist/flavors/itu/grammar.js +205 -0
- package/dist/flavors/itu/index.d.ts +2 -0
- package/dist/flavors/itu/index.js +224 -0
- package/dist/flavors/itu/model.d.ts +184 -0
- package/dist/flavors/itu/model.js +599 -0
- package/dist/flavors/jcgm/index.d.ts +7 -0
- package/dist/flavors/jcgm/index.js +300 -0
- package/dist/flavors/jis/index.d.ts +4 -0
- package/dist/flavors/jis/index.js +267 -0
- package/dist/flavors/nist/grammar.d.ts +2 -0
- package/dist/flavors/nist/grammar.js +287 -0
- package/dist/flavors/nist/index.d.ts +2 -0
- package/dist/flavors/nist/index.js +1244 -0
- package/dist/flavors/nist/model.d.ts +155 -0
- package/dist/flavors/nist/model.js +823 -0
- package/dist/flavors/nist/preprocessor.d.ts +11 -0
- package/dist/flavors/nist/preprocessor.js +281 -0
- package/dist/flavors/oasis/index.d.ts +23 -0
- package/dist/flavors/oasis/index.js +110 -0
- package/dist/flavors/ogc/index.d.ts +20 -0
- package/dist/flavors/ogc/index.js +75 -0
- package/dist/flavors/oiml/grammar.d.ts +2 -0
- package/dist/flavors/oiml/grammar.js +206 -0
- package/dist/flavors/oiml/implementation.d.ts +9 -0
- package/dist/flavors/oiml/implementation.js +45 -0
- package/dist/flavors/oiml/model.d.ts +23 -0
- package/dist/flavors/oiml/model.js +592 -0
- package/dist/flavors/omg/index.d.ts +14 -0
- package/dist/flavors/omg/index.js +73 -0
- package/dist/flavors/plateau/index.d.ts +4 -0
- package/dist/flavors/plateau/index.js +97 -0
- package/dist/flavors/tgpp/index.d.ts +4 -0
- package/dist/flavors/tgpp/index.js +122 -0
- package/dist/flavors/un/index.d.ts +13 -0
- package/dist/flavors/un/index.js +90 -0
- package/dist/flavors/w3c/index.d.ts +14 -0
- package/dist/flavors/w3c/index.js +148 -0
- package/dist/flavors/xsf/index.d.ts +18 -0
- package/dist/flavors/xsf/index.js +65 -0
- package/dist/grammar/engine.d.ts +78 -0
- package/dist/grammar/engine.js +351 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/model/attribute.d.ts +54 -0
- package/dist/model/attribute.js +53 -0
- package/dist/model/builder.d.ts +39 -0
- package/dist/model/builder.js +124 -0
- package/dist/model/component.d.ts +70 -0
- package/dist/model/component.js +170 -0
- package/dist/model/identifier.d.ts +84 -0
- package/dist/model/identifier.js +270 -0
- package/dist/model/urn-generator.d.ts +27 -0
- package/dist/model/urn-generator.js +91 -0
- package/package.json +1 -1
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import { ParseFailed, parseGrammar } from "../../grammar/engine.js";
|
|
2
|
+
import { Language, PubidDate } from "../../model/component.js";
|
|
3
|
+
import { iecGrammar, preprocessIec } from "./grammar.js";
|
|
4
|
+
import { locateStage, IEC_CLASSES, } from "./model.js";
|
|
5
|
+
const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
6
|
+
const s = (v) => v === undefined || v === null ? undefined : String(v);
|
|
7
|
+
function flatten(data) {
|
|
8
|
+
return Array.isArray(data) ? Object.assign({}, ...data) : data;
|
|
9
|
+
}
|
|
10
|
+
/** Splits a number_with_part tree into number/part/subpart. */
|
|
11
|
+
function numberComponents(tree) {
|
|
12
|
+
const obj = isObj(tree) ? tree : undefined;
|
|
13
|
+
const raw = obj === undefined ? s(tree) : s(obj["number"]);
|
|
14
|
+
if (raw === undefined)
|
|
15
|
+
return {};
|
|
16
|
+
const out = { number: raw };
|
|
17
|
+
const part = obj === undefined ? undefined : s(obj["part"]);
|
|
18
|
+
const subpart = obj === undefined ? undefined : s(obj["subpart"]);
|
|
19
|
+
if (part !== undefined)
|
|
20
|
+
out["part"] = part;
|
|
21
|
+
if (subpart !== undefined)
|
|
22
|
+
out["subpart"] = subpart;
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
function buildDate(tree) {
|
|
26
|
+
const dateStr = s(flatten(tree));
|
|
27
|
+
if (dateStr === undefined)
|
|
28
|
+
return undefined;
|
|
29
|
+
const parts = dateStr.split("-");
|
|
30
|
+
return new PubidDate({ year: parts[0], month: parts[1], day: parts[2] });
|
|
31
|
+
}
|
|
32
|
+
function parseLanguages(tree) {
|
|
33
|
+
const raw = s(tree);
|
|
34
|
+
if (raw === undefined)
|
|
35
|
+
return [];
|
|
36
|
+
const tokens = raw.split(",").flatMap((chunk) => chunk.split("/"));
|
|
37
|
+
return tokens
|
|
38
|
+
.map((token) => token.trim())
|
|
39
|
+
.filter((token) => token !== "")
|
|
40
|
+
.map((original) => {
|
|
41
|
+
const code = original.length === 1
|
|
42
|
+
? (Language.CHAR_MAP[original] ?? original.toLowerCase())
|
|
43
|
+
: original;
|
|
44
|
+
return new Language({ code, originalCode: original });
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
class IecBuilder {
|
|
48
|
+
build(data) {
|
|
49
|
+
return this.buildInner(data);
|
|
50
|
+
}
|
|
51
|
+
buildInner(data) {
|
|
52
|
+
const tree = flatten(data);
|
|
53
|
+
// --- working programme / working document ---
|
|
54
|
+
if (tree["wp_stage"] !== undefined)
|
|
55
|
+
return this.buildWorkingProgramme(tree);
|
|
56
|
+
if (tree["technical_committee"] !== undefined && tree["wd_number"] !== undefined) {
|
|
57
|
+
return this.buildWorkingDocument(tree);
|
|
58
|
+
}
|
|
59
|
+
if (tree["technical_committee"] !== undefined) {
|
|
60
|
+
throw new ParseFailed("IEC: technical groups are not corpus-covered", 0);
|
|
61
|
+
}
|
|
62
|
+
// --- fragment of an amendment/corrigendum (third_part notation) ---
|
|
63
|
+
if (tree["fragment_type"] !== undefined && tree["base"] !== undefined) {
|
|
64
|
+
return this.buildFragment(tree);
|
|
65
|
+
}
|
|
66
|
+
// --- sheets ---
|
|
67
|
+
if (tree["base"] !== undefined && (tree["sheet_number"] !== undefined || tree["sheet_year"] !== undefined)) {
|
|
68
|
+
return this.buildSheet(tree);
|
|
69
|
+
}
|
|
70
|
+
// --- supplements (incl. supplement-of-supplement) ---
|
|
71
|
+
if (tree["base"] !== undefined)
|
|
72
|
+
return this.buildSupplement(tree);
|
|
73
|
+
return this.buildSingle(tree);
|
|
74
|
+
}
|
|
75
|
+
commonAttrs(tree, attrs) {
|
|
76
|
+
Object.assign(attrs, numberComponents(tree["number_with_part"]));
|
|
77
|
+
// Letters-prefixed numbers (IECEE "CAB", "AD") only take digit-first
|
|
78
|
+
// parts — "CAB-G01" rejects (Ruby parity; digit numbers like 61158-X
|
|
79
|
+
// keep letter parts).
|
|
80
|
+
const number = attrs["number"];
|
|
81
|
+
const part = attrs["part"];
|
|
82
|
+
if (number !== undefined && /^[A-Z]{2,4}$/.test(number) &&
|
|
83
|
+
part !== undefined && /^[A-Za-z]/.test(part)) {
|
|
84
|
+
throw new ParseFailed(`IEC: letter part ${part} after letters number ${number}`, 0);
|
|
85
|
+
}
|
|
86
|
+
const dateTree = tree["date"];
|
|
87
|
+
if (dateTree !== undefined) {
|
|
88
|
+
const date = buildDate(dateTree);
|
|
89
|
+
if (date)
|
|
90
|
+
attrs["date"] = date;
|
|
91
|
+
}
|
|
92
|
+
if (tree["undated_marker"] !== undefined)
|
|
93
|
+
attrs["date"] = new PubidDate({ undated: true });
|
|
94
|
+
const edition = s(tree["edition"]);
|
|
95
|
+
if (edition !== undefined)
|
|
96
|
+
attrs["edition"] = edition;
|
|
97
|
+
if (tree["languages"] !== undefined) {
|
|
98
|
+
attrs["languages"] = parseLanguages(tree["languages"]);
|
|
99
|
+
}
|
|
100
|
+
const publisher = s(tree["publisher"]);
|
|
101
|
+
if (publisher !== undefined)
|
|
102
|
+
attrs["publisher"] = publisher;
|
|
103
|
+
const copubTree = tree["copublishers"];
|
|
104
|
+
if (copubTree !== undefined && copubTree !== null && copubTree !== "") {
|
|
105
|
+
const list = Array.isArray(copubTree) ? copubTree : [copubTree];
|
|
106
|
+
const bodies = list
|
|
107
|
+
.map((c) => (isObj(c) ? String(c["copublisher"]) : String(c)))
|
|
108
|
+
.filter((b) => b !== "" && b !== "null" && b !== "undefined");
|
|
109
|
+
if (bodies.length > 0)
|
|
110
|
+
attrs["copublishers"] = bodies;
|
|
111
|
+
}
|
|
112
|
+
if (tree["all_parts"] !== undefined)
|
|
113
|
+
attrs["all_parts"] = true;
|
|
114
|
+
if (tree["database"] !== undefined)
|
|
115
|
+
attrs["database"] = true;
|
|
116
|
+
}
|
|
117
|
+
buildSingle(tree) {
|
|
118
|
+
const stageAbbr = s(tree["type_with_stage"]) ?? s(tree["type_with_stage_fr"]) ?? "";
|
|
119
|
+
const typedStage = locateStage(stageAbbr) ?? locateStage("IS");
|
|
120
|
+
const kind = typedStage.typeCode === "wp" ? "is" : typedStage.typeCode;
|
|
121
|
+
const attrs = {};
|
|
122
|
+
this.commonAttrs(tree, attrs);
|
|
123
|
+
// TRF with an embedded CISPR document: the CISPR number is runtime-only
|
|
124
|
+
// (hash carries just year/publisher; the URN names the CISPR document).
|
|
125
|
+
if (tree["trf_org"] !== undefined) {
|
|
126
|
+
const cisprComponents = numberComponents(tree["number_with_part"]);
|
|
127
|
+
attrs["cispr_number"] = cisprComponents["number"];
|
|
128
|
+
attrs["cispr_part"] = cisprComponents["part"];
|
|
129
|
+
delete attrs["number"];
|
|
130
|
+
delete attrs["part"];
|
|
131
|
+
delete attrs["subpart"];
|
|
132
|
+
}
|
|
133
|
+
// The supplement-of-supplement path stores the base separately.
|
|
134
|
+
const parsed = {
|
|
135
|
+
typedStage,
|
|
136
|
+
kind: kind,
|
|
137
|
+
attrs,
|
|
138
|
+
};
|
|
139
|
+
// Consolidated supplements (+AMD…+AMD…)
|
|
140
|
+
const consolidated = tree["consolidated_supplements"];
|
|
141
|
+
let identifier = this.construct(parsed, attrs);
|
|
142
|
+
if (consolidated !== undefined) {
|
|
143
|
+
identifier = this.wrapConsolidated(identifier, consolidated);
|
|
144
|
+
}
|
|
145
|
+
const vapSuffix = s(tree["vap_suffix"]);
|
|
146
|
+
if (vapSuffix !== undefined) {
|
|
147
|
+
identifier = this.wrapVap(identifier, vapSuffix);
|
|
148
|
+
}
|
|
149
|
+
return identifier;
|
|
150
|
+
}
|
|
151
|
+
construct(parsed, attrs) {
|
|
152
|
+
const klass = IEC_CLASSES.KIND_CLASSES[parsed.kind];
|
|
153
|
+
if (klass === undefined)
|
|
154
|
+
throw new ParseFailed(`IEC: unknown kind ${String(parsed.kind)}`, 0);
|
|
155
|
+
const identifier = new klass(attrs);
|
|
156
|
+
identifier.typedStage = parsed.typedStage;
|
|
157
|
+
// The wire "stage" is the draft stage code (published stages drop).
|
|
158
|
+
const ts = parsed.typedStage;
|
|
159
|
+
if (ts !== undefined && ts.stageCode !== "published") {
|
|
160
|
+
identifier["stage"] = ts.code;
|
|
161
|
+
}
|
|
162
|
+
return identifier;
|
|
163
|
+
}
|
|
164
|
+
wrapConsolidated(base, data) {
|
|
165
|
+
const list = Array.isArray(data) ? data : [data];
|
|
166
|
+
const members = [base];
|
|
167
|
+
for (const raw of list) {
|
|
168
|
+
if (!isObj(raw))
|
|
169
|
+
continue;
|
|
170
|
+
const type = s(raw["supplement_type"]);
|
|
171
|
+
const number = s(raw["supplement_number"]);
|
|
172
|
+
if (type === undefined || number === undefined)
|
|
173
|
+
continue;
|
|
174
|
+
const date = s(raw["supplement_year"]);
|
|
175
|
+
const attrs = {
|
|
176
|
+
number,
|
|
177
|
+
...(date !== undefined ? { date: new PubidDate({ year: date }) } : {}),
|
|
178
|
+
};
|
|
179
|
+
const kind = type === "AMD" ? "amd" : "cor";
|
|
180
|
+
const member = this.construct({ kind: kind, attrs, typedStage: locateStage(type) }, attrs);
|
|
181
|
+
members.push(member);
|
|
182
|
+
}
|
|
183
|
+
return new IEC_CLASSES.CONSOLIDATED_CLASS({
|
|
184
|
+
identifiers: members,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
wrapVap(base, vapSuffix) {
|
|
188
|
+
const vap = vapSuffix.split("-");
|
|
189
|
+
let edition;
|
|
190
|
+
// A consolidated base carries the edition on its FIRST member (Ruby).
|
|
191
|
+
const kind = base.constructor.polymorphicName.slice("pubid:iec:".length);
|
|
192
|
+
const holder = kind === "consolidated-identifier"
|
|
193
|
+
? base.identifiers[0]
|
|
194
|
+
: base;
|
|
195
|
+
if (holder["edition"] !== undefined) {
|
|
196
|
+
edition = String(holder["edition"]);
|
|
197
|
+
delete holder["edition"];
|
|
198
|
+
}
|
|
199
|
+
return new IEC_CLASSES.VAP_CLASS({
|
|
200
|
+
base,
|
|
201
|
+
vap,
|
|
202
|
+
...(edition !== undefined ? { edition } : {}),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
buildSupplement(tree) {
|
|
206
|
+
const base = this.buildInner(tree["base"]);
|
|
207
|
+
const stageAbbr = s(tree["type_with_stage"]) ?? "";
|
|
208
|
+
const typedStage = locateStage(stageAbbr) ?? locateStage("AMD");
|
|
209
|
+
let kind;
|
|
210
|
+
if (typedStage.typeCode === "cor")
|
|
211
|
+
kind = "cor";
|
|
212
|
+
else if (typedStage.typeCode === "ish")
|
|
213
|
+
kind = "ish";
|
|
214
|
+
else if (typedStage.typeCode === "frag") {
|
|
215
|
+
// FRAG parses as a supplement stage; the class is the fragment.
|
|
216
|
+
const innerAttrs = {};
|
|
217
|
+
this.commonAttrs(tree, innerAttrs);
|
|
218
|
+
const number = innerAttrs["number"];
|
|
219
|
+
delete innerAttrs["number"];
|
|
220
|
+
const baseRec2 = base;
|
|
221
|
+
if (baseRec2["publisher"] !== undefined && baseRec2["publisher"] !== "IEC")
|
|
222
|
+
innerAttrs["publisher"] = baseRec2["publisher"];
|
|
223
|
+
const fragment = new IEC_CLASSES.FRAGMENT_CLASS({
|
|
224
|
+
base,
|
|
225
|
+
...(number !== undefined ? { fragment_number: number } : {}),
|
|
226
|
+
...innerAttrs,
|
|
227
|
+
});
|
|
228
|
+
return fragment;
|
|
229
|
+
}
|
|
230
|
+
else
|
|
231
|
+
kind = "amd";
|
|
232
|
+
const attrs = {};
|
|
233
|
+
this.commonAttrs(tree, attrs);
|
|
234
|
+
// Supplements copy the delegated publisher/copublishers level.
|
|
235
|
+
const baseRec = base;
|
|
236
|
+
if (attrs["publisher"] === undefined && baseRec["publisher"] !== undefined && baseRec["publisher"] !== "IEC") {
|
|
237
|
+
attrs["publisher"] = baseRec["publisher"];
|
|
238
|
+
}
|
|
239
|
+
if (attrs["copublishers"] === undefined && Array.isArray(baseRec["copublishers"]) && baseRec["copublishers"].length > 0) {
|
|
240
|
+
attrs["copublishers"] = baseRec["copublishers"];
|
|
241
|
+
}
|
|
242
|
+
const supplement = this.construct({ kind: kind, attrs, typedStage }, attrs);
|
|
243
|
+
const suppRec = supplement;
|
|
244
|
+
suppRec["base"] = base;
|
|
245
|
+
const vapSuffix = s(tree["vap_suffix"]);
|
|
246
|
+
if (vapSuffix !== undefined)
|
|
247
|
+
return this.wrapVap(supplement, vapSuffix);
|
|
248
|
+
return supplement;
|
|
249
|
+
}
|
|
250
|
+
buildSheet(tree) {
|
|
251
|
+
let base = this.buildInner(tree["base"]);
|
|
252
|
+
const sheet_number = s(tree["sheet_number"]);
|
|
253
|
+
const sheet_year = s(tree["sheet_year"]);
|
|
254
|
+
base = new IEC_CLASSES.SHEET_CLASS({
|
|
255
|
+
base,
|
|
256
|
+
...(sheet_number !== undefined ? { sheet_number } : {}),
|
|
257
|
+
...(sheet_year !== undefined ? { sheet_year } : {}),
|
|
258
|
+
});
|
|
259
|
+
// A supplement wrapping the sheet: IEC 60695-2-1/1:1994/COR1:1995.
|
|
260
|
+
if (tree["type_with_stage"] !== undefined) {
|
|
261
|
+
const stageAbbr = s(tree["type_with_stage"]) ?? "";
|
|
262
|
+
const typedStage = locateStage(stageAbbr) ?? locateStage("COR");
|
|
263
|
+
const kind = typedStage.typeCode === "cor" ? "cor" : "amd";
|
|
264
|
+
const attrs = {};
|
|
265
|
+
const numberObj = numberComponents(tree["number_with_part"]);
|
|
266
|
+
Object.assign(attrs, numberObj);
|
|
267
|
+
const dateTree = tree["date"];
|
|
268
|
+
if (dateTree !== undefined) {
|
|
269
|
+
const date = buildDate(dateTree);
|
|
270
|
+
if (date)
|
|
271
|
+
attrs["date"] = date;
|
|
272
|
+
}
|
|
273
|
+
const supplement = this.construct({ kind: kind, attrs, typedStage }, attrs);
|
|
274
|
+
supplement["base"] = base;
|
|
275
|
+
return supplement;
|
|
276
|
+
}
|
|
277
|
+
return base;
|
|
278
|
+
}
|
|
279
|
+
buildFragment(tree) {
|
|
280
|
+
// fragment_notation on the outer identifier (third_part)
|
|
281
|
+
const innerTree = { ...tree };
|
|
282
|
+
delete innerTree["fragment_type"];
|
|
283
|
+
delete innerTree["fragment_number"];
|
|
284
|
+
const base = this.buildInner(innerTree);
|
|
285
|
+
const fragment_number = s(tree["fragment_number"]);
|
|
286
|
+
const edition = s(tree["edition"]);
|
|
287
|
+
return new IEC_CLASSES.FRAGMENT_CLASS({
|
|
288
|
+
base,
|
|
289
|
+
...(fragment_number !== undefined ? { fragment_number } : {}),
|
|
290
|
+
...(edition !== undefined ? { edition } : {}),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
buildWorkingProgramme(tree) {
|
|
294
|
+
const attrs = {
|
|
295
|
+
wp_stage: s(tree["wp_stage"]),
|
|
296
|
+
wp_type: s(tree["wp_type"]),
|
|
297
|
+
};
|
|
298
|
+
if (tree["wp_raw"] !== undefined) {
|
|
299
|
+
// TC-style name ("SyCCOMM-1"): split the trailing number as the part.
|
|
300
|
+
const raw = String(tree["wp_raw"]).replaceAll("‑", "-").replaceAll("‐", "-");
|
|
301
|
+
const segments = raw.split("-").filter((seg) => seg !== "");
|
|
302
|
+
attrs["number"] = segments.shift();
|
|
303
|
+
if (segments.length > 0)
|
|
304
|
+
attrs["part"] = segments.shift();
|
|
305
|
+
if (segments.length > 0)
|
|
306
|
+
attrs["subpart"] = segments.join("-");
|
|
307
|
+
}
|
|
308
|
+
Object.assign(attrs, numberComponents(tree["number_with_part"]));
|
|
309
|
+
const edition = s(tree["edition"]);
|
|
310
|
+
if (edition !== undefined)
|
|
311
|
+
attrs["edition"] = edition;
|
|
312
|
+
const identifier = new IEC_CLASSES.KIND_CLASSES["wp"](attrs);
|
|
313
|
+
const ts = locateStage(s(tree["wp_stage"]) === "PNW" ? "PNW" : "PWI");
|
|
314
|
+
identifier.typedStage = ts;
|
|
315
|
+
if (ts !== undefined && ts.stageCode !== "published") {
|
|
316
|
+
identifier["stage"] = ts.code;
|
|
317
|
+
}
|
|
318
|
+
return identifier;
|
|
319
|
+
}
|
|
320
|
+
buildWorkingDocument(tree) {
|
|
321
|
+
const attrs = {
|
|
322
|
+
technical_committee: s(tree["technical_committee"]),
|
|
323
|
+
wd_number: s(tree["wd_number"]),
|
|
324
|
+
wd_language: s(tree["wd_language"]),
|
|
325
|
+
wd_stage: s(tree["wd_stage"]),
|
|
326
|
+
};
|
|
327
|
+
return new IEC_CLASSES.KIND_CLASSES["wp"](attrs);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/** --- URN: the positional-slot machine (lib/pubid/iec/urn_generator.rb) --- */
|
|
331
|
+
const TYPE_SLOT_CODES = new Set(["tr", "ts", "pas", "srd", "guide", "tec", "wp"]);
|
|
332
|
+
const DELIVERABLES = new Set(["cmv", "csv", "exv", "prv", "rlv", "ser"]);
|
|
333
|
+
const ADJUNCT_TOKENS = {
|
|
334
|
+
amendment: "amd",
|
|
335
|
+
corrigendum: "cor",
|
|
336
|
+
"interpretation-sheet": "ish",
|
|
337
|
+
};
|
|
338
|
+
function idKind(id) {
|
|
339
|
+
return id.constructor.polymorphicName.slice("pubid:iec:".length);
|
|
340
|
+
}
|
|
341
|
+
function maybeOf(id, attr) {
|
|
342
|
+
return id[attr];
|
|
343
|
+
}
|
|
344
|
+
function findDown(id, attr) {
|
|
345
|
+
let current = id;
|
|
346
|
+
while (current !== undefined) {
|
|
347
|
+
const value = maybeOf(current, attr);
|
|
348
|
+
if (value !== undefined && !(Array.isArray(value) && value.length === 0) && value !== "") {
|
|
349
|
+
return value;
|
|
350
|
+
}
|
|
351
|
+
current =
|
|
352
|
+
maybeOf(current, "base") ??
|
|
353
|
+
maybeOf(current, "identifiers")?.[0];
|
|
354
|
+
}
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
function rootOf(id) {
|
|
358
|
+
let current = id;
|
|
359
|
+
while (true) {
|
|
360
|
+
const base = maybeOf(current, "base");
|
|
361
|
+
if (base !== undefined) {
|
|
362
|
+
current = base;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
const ids = maybeOf(current, "identifiers");
|
|
366
|
+
if (ids !== undefined && ids.length > 0 && ids[0] !== current) {
|
|
367
|
+
current = ids[0];
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
return current;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function adjunctFields(id, plus) {
|
|
374
|
+
const kind = idKind(id);
|
|
375
|
+
const token = ADJUNCT_TOKENS[kind];
|
|
376
|
+
if (token === undefined)
|
|
377
|
+
return [];
|
|
378
|
+
const date = maybeOf(id, "date");
|
|
379
|
+
return [
|
|
380
|
+
plus ? "plus" : "",
|
|
381
|
+
token,
|
|
382
|
+
String(maybeOf(id, "number") ?? ""),
|
|
383
|
+
date !== undefined ? (date.render() ?? "") : "",
|
|
384
|
+
];
|
|
385
|
+
}
|
|
386
|
+
function adjunctSlots(id) {
|
|
387
|
+
if (id === undefined)
|
|
388
|
+
return [];
|
|
389
|
+
const kind = idKind(id);
|
|
390
|
+
if (kind === "consolidated-identifier") {
|
|
391
|
+
const members = maybeOf(id, "identifiers") ?? [];
|
|
392
|
+
return [
|
|
393
|
+
...adjunctSlots(members[0]),
|
|
394
|
+
...members.slice(1).flatMap((m) => adjunctFields(m, true)),
|
|
395
|
+
];
|
|
396
|
+
}
|
|
397
|
+
const recursed = adjunctSlots(maybeOf(id, "base"));
|
|
398
|
+
if (ADJUNCT_TOKENS[kind] !== undefined) {
|
|
399
|
+
return [...recursed, ...adjunctFields(id, false)];
|
|
400
|
+
}
|
|
401
|
+
return recursed;
|
|
402
|
+
}
|
|
403
|
+
export function iecToUrn(identifier) {
|
|
404
|
+
// The document the URN names: the origin standard with wrappers peeled.
|
|
405
|
+
const doc = rootOf(identifier);
|
|
406
|
+
const trf = doc;
|
|
407
|
+
let docWithNumber;
|
|
408
|
+
if (maybeOf(trf, "number") === undefined && maybeOf(trf, "cispr_number") !== undefined) {
|
|
409
|
+
// TRF: read number/part from the runtime CISPR columns.
|
|
410
|
+
docWithNumber = {
|
|
411
|
+
get number() { return maybeOf(trf, "cispr_number"); },
|
|
412
|
+
get part() { return maybeOf(trf, "cispr_part"); },
|
|
413
|
+
get subpart() { return undefined; },
|
|
414
|
+
get publisher() { return "CISPR"; },
|
|
415
|
+
get copublishers() { return []; },
|
|
416
|
+
get date() { return undefined; },
|
|
417
|
+
get typedStage() { return trf.typedStage; },
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
docWithNumber = maybeOf(doc, "number") !== undefined
|
|
422
|
+
? doc
|
|
423
|
+
: maybeOf(doc, "cispr_identifier") ?? doc;
|
|
424
|
+
}
|
|
425
|
+
const number = maybeOf(docWithNumber, "number");
|
|
426
|
+
if (number === undefined)
|
|
427
|
+
return "urn:iec:std:";
|
|
428
|
+
const numSegment = [number, maybeOf(docWithNumber, "part"), maybeOf(docWithNumber, "subpart")]
|
|
429
|
+
.filter((v) => v !== undefined && v !== "")
|
|
430
|
+
.join("-")
|
|
431
|
+
.replace(/\s+/g, "-");
|
|
432
|
+
if (numSegment === "")
|
|
433
|
+
return "urn:iec:std:";
|
|
434
|
+
const publishers = [
|
|
435
|
+
maybeOf(docWithNumber, "publisher") ?? "IEC",
|
|
436
|
+
...(maybeOf(docWithNumber, "copublishers") ?? []),
|
|
437
|
+
]
|
|
438
|
+
.filter((p) => p !== undefined && p !== "")
|
|
439
|
+
.map((p) => p.split(/\s+/).at(-1))
|
|
440
|
+
.filter((p) => p !== "");
|
|
441
|
+
const authority = publishers.join("-");
|
|
442
|
+
const date = maybeOf(docWithNumber, "date");
|
|
443
|
+
const dateSlot = date !== undefined ? (date.render() ?? "") : "";
|
|
444
|
+
const ts = docWithNumber.typedStage;
|
|
445
|
+
const typeToken = ts !== undefined && TYPE_SLOT_CODES.has(ts.typeCode) ? ts.typeCode : "";
|
|
446
|
+
const stageToken = ts !== undefined && ts.stageCode !== "published"
|
|
447
|
+
? `stage-${ts.harmonized ?? ts.stageCode}`
|
|
448
|
+
: "";
|
|
449
|
+
const stageOrType = [typeToken, stageToken].filter((t) => t !== "").join("-");
|
|
450
|
+
const vap = findDown(identifier, "vap");
|
|
451
|
+
const vapCodes = (vap ?? [])
|
|
452
|
+
.map((c) => c.toLowerCase())
|
|
453
|
+
.filter((c) => DELIVERABLES.has(c));
|
|
454
|
+
let deliverable = vapCodes.join("-");
|
|
455
|
+
if (deliverable === "") {
|
|
456
|
+
const edition = findDown(identifier, "edition");
|
|
457
|
+
deliverable = edition !== undefined ? `ed-${edition}` : "";
|
|
458
|
+
}
|
|
459
|
+
const langs = maybeOf(identifier, "languages") ?? [];
|
|
460
|
+
const language = langs.map((l) => l.code).join("-");
|
|
461
|
+
const slots = ["urn", "iec", "std", authority, numSegment, dateSlot, stageOrType, deliverable];
|
|
462
|
+
const adjuncts = adjunctSlots(identifier);
|
|
463
|
+
const bareSeries = slots.at(-1) === "ser" &&
|
|
464
|
+
slots.slice(5, 7).every((x) => x === "") &&
|
|
465
|
+
adjuncts.length === 0 &&
|
|
466
|
+
language === "";
|
|
467
|
+
if (!bareSeries)
|
|
468
|
+
slots.push(language);
|
|
469
|
+
return [...slots, ...adjuncts].join(":").toLowerCase();
|
|
470
|
+
}
|
|
471
|
+
// Attach the URN generator to every registered IEC class (the base
|
|
472
|
+
// toUrn resolves constructor.urnGenerator).
|
|
473
|
+
class IecUrnGenerator {
|
|
474
|
+
identifier;
|
|
475
|
+
constructor(identifier) {
|
|
476
|
+
this.identifier = identifier;
|
|
477
|
+
}
|
|
478
|
+
generate() {
|
|
479
|
+
return iecToUrn(this.identifier);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
for (const klass of [
|
|
483
|
+
...Object.values(IEC_CLASSES.KIND_CLASSES),
|
|
484
|
+
IEC_CLASSES.SHEET_CLASS,
|
|
485
|
+
IEC_CLASSES.VAP_CLASS,
|
|
486
|
+
IEC_CLASSES.CONSOLIDATED_CLASS,
|
|
487
|
+
IEC_CLASSES.FRAGMENT_CLASS,
|
|
488
|
+
]) {
|
|
489
|
+
if (klass !== undefined) {
|
|
490
|
+
klass.urnGenerator = IecUrnGenerator;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
export function iecGrammarImplementation() {
|
|
494
|
+
const builder = new IecBuilder();
|
|
495
|
+
return {
|
|
496
|
+
parse(input) {
|
|
497
|
+
let tree;
|
|
498
|
+
try {
|
|
499
|
+
tree = parseGrammar(iecGrammar, preprocessIec(input));
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
throw new ParseFailed("IEC: parse failed", 0);
|
|
503
|
+
}
|
|
504
|
+
return builder.build(tree);
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { BaseIdentifier } from "../../model/identifier.js";
|
|
2
|
+
import type { IdentifierStatic } from "../../model/identifier.js";
|
|
3
|
+
import { Language, PubidDate } from "../../model/component.js";
|
|
4
|
+
/**
|
|
5
|
+
* IEC model classes (lib/pubid/iec/identifiers/*). The typed stage is a
|
|
6
|
+
* runtime attribute driving type derivation and the URN stage slot; the
|
|
7
|
+
* wire hash carries only the stage CODE for non-published drafts.
|
|
8
|
+
*/
|
|
9
|
+
export type IecTypeCode = "is" | "tr" | "ts" | "pas" | "srd" | "od" | "guide" | "ish" | "frag" | "trf" | "tec" | "wp" | "sttr" | "amd" | "cor";
|
|
10
|
+
export interface TypedStage {
|
|
11
|
+
code: string;
|
|
12
|
+
stageCode: string;
|
|
13
|
+
typeCode: IecTypeCode;
|
|
14
|
+
abbr: string;
|
|
15
|
+
/** Harmonized stage code for the URN "stage-…" slot (drafts only). */
|
|
16
|
+
harmonized?: string | undefined;
|
|
17
|
+
/** Grammar tokens that resolve to this stage (defaults to [abbr]). */
|
|
18
|
+
tokens?: string[] | undefined;
|
|
19
|
+
}
|
|
20
|
+
export declare const TYPED_STAGES: TypedStage[];
|
|
21
|
+
export declare function stageTokens(): string[];
|
|
22
|
+
export declare function locateStage(token: string): TypedStage | undefined;
|
|
23
|
+
export declare abstract class IecIdentifier extends BaseIdentifier {
|
|
24
|
+
readonly number: string | undefined;
|
|
25
|
+
/** Walk the supplement chain to the base document (BSI's URN
|
|
26
|
+
* generator reaches the root identity of adopted bases). */
|
|
27
|
+
root(): IecIdentifier;
|
|
28
|
+
readonly part: string | undefined;
|
|
29
|
+
readonly subpart: string | undefined;
|
|
30
|
+
readonly date: PubidDate | undefined;
|
|
31
|
+
readonly edition: string | undefined;
|
|
32
|
+
readonly languages: Language[];
|
|
33
|
+
readonly publisher: string | undefined;
|
|
34
|
+
readonly copublishers: string[];
|
|
35
|
+
readonly stage: string | undefined;
|
|
36
|
+
readonly all_parts: boolean | undefined;
|
|
37
|
+
readonly database: boolean | undefined;
|
|
38
|
+
readonly base: IecIdentifier | undefined;
|
|
39
|
+
/** The runtime typed stage — never serialized directly. */
|
|
40
|
+
typedStage: TypedStage | undefined;
|
|
41
|
+
/** Ruby SingleIdentifier#publisher_portion. */
|
|
42
|
+
publisherPortion(): string;
|
|
43
|
+
/** Ruby SingleIdentifier#number_portion. */
|
|
44
|
+
numberPortion(): string;
|
|
45
|
+
stageIterationValue(): string | undefined;
|
|
46
|
+
languagePortion(): string;
|
|
47
|
+
}
|
|
48
|
+
export declare const IEC_CLASSES: {
|
|
49
|
+
readonly KIND_CLASSES: Partial<Record<IecTypeCode, IdentifierStatic>>;
|
|
50
|
+
readonly SHEET_CLASS: IdentifierStatic;
|
|
51
|
+
readonly VAP_CLASS: IdentifierStatic;
|
|
52
|
+
readonly CONSOLIDATED_CLASS: IdentifierStatic;
|
|
53
|
+
readonly FRAGMENT_CLASS: IdentifierStatic;
|
|
54
|
+
};
|