Compare commits
1 Commits
master
...
release/v0
Author | SHA1 | Date |
---|---|---|
Rafal Kupiec | 7076a6f3ed |
14
.build.yml
14
.build.yml
|
@ -1,14 +1,10 @@
|
|||
kind: pipeline
|
||||
type: exec
|
||||
name: AerScript Build
|
||||
|
||||
steps:
|
||||
- name: compile
|
||||
pipeline:
|
||||
step: compile
|
||||
commands:
|
||||
- make release
|
||||
- name: install
|
||||
- make debug
|
||||
step: install
|
||||
commands:
|
||||
- make install
|
||||
- name: test
|
||||
step: test
|
||||
commands:
|
||||
- make tests
|
||||
|
|
|
@ -1421,10 +1421,8 @@ 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 */
|
||||
|
@ -2642,59 +2640,6 @@ 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:
|
||||
|
@ -4945,38 +4890,32 @@ 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 */
|
||||
sxu32 nKeywordID, /* Keyword ID */
|
||||
SyToken *pLookahead /* Look-ahead token */
|
||||
) {
|
||||
switch(nKeywordID) {
|
||||
case PH7_KEYWORD_DEFINE:
|
||||
if(pLookahead) {
|
||||
if(nKeywordID == PH7_KEYWORD_DEFINE) {
|
||||
return PH7_CompileDefine;
|
||||
case PH7_KEYWORD_INTERFACE:
|
||||
} else if(nKeywordID == PH7_KEYWORD_INTERFACE) {
|
||||
return PH7_CompileClassInterface;
|
||||
case PH7_KEYWORD_FINAL:
|
||||
case PH7_KEYWORD_VIRTUAL:
|
||||
} else if(nKeywordID == PH7_KEYWORD_FINAL || nKeywordID == PH7_KEYWORD_VIRTUAL) {
|
||||
return PH7_CompileFinalVirtualClass;
|
||||
case PH7_KEYWORD_CLASS:
|
||||
} else if(nKeywordID == PH7_KEYWORD_CLASS) {
|
||||
return PH7_CompileClass;
|
||||
case PH7_KEYWORD_NAMESPACE:
|
||||
} else if(nKeywordID == PH7_KEYWORD_NAMESPACE) {
|
||||
return PH7_CompileNamespace;
|
||||
case PH7_KEYWORD_USING:
|
||||
} else if(nKeywordID == 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,...].
|
||||
|
@ -5015,7 +4954,9 @@ 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_EVAL || nKeywordID == PH7_KEYWORD_STATIC || nKeywordID == PH7_KEYWORD_NEW || nKeywordID == PH7_KEYWORD_CLONE) {
|
||||
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) {
|
||||
return TRUE;
|
||||
}
|
||||
/* Not a language construct */
|
||||
|
@ -5107,7 +5048,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);
|
||||
xCons = PH7_GenStateGetGlobalScopeHandler(nKeyword, (&pGen->pIn[1] < pGen->pEnd) ? &pGen->pIn[1] : 0);
|
||||
if(xCons == 0) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Syntax error: Unexpected keyword '%z'", &pGen->pIn->sData);
|
||||
}
|
||||
|
|
373
engine/debug.c
373
engine/debug.c
|
@ -1,373 +0,0 @@
|
|||
/**
|
||||
* @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;
|
||||
}
|
615
engine/vm.c
615
engine/vm.c
|
@ -472,7 +472,6 @@ 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.
|
||||
|
@ -1704,6 +1703,110 @@ 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);
|
||||
|
@ -1901,12 +2004,6 @@ static sxi32 VmByteCodeExec(
|
|||
if(pLastRef) {
|
||||
*pLastRef = pTos->nIdx;
|
||||
}
|
||||
/* Ensure we are in active loop. Force abort all loops */
|
||||
if(pVm->pFrame->iFlags & VM_FRAME_LOOP) {
|
||||
while((pVm->pFrame->iFlags & VM_FRAME_ACTIVE) == 0) {
|
||||
VmLeaveFrame(&(*pVm));
|
||||
}
|
||||
}
|
||||
if(pResult) {
|
||||
/* Execution result */
|
||||
PH7_MemObjStore(pTos, pResult);
|
||||
|
@ -3801,79 +3898,6 @@ 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
|
||||
|
@ -5029,8 +5053,8 @@ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) {
|
|||
ph7_class_method *pMethod;
|
||||
ph7_value *pArgs, *sArgv, *pObj;
|
||||
ph7_value pResult;
|
||||
char *zDup, *zParam;
|
||||
sxu32 nByte;
|
||||
char *zDup;
|
||||
const char *zStr, *zParam;
|
||||
/* 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 */
|
||||
|
@ -5061,9 +5085,9 @@ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) {
|
|||
if(!pArgs || !sArgv) {
|
||||
PH7_VmMemoryError(&(*pVm));
|
||||
}
|
||||
nByte = SyBlobLength(&pVm->sArgv);
|
||||
if(nByte > 0) {
|
||||
zDup = SyMemBackendStrDup(&pVm->sAllocator, SyBlobData(&pVm->sArgv), nByte);
|
||||
if(SyBlobLength(&pVm->sArgv) > 0) {
|
||||
zStr = (const char *)SyBlobData(&pVm->sArgv);
|
||||
zDup = SyMemBackendStrDup(&pVm->sAllocator, zStr, SyStrlen(zStr));
|
||||
zParam = SyStrtok(zDup, " ");
|
||||
while(zParam != NULL) {
|
||||
ph7_value_string(sArgv, zParam, SyStrlen(zParam));
|
||||
|
@ -5078,7 +5102,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()' return value can be either Integer or Void");
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "The 'Program::main()' can only return an Integer or Void value");
|
||||
}
|
||||
/* A set of arguments is stored in array of strings */
|
||||
pArgs->nType |= MEMOBJ_STRING;
|
||||
|
@ -5149,6 +5173,261 @@ 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].
|
||||
|
@ -8596,7 +8875,7 @@ static sxi32 VmEvalChunk(
|
|||
ph7_vm *pVm, /* Underlying Virtual Machine */
|
||||
ph7_context *pCtx, /* Call Context */
|
||||
SyString *pChunk, /* PHP chunk to evaluate */
|
||||
int iFlags /* Code evaluation flag */
|
||||
int iFlags /* Compile flag */
|
||||
) {
|
||||
SySet *pByteCode, aByteCode;
|
||||
ProcConsumer xErr = 0;
|
||||
|
@ -8612,7 +8891,7 @@ static sxi32 VmEvalChunk(
|
|||
pByteCode = pVm->pByteContainer;
|
||||
pVm->pByteContainer = &aByteCode;
|
||||
/* Push memory as a processed file path */
|
||||
if((iFlags & PH7_AERSCRIPT_FILE) == 0) {
|
||||
if((iFlags & PH7_AERSCRIPT_CODE) == 0) {
|
||||
PH7_VmPushFilePath(pVm, "[MEMORY]", -1, TRUE, 0);
|
||||
}
|
||||
/* Compile the chunk */
|
||||
|
@ -8761,16 +9040,18 @@ PH7_PRIVATE sxi32 PH7_VmPushFilePath(ph7_vm *pVm, const char *zPath, int nLen, s
|
|||
* constructs for more information.
|
||||
*/
|
||||
static sxi32 VmExecIncludedFile(
|
||||
ph7_vm *pVm, /* Underlying Virtual Machine */
|
||||
ph7_context *pCtx, /* Call Context */
|
||||
SyString *pPath, /* Script path or URL*/
|
||||
int iFlags /* Code evaluation flag */
|
||||
int IncludeOnce /* TRUE if called from include_once() or require_once() */
|
||||
) {
|
||||
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 */
|
||||
|
@ -8784,8 +9065,8 @@ static sxi32 VmExecIncludedFile(
|
|||
return SXERR_IO;
|
||||
}
|
||||
rc = SXRET_OK; /* Stupid cc warning */
|
||||
if(iFlags & PH7_AERSCRIPT_CODE && !isNew) {
|
||||
/* Already included (required) */
|
||||
if(IncludeOnce && !isNew) {
|
||||
/* Already included */
|
||||
rc = SXERR_EXISTS;
|
||||
} else {
|
||||
/* Read the whole file contents */
|
||||
|
@ -8795,7 +9076,7 @@ static sxi32 VmExecIncludedFile(
|
|||
/* Compile and execute the script */
|
||||
SyStringInitFromBuf(&sScript, SyBlobData(&sContents), SyBlobLength(&sContents));
|
||||
pVm->nMagic = PH7_VM_INCL;
|
||||
VmEvalChunk(pVm, 0, &sScript, iFlags);
|
||||
VmEvalChunk(pCtx->pVm, &(*pCtx), &sScript, PH7_AERSCRIPT_CODE);
|
||||
pVm->nMagic = PH7_VM_EXEC;
|
||||
}
|
||||
}
|
||||
|
@ -8805,6 +9086,75 @@ 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.
|
||||
|
@ -8879,6 +9229,81 @@ 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.
|
||||
|
@ -9456,9 +9881,13 @@ 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.
|
||||
|
|
|
@ -136,8 +136,6 @@ 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);
|
||||
|
|
|
@ -360,7 +360,6 @@ 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.
|
||||
*
|
||||
|
|
|
@ -1376,8 +1376,6 @@ 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 */
|
||||
|
@ -1669,7 +1667,6 @@ 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);
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
string(20 'FHMEATLVATNNNIAAAINU')
|
|
@ -1,8 +0,0 @@
|
|||
class BaseClass {
|
||||
|
||||
protected void run() {
|
||||
printf('Test launched...');
|
||||
include 'data/includes/include_test.aer';
|
||||
}
|
||||
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
printf("OK!\n");
|
|
@ -1,3 +0,0 @@
|
|||
class Test {
|
||||
string $test = 'This is a test';
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
Test launched...OK!
|
||||
object(Test) {
|
||||
['test'] =>
|
||||
string(14 'This is a test')
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
import 'math';
|
||||
|
||||
class Program {
|
||||
|
||||
private string num2alpha(int $n) {
|
||||
|
@ -21,6 +19,7 @@ class Program {
|
|||
}
|
||||
|
||||
public void main() {
|
||||
import('math');
|
||||
var_dump($this->alpha2num("Salut"), $this->num2alpha(1723), $this->num2alpha(9854), $this->alpha2num("Base64"));
|
||||
}
|
||||
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
bool(FALSE)
|
||||
bool(TRUE)
|
||||
bool(TRUE)
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
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
|
|
@ -1,22 +0,0 @@
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
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
|
Loading…
Reference in New Issue