pubid 2.0.20 → 2.0.22

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +73 -31
  3. data/data/iec/legacy-notation.md +298 -0
  4. data/data/iho/s4-reserved-specs.yaml +15 -0
  5. data/data/iso/historic-schemes.md +368 -0
  6. data/data/parg/bipm.json +1 -1
  7. data/data/parg/bipm.parg +46 -11
  8. data/data/parg/iec.json +1 -1
  9. data/data/parg/iho.json +1 -1
  10. data/data/parg/iho.parg +128 -11
  11. data/data/parg/iso.json +1 -1
  12. data/data/parg/iso.parg +142 -2
  13. data/data/parg/plateau.json +1 -1
  14. data/data/parg/tables/iec_typed_stages.yaml +3 -0
  15. data/data/parg/tables/typed_stages_ballots.yaml +6 -0
  16. data/data/parg/tables/typed_stages_supplements.yaml +1 -0
  17. data/lib/pubid/bipm/CLAUDE.md +6 -1
  18. data/lib/pubid/bipm/builder.rb +20 -1
  19. data/lib/pubid/bipm/renderer.rb +14 -2
  20. data/lib/pubid/bipm/urn_generator.rb +80 -14
  21. data/lib/pubid/bipm/urn_parser.rb +70 -6
  22. data/lib/pubid/ecma/CLAUDE.md +12 -0
  23. data/lib/pubid/ecma/identifier.rb +1 -2
  24. data/lib/pubid/ecma/parser.rb +49 -0
  25. data/lib/pubid/iec/CLAUDE.md +9 -1
  26. data/lib/pubid/iec/builder.rb +47 -0
  27. data/lib/pubid/iec/identifier.rb +19 -70
  28. data/lib/pubid/iec/identifiers/amendment.rb +34 -0
  29. data/lib/pubid/iec/identifiers/corrigendum.rb +6 -1
  30. data/lib/pubid/iec/identifiers/fragment_identifier.rb +3 -3
  31. data/lib/pubid/iec/single_identifier.rb +69 -4
  32. data/lib/pubid/iec/supplement_identifier.rb +89 -83
  33. data/lib/pubid/iho/CLAUDE.md +54 -1
  34. data/lib/pubid/iho/builder.rb +105 -2
  35. data/lib/pubid/iho/identifiers/base.rb +52 -1
  36. data/lib/pubid/iho/pubid_urn_schema.rb +12 -4
  37. data/lib/pubid/iho/renderer.rb +39 -2
  38. data/lib/pubid/iho/reserved_specs.rb +54 -0
  39. data/lib/pubid/iho/urn_generator.rb +6 -0
  40. data/lib/pubid/iho/urn_parser.rb +50 -9
  41. data/lib/pubid/iho.rb +1 -0
  42. data/lib/pubid/iso/CLAUDE.md +49 -0
  43. data/lib/pubid/iso/builder.rb +77 -0
  44. data/lib/pubid/iso/identifiers/amendment.rb +1 -1
  45. data/lib/pubid/iso/single_identifier.rb +7 -1
  46. data/lib/pubid/nist/CLAUDE.md +49 -0
  47. data/lib/pubid/nist/builder.rb +5 -0
  48. data/lib/pubid/nist/identifiers/base.rb +42 -1
  49. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +44 -5
  50. data/lib/pubid/nist/preprocessor.rb +18 -0
  51. data/lib/pubid/nist/urn_parser.rb +1 -1
  52. data/lib/pubid/plateau/CLAUDE.md +35 -0
  53. data/lib/pubid/plateau/builder.rb +4 -0
  54. data/lib/pubid/plateau/identifiers/handbook.rb +10 -2
  55. data/lib/pubid/plateau/pubid_urn_schema.rb +1 -0
  56. data/lib/pubid/plateau/renderer.rb +6 -1
  57. data/lib/pubid/sae/CLAUDE.md +11 -2
  58. data/lib/pubid/version.rb +1 -1
  59. metadata +7 -2
