diff --git a/index.html b/index.html
index 8b0a425f9..d39158d63 100644
--- a/index.html
+++ b/index.html
@@ -12,6 +12,11 @@
+
+
+
+
+
diff --git a/src/Netscript/Evaluator.js b/src/Netscript/Evaluator.js
index d057f6053..ae59ed9b3 100644
--- a/src/Netscript/Evaluator.js
+++ b/src/Netscript/Evaluator.js
@@ -2,28 +2,78 @@
* Evaluates the Abstract Syntax Tree for Netscript
* generated by the Parser class
*/
-function evaluate(exp, env) {
+// Evaluator should return a Promise, so that any call to evaluate() can just
+//wait for that promise to finish before continuing
+function evaluate(exp, workerScript) {
+ var env = workerScript.env;
switch (exp.type) {
case "num":
case "str":
case "bool":
- return exp.value;
-
+ return new Promise(function(resolve, reject) {
+ resolve(exp.value);
+ });
+ break;
case "var":
- return env.get(exp.value);
-
+ return new Promise(function(resolve, reject) {
+ resolve(env.get(exp.value));
+ });
+ break;
//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));
-
+ console.log("Evaluating assign operation");
+ return new Promise(function(resolve, reject) {
+ if (exp.left.type != "var")
+ throw new Error("Cannot assign to " + JSON.stringify(exp.left));
+
+ var p = new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ var expRightPromise = evaluate(exp.right, workerScript);
+ expRightPromise.then(function(expRight) {
+ resolve(expRight);
+ });
+ }, CONSTANTS.CodeInstructionRunTime)
+ });
+
+ p.then(function(expRight) {
+ console.log("Right side of assign operation resolved with value: " + expRight);
+ env.set(exp.left.value, expRight);
+ console.log("Assign operation finished");
+ resolve("assignFinished");
+ });
+ });
+
case "binary":
- return apply_op(exp.operator,
- evaluate(exp.left, env),
- evaluate(exp.right, env));
-
+ console.log("Binary operation called");
+ return new Promise(function(resolve, reject) {
+ var pLeft = new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ var promise = evaluate(exp.left, workerScript);
+ promise.then(function(valLeft) {
+ resolve(valLeft);
+ });
+ }, CONSTANTS.CodeInstructionRunTime);
+ });
+
+ pLeft.then(function(valLeft) {
+ var pRight = new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ var promise = evaluate(exp.right, workerScript);
+ promise.then(function(valRight) {
+ resolve([valLeft, valRight]);
+ });
+ }, CONSTANTS.CodeInstructionRunTime);
+ });
+
+ pRight.then(function(args) {
+ console.log("Resolving binary operation");
+ resolve(apply_op(exp.operator, args[0], args[1]));
+ });
+ });
+ });
+ break;
+ //TODO
case "if":
var numConds = exp.cond.length;
var numThens = exp.then.length;
@@ -32,40 +82,51 @@ function evaluate(exp, env) {
}
for (var i = 0; i < numConds; i++) {
- var cond = evaluate(exp.cond[i], env);
- if (cond) return evaluate(exp.then, env);
+ var cond = evaluate(exp.cond[i], workerScript);
+ if (cond) return evaluate(exp.then[i], workerScript);
}
//Evaluate else if it exists, snce none of the conditionals
//were true
- return exp.else ? evaluate(exp.else, env) : false;
+ return exp.else ? evaluate(exp.else, workerScript) : 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
+ return new Promise(function(resolve, reject) {
+ console.log("for loop encountered in evaluator");
+ var pInit = new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ var resInit = evaluate(exp.init, workerScript);
+ resInit.then(function(foo) {
+ resolve(resInit);
+ });
+ }, CONSTANTS.CodeInstructionRunTime);
+ });
+
+ pInit.then(function(expInit) {
+ var pForLoop = evaluateFor(exp, workerScript);
+ pForLoop.then(function(forLoopRes) {
+ resolve("forLoopDone");
+ });
+ });
+ });
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
+ console.log("Evaluating while loop");
+ return new Promise(function(resolve, reject) {
+ var pEvaluateWhile = evaluateWhile(exp, workerScript);
+ pEvaluateWhile.then(function(whileLoopRes) {
+ resolve("whileLoopDone");
+ });
+ });
break;
case "prog":
- var val = false;
- exp.prog.forEach(function(exp){ val = evaluate(exp, env) });
- return val;
+ return new Promise(function(resolve, reject) {
+ var evaluateProgPromise = evaluateProg(exp, workerScript, 0);
+ evaluateProgPromise.then(function(res) {
+ resolve(res);
+ });
+ });
+ break;
/* Currently supported function calls:
* hack()
@@ -79,13 +140,32 @@ function evaluate(exp, 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());
- }
+ return new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ if (exp.func.value == "hack") {
+ console.log("Execute hack()");
+ resolve("hackExecuted");
+ } else if (exp.func.value == "sleep") {
+ console.log("Execute sleep()");
+ resolve("sleepExecuted");
+ } else if (exp.func.value == "print") {
+ var p = new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ var evaluatePromise = evaluate(exp.args[0], workerScript);
+ evaluatePromise.then(function(res) {
+ resolve(res);
+ });
+ }, CONSTANTS.CodeInstructionRunTime);
+ });
+
+ p.then(function(res) {
+ post(res.toString());
+ console.log("Print call executed");
+ resolve("printExecuted");
+ });
+ }
+ }, CONSTANTS.CodeInstructionRunTime);
+ });
break;
default:
@@ -93,6 +173,131 @@ function evaluate(exp, env) {
}
}
+//Evaluate the looping part of a for loop (Initialization block is NOT done in here)
+function evaluateFor(exp, workerScript) {
+ console.log("evaluateFor() called");
+ return new Promise(function(resolve, reject) {
+ var pCond = new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ var evaluatePromise = evaluate(exp.cond, workerScript);
+ evaluatePromise.then(function(resCond) {
+ console.log("Conditional evaluated to: " + resCond);
+ resolve(resCond);
+ });
+ }, CONSTANTS.CodeInstructionRunTime);
+ });
+
+ pCond.then(function(resCond) {
+ if (resCond) {
+ console.log("About to evaluate an iteration of for loop code");
+ //Run the for loop code
+ var pCode = new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ var evaluatePromise = evaluate(exp.code, workerScript);
+ evaluatePromise.then(function(resCode) {
+ console.log("Evaluated an iteration of for loop code");
+ resolve(resCode);
+ });
+ }, CONSTANTS.CodeInstructionRunTime);
+ });
+
+ //After the code executes make a recursive call
+ pCode.then(function(resCode) {
+ var pPostLoop = new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ var evaluatePromise = evaluate(exp.postloop, workerScript);
+ evaluatePromise.then(function(foo) {
+ console.log("Evaluated for loop postloop");
+ resolve("postLoopFinished");
+ });
+ }, CONSTANTS.CodeInstructionRunTime);
+ });
+
+ pPostLoop.then(function(resPostloop) {
+ var recursiveCall = evaluateFor(exp, workerScript);
+ recursiveCall.then(function(foo) {
+ resolve("endForLoop");
+ });
+ });
+
+ });
+ } else {
+ console.log("Cond is false, stopping for loop");
+ resolve("endForLoop"); //Doesn't need to resolve to any particular value
+ }
+ });
+ });
+}
+
+function evaluateWhile(exp, workerScript) {
+ console.log("evaluateWhile() called");
+ return new Promise(function(resolve, reject) {
+ var pCond = new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ var evaluatePromise = evaluate(exp.cond, workerScript);
+ evaluatePromise.then(function(resCond) {
+ console.log("Conditional evaluated to: " + resCond);
+ resolve(resCond);
+ });
+ }, CONSTANTS.CodeInstructionRunTime);
+ });
+
+ pCond.then(function(resCond) {
+ if (resCond) {
+ //Run the while loop code
+ var pCode = new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ var evaluatePromise = evaluate(exp.code, workerScript);
+ evaluatePromise.then(function(resCode) {
+ console.log("Evaluated an iteration of while loop code");
+ resolve(resCode);
+ });
+ }, CONSTANTS.CodeInstructionRunTime);
+ });
+
+ //After the code executes make a recursive call
+ pCode.then(function(resCode) {
+ var recursiveCall = evaluateWhile(exp, workerScript);
+ recursiveCall.then(function(foo) {
+ resolve("endWhileLoop");
+ });
+ });
+ } else {
+ console.log("Cond is false, stopping while loop");
+ resolve("endWhileLoop"); //Doesn't need to resolve to any particular value
+ }
+ });
+ });
+}
+
+function evaluateProg(exp, workerScript, index) {
+ console.log("evaluateProg() called");
+ return new Promise(function(resolve, reject) {
+ if (index >= exp.prog.length) {
+ console.log("Prog done. Resolving recursively");
+ resolve("progFinished");
+ } else {
+ //Evaluate this line of code in the prog
+ var code = new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ var evaluatePromise = evaluate(exp.prog[index], workerScript);
+ evaluatePromise.then(function(evalRes) {
+ resolve(evalRes);
+ });
+ }, CONSTANTS.CodeInstructionRunTime);
+ });
+
+ //After the code finishes evaluating, evaluate the next line recursively
+ code.then(function(codeRes) {
+ var nextLine = evaluateProg(exp, workerScript, index + 1);
+ nextLine.then(function(nextLineRes) {
+ resolve("progDone");
+ });
+ });
+ }
+ });
+}
+
function apply_op(op, a, b) {
function num(x) {
if (typeof x != "number")
@@ -120,4 +325,4 @@ function apply_op(op, a, b) {
case "!=": return a !== b;
}
throw new Error("Can't apply operator " + op);
-}
\ No newline at end of file
+}
diff --git a/src/Netscript/NetscriptWorker.js b/src/Netscript/NetscriptWorker.js
index 855fb7ed0..c9dba3b8a 100644
--- a/src/Netscript/NetscriptWorker.js
+++ b/src/Netscript/NetscriptWorker.js
@@ -1,842 +1,5 @@
-/* Contains the entire implementation (parser and evaluator) of
- * the Netscript language. It needs to be all in one file because the scripts
- * are evaluated using Web Workers, which runs code from a single file */
-
+/* Worker code, contains Netscript scripts that are actually running */
-/* Evaluator
- * Evaluates the Abstract Syntax Tree for Netscript
- * generated by the Parser class
- */
-// Evaluator should return a Promise, so that any call to evaluate() can just
-//wait for that promise to finish before continuing
-function evaluate(exp, workerScript) {
- var env = workerScript.env;
- switch (exp.type) {
- case "num":
- case "str":
- case "bool":
- return new Promise(function(resolve, reject) {
- resolve(exp.value);
- });
- break;
- case "var":
- return new Promise(function(resolve, reject) {
- resolve(env.get(exp.value));
- });
- break;
- //Can currently only assign to "var"s
- case "assign":
- console.log("Evaluating assign operation");
- return new Promise(function(resolve, reject) {
- if (exp.left.type != "var")
- throw new Error("Cannot assign to " + JSON.stringify(exp.left));
-
- var p = new Promise(function(resolve, reject) {
- setTimeout(function() {
- var expRightPromise = evaluate(exp.right, workerScript);
- expRightPromise.then(function(expRight) {
- resolve(expRight);
- });
- }, CONSTANTS.CodeInstructionRunTime)
- });
-
- p.then(function(expRight) {
- console.log("Right side of assign operation resolved with value: " + expRight);
- env.set(exp.left.value, expRight);
- console.log("Assign operation finished");
- resolve("assignFinished");
- });
- });
-
- case "binary":
- console.log("Binary operation called");
- return new Promise(function(resolve, reject) {
- var pLeft = new Promise(function(resolve, reject) {
- setTimeout(function() {
- var promise = evaluate(exp.left, workerScript);
- promise.then(function(valLeft) {
- resolve(valLeft);
- });
- }, CONSTANTS.CodeInstructionRunTime);
- });
-
- pLeft.then(function(valLeft) {
- var pRight = new Promise(function(resolve, reject) {
- setTimeout(function() {
- var promise = evaluate(exp.right, workerScript);
- promise.then(function(valRight) {
- resolve([valLeft, valRight]);
- });
- }, CONSTANTS.CodeInstructionRunTime);
- });
-
- pRight.then(function(args) {
- console.log("Resolving binary operation");
- resolve(apply_op(exp.operator, args[0], args[1]));
- });
- });
- });
- break;
-
- //TODO
- 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], workerScript);
- if (cond) return evaluate(exp.then[i], workerScript);
- }
-
- //Evaluate else if it exists, snce none of the conditionals
- //were true
- return exp.else ? evaluate(exp.else, workerScript) : false;
-
- case "for":
- return new Promise(function(resolve, reject) {
- console.log("for loop encountered in evaluator");
- var pInit = new Promise(function(resolve, reject) {
- setTimeout(function() {
- var resInit = evaluate(exp.init, workerScript);
- resInit.then(function(foo) {
- resolve(resInit);
- });
- }, CONSTANTS.CodeInstructionRunTime);
- });
-
- pInit.then(function(expInit) {
- var pForLoop = evaluateFor(exp, workerScript);
- pForLoop.then(function(forLoopRes) {
- resolve("forLoopDone");
- });
- });
- });
- break;
- case "while":
- console.log("Evaluating while loop");
- return new Promise(function(resolve, reject) {
- var pEvaluateWhile = evaluateWhile(exp, workerScript);
- pEvaluateWhile.then(function(whileLoopRes) {
- resolve("whileLoopDone");
- });
- });
- break;
- case "prog":
- return new Promise(function(resolve, reject) {
- var evaluateProgPromise = evaluateProg(exp, workerScript, 0);
- evaluateProgPromise.then(function(res) {
- resolve(res);
- });
- });
- break;
-
- /* 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);
- //}));
- return new Promise(function(resolve, reject) {
- setTimeout(function() {
- if (exp.func.value == "hack") {
- console.log("Execute hack()");
- resolve("hackExecuted");
- } else if (exp.func.value == "sleep") {
- console.log("Execute sleep()");
- resolve("sleepExecuted");
- } else if (exp.func.value == "print") {
- var p = new Promise(function(resolve, reject) {
- setTimeout(function() {
- var evaluatePromise = evaluate(exp.args[0], workerScript);
- evaluatePromise.then(function(res) {
- resolve(res);
- });
- }, CONSTANTS.CodeInstructionRunTime);
- });
-
- p.then(function(res) {
- post(res.toString());
- console.log("Print call executed");
- resolve("printExecuted");
- });
- }
- }, CONSTANTS.CodeInstructionRunTime);
- });
- break;
-
- default:
- throw new Error("I don't know how to evaluate " + exp.type);
- }
-}
-
-//Evaluate the looping part of a for loop (Initialization block is NOT done in here)
-function evaluateFor(exp, workerScript) {
- console.log("evaluateFor() called");
- return new Promise(function(resolve, reject) {
- var pCond = new Promise(function(resolve, reject) {
- setTimeout(function() {
- var evaluatePromise = evaluate(exp.cond, workerScript);
- evaluatePromise.then(function(resCond) {
- console.log("Conditional evaluated to: " + resCond);
- resolve(resCond);
- });
- }, CONSTANTS.CodeInstructionRunTime);
- });
-
- pCond.then(function(resCond) {
- if (resCond) {
- console.log("About to evaluate an iteration of for loop code");
- //Run the for loop code
- var pCode = new Promise(function(resolve, reject) {
- setTimeout(function() {
- var evaluatePromise = evaluate(exp.code, workerScript);
- evaluatePromise.then(function(resCode) {
- console.log("Evaluated an iteration of for loop code");
- resolve(resCode);
- });
- }, CONSTANTS.CodeInstructionRunTime);
- });
-
- //After the code executes make a recursive call
- pCode.then(function(resCode) {
- var pPostLoop = new Promise(function(resolve, reject) {
- setTimeout(function() {
- var evaluatePromise = evaluate(exp.postloop, workerScript);
- evaluatePromise.then(function(foo) {
- console.log("Evaluated for loop postloop");
- resolve("postLoopFinished");
- });
- }, CONSTANTS.CodeInstructionRunTime);
- });
-
- pPostLoop.then(function(resPostloop) {
- var recursiveCall = evaluateFor(exp, workerScript);
- recursiveCall.then(function(foo) {
- resolve("endForLoop");
- });
- });
-
- });
- } else {
- console.log("Cond is false, stopping for loop");
- resolve("endForLoop"); //Doesn't need to resolve to any particular value
- }
- });
- });
-}
-
-function evaluateWhile(exp, workerScript) {
- console.log("evaluateWhile() called");
- return new Promise(function(resolve, reject) {
- var pCond = new Promise(function(resolve, reject) {
- setTimeout(function() {
- var evaluatePromise = evaluate(exp.cond, workerScript);
- evaluatePromise.then(function(resCond) {
- console.log("Conditional evaluated to: " + resCond);
- resolve(resCond);
- });
- }, CONSTANTS.CodeInstructionRunTime);
- });
-
- pCond.then(function(resCond) {
- if (resCond) {
- //Run the while loop code
- var pCode = new Promise(function(resolve, reject) {
- setTimeout(function() {
- var evaluatePromise = evaluate(exp.code, workerScript);
- evaluatePromise.then(function(resCode) {
- console.log("Evaluated an iteration of while loop code");
- resolve(resCode);
- });
- }, CONSTANTS.CodeInstructionRunTime);
- });
-
- //After the code executes make a recursive call
- pCode.then(function(resCode) {
- var recursiveCall = evaluateWhile(exp, workerScript);
- recursiveCall.then(function(foo) {
- resolve("endWhileLoop");
- });
- });
- } else {
- console.log("Cond is false, stopping while loop");
- resolve("endWhileLoop"); //Doesn't need to resolve to any particular value
- }
- });
- });
-}
-
-function evaluateProg(exp, workerScript, index) {
- console.log("evaluateProg() called");
- return new Promise(function(resolve, reject) {
- if (index >= exp.prog.length) {
- console.log("Prog done. Resolving recursively");
- resolve("progFinished");
- } else {
- //Evaluate this line of code in the prog
- var code = new Promise(function(resolve, reject) {
- setTimeout(function() {
- var evaluatePromise = evaluate(exp.prog[index], workerScript);
- evaluatePromise.then(function(evalRes) {
- resolve(evalRes);
- });
- }, CONSTANTS.CodeInstructionRunTime);
- });
-
- //After the code finishes evaluating, evaluate the next line recursively
- code.then(function(codeRes) {
- var nextLine = evaluateProg(exp, workerScript, index + 1);
- nextLine.then(function(nextLineRes) {
- resolve("progDone");
- });
- });
- }
- });
-}
-
-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);
-}
-
-
-
-/* Environment
- * NetScript program environment
- */
-function Environment(parent) {
- this.vars = Object.create(parent ? parent.vars : null);
- this.parent = parent;
-}
-Environment.prototype = {
- //Create a "subscope", which is a new new "sub-environment"
- //The subscope is linked to this through its parent variable
- extend: function() {
- return new Environment(this);
- },
-
- //Finds the scope where the variable with the given name is defined
- lookup: function(name) {
- var scope = this;
- while (scope) {
- if (Object.prototype.hasOwnProperty.call(scope.vars, name))
- return scope;
- scope = scope.parent;
- }
- },
-
- //Get the current value of a variable
- get: function(name) {
- if (name in this.vars)
- return this.vars[name];
- throw new Error("Undefined variable " + name);
- },
-
- //Sets the value of a variable in any scope
- set: function(name, value) {
- var scope = this.lookup(name);
- // let's not allow defining globals from a nested environment
- //
- // If scope is null (aka existing variable with name could not be found)
- // and this is NOT the global scope, throw error
- if (!scope && this.parent)
- throw new Error("Undefined variable " + name);
- return (scope || this).vars[name] = value;
- },
-
- //Creates (or overwrites) a variable in the current scope
- def: function(name, value) {
- return this.vars[name] = value;
- }
-};
-
-
-
-
-/* Parser
- * Creates Abstract Syntax Tree Nodes
- * Operates on a stream of tokens from the Tokenizer
- */
-
-var FALSE = {type: "bool", value: false};
-
-function Parser(input) {
- var PRECEDENCE = {
- "=": 1,
- "||": 2,
- "&&": 3,
- "<": 7, ">": 7, "<=": 7, ">=": 7, "==": 7, "!=": 7,
- "+": 10, "-": 10,
- "*": 20, "/": 20, "%": 20,
- };
- return parse_toplevel();
-
- //Returns true if the next token is a punc type with value ch
- function is_punc(ch) {
- var tok = input.peek();
- return tok && tok.type == "punc" && (!ch || tok.value == ch) && tok;
- }
-
- //Returns true if the next token is the kw keyword
- function is_kw(kw) {
- var tok = input.peek();
- return tok && tok.type == "kw" && (!kw || tok.value == kw) && tok;
- }
-
- //Returns true if the next token is an op type with the given op value
- function is_op(op) {
- var tok = input.peek();
- return tok && tok.type == "op" && (!op || tok.value == op) && tok;
- }
-
- //Checks that the next character is the given punctuation character and throws
- //an error if it's not. If it is, skips over it in the input
- function checkPuncAndSkip(ch) {
- if (is_punc(ch)) input.next();
- else input.croak("Expecting punctuation: \"" + ch + "\"");
- }
-
- //Checks that the next character is the given keyword and throws an error
- //if its not. If it is, skips over it in the input
- function checkKeywordAndSkip(kw) {
- if (is_kw(kw)) input.next();
- else input.croak("Expecting keyword: \"" + kw + "\"");
- }
-
- //Checks that the next character is the given operator and throws an error
- //if its not. If it is, skips over it in the input
- function checkOpAndSkip(op) {
- if (is_op(op)) input.next();
- else input.croak("Expecting operator: \"" + op + "\"");
- }
-
- function unexpected() {
- input.croak("Unexpected token: " + JSON.stringify(input.peek()));
- }
-
- function maybe_binary(left, my_prec) {
- var tok = is_op();
- if (tok) {
- var his_prec = PRECEDENCE[tok.value];
- if (his_prec > my_prec) {
- input.next();
- return maybe_binary({
- type : tok.value == "=" ? "assign" : "binary",
- operator : tok.value,
- left : left,
- right : maybe_binary(parse_atom(), his_prec)
- }, my_prec);
- }
- }
- return left;
- }
-
- function delimited(start, stop, separator, parser) {
- var a = [], first = true;
- checkPuncAndSkip(start);
- while (!input.eof()) {
- if (is_punc(stop)) break;
- if (first) first = false; else checkPuncAndSkip(separator);
- if (is_punc(stop)) break;
- a.push(parser());
- }
- checkPuncAndSkip(stop);
- return a;
- }
-
- function parse_call(func) {
- return {
- type: "call",
- func: func,
- args: delimited("(", ")", ",", parse_expression),
- };
- }
-
- function parse_varname() {
- var name = input.next();
- if (name.type != "var") input.croak("Expecting variable name");
- return name.value;
- }
-
- /* type: "if",
- * cond: [ {"type": "var", "value": "cond1"}, {"type": "var", "value": "cond2"}...]
- * then: [ {"type": "var", "value": "then1"}, {"type": "var", "value": "then2"}...]
- * else: {"type": "var", "value": "foo"}
- */
- function parse_if() {
- console.log("Parsing if token");
- checkKeywordAndSkip("if");
-
- //Conditional
- var cond = parse_expression();
-
- //Body
- var then = parse_expression();
- var ret = {
- type: "if",
- cond: [],
- then: [],
- };
- ret.cond.push(cond);
- ret.then.push(then);
-
- // Parse all elif branches
- while (is_kw("elif")) {
- input.next();
- var cond = parse_expression();
- var then = parse_expression();
- ret.cond.push(cond);
- ret.then.push(then);
- }
-
- // Parse else branch, if it exists
- if (is_kw("else")) {
- input.next();
- ret.else = parse_expression();
- }
-
- return ret;
- }
-
- /* for (init, cond, postloop) {code;}
- *
- * type: "for",
- * init: assign node,
- * cond: var node,
- * postloop: assign node
- * code: prog node
- */
- function parse_for() {
- console.log("Parsing for token");
- checkKeywordAndSkip("for");
-
- splitExpressions = delimited("(", ")", ";", parse_expression);
- console.log("Parsing code in for loop");
- code = parse_expression();
-
- if (splitExpressions.length != 3) {
- throw new Error("for statement has incorrect number of arugments");
- }
-
- //TODO Check type of the init, cond, and postloop nodes
- return {
- type: "for",
- init: splitExpressions[0],
- cond: splitExpressions[1],
- postloop: splitExpressions[2],
- code: code
- }
- }
-
- /* while (cond) {}
- *
- * type: "while",
- * cond: var node
- * code: prog node
- */
- function parse_while() {
- console.log("Parsing while token");
- checkKeywordAndSkip("while");
-
- var cond = parse_expression();
- var code = parse_expression();
- return {
- type: "while",
- cond: cond,
- code: code
- }
-
- }
-
- function parse_bool() {
- return {
- type : "bool",
- value : input.next().value == "true"
- };
- }
-
- function maybe_call(expr) {
- expr = expr();
- return is_punc("(") ? parse_call(expr) : expr;
- }
-
- function parse_atom() {
- return maybe_call(function(){
- if (is_punc("(")) {
- input.next();
- var exp = parse_expression();
- checkPuncAndSkip(")");
- return exp;
- }
- if (is_punc("{")) return parse_prog();
- if (is_kw("if")) return parse_if();
- if (is_kw("for")) return parse_for();
- if (is_kw("while")) return parse_while();
- //Note, let for loops be function calls (call node types)
- if (is_kw("true") || is_kw("false")) return parse_bool();
-
- var tok = input.next();
- if (tok.type == "var" || tok.type == "num" || tok.type == "str")
- return tok;
- unexpected();
- });
- }
-
- function parse_toplevel() {
- var prog = [];
- while (!input.eof()) {
- prog.push(parse_expression());
- if (!input.eof()) checkPuncAndSkip(";");
- }
- //Return the top level Abstract Syntax Tree, where the top node is a "prog" node
- return { type: "prog", prog: prog };
- }
-
- function parse_prog() {
- console.log("Parsing prog token");
- var prog = delimited("{", "}", ";", parse_expression);
- if (prog.length == 0) return FALSE;
- if (prog.length == 1) return prog[0];
- return { type: "prog", prog: prog };
- }
-
- function parse_expression() {
- return maybe_call(function(){
- return maybe_binary(parse_atom(), 0);
- });
- }
-}
-
-
- /* Tokenizer
- * Acts on top of the InputStream class. Takes in a character input stream and and parses it into tokens.
- * Tokens can be accessed with peek() and next().
- *
- * Token types:
- * {type: "punc", value: "(" } // punctuation: parens, comma, semicolon etc.
- * {type: "num", value: 5 } // numbers (including floats)
- * {type: "str", value: "Hello World!" } // strings
- * {type: "kw", value: "for/if/" } // keywords, see defs below
- * {type: "var", value: "a" } // identifiers/variables
- * {type: "op", value: "!=" } // operator characters
- * {type: "bool", value: "true" } // Booleans
- *
- */
-
-function Tokenizer(input) {
- var current = null;
- var keywords = " if elif else true false while for ";
-
- return {
- next : next,
- peek : peek,
- eof : eof,
- croak : input.croak
- }
-
- function is_keyword(x) {
- return keywords.indexOf(" " + x + " ") >= 0;
- }
-
- function is_digit(ch) {
- return /[0-9]/i.test(ch);
- }
-
- //An identifier can start with any letter or an underscore
- function is_id_start(ch) {
- return /[a-z_]/i.test(ch);
- }
-
- function is_id(ch) {
- return is_id_start(ch) || "?!-<>=0123456789".indexOf(ch) >= 0;
- }
-
- function is_op_char(ch) {
- return "+-*/%=&|<>!".indexOf(ch) >= 0;
- }
-
- function is_punc(ch) {
- return ",;(){}[]".indexOf(ch) >= 0;
- }
-
- function is_whitespace(ch) {
- return " \t\n".indexOf(ch) >= 0;
- }
-
- function read_while(predicate) {
- var str = "";
- while (!input.eof() && predicate(input.peek()))
- str += input.next();
- return str;
- }
-
- function read_number() {
- var has_dot = false;
- //Reads the number from the input. Checks for only a single decimal point
- var number = read_while(function(ch){
- if (ch == ".") {
- if (has_dot) return false;
- has_dot = true;
- return true;
- }
- return is_digit(ch);
- });
- return { type: "num", value: parseFloat(number) };
- }
-
- //This function also checks the identifier against a list of known keywords (defined at the top)
- //and will return a kw object rather than identifier if it is one
- function read_ident() {
- //Identifier must start with a letter or underscore..and can contain anything from ?!-<>=0123456789
- var id = read_while(is_id);
- return {
- type : is_keyword(id) ? "kw" : "var",
- value : id
- };
- }
-
- function read_escaped(end) {
- var escaped = false, str = "";
- input.next(); //Skip the quotation mark
- while (!input.eof()) {
- var ch = input.next();
- if (escaped) {
- str += ch;
- escaped = false;
- } else if (ch == "\\") {
- escaped = true;
- } else if (ch == end) {
- break;
- } else {
- str += ch;
- }
- }
- return str;
- }
-
- function read_string(ch) {
- if (ch == '"') {
- return { type: "str", value: read_escaped('"') };
- } else if (ch == '\'') {
- return { type: "str", value: read_escaped('\'') };
- }
- }
-
- //Only supports single-line comments right now
- function skip_comment() {
- read_while(function(ch){ return ch != "\n" });
- input.next();
- }
-
- //Gets the next token
- function read_next() {
- //Skip over whitespace
- read_while(is_whitespace);
-
- if (input.eof()) return null;
-
- //Peek the next character and decide what to do based on what that
- //next character is
- var ch = input.peek();
-
- if (ch == "//") {
- skip_comment();
- return read_next();
- }
-
- if (ch == '"' || ch == '\'') return read_string(ch);
- if (is_digit(ch)) return read_number();
- if (is_id_start(ch)) return read_ident();
- if (is_punc(ch)) return {
- type : "punc",
- value : input.next()
- }
- if (is_op_char(ch)) return {
- type : "op",
- value : read_while(is_op_char)
- }
-
- }
-
- function peek() {
- //Returns current token, unless its null in which case it grabs the next one
- //and returns it
- return current || (current = read_next());
- }
-
- function next() {
- //The token might have been peaked already, in which case read_next() was already
- //called so just return current
- var tok = current;
- current = null;
- return tok || read_next();
- }
-
- function eof() {
- return peek() == null;
- }
-}
-
-
-/* InputStream class. Creates a "stream object" that provides operations to read
-* from a string. */
-function InputStream(input) {
- var pos = 0, line = 1, col = 0;
- return {
- next : next,
- peek : peek,
- eof : eof,
- croak : croak,
- };
- function next() {
- var ch = input.charAt(pos++);
- if (ch == "\n") line++, col = 0; else col++;
- return ch;
- }
- function peek() {
- return input.charAt(pos);
- }
- function eof() {
- return peek() == "";
- }
- function croak(msg) {
- throw new Error(msg + " (" + line + ":" + col + ")");
- }
-}
-
/* Actual Worker Code */
function WorkerScript() {
this.name = "";
@@ -847,6 +10,7 @@ function WorkerScript() {
this.timeout = null;
}
+//Array containing all scripts that are running across all servers, to easily run them all
var workerScripts = [];
//Loop through workerScripts and run every script that is not currently running