pubid 2.0.26 → 2.0.28

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: ef65c4cbf5afc5635f55300fc57894462ecc211d2dd51d2c2605dafda75a84a3
4
- data.tar.gz: 89c5dccd912343c12ea157220089f7b1e0cf708abefe45e63c30c3bf712177e8
3
+ metadata.gz: 48f48ae087543c9b2a521a0ee3c062cf6336df6407c89e050f85fe213ccd22a1
4
+ data.tar.gz: 52701aa722dd5e759d078fbad77f3856d090cb9485df26ad0888852e3c85c0fd
5
5
  SHA512:
6
- metadata.gz: 135acc1bf9bd56785e0c861db78c0cad0b5026527ca6798ba1e5df7f9d2a19124211cc2b3583db2b332bcd7fc29300736b28c44b0dbbbdd482742607df2f2ddc
7
- data.tar.gz: ef9dc843feed906ae25c564839c6d710c30e3e4a3d7fb1e96fc4376ca73551deaade688e3bf7ceb825fd93292962a4db722f7994b4036a73c61d99e91a4f5626
6
+ metadata.gz: 30ed85c433a45fad6858fbee3fdd57a6fe3e30a4a2e489c258f0e077a43a59c90e25cc04ab97fa0d5960cfc0665a4106c850973d862699379e3369e48653ebd4
7
+ data.tar.gz: fa6e95e0c5829daae1ef08d468167ef363013fb4a16453eb377cc7e96a252b47d1097dce2dd108906deb24cd2652f1f1f831738d6cc43754b83aba111ee3952c
data/README.adoc CHANGED
@@ -272,23 +272,18 @@ PubID V2 provides two-way machine-readable conversion for all identifiers, suppo
272
272
 
273
273
  === Export to Hash
274
274
 
275
+ (The v1 reader chain `id.to_h` is historic and was removed; pubid 2 serializes
276
+ through `#to_hash`.)
277
+
275
278
  [source,ruby]
276
279
  ----
277
280
  require 'pubid/iso'
278
281
 
279
282
  id = Pubid::Iso.parse("ISO 9001:2015/Amd 1:2020")
280
- id.to_h
281
- # => {
282
- # flavor: "iso",
283
- # type: "amendment",
284
- # publisher: "ISO",
285
- # number: "9001",
286
- # year: "2015",
287
- # supplements: [
288
- # { type: "amendment", number: "1", year: "2020" }
289
- # ],
290
- # urn: "urn:iso:std:iso:9001:amd:2020:v1"
291
- # }
283
+ id.to_hash
284
+ # => {"_type" => "pubid:iso:amendment", "number" => "1", "year" => "2020",
285
+ # "base" => {"_type" => "pubid:iso:international-standard",
286
+ # "number" => "9001", "year" => "2015"}}
292
287
  ----
293
288
 
294
289
  === Export to JSON
@@ -299,7 +294,7 @@ require 'pubid/iso'
299
294
 
300
295
  id = Pubid::Iso.parse("ISO 9001:2015")
301
296
  id.to_json
302
- # => '{"flavor":"iso","publisher":"ISO","number":"9001","year":"2015",...}'
297
+ # => '{"_type":"pubid:iso:international-standard","number":"9001","year":"2015"}'
303
298
  ----
304
299
 
305
300
  === Import from Hash
@@ -337,12 +332,17 @@ Pubid.from_hash({ "number" => "9001" })
337
332
 
338
333
  === Import from JSON
339
334
 
335
+ (The v1 `Pubid::Serializable` import surface is historic; pubid 2 imports
336
+ through `Pubid.from_hash`, the hash counterpart of `Pubid.parse` documented
337
+ above, fed with `JSON.parse`.)
338
+
340
339
  [source,ruby]
341
340
  ----
341
+ require 'json'
342
342
  require 'pubid/iso'
343
343
 
344
- json = '{"flavor":"iso","publisher":"ISO","number":"9001","year":"2015"}'
345
- id = Pubid::Serializable.from_json(json)
344
+ json = '{"_type":"pubid:iso:international-standard","number":"9001","year":"2015"}'
345
+ id = Pubid.from_hash(JSON.parse(json))
346
346
  id.to_s # => "ISO 9001:2015"
347
347
  ----
348
348
 
@@ -598,9 +598,10 @@ bundle exec rake export:website_data
598
598
  bundle exec rake export:audit
599
599
  ----
600
600
 
601
- The export task produces a JSON file covering 23 flavors and 162 identifier
602
- types. Each flavor's identifier classes, typed stages, abbreviations, and up to
603
- 10 fixture examples are extracted automatically from the library source.
601
+ The export task produces a JSON file covering all 33 registered flavors and
602
+ 228 identifier types (2.0.26). Each flavor's identifier classes, typed stages,
603
+ abbreviations, and up to 10 fixture examples are extracted automatically from
604
+ the library source.
604
605
 
605
606
  === Programmatic API
606
607
 
@@ -613,9 +614,9 @@ data = Pubid::Export::Exporter.export_all
613
614
  # => { "iso" => { identifier_types: [...], attributes: [...] }, ... }
614
615
 
615
616
  # Export a single flavor
616
- exporter = Pubid::Export::SchemeExporter.new(:iso)
617
+ exporter = Pubid::Export::FlavorExporter.new(:iso)
617
618
  result = exporter.export
618
- result.to_h # => { identifier_types: [...], attributes: [...] }
619
+ result.to_h # => { identifier_types: [...], attributes: [...], ... }
619
620
  ----
620
621
 
621
622
  === Output Schema
@@ -644,7 +645,7 @@ The exported JSON follows this structure:
644
645
  "examples": ["ISO 9001:2015", "ISO/IEC 17031-1:2020", ...]
645
646
  }
646
647
  ],
647
- "attributes": ["number", "part", "date", "edition", ...]
648
+ "attributes": []
648
649
  }
649
650
  }
650
651
  ----
@@ -700,50 +701,26 @@ The exported JSON follows this structure:
700
701
 
701
702
  |`attributes`
702
703
  |`string[]`
703
- |Lutaml::Model attribute names on the identifier class
704
+ |Lutaml::Model attribute names on the identifier class (currently always
705
+ empty on 2.0.26)
704
706
 
705
707
  |`wrapper_types`
706
708
  |`object[]`
707
- |Value-added overlay types (VAP, Redline, etc.) — same schema as `identifier_types`
709
+ |Value-added overlay types (VAP, Redline, etc.) — same schema as `identifier_types`;
710
+ present when the flavor lists wrapper classes (`iec`, `bsi`)
708
711
  |===
709
712
 
710
- === Extraction Strategies
711
-
712
- Different flavors use different internal architectures. The export layer uses
713
- the Strategy pattern to handle each without modifying existing code:
714
-
715
- .Strategy classes
716
- [cols="1,2,2", options="header"]
717
- |===
718
- |Strategy |Flavors |Pattern
719
-
720
- |`SchemeExporter`
721
- |ISO, IEC, ASTM, ASHRAE, ASME, CCSDS, CIE, CSA, JIS, JCGM, OIML, IDF, API, SAE, ANSI
722
- |`Scheme.identifiers` + per-class `def self.type` + `TYPED_STAGES`
723
-
724
- |`RegistryExporter`
725
- |BSI, CEN
726
- |`TYPED_STAGES_REGISTRY` on Scheme class (centralized registry)
713
+ === Extraction
727
714
 