data/data/parg/iho.json CHANGED
@@ -1 +1 @@
1
- {"bindings":null,"version":"0.2.0","grammar":"PubidIho","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Str":{"pattern":"IHO"}},{"Entity":{"atom":2}},{"Str":{"pattern":" "}},{"Sequence":{"atoms":[0,1]}},{"Repetition":{"atom":3,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":12}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[6,7,8,9,10]}},{"Named":{"name":"type","atom":11}},{"Entity":{"atom":14}},{"Str":{"pattern":"-"}},{"Entity":{"atom":26}},{"Entity":{"atom":17}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":16,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":23}},{"Str":{"pattern":":"}},{"Entity":{"atom":17}},{"Repetition":{"atom":21,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[20,22]}},{"Repetition":{"atom":19,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[18,24]}},{"Named":{"name":"number","atom":25}},{"Entity":{"atom":53}},{"Str":{"pattern":"-"}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[29,30,31,32,33]}},{"Entity":{"atom":14}},{"Entity":{"atom":17}},{"Repetition":{"atom":36,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[34,35,37]}},{"Entity":{"atom":17}},{"Repetition":{"atom":39,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":46}},{"Entity":{"atom":43}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Entity":{"atom":45}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Alternative":{"atoms":[42,44]}},{"Repetition":{"atom":41,"min":0,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":47,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[40,48]}},{"Entity":{"atom":43}},{"Alternative":{"atoms":[38,49,50]}},{"Named":{"name":"part","atom":51}},{"Sequence":{"atoms":[28,52]}},{"Repetition":{"atom":27,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":57}},{"Entity":{"atom":43}},{"Named":{"name":"part","atom":56}},{"Repetition":{"atom":55,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":94}},{"Str":{"pattern":"/"}},{"Entity":{"atom":17}},{"Repetition":{"atom":61,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"volume","atom":62}},{"Entity":{"atom":92}},{"Entity":{"atom":78}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":67,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Str":{"pattern":"."}},{"Entity":{"atom":17}},{"Repetition":{"atom":71,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":74,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[68,69,72,73,75]}},{"Named":{"name":"volume_edition","atom":76}},{"Sequence":{"atoms":[66,77]}},{"Entity":{"atom":90}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[82,83]}},{"Entity":{"atom":17}},{"Repetition":{"atom":85,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[84,86]}},{"Named":{"name":"volume_year","atom":87}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[80,81,88,89]}},{"Repetition":{"atom":79,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[65,91]}},{"Repetition":{"atom":64,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[60,63,93]}},{"Repetition":{"atom":59,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":137}},{"Entity":{"atom":109}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":99,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":102,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":105,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[100,101,103,104,106]}},{"Named":{"name":"edition","atom":107}},{"Sequence":{"atoms":[98,108]}},{"Entity":{"atom":122}},{"Entity":{"atom":2}},{"Str":{"pattern":"v"}},{"Entity":{"atom":17}},{"Repetition":{"atom":113,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Entity":{"atom":17}},{"Repetition":{"atom":116,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[115,117]}},{"Repetition":{"atom":118,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[114,119]}},{"Named":{"name":"edition_shorthand","atom":120}},{"Sequence":{"atoms":[111,112,121]}},{"Alternative":{"atoms":[97,110]}},{"Entity":{"atom":135}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[127,128]}},{"Entity":{"atom":17}},{"Repetition":{"atom":130,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[129,131]}},{"Named":{"name":"year","atom":132}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[125,126,133,134]}},{"Repetition":{"atom":124,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[123,136]}},{"Repetition":{"atom":96,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":177}},{"Entity":{"atom":2}},{"Str":{"pattern":"Suppl"}},{"Str":{"pattern":"Sup"}},{"Alternative":{"atoms":[141,142]}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":145,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"supplement","atom":146}},{"Entity":{"atom":175}},{"Entity":{"atom":161}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":151,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":154,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":157,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[152,153,155,156,158]}},{"Named":{"name":"supplement_edition","atom":159}},{"Sequence":{"atoms":[150,160]}},{"Entity":{"atom":173}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[165,166]}},{"Entity":{"atom":17}},{"Repetition":{"atom":168,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[167,169]}},{"Named":{"name":"supplement_year","atom":170}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[163,164,171,172]}},{"Repetition":{"atom":162,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[149,174]}},{"Repetition":{"atom":148,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[140,143,144,147,176]}},{"Repetition":{"atom":139,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":233}},{"Entity":{"atom":2}},{"Str":{"pattern":"Part"}},{"Entity":{"atom":2}},{"Entity":{"atom":203}},{"Entity":{"atom":194}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[185,186,187,188,189]}},{"Entity":{"atom":14}},{"Entity":{"atom":17}},{"Repetition":{"atom":192,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[190,191,193]}},{"Entity":{"atom":17}},{"Repetition":{"atom":195,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":46}},{"Repetition":{"atom":197,"min":0,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":198,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[196,199]}},{"Entity":{"atom":43}},{"Alternative":{"atoms":[184,200,201]}},{"Named":{"name":"part","atom":202}},{"Entity":{"atom":231}},{"Entity":{"atom":217}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":207,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":210,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":213,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[208,209,211,212,214]}},{"Named":{"name":"part_edition","atom":215}},{"Sequence":{"atoms":[206,216]}},{"Entity":{"atom":229}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[221,222]}},{"Entity":{"atom":17}},{"Repetition":{"atom":224,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[223,225]}},{"Named":{"name":"part_year","atom":226}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[219,220,227,228]}},{"Repetition":{"atom":218,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[205,230]}},{"Repetition":{"atom":204,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[180,181,182,183,232]}},{"Repetition":{"atom":179,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":270}},{"Entity":{"atom":2}},{"Str":{"pattern":"Annex"}},{"Entity":{"atom":2}},{"Entity":{"atom":43}},{"Named":{"name":"annex","atom":239}},{"Entity":{"atom":268}},{"Entity":{"atom":254}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":244,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":247,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":250,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[245,246,248,249,251]}},{"Named":{"name":"annex_edition","atom":252}},{"Sequence":{"atoms":[243,253]}},{"Entity":{"atom":266}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[258,259]}},{"Entity":{"atom":17}},{"Repetition":{"atom":261,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[260,262]}},{"Named":{"name":"annex_year","atom":263}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[256,257,264,265]}},{"Repetition":{"atom":255,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[242,267]}},{"Repetition":{"atom":241,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[236,237,238,240,269]}},{"Repetition":{"atom":235,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":321}},{"Entity":{"atom":2}},{"Str":{"pattern":"Appendix"}},{"Str":{"pattern":"App"}},{"Str":{"pattern":"Ap."}},{"Alternative":{"atoms":[274,275,276]}},{"Entity":{"atom":2}},{"Entity":{"atom":284}},{"Entity":{"atom":43}},{"Entity":{"atom":17}},{"Repetition":{"atom":281,"min":1,"max":null,"tag":"Repetition"}},{"Alternative":{"atoms":[280,282]}},{"Named":{"name":"appendix","atom":283}},{"Entity":{"atom":290}},{"Entity":{"atom":14}},{"Entity":{"atom":17}},{"Repetition":{"atom":287,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"appendix_subnumber","atom":288}},{"Sequence":{"atoms":[286,289]}},{"Repetition":{"atom":285,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":319}},{"Entity":{"atom":305}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":295,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":298,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":70}},{"Entity":{"atom":17}},{"Repetition":{"atom":301,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[296,297,299,300,302]}},{"Named":{"name":"appendix_edition","atom":303}},{"Sequence":{"atoms":[294,304]}},{"Entity":{"atom":317}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[309,310]}},{"Entity":{"atom":17}},{"Repetition":{"atom":312,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[311,313]}},{"Named":{"name":"appendix_year","atom":314}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[307,308,315,316]}},{"Repetition":{"atom":306,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[293,318]}},{"Repetition":{"atom":292,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[273,277,278,279,291,320]}},{"Repetition":{"atom":272,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":362}},{"Entity":{"atom":348}},{"Entity":{"atom":2}},{"Str":{"pattern":"Vol."}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":328,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"volume","atom":329}},{"Entity":{"atom":346}},{"Entity":{"atom":2}},{"Str":{"pattern":"Issue"}},{"Entity":{"atom":2}},{"Entity":{"atom":17}},{"Repetition":{"atom":335,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"issue","atom":336}},{"Sequence":{"atoms":[332,333,334,337]}},{"Entity":{"atom":2}},{"Str":{"pattern":"("}},{"Entity":{"atom":17}},{"Repetition":{"atom":341,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"issue","atom":342}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[339,340,343,344]}},{"Alternative":{"atoms":[338,345]}},{"Repetition":{"atom":331,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[325,326,327,330,347]}},{"Entity":{"atom":358}},{"Entity":{"atom":2}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[351,352]}},{"Entity":{"atom":17}},{"Repetition":{"atom":354,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[353,355]}},{"Named":{"name":"volume","atom":356}},{"Sequence":{"atoms":[350,357]}},{"Alternative":{"atoms":[324,349]}},{"Entity":{"atom":92}},{"Repetition":{"atom":360,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[359,361]}},{"Repetition":{"atom":323,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":135}},{"Repetition":{"atom":364,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[4,5,13,15,54,58,95,138,178,234,271,322,363,365]}}],"root":366},"bindings":[{"capture":"type","path":"type","type":"string","card":null,"preprocess":null},{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"part","path":"part","type":"string","card":null,"preprocess":null},{"capture":"part_edition","path":"part_edition","type":"string","card":null,"preprocess":null},{"capture":"part_year","path":"part_year","type":"string","card":null,"preprocess":null},{"capture":"annex","path":"annex","type":"string","card":null,"preprocess":null},{"capture":"annex_edition","path":"annex_edition","type":"string","card":null,"preprocess":null},{"capture":"annex_year","path":"annex_year","type":"string","card":null,"preprocess":null},{"capture":"appendix","path":"appendix","type":"string","card":null,"preprocess":null},{"capture":"appendix_subnumber","path":"appendix_subnumber","type":"string","card":null,"preprocess":null},{"capture":"appendix_edition","path":"appendix_edition","type":"string","card":null,"preprocess":null},{"capture":"appendix_year","path":"appendix_year","type":"string","card":null,"preprocess":null},{"capture":"supplement","path":"supplement","type":"string","card":null,"preprocess":null},{"capture":"supplement_edition","path":"supplement_edition","type":"string","card":null,"preprocess":null},{"capture":"supplement_year","path":"supplement_year","type":"string","card":null,"preprocess":null},{"capture":"edition","path":"edition","type":"string","card":null,"preprocess":null},{"capture":"edition_shorthand","path":"edition_shorthand","type":"string","card":null,"preprocess":null},{"capture":"year","path":"year","type":"string","card":null,"preprocess":null},{"capture":"volume","path":"volume","type":"string","card":null,"preprocess":null},{"capture":"volume_edition","path":"volume_edition","type":"string","card":null,"preprocess":null},{"capture":"volume_year","path":"volume_year","type":"string","card":null,"preprocess":null},{"capture":"issue","path":"issue","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule volume_issue: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"S-5","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5 Appendix 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO B-12 3.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"P-5 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5 Vol. 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5/2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO B-7 Vol. 2023","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30 (2)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30 Issue 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1/21","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1/21 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Part 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 1.0.0 Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-65 1.1.0 Annex A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-122 1.0.0 Appendix D-2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-52 4.0.0 Appendix 1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO M-2 Sup 1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO M-2 Suppl 6","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-66 2.0.0 Sup 4","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5-B","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100-1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100-1 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6-3 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5A 1.0.2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5 1.0.2 Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Part 2 (2024)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 v5.1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 v1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 1.0.0 (2024) Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-53 Ap. 1 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 Part A 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158:100 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158:100 Part 2 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-1001","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158 Part S-102 0.2.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158-S-102 0.2.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6 1.0.0 Vol. 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6 Vol. 2 1.0.1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 1.0.0 Annex A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Sup 1 2.0.0","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO B-12 3.0","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO P-5 1850","expect":{}}],"docs":{},"source":"# Port of lib/pubid/iho/parser.rb — IHO identifiers: letter series\n# (S/P/M/B/C), a number with an optional suffix, and the full component\n# tail (edition, supplement, part, annex, appendix + subnumber, volume,\n# issue, year qualifier).\n#\n# == Component model (owner rulings, 2026-11-03)\n#\n# Every component below is a first-class semantic component: its own\n# named capture, its own typed binding, independently addressable in\n# the model, URN-encodable and matchable — never a regex/string suffix.\n#\n# component face capture binding type\n# edition x.y.z (bare) edition string\n# edition v1 / v5.1 (alias) edition_shorthand string (parse-only;\n# the builder pads to\n# x.y.0 — official IHO\n# edition is dotted\n# x.y.z WITHOUT a year)\n# supplement Sup N (Suppl N) supplement string\n# part Part X / Part S-102 part string (letter,\n# number, or SERIES\n# REFERENCE)\n# part -X / -S-102 (short) part string (hyphenated)\n# annex Annex A annex string\n# appendix Appendix B appendix string\n# appendix Appendix D-2 appendix_subnumber string (the -2)\n# volume Vol. N volume string\n# volume /N (short) volume string (the slash\n# chain, P-1/21 or\n# P-5/2020)\n# volume N (bare, legacy) volume string (the yearbook\n# year IS a volume —\n# one serial-locator\n# component)\n# issue (N) / Issue N issue string\n# year (2024) year string (the with-year\n# qualifier on\n# standards; NOT a\n# serial locator)\n#\n# == Component-edition duality (owner ruling: \"this can happen with any\n# independently publishable artifacts\")\n#\n# Supplement, part, annex, appendix and volume can be published on\n# their own publication cycle, so EACH carries an optional\n# `<kind>_edition` (+ `<kind>_year` qualifier) of its own, mutually\n# exclusive with the base edition context:\n#\n# within-edition: IHO P-6 1.0.0 Vol. 2 (the volume rides\n# the base's edition)\n# separately-published: IHO P-6 Vol. 2 1.0.1 (the volume carries\n# IHO S-98 Part A 1.0.0 its own edition;\n# IHO S-158 Part S-102 0.2.0 the base is\n# cited edition-less)\n#\n# In the URN the ed- segment attaches to the node it follows, so the\n# same positional slot records both modes.\n#\n# The part has a COMPACT face, hyphenated onto the code — `IHO S-5-B`\n# (lettered part), `IHO S-100-1` (numbered part), `IHO S-158-S-102`\n# (series-reference part) — which both parses and renders; the\n# canonical expanded face stays `IHO S-100 x.y.z Part 1`. Gluing a part\n# onto the code without a separator (S-5A) is DISALLOWED as a form: it\n# cannot extend to numbered parts without ambiguity (S-1001 = series\n# 1001 vs series 100 part 1). The four lettered bases the corpus\n# records (S-5A, S-5B, S-8A, S-8B) are LEGACY data spellings: they\n# still parse as aliases (S-5A == S-5 Part A) but are never rendered;\n# the owner rules later whether to reject them outright.\n#\n# Series-reference parts: a check standard applied to another IHO\n# series cites the applied series as its part — `IHO S-158 0.2.0 Part\n# S-102` (the wild glued `S-158-102` spelling is a legacy alias whose\n# normalization to Part S-102 needs an IHO series table; the syntax\n# alone cannot tell it from S-100-1 = Part 1, so it currently parses\n# as the plain number part).\n#\n# Two main syntaxes per the owner ruling: `IHO S-100 1.0.0` (dated =\n# edition-specific) and `IHO S-100` (undated = edition-non-specific).\n# A year beside an edition is the with-year variant qualifier\n# (`IHO S-100 1.0.0 Part X (2024)`), never a year-keyed docidentifier —\n# year-keyed docidentifiers for edition-keyed products are wrong. The\n# continuously-updated series have ONE serial locator, the volume:\n# P-1 and P-6 are volume-keyed (`IHO P-1 Vol. 30 (1)`), and the\n# yearbook year IS a volume (`IHO P-5 Vol. 2020`) — the bare year\n# (`IHO P-5 2020`) and all year-faced forms are parse-only legacy\n# aliases that bind into the volume component, never rendered.\n#\n# Aliases: \"Suppl\" (canonical face Sup, the M-2 spelling — why abbreviate\n# by two letters when Annex and Appendix stay full-length), \"App\"/\"Ap.\"\n# (canonical face Appendix), the hyphenated compact part face (S-5-B ==\n# S-5 Part B, S-100-1 == S-100 Part 1), the legacy glued lettered bases\n# (S-5A, parse-only), \"S-100 v1..v5.1\" (edition shorthand), \"Issue N\"\n# (canonical (N)), the bare year (canonical Vol. N).\n#\n# == URN segment grammar (positional, containment-ordered)\n#\n# The URN mirrors the containment tree, one semantic atom per segment:\n# Part contains Annexes; Annex contains Appendices; a D-2 subnumber is\n# a child atom of Appendix D. No key.value concatenations — every\n# non-number component is a marker-prefixed segment whose marker is the\n# lowercased human-face token (edition follows ISO's ed- precedent):\n#\n# urn:iho:<type>:<number>[:ed-<x.y.z>][:year-<yyyy>]\n# [:sup-<n>[:ed-<x.y.z>][:year-<yyyy>]]\n# [:part-<X>[:ed-<x.y.z>][:year-<yyyy>]]\n# [:annex-<A>[...]]\n# [:appendix-<B>[:<sub>][...]]\n# [:vol-<n>[:issue-<n>][...]]\n# [:year-<yyyy>]\n#\n# position segment value vocabulary containment\n# -------- --------------- -------------------------- ------------------\n# 1 <type> s / p / m / b / c namespace root\n# 2 <number> digits + : suffix; may document code\n# span bare segments until\n# the first marker\n# 3 ed-<x.y.z> dotted a.b.c edition qualifier;\n# 4 year-<yyyy> 19xx/20xx ATTACHES TO THE\n# NODE IT FOLLOWS: the\n# base (no component\n# before it) or the\n# component whose\n# segment precedes\n# 5 sup-<n> 1*digit wraps the base\n# 6 part-<X> letter (case kept), contains annexes\n# digits [*letter], or a\n# series reference\n# [SPMBC]-<digits>\n# 7 annex-<A> upper letter (case kept) contains appendices\n# 8 appendix-<B> upper letter or digits contains subnumber\n# 9 <sub> 1*digit, BARE, only child of appendix\n# directly after appendix-\n# 10 vol-<n> 1*digit (incl. yearbook contains issue\n# years, Vol. 2020)\n# 11 issue-<n> 1*digit, only after vol- child of volume\n#\n# The number occupies every leading bare segment up to the first\n# marker, so a colon number (S-158:100) round-trips without escaping;\n# after the markers the only bare segment allowed is the appendix\n# subnumber in its own slot. Both the year qualifier and the volume are\n# marker-tagged so neither can collide with a colon-number segment (the\n# #637 hazard, kept under the positional scheme). Letters are\n# case-preserved: part-A and part-a are distinct documents.\n#\n# Legacy 2.0.x spellings remain decodable but are never emitted: the\n# bare a.b.c segment (edition), the dotted keys part./annex./suppl./\n# ap./appendix./vol./issue. and the single-letter y. year tag (whose\n# value decodes into the volume — the serial-locator unification).\n#\n# Edition 2: ruby#400's capture-flatten keeps text captures over\n# reference-built rules as the matched text, so the v2 tree is the v1\n# tree plus named leaves for non-captured structure.\n\ngrammar PubidIho version \"0.2.0\" edition 2 {\n digit = %x30-39\n upper = %x41-5A\n lower = %x61-7A\n letter = upper / lower\n space = \" \"\n dash = \"-\"\n dot = \".\"\n\n series = (\"S\" / \"P\" / \"M\" / \"B\" / \"C\") as type\n number_suffix = (\":\" 1*digit)\n number = (1*digit [number_suffix]) as number\n # The compact part face, hyphenated onto the code: S-5-B (lettered),\n # S-100-1 (numbered) and S-158-S-102 (series reference) — the dash\n # replaces the old bare-glued suffix, so a number like S-1001 stays a\n # whole number (the dash is what makes the part unambiguous). The\n # dash is an inlined literal: a named dash ref here collides with the\n # sequence's own dash node in the edition-2 tree.\n compact_part = \"-\" (((\"S\" / \"P\" / \"M\" / \"B\" / \"C\") dash (1*digit)) / (1*digit [*letter]) / upper) as part\n # LEGACY glued lettered bases (S-5A, S-5B, S-8A, S-8B in the corpus):\n # parse-only aliases, never rendered. Kept because the records exist;\n # the owner rules later whether to reject them.\n glued = upper as part\n\n # The official edition is the dotted x.y.z; \"v1\"/\"v5.1\" is the S-100\n # shorthand (parse-only, the builder pads to x.y.0).\n edition = space (1*digit dot 1*digit dot 1*digit) as edition\n v_edition = space \"v\" ((1*digit) [\".\" (1*digit)]) as edition_shorthand\n # The with-year qualifier on standards: a parenthesized year beside an\n # edition. This is the ONLY base-level year capture — periodical\n # years are volumes (the serial-locator unification).\n year_qualifier = space \"(\" ((\"19\" / \"20\") 2*digit) as year \")\"\n\n # The series-reference part value: a check standard applied to\n # another IHO series (S-158 checks S-102). Ordered FIRST so \"Part\n # S-102\" takes the series-reference branch, not the lone letter.\n part_series_ref = ((\"S\" / \"P\" / \"M\" / \"B\" / \"C\") dash (1*digit))\n part_value = (part_series_ref / (1*digit [*letter]) / upper) as part\n\n # Component-edition duality: each independently-publishable component\n # may carry its own edition (+ year qualifier), mutually exclusive\n # with the base edition context. The captures are position-scoped\n # (<kind>_edition / <kind>_year) so the model knows which component\n # the edition belongs to and the URN attaches ed- to the node it\n # follows.\n part = space \"Part\" space part_value [part_dated]\n part_dated = {\n part_edition_rule\n [part_year_rule]\n }\n part_edition_rule = space (1*digit dot 1*digit dot 1*digit) as part_edition\n part_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as part_year \")\"\n\n supplement = space (\"Suppl\" / \"Sup\") space (1*digit) as supplement [supplement_dated]\n supplement_dated = {\n supplement_edition_rule\n [supplement_year_rule]\n }\n supplement_edition_rule = space (1*digit dot 1*digit dot 1*digit) as supplement_edition\n supplement_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as supplement_year \")\"\n\n annex = space \"Annex\" space upper as annex [annex_dated]\n annex_dated = {\n annex_edition_rule\n [annex_year_rule]\n }\n annex_edition_rule = space (1*digit dot 1*digit dot 1*digit) as annex_edition\n annex_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as annex_year \")\"\n\n appendix_value = (upper / (1*digit)) as appendix\n appendix_subnumber = dash (1*digit) as appendix_subnumber\n appendix = space (\"Appendix\" / \"App\" / \"Ap.\") space appendix_value [appendix_subnumber] [appendix_dated]\n appendix_dated = {\n appendix_edition_rule\n [appendix_year_rule]\n }\n appendix_edition_rule = space (1*digit dot 1*digit dot 1*digit) as appendix_edition\n appendix_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as appendix_year \")\"\n\n volume = space \"Vol.\" space (1*digit) as volume [volume_issue]\n volume_issue = (space \"Issue\" space (1*digit) as issue) / (space \"(\" (1*digit) as issue \")\")\n # The volume's short face — the slash chain (\"IHO P-1/21\",\n # \"IHO P-5/2020\"). The slash no longer joins the number.\n volume_short = \"/\" ((1*digit) as volume) [volume_dated]\n # LEGACY bare year (\"IHO P-5 2024\"): the yearbook year IS a volume,\n # so a bare year binds INTO the volume component. Parse-only alias;\n # the canonical print is \"Vol. N\".\n volume_bare_year = space ((\"19\" / \"20\") 2*digit) as volume\n # The volume's own edition (+ year), shared by the Vol. and slash\n # forms.\n volume_dated = {\n volume_edition_rule\n [volume_year_rule]\n }\n volume_edition_rule = space (1*digit dot 1*digit dot 1*digit) as volume_edition\n volume_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as volume_year \")\"\n\n # The edition slot accepts the dotted form or the v-shorthand; the\n # with-year qualifier may sit right after it.\n dated = {\n (edition / v_edition)\n [year_qualifier]\n }\n\n # The serial tail picks exactly one discriminator: the long volume\n # (with its optional issue) or the legacy bare year — both bind the\n # volume component, and both may carry the volume's own edition.\n serial = {\n (volume / volume_bare_year)\n [volume_dated]\n }\n\n identifier = {\n [(\"IHO\" space)]\n series\n dash\n number\n [compact_part]\n [glued]\n [volume_short]\n [dated]\n [supplement]\n [part]\n [annex]\n [appendix]\n [serial]\n [year_qualifier]\n }\n\n entry identifier: identifier\n\n test {\n accept \"S-5\"\n accept \"IHO S-5 Appendix 2\"\n accept \"IHO B-12 3.0.0\"\n accept \"IHO P-5 2024\"\n accept \"P-5 2024\"\n accept \"IHO P-5 Vol. 2024\"\n accept \"IHO P-5/2024\"\n accept \"IHO B-7 Vol. 2023\"\n accept \"IHO P-1 Vol. 30\"\n accept \"IHO P-1 Vol. 30 (2)\"\n accept \"IHO P-1 Vol. 30 Issue 2\"\n accept \"IHO P-1/21\"\n accept \"IHO P-1/21 1.0.0\"\n accept \"IHO S-100 5.0.0 Part 2\"\n accept \"IHO S-98 1.0.0 Part A\"\n accept \"IHO S-65 1.1.0 Annex A\"\n accept \"IHO S-122 1.0.0 Appendix D-2\"\n accept \"IHO S-52 4.0.0 Appendix 1\"\n accept \"IHO M-2 Sup 1\"\n accept \"IHO M-2 Suppl 6\"\n accept \"IHO S-66 2.0.0 Sup 4\"\n accept \"IHO S-5-B\"\n accept \"IHO S-100-1\"\n accept \"IHO S-100-1 1.0.0\"\n accept \"IHO P-6-3 1.0.0\"\n accept \"IHO S-5A\"\n accept \"IHO S-5A 1.0.2\"\n accept \"IHO S-5 1.0.2 Part A\"\n accept \"IHO S-100 5.0.0 Part 2 (2024)\"\n accept \"IHO S-100 v5.1\"\n accept \"IHO S-100 v1\"\n accept \"IHO S-100 1.0.0 (2024) Part A\"\n accept \"IHO S-53 Ap. 1 1.0.0\"\n accept \"IHO S-98 Part A 1.0.0\"\n accept \"IHO S-158:100 1.0.0\"\n accept \"IHO S-158:100 Part 2 1.0.0\"\n accept \"IHO S-1001\"\n accept \"IHO S-158 Part S-102 0.2.0\"\n accept \"IHO S-158-S-102 0.2.0\"\n accept \"IHO P-6 1.0.0 Vol. 2\"\n accept \"IHO P-6 Vol. 2 1.0.1\"\n accept \"IHO S-98 1.0.0 Annex A\"\n accept \"IHO S-100 5.0.0 Sup 1 2.0.0\"\n reject \"nonsense\"\n reject \"IHO B-12 3.0\"\n reject \"IHO P-5 1850\"\n }\n\n bindings identifier {\n type -> type (string)\n number -> number (string)\n part -> part (string)\n part_edition -> part_edition (string)\n part_year -> part_year (string)\n annex -> annex (string)\n annex_edition -> annex_edition (string)\n annex_year -> annex_year (string)\n appendix -> appendix (string)\n appendix_subnumber -> appendix_subnumber (string)\n appendix_edition -> appendix_edition (string)\n appendix_year -> appendix_year (string)\n supplement -> supplement (string)\n supplement_edition -> supplement_edition (string)\n supplement_year -> supplement_year (string)\n edition -> edition (string)\n edition_shorthand -> edition_shorthand (string)\n year -> year (string)\n volume -> volume (string)\n volume_edition -> volume_edition (string)\n volume_year -> volume_year (string)\n issue -> issue (string)\n }\n}\n","source_hash":"66d50de135e5c416bfe003de604effff57ed3b556a7126262c449b5c850995ac","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.126","edition":2,"semantics_hash":"sha256:711b011976f08ffc691672fafa41f9b60e3f6024a2608451043836fd6d05eff0","checksum":"sha256:15324490896b11703cb63893072ed77fbc43f43e47dd166ae8ddb235e3c2202e"}
1
+ {"bindings":null,"version":"0.3.0","grammar":"PubidIho","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier_entry","grammar":{"atoms":[{"Entity":{"atom":41}},{"Str":{"pattern":"IHO"}},{"Entity":{"atom":3}},{"Str":{"pattern":" "}},{"Sequence":{"atoms":[1,2]}},{"Repetition":{"atom":4,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"B"}},{"Named":{"name":"spec_part","atom":6}},{"Entity":{"atom":9}},{"Str":{"pattern":"-"}},{"Entity":{"atom":23}},{"Entity":{"atom":22}},{"Str":{"pattern":"1"}},{"Str":{"pattern":"2"}},{"Str":{"pattern":"3"}},{"Str":{"pattern":"4"}},{"Str":{"pattern":"5"}},{"Str":{"pattern":"6"}},{"Alternative":{"atoms":[12,13,14,15,16,17]}},{"Entity":{"atom":20}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Entity":{"atom":20}},{"Sequence":{"atoms":[18,19,21]}},{"Named":{"name":"spec","atom":11}},{"Entity":{"atom":30}},{"Entity":{"atom":26}},{"Str":{"pattern":"."}},{"Entity":{"atom":20}},{"Repetition":{"atom":27,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"spec_sub","atom":28}},{"Sequence":{"atoms":[25,29]}},{"Repetition":{"atom":24,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":39}},{"Str":{"pattern":"("}},{"Entity":{"atom":35}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Repetition":{"atom":34,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"spec_variant","atom":36}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[33,37,38]}},{"Repetition":{"atom":32,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[5,7,8,10,31,40]}},{"Entity":{"atom":435}},{"Str":{"pattern":"IHO"}},{"Entity":{"atom":3}},{"Sequence":{"atoms":[43,44]}},{"Repetition":{"atom":45,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":54}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[48,49,50,51,52]}},{"Named":{"name":"type","atom":53}},{"Entity":{"atom":9}},{"Entity":{"atom":66}},{"Entity":{"atom":20}},{"Repetition":{"atom":57,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":63}},{"Str":{"pattern":":"}},{"Entity":{"atom":20}},{"Repetition":{"atom":61,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[60,62]}},{"Repetition":{"atom":59,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[58,64]}},{"Named":{"name":"number","atom":65}},{"Entity":{"atom":92}},{"Str":{"pattern":"-"}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[69,70,71,72,73]}},{"Entity":{"atom":9}},{"Entity":{"atom":20}},{"Repetition":{"atom":76,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[74,75,77]}},{"Entity":{"atom":20}},{"Repetition":{"atom":79,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":85}},{"Entity":{"atom":35}},{"Entity":{"atom":84}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Alternative":{"atoms":[82,83]}},{"Repetition":{"atom":81,"min":0,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":86,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[80,87]}},{"Entity":{"atom":35}},{"Alternative":{"atoms":[78,88,89]}},{"Named":{"name":"part","atom":90}},{"Sequence":{"atoms":[68,91]}},{"Repetition":{"atom":67,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":96}},{"Entity":{"atom":35}},{"Named":{"name":"part","atom":95}},{"Repetition":{"atom":94,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":132}},{"Str":{"pattern":"/"}},{"Entity":{"atom":20}},{"Repetition":{"atom":100,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"volume","atom":101}},{"Entity":{"atom":130}},{"Entity":{"atom":116}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":106,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":109,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":112,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[107,108,110,111,113]}},{"Named":{"name":"volume_edition","atom":114}},{"Sequence":{"atoms":[105,115]}},{"Entity":{"atom":128}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[120,121]}},{"Entity":{"atom":20}},{"Repetition":{"atom":123,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[122,124]}},{"Named":{"name":"volume_year","atom":125}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[118,119,126,127]}},{"Repetition":{"atom":117,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[104,129]}},{"Repetition":{"atom":103,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[99,102,131]}},{"Repetition":{"atom":98,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":175}},{"Entity":{"atom":147}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":137,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":140,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":143,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[138,139,141,142,144]}},{"Named":{"name":"edition","atom":145}},{"Sequence":{"atoms":[136,146]}},{"Entity":{"atom":160}},{"Entity":{"atom":3}},{"Str":{"pattern":"v"}},{"Entity":{"atom":20}},{"Repetition":{"atom":151,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Entity":{"atom":20}},{"Repetition":{"atom":154,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[153,155]}},{"Repetition":{"atom":156,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[152,157]}},{"Named":{"name":"edition_shorthand","atom":158}},{"Sequence":{"atoms":[149,150,159]}},{"Alternative":{"atoms":[135,148]}},{"Entity":{"atom":173}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[165,166]}},{"Entity":{"atom":20}},{"Repetition":{"atom":168,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[167,169]}},{"Named":{"name":"year","atom":170}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[163,164,171,172]}},{"Repetition":{"atom":162,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[161,174]}},{"Repetition":{"atom":134,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":215}},{"Entity":{"atom":3}},{"Str":{"pattern":"Suppl"}},{"Str":{"pattern":"Sup"}},{"Alternative":{"atoms":[179,180]}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":183,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"supplement","atom":184}},{"Entity":{"atom":213}},{"Entity":{"atom":199}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":189,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":192,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":195,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[190,191,193,194,196]}},{"Named":{"name":"supplement_edition","atom":197}},{"Sequence":{"atoms":[188,198]}},{"Entity":{"atom":211}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[203,204]}},{"Entity":{"atom":20}},{"Repetition":{"atom":206,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[205,207]}},{"Named":{"name":"supplement_year","atom":208}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[201,202,209,210]}},{"Repetition":{"atom":200,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[187,212]}},{"Repetition":{"atom":186,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[178,181,182,185,214]}},{"Repetition":{"atom":177,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":297}},{"Entity":{"atom":3}},{"Str":{"pattern":"Part"}},{"Entity":{"atom":3}},{"Entity":{"atom":241}},{"Entity":{"atom":232}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"P"}},{"Str":{"pattern":"M"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[223,224,225,226,227]}},{"Entity":{"atom":9}},{"Entity":{"atom":20}},{"Repetition":{"atom":230,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[228,229,231]}},{"Entity":{"atom":20}},{"Repetition":{"atom":233,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":85}},{"Repetition":{"atom":235,"min":0,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":236,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[234,237]}},{"Entity":{"atom":35}},{"Alternative":{"atoms":[222,238,239]}},{"Named":{"name":"part","atom":240}},{"Entity":{"atom":269}},{"Entity":{"atom":255}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":245,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":248,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":251,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[246,247,249,250,252]}},{"Named":{"name":"part_edition","atom":253}},{"Sequence":{"atoms":[244,254]}},{"Entity":{"atom":267}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[259,260]}},{"Entity":{"atom":20}},{"Repetition":{"atom":262,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[261,263]}},{"Named":{"name":"part_year","atom":264}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[257,258,265,266]}},{"Repetition":{"atom":256,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[243,268]}},{"Repetition":{"atom":242,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":295}},{"Str":{"pattern":","}},{"Entity":{"atom":3}},{"Str":{"pattern":"Section"}},{"Entity":{"atom":3}},{"Entity":{"atom":22}},{"Named":{"name":"spec_section","atom":276}},{"Str":{"pattern":","}},{"Entity":{"atom":3}},{"Entity":{"atom":292}},{"Str":{"pattern":"A"}},{"Str":{"pattern":"B"}},{"Str":{"pattern":"C"}},{"Alternative":{"atoms":[281,282,283]}},{"Named":{"name":"spec_part","atom":284}},{"Entity":{"atom":9}},{"Entity":{"atom":23}},{"Entity":{"atom":30}},{"Repetition":{"atom":288,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":39}},{"Repetition":{"atom":290,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[285,286,287,289,291]}},{"Sequence":{"atoms":[278,279,280]}},{"Repetition":{"atom":293,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[272,273,274,275,277,294]}},{"Repetition":{"atom":271,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[218,219,220,221,270,296]}},{"Repetition":{"atom":217,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":302}},{"Entity":{"atom":3}},{"Entity":{"atom":292}},{"Sequence":{"atoms":[300,301]}},{"Repetition":{"atom":299,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":339}},{"Entity":{"atom":3}},{"Str":{"pattern":"Annex"}},{"Entity":{"atom":3}},{"Entity":{"atom":35}},{"Named":{"name":"annex","atom":308}},{"Entity":{"atom":337}},{"Entity":{"atom":323}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":313,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":316,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":319,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[314,315,317,318,320]}},{"Named":{"name":"annex_edition","atom":321}},{"Sequence":{"atoms":[312,322]}},{"Entity":{"atom":335}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[327,328]}},{"Entity":{"atom":20}},{"Repetition":{"atom":330,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[329,331]}},{"Named":{"name":"annex_year","atom":332}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[325,326,333,334]}},{"Repetition":{"atom":324,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[311,336]}},{"Repetition":{"atom":310,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[305,306,307,309,338]}},{"Repetition":{"atom":304,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":390}},{"Entity":{"atom":3}},{"Str":{"pattern":"Appendix"}},{"Str":{"pattern":"App"}},{"Str":{"pattern":"Ap."}},{"Alternative":{"atoms":[343,344,345]}},{"Entity":{"atom":3}},{"Entity":{"atom":353}},{"Entity":{"atom":35}},{"Entity":{"atom":20}},{"Repetition":{"atom":350,"min":1,"max":null,"tag":"Repetition"}},{"Alternative":{"atoms":[349,351]}},{"Named":{"name":"appendix","atom":352}},{"Entity":{"atom":359}},{"Entity":{"atom":9}},{"Entity":{"atom":20}},{"Repetition":{"atom":356,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"appendix_subnumber","atom":357}},{"Sequence":{"atoms":[355,358]}},{"Repetition":{"atom":354,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":388}},{"Entity":{"atom":374}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":364,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":367,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":26}},{"Entity":{"atom":20}},{"Repetition":{"atom":370,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[365,366,368,369,371]}},{"Named":{"name":"appendix_edition","atom":372}},{"Sequence":{"atoms":[363,373]}},{"Entity":{"atom":386}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[378,379]}},{"Entity":{"atom":20}},{"Repetition":{"atom":381,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[380,382]}},{"Named":{"name":"appendix_year","atom":383}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[376,377,384,385]}},{"Repetition":{"atom":375,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[362,387]}},{"Repetition":{"atom":361,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[342,346,347,348,360,389]}},{"Repetition":{"atom":341,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":431}},{"Entity":{"atom":417}},{"Entity":{"atom":3}},{"Str":{"pattern":"Vol."}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":397,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"volume","atom":398}},{"Entity":{"atom":415}},{"Entity":{"atom":3}},{"Str":{"pattern":"Issue"}},{"Entity":{"atom":3}},{"Entity":{"atom":20}},{"Repetition":{"atom":404,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"issue","atom":405}},{"Sequence":{"atoms":[401,402,403,406]}},{"Entity":{"atom":3}},{"Str":{"pattern":"("}},{"Entity":{"atom":20}},{"Repetition":{"atom":410,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"issue","atom":411}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[408,409,412,413]}},{"Alternative":{"atoms":[407,414]}},{"Repetition":{"atom":400,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[394,395,396,399,416]}},{"Entity":{"atom":427}},{"Entity":{"atom":3}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[420,421]}},{"Entity":{"atom":20}},{"Repetition":{"atom":423,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[422,424]}},{"Named":{"name":"volume","atom":425}},{"Sequence":{"atoms":[419,426]}},{"Alternative":{"atoms":[393,418]}},{"Entity":{"atom":130}},{"Repetition":{"atom":429,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[428,430]}},{"Repetition":{"atom":392,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":173}},{"Repetition":{"atom":433,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[46,47,55,56,93,97,133,176,216,298,303,340,391,432,434]}},{"Alternative":{"atoms":[0,42]}}],"root":436},"bindings":[{"capture":"type","path":"type","type":"string","card":null,"preprocess":null},{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"part","path":"part","type":"string","card":null,"preprocess":null},{"capture":"part_edition","path":"part_edition","type":"string","card":null,"preprocess":null},{"capture":"part_year","path":"part_year","type":"string","card":null,"preprocess":null},{"capture":"annex","path":"annex","type":"string","card":null,"preprocess":null},{"capture":"annex_edition","path":"annex_edition","type":"string","card":null,"preprocess":null},{"capture":"annex_year","path":"annex_year","type":"string","card":null,"preprocess":null},{"capture":"appendix","path":"appendix","type":"string","card":null,"preprocess":null},{"capture":"appendix_subnumber","path":"appendix_subnumber","type":"string","card":null,"preprocess":null},{"capture":"appendix_edition","path":"appendix_edition","type":"string","card":null,"preprocess":null},{"capture":"appendix_year","path":"appendix_year","type":"string","card":null,"preprocess":null},{"capture":"supplement","path":"supplement","type":"string","card":null,"preprocess":null},{"capture":"supplement_edition","path":"supplement_edition","type":"string","card":null,"preprocess":null},{"capture":"supplement_year","path":"supplement_year","type":"string","card":null,"preprocess":null},{"capture":"edition","path":"edition","type":"string","card":null,"preprocess":null},{"capture":"edition_shorthand","path":"edition_shorthand","type":"string","card":null,"preprocess":null},{"capture":"year","path":"year","type":"string","card":null,"preprocess":null},{"capture":"volume","path":"volume","type":"string","card":null,"preprocess":null},{"capture":"volume_edition","path":"volume_edition","type":"string","card":null,"preprocess":null},{"capture":"volume_year","path":"volume_year","type":"string","card":null,"preprocess":null},{"capture":"issue","path":"issue","type":"string","card":null,"preprocess":null},{"capture":"spec","path":"spec","type":"string","card":null,"preprocess":null},{"capture":"spec_sub","path":"spec_sub","type":"string","card":null,"preprocess":null},{"capture":"spec_variant","path":"spec_variant","type":"string","card":null,"preprocess":null},{"capture":"spec_part","path":"spec_part","type":"string","card":null,"preprocess":null},{"capture":"spec_section","path":"spec_section","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule volume_issue: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier_entry: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"S-5","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5 Appendix 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO B-12 3.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"P-5 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5 Vol. 2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-5/2024","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO B-7 Vol. 2023","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30 (2)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1 Vol. 30 Issue 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1/21","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-1/21 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Part 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 1.0.0 Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-65 1.1.0 Annex A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-122 1.0.0 Appendix D-2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-52 4.0.0 Appendix 1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO M-2 Sup 1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO M-2 Suppl 6","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-66 2.0.0 Sup 4","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5-B","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100-1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100-1 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6-3 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5A 1.0.2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-5 1.0.2 Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Part 2 (2024)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 v5.1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 v1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 1.0.0 (2024) Part A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-53 Ap. 1 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 Part A 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158:100 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158:100 Part 2 1.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-1001","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158 Part S-102 0.2.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-158-S-102 0.2.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6 1.0.0 Vol. 2","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO P-6 Vol. 2 1.0.1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-98 1.0.0 Annex A","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-100 5.0.0 Sup 1 2.0.0","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-401","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 4.10.0 B-401","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-122.1","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-521(I)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-351.1(I)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-400","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 B-170","expect":{}},{"entry":"identifier","kind":"accept","input":"B-401","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO B-401","expect":{}},{"entry":"identifier","kind":"accept","input":"B-122.1","expect":{}},{"entry":"identifier","kind":"accept","input":"B-521(I)","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 Part B, Section 400","expect":{}},{"entry":"identifier","kind":"accept","input":"S-4 Part B, Section 400","expect":{}},{"entry":"identifier","kind":"accept","input":"IHO S-4 Part B, Section 400, B-401","expect":{}},{"entry":"identifier","kind":"accept","input":"B-12","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO S-4 B-4","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO S-4 B-40","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO S-4 B-800","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO B-12 3.0","expect":{}},{"entry":"identifier","kind":"reject","input":"IHO P-5 1850","expect":{}}],"docs":{},"source":"# Port of lib/pubid/iho/parser.rb — IHO identifiers: letter series\n# (S/P/M/B/C), a number with an optional suffix, and the full component\n# tail (edition, supplement, part, annex, appendix + subnumber, volume,\n# issue, year qualifier).\n#\n# == Component model (owner rulings, 2026-11-03)\n#\n# Every component below is a first-class semantic component: its own\n# named capture, its own typed binding, independently addressable in\n# the model, URN-encodable and matchable — never a regex/string suffix.\n#\n# component face capture binding type\n# edition x.y.z (bare) edition string\n# edition v1 / v5.1 (alias) edition_shorthand string (parse-only;\n# the builder pads to\n# x.y.0 — official IHO\n# edition is dotted\n# x.y.z WITHOUT a year)\n# supplement Sup N (Suppl N) supplement string\n# part Part X / Part S-102 part string (letter,\n# number, or SERIES\n# REFERENCE)\n# part -X / -S-102 (short) part string (hyphenated)\n# annex Annex A annex string\n# appendix Appendix B appendix string\n# appendix Appendix D-2 appendix_subnumber string (the -2)\n# volume Vol. N volume string\n# volume /N (short) volume string (the slash\n# chain, P-1/21 or\n# P-5/2020)\n# volume N (bare, legacy) volume string (the yearbook\n# year IS a volume —\n# one serial-locator\n# component)\n# issue (N) / Issue N issue string\n# year (2024) year string (the with-year\n# qualifier on\n# standards; NOT a\n# serial locator)\n#\n# == Component-edition duality (owner ruling: \"this can happen with any\n# independently publishable artifacts\")\n#\n# Supplement, part, annex, appendix and volume can be published on\n# their own publication cycle, so EACH carries an optional\n# `<kind>_edition` (+ `<kind>_year` qualifier) of its own, mutually\n# exclusive with the base edition context:\n#\n# within-edition: IHO P-6 1.0.0 Vol. 2 (the volume rides\n# the base's edition)\n# separately-published: IHO P-6 Vol. 2 1.0.1 (the volume carries\n# IHO S-98 Part A 1.0.0 its own edition;\n# IHO S-158 Part S-102 0.2.0 the base is\n# cited edition-less)\n#\n# In the URN the ed- segment attaches to the node it follows, so the\n# same positional slot records both modes.\n#\n# The part has a COMPACT face, hyphenated onto the code — `IHO S-5-B`\n# (lettered part), `IHO S-100-1` (numbered part), `IHO S-158-S-102`\n# (series-reference part) — which both parses and renders; the\n# canonical expanded face stays `IHO S-100 x.y.z Part 1`. Gluing a part\n# onto the code without a separator (S-5A) is DISALLOWED as a form: it\n# cannot extend to numbered parts without ambiguity (S-1001 = series\n# 1001 vs series 100 part 1). The four lettered bases the corpus\n# records (S-5A, S-5B, S-8A, S-8B) are LEGACY data spellings: they\n# still parse as aliases (S-5A == S-5 Part A) but are never rendered;\n# the owner rules later whether to reject them outright.\n#\n# Series-reference parts: a check standard applied to another IHO\n# series cites the applied series as its part — `IHO S-158 0.2.0 Part\n# S-102` (the wild glued `S-158-102` spelling is a legacy alias whose\n# normalization to Part S-102 needs an IHO series table; the syntax\n# alone cannot tell it from S-100-1 = Part 1, so it currently parses\n# as the plain number part).\n#\n# Two main syntaxes per the owner ruling: `IHO S-100 1.0.0` (dated =\n# edition-specific) and `IHO S-100` (undated = edition-non-specific).\n# A year beside an edition is the with-year variant qualifier\n# (`IHO S-100 1.0.0 Part X (2024)`), never a year-keyed docidentifier —\n# year-keyed docidentifiers for edition-keyed products are wrong. The\n# continuously-updated series have ONE serial locator, the volume:\n# P-1 and P-6 are volume-keyed (`IHO P-1 Vol. 30 (1)`), and the\n# yearbook year IS a volume (`IHO P-5 Vol. 2020`) — the bare year\n# (`IHO P-5 2020`) and all year-faced forms are parse-only legacy\n# aliases that bind into the volume component, never rendered.\n#\n# Aliases: \"Suppl\" (canonical face Sup, the M-2 spelling — why abbreviate\n# by two letters when Annex and Appendix stay full-length), \"App\"/\"Ap.\"\n# (canonical face Appendix), the hyphenated compact part face (S-5-B ==\n# S-5 Part B, S-100-1 == S-100 Part 1), the legacy glued lettered bases\n# (S-5A, parse-only), \"S-100 v1..v5.1\" (edition shorthand), \"Issue N\"\n# (canonical (N)), the bare year (canonical Vol. N).\n#\n# == URN segment grammar (positional, containment-ordered)\n#\n# The URN mirrors the containment tree, one semantic atom per segment:\n# Part contains Annexes; Annex contains Appendices; a D-2 subnumber is\n# a child atom of Appendix D. No key.value concatenations — every\n# non-number component is a marker-prefixed segment whose marker is the\n# lowercased human-face token (edition follows ISO's ed- precedent).\n# An S-4 specification is a child of its Part (the citation letter rides\n# the part- atom), with the dotted sub and the parenthesized variant as\n# its own children:\n#\n# urn:iho:<type>:<number>[:ed-<x.y.z>][:year-<yyyy>]\n# [:sup-<n>[:ed-<x.y.z>][:year-<yyyy>]]\n# [:part-<X>[:ed-<x.y.z>][:year-<yyyy>]]\n# [:spec-<n>[:specsub-<n>][:specvar-<X>]]\n# [:annex-<A>[...]]\n# [:appendix-<B>[:<sub>][...]]\n# [:vol-<n>[:issue-<n>][...]]\n# [:year-<yyyy>]\n#\n# position segment value vocabulary containment\n# -------- --------------- -------------------------- ------------------\n# 1 <type> s / p / m / b / c namespace root\n# 2 <number> digits + : suffix; may document code\n# span bare segments until\n# the first marker\n# 3 ed-<x.y.z> dotted a.b.c edition qualifier;\n# 4 year-<yyyy> 19xx/20xx ATTACHES TO THE\n# NODE IT FOLLOWS: the\n# base (no component\n# before it) or the\n# component whose\n# segment precedes\n# 5 sup-<n> 1*digit wraps the base\n# 6 part-<X> letter (case kept), contains annexes\n# digits [*letter], or a and S-4 specs\n# series reference\n# [SPMBC]-<digits>\n# 7 spec-<n> [1-6]dd, only under an child of part (the\n# S-4 anchor (s:4) S-4 leaf; the\n# section is the\n# hundreds digit,\n# derivable, never\n# emitted)\n# 8 specsub-<n> 1*digit, only after spec- child of spec\n# 9 specvar-<X> upper letters, after child of spec\n# spec- or specsub-\n# 10 annex-<A> upper letter (case kept) contains appendices\n# 11 appendix-<B> upper letter or digits contains subnumber\n# 12 <sub> 1*digit, BARE, only child of appendix\n# directly after appendix-\n# 13 vol-<n> 1*digit (incl. yearbook contains issue\n# years, Vol. 2020)\n# 14 issue-<n> 1*digit, only after vol- child of volume\n#\n# The number occupies every leading bare segment up to the first\n# marker, so a colon number (S-158:100) round-trips without escaping;\n# after the markers the only bare segment allowed is the appendix\n# subnumber in its own slot. Both the year qualifier and the volume are\n# marker-tagged so neither can collide with a colon-number segment (the\n# #637 hazard, kept under the positional scheme). Letters are\n# case-preserved: part-A and part-a are distinct documents.\n#\n# Legacy 2.0.x spellings remain decodable but are never emitted: the\n# bare a.b.c segment (edition), the dotted keys part./annex./suppl./\n# ap./appendix./vol./issue. and the single-letter y. year tag (whose\n# value decodes into the volume — the serial-locator unification).\n#\n# == S-4 loose-leaf sections and specifications (issue #691; owner\n# rulings of 2026-11-11)\n#\n# The S-4 loose-leaf publication cites its regulations/specifications as\n# <letter>-<hundreds><seq>: B-401 is Part B, Section 400, item 01. ONE\n# component, not two tiers: the spec number IS the leaf; the SECTION is\n# the hundreds digit, DERIVED (never stored, never emitted). The `00`\n# slot (B-400) IS the section-level citation, so \"S-4 Part B, Section\n# 400\" and \"IHO S-4 B-400\" are one identity, two spellings.\n#\n# capture example role\n# spec B-401 -> \"401\" the leaf number (3 digits,\n# hundreds 1-6: sections 100-600)\n# spec_sub B-122.1 -> \"1\" the dotted sub\n# spec_variant B-521(I) -> \"I\" the parenthesized variant\n# spec_part B-401 -> \"B\" the citation letter (the builder\n# binds it into `part`; a Part-rule\n# capture must agree)\n# spec_section \", Section 400\" -> \"400\" the long form's spelled\n# section (becomes `spec` alone;\n# checked against a citation)\n#\n# Faces: canonical compact \"IHO S-4 B-401\" (dated: \"IHO S-4 4.10.0\n# B-401\"); long \"S-4 Part B, Section 400\" (a normalizing parse; also\n# takes a trailing citation: \"S-4 Part B, Section 400, B-401\", where the\n# spelled section only cross-checks the citation's derived section); bare\n# \"B-401\" (implicit S-4 anchor; B only — bare C-x stays the Circular\n# Letter series). The builder binds the anchor: a citation requires the\n# S-4 anchor (or synthesizes it for the bare form).\n#\n# Reserved numbers (\"NOT CURRENTLY USED\" in the S-4 contents — B-170,\n# B-230, B-305, B-323 in Ed 4.9.0 and 4.10.0) parse and resolve AS\n# RESERVED, never invalid; the edition-keyed registry lives in the pubid\n# gem (data/iho/s4-reserved-specs.yaml).\n#\n# URN atoms: part-<letter>:spec-<n>[:specsub-<n>][:specvar-<X>] — the\n# section is NOT emitted (derivable); the citation letter rides the\n# existing part- atom (case preserved: part-B).\n#\n# Edition 2: ruby#400's capture-flatten keeps text captures over\n# reference-built rules as the matched text, so the v2 tree is the v1\n# tree plus named leaves for non-captured structure.\n\ngrammar PubidIho version \"0.3.0\" edition 2 {\n digit = %x30-39\n upper = %x41-5A\n lower = %x61-7A\n letter = upper / lower\n space = \" \"\n dash = \"-\"\n dot = \".\"\n\n series = (\"S\" / \"P\" / \"M\" / \"B\" / \"C\") as type\n number_suffix = (\":\" 1*digit)\n number = (1*digit [number_suffix]) as number\n # The compact part face, hyphenated onto the code: S-5-B (lettered),\n # S-100-1 (numbered) and S-158-S-102 (series reference) — the dash\n # replaces the old bare-glued suffix, so a number like S-1001 stays a\n # whole number (the dash is what makes the part unambiguous). The\n # dash is an inlined literal: a named dash ref here collides with the\n # sequence's own dash node in the edition-2 tree.\n compact_part = \"-\" (((\"S\" / \"P\" / \"M\" / \"B\" / \"C\") dash (1*digit)) / (1*digit [*letter]) / upper) as part\n # LEGACY glued lettered bases (S-5A, S-5B, S-8A, S-8B in the corpus):\n # parse-only aliases, never rendered. Kept because the records exist;\n # the owner rules later whether to reject them.\n glued = upper as part\n\n # The official edition is the dotted x.y.z; \"v1\"/\"v5.1\" is the S-100\n # shorthand (parse-only, the builder pads to x.y.0).\n edition = space (1*digit dot 1*digit dot 1*digit) as edition\n v_edition = space \"v\" ((1*digit) [\".\" (1*digit)]) as edition_shorthand\n # The with-year qualifier on standards: a parenthesized year beside an\n # edition. This is the ONLY base-level year capture — periodical\n # years are volumes (the serial-locator unification).\n year_qualifier = space \"(\" ((\"19\" / \"20\") 2*digit) as year \")\"\n\n # The series-reference part value: a check standard applied to\n # another IHO series (S-158 checks S-102). Ordered FIRST so \"Part\n # S-102\" takes the series-reference branch, not the lone letter.\n part_series_ref = ((\"S\" / \"P\" / \"M\" / \"B\" / \"C\") dash (1*digit))\n part_value = (part_series_ref / (1*digit [*letter]) / upper) as part\n\n # Component-edition duality: each independently-publishable component\n # may carry its own edition (+ year qualifier), mutually exclusive\n # with the base edition context. The captures are position-scoped\n # (<kind>_edition / <kind>_year) so the model knows which component\n # the edition belongs to and the URN attaches ed- to the node it\n # follows.\n part = space \"Part\" space part_value [part_dated] [part_section_tail]\n part_dated = {\n part_edition_rule\n [part_year_rule]\n }\n part_edition_rule = space (1*digit dot 1*digit dot 1*digit) as part_edition\n part_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as part_year \")\"\n\n # --- S-4 loose-leaf sections and specifications (see the header) ---\n # The spec number's vocabulary: the S-4 sections run 100-600 (Part C\n # 100-500), so the leaf number is 3 digits with hundreds digit 1-6 —\n # the same shape the bare citation branch leans on to keep the B\n # Bibliographic series (B-1..B-13 style) distinct from B-4xx specs.\n spec_shape = (\"1\" / \"2\" / \"3\" / \"4\" / \"5\" / \"6\") digit digit\n spec_number = spec_shape as spec\n spec_subnumber = dot (1*digit) as spec_sub\n spec_variant = \"(\" (1*upper) as spec_variant \")\"\n # The citation letter is the spec's part (A/B/C); the builder binds it\n # into `part` and rejects a conflicting Part-rule capture.\n spec_citation_body = (\"A\" / \"B\" / \"C\") as spec_part dash spec_number [spec_subnumber] [spec_variant]\n # The compact anchor form: the citation follows the code after one\n # space (\"IHO S-4 B-401\").\n spec_citation = space spec_citation_body\n # The spelled section long form: \"Part B, Section 400\" — the spelled\n # number becomes the spec (the section-level citation), and a trailing\n # citation (\", Section 400, B-401\") makes it a cross-check only. The\n # commas are inlined literals: named comma refs collide as duplicate\n # edition-2 tree nodes under the entry's alternation (the same node\n # collision the compact_part dash-ref comment documents).\n part_section_tail = {\n \",\" space \"Section\" space (spec_shape as spec_section)\n [\",\" space spec_citation_body]\n }\n\n # The bare citation branch (the entry's FIRST alternative): \"B-401\" /\n # \"IHO B-401\" — implicit S-4 anchor (the builder synthesizes series S\n # number 4). B only: bare C-x stays the Circular Letter series, and the\n # 3-digit [1-6]xx shape keeps the B Bibliographic series (B-12) on the\n # main branch.\n bare_spec_citation = [(\"IHO\" space)] (\"B\" as spec_part) dash spec_number [spec_subnumber] [spec_variant]\n\n supplement = space (\"Suppl\" / \"Sup\") space (1*digit) as supplement [supplement_dated]\n supplement_dated = {\n supplement_edition_rule\n [supplement_year_rule]\n }\n supplement_edition_rule = space (1*digit dot 1*digit dot 1*digit) as supplement_edition\n supplement_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as supplement_year \")\"\n\n annex = space \"Annex\" space upper as annex [annex_dated]\n annex_dated = {\n annex_edition_rule\n [annex_year_rule]\n }\n annex_edition_rule = space (1*digit dot 1*digit dot 1*digit) as annex_edition\n annex_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as annex_year \")\"\n\n appendix_value = (upper / (1*digit)) as appendix\n appendix_subnumber = dash (1*digit) as appendix_subnumber\n appendix = space (\"Appendix\" / \"App\" / \"Ap.\") space appendix_value [appendix_subnumber] [appendix_dated]\n appendix_dated = {\n appendix_edition_rule\n [appendix_year_rule]\n }\n appendix_edition_rule = space (1*digit dot 1*digit dot 1*digit) as appendix_edition\n appendix_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as appendix_year \")\"\n\n volume = space \"Vol.\" space (1*digit) as volume [volume_issue]\n volume_issue = (space \"Issue\" space (1*digit) as issue) / (space \"(\" (1*digit) as issue \")\")\n # The volume's short face — the slash chain (\"IHO P-1/21\",\n # \"IHO P-5/2020\"). The slash no longer joins the number.\n volume_short = \"/\" ((1*digit) as volume) [volume_dated]\n # LEGACY bare year (\"IHO P-5 2024\"): the yearbook year IS a volume,\n # so a bare year binds INTO the volume component. Parse-only alias;\n # the canonical print is \"Vol. N\".\n volume_bare_year = space ((\"19\" / \"20\") 2*digit) as volume\n # The volume's own edition (+ year), shared by the Vol. and slash\n # forms.\n volume_dated = {\n volume_edition_rule\n [volume_year_rule]\n }\n volume_edition_rule = space (1*digit dot 1*digit dot 1*digit) as volume_edition\n volume_year_rule = space \"(\" ((\"19\" / \"20\") 2*digit) as volume_year \")\"\n\n # The edition slot accepts the dotted form or the v-shorthand; the\n # with-year qualifier may sit right after it.\n dated = {\n (edition / v_edition)\n [year_qualifier]\n }\n\n # The serial tail picks exactly one discriminator: the long volume\n # (with its optional issue) or the legacy bare year — both bind the\n # volume component, and both may carry the volume's own edition.\n serial = {\n (volume / volume_bare_year)\n [volume_dated]\n }\n\n identifier = {\n [(\"IHO\" space)]\n series\n dash\n number\n [compact_part]\n [glued]\n [volume_short]\n [dated]\n [supplement]\n [part]\n [spec_citation]\n [annex]\n [appendix]\n [serial]\n [year_qualifier]\n }\n\n # The entry dispatches the bare citation branch first (PEG order): a\n # bare \"B-401\"-shaped input takes the S-4 branch, everything else the\n # main identifier.\n identifier_entry = {\n bare_spec_citation / identifier\n }\n\n entry identifier: identifier_entry\n\n test {\n accept \"S-5\"\n accept \"IHO S-5 Appendix 2\"\n accept \"IHO B-12 3.0.0\"\n accept \"IHO P-5 2024\"\n accept \"P-5 2024\"\n accept \"IHO P-5 Vol. 2024\"\n accept \"IHO P-5/2024\"\n accept \"IHO B-7 Vol. 2023\"\n accept \"IHO P-1 Vol. 30\"\n accept \"IHO P-1 Vol. 30 (2)\"\n accept \"IHO P-1 Vol. 30 Issue 2\"\n accept \"IHO P-1/21\"\n accept \"IHO P-1/21 1.0.0\"\n accept \"IHO S-100 5.0.0 Part 2\"\n accept \"IHO S-98 1.0.0 Part A\"\n accept \"IHO S-65 1.1.0 Annex A\"\n accept \"IHO S-122 1.0.0 Appendix D-2\"\n accept \"IHO S-52 4.0.0 Appendix 1\"\n accept \"IHO M-2 Sup 1\"\n accept \"IHO M-2 Suppl 6\"\n accept \"IHO S-66 2.0.0 Sup 4\"\n accept \"IHO S-5-B\"\n accept \"IHO S-100-1\"\n accept \"IHO S-100-1 1.0.0\"\n accept \"IHO P-6-3 1.0.0\"\n accept \"IHO S-5A\"\n accept \"IHO S-5A 1.0.2\"\n accept \"IHO S-5 1.0.2 Part A\"\n accept \"IHO S-100 5.0.0 Part 2 (2024)\"\n accept \"IHO S-100 v5.1\"\n accept \"IHO S-100 v1\"\n accept \"IHO S-100 1.0.0 (2024) Part A\"\n accept \"IHO S-53 Ap. 1 1.0.0\"\n accept \"IHO S-98 Part A 1.0.0\"\n accept \"IHO S-158:100 1.0.0\"\n accept \"IHO S-158:100 Part 2 1.0.0\"\n accept \"IHO S-1001\"\n accept \"IHO S-158 Part S-102 0.2.0\"\n accept \"IHO S-158-S-102 0.2.0\"\n accept \"IHO P-6 1.0.0 Vol. 2\"\n accept \"IHO P-6 Vol. 2 1.0.1\"\n accept \"IHO S-98 1.0.0 Annex A\"\n accept \"IHO S-100 5.0.0 Sup 1 2.0.0\"\n # --- S-4 loose-leaf sections and specifications ---\n accept \"IHO S-4 B-401\"\n accept \"IHO S-4 4.10.0 B-401\"\n accept \"IHO S-4 B-122.1\"\n accept \"IHO S-4 B-521(I)\"\n accept \"IHO S-4 B-351.1(I)\"\n accept \"IHO S-4 B-400\"\n accept \"IHO S-4 B-170\"\n accept \"B-401\"\n accept \"IHO B-401\"\n accept \"B-122.1\"\n accept \"B-521(I)\"\n accept \"IHO S-4 Part B, Section 400\"\n accept \"S-4 Part B, Section 400\"\n accept \"IHO S-4 Part B, Section 400, B-401\"\n accept \"B-12\"\n reject \"IHO S-4 B-4\"\n reject \"IHO S-4 B-40\"\n reject \"IHO S-4 B-800\"\n reject \"nonsense\"\n reject \"IHO B-12 3.0\"\n reject \"IHO P-5 1850\"\n }\n\n bindings identifier_entry {\n type -> type (string)\n number -> number (string)\n part -> part (string)\n part_edition -> part_edition (string)\n part_year -> part_year (string)\n annex -> annex (string)\n annex_edition -> annex_edition (string)\n annex_year -> annex_year (string)\n appendix -> appendix (string)\n appendix_subnumber -> appendix_subnumber (string)\n appendix_edition -> appendix_edition (string)\n appendix_year -> appendix_year (string)\n supplement -> supplement (string)\n supplement_edition -> supplement_edition (string)\n supplement_year -> supplement_year (string)\n edition -> edition (string)\n edition_shorthand -> edition_shorthand (string)\n year -> year (string)\n volume -> volume (string)\n volume_edition -> volume_edition (string)\n volume_year -> volume_year (string)\n issue -> issue (string)\n spec -> spec (string)\n spec_sub -> spec_sub (string)\n spec_variant -> spec_variant (string)\n spec_part -> spec_part (string)\n spec_section -> spec_section (string)\n }\n}\n","source_hash":"2b9f7e6115555242a774cdeb356401be4085991bca0dc92fc9c8fc31ee36b842","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.126","edition":2,"semantics_hash":"sha256:eb5104d7afed4e2d261a17380e1c7ea678e354df6889cbfbbd27cd01e0442a77","checksum":"sha256:54b4f297d6fae2c2af954387ba09f87428ca42661eb170197f8791e7e3dd1c94"}
data/data/parg/iho.parg CHANGED
@@ -98,11 +98,15 @@
98
98
  # Part contains Annexes; Annex contains Appendices; a D-2 subnumber is
