A JavaScript Parser in JavaScript

The first step towards building a compiler is building a parser. The parser takes program code written by a developer and turns it into a “graph” or “tree” of interrelated program objects. This graph is then translated into the corresponding machine instructions.

The JavaScript code included below will read the contents of a text file, which itself contains JavaScript, parses the code into a tree of objects, and displays that structure in a web browser window.

Note that this code has not been tested thoroughly, so it may not be able to parse certain programs, and is certain to still be extremely buggy. Also, since it was intended for this code to eventually be expanded into a full compiler, there is some extra (the uncharitable might call it “extraneous”) code in there to support that.

1. In any convenient location, create a new directory named “ParserTest”.

2. In the newly created ParserTest directory, create a new text file named “ParserTest.html”, containing the following text.

<html>
<body>
<script type='text/javascript'>

function CompilerTest()
{
	this.main = function()
	{
		var pathOfCodeFile = "Test.js";
		var codeAsString = FileHelper.readStringFromFileAtPath(pathOfCodeFile);
		var code = new Code(codeAsString);
		code.parseStringIntoCodeTree();
		document.body.appendChild(code.nodeRoot.toHTMLElement());
	}
}

// classes

function Code(codeAsString)
{
	this.codeAsString = codeAsString;
}
{
	Code.prototype.parseStringIntoCodeTree = function()
	{	
		var nodeDefns = CodeNodeDefn.Instances;
		var keywords = Keyword.Instances;

		this.nodeRoot = new CodeNode(nodeDefns.Program, null);

		this.nodeCurrent = new CodeNode(nodeDefns.CodeBlock, this.nodeRoot);

		this.tokens = Code.tokenizeCodeString(this.codeAsString);
		
		this.token = null;
		this.t = 0;

		var numberOfTokens = this.tokens.length;

		while (this.t < numberOfTokens)
		{
			this.token = this.tokens[this.t];

			this.parseStringIntoCodeTree_ProcessToken();
		}
	
		return this.nodeRoot;
	}	

	Code.prototype.parseStringIntoCodeTree_ProcessToken = function()
	{
		var nodeDefns = CodeNodeDefn.Instances;
		var keywords = Keyword.Instances;
		var operators = Operator.Instances;

		var nodeCurrent = this.nodeCurrent;
		var tokens = this.tokens;
		var token = this.token;
		var t = this.t;

		var parent = nodeCurrent.parent;
		var nodeDefn = nodeCurrent.defn;

		// this block should be split out into the various nodeDefns 

		if (nodeDefn == nodeDefns.Expression)
		{	
			if (token == keywords.Semicolon.symbol)
			{
				nodeCurrent = parent;
			}
			else if (token == keywords.ParenthesisClose.symbol)
			{
				nodeCurrent = parent;
				t++;
			}		
			else if 
			(
				token == keywords.ParenthesisOpen.symbol 
				&& StringHelper.isStringAnIdentifier(tokens[t - 1]) == false
			)
			{
				nodeCurrent = new CodeNode(nodeDefns.Expression, nodeCurrent);						
				t++;
			}
			else if (Operator.getBySymbol(token) != null)
			{
				if (nodeCurrent.attributes.length > 0)
				{
					var operatorNew = Operator.getBySymbol(token);
					var operatorExisting = Operator.getBySymbol(nodeCurrent.attributes[0].value);

					// lower priority is better
					if (operatorNew.priority <= operatorExisting.priority)
					{
						// 1 + 2 * 3
						var nodeOperandToMoveIndex = nodeCurrent.children.length - 1;
						var nodeOperandToMove = nodeCurrent.children[nodeOperandToMoveIndex];
						var nodeExpressionHighPriority = new CodeNode(nodeDefns.Expression, nodeCurrent);
						nodeCurrent.children.splice(nodeOperandToMoveIndex, 1);
						nodeExpressionHighPriority.children.push(nodeOperandToMove);
						nodeOperandToMove.parent = nodeExpressionHighPriority;

						nodeCurrent = nodeExpressionHighPriority;
					}
					else
					{
						// 4 * 5 + 6
						var nodeOperandToMoveIndex = nodeCurrent.children.length - 1;
						var nodeOperandToMove = nodeCurrent;	
						var nodeParent = nodeCurrent.parent;
						nodeParent.children.splice(nodeOperandToMoveIndex, 1);
						var nodeExpressionLowPriority = new CodeNode(nodeDefns.Expression, nodeCurrent.parent);
						nodeOperandToMove.parent = nodeExpressionLowPriority;
						nodeExpressionLowPriority.children.push(nodeOperandToMove);
				
						nodeCurrent = nodeExpressionLowPriority;
					}
				}

				nodeCurrent.attributes.push(new Attribute("operator", token));
				t++;
			}
			else if (StringHelper.isStringANumber(token) == true)
			{
				var nodeChild = new CodeNode(nodeDefns.Expression, nodeCurrent);
				nodeChild.attributes.push(new Attribute("operator", operators.LiteralNumber.symbol));
				nodeChild.attributes.push(new Attribute("value", token));
				t++;
			}
			else if (StringHelper.isStringEnclosedInQuotes(token) == true)
			{
				var nodeChild = new CodeNode(nodeDefns.Expression, nodeCurrent);
				nodeChild.attributes.push(new Attribute("operator", operators.LiteralString.symbol));
				nodeChild.attributes.push(new Attribute("value", token));
				t++;
			}
			else if (token == keywords.This.symbol || StringHelper.isStringAnIdentifier(token) == true)  
			{
				var nodeChild = new CodeNode(nodeDefns.Expression, nodeCurrent);
				nodeChild.attributes.push(new Attribute("operator", operators.Identifier.symbol));
				nodeChild.attributes.push(new Attribute("value", token));
				t++;
			}
			else if (token == keywords.Function.symbol)
			{
				var nodeChild = new CodeNode(nodeDefns.FunctionDeclaration, nodeCurrent);
				nodeChild.attributes.push(new Attribute("operator", operators.FunctionPointer.symbol));
				nodeChild.attributes.push(new Attribute("value", token));
				nodeCurrent = nodeChild
				t++;
			}
			else
			{
				// todo - Error?
			}
		}			
		else if (nodeDefn == nodeDefns.Statement)
		{
			if (token == keywords.Semicolon.symbol)
			{
				nodeCurrent = parent;
				t++;	
			}
			/*
			else if (token == keywords.Var.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.VariableDeclaration, nodeCurrent);	
				t++;	
			}
			*/
			else if (token == keywords.Break.symbol)
			{
				new CodeNode(nodeDefns.Break, nodeCurrent);
				t++;	
			}
			else if (token == keywords.Return.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.Expression, nodeCurrent);
				nodeCurrent.attributes.push(new Attribute("operator", "return"));
				t++;
			}
			else
			{
				nodeCurrent = new CodeNode(nodeDefns.Expression, nodeCurrent);
			}
		}
		else if (nodeDefn == nodeDefns.CodeBlock)
		{
			if (token == keywords.BraceClose.symbol)
			{
				// todo - build lookups?

				nodeCurrent = parent;
				t++;
			}
			else if (token == keywords.If.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.If, nodeCurrent);
				t++;
			}
			else if (token == keywords.While.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.While, nodeCurrent);
				t++;
			}
			else if (token == keywords.For.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.For, nodeCurrent);
				t++;
			}
			else if (token == keywords.Function.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.FunctionDeclaration, nodeCurrent);
				t++;
			}
			else
			{
				nodeCurrent = new CodeNode(nodeDefns.Statement, nodeCurrent);
			}				
		}
		/*
		else if (nodeDefn == nodeDefns.VariableDeclaration)
		{
			nodeCurrent.attributes.push(new Attribute("name", token));
			nodeCurrent = parent;
			t++;
		}
		*/
		else if (nodeDefn == nodeDefns.FunctionDeclaration)
		{
			if (token == keywords.ParenthesisOpen.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.ParameterNameList, nodeCurrent);
				t++;
			}
			else if (token == keywords.BraceOpen.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.CodeBlock, nodeCurrent);
				t++;
			}
			else if (nodeCurrent.children.length == 0)
			{
				nodeCurrent.attributes.push(new Attribute("name", token));
				t++;
			}
			else
			{
				nodeCurrent = parent;
			}
		}
		else if (nodeDefn == nodeDefns.ParameterNameList)
		{
			if (token == keywords.ParenthesisClose.symbol)
			{
				nodeCurrent = parent;
			}
			else
			{					
				var nodeChild = new CodeNode(nodeDefns.ParameterName, nodeCurrent);
				nodeChild.attributes.push(new Attribute("value", token));
			}

			t++;
		}
		else if (nodeDefn == nodeDefns.If)
		{
			if (token == keywords.ParenthesisOpen.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.Expression, nodeCurrent);
				t++;
			}
			else if (token == keywords.BraceOpen.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.CodeBlock, nodeCurrent);
				t++;
			}
			else if (token == keywords.Else.symbol)
			{
				// ignore it - This presumes braces will always be used.
				t++;
			}
			else
			{
				nodeCurrent = parent;
			}

		}
		else if (nodeDefn == nodeDefns.For)
		{
			if (token == keywords.ParenthesisOpen.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.ForConditions, nodeCurrent);
				t++;
			}
			else if (token == keywords.BraceOpen.symbol)
			{
				nodeCurrent = new CodeNode(nodeDefns.CodeBlock, nodeCurrent);
				t++;
			}
			else
			{
				nodeCurrent = parent;
			}
		}
		else if (nodeDefn == nodeDefns.ForConditions)
		{
			if (nodeCurrent.children.length == 0)
			{
				nodeCurrent = new CodeNode(nodeDefns.Statement, nodeCurrent);
			}
			else if (nodeCurrent.children.length == 1)
			{
				nodeCurrent = new CodeNode(nodeDefns.Expression, nodeCurrent);
			}
			else if (nodeCurrent.children.length == 2)
			{
				nodeCurrent = new CodeNode(nodeDefns.Expression, nodeCurrent);
				t++;
			}
			else
			{
				nodeCurrent = parent;
			}
		}
		else 
		{
			nodeCurrent = new CodeNode(nodeDefns.Unknown, nodeCurrent);
			nodeCurrent.attributes.push(new Attribute("text", token));
			t++;
		}

		this.nodeCurrent = nodeCurrent;
		this.token = token;
		this.t = t;
	}

	Code.tokenizeCodeString = function(stringToTokenize)
	{
		var returnTokens = new Array();

		var lengthOfStringToTokenize = stringToTokenize.length;
		var tokenInProgress = "";
		var charFromString = "";

		var inStringLiteral = false;

		for (var i = 0; i < lengthOfStringToTokenize; i++)
		{
			var charFromStringPrev = charFromString;
			charFromString = stringToTokenize[i];

			if (inStringLiteral == true)
			{
				tokenInProgress += charFromString

				if (charFromString == "\"")
				{
					if (charFromStringPrev != "\\")
					{
						inStringLiteral = false;
					}
				}	
			}
			else if (StringHelper.isCharWhitespace(charFromString) == true)
			{
				// do nothing
			}
			else if (charFromString == "\"")
			{
				returnTokens.push(tokenInProgress);
				tokenInProgress = "" + charFromString;
				inStringLiteral = true;
			}
			else if (StringHelper.isCharALetterOrNumeral(charFromString) == true)
			{				
				if (StringHelper.isCharALetterOrNumeral(charFromStringPrev) == true)
				{
					tokenInProgress += charFromString;
				}
				else
				{
					returnTokens.push(tokenInProgress);
					tokenInProgress = "" + charFromString;
				}
			}
			else if (StringHelper.isCharASymbol(charFromString) == true)
			{
				if (StringHelper.isCharASymbol(charFromStringPrev) == true)
				{
					// hack - To prevent "()" from being one symbol, etc. 
					if (StringHelper.isCharABreakingSymbol(charFromString) == true)
					{
						returnTokens.push(tokenInProgress);
						tokenInProgress = "" + charFromString;
					}
					else
					{
						tokenInProgress += charFromString;
					}
				}
				else
				{
					returnTokens.push(tokenInProgress);
					tokenInProgress = "" + charFromString;
				}
			}
		}

		return returnTokens;	
	}
}

