pubid 2.0.25 → 2.0.26

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4c2df973dfad39ae259a1f12504977716b523133f7ba6e98f86dc1359274206
4
- data.tar.gz: c4c7e18a50914f61ca002b3ce53d00b4b7dcdfdcac33168b34e6109f8f6b2890
3
+ metadata.gz: ef65c4cbf5afc5635f55300fc57894462ecc211d2dd51d2c2605dafda75a84a3
4
+ data.tar.gz: 89c5dccd912343c12ea157220089f7b1e0cf708abefe45e63c30c3bf712177e8
5
5
  SHA512:
6
- metadata.gz: 1224c069ca1bc1caa4ca1968252a4686dff6a99ac99b167e65546a800f0b1067e88e807dbb87da80db18744bdde4a1c3aee630da35d4610cefa7d36d52a055fc
7
- data.tar.gz: d9091695721ae3b73953f550b5515b598c7328875fc1c679f549e339d0de2daa517ed0412bcf33fe3c04568cd1522a0635f9033ba79983981cacbc04dbedd1cf
6
+ metadata.gz: 135acc1bf9bd56785e0c861db78c0cad0b5026527ca6798ba1e5df7f9d2a19124211cc2b3583db2b332bcd7fc29300736b28c44b0dbbbdd482742607df2f2ddc
7
+ data.tar.gz: ef9dc843feed906ae25c564839c6d710c30e3e4a3d7fb1e96fc4376ca73551deaade688e3bf7ceb825fd93292962a4db722f7994b4036a73c61d99e91a4f5626
@@ -1 +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"}
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}},{"Str":{"pattern":" "}},{"Entity":{"atom":59}},{"Entity":{"atom":24}},{"Repetition":{"atom":57,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"minor","atom":58}},{"Sequence":{"atoms":[55,56]}},{"Repetition":{"atom":60,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":70}},{"Str":{"pattern":"-"}},{"Entity":{"atom":67}},{"Entity":{"atom":40}},{"Repetition":{"atom":65,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"part","atom":66}},{"Sequence":{"atoms":[63,64]}},{"Repetition":{"atom":68,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"parts","atom":69}},{"Repetition":{"atom":62,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":87}},{"Entity":{"atom":85}},{"Str":{"pattern":"/"}},{"Entity":{"atom":79}},{"Str":{"pattern":"A"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[76,77]}},{"Named":{"name":"supplement_kind","atom":78}},{"Entity":{"atom":83}},{"Entity":{"atom":24}},{"Repetition":{"atom":81,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"supplement_number","atom":82}},{"Sequence":{"atoms":[74,75,80]}},{"Named":{"name":"supplement","atom":84}},{"Repetition":{"atom":73,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"supplements","atom":86}},{"Str":{"pattern":" V"}},{"Entity":{"atom":95}},{"Entity":{"atom":93}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Re":{"pattern":"[.]"}},{"Alternative":{"atoms":[91,92]}},{"Repetition":{"atom":90,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"version","atom":94}},{"Sequence":{"atoms":[88,89]}},{"Str":{"pattern":" ed."}},{"Entity":{"atom":101}},{"Entity":{"atom":24}},{"Repetition":{"atom":99,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"edition","atom":100}},{"Sequence":{"atoms":[97,98]}},{"Alternative":{"atoms":[96,102]}},{"Repetition":{"atom":103,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":" ("}},{"Entity":{"atom":109}},{"Entity":{"atom":24}},{"Repetition":{"atom":107,"min":4,"max":4,"tag":"Repetition"}},{"Named":{"name":"year","atom":108}},{"Str":{"pattern":"-"}},{"Entity":{"atom":114}},{"Entity":{"atom":24}},{"Repetition":{"atom":112,"min":2,"max":2,"tag":"Repetition"}},{"Named":{"name":"month","atom":113}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[105,106,110,111,115]}},{"Repetition":{"atom":116,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[0,1,19,20,61,71,72,104,117]}},{"Str":{"pattern":"ETSI "}},{"Entity":{"atom":18}},{"Str":{"pattern":" "}},{"Entity":{"atom":54}},{"Str":{"pattern":" "}},{"Entity":{"atom":59}},{"Sequence":{"atoms":[123,124]}},{"Repetition":{"atom":125,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":70}},{"Repetition":{"atom":127,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":87}},{"Repetition":{"atom":129,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":" V"}},{"Entity":{"atom":95}},{"Sequence":{"atoms":[131,132]}},{"Str":{"pattern":" ed."}},{"Entity":{"atom":101}},{"Sequence":{"atoms":[134,135]}},{"Alternative":{"atoms":[133,136]}},{"Repetition":{"atom":137,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":" ("}},{"Entity":{"atom":109}},{"Str":{"pattern":"-"}},{"Entity":{"atom":114}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[139,140,141,142,143]}},{"Repetition":{"atom":144,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[119,120,121,122,126,128,130,138,145]}},{"Alternative":{"atoms":[118,146]}}],"root":147},"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":"part","path":"part","type":"string","card":null,"preprocess":null},{"capture":"part","path":"parts[].part","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},{"capture":"supplement","path":"supplements[].supplement","type":"raw","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","part":"2"}},{"entry":"identifier","kind":"example","input":"ETSI EN 300 019-1-0 V2.1.2 (2003-09)","expect":{"type":"EN","number":"300 019","part":"1","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","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","part":"1","edition":"1","year":"1994","month":"04"}}],"docs":{},"source":"# PFS-generated grammar — DO NOT EDIT BY HAND.\n# Source: definitions/etsi.pfs (pfs:1) sha256:490ba9435f44dbd2d59429ae6d9527909c39444f19fec5a33a8cad91e89e3581\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_part = (1*alnum_char) as part\n etsi_parts = (1*(\"-\" etsi_part)) as parts\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 # Supplement run (compiled from the supplements declaration):\n # each item binds its kind face and ordinal; the builder folds\n # the run into the declared wrapper classes.\n etsi_supplement_kind = (\"A\" / \"C\") as supplement_kind\n etsi_supplement_number = (1*digit) as supplement_number\n etsi_supplement = ( \"/\" etsi_supplement_kind etsi_supplement_number ) as supplement\n etsi_supplements = (1*etsi_supplement) as supplements\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 [\" \" etsi_minor] [etsi_parts] etsi_supplements [( \" V\" etsi_version / \" ed.\" etsi_edition )] [\" (\" etsi_year \"-\" etsi_month \")\"]\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 part -> part (string)\n part -> parts[].part (string)\n version -> version (string)\n edition -> edition (string)\n year -> year (string)\n month -> month (string)\n supplement -> supplements[].supplement (raw)\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\" part: \"2\" }\n example \"ETSI EN 300 019-1-0 V2.1.2 (2003-09)\" { type: \"EN\" number: \"300 019\" part: \"1\" 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\" edition: \"1\" year: \"1995\" month: \"08\" }\n example \"ETSI ETS 300 092-1/A1/C1 ed.1 (1994-04)\" { type: \"ETS\" number: \"300 092\" part: \"1\" edition: \"1\" year: \"1994\" month: \"04\" }\n }\n}\n","source_hash":"12923bc0c9d666d9c01feb4667bce45e7a3acb7b221bcdc59b4886ecbeda8f92","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.126","edition":2,"semantics_hash":"sha256:1078102d9883a5c7906a15119592a43447800ee5aaca2bbf060ac6394a089aa3","checksum":"sha256:bea8fc40624aa907dd59befc802bf84ee56591ed0130437c2d0fe6065ebaf3d6"}
@@ -7,8 +7,8 @@
7
7
 
8
8
  def build(data)
9
9
  Identifiers::Resource.new(
10
- prefix: data[:prefix],
11
- suffix: data[:suffix],
10
+ prefix: data[:prefix],
11
+ suffix: data[:suffix],
12
12
  )
13
13
  end
14
14
  end
@@ -39,57 +39,65 @@ Measured surface (see the spec header for the numbers and their meaning):
39
39
  (all 15 type tokens; the four number shapes GSM/dotted/complex/simple;
40
40
  minor; the parts run; the /A//C supplement runs; `V`-versions and
41
41
  `ed.N`; the `(YYYY-MM)` date).
42
- - **to_hash and to_pubid_urn byte parity: 18,945/24,718** — exactly the
43
- rows carrying none of the three constructs pfs:1 cannot declare. Each
44
- gap is pinned per construct in the spec, so compiler progress turns the
45
- pin red:
46
- 1. **collection bindings** — the coded model stores `["1","0"]` (the
47
- `part -> parts[].part` binding); the compiled capture is the printed
48
- dash run `"-1-0"`.
49
- 2. **the edition face** — the coded model folds `ed.N` into
50
- `Version(version: "N", is_edition: true)`; the compiled hash carries
51
- `edition: "N"`.
52
- 3. **supplement wrappers** — the coded model wraps `/A1` `/C1` as
53
- Amendment/Corrigendum classes with an Integer ordinal delegating to
54
- their base; the compiled row stays a flat etsi-standard carrying the
55
- printed run `"/A1"`.
56
- - **to_s** renders the canonical CORE face (`ETSI <type> <number>`) —
57
- pfs:1 render faces cannot render optionals, so every corpus row (all of
58
- which carry version/ed + date) renders shorter; bare ids agree
59
- byte-for-byte.
60
- - **to_urn** is a compiler gap (the coded generator is hand-written Ruby
61
- composing `urn:etsi:{type}:{code}:{version}:{date}`; the generated
62
- generator pins the namespace only). Note the CODED generator raises
63
- `ArgumentError` on months 08/09 (`format('%02d', "08")` — the zero-padded
64
- string reads as an invalid octal); pre-existing on main, pinned by the
65
- parity spec.
66
- - **to_mr_string** is a compiler gap (the coded slug reads the flavor's
67
- `mr_*` hooks — type/parts/edition/date segments; the base hooks find
68
- none of them on the compiled columns).
42
+ - **to_hash and to_pubid_urn byte parity: 24,718/24,718** (phase 3
43
+ closed all three constructs; the former per-construct pins are now
44
+ parity rows):
45
+ 1. **collection bindings** (3,772 rows) — `parts` is a `kind: list`
46
+ field binding its `part` item per repetition; the compiled model
47
+ stores the coded `["1","0"]` array, dropped when empty.
48
+ 2. **the edition face** (1,833 rows) — the `ed.N` capture folds onto
49
+ `version` + `is_edition: true` (the coded Version component shape).
50
+ 3. **supplement wrappers** (168 rows) — the `supplements:` declaration
51
+ folds the printed run into SYNTHESIZED Amendment/Corrigendum classes
52
+ over the base standard, outermost last ("/A1/C1" is Corrigendum over
53
+ Amendment over the standard).
54
+ Trajectory: 18,945 → 22,717 (collections) → 24,718 (fold + wrappers).
55
+ - **to_s** renders the full printed face — the canonical template's
56
+ presence-guarded optionals and the V/ed alternated tail render what
57
+ the input carried (the conditional-render segment feature).
58
+ - **to_urn** is DECLARED (`urn.official`): type lowercased, the composed
59
+ code (number + minor + prefix-glued parts), the v/ed version face
60
+ (the edition affix under the fold flag), the year-month date group —
61
+ byte-identical with the coded generator everywhere the coded one
62
+ renders at all. The CODED generator raises `ArgumentError` on months
63
+ 08/09 (`format('%02d', "08")` — the zero-padded string reads as an
64
+ invalid octal); pre-existing on main, pinned by the parity spec — the
65
+ compiled generator renders the captured month verbatim and does not
66
+ share the defect.
67
+ - **to_mr_string** is DECLARED (`mr:`): the coded mr_type /
68
+ mr_number_with_part / mr_year / mr_edition hooks over the columns,
69
+ byte-identical slugs (`etsi.eg.200-053.2004-06.1-5-1`), with the
70
+ wrapper's supplement suffix (`…_a1.1`) carried by the synthesized
71
+ classes.
69
72
 
70
73
  ## Non-obvious facts, learned the hard way
71
74
 
72
75
  - The generated `--ruby-out` lands at `lib/etsi_pfs.rb`; the autoload and
73
76
  the `#{__dir__}` paths require it at `lib/pubid/etsi_pfs.rb` — move both
74
77
  the file and the directory (commands above).
75
- - `core: ["number"]` in a `.pfs` urn type row would generate
76
- `core: [:number]`, which crashes `PubidUrn.define` at load time (the
77
- generator emits `core.map(&:to_sym)`); only `core: []` round-trips.
78
- The instance's URNs resolve through the CODED schema anyway (the flavor
79
- segment of the canonical tag is `etsi`), so the alias rows are the
80
- future-cutover shape.
78
+ - The instance's URNs resolve through the CODED schema anyway (the
79
+ flavor segment of the canonical tag is `etsi`), so the alias's schema
80
+ rows are the future-cutover shape; the wrapper rows now carry the
81
+ coded `core: [["number", :int]]` ordinals (the phase-3 int-kind
82
+ cores).
81
83
  - `V1..1` parses in BOTH flavors (`.` is a version_char) and `GS` is a
