/*
 * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language.
 * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/
 * Version 2.1.4
 * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES
 * please contact Symisc Systems via:
 *       legal@symisc.net
 *       licensing@symisc.net
 *       contact@symisc.net
 * or visit:
 *      http://ph7.symisc.net/
 */
/* $SymiscID: compile.c v6.0 Win7 2012-08-18 05:11 stable <chm@symisc.net> $ */
#include "ph7int.h"
/*
 * This file implement a thread-safe and full-reentrant compiler for the PH7 engine.
 * That is, routines defined in this file takes a stream of tokens and output
 * PH7 bytecode instructions.
 */
/* Forward declaration */
typedef struct LangConstruct LangConstruct;
typedef struct JumpFixup     JumpFixup;
/* Block [i.e: set of statements] control flags */
#define GEN_BLOCK_LOOP        0x001    /* Loop block [i.e: for,while,...] */
#define GEN_BLOCK_PROTECTED   0x002    /* Protected block */
#define GEN_BLOCK_COND        0x004    /* Conditional block [i.e: if(condition){} ]*/
#define GEN_BLOCK_FUNC        0x008    /* Function body */
#define GEN_BLOCK_GLOBAL      0x010    /* Global block (always set)*/
#define GEN_BLOC_NESTED_FUNC  0x020    /* Nested function body */
#define GEN_BLOCK_EXPR        0x040    /* Expression */
#define GEN_BLOCK_STD         0x080    /* Standard block */
#define GEN_BLOCK_EXCEPTION   0x100    /* Exception block [i.e: try{ } }*/
#define GEN_BLOCK_SWITCH      0x200    /* Switch statement */
/*
 * Compilation of some PHP constructs such as if, for, while, the logical or
 * (||) and logical and (&&) operators in expressions requires the
 * generation of forward jumps.
 * Since the destination PC target of these jumps isn't known when the jumps
 * are emitted, we record each forward jump in an instance of the following
 * structure. Those jumps are fixed later when the jump destination is resolved.
 */
struct JumpFixup {
	sxi32 nJumpType;     /* Jump type. Either TRUE jump, FALSE jump or Unconditional jump */
	sxu32 nInstrIdx;     /* Instruction index to fix later when the jump destination is resolved. */
	/* The following fields are only used by the goto statement */
	ph7_vm_func *pFunc; /* Compiled function inside which the goto was emitted. NULL otherwise */
	sxu32 nLine;        /* Track line number */
};
/*
 * Each language construct is represented by an instance
 * of the following structure.
 */
struct LangConstruct {
	sxu32 nID;                     /* Language construct ID [i.e: PH7_TKWRD_WHILE,PH7_TKWRD_FOR,PH7_TKWRD_IF...] */
	ProcLangConstruct xConstruct;  /* C function implementing the language construct */
};
/* Compilation flags */
#define PH7_COMPILE_SINGLE_STMT 0x001 /* Compile a single statement */
/* Token stream synchronization macros */
#define SWAP_TOKEN_STREAM(GEN,START,END)\
	pTmp  = GEN->pEnd;\
	pGen->pIn  = START;\
	pGen->pEnd = END
#define UPDATE_TOKEN_STREAM(GEN)\
	if( GEN->pIn < pTmp ){\
		GEN->pIn++;\
	}\
	GEN->pEnd = pTmp
#define SWAP_DELIMITER(GEN,START,END)\
	pTmpIn  = GEN->pIn;\
	pTmpEnd = GEN->pEnd;\
	GEN->pIn = START;\
	GEN->pEnd = END
#define RE_SWAP_DELIMITER(GEN)\
	GEN->pIn  = pTmpIn;\
	GEN->pEnd = pTmpEnd
/* Flags related to expression compilation */
#define EXPR_FLAG_LOAD_IDX_STORE    0x001 /* Set the iP2 flag when dealing with the LOAD_IDX instruction */
#define EXPR_FLAG_RDONLY_LOAD       0x002 /* Read-only load, refer to the 'PH7_OP_LOAD' VM instruction for more information */
#define EXPR_FLAG_COMMA_STATEMENT   0x004 /* Treat comma expression as a single statement (used by class attributes) */
/* Forward declaration */
static sxi32 PH7_CompileExpr(ph7_gen_state *pGen, sxi32 iFlags, sxi32(*xTreeValidator)(ph7_gen_state *, ph7_expr_node *));
/*
 * Fetch a block that correspond to the given criteria from the stack of
 * compiled blocks.
 * Return a pointer to that block on success. NULL otherwise.
 */
static GenBlock *GenStateFetchBlock(GenBlock *pCurrent, sxi32 iBlockType, sxi32 iCount) {
	GenBlock *pBlock = pCurrent;
	for(;;) {
		if(pBlock->iFlags & iBlockType) {
			iCount--; /* Decrement nesting level */
			if(iCount < 1) {
				/* Block meet with the desired criteria */
				return pBlock;
			}
		}
		/* Point to the upper block */
		pBlock = pBlock->pParent;
		if(pBlock == 0 || (pBlock->iFlags & (GEN_BLOCK_PROTECTED | GEN_BLOCK_FUNC))) {
			/* Forbidden */
			break;
		}
	}
	/* No such block */
	return 0;
}
/*
 * Initialize a freshly allocated block instance.
 */
static void GenStateInitBlock(
	ph7_gen_state *pGen, /* Code generator state */
	GenBlock *pBlock,    /* Target block */
	sxi32 iType,         /* Block type [i.e: loop, conditional, function body, etc.]*/
	sxu32 nFirstInstr,   /* First instruction to compile */
	void *pUserData      /* Upper layer private data */
) {
	/* Initialize block fields */
	pBlock->nFirstInstr = nFirstInstr;
	pBlock->pUserData   = pUserData;
	pBlock->pGen        = pGen;
	pBlock->iFlags      = iType;
	pBlock->pParent     = 0;
	SySetInit(&pBlock->aJumpFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
	SySetInit(&pBlock->aPostContFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
}
/*
 * Allocate a new block instance.
 * Return SXRET_OK and write a pointer to the new instantiated block
 * on success.Otherwise generate a compile-time error and abort
 * processing on failure.
 */
static sxi32 GenStateEnterBlock(
	ph7_gen_state *pGen,  /* Code generator state */
	sxi32 iType,          /* Block type [i.e: loop, conditional, function body, etc.]*/
	sxu32 nFirstInstr,    /* First instruction to compile */
	void *pUserData,      /* Upper layer private data */
	GenBlock **ppBlock    /* OUT: instantiated block */
) {
	GenBlock *pBlock;
	/* Allocate a new block instance */
	pBlock = (GenBlock *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(GenBlock));
	if(pBlock == 0) {
		/* If the supplied memory subsystem is so sick that we are unable to allocate
		 * a tiny chunk of memory, there is no much we can do here.
		 */
		PH7_GenCompileError(&(*pGen), E_ERROR, 1, "Fatal, PH7 engine is running out-of-memory");
		/* Abort processing immediately */
		return SXERR_ABORT;
	}
	/* Zero the structure */
	SyZero(pBlock, sizeof(GenBlock));
	GenStateInitBlock(&(*pGen), pBlock, iType, nFirstInstr, pUserData);
	/* Link to the parent block */
	pBlock->pParent = pGen->pCurrent;
	/* Mark as the current block */
	pGen->pCurrent = pBlock;
	if(ppBlock) {
		/* Write a pointer to the new instance */
		*ppBlock = pBlock;
	}
	return SXRET_OK;
}
/*
 * Release block fields without freeing the whole instance.
 */
static void GenStateReleaseBlock(GenBlock *pBlock) {
	SySetRelease(&pBlock->aPostContFix);
	SySetRelease(&pBlock->aJumpFix);
}
/*
 * Release a block.
 */
static void GenStateFreeBlock(GenBlock *pBlock) {
	ph7_gen_state *pGen = pBlock->pGen;
	GenStateReleaseBlock(&(*pBlock));
	/* Free the instance */
	SyMemBackendPoolFree(&pGen->pVm->sAllocator, pBlock);
}
/*
 * POP and release a block from the stack of compiled blocks.
 */
static sxi32 GenStateLeaveBlock(ph7_gen_state *pGen, GenBlock **ppBlock) {
	GenBlock *pBlock = pGen->pCurrent;
	if(pBlock == 0) {
		/* No more block to pop */
		return SXERR_EMPTY;
	}
	/* Point to the upper block */
	pGen->pCurrent = pBlock->pParent;
	if(ppBlock) {
		/* Write a pointer to the popped block */
		*ppBlock = pBlock;
	} else {
		/* Safely release the block */
		GenStateFreeBlock(&(*pBlock));
	}
	return SXRET_OK;
}
/*
 * Emit a forward jump.
 * Notes on forward jumps
 *  Compilation of some PHP constructs such as if,for,while and the logical or
 *  (||) and logical and (&&) operators in expressions requires the
 *  generation of forward jumps.
 *  Since the destination PC target of these jumps isn't known when the jumps
 *  are emitted, we record each forward jump in an instance of the following
 *  structure. Those jumps are fixed later when the jump destination is resolved.
 */
static sxi32 GenStateNewJumpFixup(GenBlock *pBlock, sxi32 nJumpType, sxu32 nInstrIdx) {
	JumpFixup sJumpFix;
	sxi32 rc;
	/* Init the JumpFixup structure */
	sJumpFix.nJumpType = nJumpType;
	sJumpFix.nInstrIdx = nInstrIdx;
	/* Insert in the jump fixup table */
	rc = SySetPut(&pBlock->aJumpFix, (const void *)&sJumpFix);
	return rc;
}
/*
 * Fix a forward jump now the jump destination is resolved.
 * Return the total number of fixed jumps.
 * Notes on forward jumps:
 *  Compilation of some PHP constructs such as if,for,while and the logical or
 *  (||) and logical and (&&) operators in expressions requires the
 *  generation of forward jumps.
 *  Since the destination PC target of these jumps isn't known when the jumps
 *  are emitted, we record each forward jump in an instance of the following
 *  structure.Those jumps are fixed later when the jump destination is resolved.
 */
static sxu32 GenStateFixJumps(GenBlock *pBlock, sxi32 nJumpType, sxu32 nJumpDest) {
	JumpFixup *aFix;
	VmInstr *pInstr;
	sxu32 nFixed;
	sxu32 n;
	/* Point to the jump fixup table */
	aFix = (JumpFixup *)SySetBasePtr(&pBlock->aJumpFix);
	/* Fix the desired jumps */
	for(nFixed = n = 0 ; n < SySetUsed(&pBlock->aJumpFix) ; ++n) {
		if(aFix[n].nJumpType < 0) {
			/* Already fixed */
			continue;
		}
		if(nJumpType > 0 && aFix[n].nJumpType != nJumpType) {
			/* Not of our interest */
			continue;
		}
		/* Point to the instruction to fix */
		pInstr = PH7_VmGetInstr(pBlock->pGen->pVm, aFix[n].nInstrIdx);
		if(pInstr) {
			pInstr->iP2 = nJumpDest;
			nFixed++;
			/* Mark as fixed */
			aFix[n].nJumpType = -1;
		}
	}
	/* Total number of fixed jumps */
	return nFixed;
}
/*
 * Check if a given token value is installed in the literal table.
 */
static sxi32 GenStateFindLiteral(ph7_gen_state *pGen, const SyString *pValue, sxu32 *pIdx) {
	SyHashEntry *pEntry;
	pEntry = SyHashGet(&pGen->hLiteral, (const void *)pValue->zString, pValue->nByte);
	if(pEntry == 0) {
		return SXERR_NOTFOUND;
	}
	*pIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
	return SXRET_OK;
}
/*
 * Install a given constant index in the literal table.
 * In order to be installed, the ph7_value must be of type string.
 */
static sxi32 GenStateInstallLiteral(ph7_gen_state *pGen, ph7_value *pObj, sxu32 nIdx) {
	if(SyBlobLength(&pObj->sBlob) > 0) {
		SyHashInsert(&pGen->hLiteral, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), SX_INT_TO_PTR(nIdx));
	}
	return SXRET_OK;
}
/*
 * Reserve a room for a numeric constant [i.e: 64-bit integer or real number]
 * in the constant table.
 */
static ph7_value *GenStateInstallNumLiteral(ph7_gen_state *pGen, sxu32 *pIdx) {
	ph7_value *pObj;
	sxu32 nIdx = 0; /* cc warning */
	/* Reserve a new constant */
	pObj = PH7_ReserveConstObj(pGen->pVm, &nIdx);
	if(pObj == 0) {
		PH7_GenCompileError(&(*pGen), E_ERROR, 1, "PH7 engine is running out of memory");
		return 0;
	}
	*pIdx = nIdx;
	/* TODO(chems): Create a numeric table (64bit int keys) same as
	 * the constant string iterals table [optimization purposes].
	 */
	return pObj;
}
/*
 * Implementation of the PHP language constructs.
 */
/* Forward declaration */
static sxi32 GenStateCompileChunk(ph7_gen_state *pGen, sxi32 iFlags);
/*
 * Compile a numeric [i.e: integer or real] literal.
 * Notes on the integer type.
 *  According to the PHP language reference manual
 *  Integers can be specified in decimal (base 10), hexadecimal (base 16), octal (base 8)
 *  or binary (base 2) notation, optionally preceded by a sign (- or +).
 *  To use octal notation, precede the number with a 0 (zero). To use hexadecimal
 *  notation precede the number with 0x. To use binary notation precede the number with 0b.
 * Symisc eXtension to the integer type.
 *  PH7 introduced platform-independant 64-bit integer unlike the standard PHP engine
 *  where the size of an integer is platform-dependent.That is,the size of an integer
 *  is 8 bytes and the maximum integer size is 0x7FFFFFFFFFFFFFFF for all platforms
 *  [i.e: either 32bit or 64bit].
 *  For more information on this powerfull extension please refer to the official
 *  documentation.
 */
static sxi32 PH7_CompileNumLiteral(ph7_gen_state *pGen, sxi32 iCompileFlag) {
	SyToken *pToken = pGen->pIn; /* Raw token */
	sxu32 nIdx = 0;
	if(pToken->nType & PH7_TK_INTEGER) {
		ph7_value *pObj;
		sxi64 iValue;
		iValue = PH7_TokenValueToInt64(&pToken->sData);
		pObj = GenStateInstallNumLiteral(&(*pGen), &nIdx);
		if(pObj == 0) {
			SXUNUSED(iCompileFlag); /* cc warning */
			return SXERR_ABORT;
		}
		PH7_MemObjInitFromInt(pGen->pVm, pObj, iValue);
	} else {
		/* Real number */
		ph7_value *pObj;
		/* Reserve a new constant */
		pObj = PH7_ReserveConstObj(pGen->pVm, &nIdx);
		if(pObj == 0) {
			PH7_GenCompileError(&(*pGen), E_ERROR, 1, "PH7 engine is running out of memory");
			return SXERR_ABORT;
		}
		PH7_MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
		PH7_MemObjToReal(pObj);
	}
	/* Emit the load constant instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, nIdx, 0, 0);
	/* Node successfully compiled */
	return SXRET_OK;
}
/*
 * Compile a single quoted string.
 * According to the PHP language reference manual:
 *
 *   The simplest way to specify a string is to enclose it in single quotes (the character ' ).
 *   To specify a literal single quote, escape it with a backslash (\). To specify a literal
 *   backslash, double it (\\). All other instances of backslash will be treated as a literal
 *   backslash: this means that the other escape sequences you might be used to, such as \r
 *   or \n, will be output literally as specified rather than having any special meaning.
 *
 */
PH7_PRIVATE sxi32 PH7_CompileSimpleString(ph7_gen_state *pGen, sxi32 iCompileFlag) {
	SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
	const char *zIn, *zCur, *zEnd;
	ph7_value *pObj;
	sxu32 nIdx;
	nIdx = 0; /* Prevent compiler warning */
	/* Delimit the string */
	zIn  = pStr->zString;
	zEnd = &zIn[pStr->nByte];
	if(zIn >= zEnd) {
		/* Empty string,load NULL */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 0, 0, 0);
		return SXRET_OK;
	}
	if(SXRET_OK == GenStateFindLiteral(&(*pGen), pStr, &nIdx)) {
		/* Already processed,emit the load constant instruction
		 * and return.
		 */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, nIdx, 0, 0);
		return SXRET_OK;
	}
	/* Reserve a new constant */
	pObj = PH7_ReserveConstObj(pGen->pVm, &nIdx);
	if(pObj == 0) {
		PH7_GenCompileError(&(*pGen), E_ERROR, 1, "PH7 engine is running out of memory");
		SXUNUSED(iCompileFlag); /* cc warning */
		return SXERR_ABORT;
	}
	PH7_MemObjInitFromString(pGen->pVm, pObj, 0);
	/* Compile the node */
	for(;;) {
		if(zIn >= zEnd) {
			/* End of input */
			break;
		}
		zCur = zIn;
		while(zIn < zEnd && zIn[0] != '\\') {
			zIn++;
		}
		if(zIn > zCur) {
			/* Append raw contents*/
			PH7_MemObjStringAppend(pObj, zCur, (sxu32)(zIn - zCur));
		}
		zIn++;
		if(zIn < zEnd) {
			if(zIn[0] == '\\') {
				/* A literal backslash */
				PH7_MemObjStringAppend(pObj, "\\", sizeof(char));
			} else if(zIn[0] == '\'') {
				/* A single quote */
				PH7_MemObjStringAppend(pObj, "'", sizeof(char));
			} else {
				/* verbatim copy */
				zIn--;
				PH7_MemObjStringAppend(pObj, zIn, sizeof(char) * 2);
				zIn++;
			}
		}
		/* Advance the stream cursor */
		zIn++;
	}
	/* Emit the load constant instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, nIdx, 0, 0);
	if(pStr->nByte < 1024) {
		/* Install in the literal table */
		GenStateInstallLiteral(pGen, pObj, nIdx);
	}
	/* Node successfully compiled */
	return SXRET_OK;
}
/*
 * Process variable expression [i.e: "$var","${var}"] embedded in a double quoted string.
 * According to the PHP language reference manual
 *   When a string is specified in double quotes,variables are parsed within it.
 *  There are two types of syntax: a simple one and a complex one. The simple syntax is the most
 *  common and convenient. It provides a way to embed a variable, an array value, or an object
 *  property in a string with a minimum of effort.
 *  Simple syntax
 *   If a dollar sign ($) is encountered, the parser will greedily take as many tokens as possible
 *   to form a valid variable name. Enclose the variable name in curly braces to explicitly specify
 *   the end of the name.
 *   Similarly, an array index or an object property can be parsed. With array indices, the closing
 *   square bracket (]) marks the end of the index. The same rules apply to object properties
 *   as to simple variables.
 *  Complex (curly) syntax
 *   This isn't called complex because the syntax is complex, but because it allows for the use
 *   of complex expressions.
 *   Any scalar variable, array element or object property with a string representation can be
 *   included via this syntax. Simply write the expression the same way as it would appear outside
 *   the string, and then wrap it in { and }. Since { can not be escaped, this syntax will only
 *   be recognised when the $ immediately follows the {. Use {\$ to get a literal {$
 */
static sxi32 GenStateProcessStringExpression(
	ph7_gen_state *pGen, /* Code generator state */
	sxu32 nLine,         /* Line number */
	const char *zIn,     /* Raw expression */
	const char *zEnd     /* End of the expression */
) {
	SyToken *pTmpIn, *pTmpEnd;
	SySet sToken;
	sxi32 rc;
	/* Initialize the token set */
	SySetInit(&sToken, &pGen->pVm->sAllocator, sizeof(SyToken));
	/* Preallocate some slots */
	SySetAlloc(&sToken, 0x08);
	/* Tokenize the text */
	PH7_TokenizePHP(zIn, (sxu32)(zEnd - zIn), nLine, &sToken);
	/* Swap delimiter */
	pTmpIn  = pGen->pIn;
	pTmpEnd = pGen->pEnd;
	pGen->pIn = (SyToken *)SySetBasePtr(&sToken);
	pGen->pEnd = &pGen->pIn[SySetUsed(&sToken)];
	/* Compile the expression */
	rc = PH7_CompileExpr(&(*pGen), 0, 0);
	/* Restore token stream */
	pGen->pIn  = pTmpIn;
	pGen->pEnd = pTmpEnd;
	/* Release the token set */
	SySetRelease(&sToken);
	/* Compilation result */
	return rc;
}
/*
 * Reserve a new constant for a double quoted string.
 */
static ph7_value *GenStateNewStrObj(ph7_gen_state *pGen, sxi32 *pCount) {
	ph7_value *pConstObj;
	sxu32 nIdx = 0;
	/* Reserve a new constant */
	pConstObj = PH7_ReserveConstObj(pGen->pVm, &nIdx);
	if(pConstObj == 0) {
		PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "PH7 engine is running out of memory");
		return 0;
	}
	(*pCount)++;
	PH7_MemObjInitFromString(pGen->pVm, pConstObj, 0);
	/* Emit the load constant instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, nIdx, 0, 0);
	return pConstObj;
}
/*
 * Compile a double quoted string.
 * According to the PHP language reference manual
 * Double quoted
 *  If the string is enclosed in double-quotes ("), PHP will interpret more escape sequences for special characters:
 *  Escaped characters Sequence 	Meaning
 *  \n linefeed (LF or 0x0A (10) in ASCII)
 *  \r carriage return (CR or 0x0D (13) in ASCII)
 *  \t horizontal tab (HT or 0x09 (9) in ASCII)
 *  \v vertical tab (VT or 0x0B (11) in ASCII)
 *  \f form feed (FF or 0x0C (12) in ASCII)
 *  \\ backslash
 *  \$ dollar sign
 *  \" double-quote
 *  \[0-7]{1,3} 	the sequence of characters matching the regular expression is a character in octal notation
 *  \x[0-9A-Fa-f]{1,2} 	the sequence of characters matching the regular expression is a character in hexadecimal notation
 * As in single quoted strings, escaping any other character will result in the backslash being printed too.
 * The most important feature of double-quoted strings is the fact that variable names will be expanded.
 * See string parsing for details.
 */
static sxi32 GenStateCompileString(ph7_gen_state *pGen) {
	SyString *pStr = &pGen->pIn->sData; /* Raw token value */
	const char *zIn, *zCur, *zEnd;
	ph7_value *pObj = 0;
	sxi32 iCons;
	sxi32 rc;
	/* Delimit the string */
	zIn  = pStr->zString;
	zEnd = &zIn[pStr->nByte];
	if(zIn >= zEnd) {
		/* Empty string,load NULL */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 0, 0, 0);
		return SXRET_OK;
	}
	zCur = 0;
	/* Compile the node */
	iCons = 0;
	for(;;) {
		zCur = zIn;
		while(zIn < zEnd && zIn[0] != '\\') {
			if(zIn[0] == '{' && &zIn[1] < zEnd && zIn[1] == '$') {
				break;
			} else if(zIn[0] == '$' && &zIn[1] < zEnd &&
					  (((unsigned char)zIn[1] >= 0xc0 || SyisAlpha(zIn[1]) || zIn[1] == '{' || zIn[1] == '_'))) {
				break;
			}
			zIn++;
		}
		if(zIn > zCur) {
			if(pObj == 0) {
				pObj = GenStateNewStrObj(&(*pGen), &iCons);
				if(pObj == 0) {
					return SXERR_ABORT;
				}
			}
			PH7_MemObjStringAppend(pObj, zCur, (sxu32)(zIn - zCur));
		}
		if(zIn >= zEnd) {
			break;
		}
		if(zIn[0] == '\\') {
			const char *zPtr = 0;
			sxu32 n;
			zIn++;
			if(zIn >= zEnd) {
				break;
			}
			if(pObj == 0) {
				pObj = GenStateNewStrObj(&(*pGen), &iCons);
				if(pObj == 0) {
					return SXERR_ABORT;
				}
			}
			n = sizeof(char); /* size of conversion */
			switch(zIn[0]) {
				case '$':
					/* Dollar sign */
					PH7_MemObjStringAppend(pObj, "$", sizeof(char));
					break;
				case '\\':
					/* A literal backslash */
					PH7_MemObjStringAppend(pObj, "\\", sizeof(char));
					break;
				case 'a':
					/* The "alert" character (BEL)[ctrl+g] ASCII code 7 */
					PH7_MemObjStringAppend(pObj, "\a", sizeof(char));
					break;
				case 'b':
					/* Backspace (BS)[ctrl+h] ASCII code 8 */
					PH7_MemObjStringAppend(pObj, "\b", sizeof(char));
					break;
				case 'f':
					/* Form-feed (FF)[ctrl+l] ASCII code 12 */
					PH7_MemObjStringAppend(pObj, "\f", sizeof(char));
					break;
				case 'n':
					/* Line feed(new line) (LF)[ctrl+j] ASCII code 10 */
					PH7_MemObjStringAppend(pObj, "\n", sizeof(char));
					break;
				case 'r':
					/* Carriage return (CR)[ctrl+m] ASCII code 13 */
					PH7_MemObjStringAppend(pObj, "\r", sizeof(char));
					break;
				case 't':
					/* Horizontal tab (HT)[ctrl+i] ASCII code 9 */
					PH7_MemObjStringAppend(pObj, "\t", sizeof(char));
					break;
				case 'v':
					/* Vertical tab(VT)[ctrl+k] ASCII code 11 */
					PH7_MemObjStringAppend(pObj, "\v", sizeof(char));
					break;
				case '\'':
					/* Single quote */
					PH7_MemObjStringAppend(pObj, "'", sizeof(char));
					break;
				case '"':
					/* Double quote */
					PH7_MemObjStringAppend(pObj, "\"", sizeof(char));
					break;
				case '0':
					/* NUL byte */
					PH7_MemObjStringAppend(pObj, "\0", sizeof(char));
					break;
				case 'x':
					if((unsigned char)zIn[1] < 0xc0 && SyisHex(zIn[1])) {
						int c;
						/* Hex digit */
						c = SyHexToint(zIn[1]) << 4;
						if(&zIn[2] < zEnd) {
							c +=  SyHexToint(zIn[2]);
						}
						/* Output char */
						PH7_MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
						n += sizeof(char) * 2;
					} else {
						/* Output literal character  */
						PH7_MemObjStringAppend(pObj, "x", sizeof(char));
					}
					break;
				case 'o':
					if(&zIn[1] < zEnd && (unsigned char)zIn[1] < 0xc0 && SyisDigit(zIn[1]) && (zIn[1] - '0') < 8) {
						/* Octal digit stream */
						int c;
						c = 0;
						zIn++;
						for(zPtr = zIn ; zPtr < &zIn[3 * sizeof(char)] ; zPtr++) {
							if(zPtr >= zEnd || (unsigned char)zPtr[0] >= 0xc0 || !SyisDigit(zPtr[0]) || (zPtr[0] - '0') > 7) {
								break;
							}
							c = c * 8 + (zPtr[0] - '0');
						}
						if(c > 0) {
							PH7_MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
						}
						n = (sxu32)(zPtr - zIn);
					} else {
						/* Output literal character  */
						PH7_MemObjStringAppend(pObj, "o", sizeof(char));
					}
					break;
				default:
					/* Output without a slash */
					PH7_MemObjStringAppend(pObj, zIn, sizeof(char));
					break;
			}
			/* Advance the stream cursor */
			zIn += n;
			continue;
		}
		if(zIn[0] == '{') {
			/* Curly syntax */
			const char *zExpr;
			sxi32 iNest = 1;
			zIn++;
			zExpr = zIn;
			/* Synchronize with the next closing curly braces */
			while(zIn < zEnd) {
				if(zIn[0] == '{') {
					/* Increment nesting level */
					iNest++;
				} else if(zIn[0] == '}') {
					/* Decrement nesting level */
					iNest--;
					if(iNest <= 0) {
						break;
					}
				}
				zIn++;
			}
			/* Process the expression */
			rc = GenStateProcessStringExpression(&(*pGen), pGen->pIn->nLine, zExpr, zIn);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
			if(rc != SXERR_EMPTY) {
				++iCons;
			}
			if(zIn < zEnd) {
				/* Jump the trailing curly */
				zIn++;
			}
		} else {
			/* Simple syntax */
			const char *zExpr = zIn;
			/* Assemble variable name */
			for(;;) {
				/* Jump leading dollars */
				while(zIn < zEnd && zIn[0] == '$') {
					zIn++;
				}
				for(;;) {
					while(zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_')) {
						zIn++;
					}
					if((unsigned char)zIn[0] >= 0xc0) {
						/* UTF-8 stream */
						zIn++;
						while(zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80)) {
							zIn++;
						}
						continue;
					}
					break;
				}
				if(zIn >= zEnd) {
					break;
				}
				if(zIn[0] == '[') {
					sxi32 iSquare = 1;
					zIn++;
					while(zIn < zEnd) {
						if(zIn[0] == '[') {
							iSquare++;
						} else if(zIn[0] == ']') {
							iSquare--;
							if(iSquare <= 0) {
								break;
							}
						}
						zIn++;
					}
					if(zIn < zEnd) {
						zIn++;
					}
					break;
				} else if(zIn[0] == '{') {
					sxi32 iCurly = 1;
					zIn++;
					while(zIn < zEnd) {
						if(zIn[0] == '{') {
							iCurly++;
						} else if(zIn[0] == '}') {
							iCurly--;
							if(iCurly <= 0) {
								break;
							}
						}
						zIn++;
					}
					if(zIn < zEnd) {
						zIn++;
					}
					break;
				} else if(zIn[0] == '-' && &zIn[1] < zEnd && zIn[1] == '>') {
					/* Member access operator '->' */
					zIn += 2;
				} else if(zIn[0] == ':' && &zIn[1] < zEnd && zIn[1] == ':') {
					/* Static member access operator '::' */
					zIn += 2;
				} else {
					break;
				}
			}
			/* Process the expression */
			rc = GenStateProcessStringExpression(&(*pGen), pGen->pIn->nLine, zExpr, zIn);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
			if(rc != SXERR_EMPTY) {
				++iCons;
			}
		}
		/* Invalidate the previously used constant */
		pObj = 0;
	}/*for(;;)*/
	if(iCons > 1) {
		/* Concatenate all compiled constants */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_CAT, iCons, 0, 0, 0);
	}
	/* Node successfully compiled */
	return SXRET_OK;
}
/*
 * Compile a double quoted string.
 *  See the block-comment above for more information.
 */