//

function Attribute(name, value)
{
	this.name = name;
	this.value = value;
}

function CodeNode(defn, parent)
{
	this.defn = defn;
	this.parent = parent;

	this.attributes = new Array();
	this.children = new Array();

	if (this.parent != null)
	{
		this.parent.children.push(this);
	}
}
{
	CodeNode.prototype.execute = function(codeRun)
	{	
		this.defn.execute(codeRun, this);
	}

	CodeNode.prototype.toHTMLElement = function()
	{
		var returnValue = document.createElement("p");
		returnValue.innerHTML = this.toStringXML("", true);

		return returnValue;
	}

	CodeNode.prototype.toStringXML = function(indent, isEscaped)
	{
		var lessThan;
		var greaterThan;
		var space;
		var newline;

		if (isEscaped == true)
		{
			lessThan = "&lt";
			greaterThan = "&gt;";
			space = "&nbsp;";
			newline = "<br />";
		}
		else
		{
			lessThan = "<";
			greaterThan = ">";
			space = " ";
			newline = "\n";
		}

		var returnValue = 
			indent 
			+ lessThan + this.defn.name;

		for (var i = 0; i < this.attributes.length; i++)
		{
			var attribute = this.attributes[i];

			returnValue += " " + attribute.name + "='" + attribute.value + "'";
		}

		if (this.children.length == 0)
		{
			returnValue += "/" + greaterThan + newline;
		}
		else
		{
		
			returnValue += greaterThan + newline;

			for (var i = 0; i < this.children.length; i++)
			{
				var child = this.children[i];
	
				returnValue += child.toStringXML(indent + space + space + space + space, isEscaped);
			}
	
			returnValue += indent + lessThan + "/" + this.defn.name + greaterThan + newline;
		}

		return returnValue;
	}
}

