@pubid/pubid 0.1.38 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
@@ -0,0 +1,35 @@
1
+ import type { FlavorImplementation } from "../../conformance/implementation.js";
2
+ import { BaseIdentifier } from "../../model/identifier.js";
3
+ /**
4
+ * The PFS-compiled doi flavor (pubid/pubid#706 §2.2) — one definition
5
+ * file, both engines: the grammar runtime is the SAME baked artifact the
6
+ * Ruby flavor runs (pubid-grammar artifacts/doi_pfs.json, compiled from
7
+ * definitions/doi.pfs), loaded through the artifact runtime, so grammar
8
+ * changes land once and this side cannot drift. Nothing is hand-rolled:
9
+ * the model is declared attributes (serialization derives from
10
+ * BaseIdentifier), the builder is the shared one, and the render body is
11
+ * the definition's canonical template. The parity gate
12
+ * (test/doi-pfs-parity.test.ts) pins byte-identity with the coded
13
+ * {@linkcode import("../doi").DoiIdentifier} over the shared conformance
14
+ * set.
15
+ */
16
+ export declare class DoiPfsIdentifier extends BaseIdentifier {
17
+ /**
18
+ * The SCHEME-canonical wire tag (declared in the definition): to_hash
19
+ * is byte-identical with the coded flavor. Deliberately NOT registered
20
+ * in the global type map — one scheme, one `_type`, one global
21
+ * resolution (the coded class); {@linkcode fromHash} routes the tag
22
+ * back here flavor-locally, mirroring Ruby's DOI_TYPE_MAP.
23
+ */
24
+ static polymorphicName: string;
25
+ static attributes: import("../../model/attribute.js").AttributeTable;
26
+ static mappings: import("../../model/attribute.js").FieldMapping[];
27
+ render(): string;
28
+ readonly prefix: string;
29
+ readonly suffix: string;
30
+ /** The alias's own from_hash: the scheme tag routes HERE, while the
31
+ * global resolution keeps answering the coded class (coexistence, not
32
+ * cutover). */
33
+ static fromHash(hash: Record<string, unknown>): BaseIdentifier;
34
+ }
35
+ export declare function doiPfsGrammarImplementation(): FlavorImplementation;
@@ -0,0 +1,61 @@
1
+ import { ParseFailed } from "../../grammar/engine.js";
2
+ import { PubidRuntime } from "../../parg/runtime.js";
3
+ import { BaseIdentifier } from "../../model/identifier.js";
4
+ import { extendAttributes, keyValue } from "../../model/attribute.js";
5
+ /**
6
+ * The PFS-compiled doi flavor (pubid/pubid#706 §2.2) — one definition
7
+ * file, both engines: the grammar runtime is the SAME baked artifact the
8
+ * Ruby flavor runs (pubid-grammar artifacts/doi_pfs.json, compiled from
9
+ * definitions/doi.pfs), loaded through the artifact runtime, so grammar
10
+ * changes land once and this side cannot drift. Nothing is hand-rolled:
11
+ * the model is declared attributes (serialization derives from
12
+ * BaseIdentifier), the builder is the shared one, and the render body is
13
+ * the definition's canonical template. The parity gate
14
+ * (test/doi-pfs-parity.test.ts) pins byte-identity with the coded
15
+ * {@linkcode import("../doi").DoiIdentifier} over the shared conformance
16
+ * set.
17
+ */
18
+ export class DoiPfsIdentifier extends BaseIdentifier {
19
+ /**
20
+ * The SCHEME-canonical wire tag (declared in the definition): to_hash
21
+ * is byte-identical with the coded flavor. Deliberately NOT registered
22
+ * in the global type map — one scheme, one `_type`, one global
23
+ * resolution (the coded class); {@linkcode fromHash} routes the tag
24
+ * back here flavor-locally, mirroring Ruby's DOI_TYPE_MAP.
25
+ */
26
+ static polymorphicName = "pubid:doi:resource";
27
+ static attributes = extendAttributes(BaseIdentifier, {
28
+ prefix: { type: "string" },
29
+ suffix: { type: "string" },
30
+ });
31
+ static mappings = keyValue({ wire: "prefix", to: "prefix" }, { wire: "suffix", to: "suffix" });
32
+ render() {
33
+ return `doi:${this.prefix}/${this.suffix}`;
34
+ }
35
+ /** The alias's own from_hash: the scheme tag routes HERE, while the
36
+ * global resolution keeps answering the coded class (coexistence, not
37
+ * cutover). */
38
+ static fromHash(hash) {
39
+ return new DoiPfsIdentifier(this.applyMappings(this.inflateScalarComponents(hash)));
40
+ }
41
+ }
42
+ /**
43
+ * The artifact's declared bindings (prefix -> prefix, suffix -> suffix)
44
+ * carry the whole printed prefix face ("10.1000") — the doi.pfs pattern
45
+ * captures the "10." face the coded grammar re-assembles — so the bound
46
+ * map lands on the model fields directly, no builder cast.
47
+ */
48
+ let cachedRuntime;
49
+ export function doiPfsGrammarImplementation() {
50
+ return {
51
+ parse(input) {
52
+ cachedRuntime ??= PubidRuntime.load("doi_pfs");
53
+ const bound = cachedRuntime.parseAndBind(input);
54
+ if (bound === null || typeof bound !== "object") {
55
+ throw new ParseFailed("doi_pfs: the artifact rejected the input", 0);
56
+ }
57
+ const identifier = new DoiPfsIdentifier(bound);
58
+ return identifier;
59
+ },
60
+ };
61
+ }
@@ -2,6 +2,7 @@ import { lookupUrnParser, parseUrnFor } from "../urn/parsers.js";
2
2
  import { oimlGrammarImplementation } from "./oiml/implementation.js";
3
3
  import { xsfGrammarImplementation } from "./xsf/index.js";
4
4
  import { doiGrammarImplementation } from "./doi/index.js";
5
+ import { doiPfsGrammarImplementation } from "./doi_pfs/index.js";
5
6
  import { ianaGrammarImplementation } from "./iana/index.js";
6
7
  import { unGrammarImplementation } from "./un/index.js";
7
8
  import { plateauGrammarImplementation } from "./plateau/index.js";