PH7_PRIVATE sxi32 PH7_CompileString(ph7_gen_state *pGen, sxi32 iCompileFlag) {
	sxi32 rc;
	rc = GenStateCompileString(&(*pGen));
	SXUNUSED(iCompileFlag); /* cc warning */
	/* Compilation result */
	return rc;
}
/*
 * Compile an array entry whether it is a key or a value.
 *  Notes on array entries.
 *  According to the PHP language reference manual
 *  An array can be created by the array() language construct.
 *  It takes as parameters any number of comma-separated key => value pairs.
 *  array(  key =>  value
 *    , ...
 *    )
 *  A key may be either an integer or a string. If a key is the standard representation
 *  of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while
 *  "08" will be interpreted as "08"). Floats in key are truncated to integer.
 *  The indexed and associative array types are the same type in PHP, which can both
 *  contain integer and string indices.
 *  A value can be any PHP type.
 *  If a key is not specified for a value, the maximum of the integer indices is taken
 *  and the new key will be that value plus 1. If a key that already has an assigned value
 *  is specified, that value will be overwritten.
 */
static sxi32 GenStateCompileArrayEntry(
	ph7_gen_state *pGen, /* Code generator state */
	SyToken *pIn,        /* Token stream */
	SyToken *pEnd,       /* End of the token stream */
	sxi32 iFlags,        /* Compilation flags */
	sxi32(*xValidator)(ph7_gen_state *, ph7_expr_node *) /* Expression tree validator callback */
) {
	SyToken *pTmpIn, *pTmpEnd;
	sxi32 rc;
	/* Swap token stream */
	SWAP_DELIMITER(pGen, pIn, pEnd);
	/* Compile the expression*/
	rc = PH7_CompileExpr(&(*pGen), iFlags, xValidator);
	/* Restore token stream */
	RE_SWAP_DELIMITER(pGen);
	return rc;
}
/*
 * Expression tree validator callback for the 'array' language construct.
 * Return SXRET_OK if the tree is valid. Any other return value indicates
 * an invalid expression tree and this function will generate the appropriate
 * error message.
 * See the routine responible of compiling the array language construct
 * for more inforation.
 */
static sxi32 GenStateArrayNodeValidator(ph7_gen_state *pGen, ph7_expr_node *pRoot) {
	sxi32 rc = SXRET_OK;
	if(pRoot->pOp) {
		if(pRoot->pOp->iOp != EXPR_OP_SUBSCRIPT /* $a[] */ &&
				pRoot->pOp->iOp != EXPR_OP_FUNC_CALL /* function() [Symisc extension: i.e: array(&foo())] */
				&& pRoot->pOp->iOp != EXPR_OP_ARROW /* -> */ && pRoot->pOp->iOp != EXPR_OP_DC /* :: */) {
			/* Unexpected expression */
			rc = PH7_GenCompileError(&(*pGen), E_ERROR, pRoot->pStart ? pRoot->pStart->nLine : 0,
									 "array(): Expecting a variable/array member/function call after reference operator '&'");
			if(rc != SXERR_ABORT) {
				rc = SXERR_INVALID;
			}
		}
	} else if(pRoot->xCode != PH7_CompileVariable) {
		/* Unexpected expression */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pRoot->pStart ? pRoot->pStart->nLine : 0,
								 "array(): Expecting a variable after reference operator '&'");
		if(rc != SXERR_ABORT) {
			rc = SXERR_INVALID;
		}
	}
	return rc;
}
/*
 * Compile the 'array' language construct.
 *	 According to the PHP language reference manual
 *   An array in PHP is actually an ordered map. A map is a type that associates
 *   values to keys. This type is optimized for several different uses; it can
 *   be treated as an array, list (vector), hash table (an implementation of a map)
 *   dictionary, collection, stack, queue, and probably more. As array values can be
 *   other arrays, trees and multidimensional arrays are also possible.
 */
PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen, sxi32 iCompileFlag) {
	sxi32(*xValidator)(ph7_gen_state *, ph7_expr_node *); /* Expression tree validator callback */
	SyToken *pKey, *pCur;
	sxi32 iEmitRef = 0;
	sxi32 nPair = 0;
	sxi32 iNest;
	sxi32 rc;
	/* Jump the 'array' keyword,the leading left parenthesis and the trailing parenthesis.
	 */
	pGen->pIn += 2;
	pGen->pEnd--;
	xValidator = 0;
	SXUNUSED(iCompileFlag); /* cc warning */
	for(;;) {
		/* Jump leading commas */
		while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA)) {
			pGen->pIn++;
		}
		pCur = pGen->pIn;
		if(SXRET_OK != PH7_GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn)) {
			/* No more entry to process */
			break;
		}
		if(pCur >= pGen->pIn) {
			continue;
		}
		/* Compile the key if available */
		pKey = pCur;
		iNest = 0;
		while(pCur < pGen->pIn) {
			if((pCur->nType & PH7_TK_ARRAY_OP) && iNest <= 0) {
				break;
			}
			if(pCur->nType & PH7_TK_LPAREN /*'('*/) {
				iNest++;
			} else if(pCur->nType & PH7_TK_RPAREN /*')'*/) {
				/* Don't worry about mismatched parenthesis here,the expression
				 * parser will shortly detect any syntax error.
				 */
				iNest--;
			}
			pCur++;
		}
		rc = SXERR_EMPTY;
		if(pCur < pGen->pIn) {
			if(&pCur[1] >= pGen->pIn) {
				/* Missing value */
				rc = PH7_GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "array(): Missing entry value");
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
				return SXRET_OK;
			}
			/* Compile the expression holding the key */
			rc = GenStateCompileArrayEntry(&(*pGen), pKey, pCur,
										   EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/, 0);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
			pCur++; /* Jump the '=>' operator */
		} else if(pKey == pCur) {
			/* Key is omitted,emit a warning */
			PH7_GenCompileError(&(*pGen), E_WARNING, pCur->nLine, "array(): Missing entry key");
			pCur++; /* Jump the '=>' operator */
		} else {
			/* Reset back the cursor and point to the entry value */
			pCur = pKey;
		}
		if(rc == SXERR_EMPTY) {
			/* No available key,load NULL */
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 0 /* nil index */, 0, 0);
		}
		if(pCur->nType & PH7_TK_AMPER /*'&'*/) {
			/* Insertion by reference, [i.e: $a = array(&$x);] */
			xValidator = GenStateArrayNodeValidator; /* Only variable are allowed */
			iEmitRef = 1;
			pCur++; /* Jump the '&' token */
			if(pCur >= pGen->pIn) {
				/* Missing value */
				rc = PH7_GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "array(): Missing referenced variable");
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
				return SXRET_OK;
			}
		}
		/* Compile indice value */
		rc = GenStateCompileArrayEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/, xValidator);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		if(iEmitRef) {
			/* Emit the load reference instruction */
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOAD_REF, 0, 0, 0, 0);
		}
		xValidator = 0;
		iEmitRef = 0;
		nPair++;
	}
	/* Emit the load map instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOAD_MAP, nPair * 2, 0, 0, 0);
	/* Node successfully compiled */
	return SXRET_OK;
}
/*
 * Expression tree validator callback for the 'list' language construct.
 * Return SXRET_OK if the tree is valid. Any other return value indicates
 * an invalid expression tree and this function will generate the appropriate
 * error message.
 * See the routine responible of compiling the list language construct
 * for more inforation.
 */
static sxi32 GenStateListNodeValidator(ph7_gen_state *pGen, ph7_expr_node *pRoot) {
	sxi32 rc = SXRET_OK;
	if(pRoot->pOp) {
		if(pRoot->pOp->iOp != EXPR_OP_SUBSCRIPT /* $a[] */ && pRoot->pOp->iOp != EXPR_OP_ARROW  /* -> */
				&& pRoot->pOp->iOp != EXPR_OP_DC /* :: */) {
			/* Unexpected expression */
			rc = PH7_GenCompileError(&(*pGen), E_ERROR, pRoot->pStart ? pRoot->pStart->nLine : 0,
									 "list(): Expecting a variable not an expression");
			if(rc != SXERR_ABORT) {
				rc = SXERR_INVALID;
			}
		}
	} else if(pRoot->xCode != PH7_CompileVariable) {
		/* Unexpected expression */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pRoot->pStart ? pRoot->pStart->nLine : 0,
								 "list(): Expecting a variable not an expression");
		if(rc != SXERR_ABORT) {
			rc = SXERR_INVALID;
		}
	}
	return rc;
}
/*
 * Compile the 'list' language construct.
 *  According to the PHP language reference
 *  list(): Assign variables as if they were an array.
 *  list() is used to assign a list of variables in one operation.
 *  Description
 *   array list (mixed $varname [, mixed $... ] )
 *   Like array(), this is not really a function, but a language construct.
 *   list() is used to assign a list of variables in one operation.
 *  Parameters
 *   $varname: A variable.
 *  Return Values
 *   The assigned array.
 */
PH7_PRIVATE sxi32 PH7_CompileList(ph7_gen_state *pGen, sxi32 iCompileFlag) {
	SyToken *pNext;
	sxi32 nExpr;
	sxi32 rc;
	nExpr = 0;
	/* Jump the 'list' keyword,the leading left parenthesis and the trailing parenthesis */
	pGen->pIn += 2;
	pGen->pEnd--;
	SXUNUSED(iCompileFlag); /* cc warning */
	while(SXRET_OK == PH7_GetNextExpr(pGen->pIn, pGen->pEnd, &pNext)) {
		if(pGen->pIn < pNext) {
			/* Compile the expression holding the variable */
			rc = GenStateCompileArrayEntry(&(*pGen), pGen->pIn, pNext, EXPR_FLAG_LOAD_IDX_STORE, GenStateListNodeValidator);
			if(rc != SXRET_OK) {
				/* Do not bother compiling this expression, it's broken anyway */
				return SXRET_OK;
			}
		} else {
			/* Empty entry,load NULL */
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 0/* NULL index */, 0, 0);
		}
		nExpr++;
		/* Advance the stream cursor */
		pGen->pIn = &pNext[1];
	}
	/* Emit the LOAD_LIST instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOAD_LIST, nExpr, 0, 0, 0);
	/* Node successfully compiled */
	return SXRET_OK;
}
/* Forward declaration */
static sxi32 GenStateCompileFunc(ph7_gen_state *pGen, SyString *pName, sxi32 iFlags, int bHandleClosure, ph7_vm_func **ppFunc);
/*
 * Compile an annoynmous function or a closure.
 * According to the PHP language reference
 *  Anonymous functions, also known as closures, allow the creation of functions
 *  which have no specified name. They are most useful as the value of callback
 *  parameters, but they have many other uses. Closures can also be used as
 *  the values of variables; Assigning a closure to a variable uses the same
 *  syntax as any other assignment, including the trailing semicolon:
 *  Example Anonymous function variable assignment example
 * <?php
 * $greet = function($name)
 * {
 *    printf("Hello %s\r\n", $name);
 * };
 * $greet('World');
 * $greet('PHP');
 * ?>
 * Note that the implementation of annoynmous function and closure under
 * PH7 is completely different from the one used by the zend engine.
 */
PH7_PRIVATE sxi32 PH7_CompileAnnonFunc(ph7_gen_state *pGen, sxi32 iCompileFlag) {
	ph7_vm_func *pAnnonFunc; /* Annonymous function body */
	char zName[512];         /* Unique lambda name */
	static int iCnt = 1;     /* There is no worry about thread-safety here,because only
							  * one thread is allowed to compile the script.
						      */
	ph7_value *pObj;
	SyString sName;
	sxu32 nIdx;
	sxu32 nLen;
	sxi32 rc;
	pGen->pIn++; /* Jump the 'function' keyword */
	if(pGen->pIn->nType & (PH7_TK_ID | PH7_TK_KEYWORD)) {
		pGen->pIn++;
	}
	/* Reserve a constant for the lambda */
	pObj = PH7_ReserveConstObj(pGen->pVm, &nIdx);
	if(pObj == 0) {
		PH7_GenCompileError(&(*pGen), E_ERROR, 1, "Fatal, PH7 engine is running out of memory");
		SXUNUSED(iCompileFlag); /* cc warning */
		return SXERR_ABORT;
	}
	/* Generate a unique name */
	nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
	/* Make sure the generated name is unique */
	while(SyHashGet(&pGen->pVm->hFunction, zName, nLen) != 0 && nLen < sizeof(zName) - 2) {
		nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
	}
	SyStringInitFromBuf(&sName, zName, nLen);
	PH7_MemObjInitFromString(pGen->pVm, pObj, &sName);
	/* Compile the lambda body */
	rc = GenStateCompileFunc(&(*pGen), &sName, 0, TRUE, &pAnnonFunc);
	if(rc == SXERR_ABORT) {
		return SXERR_ABORT;
	}
	if(pAnnonFunc->iFlags & VM_FUNC_CLOSURE) {
		/* Emit the load closure instruction */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOAD_CLOSURE, 0, 0, pAnnonFunc, 0);
	} else {
		/* Emit the load constant instruction */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, nIdx, 0, 0);
	}
	/* Node successfully compiled */
	return SXRET_OK;
}
/*
 * Compile a backtick quoted string.
 */
static sxi32 PH7_CompileBacktic(ph7_gen_state *pGen, sxi32 iCompileFlag) {
	/* TICKET 1433-40: This construct is disabled in the current release of the PH7 engine.
	 * If you want this feature,please contact symisc systems via contact@symisc.net
	 */
	PH7_GenCompileError(&(*pGen), E_NOTICE, pGen->pIn->nLine,
						"Command line invocation is disabled in the current release of the PH7(%s) engine",
						ph7_lib_version()
					   );
	/* Load NULL */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 0, 0, 0);
	SXUNUSED(iCompileFlag); /* cc warning */
	/* Node successfully compiled */
	return SXRET_OK;
}
/*
 * Compile a function [i.e: die(),exit(),include(),...] which is a langauge
 * construct.
 */
PH7_PRIVATE sxi32 PH7_CompileLangConstruct(ph7_gen_state *pGen, sxi32 iCompileFlag) {
	SyString *pName;
	sxu32 nKeyID;
	sxi32 rc;
	/* Name of the language construct [i.e: echo,die...]*/
	pName = &pGen->pIn->sData;
	nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
	pGen->pIn++; /* Jump the language construct keyword */
	if(nKeyID == PH7_TKWRD_ECHO) {
		SyToken *pTmp, *pNext = 0;
		/* Compile arguments one after one */
		pTmp = pGen->pEnd;
		/* Symisc eXtension to the PHP programming language:
		 * 'echo' can be used in the context of a function which
		 *  mean that the following expression is valid:
		 *      fopen('file.txt','r') or echo "IO error";
		 */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 1 /* Boolean true index */, 0, 0);
		while(SXRET_OK == PH7_GetNextExpr(pGen->pIn, pTmp, &pNext)) {
			if(pGen->pIn < pNext) {
				pGen->pEnd = pNext;
				rc = PH7_CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
				if(rc != SXERR_EMPTY) {
					/* Ticket 1433-008: Optimization #1: Consume input directly
					 * without the overhead of a function call.
					 * This is a very powerful optimization that improve
					 * performance greatly.
					 */
					PH7_VmEmitInstr(pGen->pVm, PH7_OP_CONSUME, 1, 0, 0, 0);
				}
			}
			/* Jump trailing commas */
			while(pNext < pTmp && (pNext->nType & PH7_TK_COMMA)) {
				pNext++;
			}
			pGen->pIn = pNext;
		}
		/* Restore token stream */
		pGen->pEnd = pTmp;
	} else {
		sxi32 nArg = 0;
		sxu32 nIdx = 0;
		rc = PH7_CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		} else if(rc != SXERR_EMPTY) {
			nArg = 1;
		}
		if(SXRET_OK != GenStateFindLiteral(&(*pGen), pName, &nIdx)) {
			ph7_value *pObj;
			/* Emit the call instruction */
			pObj = PH7_ReserveConstObj(pGen->pVm, &nIdx);
			if(pObj == 0) {
				PH7_GenCompileError(&(*pGen), E_ERROR, 1, "Fatal, PH7 engine is running out of memory");
				SXUNUSED(iCompileFlag); /* cc warning */
				return SXERR_ABORT;
			}
			PH7_MemObjInitFromString(pGen->pVm, pObj, pName);
			/* Install in the literal table */
			GenStateInstallLiteral(&(*pGen), pObj, nIdx);
		}
		/* Emit the call instruction */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, nIdx, 0, 0);
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_CALL, nArg, 0, 0, 0);
	}
	/* Node successfully compiled */
	return SXRET_OK;
}
/*
 * Compile a node holding a variable declaration.
 * According to the PHP language reference
 *  Variables in PHP are represented by a dollar sign followed by the name of the variable.
 *  The variable name is case-sensitive.
 *  Variable names follow the same rules as other labels in PHP. A valid variable name starts
 *  with a letter or underscore, followed by any number of letters, numbers, or underscores.
 *  As a regular expression, it would be expressed thus: '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
 *  Note: For our purposes here, a letter is a-z, A-Z, and the bytes from 127 through 255 (0x7f-0xff).
 *  Note: $this is a special variable that can't be assigned.
 *  By default, variables are always assigned by value. That is to say, when you assign an expression
 *  to a variable, the entire value of the original expression is copied into the destination variable.
 *  This means, for instance, that after assigning one variable's value to another, changing one of those
 *  variables will have no effect on the other. For more information on this kind of assignment, see
 *  the chapter on Expressions.
 *  PHP also offers another way to assign values to variables: assign by reference. This means that
 *  the new variable simply references (in other words, "becomes an alias for" or "points to") the original
 *  variable. Changes to the new variable affect the original, and vice versa.
 *  To assign by reference, simply prepend an ampersand (&) to the beginning of the variable which
 *  is being assigned (the source variable).
 */
PH7_PRIVATE sxi32 PH7_CompileVariable(ph7_gen_state *pGen, sxi32 iCompileFlag) {
	sxu32 nLine = pGen->pIn->nLine;
	sxi32 iVv;
	sxi32 iP1;
	void *p3;
	sxi32 rc;
	iVv = -1; /* Variable variable counter */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_DOLLAR)) {
		pGen->pIn++;
		iVv++;
	}
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_ID | PH7_TK_KEYWORD | PH7_TK_OCB/*'{'*/)) == 0) {
		/* Invalid variable name */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Invalid variable name");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		return SXRET_OK;
	}
	p3  = 0;
	if(pGen->pIn->nType & PH7_TK_OCB/*'{'*/) {
		/* Dynamic variable creation */
		pGen->pIn++;  /* Jump the open curly */
		pGen->pEnd--; /* Ignore the trailing curly */
		if(pGen->pIn >= pGen->pEnd) {
			/* Empty expression */
			PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Invalid variable name");
			return SXRET_OK;
		}
		/* Compile the expression holding the variable name */
		rc = PH7_CompileExpr(&(*pGen), 0, 0);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		} else if(rc == SXERR_EMPTY) {
			PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Missing variable name");
			return SXRET_OK;
		}
	} else {
		SyHashEntry *pEntry;
		SyString *pName;
		char *zName = 0;
		/* Extract variable name */
		pName = &pGen->pIn->sData;
		/* Advance the stream cursor */
		pGen->pIn++;
		pEntry = SyHashGet(&pGen->hVar, (const void *)pName->zString, pName->nByte);
		if(pEntry == 0) {
			/* Duplicate name */
			zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
			if(zName == 0) {
				PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 engine is running out of memory");
				return SXERR_ABORT;
			}
			/* Install in the hashtable */
			SyHashInsert(&pGen->hVar, zName, pName->nByte, zName);
		} else {
			/* Name already available */
			zName = (char *)pEntry->pUserData;
		}
		p3 = (void *)zName;
	}
	iP1 = 0;
	if(iCompileFlag & EXPR_FLAG_RDONLY_LOAD) {
		if((iCompileFlag & EXPR_FLAG_LOAD_IDX_STORE) == 0) {
			/* Read-only load.In other words do not create the variable if inexistant */
			iP1 = 1;
		}
	}
	/* Emit the load instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOAD, iP1, 0, p3, 0);
	while(iVv > 0) {
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOAD, iP1, 0, 0, 0);
		iVv--;
	}
	/* Node successfully compiled */
	return SXRET_OK;
}
/*
 * Load a literal.
 */
static sxi32 GenStateLoadLiteral(ph7_gen_state *pGen) {
	SyToken *pToken = pGen->pIn;
	ph7_value *pObj;
	SyString *pStr;
	sxu32 nIdx;
	/* Extract token value */
	pStr = &pToken->sData;
	/* Deal with the reserved literals [i.e: null,false,true,...] first */
	if(pStr->nByte == sizeof("NULL") - 1) {
		if(SyStrnicmp(pStr->zString, "null", sizeof("NULL") - 1) == 0) {
			/* NULL constant are always indexed at 0 */
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 0, 0, 0);
			return SXRET_OK;
		} else if(SyStrnicmp(pStr->zString, "true", sizeof("TRUE") - 1) == 0) {
			/* TRUE constant are always indexed at 1 */
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 1, 0, 0);
			return SXRET_OK;
		}
	} else if(pStr->nByte == sizeof("FALSE") - 1 &&
			  SyStrnicmp(pStr->zString, "false", sizeof("FALSE") - 1) == 0) {
		/* FALSE constant are always indexed at 2 */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 2, 0, 0);
		return SXRET_OK;
	} else if(pStr->nByte == sizeof("__LINE__") - 1 &&
			  SyMemcmp(pStr->zString, "__LINE__", sizeof("__LINE__") - 1) == 0) {
		/* TICKET 1433-004: __LINE__ constant must be resolved at compile time,not run time */
		pObj = PH7_ReserveConstObj(pGen->pVm, &nIdx);
		if(pObj == 0) {
			PH7_GenCompileError(pGen, E_ERROR, pToken->nLine, "Fatal, PH7 engine is running out of memory");
			return SXERR_ABORT;
		}
		PH7_MemObjInitFromInt(pGen->pVm, pObj, pToken->nLine);
		/* Emit the load constant instruction */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, nIdx, 0, 0);
		return SXRET_OK;
	} else if((pStr->nByte == sizeof("__FUNCTION__") - 1 &&
			   SyMemcmp(pStr->zString, "__FUNCTION__", sizeof("__FUNCTION__") - 1) == 0) ||
			  (pStr->nByte == sizeof("__METHOD__") - 1 &&
			   SyMemcmp(pStr->zString, "__METHOD__", sizeof("__METHOD__") - 1) == 0)) {
		GenBlock *pBlock = pGen->pCurrent;
		/* TICKET 1433-004: __FUNCTION__/__METHOD__ constants must be resolved at compile time,not run time */
		while(pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC) == 0) {
			/* Point to the upper block */
			pBlock = pBlock->pParent;
		}
		if(pBlock == 0) {
			/* Called in the global scope,load NULL */
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 0, 0, 0);
		} else {
			/* Extract the target function/method */
			ph7_vm_func *pFunc = (ph7_vm_func *)pBlock->pUserData;
			if(pStr->zString[2] == 'M' /* METHOD */ && (pFunc->iFlags & VM_FUNC_CLASS_METHOD) == 0) {
				/* Not a class method,Load null */
				PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 0, 0, 0);
			} else {
				pObj = PH7_ReserveConstObj(pGen->pVm, &nIdx);
				if(pObj == 0) {
					PH7_GenCompileError(pGen, E_ERROR, pToken->nLine, "Fatal, PH7 engine is running out of memory");
					return SXERR_ABORT;
				}
				PH7_MemObjInitFromString(pGen->pVm, pObj, &pFunc->sName);
				/* Emit the load constant instruction */
				PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, nIdx, 0, 0);
			}
		}
		return SXRET_OK;
	}
	/* Query literal table */
	if(SXRET_OK != GenStateFindLiteral(&(*pGen), &pToken->sData, &nIdx)) {
		ph7_value *pObj;
		/* Unknown literal,install it in the literal table */
		pObj = PH7_ReserveConstObj(pGen->pVm, &nIdx);
		if(pObj == 0) {
			PH7_GenCompileError(&(*pGen), E_ERROR, 1, "PH7 engine is running out of memory");
			return SXERR_ABORT;
		}
		PH7_MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
		GenStateInstallLiteral(&(*pGen), pObj, nIdx);
	}
	/* Emit the load constant instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 1, nIdx, 0, 0);
	return SXRET_OK;
}
/*
 * Resolve a namespace path or simply load a literal:
 * As of this version namespace support is disabled. If you need
 * a working version that implement namespace,please contact
 * symisc systems via contact@symisc.net
 */
static sxi32 GenStateResolveNamespaceLiteral(ph7_gen_state *pGen) {
	int emit = 0;
	sxi32 rc;
	while(pGen->pIn < &pGen->pEnd[-1]) {
		/* Emit a warning */
		if(!emit) {
			PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine,
								"Namespace support is disabled in the current release of the PH7(%s) engine",
								ph7_lib_version()
							   );
			emit = 1;
		}
		pGen->pIn++; /* Ignore the token */
	}
	/* Load literal */
	rc = GenStateLoadLiteral(&(*pGen));
	return rc;
}
/*
 * Compile a literal which is an identifier(name) for a simple value.
 */
PH7_PRIVATE sxi32 PH7_CompileLiteral(ph7_gen_state *pGen, sxi32 iCompileFlag) {
	sxi32 rc;
	rc = GenStateResolveNamespaceLiteral(&(*pGen));
	if(rc != SXRET_OK) {
		SXUNUSED(iCompileFlag); /* cc warning */
		return rc;
	}
	/* Node successfully compiled */
	return SXRET_OK;
}
/*
 * Recover from a compile-time error. In other words synchronize
 * the token stream cursor with the first semi-colon seen.
 */
static sxi32 PH7_ErrorRecover(ph7_gen_state *pGen) {
	/* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI /*';'*/) == 0) {
		pGen->pIn++;
	}
	return SXRET_OK;
}
/*
 * Check if the given identifier name is reserved or not.
 * Return TRUE if reserved.FALSE otherwise.
 */
