Aer Interpreter Source
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5704 lines
197 KiB

/*
* 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/
*/
3 years ago
/* $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.
*/
3 years ago
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. */
};
/*
* Each language construct is represented by an instance
* of the following structure.
*/
3 years ago
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 ){\
3 years ago
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 */
3 years ago
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.
*/
3 years ago
static GenBlock *GenStateFetchBlock(GenBlock *pCurrent, sxi32 iBlockType, sxi32 iCount) {
GenBlock *pBlock = pCurrent;
3 years ago
for(;;) {
if(pBlock->iFlags & iBlockType) {
iCount--; /* Decrement nesting level */
3 years ago
if(iCount < 1) {
/* Block meet with the desired criteria */
return pBlock;
}
}
/* Point to the upper block */
pBlock = pBlock->pParent;
3 years ago
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 */
3 years ago
) {
/* Initialize block fields */
pBlock->nFirstInstr = nFirstInstr;
pBlock->pUserData = pUserData;
pBlock->pGen = pGen;
pBlock->iFlags = iType;
pBlock->pParent = 0;
3 years ago
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 */
3 years ago
) {
GenBlock *pBlock;
/* Allocate a new block instance */
3 years ago
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.
*/
3 years ago
PH7_GenCompileError(&(*pGen), E_ERROR, 1, "Fatal, PH7 engine is running out-of-memory");
/* Abort processing immediately */
return SXERR_ABORT;
}
/* Zero the structure */
3 years ago
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;
3 years ago
if(ppBlock) {
/* Write a pointer to the new instance */
*ppBlock = pBlock;
}
return SXRET_OK;
}
/*
* Release block fields without freeing the whole instance.
*/
3 years ago
static void GenStateReleaseBlock(GenBlock *pBlock) {
SySetRelease(&pBlock->aPostContFix);
SySetRelease(&pBlock->aJumpFix);
}
/*
* Release a block.
*/
3 years ago
static void GenStateFreeBlock(GenBlock *pBlock) {
ph7_gen_state *pGen = pBlock->pGen;
GenStateReleaseBlock(&(*pBlock));
/* Free the instance */
3 years ago
SyMemBackendPoolFree(&pGen->pVm->sAllocator, pBlock);
}
/*
* POP and release a block from the stack of compiled blocks.
*/
3 years ago
static sxi32 GenStateLeaveBlock(ph7_gen_state *pGen, GenBlock **ppBlock) {
GenBlock *pBlock = pGen->pCurrent;
3 years ago
if(pBlock == 0) {
/* No more block to pop */
return SXERR_EMPTY;
}
/* Point to the upper block */
pGen->pCurrent = pBlock->pParent;
3 years ago
if(ppBlock) {
/* Write a pointer to the popped block */
*ppBlock = pBlock;
3 years ago
} else {
/* Safely release the block */
3 years ago
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.
*/
3 years ago
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 */
3 years ago
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.
*/
3 years ago
static sxu32 GenStateFixJumps(GenBlock *pBlock, sxi32 nJumpType, sxu32 nJumpDest) {
JumpFixup *aFix;
VmInstr *pInstr;
3 years ago
sxu32 nFixed;
sxu32 n;
/* Point to the jump fixup table */
aFix = (JumpFixup *)SySetBasePtr(&pBlock->aJumpFix);
/* Fix the desired jumps */
3 years ago
for(nFixed = n = 0 ; n < SySetUsed(&pBlock->aJumpFix) ; ++n) {
if(aFix[n].nJumpType < 0) {
/* Already fixed */
continue;
}
3 years ago
if(nJumpType > 0 && aFix[n].nJumpType != nJumpType) {
/* Not of our interest */
continue;
}
/* Point to the instruction to fix */
3 years ago
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.
*/
3 years ago
static sxi32 GenStateFindLiteral(ph7_gen_state *pGen, const SyString *pValue, sxu32 *pIdx) {
SyHashEntry *pEntry;
3 years ago
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.
*/
3 years ago
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.
*/
3 years ago
static ph7_value *GenStateInstallNumLiteral(ph7_gen_state *pGen, sxu32 *pIdx) {
ph7_value *pObj;
sxu32 nIdx = 0; /* cc warning */
/* Reserve a new constant */
3 years ago
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;
3 years ago
/* 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 */
3 years ago
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)
3 years ago
* 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.
*/
3 years ago
static sxi32 PH7_CompileNumLiteral(ph7_gen_state *pGen, sxi32 iCompileFlag) {
SyToken *pToken = pGen->pIn; /* Raw token */
sxu32 nIdx = 0;
3 years ago
if(pToken->nType & PH7_TK_INTEGER) {
ph7_value *pObj;
sxi64 iValue;
iValue = PH7_TokenValueToInt64(&pToken->sData);
3 years ago
pObj = GenStateInstallNumLiteral(&(*pGen), &nIdx);
if(pObj == 0) {
SXUNUSED(iCompileFlag); /* cc warning */
return SXERR_ABORT;
}
3 years ago
PH7_MemObjInitFromInt(pGen->pVm, pObj, iValue);
} else {
/* Real number */
ph7_value *pObj;
/* Reserve a new constant */
3 years ago
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;
}
3 years ago
PH7_MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
PH7_MemObjToReal(pObj);
}
/* Emit the load constant instruction */
3 years ago
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
3 years ago
* 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.
3 years ago
*
*/
3 years ago
PH7_PRIVATE sxi32 PH7_CompileSimpleString(ph7_gen_state *pGen, sxi32 iCompileFlag) {
SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
3 years ago
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];
3 years ago
if(SXRET_OK == GenStateFindLiteral(&(*pGen), pStr, &nIdx)) {
/* Already processed,emit the load constant instruction
* and return.
*/
3 years ago
PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, nIdx, 0, 0);
return SXRET_OK;
}
/* Reserve a new constant */
3 years ago
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;
}
3 years ago
PH7_MemObjInitFromString(pGen->pVm, pObj, 0);
/* Compile the node */
3 years ago
for(;;) {
if(zIn >= zEnd) {
/* End of input */
break;
}
zCur = zIn;
3 years ago
while(zIn < zEnd && zIn[0] != '\\') {
zIn++;
}
3 years ago
if(zIn > zCur) {
/* Append raw contents*/
3 years ago
PH7_MemObjStringAppend(pObj, zCur, (sxu32)(zIn - zCur));
}
zIn++;
3 years ago
if(zIn < zEnd) {
if(zIn[0] == '\\') {
/* A literal backslash */
3 years ago
PH7_MemObjStringAppend(pObj, "\\", sizeof(char));
} else if(zIn[0] == '\'') {
/* A single quote */
3 years ago
PH7_MemObjStringAppend(pObj, "'", sizeof(char));
} else {
/* verbatim copy */
zIn--;
3 years ago
PH7_MemObjStringAppend(pObj, zIn, sizeof(char) * 2);
zIn++;
}
}
/* Advance the stream cursor */
zIn++;
}
/* Emit the load constant instruction */
3 years ago
PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, nIdx, 0, 0);
if(pStr->nByte < 1024) {
/* Install in the literal table */
3 years ago
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
3 years ago
* as to simple variables.
* Complex (curly) syntax
3 years ago
* 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 */
3 years ago
) {
SyToken *pTmpIn, *pTmpEnd;
SySet sToken;
sxi32 rc;
/* Initialize the token set */
3 years ago
SySetInit(&sToken, &pGen->pVm->sAllocator, sizeof(SyToken));
/* Preallocate some slots */
3 years ago
SySetAlloc(&sToken, 0x08);
/* Tokenize the text */
3 years ago
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 */
3 years ago
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.
*/
3 years ago
static ph7_value *GenStateNewStrObj(ph7_gen_state *pGen, sxi32 *pCount) {
ph7_value *pConstObj;
sxu32 nIdx = 0;
/* Reserve a new constant */
3 years ago
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)++;
3 years ago
PH7_MemObjInitFromString(pGen->pVm, pConstObj, 0);
/* Emit the load constant instruction */
3 years ago
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.
*/
3 years ago
static sxi32 GenStateCompileString(ph7_gen_state *pGen) {
SyString *pStr = &pGen->pIn->sData; /* Raw token value */
3 years ago
const char *zIn, *zCur, *zEnd;
ph7_value *pObj = 0;
3 years ago
sxi32 iCons;
sxi32 rc;
/* Delimit the string */
zIn = pStr->zString;
zEnd = &zIn[pStr->nByte];
3 years ago
if(zIn >= zEnd) {
/* Empty string can be served as single-quoted string */
return PH7_CompileSimpleString(pGen, 0);
}
zCur = 0;
/* Compile the node */
iCons = 0;
3 years ago
for(;;) {
zCur = zIn;
3 years ago
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++;
}
3 years ago
if(zIn > zCur) {
if(pObj == 0) {
pObj = GenStateNewStrObj(&(*pGen), &iCons);
if(pObj == 0) {
return SXERR_ABORT;
}
}
3 years ago
PH7_MemObjStringAppend(pObj, zCur, (sxu32)(zIn - zCur));
}
3 years ago
if(zIn >= zEnd) {
break;
}
3 years ago
if(zIn[0] == '\\') {
const char *zPtr = 0;
sxu32 n;
zIn++;
3 years ago
if(zIn >= zEnd) {
break;
}
3 years ago
if(pObj == 0) {
pObj = GenStateNewStrObj(&(*pGen), &iCons);
if(pObj == 0) {
return SXERR_ABORT;
}
}
n = sizeof(char); /* size of conversion */
3 years ago
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]);
}
3 years ago
/* Output char */
PH7_MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
n += sizeof(char) * 2;
} else {
/* Output literal character */
PH7_MemObjStringAppend(pObj, "x", sizeof(char));
}
3 years ago
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));
}
3 years ago
break;
default:
/* Output without a slash */
PH7_MemObjStringAppend(pObj, zIn, sizeof(char));
break;
}
/* Advance the stream cursor */
zIn += n;
continue;
}
3 years ago
if(zIn[0] == '{') {
/* Curly syntax */
const char *zExpr;
sxi32 iNest = 1;
zIn++;
zExpr = zIn;
/* Synchronize with the next closing curly braces */
3 years ago
while(zIn < zEnd) {
if(zIn[0] == '{') {
/* Increment nesting level */
iNest++;
3 years ago
} else if(zIn[0] == '}') {
/* Decrement nesting level */
iNest--;
3 years ago
if(iNest <= 0) {
break;
}
}
zIn++;
}
/* Process the expression */
3 years ago
rc = GenStateProcessStringExpression(&(*pGen), pGen->pIn->nLine, zExpr, zIn);
if(rc == SXERR_ABORT) {
return SXERR_ABORT;
}
3 years ago
if(rc != SXERR_EMPTY) {
++iCons;
}
3 years ago
if(zIn < zEnd) {
/* Jump the trailing curly */
zIn++;
}
3 years ago
} else {
/* Simple syntax */
const char *zExpr = zIn;
/* Assemble variable name */
3 years ago
for(;;) {
/* Jump leading dollars */
3 years ago
while(zIn < zEnd && zIn[0] == '$') {
zIn++;
}
3 years ago
for(;;) {
while(zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_')) {
zIn++;
}
3 years ago
if((unsigned char)zIn[0] >= 0xc0) {
/* UTF-8 stream */
zIn++;
3 years ago
while(zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80)) {
zIn++;
}
continue;
}
break;
}
3 years ago
if(zIn >= zEnd) {
break;
}
3 years ago
if(zIn[0] == '[') {
sxi32 iSquare = 1;
zIn++;
3 years ago
while(zIn < zEnd) {
if(zIn[0] == '[') {
iSquare++;
3 years ago
} else if(zIn[0] == ']') {
iSquare--;
3 years ago
if(iSquare <= 0) {
break;
}
}
zIn++;
}
3 years ago
if(zIn < zEnd) {
zIn++;
}
break;
3 years ago
} else if(zIn[0] == '{') {
sxi32 iCurly = 1;
zIn++;
3 years ago
while(zIn < zEnd) {
if(zIn[0] == '{') {
iCurly++;
3 years ago
} else if(zIn[0] == '}') {
iCurly--;
3 years ago
if(iCurly <= 0) {
break;
}
}
zIn++;
}
3 years ago
if(zIn < zEnd) {
zIn++;
}
break;
3 years ago
} else if(zIn[0] == '-' && &zIn[1] < zEnd && zIn[1] == '>') {
/* Member access operator '->' */
zIn += 2;
3 years ago
} else if(zIn[0] == ':' && &zIn[1] < zEnd && zIn[1] == ':') {
/* Static member access operator '::' */
zIn += 2;
3 years ago
} else {
break;
}
}
/* Process the expression */
3 years ago
rc = GenStateProcessStringExpression(&(*pGen), pGen->pIn->nLine, zExpr, zIn);
if(rc == SXERR_ABORT) {
return SXERR_ABORT;
}
3 years ago
if(rc != SXERR_EMPTY) {
++iCons;
}
}
/* Invalidate the previously used constant */
pObj = 0;
}/*for(;;)*/
3 years ago
if(iCons > 1) {
/* Concatenate all compiled constants */
3 years ago
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.
*/
3 years ago
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
3 years ago
* 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 */
3 years ago
sxi32(*xValidator)(ph7_gen_state *, ph7_expr_node *) /* Expression tree validator callback */
) {
SyToken *pTmpIn, *pTmpEnd;
sxi32 rc;
/* Swap token stream */
3 years ago
SWAP_DELIMITER(pGen, pIn, pEnd);
/* Compile the expression*/
3 years ago
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 responsible of compiling the array language construct
* for more inforation.
*/
3 years ago
static sxi32 GenStateArrayNodeValidator(ph7_gen_state *pGen, ph7_expr_node *pRoot) {
sxi32 rc = SXRET_OK;
3 years ago
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 */
3 years ago
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;
}
}
3 years ago
} else if(pRoot->xCode != PH7_CompileVariable) {
/* Unexpected expression */
3 years ago
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
3 years ago
* other arrays, trees and multidimensional arrays are also possible.
*/
3 years ago
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 */
3 years ago
for(;;) {
/* Jump leading commas */
3 years ago
while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_COMMA)) {
pGen->pIn++;
}
pCur = pGen->pIn;
3 years ago
if(SXRET_OK != PH7_GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn)) {
/* No more entry to process */
break;
}
3 years ago
if(pCur >= pGen->pIn) {
continue;
}
/* Compile the key if available */
pKey = pCur;
iNest = 0;
3 years ago
while(pCur < pGen->pIn) {
if((pCur->nType & PH7_TK_ARRAY_OP) && iNest <= 0) {
break;
}
3 years ago
if(pCur->nType & PH7_TK_LPAREN /*'('*/) {
iNest++;
3 years ago
} 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;
3 years ago
if(pCur < pGen->pIn) {
if(&pCur[1] >= pGen->pIn) {
/* Missing value */
3 years ago
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 */
3 years ago
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 */
3 years ago
} else if(pKey == pCur) {
/* Key is omitted,emit a warning */
3 years ago
PH7_GenCompileError(&(*pGen), E_WARNING, pCur->nLine, "array(): Missing entry key");
pCur++; /* Jump the '=>' operator */
3 years ago
} else {
/* Reset back the cursor and point to the entry value */
pCur = pKey;
}
3 years ago
if(rc == SXERR_EMPTY) {
/* No available key,load NULL */
3 years ago
PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOADC, 0, 0 /* nil index */, 0, 0);
}
3 years ago
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 */
3 years ago
if(pCur >= pGen->pIn) {
/* Missing value */
3 years ago
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 */
3 years ago
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;
}
3 years ago
if(iEmitRef) {
/* Emit the load reference instruction */
3 years ago
PH7_VmEmitInstr(pGen->pVm, PH7_OP_LOAD_REF, 0, 0, 0, 0);
}
xValidator = 0;
iEmitRef = 0;
nPair++;
}
/* Emit the load map instruction */
3 years ago
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 responsible of compiling the list language construct
* for more inforation.
*/
3 years ago
static sxi32 GenStateListNodeValidator(ph7_gen_state *pGen, ph7_expr_node *pRoot) {
sxi32 rc = SXRET_OK;
3 years ago
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;