@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.
Files changed (88) hide show
  1. package/README.md +83 -0
  2. package/build/example.d.ts +1 -0
  3. package/build/example.js +18 -0
  4. package/build/index.d.ts +16 -0
  5. package/build/index.js +16 -0
  6. package/build/src/enums/binary_node_operator.d.ts +7 -0
  7. package/build/src/enums/binary_node_operator.js +8 -0
  8. package/build/src/enums/node_type.d.ts +6 -0
  9. package/build/src/enums/node_type.js +7 -0
  10. package/build/src/enums/unary_node_operator.d.ts +5 -0
  11. package/build/src/enums/unary_node_operator.js +6 -0
  12. package/build/src/errors/division_by_zero_error.d.ts +3 -0
  13. package/build/src/errors/division_by_zero_error.js +6 -0
  14. package/build/src/errors/invalid_expression_error.d.ts +3 -0
  15. package/build/src/errors/invalid_expression_error.js +6 -0
  16. package/build/src/errors/invalid_number_error.d.ts +4 -0
  17. package/build/src/errors/invalid_number_error.js +8 -0
  18. package/build/src/errors/invalid_square_root_error.d.ts +3 -0
  19. package/build/src/errors/invalid_square_root_error.js +6 -0
  20. package/build/src/errors/missing_parenthese_error.d.ts +3 -0
  21. package/build/src/errors/missing_parenthese_error.js +6 -0
  22. package/build/src/errors/unknown_binary_operator_error.d.ts +3 -0
  23. package/build/src/errors/unknown_binary_operator_error.js +6 -0
  24. package/build/src/errors/unknown_node_type_error.d.ts +3 -0
  25. package/build/src/errors/unknown_node_type_error.js +6 -0
  26. package/build/src/errors/unknown_unary_operator_error.d.ts +3 -0
  27. package/build/src/errors/unknown_unary_operator_error.js +6 -0
  28. package/build/src/expression_cutter.d.ts +4 -0
  29. package/build/src/expression_cutter.js +7 -0
  30. package/build/src/expression_evaluator.d.ts +10 -0
  31. package/build/src/expression_evaluator.js +74 -0
  32. package/build/src/expression_parser.d.ts +14 -0
  33. package/build/src/expression_parser.js +94 -0
  34. package/build/src/expression_tree.d.ts +10 -0
  35. package/build/src/expression_tree.js +18 -0
  36. package/build/src/index.d.ts +1 -0
  37. package/build/src/index.js +1 -0
  38. package/build/src/models/base_node.d.ts +5 -0
  39. package/build/src/models/base_node.js +6 -0
  40. package/build/src/models/binary_node.d.ts +8 -0
  41. package/build/src/models/binary_node.js +13 -0
  42. package/build/src/models/number_node.d.ts +6 -0
  43. package/build/src/models/number_node.js +12 -0
  44. package/build/src/models/unary_node.d.ts +7 -0
  45. package/build/src/models/unary_node.js +11 -0
  46. package/build/src/render_expression.d.ts +2 -0
  47. package/build/src/render_expression.js +56 -0
  48. package/build/tests/expression_cutter.test.d.ts +1 -0
  49. package/build/tests/expression_cutter.test.js +9 -0
  50. package/build/tests/expression_evaluator.test.d.ts +1 -0
  51. package/build/tests/expression_evaluator.test.js +110 -0
  52. package/build/tests/expression_parser.test.d.ts +1 -0
  53. package/build/tests/expression_parser.test.js +44 -0
  54. package/build/tests/expression_tree.test.d.ts +1 -0
  55. package/build/tests/expression_tree.test.js +12 -0
  56. package/build/tests/inputs/expression_cutter_input.d.ts +1 -0
  57. package/build/tests/inputs/expression_cutter_input.js +230 -0
  58. package/build/tests/inputs/expression_parser_input.d.ts +2 -0
  59. package/build/tests/inputs/expression_parser_input.js +197 -0
  60. package/build/tests/inputs/expression_tree_input.d.ts +1 -0
  61. package/build/tests/inputs/expression_tree_input.js +44 -0
  62. package/build/tests/number_node.test.d.ts +1 -0
  63. package/build/tests/number_node.test.js +16 -0
  64. package/build/tests/render_expression.test.d.ts +1 -0
  65. package/build/tests/render_expression.test.js +85 -0
  66. package/build/vitest.config.d.ts +2 -0
  67. package/build/vitest.config.js +10 -0
  68. package/package.json +47 -0
  69. package/src/enums/binary_node_operator.ts +7 -0
  70. package/src/enums/node_type.ts +6 -0
  71. package/src/enums/unary_node_operator.ts +5 -0
  72. package/src/errors/division_by_zero_error.ts +6 -0
  73. package/src/errors/invalid_expression_error.ts +6 -0
  74. package/src/errors/invalid_number_error.ts +6 -0
  75. package/src/errors/invalid_square_root_error.ts +6 -0
  76. package/src/errors/missing_parenthese_error.ts +6 -0
  77. package/src/errors/unknown_binary_operator_error.ts +6 -0
  78. package/src/errors/unknown_node_type_error.ts +6 -0
  79. package/src/errors/unknown_unary_operator_error.ts +6 -0
  80. package/src/expression_cutter.ts +9 -0
  81. package/src/expression_evaluator.ts +76 -0
  82. package/src/expression_parser.ts +117 -0
  83. package/src/expression_tree.ts +19 -0
  84. package/src/models/base_node.ts +5 -0
  85. package/src/models/binary_node.ts +13 -0
  86. package/src/models/number_node.ts +12 -0
  87. package/src/models/unary_node.ts +12 -0
  88. package/src/render_expression.ts +57 -0