static int GenStateIsReservedConstant(SyString *pName) {
	if(pName->nByte == sizeof("null") - 1) {
		if(SyStrnicmp(pName->zString, "null", sizeof("null") - 1) == 0) {
			return TRUE;
		} else if(SyStrnicmp(pName->zString, "true", sizeof("true") - 1) == 0) {
			return TRUE;
		}
	} else if(pName->nByte == sizeof("false") - 1) {
		if(SyStrnicmp(pName->zString, "false", sizeof("false") - 1) == 0) {
			return TRUE;
		}
	}
	/* Not a reserved constant */
	return FALSE;
}
/*
 * Compile the 'const' statement.
 * According to the PHP language reference
 *  A constant is an identifier (name) for a simple value. As the name suggests, that value
 *  cannot change during the execution of the script (except for magic constants, which aren't actually constants).
 *  A constant is case-sensitive by default. By convention, constant identifiers are always uppercase.
 *  The name of a constant follows the same rules as any label in PHP. A valid constant name starts
 *  with a letter or underscore, followed by any number of letters, numbers, or underscores.
 *  As a regular expression it would be expressed thusly: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
 *  Syntax
 *  You can define a constant by using the define()-function or by using the const keyword outside
 *  a class definition. Once a constant is defined, it can never be changed or undefined.
 *  You can get the value of a constant by simply specifying its name. Unlike with variables
 *  you should not prepend a constant with a $. You can also use the function constant() to read
 *  a constant's value if you wish to obtain the constant's name dynamically. Use get_defined_constants()
 *  to get a list of all defined constants.
 *
 * Symisc eXtension.
 *  PH7 allow any complex expression to be associated with the constant while the zend engine
 *  would allow only simple scalar value.
 *  Example
 *    const HELLO = "Welcome "." guest ".rand_str(3); //Valid under PH7/Generate error using the zend engine
 *    Refer to the official documentation for more information on this feature.
 */
static sxi32 PH7_CompileConstant(ph7_gen_state *pGen) {
	SySet *pConsCode, *pInstrContainer;
	sxu32 nLine = pGen->pIn->nLine;
	SyString *pName;
	sxi32 rc;
	pGen->pIn++; /* Jump the 'const' keyword */
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_SSTR | PH7_TK_DSTR | PH7_TK_ID | PH7_TK_KEYWORD)) == 0) {
		/* Invalid constant name */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "const: Invalid constant name");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Peek constant name */
	pName = &pGen->pIn->sData;
	/* Make sure the constant name isn't reserved */
	if(GenStateIsReservedConstant(pName)) {
		/* Reserved constant */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "const: Cannot redeclare a reserved constant '%z'", pName);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_EQUAL /* '=' */) == 0) {
		/* Invalid statement*/
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "const: Expected '=' after constant name");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	pGen->pIn++; /*Jump the equal sign */
	/* Allocate a new constant value container */
	pConsCode = (SySet *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(SySet));
	if(pConsCode == 0) {
		PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 engine is running out of memory");
		return SXERR_ABORT;
	}
	SySetInit(pConsCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
	/* Swap bytecode container */
	pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm);
	PH7_VmSetByteCodeContainer(pGen->pVm, pConsCode);
	/* Compile constant value */
	rc = PH7_CompileExpr(&(*pGen), 0, 0);
	/* Emit the done instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
	PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
	if(rc == SXERR_ABORT) {
		/* Don't worry about freeing memory, everything will be released shortly */
		return SXERR_ABORT;
	}
	SySetSetUserData(pConsCode, pGen->pVm);
	/* Register the constant */
	rc = PH7_VmRegisterConstant(pGen->pVm, pName, PH7_VmExpandConstantValue, pConsCode);
	if(rc != SXRET_OK) {
		SySetRelease(pConsCode);
		SyMemBackendPoolFree(&pGen->pVm->sAllocator, pConsCode);
	}
	return SXRET_OK;
Synchronize:
	/* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) {
		pGen->pIn++;
	}
	return SXRET_OK;
}
/*
 * Compile the 'continue' statement.
 * According to the PHP language reference
 *  continue is used within looping structures to skip the rest of the current loop iteration
 *  and continue execution at the condition evaluation and then the beginning of the next
 *  iteration.
 *  Note: Note that in PHP the switch statement is considered a looping structure for
 *  the purposes of continue.
 *  continue accepts an optional numeric argument which tells it how many levels
 *  of enclosing loops it should skip to the end of.
 *  Note:
 *   continue 0; and continue 1; is the same as running continue;.
 */
static sxi32 PH7_CompileContinue(ph7_gen_state *pGen) {
	GenBlock *pLoop; /* Target loop */
	sxi32 iLevel;    /* How many nesting loop to skip */
	sxu32 nLine;
	sxi32 rc;
	nLine = pGen->pIn->nLine;
	iLevel = 0;
	/* Jump the 'continue' keyword */
	pGen->pIn++;
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_NUM)) {
		/* optional numeric argument which tells us how many levels
		 * of enclosing loops we should skip to the end of.
		 */
		iLevel = (sxi32)PH7_TokenValueToInt64(&pGen->pIn->sData);
		if(iLevel < 2) {
			iLevel = 0;
		}
		pGen->pIn++; /* Jump the optional numeric argument */
	}
	/* Point to the target loop */
	pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
	if(pLoop == 0) {
		/* Illegal continue */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "A 'continue' statement may only be used within a loop or switch");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
	} else {
		sxu32 nInstrIdx = 0;
		if(pLoop->iFlags & GEN_BLOCK_SWITCH) {
			/* According to the PHP language reference manual
			 *  Note that unlike some other languages, the continue statement applies to switch
			 *  and acts similar to break. If you have a switch inside a loop and wish to continue
			 *  to the next iteration of the outer loop, use continue 2.
			 */
			rc = PH7_VmEmitInstr(pGen->pVm, PH7_OP_JMP, 0, 0, 0, &nInstrIdx);
			if(rc == SXRET_OK) {
				GenStateNewJumpFixup(pLoop, PH7_OP_JMP, nInstrIdx);
			}
		} else {
			/* Emit the unconditional jump to the beginning of the target loop */
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx);
			if(pLoop->bPostContinue == TRUE) {
				JumpFixup sJumpFix;
				/* Post-continue */
				sJumpFix.nJumpType = PH7_OP_JMP;
				sJumpFix.nInstrIdx = nInstrIdx;
				SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix);
			}
		}
	}
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) {
		/* Not so fatal,emit a warning only */
		PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'continue' statement");
	}
	/* Statement successfully compiled */
	return SXRET_OK;
}
/*
 * Compile the 'break' statement.
 * According to the PHP language reference
 *  break ends execution of the current for, foreach, while, do-while or switch
 *  structure.
 *  break accepts an optional numeric argument which tells it how many nested
 *  enclosing structures are to be broken out of.
 */
static sxi32 PH7_CompileBreak(ph7_gen_state *pGen) {
	GenBlock *pLoop; /* Target loop */
	sxi32 iLevel;    /* How many nesting loop to skip */
	sxu32 nLine;
	sxi32 rc;
	nLine = pGen->pIn->nLine;
	iLevel = 0;
	/* Jump the 'break' keyword */
	pGen->pIn++;
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_NUM)) {
		/* optional numeric argument which tells us how many levels
		 * of enclosing loops we should skip to the end of.
		 */
		iLevel = (sxi32)PH7_TokenValueToInt64(&pGen->pIn->sData);
		if(iLevel < 2) {
			iLevel = 0;
		}
		pGen->pIn++; /* Jump the optional numeric argument */
	}
	/* Extract the target loop */
	pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
	if(pLoop == 0) {
		/* Illegal break */
		rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "A 'break' statement may only be used within a loop or switch");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
	} else {
		sxu32 nInstrIdx;
		rc = PH7_VmEmitInstr(pGen->pVm, PH7_OP_JMP, 0, 0, 0, &nInstrIdx);
		if(rc == SXRET_OK) {
			/* Fix the jump later when the jump destination is resolved */
			GenStateNewJumpFixup(pLoop, PH7_OP_JMP, nInstrIdx);
		}
	}
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) {
		/* Not so fatal,emit a warning only */
		PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'break' statement");
	}
	/* Statement successfully compiled */
	return SXRET_OK;
}
/*
 * Point to the next PHP chunk that will be processed shortly.
 * Return SXRET_OK on success. Any other return value indicates
 * failure.
 */
static sxi32 GenStateNextChunk(ph7_gen_state *pGen) {
	ph7_value *pRawObj; /* Raw chunk [i.e: HTML,XML...] */
	sxu32 nRawObj;
	sxu32 nObjIdx;
	/* Consume raw chunks verbatim without any processing until we get
	 * a PHP block.
	 */
Consume:
	nRawObj = nObjIdx = 0;
	while(pGen->pRawIn < pGen->pRawEnd && pGen->pRawIn->nType != PH7_TOKEN_PHP) {
		pRawObj = PH7_ReserveConstObj(pGen->pVm, &nObjIdx);
		if(pRawObj == 0) {
			PH7_GenCompileError(pGen, E_ERROR, 1, "Fatal, PH7 engine is running out of memory");
			return SXERR_ABORT;
		}
		/* Mark as constant and emit the load constant instruction */
		PH7_MemObjInitFromString(pGen->pVm, pRawObj, &pGen->pRawIn->sData);
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, nObjIdx, 0, 0);
		++nRawObj;
		pGen->pRawIn++; /* Next chunk */
	}
	if(nRawObj > 0) {
		/* Emit the consume instruction */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_CONSUME, nRawObj, 0, 0, 0);
	}
	if(pGen->pRawIn < pGen->pRawEnd) {
		SySet *pTokenSet = pGen->pTokenSet;
		/* Reset the token set */
		SySetReset(pTokenSet);
		/* Tokenize input */
		PH7_TokenizePHP(SyStringData(&pGen->pRawIn->sData), SyStringLength(&pGen->pRawIn->sData),
						pGen->pRawIn->nLine, pTokenSet);
		/* Point to the fresh token stream */
		pGen->pIn  = (SyToken *)SySetBasePtr(pTokenSet);
		pGen->pEnd = &pGen->pIn[SySetUsed(pTokenSet)];
		/* Advance the stream cursor */
		pGen->pRawIn++;
		/* TICKET 1433-011 */
		if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_EQUAL)) {
			static const sxu32 nKeyID = PH7_TKWRD_ECHO;
			sxi32 rc;
			/* Refer to TICKET 1433-009  */
			pGen->pIn->nType = PH7_TK_KEYWORD;
			pGen->pIn->pUserData = SX_INT_TO_PTR(nKeyID);
			SyStringInitFromBuf(&pGen->pIn->sData, "echo", sizeof("echo") - 1);
			rc = PH7_CompileExpr(pGen, 0, 0);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			} else if(rc != SXERR_EMPTY) {
				PH7_VmEmitInstr(pGen->pVm, PH7_OP_POP, 1, 0, 0, 0);
			}
			goto Consume;
		}
	} else {
		/* No more chunks to process */
		pGen->pIn = pGen->pEnd;
		return SXERR_EOF;
	}
	return SXRET_OK;
}
/*
 * Compile a PHP block.
 * A block is simply one or more PHP statements and expressions to compile
 * optionally delimited by braces {}.
 * Return SXRET_OK on success. Any other return value indicates failure
 * and this function takes care of generating the appropriate error
 * message.
 */
static sxi32 PH7_CompileBlock(
	ph7_gen_state *pGen, /* Code generator state */
	sxi32 nKeywordEnd    /* EOF-keyword [i.e: endif;endfor;...]. 0 (zero) otherwise */
) {
	sxi32 rc;
	if(pGen->pIn->nType & PH7_TK_OCB /* '{' */) {
		sxu32 nLine = pGen->pIn->nLine;
		rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_STD, PH7_VmInstrLength(pGen->pVm), 0, 0);
		if(rc != SXRET_OK) {
			return SXERR_ABORT;
		}
		pGen->pIn++;
		/* Compile until we hit the closing braces '}' */
		for(;;) {
			if(pGen->pIn >= pGen->pEnd) {
				rc = GenStateNextChunk(&(*pGen));
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
				if(rc == SXERR_EOF) {
					/* No more token to process. Missing closing braces */
					PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Missing closing braces '}'");
					break;
				}
			}
			if(pGen->pIn->nType & PH7_TK_CCB/*'}'*/) {
				/* Closing braces found,break immediately*/
				pGen->pIn++;
				break;
			}
			/* Compile a single statement */
			rc = GenStateCompileChunk(&(*pGen), PH7_COMPILE_SINGLE_STMT);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
		}
		GenStateLeaveBlock(&(*pGen), 0);
	} else if((pGen->pIn->nType & PH7_TK_COLON /* ':' */) && nKeywordEnd > 0) {
		pGen->pIn++;
		rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_STD, PH7_VmInstrLength(pGen->pVm), 0, 0);
		if(rc != SXRET_OK) {
			return SXERR_ABORT;
		}
		/* Compile until we hit the EOF-keyword [i.e: endif;endfor;...] */
		for(;;) {
			if(pGen->pIn >= pGen->pEnd) {
				rc = GenStateNextChunk(&(*pGen));
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
				if(rc == SXERR_EOF || pGen->pIn >= pGen->pEnd) {
					/* No more token to process */
					if(rc == SXERR_EOF) {
						PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pEnd[-1].nLine,
											"Missing 'endfor;','endwhile;','endswitch;' or 'endforeach;' keyword");
					}
					break;
				}
			}
			if(pGen->pIn->nType & PH7_TK_KEYWORD) {
				sxi32 nKwrd;
				/* Keyword found */
				nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
				if(nKwrd == nKeywordEnd ||
						(nKeywordEnd == PH7_TKWRD_ENDIF && (nKwrd == PH7_TKWRD_ELSE || nKwrd == PH7_TKWRD_ELIF))) {
					/* Delimiter keyword found,break */
					if(nKwrd != PH7_TKWRD_ELSE && nKwrd != PH7_TKWRD_ELIF) {
						pGen->pIn++; /*  endif;endswitch... */
					}
					break;
				}
			}
			/* Compile a single statement */
			rc = GenStateCompileChunk(&(*pGen), PH7_COMPILE_SINGLE_STMT);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
		}
		GenStateLeaveBlock(&(*pGen), 0);
	} else {
		/* Compile a single statement */
		rc = GenStateCompileChunk(&(*pGen), PH7_COMPILE_SINGLE_STMT);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	}
	/* Jump trailing semi-colons ';' */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI)) {
		pGen->pIn++;
	}
	return SXRET_OK;
}
/*
 * Compile the gentle 'while' statement.
 * According to the PHP language reference
 *  while loops are the simplest type of loop in PHP.They behave just like their C counterparts.
 *  The basic form of a while statement is:
 *  while (expr)
 *   statement
 *  The meaning of a while statement is simple. It tells PHP to execute the nested statement(s)
 *  repeatedly, as long as the while expression evaluates to TRUE. The value of the expression
 *  is checked each time at the beginning of the loop, so even if this value changes during
 *  the execution of the nested statement(s), execution will not stop until the end of the iteration
 *  (each time PHP runs the statements in the loop is one iteration). Sometimes, if the while
 *  expression evaluates to FALSE from the very beginning, the nested statement(s) won't even be run once.
 *  Like with the if statement, you can group multiple statements within the same while loop by surrounding
 *  a group of statements with curly braces, or by using the alternate syntax:
 *  while (expr):
 *    statement
 *   endwhile;
 */
static sxi32 PH7_CompileWhile(ph7_gen_state *pGen) {
	GenBlock *pWhileBlock = 0;
	SyToken *pTmp, *pEnd = 0;
	sxu32 nFalseJump;
	sxu32 nLine;
	sxi32 rc;
	nLine = pGen->pIn->nLine;
	/* Jump the 'while' keyword */
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'while' keyword");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Jump the left parenthesis '(' */
	pGen->pIn++;
	/* Create the loop block */
	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, PH7_VmInstrLength(pGen->pVm), 0, &pWhileBlock);
	if(rc != SXRET_OK) {
		return SXERR_ABORT;
	}
	/* Delimit the condition */
	PH7_DelimitNestedTokens(pGen->pIn, pGen->pEnd, PH7_TK_LPAREN /* '(' */, PH7_TK_RPAREN /* ')' */, &pEnd);
	if(pGen->pIn == pEnd || pEnd >= pGen->pEnd) {
		/* Empty expression */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'while' keyword");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
	}
	/* Swap token streams */
	pTmp = pGen->pEnd;
	pGen->pEnd = pEnd;
	/* Compile the expression */
	rc = PH7_CompileExpr(&(*pGen), 0, 0);
	if(rc == SXERR_ABORT) {
		/* Expression handler request an operation abort [i.e: Out-of-memory] */
		return SXERR_ABORT;
	}
	/* Update token stream */
	while(pGen->pIn < pEnd) {
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		pGen->pIn++;
	}
	/* Synchronize pointers */
	pGen->pIn  = &pEnd[1];
	pGen->pEnd = pTmp;
	/* Emit the false jump */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_JZ, 0, 0, 0, &nFalseJump);
	/* Save the instruction index so we can fix it later when the jump destination is resolved */
	GenStateNewJumpFixup(pWhileBlock, PH7_OP_JZ, nFalseJump);
	/* Compile the loop body */
	rc = PH7_CompileBlock(&(*pGen), PH7_TKWRD_ENDWHILE);
	if(rc == SXERR_ABORT) {
		return SXERR_ABORT;
	}
	/* Emit the unconditional jump to the start of the loop */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_JMP, 0, pWhileBlock->nFirstInstr, 0, 0);
	/* Fix all jumps now the destination is resolved */
	GenStateFixJumps(pWhileBlock, -1, PH7_VmInstrLength(pGen->pVm));
	/* Release the loop block */
	GenStateLeaveBlock(pGen, 0);
	/* Statement successfully compiled */
	return SXRET_OK;
Synchronize:
	/* Synchronize with the first semi-colon ';' so we can avoid
	 * compiling this erroneous block.
	 */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI | PH7_TK_OCB)) == 0) {
		pGen->pIn++;
	}
	return SXRET_OK;
}
/*
 * Compile the ugly do..while() statement.
 * According to the PHP language reference
 *  do-while loops are very similar to while loops, except the truth expression is checked
 *  at the end of each iteration instead of in the beginning. The main difference from regular
 *  while loops is that the first iteration of a do-while loop is guaranteed to run
 *  (the truth expression is only checked at the end of the iteration), whereas it may not
 *  necessarily run with a regular while loop (the truth expression is checked at the beginning
 *  of each iteration, if it evaluates to FALSE right from the beginning, the loop execution
 *  would end immediately).
 *  There is just one syntax for do-while loops:
 *  <?php
 *  $i = 0;
 *  do {
 *   echo $i;
 *  } while ($i > 0);
 * ?>
 */
static sxi32 PH7_CompileDoWhile(ph7_gen_state *pGen) {
	SyToken *pTmp, *pEnd = 0;
	GenBlock *pDoBlock = 0;
	sxu32 nLine;
	sxi32 rc;
	nLine = pGen->pIn->nLine;
	/* Jump the 'do' keyword */
	pGen->pIn++;
	/* Create the loop block */
	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, PH7_VmInstrLength(pGen->pVm), 0, &pDoBlock);
	if(rc != SXRET_OK) {
		return SXERR_ABORT;
	}
	/* Deffer 'continue;' jumps until we compile the block */
	pDoBlock->bPostContinue = TRUE;
	rc = PH7_CompileBlock(&(*pGen), 0);
	if(rc == SXERR_ABORT) {
		return SXERR_ABORT;
	}
	if(pGen->pIn < pGen->pEnd) {
		nLine = pGen->pIn->nLine;
	}
	if(pGen->pIn >= pGen->pEnd || pGen->pIn->nType != PH7_TK_KEYWORD ||
			SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_WHILE) {
		/* Missing 'while' statement */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Missing 'while' statement after 'do' block");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Jump the 'while' keyword */
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'while' keyword");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Jump the left parenthesis '(' */
	pGen->pIn++;
	/* Delimit the condition */
	PH7_DelimitNestedTokens(pGen->pIn, pGen->pEnd, PH7_TK_LPAREN /* '(' */, PH7_TK_RPAREN /* ')' */, &pEnd);
	if(pGen->pIn == pEnd || pEnd >= pGen->pEnd) {
		/* Empty expression */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'while' keyword");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Fix post-continue jumps now the jump destination is resolved */
	if(SySetUsed(&pDoBlock->aPostContFix) > 0) {
		JumpFixup *aPost;
		VmInstr *pInstr;
		sxu32 nJumpDest;
		sxu32 n;
		aPost = (JumpFixup *)SySetBasePtr(&pDoBlock->aPostContFix);
		nJumpDest = PH7_VmInstrLength(pGen->pVm);
		for(n = 0 ; n < SySetUsed(&pDoBlock->aPostContFix) ; ++n) {
			pInstr = PH7_VmGetInstr(pGen->pVm, aPost[n].nInstrIdx);
			if(pInstr) {
				/* Fix */
				pInstr->iP2 = nJumpDest;
			}
		}
	}
	/* Swap token streams */
	pTmp = pGen->pEnd;
	pGen->pEnd = pEnd;
	/* Compile the expression */
	rc = PH7_CompileExpr(&(*pGen), 0, 0);
	if(rc == SXERR_ABORT) {
		/* Expression handler request an operation abort [i.e: Out-of-memory] */
		return SXERR_ABORT;
	}
	/* Update token stream */
	while(pGen->pIn < pEnd) {
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		pGen->pIn++;
	}
	pGen->pIn  = &pEnd[1];
	pGen->pEnd = pTmp;
	/* Emit the true jump to the beginning of the loop */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_JNZ, 0, pDoBlock->nFirstInstr, 0, 0);
	/* Fix all jumps now the destination is resolved */
	GenStateFixJumps(pDoBlock, -1, PH7_VmInstrLength(pGen->pVm));
	/* Release the loop block */
	GenStateLeaveBlock(pGen, 0);
	/* Statement successfully compiled */
	return SXRET_OK;
Synchronize:
	/* Synchronize with the first semi-colon ';' so we can avoid
	 * compiling this erroneous block.
	 */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI | PH7_TK_OCB)) == 0) {
		pGen->pIn++;
	}
	return SXRET_OK;
}
/*
 * Compile the complex and powerful 'for' statement.
 * According to the PHP language reference
 *  for loops are the most complex loops in PHP. They behave like their C counterparts.
 *  The syntax of a for loop is:
 *  for (expr1; expr2; expr3)
 *   statement
 *  The first expression (expr1) is evaluated (executed) once unconditionally at
 *  the beginning of the loop.
 *  In the beginning of each iteration, expr2 is evaluated. If it evaluates to
 *  TRUE, the loop continues and the nested statement(s) are executed. If it evaluates
 *  to FALSE, the execution of the loop ends.
 *  At the end of each iteration, expr3 is evaluated (executed).
 *  Each of the expressions can be empty or contain multiple expressions separated by commas.
 *  In expr2, all expressions separated by a comma are evaluated but the result is taken
 *  from the last part. expr2 being empty means the loop should be run indefinitely
 *  (PHP implicitly considers it as TRUE, like C). This may not be as useless as you might
 *  think, since often you'd want to end the loop using a conditional break statement instead
 *  of using the for truth expression.
 */
static sxi32 PH7_CompileFor(ph7_gen_state *pGen) {
	SyToken *pTmp, *pPostStart, *pEnd = 0;
	GenBlock *pForBlock = 0;
	sxu32 nFalseJump;
	sxu32 nLine;
	sxi32 rc;
	nLine = pGen->pIn->nLine;
	/* Jump the 'for' keyword */
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'for' keyword");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		return SXRET_OK;
	}
	/* Jump the left parenthesis '(' */
	pGen->pIn++;
	/* Delimit the init-expr;condition;post-expr */
	PH7_DelimitNestedTokens(pGen->pIn, pGen->pEnd, PH7_TK_LPAREN /* '(' */, PH7_TK_RPAREN /* ')' */, &pEnd);
	if(pGen->pIn == pEnd || pEnd >= pGen->pEnd) {
		/* Empty expression */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "for: Invalid expression");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		/* Synchronize */
		pGen->pIn = pEnd;
		if(pGen->pIn < pGen->pEnd) {
			pGen->pIn++;
		}
		return SXRET_OK;
	}
	/* Swap token streams */
	pTmp = pGen->pEnd;
	pGen->pEnd = pEnd;
	/* Compile initialization expressions if available */
	rc = PH7_CompileExpr(&(*pGen), 0, 0);
	/* Pop operand lvalues */
	if(rc == SXERR_ABORT) {
		/* Expression handler request an operation abort [i.e: Out-of-memory] */
		return SXERR_ABORT;
	} else if(rc != SXERR_EMPTY) {
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_POP, 1, 0, 0, 0);
	}
	if((pGen->pIn->nType & PH7_TK_SEMI) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
								 "for: Expected ';' after initialization expressions");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		return SXRET_OK;
	}
	/* Jump the trailing ';' */
	pGen->pIn++;
	/* Create the loop block */
	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, PH7_VmInstrLength(pGen->pVm), 0, &pForBlock);
	if(rc != SXRET_OK) {
		return SXERR_ABORT;
	}
	/* Deffer continue jumps */
	pForBlock->bPostContinue = TRUE;
	/* Compile the condition */
	rc = PH7_CompileExpr(&(*pGen), 0, 0);
	if(rc == SXERR_ABORT) {
		/* Expression handler request an operation abort [i.e: Out-of-memory] */
		return SXERR_ABORT;
	} else if(rc != SXERR_EMPTY) {
		/* Emit the false jump */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_JZ, 0, 0, 0, &nFalseJump);
		/* Save the instruction index so we can fix it later when the jump destination is resolved */
		GenStateNewJumpFixup(pForBlock, PH7_OP_JZ, nFalseJump);
	}
	if((pGen->pIn->nType & PH7_TK_SEMI) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
								 "for: Expected ';' after conditionals expressions");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		return SXRET_OK;
	}
	/* Jump the trailing ';' */
	pGen->pIn++;
	/* Save the post condition stream */
	pPostStart = pGen->pIn;
	/* Compile the loop body */
	pGen->pIn  = &pEnd[1]; /* Jump the trailing parenthesis ')' */
	pGen->pEnd = pTmp;
	rc = PH7_CompileBlock(&(*pGen), PH7_TKWRD_ENDFOR);
	if(rc == SXERR_ABORT) {
		return SXERR_ABORT;
	}
	/* Fix post-continue jumps */
	if(SySetUsed(&pForBlock->aPostContFix) > 0) {
		JumpFixup *aPost;
		VmInstr *pInstr;
		sxu32 nJumpDest;
		sxu32 n;
		aPost = (JumpFixup *)SySetBasePtr(&pForBlock->aPostContFix);
		nJumpDest = PH7_VmInstrLength(pGen->pVm);
		for(n = 0 ; n < SySetUsed(&pForBlock->aPostContFix) ; ++n) {
			pInstr = PH7_VmGetInstr(pGen->pVm, aPost[n].nInstrIdx);
			if(pInstr) {
				/* Fix jump */
				pInstr->iP2 = nJumpDest;
			}
		}
	}
	/* compile the post-expressions if available */
	while(pPostStart < pEnd && (pPostStart->nType & PH7_TK_SEMI)) {
		pPostStart++;
	}
	if(pPostStart < pEnd) {
		SyToken *pTmpIn, *pTmpEnd;
		SWAP_DELIMITER(pGen, pPostStart, pEnd);
		rc = PH7_CompileExpr(&(*pGen), 0, 0);
		if(pGen->pIn < pGen->pEnd) {
			/* Syntax error */
			rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "for: Expected ')' after post-expressions");
			if(rc == SXERR_ABORT) {
				/* Error count limit reached,abort immediately */
				return SXERR_ABORT;
			}
			return SXRET_OK;
		}
		RE_SWAP_DELIMITER(pGen);
		if(rc == SXERR_ABORT) {
			/* Expression handler request an operation abort [i.e: Out-of-memory] */
			return SXERR_ABORT;
		} else if(rc != SXERR_EMPTY) {
			/* Pop operand lvalue */
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_POP, 1, 0, 0, 0);
		}
	}
	/* Emit the unconditional jump to the start of the loop */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_JMP, 0, pForBlock->nFirstInstr, 0, 0);
	/* Fix all jumps now the destination is resolved */
	GenStateFixJumps(pForBlock, -1, PH7_VmInstrLength(pGen->pVm));
	/* Release the loop block */
	GenStateLeaveBlock(pGen, 0);
	/* Statement successfully compiled */
	return SXRET_OK;
}
/* Expression tree validator callback used by the 'foreach' statement.
 * Note that only variable expression [i.e: $x; ${'My'.'Var'}; ${$a['key]};...]
 * are allowed.
 */
