var psr_newDataType;
var psr_list;
var psr_tokens;
var psr_t;
var psr_tab;
var psr_dot;
var psr_output;

function Parser(list){
	this.format = psr_format;
	this.report = psr_report;
	this.print = psr_print;
	psr_tokens = list;
	psr_t = psr_tokens[psr_dot=psr_tab=0];
	psr_list = new ArrayList();
	psr_newDataType = new ArrayList()
	psr_output = "";
}

function psr_addReport(x){
	var token = psr_t;
	if (token==null) token = psr_tokens[psr_dot];
	psr_output += "line: " + token.line_number + 
		" char: " +	token.char_number + " msg: " + x + "<br>";
}

function psr_printTab(){ for (var i=0; i<psr_tab; i++) psr_write("&nbsp; "); }
function psr_report(){ return "Parser report: " + ((psr_output=="")?("Success<br>"):("<br>"+psr_output)); }
function psr_print(){ psr_format(); return psr_list.list.join(""); }
function psr_write(x){ psr_list.add(x); }
function psr_newline(){ psr_write("<br>"); }
function psr_space(){ psr_write(" "); }

function psr_formatToken(){
	if (psr_t==null) return;

	if (!psr_isBlkCmt())
		psr_t.token = psr_t.token.replace("<","&lt;");

	if (psr_isBlkCmt() || psr_isLineCmt() || psr_isQuote2() || psr_isQuote1()){
		psr_t.token = psr_t.token.replace('\n',"<br>");
		psr_t.token = psr_t.token.replace("  ","&nbsp; ");
	}

	psr_colorToken();

	if (psr_isBlkCmt()) psr_newline();
	psr_write(psr_t.token);
	if (psr_isLineCmt() || psr_isBlkCmt()) psr_newline();
}

function psr_skipUnusedToken(){
	while (psr_t!=null && (psr_isLineCmt() || psr_isBlkCmt() || psr_isNewline())){
		psr_formatToken();
		if (psr_dot+1<psr_tokens.length) psr_t = psr_tokens[++psr_dot];
		else psr_t = null;
	}
}

function psr_nextUnfilteredToken(){
	psr_formatToken();
	if (psr_dot+1<psr_tokens.length) psr_t = psr_tokens[++psr_dot];
	else psr_t = null;
}

function psr_prevIsCloseBrace(){
	if (psr_dot>0 && psr_tokens[psr_dot-1].token.indexOf("}")!=-1) return true;
	else return false;
}

function psr_nextToken(){
	psr_nextUnfilteredToken();
	psr_skipUnusedToken();
	//if (psr_t!=null) psr_addReport(psr_t.token);
}

function psr_look(txt)			{ if (psr_t==null) return false; return (psr_t.token==txt); }
function psr_isDirective()	{ if (psr_t==null) return false; return (psr_t.token_class=="DIRECTIVE"); }
function psr_isDelimiter()	{ if (psr_t==null) return false; return (psr_t.token_class=="DELIMITER"); }
function psr_isIdentifier()	{ if (psr_t==null) return false; return (psr_t.token_class=="IDENTIFIER"); }
function psr_isOperator()		{ if (psr_t==null) return false; return (psr_t.token_class=="OPERATOR"); }
function psr_isNumber()			{ if (psr_t==null) return false; return (psr_t.token_class=="NUMBER"); }
function psr_isNewline()		{ if (psr_t==null) return false; return (psr_t.token_class=="NEWLINE"); }
function psr_isQuote1()			{ if (psr_t==null) return false; return (psr_t.token_class=="QUOTE1"); }
function psr_isQuote2()			{ if (psr_t==null) return false; return (psr_t.token_class=="QUOTE2"); }
function psr_isBlkCmt()			{ if (psr_t==null) return false; return (psr_t.token_class=="BLK_CMT"); }
function psr_isLineCmt()		{ if (psr_t==null) return false; return (psr_t.token_class=="LINE_CMT"); }
function psr_isKeyword2(key){ return psr_isNewDataType(key); }

function psr_isKeyword(){
	if (psr_t==null) return false; 
	if (psr_t.token_class=="KEYWORD") return true;
	return psr_isNewDataType(psr_t.token);
}

function psr_isNewDataType(x){
	for (var i=0; i<psr_newDataType.list.length; i++)
		if (psr_newDataType.list[i] == x) return true;
	return false;
}

