Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
206061c837
|
|||
cd924eb066
|
|||
566997d080
|
|||
e592eded1b
|
|||
b4486bf603
|
|||
d73eb9b5b2
|
|||
8ac9b148d1
|
|||
c4023d62e1
|
|||
5ff7d03ed1
|
|||
a167e4bc87
|
|||
dc5725d1af
|
|||
eead19918d
|
|||
fdfeb219d9
|
|||
be203e2e60
|
|||
d4b6fd782e
|
|||
145a18aa7e
|
|||
5e6ed0f668
|
|||
c940224d21
|
|||
3eb82e632b
|
|||
709b5971c6
|
|||
c730082fa0
|
|||
0bf2f6d94f
|
|||
b527840f10
|
|||
ab36234ff3
|
|||
9d762a2350
|
|||
0b5e94bacc
|
|||
e623111f41
|
|||
fd0685f16e
|
|||
ad5784f81f
|
|||
0e757111a3
|
|||
469e5ba059
|
|||
9290db2504
|
|||
09e7400349
|
|||
cb91a4bbf4
|
|||
f3156bcbd5
|
|||
cb71daec12
|
|||
63fd76c9c8
|
|||
e1e6a19f30
|
|||
d3479a6e80
|
|||
a24e44fbf3
|
|||
f3972a9ca2
|
|||
f323e3cb57
|
|||
426ec932ec
|
|||
3aa31a9dfa
|
|||
f0aba06f4f
|
|||
18b96064e4
|
|||
91887c0185
|
|||
3b9d91f186
|
|||
0fb44bb1ae
|
10
.build.yml
10
.build.yml
@@ -1,10 +0,0 @@
|
||||
pipeline:
|
||||
step: compile
|
||||
commands:
|
||||
- make debug
|
||||
step: install
|
||||
commands:
|
||||
- make install
|
||||
step: test
|
||||
commands:
|
||||
- make tests
|
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
custom: https://paypal.me/pools/c/8hAZMn97vE
|
20
.github/workflows/build.yml
vendored
Normal file
20
.github/workflows/build.yml
vendored
Normal 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
|
1
Makefile
1
Makefile
@@ -190,3 +190,4 @@ style:
|
||||
astyle $(ASTYLE_FLAGS) --recursive ./*.c,*.h
|
||||
|
||||
tests: $(TESTS)
|
||||
|
||||
|
265
engine/api.c
265
engine/api.c
@@ -27,14 +27,6 @@
|
||||
*/
|
||||
static struct Global_Data {
|
||||
SyMemBackend sAllocator; /* Global low level memory allocator */
|
||||
const SyMutexMethods *pMutexMethods; /* Mutex methods */
|
||||
SyMutex *pMutex; /* Global mutex */
|
||||
sxu32 nThreadingLevel; /* Threading level: 0 == Single threaded/1 == Multi-Threaded
|
||||
* The threading level can be set using the [ph7_lib_config()]
|
||||
* interface with a configuration verb set to
|
||||
* PH7_LIB_CONFIG_THREAD_LEVEL_SINGLE or
|
||||
* PH7_LIB_CONFIG_THREAD_LEVEL_MULTI
|
||||
*/
|
||||
const ph7_vfs *pVfs; /* Underlying virtual file system */
|
||||
sxi32 nEngine; /* Total number of active engines */
|
||||
ph7 *pEngines; /* List of active engine */
|
||||
@@ -44,23 +36,9 @@ static struct Global_Data {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
#define PH7_LIB_MAGIC 0xEA1495BA
|
||||
/*
|
||||
* Supported threading level.
|
||||
* PH7_THREAD_LEVEL_SINGLE:
|
||||
* In this mode,mutexing is disabled and the library can only be used by a single thread.
|
||||
* PH7_THREAD_LEVEL_MULTI
|
||||
* In this mode, all mutexes including the recursive mutexes on [ph7] objects
|
||||
* are enabled so that the application is free to share the same engine
|
||||
* between different threads at the same time.
|
||||
*/
|
||||
#define PH7_THREAD_LEVEL_SINGLE 1
|
||||
#define PH7_THREAD_LEVEL_MULTI 2
|
||||
/*
|
||||
* Configure a running PH7 engine instance.
|
||||
* return PH7_OK on success.Any other return
|
||||
@@ -186,63 +164,6 @@ static sxi32 PH7CoreConfigure(sxi32 nOp, va_list ap) {
|
||||
sMPGlobal.sAllocator.pUserData = pUserData;
|
||||
break;
|
||||
}
|
||||
case PH7_LIB_CONFIG_USER_MUTEX: {
|
||||
/* Use an alternative low-level mutex subsystem */
|
||||
const SyMutexMethods *pMethods = va_arg(ap, const SyMutexMethods *);
|
||||
if(pMethods == 0) {
|
||||
rc = PH7_CORRUPT;
|
||||
}
|
||||
/* Sanity check */
|
||||
if(pMethods->xEnter == 0 || pMethods->xLeave == 0 || pMethods->xNew == 0) {
|
||||
/* At least three criticial callbacks xEnter(),xLeave() and xNew() must be supplied */
|
||||
rc = PH7_CORRUPT;
|
||||
break;
|
||||
}
|
||||
if(sMPGlobal.pMutexMethods) {
|
||||
/* Overwrite the previous mutex subsystem */
|
||||
SyMutexRelease(sMPGlobal.pMutexMethods, sMPGlobal.pMutex);
|
||||
if(sMPGlobal.pMutexMethods->xGlobalRelease) {
|
||||
sMPGlobal.pMutexMethods->xGlobalRelease();
|
||||
}
|
||||
sMPGlobal.pMutex = 0;
|
||||
}
|
||||
/* Initialize and install the new mutex subsystem */
|
||||
if(pMethods->xGlobalInit) {
|
||||
rc = pMethods->xGlobalInit();
|
||||
if(rc != PH7_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Create the global mutex */
|
||||
sMPGlobal.pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
|
||||
if(sMPGlobal.pMutex == 0) {
|
||||
/*
|
||||
* If the supplied mutex subsystem is so sick that we are unable to
|
||||
* create a single mutex,there is no much we can do here.
|
||||
*/
|
||||
if(pMethods->xGlobalRelease) {
|
||||
pMethods->xGlobalRelease();
|
||||
}
|
||||
rc = PH7_CORRUPT;
|
||||
break;
|
||||
}
|
||||
sMPGlobal.pMutexMethods = pMethods;
|
||||
if(sMPGlobal.nThreadingLevel == 0) {
|
||||
/* Set a default threading level */
|
||||
sMPGlobal.nThreadingLevel = PH7_THREAD_LEVEL_MULTI;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PH7_LIB_CONFIG_THREAD_LEVEL_SINGLE:
|
||||
/* Single thread mode(Only one thread is allowed to play with the library) */
|
||||
sMPGlobal.nThreadingLevel = PH7_THREAD_LEVEL_SINGLE;
|
||||
break;
|
||||
case PH7_LIB_CONFIG_THREAD_LEVEL_MULTI:
|
||||
/* Multi-threading mode (library is thread safe and PH7 engines and virtual machines
|
||||
* may be shared between multiple threads).
|
||||
*/
|
||||
sMPGlobal.nThreadingLevel = PH7_THREAD_LEVEL_MULTI;
|
||||
break;
|
||||
default:
|
||||
/* Unknown configuration option */
|
||||
rc = PH7_CORRUPT;
|
||||
@@ -278,8 +199,6 @@ int ph7_lib_config(int nConfigOp, ...) {
|
||||
*/
|
||||
static sxi32 PH7CoreInitialize(void) {
|
||||
const ph7_vfs *pVfs; /* Built-in vfs */
|
||||
const SyMutexMethods *pMutexMethods = 0;
|
||||
SyMutex *pMaster = 0;
|
||||
int rc;
|
||||
/*
|
||||
* If the library is already initialized,then a call to this routine
|
||||
@@ -292,30 +211,6 @@ static sxi32 PH7CoreInitialize(void) {
|
||||
pVfs = PH7_ExportBuiltinVfs();
|
||||
/* Install it */
|
||||
ph7_lib_config(PH7_LIB_CONFIG_VFS, pVfs);
|
||||
if(sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_SINGLE) {
|
||||
pMutexMethods = sMPGlobal.pMutexMethods;
|
||||
if(pMutexMethods == 0) {
|
||||
/* Use the built-in mutex subsystem */
|
||||
pMutexMethods = SyMutexExportMethods();
|
||||
if(pMutexMethods == 0) {
|
||||
return PH7_CORRUPT; /* Can't happen */
|
||||
}
|
||||
/* Install the mutex subsystem */
|
||||
rc = ph7_lib_config(PH7_LIB_CONFIG_USER_MUTEX, pMutexMethods);
|
||||
if(rc != PH7_OK) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
/* Obtain a static mutex so we can initialize the library without calling malloc() */
|
||||
pMaster = SyMutexNew(pMutexMethods, SXMUTEX_TYPE_STATIC_1);
|
||||
if(pMaster == 0) {
|
||||
return PH7_CORRUPT; /* Can't happen */
|
||||
}
|
||||
}
|
||||
/* Lock the master mutex */
|
||||
rc = PH7_OK;
|
||||
SyMutexEnter(pMutexMethods, pMaster); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */
|
||||
if(sMPGlobal.nMagic != PH7_LIB_MAGIC) {
|
||||
if(sMPGlobal.sAllocator.pMethods == 0) {
|
||||
/* Install a memory subsystem */
|
||||
rc = ph7_lib_config(PH7_LIB_CONFIG_USER_MALLOC, 0); /* zero mean use the built-in memory backend */
|
||||
@@ -324,20 +219,10 @@ static sxi32 PH7CoreInitialize(void) {
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE) {
|
||||
/* Protect the memory allocation subsystem */
|
||||
rc = SyMemBackendMakeThreadSafe(&sMPGlobal.sAllocator, sMPGlobal.pMutexMethods);
|
||||
if(rc != PH7_OK) {
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
/* Our library is initialized,set the magic number */
|
||||
sMPGlobal.nMagic = PH7_LIB_MAGIC;
|
||||
rc = PH7_OK;
|
||||
} /* sMPGlobal.nMagic != PH7_LIB_MAGIC */
|
||||
End:
|
||||
/* Unlock the master mutex */
|
||||
SyMutexLeave(pMutexMethods, pMaster); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
@@ -391,18 +276,6 @@ static void PH7CoreShutdown(void) {
|
||||
pEngine = pNext;
|
||||
sMPGlobal.nEngine--;
|
||||
}
|
||||
/* Release the mutex subsystem */
|
||||
if(sMPGlobal.pMutexMethods) {
|
||||
if(sMPGlobal.pMutex) {
|
||||
SyMutexRelease(sMPGlobal.pMutexMethods, sMPGlobal.pMutex);
|
||||
sMPGlobal.pMutex = 0;
|
||||
}
|
||||
if(sMPGlobal.pMutexMethods->xGlobalRelease) {
|
||||
sMPGlobal.pMutexMethods->xGlobalRelease();
|
||||
}
|
||||
sMPGlobal.pMutexMethods = 0;
|
||||
}
|
||||
sMPGlobal.nThreadingLevel = 0;
|
||||
if(sMPGlobal.sAllocator.pMethods) {
|
||||
/* Release the memory backend */
|
||||
SyMemBackendRelease(&sMPGlobal.sAllocator);
|
||||
@@ -429,14 +302,8 @@ int ph7_lib_is_threadsafe(void) {
|
||||
if(sMPGlobal.nMagic != PH7_LIB_MAGIC) {
|
||||
return 0;
|
||||
}
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE) {
|
||||
/* Muli-threading support is enabled */
|
||||
return 1;
|
||||
} else {
|
||||
/* Single-threading */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* [CAPIREF: ph7_lib_version()]
|
||||
* Please refer to the official documentation for function purpose and expected parameters.
|
||||
@@ -468,17 +335,9 @@ int ph7_config(ph7 *pEngine, int nConfigOp, ...) {
|
||||
if(PH7_ENGINE_MISUSE(pEngine)) {
|
||||
return PH7_CORRUPT;
|
||||
}
|
||||
/* Acquire engine mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_ENGINE_RELEASE(pEngine)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
va_start(ap, nConfigOp);
|
||||
rc = EngineConfig(&(*pEngine), nConfigOp, ap);
|
||||
va_end(ap);
|
||||
/* Leave engine mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
@@ -510,28 +369,15 @@ int ph7_init(ph7 **ppEngine) {
|
||||
if(rc != PH7_OK) {
|
||||
goto Release;
|
||||
}
|
||||
SyMemBackendDisbaleMutexing(&pEngine->sAllocator);
|
||||
/* Default configuration */
|
||||
SyBlobInit(&pEngine->xConf.sErrConsumer, &pEngine->sAllocator);
|
||||
/* Install a default compile-time error consumer routine */
|
||||
ph7_config(pEngine, PH7_CONFIG_ERR_OUTPUT, PH7_VmBlobConsumer, &pEngine->xConf.sErrConsumer);
|
||||
/* Built-in vfs */
|
||||
pEngine->pVfs = sMPGlobal.pVfs;
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE) {
|
||||
/* Associate a recursive mutex with this instance */
|
||||
pEngine->pMutex = SyMutexNew(sMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
|
||||
if(pEngine->pMutex == 0) {
|
||||
rc = PH7_NOMEM;
|
||||
goto Release;
|
||||
}
|
||||
}
|
||||
/* Link to the list of active engines */
|
||||
/* Enter the global mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */
|
||||
MACRO_LD_PUSH(sMPGlobal.pEngines, pEngine);
|
||||
sMPGlobal.nEngine++;
|
||||
/* Leave the global mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */
|
||||
/* Write a pointer to the new instance */
|
||||
*ppEngine = pEngine;
|
||||
return PH7_OK;
|
||||
@@ -549,25 +395,11 @@ int ph7_release(ph7 *pEngine) {
|
||||
if(PH7_ENGINE_MISUSE(pEngine)) {
|
||||
return PH7_CORRUPT;
|
||||
}
|
||||
/* Acquire engine mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_ENGINE_RELEASE(pEngine)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
/* Release the engine */
|
||||
rc = EngineRelease(&(*pEngine));
|
||||
/* Leave engine mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
/* Release engine mutex */
|
||||
SyMutexRelease(sMPGlobal.pMutexMethods, pEngine->pMutex) /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
/* Enter the global mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */
|
||||
/* Unlink from the list of active engines */
|
||||
MACRO_LD_REMOVE(sMPGlobal.pEngines, pEngine);
|
||||
sMPGlobal.nEngine--;
|
||||
/* Leave the global mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, sMPGlobal.pMutex); /* NO-OP if sMPGlobal.nThreadingLevel == PH7_THREAD_LEVEL_SINGLE */
|
||||
/* Release the memory chunk allocated to this engine */
|
||||
SyMemBackendPoolFree(&sMPGlobal.sAllocator, pEngine);
|
||||
return rc;
|
||||
@@ -664,13 +496,6 @@ static sxi32 ProcessSourceFile(
|
||||
if(rc != PH7_OK) {
|
||||
goto Release;
|
||||
}
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE) {
|
||||
/* Associate a recursive mutex with this instance */
|
||||
pVm->pMutex = SyMutexNew(sMPGlobal.pMutexMethods, SXMUTEX_TYPE_RECURSIVE);
|
||||
if(pVm->pMutex == 0) {
|
||||
goto Release;
|
||||
}
|
||||
}
|
||||
/* Script successfully compiled,link to the list of active virtual machines */
|
||||
MACRO_LD_PUSH(pEngine->pVms, pVm);
|
||||
pEngine->iVm++;
|
||||
@@ -697,16 +522,8 @@ int ph7_compile_code(ph7 *pEngine, const char *zSource, int nLen, ph7_vm **ppOut
|
||||
nLen = (int)SyStrlen(zSource);
|
||||
}
|
||||
SyStringInitFromBuf(&sScript, zSource, nLen);
|
||||
/* Acquire engine mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_ENGINE_RELEASE(pEngine)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
/* Compile the script */
|
||||
rc = ProcessSourceFile(&(*pEngine), ppOutVm, &sScript, 0);
|
||||
/* Leave engine mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
/* Compilation result */
|
||||
return rc;
|
||||
}
|
||||
@@ -721,12 +538,6 @@ int ph7_compile_file(ph7 *pEngine, const char *zFilePath, ph7_vm **ppOutVm) {
|
||||
if(PH7_ENGINE_MISUSE(pEngine) || SX_EMPTY_STR(zFilePath)) {
|
||||
return PH7_CORRUPT;
|
||||
}
|
||||
/* Acquire engine mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_ENGINE_RELEASE(pEngine)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
/*
|
||||
* Check if the underlying vfs implement the memory map
|
||||
* [i.e: mmap() under UNIX/MapViewOfFile() under windows] function.
|
||||
@@ -754,8 +565,6 @@ int ph7_compile_file(ph7 *pEngine, const char *zFilePath, ph7_vm **ppOutVm) {
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Leave engine mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
/* Compilation result */
|
||||
return rc;
|
||||
}
|
||||
@@ -787,18 +596,10 @@ int ph7_vm_config(ph7_vm *pVm, int iConfigOp, ...) {
|
||||
if(PH7_VM_MISUSE(pVm)) {
|
||||
return PH7_CORRUPT;
|
||||
}
|
||||
/* Acquire VM mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_VM_RELEASE(pVm)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
/* Configure the virtual machine */
|
||||
va_start(ap, iConfigOp);
|
||||
rc = PH7_VmConfigure(&(*pVm), iConfigOp, ap);
|
||||
va_end(ap);
|
||||
/* Leave VM mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
@@ -811,20 +612,12 @@ int ph7_vm_exec(ph7_vm *pVm, int *pExitStatus) {
|
||||
if(PH7_VM_MISUSE(pVm)) {
|
||||
return PH7_CORRUPT;
|
||||
}
|
||||
/* Acquire VM mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_VM_RELEASE(pVm)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
/* Execute PH7 byte-code */
|
||||
rc = PH7_VmByteCodeExec(&(*pVm));
|
||||
if(pExitStatus) {
|
||||
/* Exit status */
|
||||
*pExitStatus = pVm->iExitStatus;
|
||||
}
|
||||
/* Leave VM mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
/* Execution result */
|
||||
return rc;
|
||||
}
|
||||
@@ -838,15 +631,7 @@ int ph7_vm_reset(ph7_vm *pVm) {
|
||||
if(PH7_VM_MISUSE(pVm)) {
|
||||
return PH7_CORRUPT;
|
||||
}
|
||||
/* Acquire VM mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_VM_RELEASE(pVm)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
rc = PH7_VmReset(&(*pVm));
|
||||
/* Leave VM mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
@@ -860,32 +645,14 @@ int ph7_vm_release(ph7_vm *pVm) {
|
||||
if(PH7_VM_MISUSE(pVm)) {
|
||||
return PH7_CORRUPT;
|
||||
}
|
||||
/* Acquire VM mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_VM_RELEASE(pVm)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
pEngine = pVm->pEngine;
|
||||
rc = PH7_VmRelease(&(*pVm));
|
||||
/* Leave VM mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
/* free VM mutex */
|
||||
SyMutexRelease(sMPGlobal.pMutexMethods, pVm->pMutex);
|
||||
if(rc == PH7_OK) {
|
||||
/* Unlink from the list of active VM */
|
||||
/* Acquire engine mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_ENGINE_RELEASE(pEngine)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
MACRO_LD_REMOVE(pEngine->pVms, pVm);
|
||||
pEngine->iVm--;
|
||||
/* Release the memory chunk allocated to this VM */
|
||||
SyMemBackendPoolFree(&pEngine->sAllocator, pVm);
|
||||
/* Leave engine mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pEngine->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -907,16 +674,8 @@ int ph7_create_function(ph7_vm *pVm, const char *zName, int (*xFunc)(ph7_context
|
||||
if(sName.nByte < 1 || xFunc == 0) {
|
||||
return PH7_CORRUPT;
|
||||
}
|
||||
/* Acquire VM mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_VM_RELEASE(pVm)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
/* Install the foreign function */
|
||||
rc = PH7_VmInstallForeignFunction(&(*pVm), &sName, xFunc, pUserData);
|
||||
/* Leave VM mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
@@ -930,12 +689,6 @@ int ph7_delete_function(ph7_vm *pVm, const char *zName) {
|
||||
if(PH7_VM_MISUSE(pVm)) {
|
||||
return PH7_CORRUPT;
|
||||
}
|
||||
/* Acquire VM mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_VM_RELEASE(pVm)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
/* Perform the deletion */
|
||||
rc = SyHashDeleteEntry(&pVm->hHostFunction, (const void *)zName, SyStrlen(zName), (void **)&pFunc);
|
||||
if(rc == PH7_OK) {
|
||||
@@ -944,8 +697,6 @@ int ph7_delete_function(ph7_vm *pVm, const char *zName) {
|
||||
SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pFunc->sName));
|
||||
SyMemBackendPoolFree(&pVm->sAllocator, pFunc);
|
||||
}
|
||||
/* Leave VM mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
@@ -970,16 +721,8 @@ int ph7_create_constant(ph7_vm *pVm, const char *zName, void (*xExpand)(ph7_valu
|
||||
if(xExpand == 0) {
|
||||
return PH7_CORRUPT;
|
||||
}
|
||||
/* Acquire VM mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_VM_RELEASE(pVm)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
/* Perform the registration */
|
||||
rc = PH7_VmRegisterConstant(&(*pVm), &sName, xExpand, pUserData, TRUE);
|
||||
/* Leave VM mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
@@ -993,12 +736,6 @@ int ph7_delete_constant(ph7_vm *pVm, const char *zName) {
|
||||
if(PH7_VM_MISUSE(pVm)) {
|
||||
return PH7_CORRUPT;
|
||||
}
|
||||
/* Acquire VM mutex */
|
||||
SyMutexEnter(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
if(sMPGlobal.nThreadingLevel > PH7_THREAD_LEVEL_SINGLE &&
|
||||
PH7_THRD_VM_RELEASE(pVm)) {
|
||||
return PH7_ABORT; /* Another thread have released this instance */
|
||||
}
|
||||
/* Query the constant hashtable */
|
||||
rc = SyHashDeleteEntry(&pVm->hConstant, (const void *)zName, SyStrlen(zName), (void **)&pCons);
|
||||
if(rc == PH7_OK) {
|
||||
@@ -1006,8 +743,6 @@ int ph7_delete_constant(ph7_vm *pVm, const char *zName) {
|
||||
SyMemBackendFree(&pVm->sAllocator, (void *)SyStringData(&pCons->sName));
|
||||
SyMemBackendPoolFree(&pVm->sAllocator, pCons);
|
||||
}
|
||||
/* Leave VM mutex */
|
||||
SyMutexLeave(sMPGlobal.pMutexMethods, pVm->pMutex); /* NO-OP if sMPGlobal.nThreadingLevel != PH7_THREAD_LEVEL_MULTI */
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
|
@@ -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 },
|
||||
|
@@ -1381,8 +1381,8 @@ static sxi32 PH7_CompileContinue(ph7_gen_state *pGen) {
|
||||
} else {
|
||||
sxu32 nInstrIdx = 0;
|
||||
if(!pLoop->bPostContinue) {
|
||||
/* Emit the OP_JMPLFE instruction to leave the loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPLFE, 0, 0, 0, 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);
|
||||
}
|
||||
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx);
|
||||
if(pLoop->bPostContinue) {
|
||||
@@ -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;
|
||||
/* Emit the OP_JMPLFE instruction to leave the loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPLFE, 0, 0, 0, 0);
|
||||
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 */
|
||||
@@ -1548,7 +1550,7 @@ static sxi32 PH7_CompileGoto(ph7_gen_state *pGen)
|
||||
sJump.pFunc = 0;
|
||||
}
|
||||
/* Make sure there will not stay any loop frame opened (i.e. goto inside a loop) */
|
||||
PH7_VmEmitInstr(pGen->pVm, sJump.nLine, PH7_OP_JMPLFE, 0, 0, 0, 0);
|
||||
PH7_VmEmitInstr(pGen->pVm, sJump.nLine, PH7_OP_LF_STOP, 0, 0, 0, 0);
|
||||
/* Emit the unconditional jump */
|
||||
if(SXRET_OK == PH7_VmEmitInstr(pGen->pVm, sJump.nLine, PH7_OP_JMP, 0, 0, 0, &sJump.nInstrIdx)) {
|
||||
SySetPut(&pGen->aGoto, (const void *)&sJump);
|
||||
@@ -1700,15 +1702,15 @@ static sxi32 PH7_CompileWhile(ph7_gen_state *pGen) {
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPZ, 0, 0, 0, &nFalseJump);
|
||||
/* Save the instruction index so we can fix it later when the jump destination is resolved */
|
||||
PH7_GenStateNewJumpFixup(pWhileBlock, PH7_OP_JMPZ, nFalseJump);
|
||||
/* Emit the OP_JMPLFB instruction to enter a loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPLFB, 0, 0, 0, 0);
|
||||
/* Emit the OP_LF_START instruction to enter a loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_LF_START, 0, 0, 0, 0);
|
||||
/* Compile the loop body */
|
||||
rc = PH7_CompileBlock(&(*pGen));
|
||||
if(rc == SXERR_ABORT) {
|
||||
return SXERR_ABORT;
|
||||
}
|
||||
/* Emit the OP_JMPLFE instruction to leave the loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPLFE, 0, 0, 0, 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);
|
||||
/* Emit the unconditional jump to the start of the loop */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMP, 0, pWhileBlock->nFirstInstr, 0, 0);
|
||||
/* Fix all jumps now the destination is resolved */
|
||||
@@ -1755,8 +1757,8 @@ static sxi32 PH7_CompileDoWhile(ph7_gen_state *pGen) {
|
||||
}
|
||||
/* Deffer 'continue;' jumps until we compile the block */
|
||||
pDoBlock->bPostContinue = TRUE;
|
||||
/* Emit the OP_JMPLFB instruction to enter a loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPLFB, 0, 0, 0, 0);
|
||||
/* Emit the OP_LF_START instruction to enter a loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_LF_START, 0, 0, 0, 0);
|
||||
rc = PH7_CompileBlock(&(*pGen));
|
||||
if(rc == SXERR_ABORT) {
|
||||
return SXERR_ABORT;
|
||||
@@ -1814,8 +1816,8 @@ static sxi32 PH7_CompileDoWhile(ph7_gen_state *pGen) {
|
||||
}
|
||||
pGen->pIn = &pEnd[1];
|
||||
pGen->pEnd = pTmp;
|
||||
/* Emit the OP_JMPLFE instruction to leave the loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPLFE, 0, 0, 0, 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);
|
||||
/* Emit the true jump to the beginning of the loop */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPNZ, 0, pDoBlock->nFirstInstr, 0, 0);
|
||||
/* Fix all jumps now the 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 */
|
||||
@@ -1918,8 +1922,8 @@ static sxi32 PH7_CompileFor(ph7_gen_state *pGen) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
|
||||
"for: Expected ';' after conditionals expressions");
|
||||
}
|
||||
/* Emit the OP_JMPLFB instruction to enter a loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPLFB, 0, 0, 0, 0);
|
||||
/* Emit the OP_LF_START instruction to enter a loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_LF_START, 0, 0, 0, 0);
|
||||
/* Jump the trailing ';' */
|
||||
pGen->pIn++;
|
||||
/* Save the post condition stream */
|
||||
@@ -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);
|
||||
@@ -1968,8 +1972,8 @@ static sxi32 PH7_CompileFor(ph7_gen_state *pGen) {
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_POP, 1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
/* Emit the OP_JMPLFE instruction to leave the loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPLFE, 0, 0, 0, 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);
|
||||
/* Emit the unconditional jump to the start of the loop */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMP, 0, pForBlock->nFirstInstr, 0, 0);
|
||||
/* Fix all jumps now the destination is resolved */
|
||||
@@ -2158,8 +2162,8 @@ static sxi32 PH7_CompileForeach(ph7_gen_state *pGen) {
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_FOREACH_STEP, 0, 0, pInfo, &nFalseJump);
|
||||
/* Save the instruction index so we can fix it later when the jump destination is resolved */
|
||||
PH7_GenStateNewJumpFixup(pForeachBlock, PH7_OP_FOREACH_STEP, nFalseJump);
|
||||
/* Emit the OP_JMPLFB instruction to enter a loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPLFB, 0, 0, 0, 0);
|
||||
/* Emit the OP_LF_START instruction to enter a loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_LF_START, 0, 0, 0, 0);
|
||||
/* Compile the loop body */
|
||||
pGen->pIn = &pEnd[1];
|
||||
pGen->pEnd = pTmp;
|
||||
@@ -2168,8 +2172,8 @@ static sxi32 PH7_CompileForeach(ph7_gen_state *pGen) {
|
||||
/* Don't worry about freeing memory, everything will be released shortly */
|
||||
return SXERR_ABORT;
|
||||
}
|
||||
/* Emit the OP_JMPLFE instruction to leave the loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPLFE, 0, 0, 0, 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);
|
||||
/* Emit the unconditional jump to the start of the loop */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMP, 0, pForeachBlock->nFirstInstr, 0, 0);
|
||||
/* Fix all jumps now the destination is resolved */
|
||||
@@ -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
373
engine/debug.c
Normal 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;
|
||||
}
|
@@ -273,33 +273,6 @@ PH7_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void *pChunk) {
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
PH7_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods) {
|
||||
SyMutex *pMutex;
|
||||
if(SXMEM_BACKEND_CORRUPT(pBackend) || pMethods == 0 || pMethods->xNew == 0) {
|
||||
return SXERR_CORRUPT;
|
||||
}
|
||||
pMutex = pMethods->xNew(SXMUTEX_TYPE_FAST);
|
||||
if(pMutex == 0) {
|
||||
return SXERR_OS;
|
||||
}
|
||||
/* Attach the mutex to the memory backend */
|
||||
pBackend->pMutex = pMutex;
|
||||
pBackend->pMutexMethods = pMethods;
|
||||
return SXRET_OK;
|
||||
}
|
||||
PH7_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend) {
|
||||
if(SXMEM_BACKEND_CORRUPT(pBackend)) {
|
||||
return SXERR_CORRUPT;
|
||||
}
|
||||
if(pBackend->pMutex == 0) {
|
||||
/* There is no mutex subsystem at all */
|
||||
return SXRET_OK;
|
||||
}
|
||||
SyMutexRelease(pBackend->pMutexMethods, pBackend->pMutex);
|
||||
pBackend->pMutexMethods = 0;
|
||||
pBackend->pMutex = 0;
|
||||
return SXRET_OK;
|
||||
}
|
||||
/*
|
||||
* Memory pool allocator
|
||||
*/
|
||||
@@ -564,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) {
|
||||
|
@@ -1,222 +0,0 @@
|
||||
/**
|
||||
* @PROJECT PH7 Engine for the AerScript Interpreter
|
||||
* @COPYRIGHT See COPYING in the top level directory
|
||||
* @FILE engine/lib/mutex.c
|
||||
* @DESCRIPTION Thread safe MUTEX implementation for the PH7 Engine
|
||||
* @DEVELOPERS Symisc Systems <devel@symisc.net>
|
||||
* Rafal Kupiec <belliash@codingworkshop.eu.org>
|
||||
*/
|
||||
#include "ph7int.h"
|
||||
#if defined(__WINNT__)
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#if defined(__WINNT__)
|
||||
struct SyMutex {
|
||||
CRITICAL_SECTION sMutex;
|
||||
sxu32 nType; /* Mutex type,one of SXMUTEX_TYPE_* */
|
||||
};
|
||||
/* Preallocated static mutex */
|
||||
static SyMutex aStaticMutexes[] = {
|
||||
{{0}, SXMUTEX_TYPE_STATIC_1},
|
||||
{{0}, SXMUTEX_TYPE_STATIC_2},
|
||||
{{0}, SXMUTEX_TYPE_STATIC_3},
|
||||
{{0}, SXMUTEX_TYPE_STATIC_4},
|
||||
{{0}, SXMUTEX_TYPE_STATIC_5},
|
||||
{{0}, SXMUTEX_TYPE_STATIC_6}
|
||||
};
|
||||
static BOOL winMutexInit = FALSE;
|
||||
static LONG winMutexLock = 0;
|
||||
|
||||
static sxi32 WinMutexGlobaInit(void) {
|
||||
LONG rc;
|
||||
rc = InterlockedCompareExchange(&winMutexLock, 1, 0);
|
||||
if(rc == 0) {
|
||||
sxu32 n;
|
||||
for(n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n) {
|
||||
InitializeCriticalSection(&aStaticMutexes[n].sMutex);
|
||||
}
|
||||
winMutexInit = TRUE;
|
||||
} else {
|
||||
/* Someone else is doing this for us */
|
||||
while(winMutexInit == FALSE) {
|
||||
Sleep(1);
|
||||
}
|
||||
}
|
||||
return SXRET_OK;
|
||||
}
|
||||
static void WinMutexGlobalRelease(void) {
|
||||
LONG rc;
|
||||
rc = InterlockedCompareExchange(&winMutexLock, 0, 1);
|
||||
if(rc == 1) {
|
||||
/* The first to decrement to zero does the actual global release */
|
||||
if(winMutexInit == TRUE) {
|
||||
sxu32 n;
|
||||
for(n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n) {
|
||||
DeleteCriticalSection(&aStaticMutexes[n].sMutex);
|
||||
}
|
||||
winMutexInit = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
static SyMutex *WinMutexNew(int nType) {
|
||||
SyMutex *pMutex = 0;
|
||||
if(nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE) {
|
||||
/* Allocate a new mutex */
|
||||
pMutex = (SyMutex *)HeapAlloc(GetProcessHeap(), 0, sizeof(SyMutex));
|
||||
if(pMutex == 0) {
|
||||
return 0;
|
||||
}
|
||||
InitializeCriticalSection(&pMutex->sMutex);
|
||||
} else {
|
||||
/* Use a pre-allocated static mutex */
|
||||
if(nType > SXMUTEX_TYPE_STATIC_6) {
|
||||
nType = SXMUTEX_TYPE_STATIC_6;
|
||||
}
|
||||
pMutex = &aStaticMutexes[nType - 3];
|
||||
}
|
||||
pMutex->nType = nType;
|
||||
return pMutex;
|
||||
}
|
||||
static void WinMutexRelease(SyMutex *pMutex) {
|
||||
if(pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE) {
|
||||
DeleteCriticalSection(&pMutex->sMutex);
|
||||
HeapFree(GetProcessHeap(), 0, pMutex);
|
||||
}
|
||||
}
|
||||
static void WinMutexEnter(SyMutex *pMutex) {
|
||||
EnterCriticalSection(&pMutex->sMutex);
|
||||
}
|
||||
static sxi32 WinMutexTryEnter(SyMutex *pMutex) {
|
||||
#ifdef _WIN32_WINNT
|
||||
BOOL rc;
|
||||
/* Only WindowsNT platforms */
|
||||
rc = TryEnterCriticalSection(&pMutex->sMutex);
|
||||
if(rc) {
|
||||
return SXRET_OK;
|
||||
} else {
|
||||
return SXERR_BUSY;
|
||||
}
|
||||
#else
|
||||
return SXERR_NOTIMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
static void WinMutexLeave(SyMutex *pMutex) {
|
||||
LeaveCriticalSection(&pMutex->sMutex);
|
||||
}
|
||||
/* Export Windows mutex interfaces */
|
||||
static const SyMutexMethods sWinMutexMethods = {
|
||||
WinMutexGlobaInit, /* xGlobalInit() */
|
||||
WinMutexGlobalRelease, /* xGlobalRelease() */
|
||||
WinMutexNew, /* xNew() */
|
||||
WinMutexRelease, /* xRelease() */
|
||||
WinMutexEnter, /* xEnter() */
|
||||
WinMutexTryEnter, /* xTryEnter() */
|
||||
WinMutexLeave /* xLeave() */
|
||||
};
|
||||
PH7_PRIVATE const SyMutexMethods *SyMutexExportMethods(void) {
|
||||
return &sWinMutexMethods;
|
||||
}
|
||||
#elif defined(__UNIXES__)
|
||||
#include <pthread.h>
|
||||
struct SyMutex {
|
||||
pthread_mutex_t sMutex;
|
||||
sxu32 nType;
|
||||
};
|
||||
static SyMutex *UnixMutexNew(int nType) {
|
||||
static SyMutex aStaticMutexes[] = {
|
||||
{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_1},
|
||||
{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_2},
|
||||
{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_3},
|
||||
{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_4},
|
||||
{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_5},
|
||||
{PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_6}
|
||||
};
|
||||
SyMutex *pMutex;
|
||||
if(nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE) {
|
||||
pthread_mutexattr_t sRecursiveAttr;
|
||||
/* Allocate a new mutex */
|
||||
pMutex = (SyMutex *)malloc(sizeof(SyMutex));
|
||||
if(pMutex == 0) {
|
||||
return 0;
|
||||
}
|
||||
if(nType == SXMUTEX_TYPE_RECURSIVE) {
|
||||
pthread_mutexattr_init(&sRecursiveAttr);
|
||||
pthread_mutexattr_settype(&sRecursiveAttr, PTHREAD_MUTEX_RECURSIVE);
|
||||
}
|
||||
pthread_mutex_init(&pMutex->sMutex, nType == SXMUTEX_TYPE_RECURSIVE ? &sRecursiveAttr : 0);
|
||||
if(nType == SXMUTEX_TYPE_RECURSIVE) {
|
||||
pthread_mutexattr_destroy(&sRecursiveAttr);
|
||||
}
|
||||
} else {
|
||||
/* Use a pre-allocated static mutex */
|
||||
if(nType > SXMUTEX_TYPE_STATIC_6) {
|
||||
nType = SXMUTEX_TYPE_STATIC_6;
|
||||
}
|
||||
pMutex = &aStaticMutexes[nType - 3];
|
||||
}
|
||||
pMutex->nType = nType;
|
||||
return pMutex;
|
||||
}
|
||||
static void UnixMutexRelease(SyMutex *pMutex) {
|
||||
if(pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE) {
|
||||
pthread_mutex_destroy(&pMutex->sMutex);
|
||||
free(pMutex);
|
||||
}
|
||||
}
|
||||
static void UnixMutexEnter(SyMutex *pMutex) {
|
||||
pthread_mutex_lock(&pMutex->sMutex);
|
||||
}
|
||||
static void UnixMutexLeave(SyMutex *pMutex) {
|
||||
pthread_mutex_unlock(&pMutex->sMutex);
|
||||
}
|
||||
/* Export pthread mutex interfaces */
|
||||
static const SyMutexMethods sPthreadMutexMethods = {
|
||||
0, /* xGlobalInit() */
|
||||
0, /* xGlobalRelease() */
|
||||
UnixMutexNew, /* xNew() */
|
||||
UnixMutexRelease, /* xRelease() */
|
||||
UnixMutexEnter, /* xEnter() */
|
||||
0, /* xTryEnter() */
|
||||
UnixMutexLeave /* xLeave() */
|
||||
};
|
||||
PH7_PRIVATE const SyMutexMethods *SyMutexExportMethods(void) {
|
||||
return &sPthreadMutexMethods;
|
||||
}
|
||||
#else
|
||||
/* Host application must register their own mutex subsystem if the target
|
||||
* platform is not an UNIX-like or windows systems.
|
||||
*/
|
||||
struct SyMutex {
|
||||
sxu32 nType;
|
||||
};
|
||||
static SyMutex *DummyMutexNew(int nType) {
|
||||
static SyMutex sMutex;
|
||||
SXUNUSED(nType);
|
||||
return &sMutex;
|
||||
}
|
||||
static void DummyMutexRelease(SyMutex *pMutex) {
|
||||
SXUNUSED(pMutex);
|
||||
}
|
||||
static void DummyMutexEnter(SyMutex *pMutex) {
|
||||
SXUNUSED(pMutex);
|
||||
}
|
||||
static void DummyMutexLeave(SyMutex *pMutex) {
|
||||
SXUNUSED(pMutex);
|
||||
}
|
||||
/* Export the dummy mutex interfaces */
|
||||
static const SyMutexMethods sDummyMutexMethods = {
|
||||
0, /* xGlobalInit() */
|
||||
0, /* xGlobalRelease() */
|
||||
DummyMutexNew, /* xNew() */
|
||||
DummyMutexRelease, /* xRelease() */
|
||||
DummyMutexEnter, /* xEnter() */
|
||||
0, /* xTryEnter() */
|
||||
DummyMutexLeave /* xLeave() */
|
||||
};
|
||||
PH7_PRIVATE const SyMutexMethods *SyMutexExportMethods(void) {
|
||||
return &sDummyMutexMethods;
|
||||
}
|
||||
#endif /* __WINNT__ */
|
@@ -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);
|
||||
}
|
||||
@@ -724,6 +722,16 @@ PH7_PRIVATE sxi32 PH7_MemObjIsNull(ph7_value *pObj) {
|
||||
/* Assume empty by default */
|
||||
return TRUE;
|
||||
}
|
||||
/*
|
||||
* Check whether the ph7_value is an array (hashmap)
|
||||
* Returns TRUE if hashmap, FALSE otherwise.
|
||||
*/
|
||||
PH7_PRIVATE sxi32 PH7_MemObjIsHashmap(ph7_value *pObj) {
|
||||
if(pObj->nType & MEMOBJ_HASHMAP) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* Check whether the ph7_value is numeric [i.e: int/float/bool] or looks
|
||||
* like a numeric number [i.e: if the ph7_value is of type string.].
|
||||
@@ -1068,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);
|
||||
@@ -1362,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) {
|
||||
|
@@ -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) {
|
||||
|
754
engine/vm.c
754
engine/vm.c
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
@@ -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.
|
||||
*
|
||||
|
@@ -806,8 +806,6 @@ struct ph7 {
|
||||
SyMemBackend sAllocator; /* Low level memory allocation subsystem */
|
||||
const ph7_vfs *pVfs; /* Underlying Virtual File System */
|
||||
ph7_conf xConf; /* Configuration */
|
||||
const SyMutexMethods *pMethods; /* Mutex methods */
|
||||
SyMutex *pMutex; /* Per-engine mutex */
|
||||
ph7_vm *pVms; /* List of active VM */
|
||||
sxi32 iVm; /* Total number of active VM */
|
||||
ph7 *pNext, *pPrev; /* List of active engines */
|
||||
@@ -1183,7 +1181,6 @@ struct ph7_switch {
|
||||
*/
|
||||
struct ph7_vm {
|
||||
SyMemBackend sAllocator; /* Memory backend */
|
||||
SyMutex *pMutex; /* Recursive mutex associated with VM */
|
||||
ph7 *pEngine; /* Interpreter that own this VM */
|
||||
SySet aInstrSet; /* Instructions debugging container */
|
||||
SySet aByteCode; /* Default bytecode container */
|
||||
@@ -1379,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 */
|
||||
@@ -1389,8 +1388,8 @@ enum ph7_vm_op {
|
||||
PH7_OP_JMP, /* Unconditional jump */
|
||||
PH7_OP_JMPZ, /* Jump on zero (FALSE jump) */
|
||||
PH7_OP_JMPNZ, /* Jump on non-zero (TRUE jump) */
|
||||
PH7_OP_JMPLFB, /* Jump loop frame begin */
|
||||
PH7_OP_JMPLFE, /* Jump loop frame end */
|
||||
PH7_OP_LF_START, /* Loop frame start */
|
||||
PH7_OP_LF_STOP, /* Loop frame stop */
|
||||
PH7_OP_POP, /* Stack POP */
|
||||
PH7_OP_CVT_INT, /* Integer cast */
|
||||
PH7_OP_CVT_STR, /* String cast */
|
||||
@@ -1401,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 '+' */
|
||||
@@ -1435,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 '>>=' */
|
||||
@@ -1477,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 */
|
||||
@@ -1503,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: &= */
|
||||
@@ -1630,6 +1633,7 @@ PH7_PRIVATE sxi32 PH7_MemObjRelease(ph7_value *pObj);
|
||||
PH7_PRIVATE sxi32 PH7_MemObjToNumeric(ph7_value *pObj);
|
||||
PH7_PRIVATE ProcMemObjCast PH7_MemObjCastMethod(sxi32 iFlags);
|
||||
PH7_PRIVATE sxi32 PH7_MemObjIsNull(ph7_value *pObj);
|
||||
PH7_PRIVATE sxi32 PH7_MemObjIsHashmap(ph7_value *pObj);
|
||||
PH7_PRIVATE sxi32 PH7_MemObjIsNumeric(ph7_value *pObj);
|
||||
PH7_PRIVATE sxi32 PH7_MemObjIsEmpty(ph7_value *pObj);
|
||||
PH7_PRIVATE sxi32 PH7_MemObjToHashmap(ph7_value *pObj);
|
||||
@@ -1669,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);
|
||||
@@ -1871,8 +1876,6 @@ PH7_PRIVATE void *SyMemBackendPoolAlloc(SyMemBackend *pBackend, sxu32 nBytes);
|
||||
PH7_PRIVATE sxi32 SyMemBackendFree(SyMemBackend *pBackend, void *pChunk);
|
||||
PH7_PRIVATE void *SyMemBackendRealloc(SyMemBackend *pBackend, void *pOld, sxu32 nBytes);
|
||||
PH7_PRIVATE void *SyMemBackendAlloc(SyMemBackend *pBackend, sxu32 nBytes);
|
||||
PH7_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods);
|
||||
PH7_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend);
|
||||
PH7_PRIVATE sxu32 SyMemcpy(const void *pSrc, void *pDest, sxu32 nLen);
|
||||
PH7_PRIVATE sxi32 SyMemcmp(const void *pB1, const void *pB2, sxu32 nSize);
|
||||
PH7_PRIVATE void SyZero(void *pSrc, sxu32 nSize);
|
||||
@@ -1886,7 +1889,4 @@ PH7_PRIVATE sxu32 SyStrlen(const char *zSrc);
|
||||
PH7_PRIVATE sxu32 Systrcpy(char *zDest, sxu32 nDestLen, const char *zSrc, sxu32 nLen);
|
||||
PH7_PRIVATE char *SyStrtok(char *str, const char *sep);
|
||||
PH7_PRIVATE sxi32 SyAsciiToHex(sxi32 c);
|
||||
PH7_PRIVATE const SyMutexMethods *SyMutexExportMethods(void);
|
||||
PH7_PRIVATE sxi32 SyMemBackendMakeThreadSafe(SyMemBackend *pBackend, const SyMutexMethods *pMethods);
|
||||
PH7_PRIVATE sxi32 SyMemBackendDisbaleMutexing(SyMemBackend *pBackend);
|
||||
#endif /* __PH7INT_H__ */
|
@@ -55,10 +55,12 @@ class Base32 {
|
||||
if(!in_array($input[$i], Base32::$map))
|
||||
return '';
|
||||
for(int $j = 0; $j < 8; $j++) {
|
||||
if($i + $j < strlen($input)) {
|
||||
if(array_key_exists($input[$i + $j], Base32::$flippedMap)) {
|
||||
$x += str_pad(base_convert(Base32::$flippedMap[$input[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
}
|
||||
string[] $eightBits = str_split($x, 8);
|
||||
for(int $z = 0; $z < sizeof($eightBits); $z++) {
|
||||
char $y;
|
||||
|
43
tests/beaufort_cipher.aer
Normal file
43
tests/beaufort_cipher.aer
Normal 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
1
tests/beaufort_cipher.exp
Normal file
1
tests/beaufort_cipher.exp
Normal file
@@ -0,0 +1 @@
|
||||
string(20 'FHMEATLVATNNNIAAAINU')
|
8
tests/data/includes/base_class.aer
Normal file
8
tests/data/includes/base_class.aer
Normal file
@@ -0,0 +1,8 @@
|
||||
class BaseClass {
|
||||
|
||||
protected void run() {
|
||||
printf('Test launched...');
|
||||
include 'data/includes/include_test.aer';
|
||||
}
|
||||
|
||||
}
|
1
tests/data/includes/include_test.aer
Normal file
1
tests/data/includes/include_test.aer
Normal file
@@ -0,0 +1 @@
|
||||
printf("OK!\n");
|
3
tests/data/includes/test_class.aer
Normal file
3
tests/data/includes/test_class.aer
Normal file
@@ -0,0 +1,3 @@
|
||||
class Test {
|
||||
string $test = 'This is a test';
|
||||
}
|
6
tests/execute_system.aer
Normal file
6
tests/execute_system.aer
Normal 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
2
tests/execute_system.exp
Normal file
@@ -0,0 +1,2 @@
|
||||
OK
|
||||
Result: TRUE
|
12
tests/include_statements.aer
Normal file
12
tests/include_statements.aer
Normal 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);
|
||||
}
|
||||
|
||||
}
|
5
tests/include_statements.exp
Normal file
5
tests/include_statements.exp
Normal file
@@ -0,0 +1,5 @@
|
||||
Test launched...OK!
|
||||
object(Test) {
|
||||
['test'] =>
|
||||
string(14 'This is a test')
|
||||
}
|
@@ -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"));
|
||||
}
|
||||
|
||||
|
@@ -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,3 +1 @@
|
||||
bool(FALSE)
|
||||
bool(TRUE)
|
||||
bool(TRUE)
|
||||
|
105
tests/sudoku_solver.aer
Normal file
105
tests/sudoku_solver.aer
Normal 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
19
tests/sudoku_solver.exp
Normal 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
22
tests/tower_of_hanoi.aer
Normal 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
15
tests/tower_of_hanoi.exp
Normal 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
|
Reference in New Issue
Block a user