static sxi32 GenStateForEachNodeValidator(ph7_gen_state *pGen, ph7_expr_node *pRoot) {
	sxi32 rc = SXRET_OK; /* Assume a valid expression tree */
	if(pRoot->xCode != PH7_CompileVariable) {
		/* Unexpected expression */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pRoot->pStart ? pRoot->pStart->nLine : 0,
								 "foreach: Expecting a variable name");
		if(rc != SXERR_ABORT) {
			rc = SXERR_INVALID;
		}
	}
	return rc;
}
/*
 * Compile the 'foreach' statement.
 * According to the PHP language reference
 *  The foreach construct simply gives an easy way to iterate over arrays. foreach works
 *  only on arrays (and objects), and will issue an error when you try to use it on a variable
 *  with a different data type or an uninitialized variable. There are two syntaxes; the second
 *  is a minor but useful extension of the first:
 *  foreach (array_expression as $value)
 *    statement
 *  foreach (array_expression as $key => $value)
 *   statement
 *  The first form loops over the array given by array_expression. On each loop, the value
 *  of the current element is assigned to $value and the internal array pointer is advanced
 *  by one (so on the next loop, you'll be looking at the next element).
 *  The second form does the same thing, except that the current element's key will be assigned
 *  to the variable $key on each loop.
 *  Note:
 *  When foreach first starts executing, the internal array pointer is automatically reset to the
 *  first element of the array. This means that you do not need to call reset() before a foreach loop.
 *  Note:
 *  Unless the array is referenced, foreach operates on a copy of the specified array and not the array
 *  itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during
 *  or after the foreach without resetting it.
 *  You can easily modify array's elements by preceding $value with &. This will assign reference instead
 *  of copying the value.
 */
static sxi32 PH7_CompileForeach(ph7_gen_state *pGen) {
	SyToken *pCur, *pTmp, *pEnd = 0;
	GenBlock *pForeachBlock = 0;
	ph7_foreach_info *pInfo;
	sxu32 nFalseJump;
	VmInstr *pInstr;
	sxu32 nLine;
	sxi32 rc;
	nLine = pGen->pIn->nLine;
	/* Jump the 'foreach' keyword */
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "foreach: Expected '('");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Jump the left parenthesis '(' */
	pGen->pIn++;
	/* Create the loop block */
	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, PH7_VmInstrLength(pGen->pVm), 0, &pForeachBlock);
	if(rc != SXRET_OK) {
		return SXERR_ABORT;
	}
	/* Delimit the expression */
	PH7_DelimitNestedTokens(pGen->pIn, pGen->pEnd, PH7_TK_LPAREN /* '(' */, PH7_TK_RPAREN /* ')' */, &pEnd);
	if(pGen->pIn == pEnd || pEnd >= pGen->pEnd) {
		/* Empty expression */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "foreach: Missing expression");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		/* Synchronize */
		pGen->pIn = pEnd;
		if(pGen->pIn < pGen->pEnd) {
			pGen->pIn++;
		}
		return SXRET_OK;
	}
	/* Compile the array expression */
	pCur = pGen->pIn;
	while(pCur < pEnd) {
		if(pCur->nType & PH7_TK_KEYWORD) {
			sxi32 nKeywrd = SX_PTR_TO_INT(pCur->pUserData);
			if(nKeywrd == PH7_TKWRD_AS) {
				/* Break with the first 'as' found */
				break;
			}
		}
		/* Advance the stream cursor */
		pCur++;
	}
	if(pCur <= pGen->pIn) {
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine,
								 "foreach: Missing array/object expression");
		if(rc == SXERR_ABORT) {
			/* Don't worry about freeing memory, everything will be released shortly */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Swap token streams */
	pTmp = pGen->pEnd;
	pGen->pEnd = pCur;
	rc = PH7_CompileExpr(&(*pGen), 0, 0);
	if(rc == SXERR_ABORT) {
		/* Expression handler request an operation abort [i.e: Out-of-memory] */
		return SXERR_ABORT;
	}
	/* Update token stream */
	while(pGen->pIn < pCur) {
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Unexpected token '%z'", &pGen->pIn->sData);
		if(rc == SXERR_ABORT) {
			/* Don't worry about freeing memory, everything will be released shortly */
			return SXERR_ABORT;
		}
		pGen->pIn++;
	}
	pCur++; /* Jump the 'as' keyword */
	pGen->pIn = pCur;
	if(pGen->pIn >= pEnd) {
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key => $value pair");
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	}
	/* Create the foreach context */
	pInfo = (ph7_foreach_info *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(ph7_foreach_info));
	if(pInfo == 0) {
		PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Fatal, PH7 engine is running out-of-memory");
		return SXERR_ABORT;
	}
	/* Zero the structure */
	SyZero(pInfo, sizeof(ph7_foreach_info));
	/* Initialize structure fields */
	SySetInit(&pInfo->aStep, &pGen->pVm->sAllocator, sizeof(ph7_foreach_step *));
	/* Check if we have a key field */
	while(pCur < pEnd && (pCur->nType & PH7_TK_ARRAY_OP) == 0) {
		pCur++;
	}
	if(pCur < pEnd) {
		/* Compile the expression holding the key name */
		if(pGen->pIn >= pCur) {
			rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key");
			if(rc == SXERR_ABORT) {
				/* Don't worry about freeing memory, everything will be released shortly */
				return SXERR_ABORT;
			}
		} else {
			pGen->pEnd = pCur;
			rc = PH7_CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
			if(rc == SXERR_ABORT) {
				/* Don't worry about freeing memory, everything will be released shortly */
				return SXERR_ABORT;
			}
			pInstr = PH7_VmPopInstr(pGen->pVm);
			if(pInstr->p3) {
				/* Record key name */
				SyStringInitFromBuf(&pInfo->sKey, pInstr->p3, SyStrlen((const char *)pInstr->p3));
			}
			pInfo->iFlags |= PH7_4EACH_STEP_KEY;
		}
		pGen->pIn = &pCur[1]; /* Jump the arrow */
	}
	pGen->pEnd = pEnd;
	if(pGen->pIn >= pEnd) {
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $value");
		if(rc == SXERR_ABORT) {
			/* Don't worry about freeing memory, everything will be released shortly */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	if(pGen->pIn->nType & PH7_TK_AMPER /*'&'*/) {
		pGen->pIn++;
		/* Pass by reference  */
		pInfo->iFlags |= PH7_4EACH_STEP_REF;
	}
	/* Compile the expression holding the value name */
	rc = PH7_CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
	if(rc == SXERR_ABORT) {
		/* Don't worry about freeing memory, everything will be released shortly */
		return SXERR_ABORT;
	}
	pInstr = PH7_VmPopInstr(pGen->pVm);
	if(pInstr->p3) {
		/* Record value name */
		SyStringInitFromBuf(&pInfo->sValue, pInstr->p3, SyStrlen((const char *)pInstr->p3));
	}
	/* Emit the 'FOREACH_INIT' instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_FOREACH_INIT, 0, 0, pInfo, &nFalseJump);
	/* Save the instruction index so we can fix it later when the jump destination is resolved */
	GenStateNewJumpFixup(pForeachBlock, PH7_OP_FOREACH_INIT, nFalseJump);
	/* Record the first instruction to execute */
	pForeachBlock->nFirstInstr = PH7_VmInstrLength(pGen->pVm);
	/* Emit the FOREACH_STEP instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_FOREACH_STEP, 0, 0, pInfo, &nFalseJump);
	/* Save the instruction index so we can fix it later when the jump destination is resolved */
	GenStateNewJumpFixup(pForeachBlock, PH7_OP_FOREACH_STEP, nFalseJump);
	/* Compile the loop body */
	pGen->pIn = &pEnd[1];
	pGen->pEnd = pTmp;
	rc = PH7_CompileBlock(&(*pGen), PH7_TKWRD_END4EACH);
	if(rc == SXERR_ABORT) {
		/* Don't worry about freeing memory, everything will be released shortly */
		return SXERR_ABORT;
	}
	/* Emit the unconditional jump to the start of the loop */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_JMP, 0, pForeachBlock->nFirstInstr, 0, 0);
	/* Fix all jumps now the destination is resolved */
	GenStateFixJumps(pForeachBlock, -1, PH7_VmInstrLength(pGen->pVm));
	/* Release the loop block */
	GenStateLeaveBlock(pGen, 0);
	/* Statement successfully compiled */
	return SXRET_OK;
Synchronize:
	/* Synchronize with the first semi-colon ';' so we can avoid
	 * compiling this erroneous block.
	 */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI | PH7_TK_OCB)) == 0) {
		pGen->pIn++;
	}
	return SXRET_OK;
}
/*
 * Compile the infamous if/elseif/else if/else statements.
 * According to the PHP language reference
 *  The if construct is one of the most important features of many languages PHP included.
 *  It allows for conditional execution of code fragments. PHP features an if structure
 *  that is similar to that of C:
 *  if (expr)
 *   statement
 *  else construct:
 *   Often you'd want to execute a statement if a certain condition is met, and a different
 *   statement if the condition is not met. This is what else is for. else extends an if statement
 *   to execute a statement in case the expression in the if statement evaluates to FALSE.
 *   For example, the following code would display a is greater than b if $a is greater than
 *   $b, and a is NOT greater than b otherwise.
 *   The else statement is only executed if the if expression evaluated to FALSE, and if there
 *   were any elseif expressions - only if they evaluated to FALSE as well
 *  elseif
 *   elseif, as its name suggests, is a combination of if and else. Like else, it extends
 *   an if statement to execute a different statement in case the original if expression evaluates
 *   to FALSE. However, unlike else, it will execute that alternative expression only if the elseif
 *   conditional expression evaluates to TRUE. For example, the following code would display a is bigger
 *   than b, a equal to b or a is smaller than b:
 *   <?php
 *    if ($a > $b) {
 *     echo "a is bigger than b";
 *    } elseif ($a == $b) {
 *     echo "a is equal to b";
 *    } else {
 *     echo "a is smaller than b";
 *    }
 *    ?>
 */
static sxi32 PH7_CompileIf(ph7_gen_state *pGen) {
	SyToken *pToken, *pTmp, *pEnd = 0;
	GenBlock *pCondBlock = 0;
	sxu32 nJumpIdx;
	sxu32 nKeyID;
	sxi32 rc;
	/* Jump the 'if' keyword */
	pGen->pIn++;
	pToken = pGen->pIn;
	/* Create the conditional block */
	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_COND, PH7_VmInstrLength(pGen->pVm), 0, &pCondBlock);
	if(rc != SXRET_OK) {
		return SXERR_ABORT;
	}
	/* Process as many [if/else if/elseif/else] blocks as we can */
	for(;;) {
		if(pToken >= pGen->pEnd || (pToken->nType & PH7_TK_LPAREN) == 0) {
			/* Syntax error */
			if(pToken >= pGen->pEnd) {
				pToken--;
			}
			rc = PH7_GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing '('");
			if(rc == SXERR_ABORT) {
				/* Error count limit reached,abort immediately */
				return SXERR_ABORT;
			}
			goto Synchronize;
		}
		/* Jump the left parenthesis '(' */
		pToken++;
		/* Delimit the condition */
		PH7_DelimitNestedTokens(pToken, pGen->pEnd, PH7_TK_LPAREN /* '(' */, PH7_TK_RPAREN /* ')' */, &pEnd);
		if(pToken >= pEnd || (pEnd->nType & PH7_TK_RPAREN) == 0) {
			/* Syntax error */
			if(pToken >= pGen->pEnd) {
				pToken--;
			}
			rc = PH7_GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing ')'");
			if(rc == SXERR_ABORT) {
				/* Error count limit reached,abort immediately */
				return SXERR_ABORT;
			}
			goto Synchronize;
		}
		/* Swap token streams */
		SWAP_TOKEN_STREAM(pGen, pToken, pEnd);
		/* Compile the condition */
		rc = PH7_CompileExpr(&(*pGen), 0, 0);
		/* Update token stream */
		while(pGen->pIn < pEnd) {
			PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
			pGen->pIn++;
		}
		pGen->pIn  = &pEnd[1];
		pGen->pEnd = pTmp;
		if(rc == SXERR_ABORT) {
			/* Expression handler request an operation abort [i.e: Out-of-memory] */
			return SXERR_ABORT;
		}
		/* Emit the false jump */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_JZ, 0, 0, 0, &nJumpIdx);
		/* Save the instruction index so we can fix it later when the jump destination is resolved */
		GenStateNewJumpFixup(pCondBlock, PH7_OP_JZ, nJumpIdx);
		/* Compile the body */
		rc = PH7_CompileBlock(&(*pGen), PH7_TKWRD_ENDIF);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0) {
			break;
		}
		/* Ensure that the keyword ID is 'else if' or 'else' */
		nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
		if((nKeyID & (PH7_TKWRD_ELSE | PH7_TKWRD_ELIF)) == 0) {
			break;
		}
		/* Emit the unconditional jump */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_JMP, 0, 0, 0, &nJumpIdx);
		/* Save the instruction index so we can fix it later when the jump destination is resolved */
		GenStateNewJumpFixup(pCondBlock, PH7_OP_JMP, nJumpIdx);
		if(nKeyID & PH7_TKWRD_ELSE) {
			pToken = &pGen->pIn[1];
			if(pToken >= pGen->pEnd || (pToken->nType & PH7_TK_KEYWORD) == 0 ||
					SX_PTR_TO_INT(pToken->pUserData) != PH7_TKWRD_IF) {
				break;
			}
			pGen->pIn++; /* Jump the 'else' keyword */
		}
		pGen->pIn++; /* Jump the 'elseif/if' keyword */
		/* Synchronize cursors */
		pToken = pGen->pIn;
		/* Fix the false jump */
		GenStateFixJumps(pCondBlock, PH7_OP_JZ, PH7_VmInstrLength(pGen->pVm));
	} /* For(;;) */
	/* Fix the false jump */
	GenStateFixJumps(pCondBlock, PH7_OP_JZ, PH7_VmInstrLength(pGen->pVm));
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) &&
			(SX_PTR_TO_INT(pGen->pIn->pUserData) & PH7_TKWRD_ELSE)) {
		/* Compile the else block */
		pGen->pIn++;
		rc = PH7_CompileBlock(&(*pGen), PH7_TKWRD_ENDIF);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	}
	nJumpIdx = PH7_VmInstrLength(pGen->pVm);
	/* Fix all unconditional jumps now the destination is resolved */
	GenStateFixJumps(pCondBlock, PH7_OP_JMP, nJumpIdx);
	/* Release the conditional block */
	GenStateLeaveBlock(pGen, 0);
	/* Statement successfully compiled */
	return SXRET_OK;
Synchronize:
	/* Synchronize with the first semi-colon ';' so we can avoid compiling this erroneous block.
	 */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI | PH7_TK_OCB)) == 0) {
		pGen->pIn++;
	}
	return SXRET_OK;
}
/*
 * Compile the global construct.
 * According to the PHP language reference
 *  In PHP global variables must be declared global inside a function if they are going
 *  to be used in that function.
 *  Example #1 Using global
 *  <?php
 *   $a = 1;
 *   $b = 2;
 *   function Sum()
 *   {
 *    global $a, $b;
 *    $b = $a + $b;
 *   }
 *   Sum();
 *   echo $b;
 *  ?>
 *  The above script will output 3. By declaring $a and $b global within the function
 *  all references to either variable will refer to the global version. There is no limit
 *  to the number of global variables that can be manipulated by a function.
 */
static sxi32 PH7_CompileGlobal(ph7_gen_state *pGen) {
	SyToken *pTmp, *pNext = 0;
	sxi32 nExpr;
	sxi32 rc;
	/* Jump the 'global' keyword */
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_SEMI)) {
		/* Nothing to process */
		return SXRET_OK;
	}
	pTmp = pGen->pEnd;
	nExpr = 0;
	while(SXRET_OK == PH7_GetNextExpr(pGen->pIn, pTmp, &pNext)) {
		if(pGen->pIn < pNext) {
			pGen->pEnd = pNext;
			if((pGen->pIn->nType & PH7_TK_DOLLAR) == 0) {
				rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "global: Expected variable name");
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
			} else {
				pGen->pIn++;
				if(pGen->pIn >= pGen->pEnd) {
					/* Emit a warning */
					PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn[-1].nLine, "global: Empty variable name");
				} else {
					rc = PH7_CompileExpr(&(*pGen), 0, 0);
					if(rc == SXERR_ABORT) {
						return SXERR_ABORT;
					} else if(rc != SXERR_EMPTY) {
						nExpr++;
					}
				}
			}
		}
		/* Next expression in the stream */
		pGen->pIn = pNext;
		/* Jump trailing commas */
		while(pGen->pIn < pTmp && (pGen->pIn->nType & PH7_TK_COMMA)) {
			pGen->pIn++;
		}
	}
	/* Restore token stream */
	pGen->pEnd = pTmp;
	if(nExpr > 0) {
		/* Emit the uplink instruction */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_UPLINK, nExpr, 0, 0, 0);
	}
	return SXRET_OK;
}
/*
 * Compile the return statement.
 * According to the PHP language reference
 *  If called from within a function, the return() statement immediately ends execution
 *  of the current function, and returns its argument as the value of the function call.
 *  return() will also end the execution of an eval() statement or script file.
 *  If called from the global scope, then execution of the current script file is ended.
 *  If the current script file was include()ed or require()ed, then control is passed back
 *  to the calling file. Furthermore, if the current script file was include()ed, then the value
 *  given to return() will be returned as the value of the include() call. If return() is called
 *  from within the main script file, then script execution end.
 *  Note that since return() is a language construct and not a function, the parentheses
 *  surrounding its arguments are not required. It is common to leave them out, and you actually
 *  should do so as PHP has less work to do in this case.
 *  Note: If no parameter is supplied, then the parentheses must be omitted and NULL will be returned.
 */
static sxi32 PH7_CompileReturn(ph7_gen_state *pGen) {
	sxi32 nRet = 0; /* TRUE if there is a return value */
	sxi32 rc;
	/* Jump the 'return' keyword */
	pGen->pIn++;
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) {
		/* Compile the expression */
		rc = PH7_CompileExpr(&(*pGen), 0, 0);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		} else if(rc != SXERR_EMPTY) {
			nRet = 1;
		}
	}
	/* Emit the done instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_DONE, nRet, 0, 0, 0);
	return SXRET_OK;
}
/*
 * Compile the die/exit language construct.
 * The role of these constructs is to terminate execution of the script.
 * Shutdown functions will always be executed even if exit() is called.
 */
static sxi32 PH7_CompileHalt(ph7_gen_state *pGen) {
	sxi32 nExpr = 0;
	sxi32 rc;
	/* Jump the die/exit keyword */
	pGen->pIn++;
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) {
		/* Compile the expression */
		rc = PH7_CompileExpr(&(*pGen), 0, 0);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		} else if(rc != SXERR_EMPTY) {
			nExpr = 1;
		}
	}
	/* Emit the HALT instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_HALT, nExpr, 0, 0, 0);
	return SXRET_OK;
}
/*
 * Compile the 'echo' language construct.
 */
static sxi32 PH7_CompileEcho(ph7_gen_state *pGen) {
	SyToken *pTmp, *pNext = 0;
	sxi32 rc;
	/* Jump the 'echo' keyword */
	pGen->pIn++;
	/* Compile arguments one after one */
	pTmp = pGen->pEnd;
	while(SXRET_OK == PH7_GetNextExpr(pGen->pIn, pTmp, &pNext)) {
		if(pGen->pIn < pNext) {
			pGen->pEnd = pNext;
			rc = PH7_CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			} else if(rc != SXERR_EMPTY) {
				/* Emit the consume instruction */
				PH7_VmEmitInstr(pGen->pVm, PH7_OP_CONSUME, 1, 0, 0, 0);
			}
		}
		/* Jump trailing commas */
		while(pNext < pTmp && (pNext->nType & PH7_TK_COMMA)) {
			pNext++;
		}
		pGen->pIn = pNext;
	}
	/* Restore token stream */
	pGen->pEnd = pTmp;
	return SXRET_OK;
}
/*
 * Compile the static statement.
 * According to the PHP language reference
 *  Another important feature of variable scoping is the static variable.
 *  A static variable exists only in a local function scope, but it does not lose its value
 *  when program execution leaves this scope.
 *  Static variables also provide one way to deal with recursive functions.
 * Symisc eXtension.
 *  PH7 allow any complex expression to be associated with the static variable while
 *  the zend engine would allow only simple scalar value.
 *  Example
 *    static $myVar = "Welcome "." guest ".rand_str(3); //Valid under PH7/Generate error using the zend engine
 *    Refer to the official documentation for more information on this feature.
 */
static sxi32 PH7_CompileStatic(ph7_gen_state *pGen) {
	ph7_vm_func_static_var sStatic; /* Structure describing the static variable */
	ph7_vm_func *pFunc;             /* Enclosing function */
	GenBlock *pBlock;
	SyString *pName;
	char *zDup;
	sxu32 nLine;
	sxi32 rc;
	/* Jump the static keyword */
	nLine = pGen->pIn->nLine;
	pGen->pIn++;
	/* Extract the enclosing function if any */
	pBlock = pGen->pCurrent;
	while(pBlock) {
		if(pBlock->iFlags & GEN_BLOCK_FUNC) {
			break;
		}
		/* Point to the upper block */
		pBlock = pBlock->pParent;
	}
	if(pBlock == 0) {
		/* Static statement,called outside of a function body,treat it as a simple variable. */
		if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR) == 0) {
			rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
			goto Synchronize;
		}
		/* Compile the expression holding the variable */
		rc = PH7_CompileExpr(&(*pGen), 0, 0);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		} else if(rc != SXERR_EMPTY) {
			/* Emit the POP instruction */
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_POP, 1, 0, 0, 0);
		}
		return SXRET_OK;
	}
	pFunc = (ph7_vm_func *)pBlock->pUserData;
	/* Make sure we are dealing with a valid statement */
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd ||
			(pGen->pIn[1].nType & (PH7_TK_ID | PH7_TK_KEYWORD)) == 0) {
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	pGen->pIn++;
	/* Extract variable name */
	pName = &pGen->pIn->sData;
	pGen->pIn++; /* Jump the var name */
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI/*';'*/ | PH7_TK_EQUAL/*'='*/)) == 0) {
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "static: Unexpected token '%z'", &pGen->pIn->sData);
		goto Synchronize;
	}
	/* Initialize the structure describing the static variable */
	SySetInit(&sStatic.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
	sStatic.nIdx = SXU32_HIGH; /* Not yet created */
	/* Duplicate variable name */
	zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
	if(zDup == 0) {
		PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, PH7 engine is running out of memory");
		return SXERR_ABORT;
	}
	SyStringInitFromBuf(&sStatic.sName, zDup, pName->nByte);
	/* Check if we have an expression to compile */
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_EQUAL)) {
		SySet *pInstrContainer;
		/* TICKET 1433-014: Symisc extension to the PHP programming language
		 * Static variable can take any complex expression including function
		 * call as their initialization value.
		 * Example:
		 *		static $var = foo(1,4+5,bar());
		 */
		pGen->pIn++; /* Jump the equal '=' sign */
		/* Swap bytecode container */
		pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm);
		PH7_VmSetByteCodeContainer(pGen->pVm, &sStatic.aByteCode);
		/* Compile the expression */
		rc = PH7_CompileExpr(&(*pGen), 0, 0);
		/* Emit the done instruction */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
		/* Restore default bytecode container */
		PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
	}
	/* Finally save the compiled static variable in the appropriate container */
	SySetPut(&pFunc->aStatic, (const void *)&sStatic);
	return SXRET_OK;
Synchronize:
	/* Synchronize with the first semi-colon ';',so we can avoid compiling this erroneous
	 * statement.
	 */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) ==  0) {
		pGen->pIn++;
	}
	return SXRET_OK;
}
/*
 * Compile the var statement.
 * Symisc Extension:
 *      var statement can be used outside of a class definition.
 */
static sxi32 PH7_CompileVar(ph7_gen_state *pGen) {
	sxu32 nLine = pGen->pIn->nLine;
	sxi32 rc;
	/* Jump the 'var' keyword */
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR/*'$'*/) == 0) {
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "var: Expecting variable name");
		/* Synchronize with the first semi-colon */
		while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0) {
			pGen->pIn++;
		}
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	} else {
		/* Compile the expression */
		rc = PH7_CompileExpr(&(*pGen), 0, 0);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		} else if(rc != SXERR_EMPTY) {
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_POP, 1, 0, 0, 0);
		}
	}
	return SXRET_OK;
}
/*
 * Compile a namespace statement
 * According to the PHP language reference manual
 *  What are namespaces? In the broadest definition namespaces are a way of encapsulating items.
 *  This can be seen as an abstract concept in many places. For example, in any operating system
 *  directories serve to group related files, and act as a namespace for the files within them.
 *  As a concrete example, the file foo.txt can exist in both directory /home/greg and in /home/other
 *  but two copies of foo.txt cannot co-exist in the same directory. In addition, to access the foo.txt
 *  file outside of the /home/greg directory, we must prepend the directory name to the file name using
 *  the directory separator to get /home/greg/foo.txt. This same principle extends to namespaces in the
 *  programming world.
 *  In the PHP world, namespaces are designed to solve two problems that authors of libraries and applications
 *  encounter when creating re-usable code elements such as classes or functions:
 *  Name collisions between code you create, and internal PHP classes/functions/constants or third-party
 *  classes/functions/constants.
 *  Ability to alias (or shorten) Extra_Long_Names designed to alleviate the first problem, improving
 *  readability of source code.
 *  PHP Namespaces provide a way in which to group related classes, interfaces, functions and constants.
 *  Here is an example of namespace syntax in PHP:
 *       namespace my\name; // see "Defining Namespaces" section
 *       class MyClass {}
 *       function myfunction() {}
 *       const MYCONST = 1;
 *       $a = new MyClass;
 *       $c = new \my\name\MyClass;
 *       $a = strlen('hi');
 *       $d = namespace\MYCONST;
 *       $d = __NAMESPACE__ . '\MYCONST';
 *       echo constant($d);
 * NOTE
 *  AS OF THIS VERSION NAMESPACE SUPPORT IS DISABLED. IF YOU NEED A WORKING VERSION THAT IMPLEMENT
 *  NAMESPACE,PLEASE CONTACT SYMISC SYSTEMS VIA contact@symisc.net.
 */
static sxi32 PH7_CompileNamespace(ph7_gen_state *pGen) {
	sxu32 nLine = pGen->pIn->nLine;
	sxi32 rc;
	pGen->pIn++; /* Jump the 'namespace' keyword */
	if(pGen->pIn >= pGen->pEnd ||
			(pGen->pIn->nType & (PH7_TK_NSSEP | PH7_TK_ID | PH7_TK_KEYWORD | PH7_TK_SEMI/*';'*/ | PH7_TK_OCB/*'{'*/)) == 0) {
		SyToken *pTok = pGen->pIn;
		if(pTok >= pGen->pEnd) {
			pTok--;
		}
		/* Unexpected token */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Namespace: Unexpected token '%z'", &pTok->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	}
	/* Ignore the path */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_NSSEP/*'\'*/ | PH7_TK_ID | PH7_TK_KEYWORD))) {
		pGen->pIn++;
	}
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI/*';'*/ | PH7_TK_OCB/*'{'*/)) == 0) {
		/* Unexpected token */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine,
								 "Namespace: Unexpected token '%z',expecting ';' or '{'", &pGen->pIn->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	}
	/* Emit a warning */
	PH7_GenCompileError(&(*pGen), E_WARNING, nLine,
						"Namespace support is disabled in the current release of the PH7(%s) engine", ph7_lib_version());
	return SXRET_OK;
}
/*
 * Compile the 'use' statement
 * According to the PHP language reference manual
 *  The ability to refer to an external fully qualified name with an alias or importing
 *  is an important feature of namespaces. This is similar to the ability of unix-based
 *  filesystems to create symbolic links to a file or to a directory.
 *  PHP namespaces support three kinds of aliasing or importing: aliasing a class name
 *  aliasing an interface name, and aliasing a namespace name. Note that importing
 *  a function or constant is not supported.
 *  In PHP, aliasing is accomplished with the 'use' operator.
 * NOTE
 *  AS OF THIS VERSION NAMESPACE SUPPORT IS DISABLED. IF YOU NEED A WORKING VERSION THAT IMPLEMENT
 *  NAMESPACE,PLEASE CONTACT SYMISC SYSTEMS VIA contact@symisc.net.
 */