function psr_colorToken(){
	if (psr_t==null) return false; 

			 if (psr_isDelimiter())  psr_t.token = "<font color=yellow>"+psr_t.token+"</font>";
	else if (psr_isDirective())  psr_t.token = "<font color=red>"+psr_t.token+"</font>";
	else if (psr_isKeyword())    psr_t.token = "<font color=white>"+psr_t.token+"</font>";
	else if (psr_isIdentifier()) psr_t.token = "<font color=lightgreen>"+psr_t.token+"</font>";
	else if (psr_isNumber())     psr_t.token = "<font color=silver>"+psr_t.token+"</font>";
	else if (psr_isQuote1())     psr_t.token = "<font color=magenta>"+psr_t.token+"</font>";
	else if (psr_isQuote2())     psr_t.token = "<font color=red>"+psr_t.token+"</font>";
	else if (psr_isLineCmt())    psr_t.token = "<font color=green>"+psr_t.token+"</font>";
	else if (psr_isBlkCmt())     psr_t.token = "<font color=green>"+psr_t.token+"</font>";
	else if (psr_isOperator())   psr_t.token = "<font color=yellow>"+psr_t.token+"</font>";
	else if (psr_isNewline())    psr_t.token = psr_t.token;
	else { psr_addReport("Unrecognized token : "+psr_t.token); return false; }

	psr_tokens[psr_dot] = psr_t;
	return true;
}

function psr_match(txt){
	if (psr_t==null || psr_t.token!=txt)
		psr_addReport("Expected "+txt+ ((psr_t!=null)?(" found "+psr_t.token):""));
	else psr_nextToken();
}

function psr_exp(){
	while (psr_isOperator() || psr_isKeyword() || psr_isNumber() || psr_isIdentifier() ||
				 psr_isQuote1() || psr_isQuote2() || psr_look("(") || psr_look("?") || psr_look("{")){
		if (psr_look("(")){
			psr_match("(");
			if (psr_isKeyword()){
					psr_nextToken();
					while (psr_look("*")) psr_nextToken();
					psr_match(")");
					psr_exp();
			}
			else { psr_exp(); psr_match(")"); }
		}
		else if (psr_look("{")){
			psr_match("{"); psr_newline(); psr_tab++;
			psr_printTab();
			psr_exp();
			while (psr_look(",")){
				psr_match(",");
				psr_exp();
			}
			if (!psr_prevIsCloseBrace()) psr_newline();
			psr_tab--; psr_printTab(); psr_match("}");
		}
		else if (psr_look("?")){ psr_match("?"); psr_exp(); psr_match(":"); psr_exp(); }
		else if (psr_isNumber() || psr_isQuote1()) psr_nextToken();
		else if (psr_isOperator()){ psr_nextToken(); continue; }
		else {
			if (psr_look("sizeof")) psr_nextToken();
			else psr_variable();

			if (psr_look("(")) psr_callFunction();
		}

		if (psr_look("?")){ psr_match("?"); psr_exp(); psr_match(":"); psr_exp(); }

		if (psr_isOperator() || psr_look(",")){
			if (psr_look("=")){ psr_space(); psr_match("="); psr_space(); }
			else psr_nextToken();
		}
		else break;
	}
}

function psr_variable(){
	while (psr_look("*")) psr_nextToken();

	if (psr_look("(")){ psr_match("("); psr_exp(); psr_match(")"); }
	else if (psr_isIdentifier() || psr_isQuote2()){
		psr_nextToken();
		while (psr_look("[")){ psr_match("["); psr_exp(); psr_match("]"); }
	}
	else psr_addReport("Expected a variable");

	if (psr_look(".")) { psr_nextToken(); psr_variable(); }
	if (psr_look("++") || psr_look("--")) psr_nextToken();
}

function psr_callFunction(){
	psr_match("(");	psr_exp();
	while (psr_look(",")){ psr_match(","); psr_exp();	}
	psr_match(")");
}

function psr_ifConstruct(){
	psr_match("if"); psr_space(); psr_match("("); psr_exp(); psr_match(")");
	if (psr_look("{")){ psr_match("{"); psr_newline(); psr_tab++;	psr_statementBlock();	psr_tab--; psr_printTab(); psr_match("}"); }
	else { psr_newline(); psr_tab++; psr_statement(); psr_tab--; }

	if (psr_look("else")){
		psr_printTab();
		psr_match("else"); psr_space();
		if (psr_look("if")) psr_ifConstruct();
		else if (psr_look("{")){ psr_match("{"); psr_newline(); psr_tab++; psr_statementBlock(); psr_tab--; psr_printTab(); psr_match("}"); }
		else { psr_newline(); psr_tab++; psr_statement(); psr_tab--; }
	}
}