99
99
  # a child atom of Appendix D. No key.value concatenations — every
100
100
  # non-number component is a marker-prefixed segment whose marker is the
101
- # lowercased human-face token (edition follows ISO's ed- precedent):
101
+ # lowercased human-face token (edition follows ISO's ed- precedent).
102
+ # An S-4 specification is a child of its Part (the citation letter rides
103
+ # the part- atom), with the dotted sub and the parenthesized variant as
104
+ # its own children:
102
105
  #
103
106
  # urn:iho:<type>:<number>[:ed-<x.y.z>][:year-<yyyy>]
104
107
  # [:sup-<n>[:ed-<x.y.z>][:year-<yyyy>]]
105
108
  # [:part-<X>[:ed-<x.y.z>][:year-<yyyy>]]
109
+ # [:spec-<n>[:specsub-<n>][:specvar-<X>]]
106
110
  # [:annex-<A>[...]]
107
111
  # [:appendix-<B>[:<sub>][...]]
108
112
  # [:vol-<n>[:issue-<n>][...]]
@@ -123,16 +127,25 @@
123
127
  # segment precedes
124
128
  # 5 sup-<n> 1*digit wraps the base
125
129
  # 6 part-<X> letter (case kept), contains annexes
126
- # digits [*letter], or a
130
+ # digits [*letter], or a and S-4 specs
127
131
  # series reference