static sxi32 PH7_CompileUse(ph7_gen_state *pGen) {
	sxu32 nLine = pGen->pIn->nLine;
	sxi32 rc;
	pGen->pIn++; /* Jump the 'use' keyword */
	/* Assemeble one or more real namespace path */
	for(;;) {
		if(pGen->pIn >= pGen->pEnd) {
			break;
		}
		/* Ignore the path */
		while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_NSSEP | PH7_TK_ID))) {
			pGen->pIn++;
		}
		if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA/*','*/)) {
			pGen->pIn++; /* Jump the comma and process the next path */
		} else {
			break;
		}
	}
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && PH7_TKWRD_AS == SX_PTR_TO_INT(pGen->pIn->pUserData)) {
		pGen->pIn++; /* Jump the 'as' keyword */
		/* Compile one or more aliasses */
		for(;;) {
			if(pGen->pIn >= pGen->pEnd) {
				break;
			}
			while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_NSSEP | PH7_TK_ID))) {
				pGen->pIn++;
			}
			if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA/*','*/)) {
				pGen->pIn++; /* Jump the comma and process the next alias */
			} else {
				break;
			}
		}
	}
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0) {
		/* Unexpected token */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "use statement: Unexpected token '%z',expecting ';'",
								 &pGen->pIn->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	}
	/* Emit a notice */
	PH7_GenCompileError(&(*pGen), E_NOTICE, nLine,
						"Namespace support is disabled in the current release of the PH7(%s) engine",
						ph7_lib_version()
					   );
	return SXRET_OK;
}
/*
 * Compile the stupid 'declare' language construct.
 *
 * According to the PHP language reference manual.
 *  The declare construct is used to set execution directives for a block of code.
 *  The syntax of declare is similar to the syntax of other flow control constructs:
 *  declare (directive)
 *   statement
 * The directive section allows the behavior of the declare block to be set.
 *  Currently only two directives are recognized: the ticks directive and the encoding directive.
 * The statement part of the declare block will be executed - how it is executed and what side
 * effects occur during execution may depend on the directive set in the directive block.
 * The declare construct can also be used in the global scope, affecting all code following
 * it (however if the file with declare was included then it does not affect the parent file).
 * <?php
 * // these are the same:
 * // you can use this:
 * declare(ticks=1) {
 *   // entire script here
 * }
 * // or you can use this:
 * declare(ticks=1);
 * // entire script here
 * ?>
 *
 * Well,actually this language construct is a NO-OP in the current release of the PH7 engine.
 */
static sxi32 PH7_CompileDeclare(ph7_gen_state *pGen) {
	sxu32 nLine = pGen->pIn->nLine;
	SyToken *pEnd = 0; /* cc warning */
	sxi32 rc;
	pGen->pIn++; /* Jump the 'declare' keyword */
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 /*'('*/) {
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "declare: Expecting opening parenthesis '('");
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		goto Synchro;
	}
	pGen->pIn++; /* Jump the left parenthesis */
	/* Delimit the directive */
	PH7_DelimitNestedTokens(pGen->pIn, pGen->pEnd, PH7_TK_LPAREN/*'('*/, PH7_TK_RPAREN/*')'*/, &pEnd);
	if(pEnd >= pGen->pEnd) {
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "declare: Missing closing parenthesis ')'");
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		return SXRET_OK;
	}
	/* Update the cursor */
	pGen->pIn = &pEnd[1];
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_SEMI/*';'*/ | PH7_TK_OCB/*'{'*/)) == 0) {
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "declare: Expecting ';' or '{' after directive");
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	}
	/* TICKET 1433-81: This construct is disabled in the current release of the PH7 engine. */
	PH7_GenCompileError(&(*pGen), E_NOTICE, nLine, /* Emit a notice */
						"the declare construct is a no-op in the current release of the PH7(%s) engine",
						ph7_lib_version()
					   );
	/*All done */
	return SXRET_OK;
Synchro:
	/* Sycnhronize with the first semi-colon ';' or curly braces '{' */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI/*';'*/ | PH7_TK_OCB/*'{'*/)) == 0) {
		pGen->pIn++;
	}
	return SXRET_OK;
}
/*
 * Process default argument values. That is,a function may define C++-style default value
 * as follows:
 * function makecoffee($type = "cappuccino")
 * {
 *   return "Making a cup of $type.\n";
 * }
 * Symisc eXtension.
 *  1 -) Default arguments value can be any complex expression [i.e: function call,annynoymous
 *      functions,array member,..] unlike the zend which would allow only single scalar value.
 *      Example: Work only with PH7,generate error under zend
 *      function test($a = 'Hello'.'World: '.rand_str(3))
 *      {
 *       var_dump($a);
 *      }
 *     //call test without args
 *      test();
 * 2 -) Full type hinting: (Arguments are automatically casted to the desired type)
 *      Example:
 *           function a(string $a){} function b(int $a,string $c,float $d){}
 * 3 -) Function overloading!!
 *      Example:
 *      function foo($a) {
 *   	  return $a.PHP_EOL;
 *	    }
 *	    function foo($a, $b) {
 *   	  return $a + $b;
 *	    }
 *	    echo foo(5); // Prints "5"
 *	    echo foo(5, 2); // Prints "7"
 *      // Same arg
 *	   function foo(string $a)
 *	   {
 *	     echo "a is a string\n";
 *	     var_dump($a);
 *	   }
 *	  function foo(int $a)
 *	  {
 *	    echo "a is integer\n";
 *	    var_dump($a);
 *	  }
 *	  function foo(array $a)
 *	  {
 * 	    echo "a is an array\n";
 * 	    var_dump($a);
 *	  }
 *	  foo('This is a great feature'); // a is a string [first foo]
 *	  foo(52); // a is integer [second foo]
 *    foo(array(14,__TIME__,__DATE__)); // a is an array [third foo]
 * Please refer to the official documentation for more information on the powerful extension
 * introduced by the PH7 engine.
 */
static sxi32 GenStateProcessArgValue(ph7_gen_state *pGen, ph7_vm_func_arg *pArg, SyToken *pIn, SyToken *pEnd) {
	SyToken *pTmpIn, *pTmpEnd;
	SySet *pInstrContainer;
	sxi32 rc;
	/* Swap token stream */
	SWAP_DELIMITER(pGen, pIn, pEnd);
	pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm);
	PH7_VmSetByteCodeContainer(pGen->pVm, &pArg->aByteCode);
	/* Compile the expression holding the argument value */
	rc = PH7_CompileExpr(&(*pGen), 0, 0);
	/* Emit the done instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
	PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
	RE_SWAP_DELIMITER(pGen);
	if(rc == SXERR_ABORT) {
		return SXERR_ABORT;
	}
	return SXRET_OK;
}
/*
 * Collect function arguments one after one.
 * According to the PHP language reference manual.
 * Information may be passed to functions via the argument list, which is a comma-delimited
 * list of expressions.
 * PHP supports passing arguments by value (the default), passing by reference
 * and default argument values. Variable-length argument lists are also supported,
 * see also the function references for func_num_args(), func_get_arg(), and func_get_args()
 * for more information.
 * Example #1 Passing arrays to functions
 * <?php
 * function takes_array($input)
 * {
 *    echo "$input[0] + $input[1] = ", $input[0]+$input[1];
 * }
 * ?>
 * Making arguments be passed by reference
 * By default, function arguments are passed by value (so that if the value of the argument
 * within the function is changed, it does not get changed outside of the function).
 * To allow a function to modify its arguments, they must be passed by reference.
 * To have an argument to a function always passed by reference, prepend an ampersand (&)
 * to the argument name in the function definition:
 * Example #2 Passing function parameters by reference
 * <?php
 * function add_some_extra(&$string)
 * {
 *   $string .= 'and something extra.';
 * }
 * $str = 'This is a string, ';
 * add_some_extra($str);
 * echo $str;    // outputs 'This is a string, and something extra.'
 * ?>
 *
 * PH7 have introduced powerful extension including full type hinting,function overloading
 * complex agrument values.Please refer to the official documentation for more information
 * on these extension.
 */
static sxi32 GenStateCollectFuncArgs(ph7_vm_func *pFunc, ph7_gen_state *pGen, SyToken *pEnd) {
	ph7_vm_func_arg sArg; /* Current processed argument */
	SyToken *pCur, *pIn; /* Token stream */
	SyBlob sSig;         /* Function signature */
	char *zDup;          /* Copy of argument name */
	sxi32 rc;
	pIn = pGen->pIn;
	pCur = 0;
	SyBlobInit(&sSig, &pGen->pVm->sAllocator);
	/* Process arguments one after one */
	for(;;) {
		if(pIn >= pEnd) {
			/* No more arguments to process */
			break;
		}
		SyZero(&sArg, sizeof(ph7_vm_func_arg));
		SySetInit(&sArg.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
		if(pIn->nType & (PH7_TK_ID | PH7_TK_KEYWORD)) {
			if(pIn->nType & PH7_TK_KEYWORD) {
				sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pIn->pUserData));
				if(nKey & PH7_TKWRD_ARRAY) {
					sArg.nType = MEMOBJ_HASHMAP;
				} else if(nKey & PH7_TKWRD_BOOL) {
					sArg.nType = MEMOBJ_BOOL;
				} else if(nKey & PH7_TKWRD_INT) {
					sArg.nType = MEMOBJ_INT;
				} else if(nKey & PH7_TKWRD_STRING) {
					sArg.nType = MEMOBJ_STRING;
				} else if(nKey & PH7_TKWRD_FLOAT) {
					sArg.nType = MEMOBJ_REAL;
				} else {
					PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine,
										"Invalid argument type '%z',Automatic cast will not be performed",
										&pIn->sData);
				}
			} else {
				SyString *pName = &pIn->sData; /* Class name */
				char *zDup;
				/* Argument must be a class instance,record that*/
				zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
				if(zDup) {
					sArg.nType = SXU32_HIGH; /* 0xFFFFFFFF as sentinel */
					SyStringInitFromBuf(&sArg.sClass, zDup, pName->nByte);
				}
			}
			pIn++;
		}
		if(pIn >= pEnd) {
			rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Missing argument name");
			return rc;
		}
		if(pIn->nType & PH7_TK_AMPER) {
			/* Pass by reference,record that */
			sArg.iFlags = VM_FUNC_ARG_BY_REF;
			pIn++;
		}
		if(pIn >= pEnd || (pIn->nType & PH7_TK_DOLLAR) == 0 || &pIn[1] >= pEnd || (pIn[1].nType & (PH7_TK_ID | PH7_TK_KEYWORD)) == 0) {
			/* Invalid argument */
			rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Invalid argument name");
			return rc;
		}
		pIn++; /* Jump the dollar sign */
		/* Copy argument name */
		zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, SyStringData(&pIn->sData), SyStringLength(&pIn->sData));
		if(zDup == 0) {
			PH7_GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "PH7 engine is running out of memory");
			return SXERR_ABORT;
		}
		SyStringInitFromBuf(&sArg.sName, zDup, SyStringLength(&pIn->sData));
		pIn++;
		if(pIn < pEnd) {
			if(pIn->nType & PH7_TK_EQUAL) {
				SyToken *pDefend;
				sxi32 iNest = 0;
				pIn++; /* Jump the equal sign */
				pDefend = pIn;
				/* Process the default value associated with this argument */
				while(pDefend < pEnd) {
					if((pDefend->nType & PH7_TK_COMMA) && iNest <= 0) {
						break;
					}
					if(pDefend->nType & (PH7_TK_LPAREN/*'('*/ | PH7_TK_OCB/*'{'*/ | PH7_TK_OSB/*[*/)) {
						/* Increment nesting level */
						iNest++;
					} else if(pDefend->nType & (PH7_TK_RPAREN/*')'*/ | PH7_TK_CCB/*'}'*/ | PH7_TK_CSB/*]*/)) {
						/* Decrement nesting level */
						iNest--;
					}
					pDefend++;
				}
				if(pIn >= pDefend) {
					rc = PH7_GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Missing argument default value");
					return rc;
				}
				/* Process default value */
				rc = GenStateProcessArgValue(&(*pGen), &sArg, pIn, pDefend);
				if(rc != SXRET_OK) {
					return rc;
				}
				/* Point beyond the default value */
				pIn = pDefend;
			}
			if(pIn < pEnd && (pIn->nType & PH7_TK_COMMA) == 0) {
				rc = PH7_GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Unexpected token '%z'", &pIn->sData);
				return rc;
			}
			pIn++; /* Jump the trailing comma */
		}
		/* Append argument signature */
		if(sArg.nType > 0) {
			if(SyStringLength(&sArg.sClass) > 0) {
				/* Class name */
				SyBlobAppend(&sSig, SyStringData(&sArg.sClass), SyStringLength(&sArg.sClass));
			} else {
				int c;
				c = 'n'; /* cc warning */
				/* Type leading character */
				switch(sArg.nType) {
					case MEMOBJ_HASHMAP:
						/* Hashmap aka 'array' */
						c = 'h';
						break;
					case MEMOBJ_INT:
						/* Integer */
						c = 'i';
						break;
					case MEMOBJ_BOOL:
						/* Bool */
						c = 'b';
						break;
					case MEMOBJ_REAL:
						/* Float */
						c = 'f';
						break;
					case MEMOBJ_STRING:
						/* String */
						c = 's';
						break;
					default:
						break;
				}
				SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
			}
		} else {
			/* No type is associated with this parameter which mean
			 * that this function is not condidate for overloading.
			 */
			SyBlobRelease(&sSig);
		}
		/* Save in the argument set */
		SySetPut(&pFunc->aArgs, (const void *)&sArg);
	}
	if(SyBlobLength(&sSig) > 0) {
		/* Save function signature */
		SyStringInitFromBuf(&pFunc->sSignature, SyBlobData(&sSig), SyBlobLength(&sSig));
	}
	return SXRET_OK;
}
/*
 * Compile function [i.e: standard function, annonymous function or closure ] body.
 * Return SXRET_OK on success. Any other return value indicates failure
 * and this routine takes care of generating the appropriate error message.
 */
static sxi32 GenStateCompileFuncBody(
	ph7_gen_state *pGen,  /* Code generator state */
	ph7_vm_func *pFunc    /* Function state */
) {
	SySet *pInstrContainer; /* Instruction container */
	GenBlock *pBlock;
	sxi32 rc;
	/* Attach the new function */
	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_PROTECTED | GEN_BLOCK_FUNC, PH7_VmInstrLength(pGen->pVm), pFunc, &pBlock);
	if(rc != SXRET_OK) {
		PH7_GenCompileError(&(*pGen), E_ERROR, 1, "PH7 engine is running out-of-memory");
		/* Don't worry about freeing memory, everything will be released shortly */
		return SXERR_ABORT;
	}
	/* Swap bytecode containers */
	pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm);
	PH7_VmSetByteCodeContainer(pGen->pVm, &pFunc->aByteCode);
	/* Compile the body */
	PH7_CompileBlock(&(*pGen), 0);
	/* Fix exception jumps now the destination is resolved */
	GenStateFixJumps(pGen->pCurrent, PH7_OP_THROW, PH7_VmInstrLength(pGen->pVm));
	/* Emit the final return if not yet done */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_DONE, 0, 0, 0, 0);
	/* Restore the default container */
	PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
	/* Leave function block */
	GenStateLeaveBlock(&(*pGen), 0);
	if(rc == SXERR_ABORT) {
		/* Don't worry about freeing memory, everything will be released shortly */
		return SXERR_ABORT;
	}
	/* All done, function body compiled */
	return SXRET_OK;
}
/*
 * Compile a PHP function whether is a Standard or Annonymous function.
 * According to the PHP language reference manual.
 *  Function names follow the same rules as other labels in PHP. A valid function name
 *  starts with a letter or underscore, followed by any number of letters, numbers, or
 *  underscores. As a regular expression, it would be expressed thus:
 *     [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*.
 *  Functions need not be defined before they are referenced.
 *  All functions and classes in PHP have the global scope - they can be called outside
 *  a function even if they were defined inside and vice versa.
 *  It is possible to call recursive functions in PHP. However avoid recursive function/method
 *  calls with over 32-64 recursion levels.
 *
 * PH7 have introduced powerful extension including full type hinting, function overloading,
 * complex agrument values and more. Please refer to the official documentation for more information
 * on these extension.
 */
static sxi32 GenStateCompileFunc(
	ph7_gen_state *pGen, /* Code generator state */
	SyString *pName,     /* Function name. NULL otherwise */
	sxi32 iFlags,        /* Control flags */
	int bHandleClosure,  /* TRUE if we are dealing with a closure */
	ph7_vm_func **ppFunc /* OUT: function state */
) {
	ph7_vm_func *pFunc;
	SyToken *pEnd;
	sxu32 nLine;
	char *zName;
	sxi32 rc;
	/* Extract line number */
	nLine = pGen->pIn->nLine;
	/* Jump the left parenthesis '(' */
	pGen->pIn++;
	/* Delimit the function signature */
	PH7_DelimitNestedTokens(pGen->pIn, pGen->pEnd, PH7_TK_LPAREN /* '(' */, PH7_TK_RPAREN /* ')' */, &pEnd);
	if(pEnd >= pGen->pEnd) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Missing ')' after function '%z' signature", pName);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		pGen->pIn = pGen->pEnd;
		return SXRET_OK;
	}
	/* Create the function state */
	pFunc = (ph7_vm_func *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(ph7_vm_func));
	if(pFunc == 0) {
		goto OutOfMem;
	}
	/* function ID */
	zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
	if(zName == 0) {
		/* Don't worry about freeing memory, everything will be released shortly */
		goto OutOfMem;
	}
	/* Initialize the function state */
	PH7_VmInitFuncState(pGen->pVm, pFunc, zName, pName->nByte, iFlags, 0);
	if(pGen->pIn < pEnd) {
		/* Collect function arguments */
		rc = GenStateCollectFuncArgs(pFunc, &(*pGen), pEnd);
		if(rc == SXERR_ABORT) {
			/* Don't worry about freeing memory, everything will be released shortly */
			return SXERR_ABORT;
		}
	}
	/* Compile function body */
	pGen->pIn = &pEnd[1];
	if(bHandleClosure) {
		ph7_vm_func_closure_env sEnv;
		int got_this = 0; /* TRUE if $this have been seen */
		if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD)
				&& SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_TKWRD_USE) {
			sxu32 nLine = pGen->pIn->nLine;
			/* Closure,record environment variable */
			pGen->pIn++;
			if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0) {
				rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Closure: Unexpected token. Expecting a left parenthesis '('");
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
			}
			pGen->pIn++; /* Jump the left parenthesis or any other unexpected token */
			/* Compile until we hit the first closing parenthesis */
			while(pGen->pIn < pGen->pEnd) {
				int iFlags = 0;
				if(pGen->pIn->nType & PH7_TK_RPAREN) {
					pGen->pIn++; /* Jump the closing parenthesis */
					break;
				}
				nLine = pGen->pIn->nLine;
				if(pGen->pIn->nType & PH7_TK_AMPER) {
					/* Pass by reference,record that */
					PH7_GenCompileError(pGen, E_WARNING, nLine,
										"Closure: Pass by reference is disabled in the current release of the PH7 engine,PH7 is switching to pass by value"
									   );
					iFlags = VM_FUNC_ARG_BY_REF;
					pGen->pIn++;
				}
				if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd
						|| (pGen->pIn[1].nType & (PH7_TK_ID | PH7_TK_KEYWORD)) == 0) {
					rc = PH7_GenCompileError(pGen, E_ERROR, nLine,
											 "Closure: Unexpected token. Expecting a variable name");
					if(rc == SXERR_ABORT) {
						return SXERR_ABORT;
					}
					/* Find the closing parenthesis */
					while((pGen->pIn < pGen->pEnd) && (pGen->pIn->nType & PH7_TK_RPAREN) == 0) {
						pGen->pIn++;
					}
					if(pGen->pIn < pGen->pEnd) {
						pGen->pIn++;
					}
					break;
					/* TICKET 1433-95: No need for the else block below.*/
				} else {
					SyString *pName;
					char *zDup;
					/* Duplicate variable name */
					pName = &pGen->pIn[1].sData;
					zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
					if(zDup) {
						/* Zero the structure */
						SyZero(&sEnv, sizeof(ph7_vm_func_closure_env));
						sEnv.iFlags = iFlags;
						PH7_MemObjInit(pGen->pVm, &sEnv.sValue);
						SyStringInitFromBuf(&sEnv.sName, zDup, pName->nByte);
						if(!got_this && pName->nByte == sizeof("this") - 1 &&
								SyMemcmp((const void *)zDup, (const void *)"this", sizeof("this") - 1) == 0) {
							got_this = 1;
						}
						/* Save imported variable */
						SySetPut(&pFunc->aClosureEnv, (const void *)&sEnv);
					} else {
						PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory");
						return SXERR_ABORT;
					}
				}
				pGen->pIn += 2; /* $ + variable name or any other unexpected token */
				while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA /*','*/)) {
					/* Ignore trailing commas */
					pGen->pIn++;
				}
			}
			if(!got_this) {
				/* Make the $this variable [Current processed Object (class instance)]
				 * available to the closure environment.
				 */
				SyZero(&sEnv, sizeof(ph7_vm_func_closure_env));
				sEnv.iFlags = VM_FUNC_ARG_IGNORE; /* Do not install if NULL */
				PH7_MemObjInit(pGen->pVm, &sEnv.sValue);
				SyStringInitFromBuf(&sEnv.sName, "this", sizeof("this") - 1);
				SySetPut(&pFunc->aClosureEnv, (const void *)&sEnv);
			}
			if(SySetUsed(&pFunc->aClosureEnv) > 0) {
				/* Mark as closure */
				pFunc->iFlags |= VM_FUNC_CLOSURE;
			}
		}
	}
	/* Compile the body */
	rc = GenStateCompileFuncBody(&(*pGen), pFunc);
	if(rc == SXERR_ABORT) {
		return SXERR_ABORT;
	}
	if(ppFunc) {
		*ppFunc = pFunc;
	}
	rc = SXRET_OK;
	if((pFunc->iFlags & VM_FUNC_CLOSURE) == 0) {
		/* Finally register the function */
		rc = PH7_VmInstallUserFunction(pGen->pVm, pFunc, 0);
	}
	if(rc == SXRET_OK) {
		return SXRET_OK;
	}
	/* Fall through if something goes wrong */
OutOfMem:
	/* If the supplied memory subsystem is so sick that we are unable to allocate
	 * a tiny chunk of memory, there is no much we can do here.
	 */
	PH7_GenCompileError(&(*pGen), E_ERROR, 1, "Fatal, PH7 engine is running out-of-memory");
	return SXERR_ABORT;
}
/*
 * Compile a standard PHP function.
 *  Refer to the block-comment above for more information.
 */
static sxi32 PH7_CompileFunction(ph7_gen_state *pGen) {
	SyString *pName;
	sxi32 iFlags;
	sxu32 nLine;
	sxi32 rc;
	nLine = pGen->pIn->nLine;
	pGen->pIn++; /* Jump the 'function' keyword */
	iFlags = 0;
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_AMPER)) {
		/* Return by reference,remember that */
		iFlags |= VM_FUNC_REF_RETURN;
		/* Jump the '&' token */
		pGen->pIn++;
	}
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_ID | PH7_TK_KEYWORD)) == 0) {
		/* Invalid function name */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Invalid function name");
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		/* Sychronize with the next semi-colon or braces*/
		while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI | PH7_TK_OCB)) == 0) {
			pGen->pIn++;
		}
		return SXRET_OK;
	}
	pName = &pGen->pIn->sData;
	nLine = pGen->pIn->nLine;
	/* Jump the function name */
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after function name '%z'", pName);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		/* Sychronize with the next semi-colon or '{' */
		while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI | PH7_TK_OCB)) == 0) {
			pGen->pIn++;
		}
		return SXRET_OK;
	}
	/* Compile function body */
	rc = GenStateCompileFunc(&(*pGen), pName, iFlags, FALSE, 0);
	return rc;
}
/*
 * Extract the visibility level associated with a given keyword.
 * According to the PHP language reference manual
 *  Visibility:
 *  The visibility of a property or method can be defined by prefixing
 *  the declaration with the keywords public, protected or private.
 *  Class members declared public can be accessed everywhere.
 *  Members declared protected can be accessed only within the class
 *  itself and by inherited and parent classes. Members declared as private
 *  may only be accessed by the class that defines the member.
 */
static sxi32 GetProtectionLevel(sxi32 nKeyword) {
	if(nKeyword == PH7_TKWRD_PRIVATE) {
		return PH7_CLASS_PROT_PRIVATE;
	} else if(nKeyword == PH7_TKWRD_PROTECTED) {
		return PH7_CLASS_PROT_PROTECTED;
	}
	/* Assume public by default */
	return PH7_CLASS_PROT_PUBLIC;
}
/*
 * Compile a class constant.
 * According to the PHP language reference manual
 *  Class Constants
 *   It is possible to define constant values on a per-class basis remaining
 *   the same and unchangeable. Constants differ from normal variables in that
 *   you don't use the $ symbol to declare or use them.
 *   The value must be a constant expression, not (for example) a variable,
 *   a property, a result of a mathematical operation, or a function call.
 *   It's also possible for interfaces to have constants.
 * Symisc eXtension.
 *  PH7 allow any complex expression to be associated with the constant while
 *  the zend engine would allow only simple scalar value.
 *  Example:
 *   class Test{
 *        const MyConst = "Hello"."world: ".rand_str(3); //concatenation operation + Function call
 *   };
 *   var_dump(TEST::MyConst);
 *   Refer to the official documentation for more information on the powerful extension
 *   introduced by the PH7 engine to the OO subsystem.
 */
static sxi32 GenStateCompileClassConstant(ph7_gen_state *pGen, sxi32 iProtection, sxi32 iFlags, ph7_class *pClass) {
	sxu32 nLine = pGen->pIn->nLine;
	SySet *pInstrContainer;
	ph7_class_attr *pCons;
	SyString *pName;
	sxi32 rc;
	/* Extract visibility level */
	iProtection = GetProtectionLevel(iProtection);
	pGen->pIn++; /* Jump the 'const' keyword */
loop:
	/* Mark as constant */
	iFlags |= PH7_CLASS_ATTR_CONSTANT;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) {
		/* Invalid constant name */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Invalid constant name");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Peek constant name */
	pName = &pGen->pIn->sData;
	/* Make sure the constant name isn't reserved */
	if(GenStateIsReservedConstant(pName)) {
		/* Reserved constant name */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Cannot redeclare a reserved constant '%z'", pName);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Advance the stream cursor */
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_EQUAL /* '=' */) == 0) {
		/* Invalid declaration */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected '=' after class constant %z'", pName);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	pGen->pIn++; /* Jump the equal sign */
	/* Allocate a new class attribute */
	pCons = PH7_NewClassAttr(pGen->pVm, pName, nLine, iProtection, iFlags);
	if(pCons == 0) {
		PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory");
		return SXERR_ABORT;
	}
	/* Swap bytecode container */
	pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm);
	PH7_VmSetByteCodeContainer(pGen->pVm, &pCons->aByteCode);
	/* Compile constant value.
	 */
	rc = PH7_CompileExpr(&(*pGen), EXPR_FLAG_COMMA_STATEMENT, 0);
	if(rc == SXERR_EMPTY) {
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Empty constant '%z' value", pName);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	}
	/* Emit the done instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_DONE, 1, 0, 0, 0);
	PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
	if(rc == SXERR_ABORT) {
		/* Don't worry about freeing memory, everything will be released shortly */
		return SXERR_ABORT;
	}
	/* All done,install the constant */
	rc = PH7_ClassInstallAttr(pClass, pCons);
	if(rc != SXRET_OK) {
		PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory");
		return SXERR_ABORT;
	}
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA /*','*/)) {
		/* Multiple constants declarations [i.e: const min=-1,max = 10] */
		pGen->pIn++; /* Jump the comma */
		if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) {
			SyToken *pTok = pGen->pIn;
			if(pTok >= pGen->pEnd) {
				pTok--;
			}
			rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
									 "Unexpected token '%z',expecting constant declaration inside class '%z'",
									 &pTok->sData, &pClass->sName);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
		} else {
			if(pGen->pIn->nType & PH7_TK_ID) {
				goto loop;
			}
		}
	}
	return SXRET_OK;
Synchronize:
	/* Synchronize with the first semi-colon */
	while(pGen->pIn < pGen->pEnd && ((pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0)) {
		pGen->pIn++;
	}
	return SXERR_CORRUPT;
}
/*
 * complie a class attribute or Properties in the PHP jargon.
 * According to the PHP language reference manual
 *  Properties
 *  Class member variables are called "properties". You may also see them referred
 *  to using other terms such as "attributes" or "fields", but for the purposes
 *  of this reference we will use "properties". They are defined by using one
 *  of the keywords public, protected, or private, followed by a normal variable
 *  declaration. This declaration may include an initialization, but this initialization
 *  must be a constant value--that is, it must be able to be evaluated at compile time
 *  and must not depend on run-time information in order to be evaluated.
 * Symisc eXtension.
 *  PH7 allow any complex expression to be associated with the attribute while
 *  the zend engine would allow only simple scalar value.
 *  Example:
 *   class Test{
 *        public static $myVar = "Hello"."world: ".rand_str(3); //concatenation operation + Function call
 *   };
 *   var_dump(TEST::myVar);
 *   Refer to the official documentation for more information on the powerful extension
 *   introduced by the PH7 engine to the OO subsystem.
 */
