@pubid/pubid 0.1.31 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifact/corpus.json.gz +0 -0
- package/dist/flavors/iec/index.js +12 -5
- package/dist/flavors/ieee/index.js +321 -113
- package/dist/flavors/ieee/model.d.ts +18 -0
- package/dist/flavors/ieee/model.js +62 -8
- package/dist/flavors/ieee/preprocessor.js +3 -0
- package/dist/flavors/ieee/update_codes.js +1 -1
- package/dist/flavors/iho/index.js +198 -35
- package/dist/flavors/jis/index.js +74 -2
- package/dist/flavors/nist/grammar.js +44 -16
- package/dist/flavors/nist/index.js +17 -2
- package/dist/urn/parsers.js +57 -20
- package/package.json +2 -2
- package/vendor/parg/adobe.json +1 -1
- package/vendor/parg/aiee.json +1 -1
- package/vendor/parg/amca.json +1 -1
- package/vendor/parg/ansi.json +1 -1
- package/vendor/parg/api.json +1 -1
- package/vendor/parg/ashrae.json +1 -1
- package/vendor/parg/asme.json +1 -1
- package/vendor/parg/astm.json +1 -1
- package/vendor/parg/bipm.json +1 -1
- package/vendor/parg/bsi.json +1 -1
- package/vendor/parg/calconnect.json +1 -1
- package/vendor/parg/ccsds.json +1 -1
- package/vendor/parg/cen_cenelec.json +1 -1
- package/vendor/parg/cie.json +1 -1
- package/vendor/parg/csa.json +1 -1
- package/vendor/parg/doi.json +1 -1
- package/vendor/parg/easc.json +1 -1
- package/vendor/parg/ecma.json +1 -1
- package/vendor/parg/etsi.json +1 -1
- package/vendor/parg/evs.json +1 -1
- package/vendor/parg/gb.json +1 -1
- package/vendor/parg/gost.json +1 -1
- package/vendor/parg/iala.json +1 -1
- package/vendor/parg/iana.json +1 -1
- package/vendor/parg/idf.json +1 -1
- package/vendor/parg/iec.json +1 -1
- package/vendor/parg/ieee.json +1 -29571
- package/vendor/parg/ietf.json +1 -1
- package/vendor/parg/iho.json +1 -0
- package/vendor/parg/ire.json +1 -1
- package/vendor/parg/isbn.json +1 -1
- package/vendor/parg/iso.json +1 -8510
- package/vendor/parg/itu.json +1 -1
- package/vendor/parg/jcgm.json +1 -1
- package/vendor/parg/jis.json +1 -1
- package/vendor/parg/nesc.json +1 -1
- package/vendor/parg/nist.json +1 -9588
- package/vendor/parg/oasis.json +1 -1
- package/vendor/parg/ogc.json +1 -1
- package/vendor/parg/oiml.json +1 -1
- package/vendor/parg/omg.json +1 -1
- package/vendor/parg/parg.json +1 -1
- package/vendor/parg/plateau.json +1 -1
- package/vendor/parg/sae.json +1 -1
- package/vendor/parg/tgpp.json +1 -1
- package/vendor/parg/un.json +1 -1
- package/vendor/parg/w3c.json +1 -1
- package/vendor/parg/xsf.json +1 -1
|
@@ -36,9 +36,27 @@ export declare class IeeeCode {
|
|
|
36
36
|
readonly number: string;
|
|
37
37
|
readonly parts: string[];
|
|
38
38
|
readonly originalSeparator: string | undefined;
|
|
39
|
+
/** The separator BEFORE each part, when the source mixed them
|
|
40
|
+
* ("7.4-3-2" — dot after the number, dashes before the subparts).
|
|
41
|
+
* Runtime-only: the serialized columns keep the single
|
|
42
|
+
* original_separator, and only a fresh parse knows the per-part
|
|
43
|
+
* spelling (the reference's Components::Code#original_separators). */
|
|
44
|
+
readonly originalSeparators: string[] | undefined;
|
|
39
45
|
constructor(attrs: Record<string, unknown>);
|
|
40
46
|
static parse(codeStr: string): IeeeCode | undefined;
|
|
47
|
+
/** The printed face. The reference's Components::Code#to_s keeps the
|
|
48
|
+
* separator each part was printed with ("7.4-3-2"), and the corpus
|
|
49
|
+
* canonically pins the printed spelling (the project-draft rows freeze
|
|
50
|
+
* "IEEE P7-4.3.2/D2" with the dashed spelling as the input alias), so
|
|
51
|
+
* this face renders per-part like the Ruby reference. */
|
|
41
52
|
render(): string;
|
|
53
|
+
/** The canonical face: one uniform separator across every part
|
|
54
|
+
* boundary (the first distinct one the source printed) — the face the
|
|
55
|
+
* URN segment, the joint IEEE/ISO faces and the reassembled PSI code
|
|
56
|
+
* render, so spellings that differ only in part separators share one
|
|
57
|
+
* identity. */
|
|
58
|
+
canonicalRender(): string;
|
|
59
|
+
private renderCode;
|
|
42
60
|
}
|
|
43
61
|
export declare const DRAFT_MONTH_NAMES: Record<string, string>;
|
|
44
62
|
export declare class IeeeDraft {
|
|
@@ -64,11 +64,20 @@ export class IeeeCode {
|
|
|
64
64
|
number;
|
|
65
65
|
parts;
|
|
66
66
|
originalSeparator;
|
|
67
|
+
/** The separator BEFORE each part, when the source mixed them
|
|
68
|
+
* ("7.4-3-2" — dot after the number, dashes before the subparts).
|
|
69
|
+
* Runtime-only: the serialized columns keep the single
|
|
70
|
+
* original_separator, and only a fresh parse knows the per-part
|
|
71
|
+
* spelling (the reference's Components::Code#original_separators). */
|
|
72
|
+
originalSeparators;
|
|
67
73
|
constructor(attrs) {
|
|
68
74
|
this.prefix = attrs["prefix"];
|
|
69
75
|
this.number = String(attrs["number"] ?? "");
|
|
70
76
|
this.parts = Array.isArray(attrs["parts"]) ? attrs["parts"] : [];
|
|
71
77
|
this.originalSeparator = attrs["original_separator"];
|
|
78
|
+
this.originalSeparators = Array.isArray(attrs["original_separators"])
|
|
79
|
+
? attrs["original_separators"]
|
|
80
|
+
: undefined;
|
|
72
81
|
}
|
|
73
82
|
static parse(codeStr) {
|
|
74
83
|
if (codeStr === "")
|
|
@@ -83,14 +92,37 @@ export class IeeeCode {
|
|
|
83
92
|
separator = "-";
|
|
84
93
|
const components = remainder.split(/[.-]/);
|
|
85
94
|
const number = components.shift() ?? "";
|
|
95
|
+
// The separators AS PRINTED, one per part boundary — a code may mix
|
|
96
|
+
// them ("7.4-3-2": dot after the number, dashes before the
|
|
97
|
+
// subparts), and to_s re-joins with these instead of the uniform
|
|
98
|
+
// original_separator when they were captured.
|
|
99
|
+
const separators = remainder.match(/[.-]/g) ?? [];
|
|
100
|
+
const perPart = separators.length === components.length ? separators : undefined;
|
|
86
101
|
return new IeeeCode({
|
|
87
102
|
prefix,
|
|
88
103
|
number,
|
|
89
104
|
parts: components,
|
|
90
105
|
original_separator: separator,
|
|
106
|
+
original_separators: perPart,
|
|
91
107
|
});
|
|
92
108
|
}
|
|
109
|
+
/** The printed face. The reference's Components::Code#to_s keeps the
|
|
110
|
+
* separator each part was printed with ("7.4-3-2"), and the corpus
|
|
111
|
+
* canonically pins the printed spelling (the project-draft rows freeze
|
|
112
|
+
* "IEEE P7-4.3.2/D2" with the dashed spelling as the input alias), so
|
|
113
|
+
* this face renders per-part like the Ruby reference. */
|
|
93
114
|
render() {
|
|
115
|
+
return this.renderCode(true);
|
|
116
|
+
}
|
|
117
|
+
/** The canonical face: one uniform separator across every part
|
|
118
|
+
* boundary (the first distinct one the source printed) — the face the
|
|
119
|
+
* URN segment, the joint IEEE/ISO faces and the reassembled PSI code
|
|
120
|
+
* render, so spellings that differ only in part separators share one
|
|
121
|
+
* identity. */
|
|
122
|
+
canonicalRender() {
|
|
123
|
+
return this.renderCode(false);
|
|
124
|
+
}
|
|
125
|
+
renderCode(perPart) {
|
|
94
126
|
let result = "";
|
|
95
127
|
if (this.prefix !== undefined)
|
|
96
128
|
result += this.prefix;
|
|
@@ -111,8 +143,14 @@ export class IeeeCode {
|
|
|
111
143
|
separator = ".";
|
|
112
144
|
}
|
|
113
145
|
}
|
|
114
|
-
for (const part of this.parts)
|
|
115
|
-
|
|
146
|
+
for (const [idx, part] of this.parts.entries()) {
|
|
147
|
+
// A fresh parse of mixed-separator codes ("7.4-3-2") carries the
|
|
148
|
+
// separator each part was printed with; the canonical face (and
|
|
149
|
+
// an unknown per-part spelling — the serialized column shape)
|
|
150
|
+
// falls back to the uniform original separator.
|
|
151
|
+
const sep = (perPart && this.originalSeparators?.[idx]) || separator;
|
|
152
|
+
result += sep + part;
|
|
153
|
+
}
|
|
116
154
|
}
|
|
117
155
|
return result;
|
|
118
156
|
}
|
|
@@ -764,7 +802,10 @@ class IeeeUrnGenerator extends BaseUrnGenerator {
|
|
|
764
802
|
? ["ieee_identifier", "primary_identifier", "first_identifier", "base"]
|
|
765
803
|
: ["ieee_identifier", "primary_identifier", "first_identifier"];
|
|
766
804
|
const wrapped = fallbacks.map((k) => self[k]).find((v) => v instanceof IeeeIdentifier);
|
|
767
|
-
|
|
805
|
+
// The URN is spelling-stable: codes printed with mixed part
|
|
806
|
+
// separators ("7.4-3-2") share one identity with their uniform
|
|
807
|
+
// spelling, so the segment renders canonically, not as printed.
|
|
808
|
+
return (wrapped ?? this.identifier).codeObj?.canonicalRender();
|
|
768
809
|
}
|
|
769
810
|
yearComponent() {
|
|
770
811
|
const id = this.identifier;
|
|
@@ -858,12 +899,19 @@ function ieeeClass(spec, opts = {}) {
|
|
|
858
899
|
return (opts.render ?? renderIeeeBase)(this);
|
|
859
900
|
}
|
|
860
901
|
get codeObj() {
|
|
902
|
+
// The parse path carries the freshly parsed Code (with its per-part
|
|
903
|
+
// separator spelling — the reference's `code:` string →
|
|
904
|
+
// Components::Code.parse in base #initialize); only the from_hash
|
|
905
|
+
// rebuild falls back to the split columns (uniform separator).
|
|
906
|
+
const self = this;
|
|
907
|
+
const runtime = self["__code_obj"];
|
|
908
|
+
if (runtime instanceof IeeeCode)
|
|
909
|
+
return runtime;
|
|
861
910
|
// Only CodeNumber leaves carry code columns; on a supplement the
|
|
862
911
|
// `number` attribute is the ORDINAL (Ruby SupplementIdentifier#code
|
|
863
912
|
// delegates to base, code_obj stays nil).
|
|
864
913
|
if (opts.codeColumns !== true)
|
|
865
914
|
return undefined;
|
|
866
|
-
const self = this;
|
|
867
915
|
const number = self["number"];
|
|
868
916
|
if (typeof number !== "string" || number === "")
|
|
869
917
|
return undefined;
|
|
@@ -927,7 +975,9 @@ export const SiStandardClass = ieeeClass({ kind: "si-standard" }, {
|
|
|
927
975
|
const isPsi = (id.typed_stage?.abbr ?? []).includes("PSI");
|
|
928
976
|
parts.push(isPsi ? "PSI" : "SI");
|
|
929
977
|
const codeObj = id.codeObj;
|
|
930
|
-
|
|
978
|
+
// Canonical code: the PSI code is reassembled from parse-tree
|
|
979
|
+
// captures (synthetic separators), so the face spells uniformly.
|
|
980
|
+
let codePart = codeObj?.canonicalRender() ?? "";
|
|
931
981
|
const draftObj = id.draftObject();
|
|
932
982
|
if (draftObj !== undefined)
|
|
933
983
|
codePart += `/D${draftObj.version}`;
|
|
@@ -1166,7 +1216,10 @@ export const JointDevelopmentClass = ieeeClass({ kind: "joint-development" }, {
|
|
|
1166
1216
|
parts.push(publishers.join("/"));
|
|
1167
1217
|
if (stageWord !== "")
|
|
1168
1218
|
parts.push(stageWord);
|
|
1169
|
-
|
|
1219
|
+
// Canonical (uniform-separator) code: a joint document parses
|
|
1220
|
+
// from both the dotted IEEE face and the dashed IEC face, and
|
|
1221
|
+
// both spellings must render — and URN — identically.
|
|
1222
|
+
let codeStr = codeObj?.canonicalRender() ?? "";
|
|
1170
1223
|
// A P-prefixed joint project prints its parts dot-joined on the
|
|
1171
1224
|
// ISO face ("ISO/IEEE DIS P15500.1"), whatever separator the
|
|
1172
1225
|
// parse captured ("IEC/IEEE FDIS P60214-2-2018(E)" aliases in).
|
|
@@ -1214,8 +1267,9 @@ export const JointDevelopmentClass = ieeeClass({ kind: "joint-development" }, {
|
|
|
1214
1267
|
if (draftStatus !== undefined)
|
|
1215
1268
|
parts.push(draftStatus);
|
|
1216
1269
|
// The P-state prints as spelled (P = project draft; no P =
|
|
1217
|
-
// standard). Never added or stripped.
|
|
1218
|
-
|
|
1270
|
+
// standard). Never added or stripped. Canonical code for the same
|
|
1271
|
+
// both-faces reason as the ISO face above.
|
|
1272
|
+
let codeStr = codeObj?.canonicalRender() ?? "";
|
|
1219
1273
|
let designatorCarriesYear = false;
|
|
1220
1274
|
if (ieeeDraft !== undefined && ieeeDraft.startsWith("D=") && id.year !== undefined) {
|
|
1221
1275
|
// The ordinal-less stage draft's canonical face (the
|
|
@@ -503,6 +503,9 @@ export function preprocessIeee(input) {
|
|
|
503
503
|
cleaned = cleaned.replaceAll("Edn.", "Edition");
|
|
504
504
|
cleaned = cleaned.replaceAll("ASHARE", "ASHRAE");
|
|
505
505
|
cleaned = cleaned.replace(/^PC(\d)/, "P$1");
|
|
506
|
+
// A two-digit draft designator spelled without its D ("IEEE P1052/08,
|
|
507
|
+
// September 2018"): the corpus face is the D ordinal ("…/D08").
|
|
508
|
+
cleaned = cleaned.replace(/^IEEE\s+(P[\d.-]+)\/(\d{2}),\s+([A-Za-z]+)\s+(\d{4})$/, "IEEE $1/D$2, $3 $4");
|
|
506
509
|
cleaned = cleaned.replace(/^(IEEE\s+C?\d[\d.]*\/D\d+)([A-Z][a-z]+\s+\d{4})/, "$1, $2");
|
|
507
510
|
cleaned = cleaned.replace(/^(ANSI\/IEEE Std):\s+.*$/, "$1");
|
|
508
511
|
cleaned = cleaned.replace(/^(IEEE\s+[\d.]+)\s+(IEC\s+\d+[-\d]*\s+.*edition\s+\d{4}-\d{2})$/i, "$1; $2");
|
|
@@ -112,7 +112,7 @@ export const UPDATE_CODES = [
|
|
|
112
112
|
{ plain: "ISO/IEC13210: 1994 (E) ANSI/IEEE Std 1003.3-1991", replacement: "ISO/IEC 13210:1994 (E); ANSI/IEEE Std 1003.3-1991" },
|
|
113
113
|
{ plain: "J-STD-016-1995", replacement: "IEEE 016-1995" },
|
|
114
114
|
{ plain: "Nuclear EQ Sourcebook and Supplement", replacement: "IEEE 7438946" },
|
|
115
|
-
{ plain: "PC37.30.2/D043 Rev 18, May 2015", replacement: "IEEE PC37.30
|
|
115
|
+
{ plain: "PC37.30.2/D043 Rev 18, May 2015", replacement: "IEEE PC37.30.2/D-043/R-18-2015" },
|
|
116
116
|
{ plain: "IEEE/IEC P62582 CD2 proposal, May 2017", replacement: "IEEE/IEC CD2 P62582-2017-05" },
|
|
117
117
|
{ plain: "ISO/IEC/IEEE P16326:201x WD.4a, July 2017", replacement: "ISO/IEC/IEEE P16326:2017/D=WD.4a" },
|
|
118
118
|
{ plain: "ISO/IEC/IEEE CD.1 P21839, October 2017", replacement: "ISO/IEC/IEEE CD1 P21839-2017-10" },
|
|
@@ -20,54 +20,150 @@ function buildRules() {
|
|
|
20
20
|
const dash = str("-");
|
|
21
21
|
const dot = str(".");
|
|
22
22
|
const digits = match("[0-9]").repeat(1, Infinity);
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const letters = match("[A-Za-z]").repeat(0, Infinity);
|
|
24
|
+
const seriesLetter = str("S").or(str("P"), str("M"), str("B"), str("C"));
|
|
25
|
+
const year4 = str("19").or(str("20")).then(match("[0-9]").repeat(2));
|
|
26
|
+
rule("series", () => seriesLetter.as("type"));
|
|
27
|
+
rule("number_suffix", () => str(":").then(digits));
|
|
25
28
|
rule("number", () => digits.then(rules["number_suffix"].maybe()).as("number"));
|
|
29
|
+
// The compact part face, hyphenated onto the code: S-5-B (lettered),
|
|
30
|
+
// S-100-1 (numbered) and S-158-S-102 (series reference).
|
|
31
|
+
rule("compact_part", () => dash.then(seriesLetter
|
|
32
|
+
.then(dash, digits)
|
|
33
|
+
.or(digits.then(letters), match("[A-Z]"))
|
|
34
|
+
.as("part")));
|
|
35
|
+
// LEGACY glued lettered bases (S-5A): parse-only alias, rendered as
|
|
36
|
+
// "Part A".
|
|
37
|
+
rule("glued", () => match("[A-Z]").as("part"));
|
|
38
|
+
rule("edition", () => space.then(digits.then(dot, digits, dot, digits).as("edition")));
|
|
39
|
+
rule("v_edition", () => space.then(str("v").then(digits.then(str(".").then(digits).maybe()).as("edition_shorthand"))));
|
|
40
|
+
rule("year_qualifier", () => space.then(str("(").then(year4.as("year"), str(")"))));
|
|
41
|
+
rule("part_series_ref", () => seriesLetter.then(dash, digits));
|
|
42
|
+
rule("part_value", () => rules["part_series_ref"].or(digits.then(letters), match("[A-Z]")).as("part"));
|
|
43
|
+
rule("part_edition_rule", () => space.then(digits.then(dot, digits, dot, digits).as("part_edition")));
|
|
44
|
+
rule("part_year_rule", () => space.then(str("(").then(year4.as("part_year"), str(")"))));
|
|
45
|
+
rule("part_dated", () => rules["part_edition_rule"].then(rules["part_year_rule"].maybe()));
|
|
46
|
+
rule("part", () => space.then(str("Part")).then(space, rules["part_value"], rules["part_dated"].maybe()));
|
|
47
|
+
rule("supplement_edition_rule", () => space.then(digits.then(dot, digits, dot, digits).as("supplement_edition")));
|
|
48
|
+
rule("supplement_year_rule", () => space.then(str("(").then(year4.as("supplement_year"), str(")"))));
|
|
49
|
+
rule("supplement_dated", () => rules["supplement_edition_rule"].then(rules["supplement_year_rule"].maybe()));
|
|
50
|
+
rule("supplement", () => space
|
|
51
|
+
.then(str("Suppl").or(str("Sup")))
|
|
52
|
+
.then(space, digits.as("supplement"), rules["supplement_dated"].maybe()));
|
|
53
|
+
rule("annex_edition_rule", () => space.then(digits.then(dot, digits, dot, digits).as("annex_edition")));
|
|
54
|
+
rule("annex_year_rule", () => space.then(str("(").then(year4.as("annex_year"), str(")"))));
|
|
55
|
+
rule("annex_dated", () => rules["annex_edition_rule"].then(rules["annex_year_rule"].maybe()));
|
|
56
|
+
rule("annex", () => space.then(str("Annex")).then(space, match("[A-Z]").as("annex"), rules["annex_dated"].maybe()));
|
|
57
|
+
rule("appendix_value", () => match("[A-Z]").or(digits).as("appendix"));
|
|
58
|
+
rule("appendix_subnumber", () => dash.then(digits.as("appendix_subnumber")));
|
|
59
|
+
rule("appendix_edition_rule", () => space.then(digits.then(dot, digits, dot, digits).as("appendix_edition")));
|
|
60
|
+
rule("appendix_year_rule", () => space.then(str("(").then(year4.as("appendix_year"), str(")"))));
|
|
61
|
+
rule("appendix_dated", () => rules["appendix_edition_rule"].then(rules["appendix_year_rule"].maybe()));
|
|
26
62
|
rule("appendix", () => space
|
|
27
|
-
.then(str("Appendix").or(str("Ap.")))
|
|
28
|
-
.then(space,
|
|
29
|
-
rule("
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
63
|
+
.then(str("Appendix").or(str("App"), str("Ap.")))
|
|
64
|
+
.then(space, rules["appendix_value"], rules["appendix_subnumber"].maybe(), rules["appendix_dated"].maybe()));
|
|
65
|
+
rule("volume_issue", () => space
|
|
66
|
+
.then(str("Issue"))
|
|
67
|
+
.then(space, digits.as("issue"))
|
|
68
|
+
.or(space.then(str("("), digits.as("issue"), str(")"))));
|
|
69
|
+
rule("volume_edition_rule", () => space.then(digits.then(dot, digits, dot, digits).as("volume_edition")));
|
|
70
|
+
rule("volume_year_rule", () => space.then(str("(").then(year4.as("volume_year"), str(")"))));
|
|
71
|
+
rule("volume_dated", () => rules["volume_edition_rule"].then(rules["volume_year_rule"].maybe()));
|
|
72
|
+
rule("volume", () => space.then(str("Vol.")).then(space, digits.as("volume"), rules["volume_issue"].maybe()));
|
|
73
|
+
// The volume's short face — the slash chain ("IHO P-1/21").
|
|
74
|
+
rule("volume_short", () => str("/").then(digits.as("volume"), rules["volume_dated"].maybe()));
|
|
75
|
+
// LEGACY bare year ("IHO P-5 2024"): the yearbook year IS a volume.
|
|
76
|
+
rule("volume_bare_year", () => space.then(year4.as("volume")));
|
|
77
|
+
rule("serial", () => rules["volume"].or(rules["volume_bare_year"]).then(rules["volume_dated"].maybe()));
|
|
78
|
+
rule("dated", () => rules["edition"].or(rules["v_edition"]).then(rules["year_qualifier"].maybe()));
|
|
33
79
|
rule("identifier", () => str("IHO")
|
|
34
80
|
.then(space)
|
|
35
81
|
.maybe()
|
|
36
|
-
.then(rules["series"], dash, rules["number"])
|
|
37
|
-
.then(rules["appendix"].maybe())
|
|
38
|
-
.then(rules["part"].maybe())
|
|
39
|
-
.then(rules["annex"].maybe())
|
|
40
|
-
.then(rules["supplement"].maybe())
|
|
41
|
-
.then(rules["version"].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()));
|
|
42
83
|
rule("root", () => rules["identifier"]);
|
|
43
84
|
return rules;
|
|
44
85
|
}
|
|
45
86
|
export const ihoGrammar = { rules: buildRules(), root: "root" };
|
|
87
|
+
function padEditionShorthand(value) {
|
|
88
|
+
const segments = value.split(".");
|
|
89
|
+
while (segments.length < 3)
|
|
90
|
+
segments.push("0");
|
|
91
|
+
return segments.join(".");
|
|
92
|
+
}
|
|
46
93
|
function ihoClass(kind) {
|
|
47
94
|
const letter = KIND_LETTERS[kind];
|
|
48
95
|
class IhoKindIdentifier extends BaseIdentifier {
|
|
49
96
|
static polymorphicName = `pubid:iho:${kind}`;
|
|
50
97
|
static attributes = extendAttributes(BaseIdentifier, {
|
|
51
98
|
number: { type: "string" },
|
|
52
|
-
appendix: { type: "string" },
|
|
53
99
|
part: { type: "string" },
|
|
100
|
+
part_edition: { type: "string" },
|
|
101
|
+
part_year: { type: "string" },
|
|
54
102
|
annex: { type: "string" },
|
|
103
|
+
annex_edition: { type: "string" },
|
|
104
|
+
annex_year: { type: "string" },
|
|
105
|
+
appendix: { type: "string" },
|
|
106
|
+
appendix_subnumber: { type: "string" },
|
|
107
|
+
appendix_edition: { type: "string" },
|
|
108
|
+
appendix_year: { type: "string" },
|
|
55
109
|
supplement: { type: "string" },
|
|
56
|
-
|
|
110
|
+
supplement_edition: { type: "string" },
|
|
111
|
+
supplement_year: { type: "string" },
|
|
112
|
+
edition: { type: "string" },
|
|
113
|
+
year: { type: "string" },
|
|
114
|
+
volume: { type: "string" },
|
|
115
|
+
volume_edition: { type: "string" },
|
|
116
|
+
volume_year: { type: "string" },
|
|
117
|
+
issue: { type: "string" },
|
|
57
118
|
});
|
|
58
|
-
static mappings = keyValue({ wire: "number", to: "number" }, { wire: "
|
|
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" });
|
|
59
120
|
render() {
|
|
60
121
|
let result = `IHO ${letter}-${this.number}`;
|
|
61
|
-
if (this.
|
|
62
|
-
result += `
|
|
63
|
-
if (this.
|
|
122
|
+
if (this.edition !== undefined)
|
|
123
|
+
result += ` ${this.edition}`;
|
|
124
|
+
if (this.supplement !== undefined) {
|
|
125
|
+
result += ` Sup ${this.supplement}`;
|
|
126
|
+
if (this.supplement_edition !== undefined)
|
|
127
|
+
result += ` ${this.supplement_edition}`;
|
|
128
|
+
if (this.supplement_year !== undefined)
|
|
129
|
+
result += ` (${this.supplement_year})`;
|
|
130
|
+
}
|
|
131
|
+
if (this.part !== undefined) {
|
|
64
132
|
result += ` Part ${this.part}`;
|
|
65
|
-
|
|
133
|
+
if (this.part_edition !== undefined)
|
|
134
|
+
result += ` ${this.part_edition}`;
|
|
135
|
+
if (this.part_year !== undefined)
|
|
136
|
+
result += ` (${this.part_year})`;
|
|
137
|
+
}
|
|
138
|
+
if (this.annex !== undefined) {
|
|
66
139
|
result += ` Annex ${this.annex}`;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
140
|
+
if (this.annex_edition !== undefined)
|
|
141
|
+
result += ` ${this.annex_edition}`;
|
|
142
|
+
if (this.annex_year !== undefined)
|
|
143
|
+
result += ` (${this.annex_year})`;
|
|
144
|
+
}
|
|
145
|
+
if (this.appendix !== undefined) {
|
|
146
|
+
result += ` Appendix ${this.appendix}`;
|
|
147
|
+
if (this.appendix_subnumber !== undefined)
|
|
148
|
+
result += `-${this.appendix_subnumber}`;
|
|
149
|
+
if (this.appendix_edition !== undefined)
|
|
150
|
+
result += ` ${this.appendix_edition}`;
|
|
151
|
+
if (this.appendix_year !== undefined)
|
|
152
|
+
result += ` (${this.appendix_year})`;
|
|
153
|
+
}
|
|
154
|
+
if (this.volume !== undefined) {
|
|
155
|
+
result += ` Vol. ${this.volume}`;
|
|
156
|
+
if (this.issue !== undefined)
|
|
157
|
+
result += ` (${this.issue})`;
|
|
158
|
+
if (this.volume_edition !== undefined)
|
|
159
|
+
result += ` ${this.volume_edition}`;
|
|
160
|
+
if (this.volume_year !== undefined)
|
|
161
|
+
result += ` (${this.volume_year})`;
|
|
162
|
+
}
|
|
163
|
+
// The base year qualifier prints last, parenthesized ("IHO
|
|
164
|
+
// S-100 1.0.0 Part A (2024)").
|
|
165
|
+
if (this.year !== undefined)
|
|
166
|
+
result += ` (${this.year})`;
|
|
71
167
|
return result;
|
|
72
168
|
}
|
|
73
169
|
}
|
|
@@ -75,16 +171,54 @@ function ihoClass(kind) {
|
|
|
75
171
|
generate() {
|
|
76
172
|
const id = this.identifier;
|
|
77
173
|
const parts = ["urn", "iho", letter.toLowerCase(), id.number];
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
174
|
+
// Each component is a marker-prefixed segment; its own edition
|
|
175
|
+
// (ed-) and year (year-) follow it directly. The base edition
|
|
176
|
+
// and year come first (they attach to the base node).
|
|
177
|
+
if (id.edition !== undefined)
|
|
178
|
+
parts.push(`ed-${id.edition}`);
|
|
179
|
+
if (id.year !== undefined)
|
|
180
|
+
parts.push(`year-${id.year}`);
|
|
181
|
+
if (id.supplement !== undefined) {
|
|
182
|
+
parts.push(`sup-${id.supplement}`);
|
|
183
|
+
if (id.supplement_edition !== undefined)
|
|
184
|
+
parts.push(`ed-${id.supplement_edition}`);
|
|
185
|
+
if (id.supplement_year !== undefined)
|
|
186
|
+
parts.push(`year-${id.supplement_year}`);
|
|
187
|
+
}
|
|
188
|
+
if (id.part !== undefined) {
|
|
189
|
+
parts.push(`part-${id.part}`);
|
|
190
|
+
if (id.part_edition !== undefined)
|
|
191
|
+
parts.push(`ed-${id.part_edition}`);
|
|
192
|
+
if (id.part_year !== undefined)
|
|
193
|
+
parts.push(`year-${id.part_year}`);
|
|
194
|
+
}
|
|
195
|
+
if (id.annex !== undefined) {
|
|
196
|
+
parts.push(`annex-${id.annex}`);
|
|
197
|
+
if (id.annex_edition !== undefined)
|
|
198
|
+
parts.push(`ed-${id.annex_edition}`);
|
|
199
|
+
if (id.annex_year !== undefined)
|
|
200
|
+
parts.push(`year-${id.annex_year}`);
|
|
201
|
+
}
|
|
202
|
+
if (id.appendix !== undefined) {
|
|
203
|
+
parts.push(`appendix-${id.appendix}`);
|
|
204
|
+
// The subnumber is a bare child segment directly after the
|
|
205
|
+
// appendix ("appendix-D:2").
|
|
206
|
+
if (id.appendix_subnumber !== undefined)
|
|
207
|
+
parts.push(id.appendix_subnumber);
|
|
208
|
+
if (id.appendix_edition !== undefined)
|
|
209
|
+
parts.push(`ed-${id.appendix_edition}`);
|
|
210
|
+
if (id.appendix_year !== undefined)
|
|
211
|
+
parts.push(`year-${id.appendix_year}`);
|
|
212
|
+
}
|
|
213
|
+
if (id.volume !== undefined) {
|
|
214
|
+
parts.push(`vol-${id.volume}`);
|
|
215
|
+
if (id.issue !== undefined)
|
|
216
|
+
parts.push(`issue-${id.issue}`);
|
|
217
|
+
if (id.volume_edition !== undefined)
|
|
218
|
+
parts.push(`ed-${id.volume_edition}`);
|
|
219
|
+
if (id.volume_year !== undefined)
|
|
220
|
+
parts.push(`year-${id.volume_year}`);
|
|
221
|
+
}
|
|
88
222
|
return parts.join(":");
|
|
89
223
|
}
|
|
90
224
|
}
|
|
@@ -111,6 +245,35 @@ class IhoBuilder extends BaseBuilder {
|
|
|
111
245
|
defaultIdentifierClass() {
|
|
112
246
|
return KIND_CLASSES["standard"];
|
|
113
247
|
}
|
|
248
|
+
build(data) {
|
|
249
|
+
const flat = Array.isArray(data)
|
|
250
|
+
? Object.assign({}, ...data)
|
|
251
|
+
: { ...data };
|
|
252
|
+
// A component year without its component edition attaches to the
|
|
253
|
+
// nearest preceding edition node — the base ("IHO S-100 1.0.0
|
|
254
|
+
// Part A (2024)" binds the base year, never part_year). With the
|
|
255
|
+
// component's own edition present it stays component-scoped
|
|
256
|
+
// ("IHO S-98 Part A 1.0.0 (2024)" keeps part_year).
|
|
257
|
+
for (const component of ["part", "supplement", "annex", "appendix", "volume"]) {
|
|
258
|
+
const yearKey = `${component}_year`;
|
|
259
|
+
if (flat[yearKey] !== undefined &&
|
|
260
|
+
flat[`${component}_edition`] === undefined &&
|
|
261
|
+
flat["edition"] !== undefined) {
|
|
262
|
+
if (flat["year"] === undefined)
|
|
263
|
+
flat["year"] = flat[yearKey];
|
|
264
|
+
delete flat[yearKey];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return super.build(flat);
|
|
268
|
+
}
|
|
269
|
+
cast(key, value) {
|
|
270
|
+
if (key === "type")
|
|
271
|
+
return null; // the kind is encoded in _type
|
|
272
|
+
if (key === "edition_shorthand") {
|
|
273
|
+
return { edition: padEditionShorthand(String(value)) };
|
|
274
|
+
}
|
|
275
|
+
return value;
|
|
276
|
+
}
|
|
114
277
|
}
|
|
115
278
|
export function ihoGrammarImplementation() {
|
|
116
279
|
const builder = new IhoBuilder();
|
|
@@ -11,6 +11,10 @@ import { BaseUrnGenerator } from "../../model/urn-generator.js";
|
|
|
11
11
|
* code ("B 0205-1") lives in the series/number/parts columns and is
|
|
12
12
|
* composed on demand. URN: urn:jis:<code>[:year][:lang]["all"]? with a
|
|
13
13
|
* supplement notation appended lowercased.
|
|
14
|
+
*
|
|
15
|
+
* WES (welding-engineering-standard, pubid#638) rides the same flavor:
|
|
16
|
+
* "WES <number>[-parts][:year[R]]…" with no series letter and no JIS
|
|
17
|
+
* prefix — the code itself carries the "WES" root.
|
|
14
18
|
*/
|
|
15
19
|
function buildRules() {
|
|
16
20
|
const rules = {};
|
|
@@ -55,7 +59,19 @@ function buildRules() {
|
|
|
55
59
|
.then(rules["all_parts"].maybe())
|
|
56
60
|
.then(rules["supplement"].maybe())
|
|
57
61
|
.then(rules["symbol_clause"].maybe()));
|
|
58
|
-
|
|
62
|
+
// WES (Japan Welding Engineering Society) rides the JIS flavor with the
|
|
63
|
+
// same tail but no prefix and no series letter; the root tries it first,
|
|
64
|
+
// as the reference grammar does.
|
|
65
|
+
rule("wes_identifier", () => str("WES").as("wes")
|
|
66
|
+
.then(space)
|
|
67
|
+
.then(rules["number"])
|
|
68
|
+
.then(rules["parts"])
|
|
69
|
+
.then(rules["year"].maybe())
|
|
70
|
+
.then(rules["language"].maybe())
|
|
71
|
+
.then(rules["all_parts"].maybe())
|
|
72
|
+
.then(rules["supplement"].maybe())
|
|
73
|
+
.then(rules["symbol_clause"].maybe()));
|
|
74
|
+
rule("root", () => rules["wes_identifier"].or(rules["identifier"]));
|
|
59
75
|
return rules;
|
|
60
76
|
}
|
|
61
77
|
export const jisGrammar = { rules: buildRules(), root: "root" };
|
|
@@ -151,6 +167,45 @@ const KIND_CLASSES = {
|
|
|
151
167
|
corrigendum: jisClass("corrigendum"),
|
|
152
168
|
explanation: jisClass("explanation"),
|
|
153
169
|
};
|
|
170
|
+
// WES 7700-1:2019 — JSA sells Welding Engineering Standards next to JIS,
|
|
171
|
+
// so they parse through the JIS flavor, but the id carries no series
|
|
172
|
+
// letter and no JIS prefix ("WES" is the root of the designation).
|
|
173
|
+
function wesClass() {
|
|
174
|
+
class JisWesIdentifier extends JisIdentifier {
|
|
175
|
+
static polymorphicName = "pubid:jis:welding-engineering-standard";
|
|
176
|
+
static attributes = extendAttributes(BaseIdentifier, {
|
|
177
|
+
number: { type: "string" },
|
|
178
|
+
parts: { type: "string", collection: true },
|
|
179
|
+
year: { type: "integer" },
|
|
180
|
+
language: { type: "string" },
|
|
181
|
+
all_parts: { type: "boolean", default: false },
|
|
182
|
+
reaffirmed: { type: "boolean" },
|
|
183
|
+
symbol: { type: "string" },
|
|
184
|
+
});
|
|
185
|
+
static mappings = keyValue({ wire: "number", to: "number" }, { wire: "parts", to: "parts" }, { wire: "year", to: "year" }, { wire: "language", to: "language" }, { wire: "all_parts", to: "all_parts" }, { wire: "reaffirmed", to: "reaffirmed" }, { wire: "symbol", to: "symbol" });
|
|
186
|
+
code() {
|
|
187
|
+
if (this.base !== undefined)
|
|
188
|
+
return this.base.code();
|
|
189
|
+
let result = `WES ${this.number}`;
|
|
190
|
+
if (this.parts !== undefined)
|
|
191
|
+
result += this.parts.map((p) => `-${p}`).join("");
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
render() {
|
|
195
|
+
let result = this.code() ?? "";
|
|
196
|
+
if (this.year !== undefined)
|
|
197
|
+
result += `:${this.yearWithReaffirmation()}`;
|
|
198
|
+
if (this.language !== undefined)
|
|
199
|
+
result += `(${this.language})`;
|
|
200
|
+
if (this.all_parts === true)
|
|
201
|
+
result += "(規格群)";
|
|
202
|
+
return result + this.symbolSuffix();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
registerType(JisWesIdentifier);
|
|
206
|
+
return JisWesIdentifier;
|
|
207
|
+
}
|
|
208
|
+
const WES_CLASS = wesClass();
|
|
154
209
|
class JisUrnGenerator extends BaseUrnGenerator {
|
|
155
210
|
generate() {
|
|
156
211
|
const id = this.identifier;
|
|
@@ -171,7 +226,7 @@ class JisUrnGenerator extends BaseUrnGenerator {
|
|
|
171
226
|
return parts.join(":");
|
|
172
227
|
}
|
|
173
228
|
}
|
|
174
|
-
for (const klass of Object.values(KIND_CLASSES)) {
|
|
229
|
+
for (const klass of [...Object.values(KIND_CLASSES), WES_CLASS]) {
|
|
175
230
|
klass.urnGenerator = JisUrnGenerator;
|
|
176
231
|
}
|
|
177
232
|
function isObj(v) {
|
|
@@ -213,6 +268,8 @@ class JisBuilder {
|
|
|
213
268
|
return out;
|
|
214
269
|
}
|
|
215
270
|
buildSingle(tree) {
|
|
271
|
+
if (tree["wes"] !== undefined)
|
|
272
|
+
return this.buildWes(tree);
|
|
216
273
|
const type = tree["type"] === undefined || tree["type"] === null ? undefined : String(tree["type"]);
|
|
217
274
|
const kind = type === "TR" ? "technical-report" : type === "TS" ? "technical-specification" : "japanese-industrial-standard";
|
|
218
275
|
const attrs = {
|
|
@@ -230,6 +287,21 @@ class JisBuilder {
|
|
|
230
287
|
attrs["reaffirmed"] = true;
|
|
231
288
|
return new KIND_CLASSES[kind](attrs);
|
|
232
289
|
}
|
|
290
|
+
buildWes(tree) {
|
|
291
|
+
const attrs = {
|
|
292
|
+
number: String(tree["number"]),
|
|
293
|
+
parts: extractParts(tree["parts"]),
|
|
294
|
+
};
|
|
295
|
+
if (tree["year"] !== undefined && tree["year"] !== null)
|
|
296
|
+
attrs["year"] = Number(tree["year"]);
|
|
297
|
+
if (tree["language"] !== undefined && tree["language"] !== null)
|
|
298
|
+
attrs["language"] = String(tree["language"]);
|
|
299
|
+
if (tree["all_parts"] !== undefined)
|
|
300
|
+
attrs["all_parts"] = true;
|
|
301
|
+
if (tree["reaffirmed"] !== undefined && tree["reaffirmed"] !== null)
|
|
302
|
+
attrs["reaffirmed"] = true;
|
|
303
|
+
return new WES_CLASS(attrs);
|
|
304
|
+
}
|
|
233
305
|
buildSupplement(tree, kind, key, prefix) {
|
|
234
306
|
const supp = isObj(tree[key]) ? tree[key] : {};
|
|
235
307
|
const base = this.buildSingle(this.baseData(tree, key));
|