/*
php.js
Copyright 2004 David Barnett

    This file is part of jsEDIT.

    jsEDIT is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    jsEDIT is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with jsEDIT; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

var NUM_TYPES;
var TOKEN_TYPE_WORD;
var TOKEN_TYPE_COMMENT;
var TOKEN_TYPE_MLCOMMENT;
var TOKEN_TYPE_1QUOTESTRING;
var TOKEN_TYPE_2QUOTESTRING;
var TOKEN_TYPE_PARENTHESES;
var TOKEN_TYPE_BRACKETS;
var TOKEN_TYPE_OBRACE;
var TOKEN_TYPE_CBRACE;
var TOKEN_TYPE_CONST;
var TOKEN_TYPE_VAR;
var TOKEN_TYPE_WHITESP;
var TOKEN_TYPE_ESCCHAR;
var TOKEN_TYPE_NEWLINE;
var TOKEN_TYPE_OPERATOR;
var TOKEN_TYPE_SEMICOLON;
var TOKEN_TYPE_MISC;

var NUM_SUBTYPES;
var TOKEN_SUBTYPE_FUNCTION;
var TOKEN_SUBTYPE_KEYWORD;
var TOKEN_SUBTYPE_VALIDVAR;
var TOKEN_SUBTYPE_INVALIDVAR;

var subTypes;

var tokenRegExps;
var tokenRegExpsFull;
var tokenRegExpsCont;
var tokenRegExpsNoEnd;
var tokenInterrupts;
var tokenTags;
var subTokenTags;
var tokenColors;
var subTokenColors;
var varDefPatterns;
var varDefProcessFuncs;
var preHaltRegExps;

function getSubType(token, scopeStack, lineScopeList)
{
	if (token.type == TOKEN_TYPE_WORD)
	{
		if (findHash(token.str, php_keywords, php_keywords_depth, php_keywords_size))
			return TOKEN_SUBTYPE_KEYWORD;
		if (findHash(token.str, php_functions, php_functions_depth, php_functions_size))
			return TOKEN_SUBTYPE_FUNCTION;
	}
	else if (token.type == TOKEN_TYPE_VAR)
	{
		if (contains(lineScopeList, token.str.replace(/[\$\{\}]/g, "")) ||
			(scopeStackContains(scopeStack, token.str.replace(/[\$\{\}]/g, ""))))
				return TOKEN_SUBTYPE_VALIDVAR;
		return TOKEN_SUBTYPE_INVALIDVAR;
	}
	return -1;
}

function varDefProcessGlobal (tokenIndex, tokens, scopeStack, nextScopeList, lineScopeList)
{
	if ((++tokenIndex < tokens.length) &&
		(tokens[tokenIndex].type == TOKEN_TYPE_WHITESP))
		while ((++tokenIndex < tokens.length) && 
			(tokens[tokenIndex].type == TOKEN_TYPE_VAR))
		{
			var curVar = tokens[tokenIndex].str.replace(/[\$\{\}]/g, "");
			if (!scopeStackContains(scopeStack, curVar))
				scopeStack[scopeStack.length - 1].push(curVar);
			if ((tokenIndex + 2 < tokens.length) &&
				(tokens[tokenIndex + 1].type == TOKEN_TYPE_MISC) &&
				(tokens[tokenIndex + 1].str == ",") &&
				(tokens[tokenIndex + 2].type == TOKEN_TYPE_WHITESP))
				tokenIndex += 2;
			else
				break;
		}
}

function varDefProcessAssign (tokenIndex, tokens, scopeStack, nextScopeList, lineScopeList)
{
	var curVar = tokens[tokenIndex].str.replace(/[\$\{\}]/g, "");
/*	if ((tokenIndex + 1 < tokens.length) &&
		(tokens[tokenIndex + 1].type == TOKEN_TYPE_WHITESP))
		++tokenIndex;	*/
	while ((tokenIndex + 1 < tokens.length) &&
		(tokens[tokenIndex + 1].type == TOKEN_TYPE_BRACKETS) &&
		(tokens[tokenIndex + 1].str == "["))
	{
		++tokenIndex;
		tokenIndex = skipTokens(tokenIndex, tokens, false, TOKEN_TYPE_BRACKETS, "]");
		++tokenIndex;
/*		if ((tokenIndex + 1 < tokens.length) &&
			(tokens[tokenIndex + 1].type == TOKEN_TYPE_WHITESP))
			++tokenIndex;	*/
	}
	
	if ((tokenIndex + 1 < tokens.length) &&
		(tokens[tokenIndex + 1].type == TOKEN_TYPE_WHITESP))
		++tokenIndex;
	if ((++tokenIndex < tokens.length) &&
		(tokens[tokenIndex].type == TOKEN_TYPE_OPERATOR) &&
		(tokens[tokenIndex].str == "="))
		if (!scopeStackContains(scopeStack, curVar))
			scopeStack[scopeStack.length - 1].push(curVar);
}

