@pubid/pubid 0.1.39 → 0.1.40

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.
Binary file
@@ -24,5 +24,8 @@ export interface Identifier {
24
24
  toUrn(): string | undefined;
25
25
  /** Deserialize the canonical hash; the idempotency gate relies on it. */
26
26
  fromHash(hash: Record<string, unknown>): Identifier;
27
+ /** Ruby ==: canonical equality skipping equalityIgnored attributes
28
+ * (the (E) position is spelling, the language itself is not). */
29
+ equals?(other: Identifier): boolean;
27
30
  }
28
31
  export type ImplementationRegistry = Map<string, FlavorImplementation>;
@@ -14,7 +14,12 @@ import { BaseUrnGenerator } from "../../model/urn-generator.js";
14
14
  *
15
15
  * WES (welding-engineering-standard, pubid#638) rides the same flavor:
16
16
  * "WES <number>[-parts][:year[R]]…" with no series letter and no JIS
17
- * prefix — the code itself carries the "WES" root.
17
+ * prefix — the code itself carries the "WES" root. A WES id may also
18
+ * carry a TR/TS type after the root ("WES TR 2032:2024", its own
19
+ * welding-engineering-technical-report/-specification classes,
20
+ * pubid#716) and a language before the year ("WES 1112(E):2022",
21
+ * bound only when a colon follows; recorded as language_before_year,
22
+ * which equality ignores — the two (E) positions are one document).
18
23
  */
19
24
  function buildRules() {
20
25
  const rules = {};
@@ -61,11 +66,18 @@ function buildRules() {
61
66
  .then(rules["symbol_clause"].maybe()));
62
67
  // WES (Japan Welding Engineering Society) rides the JIS flavor with the
63
68
  // same tail but no prefix and no series letter; the root tries it first,
64
- // as the reference grammar does.
69
+ // as the reference grammar does. A WES id may also carry a TR/TS type
70
+ // after the root ("WES TR 2032:2024") and a language before the year
71
+ // ("WES 1112(E):2022" — bound only when a colon follows, so
72
+ // "WES 1112(E)" with no year still reads as no language).
73
+ rule("wes_type", () => (str("TR").or(str("TS"))).as("wes_type").then(space));
74
+ rule("wes_language", () => jpLparen.or(str("(")).then(match("[EJ]").as("wes_language"), jpRparen.or(str(")"))).then(colon.present()));
65
75
  rule("wes_identifier", () => str("WES").as("wes")
66
76
  .then(space)
77
+ .then(rules["wes_type"].maybe())
67
78
  .then(rules["number"])
68
79
  .then(rules["parts"])
80
+ .then(rules["wes_language"].maybe())
69
81
  .then(rules["year"].maybe())
70
82
  .then(rules["language"].maybe())
71
83
  .then(rules["all_parts"].maybe())
@@ -95,6 +107,13 @@ class JisIdentifier extends BaseIdentifier {
95
107
  yearWithReaffirmation() {
96
108
  return `${this.year}${this.reaffirmed === true ? "R" : ""}`;
97
109
  }
110
+ /** The ":year[R]" and "(lang)" tail, in the order the source used
111
+ * (ruby year_and_language). */
112
+ yearAndLanguage() {
113
+ const yearPart = this.year !== undefined ? `:${this.yearWithReaffirmation()}` : "";
114
+ const langPart = this.language !== undefined ? `(${this.language})` : "";
115
+ return this.language_before_year === true ? langPart + yearPart : yearPart + langPart;
116
+ }
98
117
  symbolSuffix() {
99
118
  if (this.symbol === undefined)
100
119
  return "";
@@ -104,7 +123,7 @@ class JisIdentifier extends BaseIdentifier {
104
123
  return "";
105
124
  }
106
125
  }
107
- const JIS_MAPPINGS = keyValue({ wire: "series", to: "series" }, { 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" });
126
+ const JIS_MAPPINGS = keyValue({ wire: "series", to: "series" }, { wire: "number", to: "number" }, { wire: "parts", to: "parts" }, { wire: "year", to: "year" }, { wire: "language", to: "language" }, { wire: "language_before_year", to: "language_before_year" }, { wire: "all_parts", to: "all_parts" }, { wire: "reaffirmed", to: "reaffirmed" }, { wire: "symbol", to: "symbol" });
108
127
  function jisClass(kind) {
109
128
  const isSupplement = kind === "amendment" || kind === "corrigendum" || kind === "explanation";
110
129
  class JisKindIdentifier extends JisIdentifier {
@@ -122,6 +141,7 @@ function jisClass(kind) {
122
141
  parts: { type: "string", collection: true },
123
142
  year: { type: "integer" },
124
143
  language: { type: "string" },
144
+ language_before_year: { type: "boolean", equalityIgnored: true },
125
145
  all_parts: { type: "boolean", default: false },
126
146
  reaffirmed: { type: "boolean" },
127
147
  symbol: { type: "string" },
@@ -137,10 +157,7 @@ function jisClass(kind) {
137
157
  const typePrefix = KIND_PREFIXES[kind];
138
158
  let result = typePrefix === undefined ? "JIS" : `JIS ${typePrefix}`;
139
159
  result += ` ${this.code()}`;
140
- if (this.year !== undefined)
141
- result += `:${this.yearWithReaffirmation()}`;
142
- if (this.language !== undefined)
143
- result += `(${this.language})`;
160
+ result += this.yearAndLanguage();
144
161
  if (this.all_parts === true)
145
162
  result += "(規格群)";
146
163
  return result + this.symbolSuffix();
@@ -167,36 +184,43 @@ const KIND_CLASSES = {
167
184
  corrigendum: jisClass("corrigendum"),
168
185
  explanation: jisClass("explanation"),
169
186
  };
187
+ const WES_TYPE_PREFIXES = {
188
+ "welding-engineering-standard": undefined,
189
+ "welding-engineering-technical-report": "TR",
190
+ "welding-engineering-technical-specification": "TS",
191
+ };
170
192
  // WES 7700-1:2019 — JSA sells Welding Engineering Standards next to JIS,
171
193
  // 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() {
194
+ // letter and no JIS prefix ("WES" is the root of the designation). The
195
+ // TR/TS type token after the root picks the WES TR / WES TS classes
196
+ // (pubid#716): "WES TR 2032" is not "WES 2032" and not "TR Z 2032".
197
+ function wesClass(kind) {
174
198
  class JisWesIdentifier extends JisIdentifier {
175
- static polymorphicName = "pubid:jis:welding-engineering-standard";
199
+ static polymorphicName = `pubid:jis:${kind}`;
176
200
  static attributes = extendAttributes(BaseIdentifier, {
177
201
  number: { type: "string" },
178
202
  parts: { type: "string", collection: true },
179
203
  year: { type: "integer" },
180
204
  language: { type: "string" },
205
+ language_before_year: { type: "boolean", equalityIgnored: true },
181
206
  all_parts: { type: "boolean", default: false },
182
207
  reaffirmed: { type: "boolean" },
183
208
  symbol: { type: "string" },
184
209
  });
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" });
210
+ static mappings = keyValue({ wire: "number", to: "number" }, { wire: "parts", to: "parts" }, { wire: "year", to: "year" }, { wire: "language", to: "language" }, { wire: "language_before_year", to: "language_before_year" }, { wire: "all_parts", to: "all_parts" }, { wire: "reaffirmed", to: "reaffirmed" }, { wire: "symbol", to: "symbol" });
186
211
  code() {
187
212
  if (this.base !== undefined)
188
213
  return this.base.code();
189
- let result = `WES ${this.number}`;
214
+ const prefix = WES_TYPE_PREFIXES[kind];
215
+ const root = prefix === undefined ? "WES" : `WES ${prefix}`;
216
+ let result = `${root} ${this.number}`;
190
217
  if (this.parts !== undefined)
191
218
  result += this.parts.map((p) => `-${p}`).join("");
192
219
  return result;
193
220
  }
194
221
  render() {
195
222
  let result = this.code() ?? "";
196
- if (this.year !== undefined)
197
- result += `:${this.yearWithReaffirmation()}`;
198
- if (this.language !== undefined)
199
- result += `(${this.language})`;
223
+ result += this.yearAndLanguage();
200
224
  if (this.all_parts === true)
201
225
  result += "(規格群)";
202
226
  return result + this.symbolSuffix();
@@ -205,7 +229,11 @@ function wesClass() {
205
229
  registerType(JisWesIdentifier);
206
230
  return JisWesIdentifier;
207
231
  }
208
- const WES_CLASS = wesClass();
232
+ const WES_CLASSES = {
233
+ "welding-engineering-standard": wesClass("welding-engineering-standard"),
234
+ "welding-engineering-technical-report": wesClass("welding-engineering-technical-report"),
235
+ "welding-engineering-technical-specification": wesClass("welding-engineering-technical-specification"),
236
+ };
209
237
  class JisUrnGenerator extends BaseUrnGenerator {
210
238
  generate() {
211
239
  const id = this.identifier;
@@ -226,7 +254,7 @@ class JisUrnGenerator extends BaseUrnGenerator {
226
254
  return parts.join(":");
227
255
  }
228
256
  }
229
- for (const klass of [...Object.values(KIND_CLASSES), WES_CLASS]) {
257
+ for (const klass of [...Object.values(KIND_CLASSES), ...Object.values(WES_CLASSES)]) {
230
258
  klass.urnGenerator = JisUrnGenerator;
231
259
  }
232
260
  function isObj(v) {
@@ -294,13 +322,29 @@ class JisBuilder {
294
322
  };
295
323
  if (tree["year"] !== undefined && tree["year"] !== null)
296
324
  attrs["year"] = Number(tree["year"]);
297
- if (tree["language"] !== undefined && tree["language"] !== null)
325
+ const wesType = tree["wes_type"] === undefined || tree["wes_type"] === null ? undefined : String(tree["wes_type"]);
326
+ const kind = wesType === "TR"
327
+ ? "welding-engineering-technical-report"
328
+ : wesType === "TS"
329
+ ? "welding-engineering-technical-specification"
330
+ : "welding-engineering-standard";
331
+ // "wes_language" is a language printed before the year
332
+ // ("WES 1112(E):2022"); an id carries one language, not two.
333
+ if (tree["wes_language"] !== undefined && tree["wes_language"] !== null) {
334
+ if (tree["language"] !== undefined && tree["language"] !== null) {
335
+ throw new ParseFailed("JIS: a WES id carries one language, not two", 0);
336
+ }
337
+ attrs["language"] = String(tree["wes_language"]);
338
+ attrs["language_before_year"] = true;
339
+ }
340
+ else if (tree["language"] !== undefined && tree["language"] !== null) {
298
341
  attrs["language"] = String(tree["language"]);
342
+ }
299
343
  if (tree["all_parts"] !== undefined)
300
344
  attrs["all_parts"] = true;
301
345
  if (tree["reaffirmed"] !== undefined && tree["reaffirmed"] !== null)
302
346
  attrs["reaffirmed"] = true;
303
- return new WES_CLASS(attrs);
347
+ return new WES_CLASSES[kind](attrs);
304
348
  }
305
349
  buildSupplement(tree, kind, key, prefix) {
306
350
  const supp = isObj(tree[key]) ? tree[key] : {};
@@ -18,6 +18,9 @@ export interface AttributeSpec {
18
18
  collection?: boolean;
19
19
  /** Materialize [] when the input hash omits the key (tgpp parts). */
20
20
  initializeEmpty?: boolean;
21
+ /** Ruby `equality_ignored`: the attribute serializes, but `==` skips it
22
+ * (jis language_before_year — where the source printed "(E)" is spelling). */
23
+ equalityIgnored?: boolean;
21
24
  }
22
25
  export type AttributeTable = Record<string, AttributeSpec>;
23
26
  /**
@@ -62,6 +62,13 @@ export declare abstract class BaseIdentifier {
62
62
  * `PubidUrn.parse(this.toPubidUrn()).toHash == this.toHash`. */
63
63
  toPubidUrn(): string;
64
64
  fromHash(hash: Record<string, unknown>): BaseIdentifier;
65
+ /** Ruby == (lutaml equality with `equality_ignored`): canonical-hash
66
+ * equality that skips the attributes a flavor declares
67
+ * equalityIgnored. The skipped keys stay in to_hash — it is ==, not
68
+ * serialization, that reads the flag. */
69
+ equals(other: BaseIdentifier): boolean;
70
+ /** The canonical hash minus every equalityIgnored attribute's wire key. */
71
+ protected equalityHash(): Record<string, unknown>;
65
72
  /** SubsetMatch: an all-parts reference skips the part attributes. */
66
73
  subsetAllPartsWildcard(): boolean;
67
74
  protected classAttributes(): AttributeTable;
@@ -116,6 +116,23 @@ export class BaseIdentifier {
116
116
  fromHash(hash) {
117
117
  return this.constructor.fromHash(hash);
118
118
  }
119
+ /** Ruby == (lutaml equality with `equality_ignored`): canonical-hash
120
+ * equality that skips the attributes a flavor declares
121
+ * equalityIgnored. The skipped keys stay in to_hash — it is ==, not
122
+ * serialization, that reads the flag. */
123
+ equals(other) {
124
+ return canonicalKey(this.equalityHash()) === canonicalKey(other.equalityHash());
125
+ }
126
+ /** The canonical hash minus every equalityIgnored attribute's wire key. */
127
+ equalityHash() {
128
+ const hash = { ...this.toHash() };
129
+ for (const [name, spec] of Object.entries(this.classAttributes())) {
130
+ if (spec.equalityIgnored !== true)
131
+ continue;
132
+ delete hash[this.wireKeyFor(name).wire];
133
+ }
134
+ return hash;
135
+ }
119
136
  /** SubsetMatch: an all-parts reference skips the part attributes. */
120
137
  subsetAllPartsWildcard() {
121
138
  return Boolean(this["all_parts"]);
@@ -11,14 +11,21 @@ export const jisSchema = define("jis", (s) => {
11
11
  s.atom("parts", "part", "list");
12
12
  s.atom("year", "year", "int");
13
13
  s.atom("reaffirmed", "reaffirmed", "bool");
14
+ s.atom("language", "lang");
15
+ s.facet("language_before_year", "langfirst", "bool");
14
16
  s.type("pubid:jis:all-parts", "all");
15
- s.type("pubid:jis:amendment", "amd");
16
- s.type("pubid:jis:corrigendum", "cor");
17
- s.type("pubid:jis:explanation", "e");
17
+ s.type("pubid:jis:amendment", "amd", { core: [["number", "int"]] });
18
+ s.type("pubid:jis:corrigendum", "cor", { core: [["number", "int"]] });
19
+ s.type("pubid:jis:explanation", "e", {
20
+ core: [],
21
+ atoms: [["number", "n", "int"]],
22
+ });
18
23
  s.type("pubid:jis:identifier", "id");
19
24
  s.type("pubid:jis:japanese-industrial-standard", "jis");
20
25
  s.type("pubid:jis:standard", "std");
21
26
  s.type("pubid:jis:technical-report", "tr");
22
27
  s.type("pubid:jis:technical-specification", "ts");
23
28
  s.type("pubid:jis:welding-engineering-standard", "wes");
29
+ s.type("pubid:jis:welding-engineering-technical-report", "westr");
30
+ s.type("pubid:jis:welding-engineering-technical-specification", "wests");
24
31
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pubid/pubid",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "description": "TypeScript PubID identifiers: resolution, normalization and canonical hashes for every identifier in the pubid-testsuite corpus (41 standards bodies), with a conformance runner mirroring the Ruby reference",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +1 @@
1
- {"bindings":null,"version":"0.2.0","grammar":"PubidIho","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Str":{"pattern":"IHO"}},{"Entity":{"atom":2}},{"Str":{"pattern":" "}},{"Sequence":{"atoms":[0,1]}},{"Repetition":{"atom":3,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":12}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[6,7,8,9,10]}},{"Named":{"name":"type","atom":11}},{"Entity":{"atom":14}},{"Str":{"pattern":"-"}},{"Entity":{"atom":26}},{"Entity":{"atom":17}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":16,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":23}},{"Str":{"pattern":":"}},{"Entity":{"atom":17}},{"Repetition":{"atom":21,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[20,22]}},{"Repetition":{"atom":19,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[18,24]}},{"Named":{"name":"number","atom":25}},{"Entity":{"atom":53}},{"Str":{"pattern":"-"}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[29,30,31,32,33]}},{"Entity":{"atom":14}},{"Entity":{"atom":17}},{"Repetition":{"atom":36,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[34,35,37]}},{"Entity":{"atom":17}},{"Repetition":{"atom":39,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":46}},{"Entity":{"atom":43}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Entity":{"atom":45}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Alternative":{"atoms":[42,44]}},{"Repetition":{"atom":41,"min":0,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":47,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[40,48]}},{"Entity":{"atom":43}},{"Alternative":{"atoms":[38,49,50]}},{"Named":{"name":"part","atom":51}},{"Sequence":{"atoms":[28,52]}},{"Repetition":{"atom":27,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":57}},{"Entity":{"atom":43}},{"Named":{"name":"part","atom":56}},{"Repetition":{"atom":55,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":94}},{"Str":{"pattern":"/"}},{"Entity":{"atom":17}},{"Repetition":{"atom":61,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"volume","atom":62}},{"Entity":{"atom":92}},{"Entity":{"atom":78}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":67,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Str":{"pattern":"."}},{"Entity":{"atom":17}},{"Repetition":{"atom":71,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":74,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[68,69,72,73,75]}},{"Named":{"name":"volume_edition","atom":76}},{"Sequence":{"atoms":[66,77]}},{"Entity":{"atom":90}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[82,83]}},{"Entity":{"atom":17}},{"Repetition":{"atom":85,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[84,86]}},{"Named":{"name":"volume_year","atom":87}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[80,81,88,89]}},{"Repetition":{"atom":79,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[65,91]}},{"Repetition":{"atom":64,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[60,63,93]}},{"Repetition":{"atom":59,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":137}},{"Entity":{"atom":109}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":99,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":102,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":105,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[100,101,103,104,106]}},{"Named":{"name":"edition","atom":107}},{"Sequence":{"atoms":[98,108]}},{"Entity":{"atom":122}},{"Entity":{"atom":2}},{"Str":{"pattern":"v"}},{"Entity":{"atom":17}},{"Repetition":{"atom":113,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Entity":{"atom":17}},{"Repetition":{"atom":116,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[115,117]}},{"Repetition":{"atom":118,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[114,119]}},{"Named":{"name":"edition_shorthand","atom":120}},{"Sequence":{"atoms":[111,112,121]}},{"Alternative":{"atoms":[97,110]}},{"Entity":{"atom":135}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[127,128]}},{"Entity":{"atom":17}},{"Repetition":{"atom":130,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[129,131]}},{"Named":{"name":"year","atom":132}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[125,126,133,134]}},{"Repetition":{"atom":124,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[123,136]}},{"Repetition":{"atom":96,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":177}},{"Entity":{"atom":2}},{"Str":{"pattern":"Suppl"}},{"Str":{"pattern":"Sup"}},{"Alternative":{"atoms":[141,142]}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":145,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"supplement","atom":146}},{"Entity":{"atom":175}},{"Entity":{"atom":161}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":151,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":154,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":157,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[152,153,155,156,158]}},{"Named":{"name":"supplement_edition","atom":159}},{"Sequence":{"atoms":[150,160]}},{"Entity":{"atom":173}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[165,166]}},{"Entity":{"atom":17}},{"Repetition":{"atom":168,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[167,169]}},{"Named":{"name":"supplement_year","atom":170}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[163,164,171,172]}},{"Repetition":{"atom":162,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[149,174]}},{"Repetition":{"atom":148,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[140,143,144,147,176]}},{"Repetition":{"atom":139,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":233}},{"Entity":{"atom":2}},{"Str":{"pattern":"Part"}},{"Entity":{"atom":2}},{"Entity":{"atom":203}},{"Entity":{"atom":194}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[185,186,187,188,189]}},{"Entity":{"atom":14}},{"Entity":{"atom":17}},{"Repetition":{"atom":192,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[190,191,193]}},{"Entity":{"atom":17}},{"Repetition":{"atom":195,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":46}},{"Repetition":{"atom":197,"min":0,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":198,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[196,199]}},{"Entity":{"atom":43}},{"Alternative":{"atoms":[184,200,201]}},{"Named":{"name":"part","atom":202}},{"Entity":{"atom":231}},{"Entity":{"atom":217}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":207,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":210,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":213,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[208,209,211,212,214]}},{"Named":{"name":"part_edition","atom":215}},{"Sequence":{"atoms":[206,216]}},{"Entity":{"atom":229}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[221,222]}},{"Entity":{"atom":17}},{"Repetition":{"atom":224,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[223,225]}},{"Named":{"name":"part_year","atom":226}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[219,220,227,228]}},{"Repetition":{"atom":218,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[205,230]}},{"Repetition":{"atom":204,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[180,181,182,183,232]}},{"Repetition":{"atom":179,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":270}},{"Entity":{"atom":2}},{"Str":{"pattern":"Annex"}},{"Entity":{"atom":2}},{"Entity":{"atom":43}},{"Named":{"name":"annex","atom":239}},{"Entity":{"atom":268}},{"Entity":{"atom":254}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":244,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":247,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":250,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[245,246,248,249,251]}},{"Named":{"name":"annex_edition","atom":252}},{"Sequence":{"atoms":[243,253]}},{"Entity":{"atom":266}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[258,259]}},{"Entity":{"atom":17}},{"Repetition":{"atom":261,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[260,262]}},{"Named":{"name":"annex_year","atom":263}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[256,257,264,265]}},{"Repetition":{"atom":255,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[242,267]}},{"Repetition":{"atom":241,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[236,237,238,240,269]}},{"Repetition":{"atom":235,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":321}},{"Entity":{"atom":2}},{"Str":{"pattern":"Appendix"}},{"Str":{"pattern":"App"}},{"Str":{"pattern":"Ap."}},{"Alternative":{"atoms":[274,275,276]}},{"Entity":{"atom":2}},{"Entity":{"atom":284}},{"Entity":{"atom":43}},{"Entity":{"atom":17}},{"Repetition":{"atom":281,"min":1,"max":null,"tag":"Repetition"}},{"Alternative":{"atoms":[280,282]}},{"Named":{"name":"appendix","atom":283}},{"Entity":{"atom":290}},{"Entity":{"atom":14}},{"Entity":{"atom":17}},{"Repetition":{"atom":287,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"appendix_subnumber","atom":288}},{"Sequence":{"atoms":[286,289]}},{"Repetition":{"atom":285,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":319}},{"Entity":{"atom":305}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":295,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":298,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":301,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[296,297,299,300,302]}},{"Named":{"name":"appendix_edition","atom":303}},{"Sequence":{"atoms":[294,304]}},{"Entity":{"atom":317}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[309,310]}},{"Entity":{"atom":17}},{"Repetition":{"atom":312,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[311,313]}},{"Named":{"name":"appendix_year","atom":314}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[307,308,315,316]}},{"Repetition":{"atom":306,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[293,318]}},{"Repetition":{"atom":292,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[273,277,278,279,291,320]}},{"Repetition":{"atom":272,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":362}},{"Entity":{"atom":348}},{"Entity":{"atom":2}},{"Str":{"pattern":"Vol."}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":328,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"volume","atom":329}},{"Entity":{"atom":346}},{"Entity":{"atom":2}},{"Str":{"pattern":"Issue"}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":335,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"issue","atom":336}},{"Sequence":{"atoms":[332,333,334,337]}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Entity":{"atom":17}},{"Repetition":{"atom":341,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"issue","atom":342}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[339,340,343,344]}},{"Alternative":{"atoms":[338,345]}},{"Repetition":{"atom":331,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[325,326,327,330,347]}},{"Entity":{"atom":358}},{"Entity":{"atom":2}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[351,352]}},{"Entity":{"atom":17}},{"Repetition":{"atom":354,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[353,355]}},{"Named":{"name":"volume","atom":356}},{"Sequence":{"atoms":[350,357]}},{"Alternative":{"atoms":[324,349]}},{"Entity":{"atom":92}},{"Repetition":{"atom":360,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[359,361]}},{"Repetition":{"atom":323,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":135}},{"Repetition":{"atom":364,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[4,5,13,15,54,58,95,138,178,234,271,322,363,365]}}],"root":366},"bindings":[{"capture":"type","path":"type","type":"string","card":null,"preprocess":null},{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"part","path":"part","type":"string","card":null,"preprocess":null},{"capture":"part_edition","path":"part_edition","type":"string","card":null,"preprocess":null},{"capture":"part_year","path":"part_year","type":"string","card":null,"preprocess":null},{"capture":"annex","path":"annex","type":"string","card":null,"preprocess":null},{"capture":"annex_edition","path":"annex_edition","type":"string","card":null,"preprocess":null},{"capture":"annex_year","path":"annex_year","type":"string","card":null,"preprocess":null},{"capture":"appendix","path":"appendix","type":"string","card":null,"preprocess":null},{"capture":"appendix_subnumber","path":"appendix_subnumber","type":"string","card":null,"preprocess":null},{"capture":"appendix_edition","path":"appendix_edition","type":"string","card":null,"preprocess":null},{"capture":"appendix_year","path":"appendix_year","type":"string","card":null,"preprocess":null},{"capture":"supplement","path":"supplement","type":"string","card":null,"preprocess":null},{"capture":"supplement_edition","path":"supplement_edition","type":"string","card":null,"preprocess":null},{"capture":"supplement_year","path":"supplement_year","type":"string","card":null,"preprocess":null},{"capture":"edition","path":"edition","type":"string","card":null,"preprocess":null},{"capture":"edition_shorthand","path":"edition_shorthand","type":"string","card":null,"preprocess":null},{"capture":"year","path":"year","type":"string","card":null,"preprocess":null},{"capture":"volume","path":"volume","type":"string","card":null,"preprocess":null},{"capture":"volume_edition","path":"volume_edition","type":"string","card":null,"preprocess":null},{"capture":"volume_year","path":"volume_year","type":"string","card":null,"preprocess":null},{"capture":"issue","path":"issue","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule volume_issue: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"S-5","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5 Appendix 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO B-12 3.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"P-5 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5 Vol. 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5/2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO B-7 Vol. 2023","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30 (2)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30 Issue 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1/21","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1/21 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Part 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 1.0.0 Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-65 1.1.0 Annex A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-122 1.0.0 Appendix D-2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-52 4.0.0 Appendix 1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO M-2 Sup 1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO M-2 Suppl 6","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-66 2.0.0 Sup 4","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5-B","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100-1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100-1 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6-3 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5A 1.0.2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5 1.0.2 Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Part 2 (2024)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 v5.1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 v1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 1.0.0 (2024) Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-53 Ap. 1 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 Part A 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158:100 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158:100 Part 2 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-1001","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158 Part S-102 0.2.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158-S-102 0.2.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6 1.0.0 Vol. 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6 Vol. 2 1.0.1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 1.0.0 Annex A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Sup 1 2.0.0","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO B-12 3.0","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO P-5 1850","expect":{}}],"docs":{},"source":"# Port of lib/pubid/iho/parser.rb — IHO identifiers: letter series\n# (S/P/M/B/C), a number with an optional suffix, and the full component\n# tail (edition, supplement, part, annex, appendix + subnumber, volume,\n# issue, year qualifier).\n#\n# == Component model (owner rulings, 2026-11-03)\n#\n# Every component below is a first-class semantic component: its own\n# named capture, its own typed binding, independently addressable in\n# the model, URN-encodable and matchable — never a regex/string suffix.\n#\n# component face capture binding type\n# edition x.y.z (bare) edition string\n# edition v1 / v5.1 (alias) edition_shorthand string (parse-only;\n# the builder pads to\n# x.y.0 — official IHO\n# edition is dotted\n# x.y.z WITHOUT a year)\n# supplement Sup N (Suppl N) supplement string\n# part Part X / Part S-102 part string (letter,\n# number, or SERIES\n# REFERENCE)\n# part -X / -S-102 (short) part string (hyphenated)\n# annex Annex A annex string\n# appendix Appendix B appendix string\n# appendix Appendix D-2 appendix_subnumber string (the -2)\n# volume Vol. N volume string\n# volume /N (short) volume string (the slash\n# chain, P-1/21 or\n# P-5/2020)\n# volume N (bare, legacy) volume string (the yearbook\n# year IS a volume —\n# one serial-locator\n# component)\n# issue (N) / Issue N issue string\n# year (2024) year string (the with-year\n# qualifier on\n# standards; NOT a\n# serial locator)\n#\n# == Component-edition duality (owner ruling: \"this can happen with any\n# independently publishable artifacts\")\n#\n# Supplement, part, annex, appendix and volume can be published on\n# their own publication cycle, so EACH carries an optional\n# `<kind>_edition` (+ `<kind>_year` qualifier) of its own, mutually\n# exclusive with the base edition context:\n#\n# within-edition: IHO P-6 1.0.0 Vol. 2 (the volume rides\n# the base's edition)\n# separately-published: IHO P-6 Vol. 2 1.0.1 (the volume carries\n# IHO S-98 Part A 1.0.0 its own edition;\n# IHO S-158 Part S-102 0.2.0 the base is\n# cited edition-less)\n#\n# In the URN the ed- segment attaches to the node it follows, so the\n# same positional slot records both modes.\n#\n# The part has a COMPACT face, hyphenated onto the code — `IHO S-5-B`\n# (lettered part), `IHO S-100-1` (numbered part), `IHO S-158-S-102`\n# (series-reference part) — which both parses and renders; the\n# canonical expanded face stays `IHO S-100 x.y.z Part 1`. Gluing a part\n# onto the code without a separator (S-5A) is DISALLOWED as a form: it\n# cannot extend to numbered parts without ambiguity (S-1001 = series\n# 1001 vs series 100 part 1). The four lettered bases the corpus\n# records (S-5A, S-5B, S-8A, S-8B) are LEGACY data spellings: they\n# still parse as aliases (S-5A == S-5 Part A) but are never rendered;\n# the owner rules later whether to reject them outright.\n#\n# Series-reference parts: a check standard applied to another IHO\n# series cites the applied series as its part — `IHO S-158 0.2.0 Part\n# S-102` (the wild glued `S-158-102` spelling is a legacy alias whose\n# normalization to Part S-102 needs an IHO series table; the syntax\n# alone cannot tell it from S-100-1 = Part 1, so it currently parses\n# as the plain number part).\n#\n# Two main syntaxes per the owner ruling: `IHO S-100 1.0.0` (dated =\n# edition-specific) and `IHO S-100` (undated = edition-non-specific).\n# A year beside an edition is the with-year variant qualifier\n# (`IHO S-100 1.0.0 Part X (2024)`), never a year-keyed docidentifier —\n# year-keyed docidentifiers for edition-keyed products are wrong. The\n# continuously-updated series have ONE serial locator, the volume:\n# P-1 and P-6 are volume-keyed (`IHO P-1 Vol. 30 (1)`), and the\n# yearbook year IS a volume (`IHO P-5 Vol. 2020`) — the bare year\n# (`IHO P-5 2020`) and all year-faced forms are parse-only legacy\n# aliases that bind into the volume component, never rendered.\n#\n# Aliases: \"Suppl\" (canonical face Sup, the M-2 spelling — why abbreviate\n# by two letters when Annex and Appendix stay full-length), \"App\"/\"Ap.\"\n# (canonical face Appendix), the hyphenated compact part face (S-5-B ==\n# S-5 Part B, S-100-1 == S-100 Part 1), the legacy glued lettered bases\n# (S-5A, parse-only), \"S-100 v1..v5.1\" (edition shorthand), \"Issue N\"\n# (canonical (N)), the bare year (canonical Vol. N).\n#\n# == URN segment grammar (positional, containment-ordered)\n#\n# The URN mirrors the containment tree, one semantic atom per segment:\n# Part contains Annexes; Annex contains Appendices; a D-2 subnumber is\n# a child atom of Appendix D. No key.value concatenations — every\n# non-number component is a marker-prefixed segment whose marker is the\n# lowercased human-face token (edition follows ISO's ed- precedent):\n#\n# urn:iho:<type>:<number>[:ed-<x.y.z>][:year-<yyyy>]\n# [:sup-<n>[:ed-<x.y.z>][:year-<yyyy>]]\n# [:part-<X>[:ed-<x.y.z>][:year-<yyyy>]]\n# [:annex-<A>[...]]\n# [:appendix-<B>[:<sub>][...]]\n# [:vol-<n>[:issue-<n>][...]]\n# [:year-<yyyy>]\n#\n# position segment value vocabulary containment\n# -------- --------------- -------------------------- ------------------\n# 1 <type> s / p / m / b / c namespace root\n# 2 <number> digits + : suffix; may document code\n# span bare segments until\n# the first marker\n# 3 ed-<x.y.z> dotted a.b.c edition qualifier;\n# 4 year-<yyyy> 19xx/20xx ATTACHES TO THE\n# NODE IT FOLLOWS: the\n# base (no component\n# before it) or the\n# component whose\n# segment precedes\n# 5 sup-<n> 1*digit wraps the base\n# 6 part-<X> letter (case kept), contains annexes\n# digits [*letter], or a\n# series reference\n# [SPMBC]-<digits>\n# 7 annex-<A> upper letter (case kept) contains appendices\n# 8 appendix-<B> upper letter or digits contains subnumber\n# 9 <sub> 1*digit, BARE, only child of appendix\n# directly after appendix-\n# 10 vol-<n> 1*digit (incl. yearbook contains issue\n# years, Vol. 2020)\n# 11 issue-<n> 1*digit, only after vol- child of volume\n#\n# The number occupies every leading bare segment up to the first\n# marker, so a colon number (S-158:100) round-trips without escaping;\n# after the markers the only bare segment allowed is the appendix\n# subnumber in its own slot. Both the year qualifier and the volume are\n# marker-tagged so neither can collide with a colon-number segment (the\n# #637 hazard, kept under the positional scheme). Letters are\n# case-preserved: part-A and part-a are distinct documents.\n#\n# Legacy 2.0.x spellings remain decodable but are never emitted: the\n# bare a.b.c segment (edition), the dotted keys part./annex./suppl./\n# ap./appendix./vol./issue. and the single-letter y. year tag (whose\n# value decodes into the volume — the serial-locator unification).\n#\n# Edition 2: ruby#400's capture-flatten keeps text captures over\n# reference-built rules as the matched text, so the v2 tree is the v1\n# tree plus named leaves for non-captured structure.\n\ngrammar PubidIho version \"0.2.0\" edition 2 {\n digit = %x30-39\n upper = %x41-5A\n lower = %x61-7A\n letter = upper / lower\n space = \" \"\n dash = \"-\"\n dot = \".\"\n\n series = (\"S\" / \"P\" / \"M\" / \"B\" / \"C\") as type\n number_suffix = (\":\" 1*digit)\n number = (1*digit [number_suffix]) as number\n # The compact part face, hyphenated onto the code: S-5-B (lettered),\n # S-100-1 (numbered) and S-158-S-102 (series reference) — the dash\n # replaces the old bare-glued suffix, so a number like S-1001 stays a\n # whole number (the dash is what makes the part unambiguous). The\n # dash is an inlined literal: a named dash ref here collides with the\n # sequence's own dash node in the edition-2 tree.\n compact_part = \"-\" (((\"S\" / \"P\" / \"M\" / \"B\" / \"C\") dash (1*digit)) / (1*digit [*letter]) / upper) as part\n # LEGACY glued lettered bases (S-5A, S-5B, S-8A, S-8B in the corpus):\n # parse-only aliases, never rendered. Kept because the records exist;\n # the owner rules later whether to reject them.\n glued = upper as part\n\n # The official edition is the dotted x.y.z; \"v1\"/\"v5.1\" is the S-100\n # shorthand (parse-only, the builder pads to x.y.0).\n edition = space (1*digit dot 1*digit dot 1*digit) as edition\n v_edition = space \"v\" ((1*digit) [\".\" (1*digit)]) as edition_shorthand\n # The with-year qualifier on standards: a parenthesized year beside an\n # edition. This is the ONLY base-level year capture — periodical\n # years are volumes (the serial-locator unification).\n year_qualifier = space \"(\" ((\"19\" / \"20\") 2*digit) as year \")\"\n\n # The series-reference part value: a check standard applied to\n # another IHO series (S-158 checks S-102). Ordered FIRST so \"Part\n # S-102\" takes the series-reference branch, not the lone letter.\n part_series_ref = ((\"S\" / \"P\" / \"M\" / \"B\" / \"C\") dash (1*digit))\n part_value = (part_series_ref / (1*digit [*letter]) / upper) as part\n\n # Component-edition duality: each independently-publishable component\n # may carry its own edition (+ year qualifier), mutually exclusive\n # with the base edition context. The captures are position-scoped\n # (<kind>_edition / <kind>_year) so the model knows which component\n # the edition belongs to and the URN attaches ed- to the node it\n # follows.\n part = space \"Part\" space part_value [part_dated]\n part_dated = {\n part_edition_rule\n [part_year_rule]\n }\n part_edition_rule = space (1*digit dot 1*digit dot 1*digit) as part_edition\n part_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as part_year \")\"\n\n supplement = space (\"Suppl\" / \"Sup\") space (1*digit) as supplement [supplement_dated]\n supplement_dated = {\n supplement_edition_rule\n [supplement_year_rule]\n }\n supplement_edition_rule = space (1*digit dot 1*digit dot 1*digit) as supplement_edition\n supplement_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as supplement_year \")\"\n\n annex = space \"Annex\" space upper as annex [annex_dated]\n annex_dated = {\n annex_edition_rule\n [annex_year_rule]\n }\n annex_edition_rule = space (1*digit dot 1*digit dot 1*digit) as annex_edition\n annex_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as annex_year \")\"\n\n appendix_value = (upper / (1*digit)) as appendix\n appendix_subnumber = dash (1*digit) as appendix_subnumber\n appendix = space (\"Appendix\" / \"App\" / \"Ap.\") space appendix_value [appendix_subnumber] [appendix_dated]\n appendix_dated = {\n appendix_edition_rule\n [appendix_year_rule]\n }\n appendix_edition_rule = space (1*digit dot 1*digit dot 1*digit) as appendix_edition\n appendix_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as appendix_year \")\"\n\n volume = space \"Vol.\" space (1*digit) as volume [volume_issue]\n volume_issue = (space \"Issue\" space (1*digit) as issue) / (space \"(\" (1*digit) as issue \")\")\n # The volume's short face — the slash chain (\"IHO P-1/21\",\n # \"IHO P-5/2020\"). The slash no longer joins the number.\n volume_short = \"/\" ((1*digit) as volume) [volume_dated]\n # LEGACY bare year (\"IHO P-5 2024\"): the yearbook year IS a volume,\n # so a bare year binds INTO the volume component. Parse-only alias;\n # the canonical print is \"Vol. N\".\n volume_bare_year = space ((\"19\" / \"20\") 2*digit) as volume\n # The volume's own edition (+ year), shared by the Vol. and slash\n # forms.\n volume_dated = {\n volume_edition_rule\n [volume_year_rule]\n }\n volume_edition_rule = space (1*digit dot 1*digit dot 1*digit) as volume_edition\n volume_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as volume_year \")\"\n\n # The edition slot accepts the dotted form or the v-shorthand; the\n # with-year qualifier may sit right after it.\n dated = {\n (edition / v_edition)\n [year_qualifier]\n }\n\n # The serial tail picks exactly one discriminator: the long volume\n # (with its optional issue) or the legacy bare year — both bind the\n # volume component, and both may carry the volume's own edition.\n serial = {\n (volume / volume_bare_year)\n [volume_dated]\n }\n\n identifier = {\n [(\"IHO\" space)]\n series\n dash\n number\n [compact_part]\n [glued]\n [volume_short]\n [dated]\n [supplement]\n [part]\n [annex]\n [appendix]\n [serial]\n [year_qualifier]\n }\n\n entry identifier: identifier\n\n test {\n accept \"S-5\"\n accept \"IHO S-5 Appendix 2\"\n accept \"IHO B-12 3.0.0\"\n accept \"IHO P-5 2024\"\n accept \"P-5 2024\"\n accept \"IHO P-5 Vol. 2024\"\n accept \"IHO P-5/2024\"\n accept \"IHO B-7 Vol. 2023\"\n accept \"IHO P-1 Vol. 30\"\n accept \"IHO P-1 Vol. 30 (2)\"\n accept \"IHO P-1 Vol. 30 Issue 2\"\n accept \"IHO P-1/21\"\n accept \"IHO P-1/21 1.0.0\"\n accept \"IHO S-100 5.0.0 Part 2\"\n accept \"IHO S-98 1.0.0 Part A\"\n accept \"IHO S-65 1.1.0 Annex A\"\n accept \"IHO S-122 1.0.0 Appendix D-2\"\n accept \"IHO S-52 4.0.0 Appendix 1\"\n accept \"IHO M-2 Sup 1\"\n accept \"IHO M-2 Suppl 6\"\n accept \"IHO S-66 2.0.0 Sup 4\"\n accept \"IHO S-5-B\"\n accept \"IHO S-100-1\"\n accept \"IHO S-100-1 1.0.0\"\n accept \"IHO P-6-3 1.0.0\"\n accept \"IHO S-5A\"\n accept \"IHO S-5A 1.0.2\"\n accept \"IHO S-5 1.0.2 Part A\"\n accept \"IHO S-100 5.0.0 Part 2 (2024)\"\n accept \"IHO S-100 v5.1\"\n accept \"IHO S-100 v1\"\n accept \"IHO S-100 1.0.0 (2024) Part A\"\n accept \"IHO S-53 Ap. 1 1.0.0\"\n accept \"IHO S-98 Part A 1.0.0\"\n accept \"IHO S-158:100 1.0.0\"\n accept \"IHO S-158:100 Part 2 1.0.0\"\n accept \"IHO S-1001\"\n accept \"IHO S-158 Part S-102 0.2.0\"\n accept \"IHO S-158-S-102 0.2.0\"\n accept \"IHO P-6 1.0.0 Vol. 2\"\n accept \"IHO P-6 Vol. 2 1.0.1\"\n accept \"IHO S-98 1.0.0 Annex A\"\n accept \"IHO S-100 5.0.0 Sup 1 2.0.0\"\n reject \"nonsense\"\n reject \"IHO B-12 3.0\"\n reject \"IHO P-5 1850\"\n }\n\n bindings identifier {\n type -> type (string)\n number -> number (string)\n part -> part (string)\n part_edition -> part_edition (string)\n part_year -> part_year (string)\n annex -> annex (string)\n annex_edition -> annex_edition (string)\n annex_year -> annex_year (string)\n appendix -> appendix (string)\n appendix_subnumber -> appendix_subnumber (string)\n appendix_edition -> appendix_edition (string)\n appendix_year -> appendix_year (string)\n supplement -> supplement (string)\n supplement_edition -> supplement_edition (string)\n supplement_year -> supplement_year (string)\n edition -> edition (string)\n edition_shorthand -> edition_shorthand (string)\n year -> year (string)\n volume -> volume (string)\n volume_edition -> volume_edition (string)\n volume_year -> volume_year (string)\n issue -> issue (string)\n }\n}\n","source_hash":"66d50de135e5c416bfe003de604effff57ed3b556a7126262c449b5c850995ac","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.126","edition":2,"semantics_hash":"sha256:711b011976f08ffc691672fafa41f9b60e3f6024a2608451043836fd6d05eff0","checksum":"sha256:15324490896b11703cb63893072ed77fbc43f43e47dd166ae8ddb235e3c2202e"}
1
+ {"bindings":null,"version":"0.3.0","grammar":"PubidIho","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier_entry","grammar":{"atoms":[{"Entity":{"atom":41}},{"Str":{"pattern":"IHO"}},{"Entity":{"atom":3}},{"Str":{"pattern":" "}},{"Sequence":{"atoms":[1,2]}},{"Repetition":{"atom":4,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"B"}},{"Named":{"name":"spec_part","atom":6}},{"Entity":{"atom":9}},{"Str":{"pattern":"-"}},{"Entity":{"atom":23}},{"Entity":{"atom":22}},{"Str":{"pattern":"1"}},{"Str":{"pattern":"2"}},{"Str":{"pattern":"3"}},{"Str":{"pattern":"4"}},{"Str":{"pattern":"5"}},{"Str":{"pattern":"6"}},{"Alternative":{"atoms":[12,13,14,15,16,17]}},{"Entity":{"atom":20}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Entity":{"atom":20}},{"Sequence":{"atoms":[18,19,21]}},{"Named":{"name":"spec","atom":11}},{"Entity":{"atom":30}},{"Entity":{"atom":26}},{"Str":{"pattern":"."}},{"Entity":{"atom":20}},{"Repetition":{"atom":27,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"spec_sub","atom":28}},{"Sequence":{"atoms":[25,29]}},{"Repetition":{"atom":24,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":39}},{"Str":{"pattern":"("}},{"Entity":{"atom":35}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Repetition":{"atom":34,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"spec_variant","atom":36}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[33,37,38]}},{"Repetition":{"atom":32,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[5,7,8,10,31,40]}},{"Entity":{"atom":435}},{"Str":{"pattern":"IHO"}},{"Entity":{"atom":3}},{"Sequence":{"atoms":[43,44]}},{"Repetition":{"atom":45,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":54}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[48,49,50,51,52]}},{"Named":{"name":"type","atom":53}},{"Entity":{"atom":9}},{"Entity":{"atom":66}},{"Entity":{"atom":20}},{"Repetition":{"atom":57,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":63}},{"Str":{"pattern":":"}},{"Entity":{"atom":20}},{"Repetition":{"atom":61,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[60,62]}},{"Repetition":{"atom":59,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[58,64]}},{"Named":{"name":"number","atom":65}},{"Entity":{"atom":92}},{"Str":{"pattern":"-"}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[69,70,71,72,73]}},{"Entity":{"atom":9}},{"Entity":{"atom":20}},{"Repetition":{"atom":76,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[74,75,77]}},{"Entity":{"atom":20}},{"Repetition":{"atom":79,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":85}},{"Entity":{"atom":35}},{"Entity":{"atom":84}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Alternative":{"atoms":[82,83]}},{"Repetition":{"atom":81,"min":0,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":86,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[80,87]}},{"Entity":{"atom":35}},{"Alternative":{"atoms":[78,88,89]}},{"Named":{"name":"part","atom":90}},{"Sequence":{"atoms":[68,91]}},{"Repetition":{"atom":67,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":96}},{"Entity":{"atom":35}},{"Named":{"name":"part","atom":95}},{"Repetition":{"atom":94,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":132}},{"Str":{"pattern":"/"}},{"Entity":{"atom":20}},{"Repetition":{"atom":100,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"volume","atom":101}},{"Entity":{"atom":130}},{"Entity":{"atom":116}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":106,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":109,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":112,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[107,108,110,111,113]}},{"Named":{"name":"volume_edition","atom":114}},{"Sequence":{"atoms":[105,115]}},{"Entity":{"atom":128}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[120,121]}},{"Entity":{"atom":20}},{"Repetition":{"atom":123,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[122,124]}},{"Named":{"name":"volume_year","atom":125}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[118,119,126,127]}},{"Repetition":{"atom":117,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[104,129]}},{"Repetition":{"atom":103,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[99,102,131]}},{"Repetition":{"atom":98,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":175}},{"Entity":{"atom":147}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":137,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":140,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":143,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[138,139,141,142,144]}},{"Named":{"name":"edition","atom":145}},{"Sequence":{"atoms":[136,146]}},{"Entity":{"atom":160}},{"Entity":{"atom":3}},{"Str":{"pattern":"v"}},{"Entity":{"atom":20}},{"Repetition":{"atom":151,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Entity":{"atom":20}},{"Repetition":{"atom":154,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[153,155]}},{"Repetition":{"atom":156,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[152,157]}},{"Named":{"name":"edition_shorthand","atom":158}},{"Sequence":{"atoms":[149,150,159]}},{"Alternative":{"atoms":[135,148]}},{"Entity":{"atom":173}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[165,166]}},{"Entity":{"atom":20}},{"Repetition":{"atom":168,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[167,169]}},{"Named":{"name":"year","atom":170}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[163,164,171,172]}},{"Repetition":{"atom":162,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[161,174]}},{"Repetition":{"atom":134,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":215}},{"Entity":{"atom":3}},{"Str":{"pattern":"Suppl"}},{"Str":{"pattern":"Sup"}},{"Alternative":{"atoms":[179,180]}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":183,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"supplement","atom":184}},{"Entity":{"atom":213}},{"Entity":{"atom":199}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":189,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":192,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":195,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[190,191,193,194,196]}},{"Named":{"name":"supplement_edition","atom":197}},{"Sequence":{"atoms":[188,198]}},{"Entity":{"atom":211}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[203,204]}},{"Entity":{"atom":20}},{"Repetition":{"atom":206,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[205,207]}},{"Named":{"name":"supplement_year","atom":208}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[201,202,209,210]}},{"Repetition":{"atom":200,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[187,212]}},{"Repetition":{"atom":186,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[178,181,182,185,214]}},{"Repetition":{"atom":177,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":297}},{"Entity":{"atom":3}},{"Str":{"pattern":"Part"}},{"Entity":{"atom":3}},{"Entity":{"atom":241}},{"Entity":{"atom":232}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[223,224,225,226,227]}},{"Entity":{"atom":9}},{"Entity":{"atom":20}},{"Repetition":{"atom":230,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[228,229,231]}},{"Entity":{"atom":20}},{"Repetition":{"atom":233,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":85}},{"Repetition":{"atom":235,"min":0,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":236,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[234,237]}},{"Entity":{"atom":35}},{"Alternative":{"atoms":[222,238,239]}},{"Named":{"name":"part","atom":240}},{"Entity":{"atom":269}},{"Entity":{"atom":255}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":245,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":248,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":251,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[246,247,249,250,252]}},{"Named":{"name":"part_edition","atom":253}},{"Sequence":{"atoms":[244,254]}},{"Entity":{"atom":267}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[259,260]}},{"Entity":{"atom":20}},{"Repetition":{"atom":262,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[261,263]}},{"Named":{"name":"part_year","atom":264}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[257,258,265,266]}},{"Repetition":{"atom":256,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[243,268]}},{"Repetition":{"atom":242,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":295}},{"Str":{"pattern":","}},{"Entity":{"atom":3}},{"Str":{"pattern":"Section"}},{"Entity":{"atom":3}},{"Entity":{"atom":22}},{"Named":{"name":"spec_section","atom":276}},{"Str":{"pattern":","}},{"Entity":{"atom":3}},{"Entity":{"atom":292}},{"Str":{"pattern":"A"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[281,282,283]}},{"Named":{"name":"spec_part","atom":284}},{"Entity":{"atom":9}},{"Entity":{"atom":23}},{"Entity":{"atom":30}},{"Repetition":{"atom":288,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":39}},{"Repetition":{"atom":290,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[285,286,287,289,291]}},{"Sequence":{"atoms":[278,279,280]}},{"Repetition":{"atom":293,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[272,273,274,275,277,294]}},{"Repetition":{"atom":271,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[218,219,220,221,270,296]}},{"Repetition":{"atom":217,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":302}},{"Entity":{"atom":3}},{"Entity":{"atom":292}},{"Sequence":{"atoms":[300,301]}},{"Repetition":{"atom":299,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":339}},{"Entity":{"atom":3}},{"Str":{"pattern":"Annex"}},{"Entity":{"atom":3}},{"Entity":{"atom":35}},{"Named":{"name":"annex","atom":308}},{"Entity":{"atom":337}},{"Entity":{"atom":323}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":313,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":316,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":319,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[314,315,317,318,320]}},{"Named":{"name":"annex_edition","atom":321}},{"Sequence":{"atoms":[312,322]}},{"Entity":{"atom":335}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[327,328]}},{"Entity":{"atom":20}},{"Repetition":{"atom":330,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[329,331]}},{"Named":{"name":"annex_year","atom":332}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[325,326,333,334]}},{"Repetition":{"atom":324,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[311,336]}},{"Repetition":{"atom":310,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[305,306,307,309,338]}},{"Repetition":{"atom":304,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":390}},{"Entity":{"atom":3}},{"Str":{"pattern":"Appendix"}},{"Str":{"pattern":"App"}},{"Str":{"pattern":"Ap."}},{"Alternative":{"atoms":[343,344,345]}},{"Entity":{"atom":3}},{"Entity":{"atom":353}},{"Entity":{"atom":35}},{"Entity":{"atom":20}},{"Repetition":{"atom":350,"min":1,"max":null,"tag":"Repetition"}},{"Alternative":{"atoms":[349,351]}},{"Named":{"name":"appendix","atom":352}},{"Entity":{"atom":359}},{"Entity":{"atom":9}},{"Entity":{"atom":20}},{"Repetition":{"atom":356,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"appendix_subnumber","atom":357}},{"Sequence":{"atoms":[355,358]}},{"Repetition":{"atom":354,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":388}},{"Entity":{"atom":374}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":364,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":367,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":370,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[365,366,368,369,371]}},{"Named":{"name":"appendix_edition","atom":372}},{"Sequence":{"atoms":[363,373]}},{"Entity":{"atom":386}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[378,379]}},{"Entity":{"atom":20}},{"Repetition":{"atom":381,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[380,382]}},{"Named":{"name":"appendix_year","atom":383}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[376,377,384,385]}},{"Repetition":{"atom":375,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[362,387]}},{"Repetition":{"atom":361,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[342,346,347,348,360,389]}},{"Repetition":{"atom":341,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":431}},{"Entity":{"atom":417}},{"Entity":{"atom":3}},{"Str":{"pattern":"Vol."}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":397,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"volume","atom":398}},{"Entity":{"atom":415}},{"Entity":{"atom":3}},{"Str":{"pattern":"Issue"}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":404,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"issue","atom":405}},{"Sequence":{"atoms":[401,402,403,406]}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Entity":{"atom":20}},{"Repetition":{"atom":410,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"issue","atom":411}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[408,409,412,413]}},{"Alternative":{"atoms":[407,414]}},{"Repetition":{"atom":400,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[394,395,396,399,416]}},{"Entity":{"atom":427}},{"Entity":{"atom":3}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[420,421]}},{"Entity":{"atom":20}},{"Repetition":{"atom":423,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[422,424]}},{"Named":{"name":"volume","atom":425}},{"Sequence":{"atoms":[419,426]}},{"Alternative":{"atoms":[393,418]}},{"Entity":{"atom":130}},{"Repetition":{"atom":429,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[428,430]}},{"Repetition":{"atom":392,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":173}},{"Repetition":{"atom":433,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[46,47,55,56,93,97,133,176,216,298,303,340,391,432,434]}},{"Alternative":{"atoms":[0,42]}}],"root":436},"bindings":[{"capture":"type","path":"type","type":"string","card":null,"preprocess":null},{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"part","path":"part","type":"string","card":null,"preprocess":null},{"capture":"part_edition","path":"part_edition","type":"string","card":null,"preprocess":null},{"capture":"part_year","path":"part_year","type":"string","card":null,"preprocess":null},{"capture":"annex","path":"annex","type":"string","card":null,"preprocess":null},{"capture":"annex_edition","path":"annex_edition","type":"string","card":null,"preprocess":null},{"capture":"annex_year","path":"annex_year","type":"string","card":null,"preprocess":null},{"capture":"appendix","path":"appendix","type":"string","card":null,"preprocess":null},{"capture":"appendix_subnumber","path":"appendix_subnumber","type":"string","card":null,"preprocess":null},{"capture":"appendix_edition","path":"appendix_edition","type":"string","card":null,"preprocess":null},{"capture":"appendix_year","path":"appendix_year","type":"string","card":null,"preprocess":null},{"capture":"supplement","path":"supplement","type":"string","card":null,"preprocess":null},{"capture":"supplement_edition","path":"supplement_edition","type":"string","card":null,"preprocess":null},{"capture":"supplement_year","path":"supplement_year","type":"string","card":null,"preprocess":null},{"capture":"edition","path":"edition","type":"string","card":null,"preprocess":null},{"capture":"edition_shorthand","path":"edition_shorthand","type":"string","card":null,"preprocess":null},{"capture":"year","path":"year","type":"string","card":null,"preprocess":null},{"capture":"volume","path":"volume","type":"string","card":null,"preprocess":null},{"capture":"volume_edition","path":"volume_edition","type":"string","card":null,"preprocess":null},{"capture":"volume_year","path":"volume_year","type":"string","card":null,"preprocess":null},{"capture":"issue","path":"issue","type":"string","card":null,"preprocess":null},{"capture":"spec","path":"spec","type":"string","card":null,"preprocess":null},{"capture":"spec_sub","path":"spec_sub","type":"string","card":null,"preprocess":null},{"capture":"spec_variant","path":"spec_variant","type":"string","card":null,"preprocess":null},{"capture":"spec_part","path":"spec_part","type":"string","card":null,"preprocess":null},{"capture":"spec_section","path":"spec_section","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule volume_issue: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier_entry: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"S-5","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5 Appendix 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO B-12 3.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"P-5 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5 Vol. 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5/2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO B-7 Vol. 2023","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30 (2)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30 Issue 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1/21","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1/21 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Part 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 1.0.0 Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-65 1.1.0 Annex A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-122 1.0.0 Appendix D-2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-52 4.0.0 Appendix 1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO M-2 Sup 1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO M-2 Suppl 6","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-66 2.0.0 Sup 4","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5-B","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100-1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100-1 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6-3 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5A 1.0.2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5 1.0.2 Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Part 2 (2024)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 v5.1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 v1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 1.0.0 (2024) Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-53 Ap. 1 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 Part A 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158:100 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158:100 Part 2 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-1001","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158 Part S-102 0.2.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158-S-102 0.2.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6 1.0.0 Vol. 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6 Vol. 2 1.0.1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 1.0.0 Annex A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Sup 1 2.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-401","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 4.10.0 B-401","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-122.1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-521(I)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-351.1(I)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-400","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-170","expect":{}},{"entry":"identifier","kind":"accept","input":"B-401","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO B-401","expect":{}},{"entry":"identifier","kind":"accept","input":"B-122.1","expect":{}},{"entry":"identifier","kind":"accept","input":"B-521(I)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 Part B, Section 400","expect":{}},{"entry":"identifier","kind":"accept","input":"S-4 Part B, Section 400","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 Part B, Section 400, B-401","expect":{}},{"entry":"identifier","kind":"accept","input":"B-12","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO S-4 B-4","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO S-4 B-40","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO S-4 B-800","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO B-12 3.0","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO P-5 1850","expect":{}}],"docs":{},"source":"# Port of lib/pubid/iho/parser.rb — IHO identifiers: letter series\n# (S/P/M/B/C), a number with an optional suffix, and the full component\n# tail (edition, supplement, part, annex, appendix + subnumber, volume,\n# issue, year qualifier).\n#\n# == Component model (owner rulings, 2026-11-03)\n#\n# Every component below is a first-class semantic component: its own\n# named capture, its own typed binding, independently addressable in\n# the model, URN-encodable and matchable — never a regex/string suffix.\n#\n# component face capture binding type\n# edition x.y.z (bare) edition string\n# edition v1 / v5.1 (alias) edition_shorthand string (parse-only;\n# the builder pads to\n# x.y.0 — official IHO\n# edition is dotted\n# x.y.z WITHOUT a year)\n# supplement Sup N (Suppl N) supplement string\n# part Part X / Part S-102 part string (letter,\n# number, or SERIES\n# REFERENCE)\n# part -X / -S-102 (short) part string (hyphenated)\n# annex Annex A annex string\n# appendix Appendix B appendix string\n# appendix Appendix D-2 appendix_subnumber string (the -2)\n# volume Vol. N volume string\n# volume /N (short) volume string (the slash\n# chain, P-1/21 or\n# P-5/2020)\n# volume N (bare, legacy) volume string (the yearbook\n# year IS a volume —\n# one serial-locator\n# component)\n# issue (N) / Issue N issue string\n# year (2024) year string (the with-year\n# qualifier on\n# standards; NOT a\n# serial locator)\n#\n# == Component-edition duality (owner ruling: \"this can happen with any\n# independently publishable artifacts\")\n#\n# Supplement, part, annex, appendix and volume can be published on\n# their own publication cycle, so EACH carries an optional\n# `<kind>_edition` (+ `<kind>_year` qualifier) of its own, mutually\n# exclusive with the base edition context:\n#\n# within-edition: IHO P-6 1.0.0 Vol. 2 (the volume rides\n# the base's edition)\n# separately-published: IHO P-6 Vol. 2 1.0.1 (the volume carries\n# IHO S-98 Part A 1.0.0 its own edition;\n# IHO S-158 Part S-102 0.2.0 the base is\n# cited edition-less)\n#\n# In the URN the ed- segment attaches to the node it follows, so the\n# same positional slot records both modes.\n#\n# The part has a COMPACT face, hyphenated onto the code — `IHO S-5-B`\n# (lettered part), `IHO S-100-1` (numbered part), `IHO S-158-S-102`\n# (series-reference part) — which both parses and renders; the\n# canonical expanded face stays `IHO S-100 x.y.z Part 1`. Gluing a part\n# onto the code without a separator (S-5A) is DISALLOWED as a form: it\n# cannot extend to numbered parts without ambiguity (S-1001 = series\n# 1001 vs series 100 part 1). The four lettered bases the corpus\n# records (S-5A, S-5B, S-8A, S-8B) are LEGACY data spellings: they\n# still parse as aliases (S-5A == S-5 Part A) but are never rendered;\n# the owner rules later whether to reject them outright.\n#\n# Series-reference parts: a check standard applied to another IHO\n# series cites the applied series as its part — `IHO S-158 0.2.0 Part\n# S-102` (the wild glued `S-158-102` spelling is a legacy alias whose\n# normalization to Part S-102 needs an IHO series table; the syntax\n# alone cannot tell it from S-100-1 = Part 1, so it currently parses\n# as the plain number part).\n#\n# Two main syntaxes per the owner ruling: `IHO S-100 1.0.0` (dated =\n# edition-specific) and `IHO S-100` (undated = edition-non-specific).\n# A year beside an edition is the with-year variant qualifier\n# (`IHO S-100 1.0.0 Part X (2024)`), never a year-keyed docidentifier —\n# year-keyed docidentifiers for edition-keyed products are wrong. The\n# continuously-updated series have ONE serial locator, the volume:\n# P-1 and P-6 are volume-keyed (`IHO P-1 Vol. 30 (1)`), and the\n# yearbook year IS a volume (`IHO P-5 Vol. 2020`) — the bare year\n# (`IHO P-5 2020`) and all year-faced forms are parse-only legacy\n# aliases that bind into the volume component, never rendered.\n#\n# Aliases: \"Suppl\" (canonical face Sup, the M-2 spelling — why abbreviate\n# by two letters when Annex and Appendix stay full-length), \"App\"/\"Ap.\"\n# (canonical face Appendix), the hyphenated compact part face (S-5-B ==\n# S-5 Part B, S-100-1 == S-100 Part 1), the legacy glued lettered bases\n# (S-5A, parse-only), \"S-100 v1..v5.1\" (edition shorthand), \"Issue N\"\n# (canonical (N)), the bare year (canonical Vol. N).\n#\n# == URN segment grammar (positional, containment-ordered)\n#\n# The URN mirrors the containment tree, one semantic atom per segment:\n# Part contains Annexes; Annex contains Appendices; a D-2 subnumber is\n# a child atom of Appendix D. No key.value concatenations — every\n# non-number component is a marker-prefixed segment whose marker is the\n# lowercased human-face token (edition follows ISO's ed- precedent).\n# An S-4 specification is a child of its Part (the citation letter rides\n# the part- atom), with the dotted sub and the parenthesized variant as\n# its own children:\n#\n# urn:iho:<type>:<number>[:ed-<x.y.z>][:year-<yyyy>]\n# [:sup-<n>[:ed-<x.y.z>][:year-<yyyy>]]\n# [:part-<X>[:ed-<x.y.z>][:year-<yyyy>]]\n# [:spec-<n>[:specsub-<n>][:specvar-<X>]]\n# [:annex-<A>[...]]\n# [:appendix-<B>[:<sub>][...]]\n# [:vol-<n>[:issue-<n>][...]]\n# [:year-<yyyy>]\n#\n# position segment value vocabulary containment\n# -------- --------------- -------------------------- ------------------\n# 1 <type> s / p / m / b / c namespace root\n# 2 <number> digits + : suffix; may document code\n# span bare segments until\n# the first marker\n# 3 ed-<x.y.z> dotted a.b.c edition qualifier;\n# 4 year-<yyyy> 19xx/20xx ATTACHES TO THE\n# NODE IT FOLLOWS: the\n# base (no component\n# before it) or the\n# component whose\n# segment precedes\n# 5 sup-<n> 1*digit wraps the base\n# 6 part-<X> letter (case kept), contains annexes\n# digits [*letter], or a and S-4 specs\n# series reference\n# [SPMBC]-<digits>\n# 7 spec-<n> [1-6]dd, only under an child of part (the\n# S-4 anchor (s:4) S-4 leaf; the\n# section is the\n# hundreds digit,\n# derivable, never\n# emitted)\n# 8 specsub-<n> 1*digit, only after spec- child of spec\n# 9 specvar-<X> upper letters, after child of spec\n# spec- or specsub-\n# 10 annex-<A> upper letter (case kept) contains appendices\n# 11 appendix-<B> upper letter or digits contains subnumber\n# 12 <sub> 1*digit, BARE, only child of appendix\n# directly after appendix-\n# 13 vol-<n> 1*digit (incl. yearbook contains issue\n# years, Vol. 2020)\n# 14 issue-<n> 1*digit, only after vol- child of volume\n#\n# The number occupies every leading bare segment up to the first\n# marker, so a colon number (S-158:100) round-trips without escaping;\n# after the markers the only bare segment allowed is the appendix\n# subnumber in its own slot. Both the year qualifier and the volume are\n# marker-tagged so neither can collide with a colon-number segment (the\n# #637 hazard, kept under the positional scheme). Letters are\n# case-preserved: part-A and part-a are distinct documents.\n#\n# Legacy 2.0.x spellings remain decodable but are never emitted: the\n# bare a.b.c segment (edition), the dotted keys part./annex./suppl./\n# ap./appendix./vol./issue. and the single-letter y. year tag (whose\n# value decodes into the volume — the serial-locator unification).\n#\n# == S-4 loose-leaf sections and specifications (issue #691; owner\n# rulings of 2026-11-11)\n#\n# The S-4 loose-leaf publication cites its regulations/specifications as\n# <letter>-<hundreds><seq>: B-401 is Part B, Section 400, item 01. ONE\n# component, not two tiers: the spec number IS the leaf; the SECTION is\n# the hundreds digit, DERIVED (never stored, never emitted). The `00`\n# slot (B-400) IS the section-level citation, so \"S-4 Part B, Section\n# 400\" and \"IHO S-4 B-400\" are one identity, two spellings.\n#\n# capture example role\n# spec B-401 -> \"401\" the leaf number (3 digits,\n# hundreds 1-6: sections 100-600)\n# spec_sub B-122.1 -> \"1\" the dotted sub\n# spec_variant B-521(I) -> \"I\" the parenthesized variant\n# spec_part B-401 -> \"B\" the citation letter (the builder\n# binds it into `part`; a Part-rule\n# capture must agree)\n# spec_section \", Section 400\" -> \"400\" the long form's spelled\n# section (becomes `spec` alone;\n# checked against a citation)\n#\n# Faces: canonical compact \"IHO S-4 B-401\" (dated: \"IHO S-4 4.10.0\n# B-401\"); long \"S-4 Part B, Section 400\" (a normalizing parse; also\n# takes a trailing citation: \"S-4 Part B, Section 400, B-401\", where the\n# spelled section only cross-checks the citation's derived section); bare\n# \"B-401\" (implicit S-4 anchor; B only — bare C-x stays the Circular\n# Letter series). The builder binds the anchor: a citation requires the\n# S-4 anchor (or synthesizes it for the bare form).\n#\n# Reserved numbers (\"NOT CURRENTLY USED\" in the S-4 contents — B-170,\n# B-230, B-305, B-323 in Ed 4.9.0 and 4.10.0) parse and resolve AS\n# RESERVED, never invalid; the edition-keyed registry lives in the pubid\n# gem (data/iho/s4-reserved-specs.yaml).\n#\n# URN atoms: part-<letter>:spec-<n>[:specsub-<n>][:specvar-<X>] — the\n# section is NOT emitted (derivable); the citation letter rides the\n# existing part- atom (case preserved: part-B).\n#\n# Edition 2: ruby#400's capture-flatten keeps text captures over\n# reference-built rules as the matched text, so the v2 tree is the v1\n# tree plus named leaves for non-captured structure.\n\ngrammar PubidIho version \"0.3.0\" edition 2 {\n digit = %x30-39\n upper = %x41-5A\n lower = %x61-7A\n letter = upper / lower\n space = \" \"\n dash = \"-\"\n dot = \".\"\n\n series = (\"S\" / \"P\" / \"M\" / \"B\" / \"C\") as type\n number_suffix = (\":\" 1*digit)\n number = (1*digit [number_suffix]) as number\n # The compact part face, hyphenated onto the code: S-5-B (lettered),\n # S-100-1 (numbered) and S-158-S-102 (series reference) — the dash\n # replaces the old bare-glued suffix, so a number like S-1001 stays a\n # whole number (the dash is what makes the part unambiguous). The\n # dash is an inlined literal: a named dash ref here collides with the\n # sequence's own dash node in the edition-2 tree.\n compact_part = \"-\" (((\"S\" / \"P\" / \"M\" / \"B\" / \"C\") dash (1*digit)) / (1*digit [*letter]) / upper) as part\n # LEGACY glued lettered bases (S-5A, S-5B, S-8A, S-8B in the corpus):\n # parse-only aliases, never rendered. Kept because the records exist;\n # the owner rules later whether to reject them.\n glued = upper as part\n\n # The official edition is the dotted x.y.z; \"v1\"/\"v5.1\" is the S-100\n # shorthand (parse-only, the builder pads to x.y.0).\n edition = space (1*digit dot 1*digit dot 1*digit) as edition\n v_edition = space \"v\" ((1*digit) [\".\" (1*digit)]) as edition_shorthand\n # The with-year qualifier on standards: a parenthesized year beside an\n # edition. This is the ONLY base-level year capture — periodical\n # years are volumes (the serial-locator unification).\n year_qualifier = space \"(\" ((\"19\" / \"20\") 2*digit) as year \")\"\n\n # The series-reference part value: a check standard applied to\n # another IHO series (S-158 checks S-102). Ordered FIRST so \"Part\n # S-102\" takes the series-reference branch, not the lone letter.\n part_series_ref = ((\"S\" / \"P\" / \"M\" / \"B\" / \"C\") dash (1*digit))\n part_value = (part_series_ref / (1*digit [*letter]) / upper) as part\n\n # Component-edition duality: each independently-publishable component\n # may carry its own edition (+ year qualifier), mutually exclusive\n # with the base edition context. The captures are position-scoped\n # (<kind>_edition / <kind>_year) so the model knows which component\n # the edition belongs to and the URN attaches ed- to the node it\n # follows.\n part = space \"Part\" space part_value [part_dated] [part_section_tail]\n part_dated = {\n part_edition_rule\n [part_year_rule]\n }\n part_edition_rule = space (1*digit dot 1*digit dot 1*digit) as part_edition\n part_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as part_year \")\"\n\n # --- S-4 loose-leaf sections and specifications (see the header) ---\n # The spec number's vocabulary: the S-4 sections run 100-600 (Part C\n # 100-500), so the leaf number is 3 digits with hundreds digit 1-6 —\n # the same shape the bare citation branch leans on to keep the B\n # Bibliographic series (B-1..B-13 style) distinct from B-4xx specs.\n spec_shape = (\"1\" / \"2\" / \"3\" / \"4\" / \"5\" / \"6\") digit digit\n spec_number = spec_shape as spec\n spec_subnumber = dot (1*digit) as spec_sub\n spec_variant = \"(\" (1*upper) as spec_variant \")\"\n # The citation letter is the spec's part (A/B/C); the builder binds it\n # into `part` and rejects a conflicting Part-rule capture.\n spec_citation_body = (\"A\" / \"B\" / \"C\") as spec_part dash spec_number [spec_subnumber] [spec_variant]\n # The compact anchor form: the citation follows the code after one\n # space (\"IHO S-4 B-401\").\n spec_citation = space spec_citation_body\n # The spelled section long form: \"Part B, Section 400\" — the spelled\n # number becomes the spec (the section-level citation), and a trailing\n # citation (\", Section 400, B-401\") makes it a cross-check only. The\n # commas are inlined literals: named comma refs collide as duplicate\n # edition-2 tree nodes under the entry's alternation (the same node\n # collision the compact_part dash-ref comment documents).\n part_section_tail = {\n \",\" space \"Section\" space (spec_shape as spec_section)\n [\",\" space spec_citation_body]\n }\n\n # The bare citation branch (the entry's FIRST alternative): \"B-401\" /\n # \"IHO B-401\" — implicit S-4 anchor (the builder synthesizes series S\n # number 4). B only: bare C-x stays the Circular Letter series, and the\n # 3-digit [1-6]xx shape keeps the B Bibliographic series (B-12) on the\n # main branch.\n bare_spec_citation = [(\"IHO\" space)] (\"B\" as spec_part) dash spec_number [spec_subnumber] [spec_variant]\n\n supplement = space (\"Suppl\" / \"Sup\") space (1*digit) as supplement [supplement_dated]\n supplement_dated = {\n supplement_edition_rule\n [supplement_year_rule]\n }\n supplement_edition_rule = space (1*digit dot 1*digit dot 1*digit) as supplement_edition\n supplement_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as supplement_year \")\"\n\n annex = space \"Annex\" space upper as annex [annex_dated]\n annex_dated = {\n annex_edition_rule\n [annex_year_rule]\n }\n annex_edition_rule = space (1*digit dot 1*digit dot 1*digit) as annex_edition\n annex_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as annex_year \")\"\n\n appendix_value = (upper / (1*digit)) as appendix\n appendix_subnumber = dash (1*digit) as appendix_subnumber\n appendix = space (\"Appendix\" / \"App\" / \"Ap.\") space appendix_value [appendix_subnumber] [appendix_dated]\n appendix_dated = {\n appendix_edition_rule\n [appendix_year_rule]\n }\n appendix_edition_rule = space (1*digit dot 1*digit dot 1*digit) as appendix_edition\n appendix_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as appendix_year \")\"\n\n volume = space \"Vol.\" space (1*digit) as volume [volume_issue]\n volume_issue = (space \"Issue\" space (1*digit) as issue) / (space \"(\" (1*digit) as issue \")\")\n # The volume's short face — the slash chain (\"IHO P-1/21\",\n # \"IHO P-5/2020\"). The slash no longer joins the number.\n volume_short = \"/\" ((1*digit) as volume) [volume_dated]\n # LEGACY bare year (\"IHO P-5 2024\"): the yearbook year IS a volume,\n # so a bare year binds INTO the volume component. Parse-only alias;\n # the canonical print is \"Vol. N\".\n volume_bare_year = space ((\"19\" / \"20\") 2*digit) as volume\n # The volume's own edition (+ year), shared by the Vol. and slash\n # forms.\n volume_dated = {\n volume_edition_rule\n [volume_year_rule]\n }\n volume_edition_rule = space (1*digit dot 1*digit dot 1*digit) as volume_edition\n volume_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as volume_year \")\"\n\n # The edition slot accepts the dotted form or the v-shorthand; the\n # with-year qualifier may sit right after it.\n dated = {\n (edition / v_edition)\n [year_qualifier]\n }\n\n # The serial tail picks exactly one discriminator: the long volume\n # (with its optional issue) or the legacy bare year — both bind the\n # volume component, and both may carry the volume's own edition.\n serial = {\n (volume / volume_bare_year)\n [volume_dated]\n }\n\n identifier = {\n [(\"IHO\" space)]\n series\n dash\n number\n [compact_part]\n [glued]\n [volume_short]\n [dated]\n [supplement]\n [part]\n [spec_citation]\n [annex]\n [appendix]\n [serial]\n [year_qualifier]\n }\n\n # The entry dispatches the bare citation branch first (PEG order): a\n # bare \"B-401\"-shaped input takes the S-4 branch, everything else the\n # main identifier.\n identifier_entry = {\n bare_spec_citation / identifier\n }\n\n entry identifier: identifier_entry\n\n test {\n accept \"S-5\"\n accept \"IHO S-5 Appendix 2\"\n accept \"IHO B-12 3.0.0\"\n accept \"IHO P-5 2024\"\n accept \"P-5 2024\"\n accept \"IHO P-5 Vol. 2024\"\n accept \"IHO P-5/2024\"\n accept \"IHO B-7 Vol. 2023\"\n accept \"IHO P-1 Vol. 30\"\n accept \"IHO P-1 Vol. 30 (2)\"\n accept \"IHO P-1 Vol. 30 Issue 2\"\n accept \"IHO P-1/21\"\n accept \"IHO P-1/21 1.0.0\"\n accept \"IHO S-100 5.0.0 Part 2\"\n accept \"IHO S-98 1.0.0 Part A\"\n accept \"IHO S-65 1.1.0 Annex A\"\n accept \"IHO S-122 1.0.0 Appendix D-2\"\n accept \"IHO S-52 4.0.0 Appendix 1\"\n accept \"IHO M-2 Sup 1\"\n accept \"IHO M-2 Suppl 6\"\n accept \"IHO S-66 2.0.0 Sup 4\"\n accept \"IHO S-5-B\"\n accept \"IHO S-100-1\"\n accept \"IHO S-100-1 1.0.0\"\n accept \"IHO P-6-3 1.0.0\"\n accept \"IHO S-5A\"\n accept \"IHO S-5A 1.0.2\"\n accept \"IHO S-5 1.0.2 Part A\"\n accept \"IHO S-100 5.0.0 Part 2 (2024)\"\n accept \"IHO S-100 v5.1\"\n accept \"IHO S-100 v1\"\n accept \"IHO S-100 1.0.0 (2024) Part A\"\n accept \"IHO S-53 Ap. 1 1.0.0\"\n accept \"IHO S-98 Part A 1.0.0\"\n accept \"IHO S-158:100 1.0.0\"\n accept \"IHO S-158:100 Part 2 1.0.0\"\n accept \"IHO S-1001\"\n accept \"IHO S-158 Part S-102 0.2.0\"\n accept \"IHO S-158-S-102 0.2.0\"\n accept \"IHO P-6 1.0.0 Vol. 2\"\n accept \"IHO P-6 Vol. 2 1.0.1\"\n accept \"IHO S-98 1.0.0 Annex A\"\n accept \"IHO S-100 5.0.0 Sup 1 2.0.0\"\n # --- S-4 loose-leaf sections and specifications ---\n accept \"IHO S-4 B-401\"\n accept \"IHO S-4 4.10.0 B-401\"\n accept \"IHO S-4 B-122.1\"\n accept \"IHO S-4 B-521(I)\"\n accept \"IHO S-4 B-351.1(I)\"\n accept \"IHO S-4 B-400\"\n accept \"IHO S-4 B-170\"\n accept \"B-401\"\n accept \"IHO B-401\"\n accept \"B-122.1\"\n accept \"B-521(I)\"\n accept \"IHO S-4 Part B, Section 400\"\n accept \"S-4 Part B, Section 400\"\n accept \"IHO S-4 Part B, Section 400, B-401\"\n accept \"B-12\"\n reject \"IHO S-4 B-4\"\n reject \"IHO S-4 B-40\"\n reject \"IHO S-4 B-800\"\n reject \"nonsense\"\n reject \"IHO B-12 3.0\"\n reject \"IHO P-5 1850\"\n }\n\n bindings identifier_entry {\n type -> type (string)\n number -> number (string)\n part -> part (string)\n part_edition -> part_edition (string)\n part_year -> part_year (string)\n annex -> annex (string)\n annex_edition -> annex_edition (string)\n annex_year -> annex_year (string)\n appendix -> appendix (string)\n appendix_subnumber -> appendix_subnumber (string)\n appendix_edition -> appendix_edition (string)\n appendix_year -> appendix_year (string)\n supplement -> supplement (string)\n supplement_edition -> supplement_edition (string)\n supplement_year -> supplement_year (string)\n edition -> edition (string)\n edition_shorthand -> edition_shorthand (string)\n year -> year (string)\n volume -> volume (string)\n volume_edition -> volume_edition (string)\n volume_year -> volume_year (string)\n issue -> issue (string)\n spec -> spec (string)\n spec_sub -> spec_sub (string)\n spec_variant -> spec_variant (string)\n spec_part -> spec_part (string)\n spec_section -> spec_section (string)\n }\n}\n","source_hash":"2b9f7e6115555242a774cdeb356401be4085991bca0dc92fc9c8fc31ee36b842","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.126","edition":2,"semantics_hash":"sha256:eb5104d7afed4e2d261a17380e1c7ea678e354df6889cbfbbd27cd01e0442a77","checksum":"sha256:54b4f297d6fae2c2af954387ba09f87428ca42661eb170197f8791e7e3dd1c94"}
@@ -1 +1 @@
1
- {"bindings":null,"version":"0.1.0","grammar":"PubidJis","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Entity":{"atom":144}},{"Str":{"pattern":"WES"}},{"Named":{"name":"wes","atom":1}},{"Entity":{"atom":6}},{"Str":{"pattern":" "}},{"Str":{"pattern":" "}},{"Alternative":{"atoms":[4,5]}},{"Entity":{"atom":11}},{"Entity":{"atom":9}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":8,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"number","atom":10}},{"Entity":{"atom":24}},{"Entity":{"atom":22}},{"Entity":{"atom":18}},{"Str":{"pattern":"-"}},{"Str":{"pattern":"ー"}},{"Alternative":{"atoms":[15,16]}},{"Named":{"name":"dash","atom":17}},{"Entity":{"atom":9}},{"Repetition":{"atom":19,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"part","atom":20}},{"Sequence":{"atoms":[14,21]}},{"Repetition":{"atom":13,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"parts","atom":23}},{"Entity":{"atom":40}},{"Entity":{"atom":29}},{"Str":{"pattern":":"}},{"Str":{"pattern":":"}},{"Alternative":{"atoms":[27,28]}},{"Entity":{"atom":9}},{"Repetition":{"atom":30,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"year","atom":31}},{"Entity":{"atom":38}},{"Str":{"pattern":"R"}},{"Str":{"pattern":"R"}},{"Repetition":{"atom":35,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[34,36]}},{"Named":{"name":"reaffirmed","atom":37}},{"Repetition":{"atom":33,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[26,32,39]}},{"Repetition":{"atom":25,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":53}},{"Str":{"pattern":"("}},{"Str":{"pattern":"("}},{"Alternative":{"atoms":[43,44]}},{"Str":{"pattern":"E"}},{"Str":{"pattern":"J"}},{"Alternative":{"atoms":[46,47]}},{"Named":{"name":"language","atom":48}},{"Str":{"pattern":")"}},{"Str":{"pattern":")"}},{"Alternative":{"atoms":[50,51]}},{"Sequence":{"atoms":[45,49,52]}},{"Repetition":{"atom":42,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":65}},{"Str":{"pattern":"("}},{"Str":{"pattern":"規格群"}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[56,57,58]}},{"Str":{"pattern":"("}},{"Str":{"pattern":"規格群"}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[60,61,62]}},{"Alternative":{"atoms":[59,63]}},{"Named":{"name":"all_parts","atom":64}},{"Repetition":{"atom":55,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":127}},{"Entity":{"atom":89}},{"Entity":{"atom":70}},{"Str":{"pattern":"/"}},{"Str":{"pattern":"AMENDMENT"}},{"Str":{"pattern":"AMD"}},{"Alternative":{"atoms":[71,72]}},{"Named":{"name":"amd_type","atom":73}},{"Entity":{"atom":6}},{"Entity":{"atom":9}},{"Repetition":{"atom":76,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"amd_number","atom":77}},{"Entity":{"atom":29}},{"Entity":{"atom":9}},{"Repetition":{"atom":80,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"amd_year","atom":81}},{"Str":{"pattern":"R"}},{"Str":{"pattern":"R"}},{"Repetition":{"atom":84,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[83,85]}},{"Repetition":{"atom":86,"min":0,"max":1,"tag":"Maybe"}},{"Named":{"name":"amd_reaffirmed","atom":87}},{"Sequence":{"atoms":[69,74,75,78,79,82,88]}},{"Named":{"name":"amendment","atom":68}},{"Entity":{"atom":103}},{"Entity":{"atom":70}},{"Str":{"pattern":"EXPLANATION"}},{"Str":{"pattern":"EXPL"}},{"Alternative":{"atoms":[93,94]}},{"Named":{"name":"expl_type","atom":95}},{"Entity":{"atom":6}},{"Entity":{"atom":9}},{"Repetition":{"atom":98,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"expl_number","atom":99}},{"Sequence":{"atoms":[97,100]}},{"Repetition":{"atom":101,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[92,96,102]}},{"Named":{"name":"explanation","atom":91}},{"Entity":{"atom":125}},{"Entity":{"atom":70}},{"Str":{"pattern":"CORRIGENDUM"}},{"Str":{"pattern":"CORR"}},{"Alternative":{"atoms":[107,108]}},{"Named":{"name":"corr_type","atom":109}},{"Entity":{"atom":6}},{"Entity":{"atom":9}},{"Repetition":{"atom":112,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"corr_number","atom":113}},{"Entity":{"atom":29}},{"Entity":{"atom":9}},{"Repetition":{"atom":116,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"corr_year","atom":117}},{"Str":{"pattern":"R"}},{"Str":{"pattern":"R"}},{"Repetition":{"atom":120,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[119,121]}},{"Repetition":{"atom":122,"min":0,"max":1,"tag":"Maybe"}},{"Named":{"name":"corr_reaffirmed","atom":123}},{"Sequence":{"atoms":[106,110,111,114,115,118,124]}},{"Named":{"name":"corrigendum","atom":105}},{"Alternative":{"atoms":[90,104,126]}},{"Repetition":{"atom":67,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":142}},{"Entity":{"atom":6}},{"Str":{"pattern":"SYMBOL"}},{"Named":{"name":"symbol_present","atom":131}},{"Entity":{"atom":6}},{"Entity":{"atom":137}},{"Re":{"pattern":"[\\x01-\\x09]"}},{"Re":{"pattern":"[\\x0b-\\u{10ffff}]"}},{"Alternative":{"atoms":[135,136]}},{"Repetition":{"atom":134,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"symbol_value","atom":138}},{"Sequence":{"atoms":[133,139]}},{"Repetition":{"atom":140,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[130,132,141]}},{"Repetition":{"atom":129,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[2,3,7,12,41,54,66,128,143]}},{"Entity":{"atom":182}},{"Str":{"pattern":"JIS"}},{"Entity":{"atom":6}},{"Entity":{"atom":70}},{"Alternative":{"atoms":[147,148]}},{"Repetition":{"atom":149,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[146,150]}},{"Repetition":{"atom":151,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":161}},{"Str":{"pattern":"TR"}},{"Str":{"pattern":"TS"}},{"Alternative":{"atoms":[154,155]}},{"Named":{"name":"type","atom":156}},{"Entity":{"atom":6}},{"Entity":{"atom":70}},{"Alternative":{"atoms":[158,159]}},{"Sequence":{"atoms":[157,160]}},{"Repetition":{"atom":153,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":6}},{"Repetition":{"atom":163,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":167}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Named":{"name":"series","atom":166}},{"Entity":{"atom":6}},{"Repetition":{"atom":168,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":11}},{"Entity":{"atom":24}},{"Entity":{"atom":40}},{"Repetition":{"atom":172,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":53}},{"Repetition":{"atom":174,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":65}},{"Repetition":{"atom":176,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":127}},{"Repetition":{"atom":178,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":142}},{"Repetition":{"atom":180,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[152,162,164,165,169,170,171,173,175,177,179,181]}},{"Alternative":{"atoms":[0,145]}}],"root":183},"bindings":[{"capture":"wes","path":"wes","type":"string","card":null,"preprocess":null},{"capture":"type","path":"type","type":"string","card":null,"preprocess":null},{"capture":"series","path":"series","type":"string","card":null,"preprocess":null},{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"part","path":"parts[].part","type":"string","card":null,"preprocess":null},{"capture":"year","path":"year","type":"string","card":null,"preprocess":null},{"capture":"reaffirmed","path":"reaffirmed","type":"string","card":null,"preprocess":null},{"capture":"language","path":"language","type":"string","card":null,"preprocess":null},{"capture":"all_parts","path":"all_parts","type":"string","card":null,"preprocess":null},{"capture":"amd_type","path":"amd_type","type":"string","card":null,"preprocess":null},{"capture":"amd_number","path":"amd_number","type":"string","card":null,"preprocess":null},{"capture":"amd_year","path":"amd_year","type":"string","card":null,"preprocess":null},{"capture":"amd_reaffirmed","path":"amd_reaffirmed","type":"string","card":null,"preprocess":null},{"capture":"expl_type","path":"expl_type","type":"string","card":null,"preprocess":null},{"capture":"expl_number","path":"expl_number","type":"string","card":null,"preprocess":null},{"capture":"corr_type","path":"corr_type","type":"string","card":null,"preprocess":null},{"capture":"corr_number","path":"corr_number","type":"string","card":null,"preprocess":null},{"capture":"corr_year","path":"corr_year","type":"string","card":null,"preprocess":null},{"capture":"corr_reaffirmed","path":"corr_reaffirmed","type":"string","card":null,"preprocess":null},{"capture":"symbol_present","path":"symbol_present","type":"string","card":null,"preprocess":null},{"capture":"symbol_value","path":"symbol_value","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule supplement: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule supplement: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule supplement: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule symbol_value_char: branches 1 and 2 are order-dependent (first set not statically known)","rule identifier: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"JIS B 0001","expect":{}},{"entry":"identifier","kind":"accept","input":"JIS X 0208:1997","expect":{}},{"entry":"identifier","kind":"accept","input":"WES 7700-1:2019","expect":{}},{"entry":"identifier","kind":"accept","input":"WES 7301:2020R","expect":{}},{"entry":"identifier","kind":"reject","input":"JIS WES 7700","expect":{}},{"entry":"identifier","kind":"reject","input":"WES X 7700","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}},{"entry":"identifier","kind":"example","input":"WES 7700-1:2019","expect":{"wes":"WES","number":"7700","year":"2019"}},{"entry":"identifier","kind":"example","input":"WES 7301:2020R","expect":{"wes":"WES","number":"7301","year":"2020","reaffirmed":"R"}},{"entry":"identifier","kind":"example","input":"JIS X 0208:1997","expect":{"series":"X","number":"0208","year":"1997"}},{"entry":"identifier","kind":"example","input":"WES 7700:2019/AMD 1:2020R","expect":{"wes":"WES","number":"7700","amd_type":"AMD","amd_number":"1","amd_year":"2020","amd_reaffirmed":"R"}},{"entry":"identifier","kind":"example","input":"JIS X 0208:1997R","expect":{"series":"X","year":"1997","reaffirmed":"R"}},{"entry":"identifier","kind":"example","input":"JIS X 0208:1997RR","expect":{"series":"X","year":"1997","reaffirmed":"RR"}}],"docs":{},"source":"# Port of lib/pubid/jis/parser.rb — JIS identifiers with the Japanese\n# full-width separator tolerance (ー   : ()), TR/TS prefixes, parts,\n# reaffirmed years, (E)/(J) language codes, 規格群 all-parts, AMD/EXPL/\n# CORR supplements, and the SYMBOL clause. WES (Japan Welding Engineering\n# Society) ids share the JIS tail but carry no series letter or JIS prefix.\n\ngrammar PubidJis version \"0.1.0\" edition 2 {\n dash = (\"-\" / \"ー\") as dash\n space = (\" \" / \" \")\n colon = (\":\" / \":\")\n type_prefix = (\"TR\" / \"TS\") as type (space / slash)\n series = %x41-5A as series\n number = (1*digit) as number\n part = dash (1*digit) as part\n parts = (*part) as parts\n reaffirmed = \"R\" (*\"R\")\n year = colon (1*digit) as year [reaffirmed_rule]\n language = (\"(\" / \"(\") (\"E\" / \"J\") as language (\")\" / \")\")\n all_parts = ((\"(\" \"規格群\" \")\") / (\"(\" \"規格群\" \")\")) as all_parts\n amendment = {\n slash (\"AMENDMENT\" / \"AMD\") as amd_type\n space\n (1*digit) as amd_number\n colon (1*digit) as amd_year\n ([\"R\" (*\"R\")]) as amd_reaffirmed\n }\n explanation = {\n slash (\"EXPLANATION\" / \"EXPL\") as expl_type\n ([space (1*digit) as expl_number])\n }\n corrigendum = {\n slash (\"CORRIGENDUM\" / \"CORR\") as corr_type\n space\n (1*digit) as corr_number\n colon (1*digit) as corr_year\n ([\"R\" (*\"R\")]) as corr_reaffirmed\n }\n supplement = {\n (amendment as amendment)\n / (explanation as explanation)\n / (corrigendum as corrigendum)\n }\n symbol_value_char = %x01-09 / %x0B-10FFFF\n symbol_clause = {\n space \"SYMBOL\" as symbol_present\n ([space (1*symbol_value_char) as symbol_value])\n }\n wes_identifier = {\n \"WES\" as wes\n space\n number\n parts\n [year]\n [language]\n [all_parts]\n [supplement]\n [symbol_clause]\n }\n jis_identifier = {\n [\"JIS\" [(space / slash)]]\n [type_prefix]\n [space]\n series\n [space]\n number\n parts\n [year]\n [language]\n [all_parts]\n [supplement]\n [symbol_clause]\n }\n identifier = wes_identifier / jis_identifier\n upper = %x41-5A\n digit = %x30-39\n slash = \"/\"\n reaffirmed_rule = (\"R\" (*\"R\")) as reaffirmed\n entry identifier: identifier\n\n # Typed model bindings: bound captures land on the consumer\n # builder's fields of the same name; repeated parts collect under\n # parts[].part.\n bindings identifier {\n wes -> wes (string)\n type -> type (string)\n series -> series (string)\n number -> number (string)\n part -> parts[].part (string)\n year -> year (string)\n reaffirmed -> reaffirmed (string)\n language -> language (string)\n all_parts -> all_parts (string)\n amd_type -> amd_type (string)\n amd_number -> amd_number (string)\n amd_year -> amd_year (string)\n amd_reaffirmed -> amd_reaffirmed (string)\n expl_type -> expl_type (string)\n expl_number -> expl_number (string)\n corr_type -> corr_type (string)\n corr_number -> corr_number (string)\n corr_year -> corr_year (string)\n corr_reaffirmed -> corr_reaffirmed (string)\n symbol_present -> symbol_present (string)\n symbol_value -> symbol_value (string)\n }\n\n test {\n accept \"JIS B 0001\"\n accept \"JIS X 0208:1997\"\n accept \"WES 7700-1:2019\"\n accept \"WES 7301:2020R\"\n reject \"JIS WES 7700\"\n reject \"WES X 7700\"\n reject \"nonsense\"\n example \"WES 7700-1:2019\" { wes: \"WES\", number: \"7700\", year: \"2019\" }\n example \"WES 7301:2020R\" { wes: \"WES\", number: \"7301\", year: \"2020\", reaffirmed: \"R\" }\n example \"JIS X 0208:1997\" { series: \"X\", number: \"0208\", year: \"1997\" }\n example \"WES 7700:2019/AMD 1:2020R\" { wes: \"WES\", number: \"7700\", amd_type: \"AMD\", amd_number: \"1\", amd_year: \"2020\", amd_reaffirmed: \"R\" }\n example \"JIS X 0208:1997R\" { series: \"X\", year: \"1997\", reaffirmed: \"R\" }\n example \"JIS X 0208:1997RR\" { series: \"X\", year: \"1997\", reaffirmed: \"RR\" }\n }\n}\n","source_hash":"bb68f75e3e1d9b7e79613bd6cff60ccd77f6e2e68ace2569a2fbc968543b1f3d","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.121","edition":2,"semantics_hash":"sha256:b16cf5d2687fef91bb0c21ce6b417cf9d9dfcf9aec0280169acb246f48d6b22b","checksum":"sha256:156eb10c6a7638055e3ca2556c6bf62ecca6521b432c828269cdcbab080c44be"}
1
+ {"bindings":null,"version":"0.1.0","grammar":"PubidJis","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Entity":{"atom":165}},{"Str":{"pattern":"WES"}},{"Named":{"name":"wes","atom":1}},{"Entity":{"atom":6}},{"Str":{"pattern":" "}},{"Str":{"pattern":" "}},{"Alternative":{"atoms":[4,5]}},{"Str":{"pattern":"TR"}},{"Str":{"pattern":"TS"}},{"Alternative":{"atoms":[7,8]}},{"Named":{"name":"wes_type","atom":9}},{"Entity":{"atom":6}},{"Sequence":{"atoms":[10,11]}},{"Repetition":{"atom":12,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":18}},{"Entity":{"atom":16}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":15,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"number","atom":17}},{"Entity":{"atom":31}},{"Entity":{"atom":29}},{"Entity":{"atom":25}},{"Str":{"pattern":"-"}},{"Str":{"pattern":"ー"}},{"Alternative":{"atoms":[22,23]}},{"Named":{"name":"dash","atom":24}},{"Entity":{"atom":16}},{"Repetition":{"atom":26,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"part","atom":27}},{"Sequence":{"atoms":[21,28]}},{"Repetition":{"atom":20,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"parts","atom":30}},{"Str":{"pattern":"("}},{"Str":{"pattern":"("}},{"Alternative":{"atoms":[32,33]}},{"Str":{"pattern":"E"}},{"Str":{"pattern":"J"}},{"Alternative":{"atoms":[35,36]}},{"Named":{"name":"wes_language","atom":37}},{"Str":{"pattern":")"}},{"Str":{"pattern":")"}},{"Alternative":{"atoms":[39,40]}},{"Entity":{"atom":45}},{"Str":{"pattern":":"}},{"Str":{"pattern":":"}},{"Alternative":{"atoms":[43,44]}},{"Lookahead":{"atom":42,"positive":true}},{"Sequence":{"atoms":[34,38,41,46]}},{"Repetition":{"atom":47,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":61}},{"Entity":{"atom":45}},{"Entity":{"atom":16}},{"Repetition":{"atom":51,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"year","atom":52}},{"Entity":{"atom":59}},{"Str":{"pattern":"R"}},{"Str":{"pattern":"R"}},{"Repetition":{"atom":56,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[55,57]}},{"Named":{"name":"reaffirmed","atom":58}},{"Repetition":{"atom":54,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[50,53,60]}},{"Repetition":{"atom":49,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":74}},{"Str":{"pattern":"("}},{"Str":{"pattern":"("}},{"Alternative":{"atoms":[64,65]}},{"Str":{"pattern":"E"}},{"Str":{"pattern":"J"}},{"Alternative":{"atoms":[67,68]}},{"Named":{"name":"language","atom":69}},{"Str":{"pattern":")"}},{"Str":{"pattern":")"}},{"Alternative":{"atoms":[71,72]}},{"Sequence":{"atoms":[66,70,73]}},{"Repetition":{"atom":63,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":86}},{"Str":{"pattern":"("}},{"Str":{"pattern":"規格群"}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[77,78,79]}},{"Str":{"pattern":"("}},{"Str":{"pattern":"規格群"}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[81,82,83]}},{"Alternative":{"atoms":[80,84]}},{"Named":{"name":"all_parts","atom":85}},{"Repetition":{"atom":76,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":148}},{"Entity":{"atom":110}},{"Entity":{"atom":91}},{"Str":{"pattern":"/"}},{"Str":{"pattern":"AMENDMENT"}},{"Str":{"pattern":"AMD"}},{"Alternative":{"atoms":[92,93]}},{"Named":{"name":"amd_type","atom":94}},{"Entity":{"atom":6}},{"Entity":{"atom":16}},{"Repetition":{"atom":97,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"amd_number","atom":98}},{"Entity":{"atom":45}},{"Entity":{"atom":16}},{"Repetition":{"atom":101,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"amd_year","atom":102}},{"Str":{"pattern":"R"}},{"Str":{"pattern":"R"}},{"Repetition":{"atom":105,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[104,106]}},{"Named":{"name":"amd_reaffirmed","atom":107}},{"Repetition":{"atom":108,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[90,95,96,99,100,103,109]}},{"Named":{"name":"amendment","atom":89}},{"Entity":{"atom":124}},{"Entity":{"atom":91}},{"Str":{"pattern":"EXPLANATION"}},{"Str":{"pattern":"EXPL"}},{"Alternative":{"atoms":[114,115]}},{"Named":{"name":"expl_type","atom":116}},{"Entity":{"atom":6}},{"Entity":{"atom":16}},{"Repetition":{"atom":119,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"expl_number","atom":120}},{"Sequence":{"atoms":[118,121]}},{"Repetition":{"atom":122,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[113,117,123]}},{"Named":{"name":"explanation","atom":112}},{"Entity":{"atom":146}},{"Entity":{"atom":91}},{"Str":{"pattern":"CORRIGENDUM"}},{"Str":{"pattern":"CORR"}},{"Alternative":{"atoms":[128,129]}},{"Named":{"name":"corr_type","atom":130}},{"Entity":{"atom":6}},{"Entity":{"atom":16}},{"Repetition":{"atom":133,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"corr_number","atom":134}},{"Entity":{"atom":45}},{"Entity":{"atom":16}},{"Repetition":{"atom":137,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"corr_year","atom":138}},{"Str":{"pattern":"R"}},{"Str":{"pattern":"R"}},{"Repetition":{"atom":141,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[140,142]}},{"Named":{"name":"corr_reaffirmed","atom":143}},{"Repetition":{"atom":144,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[127,131,132,135,136,139,145]}},{"Named":{"name":"corrigendum","atom":126}},{"Alternative":{"atoms":[111,125,147]}},{"Repetition":{"atom":88,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":163}},{"Entity":{"atom":6}},{"Str":{"pattern":"SYMBOL"}},{"Named":{"name":"symbol_present","atom":152}},{"Entity":{"atom":6}},{"Entity":{"atom":158}},{"Re":{"pattern":"[\\x01-\\x09]"}},{"Re":{"pattern":"[\\x0b-\\u{10ffff}]"}},{"Alternative":{"atoms":[156,157]}},{"Repetition":{"atom":155,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"symbol_value","atom":159}},{"Sequence":{"atoms":[154,160]}},{"Repetition":{"atom":161,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[151,153,162]}},{"Repetition":{"atom":150,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[2,3,13,14,19,48,62,75,87,149,164]}},{"Entity":{"atom":203}},{"Str":{"pattern":"JIS"}},{"Entity":{"atom":6}},{"Entity":{"atom":91}},{"Alternative":{"atoms":[168,169]}},{"Repetition":{"atom":170,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[167,171]}},{"Repetition":{"atom":172,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":182}},{"Str":{"pattern":"TR"}},{"Str":{"pattern":"TS"}},{"Alternative":{"atoms":[175,176]}},{"Named":{"name":"type","atom":177}},{"Entity":{"atom":6}},{"Entity":{"atom":91}},{"Alternative":{"atoms":[179,180]}},{"Sequence":{"atoms":[178,181]}},{"Repetition":{"atom":174,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":6}},{"Repetition":{"atom":184,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":188}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Named":{"name":"series","atom":187}},{"Entity":{"atom":6}},{"Repetition":{"atom":189,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":18}},{"Entity":{"atom":31}},{"Entity":{"atom":61}},{"Repetition":{"atom":193,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":74}},{"Repetition":{"atom":195,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":86}},{"Repetition":{"atom":197,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":148}},{"Repetition":{"atom":199,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":163}},{"Repetition":{"atom":201,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[173,183,185,186,190,191,192,194,196,198,200,202]}},{"Alternative":{"atoms":[0,166]}}],"root":204},"bindings":[{"capture":"wes","path":"wes","type":"string","card":null,"preprocess":null},{"capture":"wes_type","path":"wes_type","type":"string","card":null,"preprocess":null},{"capture":"wes_language","path":"wes_language","type":"string","card":null,"preprocess":null},{"capture":"type","path":"type","type":"string","card":null,"preprocess":null},{"capture":"series","path":"series","type":"string","card":null,"preprocess":null},{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"part","path":"parts[].part","type":"string","card":null,"preprocess":null},{"capture":"year","path":"year","type":"string","card":null,"preprocess":null},{"capture":"reaffirmed","path":"reaffirmed","type":"string","card":null,"preprocess":null},{"capture":"language","path":"language","type":"string","card":null,"preprocess":null},{"capture":"all_parts","path":"all_parts","type":"string","card":null,"preprocess":null},{"capture":"amd_type","path":"amd_type","type":"string","card":null,"preprocess":null},{"capture":"amd_number","path":"amd_number","type":"string","card":null,"preprocess":null},{"capture":"amd_year","path":"amd_year","type":"string","card":null,"preprocess":null},{"capture":"amd_reaffirmed","path":"amd_reaffirmed","type":"string","card":null,"preprocess":null},{"capture":"expl_type","path":"expl_type","type":"string","card":null,"preprocess":null},{"capture":"expl_number","path":"expl_number","type":"string","card":null,"preprocess":null},{"capture":"corr_type","path":"corr_type","type":"string","card":null,"preprocess":null},{"capture":"corr_number","path":"corr_number","type":"string","card":null,"preprocess":null},{"capture":"corr_year","path":"corr_year","type":"string","card":null,"preprocess":null},{"capture":"corr_reaffirmed","path":"corr_reaffirmed","type":"string","card":null,"preprocess":null},{"capture":"symbol_present","path":"symbol_present","type":"string","card":null,"preprocess":null},{"capture":"symbol_value","path":"symbol_value","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule supplement: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule supplement: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule supplement: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule symbol_value_char: branches 1 and 2 are order-dependent (first set not statically known)","rule identifier: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"JIS B 0001","expect":{}},{"entry":"identifier","kind":"accept","input":"JIS X 0208:1997","expect":{}},{"entry":"identifier","kind":"accept","input":"WES 7700-1:2019","expect":{}},{"entry":"identifier","kind":"accept","input":"WES 7301:2020R","expect":{}},{"entry":"identifier","kind":"reject","input":"JIS WES 7700","expect":{}},{"entry":"identifier","kind":"reject","input":"WES X 7700","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}},{"entry":"identifier","kind":"example","input":"WES 7700-1:2019","expect":{"wes":"WES","number":"7700","year":"2019"}},{"entry":"identifier","kind":"example","input":"WES 7301:2020R","expect":{"wes":"WES","number":"7301","year":"2020","reaffirmed":"R"}},{"entry":"identifier","kind":"example","input":"JIS X 0208:1997","expect":{"series":"X","number":"0208","year":"1997"}},{"entry":"identifier","kind":"example","input":"WES 7700:2019/AMD 1:2020R","expect":{"wes":"WES","number":"7700","amd_type":"AMD","amd_number":"1","amd_year":"2020","amd_reaffirmed":"R"}},{"entry":"identifier","kind":"example","input":"JIS X 0208:1997R","expect":{"series":"X","year":"1997","reaffirmed":"R"}},{"entry":"identifier","kind":"example","input":"JIS X 0208:1997RR","expect":{"series":"X","year":"1997","reaffirmed":"RR"}},{"entry":"identifier","kind":"example","input":"WES 1112(E):2022","expect":{"wes":"WES","number":"1112","wes_language":"E","year":"2022"}},{"entry":"identifier","kind":"example","input":"WES TR 2032:2024","expect":{"wes":"WES","wes_type":"TR","number":"2032","year":"2024"}},{"entry":"identifier","kind":"example","input":"WES TS 2816(E):2015","expect":{"wes":"WES","wes_type":"TS","number":"2816","wes_language":"E","year":"2015"}},{"entry":"identifier","kind":"example","input":"WES TS 2816:2015","expect":{"wes":"WES","wes_type":"TS","number":"2816","year":"2015"}},{"entry":"identifier","kind":"example","input":"WES 3003(E):1995/AMENDMENT 1:2025","expect":{"wes_language":"E","year":"1995","amd_type":"AMENDMENT","amd_number":"1","amd_year":"2025"}},{"entry":"identifier","kind":"example","input":"WES 1112:2022(E)","expect":{"language":"E","year":"2022"}},{"entry":"identifier","kind":"example","input":"WES 1112(E)","expect":{"wes":"WES","number":"1112","language":"E"}},{"entry":"identifier","kind":"example","input":"JIS A 0001:1999/AMD 1:2000","expect":{"series":"A","number":"0001","year":"1999","amd_type":"AMD","amd_number":"1","amd_year":"2000"}},{"entry":"identifier","kind":"example","input":"JIS B 3700-11:1996/CORRIGENDUM 1:2002","expect":{"series":"B","number":"3700","year":"1996","corr_type":"CORRIGENDUM","corr_number":"1","corr_year":"2002"}},{"entry":"identifier","kind":"reject","input":"JIS X 0208(E):1997","expect":{}},{"entry":"identifier","kind":"reject","input":"TR WES 2032","expect":{}},{"entry":"identifier","kind":"reject","input":"JIS TR WES 2032","expect":{}},{"entry":"identifier","kind":"reject","input":"WES TX 2032:2024","expect":{}},{"entry":"identifier","kind":"reject","input":"WES TR X 2032","expect":{}},{"entry":"identifier","kind":"example","input":"WES TR 2032:2024","expect":{"wes":"WES","wes_type":"TR","number":"2032","year":"2024"}},{"entry":"identifier","kind":"example","input":"WES 1112(E):2022","expect":{"wes":"WES","number":"1112","wes_language":"E","year":"2022"}}],"docs":{},"source":"# Port of lib/pubid/jis/parser.rb — JIS identifiers with the Japanese\n# full-width separator tolerance (ー   : ()), TR/TS prefixes, parts,\n# reaffirmed years, (E)/(J) language codes, 規格群 all-parts, AMD/EXPL/\n# CORR supplements, and the SYMBOL clause. WES (Japan Welding Engineering\n# Society) ids share the JIS tail but carry no series letter or JIS prefix.\n# A WES id can carry a TR/TS type after the root (`WES TR 2032:2024`) and\n# a language before the year (`WES 1112(E):2022`, bound as wes_language).\n\ngrammar PubidJis version \"0.1.0\" edition 2 {\n dash = (\"-\" / \"ー\") as dash\n space = (\" \" / \" \")\n colon = (\":\" / \":\")\n type_prefix = (\"TR\" / \"TS\") as type (space / slash)\n series = %x41-5A as series\n number = (1*digit) as number\n part = dash (1*digit) as part\n parts = (*part) as parts\n year = colon (1*digit) as year [reaffirmed_rule]\n language = (\"(\" / \"(\") (\"E\" / \"J\") as language (\")\" / \")\")\n all_parts = ((\"(\" \"規格群\" \")\") / (\"(\" \"規格群\" \")\")) as all_parts\n amendment = {\n slash (\"AMENDMENT\" / \"AMD\") as amd_type\n space\n (1*digit) as amd_number\n colon (1*digit) as amd_year\n [(\"R\" (*\"R\")) as amd_reaffirmed]\n }\n explanation = {\n slash (\"EXPLANATION\" / \"EXPL\") as expl_type\n ([space (1*digit) as expl_number])\n }\n corrigendum = {\n slash (\"CORRIGENDUM\" / \"CORR\") as corr_type\n space\n (1*digit) as corr_number\n colon (1*digit) as corr_year\n [(\"R\" (*\"R\")) as corr_reaffirmed]\n }\n supplement = {\n (amendment as amendment)\n / (explanation as explanation)\n / (corrigendum as corrigendum)\n }\n symbol_value_char = %x01-09 / %x0B-10FFFF\n symbol_clause = {\n space \"SYMBOL\" as symbol_present\n ([space (1*symbol_value_char) as symbol_value])\n }\n wes_identifier = {\n \"WES\" as wes\n space\n [(\"TR\" / \"TS\") as wes_type space]\n number\n parts\n [(\"(\" / \"(\") (\"E\" / \"J\") as wes_language (\")\" / \")\") &colon]\n [year]\n [language]\n [all_parts]\n [supplement]\n [symbol_clause]\n }\n jis_identifier = {\n [\"JIS\" [(space / slash)]]\n [type_prefix]\n [space]\n series\n [space]\n number\n parts\n [year]\n [language]\n [all_parts]\n [supplement]\n [symbol_clause]\n }\n identifier = wes_identifier / jis_identifier\n upper = %x41-5A\n digit = %x30-39\n slash = \"/\"\n reaffirmed_rule = (\"R\" (*\"R\")) as reaffirmed\n entry identifier: identifier\n\n # Typed model bindings: bound captures land on the consumer\n # builder's fields of the same name; repeated parts collect under\n # parts[].part.\n bindings identifier {\n wes -> wes (string)\n wes_type -> wes_type (string)\n wes_language -> wes_language (string)\n type -> type (string)\n series -> series (string)\n number -> number (string)\n part -> parts[].part (string)\n year -> year (string)\n reaffirmed -> reaffirmed (string)\n language -> language (string)\n all_parts -> all_parts (string)\n amd_type -> amd_type (string)\n amd_number -> amd_number (string)\n amd_year -> amd_year (string)\n amd_reaffirmed -> amd_reaffirmed (string)\n expl_type -> expl_type (string)\n expl_number -> expl_number (string)\n corr_type -> corr_type (string)\n corr_number -> corr_number (string)\n corr_year -> corr_year (string)\n corr_reaffirmed -> corr_reaffirmed (string)\n symbol_present -> symbol_present (string)\n symbol_value -> symbol_value (string)\n }\n\n test {\n accept \"JIS B 0001\"\n accept \"JIS X 0208:1997\"\n accept \"WES 7700-1:2019\"\n accept \"WES 7301:2020R\"\n reject \"JIS WES 7700\"\n reject \"WES X 7700\"\n reject \"nonsense\"\n example \"WES 7700-1:2019\" { wes: \"WES\", number: \"7700\", year: \"2019\" }\n example \"WES 7301:2020R\" { wes: \"WES\", number: \"7301\", year: \"2020\", reaffirmed: \"R\" }\n example \"JIS X 0208:1997\" { series: \"X\", number: \"0208\", year: \"1997\" }\n example \"WES 7700:2019/AMD 1:2020R\" { wes: \"WES\", number: \"7700\", amd_type: \"AMD\", amd_number: \"1\", amd_year: \"2020\", amd_reaffirmed: \"R\" }\n example \"JIS X 0208:1997R\" { series: \"X\", year: \"1997\", reaffirmed: \"R\" }\n example \"JIS X 0208:1997RR\" { series: \"X\", year: \"1997\", reaffirmed: \"RR\" }\n example \"WES 1112(E):2022\" { wes: \"WES\", number: \"1112\", wes_language: \"E\", year: \"2022\" }\n example \"WES TR 2032:2024\" { wes: \"WES\", wes_type: \"TR\", number: \"2032\", year: \"2024\" }\n example \"WES TS 2816(E):2015\" { wes: \"WES\", wes_type: \"TS\", number: \"2816\", wes_language: \"E\", year: \"2015\" }\n example \"WES TS 2816:2015\" { wes: \"WES\", wes_type: \"TS\", number: \"2816\", year: \"2015\" }\n example \"WES 3003(E):1995/AMENDMENT 1:2025\" { wes_language: \"E\", year: \"1995\", amd_type: \"AMENDMENT\", amd_number: \"1\", amd_year: \"2025\" }\n example \"WES 1112:2022(E)\" { language: \"E\", year: \"2022\" }\n example \"WES 1112(E)\" { wes: \"WES\", number: \"1112\", language: \"E\" }\n example \"JIS A 0001:1999/AMD 1:2000\" { series: \"A\", number: \"0001\", year: \"1999\", amd_type: \"AMD\", amd_number: \"1\", amd_year: \"2000\" }\n example \"JIS B 3700-11:1996/CORRIGENDUM 1:2002\" { series: \"B\", number: \"3700\", year: \"1996\", corr_type: \"CORRIGENDUM\", corr_number: \"1\", corr_year: \"2002\" }\n reject \"JIS X 0208(E):1997\"\n reject \"TR WES 2032\"\n reject \"JIS TR WES 2032\"\n reject \"WES TX 2032:2024\"\n reject \"WES TR X 2032\"\n example \"WES TR 2032:2024\" { wes: \"WES\", wes_type: \"TR\", number: \"2032\", year: \"2024\" }\n example \"WES 1112(E):2022\" { wes: \"WES\", number: \"1112\", wes_language: \"E\", year: \"2022\" }\n }\n}\n","source_hash":"890e9d76109dd798047c6a1515901e6819cd80f1fa3eb1308e80afcfd6b272c9","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.126","edition":2,"semantics_hash":"sha256:7c4b170d64be52fae49eecbfab26897487ef87a66a25bf11d935d6a4d1e54f87","checksum":"sha256:34056972f729be65f8874efeaf5e72cc5f9d2ce2466a1447e4fcd6adaa6ccbae"}