Last active
October 24, 2016 00:17
-
-
Save ignovak/c6173c31207fd6fcbec606411460c73c to your computer and use it in GitHub Desktop.
Whitespace Interpreter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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