function forEachProcess (tokenIndex, tokens, scopeStack, nextScopeList, lineScopeList)
{
	if ((++tokenIndex < tokens.length) &&
		(tokens[tokenIndex].type == TOKEN_TYPE_WHITESP))
		if ((++tokenIndex < tokens.length) &&
			(tokens[tokenIndex].type == TOKEN_TYPE_PARENTHESES) &&
			(tokens[tokenIndex].str == "("))
		{
			if ((tokenIndex + 1 < tokens.length) &&
				(tokens[tokenIndex + 1].type == TOKEN_TYPE_WHITESP))
				++tokenIndex;
			if ((++tokenIndex < tokens.length) &&
				(tokens[tokenIndex].type == TOKEN_TYPE_VAR))
				if ((++tokenIndex < tokens.length) &&
					(tokens[tokenIndex].type == TOKEN_TYPE_WHITESP))
					if ((++tokenIndex < tokens.length) &&
						(tokens[tokenIndex].type == TOKEN_TYPE_WORD) &&
						(tokens[tokenIndex].str == "as"))
						if ((++tokenIndex < tokens.length) &&
							(tokens[tokenIndex].type == TOKEN_TYPE_WHITESP))
							if ((++tokenIndex < tokens.length) &&
								(tokens[tokenIndex].type == TOKEN_TYPE_VAR))
							{
								curVar = tokens[tokenIndex].str.replace(/[\$\{\}]/g, "");
								if (!scopeStackContains(scopeStack, curVar))
								{
									nextScopeList.push(curVar);
									lineScopeList.push(curVar);
								}
								if ((++tokenIndex < tokens.length) &&
									(tokens[tokenIndex].type == TOKEN_TYPE_OPERATOR) &&
									(tokens[tokenIndex].str == "=>"))
									if ((++tokenIndex < tokens.length) &&
										(tokens[tokenIndex].type == TOKEN_TYPE_VAR))
									{
										curVar = tokens[tokenIndex].str.replace(/[\$\{\}]/g, "");
										if (!scopeStackContains(scopeStack, curVar))
										{
											nextScopeList.push(curVar);
											lineScopeList.push(curVar);
										}
									}
							}
		}
}

function funcDefProcess (tokenIndex, tokens, scopeStack, nextScopeList, lineScopeList)
{
	if ((++tokenIndex < tokens.length) &&
		(tokens[tokenIndex].type == TOKEN_TYPE_WHITESP))
		if ((++tokenIndex < tokens.length) &&
			(tokens[tokenIndex].type == TOKEN_TYPE_WORD))
		{
			if ((tokenIndex + 1 < tokens.length) &&
				(tokens[tokenIndex + 1].type == TOKEN_TYPE_WHITESP))
				++tokenIndex;
			if ((++tokenIndex < tokens.length) &&
				(tokens[tokenIndex].type == TOKEN_TYPE_PARENTHESES) &&
				(tokens[tokenIndex].str == "("))
			{
				tokenIndex = skipTokens(tokenIndex, tokens, true, TOKEN_TYPE_WHITESP, null, TOKEN_TYPE_NEWLINE, null);
				while ((++tokenIndex < tokens.length) &&
					(tokens[tokenIndex].type == TOKEN_TYPE_VAR))
				{
					var curVar = tokens[tokenIndex].str.replace(/[\$\{\}]/g, "");
					if (!scopeStackContains(scopeStack, curVar))
					{
						nextScopeList.push(curVar);
						lineScopeList.push(curVar);
					}
					tokenIndex = skipTokens(tokenIndex, tokens, true, TOKEN_TYPE_WHITESP, null, TOKEN_TYPE_NEWLINE, null);
					if ((tokenIndex + 1 < tokens.length) &&
						(tokens[tokenIndex + 1].type == TOKEN_TYPE_MISC) &&
						(tokens[tokenIndex + 1].str == ","))
						tokenIndex += 1;
					else
						break;
					tokenIndex = skipTokens(tokenIndex, tokens, true, TOKEN_TYPE_WHITESP, null, TOKEN_TYPE_NEWLINE, null);
				}
			}
		}
}

