Page MenuHomePhorge

stack.js
No OneTemporary

Size
5 KB
Referenced Files
None
Subscribers
None

stack.js

const STACK_SIZE = 256;
const PROGRAM_SIZE = 1024;
const Opcode = {
OP_PUSH: 0,
OP_POP: 1,
OP_ADD: 2,
OP_SUB: 3,
OP_MUL: 4,
OP_DIV: 5,
OP_PRINT: 6,
OP_JMP: 7,
OP_JZ: 8,
OP_LT: 9,
OP_EQ: 10,
OP_NEG: 11,
OP_HALT: 12
};
const opcodeNames = {
[Opcode.OP_PUSH]: 'OP_PUSH',
[Opcode.OP_POP]: 'OP_POP',
[Opcode.OP_ADD]: 'OP_ADD',
[Opcode.OP_SUB]: 'OP_SUB',
[Opcode.OP_MUL]: 'OP_MUL',
[Opcode.OP_DIV]: 'OP_DIV',
[Opcode.OP_PRINT]: 'OP_PRINT',
[Opcode.OP_JMP]: 'OP_JMP',
[Opcode.OP_JZ]: 'OP_JZ',
[Opcode.OP_LT]: 'OP_LT',
[Opcode.OP_EQ]: 'OP_EQ',
[Opcode.OP_NEG]: 'OP_NEG',
[Opcode.OP_HALT]: 'OP_HALT'
};
// Debug settings
const DEBUG_ENABLED = true;
class VM {
constructor() {
this.sp = -1;
this.pc = 0;
this.stack = new Array(STACK_SIZE).fill(0);
this.program = new Uint8Array(PROGRAM_SIZE);
}
free() {
this.sp = -1;
this.pc = 0;
}
checkStackHasValues(count) {
if (this.sp < count - 1) {
throw new Error(`Stack Underflow! Need ${count} value(s), but stack has ${this.sp + 1}`);
}
}
push(value) {
if (this.sp >= STACK_SIZE - 1) {
throw new Error('Stack Overflow!');
}
this.sp++;
this.stack[this.sp] = value;
this.tracePush(value);
}
pop() {
this.checkStackHasValues(1);
const result = this.stack[this.sp];
this.sp--;
this.tracePop(result);
return result;
}
performBinary(op) {
this.checkStackHasValues(2);
const b = this.pop();
const a = this.pop();
let result;
switch (op) {
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a * b;
break;
case '/':
result = Math.trunc(a / b);
break;
}
this.push(result);
}
performUnary(op) {
this.checkStackHasValues(1);
const a = this.pop();
let result;
switch (op) {
case '-':
result = -a;
break;
}
this.push(result);
}
execute() {
this.traceHeader();
while (true) {
const op = this.program[this.pc];
this.traceInstruction(this.pc, op);
this.pc++;
switch (op) {
case Opcode.OP_PUSH: {
const val = this.readInt64();
this.push(val);
break;
}
case Opcode.OP_POP:
this.pop();
break;
case Opcode.OP_HALT:
this.traceHalt();
return;
case Opcode.OP_PRINT:
this.checkStackHasValues(1);
console.log(this.stack[this.sp]);
break;
case Opcode.OP_ADD:
this.performBinary('+');
break;
case Opcode.OP_SUB:
this.performBinary('-');
break;
case Opcode.OP_MUL:
this.performBinary('*');
break;
case Opcode.OP_DIV:
this.performBinary('/');
break;
case Opcode.OP_NEG:
this.performUnary('-');
break;
default:
throw new Error('Unknown Opcode!');
}
this.printStack();
this.traceSeparator();
}
}
readInt64() {
// Read 8 bytes as little-endian 64-bit integer
// JS doesn't handle 64-bit ints natively, but for small values this works
let val = 0;
for (let i = 0; i < 8; i++) {
val |= this.program[this.pc + i] << (i * 8);
}
this.pc += 8;
return val;
}
// Debug/trace methods
traceHeader() {
if (!DEBUG_ENABLED) return;
console.log('\n\x1b[96m\x1b[1m=== VM Execution Trace ===\x1b[0m\n');
}
traceInstruction(pc, opcode) {
if (!DEBUG_ENABLED) return;
const name = opcodeNames[opcode] || 'UNKNOWN';
console.log(`\x1b[94m[PC=${pc}]\x1b[0m \x1b[32m\x1b[1m${name}\x1b[0m`);
}
tracePush(value) {
if (!DEBUG_ENABLED) return;
console.log(`\x1b[2m \x1b[35mPUSH\x1b[0m \x1b[93m${value}\x1b[0m`);
}
tracePop(value) {
if (!DEBUG_ENABLED) return;
console.log(`\x1b[2m \x1b[31mPOP \x1b[0m \x1b[93m${value}\x1b[0m`);
}
traceHalt() {
if (!DEBUG_ENABLED) return;
console.log('\n\x1b[91m\x1b[1m=== Execution Halted ===\x1b[0m');
}
traceSeparator() {
if (!DEBUG_ENABLED) return;
console.log();
}
printStack() {
if (!DEBUG_ENABLED) return;
let stackStr = this.stack.slice(0, this.sp + 1).join(' ');
console.log(`\x1b[2m Stack \x1b[36m[sp=${this.sp}]\x1b[0m: [ \x1b[33m${stackStr}\x1b[0m ]`);
}
}
// Helper to write int64 to program
function writeInt(program, pc, val) {
for (let i = 0; i < 8; i++) {
program[pc.value] = (val >> (i * 8)) & 0xff;
pc.value++;
}
}
// Main
function main() {
const v = new VM();
const values = [10, 13, 6, 25, 42];
const pc = { value: 0 };
for (const val of values) {
v.program[pc.value++] = Opcode.OP_PUSH;
writeInt(v.program, pc, val);
}
v.program[pc.value++] = Opcode.OP_MUL;
v.program[pc.value++] = Opcode.OP_ADD;
v.program[pc.value++] = Opcode.OP_NEG;
v.program[pc.value++] = Opcode.OP_DIV;
v.program[pc.value++] = Opcode.OP_SUB;
v.program[pc.value++] = Opcode.OP_PRINT;
v.program[pc.value++] = Opcode.OP_HALT;
v.execute();
v.free();
}
main();

File Metadata

Mime Type
text/x-c++
Expires
Thu, Mar 26, 5:50 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
512026
Default Alt Text
stack.js (5 KB)

Event Timeline