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.

5299 lines
190 KiB

/**
* @PROJECT PH7 Engine for the AerScript Interpreter
* @COPYRIGHT See COPYING in the top level directory
* @FILE engine/compiler.c
* @DESCRIPTION PH7 Engine Compiler for the Aer language
* @DEVELOPERS Symisc Systems <devel@symisc.net>
* Rafal Kupiec <belliash@codingworkshop.eu.org>
* Piotr Likoski <likoski@codingworkshop.eu.org>
*/
#include "compiler.h"
/*
* Check if the given name refer to a valid label.
* Return SXRET_OK and write a pointer to that label on success.
* Any other return value indicates no such label.
*/
static sxi32 GenStateGetLabel(ph7_gen_state *pGen, JumpFixup *pJump, Label **ppOut)
{
Label *aLabel;
sxu32 n;
/* Perform a linear scan on the label table */
aLabel = (Label *)SySetBasePtr(&pGen->aLabel);
for(n = 0; n < SySetUsed(&pGen->aLabel); ++n) {
if(SyStringCmp(&aLabel[n].sName, &pJump->sLabel, SyMemcmp) == 0 && aLabel[n].pFunc == pJump->pFunc) {
/* Jump destination found */
aLabel[n].bRef = TRUE;
if(ppOut) {
*ppOut = &aLabel[n];
}
return SXRET_OK;
}
}
/* No such destination */
return SXERR_NOTFOUND;
}
/*
* 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;
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 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 */
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 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 */
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.
*/
PH7_GenCompileError(&(*pGen), E_ERROR, 1, "PH7 engine is running out-of-memory");
}
/* Zero the structure */
3 years ago
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;
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.
*/
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 */
3 years ago
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;
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 */
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 */
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 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;
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;
}
/*
* Fix a 'goto' now the jump destination is resolved.
* The goto statement can be used to jump to another section
* in the program.
* Refer to the routine responsible of compiling the goto
* statement for more information.
*/
static sxi32 GenStateFixGoto(ph7_gen_state *pGen, sxu32 nOfft)
{
JumpFixup *pJump,*aJumps;
Label *pLabel,*aLabel;
VmInstr *pInstr;
sxi32 rc;
sxu32 n;
/* Point to the goto table */
aJumps = (JumpFixup *)SySetBasePtr(&pGen->aGoto);
/* Fix */
for(n = nOfft; n < SySetUsed(&pGen->aGoto); ++n) {
pJump = &aJumps[n];
/* Extract the target label */
rc = GenStateGetLabel(&(*pGen), pJump, &pLabel);
if(rc != SXRET_OK) {
/* No such label */
PH7_GenCompileError(&(*pGen), E_ERROR, pJump->nLine, "Label '%z' was referenced but not defined", &pJump->sLabel);
}
/* Fix the jump now the destination is resolved */
pInstr = PH7_VmGetInstr(pGen->pVm, pJump->nInstrIdx);
if(pInstr) {
pInstr->iP2 = pLabel->nJumpDest;
}
}
return SXRET_OK;
}
/*
* 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;
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.
*/
static sxi32 PH7_GenStateInstallLiteral(ph7_gen_state *pGen, ph7_value *pObj, sxu32 nIdx) {
3 years ago
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 */
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");
}
*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)
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);
pObj = PH7_GenStateInstallNumLiteral(&(*pGen), &nIdx);
3 years ago
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");
}
3 years ago
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
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];
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 */
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");
}
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 */
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOADC, 0, nIdx, 0, 0);
3 years ago
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
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 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 */
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 */
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 */
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.
*/
static ph7_value *PH7_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");
}
(*pCount)++;
3 years ago
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 */
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 = PH7_GenStateNewStrObj(&(*pGen), &iCons);
3 years ago
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 = PH7_GenStateNewStrObj(&(*pGen), &iCons);
3 years ago
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 */
rc = PH7_GenStateProcessStringExpression(&(*pGen), pGen->pIn->nLine, zExpr, zIn);
3 years ago
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 */
rc = PH7_GenStateProcessStringExpression(&(*pGen), pGen->pIn->nLine, zExpr, zIn);
3 years ago
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 */
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.
*/
3 years ago
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
3 years ago
* 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 */
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;
}
/*
* 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
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 nPair = 0;
sxi32 rc;
/* Jump the opening and the trailing parenthesis. */
pGen->pIn++;
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;
3 years ago
while(pCur < pGen->pIn) {
if(pCur->nType & PH7_TK_ARRAY_OP) {
break;
}
pCur++;
}
rc = SXERR_EMPTY;
3 years ago
if(pCur < pGen->pIn) {
if(&pCur[1] >= pGen->pIn) {
/* Missing value */
PH7_GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "array(): Missing entry value");
}
/* Compile the expression holding the key */
rc = PH7_GenStateCompileArrayEntry(&(*pGen), pKey, pCur,
3 years ago
EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if non-existent*/, 0);
3 years ago
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 */
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOADC, 0, 0 /* nil index */, 0, 0);
}
/* Compile indice value */
rc = PH7_GenStateCompileArrayEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if non-existent*/, xValidator);
3 years ago
if(rc == SXERR_ABORT) {
return SXERR_ABORT;
}
xValidator = 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;
}
/*
* Compile a closure (anonymous function).
* Closures (also known as anonymous functions), 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
* $greet = function($name)
* {
* printf("Hello %s\r\n", $name);
* };
* $greet('World');
* $greet('AerScript');
*/
PH7_PRIVATE sxi32 PH7_CompileClosure(ph7_gen_state *pGen, sxi32 iCompileFlag) {
ph7_vm_func *pAnonFunc; /* Anonymous function body */
char zName[512]; /* Unique closure 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;
sxu32 nType;
sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
if(nKey & PH7_KEYWORD_BOOL) {
nType = MEMOBJ_BOOL;
} else if(nKey & PH7_KEYWORD_CALLBACK) {
nType = MEMOBJ_CALL;
} else if(nKey & PH7_KEYWORD_CHAR) {
nType = MEMOBJ_CHAR;
} else if(nKey & PH7_KEYWORD_FLOAT) {
nType = MEMOBJ_REAL;
} else if(nKey & PH7_KEYWORD_INT) {
nType = MEMOBJ_INT;
} else if(nKey & PH7_KEYWORD_MIXED) {
nType = MEMOBJ_MIXED;
} else if(nKey & PH7_KEYWORD_OBJECT) {