128
132
  # [SPMBC]-<digits>
129
- # 7 annex-<A> upper letter (case kept) contains appendices
130
- # 8 appendix-<B> upper letter or digits contains subnumber
131
- # 9 <sub> 1*digit, BARE, only child of appendix
133
+ # 7 spec-<n> [1-6]dd, only under an child of part (the
134
+ # S-4 anchor (s:4) S-4 leaf; the
135
+ # section is the
136
+ # hundreds digit,
137
+ # derivable, never
138
+ # emitted)
139
+ # 8 specsub-<n> 1*digit, only after spec- child of spec
140
+ # 9 specvar-<X> upper letters, after child of spec
141
+ # spec- or specsub-
142
+ # 10 annex-<A> upper letter (case kept) contains appendices
143
+ # 11 appendix-<B> upper letter or digits contains subnumber
144
+ # 12 <sub> 1*digit, BARE, only child of appendix
132
145
  # directly after appendix-
133
- # 10 vol-<n> 1*digit (incl. yearbook contains issue
146
+ # 13 vol-<n> 1*digit (incl. yearbook contains issue
134
147
  # years, Vol. 2020)
135
- # 11 issue-<n> 1*digit, only after vol- child of volume
148
+ # 14 issue-<n> 1*digit, only after vol- child of volume
136
149
  #
