Skip to content

Instantly share code, notes, and snippets.

@JosePedroDias
Last active October 14, 2023 00:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JosePedroDias/47696077307ebce6cc5e0c6ac76e922b to your computer and use it in GitHub Desktop.
Save JosePedroDias/47696077307ebce6cc5e0c6ac76e922b to your computer and use it in GitHub Desktop.
simplest stack calculator
import { test } from 'node:test';
import { strictEqual, deepEqual } from 'node:assert';
/*
inspired by https://www.youtube.com/watch?v=umSuLpjFUf8
3 3 * 4 4 * + sqrt
equivalent to
Math.sqrt(3*3 + 4*4)
*/
const parseTokens = (s) => s.split(' ').map(v => isFinite(v) && v !== '' ? parseFloat(v) : v);
const parseWithQuotes = (s) => {
let parts = [];
let i = 0;
let start, end;
while (true) {
start = s.indexOf('[', i);
if (start === -1) break;
else {
end = s.indexOf(']', start+1);
if (end === -1) break;
}
const prefix = s.substring(i, start);
if (prefix !== '') parts = [...parts, ...parseTokens(prefix)];
const quotedString = s.substring(start, end+1);
parts.push(quotedString);
i = end + 1;
}
const prefix = s.substring(i);
if (prefix !== '') parts = [...parts, ...parseTokens(prefix)];
parts = parts.filter((v) => v !== '');
return parts;
}
const quote = (arr) => `[${arr.join(' ')}]`;
const unquote = (s) => parseWithQuotes( s.substring(1, s.length - 1) );
function calc(args, stack = [], debug = false) {
const ops = {
'+': (a, b) => a + b,
'-': (a, b) => a - b,
'*': (a, b) => a * b,
'/': (a, b) => a / b,
'sqrt': (a) => Math.sqrt(a),
};
while (args.length > 0) {
debug && console.log(`${stack.join(' ')} | ${args.join(' ')}`);
const arg = args.shift();
switch (arg) {
case '+':
case '-':
case '*':
case '/': {
const b = stack.pop();
const a = stack.pop();
stack.push( ops[arg](a, b) );
} break;
case 'sqrt': {
const a = stack.pop();
stack.push( ops[arg](a) );
} break;
case 'dup': {
const a = stack.pop();
stack.push(a);
stack.push(a);
} break;
case 'swap': {
const a = stack.pop();
const b = stack.pop();
stack.push(a);
stack.push(b);
} break;
case 'apply': {
const a = unquote( stack.pop() );
args = [...a, ...args];
} break;
case 'dig2': {
const a = stack.splice(stack.length - 3, 1)[0];
stack.push(a);
} break;
case 'drop': {
const a = stack.pop();
console.log(`drop: ${a}`);
} break;
default:
stack.push(arg);
}
}
return stack;
}
if (true) {
const args = parseWithQuotes( process.argv[2] );
const stack_ = calc(args, [], true);
console.log(stack_[0]);
}
else {
const calc_ = (s) => calc(parseWithQuotes(s)).pop();
test('parseTokens', () => deepEqual([2, 3, '+', 'sqrt'], parseTokens('2 3 + sqrt')));
test('parseWithQuotes 1', () => deepEqual([2, 3, '+'], parseWithQuotes('2 3 +')));
test('parseWithQuotes 2', () => deepEqual([2, '[2 1 -]', '+'], parseWithQuotes('2 [2 1 -] +')));
test('parseWithQuotes 3', () => deepEqual(['[2 1 -]', 5, '+'], parseWithQuotes('[2 1 -] 5 +')));
test('parseWithQuotes 4', () => deepEqual([2, '[2 1 -]'], parseWithQuotes('2 [2 1 -]')));
test('quote', () => deepEqual('[2 1 -]', quote([2, 1, '-'])));
test('unquote', () => deepEqual([2, 1, '-'], unquote('[2 1 -]')));
test('sum', () => strictEqual(3, calc_('1 2 +')));
test('sub 1', () => strictEqual(1, calc_('2 1 -')));
test('sub 2', () => strictEqual(-1, calc_('1 2 -')));
test('mul', () => strictEqual(12, calc_('3 4 *')));
test('div', () => strictEqual(3, calc_('12 4 /')));
test('sqrt', () => strictEqual(3, calc_('9 sqrt')));
test('dup', () => strictEqual(16, calc_('4 dup *')));
test('swap', () => strictEqual(8, calc_('1 9 swap -')));
test('dig2', () => strictEqual(12, calc_('3 4 5 dig2 + +')));
test('drop', () => strictEqual(undefined, calc_('2 drop')));
test('pythagorean 1', () => strictEqual(5, calc_('3 3 * 4 4 * + sqrt')));
test('pythagorean 2', () => strictEqual(5, calc_('3 4 [dup * swap dup * + sqrt] apply')));
test('pythagorean 3', () => strictEqual(undefined, calc_('[dup * swap dup * + sqrt] dup 3 4 dig2 apply drop 5 12 dig2 apply drop')));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment