@pubid/pubid 0.1.3 → 0.1.4

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.
Files changed (183) hide show
  1. package/artifact/corpus.json.gz +0 -0
  2. package/dist/conformance/cli.js +5 -1
  3. package/dist/flavors/amca/grammar.d.ts +4 -0
  4. package/dist/flavors/amca/grammar.js +87 -0
  5. package/dist/flavors/amca/index.d.ts +4 -0
  6. package/dist/flavors/amca/index.js +76 -0
  7. package/dist/flavors/amca/model.d.ts +29 -0
  8. package/dist/flavors/amca/model.js +146 -0
  9. package/dist/flavors/ansi/index.d.ts +24 -0
  10. package/dist/flavors/ansi/index.js +147 -0
  11. package/dist/flavors/api/grammar.d.ts +4 -0
  12. package/dist/flavors/api/grammar.js +79 -0
  13. package/dist/flavors/api/index.d.ts +4 -0
  14. package/dist/flavors/api/index.js +71 -0
  15. package/dist/flavors/api/model.d.ts +27 -0
  16. package/dist/flavors/api/model.js +114 -0
  17. package/dist/flavors/ashrae/grammar.d.ts +2 -0
  18. package/dist/flavors/ashrae/grammar.js +667 -0
  19. package/dist/flavors/ashrae/index.d.ts +4 -0
  20. package/dist/flavors/ashrae/index.js +306 -0
  21. package/dist/flavors/ashrae/model.d.ts +87 -0
  22. package/dist/flavors/ashrae/model.js +335 -0
  23. package/dist/flavors/ashrae/preprocessor.d.ts +7 -0
  24. package/dist/flavors/ashrae/preprocessor.js +44 -0
  25. package/dist/flavors/asme/grammar.d.ts +2 -0
  26. package/dist/flavors/asme/grammar.js +200 -0
  27. package/dist/flavors/asme/index.d.ts +4 -0
  28. package/dist/flavors/asme/index.js +138 -0
  29. package/dist/flavors/asme/model.d.ts +34 -0
  30. package/dist/flavors/asme/model.js +123 -0
  31. package/dist/flavors/astm/grammar.d.ts +2 -0
  32. package/dist/flavors/astm/grammar.js +110 -0
  33. package/dist/flavors/astm/index.d.ts +2 -0
  34. package/dist/flavors/astm/index.js +127 -0
  35. package/dist/flavors/astm/model.d.ts +42 -0
  36. package/dist/flavors/astm/model.js +263 -0
  37. package/dist/flavors/bipm/index.d.ts +30 -0
  38. package/dist/flavors/bipm/index.js +434 -0
  39. package/dist/flavors/bsi/grammar.d.ts +4 -0
  40. package/dist/flavors/bsi/grammar.js +535 -0
  41. package/dist/flavors/bsi/index.d.ts +4 -0
  42. package/dist/flavors/bsi/index.js +941 -0
  43. package/dist/flavors/bsi/model.d.ts +127 -0
  44. package/dist/flavors/bsi/model.js +863 -0
  45. package/dist/flavors/calconnect/index.d.ts +21 -0
  46. package/dist/flavors/calconnect/index.js +113 -0
  47. package/dist/flavors/cen_cenelec/grammar.d.ts +4 -0
  48. package/dist/flavors/cen_cenelec/grammar.js +107 -0
  49. package/dist/flavors/cen_cenelec/index.d.ts +5 -0
  50. package/dist/flavors/cen_cenelec/index.js +263 -0
  51. package/dist/flavors/cen_cenelec/model.d.ts +45 -0
  52. package/dist/flavors/cen_cenelec/model.js +490 -0
  53. package/dist/flavors/cie/grammar.d.ts +5 -0
  54. package/dist/flavors/cie/grammar.js +127 -0
  55. package/dist/flavors/cie/index.d.ts +2 -0
  56. package/dist/flavors/cie/index.js +311 -0
  57. package/dist/flavors/cie/model.d.ts +122 -0
  58. package/dist/flavors/cie/model.js +464 -0
  59. package/dist/flavors/csa/grammar.d.ts +6 -0
  60. package/dist/flavors/csa/grammar.js +239 -0
  61. package/dist/flavors/csa/index.d.ts +2 -0
  62. package/dist/flavors/csa/index.js +634 -0
  63. package/dist/flavors/csa/model.d.ts +33 -0
  64. package/dist/flavors/csa/model.js +505 -0
  65. package/dist/flavors/doi/index.d.ts +13 -0
  66. package/dist/flavors/doi/index.js +65 -0
  67. package/dist/flavors/easc/index.d.ts +4 -0
  68. package/dist/flavors/easc/index.js +107 -0
  69. package/dist/flavors/ecma/index.d.ts +4 -0
  70. package/dist/flavors/ecma/index.js +117 -0
  71. package/dist/flavors/etsi/index.d.ts +14 -0
  72. package/dist/flavors/etsi/index.js +307 -0
  73. package/dist/flavors/evs/grammar.d.ts +2 -0
  74. package/dist/flavors/evs/grammar.js +22 -0
  75. package/dist/flavors/evs/index.d.ts +2 -0
  76. package/dist/flavors/evs/index.js +24 -0
  77. package/dist/flavors/evs/model.d.ts +19 -0
  78. package/dist/flavors/evs/model.js +36 -0
  79. package/dist/flavors/gb/index.d.ts +24 -0
  80. package/dist/flavors/gb/index.js +111 -0
  81. package/dist/flavors/gost/grammar.d.ts +2 -0
  82. package/dist/flavors/gost/grammar.js +46 -0
  83. package/dist/flavors/gost/index.d.ts +2 -0
  84. package/dist/flavors/gost/index.js +165 -0
  85. package/dist/flavors/gost/model.d.ts +61 -0
  86. package/dist/flavors/gost/model.js +147 -0
  87. package/dist/flavors/gost/prefix_routes.d.ts +15 -0
  88. package/dist/flavors/gost/prefix_routes.js +192 -0
  89. package/dist/flavors/iala/index.d.ts +32 -0
  90. package/dist/flavors/iala/index.js +300 -0
  91. package/dist/flavors/iana/index.d.ts +19 -0
  92. package/dist/flavors/iana/index.js +71 -0
  93. package/dist/flavors/idf/grammar.d.ts +2 -0
  94. package/dist/flavors/idf/grammar.js +71 -0
  95. package/dist/flavors/idf/index.d.ts +2 -0
  96. package/dist/flavors/idf/index.js +100 -0
  97. package/dist/flavors/idf/model.d.ts +54 -0
  98. package/dist/flavors/idf/model.js +359 -0
  99. package/dist/flavors/iec/grammar.d.ts +3 -0
  100. package/dist/flavors/iec/grammar.js +187 -0
  101. package/dist/flavors/iec/index.d.ts +4 -0
  102. package/dist/flavors/iec/index.js +507 -0
  103. package/dist/flavors/iec/model.d.ts +54 -0
  104. package/dist/flavors/iec/model.js +437 -0
  105. package/dist/flavors/ieee/grammar.d.ts +2 -0
  106. package/dist/flavors/ieee/grammar.js +626 -0
  107. package/dist/flavors/ieee/index.d.ts +3 -0
  108. package/dist/flavors/ieee/index.js +1186 -0
  109. package/dist/flavors/ieee/model.d.ts +142 -0
  110. package/dist/flavors/ieee/model.js +1148 -0
  111. package/dist/flavors/ieee/preprocessor.d.ts +8 -0
  112. package/dist/flavors/ieee/preprocessor.js +410 -0
  113. package/dist/flavors/ieee/update_codes.d.ts +8 -0
  114. package/dist/flavors/ieee/update_codes.js +191 -0
  115. package/dist/flavors/ietf/index.d.ts +30 -0
  116. package/dist/flavors/ietf/index.js +151 -0
  117. package/dist/flavors/iho/index.d.ts +4 -0
  118. package/dist/flavors/iho/index.js +126 -0
  119. package/dist/flavors/index.d.ts +9 -0
  120. package/dist/flavors/index.js +136 -0
  121. package/dist/flavors/isbn/index.d.ts +13 -0
  122. package/dist/flavors/isbn/index.js +97 -0
  123. package/dist/flavors/iso/grammar.d.ts +2 -0
  124. package/dist/flavors/iso/grammar.js +207 -0
  125. package/dist/flavors/iso/index.d.ts +2 -0
  126. package/dist/flavors/iso/index.js +343 -0
  127. package/dist/flavors/iso/model.d.ts +159 -0
  128. package/dist/flavors/iso/model.js +802 -0
  129. package/dist/flavors/iso/stages.generated.d.ts +13 -0
  130. package/dist/flavors/iso/stages.generated.js +391 -0
  131. package/dist/flavors/itu/grammar.d.ts +4 -0
  132. package/dist/flavors/itu/grammar.js +205 -0
  133. package/dist/flavors/itu/index.d.ts +2 -0
  134. package/dist/flavors/itu/index.js +224 -0
  135. package/dist/flavors/itu/model.d.ts +184 -0
  136. package/dist/flavors/itu/model.js +599 -0
  137. package/dist/flavors/jcgm/index.d.ts +7 -0
  138. package/dist/flavors/jcgm/index.js +300 -0
  139. package/dist/flavors/jis/index.d.ts +4 -0
  140. package/dist/flavors/jis/index.js +267 -0
  141. package/dist/flavors/nist/grammar.d.ts +2 -0
  142. package/dist/flavors/nist/grammar.js +287 -0
  143. package/dist/flavors/nist/index.d.ts +2 -0
  144. package/dist/flavors/nist/index.js +1244 -0
  145. package/dist/flavors/nist/model.d.ts +155 -0
  146. package/dist/flavors/nist/model.js +823 -0
  147. package/dist/flavors/nist/preprocessor.d.ts +11 -0
  148. package/dist/flavors/nist/preprocessor.js +281 -0
  149. package/dist/flavors/oasis/index.d.ts +23 -0
  150. package/dist/flavors/oasis/index.js +110 -0
  151. package/dist/flavors/ogc/index.d.ts +20 -0
  152. package/dist/flavors/ogc/index.js +75 -0
  153. package/dist/flavors/oiml/grammar.d.ts +2 -0
  154. package/dist/flavors/oiml/grammar.js +180 -0
  155. package/dist/flavors/oiml/implementation.d.ts +9 -0
  156. package/dist/flavors/oiml/implementation.js +16 -0
  157. package/dist/flavors/oiml/model.d.ts +4 -0
  158. package/dist/flavors/oiml/model.js +462 -0
  159. package/dist/flavors/omg/index.d.ts +14 -0
  160. package/dist/flavors/omg/index.js +73 -0
  161. package/dist/flavors/plateau/index.d.ts +4 -0
  162. package/dist/flavors/plateau/index.js +97 -0
  163. package/dist/flavors/tgpp/index.d.ts +4 -0
  164. package/dist/flavors/tgpp/index.js +122 -0
  165. package/dist/flavors/un/index.d.ts +13 -0
  166. package/dist/flavors/un/index.js +90 -0
  167. package/dist/flavors/w3c/index.d.ts +14 -0
  168. package/dist/flavors/w3c/index.js +148 -0
  169. package/dist/flavors/xsf/index.d.ts +18 -0
  170. package/dist/flavors/xsf/index.js +65 -0
  171. package/dist/grammar/engine.d.ts +78 -0
  172. package/dist/grammar/engine.js +351 -0
  173. package/dist/model/attribute.d.ts +54 -0
  174. package/dist/model/attribute.js +53 -0
  175. package/dist/model/builder.d.ts +39 -0
  176. package/dist/model/builder.js +124 -0
  177. package/dist/model/component.d.ts +70 -0
  178. package/dist/model/component.js +170 -0
  179. package/dist/model/identifier.d.ts +83 -0
  180. package/dist/model/identifier.js +270 -0
  181. package/dist/model/urn-generator.d.ts +27 -0
  182. package/dist/model/urn-generator.js +91 -0
  183. package/package.json +1 -1
