pubid 2.0.23 → 2.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/data/parg/calconnect_pfs.json +1 -0
  3. data/data/parg/cen_pfs.json +1 -0
  4. data/data/parg/etsi_pfs.json +1 -0
  5. data/lib/pubid/calconnect_pfs/builder.rb +35 -0
  6. data/lib/pubid/calconnect_pfs/identifier.rb +84 -0
  7. data/lib/pubid/calconnect_pfs/identifiers/standard.rb +31 -0
  8. data/lib/pubid/calconnect_pfs/identifiers.rb +7 -0
  9. data/lib/pubid/calconnect_pfs/parser.rb +10 -0
  10. data/lib/pubid/calconnect_pfs/pubid_urn_schema.rb +17 -0
  11. data/lib/pubid/calconnect_pfs/renderer.rb +10 -0
  12. data/lib/pubid/calconnect_pfs/urn_generator.rb +30 -0
  13. data/lib/pubid/calconnect_pfs.rb +60 -0
  14. data/lib/pubid/cen_pfs/builder.rb +33 -0
  15. data/lib/pubid/cen_pfs/identifier.rb +64 -0
  16. data/lib/pubid/cen_pfs/identifiers/european_norm.rb +31 -0
  17. data/lib/pubid/cen_pfs/identifiers.rb +7 -0
  18. data/lib/pubid/cen_pfs/parser.rb +10 -0
  19. data/lib/pubid/cen_pfs/pubid_urn_schema.rb +15 -0
  20. data/lib/pubid/cen_pfs/renderer.rb +10 -0
  21. data/lib/pubid/cen_pfs/urn_generator.rb +12 -0
  22. data/lib/pubid/cen_pfs.rb +71 -0
  23. data/lib/pubid/doi_pfs/identifier.rb +8 -1
  24. data/lib/pubid/doi_pfs.rb +3 -11
  25. data/lib/pubid/etsi_pfs/CLAUDE.md +95 -0
  26. data/lib/pubid/etsi_pfs/builder.rb +25 -0
  27. data/lib/pubid/etsi_pfs/identifier.rb +50 -0
  28. data/lib/pubid/etsi_pfs/identifiers/standard.rb +22 -0
  29. data/lib/pubid/etsi_pfs/identifiers.rb +7 -0
  30. data/lib/pubid/etsi_pfs/parser.rb +10 -0
  31. data/lib/pubid/etsi_pfs/pubid_urn_schema.rb +19 -0
  32. data/lib/pubid/etsi_pfs/renderer.rb +10 -0
  33. data/lib/pubid/etsi_pfs/urn_generator.rb +12 -0
  34. data/lib/pubid/etsi_pfs.rb +59 -0
  35. data/lib/pubid/itu/CLAUDE.md +25 -14
  36. data/lib/pubid/itu/builder.rb +7 -5
  37. data/lib/pubid/itu/identifiers/special_publication.rb +20 -17
  38. data/lib/pubid/itu/urn_generator.rb +3 -4
  39. data/lib/pubid/jis/CLAUDE.md +35 -0
  40. data/lib/pubid/jis/builder.rb +33 -2
  41. data/lib/pubid/jis/identifier.rb +24 -4
  42. data/lib/pubid/jis/identifiers/welding_engineering_standard.rb +21 -2
  43. data/lib/pubid/jis/identifiers/welding_engineering_technical_report.rb +13 -0
  44. data/lib/pubid/jis/identifiers/welding_engineering_technical_specification.rb +14 -0
  45. data/lib/pubid/jis/identifiers.rb +5 -0
  46. data/lib/pubid/jis/parser.rb +30 -0
  47. data/lib/pubid/jis/pubid_urn_schema.rb +14 -3
  48. data/lib/pubid/jis/renderer.rb +1 -2
  49. data/lib/pubid/version.rb +1 -1
  50. data/lib/pubid.rb +13 -0
  51. metadata +35 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41a0360b8ea34997a26ebf234cce2a8f0b913dd31f4a945225a38819945c80c5
4
- data.tar.gz: fa8af772bf90a86b5998c3cda2a2781e35ed0d985c1d3bcd77f3681ed9709242
3
+ metadata.gz: e4c2df973dfad39ae259a1f12504977716b523133f7ba6e98f86dc1359274206
4
+ data.tar.gz: c4c7e18a50914f61ca002b3ce53d00b4b7dcdfdcac33168b34e6109f8f6b2890
5
5
  SHA512:
