Skip to content

Instantly share code, notes, and snippets.

@ignovak
Last active October 24, 2016 00:17
Show Gist options
  • Save ignovak/c6173c31207fd6fcbec606411460c73c to your computer and use it in GitHub Desktop.
Save ignovak/c6173c31207fd6fcbec606411460c73c to your computer and use it in GitHub Desktop.
Whitespace Interpreter
// https://www.codewars.com/kata/52dc4688eca89d0f820004c6/
class Stack {
constructor() {
this.stack = []
}
push(x) {
return this.stack.push(x)
}
pop() {
if (!this.len) throw Error
return this.stack.pop()
}
duplicate(n = 0) {
let x = this.stack[this.len - n - 1]
if (typeof x == 'undefined') throw Error
this.stack.push(x)
}
swap() {
this.stack.splice(this.len - 2, 0, this.stack.pop())
}
slide(n) {
if (typeof this.stack[this.len - n - 1] == 'undefined') throw Error
this.stack.splice(this.len - n - 1, n)
}
get len() {
return this.stack.length
}
}
class Program {
constructor(code) {
this.code = this.unbleach(code.replace(/\S/g, ''))
this.callStack = []
this.labels = {}
let reNum = '([ts])([ts]*)n'
let reLabel = '([ts]*n)'
this.tokens = {
PUSH_NUM: 'ss' + reNum,
DUPLICATE: 'sns',
DUPLICATE_NUM: 'sts' + reNum,
SLIDE: 'stn' + reNum,
SWAP: 'snt',
DISCARD: 'snn',
POP_NUM: 'tnst',
POP_CHAR: 'tnss',
PUSH_HEAP: 'tts',
POP_HEAP: 'ttt',
ADD: 'tsss',
SUBTRACT: 'tsst',
MULTIPLY: 'tssn',
DIVIDE: 'tsts',
MOD: 'tstt',
READ: 'tntt' + reNum,
LABEL: 'nss' + reLabel,
JUMP: 'nsn' + reLabel,
JUMP_IF_ZERO: 'nts' + reLabel,
JUMP_IF_NEGATIVE: 'ntt' + reLabel,
CALL_SUB: 'nst' + reLabel,
EXIT_SUB: 'ntn',
EOF: 'nnn'
}
}
unbleach(n) {
if (n) return n.replace(/ /g, 's').replace(/\t/g, 't').replace(/\n/g, 'n');
}
shift() {
for (let token in this.tokens) {
let re = RegExp('^' + this.tokens[token])
if (this.code.match(re)) {
this.code = this.code.replace(re, (_, sign, num) => {
if (token == 'PUSH_NUM') {
this.num = (sign == 't' ? -1 : 1) *
parseInt(num.replace(/s/g, 0).replace(/t/g, 1), 2) || 0
} else if (sign) {
this.label = sign;
}
return ''
})
if (token == 'LABEL') {
if (this.label in this.labels) throw Error
this.labels[this.label] = this.code;
if (this.jumpTo == this.label) {
this.goTo(this.label);
delete this.jumpTo;
}
}
return this.jumpTo ? null : token
}
}
throw Error
}
goTo (label) {
if (this.labels[label]) {
this.code = this.labels[label];
} else {
this.jumpTo = label;
}
}
callSub (label) {
this.callStack.push(this.code);
this.goTo(label);
}
exitSub () {
this.code = this.callStack.pop();
if (!this.code) throw Error
}
finish () {
if (this.jumpTo || this.callStack.length) throw Error
}
get length() {
return this.code.length
}
}
function divide(a, b) {
if (a == 0) throw Error;
return Math.floor(b / a)
}
function mod(a, b) {
if (a == 0) throw Error;
return b % a + (a * b < 0 ? a : 0);
}
// solution
function whitespace(code, input) {
var output = '';
var stack = new Stack;
var heap = {};
var program = new Program(code)
var a;
while (program.length) {
switch (program.shift()) {
case 'PUSH_NUM': stack.push(program.num); break;
case 'DUPLICATE': stack.duplicate(); break;
case 'DUPLICATE_NUM': stack.duplicate(program.num); break;
case 'SLIDE': stack.slide(program.num); break;
case 'SWAP': stack.swap(); break;
case 'DISCARD': stack.pop(); break;
case 'POP_NUM': output += stack.pop(); break;
case 'POP_CHAR': output += String.fromCharCode(stack.pop()); break;
case 'PUSH_HEAP': a = stack.pop(); heap[stack.pop()] = a; break;
case 'POP_HEAP': a = heap[stack.pop()]; if (!a) throw Error; stack.push(a); break;
case 'ADD': stack.push(stack.pop() + stack.pop()); break;
case 'SUBTRACT': stack.push(- stack.pop() + stack.pop()); break;
case 'MULTIPLY': stack.push(stack.pop() * stack.pop()); break;
case 'DIVIDE': stack.push(divide(stack.pop(), stack.pop())); break;
case 'MOD': stack.push(mod(stack.pop(), stack.pop())); break;
case 'READ': heap[stack.pop()] = program.num; break;
case 'LABEL': break;
case 'JUMP': program.goTo(program.label); break;
case 'JUMP_IF_ZERO': if (stack.pop() == 0) program.goTo(program.label); break;
case 'JUMP_IF_NEGATIVE': if (stack.pop() < 0) program.goTo(program.label); break;
case 'CALL_SUB': program.callSub(program.label); break;
case 'EXIT_SUB': program.exitSub(); break;
case 'EOF': program.finish(); return output;
}
}
throw Error
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment