Finished implementing Netscript. Not completely tested yet. Find out how to make it multithreaded (Web Workers is the best way according to internet

This commit is contained in:
Daniel Xie 2016-11-17 16:25:40 -06:00
parent a4f92f7520
commit 8d87b74eaf
10 changed files with 544 additions and 109 deletions

@ -6,6 +6,25 @@
<link rel="stylesheet" type="text/css" href="css/styles.css" />
<link rel="stylesheet" type="text/css" href="css/terminal.css" />
<!-- We'll add in the jQuery library here - direct from
the Google CDN (Content Delivery Network). -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="src/Constants.js"></script>
<script src="src/Server.js"></script>
<script src="src/Player.js"></script>
<script src="src/Faction.js"></script>
<script src="src/Company.js"></script>
<script src="src/Terminal.js"></script>
<script src="src/netscript/InputStream.js"></script>
<script src="src/netscript/Tokenizer.js"></script>
<script src="src/netscript/Parser.js"></script>
<script src="src/netscript/Environment.js"></script>
<script src="src/netscript/Evaluator.js"></script>
<script src="src/engine.js"></script>
</head>
<body>
<div id="mainmenu-container">
@ -51,9 +70,7 @@
<p id="character-info"> </p>
</div> <!-- End terminal-container -->
<!-- We'll add in the jQuery library here - direct from
the Google CDN (Content Delivery Network). -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<!-- Always keep the terminal in focus -->
<script type="text/javascript">
@ -65,9 +82,6 @@
})
</script>
<script src="src/Server.js"></script>
<script src="src/Player.js"></script>
<script src="src/Terminal.js"></script>
<script src="src/engine.js"></script>
</body>
</html>

@ -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(),

@ -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,
}

@ -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

@ -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;
}
};

124
src/Netscript/Evaluator.js Normal file

@ -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);
}

@ -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);
});
}
}

@ -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,

@ -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) */

@ -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");
}