//bool comparison functions
var BoolFuncs = {
"==":function(a, b){
return a.value === b.value;
},
"!=":function(a, b){
return a.value !== b.value;
},
"<":function(a, b){
return a.value < b.value;
},
">":function(a, b){
return a.value > b.value;
},
"<=":function(a, b){
return a.value <= b.value;
},
">=":function(a, b){
return a.value >= b.value;
},
"&&":function(a, b){
return a.value && b.value;
},
"||":function(a, b){
return a.value || b.value;
}
};
//processes, or mini functions that facilitate operations
//dest is always the destination of where the resulting value is stored.
var procfuncs = {
//math funcs
"+":function(a, b, dest){
dest.value = a.value + b.value;
},
"-":function(a, b, dest){
dest.value = a.value - b.value;
},
"*":function(a, b, dest){
dest.value = a.value * b.value;
},
"/":function(a, b, dest){
dest.value = a.value / b.value;
},
"//":function(a, b, dest){
dest.value = Math.floor(a.value / b.value);
},
"%":function(a, b, dest){
dest.value = a.value % b.value;
},
"**":function(a, b, dest){
dest.value = Math.pow(a.value, b.value);
},
//concat oper, for strings or arrays
"+~":function(a, b, dest){
dest.value = a.concat(b);
},
//pushes or appends onto arrays
"push":function(a, b, dest){
a.value.push(b.value);
},
//pop operator only uses one operand, but needs both to be called
"pop":function(a, b, dest){
dest.value = a.value.pop();
},
//indexing operator
"get":function(a, b, dest){
dest.value = a.value[b.value];
},
//setitem oper for objects or arrays
"set":function(a, b, dest){
dest.value[a.value] = b.value;
},
//takes the length of a string or array
"len":function(a, b, dest){
dest.value = a.value.length;
}
};
//class to implement variable collection and storage
var Vardict = (function(){
//private object to hold values in the dictionary
function Valobj(value){
this.value = value;
}
function Vardict(){
}
Vardict.prototype.init = function(key, value){
this[key] = new Valobj(value);
};
Vardict.prototype.del = function(key){
delete this[key];
};
Vardict.prototype.check = function(key){
return key in this;
};
//declares variable without assigning value
Vardict.prototype.declare = function(key){
this[key] = null;
};
//gets the entire value with it's object reference
//if key does not exist in object, creates a key that is null valued
Vardict.prototype.get = function(key){
if(this.check(key)){
return this[key];
}
else{
this.init(key, null);
return this[key];
}
};
//gets only the value associated with the reference
Vardict.prototype.getValue = function(key){
if(this.check(key)){
return this[key].value;
}
else{
return false;
}
};
//modifies the value of a preexisting object
Vardict.prototype.set = function(key, value){
if(this.check(key)){
this[key].value = value;
}
else {
return false;
}
};
return Vardict;
})();
//main class for virtual machine
var VirtualMachine = (function(){
//private object to hold values in the dictionary
function Valobj(value){
this.value = value;
}
function VirtualMachine(){
this.variables = new Vardict();
this.endstate = false;
this.comps = BoolFuncs;
this.procs = procfuncs;
this.beginstate = true;
}
//loads variables from a variable object, as a preprocessor arrangement
VirtualMachine.prototype.loadVars = function(obj){
for(var key in obj){
this.variables.init(key, obj[key]);
}
};
//method that turns values into value object for easy referencing.
VirtualMachine.prototype.setupVars = function(obj){
for(var key in obj){
//loads an object from memory if its already a variable
if(typeof obj[key] === 'string' && obj[key][0] === "@"){
obj[key] = this.variables.get(obj[key]);
}
//strings that don't start with @ will not be loaded
else if(key !== "op") {
obj[key] = new Valobj(obj[key]);
}
}
};
//evaluates one process
VirtualMachine.prototype.assembleProc = function(obj){
if("a" in obj && "b" in obj && "dest" in obj && "op"in obj){
this.procs[obj.op](obj.a, obj.b, obj.dest);
}
else{
throw "Process Error, invalid keys";
}
};
//evaluates if a condition object is true
VirtualMachine.prototype.assembleCond = function(obj){
if("a" in obj && "b" in obj && "op"in obj){
if(!(obj.op in this.comps)) throw "OPER ERROR";
return this.comps[obj.op](obj.a, obj.b);
}
};
//determines if an object is a block
VirtualMachine.prototype.isBlock = function(obj){
return "type" in obj && obj.type === "block" && "stmts" in obj;
};
//parses and assembles a block statement
VirtualMachine.prototype.parseBlock = function(obj){
if("vars" in obj){
this.loadVars(obj.vars);
}
for(var i=0;i<obj.stmts.length;i++){
this.setupVars(obj.stmts[i]);
this.assembleProc(obj.stmts[i]);
}
};
VirtualMachine.prototype.Assemble = function(obj){
//continues down the JSON tree as long as end state is false.
//reference for back jumping
var pointer = obj;
while(this.endstate === false){
if(this.beginstate){
this.loadVars(pointer.vars);
pointer = obj.program;
//turns off beginstate after preprocessing.
this.beginstate = false;
}
else{
if (this.isBlock(pointer)){
this.parseBlock(pointer);
if("next" in pointer) {
pointer = pointer.next;
}
else {
throw "Ending Error, no END command before termination"
}
}
}
}
}
return VirtualMachine;
})();