asciichem 0.31.1 → 0.31.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 95da94c5e8161536c0c7c7010235e3a2f398ccfdf8687de847a53cfd76e51731
4
- data.tar.gz: 20d38e60baf576485eba8fe14d54c7335515a5e60b4ad7d96d4dcd1afa716276
3
+ metadata.gz: ab5305dc2ec00320b62efbfae105108d23c90b6f5704c64e0f2ef59ec8c85983
4
+ data.tar.gz: 7bee58c136c5590646a736c42b900c4c4322fc7beeefcc9980b4ca5bfae6c4b8
5
5
  SHA512:
6
- metadata.gz: 84d797e0d0c161552b5180243c5496503ff8b7b925d76fb2dd85e9ca3ed0571504eb8f1acc9856839d0c3391a2a43df56b295bbd09db2e946d74169f648bbfca
7
- data.tar.gz: e77507e79c23a183001523f41a425a78ad82cbd47ca412ccbf84747fb87cdca4b391382e7893c4d27abfa9f8c8248d0eaed67a28dd37fc0f9496f7a282c1ada8
6
+ metadata.gz: a59410198a58a61ff195ed6ca2e4a5a7d20d751af9dc8690e1d38405e68a81ff3470ac532c9cd4517ed470fe814dab34db1221357a09ab63919bab78b57795b4
7
+ data.tar.gz: e1da749ccc43b3b1e00db74640e761b249d2445159026729780c11fa8b54d18c6c9d51f0655317229a9cd35cc1ffa727477f0c5be9033438be25fb8388acfa35
data/CHANGELOG.md CHANGED
@@ -3,6 +3,65 @@
3
3
  All notable changes to AsciiChem are documented here.
