smiles-js 2.1.0 → 2.2.0

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.
Files changed (34) hide show
  1. package/docs/smiles.peggy +215 -0
  2. package/package.json +1 -1
  3. package/src/decompiler.js +209 -49
  4. package/src/decompiler.test.js +232 -60
  5. package/src/fragment.test.js +7 -2
  6. package/src/method-attachers.js +8 -8
  7. package/test-integration/__snapshots__/acetaminophen.test.js.snap +20 -0
  8. package/test-integration/__snapshots__/adjuvant-analgesics.test.js.snap +62 -0
  9. package/test-integration/__snapshots__/cholesterol-drugs.test.js.snap +261 -0
  10. package/test-integration/__snapshots__/dexamethasone.test.js.snap +31 -0
  11. package/test-integration/__snapshots__/endocannabinoids.test.js.snap +77 -0
  12. package/test-integration/__snapshots__/endogenous-opioids.test.js.snap +1116 -0
  13. package/test-integration/__snapshots__/hypertension-medication.test.js.snap +69 -0
  14. package/test-integration/__snapshots__/local-anesthetics.test.js.snap +97 -0
  15. package/test-integration/__snapshots__/nsaids-otc.test.js.snap +60 -0
  16. package/test-integration/__snapshots__/nsaids-prescription.test.js.snap +113 -0
  17. package/test-integration/__snapshots__/opioids.test.js.snap +109 -0
  18. package/test-integration/__snapshots__/steroids.test.js.snap +379 -0
  19. package/test-integration/acetaminophen.test.js +15 -3
  20. package/test-integration/adjuvant-analgesics.test.js +43 -7
  21. package/test-integration/cholesterol-drugs.test.js +88 -22
  22. package/test-integration/dexamethasone.test.js +8 -2
  23. package/test-integration/endocannabinoids.test.js +48 -12
  24. package/test-integration/endogenous-opioids.smiles.js +32 -0
  25. package/test-integration/endogenous-opioids.test.js +192 -0
  26. package/test-integration/hypertension-medication.test.js +32 -8
  27. package/test-integration/local-anesthetics.smiles.js +33 -0
  28. package/test-integration/local-anesthetics.test.js +64 -16
  29. package/test-integration/nsaids-otc.test.js +40 -10
  30. package/test-integration/nsaids-prescription.test.js +72 -18
  31. package/test-integration/opioids.test.js +56 -14
  32. package/test-integration/steroids.test.js +112 -28
  33. package/test-integration/utils.js +4 -2
  34. package/todo +2 -1
@@ -23,6 +23,12 @@ describe('Anandamide Integration Test', () => {
23
23
  expect(code).toMatchSnapshot();
24
24
  });
25
25
 