function CodeNodeDefn(name, execute)
{
	this.name = name;
	this.execute = execute;
}
{
	// static methods

	CodeNodeDefn.executeDefault = function(codeRun, node)
	{
		console.log(this.name);

		for (var i = 0; i < node.children.length; i++)
		{
			var child = node.children[i];
			child.execute(codeRun, child);
		}
	}

	// execute() implementations for "subclasses"

	CodeNodeDefn.execute_Expression = function(codeRun, node)
	{
		var operatorSymbol = node.attributes[0].value; // hack - assume 0
		var operator = Operator.getBySymbol(operatorSymbol); 
		var returnValue = operator.evaluate(codeRun, node);
		return returnValue;
	}

	// instances

	CodeNodeDefn.Instances = new CodeNodeDefn_Instances();

	function CodeNodeDefn_Instances()
	{
		this.ArrayIndexGet	= new CodeNodeDefn("ArrayIndexGet", 	CodeNodeDefn.executeDefault);
		this.Break		= new CodeNodeDefn("Break", 		CodeNodeDefn.executeDefault);
		this.CodeBlock 		= new CodeNodeDefn("CodeBlock", 	CodeNodeDefn.executeDefault);
		this.CodeLine 		= new CodeNodeDefn("CodeLine", 		CodeNodeDefn.executeDefault);
		this.Expression 	= new CodeNodeDefn("Expression", 	CodeNodeDefn.execute_Expression);
		this.For		= new CodeNodeDefn("For", 		CodeNodeDefn.executeDefault);
		this.ForConditions 	= new CodeNodeDefn("ForConditions", 	CodeNodeDefn.executeDefault);
		this.FunctionDeclaration= new CodeNodeDefn("FunctionDeclaration", CodeNodeDefn.executeDefault);
		this.FunctionInvocation = new CodeNodeDefn("FunctionInvocation", CodeNodeDefn.executeDefault);
		this.Identifier		= new CodeNodeDefn("Identifier", 	CodeNodeDefn.executeDefault);
		this.If			= new CodeNodeDefn("If", 		CodeNodeDefn.executeDefault);
		this.NumberLiteral	= new CodeNodeDefn("NumberLiteral", 	CodeNodeDefn.executeDefault);
		this.ParameterNameList 	= new CodeNodeDefn("ParameterNameList", CodeNodeDefn.executeDefault);
		this.ParameterName	= new CodeNodeDefn("ParameterName", 	CodeNodeDefn.executeDefault);
		this.ParameterValueList = new CodeNodeDefn("ParameterValueList", CodeNodeDefn.executeDefault);
		this.Program 		= new CodeNodeDefn("Program", 		CodeNodeDefn.executeDefault);
		this.PropertyGet	= new CodeNodeDefn("PropertyGet", 	CodeNodeDefn.executeDefault);
		this.Statement		= new CodeNodeDefn("Statement", 	CodeNodeDefn.executeDefault);
		this.StringLiteral 	= new CodeNodeDefn("StringLiteral", 	CodeNodeDefn.executeDefault);
		this.Unknown 		= new CodeNodeDefn("Unknown", 		CodeNodeDefn.executeDefault);
		//this.VariableDeclaration= new CodeNodeDefn("VariableDeclaration", CodeNodeDefn.executeDefault);
		this.While		= new CodeNodeDefn("While", 		CodeNodeDefn.executeDefault);
	}
}

