|
|
|
/*
|
|
|
|
* 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 "compiler.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 *PH7_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 PH7_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 PH7_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));
|
|
|
|
PH7_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 PH7_GenStateReleaseBlock(GenBlock *pBlock) {
|
|
|
|
SySetRelease(&pBlock->aPostContFix);
|
|
|
|
SySetRelease(&pBlock->aJumpFix);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Release a block.
|
|
|
|
*/
|
|
|
|
static void PH7_GenStateFreeBlock(GenBlock *pBlock) {
|
|
|
|
ph7_gen_state *pGen = pBlock->pGen;
|
|
|
|
PH7_GenStateReleaseBlock(&(*pBlock));
|
|
|
|
/* Free the instance */
|
|
|
|
SyMemBackendPoolFree(&pGen->pVm->sAllocator, pBlock);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* POP and release a block from the stack of compiled blocks.
|
|
|
|
*/
|
|
|
|
static sxi32 PH7_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 */
|
|
|
|
PH7_GenStateFreeBlock(&(*pBlock));
|
|
|
|
}
|
|
|
|
return SXRET_OK;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Emit a forward jump.
|
|
|
|
* Notes on forward jumps
|
|
|
|
* Compilation of some Aer 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 PH7_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 Aer 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 PH7_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 PH7_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 PH7_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 *PH7_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;
|
|
|
|
return pObj;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Compile a numeric [i.e: integer or real] literal.
|
|
|
|
* Notes on the integer type.
|
|
|
|
* 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 = PH7_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, 0, PH7_OP_LOADC, 0, nIdx, 0, 0);
|
|
|
|
/* Node successfully compiled */
|
|
|
|
return SXRET_OK;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Compile a single quoted string.
|
|
|
|
* 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(SXRET_OK == PH7_GenStateFindLiteral(&(*pGen), pStr, &nIdx)) {
|
|
|
|
/* Already processed,emit the load constant instruction
|
|
|
|
* and return.
|
|
|
|
*/
|
|
|
|
PH7_VmEmitInstr(pGen->pVm, 0, 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, 0, PH7_OP_LOADC, 0, nIdx, 0, 0);
|
|
|
|
if(pStr->nByte < 1024) {
|
|
|
|
/* Install in the literal table */
|
|
|
|
PH7_GenStateInstallLiteral(pGen, pObj, nIdx);
|
|
|
|
}
|
|
|
|
/* Node successfully compiled */
|
|
|
|
return SXRET_OK;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Process variable expression [i.e: "$var","${var}"] embedded in a double quoted string.
|
|
|
|
* 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 PH7_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_TokenizeAerScript(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 *PH7_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, 0, PH7_OP_LOADC, 0, nIdx, 0, 0);
|
|
|
|
return pConstObj;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Compile a double quoted string.
|
|
|
|
* Double quoted
|
|
|
|
* If the string is enclosed in double-quotes ("), Aer 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 PH7_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 can be served as single-quoted string */
|
|
|
|
return PH7_CompileSimpleString(pGen, 0);
|
|
|
|
}
|
|
|
|
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 = PH7_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 = PH7_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 = PH7_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 = PH7_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, 0, PH7_OP_ADD, iCons, 1, 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 = PH7_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.
|
|
|
|
* 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 Aer, which can both
|
|
|
|
* contain integer and string indices.
|
|
|
|
* A value can be any Aer 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 PH7_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 responsible of compiling the array language construct
|
|
|
|
* for more inforation.
|
|
|
|
*/
|
|
|
|
static sxi32 PH7_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.
|
|
|
|
* An array in Aer 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 = PH7_GenStateCompileArrayEntry(&(*pGen), pKey, pCur,
|
|
|
|
EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if non-existent*/, 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, 0, PH7_OP_LOADC, 0, 0 /* nil index */, 0, 0);
|
|
|
|
}
|
|
|
|
if(pCur->nType & PH7_TK_AMPER /*'&'*/) {
|
|
|
|
/* Insertion by reference, [i.e: $a = array(&$x);] */
|
|
|
|
xValidator = PH7_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 = PH7_GenStateCompileArrayEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if non-existent*/, xValidator);
|
|
|
|
if(rc == SXERR_ABORT) {
|
|
|
|
return SXERR_ABORT;
|
|
|
|
}
|
|
|
|
if(iEmitRef) {
|
|
|
|
/* Emit the load reference instruction */
|
|
|
|
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOAD_REF, 0, 0, 0, 0);
|
|
|
|
}
|
|
|
|
xValidator = 0;
|
|
|
|
iEmitRef = 0;
|
|
|
|
nPair++;
|
|
|
|
}
|
|
|
|
/* Emit the load map instruction */
|
|
|
|
PH7_VmEmitInstr(pGen->pVm, 0, 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.
|
|
|
|
*/
|
|
|
|
static sxi32 PH7_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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|