728
- |`IeeeExporter`
729
- |IEEE
730
- |`KEY_IDENTIFIER_CLASSES` from Identifiers module + module-level typed stages
715
+ One extractor, `Pubid::Export::FlavorExporter`, covers every flavor: it reads
716
+ the flavor module's self-describing `identifier_types` interface (falling back
717
+ to scanning the flavor's `Identifiers` namespace), the classes' `TYPED_STAGES`,
718
+ and fixture examples. Wrapper (overlay) types are discovered per flavor through
719
+ `FlavorExporter::WRAPPER_CLASSES` (IEC `VapIdentifier`, BSI
720
+ `ValueAddedPublication`).
731
721
 
732
- |`NistExporter`
733
- |NIST
734
- |`Scheme.identifiers` + per-class `typed_stages` class method (not constant)
735
-
736
- |`ItuExporter`
737
- |ITU
738
- |Sector-based types with transform/model pattern
739
-
740
- |`DataClassExporter`
741
- |ETSI, Plateau
742
- |`Lutaml::Model::Serializable` as Scheme (no per-class identifiers)
743
- |===
744
-
745
- Adding a new flavor requires only registering it in `FLAVOR_STRATEGIES` within
746
- `Exporter` — no existing strategy code changes.
722
+ Adding a new flavor requires only registering it in `Exporter::FLAVORS` —
723
+ no existing extractor code changes.
747
724
 
748
725
  === Adding a New Flavor to Export
749
726
 
@@ -755,42 +732,15 @@ it with the export layer:
755
732
  returning `{ key: :sym, title: "Name", short: "Abbr" }`. This is the primary
756
733
  source of metadata.
757
734
 
758
- . **Choose the right strategy** — Most flavors use one of the existing strategies:
759
-
760
- * `SchemeExporter` — if the flavor has a `Scheme` class with a class-level
761
- `identifiers` method that returns identifier classes. This is the most
762
- common pattern (ISO, IEC, ASTM, ASHRAE, etc.).
763
- * `RegistryExporter` — if the flavor uses a centralized `TYPED_STAGES_REGISTRY`
764
- on the Scheme class instead of per-class `TYPED_STAGES` (BSI, CEN).
765
- * `IeeeExporter` — only for IEEE, which has a unique constant-based discovery.
766
- * `NistExporter` — if typed stages come from a class method (not constant).
767
- * `ItuExporter` — for sector-based types with transform/model patterns.
768
- * `DataClassExporter` — for flavors where Scheme inherits from
769
- `Lutaml::Model::Serializable` (ETSI, Plateau).
770
-
771
735
  . **Register the flavor** in `lib/pubid/export/exporter.rb`:
772
736
 
773
737
  [source,ruby]
774
738
  ----
775
739
  FLAVORS = %i[iso iec ... new_flavor].freeze
776
-
777
- FLAVOR_STRATEGIES = {
778
- iso: :scheme,
779
- # ...
780
- new_flavor: :scheme, # or :registry, :data_class, etc.
781
- }.freeze
782
- ----
783
-
784
- . **Add the module mapping** in `FlavorExporter#scheme_module` (in
785
- `lib/pubid/export/flavor_exporter.rb`):
786
-
787
- [source,ruby]
788
- ----
789
- when "new_flavor" then Pubid::NewFlavor
790
740
  ----
791
741
 
792
742
  . **If the flavor has wrapper types** (value-added formats like VAP, Redline),
793
- add them to `WRAPPER_CLASSES` in `FlavorExporter`:
743
+ add them to `WRAPPER_CLASSES` in `lib/pubid/export/flavor_exporter.rb`:
794
744
 
795
745
  [source,ruby]
796
746
  ----
@@ -812,7 +762,7 @@ it with the export layer:
812
762
  require "spec_helper"
813
763
  require "pubid/export"
814
764
 
815
- RSpec.describe Pubid::Export::SchemeExporter, "for new_flavor" do
765
+ RSpec.describe Pubid::Export::FlavorExporter, "for new_flavor" do
816
766
  subject(:result) { described_class.new(:new_flavor).export }
817
767
 
818
768
  it "exports identifier types" do
@@ -851,14 +801,8 @@ puts auditor.summary(results)
851
801
  lib/pubid/export.rb # Module entry point
852
802
  lib/pubid/export/
853
803
  ├── result.rb # Immutable value objects (IdentifierTypeResult, TypedStageResult, FlavorResult)
854
- ├── flavor_exporter.rb # Abstract base class
855
- ├── scheme_exporter.rb # Strategy: Scheme.identifiers pattern
856
- ├── registry_exporter.rb # Strategy: TYPED_STAGES_REGISTRY pattern
857
- ├── ieee_exporter.rb # Strategy: IEEE identifier discovery
858
- ├── nist_exporter.rb # Strategy: NIST per-class typed_stages
859
- ├── itu_exporter.rb # Strategy: ITU transform/model pattern
860
- ├── data_class_exporter.rb # Strategy: Lutaml::Model::Serializable Scheme
861
- ├── exporter.rb # Orchestrator (FLAVOR_STRATEGIES dispatch)
804
+ ├── flavor_exporter.rb # Unified per-flavor extractor (WRAPPER_CLASSES dispatch)
805
+ ├── exporter.rb # Orchestrator (FLAVORS list)
862
806
  └── auditor.rb # Library vs website gap analysis
863
807
  lib/tasks/export.rake # Rake tasks: export:website_data, export:audit
864
808
  ----
@@ -1249,16 +1193,16 @@ IEEE corrigenda are now first-class SupplementIdentifier objects with full base
1249
1193
  [source,ruby]
1250
1194
  ----
1251
1195
  cor = Pubid::Ieee.parse("IEEE Std 535-2013/Cor. 1-2017")
1252
- cor.class # => Pubid::Ieee::Identifiers::Corrigendum
1253
- cor.cor_number # => "1"
1254
- cor.cor_year # => "2017"
1255
- cor.base_identifier # => <Base identifier object>
1256
- cor.base_identifier.to_s # => "IEEE Std 535-2013"
1257
- cor.to_s # => "IEEE Std 535-2013/Cor. 1-2017"
1196
+ cor.class # => Pubid::Ieee::Identifiers::Corrigendum
1197
+ cor.number # => "1"
1198
+ cor.year # => "2017"
1199
+ cor.base # => <the wrapped standard identifier>
1200
+ cor.base.to_s # => "IEEE Std 535-2013"
1201
+ cor.to_s # => "IEEE Std 535-2013/Cor. 1-2017"
1258
1202
 
1259
1203
  # Round-trip fidelity preserved