function CodeRun(codeToRun)
{
	this.codeToRun = codeToRun;}
{
	CodeRun.prototype.execute = function()
	{
		this.functionLookup = new Lookup("name"); 
		this.variableLookup = new Lookup("name");

		this.nodeCurrent = this.codeToRun.nodeRoot;

		this.nodeCurrent.execute(this);
	}
}

function FileHelper()
{}
{
	FileHelper.readStringFromFileAtPath = function(pathOfFileToReadFrom)
	{
		var request = new XMLHttpRequest();
		request.open("GET", pathOfFileToReadFrom, false);
		request.send(null);
		var returnValue = request.responseText;

		return returnValue;
	}
}

function Keyword(symbol)
{
	this.symbol = symbol;
}
{
	Keyword.Instances = new Keyword_Instances();

	function Keyword_Instances()	
	{
		this.Break 	= new Keyword("break");
		this.Else	= new Keyword("else");
		this.For	= new Keyword("for");
		this.Function 	= new Keyword("function");
		this.If 	= new Keyword("if");
		this.Import 	= new Keyword("import");
		this.Return	= new Keyword("return");
		this.This	= new Keyword("this");
		this.Var 	= new Keyword("var");
		this.While	= new Keyword("while");

		this.BraceClose		= new Keyword("}");
		this.BraceOpen		= new Keyword("{");
		this.Dot		= new Keyword(".");
		this.Equals 		= new Keyword("==");
		this.Gets		= new Keyword("=");
		this.ParenthesisClose  	= new Keyword(")");
		this.ParenthesisOpen  	= new Keyword("(");
		this.Semicolon		= new Keyword(";");

		this._All = [
			this.Break,
			this.Else,
			this.For,
			this.Function,
			this.If,
			this.Import,
			this.Return,
			this.This,
			this.Var,
			this.While
		];

		this._SymbolToKeywordLookup = new Array();

		for (var i = 0; i < this._All.length; i++)
		{
			var keyword = this._All[i];

			this._SymbolToKeywordLookup[keyword.symbol] = keyword; 
		}
	}

	Keyword.isStringAKeyword = function(stringToTest)
	{
		var returnValue;

		returnValue = (Keyword.Instances._SymbolToKeywordLookup[stringToTest] != null)

		return returnValue;
	}
}

