bitburner-src/src/Netscript/Evaluator.js

123 lines
3.3 KiB
JavaScript
Raw Normal View History

/* Evaluator
* Evaluates the Abstract Syntax Tree for Netscript
* generated by the Parser class
*/
function evaluate(exp, env) {
switch (exp.type) {
case "num":
case "str":
case "bool":
return exp.value;
case "var":
return env.get(exp.value);
//Can currently only assign to "var"s
case "assign":
if (exp.left.type != "var")
throw new Error("Cannot assign to " + JSON.stringify(exp.left));
return env.set(exp.left.value, evaluate(exp.right, env));
case "binary":
return apply_op(exp.operator,
evaluate(exp.left, env),
evaluate(exp.right, env));
case "if":
var numConds = exp.cond.length;
var numThens = exp.then.length;
if (numConds == 0 || numThens == 0 || numConds != numThens) {
throw new Error ("Number of conds and thens in if structure don't match (or there are none)");
}
for (var i = 0; i < numConds; i++) {
var cond = evaluate(exp.cond[i], env);
if (cond) return evaluate(exp.then, env);
}
//Evaluate else if it exists, snce none of the conditionals
//were true
return exp.else ? evaluate(exp.else, env) : false;
case "for":
evaluate(exp.init, env);
cond = evaluate(exp.cond, env);
console.log("Evaluated the conditional");
while (cond) {
evaluate(exp.code, env);
evaluate(exp.postloop, env);
cond = evaluate(exp.cond, env);
}
//TODO I don't think I need to return anything..but I might be wrong
break;
case "while":
cond = evaluate(exp.cond, env);
while (cond) {
evaluate(exp.code, env);
cond = evaluate(exp.cond, env);
}
//TODO I don't think I need to return anything..but I might be wrong
break;
case "prog":
var val = false;
exp.prog.forEach(function(exp){ val = evaluate(exp, env) });
return val;
/* Currently supported function calls:
* hack()
* sleep(N) - sleep N seconds
* print(x) - Prints a variable or constant
*
*/
case "call":
//Define only valid function calls here, like hack() and stuff
//var func = evaluate(exp.func, env);
//return func.apply(null, exp.args.map(function(arg){
// return evaluate(arg, env);
//}));
if (exp.func.value == "hack") {
console.log("Execute hack()");
} else if (exp.func.value == "sleep") {
console.log("Execute sleep()");
} else if (exp.func.value == "print") {
post(evaluate(exp.args[0], env).toString());
}
break;
default:
throw new Error("I don't know how to evaluate " + exp.type);
}
}
function apply_op(op, a, b) {
function num(x) {
if (typeof x != "number")
throw new Error("Expected number but got " + x);
return x;
}
function div(x) {
if (num(x) == 0)
throw new Error("Divide by zero");
return x;
}
switch (op) {
case "+": return num(a) + num(b);
case "-": return num(a) - num(b);
case "*": return num(a) * num(b);
case "/": return num(a) / div(b);
case "%": return num(a) % div(b);
case "&&": return a !== false && b;
case "||": return a !== false ? a : b;
case "<": return num(a) < num(b);
case ">": return num(a) > num(b);
case "<=": return num(a) <= num(b);
case ">=": return num(a) >= num(b);
case "==": return a === b;
case "!=": return a !== b;
}
throw new Error("Can't apply operator " + op);
}