pubid 2.0.19 → 2.0.21

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/data/iho/s4-reserved-specs.yaml +15 -0
  3. data/data/parg/bipm.json +1 -1
  4. data/data/parg/bipm.parg +46 -11
  5. data/data/parg/iho.json +1 -1
  6. data/data/parg/iho.parg +128 -11
  7. data/data/parg/plateau.json +1 -1
  8. data/lib/pubid/bipm/CLAUDE.md +6 -1
  9. data/lib/pubid/bipm/builder.rb +20 -1
  10. data/lib/pubid/bipm/renderer.rb +14 -2
  11. data/lib/pubid/bipm/urn_generator.rb +80 -14
  12. data/lib/pubid/bipm/urn_parser.rb +70 -6
  13. data/lib/pubid/ecma/CLAUDE.md +12 -0
  14. data/lib/pubid/ecma/identifier.rb +1 -2
  15. data/lib/pubid/ecma/parser.rb +49 -0
  16. data/lib/pubid/identifier.rb +4 -3
  17. data/lib/pubid/iec/CLAUDE.md +20 -11
  18. data/lib/pubid/iec/builder.rb +19 -1
  19. data/lib/pubid/iec/identifier.rb +19 -70
  20. data/lib/pubid/iec/identifiers/fragment_identifier.rb +3 -3
  21. data/lib/pubid/iec/identifiers/test_report_form.rb +29 -0
  22. data/lib/pubid/iec/identifiers/working_document.rb +23 -6
  23. data/lib/pubid/iec/pubid_urn_schema.rb +15 -7
  24. data/lib/pubid/iec/renderer.rb +8 -4
  25. data/lib/pubid/iec/single_identifier.rb +69 -4
  26. data/lib/pubid/iec/supplement_identifier.rb +52 -76
  27. data/lib/pubid/iho/CLAUDE.md +52 -0
  28. data/lib/pubid/iho/builder.rb +105 -2
  29. data/lib/pubid/iho/identifiers/base.rb +52 -1
  30. data/lib/pubid/iho/pubid_urn_schema.rb +11 -4
  31. data/lib/pubid/iho/renderer.rb +39 -2
  32. data/lib/pubid/iho/reserved_specs.rb +54 -0
  33. data/lib/pubid/iho/urn_generator.rb +6 -0
  34. data/lib/pubid/iho/urn_parser.rb +50 -9
  35. data/lib/pubid/iho.rb +1 -0
  36. data/lib/pubid/nist/CLAUDE.md +11 -0
  37. data/lib/pubid/nist/preprocessor.rb +16 -0
  38. data/lib/pubid/plateau/CLAUDE.md +35 -0
  39. data/lib/pubid/plateau/builder.rb +4 -0
  40. data/lib/pubid/plateau/identifiers/handbook.rb +10 -2
  41. data/lib/pubid/plateau/pubid_urn_schema.rb +1 -0
  42. data/lib/pubid/plateau/renderer.rb +6 -1
  43. data/lib/pubid/sae/CLAUDE.md +11 -2
  44. data/lib/pubid/version.rb +1 -1
  45. metadata +18 -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
  }
