Skip to content

Commit 691bc5f

Browse files
authored
<refactor> Refactor Node to standardised object (#28)
Previously, nodes in the expression tree had no defined structure. Hence, it was hard to tell what a node needed, and what the correct syntax was for that property. The standard Node class contains properties: NON-COMPUTED: type (*) content (*) leftNode rightNode parent COMPUTED: commutative precedence All website functions now interact with nodes using instances of this Node object. (*) - properties type and content (when dealing with operators) have enums that define the values they can take.
1 parent cf99ecf commit 691bc5f

File tree

3 files changed

+204
-86
lines changed

3 files changed

+204
-86
lines changed

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ <h2>Integrle</h2>
133133
</div>
134134
</div>
135135
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/dompurify@3.3.0/dist/purify.min.js"></script>
136+
<script src="pages/index/tree.js"></script>
136137
<script src="pages/index/script.js"></script>
137138
</body>
138139
</html>

pages/index/script.js

Lines changed: 49 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -154,25 +154,15 @@ function expressionToComponentList(expression)
154154
(
155155
!(list.length == 0 ||
156156
list.length > 0 &&
157-
(list[list.length - 1].type == "open bracket"
158-
|| list[list.length - 1].type == "function"))
157+
(list[list.length - 1].type == NodeType.OPEN_BRACKET
158+
|| list[list.length - 1].type == NodeType.FUNCTION))
159159
)
160160
{
161-
list.push({
162-
content: "+",
163-
type: "operator",
164-
precedence: 0,
165-
commutative: true,
166-
leftNode: -1,
167-
rightNode: -1,
168-
parent: -1,
169-
depth: -1
170-
});
171-
161+
list.push(new Node("operator", '+'));
172162
}
173-
content = "-1";
174-
type = "number";
175163

164+
type = "number";
165+
content = "-1";
176166
i++;
177167
}
178168

@@ -215,8 +205,6 @@ function expressionToComponentList(expression)
215205
{
216206
content = exp[i];
217207
type = "operator";
218-
precedence = {'+': 0, '-': 0, '/': 1, '*': 1, '^': 2}[exp[i]];
219-
commutative = {'+': true, '-': false, '/': false, '*': true, '^': false}[exp[i]];
220208
i++;
221209
}
222210

@@ -266,22 +254,7 @@ function expressionToComponentList(expression)
266254
}
267255

268256
// Add new component
269-
let newComponent = {
270-
content: content,
271-
type: type,
272-
leftNode: -1,
273-
rightNode: -1,
274-
parent: -1,
275-
depth: -1
276-
};
277-
278-
if (type == "operator" || type == "function") {
279-
newComponent.precedence = precedence;
280-
if (type == "operator") {
281-
newComponent.commutative = commutative;
282-
}
283-
}
284-
list.push(newComponent);
257+
list.push(new Node(type, content));
285258

286259
// Check for implicit * signs
287260
// If previous component is: Number, Variable, or Close Bracket
@@ -292,20 +265,11 @@ function expressionToComponentList(expression)
292265
// e.g (10 + x)(3 + x) -> ( 10 + x ) * ( 3 + x )
293266
if (
294267
list.length >= 2
295-
&& ["close bracket", "number", "variable"].includes(list[list.length - 2].type)
296-
&& ["open bracket", "number", "variable", "function"].includes(list[list.length - 1].type)
268+
&& [NodeType.CLOSE_BRACKET, NodeType.NUMBER, NodeType.VARIABLE].includes(list[list.length - 2].type)
269+
&& [NodeType.OPEN_BRACKET, NodeType.NUMBER, NodeType.VARIABLE, NodeType.FUNCTION].includes(list[list.length - 1].type)
297270
)
298271
{
299-
list.splice(-1, 0, {
300-
content: '*',
301-
type: "operator",
302-
precedence: 1,
303-
leftNode: -1,
304-
rightNode: -1,
305-
parent: -1,
306-
depth: -1,
307-
commutative: true
308-
});
272+
list.splice(-1, 0, new Node("operator", '*'));
309273
}
310274