137
150
  # The number occupies every leading bare segment up to the first
138
151
  # marker, so a colon number (S-158:100) round-trips without escaping;
@@ -147,11 +160,50 @@
147
160
  # ap./appendix./vol./issue. and the single-letter y. year tag (whose
148
161
  # value decodes into the volume — the serial-locator unification).
149
162
  #
163
+ # == S-4 loose-leaf sections and specifications (issue #691; owner
164
+ # rulings of 2026-11-11)
165
+ #
166
+ # The S-4 loose-leaf publication cites its regulations/specifications as
167
+ # <letter>-<hundreds><seq>: B-401 is Part B, Section 400, item 01. ONE
168
+ # component, not two tiers: the spec number IS the leaf; the SECTION is
169
+ # the hundreds digit, DERIVED (never stored, never emitted). The `00`
170
+ # slot (B-400) IS the section-level citation, so "S-4 Part B, Section
171
+ # 400" and "IHO S-4 B-400" are one identity, two spellings.
172
+ #
173
+ # capture example role
174
+ # spec B-401 -> "401" the leaf number (3 digits,
175
+ # hundreds 1-6: sections 100-600)
176
+ # spec_sub B-122.1 -> "1" the dotted sub
177
+ # spec_variant B-521(I) -> "I" the parenthesized variant
178
+ # spec_part B-401 -> "B" the citation letter (the builder
179
+ # binds it into `part`; a Part-rule
180
+ # capture must agree)
181
+ # spec_section ", Section 400" -> "400" the long form's spelled
182
+ # section (becomes `spec` alone;
183
+ # checked against a citation)
184
+ #
185
+ # Faces: canonical compact "IHO S-4 B-401" (dated: "IHO S-4 4.10.0
186
+ # B-401"); long "S-4 Part B, Section 400" (a normalizing parse; also
187
+ # takes a trailing citation: "S-4 Part B, Section 400, B-401", where the
188
+ # spelled section only cross-checks the citation's derived section); bare
189
+ # "B-401" (implicit S-4 anchor; B only — bare C-x stays the Circular
190
+ # Letter series). The builder binds the anchor: a citation requires the
191
+ # S-4 anchor (or synthesizes it for the bare form).
192
+ #
193
+ # Reserved numbers ("NOT CURRENTLY USED" in the S-4 contents — B-170,
194
+ # B-230, B-305, B-323 in Ed 4.9.0 and 4.10.0) parse and resolve AS
195
+ # RESERVED, never invalid; the edition-keyed registry lives in the pubid
196
+ # gem (data/iho/s4-reserved-specs.yaml).
197
+ #
198
+ # URN atoms: part-<letter>:spec-<n>[:specsub-<n>][:specvar-<X>] — the
199
+ # section is NOT emitted (derivable); the citation letter rides the
200
+ # existing part- atom (case preserved: part-B).
201
+ #
150
202
  # Edition 2: ruby#400's capture-flatten keeps text captures over