4
4
  This project follows [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [0.31.3] - 2026-10-20
7
+
8
+ ### Added
9
+ - Perception in the shared corpus (TODO.v2/13 item 2, slice C):
10
+ implicit hydrogens order aromatic bonds at 1.5 (Hückel) and declare
11
+ a value only when a default valence fits integrally — so
12
+ `c1ccccc1` and `C1=CC=CC=C1` perceive identically, and
13
+ aromatic-schematic fusion atoms decline rather than invent. Gated
14
+ by `fixtures/perception.json` in all three implementations.
15
+ - Structured condition entries (item 4, slice 1):
16
+ `Conditions#entries` derives typed `cat:`/`solv:`/`reag:` entries
17
+ (semicolon-separated) from the verbatim bracket text; free text and
18
+ unknown prefixes stay lossless, and the wire form is unchanged.
19
+ `ReactionRoles` prefers explicit roles over the above/below
20
+ heuristic.
21
+ - Reaction stoichiometry as model data (slice 2):
22
+ `Reaction#stoichiometry` and `Molecule#stoichiometric_coefficient`
23
+ — one coefficient rule shared by the reaction, `BalanceCheck`, and
24
+ composition counting.
25
+ - Mechanism step graph (slice 4a): `MechanismGraph.for` derives
26
+ parsed steps, catalysts (species on both sides), and inter-step
27
+ links from the verbatim `mechanism{}` entries.
28
+ - Reaction identity via RInChI (slice 4b): `Rxn::Writer` emits MDL
29
+ RXN V2000 and `Rinchi::BinaryEngine` wraps the opt-in `rinchi-1`
30
+ binary (install guidance, `EngineMissingError`, nothing vendored).
31
+ - Molfile stereo (L3 slice 6): `Atom#chirality` crosses as MDL atom
32
+ parity and stated hydrogens as the H-count field, so the identity
33
+ pipe derives stereoisomer-distinct InChIs; `IdentityCrossCheck`
34
+ covers stereo-layer mismatches.
35
+
36
+ ### Fixed
37
+ - SMILES writer ring-closure emission (#103): a closure-bearing
38
+ child continues the chain, so the digit lands at the end of the
39
+ traversal — `parse(write(parse(s))) == parse(s)` and writer
40
+ stability now hold for ring structures such as `C1=CCC(C)C(C)C1`.
41
+ - SMILES writer rendered stated zero hydrogens as a bare `H` (one);
42
+ it now emits `H0`.
43
+
44
+ ## [0.31.2] - 2026-10-20
45
+
46
+ ### Added
47
+ - `Reaction#stoichiometry` (TODO.v2/13 item 4, slice 2): the
48
+ coefficients are already model data; the reaction now exposes them
49
+ normalized — per side, coefficient-free canonical species text to
50
+ integer (absent coefficient = 1, duplicate species sum). Pure
51
+ model read; wire format unchanged.
52
+ - OPSIN source adapter (item 3): name-to-structure derivation via
53
+ the OPSIN service, feeding the resolver pipeline
54
+ (`AsciiChem::Resolver::Opsin`).
55
+ - Reaction role hints (item 4, slice A): `ReactionRoles.roles_for`
56
+ classifies conditions into catalyst/solvent/reagent hints per the
57
+ above/below-arrow convention.
58
+ - The perception pass (item 2): `AsciiChem::Perception` derives
59
+ implicit hydrogens and aromatic rings as pure model services; the
60
+ ValenceCheck linter consumes it.
61
+ - Differential conformance harness (item 5): 300 seeded generated
62
+ cases shared through asciichem-tests (`corpus/generated/models.json`),
63
+ exercised by the Ruby property suite and by TS/PY gates.
64
+
6
65
  ## [0.31.1] - 2026-10-20
7
66
 
8
67
  ### Changed
@@ -1 +1 @@
1
- {"bindings":null,"version":"0.3.1","grammar":"AsciiChemCore","shape":"parsanol-tree/v2","binding_version":1,"entries":{"formula":{"root":"formula","grammar":{"atoms":[{"Entity":{"atom":5}},{"Str":{"pattern":" "}},{"Re":{"pattern":"[\\x09]"}},{"Re":{"pattern":"[\\x0a]"}},{"Re":{"pattern":"[\\x0d]"}},{"Alternative":{"atoms":[1,2,3,4]}},{"Repetition":{"atom":0,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":6,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":640}},{"Entity":{"atom":426}},{"Entity":{"atom":412}},{"Entity":{"atom":409}},{"Entity":{"atom":408}},{"Entity":{"atom":30}},{"Str":{"pattern":"("}},{"Entity":{"atom":26}},{"Str":{"pattern":"alpha"}},{"Str":{"pattern":"beta"}},{"Str":{"pattern":"R"}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"E"}},{"Str":{"pattern":"Z"}},{"Str":{"pattern":"α"}},{"Str":{"pattern":"β"}},{"Str":{"pattern":"a"}},{"Str":{"pattern":"b"}},{"Alternative":{"atoms":[16,17,18,19,20,21,22,23,24,25]}},{"Named":{"name":"stereo","atom":15}},{"Str":{"pattern":")"}},{"Str":{"pattern":"-"}},{"Sequence":{"atoms":[14,27,28,29]}},{"Repetition":{"atom":13,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":43}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":33,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"("}},{"Str":{"pattern":"["}},{"Str":{"pattern":"{"}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Alternative":{"atoms":[35,36,37,38]}},{"Lookahead":{"atom":39,"positive":true}},{"Sequence":{"atoms":[34,40]}},{"Named":{"name":"value","atom":41}},{"Named":{"name":"coefficient","atom":42}},{"Repetition":{"atom":32,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":403}},{"Entity":{"atom":209}},{"Entity":{"atom":50}},{"Str":{"pattern":":"}},{"Repetition":{"atom":48,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"lone_pairs","atom":49}},{"Repetition":{"atom":47,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"^"}},{"Str":{"pattern":"_"}},{"Alternative":{"atoms":[52,53]}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":55,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[54,56]}},{"Named":{"name":"isotope","atom":57}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Repetition":{"atom":60,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[59,61]}},{"Named":{"name":"element","atom":62}},{"Entity":{"atom":124}},{"Str":{"pattern":"_"}},{"Entity":{"atom":77}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":68,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[69,70]}},{"Repetition":{"atom":71,"min":0,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[67,72,73]}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":75,"min":1,"max":null,"tag":"Repetition"}},{"Alternative":{"atoms":[74,76]}},{"Sequence":{"atoms":[65,66]}},{"Named":{"name":"subscript","atom":78}},{"Repetition":{"atom":79,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"^"}},{"Entity":{"atom":120}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":84,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[85,86]}},{"Repetition":{"atom":87,"min":0,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[83,88,89]}},{"Entity":{"atom":102}},{"Str":{"pattern":"("}},{"Re":{"pattern":"[I]"}},{"Re":{"pattern":"[V]"}},{"Re":{"pattern":"[X]"}},{"Re":{"pattern":"[L]"}},{"Re":{"pattern":"[C]"}},{"Re":{"pattern":"[D]"}},{"Alternative":{"atoms":[93,94,95,96,97,98]}},{"Repetition":{"atom":99,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[92,100,101]}},{"Entity":{"atom":118}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":104,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"+"}},{"Str":{"pattern":"-"}},{"Alternative":{"atoms":[106,107]}},{"Repetition":{"atom":108,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[105,109]}},{"Str":{"pattern":"+"}},{"Str":{"pattern":"-"}},{"Alternative":{"atoms":[111,112]}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":114,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":115,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[113,116]}},{"Alternative":{"atoms":[110,117]}},{"Alternative":{"atoms":[91,103]}},{"Alternative":{"atoms":[90,119]}},{"Sequence":{"atoms":[81,82]}},{"Named":{"name":"superscript","atom":121}},{"Repetition":{"atom":122,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[80,123]}},{"Entity":{"atom":128}},{"Str":{"pattern":"."}},{"Repetition":{"atom":126,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"radical_electrons","atom":127}},{"Repetition":{"atom":125,"min":0,"max":1,"tag":"Maybe"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":130,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"ring_closures","atom":131}},{"Repetition":{"atom":132,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":207}},{"Entity":{"atom":159}},{"Str":{"pattern":"@("}},{"Entity":{"atom":147}},{"Str":{"pattern":"-"}},{"Repetition":{"atom":138,"min":0,"max":1,"tag":"Maybe"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":140,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":143,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[142,144]}},{"Repetition":{"atom":145,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[139,141,146]}},{"Named":{"name":"x2","atom":137}},{"Str":{"pattern":","}},{"Entity":{"atom":147}},{"Named":{"name":"y2","atom":150}},{"Str":{"pattern":","}},{"Entity":{"atom":147}},{"Named":{"name":"z2","atom":153}},{"Sequence":{"atoms":[152,154]}},{"Repetition":{"atom":155,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":")"}},{"Named":{"name":"coord_close","atom":157}},{"Sequence":{"atoms":[136,148,149,151,156,158]}},{"Repetition":{"atom":135,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":167}},{"Str":{"pattern":"@"}},{"Str":{"pattern":"R"}},{"Str":{"pattern":"S"}},{"Alternative":{"atoms":[163,164]}},{"Named":{"name":"atom_parity","atom":165}},{"Sequence":{"atoms":[162,166]}},{"Repetition":{"atom":161,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":176}},{"Str":{"pattern":"@m("}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":171,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"spin_multiplicity","atom":172}},{"Str":{"pattern":")"}},{"Named":{"name":"spin_close","atom":174}},{"Sequence":{"atoms":[170,173,175]}},{"Repetition":{"atom":169,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":191}},{"Str":{"pattern":"@t("}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":181,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[182,183]}},{"Repetition":{"atom":184,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"atom_title","atom":185}},{"Str":{"pattern":"\""}},{"Named":{"name":"title_quote","atom":187}},{"Str":{"pattern":")"}},{"Named":{"name":"title_close","atom":189}},{"Sequence":{"atoms":[179,180,186,188,190]}},{"Repetition":{"atom":178,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":205}},{"Str":{"pattern":"@f("}},{"Entity":{"atom":147}},{"Named":{"name":"x_fract","atom":195}},{"Str":{"pattern":","}},{"Entity":{"atom":147}},{"Named":{"name":"y_fract","atom":198}},{"Str":{"pattern":","}},{"Entity":{"atom":147}},{"Named":{"name":"z_fract","atom":201}},{"Str":{"pattern":")"}},{"Named":{"name":"fract_close","atom":203}},{"Sequence":{"atoms":[194,196,197,199,200,202,204]}},{"Repetition":{"atom":193,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[160,168,177,192,206]}},{"Sequence":{"atoms":[51,58,63,64,129,133,134]}},{"Named":{"name":"atom","atom":208}},{"Entity":{"atom":359}},{"Entity":{"atom":216}},{"Str":{"pattern":"("}},{"Str":{"pattern":"["}},{"Str":{"pattern":"{"}},{"Alternative":{"atoms":[212,213,214]}},{"Named":{"name":"open_bracket","atom":215}},{"Entity":{"atom":355}},{"Entity":{"atom":292}},{"Named":{"name":"reactants","atom":11}},{"Entity":{"atom":249}},{"Entity":{"atom":5}},{"Repetition":{"atom":221,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":222,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":229}},{"Str":{"pattern":"<=>"}},{"Str":{"pattern":"<->"}},{"Str":{"pattern":"->"}},{"Str":{"pattern":"<-"}},{"Alternative":{"atoms":[225,226,227,228]}},{"Named":{"name":"kind","atom":224}},{"Entity":{"atom":242}},{"Str":{"pattern":"["}},{"Str":{"pattern":"]"}},{"Lookahead":{"atom":233,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[234,235]}},{"Repetition":{"atom":236,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"text","atom":237}},{"Str":{"pattern":"]"}},{"Named":{"name":"condition_close","atom":239}},{"Sequence":{"atoms":[232,238,240]}},{"Named":{"name":"condition","atom":241}},{"Named":{"name":"above","atom":231}},{"Repetition":{"atom":243,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":242}},{"Named":{"name":"below","atom":245}},{"Repetition":{"atom":246,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[223,230,244,247]}},{"Named":{"name":"arrow","atom":248}},{"Entity":{"atom":5}},{"Repetition":{"atom":250,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":251,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":289}},{"Entity":{"atom":287}},{"Entity":{"atom":5}},{"Repetition":{"atom":255,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":256,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"+"}},{"Entity":{"atom":5}},{"Repetition":{"atom":259,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":260,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":285}},{"Entity":{"atom":280}},{"Str":{"pattern":"##"}},{"Named":{"name":"quadruple","atom":264}},{"Str":{"pattern":">-"}},{"Named":{"name":"wedge","atom":266}},{"Str":{"pattern":"-<"}},{"Named":{"name":"hash","atom":268}},{"Str":{"pattern":"~>"}},{"Named":{"name":"dative","atom":270}},{"Str":{"pattern":"~~"}},{"Named":{"name":"wavy","atom":272}},{"Str":{"pattern":"#"}},{"Named":{"name":"triple","atom":274}},{"Str":{"pattern":"="}},{"Named":{"name":"double","atom":276}},{"Str":{"pattern":"-"}},{"Named":{"name":"single","atom":278}},{"Alternative":{"atoms":[265,267,269,271,273,275,277,279]}},{"Alternative":{"atoms":[45,263]}},{"Repetition":{"atom":281,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"units","atom":282}},{"Sequence":{"atoms":[31,44,283]}},{"Named":{"name":"molecule","atom":284}},{"Sequence":{"atoms":[257,258,261,262]}},{"Named":{"name":"plus_term","atom":286}},{"Repetition":{"atom":254,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[12,288]}},{"Named":{"name":"products","atom":253}},{"Sequence":{"atoms":[219,220,252,290]}},{"Named":{"name":"reaction","atom":291}},{"Entity":{"atom":315}},{"Entity":{"atom":305}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":295,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"s"}},{"Str":{"pattern":"p"}},{"Str":{"pattern":"d"}},{"Str":{"pattern":"f"}},{"Str":{"pattern":"g"}},{"Str":{"pattern":"h"}},{"Alternative":{"atoms":[297,298,299,300,301,302]}},{"Sequence":{"atoms":[296,303]}},{"Named":{"name":"orbital","atom":304}},{"Str":{"pattern":"^"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":307,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"occupancy","atom":308}},{"Entity":{"atom":5}},{"Repetition":{"atom":310,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":311,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[294,306,309,312]}},{"Repetition":{"atom":313,"min":2,"max":null,"tag":"Repetition"}},{"Named":{"name":"electron_config","atom":314}},{"Entity":{"atom":285}},{"Entity":{"atom":333}},{"Repetition":{"atom":217,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"group_nodes","atom":318}},{"Entity":{"atom":325}},{"Str":{"pattern":")"}},{"Str":{"pattern":"]"}},{"Str":{"pattern":"}"}},{"Alternative":{"atoms":[321,322,323]}},{"Named":{"name":"close_bracket","atom":324}},{"Str":{"pattern":"_"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":327,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[326,328]}},{"Named":{"name":"multiplicity","atom":329}},{"Repetition":{"atom":330,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[211,319,320,331]}},{"Named":{"name":"group","atom":332}},{"Entity":{"atom":345}},{"Str":{"pattern":"`"}},{"Str":{"pattern":"`"}},{"Lookahead":{"atom":336,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[337,338]}},{"Repetition":{"atom":339,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"math_source","atom":340}},{"Str":{"pattern":"`"}},{"Named":{"name":"math_close","atom":342}},{"Sequence":{"atoms":[335,341,343]}},{"Named":{"name":"embedded_math","atom":344}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":347,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[348,349]}},{"Repetition":{"atom":350,"min":0,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"\""}},{"Sequence":{"atoms":[346,351,352]}},{"Named":{"name":"group_text_run","atom":353}},{"Alternative":{"atoms":[218,293,316,317,334,354]}},{"Repetition":{"atom":217,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"group_nodes","atom":356}},{"Sequence":{"atoms":[211,357,320,331]}},{"Named":{"name":"group","atom":358}},{"Entity":{"atom":384}},{"Entity":{"atom":50}},{"Repetition":{"atom":361,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"H"}},{"Named":{"name":"element","atom":363}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Lookahead":{"atom":365,"positive":false}},{"Str":{"pattern":"_"}},{"Entity":{"atom":77}},{"Sequence":{"atoms":[367,368]}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":370,"min":1,"max":null,"tag":"Repetition"}},{"Alternative":{"atoms":[369,371]}},{"Named":{"name":"subscript","atom":372}},{"Repetition":{"atom":373,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"^"}},{"Entity":{"atom":120}},{"Sequence":{"atoms":[375,376]}},{"Named":{"name":"superscript","atom":377}},{"Repetition":{"atom":378,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":128}},{"Repetition":{"atom":380,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":207}},{"Sequence":{"atoms":[362,364,366,374,379,381,382]}},{"Named":{"name":"atom","atom":383}},{"Entity":{"atom":402}},{"Entity":{"atom":50}},{"Repetition":{"atom":386,"min":0,"max":1,"tag":"Maybe"}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Repetition":{"atom":389,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[388,390]}},{"Named":{"name":"element","atom":391}},{"Entity":{"atom":124}},{"Entity":{"atom":128}},{"Repetition":{"atom":394,"min":0,"max":1,"tag":"Maybe"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":396,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"ring_closures","atom":397}},{"Repetition":{"atom":398,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":207}},{"Sequence":{"atoms":[387,392,393,395,399,400]}},{"Named":{"name":"atom","atom":401}},{"Alternative":{"atoms":[46,210,360,385]}},{"Alternative":{"atoms":[45,263]}},{"Repetition":{"atom":404,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"units","atom":405}},{"Sequence":{"atoms":[31,44,406]}},{"Named":{"name":"molecule","atom":407}},{"Sequence":{"atoms":[12,288]}},{"Named":{"name":"reactants","atom":11}},{"Sequence":{"atoms":[410,220,252,290]}},{"Named":{"name":"reaction","atom":411}},{"Named":{"name":"first","atom":10}},{"Entity":{"atom":422}},{"Entity":{"atom":249}},{"Entity":{"atom":5}},{"Repetition":{"atom":416,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":417,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":409}},{"Named":{"name":"products","atom":419}},{"Sequence":{"atoms":[415,418,420]}},{"Named":{"name":"segment","atom":421}},{"Repetition":{"atom":414,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"segments","atom":423}},{"Sequence":{"atoms":[413,424]}},{"Named":{"name":"cascade","atom":425}},{"Entity":{"atom":412}},{"Entity":{"atom":315}},{"Entity":{"atom":465}},{"Str":{"pattern":"crystal"}},{"Str":{"pattern":"["}},{"Str":{"pattern":"]"}},{"Lookahead":{"atom":432,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[433,434]}},{"Repetition":{"atom":435,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"crystal_name","atom":436}},{"Str":{"pattern":"]"}},{"Named":{"name":"crystal_name_close","atom":438}},{"Sequence":{"atoms":[431,437,439]}},{"Repetition":{"atom":440,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"("}},{"Str":{"pattern":")"}},{"Lookahead":{"atom":443,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[444,445]}},{"Repetition":{"atom":446,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"crystal_params","atom":447}},{"Str":{"pattern":")"}},{"Named":{"name":"crystal_params_close","atom":449}},{"Sequence":{"atoms":[442,448,450]}},{"Repetition":{"atom":451,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":454,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[455,456]}},{"Repetition":{"atom":457,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"crystal_body","atom":458}},{"Str":{"pattern":"}"}},{"Named":{"name":"crystal_body_close","atom":460}},{"Sequence":{"atoms":[453,459,461]}},{"Repetition":{"atom":462,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[430,441,452,463]}},{"Named":{"name":"crystal_node","atom":464}},{"Entity":{"atom":502}},{"Str":{"pattern":"spectrum"}},{"Str":{"pattern":"["}},{"Str":{"pattern":"]"}},{"Lookahead":{"atom":469,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[470,471]}},{"Repetition":{"atom":472,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"spectrum_type","atom":473}},{"Str":{"pattern":"]"}},{"Named":{"name":"spectrum_type_close","atom":475}},{"Sequence":{"atoms":[468,474,476]}},{"Repetition":{"atom":477,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"("}},{"Str":{"pattern":")"}},{"Lookahead":{"atom":480,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[481,482]}},{"Repetition":{"atom":483,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"spectrum_params","atom":484}},{"Str":{"pattern":")"}},{"Named":{"name":"spectrum_params_close","atom":486}},{"Sequence":{"atoms":[479,485,487]}},{"Repetition":{"atom":488,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":491,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[492,493]}},{"Repetition":{"atom":494,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"spectrum_body","atom":495}},{"Str":{"pattern":"}"}},{"Named":{"name":"spectrum_body_close","atom":497}},{"Sequence":{"atoms":[490,496,498]}},{"Repetition":{"atom":499,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[467,478,489,500]}},{"Named":{"name":"spectrum_node","atom":501}},{"Entity":{"atom":528}},{"Str":{"pattern":"calc"}},{"Str":{"pattern":"("}},{"Str":{"pattern":")"}},{"Lookahead":{"atom":506,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[507,508]}},{"Repetition":{"atom":509,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"calc_params","atom":510}},{"Str":{"pattern":")"}},{"Named":{"name":"calc_params_close","atom":512}},{"Sequence":{"atoms":[505,511,513]}},{"Repetition":{"atom":514,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":517,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[518,519]}},{"Repetition":{"atom":520,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"calc_body","atom":521}},{"Str":{"pattern":"}"}},{"Named":{"name":"calc_body_close","atom":523}},{"Sequence":{"atoms":[516,522,524]}},{"Repetition":{"atom":525,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[504,515,526]}},{"Named":{"name":"calc_node","atom":527}},{"Entity":{"atom":543}},{"Str":{"pattern":"zmatrix"}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":532,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[533,534]}},{"Repetition":{"atom":535,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"zmatrix_body","atom":536}},{"Str":{"pattern":"}"}},{"Named":{"name":"zmatrix_body_close","atom":538}},{"Sequence":{"atoms":[531,537,539]}},{"Repetition":{"atom":540,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[530,541]}},{"Named":{"name":"zmatrix_node","atom":542}},{"Entity":{"atom":558}},{"Str":{"pattern":"mechanism"}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":547,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[548,549]}},{"Repetition":{"atom":550,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"mechanism_body","atom":551}},{"Str":{"pattern":"}"}},{"Named":{"name":"mechanism_body_close","atom":553}},{"Sequence":{"atoms":[546,552,554]}},{"Repetition":{"atom":555,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[545,556]}},{"Named":{"name":"mechanism_node","atom":557}},{"Entity":{"atom":628}},{"Entity":{"atom":408}},{"Named":{"name":"mol","atom":560}},{"Entity":{"atom":624}},{"Entity":{"atom":5}},{"Repetition":{"atom":563,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":564,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":588}},{"Str":{"pattern":"@meta("}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":569,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[570,571]}},{"Repetition":{"atom":572,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"meta_key","atom":573}},{"Str":{"pattern":"\""}},{"Str":{"pattern":","}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":578,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[579,580]}},{"Repetition":{"atom":581,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"meta_value","atom":582}},{"Str":{"pattern":"\""}},{"Named":{"name":"meta_value_close","atom":584}},{"Str":{"pattern":")"}},{"Named":{"name":"meta_close","atom":586}},{"Sequence":{"atoms":[567,568,574,575,576,577,583,585,587]}},{"Entity":{"atom":621}},{"Str":{"pattern":"@"}},{"Entity":{"atom":602}},{"Str":{"pattern":"name"}},{"Str":{"pattern":"title"}},{"Str":{"pattern":"formula"}},{"Str":{"pattern":"label"}},{"Str":{"pattern":"inchi"}},{"Str":{"pattern":"smiles"}},{"Str":{"pattern":"cas"}},{"Str":{"pattern":"iupac"}},{"Str":{"pattern":"cid"}},{"Str":{"pattern":"chebi"}},{"Alternative":{"atoms":[592,593,594,595,596,597,598,599,600,601]}},{"Entity":{"atom":605}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Repetition":{"atom":604,"min":1,"max":null,"tag":"Repetition"}},{"Alternative":{"atoms":[591,603]}},{"Named":{"name":"ann_type","atom":606}},{"Str":{"pattern":"("}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":610,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[611,612]}},{"Repetition":{"atom":613,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"ann_value","atom":614}},{"Str":{"pattern":"\""}},{"Named":{"name":"ann_value_close","atom":616}},{"Str":{"pattern":")"}},{"Named":{"name":"ann_close","atom":618}},{"Sequence":{"atoms":[607,608,609,615,617,619]}},{"Sequence":{"atoms":[590,620]}},{"Alternative":{"atoms":[566,589]}},{"Sequence":{"atoms":[565,622]}},{"Named":{"name":"annotation_item","atom":623}},{"Repetition":{"atom":562,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"annotations","atom":625}},{"Sequence":{"atoms":[561,626]}},{"Named":{"name":"annotated_molecule","atom":627}},{"Entity":{"atom":408}},{"Entity":{"atom":345}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":632,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[633,634]}},{"Repetition":{"atom":635,"min":0,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"\""}},{"Sequence":{"atoms":[631,636,637]}},{"Named":{"name":"text_run","atom":638}},{"Alternative":{"atoms":[9,427,428,429,466,503,529,544,559,629,630,639]}},{"Entity":{"atom":5}},{"Repetition":{"atom":641,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":642,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":640}},{"Sequence":{"atoms":[643,644]}},{"Repetition":{"atom":645,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[8,646]}},{"Named":{"name":"formula","atom":647}},{"Entity":{"atom":5}},{"Repetition":{"atom":649,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":650,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[7,648,651]}}],"root":652},"bindings":[]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule formula_node: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 1 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 1 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 2 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 2 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 3 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 3 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 4 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 9 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule arrow_kind: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule arrow_kind: branches 1 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule arrow_kind: branches 2 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule stereo_letter: branches 1 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule stereo_letter: branches 2 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule stereo_letter: branches 7 and 8 are order-dependent (shared first bytes; ordered choice is decisive)","rule unit: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule unit: branches 1 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule unit: branches 3 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule group_unit: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule group_unit: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule group_unit: branches 1 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule group_unit: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule group_unit: branches 3 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule bond: branches 1 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule bond: branches 3 and 8 are order-dependent (shared first bytes; ordered choice is decisive)","rule bond: branches 4 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule known_annotation_type: branches 5 and 8 are order-dependent (shared first bytes; ordered choice is decisive)","rule known_annotation_type: branches 7 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule known_annotation_type: branches 7 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule known_annotation_type: branches 9 and 10 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"formula","render":{},"derive":{},"tests":[],"docs":{"formula":"spaces? >> nodes.as(:formula) >> spaces?","formula_node":"Top-level nodes, DSL ordered-choice order (cascade before plain\nreaction, annotations before bare molecule).","cascade":"Two or more chained reactions: the transform promotes the chain\nto a ReactionCascade (first leg + arrow/products segments).","reaction":"Reactants, arrow (with optional above/below conditions), products.","arrow":"arrow.as(:arrow) {kind, above?, below?} — leading spaces ride the\narrow, conditions attach with no gap.","molecule":"A molecule: stereochemistry prefix, optional coefficient, units.","stereo_prefix":"`(R)-` style prefixes; a/b are the alpha/beta shorthand.","coefficient":"Coefficient: digits only when an element or bracket follows (else\nthe digits are subscripts/rings). Captured as {value}.","unit":"Units: prefixed (isotope) atoms, bracket groups, hydrogen\n(bare-digit subscripts allowed), plain atoms.","prefixed_atom":"Prefix isotope: ^14C or _14C — the marker binds to the atom, the\ncore semantic fix over AsciiMath. Captured with the marker.","group":"Bracket group: ( ... ) [ ... ] { ... } with optional multiplicity.","group_unit":"Inside a group: reactions, molecules, electron configs, embedded\nmath and quoted text recurse (DSL group_node set).","hydrogen_atom":"Hydrogen: bare digits after H are subscripts (H cannot ring-\nclose), but H must not steal the H of He, Hf...","atom_suffix":"Suffix markers consume `_` / `^` but capture marker + value\n(AtomBuilder strips the marker).","charge":"Charges: 2+, +2, 2 (bare digits fall through to the same capture).","oxidation_state":"Oxidation state: (IV), (II)...","lewis_prefix":"Lewis markers: leading colons = lone pairs; trailing dots =\nradical electrons.","atom_annotation":"Atom annotations, DSL order: coordinates, parity, multiplicity,\ntitle, fractional coordinates.","bond":"Bonds capture under their kind name (transform contract), longest\nfirst so `##` beats `#`; `->` is reserved for reactions.","crystal":"crystal[Name](a=X,b=Y,sg=SG){atoms with @f(x,y,z)}","spectrum":"spectrum[type](params){peak data}","calculation":"calc(method/basis){key: value units}","zmatrix":"zmatrix{lines}","mechanism":"mechanism{steps}","electron_config":"`1s2 2s2 2p6` — at least two orbital terms (DSL repeat(2)).","embedded_math":"Backtick-delimited Plurimath/AsciiMath source (delimiters stripped\nby the transform).","annotated_molecule":"Free-form text uses \"...\" delimiters, matching AsciiMath; the\ncapture includes the quotes (TextNormaliser strips them).\n@name(\"aspirin\") @cas(\"50-78-2\") @meta(\"k\",\"v\") — the annotation\nvocabulary rides the molecule.","metadata_annotation":"Two quoted args, comma-separated; keys distinguish metadata from\nregular annotations.","simple_annotation":"Known types (name, inchi, ...) handled specially by the transform;\nany other lowercase word becomes a property."},"source":"grammar AsciiChemCore version \"0.3.1\" {\n ## The AsciiChem syntax — full grammar, DSL-shaped captures\n ## (P1b, asciichem-ruby#96). Capture names and nesting mirror\n ## GrammarRules exactly so the PARG tree adapts to the same shape\n ## TransformRules consumes: sequence captures merge into one hash\n ## (parens to attach `as`), repetitions stay arrays, unwrapped\n ## rules let their captures ride into the parent context. Absent\n ## optionals omit their key (the tree adapter reinstates the ones\n ## transform patterns require, e.g. group multiplicity). Structural\n ## rules (molecule/reaction/group/annotated_molecule) are captured\n ## and the adapter unwraps them: PARG merge semantics for captured\n ## sequences with embedded repetitions yield arrays, and uncaptured\n ## sequence rules drop captures non-deterministically in reps\n ## (parsanol findings).\n ##\n ## Language notes (P0 findings): entries reference rules; sequences\n ## take no trailing capture (wrap in parens); [ ] is the option\n ## syntax so literal brackets are strings; until is exclusive\n ## (restate the closer); strict PEG — no repetition backtracking.\n\n entry formula: formula\n\n ## spaces? >> nodes.as(:formula) >> spaces?\n formula = [ 1*( ws ) ] ( formula_node 0*( [ 1*( ws ) ] formula_node ) ) as formula [ 1*( ws ) ]\n\n ws = ( \" \" / %x09 / %x0A / %x0D )\n\n ## Top-level nodes, DSL ordered-choice order (cascade before plain\n ## reaction, annotations before bare molecule).\n formula_node = cascade / reaction / electron_config / crystal / spectrum / calculation / zmatrix / mechanism / annotated_molecule / molecule / embedded_math / ( \"\\\"\" until \"\\\"\" \"\\\"\" ) as text_run\n\n # -- reactions ---------------------------------------------------------\n\n ## Two or more chained reactions: the transform promotes the chain\n ## to a ReactionCascade (first leg + arrow/products segments).\n cascade = ( ( reaction ) as first ( 1*( cascade_segment ) ) as segments ) as cascade\n\n cascade_segment = ( arrow [ 1*( ws ) ] ( terms as products ) ) as segment\n\n ## Reactants, arrow (with optional above/below conditions), products.\n reaction = ( ( terms as reactants ) arrow [ 1*( ws ) ] ( terms as products ) ) as reaction\n\n terms = molecule 0*( plus_term )\n\n plus_term = ( [ 1*( ws ) ] \"+\" [ 1*( ws ) ] molecule ) as plus_term\n\n ## arrow.as(:arrow) {kind, above?, below?} — leading spaces ride the\n ## arrow, conditions attach with no gap.\n arrow = ( [ 1*( ws ) ] ( arrow_kind ) as kind [ condition as above ] [ condition as below ] ) as arrow\n\n arrow_kind = ( \"<=>\" / \"<->\" / \"->\" / \"<-\" )\n\n condition = ( \"[\" ( until \"]\" ) as text ( \"]\" ) as condition_close ) as condition\n\n ## A molecule: stereochemistry prefix, optional coefficient, units.\n molecule = ( [ stereo_prefix ] [ coefficient ] ( 1*( unit / bond ) ) as units ) as molecule\n\n ## `(R)-` style prefixes; a/b are the alpha/beta shorthand.\n stereo_prefix = \"(\" ( stereo_letter ) as stereo \")\" \"-\"\n\n stereo_letter = ( \"alpha\" / \"beta\" / \"R\" / \"S\" / \"E\" / \"Z\" / \"α\" / \"β\" / \"a\" / \"b\" )\n\n ## Coefficient: digits only when an element or bracket follows (else\n ## the digits are subscripts/rings). Captured as {value}.\n coefficient = ( ( 1*(%x30-39) &( \"(\" / \"[\" / \"{\" / %x41-5A ) ) as value ) as coefficient\n\n ## Units: prefixed (isotope) atoms, bracket groups, hydrogen\n ## (bare-digit subscripts allowed), plain atoms.\n unit = prefixed_atom / group / hydrogen_atom / plain_atom\n\n ## Prefix isotope: ^14C or _14C — the marker binds to the atom, the\n ## core semantic fix over AsciiMath. Captured with the marker.\n prefixed_atom = ( [ lewis_prefix ] ( ( \"^\" / \"_\" ) 1*(%x30-39) ) as isotope ( %x41-5A [ %x61-7A ] ) as element atom_suffix [ lewis_radicals ] [ ( 1*(%x30-39) ) as ring_closures ] atom_annotation ) as atom\n\n ## Bracket group: ( ... ) [ ... ] { ... } with optional multiplicity.\n group = ( group_open ( 1*( group_unit ) ) as group_nodes group_close [ ( \"_\" 1*(%x30-39) ) as multiplicity ] ) as group\n\n group_open = ( \"(\" / \"[\" / \"{\" ) as open_bracket\n\n group_close = ( \")\" / \"]\" / \"}\" ) as close_bracket\n\n ## Inside a group: reactions, molecules, electron configs, embedded\n ## math and quoted text recurse (DSL group_node set).\n group_unit = reaction / electron_config / molecule / group / embedded_math / ( \"\\\"\" until \"\\\"\" \"\\\"\" ) as group_text_run\n\n ## Hydrogen: bare digits after H are subscripts (H cannot ring-\n ## close), but H must not steal the H of He, Hf...\n hydrogen_atom = ( [ lewis_prefix ] ( \"H\" ) as element !( %x61-7A ) [ ( \"_\" subscript_value / 1*(%x30-39) ) as subscript ] [ ( \"^\" superscript_value ) as superscript ] [ lewis_radicals ] atom_annotation ) as atom\n\n plain_atom = ( [ lewis_prefix ] ( %x41-5A [ %x61-7A ] ) as element atom_suffix [ lewis_radicals ] [ ( 1*(%x30-39) ) as ring_closures ] atom_annotation ) as atom\n\n ## Suffix markers consume `_` / `^` but capture marker + value\n ## (AtomBuilder strips the marker).\n atom_suffix = [ ( \"_\" subscript_value ) as subscript ] [ ( \"^\" superscript_value ) as superscript ]\n\n subscript_value = ( \"{\" until \"}\" \"}\" ) / 1*(%x30-39)\n\n superscript_value = ( \"{\" until \"}\" \"}\" ) / ( oxidation_state / charge )\n\n ## Charges: 2+, +2, 2 (bare digits fall through to the same capture).\n charge = ( 1*(%x30-39) [ \"+\" / \"-\" ] ) / ( ( \"+\" / \"-\" ) [ 1*(%x30-39) ] )\n\n ## Oxidation state: (IV), (II)...\n oxidation_state = \"(\" 1*( %x49 / %x56 / %x58 / %x4C / %x43 / %x44 ) \")\"\n\n ## Lewis markers: leading colons = lone pairs; trailing dots =\n ## radical electrons.\n lewis_prefix = 1*( \":\" ) as lone_pairs\n\n lewis_radicals = 1*( \".\" ) as radical_electrons\n\n ## Atom annotations, DSL order: coordinates, parity, multiplicity,\n ## title, fractional coordinates.\n atom_annotation = [ coordinate_annotation ] [ parity_annotation ] [ multiplicity_annotation ] [ title_annotation ] [ fractional_annotation ]\n\n coordinate_annotation = \"@(\" ( float_number ) as x2 \",\" ( float_number ) as y2 [ \",\" ( float_number ) as z2 ] ( \")\" ) as coord_close\n\n parity_annotation = \"@\" ( \"R\" / \"S\" ) as atom_parity\n\n multiplicity_annotation = \"@m(\" ( 1*(%x30-39) ) as spin_multiplicity ( \")\" ) as spin_close\n\n title_annotation = \"@t(\" \"\\\"\" ( until \"\\\"\" ) as atom_title ( \"\\\"\" ) as title_quote ( \")\" ) as title_close\n\n fractional_annotation = \"@f(\" ( float_number ) as x_fract \",\" ( float_number ) as y_fract \",\" ( float_number ) as z_fract ( \")\" ) as fract_close\n\n float_number = [ \"-\" ] 1*(%x30-39) [ \".\" 0*(%x30-39) ]\n\n # -- bonds ---------------------------------------------------------------\n\n ## Bonds capture under their kind name (transform contract), longest\n ## first so `##` beats `#`; `->` is reserved for reactions.\n bond = ( \"##\" as quadruple ) / ( \">-\" as wedge ) / ( \"-<\" as hash ) / ( \"~>\" as dative ) / ( \"~~\" as wavy ) / ( \"#\" as triple ) / ( \"=\" as double ) / ( \"-\" as single )\n\n # -- beyond-formulas constructs -----------------------------------------\n\n ## crystal[Name](a=X,b=Y,sg=SG){atoms with @f(x,y,z)}\n crystal = ( \"crystal\" [ ( \"[\" ( until \"]\" ) as crystal_name ( \"]\" ) as crystal_name_close ) ] [ ( \"(\" ( until \")\" ) as crystal_params ( \")\" ) as crystal_params_close ) ] [ ( \"{\" ( until \"}\" ) as crystal_body ( \"}\" ) as crystal_body_close ) ] ) as crystal_node\n\n ## spectrum[type](params){peak data}\n spectrum = ( \"spectrum\" [ ( \"[\" ( until \"]\" ) as spectrum_type ( \"]\" ) as spectrum_type_close ) ] [ ( \"(\" ( until \")\" ) as spectrum_params ( \")\" ) as spectrum_params_close ) ] [ ( \"{\" ( until \"}\" ) as spectrum_body ( \"}\" ) as spectrum_body_close ) ] ) as spectrum_node\n\n ## calc(method/basis){key: value units}\n calculation = ( \"calc\" [ ( \"(\" ( until \")\" ) as calc_params ( \")\" ) as calc_params_close ) ] [ ( \"{\" ( until \"}\" ) as calc_body ( \"}\" ) as calc_body_close ) ] ) as calc_node\n\n ## zmatrix{lines}\n zmatrix = ( \"zmatrix\" [ ( \"{\" ( until \"}\" ) as zmatrix_body ( \"}\" ) as zmatrix_body_close ) ] ) as zmatrix_node\n\n ## mechanism{steps}\n mechanism = ( \"mechanism\" [ ( \"{\" ( until \"}\" ) as mechanism_body ( \"}\" ) as mechanism_body_close ) ] ) as mechanism_node\n\n # -- electron configuration ----------------------------------------------\n\n ## `1s2 2s2 2p6` — at least two orbital terms (DSL repeat(2)).\n electron_config = 2*( orbital \"^\" ( 1*(%x30-39) ) as occupancy [ 1*( ws ) ] ) as electron_config\n\n orbital = ( 1*(%x30-39) ( \"s\" / \"p\" / \"d\" / \"f\" / \"g\" / \"h\" ) ) as orbital\n\n # -- embedded math and text ------------------------------------------------\n\n ## Backtick-delimited Plurimath/AsciiMath source (delimiters stripped\n ## by the transform).\n embedded_math = ( \"`\" ( until \"`\" ) as math_source ( \"`\" ) as math_close ) as embedded_math\n\n ## Free-form text uses \"...\" delimiters, matching AsciiMath; the\n ## capture includes the quotes (TextNormaliser strips them).\n\n # -- molecule annotations ---------------------------------------------------\n\n ## @name(\"aspirin\") @cas(\"50-78-2\") @meta(\"k\",\"v\") — the annotation\n ## vocabulary rides the molecule.\n annotated_molecule = ( ( molecule ) as mol ( 1*( annotation_item ) ) as annotations ) as annotated_molecule\n\n annotation_item = ( [ 1*( ws ) ] ( metadata_annotation / simple_annotation ) ) as annotation_item\n\n ## Two quoted args, comma-separated; keys distinguish metadata from\n ## regular annotations.\n metadata_annotation = \"@meta(\" \"\\\"\" ( until \"\\\"\" ) as meta_key \"\\\"\" \",\" \"\\\"\" ( until \"\\\"\" ) as meta_value ( \"\\\"\" ) as meta_value_close ( \")\" ) as meta_close\n\n ## Known types (name, inchi, ...) handled specially by the transform;\n ## any other lowercase word becomes a property.\n simple_annotation = \"@\" ( ( known_annotation_type / property_name ) as ann_type \"(\" \"\\\"\" ( until \"\\\"\" ) as ann_value ( \"\\\"\" ) as ann_value_close ( \")\" ) as ann_close )\n\n known_annotation_type = ( \"name\" / \"title\" / \"formula\" / \"label\" / \"inchi\" / \"smiles\" / \"cas\" / \"iupac\" / \"cid\" / \"chebi\" )\n\n property_name = 1*( %x61-7A )\n\n test formula {\n accept \"H_2O\"\n accept \"H2O\"\n accept \"Ca^2+\"\n accept \"SO_4^2-\"\n accept \"C1-C-C-C-C-C1\"\n accept \"C12-C-C1-C2\"\n accept \"CH_3-CH_2-OH\"\n accept \"2H_2 + O_2 -> 2H_2O\"\n accept \"N_2 + 3H_2 <=>[Fe][400C] 2NH_3\"\n accept \"A ->[heat] B ->[cool] C\"\n accept \"A -> B -> C -> D\"\n accept \"Ca(OH)_2\"\n accept \"Fe(IV)\"\n accept \"_14C\"\n accept \"^14C\"\n accept \"CC(=O)OC1=CC=CC=C1C(=O)O\"\n accept \"Ca^2+ + CO_3^2- -> CaCO_3\"\n accept \"(R)-CH_3CH(OH)COOH\"\n accept \":O\"\n accept \"1s^2 2s^2 2p^6\"\n accept \"`x^2`\"\n accept \"\\\"nitrogen gas\\\"\"\n accept \"crystal[NaCl](a=5.64,sg=Fm-3m){Na@f(0,0,0)}\"\n accept \"spectrum[nmr](type=1H){1.2: 3H s \\\"CH3\\\"}\"\n accept \"calc(b3lyp){energy: -234.5}\"\n accept \"zmatrix{ C1 H2 C1 1.09 }\"\n accept \"mechanism{ step1: A -> B }\"\n accept \"H_2O @name(\\\"water\\\") @cas(\\\"7732-18-5\\\")\"\n accept \"H_2O @meta(\\\"source\\\",\\\"handbook\\\")\"\n accept \" H_2O \"\n accept \"C@(10,20)@R@m(2)@t(\\\"C1\\\")\"\n reject \"\"\n reject \"???\"\n reject \"H_\"\n reject \"C(=O\"\n reject \"+ H_2O\"\n reject \"crystal[NaCl\"\n reject \"zmatrix{ C1 \"\n reject \"@name(\\\"x\\\") H_2O\"\n }\n}\n","source_hash":"bb8a13d71e196f3294318305cc157f60781c14e212802d6afb27b91d20237d56","compiler_version":"1.3.94","checksum":"sha256:4b042741d854fdd52375ffbb1fc980159855716b3a88b4f4ec6431b546eff343"}
1
+ {"bindings":null,"version":"0.4.0","grammar":"AsciiChemCore","shape":"parsanol-tree/v2","binding_version":1,"entries":{"formula":{"root":"formula","grammar":{"atoms":[{"Entity":{"atom":5}},{"Str":{"pattern":" "}},{"Re":{"pattern":"[\\x09]"}},{"Re":{"pattern":"[\\x0a]"}},{"Re":{"pattern":"[\\x0d]"}},{"Alternative":{"atoms":[1,2,3,4]}},{"Repetition":{"atom":0,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":6,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":652}},{"Entity":{"atom":438}},{"Entity":{"atom":424}},{"Entity":{"atom":421}},{"Entity":{"atom":420}},{"Entity":{"atom":30}},{"Str":{"pattern":"("}},{"Entity":{"atom":26}},{"Str":{"pattern":"alpha"}},{"Str":{"pattern":"beta"}},{"Str":{"pattern":"R"}},{"Str":{"pattern":"S"}},{"Str":{"pattern":"E"}},{"Str":{"pattern":"Z"}},{"Str":{"pattern":"α"}},{"Str":{"pattern":"β"}},{"Str":{"pattern":"a"}},{"Str":{"pattern":"b"}},{"Alternative":{"atoms":[16,17,18,19,20,21,22,23,24,25]}},{"Named":{"name":"stereo","atom":15}},{"Str":{"pattern":")"}},{"Str":{"pattern":"-"}},{"Sequence":{"atoms":[14,27,28,29]}},{"Repetition":{"atom":13,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":43}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":33,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"("}},{"Str":{"pattern":"["}},{"Str":{"pattern":"{"}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Alternative":{"atoms":[35,36,37,38]}},{"Lookahead":{"atom":39,"positive":true}},{"Sequence":{"atoms":[34,40]}},{"Named":{"name":"value","atom":41}},{"Named":{"name":"coefficient","atom":42}},{"Repetition":{"atom":32,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":415}},{"Entity":{"atom":217}},{"Entity":{"atom":50}},{"Str":{"pattern":":"}},{"Repetition":{"atom":48,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"lone_pairs","atom":49}},{"Repetition":{"atom":47,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"^"}},{"Str":{"pattern":"_"}},{"Alternative":{"atoms":[52,53]}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":55,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[54,56]}},{"Named":{"name":"isotope","atom":57}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Repetition":{"atom":60,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[59,61]}},{"Named":{"name":"element","atom":62}},{"Entity":{"atom":124}},{"Str":{"pattern":"_"}},{"Entity":{"atom":77}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":68,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[69,70]}},{"Repetition":{"atom":71,"min":0,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[67,72,73]}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":75,"min":1,"max":null,"tag":"Repetition"}},{"Alternative":{"atoms":[74,76]}},{"Sequence":{"atoms":[65,66]}},{"Named":{"name":"subscript","atom":78}},{"Repetition":{"atom":79,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"^"}},{"Entity":{"atom":120}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":84,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[85,86]}},{"Repetition":{"atom":87,"min":0,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[83,88,89]}},{"Entity":{"atom":102}},{"Str":{"pattern":"("}},{"Re":{"pattern":"[I]"}},{"Re":{"pattern":"[V]"}},{"Re":{"pattern":"[X]"}},{"Re":{"pattern":"[L]"}},{"Re":{"pattern":"[C]"}},{"Re":{"pattern":"[D]"}},{"Alternative":{"atoms":[93,94,95,96,97,98]}},{"Repetition":{"atom":99,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[92,100,101]}},{"Entity":{"atom":118}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":104,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"+"}},{"Str":{"pattern":"-"}},{"Alternative":{"atoms":[106,107]}},{"Repetition":{"atom":108,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[105,109]}},{"Str":{"pattern":"+"}},{"Str":{"pattern":"-"}},{"Alternative":{"atoms":[111,112]}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":114,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":115,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[113,116]}},{"Alternative":{"atoms":[110,117]}},{"Alternative":{"atoms":[91,103]}},{"Alternative":{"atoms":[90,119]}},{"Sequence":{"atoms":[81,82]}},{"Named":{"name":"superscript","atom":121}},{"Repetition":{"atom":122,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[80,123]}},{"Entity":{"atom":128}},{"Str":{"pattern":"."}},{"Repetition":{"atom":126,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"radical_electrons","atom":127}},{"Repetition":{"atom":125,"min":0,"max":1,"tag":"Maybe"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":130,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"ring_closures","atom":131}},{"Repetition":{"atom":132,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":215}},{"Entity":{"atom":159}},{"Str":{"pattern":"@("}},{"Entity":{"atom":147}},{"Str":{"pattern":"-"}},{"Repetition":{"atom":138,"min":0,"max":1,"tag":"Maybe"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":140,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"."}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":143,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[142,144]}},{"Repetition":{"atom":145,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[139,141,146]}},{"Named":{"name":"x2","atom":137}},{"Str":{"pattern":","}},{"Entity":{"atom":147}},{"Named":{"name":"y2","atom":150}},{"Str":{"pattern":","}},{"Entity":{"atom":147}},{"Named":{"name":"z2","atom":153}},{"Sequence":{"atoms":[152,154]}},{"Repetition":{"atom":155,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":")"}},{"Named":{"name":"coord_close","atom":157}},{"Sequence":{"atoms":[136,148,149,151,156,158]}},{"Repetition":{"atom":135,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":167}},{"Str":{"pattern":"@"}},{"Str":{"pattern":"R"}},{"Str":{"pattern":"S"}},{"Alternative":{"atoms":[163,164]}},{"Named":{"name":"atom_parity","atom":165}},{"Sequence":{"atoms":[162,166]}},{"Repetition":{"atom":161,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":175}},{"Str":{"pattern":"@"}},{"Str":{"pattern":"TH1"}},{"Str":{"pattern":"TH2"}},{"Alternative":{"atoms":[171,172]}},{"Named":{"name":"chirality","atom":173}},{"Sequence":{"atoms":[170,174]}},{"Repetition":{"atom":169,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":184}},{"Str":{"pattern":"@m("}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":179,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"spin_multiplicity","atom":180}},{"Str":{"pattern":")"}},{"Named":{"name":"spin_close","atom":182}},{"Sequence":{"atoms":[178,181,183]}},{"Repetition":{"atom":177,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":199}},{"Str":{"pattern":"@t("}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":189,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[190,191]}},{"Repetition":{"atom":192,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"atom_title","atom":193}},{"Str":{"pattern":"\""}},{"Named":{"name":"title_quote","atom":195}},{"Str":{"pattern":")"}},{"Named":{"name":"title_close","atom":197}},{"Sequence":{"atoms":[187,188,194,196,198]}},{"Repetition":{"atom":186,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":213}},{"Str":{"pattern":"@f("}},{"Entity":{"atom":147}},{"Named":{"name":"x_fract","atom":203}},{"Str":{"pattern":","}},{"Entity":{"atom":147}},{"Named":{"name":"y_fract","atom":206}},{"Str":{"pattern":","}},{"Entity":{"atom":147}},{"Named":{"name":"z_fract","atom":209}},{"Str":{"pattern":")"}},{"Named":{"name":"fract_close","atom":211}},{"Sequence":{"atoms":[202,204,205,207,208,210,212]}},{"Repetition":{"atom":201,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[160,168,176,185,200,214]}},{"Sequence":{"atoms":[51,58,63,64,129,133,134]}},{"Named":{"name":"atom","atom":216}},{"Entity":{"atom":371}},{"Entity":{"atom":224}},{"Str":{"pattern":"("}},{"Str":{"pattern":"["}},{"Str":{"pattern":"{"}},{"Alternative":{"atoms":[220,221,222]}},{"Named":{"name":"open_bracket","atom":223}},{"Entity":{"atom":367}},{"Entity":{"atom":304}},{"Named":{"name":"reactants","atom":11}},{"Entity":{"atom":257}},{"Entity":{"atom":5}},{"Repetition":{"atom":229,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":230,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":237}},{"Str":{"pattern":"<=>"}},{"Str":{"pattern":"<->"}},{"Str":{"pattern":"->"}},{"Str":{"pattern":"<-"}},{"Alternative":{"atoms":[233,234,235,236]}},{"Named":{"name":"kind","atom":232}},{"Entity":{"atom":250}},{"Str":{"pattern":"["}},{"Str":{"pattern":"]"}},{"Lookahead":{"atom":241,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[242,243]}},{"Repetition":{"atom":244,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"text","atom":245}},{"Str":{"pattern":"]"}},{"Named":{"name":"condition_close","atom":247}},{"Sequence":{"atoms":[240,246,248]}},{"Named":{"name":"condition","atom":249}},{"Named":{"name":"above","atom":239}},{"Repetition":{"atom":251,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":250}},{"Named":{"name":"below","atom":253}},{"Repetition":{"atom":254,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[231,238,252,255]}},{"Named":{"name":"arrow","atom":256}},{"Entity":{"atom":5}},{"Repetition":{"atom":258,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":259,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":301}},{"Entity":{"atom":299}},{"Entity":{"atom":5}},{"Repetition":{"atom":263,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":264,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"+"}},{"Entity":{"atom":5}},{"Repetition":{"atom":267,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":268,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":297}},{"Entity":{"atom":292}},{"Str":{"pattern":"/"}},{"Named":{"name":"direction_up","atom":272}},{"Str":{"pattern":"\\"}},{"Named":{"name":"direction_down","atom":274}},{"Str":{"pattern":"##"}},{"Named":{"name":"quadruple","atom":276}},{"Str":{"pattern":">-"}},{"Named":{"name":"wedge","atom":278}},{"Str":{"pattern":"-<"}},{"Named":{"name":"hash","atom":280}},{"Str":{"pattern":"~>"}},{"Named":{"name":"dative","atom":282}},{"Str":{"pattern":"~~"}},{"Named":{"name":"wavy","atom":284}},{"Str":{"pattern":"#"}},{"Named":{"name":"triple","atom":286}},{"Str":{"pattern":"="}},{"Named":{"name":"double","atom":288}},{"Str":{"pattern":"-"}},{"Named":{"name":"single","atom":290}},{"Alternative":{"atoms":[273,275,277,279,281,283,285,287,289,291]}},{"Alternative":{"atoms":[45,271]}},{"Repetition":{"atom":293,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"units","atom":294}},{"Sequence":{"atoms":[31,44,295]}},{"Named":{"name":"molecule","atom":296}},{"Sequence":{"atoms":[265,266,269,270]}},{"Named":{"name":"plus_term","atom":298}},{"Repetition":{"atom":262,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[12,300]}},{"Named":{"name":"products","atom":261}},{"Sequence":{"atoms":[227,228,260,302]}},{"Named":{"name":"reaction","atom":303}},{"Entity":{"atom":327}},{"Entity":{"atom":317}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":307,"min":1,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"s"}},{"Str":{"pattern":"p"}},{"Str":{"pattern":"d"}},{"Str":{"pattern":"f"}},{"Str":{"pattern":"g"}},{"Str":{"pattern":"h"}},{"Alternative":{"atoms":[309,310,311,312,313,314]}},{"Sequence":{"atoms":[308,315]}},{"Named":{"name":"orbital","atom":316}},{"Str":{"pattern":"^"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":319,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"occupancy","atom":320}},{"Entity":{"atom":5}},{"Repetition":{"atom":322,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":323,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[306,318,321,324]}},{"Repetition":{"atom":325,"min":2,"max":null,"tag":"Repetition"}},{"Named":{"name":"electron_config","atom":326}},{"Entity":{"atom":297}},{"Entity":{"atom":345}},{"Repetition":{"atom":225,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"group_nodes","atom":330}},{"Entity":{"atom":337}},{"Str":{"pattern":")"}},{"Str":{"pattern":"]"}},{"Str":{"pattern":"}"}},{"Alternative":{"atoms":[333,334,335]}},{"Named":{"name":"close_bracket","atom":336}},{"Str":{"pattern":"_"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":339,"min":1,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[338,340]}},{"Named":{"name":"multiplicity","atom":341}},{"Repetition":{"atom":342,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[219,331,332,343]}},{"Named":{"name":"group","atom":344}},{"Entity":{"atom":357}},{"Str":{"pattern":"`"}},{"Str":{"pattern":"`"}},{"Lookahead":{"atom":348,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[349,350]}},{"Repetition":{"atom":351,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"math_source","atom":352}},{"Str":{"pattern":"`"}},{"Named":{"name":"math_close","atom":354}},{"Sequence":{"atoms":[347,353,355]}},{"Named":{"name":"embedded_math","atom":356}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":359,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[360,361]}},{"Repetition":{"atom":362,"min":0,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"\""}},{"Sequence":{"atoms":[358,363,364]}},{"Named":{"name":"group_text_run","atom":365}},{"Alternative":{"atoms":[226,305,328,329,346,366]}},{"Repetition":{"atom":225,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"group_nodes","atom":368}},{"Sequence":{"atoms":[219,369,332,343]}},{"Named":{"name":"group","atom":370}},{"Entity":{"atom":396}},{"Entity":{"atom":50}},{"Repetition":{"atom":373,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"H"}},{"Named":{"name":"element","atom":375}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Lookahead":{"atom":377,"positive":false}},{"Str":{"pattern":"_"}},{"Entity":{"atom":77}},{"Sequence":{"atoms":[379,380]}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":382,"min":1,"max":null,"tag":"Repetition"}},{"Alternative":{"atoms":[381,383]}},{"Named":{"name":"subscript","atom":384}},{"Repetition":{"atom":385,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"^"}},{"Entity":{"atom":120}},{"Sequence":{"atoms":[387,388]}},{"Named":{"name":"superscript","atom":389}},{"Repetition":{"atom":390,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":128}},{"Repetition":{"atom":392,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":215}},{"Sequence":{"atoms":[374,376,378,386,391,393,394]}},{"Named":{"name":"atom","atom":395}},{"Entity":{"atom":414}},{"Entity":{"atom":50}},{"Repetition":{"atom":398,"min":0,"max":1,"tag":"Maybe"}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Repetition":{"atom":401,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[400,402]}},{"Named":{"name":"element","atom":403}},{"Entity":{"atom":124}},{"Entity":{"atom":128}},{"Repetition":{"atom":406,"min":0,"max":1,"tag":"Maybe"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":408,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"ring_closures","atom":409}},{"Repetition":{"atom":410,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":215}},{"Sequence":{"atoms":[399,404,405,407,411,412]}},{"Named":{"name":"atom","atom":413}},{"Alternative":{"atoms":[46,218,372,397]}},{"Alternative":{"atoms":[45,271]}},{"Repetition":{"atom":416,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"units","atom":417}},{"Sequence":{"atoms":[31,44,418]}},{"Named":{"name":"molecule","atom":419}},{"Sequence":{"atoms":[12,300]}},{"Named":{"name":"reactants","atom":11}},{"Sequence":{"atoms":[422,228,260,302]}},{"Named":{"name":"reaction","atom":423}},{"Named":{"name":"first","atom":10}},{"Entity":{"atom":434}},{"Entity":{"atom":257}},{"Entity":{"atom":5}},{"Repetition":{"atom":428,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":429,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":421}},{"Named":{"name":"products","atom":431}},{"Sequence":{"atoms":[427,430,432]}},{"Named":{"name":"segment","atom":433}},{"Repetition":{"atom":426,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"segments","atom":435}},{"Sequence":{"atoms":[425,436]}},{"Named":{"name":"cascade","atom":437}},{"Entity":{"atom":424}},{"Entity":{"atom":327}},{"Entity":{"atom":477}},{"Str":{"pattern":"crystal"}},{"Str":{"pattern":"["}},{"Str":{"pattern":"]"}},{"Lookahead":{"atom":444,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[445,446]}},{"Repetition":{"atom":447,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"crystal_name","atom":448}},{"Str":{"pattern":"]"}},{"Named":{"name":"crystal_name_close","atom":450}},{"Sequence":{"atoms":[443,449,451]}},{"Repetition":{"atom":452,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"("}},{"Str":{"pattern":")"}},{"Lookahead":{"atom":455,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[456,457]}},{"Repetition":{"atom":458,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"crystal_params","atom":459}},{"Str":{"pattern":")"}},{"Named":{"name":"crystal_params_close","atom":461}},{"Sequence":{"atoms":[454,460,462]}},{"Repetition":{"atom":463,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":466,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[467,468]}},{"Repetition":{"atom":469,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"crystal_body","atom":470}},{"Str":{"pattern":"}"}},{"Named":{"name":"crystal_body_close","atom":472}},{"Sequence":{"atoms":[465,471,473]}},{"Repetition":{"atom":474,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[442,453,464,475]}},{"Named":{"name":"crystal_node","atom":476}},{"Entity":{"atom":514}},{"Str":{"pattern":"spectrum"}},{"Str":{"pattern":"["}},{"Str":{"pattern":"]"}},{"Lookahead":{"atom":481,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[482,483]}},{"Repetition":{"atom":484,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"spectrum_type","atom":485}},{"Str":{"pattern":"]"}},{"Named":{"name":"spectrum_type_close","atom":487}},{"Sequence":{"atoms":[480,486,488]}},{"Repetition":{"atom":489,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"("}},{"Str":{"pattern":")"}},{"Lookahead":{"atom":492,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[493,494]}},{"Repetition":{"atom":495,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"spectrum_params","atom":496}},{"Str":{"pattern":")"}},{"Named":{"name":"spectrum_params_close","atom":498}},{"Sequence":{"atoms":[491,497,499]}},{"Repetition":{"atom":500,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":503,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[504,505]}},{"Repetition":{"atom":506,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"spectrum_body","atom":507}},{"Str":{"pattern":"}"}},{"Named":{"name":"spectrum_body_close","atom":509}},{"Sequence":{"atoms":[502,508,510]}},{"Repetition":{"atom":511,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[479,490,501,512]}},{"Named":{"name":"spectrum_node","atom":513}},{"Entity":{"atom":540}},{"Str":{"pattern":"calc"}},{"Str":{"pattern":"("}},{"Str":{"pattern":")"}},{"Lookahead":{"atom":518,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[519,520]}},{"Repetition":{"atom":521,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"calc_params","atom":522}},{"Str":{"pattern":")"}},{"Named":{"name":"calc_params_close","atom":524}},{"Sequence":{"atoms":[517,523,525]}},{"Repetition":{"atom":526,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":529,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[530,531]}},{"Repetition":{"atom":532,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"calc_body","atom":533}},{"Str":{"pattern":"}"}},{"Named":{"name":"calc_body_close","atom":535}},{"Sequence":{"atoms":[528,534,536]}},{"Repetition":{"atom":537,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[516,527,538]}},{"Named":{"name":"calc_node","atom":539}},{"Entity":{"atom":555}},{"Str":{"pattern":"zmatrix"}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":544,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[545,546]}},{"Repetition":{"atom":547,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"zmatrix_body","atom":548}},{"Str":{"pattern":"}"}},{"Named":{"name":"zmatrix_body_close","atom":550}},{"Sequence":{"atoms":[543,549,551]}},{"Repetition":{"atom":552,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[542,553]}},{"Named":{"name":"zmatrix_node","atom":554}},{"Entity":{"atom":570}},{"Str":{"pattern":"mechanism"}},{"Str":{"pattern":"{"}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":559,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[560,561]}},{"Repetition":{"atom":562,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"mechanism_body","atom":563}},{"Str":{"pattern":"}"}},{"Named":{"name":"mechanism_body_close","atom":565}},{"Sequence":{"atoms":[558,564,566]}},{"Repetition":{"atom":567,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[557,568]}},{"Named":{"name":"mechanism_node","atom":569}},{"Entity":{"atom":640}},{"Entity":{"atom":420}},{"Named":{"name":"mol","atom":572}},{"Entity":{"atom":636}},{"Entity":{"atom":5}},{"Repetition":{"atom":575,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":576,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":600}},{"Str":{"pattern":"@meta("}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":581,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[582,583]}},{"Repetition":{"atom":584,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"meta_key","atom":585}},{"Str":{"pattern":"\""}},{"Str":{"pattern":","}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":590,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[591,592]}},{"Repetition":{"atom":593,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"meta_value","atom":594}},{"Str":{"pattern":"\""}},{"Named":{"name":"meta_value_close","atom":596}},{"Str":{"pattern":")"}},{"Named":{"name":"meta_close","atom":598}},{"Sequence":{"atoms":[579,580,586,587,588,589,595,597,599]}},{"Entity":{"atom":633}},{"Str":{"pattern":"@"}},{"Entity":{"atom":614}},{"Str":{"pattern":"name"}},{"Str":{"pattern":"title"}},{"Str":{"pattern":"formula"}},{"Str":{"pattern":"label"}},{"Str":{"pattern":"inchi"}},{"Str":{"pattern":"smiles"}},{"Str":{"pattern":"cas"}},{"Str":{"pattern":"iupac"}},{"Str":{"pattern":"cid"}},{"Str":{"pattern":"chebi"}},{"Alternative":{"atoms":[604,605,606,607,608,609,610,611,612,613]}},{"Entity":{"atom":617}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Repetition":{"atom":616,"min":1,"max":null,"tag":"Repetition"}},{"Alternative":{"atoms":[603,615]}},{"Named":{"name":"ann_type","atom":618}},{"Str":{"pattern":"("}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":622,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[623,624]}},{"Repetition":{"atom":625,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"ann_value","atom":626}},{"Str":{"pattern":"\""}},{"Named":{"name":"ann_value_close","atom":628}},{"Str":{"pattern":")"}},{"Named":{"name":"ann_close","atom":630}},{"Sequence":{"atoms":[619,620,621,627,629,631]}},{"Sequence":{"atoms":[602,632]}},{"Alternative":{"atoms":[578,601]}},{"Sequence":{"atoms":[577,634]}},{"Named":{"name":"annotation_item","atom":635}},{"Repetition":{"atom":574,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"annotations","atom":637}},{"Sequence":{"atoms":[573,638]}},{"Named":{"name":"annotated_molecule","atom":639}},{"Entity":{"atom":420}},{"Entity":{"atom":357}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"\""}},{"Lookahead":{"atom":644,"positive":false}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[645,646]}},{"Repetition":{"atom":647,"min":0,"max":null,"tag":"Repetition"}},{"Str":{"pattern":"\""}},{"Sequence":{"atoms":[643,648,649]}},{"Named":{"name":"text_run","atom":650}},{"Alternative":{"atoms":[9,439,440,441,478,515,541,556,571,641,642,651]}},{"Entity":{"atom":5}},{"Repetition":{"atom":653,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":654,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":652}},{"Sequence":{"atoms":[655,656]}},{"Repetition":{"atom":657,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[8,658]}},{"Named":{"name":"formula","atom":659}},{"Entity":{"atom":5}},{"Repetition":{"atom":661,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":662,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[7,660,663]}}],"root":664},"bindings":[]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule formula_node: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 1 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 1 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 2 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 2 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 3 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 3 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 4 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule formula_node: branches 9 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule arrow_kind: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule arrow_kind: branches 1 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule arrow_kind: branches 2 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule stereo_letter: branches 1 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule stereo_letter: branches 2 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule stereo_letter: branches 7 and 8 are order-dependent (shared first bytes; ordered choice is decisive)","rule unit: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule unit: branches 1 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule unit: branches 3 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule group_unit: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule group_unit: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule group_unit: branches 1 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule group_unit: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule group_unit: branches 3 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule bond: branches 3 and 8 are order-dependent (shared first bytes; ordered choice is decisive)","rule bond: branches 5 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule bond: branches 6 and 7 are order-dependent (shared first bytes; ordered choice is decisive)","rule known_annotation_type: branches 5 and 8 are order-dependent (shared first bytes; ordered choice is decisive)","rule known_annotation_type: branches 7 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule known_annotation_type: branches 7 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule known_annotation_type: branches 9 and 10 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"formula","render":{},"derive":{},"tests":[],"docs":{"formula":"spaces? >> nodes.as(:formula) >> spaces?","formula_node":"Top-level nodes, DSL ordered-choice order (cascade before plain\nreaction, annotations before bare molecule).","cascade":"Two or more chained reactions: the transform promotes the chain\nto a ReactionCascade (first leg + arrow/products segments).","reaction":"Reactants, arrow (with optional above/below conditions), products.","arrow":"arrow.as(:arrow) {kind, above?, below?} — leading spaces ride the\narrow, conditions attach with no gap.","molecule":"A molecule: stereochemistry prefix, optional coefficient, units.","stereo_prefix":"`(R)-` style prefixes; a/b are the alpha/beta shorthand.","coefficient":"Coefficient: digits only when an element or bracket follows (else\nthe digits are subscripts/rings). Captured as {value}.","unit":"Units: prefixed (isotope) atoms, bracket groups, hydrogen\n(bare-digit subscripts allowed), plain atoms.","prefixed_atom":"Prefix isotope: ^14C or _14C — the marker binds to the atom, the\ncore semantic fix over AsciiMath. Captured with the marker.","group":"Bracket group: ( ... ) [ ... ] { ... } with optional multiplicity.","group_unit":"Inside a group: reactions, molecules, electron configs, embedded\nmath and quoted text recurse (DSL group_node set).","hydrogen_atom":"Hydrogen: bare digits after H are subscripts (H cannot ring-\nclose), but H must not steal the H of He, Hf...","atom_suffix":"Suffix markers consume `_` / `^` but capture marker + value\n(AtomBuilder strips the marker).","charge":"Charges: 2+, +2, 2 (bare digits fall through to the same capture).","oxidation_state":"Oxidation state: (IV), (II)...","lewis_prefix":"Lewis markers: leading colons = lone pairs; trailing dots =\nradical electrons.","atom_annotation":"Atom annotations, DSL order: coordinates, parity, multiplicity,\ntitle, fractional coordinates.","chirality_annotation":"Tetrahedral parity classes (SMILES @/@@ semantics, spelled\nexplicitly beside the CIP @R/@S label).","bond":"Bonds capture under their kind name (transform contract), longest\nfirst so `##` beats `#`; `->` is reserved for reactions.","crystal":"crystal[Name](a=X,b=Y,sg=SG){atoms with @f(x,y,z)}","spectrum":"spectrum[type](params){peak data}","calculation":"calc(method/basis){key: value units}","zmatrix":"zmatrix{lines}","mechanism":"mechanism{steps}","electron_config":"`1s2 2s2 2p6` — at least two orbital terms (DSL repeat(2)).","embedded_math":"Backtick-delimited Plurimath/AsciiMath source (delimiters stripped\nby the transform).","annotated_molecule":"Free-form text uses \"...\" delimiters, matching AsciiMath; the\ncapture includes the quotes (TextNormaliser strips them).\n@name(\"aspirin\") @cas(\"50-78-2\") @meta(\"k\",\"v\") — the annotation\nvocabulary rides the molecule.","metadata_annotation":"Two quoted args, comma-separated; keys distinguish metadata from\nregular annotations.","simple_annotation":"Known types (name, inchi, ...) handled specially by the transform;\nany other lowercase word becomes a property."},"source":"grammar AsciiChemCore version \"0.4.0\" {\n ## The AsciiChem syntax — full grammar, DSL-shaped captures\n ## (P1b, asciichem-ruby#96). Capture names and nesting mirror\n ## GrammarRules exactly so the PARG tree adapts to the same shape\n ## TransformRules consumes: sequence captures merge into one hash\n ## (parens to attach `as`), repetitions stay arrays, unwrapped\n ## rules let their captures ride into the parent context. Absent\n ## optionals omit their key (the tree adapter reinstates the ones\n ## transform patterns require, e.g. group multiplicity). Structural\n ## rules (molecule/reaction/group/annotated_molecule) are captured\n ## and the adapter unwraps them: PARG merge semantics for captured\n ## sequences with embedded repetitions yield arrays, and uncaptured\n ## sequence rules drop captures non-deterministically in reps\n ## (parsanol findings).\n ##\n ## Language notes (P0 findings): entries reference rules; sequences\n ## take no trailing capture (wrap in parens); [ ] is the option\n ## syntax so literal brackets are strings; until is exclusive\n ## (restate the closer); strict PEG — no repetition backtracking.\n\n entry formula: formula\n\n ## spaces? >> nodes.as(:formula) >> spaces?\n formula = [ 1*( ws ) ] ( formula_node 0*( [ 1*( ws ) ] formula_node ) ) as formula [ 1*( ws ) ]\n\n ws = ( \" \" / %x09 / %x0A / %x0D )\n\n ## Top-level nodes, DSL ordered-choice order (cascade before plain\n ## reaction, annotations before bare molecule).\n formula_node = cascade / reaction / electron_config / crystal / spectrum / calculation / zmatrix / mechanism / annotated_molecule / molecule / embedded_math / ( \"\\\"\" until \"\\\"\" \"\\\"\" ) as text_run\n\n # -- reactions ---------------------------------------------------------\n\n ## Two or more chained reactions: the transform promotes the chain\n ## to a ReactionCascade (first leg + arrow/products segments).\n cascade = ( ( reaction ) as first ( 1*( cascade_segment ) ) as segments ) as cascade\n\n cascade_segment = ( arrow [ 1*( ws ) ] ( terms as products ) ) as segment\n\n ## Reactants, arrow (with optional above/below conditions), products.\n reaction = ( ( terms as reactants ) arrow [ 1*( ws ) ] ( terms as products ) ) as reaction\n\n terms = molecule 0*( plus_term )\n\n plus_term = ( [ 1*( ws ) ] \"+\" [ 1*( ws ) ] molecule ) as plus_term\n\n ## arrow.as(:arrow) {kind, above?, below?} — leading spaces ride the\n ## arrow, conditions attach with no gap.\n arrow = ( [ 1*( ws ) ] ( arrow_kind ) as kind [ condition as above ] [ condition as below ] ) as arrow\n\n arrow_kind = ( \"<=>\" / \"<->\" / \"->\" / \"<-\" )\n\n condition = ( \"[\" ( until \"]\" ) as text ( \"]\" ) as condition_close ) as condition\n\n ## A molecule: stereochemistry prefix, optional coefficient, units.\n molecule = ( [ stereo_prefix ] [ coefficient ] ( 1*( unit / bond ) ) as units ) as molecule\n\n ## `(R)-` style prefixes; a/b are the alpha/beta shorthand.\n stereo_prefix = \"(\" ( stereo_letter ) as stereo \")\" \"-\"\n\n stereo_letter = ( \"alpha\" / \"beta\" / \"R\" / \"S\" / \"E\" / \"Z\" / \"α\" / \"β\" / \"a\" / \"b\" )\n\n ## Coefficient: digits only when an element or bracket follows (else\n ## the digits are subscripts/rings). Captured as {value}.\n coefficient = ( ( 1*(%x30-39) &( \"(\" / \"[\" / \"{\" / %x41-5A ) ) as value ) as coefficient\n\n ## Units: prefixed (isotope) atoms, bracket groups, hydrogen\n ## (bare-digit subscripts allowed), plain atoms.\n unit = prefixed_atom / group / hydrogen_atom / plain_atom\n\n ## Prefix isotope: ^14C or _14C — the marker binds to the atom, the\n ## core semantic fix over AsciiMath. Captured with the marker.\n prefixed_atom = ( [ lewis_prefix ] ( ( \"^\" / \"_\" ) 1*(%x30-39) ) as isotope ( %x41-5A [ %x61-7A ] ) as element atom_suffix [ lewis_radicals ] [ ( 1*(%x30-39) ) as ring_closures ] atom_annotation ) as atom\n\n ## Bracket group: ( ... ) [ ... ] { ... } with optional multiplicity.\n group = ( group_open ( 1*( group_unit ) ) as group_nodes group_close [ ( \"_\" 1*(%x30-39) ) as multiplicity ] ) as group\n\n group_open = ( \"(\" / \"[\" / \"{\" ) as open_bracket\n\n group_close = ( \")\" / \"]\" / \"}\" ) as close_bracket\n\n ## Inside a group: reactions, molecules, electron configs, embedded\n ## math and quoted text recurse (DSL group_node set).\n group_unit = reaction / electron_config / molecule / group / embedded_math / ( \"\\\"\" until \"\\\"\" \"\\\"\" ) as group_text_run\n\n ## Hydrogen: bare digits after H are subscripts (H cannot ring-\n ## close), but H must not steal the H of He, Hf...\n hydrogen_atom = ( [ lewis_prefix ] ( \"H\" ) as element !( %x61-7A ) [ ( \"_\" subscript_value / 1*(%x30-39) ) as subscript ] [ ( \"^\" superscript_value ) as superscript ] [ lewis_radicals ] atom_annotation ) as atom\n\n plain_atom = ( [ lewis_prefix ] ( %x41-5A [ %x61-7A ] ) as element atom_suffix [ lewis_radicals ] [ ( 1*(%x30-39) ) as ring_closures ] atom_annotation ) as atom\n\n ## Suffix markers consume `_` / `^` but capture marker + value\n ## (AtomBuilder strips the marker).\n atom_suffix = [ ( \"_\" subscript_value ) as subscript ] [ ( \"^\" superscript_value ) as superscript ]\n\n subscript_value = ( \"{\" until \"}\" \"}\" ) / 1*(%x30-39)\n\n superscript_value = ( \"{\" until \"}\" \"}\" ) / ( oxidation_state / charge )\n\n ## Charges: 2+, +2, 2 (bare digits fall through to the same capture).\n charge = ( 1*(%x30-39) [ \"+\" / \"-\" ] ) / ( ( \"+\" / \"-\" ) [ 1*(%x30-39) ] )\n\n ## Oxidation state: (IV), (II)...\n oxidation_state = \"(\" 1*( %x49 / %x56 / %x58 / %x4C / %x43 / %x44 ) \")\"\n\n ## Lewis markers: leading colons = lone pairs; trailing dots =\n ## radical electrons.\n lewis_prefix = 1*( \":\" ) as lone_pairs\n\n lewis_radicals = 1*( \".\" ) as radical_electrons\n\n ## Atom annotations, DSL order: coordinates, parity, multiplicity,\n ## title, fractional coordinates.\n atom_annotation = [ coordinate_annotation ] [ parity_annotation ] [ chirality_annotation ] [ multiplicity_annotation ] [ title_annotation ] [ fractional_annotation ]\n\n coordinate_annotation = \"@(\" ( float_number ) as x2 \",\" ( float_number ) as y2 [ \",\" ( float_number ) as z2 ] ( \")\" ) as coord_close\n\n parity_annotation = \"@\" ( \"R\" / \"S\" ) as atom_parity\n\n ## Tetrahedral parity classes (SMILES @/@@ semantics, spelled\n ## explicitly beside the CIP @R/@S label).\n chirality_annotation = \"@\" ( \"TH1\" / \"TH2\" ) as chirality\n\n multiplicity_annotation = \"@m(\" ( 1*(%x30-39) ) as spin_multiplicity ( \")\" ) as spin_close\n\n title_annotation = \"@t(\" \"\\\"\" ( until \"\\\"\" ) as atom_title ( \"\\\"\" ) as title_quote ( \")\" ) as title_close\n\n fractional_annotation = \"@f(\" ( float_number ) as x_fract \",\" ( float_number ) as y_fract \",\" ( float_number ) as z_fract ( \")\" ) as fract_close\n\n float_number = [ \"-\" ] 1*(%x30-39) [ \".\" 0*(%x30-39) ]\n\n # -- bonds ---------------------------------------------------------------\n\n ## Bonds capture under their kind name (transform contract), longest\n ## first so `##` beats `#`; `->` is reserved for reactions.\n bond = ( \"/\" as direction_up ) / ( \"\\\\\" as direction_down ) / ( \"##\" as quadruple ) / ( \">-\" as wedge ) / ( \"-<\" as hash ) / ( \"~>\" as dative ) / ( \"~~\" as wavy ) / ( \"#\" as triple ) / ( \"=\" as double ) / ( \"-\" as single )\n\n # -- beyond-formulas constructs -----------------------------------------\n\n ## crystal[Name](a=X,b=Y,sg=SG){atoms with @f(x,y,z)}\n crystal = ( \"crystal\" [ ( \"[\" ( until \"]\" ) as crystal_name ( \"]\" ) as crystal_name_close ) ] [ ( \"(\" ( until \")\" ) as crystal_params ( \")\" ) as crystal_params_close ) ] [ ( \"{\" ( until \"}\" ) as crystal_body ( \"}\" ) as crystal_body_close ) ] ) as crystal_node\n\n ## spectrum[type](params){peak data}\n spectrum = ( \"spectrum\" [ ( \"[\" ( until \"]\" ) as spectrum_type ( \"]\" ) as spectrum_type_close ) ] [ ( \"(\" ( until \")\" ) as spectrum_params ( \")\" ) as spectrum_params_close ) ] [ ( \"{\" ( until \"}\" ) as spectrum_body ( \"}\" ) as spectrum_body_close ) ] ) as spectrum_node\n\n ## calc(method/basis){key: value units}\n calculation = ( \"calc\" [ ( \"(\" ( until \")\" ) as calc_params ( \")\" ) as calc_params_close ) ] [ ( \"{\" ( until \"}\" ) as calc_body ( \"}\" ) as calc_body_close ) ] ) as calc_node\n\n ## zmatrix{lines}\n zmatrix = ( \"zmatrix\" [ ( \"{\" ( until \"}\" ) as zmatrix_body ( \"}\" ) as zmatrix_body_close ) ] ) as zmatrix_node\n\n ## mechanism{steps}\n mechanism = ( \"mechanism\" [ ( \"{\" ( until \"}\" ) as mechanism_body ( \"}\" ) as mechanism_body_close ) ] ) as mechanism_node\n\n # -- electron configuration ----------------------------------------------\n\n ## `1s2 2s2 2p6` — at least two orbital terms (DSL repeat(2)).\n electron_config = 2*( orbital \"^\" ( 1*(%x30-39) ) as occupancy [ 1*( ws ) ] ) as electron_config\n\n orbital = ( 1*(%x30-39) ( \"s\" / \"p\" / \"d\" / \"f\" / \"g\" / \"h\" ) ) as orbital\n\n # -- embedded math and text ------------------------------------------------\n\n ## Backtick-delimited Plurimath/AsciiMath source (delimiters stripped\n ## by the transform).\n embedded_math = ( \"`\" ( until \"`\" ) as math_source ( \"`\" ) as math_close ) as embedded_math\n\n ## Free-form text uses \"...\" delimiters, matching AsciiMath; the\n ## capture includes the quotes (TextNormaliser strips them).\n\n # -- molecule annotations ---------------------------------------------------\n\n ## @name(\"aspirin\") @cas(\"50-78-2\") @meta(\"k\",\"v\") — the annotation\n ## vocabulary rides the molecule.\n annotated_molecule = ( ( molecule ) as mol ( 1*( annotation_item ) ) as annotations ) as annotated_molecule\n\n annotation_item = ( [ 1*( ws ) ] ( metadata_annotation / simple_annotation ) ) as annotation_item\n\n ## Two quoted args, comma-separated; keys distinguish metadata from\n ## regular annotations.\n metadata_annotation = \"@meta(\" \"\\\"\" ( until \"\\\"\" ) as meta_key \"\\\"\" \",\" \"\\\"\" ( until \"\\\"\" ) as meta_value ( \"\\\"\" ) as meta_value_close ( \")\" ) as meta_close\n\n ## Known types (name, inchi, ...) handled specially by the transform;\n ## any other lowercase word becomes a property.\n simple_annotation = \"@\" ( ( known_annotation_type / property_name ) as ann_type \"(\" \"\\\"\" ( until \"\\\"\" ) as ann_value ( \"\\\"\" ) as ann_value_close ( \")\" ) as ann_close )\n\n known_annotation_type = ( \"name\" / \"title\" / \"formula\" / \"label\" / \"inchi\" / \"smiles\" / \"cas\" / \"iupac\" / \"cid\" / \"chebi\" )\n\n property_name = 1*( %x61-7A )\n\n test formula {\n accept \"H_2O\"\n accept \"H2O\"\n accept \"Ca^2+\"\n accept \"SO_4^2-\"\n accept \"C1-C-C-C-C-C1\"\n accept \"C12-C-C1-C2\"\n accept \"CH_3-CH_2-OH\"\n accept \"2H_2 + O_2 -> 2H_2O\"\n accept \"N_2 + 3H_2 <=>[Fe][400C] 2NH_3\"\n accept \"A ->[heat] B ->[cool] C\"\n accept \"A -> B -> C -> D\"\n accept \"Ca(OH)_2\"\n accept \"Fe(IV)\"\n accept \"_14C\"\n accept \"^14C\"\n accept \"CC(=O)OC1=CC=CC=C1C(=O)O\"\n accept \"Ca^2+ + CO_3^2- -> CaCO_3\"\n accept \"(R)-CH_3CH(OH)COOH\"\n accept \":O\"\n accept \"1s^2 2s^2 2p^6\"\n accept \"`x^2`\"\n accept \"\\\"nitrogen gas\\\"\"\n accept \"crystal[NaCl](a=5.64,sg=Fm-3m){Na@f(0,0,0)}\"\n accept \"spectrum[nmr](type=1H){1.2: 3H s \\\"CH3\\\"}\"\n accept \"calc(b3lyp){energy: -234.5}\"\n accept \"zmatrix{ C1 H2 C1 1.09 }\"\n accept \"mechanism{ step1: A -> B }\"\n accept \"H_2O @name(\\\"water\\\") @cas(\\\"7732-18-5\\\")\"\n accept \"H_2O @meta(\\\"source\\\",\\\"handbook\\\")\"\n accept \" H_2O \"\n accept \"C@(10,20)@R@m(2)@t(\\\"C1\\\")\"\n reject \"\"\n reject \"???\"\n reject \"H_\"\n reject \"C(=O\"\n reject \"+ H_2O\"\n reject \"crystal[NaCl\"\n reject \"zmatrix{ C1 \"\n reject \"@name(\\\"x\\\") H_2O\"\n }\n}\n","source_hash":"a94cfb4f59936a381e9974e8c141c661e56a25babe46d64b0a288bfd0b4b3e3d","compiler_version":"1.3.94","checksum":"sha256:2891ce4d795e13bdbfe556f50174e2575fa5798f13e8300e402b2a05f68c05e5"}
@@ -32,7 +32,7 @@ module AsciiChem
32
32
  class PargEngine
33
33
  # Version the vendored artifact was built from; the load fails
34
34
  # loudly on drift so the engine never runs a stale grammar.
35
- PARG_GRAMMAR_VERSION = '0.3.1'
35
+ PARG_GRAMMAR_VERSION = '0.4.0'
36
36
 
37
37
  ARTIFACT_PATH = File.expand_path('parg/asciichem.json', __dir__)
38
38
 
@@ -64,6 +64,7 @@ module AsciiChem
64
64
  parts << "#{coord})"
65
65
  end
66
66
  parts << "@#{atom.atom_parity}" if atom.atom_parity
67
+ parts << "@#{atom.chirality}" if atom.chirality
67
68
  parts << "@m(#{atom.spin_multiplicity})" if atom.spin_multiplicity
68
69
  parts << %(@t("#{atom.atom_title}")) if atom.atom_title
69
70
  if atom.x_fract && atom.y_fract && atom.z_fract
@@ -83,6 +84,11 @@ module AsciiChem
83
84
  end
84
85
 
85
86
  def visit_bond(bond)
87
+ # Directional single bonds spell the direction instead of the
88
+ # plain dash (the round-trip contract: text re-parses to the
89
+ # same model).
90
+ return { up: '/', down: '\\' }.fetch(bond.direction) if bond.direction
91
+
86
92
  bond.ascii
87
93
  end
88
94
 
@@ -263,7 +263,9 @@ module AsciiChem
263
263
  # by the reaction arrow)
264
264
  # wavy `~~` (resonance / delocalised)
265
265
  rule(:bond) do
266
- str('##').as(:quadruple) |
266
+ str('/').as(:direction_up) |
267
+ str('\\').as(:direction_down) |
268
+ str('##').as(:quadruple) |
267
269
  str('>-').as(:wedge) |
268
270
  str('-<').as(:hash) |
269
271
  str('~>').as(:dative) |
@@ -306,6 +308,7 @@ module AsciiChem
306
308
  rule(:atom_annotation) do
307
309
  coordinate_annotation.maybe >>
308
310
  parity_annotation.maybe >>
311
+ chirality_annotation.maybe >>
309
312
  multiplicity_annotation.maybe >>
310
313
  atom_title_annotation.maybe >>
311
314
  fractional_annotation.maybe
@@ -315,6 +318,12 @@ module AsciiChem
315
318
  str('@') >> (str('R') | str('S')).as(:atom_parity)
316
319
  end
317
320
 
321
+ # Tetrahedral parity classes (SMILES @/@@ semantics, spelled
322
+ # explicitly to stay unambiguous beside the CIP @R/@S label).
323
+ rule(:chirality_annotation) do
324
+ str('@') >> (str('TH1') | str('TH2')).as(:chirality)
325
+ end
326
+
318
327
  rule(:coordinate_annotation) do
319
328
  str('@(') >>
320
329
  float_number.as(:x2) >> str(',') >>
@@ -51,11 +51,10 @@ module AsciiChem
51
51
  end
52
52
 
53
53
  def count_molecule(molecule)
54
- coefficient = molecule.coefficient.to_i
55
- coefficient = 1 if coefficient.zero?
56
54
  raw = enumerate(molecule, 1)
57
55
  return nil if raw.nil?
58
56
 
57
+ coefficient = molecule.stoichiometric_coefficient
59
58
  raw.transform_values { |v| v * coefficient }
60
59
  end
61
60
 
@@ -11,9 +11,11 @@ module AsciiChem
11
11
  class ValenceCheck < Base
12
12
  register :valence
13
13
 
14
+ # Runs over the perception pass's graph view: every atom's bond
15
+ # order counts ALL its bonds (rings and branches included — the
16
+ # linear chain approximation missed both).
14
17
  def run(formula)
15
18
  diagnostics = []
16
- # Walk molecules so we know which atom is the bonding context.
17
19
  walk(formula) do |node|
18
20
  next unless node.is_a?(AsciiChem::Model::Molecule)
19
21
 
@@ -25,34 +27,17 @@ module AsciiChem
25
27
  private
26
28
 
27
29
  def check_molecule(molecule, diagnostics)
28
- # Walk the molecule's nodes; pair each atom with the bonds
29
- # immediately adjacent to it in the chain.
30
- bond_count = 0
31
- molecule.children.each do |node|
32
- case node
33
- when AsciiChem::Model::Bond
34
- bond_count += bond_order(node)
35
- when AsciiChem::Model::Atom
36
- check_atom(node, bond_count, diagnostics)
37
- bond_count = 0
38
- when AsciiChem::Model::Group
39
- # Group's internal bonds aren't visible here; reset.
40
- bond_count = 0
41
- end
30
+ atoms, edges = AsciiChem::Structure::Graph.build(molecule)
31
+ atoms.each_with_index do |atom, index|
32
+ check_atom(atom, bond_order_sum(index, edges), diagnostics)
42
33
  end
43
34
  end
44
35
 
45
- def bond_order(bond)
46
- case bond.kind
47
- when :single then 1
48
- when :double then 2
49
- when :triple then 3
50
- when :quadruple then 4
51
- else 1
52
- end
36
+ def bond_order_sum(index, edges)
37
+ AsciiChem::Perception.bond_order_sum(index, edges)
53
38
  end
54
39
 
55
- def check_atom(atom, incoming_bond_order, diagnostics)
40
+ def check_atom(atom, load, diagnostics)
56
41
  max = AsciiChem::PeriodicTable.max_valence(atom.element)
57
42
  if max.nil?
58
43
  diagnostics << info(
@@ -62,30 +47,13 @@ module AsciiChem
62
47
  return
63
48
  end
64
49
 
65
- # Approximate: an atom with subscript has multiplicity, not
66
- # extra bonds. We use incoming_bond_order + |charge| as the
67
- # rough load.
68
- charge = atom.charge.to_s
69
- charge_value = parse_charge_magnitude(charge)
70
- load = incoming_bond_order + charge_value
71
50
  return if load <= max
72
51
 
73
52
  diagnostics << error(
74
- "Atom #{atom.element} has load #{load} (bond order #{incoming_bond_order} + charge #{charge_value}); max valence is #{max}",
53
+ "Atom #{atom.element} has bond order #{load}; max valence is #{max}",
75
54
  node: atom
76
55
  )
77
56
  end
78
-
79
- def parse_charge_magnitude(charge)
80
- return 0 if charge.nil? || charge.empty?
81
-
82
- match = charge.match(/\A(?<n>\d*)(?<sign>[+-])\z/) ||
83
- charge.match(/\A(?<sign>[+-])(?<n>\d*)\z/)
84
- return 0 unless match
85
-
86
- n = match[:n].empty? ? 1 : match[:n].to_i
87
- n
88
- end
89
57
  end
90
58
  end
91
59
  end