Class: AsciiChem::Formatter::Text
- Defined in:
- lib/asciichem/formatter/text.rb
Overview
Renders a Model tree as canonical AsciiChem text. Round-trip
conformance: AsciiChem.parse(s).to_text == s for any conformant
s. The formatter is the canonicaliser — equivalent inputs map
to the same output.
Canonicalisation rules (v1):
- Explicit subscript marker:
H_2, notH2. - Coefficient before molecule:
2H_2O. - Isotope binds to atom:
^14C(no{}carrier). - Charge is number-then-sign per IUPAC:
Ca^2+. - Oxidation state in roman numerals with parens:
Fe^(III). - Group brackets preserved from input.
- Reaction arrows use the canonical ASCII spelling (
->,<-,<=>,<->).
Instance Method Summary collapse
- #atom_annotation(atom) ⇒ Object
- #format_coord(value) ⇒ Object
- #molecule_annotations(molecule) ⇒ Object
- #visit_atom(atom) ⇒ Object
- #visit_bond(bond) ⇒ Object
- #visit_electron_configuration(ec) ⇒ Object
- #visit_embedded_math(em) ⇒ Object
- #visit_formula(formula) ⇒ Object
- #visit_group(group) ⇒ Object
- #visit_molecule(molecule) ⇒ Object
- #visit_reaction(reaction) ⇒ Object
- #visit_reaction_cascade(cascade) ⇒ Object
- #visit_text(text) ⇒ Object
Methods inherited from Base
Instance Method Details
#atom_annotation(atom) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/asciichem/formatter/text.rb', line 59 def atom_annotation(atom) annotation = +"" if atom.x2 && atom.y2 annotation << "@(#{format_coord(atom.x2)},#{format_coord(atom.y2)}" annotation << ",#{format_coord(atom.z2)}" if atom.z2 annotation << ")" elsif atom.atom_parity annotation << "@#{atom.atom_parity}" end annotation end |
#format_coord(value) ⇒ Object
71 72 73 |
# File 'lib/asciichem/formatter/text.rb', line 71 def format_coord(value) value == value.to_i ? value.to_i.to_s : value.to_s end |
#molecule_annotations(molecule) ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/asciichem/formatter/text.rb', line 32 def molecule_annotations(molecule) parts = [] molecule.names.each { |n| parts << %(@name("#{n.content}")) } molecule.identifiers.each { |i| parts << %(@#{i.convention}("#{i.value}")) } parts << %(@title("#{molecule.title}")) if molecule.title molecule.formulas.each { |f| parts << %(@formula("#{f[:concise]}")) if f[:concise] } molecule.labels.each { |l| parts << %(@label("#{l[:value]}")) if l[:value] } molecule.properties.each { |p| parts << %(@#{p[:title]}("#{p[:value]}")) if p[:title] && p[:value] } molecule..each { |m| parts << %(@meta("#{m[:name]}","#{m[:content]}")) } parts.empty? ? "" : " #{parts.join}" end |
#visit_atom(atom) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/asciichem/formatter/text.rb', line 44 def visit_atom(atom) parts = [] parts << (":" * atom.lone_pairs) if atom.lone_pairs parts << "^#{atom.isotope}" if atom.isotope parts << atom.element parts << "_#{atom.subscript}" if atom.subscript parts << "^#{atom.superscript}" if atom.superscript parts << "^#{atom.charge}" if atom.charge parts << "^(#{atom.oxidation_state})" if atom.oxidation_state parts << ("." * atom.radical_electrons) if atom.radical_electrons parts << atom.ring_closures.to_s if atom.ring_closures parts << atom_annotation(atom) parts.join end |
#visit_bond(bond) ⇒ Object
81 82 83 |
# File 'lib/asciichem/formatter/text.rb', line 81 def visit_bond(bond) bond.ascii end |
#visit_electron_configuration(ec) ⇒ Object
108 109 110 111 112 |
# File 'lib/asciichem/formatter/text.rb', line 108 def visit_electron_configuration(ec) parts = ec.orbitals.map { |orb, occ| "#{orb}^#{occ}" } parts << ec.term_symbol.to_s if ec.term_symbol parts.join(" ") end |
#visit_embedded_math(em) ⇒ Object
114 115 116 |
# File 'lib/asciichem/formatter/text.rb', line 114 def (em) "`#{em.source}`" end |
#visit_formula(formula) ⇒ Object
20 21 22 |
# File 'lib/asciichem/formatter/text.rb', line 20 def visit_formula(formula) formula.nodes.map { |n| render_node(n) }.join(" ") end |
#visit_group(group) ⇒ Object
75 76 77 78 79 |
# File 'lib/asciichem/formatter/text.rb', line 75 def visit_group(group) body = group.nodes.map { |n| render_node(n) }.join suffix = group.multiplicity ? "_#{group.multiplicity}" : "" "#{group.open_char}#{body}#{group.close_char}#{suffix}" end |
#visit_molecule(molecule) ⇒ Object
24 25 26 27 28 29 30 |
# File 'lib/asciichem/formatter/text.rb', line 24 def visit_molecule(molecule) prefix = molecule.coefficient.nil? || molecule.coefficient.empty? ? "" : molecule.coefficient.to_s stereo = molecule.stereo ? "(#{molecule.stereo_letter})-" : "" body = molecule.nodes.map { |n| render_node(n) }.join annotations = molecule_annotations(molecule) "#{stereo}#{prefix}#{body}#{annotations}" end |
#visit_reaction(reaction) ⇒ Object
85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/asciichem/formatter/text.rb', line 85 def visit_reaction(reaction) left = reaction.reactants.map { |n| render_node(n) }.join(" + ") right = reaction.products.map { |n| render_node(n) }.join(" + ") arrow = reaction.arrow_ascii conds = reaction.conditions return "#{left} #{arrow} #{right}" unless conds above = conds.above ? "[#{conds.above}]" : "" below = conds.below ? "[#{conds.below}]" : "" "#{left} #{arrow}#{above}#{below} #{right}" end |
#visit_reaction_cascade(cascade) ⇒ Object
97 98 99 100 101 102 103 104 105 106 |
# File 'lib/asciichem/formatter/text.rb', line 97 def visit_reaction_cascade(cascade) return "" if cascade.steps.empty? head = cascade.steps.first out = "#{render_terms(head.reactants)} #{render_arrow_with_conditions(head)} #{render_terms(head.products)}" cascade.steps.drop(1).each do |step| out += " #{render_arrow_with_conditions(step)} #{render_terms(step.products)}" end out end |
#visit_text(text) ⇒ Object
118 119 120 |
# File 'lib/asciichem/formatter/text.rb', line 118 def visit_text(text) %("#{text.content}") end |