selfies-js 0.1.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/LICENSE +21 -0
- package/README.md +274 -0
- package/package.json +65 -0
- package/src/alphabet.js +150 -0
- package/src/alphabet.test.js +82 -0
- package/src/chemistryValidator.js +236 -0
- package/src/cli.js +206 -0
- package/src/constraints.js +186 -0
- package/src/constraints.test.js +126 -0
- package/src/decoder.js +636 -0
- package/src/decoder.test.js +560 -0
- package/src/dsl/analyzer.js +170 -0
- package/src/dsl/analyzer.test.js +139 -0
- package/src/dsl/dsl.test.js +146 -0
- package/src/dsl/importer.js +238 -0
- package/src/dsl/index.js +32 -0
- package/src/dsl/lexer.js +264 -0
- package/src/dsl/lexer.test.js +115 -0
- package/src/dsl/parser.js +201 -0
- package/src/dsl/parser.test.js +148 -0
- package/src/dsl/resolver.js +136 -0
- package/src/dsl/resolver.test.js +99 -0
- package/src/dsl/symbolTable.js +56 -0
- package/src/dsl/symbolTable.test.js +68 -0
- package/src/dsl/valenceValidator.js +147 -0
- package/src/encoder.js +467 -0
- package/src/encoder.test.js +61 -0
- package/src/errors.js +79 -0
- package/src/errors.test.js +91 -0
- package/src/grammar_rules.js +146 -0
- package/src/index.js +70 -0
- package/src/parser.js +96 -0
- package/src/parser.test.js +96 -0
- package/src/properties/atoms.js +69 -0
- package/src/properties/atoms.test.js +116 -0
- package/src/properties/formula.js +111 -0
- package/src/properties/formula.test.js +95 -0
- package/src/properties/molecularWeight.js +80 -0
- package/src/properties/molecularWeight.test.js +84 -0
- package/src/properties/properties.test.js +77 -0
- package/src/renderers/README.md +127 -0
- package/src/renderers/svg.js +113 -0
- package/src/renderers/svg.test.js +42 -0
- package/src/syntax.js +641 -0
- package/src/syntax.test.js +363 -0
- package/src/tokenizer.js +99 -0
- package/src/tokenizer.test.js +55 -0
- package/src/validator.js +70 -0
- package/src/validator.test.js +44 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for semantic constraints
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, test, expect } from 'bun:test'
|
|
6
|
+
import {
|
|
7
|
+
getPresetConstraints,
|
|
8
|
+
getSemanticConstraints,
|
|
9
|
+
setSemanticConstraints,
|
|
10
|
+
getBondingCapacity,
|
|
11
|
+
validateConstraints,
|
|
12
|
+
wouldViolateConstraints,
|
|
13
|
+
resetConstraints
|
|
14
|
+
} from './constraints.js'
|
|
15
|
+
|
|
16
|
+
describe('getPresetConstraints', () => {
|
|
17
|
+
test('returns default preset', () => {
|
|
18
|
+
const constraints = getPresetConstraints('default')
|
|
19
|
+
expect(constraints['C']).toBe(4)
|
|
20
|
+
expect(constraints['N']).toBe(3)
|
|
21
|
+
expect(constraints['O']).toBe(2)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('returns octet_rule preset', () => {
|
|
25
|
+
const constraints = getPresetConstraints('octet_rule')
|
|
26
|
+
expect(constraints['S']).toBe(2) // stricter than default
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('returns hypervalent preset', () => {
|
|
30
|
+
const constraints = getPresetConstraints('hypervalent')
|
|
31
|
+
expect(constraints['Cl']).toBe(7) // more permissive
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('throws on unknown preset', () => {
|
|
35
|
+
expect(() => getPresetConstraints('unknown')).toThrow()
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
describe('getSemanticConstraints', () => {
|
|
40
|
+
test('returns current constraints', () => {
|
|
41
|
+
resetConstraints()
|
|
42
|
+
const constraints = getSemanticConstraints()
|
|
43
|
+
expect(constraints['C']).toBe(4)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('returns copy not reference', () => {
|
|
47
|
+
const c1 = getSemanticConstraints()
|
|
48
|
+
const c2 = getSemanticConstraints()
|
|
49
|
+
expect(c1).not.toBe(c2) // different objects
|
|
50
|
+
expect(c1).toEqual(c2) // same values
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
describe('setSemanticConstraints', () => {
|
|
55
|
+
test('updates constraints', () => {
|
|
56
|
+
setSemanticConstraints({ 'C': 5, '?': 8 })
|
|
57
|
+
const constraints = getSemanticConstraints()
|
|
58
|
+
expect(constraints['C']).toBe(5)
|
|
59
|
+
resetConstraints() // Reset after test
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('validates constraints', () => {
|
|
63
|
+
expect(() => setSemanticConstraints({})).toThrow() // missing '?'
|
|
64
|
+
expect(() => setSemanticConstraints({ '?': -1 })).toThrow() // negative
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('getBondingCapacity', () => {
|
|
69
|
+
test('returns capacity for neutral atoms', () => {
|
|
70
|
+
resetConstraints()
|
|
71
|
+
expect(getBondingCapacity('C')).toBe(4)
|
|
72
|
+
expect(getBondingCapacity('N')).toBe(3)
|
|
73
|
+
expect(getBondingCapacity('O')).toBe(2)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('returns capacity for charged atoms', () => {
|
|
77
|
+
resetConstraints()
|
|
78
|
+
expect(getBondingCapacity('N', 1)).toBe(4) // N+1
|
|
79
|
+
expect(getBondingCapacity('O', -1)).toBe(1) // O-1
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('returns default for unknown elements', () => {
|
|
83
|
+
resetConstraints()
|
|
84
|
+
expect(getBondingCapacity('Xx')).toBe(8) // uses '?' default
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe('validateConstraints', () => {
|
|
89
|
+
test('accepts valid constraints', () => {
|
|
90
|
+
expect(validateConstraints({ 'C': 4, '?': 8 })).toBe(true)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('rejects missing default', () => {
|
|
94
|
+
expect(() => validateConstraints({ 'C': 4 })).toThrow()
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test('rejects negative values', () => {
|
|
98
|
+
expect(() => validateConstraints({ 'C': -1, '?': 8 })).toThrow()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('rejects non-integer values', () => {
|
|
102
|
+
expect(() => validateConstraints({ 'C': 4.5, '?': 8 })).toThrow()
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
describe('wouldViolateConstraints', () => {
|
|
107
|
+
test('detects violations', () => {
|
|
108
|
+
resetConstraints()
|
|
109
|
+
expect(wouldViolateConstraints('C', 0, 3, 2)).toBe(true) // C with 5 bonds
|
|
110
|
+
expect(wouldViolateConstraints('C', 0, 2, 2)).toBe(false) // C with 4 bonds
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('handles charged atoms', () => {
|
|
114
|
+
resetConstraints()
|
|
115
|
+
expect(wouldViolateConstraints('N', 1, 3, 2)).toBe(true) // N+1 with 5 bonds
|
|
116
|
+
expect(wouldViolateConstraints('N', 1, 2, 2)).toBe(false) // N+1 with 4 bonds
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
describe('resetConstraints', () => {
|
|
121
|
+
test('resets to default preset', () => {
|
|
122
|
+
setSemanticConstraints({ 'C': 10, '?': 8 })
|
|
123
|
+
resetConstraints()
|
|
124
|
+
expect(getSemanticConstraints()['C']).toBe(4)
|
|
125
|
+
})
|
|
126
|
+
})
|