function setLanguage()
{
	NUM_TYPES = 17;
	TOKEN_TYPE_WORD = 0;
	TOKEN_TYPE_COMMENT = 1;
	TOKEN_TYPE_MLCOMMENT = 2;
	TOKEN_TYPE_1QUOTESTRING = 3;
	TOKEN_TYPE_2QUOTESTRING = 4;
	TOKEN_TYPE_PARENTHESES = 5;
	TOKEN_TYPE_BRACKETS = 6;
	TOKEN_TYPE_OBRACE = 7;
	TOKEN_TYPE_CBRACE = 8;
	TOKEN_TYPE_CONST = 9;
	TOKEN_TYPE_VAR = 10;
	TOKEN_TYPE_WHITESP = 11;
	TOKEN_TYPE_ESCCHAR = 12;
	TOKEN_TYPE_NEWLINE = 13;
	TOKEN_TYPE_OPERATOR = 14;
	TOKEN_TYPE_SEMICOLON = 15;
	TOKEN_TYPE_MISC = 16;
	
	NUM_SUBTYPES = 4;
	TOKEN_SUBTYPE_FUNCTION = 0;
	TOKEN_SUBTYPE_KEYWORD = 1;
	TOKEN_SUBTYPE_VALIDVAR = 2;
	TOKEN_SUBTYPE_INVALIDVAR = 3;
	
	// keep track of the subtypes for each token type
	subTypes = new Array();
	subTypes[TOKEN_TYPE_WORD] = new Array(TOKEN_SUBTYPE_FUNCTION,
		TOKEN_SUBTYPE_KEYWORD);
	subTypes[TOKEN_TYPE_VAR] = new Array(TOKEN_SUBTYPE_VALIDVAR,
		TOKEN_SUBTYPE_INVALIDVAR);
	
	// store regexps representing token types for parser
	tokenRegExps = new Array();
	var emptyRegExp = /(?:)/;
	tokenRegExps[TOKEN_TYPE_WORD] = new Array(/[^\W\d\n]/, /[^\W\d\n]*/, emptyRegExp);
	tokenRegExps[TOKEN_TYPE_COMMENT] = new Array(/\/\//, /.*/, emptyRegExp);

	tokenRegExps[TOKEN_TYPE_MLCOMMENT] = new Array(/\/(?=\*)/, /([^\*]|\*(?!\/))*/, /(\*\/)/);
	tokenRegExps[TOKEN_TYPE_1QUOTESTRING] = new Array(/'/, /[^']*/, /'/);
	tokenRegExps[TOKEN_TYPE_2QUOTESTRING] = new Array(/"/, /[^"]*/, /"/);
	tokenRegExps[TOKEN_TYPE_PARENTHESES] = new Array(/\(/, /[^\)]*/, /\)/);
	tokenRegExps[TOKEN_TYPE_BRACKETS] = new Array(/\[/, /[^\]]*/, /\]/);
	tokenRegExps[TOKEN_TYPE_OBRACE] = new Array(/\{/, emptyRegExp, emptyRegExp);
	tokenRegExps[TOKEN_TYPE_CBRACE] = new Array(/\}/, emptyRegExp, emptyRegExp);
	tokenRegExps[TOKEN_TYPE_CONST] = new Array(/\d/, /\d*/, emptyRegExp);
	tokenRegExps[TOKEN_TYPE_VAR] = new Array(/\$/, /(\w[\w\d]*|\{\w[\w\d]*\})/, emptyRegExp);
	tokenRegExps[TOKEN_TYPE_WHITESP] = new Array(/[ \t]/, /[ \t]*/, emptyRegExp);
	tokenRegExps[TOKEN_TYPE_ESCCHAR] = new Array(/\\/, /./, emptyRegExp);
	tokenRegExps[TOKEN_TYPE_NEWLINE] = new Array(/\n/, emptyRegExp, emptyRegExp);
	tokenRegExps[TOKEN_TYPE_OPERATOR] = new Array(/=>|[\+\-\*\/=%><]=?|\+\+|\-\-|&&|\|\|/, emptyRegExp, emptyRegExp);
	tokenRegExps[TOKEN_TYPE_SEMICOLON] = new Array(/;/, emptyRegExp, emptyRegExp);
	tokenRegExps[TOKEN_TYPE_MISC] = new Array(/./, emptyRegExp, emptyRegExp);
	
	// tells the parser which tokens can be interrupted by which other tokens
	// and be continued afterward
	tokenInterrupts = new Array();
	var allTokens = new Array(TOKEN_TYPE_WORD,
		TOKEN_TYPE_COMMENT,
		TOKEN_TYPE_MLCOMMENT,
		TOKEN_TYPE_1QUOTESTRING,
		TOKEN_TYPE_2QUOTESTRING,
		TOKEN_TYPE_PARENTHESES,
		TOKEN_TYPE_BRACKETS,
		TOKEN_TYPE_OBRACE,
		TOKEN_TYPE_CBRACE,
		TOKEN_TYPE_CONST,
		TOKEN_TYPE_VAR,
		TOKEN_TYPE_WHITESP,
		TOKEN_TYPE_ESCCHAR,
		TOKEN_TYPE_NEWLINE,
		TOKEN_TYPE_OPERATOR,
		TOKEN_TYPE_SEMICOLON,
		TOKEN_TYPE_MISC);
	tokenInterrupts[TOKEN_TYPE_WORD] = new Array();
	tokenInterrupts[TOKEN_TYPE_COMMENT] = new Array();
	tokenInterrupts[TOKEN_TYPE_MLCOMMENT] = new Array();
	tokenInterrupts[TOKEN_TYPE_MLCOMMENT][0] = TOKEN_TYPE_NEWLINE;
	tokenInterrupts[TOKEN_TYPE_1QUOTESTRING] = new Array();
	tokenInterrupts[TOKEN_TYPE_1QUOTESTRING][0] = TOKEN_TYPE_NEWLINE;
	tokenInterrupts[TOKEN_TYPE_2QUOTESTRING] = new Array(TOKEN_TYPE_VAR,
		TOKEN_TYPE_ESCCHAR,
		TOKEN_TYPE_NEWLINE);
	tokenInterrupts[TOKEN_TYPE_PARENTHESES] = allTokens;
	tokenInterrupts[TOKEN_TYPE_BRACKETS] = allTokens;
	tokenInterrupts[TOKEN_TYPE_OBRACE] = new Array();
	tokenInterrupts[TOKEN_TYPE_CBRACE] = new Array();
	tokenInterrupts[TOKEN_TYPE_CONST] = new Array();
	tokenInterrupts[TOKEN_TYPE_VAR] = new Array();
	tokenInterrupts[TOKEN_TYPE_WHITESP] = new Array();
	tokenInterrupts[TOKEN_TYPE_ESCCHAR] = new Array();
	tokenInterrupts[TOKEN_TYPE_NEWLINE] = new Array();
	tokenInterrupts[TOKEN_TYPE_OPERATOR] = new Array();
	tokenInterrupts[TOKEN_TYPE_SEMICOLON] = new Array();
	tokenInterrupts[TOKEN_TYPE_MISC] = new Array();
	
	// tags to add before and after each token type
	var fontTags = new Array("<font color=\"", "\">", "</font>");
	var emptyTags = new Array("", "", "");
	tokenTags = new Array();
	tokenTags[TOKEN_TYPE_WORD] = emptyTags;
	tokenTags[TOKEN_TYPE_COMMENT] = new Array("<i>", "",  "</i>");
	tokenTags[TOKEN_TYPE_MLCOMMENT] = tokenTags[TOKEN_TYPE_COMMENT];
	tokenTags[TOKEN_TYPE_1QUOTESTRING] = new Array("<b>", "", "</b>");
	tokenTags[TOKEN_TYPE_2QUOTESTRING] = fontTags;
	tokenTags[TOKEN_TYPE_PARENTHESES] = emptyTags;
	tokenTags[TOKEN_TYPE_BRACKETS] = emptyTags;
	tokenTags[TOKEN_TYPE_OBRACE] = emptyTags;
	tokenTags[TOKEN_TYPE_CBRACE] = emptyTags;
	tokenTags[TOKEN_TYPE_CONST] = fontTags;
	tokenTags[TOKEN_TYPE_VAR] = fontTags;
	tokenTags[TOKEN_TYPE_WHITESP] = emptyTags;
	tokenTags[TOKEN_TYPE_ESCCHAR] = fontTags;
	tokenTags[TOKEN_TYPE_NEWLINE] = emptyTags;
	tokenTags[TOKEN_TYPE_OPERATOR] = emptyTags;
	tokenTags[TOKEN_TYPE_SEMICOLON] = emptyTags;
	tokenTags[TOKEN_TYPE_MISC] = emptyTags;
	
	subTokenTags = new Array();
	subTokenTags[TOKEN_SUBTYPE_FUNCTION] = fontTags;
	subTokenTags[TOKEN_SUBTYPE_KEYWORD] = fontTags;
	subTokenTags[TOKEN_SUBTYPE_VALIDVAR] = fontTags;
	subTokenTags[TOKEN_SUBTYPE_INVALIDVAR] = new Array("<blink><font color=\"", "\">", "</font></blink>");
	
	tokenColors = new Array();
	tokenColors[TOKEN_TYPE_WORD] = "";
	tokenColors[TOKEN_TYPE_COMMENT] = "";
	tokenColors[TOKEN_TYPE_MLCOMMENT] = "";
	tokenColors[TOKEN_TYPE_1QUOTESTRING] = "";
	tokenColors[TOKEN_TYPE_2QUOTESTRING] = "#880088";
	tokenColors[TOKEN_TYPE_PARENTHESES] = "";
	tokenColors[TOKEN_TYPE_BRACKETS] = "";
	tokenColors[TOKEN_TYPE_OBRACE] = "";
	tokenColors[TOKEN_TYPE_CBRACE] = "";
	tokenColors[TOKEN_TYPE_CONST] = "#990000";
	tokenColors[TOKEN_TYPE_VAR] = "";
	tokenColors[TOKEN_TYPE_WHITESP] = "";
	tokenColors[TOKEN_TYPE_ESCCHAR] = "#008888";
	tokenColors[TOKEN_TYPE_NEWLINE] = "";
	tokenColors[TOKEN_TYPE_OPERATOR] = "";
	tokenColors[TOKEN_TYPE_SEMICOLON] = "";
	tokenColors[TOKEN_TYPE_MISC] = "";
	
	subTokenColors = new Array();
	subTokenColors[TOKEN_SUBTYPE_FUNCTION] = "#990000";
	subTokenColors[TOKEN_SUBTYPE_KEYWORD] = "#008800";
	subTokenColors[TOKEN_SUBTYPE_VALIDVAR] = "#000088";
	subTokenColors[TOKEN_SUBTYPE_INVALIDVAR] = "#FF0000";

	// language specific language definitions for variable highlighting
	// i.e. DIM (BASIC), var (JScript), int (C)
	// (last element is assignment pattern)
	varDefPatterns = new Array(/\bglobal\b/,
		/\bvar\b/,
		mergeRegExps(tokenRegExps[TOKEN_TYPE_VAR][0],
			tokenRegExps[TOKEN_TYPE_VAR][1]),
		/\bforeach\b/,
		/\bfunction\b/);
	varDefProcessFuncs = new Array(varDefProcessGlobal, varDefProcessGlobal, varDefProcessAssign, forEachProcess, funcDefProcess);
	preHaltRegExps = getPreHaltRegExps();
}
