|
|
@ -9,6 +9,30 @@ |
|
|
|
*/ |
|
|
|
#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. |
|
|
@ -184,6 +208,39 @@ static sxu32 PH7_GenStateFixJumps(GenBlock *pBlock, sxi32 nJumpType, sxu32 nJump |
|
|
|
/* 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. |
|
|
|
*/ |
|
|
@ -1354,6 +1411,130 @@ static sxi32 PH7_CompileBreak(ph7_gen_state *pGen) { |
|
|
|
/* Statement successfully compiled */ |
|
|
|
return SXRET_OK; |
|
|
|
} |
|
|
|
/* |
|
|
|
* Compile or record a label. |
|
|
|
* A label is a target point that is specified by an identifier followed by a colon. |
|
|
|
* Example |
|
|
|
* goto LABEL; |
|
|
|
* echo 'Foo'; |
|
|
|
* LABEL: |
|
|
|
* echo 'Bar'; |
|
|
|
*/ |
|
|
|
static sxi32 PH7_CompileLabel(ph7_gen_state *pGen) |
|
|
|
{ |
|
|
|
GenBlock *pBlock; |
|
|
|
Label *aLabel; |
|
|
|
Label sLabel; |
|
|
|
/* Make sure the label does not occur inside a loop or a try{}catch(); block */ |
|
|
|
pBlock = PH7_GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP | GEN_BLOCK_EXCEPTION, 0); |
|
|
|
if(pBlock) { |
|
|
|
PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, |
|
|
|
"Label '%z' inside loop or try/catch block is disallowed", &pGen->pIn->sData); |
|
|
|
} else { |
|
|
|
SyString *pTarget = &pGen->pIn->sData; |
|
|
|
char *zDup; |
|
|
|
/* Initialize label fields */ |
|
|
|
sLabel.nJumpDest = PH7_VmInstrLength(pGen->pVm); |
|
|
|
/* Duplicate label name */ |
|
|
|
zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator,pTarget->zString,pTarget->nByte); |
|
|
|
if( zDup == 0 ){ |
|
|
|
PH7_GenCompileError(&(*pGen),E_ERROR,pGen->pIn->nLine,"PH7 is running out-of-memory"); |
|
|
|
} |
|
|
|
SyStringInitFromBuf(&sLabel.sName, zDup, pTarget->nByte); |
|
|
|
sLabel.bRef = FALSE; |
|
|
|
sLabel.nLine = pGen->pIn->nLine; |
|
|
|
pBlock = pGen->pCurrent; |
|
|
|
while(pBlock) { |
|
|
|
if(pBlock->iFlags & (GEN_BLOCK_FUNC | GEN_BLOCK_EXCEPTION)) { |
|
|
|
break; |
|
|
|
} |
|
|
|
/* Point to the upper block */ |
|
|
|
pBlock = pBlock->pParent; |
|
|
|
} |
|
|
|
if(pBlock) { |
|
|
|
sLabel.pFunc = (ph7_vm_func *)pBlock->pUserData; |
|
|
|
} else { |
|
|
|
sLabel.pFunc = 0; |
|
|
|
} |
|
|
|
aLabel = (Label *)SySetBasePtr(&pGen->aLabel); |
|
|
|
for(int n = 0; n < SySetUsed(&pGen->aLabel); ++n) { |
|
|
|
if(aLabel[n].pFunc == sLabel.pFunc && SyStringCmp(&aLabel[n].sName, &sLabel.sName, SyMemcmp) == 0) { |
|
|
|
PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Duplicate label '%z'", &sLabel.sName); |
|
|
|
} |
|
|
|
} |
|
|
|
/* Insert in label set */ |
|
|
|
SySetPut(&pGen->aLabel, (const void *)&sLabel); |
|
|
|
} |
|
|
|
pGen->pIn += 2; /* Jump the label name and the semi-colon*/ |
|
|
|
return SXRET_OK; |
|
|
|
} |
|
|
|
/* |
|
|
|
* Compile the so hated 'goto' statement. |
|
|
|
* You've probably been taught that gotos are bad, but this sort |
|
|
|
* of rewriting happens all the time, in fact every time you run |
|
|
|
* a compiler it has to do this. |
|
|
|
* According to the PHP language reference manual |
|
|
|
* The goto operator can be used to jump to another section in the program. |
|
|
|
* The target point is specified by a label followed by a colon, and the instruction |
|
|
|
* is given as goto followed by the desired target label. This is not a full unrestricted goto. |
|
|
|
* The target label must be within the same file and context, meaning that you cannot jump out |
|
|
|
* of a function or method, nor can you jump into one. You also cannot jump into any sort of loop |
|
|
|
* or switch structure. You may jump out of these, and a common use is to use a goto in place |
|
|
|
* of a multi-level break |
|
|
|
*/ |
|
|
|
|
|
|
|
static sxi32 PH7_CompileGoto(ph7_gen_state *pGen) |
|
|
|
{ |
|
|
|
JumpFixup sJump; |
|
|
|
sxi32 rc; |
|
|
|
pGen->pIn++; /* Jump the 'goto' keyword */ |
|
|
|
if(pGen->pIn >= pGen->pEnd) { |
|
|
|
/* Missing label */ |
|
|
|
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "goto: expecting a 'label_name'"); |
|
|
|
} |
|
|
|
if((pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_ID)) == 0) { |
|
|
|
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "goto: Invalid label name: '%z'", &pGen->pIn->sData); |
|
|
|
} else { |
|
|
|
SyString *pTarget = &pGen->pIn->sData; |
|
|
|
GenBlock *pBlock; |
|
|
|
char *zDup; |
|
|
|
/* Prepare the jump destination */ |
|
|
|
sJump.nJumpType = PH7_OP_JMP; |
|
|
|
sJump.nLine = pGen->pIn->nLine; |
|
|
|
/* Duplicate label name */ |
|
|
|
zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pTarget->zString, pTarget->nByte); |
|
|
|
if(zDup == 0) { |
|
|
|
PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "PH7 is running out-of-memory"); |
|
|
|
} |
|
|
|
SyStringInitFromBuf(&sJump.sLabel, zDup, pTarget->nByte); |
|
|
|
pBlock = pGen->pCurrent; |
|
|
|
while(pBlock) { |
|
|
|
if(pBlock->iFlags & (GEN_BLOCK_FUNC | GEN_BLOCK_EXCEPTION)) { |
|
|
|
break; |
|
|
|
} |
|
|
|
/* Point to the upper block */ |
|
|
|
pBlock = pBlock->pParent; |
|
|
|
} |
|
|
|
if(pBlock && pBlock->iFlags & GEN_BLOCK_EXCEPTION) { |
|
|
|
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "goto inside try/catch block is disallowed"); |
|
|
|
} |
|
|
|
if(pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC)) { |
|
|
|
sJump.pFunc = (ph7_vm_func *)pBlock->pUserData; |
|
|
|
} else { |
|
|
|
sJump.pFunc = 0; |
|
|
|
} |
|
|
|
/* Emit the unconditional jump */ |
|
|
|
if(SXRET_OK == PH7_VmEmitInstr(pGen->pVm, sJump.nLine, PH7_OP_JMP, 0, 0, 0, &sJump.nInstrIdx)) { |
|
|
|
SySetPut(&pGen->aGoto, (const void *)&sJump); |
|
|
|
} |
|
|
|
} |
|
|
|
pGen->pIn++; /* Jump the label name */ |
|
|
|
if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) { |
|
|
|
PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Expected semi-colon ';' after 'goto' statement"); |
|
|
|
} |
|
|
|
/* Statement successfully compiled */ |
|
|
|
return SXRET_OK; |
|
|
|
} |
|
|
|
/* |
|
|
|
* Point to the next AerScript chunk that will be processed shortly. |
|
|
|
* Return SXRET_OK on success. Any other return value indicates |
|
|
@ -2723,12 +2904,14 @@ static sxi32 PH7_GenStateCompileFuncBody( |
|
|
|
) { |
|
|
|
SySet *pInstrContainer; /* Instruction container */ |
|
|
|
GenBlock *pBlock; |
|
|
|
sxu32 nGotoOfft; |
|
|
|
sxi32 rc; |
|
|
|
/* Attach the new function */ |
|
|
|
rc = PH7_GenStateEnterBlock(&(*pGen), GEN_BLOCK_PROTECTED | GEN_BLOCK_FUNC, PH7_VmInstrLength(pGen->pVm), pFunc, &pBlock); |
|
|
|
if(rc != SXRET_OK) { |
|
|
|
PH7_GenCompileError(&(*pGen), E_ERROR, 1, "PH7 engine is running out-of-memory"); |
|
|
|
} |
|
|
|
nGotoOfft = SySetUsed(&pGen->aGoto); |
|
|
|
/* Swap bytecode containers */ |
|
|
|
pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); |
|
|
|
PH7_VmSetByteCodeContainer(pGen->pVm, &pFunc->aByteCode); |
|
|
@ -2738,6 +2921,11 @@ static sxi32 PH7_GenStateCompileFuncBody( |
|
|
|
PH7_GenStateFixJumps(pGen->pCurrent, PH7_OP_THROW, PH7_VmInstrLength(pGen->pVm)); |
|
|
|
/* Emit the final return if not yet done */ |
|
|
|
PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DONE, 0, 0, 0, 0); |
|
|
|
/* Fix gotos jumps now the destination is resolved */ |
|
|
|
if(SXERR_ABORT == GenStateFixGoto(&(*pGen), nGotoOfft)) { |
|
|
|
rc = SXERR_ABORT; |
|
|
|
} |
|
|
|
SySetTruncate(&pGen->aGoto,nGotoOfft); |
|
|
|
/* Restore the default container */ |
|
|
|
PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer); |
|
|
|
/* Leave function block */ |
|
|
@ -4607,6 +4795,7 @@ static const LangConstruct aLangConstruct[] = { |
|
|
|
{ PH7_KEYWORD_EXIT, PH7_CompileHalt }, /* exit language construct */ |
|
|
|
{ PH7_KEYWORD_TRY, PH7_CompileTry }, /* try statement */ |
|
|
|
{ PH7_KEYWORD_THROW, PH7_CompileThrow }, /* throw statement */ |
|
|
|
{ PH7_KEYWORD_GOTO, PH7_CompileGoto }, /* goto statement */ |
|
|
|
{ PH7_KEYWORD_CONST, PH7_CompileConstant }, /* const statement */ |
|
|
|
}; |
|
|
|
/* |
|
|
@ -4716,6 +4905,9 @@ static sxi32 PH7_GenStateCompileChunk( |
|
|
|
"Syntax error: Unexpected keyword '%z'", |
|
|
|
&pGen->pIn->sData); |
|
|
|
} |
|
|
|
} else if((pGen->pIn->nType & PH7_TK_ID) && (&pGen->pIn[1] < pGen->pEnd) && (pGen->pIn[1].nType & PH7_TK_COLON /*':'*/)) { |
|
|
|
/* Label found [i.e: Out: ],point to the routine responsible of compiling it */ |
|
|
|
xCons = PH7_CompileLabel; |
|
|
|
} |
|
|
|
if(xCons == 0) { |
|
|
|
/* Assume an expression an try to compile it */ |
|
|
@ -4827,6 +5019,13 @@ static sxi32 PH7_CompileScript( |
|
|
|
} |
|
|
|
/* Fix exceptions jumps */ |
|
|
|
PH7_GenStateFixJumps(pGen->pCurrent, PH7_OP_THROW, PH7_VmInstrLength(pGen->pVm)); |
|
|
|
/* Fix gotos now, the jump destination is resolved */ |
|
|
|
if(SXERR_ABORT == GenStateFixGoto(&(*pGen), 0)) { |
|
|
|
rc = SXERR_ABORT; |
|
|
|
} |
|
|
|
/* Reset container */ |
|
|
|
SySetReset(&pGen->aGoto); |
|
|
|
SySetReset(&pGen->aLabel); |
|
|
|
/* Compilation result */ |
|
|
|
return rc; |
|
|
|
} |
|
|
@ -4901,6 +5100,8 @@ PH7_PRIVATE sxi32 PH7_InitCodeGenerator( |
|
|
|
pGen->pVm = &(*pVm); |
|
|
|
pGen->xErr = xErr; |
|
|
|
pGen->pErrData = pErrData; |
|
|
|
SySetInit(&pGen->aLabel, &pVm->sAllocator, sizeof(Label)); |
|
|
|
SySetInit(&pGen->aGoto, &pVm->sAllocator, sizeof(JumpFixup)); |
|
|
|
SyHashInit(&pGen->hLiteral, &pVm->sAllocator, 0, 0); |
|
|
|
SyHashInit(&pGen->hVar, &pVm->sAllocator, 0, 0); |
|
|
|
/* Error log buffer */ |
|
|
@ -4924,6 +5125,8 @@ PH7_PRIVATE sxi32 PH7_ResetCodeGenerator( |
|
|
|
ph7_gen_state *pGen = &pVm->sCodeGen; |
|
|
|
GenBlock *pBlock, *pParent; |
|
|
|
/* Reset state */ |
|
|
|
SySetReset(&pGen->aLabel); |
|
|
|
SySetReset(&pGen->aGoto); |
|
|
|
SyBlobRelease(&pGen->sErrBuf); |
|
|
|
SyBlobRelease(&pGen->sWorker); |
|
|
|
/* Point to the global scope */ |
|
|
|