function psr_forConstruct(){
	psr_match("for"); psr_space(); psr_match("("); psr_exp(); psr_match(";");
	psr_space(); psr_exp(); psr_match(";"); psr_space(); psr_exp(); psr_match(")");
	if (psr_look("{")){	psr_match("{"); psr_newline(); psr_tab++;	psr_statementBlock();	psr_tab--; psr_printTab(); psr_match("}"); }
	else { psr_newline(); psr_tab++; psr_statement(); psr_tab--; }
}

function psr_whileConstruct(){
	psr_match("while"); psr_space(); psr_match("("); psr_exp(); psr_match(")");
	if (psr_look("{")){ psr_match("{"); psr_newline(); psr_tab++; psr_statementBlock(); psr_tab--; psr_printTab(); psr_match("}"); }
	else { psr_newline(); psr_tab++; psr_statement(); psr_tab--; }
}

function psr_doConstruct(){
	psr_match("do"); psr_space(); psr_match("{"); psr_newline(); psr_tab++;	psr_statementBlock();	psr_tab--; psr_printTab(); psr_match("}"); 
	psr_space(); psr_match("while"); psr_space(); psr_match("("); psr_exp(); psr_match(")");
}

function psr_switchConstruct(){
	psr_match("switch"); psr_space(); psr_match("(");
	psr_exp(); psr_match(")"); psr_match("{"); psr_newline(); psr_tab++;
	while (psr_t!=null && !psr_look("}")){
		psr_printTab();
		if (look("case")) psr_match("case");
		else if (look("default")) psr_match("default");
		else addReport("excpected \"case\" or \"default\"");
		psr_space(); psr_exp();
		psr_space(); psr_match(":"); psr_newline();
		while (psr_t!=null && !psr_look("case") && !psr_look("}")){
			psr_tab++; psr_statement(); psr_tab--;
		}
	}
	psr_tab--; psr_printTab(); psr_match("}");
}

function psr_functionConstruct(){
	psr_match("(");

	if (!psr_look(")")){
		psr_dataType();
		if (!psr_look(")") && !psr_look("[")) psr_space();
		while (psr_look("*")) psr_match("*");
		while (psr_look("[")){ psr_match("["); psr_match("]"); }

		if (psr_look(",")){ /* just a prototype */
			while (psr_look(",")){
				psr_match(",");
				psr_dataType();
				if (!psr_look(")") && !psr_look("[")) psr_space();
				while (psr_look("*")) psr_match("*");
				while (psr_look("[")){ psr_match("["); psr_match("]"); }
			}
		}
		else if (!psr_look(")")){
			psr_variable();
			while (psr_look(",")){
				psr_match(",");
				psr_space();
				psr_dataType();
				psr_space();
				psr_variable();
			}
		}
	}
	psr_match(")");

	if (psr_look(";")){ psr_match(";"); }
	else if (psr_look("{")){ psr_match("{"); psr_newline(); psr_tab++; psr_statementBlock(); psr_tab--; psr_printTab(); psr_match("}");}
	else psr_addReport("Expexted ; or { found "+psr_t.token);
}

function psr_blockInitialization(){
	if (psr_look("{")){
		psr_match("{");
		psr_newline(); psr_tab++;
		while (psr_t!=null && !psr_look("}")) psr_initialization();
		if (psr_look("}")) { psr_nextToken(); psr_space(); psr_tab--; }
		else psr_addReport("Expected }");
	}
	else psr_addReport("Expected {");
}

/* returns true if its a struct definition */
function psr_dataType(){
	if (psr_t==null) {
		psr_addReport("Expected a Data Type");
		return false;
	}

	if (psr_look("struct")){
		psr_match("struct"); psr_space();
		if (psr_look("{")) psr_blockInitialization();
		else if (psr_isIdentifier()){
			if (!psr_isKeyword2("struct "+psr_t.token)){
				psr_newDataType.add("struct "+psr_t.token);
				psr_nextToken();
				psr_blockInitialization();
				return true;
			}
			else psr_nextToken();
		}
		else psr_addReport("Expected { or a name");
	}
	else if (psr_isKeyword()) while (psr_isKeyword()) psr_nextToken();
	else psr_addReport(psr_t.token + " is NOT a valid data type!");

	return false;
}

function psr_chainInitialization(){
	if (psr_look("=")){ psr_space(); psr_match("="); psr_space(); psr_exp(); }
	while (psr_look(",")){
		psr_match(","); psr_space(); psr_variable();
		if (psr_look("=")){ psr_match("="); psr_exp(); }
	}
	psr_match(";");
}

function psr_initialization(){
	psr_printTab();
	/* if it's just struct definition, no need variables */
	var structDef = psr_dataType();
	if (!structDef || !psr_look(";")){
		psr_space(); psr_variable();
		while (psr_look(",")){ psr_match(","); psr_variable(); }
	}
	psr_match(";");
	psr_newline();
}