@@ -1 +1 @@
1
- {"bindings":null,"version":"0.1.0","grammar":"PubidPlateau","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Entity":{"atom":69}},{"Entity":{"atom":49}},{"Str":{"pattern":"PLATEAU"}},{"Str":{"pattern":" "}},{"Entity":{"atom":8}},{"Str":{"pattern":"Handbook"}},{"Str":{"pattern":"Technical Report"}},{"Alternative":{"atoms":[5,6]}},{"Named":{"name":"type","atom":7}},{"Str":{"pattern":" "}},{"Entity":{"atom":15}},{"Str":{"pattern":"#"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":12,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"number","atom":13}},{"Sequence":{"atoms":[11,14]}},{"Entity":{"atom":23}},{"Str":{"pattern":"-"}},{"Str":{"pattern":"_"}},{"Alternative":{"atoms":[17,18]}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":20,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"annex","atom":21}},{"Sequence":{"atoms":[19,22]}},{"Repetition":{"atom":16,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":47}},{"Str":{"pattern":" "}},{"Entity":{"atom":37}},{"Str":{"pattern":"第"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":29,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":32,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[30,31,33]}},{"Named":{"name":"edition","atom":34}},{"Str":{"pattern":"版"}},{"Sequence":{"atoms":[28,35,36]}},{"Entity":{"atom":45}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":39,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":42,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[40,41,43]}},{"Named":{"name":"edition","atom":44}},{"Alternative":{"atoms":[27,38]}},{"Sequence":{"atoms":[26,46]}},{"Repetition":{"atom":25,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[2,3,4,9,10,24,48]}},{"Entity":{"atom":58}},{"Str":{"pattern":"PLATEAU"}},{"Str":{"pattern":" "}},{"Entity":{"atom":8}},{"Str":{"pattern":" "}},{"Entity":{"atom":15}},{"Entity":{"atom":23}},{"Repetition":{"atom":56,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[51,52,53,54,55,57]}},{"Alternative":{"atoms":[1,50]}},{"Named":{"name":"base","atom":59}},{"Entity":{"atom":68}},{"Str":{"pattern":" "}},{"Str":{"pattern":"Annex"}},{"Str":{"pattern":" "}},{"Entity":{"atom":67}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Named":{"name":"annex_letter","atom":66}},{"Sequence":{"atoms":[62,63,64,65]}},{"Sequence":{"atoms":[60,61]}},{"Entity":{"atom":49}},{"Entity":{"atom":58}},{"Alternative":{"atoms":[0,70,71]}}],"root":72},"bindings":[{"capture":"base","path":"base","type":"raw","card":null,"preprocess":null},{"capture":"type","path":"type","type":"string","card":null,"preprocess":null},{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"annex","path":"annex","type":"string","card":null,"preprocess":null},{"capture":"edition","path":"edition","type":"string","card":null,"preprocess":null},{"capture":"annex_letter","path":"annex_letter","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule identifier: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"PLATEAU Handbook #3","expect":{}},{"entry":"identifier","kind":"accept","input":"PLATEAU Technical Report #1","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}}],"docs":{},"source":"# Port of lib/pubid/plateau/parser.rb: PLATEAU Handbook/Technical Report\n# #NN[-annex][ edition], plus Annex supplements. Japanese canonical\n# edition (第X.Y版) and legacy Latin X.Y both capture the same atom.\n#\n# Edition 2: every reference inside a captured body is inlined to raw\n# terms (a rule reference materializes as a named node in the edition-2\n# tree, so only a raw capture binds a scalar span). The base wrapper\n# keeps the v1 tree shape and the bindings name the scalar leaves.\n\ngrammar PubidPlateau version \"0.1.0\" edition 2 {\n doc_type = (\"Handbook\" / \"Technical Report\") as type\n number = \"#\" (1*%x30-39) as number\n annex = (\"-\" / \"_\") (1*%x30-39) as annex\n edition_part = \"第\" (1*%x30-39 \".\" 1*%x30-39) as edition \"版\"\n edition_latin = (1*%x30-39 \".\" 1*%x30-39) as edition\n edition = \" \" (edition_part / edition_latin)\n annex_letter = (%x41-5A) as annex_letter\n annex_supplement = \" \" \"Annex\" \" \" annex_letter\n handbook = \"PLATEAU\" \" \" doc_type \" \" number [annex] [edition]\n technical_report = \"PLATEAU\" \" \" doc_type \" \" number [annex]\n annex_identifier = (handbook / technical_report) as base annex_supplement\n identifier = annex_identifier / handbook / technical_report\n\n entry identifier: identifier\n\n test {\n accept \"PLATEAU Handbook #3\"\n accept \"PLATEAU Technical Report #1\"\n reject \"nonsense\"\n }\n\n bindings identifier {\n # The builder dispatches on base && annex_letter and RE-parses the base\n # subtree recursively (build_annex calls build(parsed[:base])), so base\n # binds uncast — the nested {type, number, annex, edition} hash.\n base -> base (raw)\n type -> type (string)\n number -> number (string)\n annex -> annex (string)\n edition -> edition (string)\n annex_letter -> annex_letter (string)\n }\n}\n","source_hash":"c794ad3672c17b5cacdfa398ef912bc11fa61559a5dec42e38e83f85ea5a03ae","unicode_tables":"onigmo (Ruby 3.4.8)","compiler_version":"1.3.114","edition":2,"checksum":"sha256:18c528e9b4808183ec56567581aedff00d4e275644fe861b9db4a2dd2dbac3e8"}
1
+ {"bindings":null,"version":"0.1.0","grammar":"PubidPlateau","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Entity":{"atom":87}},{"Entity":{"atom":67}},{"Str":{"pattern":"PLATEAU"}},{"Str":{"pattern":" "}},{"Entity":{"atom":8}},{"Str":{"pattern":"Handbook"}},{"Str":{"pattern":"Technical Report"}},{"Alternative":{"atoms":[5,6]}},{"Named":{"name":"type","atom":7}},{"Str":{"pattern":" "}},{"Entity":{"atom":15}},{"Str":{"pattern":"#"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":12,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"number","atom":13}},{"Sequence":{"atoms":[11,14]}},{"Entity":{"atom":23}},{"Str":{"pattern":"-"}},{"Str":{"pattern":"_"}},{"Alternative":{"atoms":[17,18]}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":20,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"annex","atom":21}},{"Sequence":{"atoms":[19,22]}},{"Repetition":{"atom":16,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":47}},{"Str":{"pattern":" "}},{"Entity":{"atom":37}},{"Str":{"pattern":"第"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":29,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":32,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[30,31,33]}},{"Named":{"name":"edition","atom":34}},{"Str":{"pattern":"版"}},{"Sequence":{"atoms":[28,35,36]}},{"Entity":{"atom":45}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":39,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":42,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[40,41,43]}},{"Named":{"name":"edition","atom":44}},{"Alternative":{"atoms":[27,38]}},{"Sequence":{"atoms":[26,46]}},{"Entity":{"atom":63}},{"Entity":{"atom":52}},{"Str":{"pattern":" "}},{"Re":{"pattern":"[\\u3000]"}},{"Alternative":{"atoms":[50,51]}},{"Named":{"name":"volume_sep","atom":49}},{"Str":{"pattern":"本編"}},{"Str":{"pattern":"+"}},{"Str":{"pattern":"+"}},{"Alternative":{"atoms":[55,56]}},{"Str":{"pattern":"付録"}},{"Sequence":{"atoms":[57,58]}},{"Repetition":{"atom":59,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[54,60]}},{"Named":{"name":"volume","atom":61}},{"Sequence":{"atoms":[53,62]}},{"Repetition":{"atom":48,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[25,64]}},{"Repetition":{"atom":65,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[2,3,4,9,10,24,66]}},{"Entity":{"atom":76}},{"Str":{"pattern":"PLATEAU"}},{"Str":{"pattern":" "}},{"Entity":{"atom":8}},{"Str":{"pattern":" "}},{"Entity":{"atom":15}},{"Entity":{"atom":23}},{"Repetition":{"atom":74,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[69,70,71,72,73,75]}},{"Alternative":{"atoms":[1,68]}},{"Named":{"name":"base","atom":77}},{"Entity":{"atom":86}},{"Str":{"pattern":" "}},{"Str":{"pattern":"Annex"}},{"Str":{"pattern":" "}},{"Entity":{"atom":85}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Named":{"name":"annex_letter","atom":84}},{"Sequence":{"atoms":[80,81,82,83]}},{"Sequence":{"atoms":[78,79]}},{"Entity":{"atom":67}},{"Entity":{"atom":76}},{"Alternative":{"atoms":[0,88,89]}}],"root":90},"bindings":[{"capture":"base","path":"base","type":"raw","card":null,"preprocess":null},{"capture":"type","path":"type","type":"string","card":null,"preprocess":null},{"capture":"number","path":"number","type":"string","card":null,"preprocess":null},{"capture":"annex","path":"annex","type":"string","card":null,"preprocess":null},{"capture":"edition","path":"edition","type":"string","card":null,"preprocess":null},{"capture":"volume","path":"volume","type":"string","card":null,"preprocess":null},{"capture":"annex_letter","path":"annex_letter","type":"string","card":null,"preprocess":null}]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule identifier: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"PLATEAU Handbook #3","expect":{}},{"entry":"identifier","kind":"accept","input":"PLATEAU Technical Report #1","expect":{}},{"entry":"identifier","kind":"accept","input":"PLATEAU Handbook #00 第5.0版 本編","expect":{}},{"entry":"identifier","kind":"accept","input":"PLATEAU Handbook #00 第5.0版 本編+付録","expect":{}},{"entry":"identifier","kind":"accept","input":"PLATEAU Handbook #00 第5.0版 本編+付録","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}}],"docs":{},"source":"# Port of lib/pubid/plateau/parser.rb: PLATEAU Handbook/Technical Report\n# #NN[-annex][ edition], plus Annex supplements. Japanese canonical\n# edition (第X.Y版) and legacy Latin X.Y both capture the same atom.\n#\n# Edition 2: every reference inside a captured body is inlined to raw\n# terms (a rule reference materializes as a named node in the edition-2\n# tree, so only a raw capture binds a scalar span). The base wrapper\n# keeps the v1 tree shape and the bindings name the scalar leaves.\n\ngrammar PubidPlateau version \"0.1.0\" edition 2 {\n doc_type = (\"Handbook\" / \"Technical Report\") as type\n number = \"#\" (1*%x30-39) as number\n annex = (\"-\" / \"_\") (1*%x30-39) as annex\n edition_part = \"第\" (1*%x30-39 \".\" 1*%x30-39) as edition \"版\"\n edition_latin = (1*%x30-39 \".\" 1*%x30-39) as edition\n edition = \" \" (edition_part / edition_latin)\n # MLIT's volume label after the edition: 本編 (main volume) or 本編+付録\n # (main volume with appendix). ASCII or full-width (U+3000) space; ASCII\n # \"+\" or full-width \"+\" (U+FF0B), which the builder normalizes to \"+\".\n volume_sep = \" \" / %x3000\n volume = volume_sep (\"本編\" [(\"+\" / \"+\") \"付録\"]) as volume\n annex_letter = (%x41-5A) as annex_letter\n annex_supplement = \" \" \"Annex\" \" \" annex_letter\n handbook = \"PLATEAU\" \" \" doc_type \" \" number [annex] [edition [volume]]\n technical_report = \"PLATEAU\" \" \" doc_type \" \" number [annex]\n annex_identifier = (handbook / technical_report) as base annex_supplement\n identifier = annex_identifier / handbook / technical_report\n\n entry identifier: identifier\n\n test {\n accept \"PLATEAU Handbook #3\"\n accept \"PLATEAU Technical Report #1\"\n accept \"PLATEAU Handbook #00 第5.0版 本編\"\n accept \"PLATEAU Handbook #00 第5.0版 本編+付録\"\n accept \"PLATEAU Handbook #00 第5.0版 本編+付録\"\n reject \"nonsense\"\n }\n\n bindings identifier {\n # The builder dispatches on base && annex_letter and RE-parses the base\n # subtree recursively (build_annex calls build(parsed[:base])), so base\n # binds uncast — the nested {type, number, annex, edition} hash.\n base -> base (raw)\n type -> type (string)\n number -> number (string)\n annex -> annex (string)\n edition -> edition (string)\n volume -> volume (string)\n annex_letter -> annex_letter (string)\n }\n}\n","source_hash":"f5a43fb4d638687462490f2b05b635501ac21375c917b10e28f2cad554985476","unicode_tables":"onigmo (Ruby 3.4.10)","compiler_version":"1.3.114","edition":2,"checksum":"sha256:e876e3871cd842ded4df7a9ad906a428fbf11ab466631f489f555dd9508bef8a"}
@@ -12,7 +12,12 @@ These notes were part of the root `CLAUDE.md`. Read them before you change `lib/
12
12
 
13
13
  - **Routing reaches BIPM (`Pubid.parse`, pubid#644)**: the note above says "`Pubid.parse` auto-routing does not resolve **any** BIPM form today … so `PREFIXES` was left alone" — that is no longer true, in two halves. **(1) `PREFIXES` gained the two SI-family series tokens** `SI Brochure` and `SI MEP`: both lead real printed references on their own (`SI Brochure 9e v3.01 (2019/2024, E)` — the spelling relaton's `Bibliography#search` strips the `BIPM ` off, per the note above — and `SI MEP 10`), and before this both parsed **directly** but the router could not route them (no prefix owner ⇒ no non-exact fallback). "Rapport" stays excluded per the ambiguous-token policy (generic French word). **(2) The quoted-title form** `BIPM "The International System of Units"` — the brochure IS titled that — is mapped onto the parseable `BIPM SI Brochure` section form by **`data/bipm/update_codes.yaml`** (the CIPM-MRA precedent), so it parses as a **partial reference** that wildcards every edition, exactly like `SI Brochure Part 1`; the bare `"The International System of Units"` maps to `SI Brochure`. Both are normalizing parses and live with the loose-forms expectations, never in the byte-exact fixtures. The **French title is deliberately not mapped**: it names the F-language record specifically, and mapping it would silently drop typed language information (the loose-forms rule above). Also note the router's prefix-owner mechanism is what makes the normalizing parses routable: `Pubid::Bipm.parse("BIPM SI Brochure")` happens to round-trip exactly, but `SI Brochure 9e …` renders with the leading `BIPM ` the input lacked — without prefix-owner status the router would reject it. (hand-off: router-printed-forms-644.)
14
14
 
15
- - **Corpus-cited forms + guide printings (pubid#657)**: the metanorma-bipm fetch queue cites two spellings the grammar rejected, and the 2026-11 guide update adds printings of its own. All are **parse-in forms over the existing canonical identities** — the canonical render follows the guide, nothing that already parsed changed. **(1) The em-dash meeting variant** `CGPM -- Meeting 1 (1889)`: `meeting_en` gained `<group> -- Meeting <n>` and `<group> -- <n><ord> Meeting` branches (the corpus cites only the first; the second is the same reordering over the other word order). They build through the existing `Meeting` path, so the canonical render is `CGPM 1st Meeting (1889)`. **(2) The publisher-prefixed decision** `BIPM DECN CIPM/101-1 (2012, EN)` (and `, FR`): new `committee_prefixed` rule — a **literal `BIPM`** lead (the constant publisher, deliberately NOT the `group` table, which contains BIPM and would collide with the second `group` capture), `type_abbrev`, then the body-qualified id `{group}/{number}`. Builds through `build_committee` short-form, so it equals `CIPM DECN 101-1 (2012, E)` — verified against real `index-v2` rows in `loose_consumer_forms_spec` (the corpus's citeas comes out as the BIPM-long style, which is the consumer's rendering choice, not pubid's). **(3) The guide's French body-prefixed long form** `Décision CIPM/111-10 (2022)` / `Recommandation JCRB/43-1 (2021)`: an alternative inside `committee_long_fr` (same capture name, so the same builder path — long form, language F). **The canonical French long form stays the connective spelling** (`Décision 101-1 du CIPM (2012)`) even though the guide's language-independent example prefers the body-prefixed one — the corpus freezes the connective render for what already parsed, and flipping the canonical would move frozen cases; that flip is an owner decision if the guide is adopted wholesale. **(4) SI Brochure guide forms**: the language code and the version are now OPTIONAL (`SI Brochure 9e v3.01 (2019/2024)` language-independent, `SI Brochure 9e (2019, E)` version-less — both byte round-trips with the canonical `BIPM SI Brochure ` lead), and the guide's French lead `Brochure sur le SI 9e …` parses as an alias of the `BIPM SI Brochure sur le SI …` canonical (note the lead alternation is `"BIPM SI Brochure" / "SI Brochure" / "Brochure sur le SI"` with the `["sur le SI" space]` phrase still optional after the first two — it just fails cleanly after the third). **(5) The corpus-cited ordinal-edition spelling** `SI Brochure 9th edition (2019)`: its own `si_brochure_ordinal_edition` rule (a separate branch, because the `edition` capture would otherwise bind `"9th"`); the builder strips the ordinal and appends `e`, so it normalizes onto `BIPM SI Brochure 9e (2019)`. **(6) Part numbers** (the guide's resource-specific-id numbering, `partnumbers` proposal, bipm-si-brochure#301): parts/subparts are dash forms inside `number` (`CIPM DECN 111-10-2`) and always parsed; the appendix/annex complement `CIPM DECN 112 A3[.5]` is the new optional ` A<n>[.<m>]` tail on `committee_short`, stored in the shared `appendix`/`annex` attributes (the MEP/guide tail's home), printed back byte-exactly by `appendix_segment`, folded into the URN identifier segment as `-a<n>[.<m>]` (`urn:bipm:cipm:decn:112-a3.5:2022` — unambiguous, because a bare number never carries letters; `UrnParser#split_appendix` splits it back), and joined into the MR number segment (`mr_slug` collapses the annex dot). It requires a number before it (`CIPM DECN A3` still fails). **URN deviations from the guide, deliberate**: the guide's own committee-example URNs (`urn:bipm:v1:cipm:resolution:95-2006:1`) carry a `v1` version segment and venue-qualified shapes the flavor's URNs never adopted (`urn:bipm:cipm:res:101-1:2012`, no `v1`, no venue segments — green for years, kept byte-identical); the new forms emit URNs under the EXISTING shape for the same reason, and the same applies to the brochure (`urn:bipm:si-brochure:<lang>:<edition>:<version>:<years>` vs the guide's `urn:bipm:v1:bipm:si-brochure:9:3.01:2024` — the guide strips the `e`/`v` and drops the language segment; adopting that is an index-affecting owner decision, not taken here). **One Ruby trap the URN fold exposed**: `id.number.to_s` on a String **attribute** returns the attribute itself, so building the folded segment by `<<` mutated `number` in place — every later `to_s`/`to_hash` of the same object then carried `-a3`; the fold is now interpolation, and `urn_spec` pins that a URN emission leaves `number` untouched. **The unmerged `si-brochure-id` proposal** (`SI Brochure 9`, `SI Brochure 9 (E)`, `SI Brochure 9 (2024) (F)` — bare-number edition) is NOT implemented: it conflicts with the merged guide's `9e` spelling. (hand-off: bipm-657-corpus-forms.)
15
+ - **Corpus-cited forms + guide printings (pubid#657)**: the metanorma-bipm fetch queue cites two spellings the grammar rejected, and the 2026-11 guide update adds printings of its own. All are **parse-in forms over the existing canonical identities** — the canonical render follows the guide, nothing that already parsed changed. **(1) The em-dash meeting variant** `CGPM -- Meeting 1 (1889)`: `meeting_en` gained `<group> -- Meeting <n>` and `<group> -- <n><ord> Meeting` branches (the corpus cites only the first; the second is the same reordering over the other word order). They build through the existing `Meeting` path, so the canonical render is `CGPM 1st Meeting (1889)`. **(2) The publisher-prefixed decision** `BIPM DECN CIPM/101-1 (2012, EN)` (and `, FR`): new `committee_prefixed` rule — a **literal `BIPM`** lead (the constant publisher, deliberately NOT the `group` table, which contains BIPM and would collide with the second `group` capture), `type_abbrev`, then the body-qualified id `{group}/{number}`. Builds through `build_committee` short-form, so it equals `CIPM DECN 101-1 (2012, E)` — verified against real `index-v2` rows in `loose_consumer_forms_spec` (the corpus's citeas comes out as the BIPM-long style, which is the consumer's rendering choice, not pubid's). **(3) The guide's French body-prefixed long form** `Décision CIPM/111-10 (2022)` / `Recommandation JCRB/43-1 (2021)`: an alternative inside `committee_long_fr` (same capture name, so the same builder path — long form, language F). **The canonical French long form stays the connective spelling** (`Décision 101-1 du CIPM (2012)`) even though the guide's language-independent example prefers the body-prefixed one — the corpus freezes the connective render for what already parsed, and flipping the canonical would move frozen cases; that flip is an owner decision if the guide is adopted wholesale. **(4) SI Brochure guide forms**: the language code and the version are now OPTIONAL (`SI Brochure 9e v3.01 (2019/2024)` language-independent, `SI Brochure 9e (2019, E)` version-less — both byte round-trips with the canonical `BIPM SI Brochure ` lead), and the guide's French lead `Brochure sur le SI 9e …` parses as an alias of the `BIPM SI Brochure sur le SI …` canonical (note the lead alternation is `"BIPM SI Brochure" / "SI Brochure" / "Brochure sur le SI"` with the `["sur le SI" space]` phrase still optional after the first two — it just fails cleanly after the third). **(5) The corpus-cited ordinal-edition spelling** `SI Brochure 9th edition (2019)`: its own `si_brochure_ordinal_edition` rule (a separate branch, because the `edition` capture would otherwise bind `"9th"`); the builder strips the ordinal and appends `e`, so it normalizes onto `BIPM SI Brochure 9e (2019)`. **(6) Part numbers** (the guide's resource-specific-id numbering, `partnumbers` proposal, bipm-si-brochure#301): parts/subparts are dash forms inside `number` (`CIPM DECN 111-10-2`) and always parsed; the appendix/annex complement `CIPM DECN 112 A3[.5]` is the new optional ` A<n>[.<m>]` tail on `committee_short`, stored in the shared `appendix`/`annex` attributes (the MEP/guide tail's home), printed back byte-exactly by `appendix_segment`, folded into the URN identifier segment as `-a<n>[.<m>]` (`urn:bipm:cipm:decn:112-a3.5:2022` — unambiguous, because a bare number never carries letters; `UrnParser#split_appendix` splits it back), and joined into the MR number segment (`mr_slug` collapses the annex dot). It requires a number before it (`CIPM DECN A3` still fails). **URN deviations from the guide, deliberate**: the guide's own committee-example URNs (`urn:bipm:v1:cipm:resolution:95-2006:1`) carry a `v1` version segment and venue-qualified shapes the flavor's URNs never adopted (`urn:bipm:cipm:res:101-1:2012`, no `v1`, no venue segments — green for years, kept byte-identical); the new forms emit URNs under the EXISTING shape for the same reason, and the same applies to the brochure (`urn:bipm:si-brochure:<lang>:<edition>:<version>:<years>` vs the guide's `urn:bipm:v1:bipm:si-brochure:9:3.01:2024` — the guide strips the `e`/`v` and drops the language segment; adopting that is an index-affecting owner decision, not taken here). *(Both halves superseded by the 2026-11-11 official-URN ruling, below.)* **One Ruby trap the URN fold exposed**: `id.number.to_s` on a String **attribute** returns the attribute itself, so building the folded segment by `<<` mutated `number` in place — every later `to_s`/`to_hash` of the same object then carried `-a3`; the fold is now interpolation, and `urn_spec` pins that a URN emission leaves `number` untouched. **The unmerged `si-brochure-id` proposal** (`SI Brochure 9`, `SI Brochure 9 (E)`, `SI Brochure 9 (2024) (F)` — bare-number edition) is NOT implemented: it conflicts with the merged guide's `9e` spelling. *(Superseded — adopted as parse-in aliases by the 2026-11-11 ruling, below.)* (hand-off: bipm-657-corpus-forms.)
16
+
17
+ - **The three 2026-11-11 owner rulings (official URNs, bare-number brochure editions, French-long flip)**: the owner ratified the guide-of-record wholesale, moving the official `urn:bipm:` face for the first time (a relaton-data-bipm re-crawl follows, accepted) and flipping one canonical render.
18
+ **(R1 — the `si-brochure-id` proposal adopted as parse-in aliases.** `SI Brochure 9`, `SI Brochure 9 (E)` and `SI Brochure 9 (2024) (F)` now parse, via the `si_brochure_bare_edition` grammar rule (the `si_brochure_ordinal_edition` model: a separate branch because the `edition` capture would bind the bare digit without the canonical `e`; the year and the language may each sit in their own parentheses, exactly the proposal's spellings). The builder appends the `e` — `9` → `9e`, the same normalization the ordinal spelling takes — so all three land on `BIPM SI Brochure 9e …`. **Two grammar/render consequences worth recording**: the canonical render of a year-less alias (`BIPM SI Brochure 9e`) must re-parse, so the brochure parenthetical is now OPTIONAL and accepts a **language-only paren** (`(E)` with no year — the main `si_brochure` rule's second alternative); and the renderer's paren segment had to stop printing unconditionally, because a nil `years` beside a language rendered the garbage `(, E)` — a live bug the year-less ordinal spelling already had on main (`SI Brochure 9th edition` → `BIPM SI Brochure 9e ()`). The paren now carries the year, the language or both, and stays off entirely when neither is named. Alias expectations live ONLY in `loose_consumer_forms_spec` (the ruling restates the normalizing-parses-never-in-pass rule); the three canonical faces gained byte-exact fixture lines.
19
+ **(R2 — the guide's official-URN shapes adopted.** Every `to_urn` now emits the `v1` face: committee `urn:bipm:v1:{group}:{type-word}:{identifier}[:{year}]` (the guide's `urn:bipm:v1:cipm:decision:110-17:2021` verbatim; the type segment is the lowercase ENGLISH NAME — recommendation/resolution/decision/action/statement — never the REC/RES/DECN abbreviation), meeting `urn:bipm:v1:{group}:meeting:{n}[:{year}]` (venue-type + venue-id as separate segments per the guide's component list; the proceedings example glues them as `meeting-26` and carries a publication year the flavor does not model), metrologia unchanged-shape-plus-`v1` (the guide's metrologia URN section is empty — as are MEP/Guide, which still raise: no landed URN, nothing to move), brochure `urn:bipm:v1:bipm:si-brochure:{edition-no-e}:{version-no-v}:{latest-year}` (the guide's pattern verbatim: language segment dropped, `9e`→`9`, `v3.01`→`3.01`, `2019/2024`→`2024` — the guide's "latest year of publication at the version"; the version segment stays empty when absent, per the guide's `…si-brochure:9::2019`). **Two guide examples do not fit the guide's own stated pattern** and are documented, not reproduced: `urn:bipm:v1:cipm:resolution:95-2006:1` puts a venue-id and the outcome ordinal where the pattern's identifier/date slots sit (its own git history edited it there from `95-2006:resolution:1`, which fit), and `urn:bipm:v1:cgpm:26:2018:resolution:1` carries the CGPM meeting number in a venue-id slot the flavor has no attribute for — the venue qualification this flavor holds lives inside the document number (`110-17`), so CGPM resolutions emit `urn:bipm:v1:cgpm:resolution:1:2018`. **The legacy faces stay parseable as documented parse-in aliases** (`UrnParser` branches on the leading `v1`): `urn:bipm:cipm:res:101-1:2012`, `urn:bipm:si-brochure:e:9e:v3.01:2019/2024`, `urn:bipm:metrologia:51:1:128`, `urn:bipm:cgpm:meeting:17:1983` and the empty-segment forms all rebuild the same identity whose `to_urn` is now the v1 face — no stored row or consumer-held URN stops resolving while the crawl lags; the emitter never produces them. The brochure alias parser also tightened two segments through `presence` (the legacy face's empty edition/version/years segments now read nil, matching what `to_hash` stores). The **pubid-scheme URN (`urn:pubid:bipm:…`) is untouched** — schema, atoms and emission byte-identical (0-diff verified).
20
+ **(R3 — the French-long canonical flipped to the guide's body-prefixed form.** `render_committee_long`'s F branch now prints `{name} {group}/{number} (year)` — `Décision CIPM/111-10 (2022)` — for every group, wholesale (the owner overrode the guide's own mixed pattern, which keeps the connective for CGPM resolutions). A **number-less** document has no id to qualify, so it keeps the connective spelling (`Statement de la CGPM (1889)`) — the guide's general French pattern. The connective spellings (`Décision 101-1 du CIPM (2012)`) and the group-leading word order (`CIPM Décision 101-1 (2012)`) are now normalizing aliases onto the same identity; `language: "F"` semantics unchanged. **Six byte-exact fixture lines moved** to the new canonical (recorded in the PR): the four CCL/CGPM/JCRB/CIPM connective lines became their body-prefixed spellings, the two that would have duplicated existing lines were replaced (one by the guide's own `Décision CIPM/111-10 (2022)` example, one dropped as now-identical), and the partial `Recommandation 1 du CCL` became `Recommandation CCL/1`. `to_hash` is unchanged by all three rulings — the flips are render/emitter faces only; verified by a full corpus replay (0 `to_hash` diffs, `to_pubid_urn` 0-diff).
16
21
 
17
22
  ## pubid-scheme URN (issue #670)
18
23