1260
1204
  cor2 = Pubid::Ieee.parse("IEEE Std 802.1AC-2016/Cor. 1-2018")
1261
- cor2.to_s # => "IEEE Std 802.1AC-2016/Cor. 1-2018"
1205
+ cor2.to_s # => "IEEE Std 802.1AC-2016/Cor. 1-2018"
1262
1206
  ----
1263
1207
 
1264
1208
  **Extended Relationship Types:**
@@ -1438,34 +1382,33 @@ ASME uses 23+ multi-character codes for specialized document types:
1438
1382
  ----
1439
1383
  require 'pubid/asme'
1440
1384
 
1441
- # Parse standard code
1385
+ # Parse standard (designator + number in one `number` string)
1442
1386
  id = Pubid::Asme.parse("ASME B16.5-2020")
1443
- id.code.designator # => "B"
1444
- id.code.number # => "16.5"
1445
- id.year # => "2020"
1446
- id.to_s # => "ASME B16.5-2020"
1387
+ id.number # => "B16.5"
1388
+ id.year # => "2020"
1389
+ id.to_s # => "ASME B16.5-2020"
1447
1390
 
1448
1391
  # Parse BPVC subdivision
1449
1392
  bpvc = Pubid::Asme.parse("ASME BPVC.III.1.NB-2021")
1450
- bpvc.code.designator # => "BPVC.III.1.NB"
1451
- bpvc.code.number # => ""
1452
- bpvc.year # => "2021"
1453
- bpvc.to_s # => "ASME BPVC.III.1.NB-2021"
1393
+ bpvc.number # => "BPVC.III.1.NB"
1394
+ bpvc.year # => "2021"
1395
+ bpvc.to_s # => "ASME BPVC.III.1.NB-2021"
1454
1396
 
1455
1397
  # Parse multi-char designator
1456
1398
  ptc = Pubid::Asme.parse("ASME PTC-1-2022")
1457
- ptc.code.designator # => "PTC"
1458
- ptc.code.number # => "1"
1459
- ptc.to_s # => "ASME PTC-1-2022"
1399
+ ptc.number # => "PTC-1"
1400
+ ptc.to_s # => "ASME PTC-1-2022"
1460
1401
 
1461
1402
  # Parse CSA dual-published
1462
1403
  dual = Pubid::Asme.parse("ASME A17.1/CSA B44-2022")
1463
- dual.code.designator # => "A"
1464
- dual.code.number # => "17.1"
1465
- dual.csa_number # => "B44"
1466
- dual.to_s # => "ASME A17.1/CSA B44-2022"
1404
+ dual.number # => "A17.1"
1405
+ dual.csa_number # => "B44"
1406
+ dual.to_s # => "ASME A17.1/CSA B44-2022"
1467
1407
  ----
1468
1408
 
1409
+ (The v1 `id.code.designator` / `id.code.number` component reader is historic;
1410
+ v2 stores the whole designation in the `number` attribute.)
1411
+
1469
1412
  **Known Limitations:**
1470
1413
  All 731 ASME identifiers are normative (from official ASME sources). Current parser handles 75.51% (552/731) with opportunities for further enhancement in specialized patterns.
1471
1414
 
@@ -1557,16 +1500,23 @@ bsi.to_s # => "BS ISO 37101:2016"
1557
1500
 
1558
1501
  # Adopted European Norm with ISO (three-level)
1559
1502
  bsi = Pubid::Bsi.parse("BS EN ISO 13485:2016+A11:2021")
1560
- bsi.publisher.body # => "BS"
1561
- bsi.adopted_identifier.publisher.body # => "EN"
1503
+ bsi.class # => Pubid::Bsi::Identifiers::ConsolidatedIdentifier
1504
+ bsi.identifiers.map(&:to_s)
1505
+ # => ["BS EN ISO 13485:2016", "BS EN ISO 13485:2016+A11:2021"]
1506
+ bsi.identifiers.first.publisher.body # => "BS"
1507
+ bsi.identifiers.first.base.adopted.to_s # => "ISO 13485:2016"
1562
1508
  # Three-level: BS wraps EN wraps ISO
1563
1509
 
1564
1510
  # National Annex with supplements
1565
1511
  na = Pubid::Bsi.parse("NA+A1:2012 to BS EN 1993-5:2007")
1566
- na.supplements.first.number # => "1"
1567
- na.supplements.first.year # => "2012"
1512
+ na.na_supplements.first.number # => "1"
1513
+ na.na_supplements.first.year # => "2012"
1568
1514
  ----
1569
1515
 
1516
+ (The v1 `adopted_identifier` reader is historic — v2 uses the uniform `base`
1517
+ parent accessor on the adopted identifier and the `identifiers` collection on
1518
+ the consolidated wrapper.)
1519
+
1570
1520
  **Features:**
1571
1521
  - Short year expansion: `A1:15` → `A1:2015`
1572
1522
  - Multiple supplement formats: `+A1:2021`, `+C1:2018`, `+A11:2021`
@@ -1621,24 +1571,29 @@ Features: EN documents, technical specifications/reports, joint committee public
1621
1571
  [source,ruby]
1622
1572
  ----
1623
1573
  # CEN Technical Specification (slash separator!)
1624
- cen = Pubid::Cen.parse("CEN/TS 14972")
1574
+ cen = Pubid::CenCenelec.parse("CEN/TS 14972")
1625
1575
  cen.to_s # => "CEN/TS 14972" (slash, not space!)
1626
1576
 
1627
1577
  # CLC Technical Report
1628
- clc = Pubid::Cen.parse("CLC/TR 62125:2008")
1578
+ clc = Pubid::CenCenelec.parse("CLC/TR 62125:2008")
1629
1579
  clc.to_s # => "CLC/TR 62125:2008"
1630
1580
 
1631
1581
  # Joint committee
1632
- joint = Pubid::Cen.parse("CEN/CLC/TR 17602-80-12:2021")
1633
- joint.publisher.copublisher # => ["CLC"]
1582
+ joint = Pubid::CenCenelec.parse("CEN/CLC/TR 17602-80-12:2021")
1583
+ joint.copublishers.map(&:to_s) # => ["CLC"]
1634
1584
  joint.to_s # => "CEN/CLC/TR 17602-80-12:2021"
1635
1585
 
1636
- # European Prestandard with ISO adoption (NEW)
1637
- env = Pubid::Cen.parse("ENV ISO 11079:1999")
1638
- env.adopted_identifier.to_s # => "ISO 11079:1999"
1586
+ # European Prestandard with ISO adoption
1587
+ env = Pubid::CenCenelec.parse("ENV ISO 11079:1999")
1588
+ env.adopted.to_s # => "ISO 11079:1999"
1639
1589
  env.to_s # => "ENV ISO 11079:1999"
1640
1590
  ----
1641
1591
 