function psr_typedefConstruct(){
	psr_match("typedef");
	psr_space();
	psr_dataType();
	psr_space();
	psr_newDataType.add(psr_t.token);
	psr_nextToken();
	psr_match(";");
	psr_newline();
}

function psr_directiveConstruct(){
	psr_nextUnfilteredToken();
	psr_space();
	while (psr_t!=null && !psr_isNewline()) psr_nextUnfilteredToken();
	psr_nextToken();
}

function psr_statement(){
	if (psr_t==null){
		psr_addReport("Expected a Statement");
		return false; 
	}

	psr_printTab();
	if (psr_isDirective()) psr_directiveConstruct()
	else if (psr_look("if")) psr_ifConstruct();
	else if (psr_look("for")) psr_forConstruct();
	else if (psr_look("while")) psr_whileConstruct();
	else if (psr_look("do")) psr_doConstruct();
	else if (psr_look("switch")) psr_switchConstruct();
	else if (psr_look("break") || psr_look("continue")){ psr_nextToken(); psr_match(";"); }
	else if (psr_look("return")){ psr_nextToken(); psr_space(); psr_exp(); psr_match(";"); }
	else if (psr_look("struct")) psr_initialization();
	else if (psr_look("typedef")) psr_typedefConstruct();
	else if (psr_isKeyword()){
		psr_nextToken(); psr_space(); psr_variable();
		if (psr_look("(")) psr_functionConstruct();
		else if (psr_look(";")) psr_match(";");
		else psr_chainInitialization();
	}
	else if (psr_look("{")){ psr_match("{"); psr_newline(); psr_tab++; psr_statementBlock(); psr_tab--; psr_printTab(); psr_match("}"); }
	else if (psr_isIdentifier() || psr_isOperator()){ psr_exp(); psr_match(";");  }
	else if (psr_look(";")) psr_match(";");
	else {
		psr_addReport("Invalid statement : "+psr_t.token + " - " + psr_t.token_class);
		psr_nextToken();
	}
	if (!psr_prevIsCloseBrace()) psr_newline();
}

function psr_statementBlock(){
	while (psr_t!=null && !psr_look("}")) psr_statement();
}

function psr_globalStatements(){
	if (psr_t==null){
		psr_addReport("Expected a Global Statement");
		return false; 
	}

	while (psr_t!=null){
		while (	psr_look("auto")			|| psr_look("short")		|| psr_look("register") ||
						psr_look("signed")		|| psr_look("unsigned") || psr_look("static")		||
						psr_look("volatile")	|| psr_look("extern")		|| psr_look("explicit") || psr_look("virtual"))
			nextToken();
		     if (psr_isDirective()) psr_directiveConstruct();
		else if (psr_look("struct")) psr_initialization();
		else if (psr_look("typedef")) psr_typedefConstruct();
		else if (psr_isKeyword()){
			psr_nextToken(); psr_space(); psr_variable();
			if (psr_look("(")) psr_functionConstruct();
			else if (psr_look(";")) psr_match(";");
			else psr_chainInitialization();
		}
		else if (psr_look("{")){ psr_match("{"); psr_newline(); psr_tab++; psr_statementBlock(); psr_tab--; psr_printTab(); psr_match("}"); }
		else if (psr_isIdentifier() || psr_isOperator()){ psr_variable(); psr_functionConstruct(); }
		else if (psr_look(";")) psr_match(";");
		else {
			psr_addReport("Invalid Global Statement : "+psr_t.token + " - " + psr_t.token_class);
			psr_nextToken();
		}
		psr_newline();
		psr_newline();
	}
/*
var tzr_keywords = ['auto','bool','break','case','catch','char','cerr','cin','class','const','continue',
'cout','default','delete','do','double','else','enum','explicit','extern','float','for','friend',
'goto','if','inline','int','long','namespace','new','operator','private','protected','public',
'register','return','short','signed','sizeof','static','struct','switch','template','this','throw',
'try','typedef','union','unsigned','virtual','void','volatile','while','__asm','__fastcall','__based',
'__cdecl','__pascal','__inline','__multiple_inheritance','__single_inheritance','__virtual_inheritance'];
*/
}

function psr_format(){
	psr_write("<div style='background-color:#000000; width:700; padding:10px; padding-left:30px; padding-right:30px; padding-bottom:25px'>");
	psr_write("<font face='courier new' size=-1>");

	psr_skipUnusedToken();
	psr_globalStatements();

	psr_write("</font>");

	psr_write("<font color=white>");
	psr_write("<hr>"+psr_report());
	psr_write("</font>");

	psr_write("</div>");

	return psr_list.list;
}