26
+ test('generates valid verbose code via toCode()', () => {
27
+ const ast = parse(ANANDAMIDE_SMILES);
28
+ const code = ast.toCode('v', { verbose: true });
29
+ expect(code).toMatchSnapshot();
30
+ });
31
+
26
32
  test('generated code is valid JavaScript', () => {
27
33
  const ast = parse(ANANDAMIDE_SMILES);
28
34
  const code = ast.toCode('v');
@@ -30,14 +36,14 @@ describe('Anandamide Integration Test', () => {
30
36
 
31
37
  let factory;
32
38
  expect(() => {
33
- factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', executableCode);
39
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
34
40
  }).not.toThrow();
35
41
  expect(typeof factory).toBe('function');
36
42
  });
37
43
 
38
44
  test('codegen round-trip: generated code produces valid SMILES', () => {
39
45
  const ast = parse(ANANDAMIDE_SMILES);
40
- const code = ast.toCode('v');
46
+ const code = ast.toCode('v', { verbose: true });
41
47
  const executableCode = stripExports(code);
42
48
 
43
49
  const varMatch = code.match(/export const (v\d+) = /g);
@@ -63,6 +69,12 @@ describe('2-Arachidonoylglycerol Integration Test', () => {
63
69
  expect(code).toMatchSnapshot();
64
70
  });
65
71
 
72
+ test('generates valid verbose code via toCode()', () => {
73
+ const ast = parse(ARACHIDONOYLGLYCEROL2_SMILES);
74
+ const code = ast.toCode('v', { verbose: true });
75
+ expect(code).toMatchSnapshot();
76
+ });
77
+
66
78
  test('generated code is valid JavaScript', () => {
67
79
  const ast = parse(ARACHIDONOYLGLYCEROL2_SMILES);
68
80
  const code = ast.toCode('v');
@@ -70,14 +82,14 @@ describe('2-Arachidonoylglycerol Integration Test', () => {
70
82
 
71
83
  let factory;
72
84
  expect(() => {
73
- factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', executableCode);
85
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
74
86
  }).not.toThrow();
75
87
  expect(typeof factory).toBe('function');
76
88
  });
77
89
 
78
90
  test('codegen round-trip: generated code produces valid SMILES', () => {
79
91
  const ast = parse(ARACHIDONOYLGLYCEROL2_SMILES);
80
- const code = ast.toCode('v');
92
+ const code = ast.toCode('v', { verbose: true });
81
93
  const executableCode = stripExports(code);
82
94
 
83
95
  const varMatch = code.match(/export const (v\d+) = /g);
@@ -103,6 +115,12 @@ describe('THC Integration Test', () => {
103
115
  expect(code).toMatchSnapshot();
104
116
  });
105
117
 
118
+ test('generates valid verbose code via toCode()', () => {
119
+ const ast = parse(THC_SMILES);
120
+ const code = ast.toCode('v', { verbose: true });
121
+ expect(code).toMatchSnapshot();
122
+ });
123
+
106
124
  test('generated code is valid JavaScript', () => {
107
125
  const ast = parse(THC_SMILES);
108
126
  const code = ast.toCode('v');
@@ -110,14 +128,14 @@ describe('THC Integration Test', () => {
110
128
 
111
129
  let factory;
112
130
  expect(() => {
113
- factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', executableCode);
131
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
114
132
  }).not.toThrow();
115
133
  expect(typeof factory).toBe('function');
116
134
  });
117
135
 
118
136
  test('codegen round-trip: generated code produces valid SMILES', () => {
119
137
  const ast = parse(THC_SMILES);
120
- const code = ast.toCode('v');
138
+ const code = ast.toCode('v', { verbose: true });
121
139
  const executableCode = stripExports(code);
122
140
 
123
141
  const varMatch = code.match(/export const (v\d+) = /g);
@@ -143,6 +161,12 @@ describe('CBD Integration Test', () => {
143
161
  expect(code).toMatchSnapshot();
144
162
  });
145
163
 
164
+ test('generates valid verbose code via toCode()', () => {
165
+ const ast = parse(CBD_SMILES);
166
+ const code = ast.toCode('v', { verbose: true });
167
+ expect(code).toMatchSnapshot();
168
+ });
169
+
146
170
  test('generated code is valid JavaScript', () => {
147
171
  const ast = parse(CBD_SMILES);
148
172
  const code = ast.toCode('v');
@@ -150,14 +174,14 @@ describe('CBD Integration Test', () => {
150
174
 
151
175
  let factory;
152
176
  expect(() => {
153
- factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', executableCode);
177
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
154
178
  }).not.toThrow();
155
179
  expect(typeof factory).toBe('function');
156
180
  });
157
181
 
158
182
  test('codegen round-trip: generated code produces valid SMILES', () => {
159
183
  const ast = parse(CBD_SMILES);
160
- const code = ast.toCode('v');
184
+ const code = ast.toCode('v', { verbose: true });
161
185
  const executableCode = stripExports(code);
162
186
 
163
187
  const varMatch = code.match(/export const (v\d+) = /g);
@@ -183,6 +207,12 @@ describe('Nabilone Integration Test', () => {
183
207
  expect(code).toMatchSnapshot();
184
208
  });
185
209
 
210
+ test('generates valid verbose code via toCode()', () => {
211
+ const ast = parse(NABILONE_SMILES);
212
+ const code = ast.toCode('v', { verbose: true });
213
+ expect(code).toMatchSnapshot();
214
+ });
215
+
186
216
  test('generated code is valid JavaScript', () => {
187
217
  const ast = parse(NABILONE_SMILES);
188
218
  const code = ast.toCode('v');
@@ -190,14 +220,14 @@ describe('Nabilone Integration Test', () => {
190
220
 
191
221
  let factory;
192
222
  expect(() => {
193
- factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', executableCode);
223
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
194
224
  }).not.toThrow();
195
225
  expect(typeof factory).toBe('function');
196
226
  });
197
227
 
198
228
  test('codegen round-trip: generated code produces valid SMILES', () => {
199
229
  const ast = parse(NABILONE_SMILES);
200
- const code = ast.toCode('v');
230
+ const code = ast.toCode('v', { verbose: true });
201
231
  const executableCode = stripExports(code);
202
232
 
203
233
  const varMatch = code.match(/export const (v\d+) = /g);
@@ -223,6 +253,12 @@ describe('Palmitoylethanolamide Integration Test', () => {
223
253
  expect(code).toMatchSnapshot();
224
254
  });
225
255
 
256
+ test('generates valid verbose code via toCode()', () => {
257
+ const ast = parse(PALMITOYLETHANOLAMIDE_SMILES);
258
+ const code = ast.toCode('v', { verbose: true });
259
+ expect(code).toMatchSnapshot();
260
+ });
261
+
226
262
  test('generated code is valid JavaScript', () => {
227
263
  const ast = parse(PALMITOYLETHANOLAMIDE_SMILES);
228
264
  const code = ast.toCode('v');
@@ -230,14 +266,14 @@ describe('Palmitoylethanolamide Integration Test', () => {
230
266
 
231
267
  let factory;
232
268
  expect(() => {
233
- factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', executableCode);
269
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
234
270
  }).not.toThrow();
235
271
  expect(typeof factory).toBe('function');
236
272
  });
237
273
 
238
274
  test('codegen round-trip: generated code produces valid SMILES', () => {
239
275
  const ast = parse(PALMITOYLETHANOLAMIDE_SMILES);
240
- const code = ast.toCode('v');
276
+ const code = ast.toCode('v', { verbose: true });
241
277
  const executableCode = stripExports(code);
242
278
 
243
279
  const varMatch = code.match(/export const (v\d+) = /g);
@@ -0,0 +1,32 @@
1
+ // Endogenous Opioid Peptides
2
+ // Natural peptides produced by the body that bind opioid receptors
3
+
4
+ import { Fragment } from 'smiles-js';
5
+
6
+ // === MET-ENKEPHALIN ===
7
+ // Tyr-Gly-Gly-Phe-Met - first endogenous opioid discovered (1975)
8
+ // SMILES: CSCCC(C(=O)O)NC(=O)C(CC1=CC=CC=C1)NC(=O)CNC(=O)CNC(=O)C(CC2=CC=C(C=C2)O)N
9
+ // Binds delta opioid receptors, involved in pain modulation
10
+
11
+ export const metEnkephalin = Fragment('CSCCC(C(=O)O)NC(=O)C(CC1=CC=CC=C1)NC(=O)CNC(=O)CNC(=O)C(CC2=CC=C(C=C2)O)N');
12
+
13
+ // === LEU-ENKEPHALIN ===
14
+ // Tyr-Gly-Gly-Phe-Leu - differs from met-enkephalin only at C-terminal residue
15
+ // SMILES: CC(C)CC(C(=O)O)NC(=O)C(CC1=CC=CC=C1)NC(=O)CNC(=O)CNC(=O)C(CC2=CC=C(C=C2)O)N
16
+ // Also binds delta opioid receptors
17
+
18
+ export const leuEnkephalin = Fragment('CC(C)CC(C(=O)O)NC(=O)C(CC1=CC=CC=C1)NC(=O)CNC(=O)CNC(=O)C(CC2=CC=C(C=C2)O)N');
19
+
20
+ // === ENDOMORPHIN-1 ===
21
+ // Tyr-Pro-Trp-Phe-NH2 - highest affinity endogenous mu-opioid receptor ligand
22
+ // SMILES: C1CC(N(C1)C(=O)C(CC2=CC=C(C=C2)O)N)C(=O)NC(CC3=CNC4=CC=CC=C43)C(=O)NC(CC5=CC=CC=C5)C(=O)N
23
+ // Selective for mu receptors (same target as morphine)
24
+
25
+ export const endomorphin1 = Fragment('C1CC(N(C1)C(=O)C(CC2=CC=C(C=C2)O)N)C(=O)NC(CC3=CNC4=CC=CC=C43)C(=O)NC(CC5=CC=CC=C5)C(=O)N');
26
+
27
+ // === ENDOMORPHIN-2 ===
28
+ // Tyr-Pro-Phe-Phe-NH2 - differs from endomorphin-1 at position 3 (Phe instead of Trp)
29
+ // SMILES: C1CC(N(C1)C(=O)C(CC2=CC=C(C=C2)O)N)C(=O)NC(CC3=CC=CC=C3)C(=O)NC(CC4=CC=CC=C4)C(=O)N
30
+ // Also selective for mu receptors
31
+
32
+ export const endomorphin2 = Fragment('C1CC(N(C1)C(=O)C(CC2=CC=C(C=C2)O)N)C(=O)NC(CC3=CC=CC=C3)C(=O)NC(CC4=CC=CC=C4)C(=O)N');
@@ -0,0 +1,192 @@
1
+ import { describe, test, expect } from 'bun:test';
2
+ import { parse } from '../src/parser/index.js';
3
+ import { stripExports, createFunction, executeCode } from './utils.js';
4
+
5
+ const MET_ENKEPHALIN_SMILES = 'CSCCC(C(=O)O)NC(=O)C(CC1=CC=CC=C1)NC(=O)CNC(=O)CNC(=O)C(CC2=CC=C(C=C2)O)N';
6
+ const LEU_ENKEPHALIN_SMILES = 'CC(C)CC(C(=O)O)NC(=O)C(CC1=CC=CC=C1)NC(=O)CNC(=O)CNC(=O)C(CC2=CC=C(C=C2)O)N';
7
+ const ENDOMORPHIN1_SMILES = 'C1CC(N(C1)C(=O)C(CC2=CC=C(C=C2)O)N)C(=O)NC(CC3=CNC4=CC=CC=C43)C(=O)NC(CC5=CC=CC=C5)C(=O)N';
8
+ const ENDOMORPHIN2_SMILES = 'C1CC(N(C1)C(=O)C(CC2=CC=C(C=C2)O)N)C(=O)NC(CC3=CC=CC=C3)C(=O)NC(CC4=CC=CC=C4)C(=O)N';
9
+
10
+ describe('Met-Enkephalin Integration Test', () => {
11
+ test('parses met-enkephalin', () => {
12
+ const ast = parse(MET_ENKEPHALIN_SMILES);
13
+ const obj = ast.toObject();
14
+ expect(obj).toMatchSnapshot();
15
+ expect(ast.smiles).toBe(MET_ENKEPHALIN_SMILES);
16
+ });
17
+
18
+ test('generates valid code via toCode()', () => {
19
+ const ast = parse(MET_ENKEPHALIN_SMILES);
20
+ const code = ast.toCode('v');
21
+ expect(code).toMatchSnapshot();
22
+ });
23
+
24
+ test('generates valid verbose code via toCode()', () => {
25
+ const ast = parse(MET_ENKEPHALIN_SMILES);
26
+ const code = ast.toCode('v', { verbose: true });
27
+ expect(code).toMatchSnapshot();
28
+ });
29
+
30
+ test('generated code is valid JavaScript', () => {
31
+ const ast = parse(MET_ENKEPHALIN_SMILES);
32
+ const code = ast.toCode('v');
33
+ const executableCode = stripExports(code);
34
+
35
+ let factory;
36
+ expect(() => {
37
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
38
+ }).not.toThrow();
39
+ expect(typeof factory).toBe('function');
40
+ });
41
+
42
+ test('codegen round-trip: generated code produces valid SMILES', () => {
43
+ const ast = parse(MET_ENKEPHALIN_SMILES);
44
+ const code = ast.toCode('v', { verbose: true });
45
+ const executableCode = stripExports(code);
46
+
47
+ const varMatch = code.match(/export const (v\d+) = /g);
48
+ const lastVar = varMatch ? varMatch[varMatch.length - 1].match(/export const (v\d+)/)[1] : 'v1';
49
+
50
+ const reconstructed = executeCode(executableCode, lastVar);
51
+
52
+ expect(reconstructed.smiles).toBe(MET_ENKEPHALIN_SMILES);
53
+ });
54
+ });
55
+
56
+ describe('Leu-Enkephalin Integration Test', () => {
57
+ test('parses leu-enkephalin', () => {
58
+ const ast = parse(LEU_ENKEPHALIN_SMILES);
59
+ const obj = ast.toObject();
60
+ expect(obj).toMatchSnapshot();
61
+ expect(ast.smiles).toBe(LEU_ENKEPHALIN_SMILES);
62
+ });
63
+
64
+ test('generates valid code via toCode()', () => {
65
+ const ast = parse(LEU_ENKEPHALIN_SMILES);
66
+ const code = ast.toCode('v');
67
+ expect(code).toMatchSnapshot();
68
+ });
69
+
70
+ test('generates valid verbose code via toCode()', () => {
71
+ const ast = parse(LEU_ENKEPHALIN_SMILES);
72
+ const code = ast.toCode('v', { verbose: true });
73
+ expect(code).toMatchSnapshot();
74
+ });
75
+
76
+ test('generated code is valid JavaScript', () => {
77
+ const ast = parse(LEU_ENKEPHALIN_SMILES);
78
+ const code = ast.toCode('v');
79
+ const executableCode = stripExports(code);
80
+
81
+ let factory;
82
+ expect(() => {
83
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
84
+ }).not.toThrow();
85
+ expect(typeof factory).toBe('function');
86
+ });
87
+
88
+ test('codegen round-trip: generated code produces valid SMILES', () => {
89
+ const ast = parse(LEU_ENKEPHALIN_SMILES);
90
+ const code = ast.toCode('v', { verbose: true });
91
+ const executableCode = stripExports(code);
92
+
93
+ const varMatch = code.match(/export const (v\d+) = /g);
94
+ const lastVar = varMatch ? varMatch[varMatch.length - 1].match(/export const (v\d+)/)[1] : 'v1';
95
+
96
+ const reconstructed = executeCode(executableCode, lastVar);
97
+
98
+ expect(reconstructed.smiles).toBe(LEU_ENKEPHALIN_SMILES);
99
+ });
100
+ });
101
+
102
+ describe('Endomorphin-1 Integration Test', () => {
103
+ test('parses endomorphin-1', () => {
104
+ const ast = parse(ENDOMORPHIN1_SMILES);
105
+ const obj = ast.toObject();
106
+ expect(obj).toMatchSnapshot();
107
+ expect(ast.smiles).toBe(ENDOMORPHIN1_SMILES);
108
+ });
109
+
110
+ test('generates valid code via toCode()', () => {
111
+ const ast = parse(ENDOMORPHIN1_SMILES);
112
+ const code = ast.toCode('v');
113
+ expect(code).toMatchSnapshot();
114
+ });
115
+
116
+ test('generates valid verbose code via toCode()', () => {
117
+ const ast = parse(ENDOMORPHIN1_SMILES);
118
+ const code = ast.toCode('v', { verbose: true });
119
+ expect(code).toMatchSnapshot();
120
+ });
121
+
122
+ test('generated code is valid JavaScript', () => {
123
+ const ast = parse(ENDOMORPHIN1_SMILES);
124
+ const code = ast.toCode('v');
125
+ const executableCode = stripExports(code);
126
+
127
+ let factory;
128
+ expect(() => {
129
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
130
+ }).not.toThrow();
131
+ expect(typeof factory).toBe('function');
132
+ });
133
+
134
+ test('codegen round-trip: generated code produces valid SMILES', () => {
135
+ const ast = parse(ENDOMORPHIN1_SMILES);
136
+ const code = ast.toCode('v', { verbose: true });
137
+ const executableCode = stripExports(code);
138
+
139
+ const varMatch = code.match(/export const (v\d+) = /g);
140
+ const lastVar = varMatch ? varMatch[varMatch.length - 1].match(/export const (v\d+)/)[1] : 'v1';
141
+
142
+ const reconstructed = executeCode(executableCode, lastVar);
143
+
144
+ expect(reconstructed.smiles).toBe(ENDOMORPHIN1_SMILES);
145
+ });
146
+ });
147
+
148
+ describe('Endomorphin-2 Integration Test', () => {
149
+ test('parses endomorphin-2', () => {
150
+ const ast = parse(ENDOMORPHIN2_SMILES);
151
+ const obj = ast.toObject();
152
+ expect(obj).toMatchSnapshot();
153
+ expect(ast.smiles).toBe(ENDOMORPHIN2_SMILES);
154
+ });
155
+
156
+ test('generates valid code via toCode()', () => {
157
+ const ast = parse(ENDOMORPHIN2_SMILES);
158
+ const code = ast.toCode('v');
159
+ expect(code).toMatchSnapshot();
160
+ });
161
+
162
+ test('generates valid verbose code via toCode()', () => {
163
+ const ast = parse(ENDOMORPHIN2_SMILES);
164
+ const code = ast.toCode('v', { verbose: true });
165
+ expect(code).toMatchSnapshot();
166
+ });
167
+
168
+ test('generated code is valid JavaScript', () => {
169
+ const ast = parse(ENDOMORPHIN2_SMILES);
170
+ const code = ast.toCode('v');
171
+ const executableCode = stripExports(code);
172
+
173
+ let factory;
174
+ expect(() => {
175
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
176
+ }).not.toThrow();
177
+ expect(typeof factory).toBe('function');
178
+ });
179
+
180
+ test('codegen round-trip: generated code produces valid SMILES', () => {
181
+ const ast = parse(ENDOMORPHIN2_SMILES);
182
+ const code = ast.toCode('v', { verbose: true });
183
+ const executableCode = stripExports(code);
184
+
185
+ const varMatch = code.match(/export const (v\d+) = /g);
186
+ const lastVar = varMatch ? varMatch[varMatch.length - 1].match(/export const (v\d+)/)[1] : 'v1';
187
+
188
+ const reconstructed = executeCode(executableCode, lastVar);
189
+
190
+ expect(reconstructed.smiles).toBe(ENDOMORPHIN2_SMILES);
191
+ });
192
+ });
@@ -21,6 +21,12 @@ describe('Telmisartan Integration Test', () => {
21
21
  expect(code).toMatchSnapshot();
22
22
  });
23
23
 
24
+ test('generates valid verbose code via toCode()', () => {
25
+ const ast = parse(TELMISARTAN_SMILES);
26
+ const code = ast.toCode('v', { verbose: true });
27
+ expect(code).toMatchSnapshot();
28
+ });
29
+
24
30
  test('generated code is valid JavaScript', () => {
25
31
  const ast = parse(TELMISARTAN_SMILES);
26
32
  const code = ast.toCode('v');
@@ -28,14 +34,14 @@ describe('Telmisartan Integration Test', () => {
28
34
 
29
35
  let factory;
30
36
  expect(() => {
31
- factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', executableCode);
37
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
32
38
  }).not.toThrow();
33
39
  expect(typeof factory).toBe('function');
34
40
  });
35
41
 
36
42
  test('codegen round-trip: generated code produces valid SMILES', () => {
37
43
  const ast = parse(TELMISARTAN_SMILES);
38
- const code = ast.toCode('v');
44
+ const code = ast.toCode('v', { verbose: true });
39
45
  const executableCode = stripExports(code);
40
46
 
41
47
  const varMatch = code.match(/export const (v\d+) = /g);
@@ -61,6 +67,12 @@ describe('Losartan Integration Test', () => {
61
67
  expect(code).toMatchSnapshot();
62
68
  });
63
69
 
70
+ test('generates valid verbose code via toCode()', () => {
71
+ const ast = parse(LOSARTAN_SMILES);
72
+ const code = ast.toCode('v', { verbose: true });
73
+ expect(code).toMatchSnapshot();
74
+ });
75
+
64
76
  test('generated code is valid JavaScript', () => {
65
77
  const ast = parse(LOSARTAN_SMILES);
66
78
  const code = ast.toCode('v');
@@ -68,14 +80,14 @@ describe('Losartan Integration Test', () => {
68
80
 
69
81
  let factory;
70
82
  expect(() => {
71
- factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', executableCode);
83
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
72
84
  }).not.toThrow();
73
85
  expect(typeof factory).toBe('function');
74
86
  });
75
87
 
76
88
  test('codegen round-trip: generated code produces valid SMILES', () => {
77
89
  const ast = parse(LOSARTAN_SMILES);
78
- const code = ast.toCode('v');
90
+ const code = ast.toCode('v', { verbose: true });
79
91
  const executableCode = stripExports(code);
80
92
 
81
93
  const varMatch = code.match(/export const (v\d+) = /g);
@@ -101,6 +113,12 @@ describe('Valsartan Integration Test', () => {
101
113
  expect(code).toMatchSnapshot();
102
114
  });
103
115
 
116
+ test('generates valid verbose code via toCode()', () => {
117
+ const ast = parse(VALSARTAN_SMILES);
118
+ const code = ast.toCode('v', { verbose: true });
119
+ expect(code).toMatchSnapshot();
120
+ });
121
+
104
122
  test('generated code is valid JavaScript', () => {
105
123
  const ast = parse(VALSARTAN_SMILES);
106
124
  const code = ast.toCode('v');
@@ -108,14 +126,14 @@ describe('Valsartan Integration Test', () => {
108
126
 
109
127
  let factory;
110
128
  expect(() => {
111
- factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', executableCode);
129
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
112
130
  }).not.toThrow();
113
131
  expect(typeof factory).toBe('function');
114
132
  });
115
133
 
116
134
  test('codegen round-trip: generated code produces valid SMILES', () => {
117
135
  const ast = parse(VALSARTAN_SMILES);
118
- const code = ast.toCode('v');
136
+ const code = ast.toCode('v', { verbose: true });
119
137
  const executableCode = stripExports(code);
120
138
 
121
139
  const varMatch = code.match(/export const (v\d+) = /g);
@@ -141,6 +159,12 @@ describe('Irbesartan Integration Test', () => {
141
159
  expect(code).toMatchSnapshot();
142
160
  });
143
161
 
162
+ test('generates valid verbose code via toCode()', () => {
163
+ const ast = parse(IRBESARTAN_SMILES);
164
+ const code = ast.toCode('v', { verbose: true });
165
+ expect(code).toMatchSnapshot();
166
+ });
167
+
144
168
  test('generated code is valid JavaScript', () => {
145
169
  const ast = parse(IRBESARTAN_SMILES);
146
170
  const code = ast.toCode('v');
@@ -148,14 +172,14 @@ describe('Irbesartan Integration Test', () => {
148
172
 
149
173
  let factory;
150
174
  expect(() => {
151
- factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', executableCode);
175
+ factory = createFunction('Ring', 'Linear', 'FusedRing', 'Molecule', 'RawFragment', 'Fragment', executableCode);
152
176
  }).not.toThrow();
153
177
  expect(typeof factory).toBe('function');
154
178
  });
155
179
 
156
180
  test('codegen round-trip: generated code produces valid SMILES', () => {
157
181
  const ast = parse(IRBESARTAN_SMILES);
158
- const code = ast.toCode('v');
182
+ const code = ast.toCode('v', { verbose: true });
159
183
  const executableCode = stripExports(code);
160
184
 
161
185
  const varMatch = code.match(/export const (v\d+) = /g);
@@ -2,6 +2,7 @@
2
2
  // Pain relief through nerve block or topical application
3
3
 
4
4
  import { Fragment } from 'smiles-js';
5
+ import { Ring, Linear, Molecule } from 'smiles-js';
5
6
 
6
7
  // === LIDOCAINE ===
7
8
  // Amide-type local anesthetic (first of this class)
@@ -15,6 +16,38 @@ export const lidocaine = Fragment('CCN(CC)CC(=O)NC1=C(C)C=CC=C1C');
15
16
 
16
17
  export const bupivacaine = Fragment('CCCCN1CCCCC1C(=O)NC2=C(C)C=CC=C2C');
17
18
 
19
+ // Refactored from bupivacaine:
20
+ export const bupivacaine1 = Linear(['C', 'C', 'C', 'C']);
21
+ export const bupivacaine2 = Ring({ atoms: 'C', size: 6 });
22
+ export const bupivacaine3 = bupivacaine2.substitute(1, 'N');
23
+ export const bupivacaine4 = Linear(['C', 'N']);
24
+ export const bupivacaine5 = Linear(['O'], ['=']);
25
+ export const bupivacaine6 = bupivacaine4.attach(1, bupivacaine5);
26
+ export const bupivacaine7 = Ring({
27
+ atoms: 'C', size: 6, ringNumber: 2, bonds: ['=', null, '=', null, '=', null],
28
+ });
29
+ export const bupivacaine8 = Linear(['C']);
30
+ export const bupivacaine9 = bupivacaine7.attach(2, bupivacaine8);
31
+ export const bupivacaine10 = Linear(['C']);
32
+ export const bupivacaine11 = Molecule([
33
+ bupivacaine1, bupivacaine3, bupivacaine6, bupivacaine9, bupivacaine10,
34
+ ]);
35
+
36
+ // Refactored from bupivacaine:
37
+ export const b1 = Fragment('CCCC');
38
+ export const b2 = Fragment('C1CCCCC1');
39
+ export const b3 = Fragment('N1CCCCC1');
40
+ export const b4 = Fragment('CN');
41
+ export const b5 = Linear(['O'], ['=']);
42
+ export const b6 = b4.attach(1, b5);
43
+ export const b7 = Fragment('C2=CC=CC=C2');
44
+ export const b8 = Fragment('C');
45
+ export const b9 = b7.attach(2, b8);
46
+ export const b10 = Fragment('C');
47
+ export const b11 = Molecule([
48
+ b1, b3, b6, b9, b10,
49
+ ]);
50
+
18
51
  // === ROPIVACAINE ===
19
52
  // Similar to bupivacaine but with n-propyl instead of n-butyl
20
53
  // SMILES: CCCN1CCCCC1C(=O)NC2=C(C)C=CC=C2C