82
84
  real type token — neither is a parity negative.
83
85
  - `Pubid::EtsiPfs.parse("ETSI …")` never hijacks `Pubid.parse` (registry
84
86
  keys sort `"etsi"` first), and `Pubid.from_hash` of an alias hash
85
87
  resolves the coded `EtsiStandard` (one scheme, one `_type`).
86
88
  - The corpus inventory (`spec/fixtures/etsi/identifiers/full/`) is the
87
- parity source; its bucket split is pinned: plain 18,945 / parts 3,772 /
88
- edition 1,833 / supplement 168.
89
+ parity source; its former bucket split (plain 18,945 / parts 3,772 /
90
+ edition 1,833 / supplement 168) is now the per-gap parity trajectory —
91
+ all 24,718 rows agree on to_hash, to_pubid_urn, to_s, to_urn and
92
+ to_mr_string.
93
+ - The run's internal captures (`supplement_kind` / `supplement_number`)
94
+ bind only through the run's item rows, and unbound captures arrive as
95
+ raw `Parsanol::Slice` leaves — the generated builder unwraps
96
+ `Slice#content` (the cen_cenelec precedent).
89
97
 
90
- ## v1 boundaries
98
+ ## v1 boundaries (still)
91
99
 
92
- Single-type schemes only, `kind: string` fields only, no free-standing
93
- `stages:`/`supplements:` sections, no semantic decode rules — see
94
- `pubid-grammar`'s `definitions/README.md` for the language reference and
95
- the fail-closed validation rules.
100
+ Single declared type per scheme (the `supplements:` wrappers are the one
101
+ multi-class shape), no free-standing `stages:` maps, no semantic decode
102
+ rules — see `pubid-grammar`'s `definitions/README.md` for the language
103
+ reference and the fail-closed validation rules.
@@ -1,25 +1,58 @@
1
- module Pubid
2
- module EtsiPfs
3
- class Builder
4
- def self.build(parsed_data)
5
- new.build(parsed_data)
6
- end
1
+ module Pubid
2
+ module EtsiPfs
3
+ class Builder
4
+ # The wrapper classes by kind face: the run's dispatch.
5
+ SUPPLEMENT_WRAPPERS = {
6
+ "A" => Identifiers::Amendment,
7
+ "C" => Identifiers::Corrigendum,
8
+ }.freeze
9
+
10
+ def self.build(parsed_data)
11
+ new.build(parsed_data)
12
+ end
7
13
 