1592
+ (The v1 `Pubid::Cen` module name is historic — CEN and CENELEC share the
1593
+ `cen_cenelec` flavor module, `Pubid::CenCenelec`; the v1
1594
+ `publisher.copublisher` and `adopted_identifier` readers are the v2
1595
+ `copublishers` collection and uniform `base`/`adopted` accessors.)
1596
+
1642
1597
  **Key Architectural Decision:** CEN uses **slash separator** between publisher and type (unlike ISO's space), implemented consistently throughout all identifier classes.
1643
1598
 
1644
1599
  ==== SAE (Society of Automotive Engineers) ✨
@@ -1864,9 +1819,9 @@ id.type.abbr # => "DATA"
1864
1819
  ----
1865
1820
  # Amendment
1866
1821
  id = Pubid::Iso.parse("ISO 19110:2005/Amd 1:2011")
1867
- id.class # => Pubid::Iso::Identifiers::Amendment
1868
- id.base_identifier.to_s # => "ISO 19110:2005"
1869
- id.number.value # => "1"
1822
+ id.class # => Pubid::Iso::Identifiers::Amendment
1823
+ id.base.to_s # => "ISO 19110:2005"
1824
+ id.number # => "1"
1870
1825
 
1871
1826
  # Staged amendment (FDAM = Final Draft Amendment)
1872
1827
  id = Pubid::Iso.parse("ISO/IEC 8802-3:2021/FDAM 1")
@@ -1879,9 +1834,9 @@ id.class # => Pubid::Iso::Identifiers::Corrigendum
1879
1834
 
1880
1835
  # Multi-level (Amendment to Amendment gets Corrigendum)
1881
1836
  id = Pubid::Iso.parse("ISO/IEC 13818-1:2015/Amd 3:2016/Cor 1:2017")
1882
- id.class # => Pubid::Iso::Identifiers::Corrigendum
1883
- id.base_identifier.class # => Pubid::Iso::Identifiers::Amendment
1884
- id.base_identifier.base_identifier.class # => Pubid::Iso::Identifiers::InternationalStandard
1837
+ id.class # => Pubid::Iso::Identifiers::Corrigendum
1838
+ id.base.class # => Pubid::Iso::Identifiers::Amendment
1839
+ id.base.base.class # => Pubid::Iso::Identifiers::InternationalStandard
1885
1840
  ----
1886
1841
 
1887
1842
  ===== Special patterns
@@ -1890,13 +1845,13 @@ id.base_identifier.base_identifier.class # => Pubid::Iso::Identifiers::Internati
1890
1845
  ----
1891
1846
  # Directives
1892
1847
  id = Pubid::Iso.parse("ISO/IEC DIR 1:2022")
1893
- id.class # => Pubid::Iso::Identifiers::Directives
1894
- id.number.value # => "1"
1848
+ id.class # => Pubid::Iso::Identifiers::Directives
1849
+ id.number # => "1"
1895
1850
 
1896
1851
  # Directives Supplement
1897
1852
  id = Pubid::Iso.parse("ISO/IEC DIR 1 ISO SUP:2022")
1898
- id.class # => Pubid::Iso::Identifiers::DirectivesSupplement
1899
- id.base_identifier.class # => Pubid::Iso::Identifiers::Directives
1853
+ id.class # => Pubid::Iso::Identifiers::DirectivesSupplement
1854
+ id.base.class # => Pubid::Iso::Identifiers::Directives
1900
1855
  id.supplement_publisher.to_s # => "ISO"
1901
1856
 
1902
1857
  # Bundled Directives (combined document + supplement)
@@ -2270,13 +2225,13 @@ rec.to_s(format: :long) # => "OIML R 138 Edition 2007 (E)"
2270
2225
 
2271
2226
  # With edition number
2272
2227
  guide = Pubid::Oiml.parse("OIML E 5 6th Edition 2015 (E)")
2273
- guide.edition # => "6"
2228
+ guide.edition # => "6th"
2274
2229
  guide.year # => "2015"
2275
2230
 
2276
2231
  # Amendment with recursive parsing
2277
2232
  amd = Pubid::Oiml.parse("Amendment (2009) to OIML R 138 Edition 2007 (E)")
2278
- amd.base_identifier.to_s # => "OIML R 138 Edition 2007 (E)"
2279
- amd.year # => "2009"
2233
+ amd.base.to_s # => "OIML R 138 Edition 2007"
2234
+ amd.year # => "2009"
2280
2235
  ----
2281
2236
 
2282
2237
  ==== CCSDS: Space Data Systems Standards
@@ -2289,43 +2244,36 @@ require 'pubid/ccsds'
2289
2244
 
2290
2245
  # Standard CCSDS document
2291
2246
  doc = Pubid::Ccsds.parse("CCSDS 727.0-B-5")
2292
- doc.code.number # => "727.0"
2293
- doc.version # => "B"
2294
- doc.revision # => "5"
2295
- doc.to_s # => "CCSDS 727.0-B-5"
2296
-
2297
- # With color code
2298
- doc = Pubid::Ccsds.parse("CCSDS 211.2-B-1 Magenta Book")
2299
- doc.color # => "Magenta"
2300
- doc.to_s # => "CCSDS 211.2-B-1 Magenta Book"
2247
+ doc.number # => "727"
2248
+ doc.part # => "0"
2249
+ doc.type # => "B"
2250
+ doc.edition # => "5"
2251
+ doc.to_s # => "CCSDS 727.0-B-5"
2301
2252
 
2302
2253
  # Corrigendum supplement (with lutaml-model)
2303
2254
  cor = Pubid::Ccsds.parse("CCSDS 727.0-B-5 Cor. 1")
2304
- cor.class # => Pubid::Ccsds::Identifiers::Corrigendum
2305
- cor.cor_number # => 1
2306
- cor.base_identifier.to_s # => "CCSDS 727.0-B-5"
2307
- cor.to_s # => "CCSDS 727.0-B-5 Cor. 1"
2308
-
2309
- # Language translation
2310
- doc = Pubid::Ccsds.parse("CCSDS 211.0-B-5 (Chinese)")
2311
- doc.language # => "Chinese"
2312
- doc.to_s # => "CCSDS 211.0-B-5 (Chinese)"
2255
+ cor.class # => Pubid::Ccsds::Identifiers::Corrigendum
2256
+ cor.number # => "1"
2257
+ cor.base.to_s # => "CCSDS 727.0-B-5"
2258
+ cor.to_s # => "CCSDS 727.0-B-5 Cor. 1"
2313
2259
  ----
2314
2260
 
2261
+ (The color-book label (`Magenta Book`) and the bracketed translation forms of
2262
+ the archived pre-monogem parser are historic — they are not part of the v2
2263
+ CCSDS grammar and no longer parse; see the archived
2264
+ link:https://github.com/pubid/pubid-ccsds#readme[pubid-ccsds README].)
2265
+
2315
2266
  **Architecture Quality:**
2316
2267
 
2317
2268
  * **Lutaml::Model refactoring** - SupplementIdentifier inherits from `Identifiers::Base`
2318
- * **Polymorphic attributes** - `attribute :base_identifier, Identifiers::Base, polymorphic: true`
2319
- * **Type safety** - Corrigendum uses `attribute :cor_number, :integer`
2320
- * ✅ **Serialization support** - Automatic JSON/YAML/XML via lutaml-model
2321
- * ✅ **Zero breaking changes** - All 16 tests passing (100%)
2269
+ * **Polymorphic attributes** - the wrapped document is the uniform polymorphic `base` attribute
2270
+ * **Type safety** - the corrigendum ordinal is the shared `number` attribute
2271
+ * ✅ **Serialization support** - round-trip via `#to_hash` / `Pubid.from_hash`
2322
2272
  * ✅ **Consistent pattern** - Matches ISO, IEC, NIST architecture exactly
2323
2273
 
2324
2274
  **Key Features:**
2325
2275
 
2326
- * Version-revision numbering (e.g., B-5 = Version B, Revision 5)
2327
- * Color book system (Green, Blue, Magenta, Yellow, Silver, Orange, Pink)
2276
+ * Version-revision numbering (e.g., B-5 = Version B, Edition 5, carried as `type` and `edition`)
2328
2277
  * Corrigenda as proper supplement identifiers with recursive base parsing
2329
- * Language translation support
2330
2278
  * Round-trip fidelity preserved
2331
2279
 
@@ -1 +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"}
1
+ {"bindings":null,"version":"0.1.0","grammar":"PubidCenPfs","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Entity":{"atom":4}},{"Str":{"pattern":"FprEN"}},{"Str":{"pattern":"prEN"}},{"Alternative":{"atoms":[1,2]}},{"Named":{"name":"stage","atom":3}},{"Str":{"pattern":" "}},{"Entity":{"atom":15}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":7,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":" "}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":10,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[9,11]}},{"Repetition":{"atom":12,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[8,13]}},{"Named":{"name":"number","atom":14}},{"Str":{"pattern":"-"}},{"Entity":{"atom":22}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Str":{"pattern":"-"}},{"Alternative":{"atoms":[18,19]}},{"Repetition":{"atom":20,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"part","atom":21}},{"Sequence":{"atoms":[16,17]}},{"Repetition":{"atom":23,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":37}},{"Str":{"pattern":" "}},{"Str":{"pattern":"V"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":28,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":31,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":34,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[26,27,29,30,32,33,35]}},{"Named":{"name":"version","atom":36}},{"Repetition":{"atom":25,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":":"}},{"Entity":{"atom":44}},{"Entity":{"atom":42}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":41,"min":4,"max":4,"tag":"Repetition"}},{"Named":{"name":"year","atom":43}},{"Sequence":{"atoms":[39,40]}},{"Repetition":{"atom":45,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[0,5,6,24,38,46]}},{"Entity":{"atom":58}},{"Str":{"pattern":"CWA"}},{"Str":{"pattern":"HD"}},{"Str":{"pattern":"ES"}},{"Str":{"pattern":"CR"}},{"Str":{"pattern":"ENV"}},{"Str":{"pattern":"EN"}},{"Str":{"pattern":"CEN"}},{"Str":{"pattern":"CLC"}},{"Alternative":{"atoms":[49,50,51,52,53,54,55,56]}},{"Named":{"name":"publisher","atom":57}},{"Entity":{"atom":68}},{"Str":{"pattern":"/"}},{"Entity":{"atom":65}},{"Str":{"pattern":"CLC"}},{"Str":{"pattern":"ETSI"}},{"Alternative":{"atoms":[62,63]}},{"Named":{"name":"copublisher","atom":64}},{"Sequence":{"atoms":[60,61]}},{"Repetition":{"atom":66,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"copublishers","atom":67}},{"Repetition":{"atom":59,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":" "}},{"Entity":{"atom":73}},{"Str":{"pattern":"Guide"}},{"Named":{"name":"guide","atom":72}},{"Str":{"pattern":" "}},{"Entity":{"atom":15}},{"Str":{"pattern":"-"}},{"Entity":{"atom":22}},{"Sequence":{"atoms":[76,77]}},{"Repetition":{"atom":78,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":":"}},{"Entity":{"atom":44}},{"Sequence":{"atoms":[80,81]}},{"Repetition":{"atom":82,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[48,69,70,71,74,75,79,83]}},{"Entity":{"atom":58}},{"Entity":{"atom":68}},{"Repetition":{"atom":86,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":" "}},{"Entity":{"atom":148}},{"Str":{"pattern":"ISO"}},{"Str":{"pattern":"IEC"}},{"Str":{"pattern":"CISPR"}},{"Alternative":{"atoms":[90,91,92]}},{"Entity":{"atom":138}},{"Entity":{"atom":122}},{"Str":{"pattern":"/"}},{"Named":{"name":"supplements_slash","atom":96}},{"Str":{"pattern":"+"}},{"Named":{"name":"supplements_plus","atom":98}},{"Alternative":{"atoms":[97,99]}},{"Str":{"pattern":"AC"}},{"Named":{"name":"supplement_kind","atom":101}},{"Entity":{"atom":106}},{"Entity":{"atom":42}},{"Repetition":{"atom":104,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"supplement_number","atom":105}},{"Repetition":{"atom":103,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":":"}},{"Entity":{"atom":112}},{"Entity":{"atom":42}},{"Repetition":{"atom":110,"min":4,"max":4,"tag":"Repetition"}},{"Named":{"name":"supplements_year","atom":111}},{"Sequence":{"atoms":[108,109]}},{"Repetition":{"atom":113,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"-"}},{"Entity":{"atom":119}},{"Entity":{"atom":42}},{"Repetition":{"atom":117,"min":2,"max":2,"tag":"Repetition"}},{"Named":{"name":"supplements_month","atom":118}},{"Sequence":{"atoms":[115,116]}},{"Repetition":{"atom":120,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[100,102,107,114,121]}},{"Entity":{"atom":136}},{"Str":{"pattern":"/"}},{"Named":{"name":"supplements_slash","atom":124}},{"Str":{"pattern":"+"}},{"Named":{"name":"supplements_plus","atom":126}},{"Alternative":{"atoms":[125,127]}},{"Str":{"pattern":"A"}},{"Named":{"name":"supplement_kind","atom":129}},{"Entity":{"atom":106}},{"Str":{"pattern":":"}},{"Entity":{"atom":112}},{"Sequence":{"atoms":[132,133]}},{"Repetition":{"atom":134,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[128,130,131,135]}},{"Alternative":{"atoms":[95,123]}},{"Named":{"name":"supplement","atom":137}},{"Lookahead":{"atom":94,"positive":false}},{"Str":{"pattern":" FRAG"}},{"Lookahead":{"atom":140,"positive":false}},{"Re":{"pattern":"[\\x01-\\x09]"}},{"Re":{"pattern":"[\\x0b-\\u{10ffff}]"}},{"Alternative":{"atoms":[142,143]}},{"Sequence":{"atoms":[139,141,144]}},{"Repetition":{"atom":145,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[93,146]}},{"Named":{"name":"adopted","atom":147}},{"Sequence":{"atoms":[85,87,88,89]}},{"Entity":{"atom":58}},{"Entity":{"atom":68}},{"Repetition":{"atom":151,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"/"}},{"Entity":{"atom":158}},{"Str":{"pattern":"TR"}},{"Str":{"pattern":"TS"}},{"Alternative":{"atoms":[155,156]}},{"Named":{"name":"type","atom":157}},{"Sequence":{"atoms":[153,154]}},{"Repetition":{"atom":159,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":" "}},{"Entity":{"atom":15}},{"Str":{"pattern":"-"}},{"Entity":{"atom":22}},{"Sequence":{"atoms":[163,164]}},{"Repetition":{"atom":165,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":37}},{"Repetition":{"atom":167,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":":"}},{"Entity":{"atom":44}},{"Sequence":{"atoms":[169,170]}},{"Repetition":{"atom":171,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[150,152,160,161,162,166,168,172]}},{"Alternative":{"atoms":[47,84,149,173]}},{"Entity":{"atom":178}},{"Entity":{"atom":138}},{"Repetition":{"atom":176,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"supplements","atom":177}},{"Str":{"pattern":" FRAG"}},{"Entity":{"atom":183}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":181,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"fragment_number","atom":182}},{"Sequence":{"atoms":[179,180]}},{"Repetition":{"atom":184,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[175,185]}},{"Repetition":{"atom":186,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[174,187]}},{"Entity":{"atom":58}},{"Entity":{"atom":68}},{"Repetition":{"atom":190,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":" "}},{"Entity":{"atom":158}},{"Str":{"pattern":" "}},{"Entity":{"atom":15}},{"Str":{"pattern":"-"}},{"Entity":{"atom":22}},{"Sequence":{"atoms":[196,197]}},{"Repetition":{"atom":198,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":37}},{"Repetition":{"atom":200,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":":"}},{"Entity":{"atom":44}},{"Sequence":{"atoms":[202,203]}},{"Repetition":{"atom":204,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":178}},{"Str":{"pattern":" FRAG"}},{"Entity":{"atom":183}},{"Sequence":{"atoms":[207,208]}},{"Repetition":{"atom":209,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[206,210]}},{"Repetition":{"atom":211,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[189,191,192,193,194,195,199,201,205,212]}},{"Alternative":{"atoms":[188,213]}}],"root":214},"bindings":[{"capture":"publisher","path":"publisher","type":"string","card":null,"preprocess":null},{"capture":"copublisher","path":"copublisher","type":"string","card":null,"preprocess":null},{"capture":"copublisher","path":"copublishers[].copublisher","type":"string","card":null,"preprocess":null},{"capture":"stage","path":"stage","type":"string","card":null,"preprocess":null},{"capture":"type","path":"type","type":"string","card":null,"preprocess":null},{"capture":"guide","path":"guide","type":"string","card":null,"preprocess":null},{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"part","path":"part","type":"string","card":null,"preprocess":null},{"capture":"version","path":"version","type":"string","card":null,"preprocess":null},{"capture":"year","path":"year","type":"string","card":null,"preprocess":null},{"capture":"adopted","path":"adopted","type":"string","card":null,"preprocess":null},{"capture":"fragment_number","path":"fragment_number","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":"EN 1325","expect":{}},{"entry":"identifier","kind":"accept","input":"EN 12464-1:2011","expect":{}},{"entry":"identifier","kind":"accept","input":"EN 302 326-2 V2.2.1:2026","expect":{}},{"entry":"identifier","kind":"accept","input":"prEN 12464-1:2019","expect":{}},{"entry":"identifier","kind":"accept","input":"FprEN 50600-2-4","expect":{}},{"entry":"identifier","kind":"accept","input":"CEN/TR 12101-5:2005","expect":{}},{"entry":"identifier","kind":"accept","input":"CEN/CLC/ETSI/TR 101551:2026","expect":{}},{"entry":"identifier","kind":"accept","input":"CLC/TS 50701:2021","expect":{}},{"entry":"identifier","kind":"accept","input":"CR 10313:2000","expect":{}},{"entry":"identifier","kind":"accept","input":"CWA 14050-21:2000","expect":{}},{"entry":"identifier","kind":"accept","input":"ES 59008-6-1:1999","expect":{}},{"entry":"identifier","kind":"accept","input":"ENV 1613:1995","expect":{}},{"entry":"identifier","kind":"accept","input":"HD 1215-2:1988","expect":{}},{"entry":"identifier","kind":"accept","input":"CEN Guide 414:2017","expect":{}},{"entry":"identifier","kind":"accept","input":"CEN/CLC Guide 25:2023","expect":{}},{"entry":"identifier","kind":"accept","input":"EN ISO 12100:2010","expect":{}},{"entry":"identifier","kind":"accept","input":"HD IEC 60364-8-81","expect":{}},{"entry":"identifier","kind":"accept","input":"EN 13250:2000/A1:2005","expect":{}},{"entry":"identifier","kind":"accept","input":"EN 60335-1:2012/A1","expect":{}},{"entry":"identifier","kind":"accept","input":"EN 13254:2000/AC:2003","expect":{}},{"entry":"identifier","kind":"accept","input":"EN 61375-2-3:2015/AC:2016-11","expect":{}},{"entry":"identifier","kind":"accept","input":"EN 285:2015+A1","expect":{}},{"entry":"identifier","kind":"accept","input":"EN 285:2015+A1:2021","expect":{}},{"entry":"identifier","kind":"accept","input":"EN ISO 10077-1:2006+AC:2009+AC2:2009","expect":{}},{"entry":"identifier","kind":"accept","input":"EN 60038/A1 FRAG2","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}},{"entry":"identifier","kind":"reject","input":"ETSI EN 300 175","expect":{}},{"entry":"identifier","kind":"reject","input":"CEN/XX 100","expect":{}},{"entry":"identifier","kind":"reject","input":"HD 1215-2:198","expect":{}},{"entry":"identifier","kind":"reject","input":"EN 300 175 V","expect":{}},{"entry":"identifier","kind":"reject","input":"EN 1325 EXTRA","expect":{}},{"entry":"identifier","kind":"reject","input":"EN A1","expect":{}},{"entry":"identifier","kind":"reject","input":"ISO 12100:2010","expect":{}},{"entry":"identifier","kind":"reject","input":"EN 1325:20000","expect":{}},{"entry":"identifier","kind":"reject","input":"EN /TR 100","expect":{}},{"entry":"identifier","kind":"reject","input":"EN 13250:2000/A1:","expect":{}},{"entry":"identifier","kind":"reject","input":"EN 13250:2000/ACX:2003","expect":{}},{"entry":"identifier","kind":"reject","input":"EN 285:2015+A1:","expect":{}},{"entry":"identifier","kind":"reject","input":"FRAG2","expect":{}},{"entry":"identifier","kind":"reject","input":"EN 13250:2000 FRAG2","expect":{}},{"entry":"identifier","kind":"example","input":"EN 1325","expect":{"publisher":"EN","number":"1325"}},{"entry":"identifier","kind":"example","input":"EN 12464-1:2011","expect":{"publisher":"EN","number":"12464","part":"1","year":"2011"}},{"entry":"identifier","kind":"example","input":"EN 302 326-2 V2.2.1:2026","expect":{"publisher":"EN","number":"302 326","part":"2","version":" V2.2.1","year":"2026"}},{"entry":"identifier","kind":"example","input":"prEN 12464-1:2019","expect":{"stage":"prEN","number":"12464","part":"1","year":"2019"}},{"entry":"identifier","kind":"example","input":"FprEN 50600-2-4","expect":{"stage":"FprEN","number":"50600","part":"2-4"}},{"entry":"identifier","kind":"example","input":"CEN/TR 12101-5:2005","expect":{"publisher":"CEN","type":"TR","number":"12101","part":"5","year":"2005"}},{"entry":"identifier","kind":"example","input":"CEN/CLC/ETSI/TR 101551:2026","expect":{"publisher":"CEN","copublisher":"CLC","type":"TR","number":"101551","year":"2026"}},{"entry":"identifier","kind":"example","input":"CLC/TS 50701:2021","expect":{"publisher":"CLC","type":"TS","number":"50701","year":"2021"}},{"entry":"identifier","kind":"example","input":"CR 10313:2000","expect":{"publisher":"CR","number":"10313","year":"2000"}},{"entry":"identifier","kind":"example","input":"CWA 14050-21:2000","expect":{"publisher":"CWA","number":"14050","part":"21","year":"2000"}},{"entry":"identifier","kind":"example","input":"ES 59008-6-1:1999","expect":{"publisher":"ES","number":"59008","part":"6-1","year":"1999"}},{"entry":"identifier","kind":"example","input":"ENV 1613:1995","expect":{"publisher":"ENV","number":"1613","year":"1995"}},{"entry":"identifier","kind":"example","input":"HD 1215-2:1988","expect":{"publisher":"HD","number":"1215","part":"2","year":"1988"}},{"entry":"identifier","kind":"example","input":"CEN Guide 414:2017","expect":{"publisher":"CEN","guide":"Guide","number":"414","year":"2017"}},{"entry":"identifier","kind":"example","input":"CEN/CLC Guide 25:2023","expect":{"publisher":"CEN","copublisher":"CLC","guide":"Guide","number":"25","year":"2023"}},{"entry":"identifier","kind":"example","input":"EN ISO 12100:2010","expect":{"publisher":"EN","adopted":"ISO 12100:2010"}},{"entry":"identifier","kind":"example","input":"HD IEC 60364-8-81","expect":{"publisher":"HD","adopted":"IEC 60364-8-81"}},{"entry":"identifier","kind":"example","input":"EN 13250:2000/A1:2005","expect":{"publisher":"EN","number":"13250","year":"2000"}},{"entry":"identifier","kind":"example","input":"EN 60335-1:2012/A1","expect":{"publisher":"EN","number":"60335","part":"1","year":"2012"}},{"entry":"identifier","kind":"example","input":"EN 13254:2000/AC:2003","expect":{"publisher":"EN","number":"13254","year":"2000"}},{"entry":"identifier","kind":"example","input":"EN 61375-2-3:2015/AC:2016-11","expect":{"publisher":"EN","number":"61375","part":"2-3","year":"2015"}},{"entry":"identifier","kind":"example","input":"EN 285:2015+A1","expect":{"publisher":"EN","number":"285","year":"2015"}},{"entry":"identifier","kind":"example","input":"EN 285:2015+A1:2021","expect":{"publisher":"EN","number":"285","year":"2015"}},{"entry":"identifier","kind":"example","input":"EN ISO 10077-1:2006+AC:2009+AC2:2009","expect":{"publisher":"EN","adopted":"ISO 10077-1:2006"}},{"entry":"identifier","kind":"example","input":"EN 60038/A1 FRAG2","expect":{"publisher":"EN","number":"60038","fragment_number":"2"}}],"docs":{},"source":"# PFS-generated grammar — DO NOT EDIT BY HAND.\n# Source: definitions/cen.pfs (pfs:1) sha256:b8f2c00e392dbc03c1fddedad141afa7c930c5430738721b7df0ac69a371d028\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\ngrammar PubidCenPfs version \"0.1.0\" edition 2 {\n\n # Grammar fragments: named lexical terms declared under `grammar:`.\n digit = %x30-39\n\n # Fields: one capture rule per declared field; the whole pattern\n # is the capture.\n cen_cenelec_publisher = ((\"CWA\" / \"HD\" / \"ES\" / \"CR\" / \"ENV\" / \"EN\" / \"CEN\" / \"CLC\")) as publisher\n cen_cenelec_copublisher = ((\"CLC\" / \"ETSI\")) as copublisher\n cen_cenelec_copublishers = (*( \"/\" cen_cenelec_copublisher )) as copublishers\n cen_cenelec_stage = ((\"FprEN\" / \"prEN\")) as stage\n cen_cenelec_type = ((\"TR\" / \"TS\")) as type\n cen_cenelec_guide = (\"Guide\") as guide\n cen_cenelec_number = (((1*%x30-39) (*(\" \" (1*%x30-39))))) as number\n cen_cenelec_part = (1*(%x30-39 / \"-\")) as part\n cen_cenelec_version = (\" \" \"V\" (1*%x30-39) \".\" (1*%x30-39) \".\" (1*%x30-39)) as version\n cen_cenelec_year = (4digit) as year\n cen_cenelec_adopted = ((((\"ISO\" / \"IEC\" / \"CISPR\")) *((!(cen_cenelec_supplement) !(\" FRAG\") (%x01-09 / %x0B-10FFFF))))) as adopted\n cen_cenelec_fragment_number = (1*%x30-39) as fragment_number\n\n # Supplement run (compiled from the supplements declaration):\n # each item binds its separator, kind face, ordinal and date\n # columns; the builder folds the run into the declared wrapper\n # classes (the slash run standalone, the plus run consolidated).\n cen_cenelec_supplement_number = (1*digit) as supplement_number\n cen_cenelec_supplements_year = (4digit) as supplements_year\n cen_cenelec_supplements_month = (2digit) as supplements_month\n cen_cenelec_supplement_corrigendum = {\n (\"/\" as supplements_slash / \"+\" as supplements_plus)\n \"AC\" as supplement_kind [cen_cenelec_supplement_number] [\":\" cen_cenelec_supplements_year] [\"-\" cen_cenelec_supplements_month]\n }\n cen_cenelec_supplement_amendment = {\n (\"/\" as supplements_slash / \"+\" as supplements_plus)\n \"A\" as supplement_kind cen_cenelec_supplement_number [\":\" cen_cenelec_supplements_year]\n }\n cen_cenelec_supplement = ( cen_cenelec_supplement_corrigendum / cen_cenelec_supplement_amendment ) as supplement\n cen_cenelec_supplements = (1*cen_cenelec_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 ( cen_cenelec_stage \" \" cen_cenelec_number [\"-\" cen_cenelec_part] [cen_cenelec_version] [\":\" cen_cenelec_year] / cen_cenelec_publisher [cen_cenelec_copublishers] \" \" cen_cenelec_guide \" \" cen_cenelec_number [\"-\" cen_cenelec_part] [\":\" cen_cenelec_year] / cen_cenelec_publisher [cen_cenelec_copublishers] \" \" cen_cenelec_adopted / cen_cenelec_publisher [cen_cenelec_copublishers] [\"/\" cen_cenelec_type] \" \" cen_cenelec_number [\"-\" cen_cenelec_part] [cen_cenelec_version] [\":\" cen_cenelec_year] ) [cen_cenelec_supplements [\" FRAG\" cen_cenelec_fragment_number]]\n / cen_cenelec_publisher [cen_cenelec_copublishers] \" \" cen_cenelec_type \" \" cen_cenelec_number [\"-\" cen_cenelec_part] [cen_cenelec_version] [\":\" cen_cenelec_year] [cen_cenelec_supplements [\" FRAG\" cen_cenelec_fragment_number]]\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 publisher -> publisher (string)\n copublisher -> copublisher (string)\n copublisher -> copublishers[].copublisher (string)\n stage -> stage (string)\n type -> type (string)\n guide -> guide (string)\n number -> number (string)\n part -> part (string)\n version -> version (string)\n year -> year (string)\n adopted -> adopted (string)\n fragment_number -> fragment_number (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 \"EN 1325\"\n accept \"EN 12464-1:2011\"\n accept \"EN 302 326-2 V2.2.1:2026\"\n accept \"prEN 12464-1:2019\"\n accept \"FprEN 50600-2-4\"\n accept \"CEN/TR 12101-5:2005\"\n accept \"CEN/CLC/ETSI/TR 101551:2026\"\n accept \"CLC/TS 50701:2021\"\n accept \"CR 10313:2000\"\n accept \"CWA 14050-21:2000\"\n accept \"ES 59008-6-1:1999\"\n accept \"ENV 1613:1995\"\n accept \"HD 1215-2:1988\"\n accept \"CEN Guide 414:2017\"\n accept \"CEN/CLC Guide 25:2023\"\n accept \"EN ISO 12100:2010\"\n accept \"HD IEC 60364-8-81\"\n accept \"EN 13250:2000/A1:2005\"\n accept \"EN 60335-1:2012/A1\"\n accept \"EN 13254:2000/AC:2003\"\n accept \"EN 61375-2-3:2015/AC:2016-11\"\n accept \"EN 285:2015+A1\"\n accept \"EN 285:2015+A1:2021\"\n accept \"EN ISO 10077-1:2006+AC:2009+AC2:2009\"\n accept \"EN 60038/A1 FRAG2\"\n reject \"nonsense\"\n reject \"ETSI EN 300 175\"\n reject \"CEN/XX 100\"\n reject \"HD 1215-2:198\"\n reject \"EN 300 175 V\"\n reject \"EN 1325 EXTRA\"\n reject \"EN A1\"\n reject \"ISO 12100:2010\"\n reject \"EN 1325:20000\"\n reject \"EN /TR 100\"\n reject \"EN 13250:2000/A1:\"\n reject \"EN 13250:2000/ACX:2003\"\n reject \"EN 285:2015+A1:\"\n reject \"FRAG2\"\n reject \"EN 13250:2000 FRAG2\"\n example \"EN 1325\" { publisher: \"EN\" number: \"1325\" }\n example \"EN 12464-1:2011\" { publisher: \"EN\" number: \"12464\" part: \"1\" year: \"2011\" }\n example \"EN 302 326-2 V2.2.1:2026\" { publisher: \"EN\" number: \"302 326\" part: \"2\" version: \" V2.2.1\" year: \"2026\" }\n example \"prEN 12464-1:2019\" { stage: \"prEN\" number: \"12464\" part: \"1\" year: \"2019\" }\n example \"FprEN 50600-2-4\" { stage: \"FprEN\" number: \"50600\" part: \"2-4\" }\n example \"CEN/TR 12101-5:2005\" { publisher: \"CEN\" type: \"TR\" number: \"12101\" part: \"5\" year: \"2005\" }\n example \"CEN/CLC/ETSI/TR 101551:2026\" { publisher: \"CEN\" copublisher: \"CLC\" type: \"TR\" number: \"101551\" year: \"2026\" }\n example \"CLC/TS 50701:2021\" { publisher: \"CLC\" type: \"TS\" number: \"50701\" year: \"2021\" }\n example \"CR 10313:2000\" { publisher: \"CR\" number: \"10313\" year: \"2000\" }\n example \"CWA 14050-21:2000\" { publisher: \"CWA\" number: \"14050\" part: \"21\" year: \"2000\" }\n example \"ES 59008-6-1:1999\" { publisher: \"ES\" number: \"59008\" part: \"6-1\" year: \"1999\" }\n example \"ENV 1613:1995\" { publisher: \"ENV\" number: \"1613\" year: \"1995\" }\n example \"HD 1215-2:1988\" { publisher: \"HD\" number: \"1215\" part: \"2\" year: \"1988\" }\n example \"CEN Guide 414:2017\" { publisher: \"CEN\" guide: \"Guide\" number: \"414\" year: \"2017\" }\n example \"CEN/CLC Guide 25:2023\" { publisher: \"CEN\" copublisher: \"CLC\" guide: \"Guide\" number: \"25\" year: \"2023\" }\n example \"EN ISO 12100:2010\" { publisher: \"EN\" adopted: \"ISO 12100:2010\" }\n example \"HD IEC 60364-8-81\" { publisher: \"HD\" adopted: \"IEC 60364-8-81\" }\n example \"EN 13250:2000/A1:2005\" { publisher: \"EN\" number: \"13250\" year: \"2000\" }\n example \"EN 60335-1:2012/A1\" { publisher: \"EN\" number: \"60335\" part: \"1\" year: \"2012\" }\n example \"EN 13254:2000/AC:2003\" { publisher: \"EN\" number: \"13254\" year: \"2000\" }\n example \"EN 61375-2-3:2015/AC:2016-11\" { publisher: \"EN\" number: \"61375\" part: \"2-3\" year: \"2015\" }\n example \"EN 285:2015+A1\" { publisher: \"EN\" number: \"285\" year: \"2015\" }\n example \"EN 285:2015+A1:2021\" { publisher: \"EN\" number: \"285\" year: \"2015\" }\n example \"EN ISO 10077-1:2006+AC:2009+AC2:2009\" { publisher: \"EN\" adopted: \"ISO 10077-1:2006\" }\n example \"EN 60038/A1 FRAG2\" { publisher: \"EN\" number: \"60038\" fragment_number: \"2\" }\n }\n}\n","source_hash":"af689f6d48bd5f2966cfe4fe6fee924e8af80fe2075cd1b5438b9e8dea1552f5","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.126","edition":2,"semantics_hash":"sha256:cd693464a12fefe5a83dd7621f635ca4f4432d520ef093fb7cc6f0f4e1d89142","checksum":"sha256:4eaf33574f00f7500da255feec6b63c0deb9705711fe6025f27d625a9026d4fb"}