Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239const 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 settingsconst 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 programfunction writeInt(program, pc, val) { for (let i = 0; i < 8; i++) { program[pc.value] = (val >> (i * 8)) & 0xff; pc.value++; }}
// Mainfunction 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();