Watch out for operator precedence

This commit is contained in:
Mitchell Plamann
2015-03-28 18:35:26 -04:00
parent 2d8aec3181
commit af86f56873
2 changed files with 136 additions and 116 deletions

View File

@@ -1,5 +1,4 @@
var ByteStream = function (byte_array) { var ByteStream = function (byte_array) {
console.log("Instance created.");
this.byte_array = byte_array; this.byte_array = byte_array;
this.index = 0; this.index = 0;
}; };

View File

@@ -1,225 +1,246 @@
op = require("./opcodes"); op = require("./opcodes");
var ProgramState = function(parsed_file) { var verbose = false;
var main_function = parsed_file.function_pool[0]; function log(message) {
if (verbose) console.log(message);
this.stack = [] }
var StackFrame = function(file, f) {
log("Creating stack frame");
this.stack = [];
this.pc = 0; this.pc = 0;
this.program = main_function.code; this.program = f.code;
this.variables = []; this.variables = [];
for (var i = 0; i < main_function.num_vars; i++) for (var i = 0; i < f.num_vars; i++)
variables.push(0); variables.push(0);
this.file = file;
}
var ProgramState = function(parsed_file) {
log("Creating program state with file " + parsed_file);
var main_function = parsed_file.function_pool[0];
this.frame = new StackFrame(parsed_file, parsed_file.function_pool[0]);
this.call_stack = [this.frame];
this.file = parsed_file; this.file = parsed_file;
} }
ProgramState.prototype.push = function(val) {
this.frame.stack.push(val);
}
ProgramState.prototype.doIf = function(shouldJump) { ProgramState.prototype.pop = function() {
if (shouldJump) { return this.frame.stack.pop();
var address_offset = (this.program[this.pc+1] * 0x1000) + }
this.program[this.pc+2];
this.pc += address_offset; ProgramState.prototype.doIf = function(f) {
var y = this.pop();
var x = this.pop();
if (f(x,y)) {
var address_offset = (this.frame.program[this.frame.pc+1] * 0x1000) +
this.frame.program[this.frame.pc+2];
this.frame.pc += address_offset;
} else { } else {
this.pc += 3; this.frame.pc += 3;
} }
} }
ProgramState.prototype.step = function() { ProgramState.prototype.step = function() {
console.log("Running opcode " + op.lookup_table[this.program[this.pc]]); var opcode = this.frame.program[this.frame.pc]
switch (this.program[this.pc]) { log("Running opcode " +
op.lookup_table[opcode]);
switch (opcode) {
// Stack manipulation // Stack manipulation
case op.POP: case op.POP:
this.pc++; this.frame.pc++;
this.stack.pop(); this.pop();
break; break;
case op.DUP: case op.DUP:
this.pc++; this.frame.pc++;
var v = this.stack.pop(); var v = this.pop();
this.stack.push(v); this.push(v);
this.stack.push(v); this.push(v);
break; break;
case op.SWAP: case op.SWAP:
this.pc++; this.frame.pc++;
var v1 = this.stack.pop(); var v1 = this.pop();
var v2 = this.stack.pop(); var v2 = this.pop();
this.stack.push(v1); this.push(v1);
this.stack.push(v2); this.push(v2);
break; break;
case op.BIPUSH: case op.BIPUSH:
this.pc += 2; this.frame.pc += 2;
var val = this.program[this.pc-1]; var val = this.frame.program[this.frame.pc-1];
// Do sign extension if necessary // Do sign extension if necessary
if (val & 0x80 != 0) if ((val & 0x80) != 0)
val = -0x80 + (val & 0x7F); val = -0x80 + (val & 0x7F);
this.stack.push(val); this.push(val);
break; break;
// Returning from a function // Returning from a function
case op.RETURN: case op.RETURN:
var retVal = this.stack.pop(); var retVal = this.pop();
throw retVal; throw retVal;
// Arithmetic // Arithmetic
case op.IADD: case op.IADD:
this.pc++; this.frame.pc++;
var y = this.stack.pop(); var y = this.pop();
var x = this.stack.pop(); var x = this.pop();
console.log("Adding " + x + " and " + y); log("Adding " + x + " and " + y);
this.stack.push((x+y) % 0x100000000); this.push((x+y) % 0x100000000);
break; break;
case op.ISUB: case op.ISUB:
this.pc++; this.frame.pc++;
var y = this.stack.pop(); var y = this.pop();
var x = this.stack.pop(); var x = this.pop();
this.stack.push((x-y) % 0x100000000); this.push((x-y) % 0x100000000);
break; break;
case op.IMUL: case op.IMUL:
this.pc++; this.frame.pc++;
var y = this.stack.pop(); var y = this.pop();
var x = this.stack.pop(); var x = this.pop();
this.stack.push((x*y) % 0x100000000); this.push((x*y) % 0x100000000);
break; break;
case op.IDIV: case op.IDIV:
this.pc++; this.frame.pc++;
var y = this.stack.pop(); var y = this.pop();
var x = this.stack.pop(); var x = this.pop();
if (y == 0) c0_arith_error("Divide by zero"); if (y == 0) c0_arith_error("Divide by zero");
if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow"); if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow");
// This does int division. // This does int division.
// As I understand it, the ~~ is treated as the identity on integers // As I understand it, the ~~ is treated as the identity on integers
// which forces the type to int, not float // which forces the type to int, not float
this.stack.push(~~(x/y)); this.push(~~(x/y));
break; break;
case op.IREM: case op.IREM:
this.pc++; this.frame.pc++;
var y = this.stack.pop(); var y = this.pop();
var x = this.stack.pop(); var x = this.pop();
if (y == 0) c0_arith_error("Divide by zero"); if (y == 0) c0_arith_error("Divide by zero");
if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow"); if (x == INT_MIN && y == -1) c0_arith_error("Arithmetic overflow");
this.stack.push(x%y); this.push(x%y);
break; break;
case op.IAND: case op.IAND:
this.pc++; this.frame.pc++;
var y = this.stack.pop(); var y = this.pop();
var x = this.stack.pop(); var x = this.pop();
this.stack.push(x&y); this.push(x&y);
break; break;
case op.IOR: case op.IOR:
this.pc++; this.frame.pc++;
var y = this.stack.pop(); var y = this.pop();
var x = this.stack.pop(); var x = this.pop();
this.stack.push(x|y); this.push(x|y);
break; break;
case op.IXOR: case op.IXOR:
this.pc++; this.frame.pc++;
var y = this.stack.pop(); var y = this.pop();
var x = this.stack.pop(); var x = this.pop();
this.stack.push(x^y); this.push(x^y);
break; break;
case op.ISHL: case op.ISHL:
this.pc++; this.frame.pc++;
var y = this.stack.pop(); var y = this.pop();
var x = this.stack.pop(); var x = this.pop();
if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits"); if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits");
this.stack.push(x<<y); this.push(x<<y);
break; break;
case op.ISHR: case op.ISHR:
this.pc++; this.frame.pc++;
var y = this.stack.pop(); var y = this.pop();
var x = this.stack.pop(); var x = this.pop();
console.log("y="+y+", x="+x);
if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits"); if (y < 0 || y > 31) c0_arith_error("Shifting by too many bits");
this.stack.push(x>>y); this.push(x>>y);
break; break;
// Operations on local variables // Operations on local variables
case op.VLOAD: case op.VLOAD:
this.pc += 2; this.frame.pc += 2;
var index = this.program[this.pc-1]; var index = this.frame.program[this.frame.pc-1];
this.stack.push(this.variables[index]); this.push(this.frame.variables[index]);
break; break;
case op.VSTORE: case op.VSTORE:
this.pc += 2; this.frame.pc += 2;
var index = this.program[this.pc-1]; var index = this.frame.program[this.frame.pc-1];
this.variables[index] = this.stack.pop(); this.frame.variables[index] = this.pop();
break; break;
case op.ACONST_NULL: case op.ACONST_NULL:
this.pc++; this.frame.pc++;
this.stack.push(0); this.push(0);
break; break;
case op.ILDC: case op.ILDC:
this.pc += 3; this.frame.pc += 3;
var c1 = this.program[this.pc-2]; var c1 = this.frame.program[this.frame.pc-2];
var c2 = this.program[this.pc-1]; var c2 = this.frame.program[this.frame.pc-1];
var index = (c1 * 0x1000) + c2; var index = (c1 * 0x1000) + c2;
this.stack.push(this.file.int_pool[index]); this.push(this.file.int_pool[index]);
break; break;
case op.ALDC: case op.ALDC:
console.log("Error: I don't know how to handle ALDC yet"); this.frame.pc += 3;
throw "Error - can't handle ALDC"; var c1 = this.frame.program[this.frame.pc-2];
var c2 = this.frame.program[this.frame.pc-1];
var index = (c1 * 0x1000) + c2;
this.push(this.file.string_pool[index]);
break;
// Control flow // Control flow
case op.NOP: case op.NOP:
this.pc++; this.frame.pc++;
break; break;
case op.IF_CMPEQ: case op.IF_CMPEQ:
var y = this.stack.pop(); this.doIf(function (x,y) {return y == x;});
var x = this.stack.pop();
this.doIf(y == x);
break; break;
case op.IF_CMPNE: case op.IF_CMPNE:
var y = this.stack.pop(); this.doIf(function (x,y) {return y != x;});
var x = this.stack.pop();
this.doIf(y != x);
break; break;
case op.IF_CMPLT: case op.IF_CMPLT:
var y = this.stack.pop(); this.doIf(function (x,y) {return y > x;});
var x = this.stack.pop();
this.doIf(y > x);
break; break;
case op.IF_CMPGE: case op.IF_CMPGE:
var y = this.stack.pop(); this.doIf(function (x,y) {return y <= x;});
var x = this.stack.pop();
this.doIf(y <= x);
break; break;
case op.IF_CMPGT: case op.IF_CMPGT:
var y = this.stack.pop(); this.doIf(function (x,y) {return y < x;});
var x = this.stack.pop();
this.doIf(y < x);
break; break;
case op.IF_CMPLE: case op.IF_CMPLE:
var y = this.stack.pop(); this.doIf(function (x,y) {return y >= x;});
var x = this.stack.pop();
this.doIf(y >= x);
break; break;
case op.GOTO: case op.GOTO:
this.doIf(true); this.doIf(function (x,y) {return true;});
break; break;
case op.ATHROW: case op.ATHROW:
this.pc++; this.frame.pc++;
c0_user_error(this.stack.pop()); c0_user_error(this.pop());
break; break;
case op.ASSERT: case op.ASSERT:
this.pc++; this.frame.pc++;
var a = this.stack.pop(); var a = this.pop();
if (this.stack.pop() == 0) if (this.pop() == 0)
c0_assertion_failure(a); c0_assertion_failure(a);
break; break;
default: default:
console.log("Error: Unknown opcode\n"); console.log("Error: Unknown opcode: 0x" + opcode.toString(16) + "\n");
throw "Error - unknown opcode"; throw "Error - unknown opcode";
} }
return false; return false;
} }
// Takes in a parsed .bc0 file and runs it // Takes in a parsed .bc0 file and runs it
function execute(f) { function execute(file, callbacks, v) {
console.log("Beginning execution of file " + f); verbose = typeof v !== 'undefined' ? v : true;
log("Initializing with file " + file);
var state = new ProgramState(f); var state = new ProgramState(file);
log("Beginning execution");
while (true) { while (true) {
// I'm not sure how to structure this control flow yet, // I'm not sure how to structure this control flow yet,