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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +274 -0
  3. package/package.json +65 -0
  4. package/src/alphabet.js +150 -0
  5. package/src/alphabet.test.js +82 -0
  6. package/src/chemistryValidator.js +236 -0
  7. package/src/cli.js +206 -0
  8. package/src/constraints.js +186 -0
  9. package/src/constraints.test.js +126 -0
  10. package/src/decoder.js +636 -0
  11. package/src/decoder.test.js +560 -0
  12. package/src/dsl/analyzer.js +170 -0
  13. package/src/dsl/analyzer.test.js +139 -0
  14. package/src/dsl/dsl.test.js +146 -0
  15. package/src/dsl/importer.js +238 -0
  16. package/src/dsl/index.js +32 -0
  17. package/src/dsl/lexer.js +264 -0
  18. package/src/dsl/lexer.test.js +115 -0
  19. package/src/dsl/parser.js +201 -0
  20. package/src/dsl/parser.test.js +148 -0
  21. package/src/dsl/resolver.js +136 -0
  22. package/src/dsl/resolver.test.js +99 -0
  23. package/src/dsl/symbolTable.js +56 -0
  24. package/src/dsl/symbolTable.test.js +68 -0
  25. package/src/dsl/valenceValidator.js +147 -0
  26. package/src/encoder.js +467 -0
  27. package/src/encoder.test.js +61 -0
  28. package/src/errors.js +79 -0
  29. package/src/errors.test.js +91 -0
  30. package/src/grammar_rules.js +146 -0
  31. package/src/index.js +70 -0
  32. package/src/parser.js +96 -0
  33. package/src/parser.test.js +96 -0
  34. package/src/properties/atoms.js +69 -0
  35. package/src/properties/atoms.test.js +116 -0
  36. package/src/properties/formula.js +111 -0
  37. package/src/properties/formula.test.js +95 -0
  38. package/src/properties/molecularWeight.js +80 -0
  39. package/src/properties/molecularWeight.test.js +84 -0
  40. package/src/properties/properties.test.js +77 -0
  41. package/src/renderers/README.md +127 -0
  42. package/src/renderers/svg.js +113 -0
  43. package/src/renderers/svg.test.js +42 -0
  44. package/src/syntax.js +641 -0
  45. package/src/syntax.test.js +363 -0
  46. package/src/tokenizer.js +99 -0
  47. package/src/tokenizer.test.js +55 -0
  48. package/src/validator.js +70 -0
  49. 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
+ })