summaryrefslogtreecommitdiff
path: root/Blog/Components/Pages/Calc.razor.js
diff options
context:
space:
mode:
Diffstat (limited to 'Blog/Components/Pages/Calc.razor.js')
-rw-r--r--Blog/Components/Pages/Calc.razor.js140
1 files changed, 140 insertions, 0 deletions
diff --git a/Blog/Components/Pages/Calc.razor.js b/Blog/Components/Pages/Calc.razor.js
new file mode 100644
index 0000000..d18634e
--- /dev/null
+++ b/Blog/Components/Pages/Calc.razor.js
@@ -0,0 +1,140 @@
1import { getById, div, span, h, writeError } from "/common.module.js"
2
3export function onLoad() {
4 console.log('Loaded');
5 const form = getById("form");
6 const input = getById("input");
7 const log = getById("log");
8
9 form.addEventListener("submit", submitForm);
10
11 const urlParams = new URLSearchParams(window.location.search);
12 const queryInput = urlParams.get('in');
13
14 if (input.value.length === 0) {
15 input.value = queryInput;
16 }
17}
18
19export function onUpdate() {
20 console.log('Updated');
21}
22
23export function onDispose() {
24 console.log('Disposed');
25}
26
27/** @param {SubmitEvent} event */
28function submitForm(event) {
29 console.log(input.value);
30 resetLog();
31 try {
32 const stack = evalString(input.value);
33 console.log(stack);
34
35 const path = window.location.pathname;
36 const params = new URLSearchParams(window.location.search);
37 const hash = window.location.hash;
38
39 params.set("in", input.value);
40 window.history.replaceState({}, '', `${path}?${params.toString()}${hash}`);
41 }
42 catch (error) {
43 writeError(error);
44 }
45 event.preventDefault();
46}
47
48/** @param {string} input */
49function evalString(input) {
50 let words = input.trim().split(' ').filter(i => i);
51 return evalWords([], words);
52}
53
54function effect2(f) {
55 return (stack) => {
56 if (stack.length <= 1) throw "stack underflow";
57 let [x, y, ...rest] = stack;
58 return [f(y, x), ...rest];
59 }
60}
61
62const plus = effect2((a, b) => a + b);
63const subtract = effect2((a, b) => a - b);
64const multiply = effect2((a, b) => a * b);
65const divide = effect2((a, b) => a / b);
66
67
68function evalWords(stack, words) {
69 writeLog(stack, words);
70 if (words.length === 0) return stack;
71
72 let [word, ...rest] = words;
73 return evalWords(evalWord(word, stack), rest);
74}
75
76function evalWord(word, stack, rest) {
77 switch (word) {
78 case "+": return plus(stack);
79 case "-": return subtract(stack);
80 case "*": return multiply(stack);
81 case "/": return divide(stack);
82 case "dup": return dup(stack);
83 case "drop": return drop(stack);
84 case "swap": return swap(stack);
85 default: return parse(word, stack);
86 }
87}
88
89function dup(stack) {
90 let [x, ...rest] = stack;
91 return [x, x, ...rest];
92}
93
94function drop(stack) {
95 let [_, ...rest] = stack;
96 return rest;
97}
98
99function swap(stack) {
100 let [x, y, ...rest] = stack;
101 return [y, x, ...rest];
102}
103
104function parse(word, stack) {
105 let num = Number(word);
106 if (isNaN(num)) {
107 throw `word '${word}' not recognised`;
108 }
109 return [num, ...stack];
110}
111
112function writeLog(stack, words) {
113 let log_left = span(`[${stack.join(', ')}]`);
114 let log_right = span(words.join(' '));
115
116 if (words.length === 0) {
117 log_left.textContent += " <=="
118 }
119
120 let log_row = div(log_left, log_right);
121 log_row.className = "flex-spread";
122
123 log.appendChild(log_row);
124}
125
126function resetLog() {
127 if (!log.hasChildNodes()) return;
128
129 let summary = h("summary");
130 summary.textContent = log.firstChild.lastChild.textContent;
131
132 let old_log = log.cloneNode(true);
133 old_log.id = '';
134
135 let details = h("details", summary, old_log);
136 details.className = "history"
137
138 log.insertAdjacentElement("afterend", details);
139 log.replaceChildren(); //remove children, clear log
140} \ No newline at end of file