diff --git a/index.html b/index.html index fc45fd60a..bf7678393 100644 --- a/index.html +++ b/index.html @@ -6,6 +6,25 @@ + + + + + + + + + + + + + + + + + + - - + - - - - + \ No newline at end of file diff --git a/src/Company.js b/src/Company.js index 09f984abb..6e8d66ed3 100644 --- a/src/Company.js +++ b/src/Company.js @@ -12,17 +12,17 @@ function Company() { }; -Company.prototype.init(name, salaryMult, expMult) { +Company.prototype.init = function(name, salaryMult, expMult) { this.companyName = name; this.salaryMult = salaryMult; this.expMult = expMult; } -Company.prototype.addPosition(pos) { +Company.prototype.addPosition = function(pos) { this.companyPositions.push(pos); } -Company.prototype.addPositions(positions) { +Company.prototype.addPositions = function(positions) { for (var i = 0; i < positions.length; i++) { this.addPosition(positions[i]); } @@ -51,7 +51,7 @@ function CompanyPosition(name, reqHack, reqStr, reqDef, reqDex, reqAgi, reqCha, // //NOTE: These parameters should total to 100, such that each parameter represents a "weighting" of how // important that stat/skill is for the job -CompanyPosition.prototype.setPerformanceParameters(hackEff, strEff, defEff, dexEff, agiEff, chaEff) { +CompanyPosition.prototype.setPerformanceParameters = function(hackEff, strEff, defEff, dexEff, agiEff, chaEff) { if (hackEff + strEff + defEff + dexEff + agiEff + chaEff != 100) { console.log("CompanyPosition.setPerformanceParameters() arguments do not total to 100"); return; @@ -66,7 +66,7 @@ CompanyPosition.prototype.setPerformanceParameters(hackEff, strEff, defEff, dexE //Set the stat/skill experience a Player should gain for working at a CompanyPosition. The experience is per game loop (200 ms) //These will be constant for a single position, but is affected by a company-specific multiplier -CompanyPosition.prototype.setExperienceGains(hack, str, def, dex, agi, cha) { +CompanyPosition.prototype.setExperienceGains = function(hack, str, def, dex, agi, cha) { this.hackingExpGain = hack; this.strengthExpGain = str; this.defenseExpGain = def; @@ -77,7 +77,7 @@ CompanyPosition.prototype.setExperienceGains(hack, str, def, dex, agi, cha) { //Calculate a player's effectiveness at a certain job. Returns the amount of job reputation //that should be gained every game loop (200 ms) -CompanyPosition.prototype.calculateJobPerformance(hacking, str, def, dex, agi, cha) { +CompanyPosition.prototype.calculateJobPerformance = function(hacking, str, def, dex, agi, cha) { var hackRatio = this.hackingEffectiveness * hacking / CONSTANTS.MaxSkillLevel; var strRatio = this.strengthEffectiveness * str / CONSTANTS.MaxSkillLevel; var defRatio = this.defenseEffectiveness * def / CONSTANTS.MaxSkillLevel; @@ -130,69 +130,69 @@ CompanyPositions = { init: function() { //Argument order: hack, str, def, dex, agi, cha //Software - SoftwareIntern.setPerformanceParameters(90, 0, 0, 0, 0, 10); - SoftwareIntern.setExperienceGains(.1, 0, 0, 0, 0, .02); - JuniorDev.setPerformanceParameters(85, 0, 0, 0, 0, 15); - JuniorDev.setExperienceGains(.2, 0, 0, 0, 0, .04); - SeniorDev.setPerformanceParameters(75, 0, 0, 0, 0, 25); - SeniorDev.setExperienceGains(.4, 0, 0, 0, 0, .08); - LeadDev.setPerformanceParameters(70, 0, 0, 0, 0, 30); - LeadDev.setExperienceGains(.5, 0, 0, 0, 0, .1); + CompanyPositions.SoftwareIntern.setPerformanceParameters(90, 0, 0, 0, 0, 10); + CompanyPositions.SoftwareIntern.setExperienceGains(.1, 0, 0, 0, 0, .02); + CompanyPositions.JuniorDev.setPerformanceParameters(85, 0, 0, 0, 0, 15); + CompanyPositions.JuniorDev.setExperienceGains(.2, 0, 0, 0, 0, .04); + CompanyPositions.SeniorDev.setPerformanceParameters(75, 0, 0, 0, 0, 25); + CompanyPositions.SeniorDev.setExperienceGains(.4, 0, 0, 0, 0, .08); + CompanyPositions.LeadDev.setPerformanceParameters(70, 0, 0, 0, 0, 30); + CompanyPositions.LeadDev.setExperienceGains(.5, 0, 0, 0, 0, .1); //Security - ITIntern.setPerformanceParameters(90, 0, 0, 0, 0, 10); - ITIntern.setExperienceGains(.05, 0, 0, 0, 0, .01); - ITAnalyst.setPerformanceParameters(85, 0, 0, 0, 0, 15); - ITAnalyst.setExperienceGains(.15, 0, 0, 0, 0, .02); - ITManager.setPerformanceParameters(75, 0, 0, 0, 0, 25); - ITManager.setExperienceGains(.4, 0, 0, 0, 0, .1); - SysAdmin.setPerformanceParameters(80, 0, 0, 0, 0, 20); - SysAdmin.setExperienceGains(.5, 0, 0, 0, 0, .05); - SecurityEngineer.setPerformanceParameters(85, 0, 0, 0, 0, 15); - SecurityEngineer.setExperienceGains(0.4, 0, 0, 0, 0, .05); - NetworkEngineer.setPerformanceParameters(85, 0, 0, 0, 0, 15); - NetworkEngineer.setExperienceGains(0.4, 0, 0, 0, 0, .05); - NetworkAdministrator.setPerformanceParameters(75, 0, 0, 0, 0, 25); - NetworkAdministrator.setExperienceGains(0.5, 0, 0, 0, 0, .1); + CompanyPositions.ITIntern.setPerformanceParameters(90, 0, 0, 0, 0, 10); + CompanyPositions.ITIntern.setExperienceGains(.05, 0, 0, 0, 0, .01); + CompanyPositions.ITAnalyst.setPerformanceParameters(85, 0, 0, 0, 0, 15); + CompanyPositions.ITAnalyst.setExperienceGains(.15, 0, 0, 0, 0, .02); + CompanyPositions.ITManager.setPerformanceParameters(75, 0, 0, 0, 0, 25); + CompanyPositions.ITManager.setExperienceGains(.4, 0, 0, 0, 0, .1); + CompanyPositions.SysAdmin.setPerformanceParameters(80, 0, 0, 0, 0, 20); + CompanyPositions.SysAdmin.setExperienceGains(.5, 0, 0, 0, 0, .05); + CompanyPositions.SecurityEngineer.setPerformanceParameters(85, 0, 0, 0, 0, 15); + CompanyPositions.SecurityEngineer.setExperienceGains(0.4, 0, 0, 0, 0, .05); + CompanyPositions.NetworkEngineer.setPerformanceParameters(85, 0, 0, 0, 0, 15); + CompanyPositions.NetworkEngineer.setExperienceGains(0.4, 0, 0, 0, 0, .05); + CompanyPositions.NetworkAdministrator.setPerformanceParameters(75, 0, 0, 0, 0, 25); + CompanyPositions.NetworkAdministrator.setExperienceGains(0.5, 0, 0, 0, 0, .1); //Technology management - HeadOfSoftware.setPerformanceParameters(65, 0, 0, 0, 0, 35); - HeadOfSoftware.setExperienceGains(1, 0, 0, 0, 0, .5); - HeadOfEngineering.setPerformanceParameters(60, 0, 0, 0, 0, 40); - HeadOfEngineering.setExperienceGains(1.1, 0, 0, 0, 0, .5); - VicePresident.setPerformanceParameters(60, 0, 0, 0, 0, 40); - VicePresident.setExperienceGains(1.2, 0, 0, 0, 0, .6); - CTO.setPerformanceParameters(50, 0, 0, 0, 0, 50); - CTO.setExperienceGains(1.5, 0, 0, 0, 1); + CompanyPositions.HeadOfSoftware.setPerformanceParameters(65, 0, 0, 0, 0, 35); + CompanyPositions.HeadOfSoftware.setExperienceGains(1, 0, 0, 0, 0, .5); + CompanyPositions.HeadOfEngineering.setPerformanceParameters(60, 0, 0, 0, 0, 40); + CompanyPositions.HeadOfEngineering.setExperienceGains(1.1, 0, 0, 0, 0, .5); + CompanyPositions.VicePresident.setPerformanceParameters(60, 0, 0, 0, 0, 40); + CompanyPositions.VicePresident.setExperienceGains(1.2, 0, 0, 0, 0, .6); + CompanyPositions.CTO.setPerformanceParameters(50, 0, 0, 0, 0, 50); + CompanyPositions.CTO.setExperienceGains(1.5, 0, 0, 0, 1); //Business - BusinessIntern.setPerformanceParameters(10, 0, 0, 0, 0, 90); - BusinessIntern.setExperienceGains(.01, 0, 0, 0, 0, .1); - BusinessAnalyst.setPerformanceParameters(20, 0, 0, 0, 0, 80); - BusinessAnalyst.setExperienceGains(.02, 0, 0, 0, 0, .2); - BusinessManager.setPerformanceParameters(15, 0, 0, 0, 0, 85); - BusinessManager.setExperienceGains(.02, 0, 0, 0, 0, .4); - OperationsManager.setPerformanceParameters(15, 0, 0, 0, 0, 85); - OperationsManager.setExperienceGains(.02, 0, 0, 0, 0, .4); - CFO.setPerformanceParameters(10, 0, 0, 0, 0, 90); - CFO.setExperienceGains(.05, 0, 0, 0, 0, 1); - CEO.setPerformanceParameters(10, 0, 0, 0, 0, 90); - CEO.setExperienceGains(.1, 0, 0, 0, 0, 1.5); + CompanyPositions.BusinessIntern.setPerformanceParameters(10, 0, 0, 0, 0, 90); + CompanyPositions.BusinessIntern.setExperienceGains(.01, 0, 0, 0, 0, .1); + CompanyPositions.BusinessAnalyst.setPerformanceParameters(20, 0, 0, 0, 0, 80); + CompanyPositions.BusinessAnalyst.setExperienceGains(.02, 0, 0, 0, 0, .2); + CompanyPositions.BusinessManager.setPerformanceParameters(15, 0, 0, 0, 0, 85); + CompanyPositions.BusinessManager.setExperienceGains(.02, 0, 0, 0, 0, .4); + CompanyPositions.OperationsManager.setPerformanceParameters(15, 0, 0, 0, 0, 85); + CompanyPositions.OperationsManager.setExperienceGains(.02, 0, 0, 0, 0, .4); + CompanyPositions.CFO.setPerformanceParameters(10, 0, 0, 0, 0, 90); + CompanyPositions.CFO.setExperienceGains(.05, 0, 0, 0, 0, 1); + CompanyPositions.CEO.setPerformanceParameters(10, 0, 0, 0, 0, 90); + CompanyPositions.CEO.setExperienceGains(.1, 0, 0, 0, 0, 1.5); //Non-tech/management jobs //TODO These parameters might need to be balanced - Waiter.setPerformanceParameters(0, 10, 0, 10, 10, 70); - Waiter.setExperienceGains(0, .01, 0, .01, .01, .05); - SecurityGuard.setPerformanceParameters(5, 20, 20, 20, 20, 15); - SecurityGuard.setExperienceGains(.01, .02, .02, .02, .02, .01); - PoliceOfficer.setPerformanceParameters(5, 20, 20, 20, 20, 15); - PoliceOfficer.setExperienceGains(.01, .04, .04, .04, .04, .02); - SecurityOfficer.setPerformanceParameters(10, 20, 20, 20, 20, 10); - SecurityOfficer.setExperienceGains(.02, .06, .06, .06, .06, .04); - SecuritySupervisor.setPerformanceParameters(10, 15, 15, 15, 15, 30); - SecuritySupervisor.setExperienceGains(.02, .06, .06, .06, .06, .08); - HeadOfSecurity.setPerformanceParameters(10, 15, 15, 15, 15, 30); - HeadOfSecurity.setExperienceGains(.05, .1, .1, .1, .1, .1); + CompanyPositions.Waiter.setPerformanceParameters(0, 10, 0, 10, 10, 70); + CompanyPositions.Waiter.setExperienceGains(0, .01, 0, .01, .01, .05); + CompanyPositions.SecurityGuard.setPerformanceParameters(5, 20, 20, 20, 20, 15); + CompanyPositions.SecurityGuard.setExperienceGains(.01, .02, .02, .02, .02, .01); + CompanyPositions.PoliceOfficer.setPerformanceParameters(5, 20, 20, 20, 20, 15); + CompanyPositions.PoliceOfficer.setExperienceGains(.01, .04, .04, .04, .04, .02); + CompanyPositions.SecurityOfficer.setPerformanceParameters(10, 20, 20, 20, 20, 10); + CompanyPositions.SecurityOfficer.setExperienceGains(.02, .06, .06, .06, .06, .04); + CompanyPositions.SecuritySupervisor.setPerformanceParameters(10, 15, 15, 15, 15, 30); + CompanyPositions.SecuritySupervisor.setExperienceGains(.02, .06, .06, .06, .06, .08); + CompanyPositions.HeadOfSecurity.setPerformanceParameters(10, 15, 15, 15, 15, 30); + CompanyPositions.HeadOfSecurity.setExperienceGains(.05, .1, .1, .1, .1, .1); } } @@ -242,7 +242,7 @@ Companies = { SysCoreSecurities: new Company(), CompuTek: new Company(), NetLinkTechnologies: new Company(), - CarmichaelSecurity: new Company(); + CarmichaelSecurity: new Company(), //"Low level" companies FoodNStuff: new Company(), diff --git a/src/Constants.js b/src/Constants.js index 40ae5950b..805c49c40 100644 --- a/src/Constants.js +++ b/src/Constants.js @@ -1,5 +1,5 @@ CONSTANTS = { //Max level for any skill. Determined by max numerical value in javascript and the skill level //formula in Player.js - MaxSkillLevel: 1796; + MaxSkillLevel: 1796, } \ No newline at end of file diff --git a/src/Faction.js b/src/Faction.js index d551cde17..f4c3a0f32 100644 --- a/src/Faction.js +++ b/src/Faction.js @@ -9,11 +9,11 @@ function Faction(name) { }; //TODO -Faction.prototype.init() { +Faction.prototype.init = function() { } -Faction.prototype.setAugmentations(augs) { +Faction.prototype.setAugmentations = function(augs) { for (var i = 0; i < augs.length; i++) { this.augmentations.push(augs[i]); } @@ -22,33 +22,33 @@ Faction.prototype.setAugmentations(augs) { Factions = { //TODO Saving this for later, IShima is a kinda cool name maybe use it for something //Endgame - Illuminati: new Faction("Illuminati"); - Daedalus: new Faction("Daedalus"); - Covenant: new Faction("The Covenant"); + Illuminati: new Faction("Illuminati"), + Daedalus: new Faction("Daedalus"), + Covenant: new Faction("The Covenant"), //Megacorporations, each forms its own faction - ECorp: new Faction("ECorp"); - MegaCorp: new Faction("MegaCorp"); - BachmanAndAssociates: new Faction("Bachman & Associates"); - BladeIndustries: new Faction("Blade Industries"); - NWO: new Faction("NWO"); - ClarkeIncorporated: new Faction("Clarke Incorporated"); - OmniTekIncorporated: new Faction("OmniTek Incorporated"); - FourSigma: new Faction("Four Sigma"); - KuaiGongInternational: new Faction("KuaiGong International"); + ECorp: new Faction("ECorp"), + MegaCorp: new Faction("MegaCorp"), + BachmanAndAssociates: new Faction("Bachman & Associates"), + BladeIndustries: new Faction("Blade Industries"), + NWO: new Faction("NWO"), + ClarkeIncorporated: new Faction("Clarke Incorporated"), + OmniTekIncorporated: new Faction("OmniTek Incorporated"), + FourSigma: new Faction("Four Sigma"), + KuaiGongInternational: new Faction("KuaiGong International"), //Hacker groups - BitRunners: new Faction("BitRunners"); - BlackHand: new Faction("The Black Hand"); - NiteSec: new Faction("NiteSec"); + BitRunners: new Faction("BitRunners"), + BlackHand: new Faction("The Black Hand"), + NiteSec: new Faction("NiteSec"), //City factions, essentially governments - Chongqing: new Faction("Chongqing"); - Sector12: new Faction("Sector-12"); - HongKong: new Faction("New Tokyo"); - Aevum: new Faction("Aevum"); - Ishima: new Faction("Ishima"); - Volhaven: new Faction("Volhaven"); + Chongqing: new Faction("Chongqing"), + Sector12: new Faction("Sector-12"), + HongKong: new Faction("New Tokyo"), + Aevum: new Faction("Aevum"), + Ishima: new Faction("Ishima"), + Volhaven: new Faction("Volhaven"), //Criminal Organizations/Gangs diff --git a/src/Netscript/Environment.js b/src/Netscript/Environment.js new file mode 100644 index 000000000..09c060315 --- /dev/null +++ b/src/Netscript/Environment.js @@ -0,0 +1,48 @@ +/* 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; + } +}; \ No newline at end of file diff --git a/src/Netscript/Evaluator.js b/src/Netscript/Evaluator.js new file mode 100644 index 000000000..1cda28c9b --- /dev/null +++ b/src/Netscript/Evaluator.js @@ -0,0 +1,124 @@ +/* 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 Return somethin? + break; + case "while": + cond = evaluate(exp.cond, env); + + while (cond) { + evaluate(exp.code, env); + cond = evaluate(exp.cond, env); + } + + //TODO DO i need to return anything? + 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") { + console.log(evaluate(exp.args[0], env)); + } + 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); +} \ No newline at end of file diff --git a/src/Netscript/Parser.js b/src/Netscript/Parser.js index 61d4e12ac..8ac71df0b 100644 --- a/src/Netscript/Parser.js +++ b/src/Netscript/Parser.js @@ -6,5 +6,248 @@ 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); + }); + } } \ No newline at end of file diff --git a/src/Netscript/Tokenizer.js b/src/Netscript/Tokenizer.js index 5575bb67e..f7e83252c 100644 --- a/src/Netscript/Tokenizer.js +++ b/src/Netscript/Tokenizer.js @@ -3,27 +3,19 @@ * Tokens can be accessed with peek() and next(). * * Token types: - * {type: "punc", value: "(" } // punctuation: parens, comma, semicolon etc. - * {type: "num", value: 5 } // numbers - * {type: "str", value: "Hello World!" } // strings - * {type: "kw", value: "lambda" } // keywords - * {type: "var", value: "a" } // identifiers - * {type: "op", value: "!=" } // operators - * - * - * - * - * - * - * - * - * + * {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 then else true false while for "; + var keywords = " if elif else true false while for "; return { next : next, diff --git a/src/Server.js b/src/Server.js index 60333d310..32a88aea5 100644 --- a/src/Server.js +++ b/src/Server.js @@ -19,6 +19,7 @@ function Server() { this.scripts = []; this.runningScripts = []; //Scripts currently being run + this.scriptEnvs = []; //The environment for all of the running scripts. Matched by index number this.programs = []; /* Hacking information (only valid for "foreign" aka non-purchased servers) */ diff --git a/src/Terminal.js b/src/Terminal.js index 0977fd762..8cf6b2a8c 100644 --- a/src/Terminal.js +++ b/src/Terminal.js @@ -289,6 +289,19 @@ var Terminal = { case "scp": //TODO break; + + case "test": + //TODO + //TESTED: print, for loops + //UNTESTED: + + var code = "i = 0; while (i < 100000000000) {print(i); i = i+1;}"; + var ast = Parser(Tokenizer(InputStream(code))); + console.log("Printing AST below") + console.log(ast); + var globalEnv = new Environment(); + evaluate(ast, globalEnv); + break; default: post("Command not found"); }