Skip to content

Instantly share code, notes, and snippets.

@rajivnarayana
Last active August 29, 2015 14:08
Show Gist options
  • Save rajivnarayana/64f52a2f731707a56684 to your computer and use it in GitHub Desktop.
Save rajivnarayana/64f52a2f731707a56684 to your computer and use it in GitHub Desktop.
Simple compiler
/**
* A simple arithmetic language that consists of the following keywords
*
* reset
* reset x, creates a variable x if it doesnot exist and assigns 0 as its value
* increment
* increment x, increments a previously declared variable x, generates compiler warning if it doesnot have x already.
* loop
* loop x, loops the following statements until endloop x times, generates a compiler warning if x is not defined already.
* endloop
* limits the statements within a loop.
*
* Every line should have exactly one statement and should begin with a keyword followed by a variable except endloop
* A variable should begin with an alphabet.
* Arguments to the program should be positive numeric values only.
*
* Example program to increment x a given number of times
* reset x
* loop a
* increment x
* endloop
*
*/
var Compiler = function() {
this.resetKeyword = new KeywordSymbol("reset", true);
this.incrementKeyword = new KeywordSymbol("increment", true);
this.loopKeyword = new KeywordSymbol("loop", true);
this.endLoopKeyword = new KeywordSymbol("endloop", false);
this.loopKeyword.shouldBeFollowedBySymbol(this.endLoopKeyword);
this.endLoopKeyword.shouldBePreceededBySymbol(this.loopKeyword);
this.parser = new Parser([this.resetKeyword, this.incrementKeyword, this.loopKeyword, this.endLoopKeyword]);
return this;
}
Compiler.prototype.compile = function(text) {
return this.parser.parse(text);
}
Compiler.prototype.constructRuntime = function(statements) {
var jsString = "";
for (var i = 0; i < statements.length; i++) {
var statement = statements[i];
if (statement.keywordSymbol == this.resetKeyword) {
jsString += "\n" + statement.variable + "= reset();";
} else if (statement.keywordSymbol == this.incrementKeyword) {
jsString += "\n" + statement.variable + "= increment(" + statement.variable + ")";
} else if (statement.keywordSymbol == this.loopKeyword) {
jsString += "\nloop(" + statement.variable + ", function() { \n";
} else if (statement.keywordSymbol == this.endLoopKeyword) {
jsString += "\n});"
}
}
return jsString;
}
/**
* public method,
* @param, text, sourceCode to execute
*/
Compiler.prototype.execute = function(text, arguments) {
try {
var statements = this.compile(text);
var variables = this.extractVariables(statements);
var evalString = "var fun = function() {\n";
evalString += this.prependVariables(variables, arguments);
evalString += this.constructRuntime(statements);
evalString += this.appendReturnVariables(variables);
evalString += "}";
console.log(evalString);
eval(evalString);
result = fun();
return result;
} catch (err) {
alert(err.description);
}
}
Compiler.prototype.extractVariables = function(statements) {
var varSet = [];
for (var i = 0; i < statements.length; i++) {
if (!statements[i].keywordSymbol.acceptsVariable) {
continue;
}
var variable = statements[i].variable;
if (varSet.indexOf(variable) != -1) {
continue;
}
varSet[varSet.length] = variable;
};
return varSet;
}
/**
* arguments will be indexed from 0 to n-1 and predefined variables will be from
*/
Compiler.prototype.prependVariables = function(variables, arguments) {
var jsString = "";
for (var i = 0; i < variables.length; i++) {
var variable = variables[i];
if (arguments.hasOwnProperty(variable)) {
jsString += "var " + variable + " = " + arguments[variable] + ";";
} else {
jsString += "var " + variable + " = 0;";
}
}
return jsString;
}
Compiler.prototype.appendReturnVariables = function(variables) {
var jsString = "\nreturn {";
for (var i = 0; i < variables.length; i++) {
var variable = variables[i];
jsString += "'" + variable + "' : " + variable + ","
};
jsString += "}";
return jsString;
}
//--end Compiler-------------------------------//
//--begin Parser-------------------------------//
var Parser = function(keywordSymbols) {
this.keywordSymbols = keywordSymbols;
return this;
}
Parser.prototype.parse = function(str) {
var lines = this.getLines(str);
var compiledStatements = [];
var statementStackWaitingForMatches = [];
for (i = 0; i< lines.length; i++) {
var tokens = this.getWords(lines[i]);
if (tokens.length > 2) {
throw new ParseException(i+1, 3, "Unrecognized symbol", "Symbol "+tokens[2]+" not expected");
}
if (!this.isValidKeyword(tokens[0])) {
throw new ParseException(i+1, 1, "Invalid keyword", "Every statement should begin with a keyword");
}
var keywordSymbol = this.getKeywordSymbol(tokens[0]);
if (keywordSymbol.acceptsVariable) {
if (!this.isValidVariable(tokens[1])) {
throw new ParseException(i+1, 2, "Invalid variable", "Cannot be a valid variable "+tokens[1]);
}
} else {
if (tokens.length > 1) {
throw new ParseException(i + 1, 2, "Unrecognized symbol", "Symbol "+tokens[1]+" not expected after "+keywordSymbol.name);
}
}
var statement = new Statement(keywordSymbol, tokens[1]);
compiledStatements[compiledStatements.length] = statement;
if (keywordSymbol.symbolToFollow != null) {
statementStackWaitingForMatches[statementStackWaitingForMatches.length] = statement;
}
if (keywordSymbol.symbolToPreceed != null) {
if (statementStackWaitingForMatches.length == 0) {
throw new ParseException( i + 1 , 1, "Unmatched statement", "This statement should be preceeded by a "+keywordSymbol.name+" statement");
}
if (statementStackWaitingForMatches[statementStackWaitingForMatches.length-1].keywordSymbol.name == statement.keywordSymbol.symbolToPreceed.name) {
statementStackWaitingForMatches.pop();
}
}
}
if (statementStackWaitingForMatches.length > 0) {
throw new ParseException(lines.length , 1, "Open loop", "loop not closed for "+statementStackWaitingForMatches[0].keywordSymbol.name+" "+statementStackWaitingForMatches[0].variable);
}
return compiledStatements;
}
Parser.prototype.isValidKeyword = function(token) {
return this.getKeywordSymbol(token) != null;
}
/**
* Makes sure the given token matches one of the keywordSymbols
* return true, if is one of a given keywords.
*/
Parser.prototype.getKeywordSymbol = function(token) {
for (var i = 0; i < this.keywordSymbols.length; i++) {
if (this.keywordSymbols[i].name === token) {
return this.keywordSymbols[i];
}
};
return null;
}
Parser.prototype.isValidVariable = function(token) {
if (this.isValidKeyword(token)) {
return false;
}
if (this.isSpecialVariable(token)) {
return true;
}
if (/^[a-zA-Z0-9]*$/.test(token)) {
return true;
}
return false;
}
//special kind of variable that starts with _ like _1, _2 etc
Parser.prototype.isSpecialVariable = function(token) {
// if (/^[_][0-9]*/.test(token)) {
// return true;
// }
return false;
}
/**
* Simply returns string tokens separated by new line character
*/
Parser.prototype.getLines = function(text) {
return text.match(/[^\r\n]+/g);
}
/**
* return, array of tokens separated by a white space character.
*/
Parser.prototype.getWords = function(text) {
return text.match(/\S+/g);
}
//---end of Parser------------------------------//
//----begin KeyworkSymbol----------------------------//
var KeywordSymbol = function(name, acceptsVariable) {
this.name = name;
this.acceptsVariable = acceptsVariable;
this.symbolToFollow = null;
this.symbolToPreceed = null;
return this;
}
KeywordSymbol.prototype.shouldBeFollowedBySymbol = function(keywordSymbol) {
this.symbolToFollow = keywordSymbol;
}
KeywordSymbol.prototype.shouldBePreceededBySymbol = function(keywordSymbol) {
this.symbolToPreceed = keywordSymbol;
}
//-----end Keyword Symbol----------------------------//
//---------------------------------//
var ParseException = function(lineNo, symbolNo, type, description) {
this.lineNo = lineNo;
this.symbolNo = symbolNo;
this.type = type;
this.description = description;
return this;
}
//---------------------------------//
//---------------------------------//
var Statement = function(keywordSymbol, variable) {
this.keywordSymbol = keywordSymbol;
this.variable = variable;
return this;
}
//---------------------------------//
//-----------Runtime---------------//
function increment(x) {
return x+1;
}
function loop(x, callback) {
var i;
for(i=0; i< x; i++) {
callback();
}
}
function reset() {
return 0;
}
//-----------end Runtime---------------//
@rajivnarayana
Copy link
Author

@rajivnarayana
Copy link
Author

a - 1


x=0
y = 0

for a

x = y

y++

end for

@rajivnarayana
Copy link
Author

a - b


x = 0

for b

z=0
y = 0

for a

z = y

y++

end for

x = z

end for

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment