asciichem 0.31.0 → 0.31.2
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 +4 -4
- data/CHANGELOG.md +31 -0
- data/Gemfile +1 -1
- data/asciichem.gemspec +1 -1
- data/lib/asciichem/engine/parg/asciichem.json +1 -1
- data/lib/asciichem/engine/parg_engine.rb +1 -1
- data/lib/asciichem/formatter/text.rb +6 -0
- data/lib/asciichem/grammar_rules.rb +10 -1
- data/lib/asciichem/linter/valence_check.rb +10 -42
- data/lib/asciichem/model/atom.rb +4 -1
- data/lib/asciichem/model/bond.rb +25 -17
- data/lib/asciichem/model/reaction.rb +18 -0
- data/lib/asciichem/perception.rb +117 -0
- data/lib/asciichem/reaction_roles.rb +43 -0
- data/lib/asciichem/resolver/opsin.rb +68 -0
- data/lib/asciichem/resolver.rb +1 -0
- data/lib/asciichem/smiles/parser.rb +67 -35
- data/lib/asciichem/smiles/writer.rb +99 -34
- data/lib/asciichem/structure/graph.rb +3 -2
- data/lib/asciichem/structure/linearizer.rb +6 -3
- data/lib/asciichem/transform_rules.rb +6 -1
- data/lib/asciichem/version.rb +1 -1
- data/lib/asciichem/wire/core.rb +4 -0
- data/lib/asciichem/wire_adapter.rb +8 -4
- data/lib/asciichem.rb +2 -0
- data/scripts/generate-conformance-cases +57 -0
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b221962f73e48a95f8d177ffd94fdf051b135f70d014b9b42aaa5d7fb1005840
|
|
4
|
+
data.tar.gz: b2b40147701ba90d0b8b9ed014202836fbc65cbb22f14fa98f3f1195cdd3b7bd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75aa3b257cb79d94a5fb7308b89b4a9ce1fb827dfbe073e96723fdfbe8c8f8991ef1e37d0a0af44b4c8e52c318811f63baf064cb2db646995420f6733e3114da
|
|
7
|
+
data.tar.gz: a33d70d38648147044a6eedf5a3e0afe171331862a7f08ea561ee52e3438bb3d3750636b85a2cbfd1d4a06e16937bb57f60616ee2a10f415ab479764741ef066
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,37 @@
|
|
|
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.2] - 2026-10-20
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- `Reaction#stoichiometry` (TODO.v2/13 item 4, slice 2): the
|
|
10
|
+
coefficients are already model data; the reaction now exposes them
|
|
11
|
+
normalized — per side, coefficient-free canonical species text to
|
|
12
|
+
integer (absent coefficient = 1, duplicate species sum). Pure
|
|
13
|
+
model read; wire format unchanged.
|
|
14
|
+
- OPSIN source adapter (item 3): name-to-structure derivation via
|
|
15
|
+
the OPSIN service, feeding the resolver pipeline
|
|
16
|
+
(`AsciiChem::Resolver::Opsin`).
|
|
17
|
+
- Reaction role hints (item 4, slice A): `ReactionRoles.roles_for`
|
|
18
|
+
classifies conditions into catalyst/solvent/reagent hints per the
|
|
19
|
+
above/below-arrow convention.
|
|
20
|
+
- The perception pass (item 2): `AsciiChem::Perception` derives
|
|
21
|
+
implicit hydrogens and aromatic rings as pure model services; the
|
|
22
|
+
ValenceCheck linter consumes it.
|
|
23
|
+
- Differential conformance harness (item 5): 300 seeded generated
|
|
24
|
+
cases shared through asciichem-tests (`corpus/generated/models.json`),
|
|
25
|
+
exercised by the Ruby property suite and by TS/PY gates.
|
|
26
|
+
|
|
27
|
+
## [0.31.1] - 2026-10-20
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- `chemicalml` floor raised to `~> 0.4` (from `~> 0.3.0`, which
|
|
31
|
+
excluded the current line): the 0.4 line carries the CML
|
|
32
|
+
content-model completions and the Ruby 4.0 logger fix. Validated
|
|
33
|
+
2024/2024 under both parse engines against chemicalml 0.4.1.
|
|
34
|
+
Gemfile dev: `parsanol ~> 1.3.94` (the PARG artifact runtime the
|
|
35
|
+
`:parg` engine requires), validated against 1.3.95.
|
|
36
|
+
|
|
6
37
|
## [0.31.0] - 2026-10-19
|
|
7
38
|
|
|
8
39
|
### Changed
|
data/Gemfile
CHANGED
data/asciichem.gemspec
CHANGED
|
@@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
|
|
|
35
35
|
|
|
36
36
|
# Floors are the versions the corpus suite validates against
|
|
37
37
|
# (pessimistic ~>); raise them only with a full-suite run.
|
|
38
|
-
spec.add_dependency "chemicalml", "~> 0.
|
|
38
|
+
spec.add_dependency "chemicalml", "~> 0.4"
|
|
39
39
|
spec.add_dependency "elkrb", "~> 2.0"
|
|
40
40
|
spec.add_dependency "lutaml-model", "~> 0.8"
|
|
41
41
|
spec.add_dependency "mml", "~> 2.3"
|
|
@@ -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.
|
|
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('
|
|
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(',') >>
|
|
@@ -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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
46
|
-
|
|
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,
|
|
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
|
|
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
|
data/lib/asciichem/model/atom.rb
CHANGED
|
@@ -35,7 +35,7 @@ module AsciiChem
|
|
|
35
35
|
:charge, :oxidation_state,
|
|
36
36
|
:lone_pairs, :radical_electrons,
|
|
37
37
|
:ring_closures, :aromatic, :hydrogens,
|
|
38
|
-
:x2, :y2, :z2, :atom_parity,
|
|
38
|
+
:x2, :y2, :z2, :atom_parity, :chirality,
|
|
39
39
|
:spin_multiplicity, :atom_title,
|
|
40
40
|
:x_fract, :y_fract, :z_fract
|
|
41
41
|
|
|
@@ -60,6 +60,7 @@ module AsciiChem
|
|
|
60
60
|
lone_pairs: nil, radical_electrons: nil,
|
|
61
61
|
ring_closures: nil, aromatic: nil, hydrogens: nil,
|
|
62
62
|
x2: nil, y2: nil, z2: nil, atom_parity: nil,
|
|
63
|
+
chirality: nil,
|
|
63
64
|
spin_multiplicity: nil, atom_title: nil,
|
|
64
65
|
x_fract: nil, y_fract: nil, z_fract: nil)
|
|
65
66
|
@element = element
|
|
@@ -77,6 +78,7 @@ module AsciiChem
|
|
|
77
78
|
@y2 = y2
|
|
78
79
|
@z2 = z2
|
|
79
80
|
@atom_parity = atom_parity
|
|
81
|
+
@chirality = chirality
|
|
80
82
|
@spin_multiplicity = spin_multiplicity
|
|
81
83
|
@atom_title = atom_title
|
|
82
84
|
@x_fract = x_fract
|
|
@@ -92,6 +94,7 @@ module AsciiChem
|
|
|
92
94
|
ring_closures: ring_closures, aromatic: aromatic,
|
|
93
95
|
hydrogens: hydrogens,
|
|
94
96
|
x2: x2, y2: y2, z2: z2, atom_parity: atom_parity,
|
|
97
|
+
chirality: chirality,
|
|
95
98
|
spin_multiplicity: spin_multiplicity, atom_title: atom_title,
|
|
96
99
|
x_fract: x_fract, y_fract: y_fract, z_fract: z_fract }
|
|
97
100
|
end
|