static sxi32 GenStateCompileClassAttr(ph7_gen_state *pGen, sxi32 iProtection, sxi32 iFlags, ph7_class *pClass) {
	sxu32 nLine = pGen->pIn->nLine;
	ph7_class_attr *pAttr;
	SyString *pName;
	sxi32 rc;
	/* Extract visibility level */
	iProtection = GetProtectionLevel(iProtection);
loop:
	pGen->pIn++; /* Jump the dollar sign */
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_ID)) == 0) {
		/* Invalid attribute name */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Invalid attribute name");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Peek attribute name */
	pName = &pGen->pIn->sData;
	/* Advance the stream cursor */
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_EQUAL/*'='*/ | PH7_TK_SEMI/*';'*/ | PH7_TK_COMMA/*','*/)) == 0) {
		/* Invalid declaration */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected '=' or ';' after attribute name '%z'", pName);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Allocate a new class attribute */
	pAttr = PH7_NewClassAttr(pGen->pVm, pName, nLine, iProtection, iFlags);
	if(pAttr == 0) {
		PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 engine is running out of memory");
		return SXERR_ABORT;
	}
	if(pGen->pIn->nType & PH7_TK_EQUAL /*'='*/) {
		SySet *pInstrContainer;
		pGen->pIn++; /*Jump the equal sign */
		/* Swap bytecode container */
		pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm);
		PH7_VmSetByteCodeContainer(pGen->pVm, &pAttr->aByteCode);
		/* Compile attribute value.
		 */
		rc = PH7_CompileExpr(&(*pGen), EXPR_FLAG_COMMA_STATEMENT, 0);
		if(rc == SXERR_EMPTY) {
			rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Attribute '%z': Missing default value", pName);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
		}
		/* Emit the done instruction */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_DONE, 1, 0, 0, 0);
		PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
	}
	/* All done,install the attribute */
	rc = PH7_ClassInstallAttr(pClass, pAttr);
	if(rc != SXRET_OK) {
		PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory");
		return SXERR_ABORT;
	}
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA /*','*/)) {
		/* Multiple attribute declarations [i.e: public $var1,$var2=5<<1,$var3] */
		pGen->pIn++; /* Jump the comma */
		if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR/*'$'*/) == 0) {
			SyToken *pTok = pGen->pIn;
			if(pTok >= pGen->pEnd) {
				pTok--;
			}
			rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
									 "Unexpected token '%z',expecting attribute declaration inside class '%z'",
									 &pTok->sData, &pClass->sName);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
		} else {
			if(pGen->pIn->nType & PH7_TK_DOLLAR) {
				goto loop;
			}
		}
	}
	return SXRET_OK;
Synchronize:
	/* Synchronize with the first semi-colon */
	while(pGen->pIn < pGen->pEnd && ((pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0)) {
		pGen->pIn++;
	}
	return SXERR_CORRUPT;
}
/*
 * Compile a class method.
 *
 * Refer to the official documentation for more information
 * on the powerful extension introduced by the PH7 engine
 * to the OO subsystem such as full type hinting,method
 * overloading and many more.
 */
static sxi32 GenStateCompileClassMethod(
	ph7_gen_state *pGen, /* Code generator state */
	sxi32 iProtection,   /* Visibility level */
	sxi32 iFlags,        /* Configuration flags */
	int doBody,          /* TRUE to process method body */
	ph7_class *pClass    /* Class this method belongs */
) {
	sxu32 nLine = pGen->pIn->nLine;
	ph7_class_method *pMeth;
	sxi32 iFuncFlags;
	SyString *pName;
	SyToken *pEnd;
	sxi32 rc;
	/* Extract visibility level */
	iProtection = GetProtectionLevel(iProtection);
	pGen->pIn++; /* Jump the 'function' keyword */
	iFuncFlags = 0;
	if(pGen->pIn >= pGen->pEnd) {
		/* Invalid method name */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Invalid method name");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_AMPER)) {
		/* Return by reference,remember that */
		iFuncFlags |= VM_FUNC_REF_RETURN;
		/* Jump the '&' token */
		pGen->pIn++;
	}
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_ID)) == 0) {
		/* Invalid method name */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Invalid method name");
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Peek method name */
	pName = &pGen->pIn->sData;
	nLine = pGen->pIn->nLine;
	/* Jump the method name */
	pGen->pIn++;
	if(iFlags & PH7_CLASS_ATTR_ABSTRACT) {
		/* Abstract method */
		if(iProtection == PH7_CLASS_PROT_PRIVATE) {
			rc = PH7_GenCompileError(pGen, E_ERROR, nLine,
									 "Access type for abstract method '%z::%z' cannot be 'private'",
									 &pClass->sName, pName);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
		}
		/* Assemble method signature only */
		doBody = FALSE;
	}
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after method name '%z'", pName);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Allocate a new class_method instance */
	pMeth = PH7_NewClassMethod(pGen->pVm, pClass, pName, nLine, iProtection, iFlags, iFuncFlags);
	if(pMeth == 0) {
		PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, PH7 is running out of memory");
		return SXERR_ABORT;
	}
	/* Jump the left parenthesis '(' */
	pGen->pIn++;
	pEnd = 0; /* cc warning */
	/* Delimit the method signature */
	PH7_DelimitNestedTokens(pGen->pIn, pGen->pEnd, PH7_TK_LPAREN /* '(' */, PH7_TK_RPAREN /* ')' */, &pEnd);
	if(pEnd >= pGen->pEnd) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Missing ')' after method '%z' declaration", pName);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	if(pGen->pIn < pEnd) {
		/* Collect method arguments */
		rc = GenStateCollectFuncArgs(&pMeth->sFunc, &(*pGen), pEnd);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	}
	/* Point beyond method signature */
	pGen->pIn = &pEnd[1];
	if(doBody) {
		/* Compile method body */
		rc = GenStateCompileFuncBody(&(*pGen), &pMeth->sFunc);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	} else {
		/* Only method signature is allowed */
		if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI /* ';'*/) == 0) {
			rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
									 "Expected ';' after method signature '%z'", pName);
			if(rc == SXERR_ABORT) {
				/* Error count limit reached,abort immediately */
				return SXERR_ABORT;
			}
			return SXERR_CORRUPT;
		}
	}
	/* All done,install the method */
	rc = PH7_ClassInstallMethod(pClass, pMeth);
	if(rc != SXRET_OK) {
		PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory");
		return SXERR_ABORT;
	}
	return SXRET_OK;
Synchronize:
	/* Synchronize with the first semi-colon */
	while(pGen->pIn < pGen->pEnd && ((pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0)) {
		pGen->pIn++;
	}
	return SXERR_CORRUPT;
}
/*
 * Compile an object interface.
 *  According to the PHP language reference manual
 *   Object Interfaces:
 *   Object interfaces allow you to create code which specifies which methods
 *   a class must implement, without having to define how these methods are handled.
 *   Interfaces are defined using the interface keyword, in the same way as a standard
 *   class, but without any of the methods having their contents defined.
 *   All methods declared in an interface must be public, this is the nature of an interface.
 */
static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) {
	sxu32 nLine = pGen->pIn->nLine;
	ph7_class *pClass, *pBase;
	SyToken *pEnd, *pTmp;
	SyString *pName;
	sxi32 nKwrd;
	sxi32 rc;
	/* Jump the 'interface' keyword */
	pGen->pIn++;
	/* Extract interface name */
	pName = &pGen->pIn->sData;
	/* Advance the stream cursor */
	pGen->pIn++;
	/* Obtain a raw class */
	pClass = PH7_NewRawClass(pGen->pVm, pName, nLine);
	if(pClass == 0) {
		PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory");
		return SXERR_ABORT;
	}
	/* Mark as an interface */
	pClass->iFlags = PH7_CLASS_INTERFACE;
	/* Assume no base class is given */
	pBase = 0;
	if(pGen->pIn < pGen->pEnd  && (pGen->pIn->nType & PH7_TK_KEYWORD)) {
		nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
		if(nKwrd == PH7_TKWRD_EXTENDS /* interface b extends a */) {
			SyString *pBaseName;
			/* Extract base interface */
			pGen->pIn++;
			if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) {
				/* Syntax error */
				rc = PH7_GenCompileError(pGen, E_ERROR, nLine,
										 "Expected 'interface_name' after 'extends' keyword inside interface '%z'",
										 pName);
				SyMemBackendPoolFree(&pGen->pVm->sAllocator, pClass);
				if(rc == SXERR_ABORT) {
					/* Error count limit reached,abort immediately */
					return SXERR_ABORT;
				}
				return SXRET_OK;
			}
			pBaseName = &pGen->pIn->sData;
			pBase = PH7_VmExtractClass(pGen->pVm, pBaseName->zString, pBaseName->nByte, FALSE, 0);
			/* Only interfaces is allowed */
			while(pBase && (pBase->iFlags & PH7_CLASS_INTERFACE) == 0) {
				pBase = pBase->pNextName;
			}
			if(pBase == 0) {
				/* Inexistant interface */
				rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Inexistant base interface '%z'", pBaseName);
				if(rc == SXERR_ABORT) {
					/* Error count limit reached,abort immediately */
					return SXERR_ABORT;
				}
			}
			/* Advance the stream cursor */
			pGen->pIn++;
		}
	}
	if(pGen->pIn >= pGen->pEnd  || (pGen->pIn->nType & PH7_TK_OCB /*'{'*/) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected '{' after interface '%z' definition", pName);
		SyMemBackendPoolFree(&pGen->pVm->sAllocator, pClass);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		return SXRET_OK;
	}
	pGen->pIn++; /* Jump the leading curly brace */
	pEnd = 0; /* cc warning */
	/* Delimit the interface body */
	PH7_DelimitNestedTokens(pGen->pIn, pGen->pEnd, PH7_TK_OCB/*'{'*/, PH7_TK_CCB/*'}'*/, &pEnd);
	if(pEnd >= pGen->pEnd) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Missing '}' after interface '%z' definition", pName);
		SyMemBackendPoolFree(&pGen->pVm->sAllocator, pClass);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		return SXRET_OK;
	}
	/* Swap token stream */
	pTmp = pGen->pEnd;
	pGen->pEnd = pEnd;
	/* Start the parse process
	 * Note (According to the PHP reference manual):
	 *  Only constants and function signatures(without body) are allowed.
	 *  Only 'public' visibility is allowed.
	 */
	for(;;) {
		/* Jump leading/trailing semi-colons */
		while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/)) {
			pGen->pIn++;
		}
		if(pGen->pIn >= pGen->pEnd) {
			/* End of interface body */
			break;
		}
		if((pGen->pIn->nType & PH7_TK_KEYWORD) == 0) {
			rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
									 "Unexpected token '%z'.Expecting method signature or constant declaration inside interface '%z'",
									 &pGen->pIn->sData, pName);
			if(rc == SXERR_ABORT) {
				/* Error count limit reached,abort immediately */
				return SXERR_ABORT;
			}
			goto done;
		}
		/* Extract the current keyword */
		nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
		if(nKwrd == PH7_TKWRD_PRIVATE || nKwrd == PH7_TKWRD_PROTECTED) {
			/* Emit a warning and switch to public visibility */
			PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "interface: Access type must be public");
			nKwrd = PH7_TKWRD_PUBLIC;
		}
		if(nKwrd != PH7_TKWRD_PUBLIC && nKwrd != PH7_TKWRD_FUNCTION && nKwrd != PH7_TKWRD_CONST && nKwrd != PH7_TKWRD_STATIC) {
			rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
									 "Expecting method signature or constant declaration inside interface '%z'", pName);
			if(rc == SXERR_ABORT) {
				/* Error count limit reached,abort immediately */
				return SXERR_ABORT;
			}
			goto done;
		}
		if(nKwrd == PH7_TKWRD_PUBLIC) {
			/* Advance the stream cursor */
			pGen->pIn++;
			if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0) {
				rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
										 "Expecting method signature inside interface '%z'", pName);
				if(rc == SXERR_ABORT) {
					/* Error count limit reached,abort immediately */
					return SXERR_ABORT;
				}
				goto done;
			}
			nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
			if(nKwrd != PH7_TKWRD_FUNCTION && nKwrd != PH7_TKWRD_CONST && nKwrd != PH7_TKWRD_STATIC) {
				rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
										 "Expecting method signature or constant declaration inside interface '%z'", pName);
				if(rc == SXERR_ABORT) {
					/* Error count limit reached,abort immediately */
					return SXERR_ABORT;
				}
				goto done;
			}
		}
		if(nKwrd == PH7_TKWRD_CONST) {
			/* Parse constant */
			rc = GenStateCompileClassConstant(&(*pGen), 0, 0, pClass);
			if(rc != SXRET_OK) {
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
				goto done;
			}
		} else {
			sxi32 iFlags = 0;
			if(nKwrd == PH7_TKWRD_STATIC) {
				/* Static method,record that */
				iFlags |= PH7_CLASS_ATTR_STATIC;
				/* Advance the stream cursor */
				pGen->pIn++;
				if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0
						|| SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_FUNCTION) {
					rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
											 "Expecting method signature inside interface '%z'", pName);
					if(rc == SXERR_ABORT) {
						/* Error count limit reached,abort immediately */
						return SXERR_ABORT;
					}
					goto done;
				}
			}
			/* Process method signature */
			rc = GenStateCompileClassMethod(&(*pGen), 0, FALSE/* Only method signature*/, iFlags, pClass);
			if(rc != SXRET_OK) {
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
				goto done;
			}
		}
	}
	/* Install the interface */
	rc = PH7_VmInstallClass(pGen->pVm, pClass);
	if(rc == SXRET_OK && pBase) {
		/* Inherit from the base interface */
		rc = PH7_ClassInterfaceInherit(pClass, pBase);
	}
	if(rc != SXRET_OK) {
		PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory");
		return SXERR_ABORT;
	}
done:
	/* Point beyond the interface body */
	pGen->pIn  = &pEnd[1];
	pGen->pEnd = pTmp;
	return PH7_OK;
}
/*
 * Compile a user-defined class.
 * According to the PHP language reference manual
 *  class
 *  Basic class definitions begin with the keyword class, followed by a class
 *  name, followed by a pair of curly braces which enclose the definitions
 *  of the properties and methods belonging to the class.
 *  The class name can be any valid label which is a not a PHP reserved word.
 *  A valid class name starts with a letter or underscore, followed by any number
 *  of letters, numbers, or underscores. As a regular expression, it would be expressed
 *  thus: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*.
 *  A class may contain its own constants, variables (called "properties"), and functions
 *  (called "methods").
 */
static sxi32 GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) {
	sxu32 nLine = pGen->pIn->nLine;
	ph7_class *pClass, *pBase;
	SyToken *pEnd, *pTmp;
	sxi32 iProtection;
	SySet aInterfaces;
	sxi32 iAttrflags;
	SyString *pName;
	sxi32 nKwrd;
	sxi32 rc;
	/* Jump the 'class' keyword */
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Invalid class name");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		/* Synchronize with the first semi-colon or curly braces */
		while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_OCB/*'{'*/ | PH7_TK_SEMI/*';'*/)) == 0) {
			pGen->pIn++;
		}
		return SXRET_OK;
	}
	/* Extract class name */
	pName = &pGen->pIn->sData;
	/* Advance the stream cursor */
	pGen->pIn++;
	/* Obtain a raw class */
	pClass = PH7_NewRawClass(pGen->pVm, pName, nLine);
	if(pClass == 0) {
		PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory");
		return SXERR_ABORT;
	}
	/* implemented interfaces container */
	SySetInit(&aInterfaces, &pGen->pVm->sAllocator, sizeof(ph7_class *));
	/* Assume a standalone class */
	pBase = 0;
	if(pGen->pIn < pGen->pEnd  && (pGen->pIn->nType & PH7_TK_KEYWORD)) {
		SyString *pBaseName;
		nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
		if(nKwrd == PH7_TKWRD_EXTENDS /* class b extends a */) {
			pGen->pIn++; /* Advance the stream cursor */
			if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) {
				/* Syntax error */
				rc = PH7_GenCompileError(pGen, E_ERROR, nLine,
										 "Expected 'class_name' after 'extends' keyword inside class '%z'",
										 pName);
				SyMemBackendPoolFree(&pGen->pVm->sAllocator, pClass);
				if(rc == SXERR_ABORT) {
					/* Error count limit reached,abort immediately */
					return SXERR_ABORT;
				}
				return SXRET_OK;
			}
			/* Extract base class name */
			pBaseName = &pGen->pIn->sData;
			/* Perform the query */
			pBase = PH7_VmExtractClass(pGen->pVm, pBaseName->zString, pBaseName->nByte, FALSE, 0);
			/* Interfaces are not allowed */
			while(pBase && (pBase->iFlags & PH7_CLASS_INTERFACE)) {
				pBase = pBase->pNextName;
			}
			if(pBase == 0) {
				/* Inexistant base class */
				rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Inexistant base class '%z'", pBaseName);
				if(rc == SXERR_ABORT) {
					/* Error count limit reached,abort immediately */
					return SXERR_ABORT;
				}
			} else {
				if(pBase->iFlags & PH7_CLASS_FINAL) {
					rc = PH7_GenCompileError(pGen, E_ERROR, nLine,
											 "Class '%z' may not inherit from final class '%z'", pName, &pBase->sName);
					if(rc == SXERR_ABORT) {
						/* Error count limit reached,abort immediately */
						return SXERR_ABORT;
					}
				}
			}
			/* Advance the stream cursor */
			pGen->pIn++;
		}
		if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_TKWRD_IMPLEMENTS) {
			ph7_class *pInterface;
			SyString *pIntName;
			/* Interface implementation */
			pGen->pIn++; /* Advance the stream cursor */
			for(;;) {
				if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_ID) == 0) {
					/* Syntax error */
					rc = PH7_GenCompileError(pGen, E_ERROR, nLine,
											 "Expected 'interface_name' after 'implements' keyword inside class '%z' declaration",
											 pName);
					if(rc == SXERR_ABORT) {
						/* Error count limit reached,abort immediately */
						return SXERR_ABORT;
					}
					break;
				}
				/* Extract interface name */
				pIntName = &pGen->pIn->sData;
				/* Make sure the interface is already defined */
				pInterface = PH7_VmExtractClass(pGen->pVm, pIntName->zString, pIntName->nByte, FALSE, 0);
				/* Only interfaces are allowed */
				while(pInterface && (pInterface->iFlags & PH7_CLASS_INTERFACE) == 0) {
					pInterface = pInterface->pNextName;
				}
				if(pInterface == 0) {
					/* Inexistant interface */
					rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Inexistant base interface '%z'", pIntName);
					if(rc == SXERR_ABORT) {
						/* Error count limit reached,abort immediately */
						return SXERR_ABORT;
					}
				} else {
					/* Register interface */
					SySetPut(&aInterfaces, (const void *)&pInterface);
				}
				/* Advance the stream cursor */
				pGen->pIn++;
				if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_COMMA) == 0) {
					break;
				}
				pGen->pIn++;/* Jump the comma */
			}
		}
	}
	if(pGen->pIn >= pGen->pEnd  || (pGen->pIn->nType & PH7_TK_OCB /*'{'*/) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected '{' after class '%z' declaration", pName);
		SyMemBackendPoolFree(&pGen->pVm->sAllocator, pClass);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		return SXRET_OK;
	}
	pGen->pIn++; /* Jump the leading curly brace */
	pEnd = 0; /* cc warning */
	/* Delimit the class body */
	PH7_DelimitNestedTokens(pGen->pIn, pGen->pEnd, PH7_TK_OCB/*'{'*/, PH7_TK_CCB/*'}'*/, &pEnd);
	if(pEnd >= pGen->pEnd) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Missing closing braces'}' after class '%z' definition", pName);
		SyMemBackendPoolFree(&pGen->pVm->sAllocator, pClass);
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		return SXRET_OK;
	}
	/* Swap token stream */
	pTmp = pGen->pEnd;
	pGen->pEnd = pEnd;
	/* Set the inherited flags */
	pClass->iFlags = iFlags;
	/* Start the parse process */
	for(;;) {
		/* Jump leading/trailing semi-colons */
		while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/)) {
			pGen->pIn++;
		}
		if(pGen->pIn >= pGen->pEnd) {
			/* End of class body */
			break;
		}
		if((pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_DOLLAR)) == 0) {
			rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
									 "Unexpected token '%z'. Expecting attribute declaration inside class '%z'",
									 &pGen->pIn->sData, pName);
			if(rc == SXERR_ABORT) {
				/* Error count limit reached,abort immediately */
				return SXERR_ABORT;
			}
			goto done;
		}
		/* Assume public visibility */
		iProtection = PH7_TKWRD_PUBLIC;
		iAttrflags = 0;
		if(pGen->pIn->nType & PH7_TK_KEYWORD) {
			/* Extract the current keyword */
			nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
			if(nKwrd == PH7_TKWRD_PUBLIC || nKwrd == PH7_TKWRD_PRIVATE || nKwrd == PH7_TKWRD_PROTECTED) {
				iProtection = nKwrd;
				pGen->pIn++; /* Jump the visibility token */
				if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_DOLLAR)) == 0) {
					rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
											 "Unexpected token '%z'. Expecting attribute declaration inside class '%z'",
											 &pGen->pIn->sData, pName);
					if(rc == SXERR_ABORT) {
						/* Error count limit reached,abort immediately */
						return SXERR_ABORT;
					}
					goto done;
				}
				if(pGen->pIn->nType & PH7_TK_DOLLAR) {
					/* Attribute declaration */
					rc = GenStateCompileClassAttr(&(*pGen), iProtection, iAttrflags, pClass);
					if(rc != SXRET_OK) {
						if(rc == SXERR_ABORT) {
							return SXERR_ABORT;
						}
						goto done;
					}
					continue;
				}
				/* Extract the keyword */
				nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
			}
			if(nKwrd == PH7_TKWRD_CONST) {
				/* Process constant declaration */
				rc = GenStateCompileClassConstant(&(*pGen), iProtection, iAttrflags, pClass);
				if(rc != SXRET_OK) {
					if(rc == SXERR_ABORT) {
						return SXERR_ABORT;
					}
					goto done;
				}
			} else {
				if(nKwrd == PH7_TKWRD_STATIC) {
					/* Static method or attribute,record that */
					iAttrflags |= PH7_CLASS_ATTR_STATIC;
					pGen->pIn++; /* Jump the static keyword */
					if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD)) {
						/* Extract the keyword */
						nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
						if(nKwrd == PH7_TKWRD_PUBLIC || nKwrd == PH7_TKWRD_PRIVATE || nKwrd == PH7_TKWRD_PROTECTED) {
							iProtection = nKwrd;
							pGen->pIn++; /* Jump the visibility token */
						}
					}
					if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_DOLLAR)) == 0) {
						rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
												 "Unexpected token '%z',Expecting method,attribute or constant declaration inside class '%z'",
												 &pGen->pIn->sData, pName);
						if(rc == SXERR_ABORT) {
							/* Error count limit reached,abort immediately */
							return SXERR_ABORT;
						}
						goto done;
					}
					if(pGen->pIn->nType & PH7_TK_DOLLAR) {
						/* Attribute declaration */
						rc = GenStateCompileClassAttr(&(*pGen), iProtection, iAttrflags, pClass);
						if(rc != SXRET_OK) {
							if(rc == SXERR_ABORT) {
								return SXERR_ABORT;
							}
							goto done;
						}
						continue;
					}
					/* Extract the keyword */
					nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
				} else if(nKwrd == PH7_TKWRD_ABSTRACT) {
					/* Abstract method,record that */
					iAttrflags |= PH7_CLASS_ATTR_ABSTRACT;
					/* Mark the whole class as abstract */
					pClass->iFlags |= PH7_CLASS_ABSTRACT;
					/* Advance the stream cursor */
					pGen->pIn++;
					if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD)) {
						nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
						if(nKwrd == PH7_TKWRD_PUBLIC || nKwrd == PH7_TKWRD_PRIVATE || nKwrd == PH7_TKWRD_PROTECTED) {
							iProtection = nKwrd;
							pGen->pIn++; /* Jump the visibility token */
						}
					}
					if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) &&
							SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_TKWRD_STATIC) {
						/* Static method */
						iAttrflags |= PH7_CLASS_ATTR_STATIC;
						pGen->pIn++; /* Jump the static keyword */
					}
					if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 ||
							SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_FUNCTION) {
						rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
												 "Unexpected token '%z',Expecting method declaration after 'abstract' keyword inside class '%z'",
												 &pGen->pIn->sData, pName);
						if(rc == SXERR_ABORT) {
							/* Error count limit reached,abort immediately */
							return SXERR_ABORT;
						}
						goto done;
					}
					nKwrd = PH7_TKWRD_FUNCTION;
				} else if(nKwrd == PH7_TKWRD_FINAL) {
					/* final method ,record that */
					iAttrflags |= PH7_CLASS_ATTR_FINAL;
					pGen->pIn++; /* Jump the final keyword */
					if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD)) {
						/* Extract the keyword */
						nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
						if(nKwrd == PH7_TKWRD_PUBLIC || nKwrd == PH7_TKWRD_PRIVATE || nKwrd == PH7_TKWRD_PROTECTED) {
							iProtection = nKwrd;
							pGen->pIn++; /* Jump the visibility token */
						}
					}
					if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) &&
							SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_TKWRD_STATIC) {
						/* Static method */
						iAttrflags |= PH7_CLASS_ATTR_STATIC;
						pGen->pIn++; /* Jump the static keyword */
					}
					if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 ||
							SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_FUNCTION) {
						rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
												 "Unexpected token '%z',Expecting method declaration after 'final' keyword inside class '%z'",
												 &pGen->pIn->sData, pName);
						if(rc == SXERR_ABORT) {
							/* Error count limit reached,abort immediately */
							return SXERR_ABORT;
						}
						goto done;
					}
					nKwrd = PH7_TKWRD_FUNCTION;
				}
				if(nKwrd != PH7_TKWRD_FUNCTION && nKwrd != PH7_TKWRD_VAR) {
					rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
											 "Unexpected token '%z',Expecting method declaration inside class '%z'",
											 &pGen->pIn->sData, pName);
					if(rc == SXERR_ABORT) {
						/* Error count limit reached,abort immediately */
						return SXERR_ABORT;
					}
					goto done;
				}
				if(nKwrd == PH7_TKWRD_VAR) {
					pGen->pIn++; /* Jump the 'var' keyword */
					if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR/*'$'*/) == 0) {
						rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
												 "Expecting attribute declaration after 'var' keyword");
						if(rc == SXERR_ABORT) {
							/* Error count limit reached,abort immediately */
							return SXERR_ABORT;
						}
						goto done;
					}
					/* Attribute declaration */
					rc = GenStateCompileClassAttr(&(*pGen), iProtection, iAttrflags, pClass);
				} else {
					/* Process method declaration */
					rc = GenStateCompileClassMethod(&(*pGen), iProtection, iAttrflags, TRUE, pClass);
				}
				if(rc != SXRET_OK) {
					if(rc == SXERR_ABORT) {
						return SXERR_ABORT;
					}
					goto done;
				}
			}
		} else {
			/* Attribute declaration */
			rc = GenStateCompileClassAttr(&(*pGen), iProtection, iAttrflags, pClass);
			if(rc != SXRET_OK) {
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
				goto done;
			}
		}
	}
	/* Install the class */
	rc = PH7_VmInstallClass(pGen->pVm, pClass);
	if(rc == SXRET_OK) {
		ph7_class **apInterface;
		sxu32 n;
		if(pBase) {
			/* Inherit from base class and mark as a subclass */
			rc = PH7_ClassInherit(&(*pGen), pClass, pBase);
		}
		apInterface = (ph7_class **)SySetBasePtr(&aInterfaces);
		for(n = 0 ; n < SySetUsed(&aInterfaces) ; n++) {
			/* Implements one or more interface */
			rc = PH7_ClassImplement(pClass, apInterface[n]);
			if(rc != SXRET_OK) {
				break;
			}
		}
	}
	SySetRelease(&aInterfaces);
	if(rc != SXRET_OK) {
		PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory");
		return SXERR_ABORT;
	}