@@ -81,6 +82,8 @@ function grammarImplementationSwitch(flavor) {
81
82
  return xsfGrammarImplementation();
82
83
  case "doi":
83
84
  return doiGrammarImplementation();
85
+ case "doi_pfs":
86
+ return doiPfsGrammarImplementation();
84
87
  case "iana":
85
88
  return ianaGrammarImplementation();
86
89
  case "un":
@@ -180,6 +180,18 @@ function locateIdentifierKlass(h) {
180
180
  return classForKind("dated-document");
181
181
  }
182
182
  let series = s(h["series"]);
183
+ // The default-series plain-number branch (pubid#711): the grammar's
184
+ // trailing alternatives accept a bare report number ("800-90B") and
185
+ // its publisher-led spelling ("NIST 800-90B") with NO series token.
186
+ // The SP series is the default: the docid-derived citation of
187
+ // NIST.SP.800-90B is exactly the bare docnumber "800-90B", and the
188
+ // Builder fills the same SP series the spelled form carries, so the
189
+ // two spellings are one identity to a lookup. No other branch can
190
+ // arrive here series-less (dated shapes returned above; every
191
+ // series-bearing branch captures :series).
192
+ if (series === undefined) {
193
+ return classForKind("special-publication");
194
+ }
183
195
  if (series?.startsWith("NBS ") === true || series?.startsWith("NBS.") === true || series?.startsWith("NIST.") === true) {
184
196
  const separator = series.startsWith("NBS ") ? "NBS " : series.startsWith("NBS.") ? "NBS." : "NIST.";
185
197
  let simple = series.slice(separator.length);
@@ -857,6 +869,21 @@ class NistBuilder {
857
869
  const klass = locateIdentifierKlass(h);
858
870
  const policy = seriesPolicy(s(h["series"]));
859
871
  const identifier = new klass();
872
+ // The default-series plain-number branch (pubid#711): the grammar's
873
+ // trailing alternatives carry no series token ("800-90B" /
874
+ // "NIST 800-90B"), so the identity gains the class's default series
875
+ // and parses == to the spelled "NIST SP 800-90B" form. The bare
876
+ // publisher rides the series-default-publisher fill at the tail
877
+ // (the #704 shape: filled after publisher_was_parsed is recorded,
878
+ // so the bare face keeps rendering bare). The policy stays the
879
+ // series-less Base one — Ruby's Series.for reads parsed_hash, not
880
+ // the filled attribute (SP's own policy is Base anyway).
881
+ if (h["series"] === undefined) {
882
+ const defaultSeries = klass.nistDefaultSeries;
883
+ if (defaultSeries !== undefined) {
884
+ identifier["series"] = new NistCode({ value: defaultSeries });
885
+ }
886
+ }
860
887
  const attrs = {};
861
888
  const toEdition = (v) => {
862
889
  if (isObj(v)) {
@@ -1123,16 +1150,19 @@ class NistBuilder {
1123
1150
  self["supplement"] = NistSupplement.fromRaw(supp.value, supp.hasRevision);
1124
1151
  }
1125
1152
  }
1126
- // Default publisher for series-less renders.
1153
+ // apply_series_default_publisher (the #704 rule): a series that
1154
+ // implies its publisher (bare "FIPS 197" is "NIST FIPS 197"; bare
1155
+ // "800-90B" is "NIST SP 800-90B" — pubid#712) gets it now, after the
1156
+ // publisher_was_parsed: false above, so the two spellings are ==
1157
+ // while the bare face keeps rendering bare (the printed-publisher
1158
+ // views read the flag, not the filled value). Runs only when the
1159
+ // publisher is absent AND unspelled — at this point that is exactly
1160
+ // `publisher === undefined` (the flag was recorded false above iff
1161
+ // no publisher was parsed).
1127
1162
  if (self["publisher"] === undefined) {
1128
1163
  const defaultPublisher = identifier.constructor.nistDefaultPublisher;
1129
1164
  if (defaultPublisher !== undefined && defaultPublisher !== "") {
1130
1165
  self["publisher"] = defaultPublisher;
1131
- if (self["publisher_was_parsed"] === false) {
1132
- // Circular/RPT/MP render "NBS <series>" through the default even
1133
- // when not parsed — keep the flag false (Ruby publisher_was_parsed
1134
- // stays false but to_short_style uses default only via series).
1135
- }
1136
1166
  }
1137
1167
  }
1138
1168
  return identifier;
@@ -680,6 +680,11 @@ function nistClass(spec) {
680
680
  class NistConcrete extends (spec.supplementSeam === true ? NistSupplementIdentifier : NistIdentifier) {
681
681
  static polymorphicName = `pubid:nist:${spec.kind}`;
682
682
  static urnGenerator = NistUrnGenerator;
683
+ /** Ruby `self.default_series` (pubid#711) — read by the Builder. */
684
+ static nistDefaultSeries = spec.defaultSeries;
685
+ /** Ruby `self.series_default_publisher` (#704/#712) — read by the
686
+ * Builder's series-default-publisher fill. */
687
+ static nistDefaultPublisher = spec.seriesDefaultPublisher;
683
688
  seriesCode() {
684
689
  return spec.seriesCode;
685
690
  }
@@ -819,7 +824,17 @@ export class NistSupplementIdentifier extends NistIdentifier {
819
824
  }
820
825
  }
821
826
  const TYPE_SPECS = [
822
- { kind: "special-publication", seriesCode: "SP" },
827
+ {
828
+ kind: "special-publication", seriesCode: "SP",
829
+ // The default-series plain-number branch (pubid#711): a citation
830
+ // shaped like the docnumber ("800-90B", publisher-led
831
+ // "NIST 800-90B") names an SP document — the docid-derived
832
+ // citation of NIST.SP.800-90B is exactly "800-90B" — and a bare
833
+ // plain-number parse carries the series' NIST publisher (the #704
834
+ // shape), so it is == to the spelled "NIST SP 800-90B" form a
835
+ // lookup compares against.
836
+ defaultSeries: "SP", seriesDefaultPublisher: "NIST",
837
+ },
823
838
  { kind: "federal-information-processing-standards", seriesCode: "FIPS", defaultPublisher: "", style: "fips" },
824
839
  { kind: "interagency-report", seriesCode: "IR", forceShort: true },
825
840
  { kind: "handbook", seriesCode: "HB" },
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The pubid-scheme URN schema of DOI_PFS — the transcription
3
+ * of lib/pubid/<flavor>/pubid_urn_schema.rb (pubid#656, completed by
4
+ * #670; design in docs/URN-SCHEMES.adoc). GENERATED by
5
+ * scripts/port-urn-schemas.rb from the ruby declaration: edit the ruby
6
+ * schema and regenerate, never this table by hand.
7
+ */
8
+ export declare const doiPfsSchema: import("../schema.js").FlavorSchema;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The pubid-scheme URN schema of DOI_PFS — the transcription
3
+ * of lib/pubid/<flavor>/pubid_urn_schema.rb (pubid#656, completed by
4
+ * #670; design in docs/URN-SCHEMES.adoc). GENERATED by
5
+ * scripts/port-urn-schemas.rb from the ruby declaration: edit the ruby
6
+ * schema and regenerate, never this table by hand.
7
+ */
8
+ import { define } from "../index.js";
9
+ export const doiPfsSchema = define("doi_pfs", (s) => {
10
+ s.atom("prefix", "prefix");
11
+ s.atom("suffix", "suffix");
12
+ s.type("pubid:doi_pfs:identifier", "id", { core: [] });
13
+ s.type("pubid:doi_pfs:resource", "resource", { core: [] });
14
+ });
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The registry of all 44 flavor pubid-urn schema tables — the TS mirror
2
+ * The registry of all 45 flavor pubid-urn schema tables — the TS mirror
3
3
  * of lib/pubid.rb's autoloaded Pubid::<Flavor>::PubidUrnSchema chain.
4
4
  * Declared tables only: importing this module has no side effects; the
5
5
  * pubid-urn module builds its flavor lookup lazily from this array.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The registry of all 44 flavor pubid-urn schema tables — the TS mirror
2
+ * The registry of all 45 flavor pubid-urn schema tables — the TS mirror
3
3
  * of lib/pubid.rb's autoloaded Pubid::<Flavor>::PubidUrnSchema chain.
4
4
  * Declared tables only: importing this module has no side effects; the
5
5
  * pubid-urn module builds its flavor lookup lazily from this array.
@@ -19,6 +19,7 @@ import { cencenelecSchema } from "./cencenelec.js";
19
19
  import { cieSchema } from "./cie.js";
20
20
  import { csaSchema } from "./csa.js";
21
21
  import { doiSchema } from "./doi.js";
22
+ import { doiPfsSchema } from "./doi_pfs.js";
22
23
  import { eascSchema } from "./easc.js";
23
24
  import { ecmaSchema } from "./ecma.js";
24
25
  import { etsiSchema } from "./etsi.js";
@@ -65,6 +66,7 @@ export const PUBID_URN_SCHEMAS = [
65
66
  cieSchema,
66
67
  csaSchema,
67
68
  doiSchema,
69
+ doiPfsSchema,
68
70
  eascSchema,
69
71
  ecmaSchema,
70
72
  etsiSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pubid/pubid",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
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",
@@ -0,0 +1 @@
1
+ {"bindings":null,"version":"0.1.0","grammar":"PubidCalconnectPfs","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Str":{"pattern":"CC"}},{"Str":{"pattern":"/"}},{"Entity":{"atom":8}},{"Entity":{"atom":6}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Alternative":{"atoms":[4,5]}},{"Repetition":{"atom":3,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"series","atom":7}},{"Sequence":{"atoms":[1,2]}},{"Repetition":{"atom":9,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":" "}},{"Entity":{"atom":24}},{"Entity":{"atom":14}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":13,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Str":{"pattern":"-"}},{"Alternative":{"atoms":[16,17]}},{"Entity":{"atom":14}},{"Repetition":{"atom":19,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[18,20]}},{"Repetition":{"atom":21,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[15,22]}},{"Named":{"name":"number","atom":23}},{"Str":{"pattern":":"}},{"Entity":{"atom":29}},{"Entity":{"atom":14}},{"Repetition":{"atom":27,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"year","atom":28}},{"Str":{"pattern":"-"}},{"Entity":{"atom":34}},{"Entity":{"atom":14}},{"Repetition":{"atom":32,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"month","atom":33}},{"Str":{"pattern":"-"}},{"Entity":{"atom":39}},{"Entity":{"atom":14}},{"Repetition":{"atom":37,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"day","atom":38}},{"Sequence":{"atoms":[30,31,35,36]}},{"Repetition":{"atom":40,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[25,26,41]}},{"Repetition":{"atom":42,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[0,10,11,12,43]}}],"root":44},"bindings":[{"capture":"series","path":"series","type":"string","card":null,"preprocess":null},{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"year","path":"year","type":"string","card":null,"preprocess":null},{"capture":"month","path":"month","type":"string","card":null,"preprocess":null},{"capture":"day","path":"day","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":[]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"CC 18011","expect":{}},{"entry":"identifier","kind":"accept","input":"CC 18011:2018","expect":{}},{"entry":"identifier","kind":"accept","input":"CC 11001:2024","expect":{}},{"entry":"identifier","kind":"accept","input":"CC/DIR 10006:2019","expect":{}},{"entry":"identifier","kind":"accept","input":"CC/Adv 0707.1:2007","expect":{}},{"entry":"identifier","kind":"accept","input":"CC/A 0812-1:2008","expect":{}},{"entry":"identifier","kind":"accept","input":"CC/WD 51017:2024-07-23","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}},{"entry":"identifier","kind":"reject","input":"CC","expect":{}},{"entry":"identifier","kind":"reject","input":"CC/WD","expect":{}},{"entry":"identifier","kind":"reject","input":"CC 18011:","expect":{}},{"entry":"identifier","kind":"reject","input":"CC 18011:2024-","expect":{}},{"entry":"identifier","kind":"example","input":"CC 18011","expect":{"number":"18011"}},{"entry":"identifier","kind":"example","input":"CC 18011:2018","expect":{"number":"18011","year":"2018"}},{"entry":"identifier","kind":"example","input":"CC 11001:2024","expect":{"number":"11001","year":"2024"}},{"entry":"identifier","kind":"example","input":"CC/DIR 10006:2019","expect":{"series":"DIR","number":"10006","year":"2019"}},{"entry":"identifier","kind":"example","input":"CC/Adv 0707.1:2007","expect":{"series":"Adv","number":"0707.1","year":"2007"}},{"entry":"identifier","kind":"example","input":"CC/A 0812-1:2008","expect":{"series":"A","number":"0812-1","year":"2008"}},{"entry":"identifier","kind":"example","input":"CC/WD 51017:2024-07-23","expect":{"series":"WD","number":"51017","year":"2024","month":"07","day":"23"}}],"docs":{},"source":"# PFS-generated grammar — DO NOT EDIT BY HAND.\n# Source: definitions/calconnect.pfs (pfs:1) sha256:2e864f0185a1a2f18a392f2f401ad3c9d65d1f32a9ec3f3f04f7dfca9b2226c4\n# Compiler: pubid/grammar/pfs 0.1.0\n# Flavor key: calconnect_pfs (grammar PubidCalconnectPfs)\n# Regenerate: ruby scripts/pfs compile definitions/calconnect.pfs --as calconnect_pfs\n\ngrammar PubidCalconnectPfs version \"0.1.0\" edition 2 {\n\n # Grammar fragments: named lexical terms declared under `grammar:`.\n digit = %x30-39\n letter = %x41-5A / %x61-7A\n space = \" \"\n slash = \"/\"\n colon = \":\"\n dash = \"-\"\n\n # Fields: one capture rule per declared field; the whole pattern\n # is the capture.\n calconnect_series = (1*letter) as series\n calconnect_number = (1*digit [(\".\" / \"-\") 1*digit]) as number\n calconnect_year = (1*digit) as year\n calconnect_month = (1*digit) as month\n calconnect_day = (1*digit) as day\n\n # Identifier rule: the canonical template first, then the\n # declared alternates (parse-in spellings; they normalize to the\n # canonical render face).\n identifier = \"CC\" [\"/\" calconnect_series] \" \" calconnect_number [\":\" calconnect_year [\"-\" calconnect_month \"-\" calconnect_day]]\n entry identifier: identifier\n\n # Typed model bindings: every bound capture lands on the\n # consumer model's field of the same name.\n bindings identifier {\n series -> series (string)\n number -> number (string)\n year -> year (string)\n month -> month (string)\n day -> day (string)\n }\n\n # REQUIRED examples from the definition — accepts, rejects, and\n # the capture expectations the compiler replayed at compile time.\n test {\n accept \"CC 18011\"\n accept \"CC 18011:2018\"\n accept \"CC 11001:2024\"\n accept \"CC/DIR 10006:2019\"\n accept \"CC/Adv 0707.1:2007\"\n accept \"CC/A 0812-1:2008\"\n accept \"CC/WD 51017:2024-07-23\"\n reject \"nonsense\"\n reject \"CC\"\n reject \"CC/WD\"\n reject \"CC 18011:\"\n reject \"CC 18011:2024-\"\n example \"CC 18011\" { number: \"18011\" }\n example \"CC 18011:2018\" { number: \"18011\" year: \"2018\" }\n example \"CC 11001:2024\" { number: \"11001\" year: \"2024\" }\n example \"CC/DIR 10006:2019\" { series: \"DIR\" number: \"10006\" year: \"2019\" }\n example \"CC/Adv 0707.1:2007\" { series: \"Adv\" number: \"0707.1\" year: \"2007\" }\n example \"CC/A 0812-1:2008\" { series: \"A\" number: \"0812-1\" year: \"2008\" }\n example \"CC/WD 51017:2024-07-23\" { series: \"WD\" number: \"51017\" year: \"2024\" month: \"07\" day: \"23\" }\n }\n}\n","source_hash":"35a33d659c86202186a5e6beb8c7df58af7bd6667dd55b6cb3cecb8855713b85","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.126","edition":2,"semantics_hash":"sha256:9fbceffd1828c207a9ec854699d0801fad9e52521a972e39dc1838fb80440dc8","checksum":"sha256:6557fd4360e2deab7ca4062b72e5cd6e60b6e3cd0252cb5b1e30d66f3905f467"}
@@ -0,0 +1 @@
1
+ {"bindings":null,"version":"0.1.0","grammar":"PubidCenPfs","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Str":{"pattern":"EN"}},{"Str":{"pattern":" "}},{"Entity":{"atom":8}},{"Re":{"pattern":"[\\x31-\\x39]"}},{"Entity":{"atom":5}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":4,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[3,6]}},{"Named":{"name":"number","atom":7}},{"Str":{"pattern":"-"}},{"Entity":{"atom":13}},{"Entity":{"atom":5}},{"Repetition":{"atom":11,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"part","atom":12}},{"Sequence":{"atoms":[9,10]}},{"Repetition":{"atom":14,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":":"}},{"Entity":{"atom":20}},{"Entity":{"atom":5}},{"Repetition":{"atom":18,"min":4,"max":null,"tag":"Repetition"}},{"Named":{"name":"year","atom":19}},{"Sequence":{"atoms":[16,17]}},{"Repetition":{"atom":21,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[0,1,2,15,22]}}],"root":23},"bindings":[{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"part","path":"part","type":"string","card":null,"preprocess":null},{"capture":"year","path":"year","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":[]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"EN 1325","expect":{}},{"entry":"identifier","kind":"accept","input":"EN 12464-1:2011","expect":{}},{"entry":"identifier","kind":"accept","input":"EN 1177:2018","expect":{}},{"entry":"identifier","kind":"reject","input":"EN","expect":{}},{"entry":"identifier","kind":"reject","input":"EN ISO 9001:2015","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}},{"entry":"identifier","kind":"reject","input":"CEN/TS 12345","expect":{}},{"entry":"identifier","kind":"example","input":"EN 1325","expect":{"number":"1325"}},{"entry":"identifier","kind":"example","input":"EN 12464-1:2011","expect":{"number":"12464","part":"1","year":"2011"}},{"entry":"identifier","kind":"example","input":"EN 1177:2018","expect":{"number":"1177","year":"2018"}}],"docs":{},"source":"# PFS-generated grammar — DO NOT EDIT BY HAND.\n# Source: definitions/cen.pfs (pfs:1) sha256:bc1edd5874f8938b5f946416a2b052d9d9b9df3d99f6c20a3d88fcab9ae1d70c\n# Compiler: pubid/grammar/pfs 0.1.0\n# Flavor key: cen_pfs (grammar PubidCenPfs)\n# Regenerate: ruby scripts/pfs compile definitions/cen.pfs --as cen_pfs\n# Module: iso.inner v1 (sha256:6f04cf475f1c...)\n# Module: en.overlay v1 (sha256:851af8c5f31b...)\n# Module: national.prefix v1 (sha256:c7aa2f2442e8...)\n# Module: supplements.core v1 (sha256:9fa9656375c7...)\n\ngrammar PubidCenPfs version \"0.1.0\" edition 2 {\n\n # Grammar fragments: named lexical terms declared under `grammar:`.\n iso_space = \" \"\n iso_colon = \":\"\n iso_dash = \"-\"\n iso_year = 4*digit\n iso_part = 1*digit [\"-\" 1*digit]\n iso_number = 1*(\"0\" %x31-39) *digit [iso_dash iso_part]\n iso_document = \"ISO\" iso_space iso_number [iso_colon iso_year]\n en_space = \" \"\n en_draft = (\"pr\" / \"Fpr\")\n en_face = [en_draft] \"EN\"\n en_adoption = en_face en_space iso_document\n supplement_number = 1*digit\n supplement_year = 4*digit\n supplement_month = 2*digit\n digit = %x30-39\n space = \" \"\n slash = \"/\"\n colon = \":\"\n dash = \"-\"\n\n # Fields: one capture rule per declared field; the whole pattern\n # is the capture.\n cen_cenelec_number = ((%x31-39) *digit) as number\n cen_cenelec_part = (1*digit) as part\n cen_cenelec_year = (4*digit) as year\n\n # Identifier rule: the canonical template first, then the\n # declared alternates (parse-in spellings; they normalize to the\n # canonical render face).\n identifier = \"EN\" \" \" cen_cenelec_number [\"-\" cen_cenelec_part] [\":\" cen_cenelec_year]\n entry identifier: identifier\n\n # Typed model bindings: every bound capture lands on the\n # consumer model's field of the same name.\n bindings identifier {\n number -> number (string)\n part -> part (string)\n year -> year (string)\n }\n\n # REQUIRED examples from the definition — accepts, rejects, and\n # the capture expectations the compiler replayed at compile time.\n test {\n accept \"EN 1325\"\n accept \"EN 12464-1:2011\"\n accept \"EN 1177:2018\"\n reject \"EN\"\n reject \"EN ISO 9001:2015\"\n reject \"nonsense\"\n reject \"CEN/TS 12345\"\n example \"EN 1325\" { number: \"1325\" }\n example \"EN 12464-1:2011\" { number: \"12464\" part: \"1\" year: \"2011\" }\n example \"EN 1177:2018\" { number: \"1177\" year: \"2018\" }\n }\n}\n","source_hash":"4b84b6acda6f62fbbff60e0ff603d72e3ff00b71906914855633b6f0404869db","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.126","edition":2,"semantics_hash":"sha256:4363f170082d3242a35648e8b019037e9670c804d4ea951a51487697eb0c4cee","checksum":"sha256:e19eae585ed8ee560c88d97c8289d827384d21214db02634bc3eeff5a0296ac9"}
@@ -0,0 +1 @@
1
+ {"bindings":null,"version":"0.1.0","grammar":"PubidDoiPfs","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Str":{"pattern":"doi:"}},{"Entity":{"atom":8}},{"Str":{"pattern":"10"}},{"Str":{"pattern":"."}},{"Entity":{"atom":5}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":4,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[2,3,6]}},{"Named":{"name":"prefix","atom":7}},{"Str":{"pattern":"/"}},{"Entity":{"atom":23}},{"Entity":{"atom":21}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Re":{"pattern":"[.]"}},{"Re":{"pattern":"[_]"}},{"Re":{"pattern":"[\\-]"}},{"Re":{"pattern":"[/]"}},{"Re":{"pattern":"[(]"}},{"Re":{"pattern":"[)]"}},{"Alternative":{"atoms":[12,13,14,15,16,17,18,19,20]}},{"Repetition":{"atom":11,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"suffix","atom":22}},{"Sequence":{"atoms":[0,1,9,10]}},{"Entity":{"atom":33}},{"Str":{"pattern":"https://"}},{"Str":{"pattern":"http://"}},{"Alternative":{"atoms":[26,27]}},{"Repetition":{"atom":28,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"doi.org"}},{"Repetition":{"atom":30,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[29,31]}},{"Repetition":{"atom":32,"min":0,"max":1,"tag":"Maybe"}},{"Named":{"name":"url_prefix","atom":25}},{"Repetition":{"atom":34,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":41}},{"Str":{"pattern":"doi:"}},{"Str":{"pattern":"DOI:"}},{"Str":{"pattern":"Doi:"}},{"Alternative":{"atoms":[37,38,39]}},{"Repetition":{"atom":40,"min":0,"max":1,"tag":"Maybe"}},{"Named":{"name":"scheme_prefix","atom":36}},{"Repetition":{"atom":42,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":45}},{"Str":{"pattern":"/"}},{"Named":{"name":"slash","atom":44}},{"Repetition":{"atom":46,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":8}},{"Str":{"pattern":"/"}},{"Entity":{"atom":23}},{"Sequence":{"atoms":[35,43,47,48,49,50]}},{"Alternative":{"atoms":[24,51]}}],"root":52},"bindings":[{"capture":"prefix","path":"prefix","type":"string","card":null,"preprocess":null},{"capture":"suffix","path":"suffix","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["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":"doi:10.1000/182","expect":{}},{"entry":"identifier","kind":"accept","input":"10.1006/jmbi.1998.2354","expect":{}},{"entry":"identifier","kind":"accept","input":"DOI:10.6028/NIST.2022-04-15.001","expect":{}},{"entry":"identifier","kind":"accept","input":"https://doi.org/10.1000/182","expect":{}},{"entry":"identifier","kind":"reject","input":"doi:","expect":{}},{"entry":"identifier","kind":"reject","input":"doi:10","expect":{}},{"entry":"identifier","kind":"reject","input":"doi:10.X/abc","expect":{}},{"entry":"identifier","kind":"reject","input":"doi:10.1000/","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}},{"entry":"identifier","kind":"example","input":"doi:10.1000/182","expect":{"prefix":"10.1000","suffix":"182"}},{"entry":"identifier","kind":"example","input":"10.1006/jmbi.1998.2354","expect":{"prefix":"10.1006","suffix":"jmbi.1998.2354"}},{"entry":"identifier","kind":"example","input":"DOI:10.6028/NIST.2022-04-15.001","expect":{"prefix":"10.6028","suffix":"NIST.2022-04-15.001"}},{"entry":"identifier","kind":"example","input":"https://doi.org/10.1000/182","expect":{"prefix":"10.1000","suffix":"182"}}],"docs":{},"source":"# PFS-generated grammar — DO NOT EDIT BY HAND.\n# Source: definitions/doi.pfs (pfs:1) sha256:60abc3c403c61019d727ee44d9a72591c1549672451e6609a72d36189cae6340\n# Compiler: pubid/grammar/pfs 0.1.0\n# Flavor key: doi_pfs (grammar PubidDoiPfs)\n# Regenerate: ruby scripts/pfs compile definitions/doi.pfs --as doi_pfs\n\ngrammar PubidDoiPfs version \"0.1.0\" edition 2 {\n\n # Grammar fragments: named lexical terms declared under `grammar:`.\n digit = %x30-39\n suffix_char = %x30-39 / %x41-5A / %x61-7A / %x2E / %x5F / %x2D / %x2F / %x28 / %x29\n slash = \"/\"\n url_prefix = [[\"https://\" / \"http://\"] [\"doi.org\"]]\n scheme_prefix = [\"doi:\" / \"DOI:\" / \"Doi:\"]\n\n # Fields: one capture rule per declared field; the whole pattern\n # is the capture.\n doi_prefix = (\"10\" \".\" 2*digit) as prefix\n doi_suffix = (1*suffix_char) as suffix\n\n # Identifier rule: the canonical template first, then the\n # declared alternates (parse-in spellings; they normalize to the\n # canonical render face).\n identifier = {\n \"doi:\" doi_prefix \"/\" doi_suffix\n / [url_prefix] [scheme_prefix] [slash] doi_prefix \"/\" doi_suffix\n }\n entry identifier: identifier\n\n # Typed model bindings: every bound capture lands on the\n # consumer model's field of the same name.\n bindings identifier {\n prefix -> prefix (string)\n suffix -> suffix (string)\n }\n\n # REQUIRED examples from the definition — accepts, rejects, and\n # the capture expectations the compiler replayed at compile time.\n test {\n accept \"doi:10.1000/182\"\n accept \"10.1006/jmbi.1998.2354\"\n accept \"DOI:10.6028/NIST.2022-04-15.001\"\n accept \"https://doi.org/10.1000/182\"\n reject \"doi:\"\n reject \"doi:10\"\n reject \"doi:10.X/abc\"\n reject \"doi:10.1000/\"\n reject \"nonsense\"\n example \"doi:10.1000/182\" { prefix: \"10.1000\" suffix: \"182\" }\n example \"10.1006/jmbi.1998.2354\" { prefix: \"10.1006\" suffix: \"jmbi.1998.2354\" }\n example \"DOI:10.6028/NIST.2022-04-15.001\" { prefix: \"10.6028\" suffix: \"NIST.2022-04-15.001\" }\n example \"https://doi.org/10.1000/182\" { prefix: \"10.1000\" suffix: \"182\" }\n }\n}\n","source_hash":"c872de21dfa95b1add4eed4785182c6e661fc79a0a8c15c07eb6209621a20f2c","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.126","edition":2,"semantics_hash":"sha256:06d0039460d751282b122dd5834bd505046c2cb32605c051ac296f2c29f964b3","checksum":"sha256:25f3643ce0fe181ae16b91a87a43177d495079ed590c6eee3c3cf36825e1fc05"}
@@ -0,0 +1 @@
1
+ {"bindings":null,"version":"0.1.0","grammar":"PubidEtsiPfs","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Str":{"pattern":"ETSI "}},{"Entity":{"atom":18}},{"Str":{"pattern":"I-ETS"}},{"Str":{"pattern":"TCRTR"}},{"Str":{"pattern":"GTS"}},{"Str":{"pattern":"ETR"}},{"Str":{"pattern":"ETS"}},{"Str":{"pattern":"TBR"}},{"Str":{"pattern":"NET"}},{"Str":{"pattern":"EN"}},{"Str":{"pattern":"ES"}},{"Str":{"pattern":"EG"}},{"Str":{"pattern":"TS"}},{"Str":{"pattern":"GR"}},{"Str":{"pattern":"GS"}},{"Str":{"pattern":"SR"}},{"Str":{"pattern":"TR"}},{"Alternative":{"atoms":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]}},{"Named":{"name":"type","atom":17}},{"Str":{"pattern":" "}},{"Entity":{"atom":54}},{"Str":{"pattern":"GSM"}},{"Str":{"pattern":" "}},{"Entity":{"atom":24}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":23,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Entity":{"atom":24}},{"Repetition":{"atom":27,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[21,22,25,26,28]}},{"Entity":{"atom":24}},{"Repetition":{"atom":30,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Entity":{"atom":24}},{"Repetition":{"atom":33,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[31,32,34]}},{"Entity":{"atom":40}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Alternative":{"atoms":[37,38,39]}},{"Repetition":{"atom":36,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"-"}},{"Entity":{"atom":40}},{"Repetition":{"atom":43,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[42,44]}},{"Repetition":{"atom":45,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":" "}},{"Entity":{"atom":24}},{"Repetition":{"atom":48,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[41,46,47,49]}},{"Entity":{"atom":24}},{"Repetition":{"atom":51,"min":1,"max":null,"tag":"Repetition"}},{"Alternative":{"atoms":[29,35,50,52]}},{"Named":{"name":"number","atom":53}},{"Sequence":{"atoms":[0,1,19,20]}},{"Str":{"pattern":"ETSI "}},{"Entity":{"atom":18}},{"Str":{"pattern":" "}},{"Entity":{"atom":54}},{"Str":{"pattern":" "}},{"Entity":{"atom":64}},{"Entity":{"atom":24}},{"Repetition":{"atom":62,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"minor","atom":63}},{"Sequence":{"atoms":[60,61]}},{"Repetition":{"atom":65,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":73}},{"Str":{"pattern":"-"}},{"Entity":{"atom":40}},{"Repetition":{"atom":69,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[68,70]}},{"Repetition":{"atom":71,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"parts","atom":72}},{"Repetition":{"atom":67,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":83}},{"Str":{"pattern":"/A"}},{"Str":{"pattern":"/C"}},{"Alternative":{"atoms":[76,77]}},{"Entity":{"atom":24}},{"Repetition":{"atom":79,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[78,80]}},{"Repetition":{"atom":81,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"supplements","atom":82}},{"Repetition":{"atom":75,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":" V"}},{"Entity":{"atom":92}},{"Entity":{"atom":90}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Re":{"pattern":"[.]"}},{"Alternative":{"atoms":[88,89]}},{"Repetition":{"atom":87,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"version","atom":91}},{"Sequence":{"atoms":[85,86]}},{"Str":{"pattern":" ed."}},{"Entity":{"atom":98}},{"Entity":{"atom":24}},{"Repetition":{"atom":96,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"edition","atom":97}},{"Sequence":{"atoms":[94,95]}},{"Alternative":{"atoms":[93,99]}},{"Repetition":{"atom":100,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":" ("}},{"Entity":{"atom":106}},{"Entity":{"atom":24}},{"Repetition":{"atom":104,"min":4,"max":4,"tag":"Repetition"}},{"Named":{"name":"year","atom":105}},{"Str":{"pattern":"-"}},{"Entity":{"atom":111}},{"Entity":{"atom":24}},{"Repetition":{"atom":109,"min":2,"max":2,"tag":"Repetition"}},{"Named":{"name":"month","atom":110}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[102,103,107,108,112]}},{"Repetition":{"atom":113,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[56,57,58,59,66,74,84,101,114]}},{"Alternative":{"atoms":[55,115]}}],"root":116},"bindings":[{"capture":"type","path":"type","type":"string","card":null,"preprocess":null},{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"minor","path":"minor","type":"string","card":null,"preprocess":null},{"capture":"parts","path":"parts","type":"string","card":null,"preprocess":null},{"capture":"supplements","path":"supplements","type":"string","card":null,"preprocess":null},{"capture":"version","path":"version","type":"string","card":null,"preprocess":null},{"capture":"edition","path":"edition","type":"string","card":null,"preprocess":null},{"capture":"year","path":"year","type":"string","card":null,"preprocess":null},{"capture":"month","path":"month","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["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":"ETSI EG 200 053 V1.5.1 (2004-06)","expect":{}},{"entry":"identifier","kind":"accept","input":"ETSI EN 300 175","expect":{}},{"entry":"identifier","kind":"accept","input":"ETSI EN 300 175-2","expect":{}},{"entry":"identifier","kind":"accept","input":"ETSI EN 300 019-1-0 V2.1.2 (2003-09)","expect":{}},{"entry":"identifier","kind":"accept","input":"ETSI TS 103 702","expect":{}},{"entry":"identifier","kind":"accept","input":"ETSI EG 201 880 V1.2.1","expect":{}},{"entry":"identifier","kind":"accept","input":"ETSI GTS GSM 02.01 V5.5.0 (1999-08)","expect":{}},{"entry":"identifier","kind":"accept","input":"ETSI GR NFV-EVE 008 V3.1.1 (2017-12)","expect":{}},{"entry":"identifier","kind":"accept","input":"ETSI I-ETS 300 003 ed.1 (1991-08)","expect":{}},{"entry":"identifier","kind":"accept","input":"ETSI ETR 108/A1 ed.1 (1995-08)","expect":{}},{"entry":"identifier","kind":"accept","input":"ETSI ETS 300 092-1/A1/C1 ed.1 (1994-04)","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}},{"entry":"identifier","kind":"reject","input":"ETSI","expect":{}},{"entry":"identifier","kind":"reject","input":"ETSI EN","expect":{}},{"entry":"identifier","kind":"reject","input":"ETSI XX 100","expect":{}},{"entry":"identifier","kind":"reject","input":"ETSI EN 300 175 V","expect":{}},{"entry":"identifier","kind":"reject","input":"ETSI EN 300 175 V1.2.x","expect":{}},{"entry":"identifier","kind":"reject","input":"ETSI EN 300 175 (","expect":{}},{"entry":"identifier","kind":"reject","input":"ETSI EN 300 175 (2004-6)","expect":{}},{"entry":"identifier","kind":"example","input":"ETSI EG 200 053 V1.5.1 (2004-06)","expect":{"type":"EG","number":"200 053","version":"1.5.1","year":"2004","month":"06"}},{"entry":"identifier","kind":"example","input":"ETSI EN 300 175","expect":{"type":"EN","number":"300 175"}},{"entry":"identifier","kind":"example","input":"ETSI EN 300 175-2","expect":{"type":"EN","number":"300 175","parts":"-2"}},{"entry":"identifier","kind":"example","input":"ETSI EN 300 019-1-0 V2.1.2 (2003-09)","expect":{"type":"EN","number":"300 019","parts":"-1-0","version":"2.1.2","year":"2003","month":"09"}},{"entry":"identifier","kind":"example","input":"ETSI TS 103 702","expect":{"type":"TS","number":"103 702"}},{"entry":"identifier","kind":"example","input":"ETSI EG 201 880 V1.2.1","expect":{"type":"EG","number":"201 880","version":"1.2.1"}},{"entry":"identifier","kind":"example","input":"ETSI GTS GSM 02.01 V5.5.0 (1999-08)","expect":{"type":"GTS","number":"GSM 02.01","version":"5.5.0","year":"1999","month":"08"}},{"entry":"identifier","kind":"example","input":"ETSI GR NFV-EVE 008 V3.1.1 (2017-12)","expect":{"type":"GR","number":"NFV-EVE 008","version":"3.1.1","year":"2017","month":"12"}},{"entry":"identifier","kind":"example","input":"ETSI I-ETS 300 003 ed.1 (1991-08)","expect":{"type":"I-ETS","number":"300 003","edition":"1","year":"1991","month":"08"}},{"entry":"identifier","kind":"example","input":"ETSI ETR 108/A1 ed.1 (1995-08)","expect":{"type":"ETR","number":"108","supplements":"/A1","edition":"1","year":"1995","month":"08"}},{"entry":"identifier","kind":"example","input":"ETSI ETS 300 092-1/A1/C1 ed.1 (1994-04)","expect":{"type":"ETS","number":"300 092","parts":"-1","supplements":"/A1/C1","edition":"1","year":"1994","month":"04"}}],"docs":{},"source":"# PFS-generated grammar — DO NOT EDIT BY HAND.\n# Source: definitions/etsi.pfs (pfs:1) sha256:9343088bf087b55ea790057e6be0bbebe2272407ee81f035b52e5f9a4d5c7b76\n# Compiler: pubid/grammar/pfs 0.1.0\n# Flavor key: etsi_pfs (grammar PubidEtsiPfs)\n# Regenerate: ruby scripts/pfs compile definitions/etsi.pfs --as etsi_pfs\n\ngrammar PubidEtsiPfs version \"0.1.0\" edition 2 {\n\n # Grammar fragments: named lexical terms declared under `grammar:`.\n digit = %x30-39\n alnum_char = %x41-5A / %x61-7A / %x30-39\n version_char = %x30-39 / %x2E\n\n # Fields: one capture rule per declared field; the whole pattern\n # is the capture.\n etsi_type = (\"I-ETS\" / \"TCRTR\" / \"GTS\" / \"ETR\" / \"ETS\" / \"TBR\" / \"NET\" / \"EN\" / \"ES\" / \"EG\" / \"TS\" / \"GR\" / \"GS\" / \"SR\" / \"TR\") as type\n etsi_number = ((\"GSM\" \" \" 1*digit \".\" 1*digit) / (1*digit \".\" 1*digit) / (1*alnum_char [(\"-\" 1*alnum_char)] \" \" 1*digit) / (1*digit)) as number\n etsi_minor = (1*digit) as minor\n etsi_parts = (1*(\"-\" 1*alnum_char)) as parts\n etsi_supplements = (1*((\"/A\" / \"/C\") 1*digit)) as supplements\n etsi_version = (1*version_char) as version\n etsi_edition = (1*digit) as edition\n etsi_year = (4digit) as year\n etsi_month = (2digit) as month\n\n # Identifier rule: the canonical template first, then the\n # declared alternates (parse-in spellings; they normalize to the\n # canonical render face).\n identifier = {\n \"ETSI \" etsi_type \" \" etsi_number\n / \"ETSI \" etsi_type \" \" etsi_number [\" \" etsi_minor] [etsi_parts] [etsi_supplements] [( \" V\" etsi_version / \" ed.\" etsi_edition )] [\" (\" etsi_year \"-\" etsi_month \")\"]\n }\n entry identifier: identifier\n\n # Typed model bindings: every bound capture lands on the\n # consumer model's field of the same name.\n bindings identifier {\n type -> type (string)\n number -> number (string)\n minor -> minor (string)\n parts -> parts (string)\n supplements -> supplements (string)\n version -> version (string)\n edition -> edition (string)\n year -> year (string)\n month -> month (string)\n }\n\n # REQUIRED examples from the definition — accepts, rejects, and\n # the capture expectations the compiler replayed at compile time.\n test {\n accept \"ETSI EG 200 053 V1.5.1 (2004-06)\"\n accept \"ETSI EN 300 175\"\n accept \"ETSI EN 300 175-2\"\n accept \"ETSI EN 300 019-1-0 V2.1.2 (2003-09)\"\n accept \"ETSI TS 103 702\"\n accept \"ETSI EG 201 880 V1.2.1\"\n accept \"ETSI GTS GSM 02.01 V5.5.0 (1999-08)\"\n accept \"ETSI GR NFV-EVE 008 V3.1.1 (2017-12)\"\n accept \"ETSI I-ETS 300 003 ed.1 (1991-08)\"\n accept \"ETSI ETR 108/A1 ed.1 (1995-08)\"\n accept \"ETSI ETS 300 092-1/A1/C1 ed.1 (1994-04)\"\n reject \"nonsense\"\n reject \"ETSI\"\n reject \"ETSI EN\"\n reject \"ETSI XX 100\"\n reject \"ETSI EN 300 175 V\"\n reject \"ETSI EN 300 175 V1.2.x\"\n reject \"ETSI EN 300 175 (\"\n reject \"ETSI EN 300 175 (2004-6)\"\n example \"ETSI EG 200 053 V1.5.1 (2004-06)\" { type: \"EG\" number: \"200 053\" version: \"1.5.1\" year: \"2004\" month: \"06\" }\n example \"ETSI EN 300 175\" { type: \"EN\" number: \"300 175\" }\n example \"ETSI EN 300 175-2\" { type: \"EN\" number: \"300 175\" parts: \"-2\" }\n example \"ETSI EN 300 019-1-0 V2.1.2 (2003-09)\" { type: \"EN\" number: \"300 019\" parts: \"-1-0\" version: \"2.1.2\" year: \"2003\" month: \"09\" }\n example \"ETSI TS 103 702\" { type: \"TS\" number: \"103 702\" }\n example \"ETSI EG 201 880 V1.2.1\" { type: \"EG\" number: \"201 880\" version: \"1.2.1\" }\n example \"ETSI GTS GSM 02.01 V5.5.0 (1999-08)\" { type: \"GTS\" number: \"GSM 02.01\" version: \"5.5.0\" year: \"1999\" month: \"08\" }\n example \"ETSI GR NFV-EVE 008 V3.1.1 (2017-12)\" { type: \"GR\" number: \"NFV-EVE 008\" version: \"3.1.1\" year: \"2017\" month: \"12\" }\n example \"ETSI I-ETS 300 003 ed.1 (1991-08)\" { type: \"I-ETS\" number: \"300 003\" edition: \"1\" year: \"1991\" month: \"08\" }\n example \"ETSI ETR 108/A1 ed.1 (1995-08)\" { type: \"ETR\" number: \"108\" supplements: \"/A1\" edition: \"1\" year: \"1995\" month: \"08\" }\n example \"ETSI ETS 300 092-1/A1/C1 ed.1 (1994-04)\" { type: \"ETS\" number: \"300 092\" parts: \"-1\" supplements: \"/A1/C1\" edition: \"1\" year: \"1994\" month: \"04\" }\n }\n}\n","source_hash":"f5e47bb403d9bff4737d8a200f08c71f8029df12f96c56e9774b722908bfa853","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.126","edition":2,"semantics_hash":"sha256:204a90b72aa2ea5fc2249622bd61c052b4ba5d1ebf732a2c2b57669c8de24393","checksum":"sha256:d861e040f36868dcef43c6547c5791979d4832d1ec1ca6bc310bdad82ea0e930"}