export let instructions_raw = { "+": { impl: function (thread) { thread.stack.push(thread.pop() + thread.pop()); }, desc: "b a → b + a", can_jit: true, stack_min: 2, stack_return: 1, unchecked_js_code: "stack[stack.length - 2] += stack.pop();", }, "-": { impl: function (thread) { thread.stack.push(-thread.pop() + thread.pop()); }, desc: "b a → b - a", can_jit: true, stack_min: 2, stack_return: 1, unchecked_js_code: "stack[stack.length - 2] -= stack.pop();", }, "*": { impl: function (thread) { thread.stack.push(thread.pop() * thread.pop()); }, desc: "b a → b * a", can_jit: true, stack_min: 2, stack_return: 1, unchecked_js_code: "stack[stack.length - 2] *= stack.pop();", }, "/": { impl: function (thread) { let a = thread.pop(); let b = thread.pop(); thread.stack.push(Math.round(b / a)); }, desc: "b a → b // a", can_jit: true, stack_min: 2, stack_return: 1, unchecked_js_code: "stack[stack.length - 2] /= stack.pop();", }, "%": { impl: function (thread) { let a = thread.pop(); let b = thread.pop(); thread.stack.push(b % a); }, desc: "b a → b % a", can_jit: true, stack_min: 2, stack_return: 1, unchecked_js_code: "stack[stack.length - 2] %= stack.pop();", }, "!": { impl: function (thread) { thread.stack.push(thread.pop() == 0 ? 1 : 0); }, desc: "a → a == 0 ? 1 : 0", can_jit: true, stack_min: 1, stack_return: 1, unchecked_js_code: "stack[stack.length - 1] = stack[stack.length - 1] ? 1 : 0;", }, "`": { impl: function (thread) { thread.stack.push(thread.pop() < thread.pop() ? 1 : 0); }, desc: "b a → b > a ? 1 : 0", can_jit: true, stack_min: 2, stack_return: 1, unchecked_js_code: "stack[stack.length - 2] = stack.pop() < stack[stack.length - 1] ? 0 : 1;", }, "<": { impl: function (thread) { thread.cold = -1; thread.rowd = 0; }, desc: "() → (); go left", stack_min: 0, stack_return: 0, unchecked_js_code: "", can_jit: true, }, ">": { impl: function (thread) { thread.cold = 1; thread.rowd = 0; }, desc: "() → (); go right", stack_min: 0, stack_return: 0, unchecked_js_code: "", can_jit: true, }, "^": { impl: function (thread) { thread.cold = 0; thread.rowd = -1; }, desc: "() → (); go up", stack_min: 0, stack_return: 0, unchecked_js_code: "", can_jit: true, }, "v": { impl: function (thread) { thread.cold = 0; thread.rowd = 1; }, desc: "() → (); go down", stack_min: 0, stack_return: 0, unchecked_js_code: "", can_jit: true, }, "?": { impl: function (thread) { let x = Math.floor(Math.random() * 4); thread.cold = ((x % 2) * 2 - 1) * Math.floor(x / 2); thread.rowd = ((x % 2) * 2 - 1) * ( 1 - Math.floor(x / 2)); }, desc: "() → (); go random cardinal", can_jit: false, }, "_": { impl: function (thread) { thread.rowd = 0; thread.cold = thread.pop() == 0 ? 1 : -1; }, desc: "a → (); a == 0 ? go right : go left", can_jit: false, }, "|": { impl: function (thread) { thread.cold = 0; thread.rowd = thread.pop() == 0 ? 1 : -1; }, desc: "a → (); a == 0 ? go down : go up", can_jit: false, }, '"': { impl: function (thread) { thread.mode = "string"; }, desc: "; toggle string mode", can_jit: false, // TODO this should be OK, but we'll need JIT for string mode. stack_min: 0, stack_return: 0, }, ":": { impl: function (thread) { thread.stack.push(thread.stack[thread.stack.length-1]); }, desc: "a → a a", can_jit: true, stack_min: 1, stack_return: 2, unchecked_js_code: "stack.push(stack[stack.length-1]);", }, "\\": { impl: function (thread) { let a = thread.pop(); var b = thread.pop(); thread.stack.push(a, b); }, desc: "b a → a b", can_jit: true, stack_min: 2, stack_return: 2, unchecked_js_code: "stack.push(stack.pop(), stack.pop());", }, "$": { impl: function (thread) { thread.pop(); }, desc: "a → ()", can_jit: true, stack_min: 1, stack_return: 0, unchecked_js_code: "stack.pop();", }, ".": { impl: function (thread) { thread.interpreter.out(thread.pop() + " "); }, desc: "a → (); output a", can_jit: false, }, ",": { impl: function (thread) { thread.interpreter.out(String.fromCharCode(thread.pop())); }, desc: "a → (); output chr(a)", can_jit: false, }, "#": { impl: function (thread) { thread.col += thread.cold; thread.row += thread.rowd; }, desc: "; jump one cell", can_jit: true, stack_min: 0, stack_return: 0, unchecked_js_code: "", }, "g": { impl: function (thread) { let row = thread.pop(); var col = thread.pop(); thread.stack.push(thread.interpreter.get_field(thread, col, row)); }, desc: "col row → field[row][col]", can_jit: true, stack_min: 2, stack_return: 1, unchecked_js_code: "{let row = stack.pop(); stack[stack.length-1] = (thread.interpreter.get_field(thread, stack[stack.length-1], row));}", }, "p": { impl: function (thread) { let row = thread.pop(); var col = thread.pop(); var val = thread.pop(); thread.interpreter.set_field(thread, col, row, val); }, desc: "val col row → (); field[row][col] = val", can_jit: true, stack_min: 3, stack_return: 0, unchecked_js_code: function (thread_state) { return ` { let row = stack.pop(); let col = stack.pop(); thread.interpreter.set_field(thread, col, row, stack.pop()); for (let i=0; i 0) { console.log("Have data!"); thread.stack.push(thread.interpreter.stdin_queue.pop()); } else { console.log("Have no data!"); return new Promise(r => thread.interpreter.stdin_waiters.push(r)).then(function (char_code) {thread.stack.push(char_code); thread.blocked = false;}); } }, desc: "() → x; read a character from input", can_jit: false, }, "@": { impl: function (thread) { thread.interpreter.output_error(thread, "Normal termination!"); thread.running = false; }, desc: "; stop current thread", can_jit: false, }, "]": { impl: function (thread) { thread.interpreter.output_error(thread, "Debug!"); }, desc: "; dump thread debug", can_jit: false, }, " ": { impl: function (thread) { }, desc: "; no-op", can_jit: true, stack_min: 0, stack_return: 0, unchecked_js_code: "", }, "(": { impl: function (thread) { const n = thread.pop(); let x = 0; for(let i=0; i