@flomsstaar/expression-tree 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/README.md +83 -0
- package/build/example.d.ts +1 -0
- package/build/example.js +18 -0
- package/build/index.d.ts +16 -0
- package/build/index.js +16 -0
- package/build/src/enums/binary_node_operator.d.ts +7 -0
- package/build/src/enums/binary_node_operator.js +8 -0
- package/build/src/enums/node_type.d.ts +6 -0
- package/build/src/enums/node_type.js +7 -0
- package/build/src/enums/unary_node_operator.d.ts +5 -0
- package/build/src/enums/unary_node_operator.js +6 -0
- package/build/src/errors/division_by_zero_error.d.ts +3 -0
- package/build/src/errors/division_by_zero_error.js +6 -0
- package/build/src/errors/invalid_expression_error.d.ts +3 -0
- package/build/src/errors/invalid_expression_error.js +6 -0
- package/build/src/errors/invalid_number_error.d.ts +4 -0
- package/build/src/errors/invalid_number_error.js +8 -0
- package/build/src/errors/invalid_square_root_error.d.ts +3 -0
- package/build/src/errors/invalid_square_root_error.js +6 -0
- package/build/src/errors/missing_parenthese_error.d.ts +3 -0
- package/build/src/errors/missing_parenthese_error.js +6 -0
- package/build/src/errors/unknown_binary_operator_error.d.ts +3 -0
- package/build/src/errors/unknown_binary_operator_error.js +6 -0
- package/build/src/errors/unknown_node_type_error.d.ts +3 -0
- package/build/src/errors/unknown_node_type_error.js +6 -0
- package/build/src/errors/unknown_unary_operator_error.d.ts +3 -0
- package/build/src/errors/unknown_unary_operator_error.js +6 -0
- package/build/src/expression_cutter.d.ts +4 -0
- package/build/src/expression_cutter.js +7 -0
- package/build/src/expression_evaluator.d.ts +10 -0
- package/build/src/expression_evaluator.js +74 -0
- package/build/src/expression_parser.d.ts +14 -0
- package/build/src/expression_parser.js +94 -0
- package/build/src/expression_tree.d.ts +10 -0
- package/build/src/expression_tree.js +18 -0
- package/build/src/index.d.ts +1 -0
- package/build/src/index.js +1 -0
- package/build/src/models/base_node.d.ts +5 -0
- package/build/src/models/base_node.js +6 -0
- package/build/src/models/binary_node.d.ts +8 -0
- package/build/src/models/binary_node.js +13 -0
- package/build/src/models/number_node.d.ts +6 -0
- package/build/src/models/number_node.js +12 -0
- package/build/src/models/unary_node.d.ts +7 -0
- package/build/src/models/unary_node.js +11 -0
- package/build/src/render_expression.d.ts +2 -0
- package/build/src/render_expression.js +56 -0
- package/build/tests/expression_cutter.test.d.ts +1 -0
- package/build/tests/expression_cutter.test.js +9 -0
- package/build/tests/expression_evaluator.test.d.ts +1 -0
- package/build/tests/expression_evaluator.test.js +110 -0
- package/build/tests/expression_parser.test.d.ts +1 -0
- package/build/tests/expression_parser.test.js +44 -0
- package/build/tests/expression_tree.test.d.ts +1 -0
- package/build/tests/expression_tree.test.js +12 -0
- package/build/tests/inputs/expression_cutter_input.d.ts +1 -0
- package/build/tests/inputs/expression_cutter_input.js +230 -0
- package/build/tests/inputs/expression_parser_input.d.ts +2 -0
- package/build/tests/inputs/expression_parser_input.js +197 -0
- package/build/tests/inputs/expression_tree_input.d.ts +1 -0
- package/build/tests/inputs/expression_tree_input.js +44 -0
- package/build/tests/number_node.test.d.ts +1 -0
- package/build/tests/number_node.test.js +16 -0
- package/build/tests/render_expression.test.d.ts +1 -0
- package/build/tests/render_expression.test.js +85 -0
- package/build/vitest.config.d.ts +2 -0
- package/build/vitest.config.js +10 -0
- package/package.json +47 -0
- package/src/enums/binary_node_operator.ts +7 -0
- package/src/enums/node_type.ts +6 -0
- package/src/enums/unary_node_operator.ts +5 -0
- package/src/errors/division_by_zero_error.ts +6 -0
- package/src/errors/invalid_expression_error.ts +6 -0
- package/src/errors/invalid_number_error.ts +6 -0
- package/src/errors/invalid_square_root_error.ts +6 -0
- package/src/errors/missing_parenthese_error.ts +6 -0
- package/src/errors/unknown_binary_operator_error.ts +6 -0
- package/src/errors/unknown_node_type_error.ts +6 -0
- package/src/errors/unknown_unary_operator_error.ts +6 -0
- package/src/expression_cutter.ts +9 -0
- package/src/expression_evaluator.ts +76 -0
- package/src/expression_parser.ts +117 -0
- package/src/expression_tree.ts +19 -0
- package/src/models/base_node.ts +5 -0
- package/src/models/binary_node.ts +13 -0
- package/src/models/number_node.ts +12 -0
- package/src/models/unary_node.ts +12 -0
- package/src/render_expression.ts +57 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { BinaryNodeOperator } from '../../src/enums/binary_node_operator.js';
|
|
2
|
+
import { BinaryNode } from '../../src/models/binary_node.js';
|
|
3
|
+
import { NumberNode } from '../../src/models/number_node.js';
|
|
4
|
+
import { UnaryNodeOperator } from '../../src/enums/unary_node_operator.js';
|
|
5
|
+
import { UnaryNode } from '../../src/models/unary_node.js';
|
|
6
|
+
export function expressionParserInput() {
|
|
7
|
+
return [
|
|
8
|
+
// Classic cases
|
|
9
|
+
[['6', '+', '5'], new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(6), new NumberNode(5))],
|
|
10
|
+
[
|
|
11
|
+
['6', '-', '5'],
|
|
12
|
+
new BinaryNode(BinaryNodeOperator.SUBTRACT, new NumberNode(6), new NumberNode(5)),
|
|
13
|
+
],
|
|
14
|
+
[
|
|
15
|
+
['6', '*', '5'],
|
|
16
|
+
new BinaryNode(BinaryNodeOperator.MULTIPLY, new NumberNode(6), new NumberNode(5)),
|
|
17
|
+
],
|
|
18
|
+
[
|
|
19
|
+
['6', '/', '5'],
|
|
20
|
+
new BinaryNode(BinaryNodeOperator.DIVIDE, new NumberNode(6), new NumberNode(5)),
|
|
21
|
+
],
|
|
22
|
+
[
|
|
23
|
+
['6', '^', '5'],
|
|
24
|
+
new BinaryNode(BinaryNodeOperator.POWER, new NumberNode(6), new NumberNode(5)),
|
|
25
|
+
],
|
|
26
|
+
[
|
|
27
|
+
['64', '+', '5'],
|
|
28
|
+
new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(64), new NumberNode(5)),
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
['64', '-', '5'],
|
|
32
|
+
new BinaryNode(BinaryNodeOperator.SUBTRACT, new NumberNode(64), new NumberNode(5)),
|
|
33
|
+
],
|
|
34
|
+
[
|
|
35
|
+
['64', '*', '5'],
|
|
36
|
+
new BinaryNode(BinaryNodeOperator.MULTIPLY, new NumberNode(64), new NumberNode(5)),
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
['64', '/', '5'],
|
|
40
|
+
new BinaryNode(BinaryNodeOperator.DIVIDE, new NumberNode(64), new NumberNode(5)),
|
|
41
|
+
],
|
|
42
|
+
[
|
|
43
|
+
['64', '^', '5'],
|
|
44
|
+
new BinaryNode(BinaryNodeOperator.POWER, new NumberNode(64), new NumberNode(5)),
|
|
45
|
+
],
|
|
46
|
+
[
|
|
47
|
+
['6', '+', '25'],
|
|
48
|
+
new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(6), new NumberNode(25)),
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
['6', '-', '25'],
|
|
52
|
+
new BinaryNode(BinaryNodeOperator.SUBTRACT, new NumberNode(6), new NumberNode(25)),
|
|
53
|
+
],
|
|
54
|
+
[
|
|
55
|
+
['6', '*', '25'],
|
|
56
|
+
new BinaryNode(BinaryNodeOperator.MULTIPLY, new NumberNode(6), new NumberNode(25)),
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
['6', '/', '25'],
|
|
60
|
+
new BinaryNode(BinaryNodeOperator.DIVIDE, new NumberNode(6), new NumberNode(25)),
|
|
61
|
+
],
|
|
62
|
+
[
|
|
63
|
+
['6', '^', '25'],
|
|
64
|
+
new BinaryNode(BinaryNodeOperator.POWER, new NumberNode(6), new NumberNode(25)),
|
|
65
|
+
],
|
|
66
|
+
[
|
|
67
|
+
['12', '+', '34'],
|
|
68
|
+
new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(12), new NumberNode(34)),
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
['12', '-', '34'],
|
|
72
|
+
new BinaryNode(BinaryNodeOperator.SUBTRACT, new NumberNode(12), new NumberNode(34)),
|
|
73
|
+
],
|
|
74
|
+
[
|
|
75
|
+
['12', '*', '34'],
|
|
76
|
+
new BinaryNode(BinaryNodeOperator.MULTIPLY, new NumberNode(12), new NumberNode(34)),
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
['12', '/', '34'],
|
|
80
|
+
new BinaryNode(BinaryNodeOperator.DIVIDE, new NumberNode(12), new NumberNode(34)),
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
['12', '^', '34'],
|
|
84
|
+
new BinaryNode(BinaryNodeOperator.POWER, new NumberNode(12), new NumberNode(34)),
|
|
85
|
+
],
|
|
86
|
+
// Parentheses
|
|
87
|
+
[
|
|
88
|
+
['(', '6', '+', '5', ')'],
|
|
89
|
+
new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(6), new NumberNode(5)),
|
|
90
|
+
],
|
|
91
|
+
[
|
|
92
|
+
['(', '6', '-', '5', ')'],
|
|
93
|
+
new BinaryNode(BinaryNodeOperator.SUBTRACT, new NumberNode(6), new NumberNode(5)),
|
|
94
|
+
],
|
|
95
|
+
[
|
|
96
|
+
['(', '6', '*', '5', ')'],
|
|
97
|
+
new BinaryNode(BinaryNodeOperator.MULTIPLY, new NumberNode(6), new NumberNode(5)),
|
|
98
|
+
],
|
|
99
|
+
[
|
|
100
|
+
['(', '6', '/', '5', ')'],
|
|
101
|
+
new BinaryNode(BinaryNodeOperator.DIVIDE, new NumberNode(6), new NumberNode(5)),
|
|
102
|
+
],
|
|
103
|
+
[
|
|
104
|
+
['(', '6', '^', '5', ')'],
|
|
105
|
+
new BinaryNode(BinaryNodeOperator.POWER, new NumberNode(6), new NumberNode(5)),
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
['(', '64', '+', '5', ')'],
|
|
109
|
+
new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(64), new NumberNode(5)),
|
|
110
|
+
],
|
|
111
|
+
[
|
|
112
|
+
['(', '64', '-', '5', ')'],
|
|
113
|
+
new BinaryNode(BinaryNodeOperator.SUBTRACT, new NumberNode(64), new NumberNode(5)),
|
|
114
|
+
],
|
|
115
|
+
[
|
|
116
|
+
['(', '64', '*', '5', ')'],
|
|
117
|
+
new BinaryNode(BinaryNodeOperator.MULTIPLY, new NumberNode(64), new NumberNode(5)),
|
|
118
|
+
],
|
|
119
|
+
[
|
|
120
|
+
['(', '64', '/', '5', ')'],
|
|
121
|
+
new BinaryNode(BinaryNodeOperator.DIVIDE, new NumberNode(64), new NumberNode(5)),
|
|
122
|
+
],
|
|
123
|
+
[
|
|
124
|
+
['(', '64', '^', '5', ')'],
|
|
125
|
+
new BinaryNode(BinaryNodeOperator.POWER, new NumberNode(64), new NumberNode(5)),
|
|
126
|
+
],
|
|
127
|
+
[
|
|
128
|
+
['(', '6', '+', '25', ')'],
|
|
129
|
+
new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(6), new NumberNode(25)),
|
|
130
|
+
],
|
|
131
|
+
[
|
|
132
|
+
['(', '6', '-', '25', ')'],
|
|
133
|
+
new BinaryNode(BinaryNodeOperator.SUBTRACT, new NumberNode(6), new NumberNode(25)),
|
|
134
|
+
],
|
|
135
|
+
[
|
|
136
|
+
['(', '6', '*', '25', ')'],
|
|
137
|
+
new BinaryNode(BinaryNodeOperator.MULTIPLY, new NumberNode(6), new NumberNode(25)),
|
|
138
|
+
],
|
|
139
|
+
[
|
|
140
|
+
['(', '6', '/', '25', ')'],
|
|
141
|
+
new BinaryNode(BinaryNodeOperator.DIVIDE, new NumberNode(6), new NumberNode(25)),
|
|
142
|
+
],
|
|
143
|
+
[
|
|
144
|
+
['(', '6', '^', '25', ')'],
|
|
145
|
+
new BinaryNode(BinaryNodeOperator.POWER, new NumberNode(6), new NumberNode(25)),
|
|
146
|
+
],
|
|
147
|
+
[
|
|
148
|
+
['(', '12', '+', '34', ')'],
|
|
149
|
+
new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(12), new NumberNode(34)),
|
|
150
|
+
],
|
|
151
|
+
[
|
|
152
|
+
['(', '12', '-', '34', ')'],
|
|
153
|
+
new BinaryNode(BinaryNodeOperator.SUBTRACT, new NumberNode(12), new NumberNode(34)),
|
|
154
|
+
],
|
|
155
|
+
[
|
|
156
|
+
['(', '12', '*', '34', ')'],
|
|
157
|
+
new BinaryNode(BinaryNodeOperator.MULTIPLY, new NumberNode(12), new NumberNode(34)),
|
|
158
|
+
],
|
|
159
|
+
[
|
|
160
|
+
['(', '12', '/', '34', ')'],
|
|
161
|
+
new BinaryNode(BinaryNodeOperator.DIVIDE, new NumberNode(12), new NumberNode(34)),
|
|
162
|
+
],
|
|
163
|
+
[
|
|
164
|
+
['(', '12', '^', '34', ')'],
|
|
165
|
+
new BinaryNode(BinaryNodeOperator.POWER, new NumberNode(12), new NumberNode(34)),
|
|
166
|
+
],
|
|
167
|
+
// Negative numbers
|
|
168
|
+
[
|
|
169
|
+
['-', '6', '+', '5'],
|
|
170
|
+
new BinaryNode(BinaryNodeOperator.SUM, new UnaryNode(UnaryNodeOperator.NEGATE, new NumberNode(6)), new NumberNode(5)),
|
|
171
|
+
],
|
|
172
|
+
[
|
|
173
|
+
['6', '+', '-', '5'],
|
|
174
|
+
new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(6), new UnaryNode(UnaryNodeOperator.NEGATE, new NumberNode(5))),
|
|
175
|
+
],
|
|
176
|
+
[
|
|
177
|
+
['6', '-', '-', '5'],
|
|
178
|
+
new BinaryNode(BinaryNodeOperator.SUBTRACT, new NumberNode(6), new UnaryNode(UnaryNodeOperator.NEGATE, new NumberNode(5))),
|
|
179
|
+
],
|
|
180
|
+
[
|
|
181
|
+
['-', '6', '-', '5'],
|
|
182
|
+
new BinaryNode(BinaryNodeOperator.SUBTRACT, new UnaryNode(UnaryNodeOperator.NEGATE, new NumberNode(6)), new NumberNode(5)),
|
|
183
|
+
],
|
|
184
|
+
[
|
|
185
|
+
['-', '6', '-', '-', '5'],
|
|
186
|
+
new BinaryNode(BinaryNodeOperator.SUBTRACT, new UnaryNode(UnaryNodeOperator.NEGATE, new NumberNode(6)), new UnaryNode(UnaryNodeOperator.NEGATE, new NumberNode(5))),
|
|
187
|
+
],
|
|
188
|
+
[
|
|
189
|
+
['-', '(', '5', '+', '2', ')'],
|
|
190
|
+
new UnaryNode(UnaryNodeOperator.NEGATE, new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(5), new NumberNode(2))),
|
|
191
|
+
],
|
|
192
|
+
[
|
|
193
|
+
['-', '(', '5', '*', '2', '-', '4', ')', '/', '2'],
|
|
194
|
+
new BinaryNode(BinaryNodeOperator.DIVIDE, new UnaryNode(UnaryNodeOperator.NEGATE, new BinaryNode(BinaryNodeOperator.SUBTRACT, new BinaryNode(BinaryNodeOperator.MULTIPLY, new NumberNode(5), new NumberNode(2)), new NumberNode(4))), new NumberNode(2)),
|
|
195
|
+
],
|
|
196
|
+
];
|
|
197
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function expressionTreeInput(): [string, number][];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export function expressionTreeInput() {
|
|
2
|
+
return [
|
|
3
|
+
['6 + 5', 11],
|
|
4
|
+
['6 - 5', 1],
|
|
5
|
+
['6 * 5', 30],
|
|
6
|
+
['6 / 5', 1.2],
|
|
7
|
+
['6 ^ 5', 7776],
|
|
8
|
+
['64 + 5', 69],
|
|
9
|
+
['64 - 5', 59],
|
|
10
|
+
['64 * 5', 320],
|
|
11
|
+
['64 / 5', 12.8],
|
|
12
|
+
['64 ^ 5', 1073741824],
|
|
13
|
+
['6 + 25', 31],
|
|
14
|
+
['6 - 25', -19],
|
|
15
|
+
['6 * 25', 150],
|
|
16
|
+
['6 / 25', 0.24],
|
|
17
|
+
['6 ^ 12', 2176782336],
|
|
18
|
+
['(6 ^ 2)', 36],
|
|
19
|
+
['(2 ^ 15)', 32768],
|
|
20
|
+
['(12 ^ 4)', 20736],
|
|
21
|
+
['(6 + 5) ^ (4 * 3)', 3138428376721],
|
|
22
|
+
['2 ^ -1', 0.5],
|
|
23
|
+
['(6 + 5) * (3 - 1)', 22],
|
|
24
|
+
['(6 + -2) * (3 - 1)', 8],
|
|
25
|
+
['(6 + -2) * (3 - -1)', 16],
|
|
26
|
+
['(6 + -2) * (3 - -1) / (3 + -1)', 8],
|
|
27
|
+
['(6 + -2) * (3 - -1) / (3 + -1) / (2 + -1)', 8],
|
|
28
|
+
['6 - -2 + 5', 13],
|
|
29
|
+
['6 + -(2 + 5)', -1],
|
|
30
|
+
['6 + -2 * 5', -4],
|
|
31
|
+
['6 + -2 * (5 + 3)', -10],
|
|
32
|
+
['6 + -2 * (5 + 3) / 2', -2],
|
|
33
|
+
['6 + -2 * (5 + 3) / (3 + 1)', 2],
|
|
34
|
+
['6 + -(2 * 5)', -4],
|
|
35
|
+
['6 + 2 * 3 - 1', 11],
|
|
36
|
+
['6 + 2 * (3 - 1)', 10],
|
|
37
|
+
['6 + 2 * (3 - 1) / 2', 8],
|
|
38
|
+
['6 + 2 * (3 - 1) / (3 + 1)', 7],
|
|
39
|
+
['(6 + 2) * (3 - 1)', 16],
|
|
40
|
+
['(6 + 2) * (4 - 1) / (2 + 1)', 8],
|
|
41
|
+
['(6 + -2) * (3 - 1)', 8],
|
|
42
|
+
['(6 + -2) * (3 - 2) / 2', 2],
|
|
43
|
+
];
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { NumberNode } from '../src/models/number_node.js';
|
|
3
|
+
import { NodeType } from '../src/enums/node_type.js';
|
|
4
|
+
describe('number node', () => {
|
|
5
|
+
it('should create a number node with the correct value', () => {
|
|
6
|
+
const value = 42;
|
|
7
|
+
const node = new NumberNode(value);
|
|
8
|
+
expect(node.value).toBe(value);
|
|
9
|
+
expect(node.type).toBe(NodeType.NUMBER);
|
|
10
|
+
});
|
|
11
|
+
it('should return the correct string representation', () => {
|
|
12
|
+
const value = 42;
|
|
13
|
+
const node = new NumberNode(value);
|
|
14
|
+
expect(node.toString()).toBe(value.toString());
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { describe, expect, it, test } from 'vitest';
|
|
2
|
+
import { UnaryNode } from '../src/models/unary_node.js';
|
|
3
|
+
import { UnaryNodeOperator } from '../src/enums/unary_node_operator.js';
|
|
4
|
+
import { NumberNode } from '../src/models/number_node.js';
|
|
5
|
+
import { renderExpression } from '../src/render_expression.js';
|
|
6
|
+
import { BinaryNode } from '../src/models/binary_node.js';
|
|
7
|
+
import { BinaryNodeOperator } from '../src/enums/binary_node_operator.js';
|
|
8
|
+
import { UnknownBinaryOperatorError } from '../src/errors/unknown_binary_operator_error.js';
|
|
9
|
+
import { UnknownNodeTypeError } from '../src/errors/unknown_node_type_error.js';
|
|
10
|
+
import { UnknownUnaryOperatorError } from '../src/errors/unknown_unary_operator_error.js';
|
|
11
|
+
describe('render expression', () => {
|
|
12
|
+
describe('unary node operations', () => {
|
|
13
|
+
describe('negation operation', () => {
|
|
14
|
+
it('should render a negation of a number', () => {
|
|
15
|
+
const expression = new UnaryNode(UnaryNodeOperator.NEGATE, new NumberNode(5));
|
|
16
|
+
const result = renderExpression(expression);
|
|
17
|
+
expect(result).toBe('-5');
|
|
18
|
+
});
|
|
19
|
+
it('should render a negation of zero', () => {
|
|
20
|
+
const expression = new UnaryNode(UnaryNodeOperator.NEGATE, new NumberNode(0));
|
|
21
|
+
const result = renderExpression(expression);
|
|
22
|
+
expect(result).toBe('0');
|
|
23
|
+
});
|
|
24
|
+
it('should render a negation of an operation', () => {
|
|
25
|
+
const expression = new UnaryNode(UnaryNodeOperator.NEGATE, new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(3), new NumberNode(2)));
|
|
26
|
+
const result = renderExpression(expression);
|
|
27
|
+
expect(result).toBe('-(3 + 2)');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
it('should render a square root of a number', () => {
|
|
31
|
+
const expression = new UnaryNode(UnaryNodeOperator.SQUARE_ROOT, new NumberNode(16));
|
|
32
|
+
const result = renderExpression(expression);
|
|
33
|
+
expect(result).toBe('sqrt(16)');
|
|
34
|
+
});
|
|
35
|
+
it('should render an absolute value of a number', () => {
|
|
36
|
+
const expression = new UnaryNode(UnaryNodeOperator.ABSOLUTE, new NumberNode(-10));
|
|
37
|
+
const result = renderExpression(expression);
|
|
38
|
+
expect(result).toBe('abs(-10)');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('binary node operations', () => {
|
|
42
|
+
it('should render a sum of two numbers', () => {
|
|
43
|
+
const expression = new BinaryNode(BinaryNodeOperator.SUM, new NumberNode(3), new NumberNode(2));
|
|
44
|
+
const result = renderExpression(expression);
|
|
45
|
+
expect(result).toBe('3 + 2');
|
|
46
|
+
});
|
|
47
|
+
it('should render a subtraction of two numbers', () => {
|
|
48
|
+
const expression = new BinaryNode(BinaryNodeOperator.SUBTRACT, new NumberNode(5), new NumberNode(2));
|
|
49
|
+
const result = renderExpression(expression);
|
|
50
|
+
expect(result).toBe('5 - 2');
|
|
51
|
+
});
|
|
52
|
+
it('should render a multiplication of two numbers', () => {
|
|
53
|
+
const expression = new BinaryNode(BinaryNodeOperator.MULTIPLY, new NumberNode(4), new NumberNode(3));
|
|
54
|
+
const result = renderExpression(expression);
|
|
55
|
+
expect(result).toBe('4 * 3');
|
|
56
|
+
});
|
|
57
|
+
it('should render a division of two numbers', () => {
|
|
58
|
+
const expression = new BinaryNode(BinaryNodeOperator.DIVIDE, new NumberNode(8), new NumberNode(2));
|
|
59
|
+
const result = renderExpression(expression);
|
|
60
|
+
expect(result).toBe('8 / 2');
|
|
61
|
+
});
|
|
62
|
+
it('should render a power operation', () => {
|
|
63
|
+
const expression = new BinaryNode(BinaryNodeOperator.POWER, new NumberNode(2), new NumberNode(3));
|
|
64
|
+
const result = renderExpression(expression);
|
|
65
|
+
expect(result).toBe('2 ^ 3');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe('unsupported operations', () => {
|
|
69
|
+
test('unknown unary operator', () => {
|
|
70
|
+
const expression = new UnaryNode('UNKNOWN_UNARY_OPERATOR', new NumberNode(1));
|
|
71
|
+
expect(() => renderExpression(expression)).toThrowError(UnknownUnaryOperatorError);
|
|
72
|
+
});
|
|
73
|
+
test('unknown binary operator', () => {
|
|
74
|
+
const expression = new BinaryNode('UNKNOWN_OPERATOR', new NumberNode(1), new NumberNode(2));
|
|
75
|
+
expect(() => renderExpression(expression)).toThrowError(UnknownBinaryOperatorError);
|
|
76
|
+
});
|
|
77
|
+
test('unknown node type', () => {
|
|
78
|
+
const expression = {
|
|
79
|
+
type: 'UNKNOWN_NODE_TYPE',
|
|
80
|
+
value: 42,
|
|
81
|
+
};
|
|
82
|
+
expect(() => renderExpression(expression)).toThrowError(UnknownNodeTypeError);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flomsstaar/expression-tree",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "build/index.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"src",
|
|
8
|
+
"build"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./build/index.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "bun index.ts",
|
|
15
|
+
"lint": "eslint src index.ts",
|
|
16
|
+
"lint:fix": "eslint src index.ts --fix",
|
|
17
|
+
"format": "prettier --check src index.ts",
|
|
18
|
+
"format:fix": "prettier --write src index.ts",
|
|
19
|
+
"test": "vitest",
|
|
20
|
+
"coverage": "vitest run --coverage",
|
|
21
|
+
"typecheck": "bun run tsc --noEmit"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@adonisjs/eslint-config": "^2.1.0",
|
|
25
|
+
"@adonisjs/prettier-config": "^1.4.5",
|
|
26
|
+
"@adonisjs/tsconfig": "^1.4.1",
|
|
27
|
+
"@types/bun": "latest",
|
|
28
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
29
|
+
"eslint": "^9.29.0",
|
|
30
|
+
"prettier": "^3.6.0",
|
|
31
|
+
"vitest": "^3.2.4"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"typescript": "^5.8.3"
|
|
35
|
+
},
|
|
36
|
+
"author": "Florent Marques <florent.marques19@gmail.com>",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"keywords": [
|
|
39
|
+
"expression-tree",
|
|
40
|
+
"calculator"
|
|
41
|
+
],
|
|
42
|
+
"repository": "https://github.com/flomSStaar/expression-tree-ts",
|
|
43
|
+
"eslintConfig": {
|
|
44
|
+
"extends": "@adonisjs/eslint-config/app"
|
|
45
|
+
},
|
|
46
|
+
"prettier": "@adonisjs/prettier-config"
|
|
47
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export class ExpressionCutter {
|
|
2
|
+
static readonly REGEX = /(\d+(?:\.\d+)?|\+|-|\*|\^|\/|\(|\)|sqrt|abs)/g
|
|
3
|
+
|
|
4
|
+
cut(expression: string): string[] {
|
|
5
|
+
const cleanExpr = expression.replace(/\s+/g, '')
|
|
6
|
+
|
|
7
|
+
return Array.from(cleanExpr.matchAll(ExpressionCutter.REGEX)).map((m) => m[0])
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { BinaryNode } from './models/binary_node.js'
|
|
2
|
+
import type { BaseNode } from './models/base_node.js'
|
|
3
|
+
import { UnaryNode } from './models/unary_node.js'
|
|
4
|
+
import { NumberNode } from './models/number_node.js'
|
|
5
|
+
import { UnknownNodeTypeError } from './errors/unknown_node_type_error.js'
|
|
6
|
+
import { BinaryNodeOperator } from './enums/binary_node_operator.js'
|
|
7
|
+
import { UnaryNodeOperator } from './enums/unary_node_operator.js'
|
|
8
|
+
import { NegativeSquareRootError } from './errors/invalid_square_root_error.js'
|
|
9
|
+
import { UnknownUnaryOperatorError } from './errors/unknown_unary_operator_error.js'
|
|
10
|
+
import { UnknownBinaryOperatorError } from './errors/unknown_binary_operator_error.js'
|
|
11
|
+
import { DivisionByZeroError } from './errors/division_by_zero_error.js'
|
|
12
|
+
|
|
13
|
+
export class ExpressionEvaluator {
|
|
14
|
+
public evaluate(node: BaseNode): number {
|
|
15
|
+
if (node instanceof BinaryNode) {
|
|
16
|
+
return this.evaluateBinaryNode(node)
|
|
17
|
+
} else if (node instanceof UnaryNode) {
|
|
18
|
+
return this.evaluateUnaryNode(node)
|
|
19
|
+
} else if (node instanceof NumberNode) {
|
|
20
|
+
return this.evaluateNumberNode(node)
|
|
21
|
+
} else {
|
|
22
|
+
throw new UnknownNodeTypeError(node.type)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
protected evaluateBinaryNode(node: BinaryNode): number {
|
|
27
|
+
switch (node.operator) {
|
|
28
|
+
case BinaryNodeOperator.SUM: {
|
|
29
|
+
return this.evaluate(node.left) + this.evaluate(node.right)
|
|
30
|
+
}
|
|
31
|
+
case BinaryNodeOperator.SUBTRACT: {
|
|
32
|
+
return this.evaluate(node.left) - this.evaluate(node.right)
|
|
33
|
+
}
|
|
34
|
+
case BinaryNodeOperator.MULTIPLY: {
|
|
35
|
+
return this.evaluate(node.left) * this.evaluate(node.right)
|
|
36
|
+
}
|
|
37
|
+
case BinaryNodeOperator.DIVIDE: {
|
|
38
|
+
const leftValue = this.evaluate(node.left)
|
|
39
|
+
const rightValue = this.evaluate(node.right)
|
|
40
|
+
if (rightValue === 0) {
|
|
41
|
+
throw new DivisionByZeroError()
|
|
42
|
+
}
|
|
43
|
+
return leftValue / rightValue
|
|
44
|
+
}
|
|
45
|
+
case BinaryNodeOperator.POWER: {
|
|
46
|
+
return Math.pow(this.evaluate(node.left), this.evaluate(node.right))
|
|
47
|
+
}
|
|
48
|
+
default:
|
|
49
|
+
throw new UnknownBinaryOperatorError(node.operator)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected evaluateUnaryNode(node: UnaryNode): number {
|
|
54
|
+
switch (node.operator) {
|
|
55
|
+
case UnaryNodeOperator.NEGATE: {
|
|
56
|
+
return -this.evaluate(node.value)
|
|
57
|
+
}
|
|
58
|
+
case UnaryNodeOperator.ABSOLUTE: {
|
|
59
|
+
return Math.abs(this.evaluate(node.value))
|
|
60
|
+
}
|
|
61
|
+
case UnaryNodeOperator.SQUARE_ROOT: {
|
|
62
|
+
const leftValue = this.evaluate(node.value)
|
|
63
|
+
if (leftValue < 0) {
|
|
64
|
+
throw new NegativeSquareRootError()
|
|
65
|
+
}
|
|
66
|
+
return Math.sqrt(leftValue)
|
|
67
|
+
}
|
|
68
|
+
default:
|
|
69
|
+
throw new UnknownUnaryOperatorError(node.operator)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected evaluateNumberNode(node: NumberNode): number {
|
|
74
|
+
return node.value
|
|
75
|
+
}
|
|
76
|
+
}
|