@@ -0,0 +1,207 @@
1
+ import { match, ref, str } from "../../grammar/engine.js";
2
+ import { stageTokens } from "./model.js";
3
+ /**
4
+ * Port of lib/pubid/iso/parser.rb. The identifier alternation order is
5
+ * load-bearing (PEG ordered choice): nsb_iso | tc_document |
6
+ * directives | jcgm | iso_r_supplement | iso_r | suppl³ | suppl² |
7
+ * suppl | joint | copublishers. Stage tokens come from the typed-stage
8
+ * registry (longest first), split into base (non-supplement) and
9
+ * supplement tables exactly like the Ruby TYPED_STAGES constants.
10
+ */
11
+ const SUPPLEMENT_TYPE_KEYS = ["amd", "cor", "suppl", "ext", "add"];
12
+ const BASE_STAGE_TOKENS = stageTokens().filter((t) => {
13
+ // Supplement tokens are excluded from the base table by type key, but
14
+ // stageTokens() is type-agnostic here; the split is done via the
15
+ // supplement table below (tokens unique to supplement types).
16
+ return true;
17
+ });
18
+ const DIRECTIVES_TOKENS = ["Directives Part", "Directives, Part", "Directives,", "Directives", "DIR"];
19
+ const DIRECTIVES_SUPPLEMENT_TOKENS = ["DIR SUP", "Supplement", "SUP"];
20
+ const ORGANIZATIONS = [
21
+ "ISO", "IEC", "IEEE", "CIW", "SAE", "CIE", "ASME", "ASTM", "OECD", "HL7", "CEI", "UNDP",
22
+ ];
23
+ const TC_TYPES = [
24
+ "TC", "JTC", "PC", "IT", "CAB", "CASCO", "COPOLCO", "COUNCIL", "CPSG", "CS",
25
+ "DEVCO", "GA", "GAAB", "INFCO", "ITN", "ISOlutions", "REMCO", "TMB", "TMBG",
26
+ "WMO", "DMT", "JCG", "SGPM", "ATMG", "CCCC", "CCCC-TG", "JDMT", "JSAG",
27
+ "JSCTF-TF", "JTCG", "JTCG-TF", "SAG_Acc", "SAG_CRMI", "SAG_CRMI_CG",
28
+ "SAG_ESG", "SAG_ESG_CG", "SAG_MRS", "SAG_SF", "SAG_SF_CG", "SMCC", "STMG",
29
+ "MENA_STAR",
30
+ ];
31
+ const WG_TYPES = [
32
+ "AG", "AhG", "WG", "JWG", "QC", "TF", "PPC", "CAG", "CSC", "ITSAG",
33
+ "CSC/FIN", "CSC/NOM", "CSC/OVE", "CSC/SP", "CSC", "SF", "ITSG", "JAG",
34
+ "JCTF", "JSG", "JTAG", "JTG",
35
+ ];
36
+ function literalAlternation(tokens) {
37
+ const sorted = [...new Set(tokens)].sort((a, b) => b.length - a.length);
38
+ return sorted.map((t) => str(t)).reduce((a, b) => a.or(b));
39
+ }
40
+ function buildRules() {
41
+ const rules = {};
42
+ const rule = (name, build) => {
43
+ rules[name] = build();
44
+ };
45
+ const space = str(" ");
46
+ const digit = match("[0-9]");
47
+ const digits = digit.repeat(1, Infinity);
48
+ const yearDigits = digit.repeat(4, 4);
49
+ const monthDigits = digit.repeat(2, 2);
50
+ const dayDigits = digit.repeat(2, 2);
51
+ const dashChars = ["-", "‑", "‐"];
52
+ const dash = literalAlternation(dashChars);
53
+ rule("publisher", () => str("ISO").or(str("IEC")).as("publisher"));
54
+ rule("copublishers", () => str("/")
55
+ .then(space.maybe(), literalAlternation(ORGANIZATIONS).as("copublisher"))
56
+ .repeat(0, Infinity)
57
+ .as("copublishers"));
58
+ // (:|dash)(-- undated | Y[-M[-D]]), all optional after the separator.
59
+ rule("date", () => str(":")
60
+ .or(dash)
61
+ .then(str("--")
62
+ .or(str("—"))
63
+ .as("undated_marker")
64
+ .or(yearDigits
65
+ .then(dash.then(monthDigits).maybe(), dash.then(dayDigits).maybe())
66
+ .as("date"))
67
+ .maybe()));
68
+ rule("number", () => digit.repeat(1, 5));
69
+ // Modern "-A01", "-5-1-1" (space-tolerant) vs legacy "/6" (guarded
70
+ // against supplement type names).
71
+ rule("part_and_subpart", () => dash
72
+ .then(space.maybe(), match("\\w").repeat(1, Infinity), dash.then(match("\\w").repeat(1, Infinity)).repeat(0, Infinity).maybe())
73
+ .or(str("/").then(space.maybe(), ref(rules, "supplement_type_with_stage").absent(), match("\\w").repeat(1, Infinity))));
74
+ rule("number_with_part", () => rules["number"].then(rules["part_and_subpart"].maybe()).as("number_with_part"));
75
+ rule("all_parts", () => str("(all parts)").as("all_parts"));
76
+ const supplementTypeTokens = stageTokens(SUPPLEMENT_TYPE_KEYS);
77
+ const baseStageTokens = BASE_STAGE_TOKENS.filter((t) => !supplementTypeTokens.includes(t));
78
+ rule("type_with_stage", () => literalAlternation(baseStageTokens)
79
+ .as("type_with_stage")
80
+ .then(digit.as("stage_iteration").then(space).maybe()));
81
+ rule("supplement_type_with_stage", () => literalAlternation(supplementTypeTokens).as("type_with_stage"));
82
+ // (en,fr) or (E/F) — repeated captures joined.
83
+ rule("language", () => str("(").then(match("[a-z]")
84
+ .repeat(1, Infinity)
85
+ .then(str(",").maybe())
86
+ .or(match("[EFARDS]").then(str("/").or(str(",")).maybe()))
87
+ .repeat(0, Infinity)
88
+ .as("languages"), str(")")));
89
+ rule("edition", () => literalAlternation(["Edition", "Ed.", "ED", "Ed"])
90
+ .then(space.maybe(), digits.maybe())
91
+ .as("edition"));
92
+ rule("stage_iteration", () => str(".").then(digit.as("stage_iteration")));
93
+ rule("second_part", () => rules["number_with_part"]
94
+ .then(rules["stage_iteration"].maybe(), space.maybe(), rules["date"].maybe()));
95
+ rule("third_part_edition", () => space.maybe().then(rules["edition"].maybe()));
96
+ rule("third_part", () => rules["third_part_edition"]
97
+ .then(rules["language"].maybe(), space.maybe(), rules["all_parts"].maybe()));
98
+ rule("guide_prefix", () => str("Guide").or(str("GUIDE")));
99
+ rule("prefix_with_copublishers", () => str("ISO/R")
100
+ .as("iso_r_prefix")
101
+ .then(space)
102
+ .or(rules["publisher"]
103
+ .then(space.maybe(), rules["copublishers"].maybe())));
104
+ rule("identifier_copublishers_no_third", () => rules["guide_prefix"]
105
+ .as("type_with_stage_fr")
106
+ .then(space)
107
+ .maybe()
108
+ .then(rules["prefix_with_copublishers"].maybe(), space.maybe(), str("/").maybe(), rules["type_with_stage"].maybe(), space.maybe(), rules["second_part"], rules["third_part_edition"]));
109
+ rule("identifier_copublishers", () => rules["identifier_copublishers_no_third"].then(rules["third_part"]));
110
+ // NSB-prefixed: FprISO… / WD/ISO…
111
+ const nsbBody = () => rules["publisher"]
112
+ .then(space.maybe(), rules["copublishers"].maybe(), space.maybe(), rules["type_with_stage"].maybe(), space.maybe(), rules["second_part"], rules["third_part"]);
113
+ rule("nsb_iso_identifier", () => str("Fpr")
114
+ .as("nsb_stage")
115
+ .then(nsbBody())
116
+ .or(str("WD").as("nsb_stage").then(str("/"), nsbBody())));
117
+ // TC documents: ISO/TC 184/SC 4/WG 3 N 123[:year]
118
+ rule("tc_type", () => literalAlternation(TC_TYPES).as("tc_type"));
119
+ rule("sc_type", () => str("SC/QC").or(str("SC")).as("sc_type"));
120
+ rule("wg_type", () => literalAlternation(WG_TYPES).as("wg_type"));
121
+ rule("tc_subcommittee_part", () => str("/")
122
+ .then(rules["sc_type"], space, digits.as("sc_number"), str("/"), rules["wg_type"], space, digits.as("wg_number"))
123
+ .or(str("/").then(rules["sc_type"], space, digits.as("sc_number"))));
124
+ rule("tc_document", () => rules["publisher"]
125
+ .then(str("/").or(space), rules["tc_type"], space, digits.as("tc_number"), rules["tc_subcommittee_part"].maybe(), space, str("N"), space.maybe(), digits.as("number"), str(":").then(yearDigits.as("year")).maybe()));
126
+ // JCGM 200:2008(F)
127
+ rule("jcgm_identifier", () => str("JCGM")
128
+ .as("publisher")
129
+ .then(space, rules["second_part"], rules["third_part"]));
130
+ // Legacy ISO/R forms.
131
+ rule("iso_r_second_part", () => rules["number_with_part"]
132
+ .then(rules["stage_iteration"].maybe(), space.maybe(), rules["date"].maybe()));
133
+ rule("iso_r_identifier", () => str("ISO/R")
134
+ .as("iso_r_prefix")
135
+ .then(space, rules["iso_r_second_part"], rules["third_part"]));
136
+ rule("iso_r_supplement_separator", () => str("/").or(space.maybe().then(str("—"), space.maybe()), space.then(str("-"), space)));
137
+ rule("iso_r_supplement_identifier", () => str("ISO/R")
138
+ .as("iso_r_prefix")
139
+ .then(space, rules["iso_r_second_part"])
140
+ .as("base")
141
+ .then(rules["iso_r_supplement_separator"], rules["supplement_type_with_stage"], space.maybe(), rules["second_part"], rules["third_part"]));
142
+ // Supplements: base + "/" + stage + (second_part | .N | date)? [+ third]
143
+ rule("supplement_identifier_no_third", () => rules["identifier_copublishers_no_third"]
144
+ .as("base")
145
+ .then(str("/"), rules["supplement_type_with_stage"], space
146
+ .maybe()
147
+ .then(rules["second_part"])
148
+ .or(str(".").then(digits.as("number")), space.maybe().then(rules["date"]))
149
+ .maybe()));
150
+ rule("supplement_identifier", () => rules["supplement_identifier_no_third"].then(rules["third_part"]));
151
+ rule("supplement_supplement_identifier", () => rules["supplement_identifier_no_third"]
152
+ .as("base")
153
+ .then(str("/"), rules["supplement_type_with_stage"], space
154
+ .maybe()
155
+ .then(rules["second_part"], rules["third_part"])
156
+ .or(str(".").then(digits.as("number"), rules["third_part"]))));
157
+ rule("supplement_supplement_supplement_identifier", () => rules["supplement_supplement_identifier"]
158
+ .as("base")
159
+ .then(str("/"), rules["supplement_type_with_stage"], space
160
+ .maybe()
161
+ .then(rules["second_part"], rules["third_part"])
162
+ .or(str(".").then(digits.as("number"), rules["third_part"]))));
163
+ // Directives.
164
+ rule("directives_publisher_subgroup", () => str("JTC 1").as("subgroup"));
165
+ rule("directives_part_and_subpart", () => space.then(str("ISO").or(str("IEC")), str("/").absent(), space.then(str("SUP")).absent()));
166
+ rule("directives_number_with_part", () => rules["number"]
167
+ .then(rules["directives_part_and_subpart"].maybe())
168
+ .as("number_with_part"));
169
+ rule("directives_identifier_no_third", () => rules["prefix_with_copublishers"]
170
+ .then(space.maybe(), rules["directives_publisher_subgroup"].then(space).maybe(), literalAlternation(DIRECTIVES_TOKENS).as("type_with_stage"), space
171
+ .then(rules["directives_publisher_subgroup"], space.then(str("SUP")).absent())
172
+ .maybe(), space
173
+ .then(rules["directives_number_with_part"])
174
+ .maybe()
175
+ .then(str(":").then(rules["date"]).maybe())
176
+ .maybe()));
177
+ rule("directives_identifier", () => rules["directives_identifier_no_third"]
178
+ .then(rules["third_part"].maybe(), space.maybe(), rules["date"].maybe()));
179
+ rule("directives_supplement_part_no_third", () => space
180
+ .maybe()
181
+ .then(str("--"), space.maybe(), str("Consolidated"), space)
182
+ .maybe()
183
+ .then(str("ISO/IEC").or(str("ISO"), str("IEC"), str("JTC 1")).as("publisher"), space.then(literalAlternation(DIRECTIVES_SUPPLEMENT_TOKENS).as("type_with_stage")), rules["date"].maybe()));
184
+ rule("directives_supplement_part", () => rules["directives_supplement_part_no_third"].then(rules["third_part"].maybe()));
185
+ rule("directives_supplement_identifier", () => rules["directives_identifier_no_third"]
186
+ .as("base")
187
+ .then(space.maybe(), rules["directives_supplement_part"]));
188
+ rule("directives_bundled_identifier", () => rules["directives_identifier_no_third"]
189
+ .then(rules["third_part"].then(space.maybe()).maybe(), rules["date"].then(space.maybe()).maybe())
190
+ .as("base_document")
191
+ .then(str("+ ")
192
+ .then(rules["directives_supplement_part_no_third"].as("supplement"))
193
+ .repeat(1, Infinity)
194
+ .as("supplements")));
195
+ rule("directives_identifiers", () => rules["directives_bundled_identifier"].or(rules["directives_supplement_identifier"], rules["directives_identifier"]));
196
+ // Joint ISO|IDF — IDF side unreachable by the corpus (no rows); the
197
+ // twin is captured raw like ITU's common-text twin.
198
+ rule("idf_identifier", () => match("[^|]").repeat(1, Infinity).as("idf_raw"));
199
+ rule("joint_identifier", () => rules["identifier_copublishers_no_third"]
200
+ .as("base")
201
+ .then(space.maybe(), str("|"), space.maybe(), rules["idf_identifier"])
202
+ .as("joint_identifier"));
203
+ rule("identifier", () => rules["nsb_iso_identifier"].or(rules["tc_document"], rules["directives_identifiers"], rules["jcgm_identifier"], rules["iso_r_supplement_identifier"], rules["iso_r_identifier"], rules["supplement_supplement_supplement_identifier"], rules["supplement_supplement_identifier"], rules["supplement_identifier"], rules["joint_identifier"], rules["identifier_copublishers"]));
204
+ rule("root", () => rules["identifier"]);
205
+ return rules;
206
+ }
207
+ export const isoGrammar = { rules: buildRules(), root: "root" };
@@ -0,0 +1,2 @@
1
+ import type { FlavorImplementation } from "../../conformance/implementation.js";
2
+ export declare function isoGrammarImplementation(): FlavorImplementation;
@@ -0,0 +1,343 @@
1
+ import { ParseFailed, parseGrammar } from "../../grammar/engine.js";
2
+ import { PubidDate, Language, Iteration } from "../../model/component.js";
3
+ import { isoGrammar } from "./grammar.js";
4
+ import { IsoBundledIdentifier, IsoDirectives, IsoDirectivesSupplement, IsoEdition, IsoPublisher, IsoSupplementIdentifier, IsoTcDocument, isoClassForType, locateStage, } from "./model.js";
5
+ /**
6
+ * Port of lib/pubid/iso/{builder,normalizer}.rb. Class dispatch follows
7
+ * the Ruby Builder: joint → Combined (unreachable by the corpus),
8
+ * supplements-bundle → BundledIdentifier, tc_type → TcDocument, else
9
+ * locate_type(typed_stage.type_code) with the DIR/SUP special cases.
10
+ */
11
+ const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
12
+ const s = (v) => {
13
+ if (v === undefined || v === null)
14
+ return undefined;
15
+ const str = Array.isArray(v) ? v.join("") : String(v);
16
+ return str.length > 0 ? str : undefined;
17
+ };
18
+ function flatten(data) {
19
+ return Array.isArray(data) ? Object.assign({}, ...data) : data;
20
+ }
21
+ // --- Normalizer (lib/pubid/iso/normalizer.rb) --------------------------------
22
+ const DAD_PATTERN = /^(.+?)\/(F?DAD)\s+(\d+)(?::(\d{4}))?$/;
23
+ const UPDATE_CODE_LINES = {
24
+ "ISO/R 657/IV": "ISO/R 657-4:1969",
25
+ "ISO/IEC/IEEE 8802-22.2:2015/Amd.2:2017(E)": "ISO/IEC/IEEE 8802-22:2015/Amd.2:2017(E)",
26
+ "ISO/TR 17716.2": "ISO/TR 17716",
27
+ "ISO/TR 17716.2(E)": "ISO/TR 17716(E)",
28
+ };
29
+ const UPDATE_CODE_REGEXES = [
30
+ [/^(ISO[\w/]*) DIS (TR|TS|PAS|ISP) /, "$1 D$2 "],
31
+ [/^(ISO[\w/]*) FDIS (TR|TS|PAS|ISP) /, "$1 FD$2 "],
32
+ [/DIS Amd /, "DAM "],
33
+ [/FDIS Amd /, "FDAM "],
34
+ [/DIS Cor /, "DCOR "],
35
+ [/FDIS Cor /, "FDCOR "],
36
+ [/DIS Suppl /, "DSuppl "],
37
+ [/DIS Add /, "DAD "],
38
+ ];
39
+ const CYRILLIC_MAP = [
40
+ ["Руководства", "GUIDE"],
41
+ ["Руководство", "GUIDE"],
42
+ ["ИСО/МЭК", "ISO/IEC"],
43
+ ["ИСО/ОПМС", "ISO/FDIS"],
44
+ ["ИСО/ПМС", "ISO/DIS"],
45
+ ["ИСО/ТО", "ISO/TR"],
46
+ ["ИСО/ТС", "ISO/TS"],
47
+ ["ИСО", "ISO"],
48
+ ["МЭК", "IEC"],
49
+ ["ТО", "TR"],
50
+ ["ТС", "TS"],
51
+ ["ОПМС", "FDIS"],
52
+ ["ПМС", "DIS"],
53
+ ];
54
+ function normalizeCyrillic(input) {
55
+ if (!/[а-яА-Я]/.test(input))
56
+ return input;
57
+ let normalized = input.replace(/\s*#\s*.*$/, "");
58
+ for (const [from, to] of CYRILLIC_MAP) {
59
+ normalized = normalized.split(from).join(to);
60
+ }
61
+ return normalized.trim();
62
+ }
63
+ function applyUpdateCodes(input) {
64
+ const line = UPDATE_CODE_LINES[input];
65
+ if (line !== undefined)
66
+ return line;
67
+ let out = input;
68
+ for (const [pattern, replacement] of UPDATE_CODE_REGEXES) {
69
+ // The Ruby table keys the supplement-position regexes with a "//"
70
+ // prefix marking "after the first /"; apply those only past a slash.
71
+ out = out.replace(pattern, replacement);
72
+ }
73
+ return out;
74
+ }
75
+ // --- Builder -------------------------------------------------------------------
76
+ function parseLanguages(raw) {
77
+ const str = s(raw);
78
+ if (str === undefined)
79
+ return [];
80
+ return str
81
+ .split(",")
82
+ .flatMap((chunk) => chunk.split("/"))
83
+ .map((token) => token.trim())
84
+ .filter((token) => token !== "")
85
+ .map((original) => {
86
+ const code = original.length === 1 ? (Language.CHAR_MAP[original] ?? original.toLowerCase()) : original;
87
+ return new Language({ code, originalCode: original });
88
+ });
89
+ }
90
+ /**
91
+ * Builder#parse_number_with_part: em/en dashes, slashes and spaces
92
+ * normalize to "-", then number[-part[-subpart]]; a 4-digit 1900-2099
93
+ * part is a legacy year, not a part.
94
+ */
95
+ function parseNumberWithPart(raw) {
96
+ const value = s(raw)?.replace(/[‑‐/]/g, "-").replace(/ /g, "-");
97
+ if (value === undefined)
98
+ return { number: undefined, part: undefined, subpart: undefined, date: undefined };
99
+ const segments = value.split("-").filter((seg) => seg !== "");
100
+ const [number, part, ...rest] = segments;
101
+ const result = { number, part, subpart: rest.length > 0 ? rest.join("-") : undefined, date: undefined };
102
+ if (part !== undefined && /^\d{4}$/.test(part)) {
103
+ const yearValue = Number(part);
104
+ if (yearValue >= 1900 && yearValue <= 2099 && rest.length === 0) {
105
+ result.part = undefined;
106
+ result.date = new PubidDate({ year: part });
107
+ }
108
+ }
109
+ return result;
110
+ }
111
+ function parseDate(tree) {
112
+ if (tree === undefined || !isObj(tree))
113
+ return undefined;
114
+ const flat = flatten(tree);
115
+ const year = s(flat["year"]);
116
+ if (year === undefined)
117
+ return undefined;
118
+ return new PubidDate({
119
+ year,
120
+ month: s(flat["month"]),
121
+ day: s(flat["day"]),
122
+ });
123
+ }
124
+ function parseEdition(raw) {
125
+ const text = s(raw);
126
+ if (text === undefined)
127
+ return undefined;
128
+ const number = /\d+/.exec(text)?.[0];
129
+ return new IsoEdition({ ...(number !== undefined ? { number } : {}), original_text: text });
130
+ }
131
+ function typeStateFromToken(token, fallbackTypeKey) {
132
+ const abbr = s(token);
133
+ if (abbr === undefined)
134
+ return undefined;
135
+ const entry = locateStage(abbr);
136
+ if (entry === undefined)
137
+ return undefined;
138
+ return { entry, originalAbbr: abbr };
139
+ }
140
+ class IsoBuilder {
141
+ build(data) {
142
+ const tree = flatten(data);
143
+ if (tree["joint_identifier"] !== undefined) {
144
+ // No corpus rows carry an IDF twin; fail rather than guess.
145
+ throw new ParseFailed("ISO: joint IDF identifiers are not ported", 0);
146
+ }
147
+ if (tree["supplements"] !== undefined)
148
+ return this.buildBundled(tree);
149
+ if (tree["tc_type"] !== undefined)
150
+ return this.buildTc(tree);
151
+ return this.buildStandard(tree);
152
+ }
153
+ publisherOf(tree) {
154
+ const primary = s(tree["publisher"]);
155
+ const copubTree = tree["copublishers"];
156
+ const copublishers = [];
157
+ if (Array.isArray(copubTree)) {
158
+ for (const entry of copubTree) {
159
+ if (isObj(entry)) {
160
+ const cp = s(entry["copublisher"]);
161
+ if (cp !== undefined)
162
+ copublishers.push(cp);
163
+ }
164
+ }
165
+ }
166
+ else if (copubTree !== undefined) {
167
+ const cp = s(flatten(copubTree)["copublisher"]);
168
+ if (cp !== undefined)
169
+ copublishers.push(cp);
170
+ }
171
+ if (primary === undefined && copublishers.length === 0)
172
+ return undefined;
173
+ return new IsoPublisher({ publisher: primary ?? "ISO", copublisher: copublishers });
174
+ }
175
+ baseAttrs(tree) {
176
+ const attrs = {};
177
+ const numberWithPart = tree["number_with_part"] !== undefined ? flatten(tree["number_with_part"]) : {};
178
+ const rawNumber = s(numberWithPart["number"]) ?? s(tree["number"]);
179
+ const parsed = parseNumberWithPart(s(tree["number_with_part"]) ?? s(tree["number"]));
180
+ if (parsed.number !== undefined || rawNumber !== undefined) {
181
+ if (parsed.number !== undefined)
182
+ attrs["number"] = parsed.number;
183
+ if (parsed.part !== undefined)
184
+ attrs["part"] = parsed.part;
185
+ if (parsed.subpart !== undefined)
186
+ attrs["subpart"] = parsed.subpart;
187
+ if (parsed.date !== undefined)
188
+ attrs["date"] = parsed.date;
189
+ }
190
+ const date = parseDate(tree["date"]) ?? parseDate(tree["base"] !== undefined ? undefined : undefined);
191
+ if (tree["date"] !== undefined) {
192
+ if (isObj(tree["date"])) {
193
+ const flat = flatten(tree["date"]);
194
+ const year = s(flat["year"]);
195
+ if (year !== undefined) {
196
+ attrs["date"] = new PubidDate({ year, month: s(flat["month"]), day: s(flat["day"]) });
197
+ }
198
+ }
199
+ else {
200
+ const dateStr = s(tree["date"]);
201
+ if (dateStr !== undefined) {
202
+ const [year, month, day] = dateStr.split("-");
203
+ if (year !== undefined && year !== "") {
204
+ attrs["date"] = new PubidDate({ year, month, day });
205
+ }
206
+ }
207
+ }
208
+ }
209
+ if (tree["undated_marker"] !== undefined) {
210
+ attrs["date"] = new PubidDate({ undated: true });
211
+ }
212
+ const edition = parseEdition(tree["edition"]);
213
+ if (edition !== undefined)
214
+ attrs["edition"] = edition;
215
+ const languages = parseLanguages(tree["languages"]);
216
+ if (languages.length > 0)
217
+ attrs["languages"] = languages;
218
+ if (tree["all_parts"] !== undefined)
219
+ attrs["all_parts"] = true;
220
+ if (tree["stage_iteration"] !== undefined) {
221
+ attrs["stage_iteration"] = new Iteration({ string: s(tree["stage_iteration"]) });
222
+ }
223
+ return attrs;
224
+ }
225
+ buildStandard(treeIn) {
226
+ const tree = { ...treeIn };
227
+ // Legacy ISO/R: publisher ISO + type "R".
228
+ if (tree["iso_r_prefix"] !== undefined) {
229
+ tree["publisher"] = "ISO";
230
+ tree["type_with_stage"] = "R";
231
+ }
232
+ // NSB stage prefixes map onto typed stages.
233
+ if (tree["nsb_stage"] !== undefined) {
234
+ tree["type_with_stage"] = s(tree["nsb_stage"]) === "Fpr" ? "PRF" : "WD";
235
+ }
236
+ // French Guide/GUIDE prefix.
237
+ if (tree["type_with_stage_fr"] !== undefined) {
238
+ tree["type_with_stage"] = s(tree["type_with_stage_fr"]);
239
+ }
240
+ // Directive supplement without its own SUP keyword.
241
+ if (tree["type_with_stage"] === undefined && tree["base"] !== undefined && isObj(tree["base"])) {
242
+ const baseTree = flatten(tree["base"]);
243
+ const dirTokens = new Set(["DIR", "Directives Part", "Directives, Part", "Directives,", "Directives"]);
244
+ if (dirTokens.has(s(baseTree["type_with_stage"]) ?? "")) {
245
+ tree["type_with_stage"] = "SUP";
246
+ }
247
+ }
248
+ const state = typeStateFromToken(s(tree["type_with_stage"]), "is");
249
+ const typeKey = state?.entry.typeCode ?? "is";
250
+ if (typeKey === "dir-sup")
251
+ return this.buildDirectivesSupplement(tree, state);
252
+ if (typeKey === "dir")
253
+ return this.buildDirectives(tree, state ?? { entry: locateStage("DIR"), originalAbbr: "DIR" });
254
+ const klass = isoClassForType(typeKey);
255
+ if (klass === undefined) {
256
+ throw new ParseFailed(`ISO: unknown type key ${typeKey}`, 0);
257
+ }
258
+ const attrs = this.baseAttrs(tree);
259
+ const publisher = this.publisherOf(tree);
260
+ if (publisher !== undefined)
261
+ attrs["publisher"] = publisher;
262
+ if (state !== undefined)
263
+ attrs["typedStageState"] = state;
264
+ if ((tree["base"] !== undefined || klass.prototype instanceof IsoSupplementIdentifier) && tree["base"] !== undefined) {
265
+ attrs["base"] = this.build(tree["base"]);
266
+ }
267
+ const identifier = new klass(attrs);
268
+ return identifier;
269
+ }
270
+ buildDirectives(tree, state) {
271
+ const attrs = this.baseAttrs(tree);
272
+ const publisher = this.publisherOf(tree);
273
+ if (publisher !== undefined)
274
+ attrs["publisher"] = publisher;
275
+ attrs["typedStageState"] = state;
276
+ if (tree["subgroup"] !== undefined)
277
+ attrs["subgroup"] = s(tree["subgroup"]);
278
+ return new IsoDirectives(attrs);
279
+ }
280
+ buildDirectivesSupplement(tree, state) {
281
+ const attrs = this.baseAttrs(tree);
282
+ attrs["typedStageState"] = state;
283
+ if (tree["publisher"] !== undefined)
284
+ attrs["supplement_publisher"] = s(tree["publisher"]);
285
+ if (tree["base"] !== undefined)
286
+ attrs["base"] = this.build(tree["base"]);
287
+ return new IsoDirectivesSupplement(attrs);
288
+ }
289
+ buildTc(tree) {
290
+ const attrs = {};
291
+ const publisher = this.publisherOf(tree);
292
+ if (publisher !== undefined)
293
+ attrs["publisher"] = publisher;
294
+ for (const key of ["tc_type", "tc_number", "sc_type", "sc_number", "wg_type", "wg_number", "number"]) {
295
+ const value = s(tree[key]);
296
+ if (value !== undefined)
297
+ attrs[key] = value;
298
+ }
299
+ if (tree["year"] !== undefined) {
300
+ attrs["date"] = new PubidDate({ year: s(tree["year"]) });
301
+ }
302
+ return new IsoTcDocument(attrs);
303
+ }
304
+ buildBundled(tree) {
305
+ const baseDocument = this.build(tree["base_document"]);
306
+ const supplements = Array.isArray(tree["supplements"])
307
+ ? tree["supplements"].map((entry) => this.build(flatten(entry)["supplement"]))
308
+ : [];
309
+ return new IsoBundledIdentifier({ base_document: baseDocument, supplements });
310
+ }
311
+ }
312
+ // --- Entry points ----------------------------------------------------------------
313
+ function parseHuman(input) {
314
+ const dad = DAD_PATTERN.exec(input);
315
+ if (dad !== null) {
316
+ // "ISO 2631/DAD 1[:1987]" — parsed manually; the grammar treats "/"
317
+ // as a copublisher separator.
318
+ const base = parseHuman(dad[1]);
319
+ const builder = new IsoBuilder();
320
+ const attrs = {
321
+ base,
322
+ number: dad[3],
323
+ typedStageState: { entry: locateStage(dad[2]), originalAbbr: dad[2] },
324
+ };
325
+ if (dad[4] !== undefined)
326
+ attrs["date"] = new PubidDate({ year: dad[4] });
327
+ const klass = isoClassForType("add");
328
+ return new klass(attrs);
329
+ }
330
+ const normalized = normalizeCyrillic(applyUpdateCodes(input));
331
+ const tree = parseGrammar(isoGrammar, normalized);
332
+ if (typeof tree !== "object" || tree === null) {
333
+ throw new ParseFailed("ISO: unexpected parse tree", 0);
334
+ }
335
+ return new IsoBuilder().build(tree);
336
+ }
337
+ export function isoGrammarImplementation() {
338
+ return {
339
+ parse(input) {
340
+ return parseHuman(input);
341
+ },
342
+ };
343
+ }