8
- def build(data)
9
- Identifiers::Standard.new(
14
+ def build(data)
15
+ identifier = Identifiers::Standard.new(
10
16
  type: data[:type],
11
17
  number: data[:number],
12
18
  minor: data[:minor],
13
- parts: data[:parts],
14
- supplements: data[:supplements],
15
- version: data[:version],
16
- edition: data[:edition],
19
+ parts: build_parts(data),
20
+ version: data[:version] || data[:edition],
21
+ is_edition: !data[:edition].nil?,
17
22
  year: data[:year],
18
23
  month: data[:month],
24
+ )
25
+ run = Array(data[:supplements])
26
+ run.each do |row|
27
+ item = row[:supplement] || row["supplement"] || {}
28
+ kind = unwrap(item[:supplement_kind] || item["supplement_kind"])
29
+ identifier = SUPPLEMENT_WRAPPERS.fetch(kind).new(
30
+ number: unwrap(item[:supplement_number] || item["supplement_number"]).to_i,
31
+ base: identifier,
19
32
  )
20
33
  end
34
+ identifier
35
+ end
36
+
37
+ # The run's internal captures bind only through the item row
38
+ # (the cen_cenelec precedent), so unbound ones arrive as raw
39
+ # leaves — a Parsanol::Slice or a {value:, offset: ...} span.
40
+ # Unwrap the captured value.
41
+ def unwrap(capture)
42
+ return capture.content if capture.is_a?(Parsanol::Slice)
43
+ return capture[:value] if capture.is_a?(Hash) && capture.key?(:value)
44
+
45
+ capture
46
+ end
47
+
48
+ # The parts column from its bound item rows.
49
+ def build_parts(data)
50
+ Array(data[:parts]).map do |row|
51
+ row[:part] || row["part"]
52
+ end.compact
21
53
  end
22
54
  end
23
55
  end
24
56
 
25
57
  Pubid::EtsiPfs::Builder.prepend(Pubid::Builder::AllPartsWrap)
58
+ end
@@ -1,50 +1,32 @@
1
- module Pubid
2
- module EtsiPfs
3
- class Identifier < ::Pubid::Identifier
4
- attribute :type, :string
5
- attribute :number, :string
6
- attribute :minor, :string
7
- attribute :parts, :string
8
- attribute :supplements, :string
9
- attribute :version, :string
10
- attribute :edition, :string
11
- attribute :year, :string
12
- attribute :month, :string
1
+ module Pubid
2
+ module EtsiPfs
3
+ class Identifier < ::Pubid::Identifier
4
+ attribute :publisher, :string, default: -> { "ETSI" }
13
5
 
14
- ETSI_TYPE_MAP = {
15
- "pubid:etsi:etsi-standard" => "Pubid::EtsiPfs::Identifiers::Standard",
16
- }.freeze
6
+ PUBLISHER = "ETSI"
17
7
 
18
- key_value do
19
- map "_type", to: :_type, polymorphic_map: ETSI_TYPE_MAP
20
- map "type", to: :type
21
- map "number", to: :number
22
- map "minor", to: :minor
23
- map "parts", to: :parts
24
- map "supplements", to: :supplements
25
- map "version", to: :version
26
- map "edition", to: :edition
27
- map "year", to: :year
28
- map "month", to: :month
29
- end
30
-
31
- PUBLISHER = "ETSI"
8
+ # The scheme's declared publisher token: the internal-form
9
+ # cut (to_s(with_publisher: false)) strips it from the
10
+ # rendered face.
11
+ def publisher_prefixes
12
+ [PUBLISHER]
13
+ end
32
14
 
33
- def self.parse(identifier)
34
- unless identifier.is_a?(String)
35
- raise Pubid::Errors::InvalidInputError,
36
- Pubid::INPUT_NOT_A_STRING_MESSAGE
37
- end
15
+ def self.parse(identifier)
16
+ unless identifier.is_a?(String)
17
+ raise Pubid::Errors::InvalidInputError,
18
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
19
+ end
38
20
 
39
- if identifier.length > Pubid::MAX_INPUT_LENGTH
40
- raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
41
- end
21
+ if identifier.length > Pubid::MAX_INPUT_LENGTH
22
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
23
+ end
42
24
 
43
- identifier = ::Pubid.utf8_input(identifier)
25
+ identifier = ::Pubid.utf8_input(identifier)
44
26
 
45
- parsed = Pubid::Parg::Model.bound(:etsi_pfs, identifier)
46
- Builder.build(parsed)
47
- end
27
+ parsed = Pubid::Parg::Model.bound(:etsi_pfs, identifier)
28
+ Builder.build(parsed)
48
29
  end
49
30
  end
50
31
  end
32
+ end
@@ -0,0 +1,20 @@
1
+ module Pubid
2
+ module EtsiPfs
3
+ module Identifiers
4
+ # The synthesized Amendment wrapper (PFS supplements
5
+ # declaration): the kind face "A" is the run's
6
+ # dispatch token and the printed notation's face.
7
+ class Amendment < Supplement
8
+ FACE = "A".freeze
9
+
10
+ def self.face
11
+ FACE
12
+ end
13
+
14
+ def self.polymorphic_name
15
+ "pubid:etsi:amendment"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module Pubid
2
+ module EtsiPfs
3
+ module Identifiers
4
+ # The synthesized Corrigendum wrapper (PFS supplements
5
+ # declaration): the kind face "C" is the run's
6
+ # dispatch token and the printed notation's face.
7
+ class Corrigendum < Supplement
8
+ FACE = "C".freeze
9
+
10
+ def self.face
11
+ FACE
12
+ end
13
+
14
+ def self.polymorphic_name
15
+ "pubid:etsi:corrigendum"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,22 +1,71 @@
1
- module Pubid
2
- module EtsiPfs
3
- module Identifiers
4
- # Concrete ETSI Standard identifier type (PFS type `standard`).
5
- class Standard < Identifier
1
+ module Pubid
2
+ module EtsiPfs
3
+ module Identifiers
4
+ # Concrete ETSI Standard identifier type (PFS type `standard`).
5
+ class Standard < Identifier
6
6
  TYPED_STAGES = [].freeze
7
7
 
8
- def self.type
9
- { key: :etsi, web: :etsi, title: "ETSI Standard", short: "ETSI" }
10
- end
8
+ attribute :type, :string
9
+ attribute :number, :string
10
+ attribute :minor, :string
11
+ attribute :parts, :string, collection: true, initialize_empty: true
12
+ attribute :version, :string
13
+ attribute :year, :string
14
+ attribute :month, :string
15
+ attribute :is_edition, :boolean, default: -> { false }
11
16
 
12
- # The scheme-canonical wire tag declared in the definition:
13
- # to_hash stays byte-identical with the scheme's coded
14
- # flavor, and this flavor's own from_hash routes the tag
15
- # to this class.
16
- def self.polymorphic_name
17
- "pubid:etsi:etsi-standard"
18
- end
17
+ key_value do
18
+ map "_type", to: :_type
19
+ map "type", to: :type
20
+ map "number", to: :number
21
+ map "minor", to: :minor
22
+ map "parts", to: :parts
23
+ map "version", to: :version
24
+ map "year", to: :year
25
+ map "month", to: :month
26
+ map "is_edition", to: :is_edition
27
+ end
28
+
29
+ def self.type
30
+ { key: :etsi, web: :etsi, title: "ETSI Standard", short: "ETSI" }
31
+ end
32
+
33
+ # The scheme-canonical wire tag declared in the definition:
34
+ # to_hash stays byte-identical with the scheme's coded
35
+ # flavor, and this flavor's own from_hash routes the tag
36
+ # to this class.
37
+ def self.polymorphic_name
38
+ "pubid:etsi:etsi-standard"
39
+ end
40
+
41
+ def mr_type
42
+ mr_sanitize(type)
43
+ end
44
+
45
+ def mr_number_with_part
46
+ mr_sanitize([number, minor, *Array(parts)].compact.join("-"))
47
+ end
48
+
49
+ def mr_year
50
+ mr_sanitize([year, month].compact.join("-"))
51
+ end
52
+
53
+ def mr_edition
54
+ mr_sanitize(version)
55
+ end
56
+
57
+ # The MR charset rule (the coded flavors' mr_sanitize):
58
+ # lowercase, [a-z0-9-], non-charset runs collapsed to one
59
+ # dash; empty answers nil so the joined slug never gains a
60
+ # blank segment.
61
+ def mr_sanitize(value)
62
+ return nil if value.nil? || value.to_s.empty?
63
+
64
+ sanitized = value.to_s.downcase
65
+ .gsub(/[^a-z0-9-]+/, "-").gsub(/\A-+|-+\z/, "")
66
+ sanitized.empty? ? nil : sanitized
19
67
  end
20
68
  end
21
69
  end
22
70
  end
71
+ end
@@ -0,0 +1,72 @@
1
+ module Pubid
2
+ module EtsiPfs
3
+ module Identifiers
4
+ # The supplement wrapper base (pubid#706 phase 3): the
5
+ # wrapper owns its ordinal and the base identifier; every
6
+ # delegated attribute reads through.
7
+ class Supplement < ::Pubid::EtsiPfs::Identifier
8
+ attribute :base, ::Pubid::EtsiPfs::Identifier
9
+ attribute :number, :integer
10
+
11
+ key_value do
12
+ map "_type", to: :_type
13
+ map "number", to: :number
14
+ map "base", with: { to: :base_to_kv, from: :base_from_kv }
15
+ end
16
+
17
+ # Serialize the base via its own to_hash so it collapses
18
+ # to the leaf standard shape (the coded converters'
19
+ # rule).
20
+ def base_to_kv(model, doc)
21
+ return unless model.base
22
+
23
+ doc.add_child(
24
+ Lutaml::KeyValue::DataModel::Element.new("base", model.base.to_hash),
25
+ )
26
+ end
27
+
28
+ # Re-dispatch through the flavor base so _type resolves
29
+ # to the concrete Standard (the coded JCGM/ETSI rule).
30
+ def base_from_kv(model, value)
31
+ return unless value
32
+
33
+ model.base = ::Pubid::EtsiPfs::Identifier.from_hash(value)
34
+ end
35
+
36
+ # Delegations: the wrapper reads through to the wrapped
37
+ # standard (the coded SupplementIdentifier shape).
38
+ def type
39
+ base&.type
40
+ end
41
+
42
+ def version
43
+ base&.version
44
+ end
45
+
46
+ def year
47
+ base&.year
48
+ end
49
+
50
+ def month
51
+ base&.month
52
+ end
53
+
54
+ def is_edition
55
+ base&.is_edition
56
+ end
57
+
58
+ # The printed notation: the kind face + the ordinal.
59
+ def supplement_notation
60
+ "#{self.class.face}#{number}"
61
+ end
62
+
63
+ # The MR slug's supplement suffix: the shared MrString
64
+ # recurses into base and appends this (the coded
65
+ # mr_supplement_suffix rule).
66
+ def mr_supplement_suffix
67
+ [supplement_notation.to_s.downcase, number].compact.join(".")
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,7 +1,10 @@
1
1
  module Pubid
2
2
  module EtsiPfs
3
3
  module Identifiers
4
- autoload :Standard, "#{__dir__}/identifiers/standard"
4
+ autoload :Standard, "#{__dir__}/identifiers/standard"
5
+ autoload :Supplement, "#{__dir__}/identifiers/supplement"
6
+ autoload :Amendment, "#{__dir__}/identifiers/amendment"
7
+ autoload :Corrigendum, "#{__dir__}/identifiers/corrigendum"
5
8
  end
6
9
  end
7
10
  end
@@ -12,8 +12,10 @@
12
12
  atom "year", "year"
13
13
  atom "month", "month"
14
14
  atom "version", "v"
15
- type "pubid:etsi_pfs:identifier", "id", core: []
16
- type "pubid:etsi_pfs:standard", "es", core: []
15
+ type "pubid:etsi:etsi-standard", "es", core: []
16
+ type "pubid:etsi:amendment", "amd", core: [["number", :int]]
17
+ type "pubid:etsi:corrigendum", "cor", core: [["number", :int]]
18
+ type "pubid:etsi:identifier", "id", core: []
17
19
  end
18
20
  end
19
21
  end
@@ -3,7 +3,35 @@ module Pubid
3
3
  # Human renderer, generated from the canonical template.
4
4
  class Renderer < ::Pubid::Renderers::Base
5
5
  def render(**_opts)
6
- "ETSI #{@id.type} #{@id.number}"
6
+ f = render_fields(@id)
7
+ "ETSI #{f[:type]} #{f[:number]}#{(" #{f[:minor]}") if f[:minor]}#{("#{f[:parts]}") if f[:parts]}#{f[:supplements]}#{if f[:version]; " V#{f[:version]}" elsif f[:edition]; " ed.#{f[:edition]}"; end}#{(" (#{f[:year]}-#{f[:month]})") if f[:year] && f[:month]}"
8
+ end
9
+
10
+ private
11
+
12
+ # The field view: a supplement wrapper reads through to
13
+ # the wrapped standard and carries the notation run
14
+ # ("/A1/C1", outermost last); the fold's target renders
15
+ # only outside the fold flag (the V/ed faces are mutually
16
+ # exclusive by grammar).
17
+ def render_fields(id)
18
+ root = id
19
+ run = +""
20
+ while root.is_a?(Identifiers::Supplement)
21
+ run.prepend("/#{root.supplement_notation}")
22
+ root = root.base
23
+ end
24
+ {
25
+ type: root.type,
26
+ number: root.number,
27
+ minor: root.minor,
28
+ parts: root.parts.any? ? root.parts.map { |item| "-#{item}" }.join : nil,
29
+ version: root.is_edition ? nil : root.version,
30
+ edition: root.is_edition ? root.version : nil,
31
+ year: root.year,
32
+ month: root.month,
33
+ supplements: run.empty? ? nil : run,
34
+ }
7
35
  end
8
36
  end
9
37
  end
@@ -1,12 +1,49 @@
1
1
  module Pubid
2
2
  module EtsiPfs
3
- # The official URN generator, pinned to the scheme's declared
4
- # namespace ("etsi") so to_urn stays
5
- # scheme-canonical under the compiled flavor key.
3
+ # The official URN generator: the scheme's declared namespace
4
+ # ("etsi") and the ordered segments the
5
+ # definition's urn.official declares — an absent field leaves no
6
+ # segment, a date group joins its fields with "-", a compose
7
+ # slots its fields with the declared glue, and a fold target
8
+ # switches to its edition affix while the flag holds.
6
9
  class UrnGenerator < ::Pubid::UrnGenerator::Base
7
10
  def urn_namespace
8
11
  "etsi"
9
12
  end
13
+
14
+ def generate
15
+ parts = ["urn", urn_namespace]
16
+ parts.concat(official_segments)
17
+ parts.join(":")
18
+ end
19
+
20
+ private
21
+
22
+ # A supplement wrapper reads through to the wrapped
23
+ # standard (the coded generator's own delegation shape).
24
+ def subject
25
+ current = @identifier
26
+ current = current.base while current.is_a?(Identifiers::Supplement)
27
+ current
28
+ end
29
+
30
+ def official_segments
31
+ segments = []
32
+ segments << subject.type.to_s.downcase if subject.type
33
+ code = []
34
+ code << subject.number.to_s if subject.number
35
+ code << " " + subject.minor.to_s if subject.minor
36
+ subject.parts.each { |item| code << "-" + item } if subject.parts.any?
37
+ segments << code.join unless code.empty?
38
+ if subject.is_edition
39
+ segments << "ed." + subject.version.to_s.downcase
40
+ elsif subject.version
41
+ segments << "v" + subject.version.to_s.downcase
42
+ end
43
+ date = [subject.year, subject.month].compact
44
+ segments << date.join("-") unless date.empty?
45
+ segments
46
+ end
10
47
  end
11
48
  end
12
49
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  #
3
3
  # PFS-generated flavor — DO NOT EDIT BY HAND.
4
- # Source: definitions/etsi.pfs (pfs:1) sha256:9343088bf087b55ea790057e6be0bbebe2272407ee81f035b52e5f9a4d5c7b76
4
+ # Source: definitions/etsi.pfs (pfs:1) sha256:490ba9435f44dbd2d59429ae6d9527909c39444f19fec5a33a8cad91e89e3581
5
5
  # Compiler: pubid/grammar/pfs 0.1.0 (baked with parsanol 1.3.126)
6
6
  # Regenerate: ruby scripts/pfs compile definitions/etsi.pfs --as etsi_pfs --ruby-out <dir>
7
7
 
@@ -16,6 +16,7 @@ module Pubid
16
16
  extend Pubid::PrefixesSupport
17
17
 
18
18
  PREFIXES = ["ETSI"].freeze
19
+
19
20
 
20
21
  autoload :Builder, "#{__dir__}/etsi_pfs/builder"
21
22
  autoload :Identifier, "#{__dir__}/etsi_pfs/identifier"
data/lib/pubid/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pubid
4
- VERSION = "2.0.25"
4
+ VERSION = "2.0.26"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pubid
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.25
4
+ version: 2.0.26
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -551,7 +551,10 @@ files:
551
551
  - lib/pubid/etsi_pfs/builder.rb
552
552
  - lib/pubid/etsi_pfs/identifier.rb
553
553
  - lib/pubid/etsi_pfs/identifiers.rb
554
+ - lib/pubid/etsi_pfs/identifiers/amendment.rb
555
+ - lib/pubid/etsi_pfs/identifiers/corrigendum.rb
554
556
  - lib/pubid/etsi_pfs/identifiers/standard.rb
557
+ - lib/pubid/etsi_pfs/identifiers/supplement.rb
555
558
  - lib/pubid/etsi_pfs/parser.rb
556
559
  - lib/pubid/etsi_pfs/pubid_urn_schema.rb
557
560
  - lib/pubid/etsi_pfs/renderer.rb