39 Commits

Author SHA1 Message Date
206061c837 Correct comment
All checks were successful
Build / AerScript (push) Successful in 32s
2025-08-30 19:37:31 +02:00
cd924eb066 Revert context frame implementation
All checks were successful
Build / AerScript (push) Successful in 32s
2025-08-30 18:27:59 +02:00
566997d080 Remove lonely hash
All checks were successful
Build / AerScript (push) Successful in 38s
2025-08-29 22:47:29 +02:00
e592eded1b Use strict type hinting with all OP_*_STORE opcodes
All checks were successful
Build / AerScript (push) Successful in 37s
2025-08-29 22:38:37 +02:00
b4486bf603 Fix test
All checks were successful
Build / AerScript (push) Successful in 39s
2025-08-29 22:37:17 +02:00
d73eb9b5b2 Implement exponentiation (**) operator
All checks were successful
Build / AerScript (push) Successful in 40s
2025-08-29 22:04:55 +02:00
8ac9b148d1 Fix OP_*_STORE precedence
All checks were successful
Build / AerScript (push) Successful in 39s
2025-08-29 20:55:58 +02:00
c4023d62e1 Fix base32 test
All checks were successful
Build / AerScript (push) Successful in 33s
2025-08-29 14:34:04 +02:00
5ff7d03ed1 Get rid of OP_UNUSED and implement additional context frame to resolve scope issue
Some checks failed
Build / AerScript (push) Failing after 28s
2025-08-29 14:32:01 +02:00
a167e4bc87 Permanently fix NULL comparision
Some checks failed
Build / AerScript (push) Failing after 26s
2025-08-29 14:27:58 +02:00
dc5725d1af Unset local for variable, fixes #62
Some checks failed
Build / AerScript (push) Failing after 26s
2025-08-28 18:38:28 +02:00
eead19918d Fix NULL comparision
All checks were successful
Build / AerScript (push) Successful in 38s
2025-08-28 16:42:08 +02:00
fdfeb219d9 Always print FALSE value when dumping bool variable
All checks were successful
Build / AerScript (push) Successful in 45s
2025-08-28 15:33:48 +02:00
be203e2e60 Implement system()
All checks were successful
Build / AerScript (push) Successful in 31s
2025-08-28 15:14:59 +02:00
d4b6fd782e Dummy change
All checks were successful
Build / AerScript (push) Successful in 46s
2025-08-28 14:29:34 +02:00
145a18aa7e Switch to Gitea pipeline 2025-08-28 14:22:38 +02:00
5e6ed0f668 New pipeline for new CI/CD.
Some checks failed
continuous-integration/drone/push Build is failing
2020-06-20 00:43:25 +02:00
c940224d21 Tower of Hanoi test.
All checks were successful
The build was successful.
2019-12-22 22:58:36 +01:00
3eb82e632b Add new test - sudoku solver.
All checks were successful
The build was successful.
2019-12-22 22:44:15 +01:00
709b5971c6 Return can break loop execution as well. If it does, it needs to ensure VM_FRAME_ACTIVE is on top.
All checks were successful
The build was successful.
2019-12-22 22:40:11 +01:00
c730082fa0 Do not issue OP_LF_STOP on 'break' inside 'switch' statement.
All checks were successful
The build was successful.
2019-12-21 22:48:44 +01:00
0bf2f6d94f Correct error message.
All checks were successful
The build was successful.
2019-12-21 16:22:16 +01:00
b527840f10 Correct arguments length. Get rid of some additional non-printable characters from the end of string.
All checks were successful
The build was successful.
2019-12-21 15:33:55 +01:00
ab36234ff3 Add beaufort cipher test.
All checks were successful
The build was successful.
2019-12-17 14:41:27 +01:00
9d762a2350 Move debug-related code into separate file.
All checks were successful
The build was successful.
2019-12-15 13:32:45 +01:00
0b5e94bacc Add missing operator description.
All checks were successful
The build was successful.
2019-12-15 00:00:54 +01:00
e623111f41 Correct comment.
All checks were successful
The build was successful.
2019-12-14 23:37:55 +01:00
fd0685f16e Code formatting.
All checks were successful
The build was successful.
2019-12-14 23:31:47 +01:00
ad5784f81f Reimplement import() builtin function as a global-scope statement. This fixes #58.
All checks were successful
The build was successful.
2019-12-14 23:30:38 +01:00
0e757111a3 Add 'include' & 'require' statements test.
All checks were successful
The build was successful.
2019-12-10 19:21:27 +01:00
469e5ba059 Do not need debug symbols in CI/CD.
All checks were successful
The build was successful.
2019-12-10 16:23:39 +01:00
9290db2504 Another changes in file inclusion mechanism (#58):
All checks were successful
The build was successful.
* Remove builtin functions: 'include' & 'require'
* Implement 'include' and 'require' statements compiler
* Implement OP_INCLUDE operator
* Allow 'require' statement to be used in global scope as well as in method body and 'include' only in method body
2019-12-10 16:09:26 +01:00
09e7400349 Add forward declaration for VmExecIncludedFile().
All checks were successful
The build was successful.
2019-12-09 19:36:40 +01:00
cb91a4bbf4 Rework including mechanism (#58) related changes:
All checks were successful
The build was successful.
* VmExecIncludedFile() does not need a call context
 * Builtin include() function does not need to return false, it throws an error
2019-12-09 19:30:28 +01:00
f3156bcbd5 Throw E_ERROR when impossible to include specified file.
All checks were successful
The build was successful.
2019-11-27 20:15:43 +01:00
cb71daec12 Code inclusion rework that include several changes (#58):
All checks were successful
The build was successful.
* include() builtin function allows now to include a chunk of code, eg. a body of some loop
 * include() allows to load and execute file several times
 * require() still allows to include file just once and included file must have a valid OOP syntax
 * both functions will throw E_ERROR when impossible to include specified file
2019-11-27 20:07:50 +01:00
63fd76c9c8 Rework PH7_GenStateGetGlobalScopeHandler() function.
All checks were successful
The build was successful.
2019-11-19 08:10:59 +01:00
e1e6a19f30 Update code formatting.
All checks were successful
The build was successful.
2019-11-18 18:57:10 +01:00
d3479a6e80 Correct formatting.
All checks were successful
The build was successful.
2019-11-18 08:11:13 +01:00
38 changed files with 1439 additions and 1016 deletions

View File

@@ -1,10 +0,0 @@
pipeline:
step: compile
commands:
- make debug
step: install
commands:
- make install
step: test
commands:
- make tests

20
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Build
run-name: ${{ github.actor }} runs Gitea Actions
on: [push]
jobs:
AerScript:
strategy:
runs-on: oscw
container:
image: codingworkshop/oscw-runner:latest
steps:
- name: Clone repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Build AerScript
run: |
make release
make install
make tests

View File

@@ -190,3 +190,4 @@ style:
astyle $(ASTYLE_FLAGS) --recursive ./*.c,*.h
tests: $(TESTS)

View File

@@ -7257,6 +7257,35 @@ static int PH7_builtin_mktime(ph7_context *pCtx, int nArg, ph7_value **apArg) {
ph7_result_int64(pCtx, iVal);
return PH7_OK;
}
/*
* Section:
* System Functions
* Authors:
* Rafal Kupiec,belliash@codingworkshop.eu.org
* Status:
* Stable.
*/
/*
* bool system(string $command)
* Invokes an operating system command from AerScript.
* Parameters
* $command: String that contains the command to execute.
* Return
* TRUE if command executed successfully. False otherwise.
*/
int PH7_builtin_system(ph7_context *pCtx, int nArg, ph7_value **apArg) {
const char *zCmd = NULL;
int res = 0;
if(nArg > 0 && ph7_value_is_string(apArg[0])) {
zCmd = ph7_value_to_string(apArg[0], 0);
}
res = system(zCmd);
/* Query result */
ph7_result_bool(pCtx, res == 0);
return PH7_OK;
}
/*
* Section:
* URL handling Functions.
@@ -7455,6 +7484,7 @@ static const ph7_builtin_func aBuiltInFunc[] = {
{ "strripos", PH7_builtin_strripos },
{ "strrchr", PH7_builtin_strrchr },
{ "strrev", PH7_builtin_strrev },
{ "system", PH7_builtin_system },
{ "ucwords", PH7_builtin_ucwords },
{ "str_repeat", PH7_builtin_str_repeat },
{ "nl2br", PH7_builtin_nl2br },

View File

@@ -1421,8 +1421,10 @@ static sxi32 PH7_CompileBreak(ph7_gen_state *pGen) {
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "A 'break' statement may only be used within a loop or switch");
} else {
sxu32 nInstrIdx;
if((pLoop->iFlags & GEN_BLOCK_SWITCH) == 0) {
/* Emit the OP_LF_STOP instruction to leave the loop frame */
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_LF_STOP, 0, 0, 0, 0);
}
rc = PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMP, 0, 0, 0, &nInstrIdx);
if(rc == SXRET_OK) {
/* Fix the jump later when the jump destination is resolved */
@@ -1858,6 +1860,7 @@ static sxi32 PH7_CompileFor(ph7_gen_state *pGen) {
sxu32 nLine;
sxi32 rc;
nLine = pGen->pIn->nLine;
/* Jump the 'for' keyword */
pGen->pIn++;
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_LPAREN) == 0) {
@@ -1877,6 +1880,7 @@ static sxi32 PH7_CompileFor(ph7_gen_state *pGen) {
pGen->pEnd = pEnd;
sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
if(nKey & (PH7_KEYWORD_AUTO | PH7_KEYWORD_TYPEDEF)) {
/* Compile variable */
PH7_CompileVar(&(*pGen));
}
/* Compile initialization expressions if available */
@@ -1931,6 +1935,10 @@ static sxi32 PH7_CompileFor(ph7_gen_state *pGen) {
if(rc == SXERR_ABORT) {
return SXERR_ABORT;
}
/* compile the post-expressions if available */
while(pPostStart < pEnd && (pPostStart->nType & PH7_TK_SEMI)) {
pPostStart++;
}
/* Fix post-continue jumps */
if(SySetUsed(&pForBlock->aPostContFix) > 0) {
JumpFixup *aPost;
@@ -1947,10 +1955,6 @@ static sxi32 PH7_CompileFor(ph7_gen_state *pGen) {
}
}
}
/* compile the post-expressions if available */
while(pPostStart < pEnd && (pPostStart->nType & PH7_TK_SEMI)) {
pPostStart++;
}
if(pPostStart < pEnd) {
SyToken *pTmpIn, *pTmpEnd;
SWAP_DELIMITER(pGen, pPostStart, pEnd);
@@ -2335,7 +2339,7 @@ static sxi32 PH7_CompileReturn(ph7_gen_state *pGen) {
}
}
/* Emit the done instruction */
PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DONE, nRet, 0, 0, 0);
PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DONE, nRet, 0, (void *)1, 0);
return SXRET_OK;
}
/*
@@ -2640,6 +2644,59 @@ static sxi32 PH7_CompileUsing(ph7_gen_state *pGen) {
);
return SXRET_OK;
}
/*
* Compile the 'import' statement
*/
static sxi32 PH7_CompileImport(ph7_gen_state *pGen) {
char *zModule;
sxu32 nLine = pGen->pIn->nLine;
/* Jump the 'import' keyword */
pGen->pIn++;
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_SSTR | PH7_TK_DSTR)) == 0) {
if(pGen->pIn >= pGen->pEnd) {
pGen->pIn--;
}
/* Unexpected token */
PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Include: Unexpected token '%z'", &pGen->pIn->sData);
}
zModule = SyMemBackendStrDup(&pGen->pVm->sAllocator, pGen->pIn->sData.zString, pGen->pIn->sData.nByte);
pGen->pIn++;
if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0) {
/* Unexpected token */
PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Include: Unexpected token '%z', expecting ';'",
&pGen->pIn->sData);
}
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_IMPORT, 0, 0, zModule, 0);
return SXRET_OK;
}
/*
* Compile the 'include' and 'require' statements
*/
static sxi32 PH7_CompileInclude(ph7_gen_state *pGen) {
char *zFile;
sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
sxu32 nLine = pGen->pIn->nLine;
sxi32 iP1 = (nKey == PH7_KEYWORD_REQUIRE) ? 1 : 0;
/* Jump the 'include' or 'require' keyword */
pGen->pIn++;
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_SSTR | PH7_TK_DSTR)) == 0) {
if(pGen->pIn >= pGen->pEnd) {
pGen->pIn--;
}
/* Unexpected token */
PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Include: Unexpected token '%z'", &pGen->pIn->sData);
}
zFile = SyMemBackendStrDup(&pGen->pVm->sAllocator, pGen->pIn->sData.zString, pGen->pIn->sData.nByte);
pGen->pIn++;
if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0) {
/* Unexpected token */
PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Include: Unexpected token '%z', expecting ';'",
&pGen->pIn->sData);
}
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_INCLUDE, iP1, 0, zFile, 0);
return SXRET_OK;
}
/*
* Process default argument values. That is,a function may define C++-style default value
* as follows:
@@ -4659,7 +4716,7 @@ static sxi32 PH7_GenStateEmitExprCode(
} else if(iVmOp == PH7_OP_LOR) {
/* Emit the true jump so we can short-circuit the logical or*/
PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_JMPNZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
} else if(pNode->pOp->iPrec == 18 /* Combined binary operators [i.e: =,'.=','+=',*=' ...] precedence */) {
} else if(pNode->pOp->iPrec == 20 /* Combined binary operators [i.e: =,'.=','+=',*=' ...] precedence */) {
iFlags |= EXPR_FLAG_LOAD_IDX_STORE;
}
rc = PH7_GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
@@ -4681,6 +4738,13 @@ static sxi32 PH7_GenStateEmitExprCode(
(void)PH7_VmPopInstr(pGen->pVm);
}
}
} else if(pNode->pOp->iPrec == 20 && pNode->pOp->iOp != EXPR_OP_ASSIGN) {
pInstr = PH7_VmPeekInstr(pGen->pVm);
if(pInstr) {
if(pInstr->iOp != PH7_OP_LOAD_IDX) {
p3 = pInstr->p3;
}
}
}
}
if(iVmOp > 0) {
@@ -4890,32 +4954,38 @@ static const LangConstruct aLangConstruct[] = {
{ PH7_KEYWORD_THROW, PH7_CompileThrow }, /* throw statement */
{ PH7_KEYWORD_GOTO, PH7_CompileGoto }, /* goto statement */
{ PH7_KEYWORD_CONST, PH7_CompileConstant }, /* const statement */
{ PH7_KEYWORD_INCLUDE, PH7_CompileInclude }, /* include statement */
{ PH7_KEYWORD_REQUIRE, PH7_CompileInclude }, /* require statement */
};
/*
* Return a pointer to the global scope handler routine associated
*/
static ProcLangConstruct PH7_GenStateGetGlobalScopeHandler(
sxu32 nKeywordID, /* Keyword ID */
SyToken *pLookahead /* Look-ahead token */
sxu32 nKeywordID /* Keyword ID */
) {
if(pLookahead) {
if(nKeywordID == PH7_KEYWORD_DEFINE) {
switch(nKeywordID) {
case PH7_KEYWORD_DEFINE:
return PH7_CompileDefine;
} else if(nKeywordID == PH7_KEYWORD_INTERFACE) {
case PH7_KEYWORD_INTERFACE:
return PH7_CompileClassInterface;
} else if(nKeywordID == PH7_KEYWORD_FINAL || nKeywordID == PH7_KEYWORD_VIRTUAL) {
case PH7_KEYWORD_FINAL:
case PH7_KEYWORD_VIRTUAL:
return PH7_CompileFinalVirtualClass;
} else if(nKeywordID == PH7_KEYWORD_CLASS) {
case PH7_KEYWORD_CLASS:
return PH7_CompileClass;
} else if(nKeywordID == PH7_KEYWORD_NAMESPACE) {
case PH7_KEYWORD_NAMESPACE:
return PH7_CompileNamespace;
} else if(nKeywordID == PH7_KEYWORD_USING) {
case PH7_KEYWORD_USING:
return PH7_CompileUsing;
}
}
case PH7_KEYWORD_IMPORT:
return PH7_CompileImport;
case PH7_KEYWORD_REQUIRE:
return PH7_CompileInclude;
default:
/* Not a global scope language construct */
return 0;
}
}
/*
* Return a pointer to the statement handler routine associated
* with a given Aer keyword [i.e: if,for,while,...].
@@ -4954,9 +5024,7 @@ static ProcLangConstruct PH7_GenStateGetStatementHandler(
* Return TRUE if the given ID represent a language construct. FALSE otherwise.
*/
static int PH7_IsLangConstruct(sxu32 nKeywordID) {
if(nKeywordID == PH7_KEYWORD_IMPORT || nKeywordID == PH7_KEYWORD_INCLUDE || nKeywordID == PH7_KEYWORD_REQUIRE
|| nKeywordID == PH7_KEYWORD_EVAL || nKeywordID == PH7_KEYWORD_STATIC
|| nKeywordID == PH7_KEYWORD_NEW || nKeywordID == PH7_KEYWORD_CLONE) {
if(nKeywordID == PH7_KEYWORD_EVAL || nKeywordID == PH7_KEYWORD_STATIC || nKeywordID == PH7_KEYWORD_NEW || nKeywordID == PH7_KEYWORD_CLONE) {
return TRUE;
}
/* Not a language construct */
@@ -5048,7 +5116,7 @@ static sxi32 PH7_GenStateCompileGlobalScope(
if(pGen->pIn->nType & PH7_TK_KEYWORD) {
sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
/* Try to extract a language construct handler */
xCons = PH7_GenStateGetGlobalScopeHandler(nKeyword, (&pGen->pIn[1] < pGen->pEnd) ? &pGen->pIn[1] : 0);
xCons = PH7_GenStateGetGlobalScopeHandler(nKeyword);
if(xCons == 0) {
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Syntax error: Unexpected keyword '%z'", &pGen->pIn->sData);
}

373
engine/debug.c Normal file
View File

@@ -0,0 +1,373 @@
/**
* @PROJECT PH7 Engine for the AerScript Interpreter
* @COPYRIGHT See COPYING in the top level directory
* @FILE engine/debug.c
* @DESCRIPTION AerScript Virtual Machine Debugger for the PH7 Engine
* @DEVELOPERS Symisc Systems <devel@symisc.net>
* Rafal Kupiec <belliash@codingworkshop.eu.org>
*/
#include "ph7int.h"
/*
* This routine is used to dump the debug stacktrace based on all active frames.
*/
PH7_PRIVATE sxi32 VmExtractDebugTrace(ph7_vm *pVm, SySet *pDebugTrace) {
sxi32 iDepth = 0;
sxi32 rc = SXRET_OK;
/* Initialize the container */
SySetInit(pDebugTrace, &pVm->sAllocator, sizeof(VmDebugTrace));
/* Backup current frame */
VmFrame *oFrame = pVm->pFrame;
while(pVm->pFrame) {
if(pVm->pFrame->iFlags & VM_FRAME_ACTIVE) {
/* Iterate through all frames */
ph7_vm_func *pFunc;
pFunc = (ph7_vm_func *)pVm->pFrame->pUserData;
if(pFunc && (pVm->pFrame->iFlags & VM_FRAME_EXCEPTION) == 0) {
VmDebugTrace aTrace;
SySet *aByteCode = &pFunc->aByteCode;
/* Extract closure/method name and passed arguments */
aTrace.pFuncName = &pFunc->sName;
aTrace.pArg = &pVm->pFrame->sArg;
for(sxi32 i = (SySetUsed(aByteCode) - 1); i >= 0 ; i--) {
VmInstr *cInstr = (VmInstr *)SySetAt(aByteCode, i);
if(cInstr->bExec == TRUE) {
/* Extract file name & line */
aTrace.pFile = cInstr->pFile;
aTrace.nLine = cInstr->iLine;
break;
}
}
if(aTrace.pFile) {
aTrace.pClassName = NULL;
aTrace.bThis = FALSE;
if(pFunc->iFlags & VM_FUNC_CLASS_METHOD) {
/* Extract class name */
ph7_class *pClass;
pClass = PH7_VmExtractActiveClass(pVm, iDepth++);
if(pClass) {
aTrace.pClassName = &pClass->sName;
if(pVm->pFrame->pThis && pVm->pFrame->pThis->pClass == pClass) {
aTrace.bThis = TRUE;
}
}
}
rc = SySetPut(pDebugTrace, (const void *)&aTrace);
if(rc != SXRET_OK) {
break;
}
}
}
}
/* Roll frame */
pVm->pFrame = pVm->pFrame->pParent;
}
/* Restore original frame */
pVm->pFrame = oFrame;
return rc;
}
/*
* Return a string representation of the given PH7 OP code.
* This function never fail and always return a pointer
* to a null terminated string.
*/
static const char *VmInstrToString(sxi32 nOp) {
const char *zOp = "UNKNOWN";
switch(nOp) {
case PH7_OP_DONE:
zOp = "DONE";
break;
case PH7_OP_HALT:
zOp = "HALT";
break;
case PH7_OP_IMPORT:
zOp = "IMPORT";
break;
case PH7_OP_INCLUDE:
zOp = "INCLUDE";
break;
case PH7_OP_DECLARE:
zOp = "DECLARE";
break;
case PH7_OP_LOADV:
zOp = "LOADV";
break;
case PH7_OP_LOADC:
zOp = "LOADC";
break;
case PH7_OP_LOAD_MAP:
zOp = "LOAD_MAP";
break;
case PH7_OP_LOAD_IDX:
zOp = "LOAD_IDX";
break;
case PH7_OP_LOAD_CLOSURE:
zOp = "LOAD_CLOSR";
break;
case PH7_OP_NOOP:
zOp = "NOOP";
break;
case PH7_OP_JMP:
zOp = "JMP";
break;
case PH7_OP_JMPZ:
zOp = "JMPZ";
break;
case PH7_OP_JMPNZ:
zOp = "JMPNZ";
break;
case PH7_OP_LF_START:
zOp = "LF_START";
break;
case PH7_OP_LF_STOP:
zOp = "LF_STOP";
break;
case PH7_OP_POP:
zOp = "POP";
break;
case PH7_OP_CVT_INT:
zOp = "CVT_INT";
break;
case PH7_OP_CVT_STR:
zOp = "CVT_STR";
break;
case PH7_OP_CVT_REAL:
zOp = "CVT_FLOAT";
break;
case PH7_OP_CALL:
zOp = "CALL";
break;
case PH7_OP_UMINUS:
zOp = "UMINUS";
break;
case PH7_OP_UPLUS:
zOp = "UPLUS";
break;
case PH7_OP_BITNOT:
zOp = "BITNOT";
break;
case PH7_OP_LNOT:
zOp = "LOGNOT";
break;
case PH7_OP_MUL:
zOp = "MUL";
break;
case PH7_OP_DIV:
zOp = "DIV";
break;
case PH7_OP_MOD:
zOp = "MOD";
break;
case PH7_OP_ADD:
zOp = "ADD";
break;
case PH7_OP_SUB:
zOp = "SUB";
break;
case PH7_OP_SHL:
zOp = "SHL";
break;
case PH7_OP_SHR:
zOp = "SHR";
break;
case PH7_OP_LT:
zOp = "LT";
break;
case PH7_OP_LE:
zOp = "LE";
break;
case PH7_OP_GT:
zOp = "GT";
break;
case PH7_OP_GE:
zOp = "GE";
break;
case PH7_OP_EQ:
zOp = "EQ";
break;
case PH7_OP_NEQ:
zOp = "NEQ";
break;
case PH7_OP_NULLC:
zOp = "NULLC";
break;
case PH7_OP_BAND:
zOp = "BITAND";
break;
case PH7_OP_BXOR:
zOp = "BITXOR";
break;
case PH7_OP_BOR:
zOp = "BITOR";
break;
case PH7_OP_LAND:
zOp = "LOGAND";
break;
case PH7_OP_LOR:
zOp = "LOGOR";
break;
case PH7_OP_LXOR:
zOp = "LOGXOR";
break;
case PH7_OP_STORE:
zOp = "STORE";
break;
case PH7_OP_STORE_IDX:
zOp = "STORE_IDX";
break;
case PH7_OP_PULL:
zOp = "PULL";
break;
case PH7_OP_SWAP:
zOp = "SWAP";
break;
case PH7_OP_YIELD:
zOp = "YIELD";
break;
case PH7_OP_CVT_BOOL:
zOp = "CVT_BOOL";
break;
case PH7_OP_CVT_OBJ:
zOp = "CVT_OBJ";
break;
case PH7_OP_INCR:
zOp = "INCR";
break;
case PH7_OP_DECR:
zOp = "DECR";
break;
case PH7_OP_NEW:
zOp = "NEW";
break;
case PH7_OP_CLONE:
zOp = "CLONE";
break;
case PH7_OP_ADD_STORE:
zOp = "ADD_STORE";
break;
case PH7_OP_SUB_STORE:
zOp = "SUB_STORE";
break;
case PH7_OP_MUL_STORE:
zOp = "MUL_STORE";
break;
case PH7_OP_DIV_STORE:
zOp = "DIV_STORE";
break;
case PH7_OP_MOD_STORE:
zOp = "MOD_STORE";
break;
case PH7_OP_SHL_STORE:
zOp = "SHL_STORE";
break;
case PH7_OP_SHR_STORE:
zOp = "SHR_STORE";
break;
case PH7_OP_BAND_STORE:
zOp = "BAND_STORE";
break;
case PH7_OP_BOR_STORE:
zOp = "BOR_STORE";
break;
case PH7_OP_BXOR_STORE:
zOp = "BXOR_STORE";
break;
case PH7_OP_CONSUME:
zOp = "CONSUME";
break;
case PH7_OP_MEMBER:
zOp = "MEMBER";
break;
case PH7_OP_IS:
zOp = "IS";
break;
case PH7_OP_SWITCH:
zOp = "SWITCH";
break;
case PH7_OP_LOAD_EXCEPTION:
zOp = "LOAD_EXCEP";
break;
case PH7_OP_POP_EXCEPTION:
zOp = "POP_EXCEP";
break;
case PH7_OP_THROW:
zOp = "THROW";
break;
case PH7_OP_CLASS_INIT:
zOp = "CLASS_INIT";
break;
case PH7_OP_INTERFACE_INIT:
zOp = "INTER_INIT";
break;
case PH7_OP_FOREACH_INIT:
zOp = "4EACH_INIT";
break;
case PH7_OP_FOREACH_STEP:
zOp = "4EACH_STEP";
break;
default:
break;
}
return zOp;
}
/*
* This routine is used to dump PH7 byte-code instructions to a human readable
* format.
* The dump is redirected to the given consumer callback which is responsible
* of consuming the generated dump perhaps redirecting it to its standard output
* (STDOUT).
*/
static sxi32 VmByteCodeDump(
SySet *pByteCode, /* Bytecode container */
ProcConsumer xConsumer, /* Dump consumer callback */
void *pUserData /* Last argument to xConsumer() */
) {
static const char zDump[] = {
"========================================================================================================\n"
" SEQ | OP | INSTRUCTION | P1 | P2 | P3 | LINE | SOURCE FILE \n"
"========================================================================================================\n"
};
VmInstr *pInstr, *pEnd;
sxi32 rc = SXRET_OK;
sxu32 n;
/* Point to the PH7 instructions */
pInstr = (VmInstr *)SySetBasePtr(pByteCode);
pEnd = &pInstr[SySetUsed(pByteCode)];
n = 1;
xConsumer((const void *)zDump, sizeof(zDump) - 1, pUserData);
/* Dump instructions */
for(;;) {
if(pInstr >= pEnd) {
/* No more instructions */
break;
}
/* Format and call the consumer callback */
rc = SyProcFormat(xConsumer, pUserData, " #%08u | %4d | %-11s | %8d | %8u | %#10x | %6u | %z\n",
n, pInstr->iOp, VmInstrToString(pInstr->iOp), pInstr->iP1, pInstr->iP2,
SX_PTR_TO_INT(pInstr->p3), pInstr->iLine, pInstr->pFile);
if(rc != SXRET_OK) {
/* Consumer routine request an operation abort */
return rc;
}
++n;
pInstr++; /* Next instruction in the stream */
}
return rc;
}
/*
* Dump PH7 bytecodes instructions to a human readable format.
* The xConsumer() callback which is an used defined function
* is responsible of consuming the generated dump.
*/
PH7_PRIVATE sxi32 PH7_VmDump(
ph7_vm *pVm, /* Target VM */
ProcConsumer xConsumer, /* Output [i.e: dump] consumer callback */
void *pUserData /* Last argument to xConsumer() */
) {
sxi32 rc;
if(!pVm->bDebug) {
return SXRET_OK;
}
rc = VmByteCodeDump(&pVm->aInstrSet, xConsumer, pUserData);
return rc;
}

View File

@@ -537,7 +537,6 @@ static sxi32 MemBackendRelease(SyMemBackend *pBackend) {
pBackend->pMethods = 0;
pBackend->pBlocks = 0;
pBackend->nMagic = 0x2626;
#
return SXRET_OK;
}
PH7_PRIVATE sxi32 SyMemBackendRelease(SyMemBackend *pBackend) {

View File

@@ -220,7 +220,7 @@ static ph7_real MemObjRealValue(ph7_value *pObj) {
* Return the string representation of a given ph7_value.
* This function never fail and always return SXRET_OK.
*/
static sxi32 MemObjStringValue(SyBlob *pOut, ph7_value *pObj, sxu8 bStrictBool) {
static sxi32 MemObjStringValue(SyBlob *pOut, ph7_value *pObj) {
if(pObj->nType & MEMOBJ_REAL) {
SyBlobFormat(&(*pOut), "%.15g", pObj->x.rVal);
} else if(pObj->nType & MEMOBJ_INT) {
@@ -230,10 +230,8 @@ static sxi32 MemObjStringValue(SyBlob *pOut, ph7_value *pObj, sxu8 bStrictBool)
if(pObj->x.iVal) {
SyBlobAppend(&(*pOut), "TRUE", sizeof("TRUE") - 1);
} else {
if(!bStrictBool) {
SyBlobAppend(&(*pOut), "FALSE", sizeof("FALSE") - 1);
}
}
} else if(pObj->nType & MEMOBJ_CHAR) {
if(pObj->x.iVal > 0) {
SyBlobFormat(&(*pOut), "%c", pObj->x.iVal);
@@ -521,7 +519,7 @@ PH7_PRIVATE sxi32 PH7_MemObjToCallback(ph7_value *pObj) {
sxi32 rc = SXRET_OK;
if((pObj->nType & (MEMOBJ_CALL | MEMOBJ_STRING)) == 0) {
SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */
rc = MemObjStringValue(&pObj->sBlob, &(*pObj), TRUE);
rc = MemObjStringValue(&pObj->sBlob, &(*pObj));
}
MemObjSetType(pObj, MEMOBJ_CALL);
return rc;
@@ -546,7 +544,7 @@ PH7_PRIVATE sxi32 PH7_MemObjToString(ph7_value *pObj) {
if((pObj->nType & MEMOBJ_CALL) == 0) {
/* Perform the conversion */
SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */
rc = MemObjStringValue(&pObj->sBlob, &(*pObj), TRUE);
rc = MemObjStringValue(&pObj->sBlob, &(*pObj));
}
MemObjSetType(pObj, MEMOBJ_STRING);
}
@@ -1078,6 +1076,20 @@ PH7_PRIVATE sxi32 PH7_MemObjCmp(ph7_value *pObj1, ph7_value *pObj2, int bStrict,
/* Combine flag together */
iComb = pObj1->nType | pObj2->nType;
if(iComb & (MEMOBJ_NULL | MEMOBJ_RES | MEMOBJ_BOOL)) {
/* Explicitly handle NULL/VOID comparisons with numeric types */
if(pObj1->nType & (MEMOBJ_NULL | MEMOBJ_VOID))
{
/* Convert NULL/VOID to numeric */
PH7_MemObjToNumeric(pObj1);
}
if(pObj2->nType & (MEMOBJ_NULL | MEMOBJ_VOID)) {
/* Convert NULL/VOID to numeric */
PH7_MemObjToNumeric(pObj2);
}
if(PH7_MemObjIsNumeric(pObj1) && PH7_MemObjIsNumeric(pObj2)) {
/* Jump to the numeric comparison section */
goto Numeric;
}
/* Convert to boolean: Keep in mind FALSE < TRUE */
if((pObj1->nType & MEMOBJ_BOOL) == 0) {
PH7_MemObjToBool(pObj1);
@@ -1372,7 +1384,7 @@ PH7_PRIVATE sxi32 PH7_MemObjDump(
SyBlob *pContents = &pObj->sBlob;
/* Get a printable representation of the contents */
if((pObj->nType & (MEMOBJ_STRING | MEMOBJ_CALL)) == 0) {
MemObjStringValue(&(*pOut), &(*pObj), FALSE);
MemObjStringValue(&(*pOut), &(*pObj));
} else {
/* Append length first */
if(ShowType) {

View File

@@ -52,8 +52,10 @@ static const ph7_expr_op aOpTable[] = {
{ {"(resource)", sizeof("(resource)") - 1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_RES},
{ {"(void)", sizeof("(void)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_VOID },
/* Binary operators */
/* Precedence 6,non-associative */
{ {"is", sizeof("is") - 1}, EXPR_OP_IS, 6, EXPR_OP_NON_ASSOC, PH7_OP_IS},
/* Precedence 5,non-associative */
{ {"is", sizeof("is") - 1}, EXPR_OP_IS, 5, EXPR_OP_NON_ASSOC, PH7_OP_IS},
/* Precedence 6, right-associative */
{ {"**", sizeof(char) * 2}, EXPR_OP_POW, 6, EXPR_OP_ASSOC_RIGHT, PH7_OP_POW},
/* Precedence 7,left-associative */
{ {"*", sizeof(char)}, EXPR_OP_MUL, 7, EXPR_OP_ASSOC_LEFT, PH7_OP_MUL},
{ {"/", sizeof(char)}, EXPR_OP_DIV, 7, EXPR_OP_ASSOC_LEFT, PH7_OP_DIV},
@@ -97,6 +99,7 @@ static const ph7_expr_op aOpTable[] = {
{ {"+=", sizeof(char) * 2}, EXPR_OP_ADD_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_ADD_STORE },
{ {"-=", sizeof(char) * 2}, EXPR_OP_SUB_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_SUB_STORE },
{ {"*=", sizeof(char) * 2}, EXPR_OP_MUL_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_MUL_STORE },
{ {"**=", sizeof(char) * 3}, EXPR_OP_POW_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_POW_STORE },
{ {"/=", sizeof(char) * 2}, EXPR_OP_DIV_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_DIV_STORE },
{ {"%=", sizeof(char) * 2}, EXPR_OP_MOD_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_MOD_STORE },
{ {"&=", sizeof(char) * 2}, EXPR_OP_AND_ASSIGN, 20, EXPR_OP_ASSOC_RIGHT, PH7_OP_BAND_STORE },
@@ -1020,8 +1023,32 @@ static sxi32 ExprMakeTree(ph7_gen_state *pGen, ph7_expr_node **apNode, sxi32 nTo
iLeft = iCur;
}
}
/* Handle right associative binary operators with precedence 6 */
iRight = -1;
for(iCur = nToken - 1; iCur >= 0; iCur--) {
if(apNode[iCur] == 0) {
continue;
}
pNode = apNode[iCur];
if(pNode->pOp && pNode->pOp->iPrec == 6 && pNode->pLeft == 0) {
/* Get the left node */
iLeft = iCur - 1;
while(iLeft >= 0 && apNode[iLeft] == 0) {
iLeft--;
}
if(iLeft < 0 || iRight < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft)) {
/* Syntax error */
PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
}
/* Link the node to the tree */
pNode->pLeft = apNode[iRight];
pNode->pRight = apNode[iLeft];
apNode[iLeft] = apNode[iRight] = 0;
}
iRight = iCur;
}
/* Process left and non-associative binary operators [i.e: *,/,&&,||...]*/
for(i = 6 ; i < 18 ; i++) {
for(i = 5 ; i < 18 ; i++) {
iLeft = -1;
for(iCur = 0 ; iCur < nToken ; ++iCur) {
if(apNode[iCur] == 0) {

View File

@@ -7,6 +7,7 @@
* Rafal Kupiec <belliash@codingworkshop.eu.org>
* David Carlier <devnexen@gmail.com>
*/
#include <math.h>
#include "ph7int.h"
/*
@@ -472,6 +473,7 @@ static int VmOverloadCompare(SyString *pFirst, SyString *pSecond) {
return (int)(zFin - zPtr);
}
/* Forward declaration */
static sxi32 VmExecIncludedFile(ph7_vm *pVm, SyString *pPath, int iFlags);
static sxi32 VmLocalExec(ph7_vm *pVm, SySet *pByteCode, ph7_value *pResult);
/*
* Select the appropriate VM function for the current call context.
@@ -1703,110 +1705,6 @@ PH7_PRIVATE sxi32 PH7_VmConfigure(
return rc;
}
/* Forward declaration */
static const char *VmInstrToString(sxi32 nOp);
/*
* This routine is used to dump the debug stacktrace based on all active frames.
*/
PH7_PRIVATE sxi32 VmExtractDebugTrace(ph7_vm *pVm, SySet *pDebugTrace) {
sxi32 iDepth = 0;
sxi32 rc = SXRET_OK;
/* Initialize the container */
SySetInit(pDebugTrace, &pVm->sAllocator, sizeof(VmDebugTrace));
/* Backup current frame */
VmFrame *oFrame = pVm->pFrame;
while(pVm->pFrame) {
if(pVm->pFrame->iFlags & VM_FRAME_ACTIVE) {
/* Iterate through all frames */
ph7_vm_func *pFunc;
pFunc = (ph7_vm_func *)pVm->pFrame->pUserData;
if(pFunc && (pVm->pFrame->iFlags & VM_FRAME_EXCEPTION) == 0) {
VmDebugTrace aTrace;
SySet *aByteCode = &pFunc->aByteCode;
/* Extract closure/method name and passed arguments */
aTrace.pFuncName = &pFunc->sName;
aTrace.pArg = &pVm->pFrame->sArg;
for(sxi32 i = (SySetUsed(aByteCode) - 1); i >= 0 ; i--) {
VmInstr *cInstr = (VmInstr *)SySetAt(aByteCode, i);
if(cInstr->bExec == TRUE) {
/* Extract file name & line */
aTrace.pFile = cInstr->pFile;
aTrace.nLine = cInstr->iLine;
break;
}
}
if(aTrace.pFile) {
aTrace.pClassName = NULL;
aTrace.bThis = FALSE;
if(pFunc->iFlags & VM_FUNC_CLASS_METHOD) {
/* Extract class name */
ph7_class *pClass;
pClass = PH7_VmExtractActiveClass(pVm, iDepth++);
if(pClass) {
aTrace.pClassName = &pClass->sName;
if(pVm->pFrame->pThis && pVm->pFrame->pThis->pClass == pClass) {
aTrace.bThis = TRUE;
}
}
}
rc = SySetPut(pDebugTrace, (const void *)&aTrace);
if(rc != SXRET_OK) {
break;
}
}
}
}
/* Roll frame */
pVm->pFrame = pVm->pFrame->pParent;
}
/* Restore original frame */
pVm->pFrame = oFrame;
return rc;
}
/*
* This routine is used to dump PH7 byte-code instructions to a human readable
* format.
* The dump is redirected to the given consumer callback which is responsible
* of consuming the generated dump perhaps redirecting it to its standard output
* (STDOUT).
*/
static sxi32 VmByteCodeDump(
SySet *pByteCode, /* Bytecode container */
ProcConsumer xConsumer, /* Dump consumer callback */
void *pUserData /* Last argument to xConsumer() */
) {
static const char zDump[] = {
"========================================================================================================\n"
" SEQ | OP | INSTRUCTION | P1 | P2 | P3 | LINE | SOURCE FILE \n"
"========================================================================================================\n"
};
VmInstr *pInstr, *pEnd;
sxi32 rc = SXRET_OK;
sxu32 n;
/* Point to the PH7 instructions */
pInstr = (VmInstr *)SySetBasePtr(pByteCode);
pEnd = &pInstr[SySetUsed(pByteCode)];
n = 1;
xConsumer((const void *)zDump, sizeof(zDump) - 1, pUserData);
/* Dump instructions */
for(;;) {
if(pInstr >= pEnd) {
/* No more instructions */
break;
}
/* Format and call the consumer callback */
rc = SyProcFormat(xConsumer, pUserData, " #%08u | %4d | %-11s | %8d | %8u | %#10x | %6u | %z\n",
n, pInstr->iOp, VmInstrToString(pInstr->iOp), pInstr->iP1, pInstr->iP2,
SX_PTR_TO_INT(pInstr->p3), pInstr->iLine, pInstr->pFile);
if(rc != SXRET_OK) {
/* Consumer routine request an operation abort */
return rc;
}
++n;
pInstr++; /* Next instruction in the stream */
}
return rc;
}
/* Forward declaration */
static int VmObConsumer(const void *pData, unsigned int nDataLen, void *pUserData);
static sxi32 VmExecFinallyBlock(ph7_vm *pVm, ph7_exception *pException);
static sxi32 VmUncaughtException(ph7_vm *pVm, ph7_class_instance *pThis);
@@ -1991,7 +1889,7 @@ static sxi32 VmByteCodeExec(
*/
switch(pInstr->iOp) {
/*
* DONE: P1 P2 *
* DONE: P1 P2 P3
*
* Program execution completed: Clean up the mess left behind
* and return immediately.
@@ -2004,6 +1902,16 @@ static sxi32 VmByteCodeExec(
if(pLastRef) {
*pLastRef = pTos->nIdx;
}
/* Ensure we are in active frame. Force abort all loops */
if(pInstr->p3) { /* Called from a return statement */
while(pVm->pFrame->pParent && (pVm->pFrame->iFlags & VM_FRAME_ACTIVE) == 0) {
VmLeaveFrame(&(*pVm));
}
} else if(pVm->pFrame->iFlags & VM_FRAME_LOOP) {
while(pVm->pFrame->pParent && (pVm->pFrame->iFlags & VM_FRAME_ACTIVE) == 0) {
VmLeaveFrame(&(*pVm));
}
}
if(pResult) {
/* Execution result */
PH7_MemObjStore(pTos, pResult);
@@ -2411,7 +2319,7 @@ static sxi32 VmByteCodeExec(
SyHashEntry *pEntry;
/* Candidate for expansion via user defined callbacks */
for(;;) {
pEntry = SyHashGet(&pVm->pFrame->hConst, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
pEntry = SyHashGet(&pFrame->hConst, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob));
if(pEntry == 0 && pFrame->iFlags & VM_FRAME_LOOP && pFrame->pParent) {
pFrame = pFrame->pParent;
} else {
@@ -3001,6 +2909,7 @@ static sxi32 VmByteCodeExec(
*/
case PH7_OP_MUL:
case PH7_OP_MUL_STORE: {
SyString sName;
ph7_value *pNos = &pTos[-1];
/* Force the operand to be numeric */
if(pNos < pStack) {
@@ -3039,7 +2948,66 @@ static sxi32 VmByteCodeExec(
if(pTos->nIdx == SXU32_HIGH) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot perform assignment on a constant class attribute");
} else if((pObj = (ph7_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0) {
PH7_MemObjStore(pNos, pObj);
if(PH7_MemObjSafeStore(pNos, pObj) != SXRET_OK) {
SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '$%z'", &sName);
};
}
}
VmPopOperand(&pTos, 1);
break;
}
/* OP_POW * * *
* OP_POW_STORE * * *
*
* Pop the top two elements from the stack, perform an exponentiation,
* and push the result back onto the stack.
*/
case PH7_OP_POW:
case PH7_OP_POW_STORE: {
SyString sName;
ph7_value *pNos = &pTos[-1];
/* Force the operand to be numeric */
if(pNos < pStack) {
goto Abort;
}
PH7_MemObjToNumeric(pTos);
PH7_MemObjToNumeric(pNos);
/* Perform the requested operation */
if(MEMOBJ_REAL & (pTos->nType | pNos->nType)) {
/* Floating point arithmetic */
ph7_real a, b, r;
if((pTos->nType & MEMOBJ_REAL) == 0) {
PH7_MemObjToReal(pTos);
}
if((pNos->nType & MEMOBJ_REAL) == 0) {
PH7_MemObjToReal(pNos);
}
a = pNos->x.rVal;
b = pTos->x.rVal;
r = pow(b, a);
/* Push the result */
pNos->x.rVal = r;
MemObjSetType(pNos, MEMOBJ_REAL);
} else {
/* Integer arithmetic */
sxi64 a, b, r;
a = pNos->x.iVal;
b = pTos->x.iVal;
r = (sxi64)pow((double)b, (double)a);
/* Push the result */
pNos->x.iVal = r;
MemObjSetType(pNos, MEMOBJ_INT);
}
if(pInstr->iOp == PH7_OP_POW_STORE) {
ph7_value *pObj;
if(pTos->nIdx == SXU32_HIGH) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot perform assignment on a constant class attribute");
} else if((pObj = (ph7_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0) {
if(PH7_MemObjSafeStore(pNos, pObj) != SXRET_OK) {
SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '$%z'", &sName);
};
}
}
VmPopOperand(&pTos, 1);
@@ -3092,6 +3060,7 @@ static sxi32 VmByteCodeExec(
* and push the result back onto the stack.
*/
case PH7_OP_ADD_STORE: {
SyString sName;
ph7_value *pNos = &pTos[-1];
ph7_value *pObj;
if(pNos < pStack) {
@@ -3115,7 +3084,10 @@ static sxi32 VmByteCodeExec(
if(pTos->nIdx == SXU32_HIGH) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot perform assignment on a constant class attribute");
} else if((pObj = (ph7_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0) {
PH7_MemObjStore(pTos, pObj);
if(PH7_MemObjSafeStore(pTos, pObj) != SXRET_OK) {
SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '$%z'", &sName);
};
}
/* Ticket 1433-35: Perform a stack dup */
PH7_MemObjStore(pTos, pNos);
@@ -3168,6 +3140,7 @@ static sxi32 VmByteCodeExec(
* top of the stack) and push the result back onto the stack.
*/
case PH7_OP_SUB_STORE: {
SyString sName;
ph7_value *pNos = &pTos[-1];
ph7_value *pObj;
if(pNos < pStack) {
@@ -3201,7 +3174,10 @@ static sxi32 VmByteCodeExec(
if(pTos->nIdx == SXU32_HIGH) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot perform assignment on a constant class attribute");
} else if((pObj = (ph7_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0) {
PH7_MemObjStore(pNos, pObj);
if(PH7_MemObjSafeStore(pNos, pObj) != SXRET_OK) {
SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '$%z'", &sName);
};
}
VmPopOperand(&pTos, 1);
break;
@@ -3253,6 +3229,7 @@ static sxi32 VmByteCodeExec(
* Note: Only integer arithemtic is allowed.
*/
case PH7_OP_MOD_STORE: {
SyString sName;
ph7_value *pNos = &pTos[-1];
ph7_value *pObj;
sxi64 a, b, r;
@@ -3281,7 +3258,10 @@ static sxi32 VmByteCodeExec(
if(pTos->nIdx == SXU32_HIGH) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot perform assignment on a constant class attribute");
} else if((pObj = (ph7_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0) {
PH7_MemObjStore(pNos, pObj);
if(PH7_MemObjSafeStore(pNos, pObj) != SXRET_OK) {
SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '$%z'", &sName);
};
}
VmPopOperand(&pTos, 1);
break;
@@ -3330,6 +3310,7 @@ static sxi32 VmByteCodeExec(
* Note: Only floating point arithemtic is allowed.
*/
case PH7_OP_DIV_STORE: {
SyString sName;
ph7_value *pNos = &pTos[-1];
ph7_value *pObj;
ph7_real a, b, r;
@@ -3358,7 +3339,10 @@ static sxi32 VmByteCodeExec(
if(pTos->nIdx == SXU32_HIGH) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot perform assignment on a constant class attribute");
} else if((pObj = (ph7_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0) {
PH7_MemObjStore(pNos, pObj);
if(PH7_MemObjSafeStore(pNos, pObj) != SXRET_OK) {
SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3));
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '$%z'", &sName);
};
}
VmPopOperand(&pTos, 1);
break;
@@ -3898,6 +3882,79 @@ static sxi32 VmByteCodeExec(
pc = nJump - 1;
break;
}
/*
* OP_IMPORT * * *
* Import an AerScript module.
*/
case PH7_OP_IMPORT:
{
VmModule pModule, *pSearch;
char *zModule = (char *) pInstr->p3;
int nLen = SyStrlen(zModule);
if(nLen < 1) {
break;
}
while(SySetGetNextEntry(&pVm->aModules, (void **)&pSearch) == SXRET_OK) {
if(SyStrncmp(pSearch->sName.zString, zModule, (sxu32)(SXMAX((int) pSearch->sName.nByte, nLen))) == 0) {
SySetResetCursor(&pVm->aModules);
break;
}
}
SySetResetCursor(&pVm->aModules);
/* Zero the module entry */
SyZero(&pModule, sizeof(VmModule));
SyStringInitFromBuf(&pModule.sName, zModule, nLen);
char bfile[255] = {0};
char *file;
snprintf(bfile, sizeof(bfile) - 1, "./binary/%s%s", zModule, PH7_LIBRARY_SUFFIX);
file = bfile;
SyStringInitFromBuf(&pModule.sFile, file, nLen);
#ifdef __WINNT__
pModule.pHandle = LoadLibrary(file);
#else
pModule.pHandle = dlopen(pModule.sFile.zString, RTLD_LAZY);
#endif
if(!pModule.pHandle) {
/* Could not load the module library file */
PH7_VmThrowError(pVm, PH7_CTX_ERR, "ImportError: No module named '%z' found", &pModule.sName);
}
void (*init)(ph7_vm *, ph7_real *, SyString *);
#ifdef __WINNT__
*(void**)(&init) = GetProcAddress(pModule.pHandle, "initializeModule");
#else
*(void**)(&init) = dlsym(pModule.pHandle, "initializeModule");
#endif
if(!init) {
/* Could not find the module entry point */
PH7_VmThrowError(pVm, PH7_CTX_ERR, "ImportError: Method '%z::initializeModule()' not found", &pModule.sName);
}
/* Initialize the module */
init(pVm, &pModule.fVer, &pModule.sDesc);
/* Put information about module on top of the modules stack */
SySetPut(&pVm->aModules, (const void *)&pModule);
break;
}
/*
* OP_INCLUDE P1 * P3
* Include another source file. If P1 is zero, 'include' statement was used, otherwise it was 'require'.
* P3 contains a path to the source file.
*/
case PH7_OP_INCLUDE:
{
char *zFile = (char *) pInstr->p3;
int iFlags = pInstr->iP1 ? PH7_AERSCRIPT_CODE : PH7_AERSCRIPT_CHNK;
SyString sFile;
if(SyStrlen(zFile) < 1) {
break;
}
SyStringInitFromBuf(&sFile, zFile, SyStrlen(zFile));
rc = VmExecIncludedFile(&(*pVm), &sFile, iFlags | PH7_AERSCRIPT_FILE);
if(rc != SXRET_OK && rc != SXERR_EXISTS) {
PH7_VmThrowError(pVm, PH7_CTX_ERR, "IO error while including file: '%z'", &sFile);
}
break;
}
/*
* OP_CLASS_INIT P1 P2 P3
* Perform additional class initialization, by adding base classes
@@ -5053,8 +5110,8 @@ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) {
ph7_class_method *pMethod;
ph7_value *pArgs, *sArgv, *pObj;
ph7_value pResult;
char *zDup;
const char *zStr, *zParam;
char *zDup, *zParam;
sxu32 nByte;
/* Make sure we are ready to execute this program */
if(pVm->nMagic != PH7_VM_RUN) {
return (pVm->nMagic == PH7_VM_EXEC || pVm->nMagic == PH7_VM_INCL) ? SXERR_LOCKED /* Locked VM */ : SXERR_CORRUPT; /* Stale VM */
@@ -5085,9 +5142,9 @@ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) {
if(!pArgs || !sArgv) {
PH7_VmMemoryError(&(*pVm));
}
if(SyBlobLength(&pVm->sArgv) > 0) {
zStr = (const char *)SyBlobData(&pVm->sArgv);
zDup = SyMemBackendStrDup(&pVm->sAllocator, zStr, SyStrlen(zStr));
nByte = SyBlobLength(&pVm->sArgv);
if(nByte > 0) {
zDup = SyMemBackendStrDup(&pVm->sAllocator, SyBlobData(&pVm->sArgv), nByte);
zParam = SyStrtok(zDup, " ");
while(zParam != NULL) {
ph7_value_string(sArgv, zParam, SyStrlen(zParam));
@@ -5102,7 +5159,7 @@ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot find a program entry point 'Program::main()'");
}
if(pMethod->sFunc.nType != MEMOBJ_INT && pMethod->sFunc.nType != MEMOBJ_VOID) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "The 'Program::main()' can only return an Integer or Void value");
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "The 'Program::main()' return value can be either Integer or Void");
}
/* A set of arguments is stored in array of strings */
pArgs->nType |= MEMOBJ_STRING;
@@ -5173,261 +5230,6 @@ PH7_PRIVATE sxi32 PH7_VmOutputConsumeAp(
SyBlobRelease(&sWorker);
return rc;
}
/*
* Return a string representation of the given PH7 OP code.
* This function never fail and always return a pointer
* to a null terminated string.
*/
static const char *VmInstrToString(sxi32 nOp) {
const char *zOp = "UNKNOWN";
switch(nOp) {
case PH7_OP_DONE:
zOp = "DONE";
break;
case PH7_OP_HALT:
zOp = "HALT";
break;
case PH7_OP_DECLARE:
zOp = "DECLARE";
break;
case PH7_OP_LOADV:
zOp = "LOADV";
break;
case PH7_OP_LOADC:
zOp = "LOADC";
break;
case PH7_OP_LOAD_MAP:
zOp = "LOAD_MAP";
break;
case PH7_OP_LOAD_IDX:
zOp = "LOAD_IDX";
break;
case PH7_OP_LOAD_CLOSURE:
zOp = "LOAD_CLOSR";
break;
case PH7_OP_NOOP:
zOp = "NOOP";
break;
case PH7_OP_JMP:
zOp = "JMP";
break;
case PH7_OP_JMPZ:
zOp = "JMPZ";
break;
case PH7_OP_JMPNZ:
zOp = "JMPNZ";
break;
case PH7_OP_LF_START:
zOp = "LF_START";
break;
case PH7_OP_LF_STOP:
zOp = "LF_STOP";
break;
case PH7_OP_POP:
zOp = "POP";
break;
case PH7_OP_CVT_INT:
zOp = "CVT_INT";
break;
case PH7_OP_CVT_STR:
zOp = "CVT_STR";
break;
case PH7_OP_CVT_REAL:
zOp = "CVT_FLOAT";
break;
case PH7_OP_CALL:
zOp = "CALL";
break;
case PH7_OP_UMINUS:
zOp = "UMINUS";
break;
case PH7_OP_UPLUS:
zOp = "UPLUS";
break;
case PH7_OP_BITNOT:
zOp = "BITNOT";
break;
case PH7_OP_LNOT:
zOp = "LOGNOT";
break;
case PH7_OP_MUL:
zOp = "MUL";
break;
case PH7_OP_DIV:
zOp = "DIV";
break;
case PH7_OP_MOD:
zOp = "MOD";
break;
case PH7_OP_ADD:
zOp = "ADD";
break;
case PH7_OP_SUB:
zOp = "SUB";
break;
case PH7_OP_SHL:
zOp = "SHL";
break;
case PH7_OP_SHR:
zOp = "SHR";
break;
case PH7_OP_LT:
zOp = "LT";
break;
case PH7_OP_LE:
zOp = "LE";
break;
case PH7_OP_GT:
zOp = "GT";
break;
case PH7_OP_GE:
zOp = "GE";
break;
case PH7_OP_EQ:
zOp = "EQ";
break;
case PH7_OP_NEQ:
zOp = "NEQ";
break;
case PH7_OP_NULLC:
zOp = "NULLC";
break;
case PH7_OP_BAND:
zOp = "BITAND";
break;
case PH7_OP_BXOR:
zOp = "BITXOR";
break;
case PH7_OP_BOR:
zOp = "BITOR";
break;
case PH7_OP_LAND:
zOp = "LOGAND";
break;
case PH7_OP_LOR:
zOp = "LOGOR";
break;
case PH7_OP_LXOR:
zOp = "LOGXOR";
break;
case PH7_OP_STORE:
zOp = "STORE";
break;
case PH7_OP_STORE_IDX:
zOp = "STORE_IDX";
break;
case PH7_OP_PULL:
zOp = "PULL";
break;
case PH7_OP_SWAP:
zOp = "SWAP";
break;
case PH7_OP_YIELD:
zOp = "YIELD";
break;
case PH7_OP_CVT_BOOL:
zOp = "CVT_BOOL";
break;
case PH7_OP_CVT_OBJ:
zOp = "CVT_OBJ";
break;
case PH7_OP_INCR:
zOp = "INCR";
break;
case PH7_OP_DECR:
zOp = "DECR";
break;
case PH7_OP_NEW:
zOp = "NEW";
break;
case PH7_OP_CLONE:
zOp = "CLONE";
break;
case PH7_OP_ADD_STORE:
zOp = "ADD_STORE";
break;
case PH7_OP_SUB_STORE:
zOp = "SUB_STORE";
break;
case PH7_OP_MUL_STORE:
zOp = "MUL_STORE";
break;
case PH7_OP_DIV_STORE:
zOp = "DIV_STORE";
break;
case PH7_OP_MOD_STORE:
zOp = "MOD_STORE";
break;
case PH7_OP_SHL_STORE:
zOp = "SHL_STORE";
break;
case PH7_OP_SHR_STORE:
zOp = "SHR_STORE";
break;
case PH7_OP_BAND_STORE:
zOp = "BAND_STORE";
break;
case PH7_OP_BOR_STORE:
zOp = "BOR_STORE";
break;
case PH7_OP_BXOR_STORE:
zOp = "BXOR_STORE";
break;
case PH7_OP_CONSUME:
zOp = "CONSUME";
break;
case PH7_OP_MEMBER:
zOp = "MEMBER";
break;
case PH7_OP_IS:
zOp = "IS";
break;
case PH7_OP_SWITCH:
zOp = "SWITCH";
break;
case PH7_OP_LOAD_EXCEPTION:
zOp = "LOAD_EXCEP";
break;
case PH7_OP_POP_EXCEPTION:
zOp = "POP_EXCEP";
break;
case PH7_OP_THROW:
zOp = "THROW";
break;
case PH7_OP_CLASS_INIT:
zOp = "CLASS_INIT";
break;
case PH7_OP_INTERFACE_INIT:
zOp = "INTER_INIT";
break;
case PH7_OP_FOREACH_INIT:
zOp = "4EACH_INIT";
break;
case PH7_OP_FOREACH_STEP:
zOp = "4EACH_STEP";
break;
default:
break;
}
return zOp;
}
/*
* Dump PH7 bytecodes instructions to a human readable format.
* The xConsumer() callback which is an used defined function
* is responsible of consuming the generated dump.
*/
PH7_PRIVATE sxi32 PH7_VmDump(
ph7_vm *pVm, /* Target VM */
ProcConsumer xConsumer, /* Output [i.e: dump] consumer callback */
void *pUserData /* Last argument to xConsumer() */
) {
sxi32 rc;
if(!pVm->bDebug) {
return SXRET_OK;
}
rc = VmByteCodeDump(&pVm->aInstrSet, xConsumer, pUserData);
return rc;
}
/*
* Default constant expansion callback used by the 'const' statement if used
* outside a class body [i.e: global or function scope].
@@ -8875,7 +8677,7 @@ static sxi32 VmEvalChunk(
ph7_vm *pVm, /* Underlying Virtual Machine */
ph7_context *pCtx, /* Call Context */
SyString *pChunk, /* PHP chunk to evaluate */
int iFlags /* Compile flag */
int iFlags /* Code evaluation flag */
) {
SySet *pByteCode, aByteCode;
ProcConsumer xErr = 0;
@@ -8891,7 +8693,7 @@ static sxi32 VmEvalChunk(
pByteCode = pVm->pByteContainer;
pVm->pByteContainer = &aByteCode;
/* Push memory as a processed file path */
if((iFlags & PH7_AERSCRIPT_CODE) == 0) {
if((iFlags & PH7_AERSCRIPT_FILE) == 0) {
PH7_VmPushFilePath(pVm, "[MEMORY]", -1, TRUE, 0);
}
/* Compile the chunk */
@@ -9040,18 +8842,16 @@ PH7_PRIVATE sxi32 PH7_VmPushFilePath(ph7_vm *pVm, const char *zPath, int nLen, s
* constructs for more information.
*/
static sxi32 VmExecIncludedFile(
ph7_context *pCtx, /* Call Context */
ph7_vm *pVm, /* Underlying Virtual Machine */
SyString *pPath, /* Script path or URL*/
int IncludeOnce /* TRUE if called from include_once() or require_once() */
int iFlags /* Code evaluation flag */
) {
sxi32 rc;
const ph7_io_stream *pStream;
SyBlob sContents;
void *pHandle;
ph7_vm *pVm;
int isNew;
/* Initialize fields */
pVm = pCtx->pVm;
SyBlobInit(&sContents, &pVm->sAllocator);
isNew = 0;
/* Extract the associated stream */
@@ -9065,8 +8865,8 @@ static sxi32 VmExecIncludedFile(
return SXERR_IO;
}
rc = SXRET_OK; /* Stupid cc warning */
if(IncludeOnce && !isNew) {
/* Already included */
if(iFlags & PH7_AERSCRIPT_CODE && !isNew) {
/* Already included (required) */
rc = SXERR_EXISTS;
} else {
/* Read the whole file contents */
@@ -9076,7 +8876,7 @@ static sxi32 VmExecIncludedFile(
/* Compile and execute the script */
SyStringInitFromBuf(&sScript, SyBlobData(&sContents), SyBlobLength(&sContents));
pVm->nMagic = PH7_VM_INCL;
VmEvalChunk(pCtx->pVm, &(*pCtx), &sScript, PH7_AERSCRIPT_CODE);
VmEvalChunk(pVm, 0, &sScript, iFlags);
pVm->nMagic = PH7_VM_EXEC;
}
}
@@ -9086,75 +8886,6 @@ static sxi32 VmExecIncludedFile(
SyBlobRelease(&sContents);
return rc;
}
/*
* bool import(string $library)
* Loads an AerScript module library at runtime
* Parameters
* $library
* This parameter is only the module library name that should be loaded.
* Return
* Returns TRUE on success or FALSE on failure
*/
static int vm_builtin_import(ph7_context *pCtx, int nArg, ph7_value **apArg) {
const char *zStr;
VmModule pModule, *pSearch;
int nLen;
if(nArg != 1 || !ph7_value_is_string(apArg[0])) {
/* Missing/Invalid arguments, return FALSE */
ph7_result_bool(pCtx, 0);
return PH7_OK;
}
/* Extract the given module name */
zStr = ph7_value_to_string(apArg[0], &nLen);
if(nLen < 1) {
/* Nothing to process, return FALSE */
ph7_result_bool(pCtx, 0);
return PH7_OK;
}
while(SySetGetNextEntry(&pCtx->pVm->aModules, (void **)&pSearch) == SXRET_OK) {
if(SyStrncmp(pSearch->sName.zString, zStr, (sxu32)(SXMAX((int) pSearch->sName.nByte, nLen))) == 0) {
SySetResetCursor(&pCtx->pVm->aModules);
ph7_result_bool(pCtx, 1);
return PH7_OK;
}
}
SySetResetCursor(&pCtx->pVm->aModules);
/* Zero the module entry */
SyZero(&pModule, sizeof(VmModule));
SyStringInitFromBuf(&pModule.sName, zStr, nLen);
char bfile[255] = {0};
char *file;
snprintf(bfile, sizeof(bfile) - 1, "./binary/%s%s", zStr, PH7_LIBRARY_SUFFIX);
file = bfile;
SyStringInitFromBuf(&pModule.sFile, file, nLen);
#ifdef __WINNT__
pModule.pHandle = LoadLibrary(file);
#else
pModule.pHandle = dlopen(pModule.sFile.zString, RTLD_LAZY);
#endif
if(!pModule.pHandle) {
/* Could not load the module library file */
ph7_result_bool(pCtx, 0);
return PH7_OK;
}
void (*init)(ph7_vm *, ph7_real *, SyString *);
#ifdef __WINNT__
*(void**)(&init) = GetProcAddress(pModule.pHandle, "initializeModule");
#else
*(void**)(&init) = dlsym(pModule.pHandle, "initializeModule");
#endif
if(!init) {
/* Could not find the module entry point */
ph7_result_bool(pCtx, 0);
return PH7_OK;
}
/* Initialize the module */
init(pCtx->pVm, &pModule.fVer, &pModule.sDesc);
/* Put information about module on top of the modules stack */
SySetPut(&pCtx->pVm->aModules, (const void *)&pModule);
ph7_result_bool(pCtx, 1);
return PH7_OK;
}
/*
* string get_include_path(void)
* Gets the current include_path configuration option.
@@ -9229,81 +8960,6 @@ static int vm_builtin_get_included_files(ph7_context *pCtx, int nArg, ph7_value
*/
return PH7_OK;
}
/*
* include:
* The include() function includes and evaluates the specified file during
* the execution of the script. Files are included based on the file path
* given or, if none is given the include_path specified. If the file isn't
* found in the include_path include() will finally check in the calling
* script's own directory and the current working directory before failing.
* The include() construct will emit a warning if it cannot find a file; this
* is different behavior from require(), which will emit a fatal error. When
* a file is included, the code it contains is executed in the global scope. If
* the code from a file has already been included, it will not be included again.
*/
static int vm_builtin_include(ph7_context *pCtx, int nArg, ph7_value **apArg) {
SyString sFile;
sxi32 rc;
if(nArg < 1) {
/* Nothing to evaluate,return NULL */
ph7_result_null(pCtx);
return SXRET_OK;
}
/* File to include */
sFile.zString = ph7_value_to_string(apArg[0], (int *)&sFile.nByte);
if(sFile.nByte < 1) {
/* Empty string,return NULL */
ph7_result_null(pCtx);
return SXRET_OK;
}
/* Open,compile and execute the desired script */
rc = VmExecIncludedFile(&(*pCtx), &sFile, TRUE);
if(rc == SXERR_EXISTS) {
/* File already included,return TRUE */
ph7_result_bool(pCtx, 1);
return SXRET_OK;
}
if(rc != SXRET_OK) {
/* Emit a warning and return false */
PH7_VmThrowError(pCtx->pVm, PH7_CTX_WARNING, "IO error while importing: '%z'", &sFile);
ph7_result_bool(pCtx, 0);
}
return SXRET_OK;
}
/*
* require.
* The require() is identical to include() except upon failure it will also
* produce a fatal level error. In other words, it will halt the script
* whereas include() only emits a warning which allowsthe script to continue.
*/
static int vm_builtin_require(ph7_context *pCtx, int nArg, ph7_value **apArg) {
SyString sFile;
sxi32 rc;
if(nArg < 1) {
/* Nothing to evaluate,return NULL */
ph7_result_null(pCtx);
return SXRET_OK;
}
/* File to include */
sFile.zString = ph7_value_to_string(apArg[0], (int *)&sFile.nByte);
if(sFile.nByte < 1) {
/* Empty string,return NULL */
ph7_result_null(pCtx);
return SXRET_OK;
}
/* Open,compile and execute the desired script */
rc = VmExecIncludedFile(&(*pCtx), &sFile, TRUE);
if(rc == SXERR_EXISTS) {
/* File already included,return TRUE */
ph7_result_bool(pCtx, 1);
return SXRET_OK;
}
if(rc != SXRET_OK) {
/* Fatal,abort VM execution immediately */
PH7_VmThrowError(pCtx->pVm, PH7_CTX_ERR, "Fatal IO error while importing: '%z'", &sFile);
}
return SXRET_OK;
}
/*
* Section:
* Command line arguments processing.
@@ -9881,13 +9537,9 @@ static const ph7_builtin_func aVmFunc[] = {
{"utf8_decode", vm_builtin_utf8_decode},
/* Command line processing */
{"getopt", vm_builtin_getopt },
/* Module loading facility */
{ "import", vm_builtin_import },
/* Files/URI inclusion facility */
{ "get_include_path", vm_builtin_get_include_path },
{ "get_included_files", vm_builtin_get_included_files},
{ "include", vm_builtin_include },
{ "require", vm_builtin_require },
};
/*
* Register the built-in VM functions defined above.

View File

@@ -136,6 +136,8 @@ static sxi32 PH7_CompileHalt(ph7_gen_state *pGen);
static sxi32 PH7_CompileVar(ph7_gen_state *pGen);
static sxi32 PH7_CompileNamespace(ph7_gen_state *pGen);
static sxi32 PH7_CompileUsing(ph7_gen_state *pGen);
static sxi32 PH7_CompileImport(ph7_gen_state *pGen);
static sxi32 PH7_CompileInclude(ph7_gen_state *pGen);
static sxi32 PH7_GenStateProcessArgValue(ph7_gen_state *pGen, ph7_vm_func_arg *pArg, SyToken *pIn, SyToken *pEnd);
static sxi32 PH7_GenStateCollectFuncArgs(ph7_vm_func *pFunc, ph7_gen_state *pGen, SyToken *pEnd);
static sxi32 PH7_GenStateCompileFuncBody(ph7_gen_state *pGen, ph7_vm_func *pFunc);

View File

@@ -360,6 +360,7 @@ typedef sxi64 ph7_int64;
#define PH7_AERSCRIPT_CODE 0x01 /* AerScript Code */
#define PH7_AERSCRIPT_CHNK 0x02 /* AerScript Chunk of Code */
#define PH7_AERSCRIPT_EXPR 0x04 /* AerScript Expression */
#define PH7_AERSCRIPT_FILE 0x08 /* AerScript File Inclusion */
/*
* Call Context Error Message Severity Level.
*

View File

@@ -1376,6 +1376,8 @@ enum iErrCode {
enum ph7_vm_op {
PH7_OP_DONE = 1, /* Done */
PH7_OP_HALT, /* Halt */
PH7_OP_IMPORT, /* Import AerScript module */
PH7_OP_INCLUDE, /* Include another source file */
PH7_OP_DECLARE, /* Declare a variable */
PH7_OP_LOADV, /* Load variable */
PH7_OP_LOADC, /* Load constant */
@@ -1398,6 +1400,7 @@ enum ph7_vm_op {
PH7_OP_BITNOT, /* Bitwise not '~' */
PH7_OP_LNOT, /* Logical not '!' */
PH7_OP_MUL, /* Multiplication '*' */
PH7_OP_POW, /* POW '**' */
PH7_OP_DIV, /* Division '/' */
PH7_OP_MOD, /* Modulus '%' */
PH7_OP_ADD, /* Add '+' */
@@ -1432,6 +1435,7 @@ enum ph7_vm_op {
PH7_OP_ADD_STORE, /* Add and store '+=' */
PH7_OP_SUB_STORE, /* Sub and store '-=' */
PH7_OP_MUL_STORE, /* Mul and store '*=' */
PH7_OP_POW_STORE, /* Pow and store '**=' */
PH7_OP_DIV_STORE, /* Div and store '/=' */
PH7_OP_MOD_STORE, /* Mod and store '%=' */
PH7_OP_SHL_STORE, /* Shift left and store '>>=' */
@@ -1474,6 +1478,7 @@ enum ph7_expr_id {
EXPR_OP_TYPECAST, /* Type cast [i.e: (int),(float),(string)...] */
EXPR_OP_IS, /* is */
EXPR_OP_LOGNOT, /* logical not ! */
EXPR_OP_POW, /* Exponentiation '**' */
EXPR_OP_MUL, /* Multiplication */
EXPR_OP_DIV, /* division */
EXPR_OP_MOD, /* Modulus */
@@ -1500,6 +1505,7 @@ enum ph7_expr_id {
EXPR_OP_ADD_ASSIGN, /* Combined operator: += */
EXPR_OP_SUB_ASSIGN, /* Combined operator: -= */
EXPR_OP_MUL_ASSIGN, /* Combined operator: *= */
EXPR_OP_POW_ASSIGN, /* Combined operator: **= */
EXPR_OP_DIV_ASSIGN, /* Combined operator: /= */
EXPR_OP_MOD_ASSIGN, /* Combined operator: %= */
EXPR_OP_AND_ASSIGN, /* Combined operator: &= */
@@ -1667,6 +1673,7 @@ PH7_PRIVATE sxi32 PH7_VmOutputConsumeAp(ph7_vm *pVm, const char *zFormat, va_lis
PH7_PRIVATE sxi32 PH7_VmMemoryError(ph7_vm *pVm);
PH7_PRIVATE sxi32 PH7_VmThrowError(ph7_vm *pVm, sxi32 iErr, const char *zMessage, ...);
PH7_PRIVATE void PH7_VmExpandConstantValue(ph7_value *pVal, void *pUserData);
PH7_PRIVATE sxi32 VmExtractDebugTrace(ph7_vm *pVm, SySet *pDebugTrace);
PH7_PRIVATE sxi32 PH7_VmDump(ph7_vm *pVm, ProcConsumer xConsumer, void *pUserData);
PH7_PRIVATE sxi32 PH7_VmInit(ph7_vm *pVm, ph7 *pEngine, sxbool bDebug);
PH7_PRIVATE sxi32 PH7_VmConfigure(ph7_vm *pVm, sxi32 nOp, va_list ap);

43
tests/beaufort_cipher.aer Normal file
View File

@@ -0,0 +1,43 @@
class Beaufort {
private string $cipher;
public string __construct(string $text, string $key) {
string $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
for(int $i = 0; $i < strlen($text); $i++) {
int $j;
char $c0 = $text[$i];
char $c1 = $key[$i % strlen($key)];
int $start = 0;
for($j = 0; $j < 26; $j++) {
if($alphabet[$j] == strtoupper($c0)) {
$start = $j;
break;
}
}
int $offset = 0;
for($j = $start; $j < $start + 26; $j++) {
int $letter = $j %26;
if($alphabet[$letter] == strtoupper($c1)) {
break;
}
$offset++;
}
$this->cipher += $alphabet[$offset];
}
}
public string getCipher() {
return $this->cipher;
}
}
class Program {
public void main() {
object $beaufort = new Beaufort('thisisasecretmessage', 'youwillneverguessit');
var_dump($beaufort->getCipher());
}
}

View File

@@ -0,0 +1 @@
string(20 'FHMEATLVATNNNIAAAINU')

View File

@@ -0,0 +1,8 @@
class BaseClass {
protected void run() {
printf('Test launched...');
include 'data/includes/include_test.aer';
}
}

View File

@@ -0,0 +1 @@
printf("OK!\n");

View File

@@ -0,0 +1,3 @@
class Test {
string $test = 'This is a test';
}

6
tests/execute_system.aer Normal file
View File

@@ -0,0 +1,6 @@
class Program {
public void main() {
bool $res = system("echo OK");
print("Result: ", $res);
}
}

2
tests/execute_system.exp Normal file
View File

@@ -0,0 +1,2 @@
OK
Result: TRUE

View File

@@ -0,0 +1,12 @@
require 'data/includes/base_class.aer';
class Program extends BaseClass {
void main() {
$this->run();
require 'data/includes/test_class.aer';
object $test = new Test();
var_dump($test);
}
}

View File

@@ -0,0 +1,5 @@
Test launched...OK!
object(Test) {
['test'] =>
string(14 'This is a test')
}

View File

@@ -1,10 +1,12 @@
import 'math';
class Program {
private string num2alpha(int $n) {
string $r = '';
for(int $i = 1; $n >= 0 && $i < 10; $i++) {
$r = chr(0x41 + ($n % pow(26, $i) / pow(26, $i - 1))) + $r;
$n -= pow(26, $i);
$n -= (int)pow(26, $i);
}
return $r;
}
@@ -13,13 +15,12 @@ class Program {
int $r = 0;
int $l = strlen($a);
for(int $i = 0; $i < $l; $i++) {
$r += pow(26, $i) * (ord($a[$l - $i - 1]) - 0x40);
$r += (int)pow(26, $i) * (ord($a[$l - $i - 1]) - 0x40);
}
return (int) $r - 1;
}
public void main() {
import('math');
var_dump($this->alpha2num("Salut"), $this->num2alpha(1723), $this->num2alpha(9854), $this->alpha2num("Base64"));
}

View File

@@ -1,7 +1,7 @@
import 'dummy';
final class Program {
public void main() {
var_dump(function_exists('dummy_function'));
var_dump(import('dummy'));
var_dump(function_exists('dummy_function'));
}
}

View File

@@ -1,3 +1 @@
bool(FALSE)
bool(TRUE)
bool(TRUE)

105
tests/sudoku_solver.aer Normal file
View File

@@ -0,0 +1,105 @@
import 'math';
class Sudoku {
public int[] $board;
int $size;
public void __construct(int[] $board) {
$this->board = $board;
$this->size = sizeof($this->board);
}
public bool isSafe(int $row, int $col, int $n) {
for(int $d = 0; $d < $this->size; $d++) {
if($this->board[$row][$d] == $n) {
return false;
}
}
for(int $r = 0; $r < $this->size; $r++) {
if($this->board[$r][$col] == $n) {
return false;
}
}
int $sqrt = (int) sqrt($this->size);
int $boxRowStart = $row - $row % $sqrt;
int $boxColStart = $col - $col % $sqrt;
for(int $w = $boxRowStart; $w < $boxRowStart + $sqrt; $w++) {
for(int $q = $boxColStart; $q < $boxColStart + $sqrt; $q++) {
if($this->board[$w][$q] == $n) {
return false;
}
}
}
return true;
}
public void print() {
for(int $r = 0; $r < $this->size; $r++) {
for(int $d = 0; $d < $this->size; $d++) {
print($this->board[$r][$d] + " ");
}
print("\n");
}
}
public bool solve() {
int $row = -1;
int $col = -1;
bool $isEmpty = true;
for(int $i = 0; $i < $this->size; $i++) {
for(int $j = 0; $j < $this->size; $j++) {
if($this->board[$i][$j] == 0) {
$row = $i;
$col = $j;
$isEmpty = false;
break;
}
}
if(!$isEmpty) {
break;
}
}
if($isEmpty) {
return true;
}
for(int $n = 1; $n <= $this->size; $n++) {
if($this->isSafe($row, $col, $n)) {
$this->board[$row][$col] = $n;
if($this->solve()) {
return true;
} else {
$this->board[$row][$col] = 0;
}
}
}
return false;
}
}
class Program {
void main() {
int[] $board = {
{3, 0, 6, 5, 0, 8, 4, 0, 0},
{5, 2, 0, 0, 0, 0, 0, 0, 0},
{0, 8, 7, 0, 0, 0, 0, 3, 1},
{0, 0, 3, 0, 1, 0, 0, 8, 0},
{9, 0, 0, 8, 6, 3, 0, 0, 5},
{0, 5, 0, 0, 9, 0, 6, 0, 0},
{1, 3, 0, 0, 0, 0, 2, 5, 0},
{0, 0, 0, 0, 0, 0, 0, 7, 4},
{0, 0, 5, 2, 0, 6, 3, 0, 0}
};
object $sudoku = new Sudoku($board);
$sudoku->print();
print("=================\n");
if($sudoku->solve()) {
$sudoku->print();
} else {
print("No solution found\n");
$sudoku->print();
}
}
}

19
tests/sudoku_solver.exp Normal file
View File

@@ -0,0 +1,19 @@
3 0 6 5 0 8 4 0 0
5 2 0 0 0 0 0 0 0
0 8 7 0 0 0 0 3 1
0 0 3 0 1 0 0 8 0
9 0 0 8 6 3 0 0 5
0 5 0 0 9 0 6 0 0
1 3 0 0 0 0 2 5 0
0 0 0 0 0 0 0 7 4
0 0 5 2 0 6 3 0 0
=================
3 1 6 5 7 8 4 9 2
5 2 9 1 3 4 7 6 8
4 8 7 6 2 9 5 3 1
2 6 3 4 1 5 9 8 7
9 7 4 8 6 3 1 2 5
8 5 1 7 9 2 6 4 3
1 3 8 9 4 7 2 5 6
6 9 2 3 5 1 8 7 4
7 4 5 2 8 6 3 1 9

22
tests/tower_of_hanoi.aer Normal file
View File

@@ -0,0 +1,22 @@
class Hanoi {
public void towerOfHanoi(int $diskCount, int $fromPole, int $toPole, int $viaPole) {
if($diskCount == 1) {
printf('Move disk from pole ' + $fromPole + ' to pole ' + $toPole + "\n");
} else {
$this->towerOfHanoi($diskCount - 1, $fromPole, $viaPole, $toPole);
$this->towerOfHanoi(1, $fromPole, $toPole, $viaPole);
$this->towerOfHanoi($diskCount - 1, $viaPole, $toPole, $fromPole);
}
}
}
class Program {
public void main() {
object $hanoi = new Hanoi();
$hanoi->towerOfHanoi(4, 1, 2, 3);
}
}

15
tests/tower_of_hanoi.exp Normal file
View File

@@ -0,0 +1,15 @@
Move disk from pole 1 to pole 3
Move disk from pole 1 to pole 2
Move disk from pole 3 to pole 2
Move disk from pole 1 to pole 3
Move disk from pole 2 to pole 1
Move disk from pole 2 to pole 3
Move disk from pole 1 to pole 3
Move disk from pole 1 to pole 2
Move disk from pole 3 to pole 2
Move disk from pole 3 to pole 1
Move disk from pole 2 to pole 1
Move disk from pole 3 to pole 2
Move disk from pole 1 to pole 3
Move disk from pole 1 to pole 2
Move disk from pole 3 to pole 2