package/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # expression-tree
2
+
3
+ [![Quality Gate Status](https://sonarqube.florentmarques.fr/api/project_badges/measure?project=expression-tree&metric=alert_status&token=sqb_5625e2db1fa54b8af4278b6f13b70bc99f094f0a)](https://sonarqube.florentmarques.fr/dashboard?id=expression-tree)
4
+ [![Security Rating](https://sonarqube.florentmarques.fr/api/project_badges/measure?project=expression-tree&metric=software_quality_security_rating&token=sqb_5625e2db1fa54b8af4278b6f13b70bc99f094f0a)](https://sonarqube.florentmarques.fr/dashboard?id=expression-tree)
5
+ [![Coverage](https://sonarqube.florentmarques.fr/api/project_badges/measure?project=expression-tree&metric=coverage&token=sqb_5625e2db1fa54b8af4278b6f13b70bc99f094f0a)](https://sonarqube.florentmarques.fr/dashboard?id=expression-tree)
6
+
7
+ ## Concept
8
+
9
+ This is a simple expression tree implementation in TypeScript. It allows you to parse mathematical expressions and evaluate them using a tree structure.
10
+
11
+ ![expression tree](docs/tree.png)
12
+
13
+ ## Features
14
+
15
+ - [x] Parse mathematical expressions into a tree structure.
16
+ - [x] Evaluate expressions using the tree.
17
+ - [x] Support for basic arithmetic operations
18
+ - [x] addition
19
+ - [x] subtraction
20
+ - [x] multiplication
21
+ - [x] division
22
+ - [x] exponentiation
23
+ - [x] unary minus
24
+ - [x] parentheses
25
+ - [x] operator precedence
26
+ - [x] operator associativity
27
+ - [ ] square root
28
+ - [ ] absolute value
29
+ - [ ] trigonometric functions (sin, cos, tan)
30
+ - [ ] logarithm
31
+
32
+ ## Example
33
+
34
+ ```shell
35
+ $ bun run index.ts
36
+ ```
37
+
38
+ Output:
39
+
40
+ ```text
41
+ -------------------
42
+ expected: 6 - -2 + 5 = 13
43
+ actual: 6 - -2 + 5 = 13
44
+ same ? true
45
+ -------------------
46
+ expected: 6 + -(2 + 5) = -1
47
+ actual: 6 + -(2 + 5) = -1
48
+ same ? true
49
+ -------------------
50
+ expected: 6 + -2 * 5 = -4
51
+ actual: 6 + -2 * 5 = -4
52
+ same ? true
53
+ -------------------
54
+ expected: 6 + 4 / 2 * 5 = 16
55
+ actual: 6 + 4 / 2 * 5 = 16
56
+ same ? true
57
+ -------------------
58
+ expected: 6 + -(2 * 5) = -4
59
+ actual: 6 + -(2 * 5) = -4
60
+ same ? true
61
+ -------------------
62
+ expected: 6 + 2 * 3 - 1 = 11
63
+ actual: 6 + 2 * 3 - 1 = 11
64
+ same ? true
65
+ -------------------
66
+ expected: 6 + 2 * 3 - 1 + 2 ^ 2 = 15
67
+ actual: 6 + 2 * 3 - 1 + 2 ^ 2 = 15
68
+ same ? true
69
+ ```
70
+
71
+ ## Development
72
+
73
+ To install dependencies:
74
+
75
+ ```bash
76
+ bun install
77
+ ```
78
+
79
+ To run:
80
+
81
+ ```bash
82
+ bun run index.ts
83
+ ```
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import { ExpressionTree } from './src/expression_tree.js';
2
+ function testExpression(expression, expectedResult) {
3
+ const expressionTree = new ExpressionTree();
4
+ console.log('-------------------');
5
+ const expected = `${expression} = ${expectedResult}`;
6
+ const actual = `${expression} = ${expressionTree.evaluate(expression)}`;
7
+ console.log('expected:', expected);
8
+ console.log('actual: ', actual);
9
+ console.log('same ? ', expected === actual);
10
+ }
11
+ // testExpression('sqrt(6 + 4 / 2 * 5)', 4)
12
+ testExpression('6 - -2 + 5', 13);
13
+ testExpression('6 + -(2 + 5)', -1);
14
+ testExpression('6 + -2 * 5', -4);
15
+ testExpression('6 + 4 / 2 * 5', 16);
16
+ testExpression('6 + -(2 * 5)', -4);
17
+ testExpression('6 + 2 * 3 - 1', 11);
18
+ testExpression('6 + 2 * 3 - 1 + 2 ^ 2', 15);
@@ -0,0 +1,16 @@
1
+ export { ExpressionTree } from './src/expression_tree.js';
2
+ export { ExpressionParser } from './src/expression_parser.js';
3
+ export { ExpressionEvaluator } from './src/expression_evaluator.js';
4
+ export { ExpressionCutter } from './src/expression_cutter.js';
5
+ export { InvalidExpressionError } from './src/errors/invalid_expression_error.js';
6
+ export { InvalidNumberError } from './src/errors/invalid_number_error.js';
7
+ export { MissingClosingParenthesisError } from './src/errors/missing_parenthese_error.js';
8
+ export { UnknownBinaryOperatorError } from './src/errors/unknown_binary_operator_error.js';
9
+ export { UnknownUnaryOperatorError } from './src/errors/unknown_unary_operator_error.js';
10
+ export { DivisionByZeroError } from './src/errors/division_by_zero_error.js';
11
+ export { NegativeSquareRootError } from './src/errors/invalid_square_root_error.js';
12
+ export { UnknownNodeTypeError } from './src/errors/unknown_node_type_error.js';
13
+ export { BaseNode } from './src/models/base_node.js';
14
+ export { BinaryNode } from './src/models/binary_node.js';
15
+ export { UnaryNode } from './src/models/unary_node.js';
16
+ export { NumberNode } from './src/models/number_node.js';
package/build/index.js ADDED
@@ -0,0 +1,16 @@
1
+ export { ExpressionTree } from './src/expression_tree.js';
2
+ export { ExpressionParser } from './src/expression_parser.js';
3
+ export { ExpressionEvaluator } from './src/expression_evaluator.js';
4
+ export { ExpressionCutter } from './src/expression_cutter.js';
5
+ export { InvalidExpressionError } from './src/errors/invalid_expression_error.js';
6
+ export { InvalidNumberError } from './src/errors/invalid_number_error.js';
7
+ export { MissingClosingParenthesisError } from './src/errors/missing_parenthese_error.js';
8
+ export { UnknownBinaryOperatorError } from './src/errors/unknown_binary_operator_error.js';
9
+ export { UnknownUnaryOperatorError } from './src/errors/unknown_unary_operator_error.js';
10
+ export { DivisionByZeroError } from './src/errors/division_by_zero_error.js';
11
+ export { NegativeSquareRootError } from './src/errors/invalid_square_root_error.js';
12
+ export { UnknownNodeTypeError } from './src/errors/unknown_node_type_error.js';
13
+ export { BaseNode } from './src/models/base_node.js';
14
+ export { BinaryNode } from './src/models/binary_node.js';
15
+ export { UnaryNode } from './src/models/unary_node.js';
16
+ export { NumberNode } from './src/models/number_node.js';
@@ -0,0 +1,7 @@
1
+ export declare enum BinaryNodeOperator {
2
+ SUM = "addition",
3
+ SUBTRACT = "subtraction",
4
+ MULTIPLY = "multiplication",
5
+ DIVIDE = "division",
6
+ POWER = "power"
7
+ }
@@ -0,0 +1,8 @@
1
+ export var BinaryNodeOperator;
2
+ (function (BinaryNodeOperator) {
3
+ BinaryNodeOperator["SUM"] = "addition";
4
+ BinaryNodeOperator["SUBTRACT"] = "subtraction";
5
+ BinaryNodeOperator["MULTIPLY"] = "multiplication";
6
+ BinaryNodeOperator["DIVIDE"] = "division";
7
+ BinaryNodeOperator["POWER"] = "power";
8
+ })(BinaryNodeOperator || (BinaryNodeOperator = {}));
@@ -0,0 +1,6 @@
1
+ export declare enum NodeType {
2
+ EXPRESSION = "expression",
3
+ NUMBER = "number",
4
+ BINARY = "binary",
5
+ UNARY = "unary"
6
+ }
@@ -0,0 +1,7 @@
1
+ export var NodeType;
2
+ (function (NodeType) {
3
+ NodeType["EXPRESSION"] = "expression";
4
+ NodeType["NUMBER"] = "number";
5
+ NodeType["BINARY"] = "binary";
6
+ NodeType["UNARY"] = "unary";
7
+ })(NodeType || (NodeType = {}));
@@ -0,0 +1,5 @@
1
+ export declare enum UnaryNodeOperator {
2
+ SQUARE_ROOT = "square_root",
3
+ NEGATE = "negate",
4
+ ABSOLUTE = "absolute"
5
+ }
@@ -0,0 +1,6 @@
1
+ export var UnaryNodeOperator;
2
+ (function (UnaryNodeOperator) {
3
+ UnaryNodeOperator["SQUARE_ROOT"] = "square_root";
4
+ UnaryNodeOperator["NEGATE"] = "negate";
5
+ UnaryNodeOperator["ABSOLUTE"] = "absolute";
6
+ })(UnaryNodeOperator || (UnaryNodeOperator = {}));
@@ -0,0 +1,3 @@
1
+ export declare class DivisionByZeroError extends Error {
2
+ constructor();
3
+ }
@@ -0,0 +1,6 @@
1
+ export class DivisionByZeroError extends Error {
2
+ constructor() {
3
+ super('Division by zero is not allowed');
4
+ this.name = 'DivisionByZeroError';
5
+ }
6
+ }
@@ -0,0 +1,3 @@
1
+ export declare class InvalidExpressionError extends Error {
2
+ constructor();
3
+ }
@@ -0,0 +1,6 @@
1
+ export class InvalidExpressionError extends Error {
2
+ constructor() {
3
+ super('Invalid expression encountered during parsing');
4
+ this.name = 'InvalidExpressionError';
5
+ }
6
+ }
@@ -0,0 +1,4 @@
1
+ export declare class InvalidNumberError extends Error {
2
+ readonly token: string | null;
3
+ constructor(token?: string | null);
4
+ }
@@ -0,0 +1,8 @@
1
+ export class InvalidNumberError extends Error {
2
+ token;
3
+ constructor(token = null) {
4
+ super('Invalid number format');
5
+ this.token = token;
6
+ this.name = 'InvalidNumberError';
7
+ }
8
+ }
@@ -0,0 +1,3 @@
1
+ export declare class NegativeSquareRootError extends Error {
2
+ constructor();
3
+ }
@@ -0,0 +1,6 @@
1
+ export class NegativeSquareRootError extends Error {
2
+ constructor() {
3
+ super('Cannot take square root of a negative number');
4
+ this.name = 'InvalidSquareRootError';
5
+ }
6
+ }
@@ -0,0 +1,3 @@
1
+ export declare class MissingClosingParenthesisError extends Error {
2
+ constructor();
3
+ }
@@ -0,0 +1,6 @@
1
+ export class MissingClosingParenthesisError extends Error {
2
+ constructor() {
3
+ super('Missing closing parenthesis in the expression');
4
+ this.name = 'MissingClosingParenthesisError';
5
+ }
6
+ }
@@ -0,0 +1,3 @@
1
+ export declare class UnknownBinaryOperatorError extends Error {
2
+ constructor(operator: string);
3
+ }
@@ -0,0 +1,6 @@
1
+ export class UnknownBinaryOperatorError extends Error {
2
+ constructor(operator) {
3
+ super(`Unknown binary operator: ${operator}`);
4
+ this.name = 'UnknownBinaryOperatorError';
5
+ }
6
+ }
@@ -0,0 +1,3 @@
1
+ export declare class UnknownNodeTypeError extends Error {
2
+ constructor(nodeType: string);
3
+ }
@@ -0,0 +1,6 @@
1
+ export class UnknownNodeTypeError extends Error {
2
+ constructor(nodeType) {
3
+ super(`Unknown node type: ${nodeType}`);
4
+ this.name = 'UnknownNodeTypeError';
5
+ }
6
+ }
@@ -0,0 +1,3 @@
1
+ export declare class UnknownUnaryOperatorError extends Error {
2
+ constructor(operator: string);
3
+ }
@@ -0,0 +1,6 @@
1
+ export class UnknownUnaryOperatorError extends Error {
2
+ constructor(operator) {
3
+ super(`Unknown unary operator: ${operator}`);
4
+ this.name = 'UnknownUnaryOperatorError';
5
+ }
6
+ }
@@ -0,0 +1,4 @@
1
+ export declare class ExpressionCutter {
2
+ static readonly REGEX: RegExp;
3
+ cut(expression: string): string[];
4
+ }
@@ -0,0 +1,7 @@
1
+ export class ExpressionCutter {
2
+ static REGEX = /(\d+(?:\.\d+)?|\+|-|\*|\^|\/|\(|\)|sqrt|abs)/g;
3
+ cut(expression) {
4
+ const cleanExpr = expression.replace(/\s+/g, '');
5
+ return Array.from(cleanExpr.matchAll(ExpressionCutter.REGEX)).map((m) => m[0]);
6
+ }
7
+ }
@@ -0,0 +1,10 @@
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
+ export declare class ExpressionEvaluator {
6
+ evaluate(node: BaseNode): number;
7
+ protected evaluateBinaryNode(node: BinaryNode): number;
8
+ protected evaluateUnaryNode(node: UnaryNode): number;
9
+ protected evaluateNumberNode(node: NumberNode): number;
10
+ }
@@ -0,0 +1,74 @@
1
+ import { BinaryNode } from './models/binary_node.js';
2
+ import { UnaryNode } from './models/unary_node.js';
3
+ import { NumberNode } from './models/number_node.js';
4
+ import { UnknownNodeTypeError } from './errors/unknown_node_type_error.js';
5
+ import { BinaryNodeOperator } from './enums/binary_node_operator.js';
6
+ import { UnaryNodeOperator } from './enums/unary_node_operator.js';
7
+ import { NegativeSquareRootError } from './errors/invalid_square_root_error.js';
8
+ import { UnknownUnaryOperatorError } from './errors/unknown_unary_operator_error.js';
9
+ import { UnknownBinaryOperatorError } from './errors/unknown_binary_operator_error.js';
10
+ import { DivisionByZeroError } from './errors/division_by_zero_error.js';
11
+ export class ExpressionEvaluator {
12
+ evaluate(node) {
13
+ if (node instanceof BinaryNode) {
14
+ return this.evaluateBinaryNode(node);
15
+ }
16
+ else if (node instanceof UnaryNode) {
17
+ return this.evaluateUnaryNode(node);
18
+ }
19
+ else if (node instanceof NumberNode) {
20
+ return this.evaluateNumberNode(node);
21
+ }
22
+ else {
23
+ throw new UnknownNodeTypeError(node.type);
24
+ }
25
+ }
26
+ evaluateBinaryNode(node) {
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
+ evaluateUnaryNode(node) {
53
+ switch (node.operator) {
54
+ case UnaryNodeOperator.NEGATE: {
55
+ return -this.evaluate(node.value);
56
+ }
57
+ case UnaryNodeOperator.ABSOLUTE: {
58
+ return Math.abs(this.evaluate(node.value));
59
+ }
60
+ case UnaryNodeOperator.SQUARE_ROOT: {
61
+ const leftValue = this.evaluate(node.value);
62
+ if (leftValue < 0) {
63
+ throw new NegativeSquareRootError();
64
+ }
65
+ return Math.sqrt(leftValue);
66
+ }
67
+ default:
68
+ throw new UnknownUnaryOperatorError(node.operator);
69
+ }
70
+ }
71
+ evaluateNumberNode(node) {
72
+ return node.value;
73
+ }
74
+ }
@@ -0,0 +1,14 @@
1
+ import { BaseNode } from './models/base_node.js';
2
+ import { BinaryNodeOperator } from './enums/binary_node_operator.js';
3
+ export declare class ExpressionParser {
4
+ protected tokens: string[];
5
+ protected pos: number;
6
+ protected peek(): string | null;
7
+ protected consume(): string;
8
+ protected match(expected: string[]): string | null;
9
+ parse(expression: string[]): BaseNode;
10
+ protected parseExpression(): BaseNode;
11
+ protected parseTerm(): BaseNode;
12
+ protected parseFactor(): BaseNode;
13
+ protected mapRowOperator(operator: string): BinaryNodeOperator;
14
+ }
@@ -0,0 +1,94 @@
1
+ import { InvalidExpressionError } from './errors/invalid_expression_error.js';
2
+ import { BinaryNodeOperator } from './enums/binary_node_operator.js';
3
+ import { UnknownBinaryOperatorError } from './errors/unknown_binary_operator_error.js';
4
+ import { NumberNode } from './models/number_node.js';
5
+ import { UnaryNode } from './models/unary_node.js';
6
+ import { UnaryNodeOperator } from './enums/unary_node_operator.js';
7
+ import { MissingClosingParenthesisError } from './errors/missing_parenthese_error.js';
8
+ import { InvalidNumberError } from './errors/invalid_number_error.js';
9
+ import { BinaryNode } from './models/binary_node.js';
10
+ export class ExpressionParser {
11
+ tokens = [];
12
+ pos = 0;
13
+ peek() {
14
+ return this.tokens[this.pos] ?? null;
15
+ }
16
+ consume() {
17
+ if (this.pos >= this.tokens.length) {
18
+ throw new InvalidExpressionError();
19
+ }
20
+ return this.tokens[this.pos++];
21
+ }
22
+ match(expected) {
23
+ const token = this.peek();
24
+ if (token && expected.includes(token)) {
25
+ this.consume();
26
+ return token;
27
+ }
28
+ return null;
29
+ }
30
+ parse(expression) {
31
+ this.tokens = [...expression];
32
+ this.pos = 0;
33
+ const node = this.parseExpression();
34
+ if (this.pos < this.tokens.length) {
35
+ throw new InvalidExpressionError();
36
+ }
37
+ return node;
38
+ }
39
+ parseExpression() {
40
+ let node = this.parseTerm();
41
+ while (this.match(['+', '-'])) {
42
+ const operator = this.mapRowOperator(this.tokens[this.pos - 1]);
43
+ const right = this.parseTerm();
44
+ node = new BinaryNode(operator, node, right);
45
+ }
46
+ return node;
47
+ }
48
+ parseTerm() {
49
+ let node = this.parseFactor();
50
+ while (this.match(['^', '*', '/'])) {
51
+ const operator = this.mapRowOperator(this.tokens[this.pos - 1]);
52
+ const right = this.parseFactor();
53
+ node = new BinaryNode(operator, node, right);
54
+ }
55
+ return node;
56
+ }
57
+ parseFactor() {
58
+ const token = this.peek();
59
+ if (token === '-') {
60
+ this.consume(); // consume '-'
61
+ const value = this.parseFactor();
62
+ return new UnaryNode(UnaryNodeOperator.NEGATE, value);
63
+ }
64
+ if (token === '(') {
65
+ this.consume(); // consume '('
66
+ const node = this.parseExpression();
67
+ if (this.consume() !== ')') {
68
+ throw new MissingClosingParenthesisError();
69
+ }
70
+ return node;
71
+ }
72
+ const value = Number(this.consume());
73
+ if (Number.isNaN(value)) {
74
+ throw new InvalidNumberError(token);
75
+ }
76
+ return new NumberNode(value);
77
+ }
78
+ mapRowOperator(operator) {
79
+ switch (operator) {
80
+ case '+':
81
+ return BinaryNodeOperator.SUM;
82
+ case '-':
83
+ return BinaryNodeOperator.SUBTRACT;
84
+ case '*':
85
+ return BinaryNodeOperator.MULTIPLY;
86
+ case '/':
87
+ return BinaryNodeOperator.DIVIDE;
88
+ case '^':
89
+ return BinaryNodeOperator.POWER;
90
+ default:
91
+ throw new UnknownBinaryOperatorError(operator);
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,10 @@
1
+ import { ExpressionCutter } from './expression_cutter.js';
2
+ import { ExpressionParser } from './expression_parser.js';
3
+ import { ExpressionEvaluator } from './expression_evaluator.js';
4
+ export declare class ExpressionTree {
5
+ protected cutter: ExpressionCutter;
6
+ protected parser: ExpressionParser;
7
+ protected evaluator: ExpressionEvaluator;
8
+ constructor(cutter?: ExpressionCutter, parser?: ExpressionParser, evaluator?: ExpressionEvaluator);
9
+ evaluate(expression: string): number;
10
+ }
@@ -0,0 +1,18 @@
1
+ import { ExpressionCutter } from './expression_cutter.js';
2
+ import { ExpressionParser } from './expression_parser.js';
3
+ import { ExpressionEvaluator } from './expression_evaluator.js';
4
+ export class ExpressionTree {
5
+ cutter;
6
+ parser;
7
+ evaluator;
8
+ constructor(cutter = new ExpressionCutter(), parser = new ExpressionParser(), evaluator = new ExpressionEvaluator()) {
9
+ this.cutter = cutter;
10
+ this.parser = parser;
11
+ this.evaluator = evaluator;
12
+ }
13
+ evaluate(expression) {
14
+ const cutExpression = this.cutter.cut(expression);
15
+ const baseNode = this.parser.parse(cutExpression);
16
+ return this.evaluator.evaluate(baseNode);
17
+ }
18
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { NodeType } from '../enums/node_type.js';
2
+ export declare abstract class BaseNode {
3
+ readonly type: NodeType;
4
+ protected constructor(type: NodeType);
5
+ }
@@ -0,0 +1,6 @@
1
+ export class BaseNode {
2
+ type;
3
+ constructor(type) {
4
+ this.type = type;
5
+ }
6
+ }
@@ -0,0 +1,8 @@
1
+ import { BaseNode } from './base_node.js';
2
+ import { BinaryNodeOperator } from '../enums/binary_node_operator.js';
3
+ export declare class BinaryNode extends BaseNode {
4
+ operator: BinaryNodeOperator;
5
+ left: BaseNode;
6
+ right: BaseNode;
7
+ constructor(operator: BinaryNodeOperator, left: BaseNode, right: BaseNode);
8
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseNode } from './base_node.js';
2
+ import { NodeType } from '../enums/node_type.js';
3
+ export class BinaryNode extends BaseNode {
4
+ operator;
5
+ left;
6
+ right;
7
+ constructor(operator, left, right) {
8
+ super(NodeType.BINARY);
9
+ this.operator = operator;
10
+ this.left = left;
11
+ this.right = right;
12
+ }
13
+ }
@@ -0,0 +1,6 @@
1
+ import { BaseNode } from './base_node.js';
2
+ export declare class NumberNode extends BaseNode {
3
+ readonly value: number;
4
+ constructor(value: number);
5
+ toString(): string;
6
+ }
@@ -0,0 +1,12 @@
1
+ import { BaseNode } from './base_node.js';
2
+ import { NodeType } from '../enums/node_type.js';
3
+ export class NumberNode extends BaseNode {
4
+ value;
5
+ constructor(value) {
6
+ super(NodeType.NUMBER);
7
+ this.value = value;
8
+ }
9
+ toString() {
10
+ return this.value.toString();
11
+ }
12
+ }
@@ -0,0 +1,7 @@
1
+ import { BaseNode } from './base_node.js';
2
+ import type { UnaryNodeOperator } from '../enums/unary_node_operator.js';
3
+ export declare class UnaryNode extends BaseNode {
4
+ operator: UnaryNodeOperator;
5
+ value: BaseNode;
6
+ constructor(operator: UnaryNodeOperator, value: BaseNode);
7
+ }
@@ -0,0 +1,11 @@
1
+ import { BaseNode } from './base_node.js';
2
+ import { NodeType } from '../enums/node_type.js';
3
+ export class UnaryNode extends BaseNode {
4
+ operator;
5
+ value;
6
+ constructor(operator, value) {
7
+ super(NodeType.UNARY);
8
+ this.operator = operator;
9
+ this.value = value;
10
+ }
11
+ }
@@ -0,0 +1,2 @@
1
+ import type { BaseNode } from './models/base_node.js';
2
+ export declare function renderExpression(node: BaseNode): string;