151
203
  # reference-built rules as the matched text, so the v2 tree is the v1
152
204
  # tree plus named leaves for non-captured structure.
153
205
 
154
- grammar PubidIho version "0.2.0" edition 2 {
206
+ grammar PubidIho version "0.3.0" edition 2 {
155
207
  digit = %x30-39
156
208
  upper = %x41-5A
157
209
  lower = %x61-7A
@@ -196,7 +248,7 @@ grammar PubidIho version "0.2.0" edition 2 {
196
248
  # (<kind>_edition / <kind>_year) so the model knows which component
197
249
  # the edition belongs to and the URN attaches ed- to the node it
198
250
  # follows.
199
- part = space "Part" space part_value [part_dated]
251
+ part = space "Part" space part_value [part_dated] [part_section_tail]
200
252
  part_dated = {
201
253
  part_edition_rule
202
254
  [part_year_rule]
@@ -204,6 +256,39 @@ grammar PubidIho version "0.2.0" edition 2 {
204
256
  part_edition_rule = space (1*digit dot 1*digit dot 1*digit) as part_edition
205
257
  part_year_rule = space "(" (("19" / "20") 2*digit) as part_year ")"
206
258
 
259
+ # --- S-4 loose-leaf sections and specifications (see the header) ---
260
+ # The spec number's vocabulary: the S-4 sections run 100-600 (Part C
261
+ # 100-500), so the leaf number is 3 digits with hundreds digit 1-6 —
262
+ # the same shape the bare citation branch leans on to keep the B
263
+ # Bibliographic series (B-1..B-13 style) distinct from B-4xx specs.
264
+ spec_shape = ("1" / "2" / "3" / "4" / "5" / "6") digit digit
265
+ spec_number = spec_shape as spec
266
+ spec_subnumber = dot (1*digit) as spec_sub
267
+ spec_variant = "(" (1*upper) as spec_variant ")"
268
+ # The citation letter is the spec's part (A/B/C); the builder binds it
269
+ # into `part` and rejects a conflicting Part-rule capture.
270
+ spec_citation_body = ("A" / "B" / "C") as spec_part dash spec_number [spec_subnumber] [spec_variant]
271
+ # The compact anchor form: the citation follows the code after one
272
+ # space ("IHO S-4 B-401").
273
+ spec_citation = space spec_citation_body
274
+ # The spelled section long form: "Part B, Section 400" — the spelled
275
+ # number becomes the spec (the section-level citation), and a trailing
276
+ # citation (", Section 400, B-401") makes it a cross-check only. The
277
+ # commas are inlined literals: named comma refs collide as duplicate
278
+ # edition-2 tree nodes under the entry's alternation (the same node
279
+ # collision the compact_part dash-ref comment documents).
280
+ part_section_tail = {
281
+ "," space "Section" space (spec_shape as spec_section)
282
+ ["," space spec_citation_body]
283
+ }
284
+
285
+ # The bare citation branch (the entry's FIRST alternative): "B-401" /
286
+ # "IHO B-401" — implicit S-4 anchor (the builder synthesizes series S
287
+ # number 4). B only: bare C-x stays the Circular Letter series, and the
288
+ # 3-digit [1-6]xx shape keeps the B Bibliographic series (B-12) on the
289
+ # main branch.
290
+ bare_spec_citation = [("IHO" space)] ("B" as spec_part) dash spec_number [spec_subnumber] [spec_variant]
291
+
207
292
  supplement = space ("Suppl" / "Sup") space (1*digit) as supplement [supplement_dated]
208
293
  supplement_dated = {
209
294
  supplement_edition_rule
@@ -274,13 +359,21 @@ grammar PubidIho version "0.2.0" edition 2 {
274
359
  [dated]
275
360
  [supplement]
276
361
  [part]
362
+ [spec_citation]
277
363
  [annex]
278
364
  [appendix]
279
365
  [serial]
280
366
  [year_qualifier]
281
367
  }
282
368
 
283
- entry identifier: identifier
369
+ # The entry dispatches the bare citation branch first (PEG order): a
370
+ # bare "B-401"-shaped input takes the S-4 branch, everything else the
371
+ # main identifier.
372
+ identifier_entry = {
373
+ bare_spec_citation / identifier
374
+ }
375
+
376
+ entry identifier: identifier_entry
284
377
 
285
378
  test {
286
379
  accept "S-5"
@@ -326,12 +419,31 @@ grammar PubidIho version "0.2.0" edition 2 {
326
419
  accept "IHO P-6 Vol. 2 1.0.1"
327
420
  accept "IHO S-98 1.0.0 Annex A"
328
421
  accept "IHO S-100 5.0.0 Sup 1 2.0.0"
422
+ # --- S-4 loose-leaf sections and specifications ---
423
+ accept "IHO S-4 B-401"
424
+ accept "IHO S-4 4.10.0 B-401"
425
+ accept "IHO S-4 B-122.1"
426
+ accept "IHO S-4 B-521(I)"
427
+ accept "IHO S-4 B-351.1(I)"
428
+ accept "IHO S-4 B-400"
429
+ accept "IHO S-4 B-170"
430
+ accept "B-401"
431
+ accept "IHO B-401"
432
+ accept "B-122.1"
433
+ accept "B-521(I)"
434
+ accept "IHO S-4 Part B, Section 400"
435
+ accept "S-4 Part B, Section 400"
436
+ accept "IHO S-4 Part B, Section 400, B-401"
437
+ accept "B-12"
438
+ reject "IHO S-4 B-4"
439
+ reject "IHO S-4 B-40"
440
+ reject "IHO S-4 B-800"
329
441
  reject "nonsense"
330
442
  reject "IHO B-12 3.0"
331
443
  reject "IHO P-5 1850"
332
444
  }
333
445
 
334
- bindings identifier {
446
+ bindings identifier_entry {
335
447
  type -> type (string)
336
448
  number -> number (string)
337
449
  part -> part (string)
@@ -354,5 +466,10 @@ grammar PubidIho version "0.2.0" edition 2 {
354
466
  volume_edition -> volume_edition (string)
355
467
  volume_year -> volume_year (string)
356
468
  issue -> issue (string)
469
+ spec -> spec (string)
470
+ spec_sub -> spec_sub (string)
471
+ spec_variant -> spec_variant (string)
472
+ spec_part -> spec_part (string)
473
+ spec_section -> spec_section (string)
357
474
  }
358
475
  }