function Lookup(nameOfKeyField)
{
	this.nameOfKeyField = nameOfKeyField;

	this.systemArray = new Array();
	this.systemLookup = new Array();
}
{
	Lookup.prototype.add = function(item, nameOfKeyField)
	{
		this.systemArray.push(item);
		this.systemLookup[item[nameOfKeyField]] = item; 
	}

	Lookup.prototype.getByIndex = function(indexToGet)
	{
		return this.systemArray[indexToGet];
	}

	Lookup.prototype.getByKey = function(keyToGet)
	{
		return this.systemLookup[keyToGet];
	}
}

function Operator(name, symbol, priority, evaluate)
{
	this.name = name;
	this.symbol = symbol;
	this.priority = priority;
	this.evaluate = evaluate;
}
{
	// static methods

	Operator.getBySymbol = function(symbolToGet)
	{
		var returnValue = Operator.Instances._SymbolToOperatorLookup[symbolToGet];

		return returnValue;
	}

	// evaluate() implementations for "subclasses"

	Operator.evaluate_Decrement = function(codeRun, node, expression) { return null; }
	Operator.evaluate_DividedBy = function(codeRun, node, expression) { return null; }

	Operator.evaluate_Dot = function(codeRun, node, expression) 
	{
		--todo

		var parentIdentifier = node.children[0].attributes[1].value;
		var childIdentifier = node.children[1].attributes[1].value;

		var parent = codeRun.variableLookup.getByKey(parentIdentifier);
		var child = parent[childIdentifier];

		return child; 
	}

	Operator.evaluate_Equals = function(codeRun, node, expression) { return null; }
	Operator.evaluate_FunctionCall = function(codeRun, node, expression) { return null; }
	Operator.evaluate_FunctionPointer = function(codeRun, node, expression) { return null; }

	Operator.evaluate_Gets = function(codeRun, node, expression) 
	{ 
		-- todo

		return null;
	}

	Operator.evaluate_GreaterThan = function(codeRun, node, expression) { return null; }
	Operator.evaluate_GreaterThanOrEqualTo = function(codeRun, node, expression) { return null; }
	Operator.evaluate_Identifier = function(codeRun, node, expression) { return null; }
	Operator.evaluate_Increment = function(codeRun, node, expression) { return null; }
	Operator.evaluate_LessThan = function(codeRun, node, expression) { return null; }
	Operator.evaluate_LessThanOrEqualTo = function(codeRun, node, expression) { return null; }
	Operator.evaluate_LiteralNull = function(codeRun, node, expression) { return null; }
	Operator.evaluate_LiteralNumber = function(codeRun, node, expression) { return null; }
	Operator.evaluate_LiteralString = function(codeRun, node, expression) { return null; }
	Operator.evaluate_NotEqualTo = function(codeRun, node, expression) { return null; }
	Operator.evaluate_Minus = function(codeRun, node, expression) { return null; }
	Operator.evaluate_Plus = function(codeRun, node, expression) { return null; }
	Operator.evaluate_Return = function(codeRun, node, expression) { return null; }
	Operator.evaluate_Times = function(codeRun, node, expression) { return null; }

	Operator.evaluate_Var = function(codeRun, node, expression) 
	{ 
		-- todo

		var variableName = node.children[0].attributes[1].value; // hack - assuming indices
		var variable = new Variable(variableName, null)
		codeRun.variableLookup.add(variable);

		return variable.value; 
	}

	// instances

	Operator.Instances = new Operator_Instances();

	function Operator_Instances()
	{
		this.FunctionPointer 	= new Operator("FunctionPointer",	"[function pointer]", 	0, Operator.evaluate_FunctionPointer);
		this.LiteralString 	= new Operator("StringLiteral",		"[string literal]", 	0, Operator.evaluate_LiteralString);
		this.LiteralNumber 	= new Operator("NumberLiteral",		"[number literal]", 	0, Operator.evaluate_LiteralNumber);
		this.LiteralNull	= new Operator("LiteralNull",		"null",			0, Operator.evaluate_LiteralNull);
		this.Identifier 	= new Operator("Identifier",		"[identifier]", 	0, Operator.evaluate_Identifier);		

		this.Var		= new Operator("Var",			"var", 	0, Operator.evaluate_Var);
		this.Dot		= new Operator("Dot", 			".", 	0, Operator.evaluate_Dot);

		this.FunctionCall 	= new Operator("FunctionCall",		"(", 	1, Operator.evaluate_FunctionCall);

		this.Times		= new Operator("Times", 		"*", 	2, Operator.evaluate_Times);
		this.DividedBy		= new Operator("DividedBy", 		"/", 	2, Operator.evaluate_DividedBy);

		this.Decrement 		= new Operator("Decrement", 		"--", 	3, Operator.evaluate_Decrement);
		this.Increment 		= new Operator("Increment", 		"++", 	3, Operator.evaluate_Increment);
		this.Minus		= new Operator("Minus", 		"-", 	3, Operator.evaluate_Minus);
		this.Plus 		= new Operator("Plus", 			"+", 	3, Operator.evaluate_Plus);

		this.Equals 		= new Operator("Equals", 		"==", 	4, Operator.evaluate_Equals);
		this.GreaterThan	= new Operator("GreaterThan", 		">", 	4, Operator.evaluate_GreaterThan);
		this.GreaterThanOrEqualTo = new Operator("GreaterThanOrEqualTo", ">=", 	4, Operator.evaluate_GreaterThanOrEqualTo);
		this.LessThan 		= new Operator("LessThan", 		"<", 	4, Operator.evaluate_LessThan);
		this.LessThanOrEqualTo 	= new Operator("LessThanOrEqualTo", 	"<=", 	4, Operator.evaluate_LessThanOrEqualTo);
		this.NotEqualTo		= new Operator("NotEqualTo",		"!=", 	4, Operator.evaluate_NotEqualTo);

		this.Gets		= new Operator("Gets", 			"=", 	5, Operator.evaluate_Gets);

		this.Return		= new Operator("Return", 		"return", 6, Operator.evaluate_Return);

		this._All = 
		[
			this.Decrement,
			this.DividedBy,
			this.Dot,
			this.Equals,
			this.FunctionCall,
			this.FunctionPointer,
			this.Gets,
			this.GreaterThan,
			this.GreaterThanOrEqualTo,
			this.Identifier,
			this.Increment,
			this.LessThan,
			this.LessThanOrEqualTo,
			this.LiteralNull,
			this.LiteralNumber,
			this.LiteralString,
			this.NotEqualTo,
			this.Minus,
			this.Plus,
			this.Return,
			this.Times,
			this.Var,
		];

		this._SymbolToOperatorLookup = new Array();

		for (var i = 0; i < this._All.length; i++)
		{
			var operator = this._All[i];

			this._SymbolToOperatorLookup[operator.symbol] = operator; 
		}
	}
}

