@pubid/pubid 0.1.37 → 0.1.39
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/doi_pfs/index.d.ts +35 -0
- package/dist/flavors/doi_pfs/index.js +61 -0
- package/dist/flavors/ecma/index.js +35 -1
- package/dist/flavors/iec/grammar.js +39 -2
- package/dist/flavors/iec/index.js +110 -0
- package/dist/flavors/iec/model.js +29 -4
- package/dist/flavors/iho/index.js +126 -5
- package/dist/flavors/index.js +3 -0
- package/dist/flavors/iso/grammar.d.ts +5 -0
- package/dist/flavors/iso/grammar.js +68 -1
- package/dist/flavors/iso/index.js +65 -1
- package/dist/flavors/iso/model.js +7 -1
- package/dist/flavors/iso/stages.generated.js +1 -1
- package/dist/flavors/nist/index.js +36 -6
- package/dist/flavors/nist/model.js +34 -3
- package/dist/flavors/nist/preprocessor.js +11 -0
- package/dist/flavors/plateau/index.js +21 -2
- package/dist/pubid-urn/schemas/doi_pfs.d.ts +8 -0
- package/dist/pubid-urn/schemas/doi_pfs.js +14 -0
- package/dist/pubid-urn/schemas/iho.js +3 -0
- package/dist/pubid-urn/schemas/index.d.ts +1 -1
- package/dist/pubid-urn/schemas/index.js +3 -1
- package/dist/pubid-urn/schemas/plateau.js +1 -0
- package/package.json +1 -1
- package/vendor/parg/calconnect_pfs.json +1 -0
- package/vendor/parg/cen_pfs.json +1 -0
- package/vendor/parg/doi_pfs.json +1 -0
- package/vendor/parg/etsi_pfs.json +1 -0
- package/vendor/parg/iec.json +1 -1
- package/vendor/parg/iso.json +1 -1
- package/vendor/parg/nist.json +1 -1
package/artifact/corpus.json.gz
CHANGED
|
Binary file
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { FlavorImplementation } from "../../conformance/implementation.js";
|
|
2
|
+
import { BaseIdentifier } from "../../model/identifier.js";
|
|
3
|
+
/**
|
|
4
|
+
* The PFS-compiled doi flavor (pubid/pubid#706 §2.2) — one definition
|
|
5
|
+
* file, both engines: the grammar runtime is the SAME baked artifact the
|
|
6
|
+
* Ruby flavor runs (pubid-grammar artifacts/doi_pfs.json, compiled from
|
|
7
|
+
* definitions/doi.pfs), loaded through the artifact runtime, so grammar
|
|
8
|
+
* changes land once and this side cannot drift. Nothing is hand-rolled:
|
|
9
|
+
* the model is declared attributes (serialization derives from
|
|
10
|
+
* BaseIdentifier), the builder is the shared one, and the render body is
|
|
11
|
+
* the definition's canonical template. The parity gate
|
|
12
|
+
* (test/doi-pfs-parity.test.ts) pins byte-identity with the coded
|
|
13
|
+
* {@linkcode import("../doi").DoiIdentifier} over the shared conformance
|
|
14
|
+
* set.
|
|
15
|
+
*/
|
|
16
|
+
export declare class DoiPfsIdentifier extends BaseIdentifier {
|
|
17
|
+
/**
|
|
18
|
+
* The SCHEME-canonical wire tag (declared in the definition): to_hash
|
|
19
|
+
* is byte-identical with the coded flavor. Deliberately NOT registered
|
|
20
|
+
* in the global type map — one scheme, one `_type`, one global
|
|
21
|
+
* resolution (the coded class); {@linkcode fromHash} routes the tag
|
|
22
|
+
* back here flavor-locally, mirroring Ruby's DOI_TYPE_MAP.
|
|
23
|
+
*/
|
|
24
|
+
static polymorphicName: string;
|
|
25
|
+
static attributes: import("../../model/attribute.js").AttributeTable;
|
|
26
|
+
static mappings: import("../../model/attribute.js").FieldMapping[];
|
|
27
|
+
render(): string;
|
|
28
|
+
readonly prefix: string;
|
|
29
|
+
readonly suffix: string;
|
|
30
|
+
/** The alias's own from_hash: the scheme tag routes HERE, while the
|
|
31
|
+
* global resolution keeps answering the coded class (coexistence, not
|
|
32
|
+
* cutover). */
|
|
33
|
+
static fromHash(hash: Record<string, unknown>): BaseIdentifier;
|
|
34
|
+
}
|
|
35
|
+
export declare function doiPfsGrammarImplementation(): FlavorImplementation;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ParseFailed } from "../../grammar/engine.js";
|
|
2
|
+
import { PubidRuntime } from "../../parg/runtime.js";
|
|
3
|
+
import { BaseIdentifier } from "../../model/identifier.js";
|
|
4
|
+
import { extendAttributes, keyValue } from "../../model/attribute.js";
|
|
5
|
+
/**
|
|
6
|
+
* The PFS-compiled doi flavor (pubid/pubid#706 §2.2) — one definition
|
|
7
|
+
* file, both engines: the grammar runtime is the SAME baked artifact the
|
|
8
|
+
* Ruby flavor runs (pubid-grammar artifacts/doi_pfs.json, compiled from
|
|
9
|
+
* definitions/doi.pfs), loaded through the artifact runtime, so grammar
|
|
10
|
+
* changes land once and this side cannot drift. Nothing is hand-rolled:
|
|
11
|
+
* the model is declared attributes (serialization derives from
|
|
12
|
+
* BaseIdentifier), the builder is the shared one, and the render body is
|
|
13
|
+
* the definition's canonical template. The parity gate
|
|
14
|
+
* (test/doi-pfs-parity.test.ts) pins byte-identity with the coded
|
|
15
|
+
* {@linkcode import("../doi").DoiIdentifier} over the shared conformance
|
|
16
|
+
* set.
|
|
17
|
+
*/
|
|
18
|
+
export class DoiPfsIdentifier extends BaseIdentifier {
|
|
19
|
+
/**
|
|
20
|
+
* The SCHEME-canonical wire tag (declared in the definition): to_hash
|
|
21
|
+
* is byte-identical with the coded flavor. Deliberately NOT registered
|
|
22
|
+
* in the global type map — one scheme, one `_type`, one global
|
|
23
|
+
* resolution (the coded class); {@linkcode fromHash} routes the tag
|
|
24
|
+
* back here flavor-locally, mirroring Ruby's DOI_TYPE_MAP.
|
|
25
|
+
*/
|
|
26
|
+
static polymorphicName = "pubid:doi:resource";
|
|
27
|
+
static attributes = extendAttributes(BaseIdentifier, {
|
|
28
|
+
prefix: { type: "string" },
|
|
29
|
+
suffix: { type: "string" },
|
|
30
|
+
});
|
|
31
|
+
static mappings = keyValue({ wire: "prefix", to: "prefix" }, { wire: "suffix", to: "suffix" });
|
|
32
|
+
render() {
|
|
33
|
+
return `doi:${this.prefix}/${this.suffix}`;
|
|
34
|
+
}
|
|
35
|
+
/** The alias's own from_hash: the scheme tag routes HERE, while the
|
|
36
|
+
* global resolution keeps answering the coded class (coexistence, not
|
|
37
|
+
* cutover). */
|
|
38
|
+
static fromHash(hash) {
|
|
39
|
+
return new DoiPfsIdentifier(this.applyMappings(this.inflateScalarComponents(hash)));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The artifact's declared bindings (prefix -> prefix, suffix -> suffix)
|
|
44
|
+
* carry the whole printed prefix face ("10.1000") — the doi.pfs pattern
|
|
45
|
+
* captures the "10." face the coded grammar re-assembles — so the bound
|
|
46
|
+
* map lands on the model fields directly, no builder cast.
|
|
47
|
+
*/
|
|
48
|
+
let cachedRuntime;
|
|
49
|
+
export function doiPfsGrammarImplementation() {
|
|
50
|
+
return {
|
|
51
|
+
parse(input) {
|
|
52
|
+
cachedRuntime ??= PubidRuntime.load("doi_pfs");
|
|
53
|
+
const bound = cachedRuntime.parseAndBind(input);
|
|
54
|
+
if (bound === null || typeof bound !== "object") {
|
|
55
|
+
throw new ParseFailed("doi_pfs: the artifact rejected the input", 0);
|
|
56
|
+
}
|
|
57
|
+
const identifier = new DoiPfsIdentifier(bound);
|
|
58
|
+
return identifier;
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -106,11 +106,45 @@ class EcmaBuilder extends BaseBuilder {
|
|
|
106
106
|
return value;
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* The human edition spellings that alias the glued " ed<N>" token
|
|
111
|
+
* (pubid#698; ruby Ecma::Parser.normalize_input), in rewrite order.
|
|
112
|
+
* Each rule rewrites INTO the exact canonical face, so the parse (and
|
|
113
|
+
* therefore the render, the URN and the index key) is the one " ed<N>"
|
|
114
|
+
* already produces, and a canonical face passes through
|
|
115
|
+
* byte-identically.
|
|
116
|
+
*
|
|
117
|
+
* ECMA-262 Edition 5.1 the document cover's own form
|
|
118
|
+
* ECMA-262 ed. 5.1 dotted, spaced
|
|
119
|
+
* ECMA-262 ed 5.1 spaced, undotted
|
|
120
|
+
* ECMA-262 ed.5.1 dotted, unspaced
|
|
121
|
+
* ECMA 262 5.1 bare trailing edition
|
|
122
|
+
*
|
|
123
|
+
* " ed5.1" (no separator) is the grammar's own face and is left alone;
|
|
124
|
+
* a " vol<N>" number is never rewritten — the bare-trailing rule fires
|
|
125
|
+
* only behind a document number's own digit ("ECMA 262 5.1"), so the
|
|
126
|
+
* space-separator form itself ("ECMA 6") keeps its number and the
|
|
127
|
+
* grammar's ed-then-vol order is untouched.
|
|
128
|
+
*/
|
|
129
|
+
const EDITION_ALIASES = [
|
|
130
|
+
// "Edition <N>" starts mid-word, so the replacement carries no space
|
|
131
|
+
// of its own — the space before it is kept as-is.
|
|
132
|
+
[/\bedition[.\s]*(\d+(?:\.\d+)?)\b/gi, "ed$1"],
|
|
133
|
+
// "ed. <N>" / "ed <N>" / "ed.<N>": same mid-word shape. The glued
|
|
134
|
+
// "ed5.1" has no separator after "ed" and cannot match [.\s]+.
|
|
135
|
+
[/\bed[.\s]+(\d+(?:\.\d+)?)\b/g, "ed$1"],
|
|
136
|
+
// Bare trailing edition: fires only when a document number is already
|
|
137
|
+
// present (a digit directly before the space — "ECMA 262 5.1"). A
|
|
138
|
+
// glued "vol2" has no space, and a spaced "vol 2" has no digit before
|
|
139
|
+
// the space, so a volume number is never rewritten.
|
|
140
|
+
[/(\d) (\d+(?:\.\d+)?)$/, "$1 ed$2"],
|
|
141
|
+
];
|
|
109
142
|
export function ecmaGrammarImplementation() {
|
|
110
143
|
const builder = new EcmaBuilder();
|
|
111
144
|
return {
|
|
112
145
|
parse(input) {
|
|
113
|
-
const
|
|
146
|
+
const normalized = EDITION_ALIASES.reduce((acc, [pattern, replacement]) => acc.replace(pattern, replacement), input);
|
|
147
|
+
const tree = parseGrammar(ecmaGrammar, normalized);
|
|
114
148
|
if (typeof tree !== "object" || tree === null || Array.isArray(tree)) {
|
|
115
149
|
throw new ParseFailed("ECMA: unexpected parse tree", 0);
|
|
116
150
|
}
|
|
@@ -86,8 +86,9 @@ function buildRules() {
|
|
|
86
86
|
// --- supplements ---
|
|
87
87
|
const SUPP_ABBRS = [
|
|
88
88
|
"PRF Amd", "PWI Amd", "NP Amd", "ANW Amd", "WD Amd",
|
|
89
|
-
"PRF Cor", "PWI Cor", "NP Cor", "ANW Cor", "WD Cor", "CDCor", "WDCor",
|
|
90
|
-
"CDAM", "DAM", "FDAM", "
|
|
89
|
+
"PRF Cor", "PWI Cor", "NP Cor", "ANW Cor", "WD Cor", "CDCor", "CDCOR", "WDCor",
|
|
90
|
+
"CDAM", "DAM", "FDAM", "PDAM", "FPDAM", "DCOR", "FDCOR", "AMD", "Amd", "COR", "Cor",
|
|
91
|
+
"CDISH", "DISH", "ISH", "Ish",
|
|
91
92
|
];
|
|
92
93
|
rule("supplement_type_with_stage", () => SUPP_ABBRS.reduce((acc, a) => acc.or(str(a)), str(SUPP_ABBRS[0]))
|
|
93
94
|
.or(str("FRAG"))
|
|
@@ -155,6 +156,15 @@ function buildRules() {
|
|
|
155
156
|
.then(str("/"), rules["supplement_type_with_stage"])
|
|
156
157
|
.then(spaceQ, rules["second_part"], rules["third_part"]));
|
|
157
158
|
rule("supplement_identifier_no_third", () => rules["identifier_copublishers_no_third"].as("base")
|
|
159
|
+
// The [language] slot between the base designation and the
|
|
160
|
+
// supplement token: the renderer prints the BASE document's
|
|
161
|
+
// languages right after its date
|
|
162
|
+
// ("ISO/IEC 19772:2009(en)/DAM1:2019" — the rendered twin of the
|
|
163
|
+
// stored "ISO/IEC 19772:2009/DAM 1:2019(E)" input), and without
|
|
164
|
+
// the slot the canonical face could not re-parse. The capture
|
|
165
|
+
// lands on the base (the builder's supplement branch routes it
|
|
166
|
+
// there, like the supplement's own third_part language).
|
|
167
|
+
.then(rules["language"].maybe())
|
|
158
168
|
.then(str("/"), rules["supplement_type_with_stage"])
|
|
159
169
|
.then(spaceQ, rules["second_part"]));
|
|
160
170
|
rule("supplement_identifier", () => rules["supplement_identifier_no_third"].then(rules["third_part"]));
|
|
@@ -164,9 +174,36 @@ function buildRules() {
|
|
|
164
174
|
rule("standalone_supplement", () => rules["prefix_with_copublishers"]
|
|
165
175
|
.then(str("/"), rules["supplement_type_with_stage"])
|
|
166
176
|
.then(space, rules["second_part"], rules["third_part"]));
|
|
177
|
+
// --- the legacy DAM-family prefix face (the JTC1 ballot spelling;
|
|
178
|
+
// decode-to-modern, owner ruling 2026-11-12) ---
|
|
179
|
+
// The stage token precedes the base designation — "IEC/FDAM 60038-1",
|
|
180
|
+
// "ISO/IEC PDAM 12207", "ISO/IEC PDAM 2 10646:2012",
|
|
181
|
+
// "ISO/IEC FPDAM 9075/1", "ISO/IEC FDAM 1 14496-12:2004". A number
|
|
182
|
+
// between the token and the designation is the SUPPLEMENT number; the
|
|
183
|
+
// designation is the base's and is kept whole (unnumbered
|
|
184
|
+
// DAM/PDAM/FDAM/FPDAM means Amendment 1, and the old slash-part
|
|
185
|
+
// spelling "9075/1" carries the base's part). The date rides with the
|
|
186
|
+
// designation — old notation prints the base's year, and the draft
|
|
187
|
+
// supplement carries none of its own. Longest tokens first: FPDAM
|
|
188
|
+
// before PDAM, FDAM before DAM. The slash-part alternative precedes
|
|
189
|
+
// the dash-part one: the designation body also matches a bare number,
|
|
190
|
+
// so a slash-part input must be claimed first or the "/1" would
|
|
191
|
+
// dangle unclaimed.
|
|
192
|
+
rule("legacy_dam_token", () => str("FPDAM").or(str("PDAM"), str("FDAM"), str("DAM")).as("legacy_stage"));
|
|
193
|
+
// The copublisher captures as its own top-level scalar (the wasm
|
|
194
|
+
// binder scatters array bindings over the sibling list when a bare
|
|
195
|
+
// copublisher node rides a flat shape — the parg uses the same
|
|
196
|
+
// legacy_copublisher_item rule for that).
|
|
197
|
+
rule("legacy_copublisher_item", () => str("/").then(spaceQ, ORGS.reduce((acc, o) => acc.or(str(o)), str(ORGS[0])).as("legacy_copublisher")));
|
|
198
|
+
rule("legacy_base_designation", () => match("[0-9]").repeat(1, 6).as("legacy_base_number")
|
|
199
|
+
.then(str("/"), match("[0-9]").repeat(1, Infinity).as("legacy_base_part"))
|
|
200
|
+
.or(rules["number_with_part"].as("legacy_base_number_with_part")));
|
|
201
|
+
rule("legacy_supplement", () => rules["prefix_sole_publisher"]
|
|
202
|
+
.then(spaceQ, rules["legacy_copublisher_item"].maybe(), spaceQ, str("/").maybe(), rules["legacy_dam_token"], space, match("[0-9]").repeat(1, Infinity).as("legacy_supplement_number").then(space).maybe(), rules["legacy_base_designation"], (spaceQ.then(rules["date"])).maybe(), rules["third_part"]));
|
|
167
203
|
rule("identifier", () => rules["working_programme"]
|
|
168
204
|
.or(rules["technical_group"])
|
|
169
205
|
.or(rules["working_document"])
|
|
206
|
+
.or(rules["legacy_supplement"])
|
|
170
207
|
.or(rules["sheet_supplement_identifier"])
|
|
171
208
|
.or(rules["sheet_identifier"])
|
|
172
209
|
.or(rules["supplement_supplement_identifier"])
|
|
@@ -62,6 +62,20 @@ class IecBuilder {
|
|
|
62
62
|
delete tree["vap_suffix"];
|
|
63
63
|
tree["base"]["vap_suffix"] = vapSuffix;
|
|
64
64
|
}
|
|
65
|
+
// The third_part language rides the BASE document on every
|
|
66
|
+
// attached-supplement layer: the ruby grammar binds the layers'
|
|
67
|
+
// language unprefixed, the supplement field set keeps none
|
|
68
|
+
// (SUPP_KEYS), and the wire carries base.languages — the ruby
|
|
69
|
+
// render prints it inside the base portion
|
|
70
|
+
// ("ISO/IEC 19772:2009(en)/DAM1:2019"). Push it down before
|
|
71
|
+
// dispatching; the recursion re-applies it until it reaches the
|
|
72
|
+
// base. A standalone supplement (no base tree) keeps its own
|
|
73
|
+
// languages, like ruby's standalone slice.
|
|
74
|
+
const languages = tree["languages"];
|
|
75
|
+
if (languages !== undefined && isObj(tree["base"])) {
|
|
76
|
+
delete tree["languages"];
|
|
77
|
+
tree["base"]["languages"] = languages;
|
|
78
|
+
}
|
|
65
79
|
// --- working programme / working document ---
|
|
66
80
|
if (tree["wp_stage"] !== undefined)
|
|
67
81
|
return this.buildWorkingProgramme(tree);
|
|
@@ -71,6 +85,10 @@ class IecBuilder {
|
|
|
71
85
|
if (tree["technical_committee"] !== undefined) {
|
|
72
86
|
throw new ParseFailed("IEC: technical groups are not corpus-covered", 0);
|
|
73
87
|
}
|
|
88
|
+
// --- the legacy DAM-family prefix face (decode-to-modern) ---
|
|
89
|
+
if (tree["legacy_stage"] !== undefined) {
|
|
90
|
+
return this.buildLegacySupplement(tree);
|
|
91
|
+
}
|
|
74
92
|
// --- fragment of an amendment/corrigendum (third_part notation) ---
|
|
75
93
|
if (tree["fragment_type"] !== undefined && tree["base"] !== undefined) {
|
|
76
94
|
return this.buildFragment(tree);
|
|
@@ -153,6 +171,41 @@ class IecBuilder {
|
|
|
153
171
|
kind: kind,
|
|
154
172
|
attrs,
|
|
155
173
|
};
|
|
174
|
+
// The ensure_base decode (ruby SupplementIdentifier#ensure_base;
|
|
175
|
+
// the 2026-11-12 owner ruling reversing #693): a standalone draft
|
|
176
|
+
// face whose designation is designation-shaped ("IEC/DCOR 60038-1",
|
|
177
|
+
// "IEC CDISH 60050-191") moves the designation WHOLE onto a
|
|
178
|
+
// concrete InternationalStandard base — number, part, subpart and
|
|
179
|
+
// the date (old notation prints the base's year) — while the
|
|
180
|
+
// supplement keeps a number of its own, defaulting to 1 (the
|
|
181
|
+
// no-number rule: an unnumbered DCOR/DISH/CDISH means
|
|
182
|
+
// Corrigendum/Interpretation-Sheet 1). The concrete base is what
|
|
183
|
+
// makes the decode hash-match (and equal) the modern on-base parse
|
|
184
|
+
// of the same document. A supplement with no part (a true
|
|
185
|
+
// standalone like "IEC/AMD 1:2017") keeps its own number below.
|
|
186
|
+
if ((kind === "amd" || kind === "cor" || kind === "ish") &&
|
|
187
|
+
tree["base"] === undefined &&
|
|
188
|
+
attrs["part"] !== undefined) {
|
|
189
|
+
const baseAttrs = {};
|
|
190
|
+
for (const key of ["publisher", "copublishers", "number", "part", "subpart", "date"]) {
|
|
191
|
+
if (attrs[key] !== undefined)
|
|
192
|
+
baseAttrs[key] = attrs[key];
|
|
193
|
+
}
|
|
194
|
+
const base = new IEC_CLASSES.KIND_CLASSES["is"](baseAttrs);
|
|
195
|
+
const suppAttrs = { base, number: "1" };
|
|
196
|
+
// Supplements copy the delegated publisher/copublishers level
|
|
197
|
+
// (ruby reads them through the base; the stored wire copies the
|
|
198
|
+
// non-default level).
|
|
199
|
+
if (attrs["publisher"] !== undefined && attrs["publisher"] !== "IEC") {
|
|
200
|
+
suppAttrs["publisher"] = attrs["publisher"];
|
|
201
|
+
}
|
|
202
|
+
if (Array.isArray(attrs["copublishers"]) && attrs["copublishers"].length > 0) {
|
|
203
|
+
suppAttrs["copublishers"] = attrs["copublishers"];
|
|
204
|
+
}
|
|
205
|
+
const supplement = this.construct({ kind: kind, attrs: suppAttrs, typedStage }, suppAttrs);
|
|
206
|
+
supplement["base"] = base;
|
|
207
|
+
return supplement;
|
|
208
|
+
}
|
|
156
209
|
// Consolidated supplements (+AMD…+AMD…)
|
|
157
210
|
const consolidated = tree["consolidated_supplements"];
|
|
158
211
|
let identifier = this.construct(parsed, attrs);
|
|
@@ -219,6 +272,63 @@ class IecBuilder {
|
|
|
219
272
|
...(edition !== undefined ? { edition } : {}),
|
|
220
273
|
});
|
|
221
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* The legacy DAM-family prefix face (IEC/FDAM 60038-1, ISO/IEC PDAM
|
|
277
|
+
* 2 10646:2012, ISO/IEC FPDAM 9075/1): the JTC1 ballot spelling puts
|
|
278
|
+
* the stage token before the base designation. Decode onto the
|
|
279
|
+
* modern identity (owner ruling 2026-11-12): the designation is the
|
|
280
|
+
* BASE's, kept whole — the old slash-part spelling "9075/1" is base
|
|
281
|
+
* 9075 part 1 — and the supplement's number is the captured N,
|
|
282
|
+
* defaulting to 1 (the no-number rule). The date rides with the
|
|
283
|
+
* designation (old notation prints the base's year); a draft
|
|
284
|
+
* supplement carries none of its own. The base builds through the
|
|
285
|
+
* ordinary single-document path, so it is the same
|
|
286
|
+
* InternationalStandard the modern on-base parse holds and
|
|
287
|
+
* parse(legacy) == parse(modern).
|
|
288
|
+
*/
|
|
289
|
+
buildLegacySupplement(tree) {
|
|
290
|
+
const baseTree = {};
|
|
291
|
+
if (tree["publisher"] !== undefined)
|
|
292
|
+
baseTree["publisher"] = tree["publisher"];
|
|
293
|
+
if (tree["legacy_copublisher"] !== undefined) {
|
|
294
|
+
baseTree["copublishers"] = [s(tree["legacy_copublisher"])];
|
|
295
|
+
}
|
|
296
|
+
if (tree["legacy_base_number"] !== undefined) {
|
|
297
|
+
// The slash-part designation ("9075/1") captured number and part
|
|
298
|
+
// separately; they go on straight as the plain strings the
|
|
299
|
+
// retyped attributes are.
|
|
300
|
+
baseTree["number_with_part"] = {
|
|
301
|
+
number: s(tree["legacy_base_number"]),
|
|
302
|
+
...(s(tree["legacy_base_part"]) !== undefined ? { part: s(tree["legacy_base_part"]) } : {}),
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
else if (tree["legacy_base_number_with_part"] !== undefined) {
|
|
306
|
+
const designation = tree["legacy_base_number_with_part"];
|
|
307
|
+
baseTree["number_with_part"] = isObj(designation)
|
|
308
|
+
? flatten(designation)["number_with_part"]
|
|
309
|
+
: designation;
|
|
310
|
+
}
|
|
311
|
+
if (tree["date"] !== undefined)
|
|
312
|
+
baseTree["date"] = tree["date"];
|
|
313
|
+
if (tree["undated_marker"] !== undefined)
|
|
314
|
+
baseTree["undated_marker"] = tree["undated_marker"];
|
|
315
|
+
if (tree["edition"] !== undefined)
|
|
316
|
+
baseTree["edition"] = tree["edition"];
|
|
317
|
+
if (tree["languages"] !== undefined)
|
|
318
|
+
baseTree["languages"] = tree["languages"];
|
|
319
|
+
// The wrap: one amendment/corrigendum layer over the whole base;
|
|
320
|
+
// third_part's fragment notation rides the outermost layer.
|
|
321
|
+
const suppTree = {
|
|
322
|
+
base: baseTree,
|
|
323
|
+
type_with_stage: s(tree["legacy_stage"]),
|
|
324
|
+
number_with_part: { number: s(tree["legacy_supplement_number"]) ?? "1" },
|
|
325
|
+
};
|
|
326
|
+
if (tree["fragment_type"] !== undefined) {
|
|
327
|
+
suppTree["fragment_type"] = tree["fragment_type"];
|
|
328
|
+
suppTree["fragment_number"] = tree["fragment_number"];
|
|
329
|
+
}
|
|
330
|
+
return this.buildInner(suppTree);
|
|
331
|
+
}
|
|
222
332
|
buildSupplement(tree) {
|
|
223
333
|
const base = this.buildInner(tree["base"]);
|
|
224
334
|
const stageAbbr = s(tree["type_with_stage"]) ?? "";
|
|
@@ -44,8 +44,20 @@ export const TYPED_STAGES = [
|
|
|
44
44
|
TS("ndts", "ndts", "ts", "NDTS"),
|
|
45
45
|
TS("dpas", "dpas", "pas", "DPAS"),
|
|
46
46
|
TS("cdpas", "cdpas", "pas", "CDPAS"),
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
// The DAM family's ballot stages carry the CIRCULATION slots as the
|
|
48
|
+
// lead harmonized code (30.20/50.20), not ISO's 30.00/50.00 — CDAM/
|
|
49
|
+
// FDAM keep those (type, leading code) URN keys (gem Amendment
|
|
50
|
+
// registry; the codes are the per-typed-stage keys the wire
|
|
51
|
+
// serializes: pdam, damd, fdamd, fpdam).
|
|
52
|
+
TS("pdam", "cd", "amd", "PDAM", "30.20"),
|
|
53
|
+
TS("damd", "damd", "amd", "DAM", "40.00"),
|
|
54
|
+
TS("fdamd", "fdamd", "amd", "FDAM", "50.00", ["FDAM", "PRF Amd"]),
|
|
55
|
+
TS("fpdam", "fdamd", "amd", "FPDAM", "50.20"),
|
|
56
|
+
// "CDCOR" is the all-caps spelling the attached-supplement renderer
|
|
57
|
+
// emits (abbr.first upcase) when a legacy prefix face decodes onto a
|
|
58
|
+
// CDCor draft; appended as a token so the canonical render re-parses
|
|
59
|
+
// (spelling-only: abbr stays "CDCor").
|
|
60
|
+
TS("cdcor", "cd", "cor", "CDCor", "30.00", ["CDCor", "CDCOR"]),
|
|
49
61
|
TS("dcor", "dcor", "cor", "DCOR"),
|
|
50
62
|
TS("fdcor", "fdcor", "cor", "FDCOR"),
|
|
51
63
|
TS("cdish", "cdish", "ish", "CDISH"),
|
|
@@ -311,6 +323,10 @@ function iecSupplementClass(kind) {
|
|
|
311
323
|
static attributes = extendAttributes(BaseIdentifier, {
|
|
312
324
|
base: { type: IecIdentifier },
|
|
313
325
|
number: { type: "string" },
|
|
326
|
+
// The draft stage rides the wire as the typed stage's code
|
|
327
|
+
// ("stage: fdamd") — the single classes declare the same attr
|
|
328
|
+
// (SINGLE_ATTRS.stage); construct sets it on the parse path.
|
|
329
|
+
stage: { type: "string" },
|
|
314
330
|
synthetic_base: { type: "boolean", default: false },
|
|
315
331
|
});
|
|
316
332
|
static mappings = keyValue({ wire: "number", to: "number" }, { wire: "year", to: "date", toWire: (m) => m["date"]?.year, fromWire: yearFromWire }, { wire: "month", to: "date", toWire: (m) => m["date"]?.month, fromWire: () => undefined }, { wire: "day", to: "date", toWire: (m) => m["date"]?.day, fromWire: () => undefined }, { wire: "edition", to: "edition" }, {
|
|
@@ -326,7 +342,11 @@ function iecSupplementClass(kind) {
|
|
|
326
342
|
return list !== undefined && list.length > 0 ? list : undefined;
|
|
327
343
|
},
|
|
328
344
|
fromWire: (h) => (Array.isArray(h["copublishers"]) ? h["copublishers"].map(String) : undefined),
|
|
329
|
-
},
|
|
345
|
+
},
|
|
346
|
+
// The draft stage rides the wire as the typed stage's code
|
|
347
|
+
// ("stage: fdamd"); the single classes map the same key (ruby
|
|
348
|
+
// serializes typed_stage.code and restores it in from_hash).
|
|
349
|
+
{ wire: "stage", to: "stage" }, { wire: "base", to: "base" });
|
|
330
350
|
render() {
|
|
331
351
|
const base = this.base;
|
|
332
352
|
if (base === undefined || this.synthetic_base === true) {
|
|
@@ -340,7 +360,12 @@ function iecSupplementClass(kind) {
|
|
|
340
360
|
return result;
|
|
341
361
|
}
|
|
342
362
|
let result = base.render();
|
|
343
|
-
|
|
363
|
+
// The attached-supplement renderer prints the typed stage's own
|
|
364
|
+
// abbreviation, upcased (ruby renderer.rb: abbr.first.upcase) —
|
|
365
|
+
// a published Amendment renders "/AMD1", a draft stage its own
|
|
366
|
+
// token ("/FDAM1", "/DCOR1", "/CDCOR1" from the "CDCor" entry's
|
|
367
|
+
// spelling-only upcase).
|
|
368
|
+
result += `/${(this.typedStage?.abbr ?? abbr).toUpperCase()}${this.number ?? ""}`;
|
|
344
369
|
if (this.date !== undefined)
|
|
345
370
|
result += `:${this.date.render()}`;
|
|
346
371
|
if (this.edition !== undefined)
|
|
@@ -43,7 +43,28 @@ function buildRules() {
|
|
|
43
43
|
rule("part_edition_rule", () => space.then(digits.then(dot, digits, dot, digits).as("part_edition")));
|
|
44
44
|
rule("part_year_rule", () => space.then(str("(").then(year4.as("part_year"), str(")"))));
|
|
45
45
|
rule("part_dated", () => rules["part_edition_rule"].then(rules["part_year_rule"].maybe()));
|
|
46
|
-
|
|
46
|
+
// --- S-4 loose-leaf sections and specifications (issue #691) ---
|
|
47
|
+
// The spec number's vocabulary: the S-4 sections run 100-600 (Part C
|
|
48
|
+
// 100-500), so the leaf number is 3 digits with hundreds digit 1-6 —
|
|
49
|
+
// the same shape the bare citation branch leans on to keep the B
|
|
50
|
+
// Bibliographic series (B-1..B-13 style) distinct from B-4xx specs.
|
|
51
|
+
const specShape = match("[1-6]").then(match("[0-9]").repeat(2, 2));
|
|
52
|
+
// The citation letter is the spec's part (A/B/C); the builder binds
|
|
53
|
+
// it into `part` and rejects a conflicting Part-rule capture.
|
|
54
|
+
rule("spec_citation_body", () => str("A").or(str("B"), str("C")).as("spec_part")
|
|
55
|
+
.then(dash, specShape.as("spec"))
|
|
56
|
+
.then(dot.then(digits.as("spec_sub")).maybe())
|
|
57
|
+
.then(str("(").then(match("[A-Z]").repeat(1, Infinity).as("spec_variant"), str(")")).maybe()));
|
|
58
|
+
// The compact anchor form: the citation follows the code after one
|
|
59
|
+
// space ("IHO S-4 B-401").
|
|
60
|
+
rule("spec_citation", () => space.then(rules["spec_citation_body"]));
|
|
61
|
+
// The spelled section long form: "Part B, Section 400" — the spelled
|
|
62
|
+
// number becomes the spec (the section-level citation), and a
|
|
63
|
+
// trailing citation (", Section 400, B-401") makes it a cross-check
|
|
64
|
+
// only.
|
|
65
|
+
rule("part_section_tail", () => str(",").then(space, str("Section"), space, specShape.as("spec_section"))
|
|
66
|
+
.then(str(",").then(space, rules["spec_citation_body"]).maybe()));
|
|
67
|
+
rule("part", () => space.then(str("Part")).then(space, rules["part_value"], rules["part_dated"].maybe(), rules["part_section_tail"].maybe()));
|
|
47
68
|
rule("supplement_edition_rule", () => space.then(digits.then(dot, digits, dot, digits).as("supplement_edition")));
|
|
48
69
|
rule("supplement_year_rule", () => space.then(str("(").then(year4.as("supplement_year"), str(")"))));
|
|
49
70
|
rule("supplement_dated", () => rules["supplement_edition_rule"].then(rules["supplement_year_rule"].maybe()));
|
|
@@ -79,8 +100,16 @@ function buildRules() {
|
|
|
79
100
|
rule("identifier", () => str("IHO")
|
|
80
101
|
.then(space)
|
|
81
102
|
.maybe()
|
|
82
|
-
.then(rules["series"], dash, rules["number"], rules["compact_part"].maybe(), rules["glued"].maybe(), rules["volume_short"].maybe(), rules["dated"].maybe(), rules["supplement"].maybe(), rules["part"].maybe(), rules["annex"].maybe(), rules["appendix"].maybe(), rules["serial"].maybe(), rules["year_qualifier"].maybe()));
|
|
83
|
-
|
|
103
|
+
.then(rules["series"], dash, rules["number"], rules["compact_part"].maybe(), rules["glued"].maybe(), rules["volume_short"].maybe(), rules["dated"].maybe(), rules["supplement"].maybe(), rules["part"].maybe(), rules["spec_citation"].maybe(), rules["annex"].maybe(), rules["appendix"].maybe(), rules["serial"].maybe(), rules["year_qualifier"].maybe()));
|
|
104
|
+
// The bare citation branch (the entry's FIRST alternative): "B-401" /
|
|
105
|
+
// "IHO B-401" — implicit S-4 anchor (the builder synthesizes series S
|
|
106
|
+
// number 4). B only: bare C-x stays the Circular Letter series, and
|
|
107
|
+
// the 3-digit [1-6]xx shape keeps the B Bibliographic series (B-12)
|
|
108
|
+
// on the main branch.
|
|
109
|
+
rule("bare_spec_citation", () => str("IHO").then(space).maybe()
|
|
110
|
+
.then(str("B").as("spec_part"), dash, specShape.as("spec"), dot.then(digits.as("spec_sub")).maybe(), str("(").then(match("[A-Z]").repeat(1, Infinity).as("spec_variant"), str(")")).maybe()));
|
|
111
|
+
rule("identifier_entry", () => rules["bare_spec_citation"].or(rules["identifier"]));
|
|
112
|
+
rule("root", () => rules["identifier_entry"]);
|
|
84
113
|
return rules;
|
|
85
114
|
}
|
|
86
115
|
export const ihoGrammar = { rules: buildRules(), root: "root" };
|
|
@@ -115,8 +144,15 @@ function ihoClass(kind) {
|
|
|
115
144
|
volume_edition: { type: "string" },
|
|
116
145
|
volume_year: { type: "string" },
|
|
117
146
|
issue: { type: "string" },
|
|
147
|
+
// The S-4 loose-leaf spec citation (issue #691): spec is the leaf
|
|
148
|
+
// number, the dotted sub and the parenthesized variant its
|
|
149
|
+
// children. The citation letter rides `part`; the spelled section
|
|
150
|
+
// is resolve-only (never an atom).
|
|
151
|
+
spec: { type: "string" },
|
|
152
|
+
spec_sub: { type: "string" },
|
|
153
|
+
spec_variant: { type: "string" },
|
|
118
154
|
});
|
|
119
|
-
static mappings = keyValue({ wire: "number", to: "number" }, { wire: "part", to: "part" }, { wire: "part_edition", to: "part_edition" }, { wire: "part_year", to: "part_year" }, { wire: "annex", to: "annex" }, { wire: "annex_edition", to: "annex_edition" }, { wire: "annex_year", to: "annex_year" }, { wire: "appendix", to: "appendix" }, { wire: "appendix_subnumber", to: "appendix_subnumber" }, { wire: "appendix_edition", to: "appendix_edition" }, { wire: "appendix_year", to: "appendix_year" }, { wire: "supplement", to: "supplement" }, { wire: "supplement_edition", to: "supplement_edition" }, { wire: "supplement_year", to: "supplement_year" }, { wire: "edition", to: "edition" }, { wire: "year", to: "year" }, { wire: "volume", to: "volume" }, { wire: "volume_edition", to: "volume_edition" }, { wire: "volume_year", to: "volume_year" }, { wire: "issue", to: "issue" });
|
|
155
|
+
static mappings = keyValue({ wire: "number", to: "number" }, { wire: "part", to: "part" }, { wire: "part_edition", to: "part_edition" }, { wire: "part_year", to: "part_year" }, { wire: "annex", to: "annex" }, { wire: "annex_edition", to: "annex_edition" }, { wire: "annex_year", to: "annex_year" }, { wire: "appendix", to: "appendix" }, { wire: "appendix_subnumber", to: "appendix_subnumber" }, { wire: "appendix_edition", to: "appendix_edition" }, { wire: "appendix_year", to: "appendix_year" }, { wire: "supplement", to: "supplement" }, { wire: "supplement_edition", to: "supplement_edition" }, { wire: "supplement_year", to: "supplement_year" }, { wire: "edition", to: "edition" }, { wire: "year", to: "year" }, { wire: "volume", to: "volume" }, { wire: "volume_edition", to: "volume_edition" }, { wire: "volume_year", to: "volume_year" }, { wire: "issue", to: "issue" }, { wire: "spec", to: "spec" }, { wire: "spec_sub", to: "spec_sub" }, { wire: "spec_variant", to: "spec_variant" });
|
|
120
156
|
render() {
|
|
121
157
|
let result = `IHO ${letter}-${this.number}`;
|
|
122
158
|
if (this.edition !== undefined)
|
|
@@ -128,7 +164,17 @@ function ihoClass(kind) {
|
|
|
128
164
|
if (this.supplement_year !== undefined)
|
|
129
165
|
result += ` (${this.supplement_year})`;
|
|
130
166
|
}
|
|
131
|
-
if (this.
|
|
167
|
+
if (this.spec !== undefined) {
|
|
168
|
+
// The S-4 citation face replaces the Part face (same
|
|
169
|
+
// containment slot: the citation letter IS the part):
|
|
170
|
+
// "B-401", "B-122.1", "B-521(I)".
|
|
171
|
+
result += ` ${this.part}-${this.spec}`;
|
|
172
|
+
if (this.spec_sub !== undefined)
|
|
173
|
+
result += `.${this.spec_sub}`;
|
|
174
|
+
if (this.spec_variant !== undefined)
|
|
175
|
+
result += `(${this.spec_variant})`;
|
|
176
|
+
}
|
|
177
|
+
else if (this.part !== undefined) {
|
|
132
178
|
result += ` Part ${this.part}`;
|
|
133
179
|
if (this.part_edition !== undefined)
|
|
134
180
|
result += ` ${this.part_edition}`;
|
|
@@ -192,6 +238,17 @@ function ihoClass(kind) {
|
|
|
192
238
|
if (id.part_year !== undefined)
|
|
193
239
|
parts.push(`year-${id.part_year}`);
|
|
194
240
|
}
|
|
241
|
+
// The S-4 spec citation: a child of its part (the citation letter
|
|
242
|
+
// rides the part- atom), the dotted sub and the parenthesized
|
|
243
|
+
// variant its own children. The spelled SECTION is NOT emitted —
|
|
244
|
+
// derivable from the spec's hundreds digit.
|
|
245
|
+
if (id.spec !== undefined) {
|
|
246
|
+
parts.push(`spec-${id.spec}`);
|
|
247
|
+
if (id.spec_sub !== undefined)
|
|
248
|
+
parts.push(`specsub-${id.spec_sub}`);
|
|
249
|
+
if (id.spec_variant !== undefined)
|
|
250
|
+
parts.push(`specvar-${id.spec_variant}`);
|
|
251
|
+
}
|
|
195
252
|
if (id.annex !== undefined) {
|
|
196
253
|
parts.push(`annex-${id.annex}`);
|
|
197
254
|
if (id.annex_edition !== undefined)
|
|
@@ -235,6 +292,18 @@ const KIND_CLASSES = {
|
|
|
235
292
|
};
|
|
236
293
|
const LETTER_TO_CLASS = new Map(Object.keys(KIND_LETTERS).map((kind) => [KIND_LETTERS[kind], KIND_CLASSES[kind]]));
|
|
237
294
|
class IhoBuilder extends BaseBuilder {
|
|
295
|
+
// The S-4 anchor: the loose-leaf publication every spec citation
|
|
296
|
+
// resolves through (bare "B-401" carries it implicitly).
|
|
297
|
+
static S4_TYPE = "S";
|
|
298
|
+
static S4_NUMBER = "4";
|
|
299
|
+
// A citation letter is one of S-4's lettered Parts; the section
|
|
300
|
+
// vocabulary (100-600, Part C 100-500) is theirs.
|
|
301
|
+
static SPEC_LETTERS = ["A", "B", "C"];
|
|
302
|
+
// Components an S-4 spec citation cannot carry: the citation is a
|
|
303
|
+
// leaf of the Part, not a wrappable publication.
|
|
304
|
+
static SPEC_FOREIGN_COMPONENTS = [
|
|
305
|
+
"supplement", "annex", "appendix", "appendix_subnumber", "volume", "issue",
|
|
306
|
+
];
|
|
238
307
|
selectClass(data) {
|
|
239
308
|
const kind = LETTER_TO_CLASS.get(String(data["type"]));
|
|
240
309
|
if (kind === undefined) {
|
|
@@ -264,6 +333,58 @@ class IhoBuilder extends BaseBuilder {
|
|
|
264
333
|
delete flat[yearKey];
|
|
265
334
|
}
|
|
266
335
|
}
|
|
336
|
+
// The S-4 spec citation resolution (ruby resolve_spec_citation!):
|
|
337
|
+
// the citation letter folds into `part` (a Part-rule capture must
|
|
338
|
+
// agree), and the long form's spelled section becomes the spec
|
|
339
|
+
// alone — beside a citation it only cross-checks the citation's
|
|
340
|
+
// derived section ("Part B, Section 400, B-401" stores spec 401;
|
|
341
|
+
// "Part B, Section 500, B-401" is rejected).
|
|
342
|
+
const letter = flat["spec_part"];
|
|
343
|
+
const spelled = flat["spec_section"];
|
|
344
|
+
delete flat["spec_part"];
|
|
345
|
+
delete flat["spec_section"];
|
|
346
|
+
if (letter !== undefined) {
|
|
347
|
+
if (flat["part"] === undefined || flat["part"] === letter) {
|
|
348
|
+
flat["part"] = letter;
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
throw new ParseFailed(`IHO: the citation letter ${String(letter)} conflicts with part ${String(flat["part"])}`, 0);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (spelled !== undefined) {
|
|
355
|
+
if (flat["spec"] === undefined) {
|
|
356
|
+
flat["spec"] = spelled;
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
const section = `${String(flat["spec"])[0]}00`;
|
|
360
|
+
if (spelled !== section) {
|
|
361
|
+
throw new ParseFailed(`IHO: the spelled section ${String(spelled)} disagrees with the citation ${String(flat["part"])}-${String(flat["spec"])}`, 0);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (flat["spec"] !== undefined) {
|
|
366
|
+
if (!IhoBuilder.SPEC_LETTERS.includes(String(flat["part"]))) {
|
|
367
|
+
throw new ParseFailed(`IHO: an S-4 spec citation needs a lettered Part (A/B/C), not ${JSON.stringify(flat["part"])}`, 0);
|
|
368
|
+
}
|
|
369
|
+
if (flat["part_edition"] !== undefined || flat["part_year"] !== undefined) {
|
|
370
|
+
throw new ParseFailed("IHO: a spec citation rides the S-4 edition, not a part edition", 0);
|
|
371
|
+
}
|
|
372
|
+
const foreign = IhoBuilder.SPEC_FOREIGN_COMPONENTS.filter((k) => flat[k] !== undefined);
|
|
373
|
+
if (foreign.length > 0) {
|
|
374
|
+
throw new ParseFailed(`IHO: an S-4 spec citation is a leaf of its Part and cannot carry ${foreign.join(", ")}`, 0);
|
|
375
|
+
}
|
|
376
|
+
// The series letter: a bare citation branch parse has no series
|
|
377
|
+
// capture, and its anchor is implicitly the S-4 publication (the
|
|
378
|
+
// number binds with it). An explicit series must BE the anchor.
|
|
379
|
+
if (flat["type"] === undefined) {
|
|
380
|
+
flat["type"] = IhoBuilder.S4_TYPE;
|
|
381
|
+
flat["number"] = IhoBuilder.S4_NUMBER;
|
|
382
|
+
}
|
|
383
|
+
else if (flat["type"] !== IhoBuilder.S4_TYPE ||
|
|
384
|
+
flat["number"] !== IhoBuilder.S4_NUMBER) {
|
|
385
|
+
throw new ParseFailed(`IHO: an S-4 spec citation requires the S-4 anchor: ${String(flat["type"])}-${String(flat["number"])} cannot carry spec ${String(flat["spec"])}`, 0);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
267
388
|
return super.build(flat);
|
|
268
389
|
}
|
|
269
390
|
cast(key, value) {
|
package/dist/flavors/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { lookupUrnParser, parseUrnFor } from "../urn/parsers.js";
|
|
|
2
2
|
import { oimlGrammarImplementation } from "./oiml/implementation.js";
|
|
3
3
|
import { xsfGrammarImplementation } from "./xsf/index.js";
|
|
4
4
|
import { doiGrammarImplementation } from "./doi/index.js";
|
|
5
|
+
import { doiPfsGrammarImplementation } from "./doi_pfs/index.js";
|
|
5
6
|
import { ianaGrammarImplementation } from "./iana/index.js";
|
|
6
7
|
import { unGrammarImplementation } from "./un/index.js";
|
|
7
8
|
import { plateauGrammarImplementation } from "./plateau/index.js";
|
|
@@ -81,6 +82,8 @@ function grammarImplementationSwitch(flavor) {
|
|
|
81
82
|
return xsfGrammarImplementation();
|
|
82
83
|
case "doi":
|
|
83
84
|
return doiGrammarImplementation();
|
|
85
|
+
case "doi_pfs":
|
|
86
|
+
return doiPfsGrammarImplementation();
|
|
84
87
|
case "iana":
|
|
85
88
|
return ianaGrammarImplementation();
|
|
86
89
|
case "un":
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import { Grammar } from "../../grammar/engine.js";
|
|
2
|
+
/** The DAM-family ballot stage codes (grammar table
|
|
3
|
+
* typed_stages_ballots: PDAM, DAM, DAmd, FDAM, FDAmd, FPDAM — exactly
|
|
4
|
+
* these entries' abbrs); the TYPE-first ballot branch stays scoped to
|
|
5
|
+
* the ballot family. */
|
|
6
|
+
export declare const BALLOT_STAGE_CODES: string[];
|
|
2
7
|
export declare const isoGrammar: Grammar;
|