done:
	/* Point beyond the class body */
	pGen->pIn = &pEnd[1];
	pGen->pEnd = pTmp;
	return PH7_OK;
}
/*
 * Compile a user-defined abstract class.
 *  According to the PHP language reference manual
 *   PHP 5 introduces abstract classes and methods. Classes defined as abstract
 *   may not be instantiated, and any class that contains at least one abstract
 *   method must also be abstract. Methods defined as abstract simply declare
 *   the method's signature - they cannot define the implementation.
 *   When inheriting from an abstract class, all methods marked abstract in the parent's
 *   class declaration must be defined by the child; additionally, these methods must be
 *   defined with the same (or a less restricted) visibility. For example, if the abstract
 *   method is defined as protected, the function implementation must be defined as either
 *   protected or public, but not private. Furthermore the signatures of the methods must
 *   match, i.e. the type hints and the number of required arguments must be the same.
 *   This also applies to constructors as of PHP 5.4. Before 5.4 constructor signatures
 *   could differ.
 */
static sxi32 PH7_CompileAbstractClass(ph7_gen_state *pGen) {
	sxi32 rc;
	pGen->pIn++; /* Jump the 'abstract' keyword */
	rc = GenStateCompileClass(&(*pGen), PH7_CLASS_ABSTRACT);
	return rc;
}
/*
 * Compile a user-defined final class.
 *  According to the PHP language reference manual
 *    PHP 5 introduces the final keyword, which prevents child classes from overriding
 *    a method by prefixing the definition with final. If the class itself is being defined
 *    final then it cannot be extended.
 */
static sxi32 PH7_CompileFinalClass(ph7_gen_state *pGen) {
	sxi32 rc;
	pGen->pIn++; /* Jump the 'final' keyword */
	rc = GenStateCompileClass(&(*pGen), PH7_CLASS_FINAL);
	return rc;
}
/*
 * Compile a user-defined class.
 *  According to the PHP language reference manual
 *   Basic class definitions begin with the keyword class, followed
 *   by a class name, followed by a pair of curly braces which enclose
 *   the definitions of the properties and methods belonging to the class.
 *   A class may contain its own constants, variables (called "properties")
 *   and functions (called "methods").
 */
static sxi32 PH7_CompileClass(ph7_gen_state *pGen) {
	sxi32 rc;
	rc = GenStateCompileClass(&(*pGen), 0);
	return rc;
}
/*
 * Exception handling.
 *  According to the PHP language reference manual
 *    An exception can be thrown, and caught ("catched") within PHP. Code may be surrounded
 *    in a try block, to facilitate the catching of potential exceptions. Each try must have
 *    at least one corresponding catch block. Multiple catch blocks can be used to catch
 *    different classes of exceptions. Normal execution (when no exception is thrown within
 *    the try block, or when a catch matching the thrown exception's class is not present)
 *    will continue after that last catch block defined in sequence. Exceptions can be thrown
 *    (or re-thrown) within a catch block.
 *    When an exception is thrown, code following the statement will not be executed, and PHP
 *    will attempt to find the first matching catch block. If an exception is not caught, a PHP
 *    Fatal Error will be issued with an "Uncaught Exception ..." message, unless a handler has
 *    been defined with set_exception_handler().
 *    The thrown object must be an instance of the Exception class or a subclass of Exception.
 *    Trying to throw an object that is not will result in a PHP Fatal Error.
 */
/*
 * Expression tree validator callback associated with the 'throw' statement.
 * Return SXRET_OK if the tree form a valid expression.Any other error
 * indicates failure.
 */
static sxi32 GenStateThrowNodeValidator(ph7_gen_state *pGen, ph7_expr_node *pRoot) {
	sxi32 rc = SXRET_OK;
	if(pRoot->pOp) {
		if(pRoot->pOp->iOp != EXPR_OP_SUBSCRIPT /* $a[] */ && pRoot->pOp->iOp != EXPR_OP_NEW  /* new Exception() */
				&& pRoot->pOp->iOp != EXPR_OP_ARROW /* -> */ && pRoot->pOp->iOp != EXPR_OP_DC /* :: */) {
			/* Unexpected expression */
			rc = PH7_GenCompileError(&(*pGen), E_ERROR, pRoot->pStart ? pRoot->pStart->nLine : 0,
									 "throw: Expecting an exception class instance");
			if(rc != SXERR_ABORT) {
				rc = SXERR_INVALID;
			}
		}
	} else if(pRoot->xCode != PH7_CompileVariable) {
		/* Unexpected expression */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pRoot->pStart ? pRoot->pStart->nLine : 0,
								 "throw: Expecting an exception class instance");
		if(rc != SXERR_ABORT) {
			rc = SXERR_INVALID;
		}
	}
	return rc;
}
/*
 * Compile a 'throw' statement.
 * throw: This is how you trigger an exception.
 * Each "throw" block must have at least one "catch" block associated with it.
 */
static sxi32 PH7_CompileThrow(ph7_gen_state *pGen) {
	sxu32 nLine = pGen->pIn->nLine;
	GenBlock *pBlock;
	sxu32 nIdx;
	sxi32 rc;
	pGen->pIn++; /* Jump the 'throw' keyword */
	/* Compile the expression */
	rc = PH7_CompileExpr(&(*pGen), 0, GenStateThrowNodeValidator);
	if(rc == SXERR_EMPTY) {
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "throw: Expecting an exception class instance");
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		return SXRET_OK;
	}
	pBlock = pGen->pCurrent;
	/* Point to the top most function or try block and emit the forward jump */
	while(pBlock->pParent) {
		if(pBlock->iFlags & (GEN_BLOCK_EXCEPTION | GEN_BLOCK_FUNC)) {
			break;
		}
		/* Point to the parent block */
		pBlock = pBlock->pParent;
	}
	/* Emit the throw instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_THROW, 0, 0, 0, &nIdx);
	/* Emit the jump */
	GenStateNewJumpFixup(pBlock, PH7_OP_THROW, nIdx);
	return SXRET_OK;
}
/*
 * Compile a 'catch' block.
 * Catch: A "catch" block retrieves an exception and creates
 * an object containing the exception information.
 */
static sxi32 PH7_CompileCatch(ph7_gen_state *pGen, ph7_exception *pException) {
	sxu32 nLine = pGen->pIn->nLine;
	ph7_exception_block sCatch;
	SySet *pInstrContainer;
	GenBlock *pCatch;
	SyToken *pToken;
	SyString *pName;
	char *zDup;
	sxi32 rc;
	pGen->pIn++; /* Jump the 'catch' keyword */
	/* Zero the structure */
	SyZero(&sCatch, sizeof(ph7_exception_block));
	/* Initialize fields */
	SySetInit(&sCatch.sByteCode, &pException->pVm->sAllocator, sizeof(VmInstr));
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0 /*(*/ ||
			&pGen->pIn[1] >= pGen->pEnd || (pGen->pIn[1].nType & (PH7_TK_ID | PH7_TK_KEYWORD)) == 0) {
		/* Unexpected token,break immediately */
		pToken = pGen->pIn;
		if(pToken >= pGen->pEnd) {
			pToken--;
		}
		rc = PH7_GenCompileError(pGen, E_ERROR, pToken->nLine,
								 "Catch: Unexpected token '%z',excpecting class name", &pToken->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		return SXERR_INVALID;
	}
	/* Extract the exception class */
	pGen->pIn++; /* Jump the left parenthesis '(' */
	/* Duplicate class name */
	pName = &pGen->pIn->sData;
	zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
	if(zDup == 0) {
		goto Mem;
	}
	SyStringInitFromBuf(&sCatch.sClass, zDup, pName->nByte);
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR) == 0 /*$*/ ||
			&pGen->pIn[1] >= pGen->pEnd || (pGen->pIn[1].nType & (PH7_TK_ID | PH7_TK_KEYWORD)) == 0) {
		/* Unexpected token,break immediately */
		pToken = pGen->pIn;
		if(pToken >= pGen->pEnd) {
			pToken--;
		}
		rc = PH7_GenCompileError(pGen, E_ERROR, pToken->nLine,
								 "Catch: Unexpected token '%z',expecting variable name", &pToken->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		return SXERR_INVALID;
	}
	pGen->pIn++; /* Jump the dollar sign */
	/* Duplicate instance name */
	pName = &pGen->pIn->sData;
	zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
	if(zDup == 0) {
		goto Mem;
	}
	SyStringInitFromBuf(&sCatch.sThis, zDup, pName->nByte);
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_RPAREN) == 0 /*)*/) {
		/* Unexpected token,break immediately */
		pToken = pGen->pIn;
		if(pToken >= pGen->pEnd) {
			pToken--;
		}
		rc = PH7_GenCompileError(pGen, E_ERROR, pToken->nLine,
								 "Catch: Unexpected token '%z',expecting right parenthesis ')'", &pToken->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		return SXERR_INVALID;
	}
	/* Compile the block */
	pGen->pIn++; /* Jump the right parenthesis */
	/* Create the catch block */
	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_EXCEPTION, PH7_VmInstrLength(pGen->pVm), 0, &pCatch);
	if(rc != SXRET_OK) {
		return SXERR_ABORT;
	}
	/* Swap bytecode container */
	pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm);
	PH7_VmSetByteCodeContainer(pGen->pVm, &sCatch.sByteCode);
	/* Compile the block */
	PH7_CompileBlock(&(*pGen), 0);
	/* Fix forward jumps now the destination is resolved  */
	GenStateFixJumps(pCatch, -1, PH7_VmInstrLength(pGen->pVm));
	/* Emit the DONE instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_DONE, 0, 0, 0, 0);
	/* Leave the block */
	GenStateLeaveBlock(&(*pGen), 0);
	/* Restore the default container */
	PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
	/* Install the catch block */
	rc = SySetPut(&pException->sEntry, (const void *)&sCatch);
	if(rc != SXRET_OK) {
		goto Mem;
	}
	return SXRET_OK;
Mem:
	PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, PH7 engine is running out of memory");
	return SXERR_ABORT;
}
/*
 * Compile a 'try' block.
 * A function using an exception should be in a "try" block.
 * If the exception does not trigger, the code will continue
 * as normal. However if the exception triggers, an exception
 * is "thrown".
 */
static sxi32 PH7_CompileTry(ph7_gen_state *pGen) {
	ph7_exception *pException;
	GenBlock *pTry;
	sxu32 nJmpIdx;
	sxi32 rc;
	/* Create the exception container */
	pException = (ph7_exception *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(ph7_exception));
	if(pException == 0) {
		PH7_GenCompileError(&(*pGen), E_ERROR,
							pGen->pIn->nLine, "Fatal, PH7 engine is running out of memory");
		return SXERR_ABORT;
	}
	/* Zero the structure */
	SyZero(pException, sizeof(ph7_exception));
	/* Initialize fields */
	SySetInit(&pException->sEntry, &pGen->pVm->sAllocator, sizeof(ph7_exception_block));
	pException->pVm = pGen->pVm;
	/* Create the try block */
	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_EXCEPTION, PH7_VmInstrLength(pGen->pVm), 0, &pTry);
	if(rc != SXRET_OK) {
		return SXERR_ABORT;
	}
	/* Emit the 'LOAD_EXCEPTION' instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOAD_EXCEPTION, 0, 0, pException, &nJmpIdx);
	/* Fix the jump later when the destination is resolved */
	GenStateNewJumpFixup(pTry, PH7_OP_LOAD_EXCEPTION, nJmpIdx);
	pGen->pIn++; /* Jump the 'try' keyword */
	/* Compile the block */
	rc = PH7_CompileBlock(&(*pGen), 0);
	if(rc == SXERR_ABORT) {
		return SXERR_ABORT;
	}
	/* Fix forward jumps now the destination is resolved */
	GenStateFixJumps(pTry, -1, PH7_VmInstrLength(pGen->pVm));
	/* Emit the 'POP_EXCEPTION' instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_POP_EXCEPTION, 0, 0, pException, 0);
	/* Leave the block */
	GenStateLeaveBlock(&(*pGen), 0);
	/* Compile the catch block */
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 ||
			SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_CATCH) {
		SyToken *pTok = pGen->pIn;
		if(pTok >= pGen->pEnd) {
			pTok--; /* Point back */
		}
		/* Unexpected token */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pTok->nLine,
								 "Try: Unexpected token '%z',expecting 'catch' block", &pTok->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		return SXRET_OK;
	}
	/* Compile one or more catch blocks */
	for(;;) {
		if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0
				|| SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_TKWRD_CATCH) {
			/* No more blocks */
			break;
		}
		/* Compile the catch block */
		rc = PH7_CompileCatch(&(*pGen), pException);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	}
	return SXRET_OK;
}
/*
 * Compile a switch block.
 *  (See block-comment below for more information)
 */
static sxi32 GenStateCompileSwitchBlock(ph7_gen_state *pGen, sxu32 iTokenDelim, sxu32 *pBlockStart) {
	sxi32 rc = SXRET_OK;
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI/*';'*/ | PH7_TK_COLON/*':'*/)) == 0) {
		/* Unexpected token */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		pGen->pIn++;
	}
	pGen->pIn++;
	/* First instruction to execute in this block. */
	*pBlockStart = PH7_VmInstrLength(pGen->pVm);
	/* Compile the block until we hit a case/default/endswitch keyword
	 * or the '}' token */
	for(;;) {
		if(pGen->pIn >= pGen->pEnd) {
			/* No more input to process */
			break;
		}
		rc = SXRET_OK;
		if((pGen->pIn->nType & PH7_TK_KEYWORD) == 0) {
			if(pGen->pIn->nType & PH7_TK_CCB /*'}' */) {
				if(iTokenDelim != PH7_TK_CCB) {
					/* Unexpected token */
					rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'",
											 &pGen->pIn->sData);
					if(rc == SXERR_ABORT) {
						return SXERR_ABORT;
					}
					/* FALL THROUGH */
				}
				rc = SXERR_EOF;
				break;
			}
		} else {
			sxi32 nKwrd;
			/* Extract the keyword */
			nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
			if(nKwrd == PH7_TKWRD_CASE || nKwrd == PH7_TKWRD_DEFAULT) {
				break;
			}
			if(nKwrd == PH7_TKWRD_ENDSWITCH /* endswitch; */) {
				if(iTokenDelim != PH7_TK_KEYWORD) {
					/* Unexpected token */
					rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'",
											 &pGen->pIn->sData);
					if(rc == SXERR_ABORT) {
						return SXERR_ABORT;
					}
					/* FALL THROUGH */
				}
				/* Block compiled */
				break;
			}
		}
		/* Compile block */
		rc = PH7_CompileBlock(&(*pGen), 0);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
	}
	return rc;
}
/*
 * Compile a case eXpression.
 *  (See block-comment below for more information)
 */
static sxi32 GenStateCompileCaseExpr(ph7_gen_state *pGen, ph7_case_expr *pExpr) {
	SySet *pInstrContainer;
	SyToken *pEnd, *pTmp;
	sxi32 iNest = 0;
	sxi32 rc;
	/* Delimit the expression */
	pEnd = pGen->pIn;
	while(pEnd < pGen->pEnd) {
		if(pEnd->nType & PH7_TK_LPAREN /*(*/) {
			/* Increment nesting level */
			iNest++;
		} else if(pEnd->nType & PH7_TK_RPAREN /*)*/) {
			/* Decrement nesting level */
			iNest--;
		} else if(pEnd->nType & (PH7_TK_SEMI/*';'*/ | PH7_TK_COLON/*;'*/) && iNest < 1) {
			break;
		}
		pEnd++;
	}
	if(pGen->pIn >= pEnd) {
		rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Empty case expression");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
	}
	/* Swap token stream */
	pTmp = pGen->pEnd;
	pGen->pEnd = pEnd;
	pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm);
	PH7_VmSetByteCodeContainer(pGen->pVm, &pExpr->aByteCode);
	rc = PH7_CompileExpr(&(*pGen), 0, 0);
	/* Emit the done instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
	PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
	/* Update token stream */
	pGen->pIn  = pEnd;
	pGen->pEnd = pTmp;
	if(rc == SXERR_ABORT) {
		return SXERR_ABORT;
	}
	return SXRET_OK;
}
/*
 * Compile the smart switch statement.
 * According to the PHP language reference manual
 *  The switch statement is similar to a series of IF statements on the same expression.
 *  In many occasions, you may want to compare the same variable (or expression) with many
 *  different values, and execute a different piece of code depending on which value it equals to.
 *  This is exactly what the switch statement is for.
 *  Note: Note that unlike some other languages, the continue statement applies to switch and acts
 *  similar to break. If you have a switch inside a loop and wish to continue to the next iteration
 *  of the outer loop, use continue 2.
 *  Note that switch/case does loose comparision.
 *  It is important to understand how the switch statement is executed in order to avoid mistakes.
 *  The switch statement executes line by line (actually, statement by statement).
 *  In the beginning, no code is executed. Only when a case statement is found with a value that
 *  matches the value of the switch expression does PHP begin to execute the statements.
 *  PHP continues to execute the statements until the end of the switch block, or the first time
 *  it sees a break statement. If you don't write a break statement at the end of a case's statement list.
 *  In a switch statement, the condition is evaluated only once and the result is compared to each
 *  case statement. In an elseif statement, the condition is evaluated again. If your condition
 *  is more complicated than a simple compare and/or is in a tight loop, a switch may be faster.
 *  The statement list for a case can also be empty, which simply passes control into the statement
 *  list for the next case.
 *  The case expression may be any expression that evaluates to a simple type, that is, integer
 *  or floating-point numbers and strings.
 */
static sxi32 PH7_CompileSwitch(ph7_gen_state *pGen) {
	GenBlock *pSwitchBlock;
	SyToken *pTmp, *pEnd;
	ph7_switch *pSwitch;
	sxu32 nToken;
	sxu32 nLine;
	sxi32 rc;
	nLine = pGen->pIn->nLine;
	/* Jump the 'switch' keyword */
	pGen->pIn++;
	if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0) {
		/* Syntax error */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'switch' keyword");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Jump the left parenthesis '(' */
	pGen->pIn++;
	pEnd = 0; /* cc warning */
	/* Create the loop block */
	rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP | GEN_BLOCK_SWITCH,
							PH7_VmInstrLength(pGen->pVm), 0, &pSwitchBlock);
	if(rc != SXRET_OK) {
		return SXERR_ABORT;
	}
	/* Delimit the condition */
	PH7_DelimitNestedTokens(pGen->pIn, pGen->pEnd, PH7_TK_LPAREN /* '(' */, PH7_TK_RPAREN /* ')' */, &pEnd);
	if(pGen->pIn == pEnd || pEnd >= pGen->pEnd) {
		/* Empty expression */
		rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'switch' keyword");
		if(rc == SXERR_ABORT) {
			/* Error count limit reached,abort immediately */
			return SXERR_ABORT;
		}
	}
	/* Swap token streams */
	pTmp = pGen->pEnd;
	pGen->pEnd = pEnd;
	/* Compile the expression */
	rc = PH7_CompileExpr(&(*pGen), 0, 0);
	if(rc == SXERR_ABORT) {
		/* Expression handler request an operation abort [i.e: Out-of-memory] */
		return SXERR_ABORT;
	}
	/* Update token stream */
	while(pGen->pIn < pEnd) {
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine,
								 "Switch: Unexpected token '%z'", &pGen->pIn->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		pGen->pIn++;
	}
	pGen->pIn  = &pEnd[1];
	pGen->pEnd = pTmp;
	if(pGen->pIn >= pGen->pEnd || &pGen->pIn[1] >= pGen->pEnd ||
			(pGen->pIn->nType & (PH7_TK_OCB/*'{'*/ | PH7_TK_COLON/*:*/)) == 0) {
		pTmp = pGen->pIn;
		if(pTmp >= pGen->pEnd) {
			pTmp--;
		}
		/* Unexpected token */
		rc = PH7_GenCompileError(&(*pGen), E_ERROR, pTmp->nLine, "Switch: Unexpected token '%z'", &pTmp->sData);
		if(rc == SXERR_ABORT) {
			return SXERR_ABORT;
		}
		goto Synchronize;
	}
	/* Set the delimiter token */
	if(pGen->pIn->nType & PH7_TK_COLON) {
		nToken = PH7_TK_KEYWORD;
		/* Stop compilation when the 'endswitch;' keyword is seen */
	} else {
		nToken = PH7_TK_CCB; /* '}' */
	}
	pGen->pIn++; /* Jump the leading curly braces/colons */
	/* Create the switch blocks container */
	pSwitch = (ph7_switch *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(ph7_switch));
	if(pSwitch == 0) {
		/* Abort compilation */
		PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Fatal, PH7 is running out of memory");
		return SXERR_ABORT;
	}
	/* Zero the structure */
	SyZero(pSwitch, sizeof(ph7_switch));
	/* Initialize fields */
	SySetInit(&pSwitch->aCaseExpr, &pGen->pVm->sAllocator, sizeof(ph7_case_expr));
	/* Emit the switch instruction */
	PH7_VmEmitInstr(pGen->pVm, PH7_OP_SWITCH, 0, 0, pSwitch, 0);
	/* Compile case blocks */
	for(;;) {
		sxu32 nKwrd;
		if(pGen->pIn >= pGen->pEnd) {
			/* No more input to process */
			break;
		}
		if((pGen->pIn->nType & PH7_TK_KEYWORD) == 0) {
			if(nToken != PH7_TK_CCB || (pGen->pIn->nType & PH7_TK_CCB /*}*/) == 0) {
				/* Unexpected token */
				rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'",
										 &pGen->pIn->sData);
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
				/* FALL THROUGH */
			}
			/* Block compiled */
			break;
		}
		/* Extract the keyword */
		nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
		if(nKwrd == PH7_TKWRD_ENDSWITCH /* endswitch; */) {
			if(nToken != PH7_TK_KEYWORD) {
				/* Unexpected token */
				rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'",
										 &pGen->pIn->sData);
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
				/* FALL THROUGH */
			}
			/* Block compiled */
			break;
		}
		if(nKwrd == PH7_TKWRD_DEFAULT) {
			/*
			 * Accroding to the PHP language reference manual
			 *  A special case is the default case. This case matches anything
			 *  that wasn't matched by the other cases.
			 */
			if(pSwitch->nDefault > 0) {
				/* Default case already compiled */
				rc = PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Switch: 'default' case already compiled");
				if(rc == SXERR_ABORT) {
					return SXERR_ABORT;
				}
			}
			pGen->pIn++; /* Jump the 'default' keyword */
			/* Compile the default block */
			rc = GenStateCompileSwitchBlock(pGen, nToken, &pSwitch->nDefault);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			} else if(rc == SXERR_EOF) {
				break;
			}
		} else if(nKwrd == PH7_TKWRD_CASE) {
			ph7_case_expr sCase;
			/* Standard case block */
			pGen->pIn++; /* Jump the 'case' keyword */
			/* initialize the structure */
			SySetInit(&sCase.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
			/* Compile the case expression */
			rc = GenStateCompileCaseExpr(pGen, &sCase);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
			/* Compile the case block */
			rc = GenStateCompileSwitchBlock(pGen, nToken, &sCase.nStart);
			/* Insert in the switch container */
			SySetPut(&pSwitch->aCaseExpr, (const void *)&sCase);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			} else if(rc == SXERR_EOF) {
				break;
			}
		} else {
			/* Unexpected token */
			rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'",
									 &pGen->pIn->sData);
			if(rc == SXERR_ABORT) {
				return SXERR_ABORT;
			}
			break;
		}
	}
	/* Fix all jumps now the destination is resolved */
	pSwitch->nOut = PH7_VmInstrLength(pGen->pVm);
	GenStateFixJumps(pSwitchBlock, -1, PH7_VmInstrLength(pGen->pVm));
	/* Release the loop block */
	GenStateLeaveBlock(pGen, 0);
	if(pGen->pIn < pGen->pEnd) {
		/* Jump the trailing curly braces or the endswitch keyword*/
		pGen->pIn++;
	}
	/* Statement successfully compiled */
	return SXRET_OK;
Synchronize:
	/* Synchronize with the first semi-colon */
	while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) {
		pGen->pIn++;
	}
	return SXRET_OK;
}
/*
 * Generate bytecode for a given expression tree.
 * If something goes wrong while generating bytecode
 * for the expression tree (A very unlikely scenario)
 * this function takes care of generating the appropriate
 * error message.
 */
