Created
August 16, 2017 20:57
-
-
Save csauve/5912c4c58a42052df8300902d4b012ea to your computer and use it in GitHub Desktop.
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
const R = require("ramda"); | |
//state operations | |
const updStack = R.curry((prop, updater, state) => R.over(R.lensProp(prop), updater, state)); | |
const updCall = (stackOp) => updStack("callStack", stackOp); | |
const updData = (stackOp) => updStack("dataStack", stackOp); | |
//stack operations | |
const pop = R.curry((num, stack) => { | |
if (stack.length < num) throw new Error("Stack underflow!"); | |
return R.dropLast(num, stack); | |
}); | |
const peek = R.curry((num, stack) => { | |
if (stack.length < num) throw new Error("Stack underflow!"); | |
return R.takeLast(num, stack); | |
}); | |
const push = R.curry((args, stack) => R.concat(stack, args)); | |
const popPush = R.curry((numArgs, reducer, stack) => push( | |
reducer(peek(numArgs, stack)), | |
pop(numArgs, stack) | |
)); | |
const advance = updCall(popPush(1, ([a]) => [a + 1])); | |
const popPushAdv = (numArgs, reducer) => R.pipe( | |
updData(popPush(numArgs, reducer)), | |
advance | |
); | |
const pushAdv = (vals) => R.pipe( | |
updData(push(vals)), | |
advance | |
); | |
const call = (ptr) => R.pipe( | |
advance, | |
updCall(push([ptr])) | |
); | |
// opcode: (opArgs) -> (state) -> state' | |
const operations = { | |
noop: () => advance, | |
add: () => popPushAdv(2, ([x, y]) => [x + y]), | |
sub: () => popPushAdv(2, ([x, y]) => [x - y]), | |
mul: () => popPushAdv(2, ([x, y]) => [x * y]), | |
div: () => popPushAdv(2, ([x, y]) => [x / y]), | |
or: () => popPushAdv(2, ([x, y]) => [x || y ? 1 : 0]), | |
and: () => popPushAdv(2, ([x, y]) => [x && y ? 1 : 0]), | |
xor: () => popPushAdv(2, ([x, y]) => [!!x ^ !!y]), | |
not: () => popPushAdv(1, ([x]) => [x ? 0 : 1]), | |
// TODO: comparisons, conditions | |
// max: () => popPushAdv(2, ([x, y]) => [Math.max(x, y)]), | |
// min: () => popPushAdv(2, ([x, y]) => [Math.min(x, y)]), | |
dup: () => popPushAdv(1, ([x]) => [x, x]), | |
swap: () => popPushAdv(2, ([x, y]) => [y, x]), | |
push: (args) => pushAdv(args), | |
call: ([ptr]) => call(ptr), | |
ret: () => updCall(pop(1)) | |
}; | |
const isIgnoredLine = (line) => R.startsWith("#", line) || R.isEmpty(line); | |
const isLabel = (line) => line.includes(":"); | |
const parseLabel = (line, currentPc) => { | |
const [label, givenVal] = line.split(":"); | |
return {label, val: givenVal == "" ? currentPc : Number.parseFloat(givenVal)}; | |
}; | |
const parseOpArgs = (opArgs, labels) => opArgs.map((opArg) => | |
R.startsWith("$", opArg) ? labels[opArg.substring(1)] : Number.parseFloat(opArg) | |
); | |
const parseProgram = (programSrc) => { | |
const instructions = [], labels = {}; | |
for (let line of programSrc.split("\n").map(R.trim)) { | |
if (isIgnoredLine(line)) { | |
continue; | |
} else if (isLabel(line)) { | |
const {label, val} = parseLabel(line, instructions.length); | |
labels[label] = val; | |
} else { | |
const [opcode, ...opArgs] = line.split(" "); | |
instructions.push({opcode, opArgs: parseOpArgs(opArgs, labels)}); | |
} | |
} | |
return {instructions, mainAddr: labels["main"]}; | |
}; | |
const runProgram = (initialStack, programSrc) => { | |
const {instructions, mainAddr} = parseProgram(programSrc); | |
let state = {dataStack: initialStack, callStack: [mainAddr]}; | |
while (true) { | |
let pc = R.takeLast(1, state.callStack)[0]; | |
if (pc >= instructions.length) break; | |
const {opcode, opArgs} = instructions[pc]; | |
if (!operations[opcode]) throw new Error(`Unknown opcode: ${opcode}`); | |
console.log(`${opcode} ${opArgs} ${JSON.stringify(state)}`); | |
state = operations[opcode](opArgs)(state); | |
} | |
return state.dataStack; | |
}; | |
const resultStack = runProgram([], ` | |
initial:3 | |
increment: | |
push 1 | |
add | |
ret | |
main: | |
push $initial | |
call $increment | |
`); | |
console.log(resultStack); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment