nx_chem_smiles_emit.nx
buildroot/runtime/nx_chem_smiles_emit.nx
about
nx_chem_smiles_emit.nx -- C2.3b milestone: SMILES emit from MolGraph.
DFS-based emit with:
- Organic-subset atoms emitted without brackets when possible
(no charge, no isotope, no stereo, no map_num, no h_count override)
- Bracket atoms for everything else
- Bond markers emitted only when non-default
(single between aliphatic = default; aromatic between aromatics = default)
- Ring closures via single digits 1..9 then %10..%99
- Branches via parentheses
- Multi-component graphs via '.' separator
Round-trip property: parse -> emit -> parse -> emit reaches a fixed
point. Two different input SMILES of the same molecule do NOT yet
produce byte-identical output -- that requires Morgan canonical
ranking (C2.3c).
Honest gaps (deferred):
- Morgan canonical atom ranking (C2.3c) -- unlocks bit-reproducible
canonical SMILES across versions/platforms (landscape EXCEED axis E1)
- Stereo emit (@ / @@ for atoms, / \\ for bonds) (C2.3c after canonical)
- Bond stereo E/Z resolution (C2.3c CIP rules)
nx_safety_envelope:
intended_use: MolGraph -> SMILES string emit; round-trip clean
sil_target: SIL1
evidence: [C2.3b KAT in nx_chem_smiles_emit_test.nx; bounded buffer writes; round-trip via parse]
verdict: BENCH-PENDING
dependencies 4 imports · 3 importers
imports: nx_chem.nxnx_chem_molecule.nxnx_chem_smiles.nxnx_chem_morgan.nx
imported by: nx_chem_morgan_test.nxnx_chem_smiles_emit_test.nxnx_chem_stereo_test.nx
structs
| 42 | struct EmitState |
consts
| 35 | const NX_MAX_BACK_PER_ATOM: nx_int = 8 // generous cap; molecules rarely > 4 ring closures per atom |
| 36 | const NX_MAX_TREE_PER_ATOM: nx_int = 8 // organic atoms have <= 4 bonds typically |
| 58 | const NX_EMIT_STATE_BYTES: nx_int = 96 |
functions
| 63 | func nx_chem_emit_state_new(n_atoms: nx_int) -> *EmitState |
| 89 | func nx_chem_emit_byte(buf: *u8, len_io: *nx_int, cap: nx_int, b: nx_int) -> nx_int |
| 99 | func nx_chem_emit_uint(buf: *u8, len_io: *nx_int, cap: nx_int, v: nx_int) -> nx_int |
| 124 | func nx_chem_emit_ring_digit(buf: *u8, len_io: *nx_int, cap: nx_int, d: nx_int) -> nx_int |
| 139 | func nx_chem_emit_bond_marker(buf: *u8, len_io: *nx_int, cap: nx_int, order: nx_int, both_aromatic: nx_int, stereo: nx_int) -> nx_int |
| 167 | func nx_chem_z_to_organic_aliphatic(z: nx_int, c1_out: *nx_int, c2_out: *nx_int) -> nx_int called by 1: nx_chem_emit_atom_with_stereo |
| 184 | func nx_chem_z_to_aromatic_lower(z: nx_int) -> nx_int called by 1: nx_chem_emit_atom_with_stereo |
| 198 | func nx_chem_z_to_bracket_symbol(z: nx_int, c1_out: *nx_int, c2_out: *nx_int) -> nx_int called by 1: nx_chem_emit_atom_with_stereo |
| 336 | func nx_chem_atom_needs_brackets(a: *Atom) -> nx_int called by 1: nx_chem_emit_atom_with_stereo |
| 361 | func nx_chem_emit_atom_with_stereo(buf: *u8, len_io: *nx_int, cap: nx_int, a: *Atom, stereo_override: nx_int) -> nx_int |
| 463 | func nx_chem_emit_atom(buf: *u8, len_io: *nx_int, cap: nx_int, a: *Atom) -> nx_int calls 1: nx_chem_emit_atom_with_stereo |
| 480 | func nx_chem_emit_prescan(m: *MolGraph, s: *EmitState, root: nx_int) -> nx_int called by 1: nx_chem_emit_smiles |
| 583 | func nx_chem_compute_stereo_parity(s: *EmitState, atom_idx: nx_int, a: *Atom) -> nx_int called by 1: nx_chem_emit_subtree |
| 658 | func nx_chem_emit_subtree( |
| 719 | func nx_chem_emit_prescan_canonical(m: *MolGraph, s: *EmitState, root: nx_int, rank: *nx_int) -> nx_int called by 1: nx_chem_emit_canonical_smiles |
| 876 | func nx_chem_emit_canonical_smiles(m: *MolGraph, out_buf: *u8, cap: nx_int, out_len_io: *nx_int) -> nx_int |
| 921 | func nx_chem_emit_smiles(m: *MolGraph, out_buf: *u8, cap: nx_int, out_len_io: *nx_int) -> nx_int |