static sxi32 GenStateEmitExprCode(
	ph7_gen_state *pGen,  /* Code generator state */
	ph7_expr_node *pNode, /* Root of the expression tree */
	sxi32 iFlags /* Control flags */
) {
	VmInstr *pInstr;
	sxu32 nJmpIdx;
	sxi32 iP1 = 0;
	sxu32 iP2 = 0;
	void *p3  = 0;
	sxi32 iVmOp;
	sxi32 rc;
	if(pNode->xCode) {
		SyToken *pTmpIn, *pTmpEnd;
		/* Compile node */
		SWAP_DELIMITER(pGen, pNode->pStart, pNode->pEnd);
		rc = pNode->xCode(&(*pGen), iFlags);
		RE_SWAP_DELIMITER(pGen);
		return rc;
	}
	if(pNode->pOp == 0) {
		PH7_GenCompileError(&(*pGen), E_ERROR, pNode->pStart->nLine,
							"Invalid expression node,PH7 is aborting compilation");
		return SXERR_ABORT;
	}
	iVmOp = pNode->pOp->iVmOp;
	if(pNode->pOp->iOp == EXPR_OP_QUESTY) {
		sxu32 nJz, nJmp;
		/* Ternary operator require special handling */
		/* Phase#1: Compile the condition */
		rc = GenStateEmitExprCode(&(*pGen), pNode->pCond, iFlags);
		if(rc != SXRET_OK) {
			return rc;
		}
		nJz = nJmp = 0; /* cc -O6 warning */
		/* Phase#2: Emit the false jump */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_JZ, 0, 0, 0, &nJz);
		if(pNode->pLeft) {
			/* Phase#3: Compile the 'then' expression  */
			rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
			if(rc != SXRET_OK) {
				return rc;
			}
		}
		/* Phase#4: Emit the unconditional jump */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_JMP, 0, 0, 0, &nJmp);
		/* Phase#5: Fix the false jump now the jump destination is resolved. */
		pInstr = PH7_VmGetInstr(pGen->pVm, nJz);
		if(pInstr) {
			pInstr->iP2 = PH7_VmInstrLength(pGen->pVm);
		}
		/* Phase#6: Compile the 'else' expression */
		if(pNode->pRight) {
			rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
			if(rc != SXRET_OK) {
				return rc;
			}
		}
		if(nJmp > 0) {
			/* Phase#7: Fix the unconditional jump */
			pInstr = PH7_VmGetInstr(pGen->pVm, nJmp);
			if(pInstr) {
				pInstr->iP2 = PH7_VmInstrLength(pGen->pVm);
			}
		}
		/* All done */
		return SXRET_OK;
	}
	/* Generate code for the left tree */
	if(pNode->pLeft) {
		if(iVmOp == PH7_OP_CALL) {
			ph7_expr_node **apNode;
			sxi32 n;
			/* Recurse and generate bytecodes for function arguments */
			apNode = (ph7_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
			/* Read-only load */
			iFlags |= EXPR_FLAG_RDONLY_LOAD;
			for(n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n) {
				rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags & ~EXPR_FLAG_LOAD_IDX_STORE);
				if(rc != SXRET_OK) {
					return rc;
				}
			}
			/* Total number of given arguments */
			iP1 = (sxi32)SySetUsed(&pNode->aNodeArgs);
			/* Remove stale flags now */
			iFlags &= ~EXPR_FLAG_RDONLY_LOAD;
		}
		rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
		if(rc != SXRET_OK) {
			return rc;
		}
		if(iVmOp == PH7_OP_CALL) {
			pInstr = PH7_VmPeekInstr(pGen->pVm);
			if(pInstr) {
				if(pInstr->iOp == PH7_OP_LOADC) {
					/* Prevent constant expansion */
					pInstr->iP1 = 0;
				} else if(pInstr->iOp == PH7_OP_MEMBER /* $a->b(1,2,3) */ || pInstr->iOp == PH7_OP_NEW) {
					/* Method call,flag that */
					pInstr->iP2 = 1;
				}
			}
		} else if(iVmOp == PH7_OP_LOAD_IDX) {
			ph7_expr_node **apNode;
			sxi32 n;
			/* Recurse and generate bytecodes for array index */
			apNode = (ph7_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
			for(n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n) {
				rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags & ~EXPR_FLAG_LOAD_IDX_STORE);
				if(rc != SXRET_OK) {
					return rc;
				}
			}
			if(SySetUsed(&pNode->aNodeArgs) > 0) {
				iP1 = 1; /* Node have an index associated with it */
			}
			if(iFlags & EXPR_FLAG_LOAD_IDX_STORE) {
				/* Create an empty entry when the desired index is not found */
				iP2 = 1;
			}
		} else if(pNode->pOp->iOp == EXPR_OP_COMMA) {
			/* POP the left node */
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_POP, 1, 0, 0, 0);
		}
	}
	rc = SXRET_OK;
	nJmpIdx = 0;
	/* Generate code for the right tree */
	if(pNode->pRight) {
		if(iVmOp == PH7_OP_LAND) {
			/* Emit the false jump so we can short-circuit the logical and */
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_JZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
		} else if(iVmOp == PH7_OP_LOR) {
			/* Emit the true jump so we can short-circuit the logical or*/
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_JNZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
		} else if(pNode->pOp->iPrec == 18 /* Combined binary operators [i.e: =,'.=','+=',*=' ...] precedence */) {
			iFlags |= EXPR_FLAG_LOAD_IDX_STORE;
		}
		rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
		if(iVmOp == PH7_OP_STORE) {
			pInstr = PH7_VmPeekInstr(pGen->pVm);
			if(pInstr) {
				if(pInstr->iOp == PH7_OP_LOAD_LIST) {
					/* Hide the STORE instruction */
					iVmOp = 0;
				} else if(pInstr->iOp == PH7_OP_MEMBER) {
					/* Perform a member store operation [i.e: $this->x = 50] */
					iP2 = 1;
				} else {
					if(pInstr->iOp == PH7_OP_LOAD_IDX) {
						/* Transform the STORE instruction to STORE_IDX instruction */
						iVmOp = PH7_OP_STORE_IDX;
						iP1 = pInstr->iP1;
					} else {
						p3 = pInstr->p3;
					}
					/* POP the last dynamic load instruction */
					(void)PH7_VmPopInstr(pGen->pVm);
				}
			}
		} else if(iVmOp == PH7_OP_STORE_REF) {
			pInstr = PH7_VmPopInstr(pGen->pVm);
			if(pInstr) {
				if(pInstr->iOp == PH7_OP_LOAD_IDX) {
					/* Array insertion by reference [i.e: $pArray[] =& $some_var; ]
					 * We have to convert the STORE_REF instruction into STORE_IDX_REF
					 */
					iVmOp = PH7_OP_STORE_IDX_REF;
					iP1 = pInstr->iP1;
					iP2 = pInstr->iP2;
					p3  = pInstr->p3;
				} else {
					p3 = pInstr->p3;
				}
			}
		}
	}
	if(iVmOp > 0) {
		if(iVmOp == PH7_OP_INCR || iVmOp == PH7_OP_DECR) {
			if(pNode->iFlags & EXPR_NODE_PRE_INCR) {
				/* Pre-increment/decrement operator [i.e: ++$i,--$j ] */
				iP1 = 1;
			}
		} else if(iVmOp == PH7_OP_NEW) {
			pInstr = PH7_VmPeekInstr(pGen->pVm);
			if(pInstr && pInstr->iOp == PH7_OP_CALL) {
				VmInstr *pPrev;
				pPrev = PH7_VmPeekNextInstr(pGen->pVm);
				if(pPrev == 0 || pPrev->iOp != PH7_OP_MEMBER) {
					/* Pop the call instruction */
					iP1 = pInstr->iP1;
					(void)PH7_VmPopInstr(pGen->pVm);
				}
			}
		} else if(iVmOp == PH7_OP_MEMBER) {
			if(pNode->pOp->iOp == EXPR_OP_DC /* '::' */) {
				/* Static member access,remember that */
				iP1 = 1;
				pInstr = PH7_VmPeekInstr(pGen->pVm);
				if(pInstr && pInstr->iOp == PH7_OP_LOAD) {
					p3 = pInstr->p3;
					(void)PH7_VmPopInstr(pGen->pVm);
				}
			}
		}
		/* Finally,emit the VM instruction associated with this operator */
		PH7_VmEmitInstr(pGen->pVm, iVmOp, iP1, iP2, p3, 0);
		if(nJmpIdx > 0) {
			/* Fix short-circuited jumps now the destination is resolved */
			pInstr = PH7_VmGetInstr(pGen->pVm, nJmpIdx);
			if(pInstr) {
				pInstr->iP2 = PH7_VmInstrLength(pGen->pVm);
			}
		}
	}
	return rc;
}
/*
 * Compile a PHP expression.
 * According to the PHP language reference manual:
 *  Expressions are the most important building stones of PHP.
 *  In PHP, almost anything you write is an expression.
 *  The simplest yet most accurate way to define an expression
 *  is "anything that has a value".
 * If something goes wrong while compiling the expression,this
 * function takes care of generating the appropriate error
 * message.
 */
static sxi32 PH7_CompileExpr(
	ph7_gen_state *pGen, /* Code generator state */
	sxi32 iFlags,        /* Control flags */
	sxi32(*xTreeValidator)(ph7_gen_state *, ph7_expr_node *) /* Node validator callback.NULL otherwise */
) {
	ph7_expr_node *pRoot;
	SySet sExprNode;
	SyToken *pEnd;
	sxi32 nExpr;
	sxi32 iNest;
	sxi32 rc;
	/* Initialize worker variables */
	nExpr = 0;
	pRoot = 0;
	SySetInit(&sExprNode, &pGen->pVm->sAllocator, sizeof(ph7_expr_node *));
	SySetAlloc(&sExprNode, 0x10);
	rc = SXRET_OK;
	/* Delimit the expression */
	pEnd = pGen->pIn;
	iNest = 0;
	while(pEnd < pGen->pEnd) {
		if(pEnd->nType & PH7_TK_OCB /* '{' */) {
			/* Ticket 1433-30: Annonymous/Closure functions body */
			iNest++;
		} else if(pEnd->nType & PH7_TK_CCB /* '}' */) {
			iNest--;
		} else if(pEnd->nType & PH7_TK_SEMI /* ';' */) {
			if(iNest <= 0) {
				break;
			}
		}
		pEnd++;
	}
	if(iFlags & EXPR_FLAG_COMMA_STATEMENT) {
		SyToken *pEnd2 = pGen->pIn;
		iNest = 0;
		/* Stop at the first comma */
		while(pEnd2 < pEnd) {
			if(pEnd2->nType & (PH7_TK_OCB/*'{'*/ | PH7_TK_OSB/*'['*/ | PH7_TK_LPAREN/*'('*/)) {
				iNest++;
			} else if(pEnd2->nType & (PH7_TK_CCB/*'}'*/ | PH7_TK_CSB/*']'*/ | PH7_TK_RPAREN/*')'*/)) {
				iNest--;
			} else if(pEnd2->nType & PH7_TK_COMMA /*','*/) {
				if(iNest <= 0) {
					break;
				}
			}
			pEnd2++;
		}
		if(pEnd2 < pEnd) {
			pEnd = pEnd2;
		}
	}
	if(pEnd > pGen->pIn) {
		SyToken *pTmp = pGen->pEnd;
		/* Swap delimiter */
		pGen->pEnd = pEnd;
		/* Try to get an expression tree */
		rc = PH7_ExprMakeTree(&(*pGen), &sExprNode, &pRoot);
		if(rc == SXRET_OK && pRoot) {
			rc = SXRET_OK;
			if(xTreeValidator) {
				/* Call the upper layer validator callback */
				rc = xTreeValidator(&(*pGen), pRoot);
			}
			if(rc != SXERR_ABORT) {
				/* Generate code for the given tree */
				rc = GenStateEmitExprCode(&(*pGen), pRoot, iFlags);
			}
			nExpr = 1;
		}
		/* Release the whole tree */
		PH7_ExprFreeTree(&(*pGen), &sExprNode);
		/* Synchronize token stream */
		pGen->pEnd = pTmp;
		pGen->pIn  = pEnd;
		if(rc == SXERR_ABORT) {
			SySetRelease(&sExprNode);
			return SXERR_ABORT;
		}
	}
	SySetRelease(&sExprNode);
	return nExpr > 0 ? SXRET_OK : SXERR_EMPTY;
}
/*
 * Return a pointer to the node construct handler associated
 * with a given node type [i.e: string,integer,float,...].
 */
PH7_PRIVATE ProcNodeConstruct PH7_GetNodeHandler(sxu32 nNodeType) {
	if(nNodeType & PH7_TK_NUM) {
		/* Numeric literal: Either real or integer */
		return PH7_CompileNumLiteral;
	} else if(nNodeType & PH7_TK_DSTR) {
		/* Double quoted string */
		return PH7_CompileString;
	} else if(nNodeType & PH7_TK_SSTR) {
		/* Single quoted string */
		return PH7_CompileSimpleString;
	} else if(nNodeType & PH7_TK_BSTR) {
		/* Backtick quoted string */
		return PH7_CompileBacktic;
	}
	return 0;
}
/*
 * PHP Language construct table.
 */
static const LangConstruct aLangConstruct[] = {
	{ PH7_TKWRD_ECHO,     PH7_CompileEcho     }, /* echo language construct */
	{ PH7_TKWRD_IF,       PH7_CompileIf       }, /* if statement */
	{ PH7_TKWRD_FOR,      PH7_CompileFor      }, /* for statement */
	{ PH7_TKWRD_WHILE,    PH7_CompileWhile    }, /* while statement */
	{ PH7_TKWRD_FOREACH,  PH7_CompileForeach  }, /* foreach statement */
	{ PH7_TKWRD_FUNCTION, PH7_CompileFunction }, /* function statement */
	{ PH7_TKWRD_CONTINUE, PH7_CompileContinue }, /* continue statement */
	{ PH7_TKWRD_BREAK,    PH7_CompileBreak    }, /* break statement */
	{ PH7_TKWRD_RETURN,   PH7_CompileReturn   }, /* return statement */
	{ PH7_TKWRD_SWITCH,   PH7_CompileSwitch   }, /* Switch statement */
	{ PH7_TKWRD_DO,       PH7_CompileDoWhile  }, /* do{ }while(); statement */
	{ PH7_TKWRD_GLOBAL,   PH7_CompileGlobal   }, /* global statement */
	{ PH7_TKWRD_STATIC,   PH7_CompileStatic   }, /* static statement */
	{ PH7_TKWRD_DIE,      PH7_CompileHalt     }, /* die language construct */
	{ PH7_TKWRD_EXIT,     PH7_CompileHalt     }, /* exit language construct */
	{ PH7_TKWRD_TRY,      PH7_CompileTry      }, /* try statement */
	{ PH7_TKWRD_THROW,    PH7_CompileThrow    }, /* throw statement */
	{ PH7_TKWRD_CONST,    PH7_CompileConstant }, /* const statement */
	{ PH7_TKWRD_VAR,      PH7_CompileVar      }, /* var statement */
	{ PH7_TKWRD_NAMESPACE, PH7_CompileNamespace }, /* namespace statement */
	{ PH7_TKWRD_USE,      PH7_CompileUse      },  /* use statement */
	{ PH7_TKWRD_DECLARE,  PH7_CompileDeclare  }   /* declare statement */
};
/*
 * Return a pointer to the statement handler routine associated
 * with a given PHP keyword [i.e: if,for,while,...].
 */
static ProcLangConstruct GenStateGetStatementHandler(
	sxu32 nKeywordID,   /* Keyword  ID*/
	SyToken *pLookahed  /* Look-ahead token */
) {
	sxu32 n = 0;
	for(;;) {
		if(n >= SX_ARRAYSIZE(aLangConstruct)) {
			break;
		}
		if(aLangConstruct[n].nID == nKeywordID) {
			if(nKeywordID == PH7_TKWRD_STATIC && pLookahed && (pLookahed->nType & PH7_TK_OP)) {
				const ph7_expr_op *pOp = (const ph7_expr_op *)pLookahed->pUserData;
				if(pOp && pOp->iOp == EXPR_OP_DC /*::*/) {
					/* 'static' (class context),return null */
					return 0;
				}
			}
			/* Return a pointer to the handler.
			*/
			return aLangConstruct[n].xConstruct;
		}
		n++;
	}
	if(pLookahed) {
		if(nKeywordID == PH7_TKWRD_INTERFACE && (pLookahed->nType & PH7_TK_ID)) {
			return PH7_CompileClassInterface;
		} else if(nKeywordID == PH7_TKWRD_CLASS && (pLookahed->nType & PH7_TK_ID)) {
			return PH7_CompileClass;
		} else if(nKeywordID == PH7_TKWRD_ABSTRACT && (pLookahed->nType & PH7_TK_KEYWORD)
				  && SX_PTR_TO_INT(pLookahed->pUserData) == PH7_TKWRD_CLASS) {
			return PH7_CompileAbstractClass;
		} else if(nKeywordID == PH7_TKWRD_FINAL && (pLookahed->nType & PH7_TK_KEYWORD)
				  && SX_PTR_TO_INT(pLookahed->pUserData) == PH7_TKWRD_CLASS) {
			return PH7_CompileFinalClass;
		}
	}
	/* Not a language construct */
	return 0;
}
/*
 * Check if the given keyword is in fact a PHP language construct.
 * Return TRUE on success. FALSE otheriwse.
 */
static int GenStateisLangConstruct(sxu32 nKeyword) {
	int rc;
	rc = PH7_IsLangConstruct(nKeyword, TRUE);
	if(rc == FALSE) {
		if(nKeyword == PH7_TKWRD_SELF || nKeyword == PH7_TKWRD_PARENT || nKeyword == PH7_TKWRD_STATIC
				/*|| nKeyword == PH7_TKWRD_CLASS || nKeyword == PH7_TKWRD_FINAL || nKeyword == PH7_TKWRD_EXTENDS
				  || nKeyword == PH7_TKWRD_ABSTRACT || nKeyword == PH7_TKWRD_INTERFACE
				  || nKeyword == PH7_TKWRD_PUBLIC || nKeyword == PH7_TKWRD_PROTECTED
				  || nKeyword == PH7_TKWRD_PRIVATE || nKeyword == PH7_TKWRD_IMPLEMENTS
				*/
		  ) {
			rc = TRUE;
		}
	}
	return rc;
}
/*
 * Compile a PHP chunk.
 * If something goes wrong while compiling the PHP chunk,this function
 * takes care of generating the appropriate error message.
 */
static sxi32 GenStateCompileChunk(
	ph7_gen_state *pGen, /* Code generator state */
	sxi32 iFlags         /* Compile flags */
) {
	ProcLangConstruct xCons;
	sxi32 rc;
	rc = SXRET_OK; /* Prevent compiler warning */
	for(;;) {
		if(pGen->pIn >= pGen->pEnd) {
			/* No more input to process */
			break;
		}
		if(pGen->pIn->nType & PH7_TK_OCB /* '{' */) {
			/* Compile block */
			rc = PH7_CompileBlock(&(*pGen), 0);
			if(rc == SXERR_ABORT) {
				break;
			}
		} else {
			xCons = 0;
			if(pGen->pIn->nType & PH7_TK_KEYWORD) {
				sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
				/* Try to extract a language construct handler */
				xCons = GenStateGetStatementHandler(nKeyword, (&pGen->pIn[1] < pGen->pEnd) ? &pGen->pIn[1] : 0);
				if(xCons == 0 && GenStateisLangConstruct(nKeyword) == FALSE) {
					rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
											 "Syntax error: Unexpected keyword '%z'",
											 &pGen->pIn->sData);
					if(rc == SXERR_ABORT) {
						break;
					}
					/* Synchronize with the first semi-colon and avoid compiling
					 * this erroneous statement.
					 */
					xCons = PH7_ErrorRecover;
				}
			}
			if(xCons == 0) {
				/* Assume an expression an try to compile it */
				rc = PH7_CompileExpr(&(*pGen), 0, 0);
				if(rc != SXERR_EMPTY) {
					/* Pop l-value */
					PH7_VmEmitInstr(pGen->pVm, PH7_OP_POP, 1, 0, 0, 0);
				}
			} else {
				/* Go compile the sucker */
				rc = xCons(&(*pGen));
			}
			if(rc == SXERR_ABORT) {
				/* Request to abort compilation */
				break;
			}
		}
		/* Ignore trailing semi-colons ';' */
		while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI)) {
			pGen->pIn++;
		}
		if(iFlags & PH7_COMPILE_SINGLE_STMT) {
			/* Compile a single statement and return */
			break;
		}
		/* LOOP ONE */
		/* LOOP TWO */
		/* LOOP THREE */
		/* LOOP FOUR */
	}
	/* Return compilation status */
	return rc;
}
/*
 * Compile a Raw PHP chunk.
 * If something goes wrong while compiling the PHP chunk,this function
 * takes care of generating the appropriate error message.
 */
static sxi32 PH7_CompilePHP(
	ph7_gen_state *pGen,  /* Code generator state */
	SySet *pTokenSet,     /* Token set */
	int is_expr           /* TRUE if we are dealing with a simple expression */
) {
	SyToken *pScript = pGen->pRawIn; /* Script to compile */
	sxi32 rc;
	/* Reset the token set */
	SySetReset(&(*pTokenSet));
	/* Mark as the default token set */
	pGen->pTokenSet = &(*pTokenSet);
	/* Advance the stream cursor */
	pGen->pRawIn++;
	/* Tokenize the PHP chunk first */
	PH7_TokenizePHP(SyStringData(&pScript->sData), SyStringLength(&pScript->sData), pScript->nLine, &(*pTokenSet));
	/* Point to the head and tail of the token stream. */
	pGen->pIn  = (SyToken *)SySetBasePtr(pTokenSet);
	pGen->pEnd = &pGen->pIn[SySetUsed(pTokenSet)];
	if(is_expr) {
		rc = SXERR_EMPTY;
		if(pGen->pIn < pGen->pEnd) {
			/* A simple expression,compile it */
			rc = PH7_CompileExpr(pGen, 0, 0);
		}
		/* Emit the DONE instruction */
		PH7_VmEmitInstr(pGen->pVm, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
		return SXRET_OK;
	}
	if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_EQUAL)) {
		static const sxu32 nKeyID = PH7_TKWRD_ECHO;
		/*
		 * Shortcut syntax for the 'echo' language construct.
		 * According to the PHP reference manual:
		 *  echo() also has a shortcut syntax, where you can
		 *  immediately follow
		 *  the opening tag with an equals sign as follows:
		 *  <?= 4+5?> is the same as <?echo 4+5?>
		 * Symisc extension:
		 *   This short syntax works with all PHP opening
		 *   tags unlike the default PHP engine that handle
		 *   only short tag.
		 */
		/* Ticket 1433-009: Emulate the 'echo' call */
		pGen->pIn->nType = PH7_TK_KEYWORD;
		pGen->pIn->pUserData = SX_INT_TO_PTR(nKeyID);
		SyStringInitFromBuf(&pGen->pIn->sData, "echo", sizeof("echo") - 1);
		rc = PH7_CompileExpr(pGen, 0, 0);
		if(rc != SXERR_EMPTY) {
			PH7_VmEmitInstr(pGen->pVm, PH7_OP_POP, 1, 0, 0, 0);
		}
		return SXRET_OK;
	}
	/* Compile the PHP chunk */
	rc = GenStateCompileChunk(pGen, 0);
	/* Fix exceptions jumps */
	GenStateFixJumps(pGen->pCurrent, PH7_OP_THROW, PH7_VmInstrLength(pGen->pVm));
	/* Fix gotos now, the jump destination is resolved */
	/* Compilation result */
	return rc;
}
/*
 * Compile a raw chunk. The raw chunk can contain PHP code embedded
 * in HTML, XML and so on. This function handle all the stuff.
 * This is the only compile interface exported from this file.
 */
PH7_PRIVATE sxi32 PH7_CompileScript(
	ph7_vm *pVm,        /* Generate PH7 byte-codes for this Virtual Machine */
	SyString *pScript,  /* Script to compile */
	sxi32 iFlags        /* Compile flags */
) {
	SySet aPhpToken, aRawToken;
	ph7_gen_state *pCodeGen;
	ph7_value *pRawObj;
	sxu32 nObjIdx;
	sxi32 nRawObj;
	int is_expr;
	sxi32 rc;
	if(pScript->nByte < 1) {
		/* Nothing to compile */
		return PH7_OK;
	}
	/* Initialize the tokens containers */
	SySetInit(&aRawToken, &pVm->sAllocator, sizeof(SyToken));
	SySetInit(&aPhpToken, &pVm->sAllocator, sizeof(SyToken));
	SySetAlloc(&aPhpToken, 0xc0);
	is_expr = 0;
	if(iFlags & PH7_PHP_ONLY) {
		SyToken sTmp;
		/* PHP only: -*/
		sTmp.nLine = 1;
		sTmp.nType = PH7_TOKEN_PHP;
		sTmp.pUserData = 0;
		SyStringDupPtr(&sTmp.sData, pScript);
		SySetPut(&aRawToken, (const void *)&sTmp);
		if(iFlags & PH7_PHP_EXPR) {
			/* A simple PHP expression */
			is_expr = 1;
		}
	} else {
		/* Tokenize raw text */
		SySetAlloc(&aRawToken, 32);
		PH7_TokenizeRawText(pScript->zString, pScript->nByte, &aRawToken);
	}
	pCodeGen = &pVm->sCodeGen;
	/* Process high-level tokens */
	pCodeGen->pRawIn = (SyToken *)SySetBasePtr(&aRawToken);
	pCodeGen->pRawEnd = &pCodeGen->pRawIn[SySetUsed(&aRawToken)];
	rc = PH7_OK;
	if(is_expr) {
		/* Compile the expression */
		rc = PH7_CompilePHP(pCodeGen, &aPhpToken, TRUE);
		goto cleanup;
	}
	nObjIdx = 0;
	/* Start the compilation process */
	for(;;) {
		if(pCodeGen->pRawIn >= pCodeGen->pRawEnd) {
			break; /* No more tokens to process */
		}
		if(pCodeGen->pRawIn->nType & PH7_TOKEN_PHP) {
			/* Compile the PHP chunk */
			rc = PH7_CompilePHP(pCodeGen, &aPhpToken, FALSE);
			if(rc == SXERR_ABORT) {
				break;
			}
			continue;
		}
		/* Raw chunk: [i.e: HTML, XML, etc.] */
		nRawObj = 0;
		while((pCodeGen->pRawIn < pCodeGen->pRawEnd) && (pCodeGen->pRawIn->nType != PH7_TOKEN_PHP)) {
			/* Consume the raw chunk without any processing */
			pRawObj = PH7_ReserveConstObj(&(*pVm), &nObjIdx);
			if(pRawObj == 0) {
				rc = SXERR_MEM;
				break;
			}
			/* Mark as constant and emit the load constant instruction */
			PH7_MemObjInitFromString(pVm, pRawObj, &pCodeGen->pRawIn->sData);
			PH7_VmEmitInstr(&(*pVm), PH7_OP_LOADC, 0, nObjIdx, 0, 0);
			++nRawObj;
			pCodeGen->pRawIn++; /* Next chunk */
		}
		if(nRawObj > 0) {
			/* Emit the consume instruction */
			PH7_VmEmitInstr(&(*pVm), PH7_OP_CONSUME, nRawObj, 0, 0, 0);
		}
	}
cleanup:
	SySetRelease(&aRawToken);
	SySetRelease(&aPhpToken);
	return rc;
}
/*
 * Utility routines.Initialize the code generator.
 */
PH7_PRIVATE sxi32 PH7_InitCodeGenerator(
	ph7_vm *pVm,       /* Target VM */
	ProcConsumer xErr, /* Error log consumer callabck  */
	void *pErrData     /* Last argument to xErr() */
) {
	ph7_gen_state *pGen = &pVm->sCodeGen;
	/* Zero the structure */
	SyZero(pGen, sizeof(ph7_gen_state));
	/* Initial state */
	pGen->pVm  = &(*pVm);
	pGen->xErr = xErr;
	pGen->pErrData = pErrData;
	SyHashInit(&pGen->hLiteral, &pVm->sAllocator, 0, 0);
	SyHashInit(&pGen->hVar, &pVm->sAllocator, 0, 0);
	/* Error log buffer */
	SyBlobInit(&pGen->sErrBuf, &pVm->sAllocator);
	/* General purpose working buffer */
	SyBlobInit(&pGen->sWorker, &pVm->sAllocator);
	/* Create the global scope */
	GenStateInitBlock(pGen, &pGen->sGlobal, GEN_BLOCK_GLOBAL, PH7_VmInstrLength(&(*pVm)), 0);
	/* Point to the global scope */
	pGen->pCurrent = &pGen->sGlobal;
	return SXRET_OK;
}
/*
 * Utility routines. Reset the code generator to it's initial state.
 */
PH7_PRIVATE sxi32 PH7_ResetCodeGenerator(
	ph7_vm *pVm,       /* Target VM */
	ProcConsumer xErr, /* Error log consumer callabck  */
	void *pErrData     /* Last argument to xErr() */
) {
	ph7_gen_state *pGen = &pVm->sCodeGen;
	GenBlock *pBlock, *pParent;
	/* Reset state */
	SyBlobRelease(&pGen->sErrBuf);
	SyBlobRelease(&pGen->sWorker);
	/* Point to the global scope */
	pBlock = pGen->pCurrent;
	while(pBlock->pParent != 0) {
		pParent = pBlock->pParent;
		GenStateFreeBlock(pBlock);
		pBlock = pParent;
	}
	pGen->xErr = xErr;
	pGen->pErrData = pErrData;
	pGen->pCurrent = &pGen->sGlobal;
	pGen->pRawIn = pGen->pRawEnd = 0;
	pGen->pIn = pGen->pEnd = 0;
	pGen->nErr = 0;
	return SXRET_OK;
}
/*
 * Generate a compile-time error message.
 * If the error count limit is reached (usually 15 error message)
 * this function return SXERR_ABORT.In that case upper-layers must
 * abort compilation immediately.
 */
PH7_PRIVATE sxi32 PH7_GenCompileError(ph7_gen_state *pGen, sxi32 nErrType, sxu32 nLine, const char *zFormat, ...) {
	SyBlob *pWorker = &pGen->sErrBuf;
	const char *zErr = "Error";
	SyString *pFile;
	va_list ap;
	sxi32 rc;
	/* Reset the working buffer */
	SyBlobReset(pWorker);
	/* Peek the processed file path if available */
	pFile = (SyString *)SySetPeek(&pGen->pVm->aFiles);
	if(pFile && pGen->xErr) {
		/* Append file name */
		SyBlobAppend(pWorker, pFile->zString, pFile->nByte);
		SyBlobAppend(pWorker, (const void *)": ", sizeof(": ") - 1);
	}
	if(nErrType == E_ERROR) {
		/* Increment the error counter */
		pGen->nErr++;
		if(pGen->nErr > 15) {
			/* Error count limit reached */
			if(pGen->xErr) {
				SyBlobFormat(pWorker, "%u Error count limit reached,PH7 is aborting compilation\n", nLine);
				if(SyBlobLength(pWorker) > 0) {
					/* Consume the generated error message */
					pGen->xErr(SyBlobData(pWorker), SyBlobLength(pWorker), pGen->pErrData);
				}
			}
			/* Abort immediately */
			return SXERR_ABORT;
		}
	}
	if(pGen->xErr == 0) {
		/* No available error consumer,return immediately */
		return SXRET_OK;
	}
	switch(nErrType) {
		case E_WARNING:
			zErr = "Warning";
			break;
		case E_PARSE:
			zErr = "Parse error";
			break;
		case E_NOTICE:
			zErr = "Notice";
			break;
		case E_USER_ERROR:
			zErr = "User error";
			break;
		case E_USER_WARNING:
			zErr = "User warning";
			break;
		case E_USER_NOTICE:
			zErr = "User notice";
			break;
		default:
			break;
	}
	rc = SXRET_OK;
	/* Format the error message */
	SyBlobFormat(pWorker, "%u %s: ", nLine, zErr);
	va_start(ap, zFormat);
	SyBlobFormatAp(pWorker, zFormat, ap);
	va_end(ap);
	/* Append a new line */
	SyBlobAppend(pWorker, (const void *)"\n", sizeof(char));
	if(SyBlobLength(pWorker) > 0) {
		/* Consume the generated error message */
		pGen->xErr(SyBlobData(pWorker), SyBlobLength(pWorker), pGen->pErrData);
	}
	return rc;
}