311275
}
@@ -324,12 +288,12 @@ function componentListToPostfix(list)
324288
{
325289
let component = list[index];
326290
// If number, constant, or variable, put in output
327-
if (["number", "constant", "variable"].includes(component.type))
291+
if ([NodeType.NUMBER, NodeType.CONSTANT, NodeType.VARIABLE].includes(component.type))
328292
{
329293
postfixList.push(component);
330294
}
331295
// If (, recurse and add to string
332-
else if (component.type == "open bracket")
296+
else if (component.type == NodeType.OPEN_BRACKET)
333297
{
334298
let bracketEval = componentListToPostfix(list.slice(index+1));
335299
index += bracketEval.index + 1; // +1, as list indices start from 0
@@ -338,7 +302,7 @@ function componentListToPostfix(list)
338302
postfixList.push(operatorStack.pop());
339303
}
340304
// If ), return
341-
else if (component.type == "close bracket")
305+
else if (component.type == NodeType.CLOSE_BRACKET)
342306
{
343307
postfixList.push(...operatorStack.reverse());
344308
return {
@@ -347,12 +311,11 @@ function componentListToPostfix(list)
347311
};
348312
}
349313
// If operator or function, look at stack
350-
else if (component.type == "operator" || component.type == "function")
351-
{
314+
else if (component.type == NodeType.OPERATOR || component.type == NodeType.FUNCTION) {
352315
// If function, push
353316
// Functions never cause an operator to be popped. e.g: in 1 * 2 + 3, the + causes the * to be popped.
354317
// In 1 * sin(3), the sin doesn't cause the * to be popped.
355-
if (component.type == "function")
318+
if (component.type == NodeType.FUNCTION)
356319
operatorStack.push(component);
357320

358321
// If higher precedence than top of stack, push
@@ -392,22 +355,22 @@ function postfixToTree(components, index=0, parentIndex=-1, depth=0)
392355

393356
switch(currentComponent.type)
394357
{
395-
case "function":
396-
case "operator": {
358+
case NodeType.FUNCTION:
359+
case NodeType.OPERATOR: {
397360
let componentIndex = index;
398361

399362
currentComponent.leftNode = index+1;
400363
index = postfixToTree(components, index+1, componentIndex, depth+1);
401364

402-
if (currentComponent.type == "operator")
365+
if (currentComponent.type == NodeType.OPERATOR)
403366
{
404367
currentComponent.rightNode = index+1;
405368
index = postfixToTree(components, index+1, componentIndex, depth+1);
406369
}
407370
break;
408371
}
409-
case "number":
410-
case "constant":
372+
case NodeType.NUMBER:
373+
case NodeType.CONSTANT:
411374
break;
412375
}
413376

@@ -427,7 +390,7 @@ function normaliseTree(tree, rootNodeIndex=0)
427390
{
428391
let currentNode = tree[currentNodeIndex];
429392
// If commutative node found, add children to list
430-
if (currentNode.type == "operator" && currentNode.commutative == true)
393+
if (currentNode.type == NodeType.OPERATOR && currentNode.commutative == true)
431394
{
432395
let terms = findCommutativeNodes(tree, currentNodeIndex, currentNode.content);
433396
let commutativeNodes = terms.nodes;
@@ -498,7 +461,7 @@ function findCommutativeNodes(tree, opNodeIndex, operator)
498461

499462
for (const node of nodesToCheck)
500463
{
501-
if (node.type != "operator" || node.commutative == false)
464+
if (node.type != NodeType.OPERATOR || node.commutative == false)
502465
{
503466
commutativeNodesList.push(tree.indexOf(node));
504467
let parentNode = tree[node.parent];
@@ -647,23 +610,23 @@ function treeToMathJax(tree, currentNodeIndex=0)
647610
let output = "";
648611
switch (currentNode.type)
649612
{
650-
case "operator":
613+
case NodeType.OPERATOR:
651614
{
652615
// If children of operator are another operator, use ()s
653616
let rightNodeIndex = currentNode.rightNode;
654617
let rightNode = tree[rightNodeIndex];
655618
let leftNodeIndex = currentNode.leftNode;
656619
let leftNode = tree[leftNodeIndex];
657620

658-
if (rightNode.type == "operator")
621+
if (rightNode.type == NodeType.OPERATOR)
659622
{
660623
// If division, use {}s instead of ()s
661624
switch (rightNode.content)
662625
{
663-
case '+':
626+
case Operator.ADDITION:
664627
output += `(${treeToMathJax(tree, rightNodeIndex)})`;
665628
break;
666-
case '/':
629+
case Operator.DIVISION:
667630
output += `{${treeToMathJax(tree, rightNodeIndex)}}`;
668631
break;
669632
default:
@@ -676,46 +639,46 @@ function treeToMathJax(tree, currentNodeIndex=0)
676639

677640
switch (currentNode.content)
678641
{
679-
case '/':
642+
case Operator.DIVISION:
680643
output += " \\over ";
681644
break;
682-
case '*':
645+
case Operator.MULTIPLICATION:
683646
// Implied * sign
684647
// Due to mutiplication representing a -ive number (e.g: -4 -> -1 * 4)
685-
if (rightNode.type == "number" && rightNode.content == "-1")
648+
if (rightNode.type == NodeType.NUMBER && rightNode.content == "-1")
686649
break;
687-
if (rightNode.type == "number" ||
688-
(rightNode.type == "operator" &&
689-
(rightNode.content == '+' || rightNode.content == '/' || rightNode.content == '*')
650+
if (rightNode.type == NodeType.NUMBER ||
651+
(rightNode.type == NodeType.OPERATOR &&
652+
(rightNode.content == Operator.ADDITION || rightNode.content == Operator.DIVISION || rightNode.content == Operator.MULTIPLICATION)
690653
)
691654
)
692655
{
693-
if (leftNode.type == "constant" || leftNode.type == "variable" || leftNode.type == "function" ||
694-
(leftNode.type == "operator" &&
695-
(leftNode.content == '+')
656+
if (leftNode.type == NodeType.CONSTANT || leftNode.type == NodeType.VARIABLE || leftNode.type == NodeType.FUNCTION ||
657+
(leftNode.type == NodeType.OPERATOR &&
658+
(leftNode.content == Operator.ADDITION)
696659
)
697660
)
698661
{
699662
break;
700663
}
701664
}
702-
if (rightNode.type == "variable")
665+
if (rightNode.type == NodeType.VARIABLE)
703666
{
704-
if (leftNode.type == "function" || (leftNode.type == "operator" && leftNode.content == '+'))
667+
if (leftNode.type == NodeType.FUNCTION || (leftNode.type == NodeType.OPERATOR && leftNode.content == Operator.ADDITION))
705668
break;
706669
}
707-
if (rightNode.type == "function")
670+
if (rightNode.type == NodeType.FUNCTION)
708671
{
709-
if (leftNode.type == "function")
672+
if (leftNode.type == NodeType.FUNCTION)
710673
break;
711674
}
712-
output += '*';
675+
output += Operator.MULTIPLICATION;
713676
break;
714-
case '+':
677+
case Operator.ADDITION:
715678
// If addition is actually representing a subtraction, ignore + sign (e.g: 1-2 -> 1+(-1*2))
716-
if (leftNode.type == "operator" && leftNode.content == '*')
679+
if (leftNode.type == NodeType.OPERATOR && leftNode.content == Operator.MULTIPLICATION)
717680
{
718-
if (tree[leftNode.rightNode].type == "number" && tree[leftNode.rightNode].content == '-1')
681+
if (tree[leftNode.rightNode].type == NodeType.NUMBER && tree[leftNode.rightNode].content == '-1')
719682
break;
720683
}
721684
else
@@ -728,15 +691,15 @@ function treeToMathJax(tree, currentNodeIndex=0)
728691
break;
729692
}
730693

731-
if (leftNode.type == "operator")
694+
if (leftNode.type == NodeType.OPERATOR)
732695
{
733696
// If division, use {}s instead of ()s
734697
switch (leftNode.content)
735698
{
736-
case '+':
699+
case Operator.ADDITION:
737700
output += `(${treeToMathJax(tree, leftNodeIndex)})`;
738701
break;
739-
case '/':
702+
case Operator.DIVISION:
740703
output += `{${treeToMathJax(tree, leftNodeIndex)}}`;
741704
break;
742705
default:
@@ -749,16 +712,16 @@ function treeToMathJax(tree, currentNodeIndex=0)
749712

750713
break;
751714
}
752-
case "function":
715+
case NodeType.FUNCTION:
753716
output += `\\${currentNode.content}`;
754717
let leftNodeIndex = currentNode.leftNode;
755718
let leftNode = tree[leftNodeIndex];
756-
if (leftNode.type == "operator")
719+
if (leftNode.type == NodeType.OPERATOR)
757720
{
758721
// If division, use {}s instead of ()s
759722
switch (leftNode.content)
760723
{
761-
case '/':
724+
case Operator.DIVISION:
762725
output += `{${treeToMathJax(tree, leftNodeIndex)}}`;
763726
break;
764727
default:
@@ -770,7 +733,7 @@ function treeToMathJax(tree, currentNodeIndex=0)
770733
output += `({${treeToMathJax(tree, leftNodeIndex)}})`;
771734

772735
break;
773-
case "number":
736+
case NodeType.NUMBER:
774737
if (currentNode.content == "-1")
775738
{
776739
output += '-';

0 commit comments

Comments
 (0)