code wiki / (root) / nx_chem_smiles_test.nx

nx_chem_smiles_test.nx source

↩ module page · 653 lines · 26180 B

1// nx_chem_smiles_test.nx -- C2.1 KAT for the basic SMILES parser. 2// 3// Each test case calls nx_chem_parse_smiles with a literal SMILES, 4// verifies the resulting MolGraph has the expected atom + bond 5// structure, and (for negative tests) verifies the structured 6// error-code path. 7// 8// All test SMILES are well-known reference molecules; expected 9// graphs hand-verified against OpenSMILES specification examples. 10// 11// expect_exit: 0 12// 13// license_tier: ORIGINAL 14 15import "nx_chem_molecule.nx" 16import "nx_chem_smiles.nx" 17 18// ================================================================= 19// Helper: get atom Z by index (assumes valid). 20// ================================================================= 21func tz(m: *MolGraph, i: nx_int) -> nx_int { 22 let a: *Atom = nx_chem_mol_atom(m, i) 23 return a.z 24} 25 26// Helper: get bond order by index. 27func tbo(m: *MolGraph, i: nx_int) -> nx_int { 28 let b: *Bond = nx_chem_mol_bond(m, i) 29 return b.order 30} 31 32// Helper: parse a 1-char SMILES (fits in 8 bytes for sys_mmap). 33func mk1(c: nx_int) -> *MolGraph { 34 let s: *u8 = sys_mmap(8) 35 s[0] = c & 0xff 36 return nx_chem_parse_smiles(s, 1) 37} 38 39// Helper: parse a 2-char SMILES. 40func mk2(c1: nx_int, c2: nx_int) -> *MolGraph { 41 let s: *u8 = sys_mmap(8) 42 s[0] = c1 & 0xff 43 s[1] = c2 & 0xff 44 return nx_chem_parse_smiles(s, 2) 45} 46 47// Helper: parse a 3-char SMILES. 48func mk3(c1: nx_int, c2: nx_int, c3: nx_int) -> *MolGraph { 49 let s: *u8 = sys_mmap(8) 50 s[0] = c1 & 0xff 51 s[1] = c2 & 0xff 52 s[2] = c3 & 0xff 53 return nx_chem_parse_smiles(s, 3) 54} 55 56// Helper: parse N-char SMILES. 57func mkn(bytes: *u8, n: nx_int) -> *MolGraph { 58 return nx_chem_parse_smiles(bytes, n) 59} 60 61// ================================================================= 62// A -- single organic atoms. 63// ================================================================= 64func a_single_atoms() -> nx_int { 65 // "O" -> 1 atom O, 0 bonds 66 let m1: *MolGraph = mk1(0x4F) // 'O' 67 if m1.is_valid != 1 { return 11 } 68 if m1.n_atoms != 1 { return 12 } 69 if m1.n_bonds != 0 { return 13 } 70 if tz(m1, 0) != 8 { return 14 } 71 72 // "C" -> 1 atom C 73 let m2: *MolGraph = mk1(0x43) // 'C' 74 if m2.n_atoms != 1 { return 15 } 75 if tz(m2, 0) != 6 { return 16 } 76 77 // "N" -> 1 atom N 78 let m3: *MolGraph = mk1(0x4E) // 'N' 79 if m3.n_atoms != 1 { return 17 } 80 if tz(m3, 0) != 7 { return 18 } 81 82 // "F" -> 1 atom F 83 let m4: *MolGraph = mk1(0x46) // 'F' 84 if m4.n_atoms != 1 { return 19 } 85 if tz(m4, 0) != 9 { return 20 } 86 87 return 0 88} 89 90// ================================================================= 91// B -- two-char organic atoms Cl, Br. 92// ================================================================= 93func b_two_char_organic() -> nx_int { 94 // "Cl" -> 1 atom Cl 95 let s1: *u8 = sys_mmap(8); s1[0] = 0x43; s1[1] = 0x6C // 'C' 'l' 96 let m1: *MolGraph = nx_chem_parse_smiles(s1, 2) 97 if m1.is_valid != 1 { return 31 } 98 if m1.n_atoms != 1 { return 32 } 99 if tz(m1, 0) != 17 { return 33 } // Cl 100 101 // "Br" -> 1 atom Br 102 let s2: *u8 = sys_mmap(8); s2[0] = 0x42; s2[1] = 0x72 103 let m2: *MolGraph = nx_chem_parse_smiles(s2, 2) 104 if m2.is_valid != 1 { return 34 } 105 if tz(m2, 0) != 35 { return 35 } // Br 106 107 return 0 108} 109 110// ================================================================= 111// C -- explicit single/double/triple bonds. 112// ================================================================= 113func c_bonds() -> nx_int { 114 // "CC" -> C-C single bond (default) 115 let m1: *MolGraph = mk2(0x43, 0x43) 116 if m1.is_valid != 1 { return 41 } 117 if m1.n_atoms != 2 { return 42 } 118 if m1.n_bonds != 1 { return 43 } 119 if tbo(m1, 0) != NX_BOND_SINGLE { return 44 } 120 121 // "C=C" -> C=C double bond 122 let m2: *MolGraph = mk3(0x43, 0x3D, 0x43) // C = C 123 if m2.is_valid != 1 { return 45 } 124 if m2.n_bonds != 1 { return 46 } 125 if tbo(m2, 0) != NX_BOND_DOUBLE { return 47 } 126 127 // "C#C" -> C#C triple bond 128 let m3: *MolGraph = mk3(0x43, 0x23, 0x43) // C # C 129 if m3.is_valid != 1 { return 48 } 130 if tbo(m3, 0) != NX_BOND_TRIPLE { return 49 } 131 132 return 0 133} 134 135// ================================================================= 136// D -- ethanol "CCO". 137// ================================================================= 138func d_ethanol() -> nx_int { 139 let m: *MolGraph = mk3(0x43, 0x43, 0x4F) // C C O 140 if m.is_valid != 1 { return 51 } 141 if m.n_atoms != 3 { return 52 } 142 if m.n_bonds != 2 { return 53 } 143 if tz(m, 0) != 6 { return 54 } 144 if tz(m, 1) != 6 { return 55 } 145 if tz(m, 2) != 8 { return 56 } 146 if tbo(m, 0) != NX_BOND_SINGLE { return 57 } 147 if tbo(m, 1) != NX_BOND_SINGLE { return 58 } 148 let b0: *Bond = nx_chem_mol_bond(m, 0) 149 if b0.a != 0 { return 59 } 150 if b0.b != 1 { return 60 } 151 let b1: *Bond = nx_chem_mol_bond(m, 1) 152 if b1.a != 1 { return 61 } 153 if b1.b != 2 { return 62 } 154 return 0 155} 156 157// ================================================================= 158// E -- isobutane "CC(C)C" -- tests branch open/close. 159// Graph: C0-C1, C1-C2 (branch), C1-C3 160// Atoms: C0=0, C1=1, C2=2, C3=3 161// Bonds: 0-1, 1-2, 1-3 (3 bonds total) 162// ================================================================= 163func e_isobutane() -> nx_int { 164 let s: *u8 = sys_mmap(8) 165 s[0] = 0x43; s[1] = 0x43; s[2] = 0x28; s[3] = 0x43; s[4] = 0x29; s[5] = 0x43 166 let m: *MolGraph = nx_chem_parse_smiles(s, 6) 167 if m.is_valid != 1 { return 71 } 168 if m.n_atoms != 4 { return 72 } 169 if m.n_bonds != 3 { return 73 } 170 let b0: *Bond = nx_chem_mol_bond(m, 0) 171 if b0.a != 0 { return 74 } 172 if b0.b != 1 { return 75 } 173 let b1: *Bond = nx_chem_mol_bond(m, 1) 174 if b1.a != 1 { return 76 } 175 if b1.b != 2 { return 77 } 176 let b2: *Bond = nx_chem_mol_bond(m, 2) 177 if b2.a != 1 { return 78 } 178 if b2.b != 3 { return 79 } 179 return 0 180} 181 182// ================================================================= 183// F -- benzene "c1ccccc1" -- aromatic + ring closure. 184// 6 atoms, 6 bonds, all aromatic, all in ring. 185// ================================================================= 186func f_benzene() -> nx_int { 187 let s: *u8 = sys_mmap(16) 188 s[0] = 0x63; s[1] = 0x31 // c1 189 s[2] = 0x63; s[3] = 0x63 // cc 190 s[4] = 0x63; s[5] = 0x63 // cc 191 s[6] = 0x63; s[7] = 0x31 // c1 192 let m: *MolGraph = nx_chem_parse_smiles(s, 8) 193 if m.is_valid != 1 { return 91 } 194 if m.n_atoms != 6 { return 92 } 195 if m.n_bonds != 6 { return 93 } 196 var i: nx_int = 0 197 while i < 6 { 198 let a: *Atom = nx_chem_mol_atom(m, i) 199 if a.z != 6 { return 100 + i } 200 if a.aromaticity != NX_AROM_DAYLIGHT { return 110 + i } 201 i = i + 1 202 } 203 // bond 0..4: aromatic chain; bond 5: ring-closure 204 var j: nx_int = 0 205 while j < 6 { 206 let b: *Bond = nx_chem_mol_bond(m, j) 207 if b.order != NX_BOND_AROMATIC { return 120 + j } 208 j = j + 1 209 } 210 // ring-closure bond connects atom 5 -> atom 0 211 let b5: *Bond = nx_chem_mol_bond(m, 5) 212 if b5.a != 5 { return 131 } 213 if b5.b != 0 { return 132 } 214 if b5.in_ring != 1 { return 133 } 215 return 0 216} 217 218// ================================================================= 219// G -- cyclohexane "C1CCCCC1" -- aliphatic ring, all single bonds. 220// ================================================================= 221func g_cyclohexane() -> nx_int { 222 let s: *u8 = sys_mmap(16) 223 s[0] = 0x43; s[1] = 0x31 // C1 224 s[2] = 0x43; s[3] = 0x43 // CC 225 s[4] = 0x43; s[5] = 0x43 // CC 226 s[6] = 0x43; s[7] = 0x31 // C1 227 let m: *MolGraph = nx_chem_parse_smiles(s, 8) 228 if m.is_valid != 1 { return 141 } 229 if m.n_atoms != 6 { return 142 } 230 if m.n_bonds != 6 { return 143 } 231 var j: nx_int = 0 232 while j < 6 { 233 if tbo(m, j) != NX_BOND_SINGLE { return 150 + j } 234 j = j + 1 235 } 236 return 0 237} 238 239// ================================================================= 240// H -- bracket atoms. 241// ================================================================= 242func h_brackets() -> nx_int { 243 // "[Na+]" -> 1 atom Na, charge +1 244 let s1: *u8 = sys_mmap(16) 245 s1[0] = 0x5B; s1[1] = 0x4E; s1[2] = 0x61; s1[3] = 0x2B; s1[4] = 0x5D 246 let m1: *MolGraph = nx_chem_parse_smiles(s1, 5) 247 if m1.is_valid != 1 { return 161 } 248 if m1.n_atoms != 1 { return 162 } 249 let a1: *Atom = nx_chem_mol_atom(m1, 0) 250 if a1.z != 11 { return 163 } 251 if a1.charge != 1 { return 164 } 252 253 // "[Cl-]" -> 1 atom Cl, charge -1 254 let s2: *u8 = sys_mmap(16) 255 s2[0] = 0x5B; s2[1] = 0x43; s2[2] = 0x6C; s2[3] = 0x2D; s2[4] = 0x5D 256 let m2: *MolGraph = nx_chem_parse_smiles(s2, 5) 257 if m2.is_valid != 1 { return 165 } 258 let a2: *Atom = nx_chem_mol_atom(m2, 0) 259 if a2.z != 17 { return 166 } 260 if a2.charge != -1 { return 167 } 261 262 // "[CH4]" -> 1 atom C with h_count=4 263 let s3: *u8 = sys_mmap(16) 264 s3[0] = 0x5B; s3[1] = 0x43; s3[2] = 0x48; s3[3] = 0x34; s3[4] = 0x5D 265 let m3: *MolGraph = nx_chem_parse_smiles(s3, 5) 266 if m3.is_valid != 1 { return 168 } 267 let a3: *Atom = nx_chem_mol_atom(m3, 0) 268 if a3.z != 6 { return 169 } 269 if a3.h_count != 4 { return 170 } 270 271 // "[NH4+]" -> ammonium ion 272 let s4: *u8 = sys_mmap(16) 273 s4[0] = 0x5B; s4[1] = 0x4E; s4[2] = 0x48; s4[3] = 0x34; s4[4] = 0x2B; s4[5] = 0x5D 274 let m4: *MolGraph = nx_chem_parse_smiles(s4, 6) 275 if m4.is_valid != 1 { return 171 } 276 let a4: *Atom = nx_chem_mol_atom(m4, 0) 277 if a4.z != 7 { return 172 } 278 if a4.h_count != 4 { return 173 } 279 if a4.charge != 1 { return 174 } 280 281 // "[13C]" -> isotope 13 of carbon 282 let s5: *u8 = sys_mmap(16) 283 s5[0] = 0x5B; s5[1] = 0x31; s5[2] = 0x33; s5[3] = 0x43; s5[4] = 0x5D 284 let m5: *MolGraph = nx_chem_parse_smiles(s5, 5) 285 if m5.is_valid != 1 { return 175 } 286 let a5: *Atom = nx_chem_mol_atom(m5, 0) 287 if a5.z != 6 { return 176 } 288 if a5.isotope != 13 { return 177 } 289 290 // "[Hg+2]" -> mercury(II) cation (supplement-arc heavy metal) 291 let s6: *u8 = sys_mmap(16) 292 s6[0] = 0x5B; s6[1] = 0x48; s6[2] = 0x67; s6[3] = 0x2B; s6[4] = 0x32; s6[5] = 0x5D 293 let m6: *MolGraph = nx_chem_parse_smiles(s6, 6) 294 if m6.is_valid != 1 { return 178 } 295 let a6: *Atom = nx_chem_mol_atom(m6, 0) 296 if a6.z != 80 { return 179 } 297 if a6.charge != 2 { return 180 } 298 299 // "[Pb+2]" -> lead(II) cation 300 let s7: *u8 = sys_mmap(16) 301 s7[0] = 0x5B; s7[1] = 0x50; s7[2] = 0x62; s7[3] = 0x2B; s7[4] = 0x32; s7[5] = 0x5D 302 let m7: *MolGraph = nx_chem_parse_smiles(s7, 6) 303 if m7.is_valid != 1 { return 181 } 304 let a7: *Atom = nx_chem_mol_atom(m7, 0) 305 if a7.z != 82 { return 182 } 306 if a7.charge != 2 { return 183 } 307 308 return 0 309} 310 311// ================================================================= 312// I -- wildcard "*". 313// ================================================================= 314func i_wildcard() -> nx_int { 315 let m: *MolGraph = mk1(0x2A) 316 if m.is_valid != 1 { return 191 } 317 if m.n_atoms != 1 { return 192 } 318 if tz(m, 0) != 0 { return 193 } 319 return 0 320} 321 322// ================================================================= 323// J -- disconnect "CC.O" -> two fragments. 324// ================================================================= 325func j_disconnect() -> nx_int { 326 let s: *u8 = sys_mmap(8) 327 s[0] = 0x43; s[1] = 0x43; s[2] = 0x2E; s[3] = 0x4F 328 let m: *MolGraph = nx_chem_parse_smiles(s, 4) 329 if m.is_valid != 1 { return 201 } 330 if m.n_atoms != 3 { return 202 } 331 if m.n_bonds != 1 { return 203 } // only C-C, no C-O after '.' 332 return 0 333} 334 335// ================================================================= 336// K -- negative tests: structured error codes. 337// ================================================================= 338func k_errors() -> nx_int { 339 // empty input 340 let s_empty: *u8 = sys_mmap(8) 341 let m1: *MolGraph = nx_chem_parse_smiles(s_empty, 0) 342 if m1.is_valid != 0 { return 211 } 343 if m1.err_code != NX_MOL_ERR_SMILES_EMPTY { return 212 } 344 345 // unclosed bracket "[Na" (no closing ]) 346 let s2: *u8 = sys_mmap(8) 347 s2[0] = 0x5B; s2[1] = 0x4E; s2[2] = 0x61 348 let m2: *MolGraph = nx_chem_parse_smiles(s2, 3) 349 if m2.is_valid != 0 { return 213 } 350 if m2.err_code != NX_MOL_ERR_SMILES_UNCLOSED_BR { return 214 } 351 352 // unclosed paren "C(" 353 let s3: *u8 = sys_mmap(8) 354 s3[0] = 0x43; s3[1] = 0x28 355 let m3: *MolGraph = nx_chem_parse_smiles(s3, 2) 356 if m3.is_valid != 0 { return 215 } 357 if m3.err_code != NX_MOL_ERR_SMILES_UNCLOSED_PAREN { return 216 } 358 359 // orphan ring "C1" (ring digit never closed) 360 let s4: *u8 = sys_mmap(8) 361 s4[0] = 0x43; s4[1] = 0x31 362 let m4: *MolGraph = nx_chem_parse_smiles(s4, 2) 363 if m4.is_valid != 0 { return 217 } 364 if m4.err_code != NX_MOL_ERR_SMILES_ORPHAN_RING { return 218 } 365 366 // bad char (uppercase letter not in organic subset, e.g. "X") 367 let s5: *u8 = sys_mmap(8) 368 s5[0] = 0x58 // 'X' 369 let m5: *MolGraph = nx_chem_parse_smiles(s5, 1) 370 if m5.is_valid != 0 { return 219 } 371 if m5.err_code != NX_MOL_ERR_SMILES_BAD_CHAR { return 220 } 372 373 // bracket atom with unknown symbol "[Xx]" 374 let s6: *u8 = sys_mmap(8) 375 s6[0] = 0x5B; s6[1] = 0x58; s6[2] = 0x78; s6[3] = 0x5D 376 let m6: *MolGraph = nx_chem_parse_smiles(s6, 4) 377 if m6.is_valid != 0 { return 221 } 378 if m6.err_code != NX_MOL_ERR_SMILES_INVALID_BR { return 222 } 379 380 return 0 381} 382 383// ================================================================= 384// L -- diethyl ether "CCOCC". 385// ================================================================= 386func l_diethyl_ether() -> nx_int { 387 let s: *u8 = sys_mmap(8) 388 s[0] = 0x43; s[1] = 0x43; s[2] = 0x4F; s[3] = 0x43; s[4] = 0x43 389 let m: *MolGraph = nx_chem_parse_smiles(s, 5) 390 if m.is_valid != 1 { return 231 } 391 if m.n_atoms != 5 { return 232 } 392 if m.n_bonds != 4 { return 233 } 393 if nx_chem_mol_count_z(m, 6) != 4 { return 234 } 394 if nx_chem_mol_count_z(m, 8) != 1 { return 235 } 395 return 0 396} 397 398// ================================================================= 399// M -- C2.2: full 118-element bracket atoms (sample beyond C2.1 set). 400// Verifies the expanded symbol-to-Z table covers lanthanides / 401// actinides / super-heavies / precious metals. 402// ================================================================= 403func m_full_118_brackets() -> nx_int { 404 // [Au] -> gold, Z=79 405 let s1: *u8 = sys_mmap(8) 406 s1[0] = 0x5B; s1[1] = 0x41; s1[2] = 0x75; s1[3] = 0x5D 407 let m1: *MolGraph = nx_chem_parse_smiles(s1, 4) 408 if m1.is_valid != 1 { return 241 } 409 if tz(m1, 0) != 79 { return 242 } 410 // [Pt] -> platinum, Z=78 411 let s2: *u8 = sys_mmap(8) 412 s2[0] = 0x5B; s2[1] = 0x50; s2[2] = 0x74; s2[3] = 0x5D 413 let m2: *MolGraph = nx_chem_parse_smiles(s2, 4) 414 if m2.is_valid != 1 { return 243 } 415 if tz(m2, 0) != 78 { return 244 } 416 // [La] -> lanthanum, Z=57 417 let s3: *u8 = sys_mmap(8) 418 s3[0] = 0x5B; s3[1] = 0x4C; s3[2] = 0x61; s3[3] = 0x5D 419 let m3: *MolGraph = nx_chem_parse_smiles(s3, 4) 420 if m3.is_valid != 1 { return 245 } 421 if tz(m3, 0) != 57 { return 246 } 422 // [Lu] -> lutetium, Z=71 423 let s4: *u8 = sys_mmap(8) 424 s4[0] = 0x5B; s4[1] = 0x4C; s4[2] = 0x75; s4[3] = 0x5D 425 let m4: *MolGraph = nx_chem_parse_smiles(s4, 4) 426 if m4.is_valid != 1 { return 247 } 427 if tz(m4, 0) != 71 { return 248 } 428 // [Th] -> thorium, Z=90 429 let s5: *u8 = sys_mmap(8) 430 s5[0] = 0x5B; s5[1] = 0x54; s5[2] = 0x68; s5[3] = 0x5D 431 let m5: *MolGraph = nx_chem_parse_smiles(s5, 4) 432 if m5.is_valid != 1 { return 249 } 433 if tz(m5, 0) != 90 { return 250 } 434 // [U] -> uranium, Z=92 (single-char) 435 let s6: *u8 = sys_mmap(8) 436 s6[0] = 0x5B; s6[1] = 0x55; s6[2] = 0x5D 437 let m6: *MolGraph = nx_chem_parse_smiles(s6, 3) 438 if m6.is_valid != 1 { return 251 } 439 if tz(m6, 0) != 92 { return 252 } 440 // [Og] -> oganesson, Z=118 441 let s7: *u8 = sys_mmap(8) 442 s7[0] = 0x5B; s7[1] = 0x4F; s7[2] = 0x67; s7[3] = 0x5D 443 let m7: *MolGraph = nx_chem_parse_smiles(s7, 4) 444 if m7.is_valid != 1 { return 253 } 445 if tz(m7, 0) != 118 { return 254 } 446 // [Bi] -> bismuth, Z=83 447 let s8: *u8 = sys_mmap(8) 448 s8[0] = 0x5B; s8[1] = 0x42; s8[2] = 0x69; s8[3] = 0x5D 449 let m8: *MolGraph = nx_chem_parse_smiles(s8, 4) 450 if m8.is_valid != 1 { return 255 } 451 if tz(m8, 0) != 83 { return 256 } 452 return 0 453} 454 455// ================================================================= 456// N -- C2.2: two-digit ring closures %nn. 457// "C%12CCCCC%12" -- 6-atom ring via %12 syntax. 458// ================================================================= 459func n_percent_rings() -> nx_int { 460 // Build "C%12CCCCC%12" 461 let s: *u8 = sys_mmap(16) 462 s[0] = 0x43 // C 463 s[1] = 0x25; s[2] = 0x31; s[3] = 0x32 // %12 464 s[4] = 0x43 // C 465 s[5] = 0x43 // C 466 s[6] = 0x43 // C 467 s[7] = 0x43 // C 468 s[8] = 0x43 // C 469 s[9] = 0x25; s[10] = 0x31; s[11] = 0x32 // %12 470 let m: *MolGraph = nx_chem_parse_smiles(s, 12) 471 if m.is_valid != 1 { return 261 } 472 if m.n_atoms != 6 { return 262 } 473 if m.n_bonds != 6 { return 263 } // 5 chain + 1 ring closure 474 // last bond should be the ring closure (atom 5 -> atom 0) 475 let b5: *Bond = nx_chem_mol_bond(m, 5) 476 if b5.a != 5 { return 264 } 477 if b5.b != 0 { return 265 } 478 if b5.in_ring != 1 { return 266 } 479 return 0 480} 481 482// ================================================================= 483// O -- C2.2: atom stereo @ and @@ populated into Atom.stereo. 484// ================================================================= 485func o_atom_stereo() -> nx_int { 486 // "[C@H]" -> Atom.stereo = NX_STEREO_CCW 487 let s1: *u8 = sys_mmap(8) 488 s1[0] = 0x5B; s1[1] = 0x43; s1[2] = 0x40; s1[3] = 0x48; s1[4] = 0x5D 489 let m1: *MolGraph = nx_chem_parse_smiles(s1, 5) 490 if m1.is_valid != 1 { return 281 } 491 let a1: *Atom = nx_chem_mol_atom(m1, 0) 492 if a1.z != 6 { return 282 } 493 if a1.stereo != NX_STEREO_CCW { return 283 } 494 if a1.h_count != 1 { return 284 } 495 // "[C@@H4]" -> Atom.stereo = NX_STEREO_CW, h_count = 4 496 let s2: *u8 = sys_mmap(8) 497 s2[0] = 0x5B; s2[1] = 0x43; s2[2] = 0x40; s2[3] = 0x40; s2[4] = 0x48; s2[5] = 0x34; s2[6] = 0x5D 498 let m2: *MolGraph = nx_chem_parse_smiles(s2, 7) 499 if m2.is_valid != 1 { return 285 } 500 let a2: *Atom = nx_chem_mol_atom(m2, 0) 501 if a2.stereo != NX_STEREO_CW { return 286 } 502 if a2.h_count != 4 { return 287 } 503 return 0 504} 505 506// ================================================================= 507// P -- C2.2: bond stereo / and \\ populated into Bond.stereo. 508// "F/C=C/F" -- a trans-1,2-difluoroethene-class SMILES; the slashes 509// apply to the F-C single bonds flanking the C=C double bond. 510// ================================================================= 511func p_bond_stereo() -> nx_int { 512 // "F/C=C/F" 513 let s: *u8 = sys_mmap(8) 514 s[0] = 0x46; s[1] = 0x2F; s[2] = 0x43; s[3] = 0x3D; s[4] = 0x43; s[5] = 0x2F; s[6] = 0x46 515 let m: *MolGraph = nx_chem_parse_smiles(s, 7) 516 if m.is_valid != 1 { return 301 } 517 if m.n_atoms != 4 { return 302 } 518 if m.n_bonds != 3 { return 303 } 519 // bond 0: F-C single with stereo UP 520 let b0: *Bond = nx_chem_mol_bond(m, 0) 521 if b0.order != NX_BOND_SINGLE { return 304 } 522 if b0.stereo != NX_BSTEREO_UP { return 305 } 523 // bond 1: C=C double, no stereo on the double bond itself this milestone 524 let b1: *Bond = nx_chem_mol_bond(m, 1) 525 if b1.order != NX_BOND_DOUBLE { return 306 } 526 // bond 2: C-F single with stereo UP 527 let b2: *Bond = nx_chem_mol_bond(m, 2) 528 if b2.order != NX_BOND_SINGLE { return 307 } 529 if b2.stereo != NX_BSTEREO_UP { return 308 } 530 531 // "F/C=C\\F" -- backslash variant 532 let s2: *u8 = sys_mmap(8) 533 s2[0] = 0x46; s2[1] = 0x2F; s2[2] = 0x43; s2[3] = 0x3D; s2[4] = 0x43; s2[5] = 0x5C; s2[6] = 0x46 534 let m2: *MolGraph = nx_chem_parse_smiles(s2, 7) 535 if m2.is_valid != 1 { return 309 } 536 let b2_0: *Bond = nx_chem_mol_bond(m2, 0) 537 if b2_0.stereo != NX_BSTEREO_UP { return 310 } 538 let b2_2: *Bond = nx_chem_mol_bond(m2, 2) 539 if b2_2.stereo != NX_BSTEREO_DOWN { return 311 } 540 return 0 541} 542 543// ================================================================= 544// Q -- C2.2: atom map numbers (:n inside brackets) stored in Atom.map_num. 545// ================================================================= 546func q_atom_maps() -> nx_int { 547 // "[CH3:1]" -> Atom.map_num = 1, h_count = 3 548 let s1: *u8 = sys_mmap(8) 549 s1[0] = 0x5B; s1[1] = 0x43; s1[2] = 0x48; s1[3] = 0x33; s1[4] = 0x3A; s1[5] = 0x31; s1[6] = 0x5D 550 let m1: *MolGraph = nx_chem_parse_smiles(s1, 7) 551 if m1.is_valid != 1 { return 321 } 552 let a1: *Atom = nx_chem_mol_atom(m1, 0) 553 if a1.z != 6 { return 322 } 554 if a1.h_count != 3 { return 323 } 555 if a1.map_num != 1 { return 324 } 556 // "[OH:42]" -> Atom.map_num = 42 557 let s2: *u8 = sys_mmap(16) 558 s2[0] = 0x5B; s2[1] = 0x4F; s2[2] = 0x48; s2[3] = 0x3A; s2[4] = 0x34; s2[5] = 0x32; s2[6] = 0x5D 559 let m2: *MolGraph = nx_chem_parse_smiles(s2, 7) 560 if m2.is_valid != 1 { return 325 } 561 let a2: *Atom = nx_chem_mol_atom(m2, 0) 562 if a2.z != 8 { return 326 } 563 if a2.h_count != 1 { return 327 } 564 if a2.map_num != 42 { return 328 } 565 return 0 566} 567 568func main() -> nx_exit { 569 println("=== nx_chem_smiles -- C2.1 + C2.2 KAT: basic SMILES parser ===" as *u8) 570 571 let ra: nx_int = a_single_atoms() 572 if ra != 0 { println("A single_atoms FAIL" as *u8); return ra } 573 println("A single_atoms PASS O / C / N / F -> single-atom graphs" as *u8) 574 575 let rb: nx_int = b_two_char_organic() 576 if rb != 0 { println("B two_char_organic FAIL" as *u8); return rb } 577 println("B two_char_organic PASS Cl + Br disambiguated from C + B" as *u8) 578 579 let rc: nx_int = c_bonds() 580 if rc != 0 { println("C bonds FAIL" as *u8); return rc } 581 println("C bonds PASS CC (single) + C=C (double) + C#C (triple)" as *u8) 582 583 let rd: nx_int = d_ethanol() 584 if rd != 0 { println("D ethanol FAIL" as *u8); return rd } 585 println("D ethanol PASS CCO -> 3 atoms / 2 single bonds / C-C-O graph" as *u8) 586 587 let re: nx_int = e_isobutane() 588 if re != 0 { println("E isobutane FAIL" as *u8); return re } 589 println("E isobutane PASS CC(C)C -> 4 atoms / 3 bonds / branch open+close" as *u8) 590 591 let rf: nx_int = f_benzene() 592 if rf != 0 { println("F benzene FAIL" as *u8); return rf } 593 println("F benzene PASS c1ccccc1 -> 6 aromatic C; 6 aromatic bonds; ring closure to atom 0" as *u8) 594 595 let rg: nx_int = g_cyclohexane() 596 if rg != 0 { println("G cyclohexane FAIL" as *u8); return rg } 597 println("G cyclohexane PASS C1CCCCC1 -> 6 aliphatic C; 6 single bonds; aliphatic ring" as *u8) 598 599 let rh: nx_int = h_brackets() 600 if rh != 0 { println("H brackets FAIL" as *u8); return rh } 601 println("H brackets PASS [Na+]/[Cl-]/[CH4]/[NH4+]/[13C]/[Hg+2]/[Pb+2] -- charge + H + isotope + supplement-heavy-metals" as *u8) 602 603 let ri: nx_int = i_wildcard() 604 if ri != 0 { println("I wildcard FAIL" as *u8); return ri } 605 println("I wildcard PASS * -> atom with z=0" as *u8) 606 607 let rj: nx_int = j_disconnect() 608 if rj != 0 { println("J disconnect FAIL" as *u8); return rj } 609 println("J disconnect PASS CC.O -> 3 atoms / 1 bond (no C-O after '.')" as *u8) 610 611 let rk: nx_int = k_errors() 612 if rk != 0 { println("K errors FAIL" as *u8); return rk } 613 println("K errors PASS empty + unclosed-bracket + unclosed-paren + orphan-ring + bad-char + invalid-bracket-symbol all caught with structured err_code" as *u8) 614 615 let rl: nx_int = l_diethyl_ether() 616 if rl != 0 { println("L diethyl_ether FAIL" as *u8); return rl } 617 println("L diethyl_ether PASS CCOCC -> 5 atoms / 4 bonds / 4C + 1O" as *u8) 618 619 let rm: nx_int = m_full_118_brackets() 620 if rm != 0 { println("M full_118_brackets FAIL" as *u8); return rm } 621 println("M full_118_brackets PASS [Au]/[Pt]/[La]/[Lu]/[Th]/[U]/[Og]/[Bi] -- full IUPAC bracket-symbol-to-Z" as *u8) 622 623 let rn: nx_int = n_percent_rings() 624 if rn != 0 { println("N percent_rings FAIL" as *u8); return rn } 625 println("N percent_rings PASS C%12CCCCC%12 -- 2-digit ring closure via %nn syntax" as *u8) 626 627 let ro: nx_int = o_atom_stereo() 628 if ro != 0 { println("O atom_stereo FAIL" as *u8); return ro } 629 println("O atom_stereo PASS [C@H] -> Atom.stereo=CCW; [C@@H4] -> Atom.stereo=CW + h_count=4" as *u8) 630 631 let rp: nx_int = p_bond_stereo() 632 if rp != 0 { println("P bond_stereo FAIL" as *u8); return rp } 633 println("P bond_stereo PASS F/C=C/F -> Bond.stereo=UP on F-C bonds; F/C=C\\F -> UP + DOWN" as *u8) 634 635 let rq: nx_int = q_atom_maps() 636 if rq != 0 { println("Q atom_maps FAIL" as *u8); return rq } 637 println("Q atom_maps PASS [CH3:1] -> Atom.map_num=1; [OH:42] -> Atom.map_num=42 (SMIRKS-ready)" as *u8) 638 639 println("" as *u8) 640 println("=== C2.1 + C2.2 substrate milestone PASS ===" as *u8) 641 println(" C2.1 parser : organic subset + lowercase aromatic + bracket atoms (supplement subset)" as *u8) 642 println(" + bonds -/=/#/:/. + branches + single-digit ring closures (0..9)" as *u8) 643 println(" C2.2 parser : full IUPAC 118-element brackets + %nn rings + Atom.stereo (@/@@)" as *u8) 644 println(" + Bond.stereo (//\\) + Atom.map_num (:n SMIRKS-ready)" as *u8) 645 println(" memory-safety : all reads bounded; every error -> structured err_code + err_pos" as *u8) 646 println(" EXCEED axes hit : E2 + E4 (stereo populated) + E7 (full periodic + radicals + isotopes)" as *u8) 647 println(" + E11 (no format lock-in)" as *u8) 648 println(" honest gaps : E/Z resolution from /\\ (C2.3); atropisomer+enhanced-stereo (C2.4);" as *u8) 649 println(" implicit-H valence inference (C2.3); 6-aromaticity-tuple model dispatch (C2.5);" as *u8) 650 println(" canonical SMILES output (C2.3); 10^9 fuzzer harness (C2.9)" as *u8) 651 println(" next : C2.3 -- canonical SMILES output + implicit-H + E/Z resolution" as *u8) 652 return 0 653}