6
- metadata.gz: cf7d608f5fac874a6c650abb0f73ff2f59bfcfd834972eecdd05f84cf69f822c8b5856705b3608cd4c307b4f9cbf3767b88a4ca8c9b0ad2c35d8abce70e9ea55
7
- data.tar.gz: 1e044ccf572203720c893e16a269051da6721d72874dd3d57658458b9eee002621a4364c0bc55d951eb55e4195b411dcaf7275b022ca9b2f87abbb2de0f6e731
6
+ metadata.gz: 1224c069ca1bc1caa4ca1968252a4686dff6a99ac99b167e65546a800f0b1067e88e807dbb87da80db18744bdde4a1c3aee630da35d4610cefa7d36d52a055fc
7
+ data.tar.gz: d9091695721ae3b73953f550b5515b598c7328875fc1c679f549e339d0de2daa517ed0412bcf33fe3c04568cd1522a0635f9033ba79983981cacbc04dbedd1cf
@@ -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":"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"}
@@ -0,0 +1,35 @@
1
+ module Pubid
2
+ module CalconnectPfs
3
+ class Builder
4
+ def self.build(parsed_data)
5
+ new.build(parsed_data)
6
+ end
7
+
8
+ def build(data)
9
+ Identifiers::Standard.new(
10
+ series: data[:series],
11
+ number: data[:number],
12
+ date: build_date(data),
13
+ )
14
+ end
15
+
16
+ private
17
+
18
+ # The publication date as the shared Components::Date, or
19
+ # nil when the reference omits the calendar fields (a
20
+ # partial reference matches by excluding the date).
21
+ def build_date(data)
22
+ values = [data[:year], data[:month], data[:day]]
23
+ return nil if values.compact.empty?
24
+
25
+ ::Pubid::Components::Date.new(
26
+ year: data[:year]&.to_s,
27
+ month: data[:month]&.to_s,
28
+ day: data[:day]&.to_s,
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ Pubid::CalconnectPfs::Builder.prepend(Pubid::Builder::AllPartsWrap)
@@ -0,0 +1,84 @@
1
+ module Pubid
2
+ module CalconnectPfs
3
+ class Identifier < ::Pubid::Identifier
4
+ attribute :series, :string
5
+ attribute :number, :string
6
+ attribute :date, ::Pubid::Components::Date
7
+
8
+ CC_TYPE_MAP = {
9
+ "pubid:calconnect:standard" => "Pubid::CalconnectPfs::Identifiers::Standard",
10
+ }.freeze
11
+
12
+ key_value do
13
+ map "_type", to: :_type, polymorphic_map: CC_TYPE_MAP
14
+ map "series", to: :series
15
+ map "number", to: :number
16
+ map "year", with: { to: :year_to_kv, from: :year_from_kv }
17
+ map "month", with: { to: :month_to_kv, from: :month_from_kv }
18
+ map "day", with: { to: :day_to_kv, from: :day_from_kv }
19
+ end
20
+
21
+ # --- date serialized flat (the shared flat-scalar shape) ---
22
+ def year_to_kv(model, doc)
23
+ emit_date_part(doc, "year", model.date&.year)
24
+ end
25
+
26
+ def year_from_kv(model, value)
27
+ date_for(model).year = value.to_s
28
+ end
29
+
30
+ # --- date serialized flat (the shared flat-scalar shape) ---
31
+ def month_to_kv(model, doc)
32
+ emit_date_part(doc, "month", model.date&.month)
33
+ end
34
+
35
+ def month_from_kv(model, value)
36
+ date_for(model).month = value.to_s
37
+ end
38
+
39
+ # --- date serialized flat (the shared flat-scalar shape) ---
40
+ def day_to_kv(model, doc)
41
+ emit_date_part(doc, "day", model.date&.day)
42
+ end
43
+
44
+ def day_from_kv(model, value)
45
+ date_for(model).day = value.to_s
46
+ end
47
+
48
+ def emit_date_part(doc, key, val)
49
+ return if val.nil? || val.to_s.empty?
50
+
51
+ doc.add_child(Lutaml::KeyValue::DataModel::Element.new(key, val.to_s))
52
+ end
53
+
54
+ def date_for(model)
55
+ model.date ||= ::Pubid::Components::Date.new
56
+ end
57
+
58
+ PUBLISHER = "CC"
59
+
60
+ # The scheme's declared publisher token: the internal-form
61
+ # cut (to_s(with_publisher: false)) strips it from the
62
+ # rendered face.
63
+ def publisher_prefixes
64
+ [PUBLISHER]
65
+ end
66
+
67
+ def self.parse(identifier)
68
+ unless identifier.is_a?(String)
69
+ raise Pubid::Errors::InvalidInputError,
70
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
71
+ end
72
+
73
+ if identifier.length > Pubid::MAX_INPUT_LENGTH
74
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
75
+ end
76
+
77
+ identifier = ::Pubid.utf8_input(identifier)
78
+
79
+ parsed = Pubid::Parg::Model.bound(:calconnect_pfs, identifier)
80
+ Builder.build(parsed)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,31 @@
1
+ module Pubid
2
+ module CalconnectPfs
3
+ module Identifiers
4
+ # Concrete CalConnect Standard identifier type (PFS type `standard`).
5
+ class Standard < Identifier
6
+ TYPED_STAGES = [
7
+ Pubid::Components::TypedStage.new(
8
+ code: :pubcc,
9
+ stage_code: :published,
10
+ type_code: :cc,
11
+ abbr: ["CC"],
12
+ name: "CalConnect Standard",
13
+ harmonized_stages: [],
14
+ ),
15
+ ].freeze
16
+
17
+ def self.type
18
+ { key: :cc, web: :standard, title: "CalConnect Standard", short: "CC" }
19
+ end
20
+
21
+ # The scheme-canonical wire tag declared in the definition:
22
+ # to_hash stays byte-identical with the scheme's coded
23
+ # flavor, and this flavor's own from_hash routes the tag
24
+ # to this class.
25
+ def self.polymorphic_name
26
+ "pubid:calconnect:standard"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ module Pubid
2
+ module CalconnectPfs
3
+ module Identifiers
4
+ autoload :Standard, "#{__dir__}/identifiers/standard"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module Pubid
2
+ module CalconnectPfs
3
+ # PFS-generated parser shell. Parsing runs through the baked
4
+ # PARG artifact (data/parg/calconnect_pfs.json) via Pubid::Parg::Model.bound;
5
+ # the grammar source lives in pubid-grammar
6
+ # (grammars/calconnect_pfs.parg, compiled from definitions/calconnect.pfs).
7
+ class Parser
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ module Pubid
2
+ module CalconnectPfs
3
+ # The pubid-scheme URN schema, registered under the compiled
4
+ # flavor key. The definition's declared namespace
5
+ # ("calconnect") is the scheme's canonical
6
+ # namespace; identifiers carry the scheme's wire tag, so their
7
+ # pubid URNs encode through the scheme's own schema.
8
+ PubidUrnSchema = Pubid::PubidUrn.define("calconnect_pfs") do
9
+ atom "series", "series"
10
+ atom "year", "year"
11
+ atom "month", "month"
12
+ atom "day", "day"
13
+ type "pubid:calconnect:identifier", "id", core: []
14
+ type "pubid:calconnect:standard", "std", core: ["number"]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ module Pubid
2
+ module CalconnectPfs
3
+ # Human renderer, generated from the canonical template.
4
+ class Renderer < ::Pubid::Renderers::Base
5
+ def render(**_opts)
6
+ "CC#{"/#{@id.series}" if @id.series} #{@id.number}#{":#{@id.date.year}#{"-#{@id.date.month}-#{@id.date.day}" if @id.date&.month && @id.date&.day}" if @id.date&.year}"
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ module Pubid
2
+ module CalconnectPfs
3
+ # The official URN generator: the scheme's declared namespace
4
+ # ("calconnect") 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 "-".
7
+ class UrnGenerator < ::Pubid::UrnGenerator::Base
8
+ def urn_namespace
9
+ "calconnect"
10
+ end
11
+
12
+ def generate
13
+ parts = ["urn", urn_namespace]
14
+ parts.concat(official_segments)
15
+ parts.join(":")
16
+ end
17
+
18
+ private
19
+
20
+ def official_segments
21
+ segments = []
22
+ segments << @identifier.series if @identifier.series
23
+ segments << @identifier.number if @identifier.number
24
+ date = [@identifier.date&.year, @identifier.date&.month, @identifier.date&.day].compact
25
+ segments << date.join("-") unless date.empty?
26
+ segments
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # PFS-generated flavor — DO NOT EDIT BY HAND.
4
+ # Source: definitions/calconnect.pfs (pfs:1) sha256:2e864f0185a1a2f18a392f2f401ad3c9d65d1f32a9ec3f3f04f7dfca9b2226c4
5
+ # Compiler: pubid/grammar/pfs 0.1.0 (baked with parsanol 1.3.126)
6
+ # Regenerate: ruby scripts/pfs compile definitions/calconnect.pfs --as calconnect_pfs --ruby-out <dir>
7
+
8
+ require "pubid"
9
+
10
+ module Pubid
11
+ # CalConnect Standards flavor, compiled from its PFS definition
12
+ # (pubid/pubid#706). A compiled flavor is a peer of the coded
13
+ # flavors: same registry, same input guards, same error
14
+ # namespace, same URN contracts.
15
+ module CalconnectPfs
16
+ extend Pubid::PrefixesSupport
17
+
18
+ PREFIXES = ["CC"].freeze
19
+
20
+
21
+ autoload :Builder, "#{__dir__}/calconnect_pfs/builder"
22
+ autoload :Identifier, "#{__dir__}/calconnect_pfs/identifier"
23
+ autoload :Identifiers, "#{__dir__}/calconnect_pfs/identifiers"
24
+ autoload :Parser, "#{__dir__}/calconnect_pfs/parser"
25
+ autoload :Renderer, "#{__dir__}/calconnect_pfs/renderer"
26
+ autoload :PubidUrnSchema, "#{__dir__}/calconnect_pfs/pubid_urn_schema"
27
+ autoload :UrnGenerator, "#{__dir__}/calconnect_pfs/urn_generator"
28
+
29
+ def self.parse(identifier)
30
+ Identifier.parse(identifier)
31
+ end
32
+
33
+ Identifier.format_registry = FormatRegistry.new(parent: Identifier.format_registry)
34
+ Identifier.format_registry.register(:human, renderer: CalconnectPfs::Renderer)
35
+
36
+ def self.identifier_types
37
+ @identifier_types ||= Identifiers.constants
38
+ .filter_map { |c| begin; Identifiers.const_get(c); rescue NameError; nil; end }
39
+ .select { |c| c.is_a?(Class) && c.singleton_methods(false).include?(:type) }
40
+ .select { |c| c.type.is_a?(Hash) }
41
+ end
42
+
43
+ def self.all_typed_stages
44
+ @all_typed_stages ||= identifier_types.flat_map do |klass|
45
+ klass.const_defined?(:TYPED_STAGES) ? klass.const_get(:TYPED_STAGES) : []
46
+ end
47
+ end
48
+
49
+ def self.locate_type(code)
50
+ identifier_types.find { |t| t.type[:key].to_s == code.to_s }
51
+ end
52
+
53
+ def self.locate_stage(abbr)
54
+ abbr_str = abbr.to_s.upcase
55
+ all_typed_stages.find { |s| s.abbr.any? { |a| a.to_s.upcase == abbr_str } }
56
+ end
57
+ end
58
+ end
59
+
60
+ Pubid::Registry.register(:calconnect_pfs, Pubid::CalconnectPfs)
@@ -0,0 +1,33 @@
1
+ module Pubid
2
+ module CenPfs
3
+ class Builder
4
+ def self.build(parsed_data)
5
+ new.build(parsed_data)
6
+ end
7
+
8
+ def build(data)
9
+ Identifiers::EuropeanNorm.new(
10
+ number: data[:number],
11
+ part: data[:part],
12
+ date: build_date(data),
13
+ )
14
+ end
15
+
16
+ private
17
+
18
+ # The publication date as the shared Components::Date, or
19
+ # nil when the reference omits the calendar fields (a
20
+ # partial reference matches by excluding the date).
21
+ def build_date(data)
22
+ values = [data[:year]]
23
+ return nil if values.compact.empty?
24
+
25
+ ::Pubid::Components::Date.new(
26
+ year: data[:year]&.to_s,
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Pubid::CenPfs::Builder.prepend(Pubid::Builder::AllPartsWrap)
@@ -0,0 +1,64 @@
1
+ module Pubid
2
+ module CenPfs
3
+ class Identifier < ::Pubid::Identifier
4
+ attribute :number, :string
5
+ attribute :part, :string
6
+ attribute :date, ::Pubid::Components::Date
7
+
8
+ EN_TYPE_MAP = {
9
+ "pubid:cencenelec:european-norm" => "Pubid::CenPfs::Identifiers::EuropeanNorm",
10
+ }.freeze
11
+
12
+ key_value do
13
+ map "_type", to: :_type, polymorphic_map: EN_TYPE_MAP
14
+ map "number", to: :number
15
+ map "part", to: :part
16
+ map "year", with: { to: :year_to_kv, from: :year_from_kv }
17
+ end
18
+
19
+ # --- date serialized flat (the shared flat-scalar shape) ---
20
+ def year_to_kv(model, doc)
21
+ emit_date_part(doc, "year", model.date&.year)
22
+ end
23
+
24
+ def year_from_kv(model, value)
25
+ date_for(model).year = value.to_s
26
+ end
27
+
28
+ def emit_date_part(doc, key, val)
29
+ return if val.nil? || val.to_s.empty?
30
+
31
+ doc.add_child(Lutaml::KeyValue::DataModel::Element.new(key, val.to_s))
32
+ end
33
+
34
+ def date_for(model)
35
+ model.date ||= ::Pubid::Components::Date.new
36
+ end
37
+
38
+ PUBLISHER = "CEN"
39
+
40
+ # The scheme's declared publisher token: the internal-form
41
+ # cut (to_s(with_publisher: false)) strips it from the
42
+ # rendered face.
43
+ def publisher_prefixes
44
+ [PUBLISHER]
45
+ end
46
+
47
+ def self.parse(identifier)
48
+ unless identifier.is_a?(String)
49
+ raise Pubid::Errors::InvalidInputError,
50
+ Pubid::INPUT_NOT_A_STRING_MESSAGE
51
+ end
52
+
53
+ if identifier.length > Pubid::MAX_INPUT_LENGTH
54
+ raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
55
+ end
56
+
57
+ identifier = ::Pubid.utf8_input(identifier)
58
+
59
+ parsed = Pubid::Parg::Model.bound(:cen_pfs, identifier)
60
+ Builder.build(parsed)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,31 @@
1
+ module Pubid
2
+ module CenPfs
3
+ module Identifiers
4
+ # Concrete European Norm identifier type (PFS type `european_norm`).
5
+ class EuropeanNorm < Identifier
6
+ TYPED_STAGES = [
7
+ Pubid::Components::TypedStage.new(
8
+ code: :puben,
9
+ stage_code: :published,
10
+ type_code: :en,
11
+ abbr: ["EN"],
12
+ name: "European Norm",
13
+ harmonized_stages: [],
14
+ ),
15
+ ].freeze
16
+
17
+ def self.type
18
+ { key: :en, web: :"european-norm", title: "European Norm", short: "EN" }
19
+ end
20
+
21
+ # The scheme-canonical wire tag declared in the definition:
22
+ # to_hash stays byte-identical with the scheme's coded
23
+ # flavor, and this flavor's own from_hash routes the tag
24
+ # to this class.
25
+ def self.polymorphic_name
26
+ "pubid:cencenelec:european-norm"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ module Pubid
2
+ module CenPfs
3
+ module Identifiers
4
+ autoload :EuropeanNorm, "#{__dir__}/identifiers/european_norm"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module Pubid
2
+ module CenPfs
3
+ # PFS-generated parser shell. Parsing runs through the baked
4
+ # PARG artifact (data/parg/cen_pfs.json) via Pubid::Parg::Model.bound;
5
+ # the grammar source lives in pubid-grammar
6
+ # (grammars/cen_pfs.parg, compiled from definitions/cen.pfs).
7
+ class Parser
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ module Pubid
2
+ module CenPfs
3
+ # The pubid-scheme URN schema, registered under the compiled
4
+ # flavor key. The definition's declared namespace
5
+ # ("cen") is the scheme's canonical
6
+ # namespace; identifiers carry the scheme's wire tag, so their
7
+ # pubid URNs encode through the scheme's own schema.
8
+ PubidUrnSchema = Pubid::PubidUrn.define("cen_pfs") do
9
+ atom "part", "part"
10
+ atom "year", "year"
11
+ type "pubid:cencenelec:identifier", "id", core: []
12
+ type "pubid:cencenelec:european-norm", "en", core: ["number"]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ module Pubid
2
+ module CenPfs
3
+ # Human renderer, generated from the canonical template.
4
+ class Renderer < ::Pubid::Renderers::Base
5
+ def render(**_opts)
6
+ "EN #{@id.number}#{"-#{@id.part}" if @id.part}#{":#{@id.date.year}" if @id.date&.year}"
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ module Pubid
2
+ module CenPfs
3
+ # The official URN generator, pinned to the scheme's declared
4
+ # namespace ("cen") so to_urn stays
5
+ # scheme-canonical under the compiled flavor key.
6
+ class UrnGenerator < ::Pubid::UrnGenerator::Base
7
+ def urn_namespace
8
+ "cen"
9
+ end
10
+ end
11
+ end
12
+ end