@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
@@ -0,0 +1,117 @@
1
+ import { InvalidExpressionError } from './errors/invalid_expression_error.js'
2
+ import { BaseNode } from './models/base_node.js'
3
+ import { BinaryNodeOperator } from './enums/binary_node_operator.js'
4
+ import { UnknownBinaryOperatorError } from './errors/unknown_binary_operator_error.js'
5
+ import { NumberNode } from './models/number_node.js'
6
+ import { UnaryNode } from './models/unary_node.js'
7
+ import { UnaryNodeOperator } from './enums/unary_node_operator.js'
8
+ import { MissingClosingParenthesisError } from './errors/missing_parenthese_error.js'
9
+ import { InvalidNumberError } from './errors/invalid_number_error.js'
10
+ import { BinaryNode } from './models/binary_node.js'
11
+
12
+ export class ExpressionParser {
13
+ protected tokens: string[] = []
14
+ protected pos: number = 0
15
+
16
+ protected peek(): string | null {
17
+ return this.tokens[this.pos] ?? null
18
+ }
19
+
20
+ protected consume(): string {
21
+ if (this.pos >= this.tokens.length) {
22
+ throw new InvalidExpressionError()
23
+ }
24
+ return this.tokens[this.pos++]
25
+ }
26
+
27
+ protected match(expected: string[]): string | null {
28
+ const token = this.peek()
29
+ if (token && expected.includes(token)) {
30
+ this.consume()
31
+ return token
32
+ }
33
+ return null
34
+ }
35
+
36
+ public parse(expression: string[]): BaseNode {
37
+ this.tokens = [...expression]
38
+ this.pos = 0
39
+
40
+ const node = this.parseExpression()
41
+
42
+ if (this.pos < this.tokens.length) {
43
+ throw new InvalidExpressionError()
44
+ }
45
+
46
+ return node
47
+ }
48
+
49
+ protected parseExpression(): BaseNode {
50
+ let node = this.parseTerm()
51
+
52
+ while (this.match(['+', '-'])) {
53
+ const operator = this.mapRowOperator(this.tokens[this.pos - 1])
54
+ const right = this.parseTerm()
55
+
56
+ node = new BinaryNode(operator, node, right)
57
+ }
58
+
59
+ return node
60
+ }
61
+
62
+ protected parseTerm(): BaseNode {
63
+ let node = this.parseFactor()
64
+
65
+ while (this.match(['^', '*', '/'])) {
66
+ const operator = this.mapRowOperator(this.tokens[this.pos - 1])
67
+ const right = this.parseFactor()
68
+
69
+ node = new BinaryNode(operator, node, right)
70
+ }
71
+
72
+ return node
73
+ }
74
+
75
+ protected parseFactor(): BaseNode {
76
+ const token = this.peek()
77
+
78
+ if (token === '-') {
79
+ this.consume() // consume '-'
80
+ const value = this.parseFactor()
81
+ return new UnaryNode(UnaryNodeOperator.NEGATE, value)
82
+ }
83
+
84
+ if (token === '(') {
85
+ this.consume() // consume '('
86
+ const node = this.parseExpression()
87
+ if (this.consume() !== ')') {
88
+ throw new MissingClosingParenthesisError()
89
+ }
90
+ return node
91
+ }
92
+
93
+ const value = Number(this.consume())
94
+ if (Number.isNaN(value)) {
95
+ throw new InvalidNumberError(token)
96
+ }
97
+
98
+ return new NumberNode(value)
99
+ }
100
+
101
+ protected mapRowOperator(operator: string): BinaryNodeOperator {
102
+ switch (operator) {
103
+ case '+':
104
+ return BinaryNodeOperator.SUM
105
+ case '-':
106
+ return BinaryNodeOperator.SUBTRACT
107
+ case '*':
108
+ return BinaryNodeOperator.MULTIPLY
109
+ case '/':
110
+ return BinaryNodeOperator.DIVIDE
111
+ case '^':
112
+ return BinaryNodeOperator.POWER
113
+ default:
114
+ throw new UnknownBinaryOperatorError(operator)
115
+ }
116
+ }
117
+ }
@@ -0,0 +1,19 @@
1
+ import { ExpressionCutter } from './expression_cutter.js'
2
+ import { ExpressionParser } from './expression_parser.js'
3
+ import { ExpressionEvaluator } from './expression_evaluator.js'
4
+
5
+ export class ExpressionTree {
6
+ constructor(
7
+ protected cutter = new ExpressionCutter(),
8
+ protected parser = new ExpressionParser(),
9
+ protected evaluator = new ExpressionEvaluator()
10
+ ) {}
11
+
12
+ evaluate(expression: string): number {
13
+ const cutExpression = this.cutter.cut(expression)
14
+
15
+ const baseNode = this.parser.parse(cutExpression)
16
+
17
+ return this.evaluator.evaluate(baseNode)
18
+ }
19
+ }
@@ -0,0 +1,5 @@
1
+ import type { NodeType } from '../enums/node_type.js'
2
+
3
+ export abstract class BaseNode {
4
+ protected constructor(public readonly type: NodeType) {}
5
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseNode } from './base_node.js'
2
+ import { BinaryNodeOperator } from '../enums/binary_node_operator.js'
3
+ import { NodeType } from '../enums/node_type.js'
4
+
5
+ export class BinaryNode extends BaseNode {
6
+ constructor(
7
+ public operator: BinaryNodeOperator,
8
+ public left: BaseNode,
9
+ public right: BaseNode
10
+ ) {
11
+ super(NodeType.BINARY)
12
+ }
13
+ }
@@ -0,0 +1,12 @@
1
+ import { BaseNode } from './base_node.js'
2
+ import { NodeType } from '../enums/node_type.js'
3
+
4
+ export class NumberNode extends BaseNode {
5
+ constructor(public readonly value: number) {
6
+ super(NodeType.NUMBER)
7
+ }
8
+
9
+ toString(): string {
10
+ return this.value.toString()
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ import { BaseNode } from './base_node.js'
2
+ import type { UnaryNodeOperator } from '../enums/unary_node_operator.js'
3
+ import { NodeType } from '../enums/node_type.js'
4
+
5
+ export class UnaryNode extends BaseNode {
6
+ constructor(
7
+ public operator: UnaryNodeOperator,
8
+ public value: BaseNode
9
+ ) {
10
+ super(NodeType.UNARY)
11
+ }
12
+ }
@@ -0,0 +1,57 @@
1
+ import type { BaseNode } from './models/base_node.js'
2
+ import { NumberNode } from './models/number_node.js'
3
+ import { BinaryNode } from './models/binary_node.js'
4
+ import { BinaryNodeOperator } from './enums/binary_node_operator.js'
5
+ import { UnknownBinaryOperatorError } from './errors/unknown_binary_operator_error.js'
6
+ import { UnaryNode } from './models/unary_node.js'
7
+ import { UnaryNodeOperator } from './enums/unary_node_operator.js'
8
+ import { UnknownUnaryOperatorError } from './errors/unknown_unary_operator_error.js'
9
+ import { UnknownNodeTypeError } from './errors/unknown_node_type_error.js'
10
+
11
+ export function renderExpression(node: BaseNode): string {
12
+ if (node instanceof BinaryNode) {
13
+ const leftNode = renderExpression(node.left)
14
+ const rightNode = renderExpression(node.right)
15
+ const operator = {
16
+ [BinaryNodeOperator.SUM]: '+',
17
+ [BinaryNodeOperator.SUBTRACT]: '-',
18
+ [BinaryNodeOperator.MULTIPLY]: '*',
19
+ [BinaryNodeOperator.DIVIDE]: '/',
20
+ [BinaryNodeOperator.POWER]: '^',
21
+ }[node.operator]
22
+
23
+ if (!operator) {
24
+ throw new UnknownBinaryOperatorError(node.operator)
25
+ }
26
+
27
+ return `${leftNode} ${operator} ${rightNode}`
28
+ } else if (node instanceof UnaryNode) {
29
+ switch (node.operator) {
30
+ case UnaryNodeOperator.NEGATE: {
31
+ if (node.value instanceof NumberNode && node.value.value === 0) {
32
+ return '0'
33
+ } else if (node.value instanceof BinaryNode) {
34
+ const valueNode = renderExpression(node.value)
35
+ return `-(${valueNode})`
36
+ }
37
+
38
+ const valueNode = renderExpression(node.value)
39
+ return `-${valueNode}`
40
+ }
41
+ case UnaryNodeOperator.ABSOLUTE: {
42
+ const valueNode = renderExpression(node.value)
43
+ return `abs(${valueNode})`
44
+ }
45
+ case UnaryNodeOperator.SQUARE_ROOT: {
46
+ const valueNode = renderExpression(node.value)
47
+ return `sqrt(${valueNode})`
48
+ }
49
+ default:
50
+ throw new UnknownUnaryOperatorError(node.operator)
51
+ }
52
+ } else if (node instanceof NumberNode) {
53
+ return node.toString()
54
+ } else {
55
+ throw new UnknownNodeTypeError(node.type)
56
+ }
57
+ }