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.
- package/docs/smiles.peggy +215 -0
- package/package.json +1 -1
- package/src/decompiler.js +209 -49
- package/src/decompiler.test.js +232 -60
- package/src/fragment.test.js +7 -2
- package/src/method-attachers.js +8 -8
- package/test-integration/__snapshots__/acetaminophen.test.js.snap +20 -0
- package/test-integration/__snapshots__/adjuvant-analgesics.test.js.snap +62 -0
- package/test-integration/__snapshots__/cholesterol-drugs.test.js.snap +261 -0
- package/test-integration/__snapshots__/dexamethasone.test.js.snap +31 -0
- package/test-integration/__snapshots__/endocannabinoids.test.js.snap +77 -0
- package/test-integration/__snapshots__/endogenous-opioids.test.js.snap +1116 -0
- package/test-integration/__snapshots__/hypertension-medication.test.js.snap +69 -0
- package/test-integration/__snapshots__/local-anesthetics.test.js.snap +97 -0
- package/test-integration/__snapshots__/nsaids-otc.test.js.snap +60 -0
- package/test-integration/__snapshots__/nsaids-prescription.test.js.snap +113 -0
- package/test-integration/__snapshots__/opioids.test.js.snap +109 -0
- package/test-integration/__snapshots__/steroids.test.js.snap +379 -0
- package/test-integration/acetaminophen.test.js +15 -3
- package/test-integration/adjuvant-analgesics.test.js +43 -7
- package/test-integration/cholesterol-drugs.test.js +88 -22
- package/test-integration/dexamethasone.test.js +8 -2
- package/test-integration/endocannabinoids.test.js +48 -12
- package/test-integration/endogenous-opioids.smiles.js +32 -0
- package/test-integration/endogenous-opioids.test.js +192 -0
- package/test-integration/hypertension-medication.test.js +32 -8
- package/test-integration/local-anesthetics.smiles.js +33 -0
- package/test-integration/local-anesthetics.test.js +64 -16
- package/test-integration/nsaids-otc.test.js +40 -10
- package/test-integration/nsaids-prescription.test.js +72 -18
- package/test-integration/opioids.test.js +56 -14
- package/test-integration/steroids.test.js +112 -28
- package/test-integration/utils.js +4 -2
- 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
|