function State(name)
{
	this.name = name;
}
{
	State.Instances = new State_Instances();

	function State_Instances()
	{
		this.Default = new State("Default");
	}
}

function StringHelper()
{}
{
	// constants

	StringHelper.BreakingSymbols = "()[]{};.";
	StringHelper.LettersLowercase = "abcdefghijklmnopqrstuvwxyz";
	StringHelper.LettersUppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	StringHelper.Numerals = "0123456789";
	StringHelper.Symbols = ".=+-/*<>()[]{};";
	StringHelper.WhitespaceChars = " \t\r\n";

	StringHelper.Letters = StringHelper.LettersLowercase + StringHelper.LettersUppercase;

	// static methods

	StringHelper.isCharALetter = function(charToTest)
	{
		var returnValue = (StringHelper.Letters.indexOf(charToTest) >= 0);

		return returnValue;
	}

	StringHelper.isCharALetterOrNumeral = function(charToTest)
	{
		var returnValue = 
		(
			(StringHelper.Letters.indexOf(charToTest) >= 0)
			|| (StringHelper.Numerals.indexOf(charToTest) >= 0)
		);

		return returnValue;
	}

	StringHelper.isCharANumeral = function(charToTest)
	{		
		var returnValue = (StringHelper.Numerals.indexOf(charToTest) >= 0);

		return returnValue;
	}

	StringHelper.isCharASymbol = function(charToTest)
	{		
		var returnValue = (StringHelper.Symbols.indexOf(charToTest) >= 0);
		return returnValue;
	}

	StringHelper.isCharABreakingSymbol = function(charToTest)
	{		
		var returnValue = (StringHelper.BreakingSymbols.indexOf(charToTest) >= 0);
		return returnValue;
	}

	StringHelper.isCharWhitespace = function(charToTest)
	{		
		return (StringHelper.WhitespaceChars.indexOf(charToTest) >= 0);
	}

	StringHelper.isStringAnIdentifier = function(stringToTest)
	{
		var returnValue;

		if (Keyword.isStringAKeyword(stringToTest) == true)
		{
			returnValue = false;
		}
		else
		{
			returnValue = true;
		
			for (var i = 0; i < stringToTest.length; i++)
			{
				if (StringHelper.isCharALetterOrNumeral(stringToTest[i]) == false)
				{
					returnValue = false;
					break;
				}
			}
		}

		return returnValue;
	}

	StringHelper.isStringANumber = function(stringToTest)
	{
		var returnValue = true;

		for (var i = 0; i < stringToTest.length; i++)
		{
			if (StringHelper.isCharANumeral(stringToTest[i]) == false)
			{
				returnValue = false;
				break;
			}
		}

		return returnValue;
	}

	StringHelper.isStringEnclosedInQuotes = function(stringToTest)
	{
		// todo - single quotes

		var returnValue = 
		(
			stringToTest[0] == "\"" 
			&& stringToTest[stringToTest.length - 1] == "\""
		);

		return returnValue;
	}
}

function Variable(name, value)
{
	this.name = name;
	this.value = value;
}

// run

new CompilerTest().main();

</script>
</body>
</html>

3. Still in the ParserTest folder, create a new file named “Test.js”, containing the following text.

var result2 = a.b();
var result3 = a.b().c(d.e(arg0)) + f.g(arg1, arg2);
var result = getValue();
var fivish = 1 * 2 + 3;
var nine = 1 + 2 * 3;
var stringValue = "This is a string";

function Test(testParameter)
{
	var result = getValue();

	for (var i = 0; i < 2; i++)
	{
		if (result == true)
		{
			var one = 1;
		}
		else
		{
			var four = 2 + 2;
			var five = 2 + 3;			
			break;
		}
	}
}

function getValue()
{
	return true;
}

function Class(name)
{
	this.name = name;
}
{
	Class.prototype.method = function()
	{
		var returnValue;
		returnValue = 1;

		return returnValue;
	}
}

4. Open ParserTest.html in a web browser that runs JavaScript. The code from Test.js will be parsed into a data structure, and that data structure will be converted to a string formatted as XML, and that XML will be displayed in the browser window.

This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s