diff --git a/.build.yml b/.build.yml index dad8fac..c2a3822 100644 --- a/.build.yml +++ b/.build.yml @@ -7,4 +7,4 @@ pipeline: - make install step: test commands: - - make test + - make tests diff --git a/.vscode/tasks.json b/.vscode/tasks.json index e05894f..79161c5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,7 @@ "version": "2.0.0", "tasks": [ { - "label": "Build P# Interpreter", + "label": "Build AerScript Interpreter", "type": "shell", "command": "make", "group": { @@ -16,7 +16,7 @@ } }, { - "label": "Clean P# Interpreter", + "label": "Clean AerScript Interpreter", "type": "shell", "command": "make", "args": [ @@ -31,7 +31,7 @@ } }, { - "label": "Style P# Code", + "label": "Style AerScript Interpreter Code", "type": "shell", "command": "make", "args": [ diff --git a/Makefile b/Makefile index 0385608..0d6fe47 100644 --- a/Makefile +++ b/Makefile @@ -117,7 +117,7 @@ TESTS := $(subst .$(TEST_EXT),.test,$(wildcard $(TEST_DIR)/*.$(TEST_EXT))) .SUFFIXES: -.PHONY: clean debug install release style test +.PHONY: clean debug install release style tests debug: export CFLAGS := $(CFLAGS) $(DCFLAGS) debug: engine sapi modules @@ -183,4 +183,4 @@ install: engine modules sapi style: astyle $(ASTYLE_FLAGS) --recursive ./*.c,*.h -test: $(TESTS) +tests: $(TESTS) diff --git a/TODO b/TODO new file mode 100644 index 0000000..4306cf9 --- /dev/null +++ b/TODO @@ -0,0 +1,2 @@ +TODO list for typehinting branch. +Below list contains things that should be changed/fixed/implemented. diff --git a/engine/api.c b/engine/api.c index b6005b5..cfc02b8 100644 --- a/engine/api.c +++ b/engine/api.c @@ -1777,12 +1777,18 @@ int ph7_value_bool(ph7_value *pVal, int iBool) { return PH7_OK; } /* - * [CAPIREF: ph7_value_null()] + * [CAPIREF: ph7_value_char()] * Please refer to the official documentation for function purpose and expected parameters. */ -int ph7_value_null(ph7_value *pVal) { - /* Invalidate any prior representation and set the NULL flag */ +int ph7_value_char(ph7_value *pVal, int cValue) { + /* Invalidate any prior representation */ PH7_MemObjRelease(pVal); + if(cValue >= 0 && cValue <= 255) { + pVal->x.iVal = cValue; + } else { + pVal->x.iVal = 0; + } + MemObjSetType(pVal, MEMOBJ_CHAR); return PH7_OK; } /* @@ -1796,6 +1802,16 @@ int ph7_value_double(ph7_value *pVal, double Value) { MemObjSetType(pVal, MEMOBJ_REAL); return PH7_OK; } +/* + * [CAPIREF: ph7_value_void()] + * Please refer to the official documentation for function purpose and expected parameters. + */ +int ph7_value_void(ph7_value *pVal) { + /* Invalidate any prior representation */ + PH7_MemObjRelease(pVal); + MemObjSetType(pVal, MEMOBJ_VOID); + return PH7_OK; +} /* * [CAPIREF: ph7_value_string()] * Please refer to the official documentation for function purpose and expected parameters. @@ -1882,6 +1898,20 @@ int ph7_value_is_float(ph7_value *pVal) { int ph7_value_is_bool(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_BOOL) ? TRUE : FALSE; } +/* + * [CAPIREF: ph7_value_is_char()] + * Please refer to the official documentation for function purpose and expected parameters. + */ +int ph7_value_is_char(ph7_value *pVal) { + return (pVal->iFlags & MEMOBJ_CHAR) ? TRUE : FALSE; +} +/* + * [CAPIREF: ph7_value_is_void()] + * Please refer to the official documentation for function purpose and expected parameters. + */ +int ph7_value_is_void(ph7_value *pVal) { + return (pVal->iFlags & MEMOBJ_VOID) ? TRUE : FALSE; +} /* * [CAPIREF: ph7_value_is_string()] * Please refer to the official documentation for function purpose and expected parameters. @@ -1889,13 +1919,6 @@ int ph7_value_is_bool(ph7_value *pVal) { int ph7_value_is_string(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_STRING) ? TRUE : FALSE; } -/* - * [CAPIREF: ph7_value_is_null()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int ph7_value_is_null(ph7_value *pVal) { - return (pVal->iFlags & MEMOBJ_NULL) ? TRUE : FALSE; -} /* * [CAPIREF: ph7_value_is_numeric()] * Please refer to the official documentation for function purpose and expected parameters. @@ -1914,6 +1937,13 @@ int ph7_value_is_callable(ph7_value *pVal) { rc = PH7_VmIsCallable(pVal->pVm, pVal, FALSE); return rc; } +/* + * [CAPIREF: ph7_value_is_callback()] + * Please refer to the official documentation for function purpose and expected parameters. + */ +int ph7_value_is_callback(ph7_value *pVal) { + return (pVal->iFlags & MEMOBJ_CALL) ? TRUE : FALSE; +} /* * [CAPIREF: ph7_value_is_scalar()] * Please refer to the official documentation for function purpose and expected parameters. @@ -1942,12 +1972,3 @@ int ph7_value_is_object(ph7_value *pVal) { int ph7_value_is_resource(ph7_value *pVal) { return (pVal->iFlags & MEMOBJ_RES) ? TRUE : FALSE; } -/* - * [CAPIREF: ph7_value_is_empty()] - * Please refer to the official documentation for function purpose and expected parameters. - */ -int ph7_value_is_empty(ph7_value *pVal) { - int rc; - rc = PH7_MemObjIsEmpty(pVal); - return rc; -} diff --git a/engine/builtin.c b/engine/builtin.c index fa030e0..aaaf233 100644 --- a/engine/builtin.c +++ b/engine/builtin.c @@ -39,10 +39,42 @@ static int PH7_builtin_is_bool(ph7_context *pCtx, int nArg, ph7_value **apArg) { ph7_result_bool(pCtx, res); return PH7_OK; } +/* + * bool is_callback($var) + * Finds out whether a variable is a callback. + * Parameters + * $var: The variable being evaluated. + * Return + * TRUE if var is a callback. False otherwise. + */ +static int PH7_builtin_is_callback(ph7_context *pCtx, int nArg, ph7_value **apArg) { + int res = 0; /* Assume false by default */ + if(nArg > 0) { + res = ph7_value_is_callback(apArg[0]); + } + /* Query result */ + ph7_result_bool(pCtx, res); + return PH7_OK; +} +/* + * bool is_char($var) + * Finds out whether a variable is a character. + * Parameters + * $var: The variable being evaluated. + * Return + * TRUE if var is a character. False otherwise. + */ +static int PH7_builtin_is_char(ph7_context *pCtx, int nArg, ph7_value **apArg) { + int res = 0; /* Assume false by default */ + if(nArg > 0) { + res = ph7_value_is_char(apArg[0]); + } + /* Query result */ + ph7_result_bool(pCtx, res); + return PH7_OK; +} /* * bool is_float($var) - * bool is_real($var) - * bool is_double($var) * Finds out whether a variable is a float. * Parameters * $var: The variable being evaluated. @@ -60,8 +92,6 @@ static int PH7_builtin_is_float(ph7_context *pCtx, int nArg, ph7_value **apArg) } /* * bool is_int($var) - * bool is_integer($var) - * bool is_long($var) * Finds out whether a variable is an integer. * Parameters * $var: The variable being evaluated. @@ -95,17 +125,17 @@ static int PH7_builtin_is_string(ph7_context *pCtx, int nArg, ph7_value **apArg) return PH7_OK; } /* - * bool is_null($var) - * Finds out whether a variable is NULL. + * bool is_void($var) + * Finds out whether a variable is a void. * Parameters * $var: The variable being evaluated. * Return - * TRUE if var is NULL. False otherwise. + * TRUE if var is void. False otherwise. */ -static int PH7_builtin_is_null(ph7_context *pCtx, int nArg, ph7_value **apArg) { +static int PH7_builtin_is_void(ph7_context *pCtx, int nArg, ph7_value **apArg) { int res = 0; /* Assume false by default */ if(nArg > 0) { - res = ph7_value_is_null(apArg[0]); + res = ph7_value_is_void(apArg[0]); } /* Query result */ ph7_result_bool(pCtx, res); @@ -128,23 +158,6 @@ static int PH7_builtin_is_numeric(ph7_context *pCtx, int nArg, ph7_value **apArg ph7_result_bool(pCtx, res); return PH7_OK; } -/* - * bool is_scalar($var) - * Find out whether a variable is a scalar. - * Parameters - * $var: The variable being evaluated. - * Return - * True if var is scalar. False otherwise. - */ -static int PH7_builtin_is_scalar(ph7_context *pCtx, int nArg, ph7_value **apArg) { - int res = 0; /* Assume false by default */ - if(nArg > 0) { - res = ph7_value_is_scalar(apArg[0]); - } - /* Query result */ - ph7_result_bool(pCtx, res); - return PH7_OK; -} /* * bool is_array($var) * Find out whether a variable is an array. @@ -195,83 +208,6 @@ static int PH7_builtin_is_resource(ph7_context *pCtx, int nArg, ph7_value **apAr ph7_result_bool(pCtx, res); return PH7_OK; } -/* - * float floatval($var) - * Get float value of a variable. - * Parameter - * $var: The variable being processed. - * Return - * the float value of a variable. - */ -static int PH7_builtin_floatval(ph7_context *pCtx, int nArg, ph7_value **apArg) { - if(nArg < 1) { - /* return 0.0 */ - ph7_result_double(pCtx, 0); - } else { - double dval; - /* Perform the cast */ - dval = ph7_value_to_double(apArg[0]); - ph7_result_double(pCtx, dval); - } - return PH7_OK; -} -/* - * int intval($var) - * Get integer value of a variable. - * Parameter - * $var: The variable being processed. - * Return - * the int value of a variable. - */ -static int PH7_builtin_intval(ph7_context *pCtx, int nArg, ph7_value **apArg) { - if(nArg < 1) { - /* return 0 */ - ph7_result_int(pCtx, 0); - } else { - sxi64 iVal; - /* Perform the cast */ - iVal = ph7_value_to_int64(apArg[0]); - ph7_result_int64(pCtx, iVal); - } - return PH7_OK; -} -/* - * string strval($var) - * Get the string representation of a variable. - * Parameter - * $var: The variable being processed. - * Return - * the string value of a variable. - */ -static int PH7_builtin_strval(ph7_context *pCtx, int nArg, ph7_value **apArg) { - if(nArg < 1) { - /* return NULL */ - ph7_result_null(pCtx); - } else { - const char *zVal; - int iLen = 0; /* cc -O6 warning */ - /* Perform the cast */ - zVal = ph7_value_to_string(apArg[0], &iLen); - ph7_result_string(pCtx, zVal, iLen); - } - return PH7_OK; -} -/* - * bool empty($var) - * Determine whether a variable is empty. - * Parameters - * $var: The variable being checked. - * Return - * 0 if var has a non-empty and non-zero value.1 otherwise. - */ -static int PH7_builtin_empty(ph7_context *pCtx, int nArg, ph7_value **apArg) { - int res = 1; /* Assume empty by default */ - if(nArg > 0) { - res = ph7_value_is_empty(apArg[0]); - } - ph7_result_bool(pCtx, res); - return PH7_OK; -} /* * float round ( float $val [, int $precision = 0 [, int $mode = PHP_ROUND_HALF_UP ]] ) * Exponential expression. @@ -307,7 +243,7 @@ static int PH7_builtin_round(ph7_context *pCtx, int nArg, ph7_value **apArg) { r = ph7_value_to_double(apArg[0]); /* If Y==0 and X will fit in a 64-bit int, * handle the rounding directly.Otherwise - * use our own cutsom printf [i.e:SyBufferFormat()]. + * use our own custom printf [i.e:SyBufferFormat()]. */ if(n == 0 && r >= 0 && r < LARGEST_INT64 - 1) { r = (double)((ph7_int64)(r + 0.5)); @@ -4330,8 +4266,6 @@ static int PH7_builtin_str_getcsv(ph7_context *pCtx, int nArg, ph7_value **apArg pArray = ph7_context_new_array(pCtx); if(pArray == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_null(pCtx); - return PH7_OK; } /* Parse the raw input */ PH7_ProcessCsv(zInput, nLen, delim, encl, escape, PH7_CsvConsumer, pArray); @@ -5189,7 +5123,7 @@ static int PH7_builtin_strtok(ph7_context *pCtx, int nArg, ph7_value **apArg) { pAux = (strtok_aux_data *)ph7_context_peek_aux_data(pCtx); if(pAux == 0) { /* No aux data,return FALSE */ - ph7_result_bool(pCtx, 0); + ph7_result_string(pCtx, "", 0); return PH7_OK; } nMasklen = 0; @@ -5203,7 +5137,7 @@ static int PH7_builtin_strtok(ph7_context *pCtx, int nArg, ph7_value **apArg) { ph7_context_free_chunk(pCtx, (void *)pAux->zDup); ph7_context_free_chunk(pCtx, pAux); (void)ph7_context_pop_aux_data(pCtx); - ph7_result_bool(pCtx, 0); + ph7_result_string(pCtx, "", 0); return PH7_OK; } /* Extract the token */ @@ -5213,7 +5147,7 @@ static int PH7_builtin_strtok(ph7_context *pCtx, int nArg, ph7_value **apArg) { ph7_context_free_chunk(pCtx, (void *)pAux->zDup); ph7_context_free_chunk(pCtx, pAux); (void)ph7_context_pop_aux_data(pCtx); - ph7_result_bool(pCtx, 0); + ph7_result_string(pCtx, "", 0); } else { /* Return the extracted token */ ph7_result_string(pCtx, sToken.zString, (int)sToken.nByte); @@ -5226,7 +5160,7 @@ static int PH7_builtin_strtok(ph7_context *pCtx, int nArg, ph7_value **apArg) { zCur = zInput = ph7_value_to_string(apArg[0], &nLen); if(nLen < 1) { /* Empty input,return FALSE */ - ph7_result_bool(pCtx, 0); + ph7_result_string(pCtx, "", 0); return PH7_OK; } /* Extract the mask */ @@ -5242,7 +5176,7 @@ static int PH7_builtin_strtok(ph7_context *pCtx, int nArg, ph7_value **apArg) { rc = ExtractToken(&zInput, &zInput[nLen], zMask, nMasklen, &sToken); if(rc != SXRET_OK) { /* Empty input */ - ph7_result_bool(pCtx, 0); + ph7_result_string(pCtx, "", 0); return PH7_OK; } else { /* Return the extracted token */ @@ -5501,9 +5435,8 @@ static int StrReplaceWalker(ph7_value *pKey, ph7_value *pData, void *pUserData) TRUE /* Release the chunk automatically,upon this context is destroyed */ ); if(zDup == 0) { - /* Ignore any memory failure problem */ + /* Memory failure problem */ PH7_VmMemoryError(pRep->pCtx->pVm); - return PH7_OK; } SyMemcpy(zIn, zDup, (sxu32)nByte); /* Save the chunk */ @@ -5777,9 +5710,6 @@ PH7_PRIVATE sxi32 PH7_ParseIniString(ph7_context *pCtx, const char *zIn, sxu32 n if(pArray == 0 || pWorker == 0 || pValue == 0) { /* Out of memory */ PH7_VmMemoryError(pCtx->pVm); - /* Return FALSE */ - ph7_result_bool(pCtx, 0); - return PH7_OK; } SyHashInit(&sHash, &pCtx->pVm->sAllocator, 0, 0); pCur = pArray; @@ -7473,20 +7403,17 @@ static int PH7_builtin_urldecode(ph7_context *pCtx, int nArg, ph7_value **apArg) /* Table of the built-in functions */ static const ph7_builtin_func aBuiltInFunc[] = { /* Variable handling functions */ + { "is_array", PH7_builtin_is_array }, { "is_bool", PH7_builtin_is_bool }, + { "is_callback", PH7_builtin_is_callback }, + { "is_char", PH7_builtin_is_char }, { "is_float", PH7_builtin_is_float }, { "is_int", PH7_builtin_is_int }, - { "is_string", PH7_builtin_is_string }, - { "is_null", PH7_builtin_is_null }, - { "is_numeric", PH7_builtin_is_numeric }, - { "is_scalar", PH7_builtin_is_scalar }, - { "is_array", PH7_builtin_is_array }, { "is_object", PH7_builtin_is_object }, { "is_resource", PH7_builtin_is_resource }, - { "floatval", PH7_builtin_floatval }, - { "intval", PH7_builtin_intval }, - { "stringval", PH7_builtin_strval }, - { "empty", PH7_builtin_empty }, + { "is_string", PH7_builtin_is_string }, + { "is_void", PH7_builtin_is_void }, + { "is_numeric", PH7_builtin_is_numeric }, { "round", PH7_builtin_round }, { "dechex", PH7_builtin_dechex }, { "decoct", PH7_builtin_decoct }, diff --git a/engine/compiler.c b/engine/compiler.c index 9d0dcf7..b1181fd 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -794,13 +794,10 @@ static sxi32 PH7_GenStateArrayNodeValidator(ph7_gen_state *pGen, ph7_expr_node * PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen, sxi32 iCompileFlag) { sxi32(*xValidator)(ph7_gen_state *, ph7_expr_node *); /* Expression tree validator callback */ SyToken *pKey, *pCur; - sxi32 iEmitRef = 0; sxi32 nPair = 0; - sxi32 iNest; sxi32 rc; - /* Jump the 'array' keyword,the leading left parenthesis and the trailing parenthesis. - */ - pGen->pIn += 2; + /* Jump the opening and the trailing parenthesis. */ + pGen->pIn++; pGen->pEnd--; xValidator = 0; SXUNUSED(iCompileFlag); /* cc warning */ @@ -819,19 +816,10 @@ PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen, sxi32 iCompileFlag) { } /* Compile the key if available */ pKey = pCur; - iNest = 0; while(pCur < pGen->pIn) { - if((pCur->nType & PH7_TK_ARRAY_OP) && iNest <= 0) { + if(pCur->nType & PH7_TK_ARRAY_OP) { break; } - if(pCur->nType & PH7_TK_LPAREN /*'('*/) { - iNest++; - } else if(pCur->nType & PH7_TK_RPAREN /*')'*/) { - /* Don't worry about mismatched parenthesis here,the expression - * parser will shortly detect any syntax error. - */ - iNest--; - } pCur++; } rc = SXERR_EMPTY; @@ -863,31 +851,12 @@ PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen, sxi32 iCompileFlag) { /* No available key,load NULL */ PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOADC, 0, 0 /* nil index */, 0, 0); } - if(pCur->nType & PH7_TK_AMPER /*'&'*/) { - /* Insertion by reference, [i.e: $a = array(&$x);] */ - xValidator = PH7_GenStateArrayNodeValidator; /* Only variable are allowed */ - iEmitRef = 1; - pCur++; /* Jump the '&' token */ - if(pCur >= pGen->pIn) { - /* Missing value */ - rc = PH7_GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "array(): Missing referenced variable"); - if(rc == SXERR_ABORT) { - return SXERR_ABORT; - } - return SXRET_OK; - } - } /* Compile indice value */ rc = PH7_GenStateCompileArrayEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if non-existent*/, xValidator); if(rc == SXERR_ABORT) { return SXERR_ABORT; } - if(iEmitRef) { - /* Emit the load reference instruction */ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOAD_REF, 0, 0, 0, 0); - } xValidator = 0; - iEmitRef = 0; nPair++; } /* Emit the load map instruction */ @@ -925,49 +894,6 @@ static sxi32 PH7_GenStateListNodeValidator(ph7_gen_state *pGen, ph7_expr_node *p } return rc; } -/* - * Compile the 'list' language construct. - * list(): Assign variables as if they were an array. - * list() is used to assign a list of variables in one operation. - * Description - * array list (mixed $varname [, mixed $... ] ) - * Like array(), this is not really a function, but a language construct. - * list() is used to assign a list of variables in one operation. - * Parameters - * $varname: A variable. - * Return Values - * The assigned array. - */ -PH7_PRIVATE sxi32 PH7_CompileList(ph7_gen_state *pGen, sxi32 iCompileFlag) { - SyToken *pNext; - sxi32 nExpr; - sxi32 rc; - nExpr = 0; - /* Jump the 'list' keyword,the leading left parenthesis and the trailing parenthesis */ - pGen->pIn += 2; - pGen->pEnd--; - SXUNUSED(iCompileFlag); /* cc warning */ - while(SXRET_OK == PH7_GetNextExpr(pGen->pIn, pGen->pEnd, &pNext)) { - if(pGen->pIn < pNext) { - /* Compile the expression holding the variable */ - rc = PH7_GenStateCompileArrayEntry(&(*pGen), pGen->pIn, pNext, EXPR_FLAG_LOAD_IDX_STORE, PH7_GenStateListNodeValidator); - if(rc != SXRET_OK) { - /* Do not bother compiling this expression, it's broken anyway */ - return SXRET_OK; - } - } else { - /* Empty entry,load NULL */ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOADC, 0, 0/* NULL index */, 0, 0); - } - nExpr++; - /* Advance the stream cursor */ - pGen->pIn = &pNext[1]; - } - /* Emit the LOAD_LIST instruction */ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOAD_LIST, nExpr, 0, 0, 0); - /* Node successfully compiled */ - return SXRET_OK; -} /* * Compile a closure (anonymous function). * Closures (also known as anonymous functions), allow the creation of functions @@ -994,7 +920,36 @@ PH7_PRIVATE sxi32 PH7_CompileClosure(ph7_gen_state *pGen, sxi32 iCompileFlag) { sxu32 nIdx; sxu32 nLen; sxi32 rc; - pGen->pIn++; /* Jump the 'function' keyword */ + sxu32 nType; + sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData)); + if(nKey & PH7_KEYWORD_BOOL) { + nType = MEMOBJ_BOOL; + } else if(nKey & PH7_KEYWORD_CALLBACK) { + nType = MEMOBJ_CALL; + } else if(nKey & PH7_KEYWORD_CHAR) { + nType = MEMOBJ_CHAR; + } else if(nKey & PH7_KEYWORD_FLOAT) { + nType = MEMOBJ_REAL; + } else if(nKey & PH7_KEYWORD_INT) { + nType = MEMOBJ_INT; + } else if(nKey & PH7_KEYWORD_MIXED) { + nType = MEMOBJ_MIXED; + } else if(nKey & PH7_KEYWORD_OBJECT) { + nType = MEMOBJ_OBJ; + } else if(nKey & PH7_KEYWORD_RESOURCE) { + nType = MEMOBJ_RES; + } else if(nKey & PH7_KEYWORD_STRING) { + nType = MEMOBJ_STRING; + } else if(nKey & PH7_KEYWORD_VOID) { + nType = MEMOBJ_VOID; + } else { + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Invalid return data type '%z'", &pGen->pIn->sData); + } + pGen->pIn++; /* Jump the return data type */ + if(pGen->pIn->nType & PH7_TK_OSB && pGen->pIn[1].nType & PH7_TK_CSB) { + pGen->pIn += 2; + nType |= MEMOBJ_HASHMAP; + } if(pGen->pIn->nType & (PH7_TK_ID | PH7_TK_KEYWORD)) { pGen->pIn++; } @@ -1013,11 +968,13 @@ PH7_PRIVATE sxi32 PH7_CompileClosure(ph7_gen_state *pGen, sxi32 iCompileFlag) { } SyStringInitFromBuf(&sName, zName, nLen); PH7_MemObjInitFromString(pGen->pVm, pObj, &sName); + pObj->iFlags = MEMOBJ_CALL; /* Compile the closure body */ rc = PH7_GenStateCompileFunc(&(*pGen), &sName, 0, TRUE, &pAnonFunc); if(rc == SXERR_ABORT) { return SXERR_ABORT; } + pAnonFunc->nType = nType; if(pAnonFunc->iFlags & VM_FUNC_CLOSURE) { /* Emit the load closure instruction */ PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOAD_CLOSURE, 0, 0, pAnonFunc, 0); @@ -1090,7 +1047,6 @@ PH7_PRIVATE sxi32 PH7_CompileLangConstruct(ph7_gen_state *pGen, sxi32 iCompileFl PH7_PRIVATE sxi32 PH7_CompileVariable(ph7_gen_state *pGen, sxi32 iCompileFlag) { sxu32 nLine = pGen->pIn->nLine; sxi32 iVv; - sxi32 iP1; void *p3; sxi32 rc; iVv = -1; /* Variable variable counter */ @@ -1149,17 +1105,10 @@ PH7_PRIVATE sxi32 PH7_CompileVariable(ph7_gen_state *pGen, sxi32 iCompileFlag) { } p3 = (void *)zName; } - iP1 = 0; - if(iCompileFlag & EXPR_FLAG_RDONLY_LOAD) { - if((iCompileFlag & EXPR_FLAG_LOAD_IDX_STORE) == 0) { - /* Read-only load.In other words do not create the variable if non-existent */ - iP1 = 1; - } - } /* Emit the load instruction */ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOAD, iP1, 0, p3, 0); + PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOAD, 0, 0, p3, 0); while(iVv > 0) { - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOAD, iP1, 0, 0, 0); + PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_LOAD, 0, 0, 0, 0); iVv--; } /* Node successfully compiled */ @@ -1447,7 +1396,7 @@ static sxi32 PH7_CompileConstant(ph7_gen_state *pGen) { /* Compile constant value */ rc = PH7_CompileExpr(&(*pGen), 0, 0); /* Emit the done instruction */ - PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0); + PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 1, 0, 0); PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer); if(rc == SXERR_ABORT) { /* Don't worry about freeing memory, everything will be released shortly */ @@ -1482,59 +1431,38 @@ Synchronize: */ static sxi32 PH7_CompileContinue(ph7_gen_state *pGen) { GenBlock *pLoop; /* Target loop */ - sxi32 iLevel; /* How many nesting loop to skip */ sxu32 nLine; sxi32 rc; nLine = pGen->pIn->nLine; - iLevel = 0; /* Jump the 'continue' keyword */ pGen->pIn++; - if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_NUM)) { - /* optional numeric argument which tells us how many levels - * of enclosing loops we should skip to the end of. - */ - iLevel = (sxi32)PH7_TokenValueToInt64(&pGen->pIn->sData); - if(iLevel < 2) { - iLevel = 0; - } - pGen->pIn++; /* Jump the optional numeric argument */ - } /* Point to the target loop */ - pLoop = PH7_GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel); - if(pLoop == 0) { + pLoop = PH7_GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, 0); + if(pLoop == 0 || pLoop->iFlags & GEN_BLOCK_SWITCH) { /* Illegal continue */ - rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "A 'continue' statement may only be used within a loop or switch"); + rc = PH7_GenCompileError(pGen, E_ERROR, nLine, "A 'continue' statement may only be used within a loop"); if(rc == SXERR_ABORT) { /* Error count limit reached,abort immediately */ return SXERR_ABORT; } } else { sxu32 nInstrIdx = 0; - if(pLoop->iFlags & GEN_BLOCK_SWITCH) { - /* - * Note that unlike some other languages, the continue statement applies to switch - * and acts similar to break. If you have a switch inside a loop and wish to continue - * to the next iteration of the outer loop, use continue 2. - */ - rc = PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, 0, 0, &nInstrIdx); - if(rc == SXRET_OK) { - PH7_GenStateNewJumpFixup(pLoop, PH7_OP_JMP, nInstrIdx); - } - } else { - /* Emit the unconditional jump to the beginning of the target loop */ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx); - if(pLoop->bPostContinue == TRUE) { - JumpFixup sJumpFix; - /* Post-continue */ - sJumpFix.nJumpType = PH7_OP_JMP; - sJumpFix.nInstrIdx = nInstrIdx; - SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix); - } + 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); + } + PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx); + if(pLoop->bPostContinue) { + JumpFixup sJumpFix; + /* Post-continue */ + sJumpFix.nJumpType = PH7_OP_JMP; + sJumpFix.nInstrIdx = nInstrIdx; + SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix); } } if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) { /* Not so fatal,emit a warning only */ - PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'continue' statement"); + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Expected semi-colon ';' after 'continue' statement"); } /* Statement successfully compiled */ return SXRET_OK; @@ -1548,25 +1476,13 @@ static sxi32 PH7_CompileContinue(ph7_gen_state *pGen) { */ static sxi32 PH7_CompileBreak(ph7_gen_state *pGen) { GenBlock *pLoop; /* Target loop */ - sxi32 iLevel; /* How many nesting loop to skip */ sxu32 nLine; sxi32 rc; nLine = pGen->pIn->nLine; - iLevel = 0; /* Jump the 'break' keyword */ pGen->pIn++; - if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_NUM)) { - /* optional numeric argument which tells us how many levels - * of enclosing loops we should skip to the end of. - */ - iLevel = (sxi32)PH7_TokenValueToInt64(&pGen->pIn->sData); - if(iLevel < 2) { - iLevel = 0; - } - pGen->pIn++; /* Jump the optional numeric argument */ - } /* Extract the target loop */ - pLoop = PH7_GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel); + pLoop = PH7_GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, 0); if(pLoop == 0) { /* Illegal break */ rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "A 'break' statement may only be used within a loop or switch"); @@ -1576,6 +1492,8 @@ static sxi32 PH7_CompileBreak(ph7_gen_state *pGen) { } } 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); rc = PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, 0, 0, &nInstrIdx); if(rc == SXRET_OK) { /* Fix the jump later when the jump destination is resolved */ @@ -1584,7 +1502,7 @@ static sxi32 PH7_CompileBreak(ph7_gen_state *pGen) { } if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) { /* Not so fatal,emit a warning only */ - PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'break' statement"); + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Expected semi-colon ';' after 'break' statement"); } /* Statement successfully compiled */ return SXRET_OK; @@ -1739,14 +1657,18 @@ static sxi32 PH7_CompileWhile(ph7_gen_state *pGen) { pGen->pIn = &pEnd[1]; pGen->pEnd = pTmp; /* Emit the false jump */ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JZ, 0, 0, 0, &nFalseJump); + PH7_VmEmitInstr(pGen->pVm, 0, 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_JZ, nFalseJump); + 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); /* 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 unconditional jump to the start of the loop */ PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, pWhileBlock->nFirstInstr, 0, 0); /* Fix all jumps now the destination is resolved */ @@ -1794,6 +1716,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); rc = PH7_CompileBlock(&(*pGen)); if(rc == SXERR_ABORT) { return SXERR_ABORT; @@ -1870,8 +1794,10 @@ 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 true jump to the beginning of the loop */ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JNZ, 0, pDoBlock->nFirstInstr, 0, 0); + PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMPNZ, 0, pDoBlock->nFirstInstr, 0, 0); /* Fix all jumps now the destination is resolved */ PH7_GenStateFixJumps(pDoBlock, -1, PH7_VmInstrLength(pGen->pVm)); /* Release the loop block */ @@ -1945,6 +1871,10 @@ static sxi32 PH7_CompileFor(ph7_gen_state *pGen) { /* Swap token streams */ pTmp = pGen->pEnd; pGen->pEnd = pEnd; + sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData)); + if(nKey & PH7_KEYWORD_TYPEDEF) { + PH7_CompileVar(&(*pGen)); + } /* Compile initialization expressions if available */ rc = PH7_CompileExpr(&(*pGen), 0, 0); /* Pop operand lvalues */ @@ -1980,9 +1910,9 @@ static sxi32 PH7_CompileFor(ph7_gen_state *pGen) { return SXERR_ABORT; } else if(rc != SXERR_EMPTY) { /* Emit the false jump */ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JZ, 0, 0, 0, &nFalseJump); + PH7_VmEmitInstr(pGen->pVm, 0, 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(pForBlock, PH7_OP_JZ, nFalseJump); + PH7_GenStateNewJumpFixup(pForBlock, PH7_OP_JMPZ, nFalseJump); } if((pGen->pIn->nType & PH7_TK_SEMI) == 0) { /* Syntax error */ @@ -1994,6 +1924,8 @@ static sxi32 PH7_CompileFor(ph7_gen_state *pGen) { } return SXRET_OK; } + /* Emit the OP_JMPLFB instruction to enter a loop frame */ + PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMPLFB, 0, 0, 0, 0); /* Jump the trailing ';' */ pGen->pIn++; /* Save the post condition stream */ @@ -2047,6 +1979,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 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 */ @@ -2260,6 +2194,8 @@ static sxi32 PH7_CompileForeach(ph7_gen_state *pGen) { PH7_VmEmitInstr(pGen->pVm, 0, 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); /* Compile the loop body */ pGen->pIn = &pEnd[1]; pGen->pEnd = pTmp; @@ -2268,6 +2204,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 unconditional jump to the start of the loop */ PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, pForeachBlock->nFirstInstr, 0, 0); /* Fix all jumps now the destination is resolved */ @@ -2374,9 +2312,9 @@ static sxi32 PH7_CompileIf(ph7_gen_state *pGen) { return SXERR_ABORT; } /* Emit the false jump */ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JZ, 0, 0, 0, &nJumpIdx); + PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMPZ, 0, 0, 0, &nJumpIdx); /* Save the instruction index so we can fix it later when the jump destination is resolved */ - PH7_GenStateNewJumpFixup(pCondBlock, PH7_OP_JZ, nJumpIdx); + PH7_GenStateNewJumpFixup(pCondBlock, PH7_OP_JMPZ, nJumpIdx); /* Compile the body */ rc = PH7_CompileBlock(&(*pGen)); if(rc == SXERR_ABORT) { @@ -2385,16 +2323,16 @@ static sxi32 PH7_CompileIf(ph7_gen_state *pGen) { if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0) { break; } - /* Ensure that the keyword ID is 'else if' or 'else' */ + /* Ensure that the keyword ID is 'else' */ nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData); - if((nKeyID & (PH7_KEYWORD_ELSE | PH7_KEYWORD_ELIF)) == 0) { + if(nKeyID != PH7_KEYWORD_ELSE) { break; } /* Emit the unconditional jump */ PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, 0, 0, &nJumpIdx); /* Save the instruction index so we can fix it later when the jump destination is resolved */ PH7_GenStateNewJumpFixup(pCondBlock, PH7_OP_JMP, nJumpIdx); - if(nKeyID & PH7_KEYWORD_ELSE) { + if(nKeyID == PH7_KEYWORD_ELSE) { pToken = &pGen->pIn[1]; if(pToken >= pGen->pEnd || (pToken->nType & PH7_TK_KEYWORD) == 0 || SX_PTR_TO_INT(pToken->pUserData) != PH7_KEYWORD_IF) { @@ -2402,16 +2340,16 @@ static sxi32 PH7_CompileIf(ph7_gen_state *pGen) { } pGen->pIn++; /* Jump the 'else' keyword */ } - pGen->pIn++; /* Jump the 'elseif/if' keyword */ + pGen->pIn++; /* Jump the 'if' keyword */ /* Synchronize cursors */ pToken = pGen->pIn; /* Fix the false jump */ - PH7_GenStateFixJumps(pCondBlock, PH7_OP_JZ, PH7_VmInstrLength(pGen->pVm)); + PH7_GenStateFixJumps(pCondBlock, PH7_OP_JMPZ, PH7_VmInstrLength(pGen->pVm)); } /* For(;;) */ /* Fix the false jump */ - PH7_GenStateFixJumps(pCondBlock, PH7_OP_JZ, PH7_VmInstrLength(pGen->pVm)); + PH7_GenStateFixJumps(pCondBlock, PH7_OP_JMPZ, PH7_VmInstrLength(pGen->pVm)); if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && - (SX_PTR_TO_INT(pGen->pIn->pUserData) & PH7_KEYWORD_ELSE)) { + (SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_KEYWORD_ELSE)) { /* Compile the else block */ pGen->pIn++; rc = PH7_CompileBlock(&(*pGen)); @@ -2490,144 +2428,144 @@ static sxi32 PH7_CompileHalt(ph7_gen_state *pGen) { PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_HALT, nExpr, 0, 0, 0); return SXRET_OK; } -/* - * Compile the static statement. - * Another important feature of variable scoping is the static variable. - * A static variable exists only in a local function scope, but it does not lose its value - * when program execution leaves this scope. - * Static variables also provide one way to deal with recursive functions. - * Symisc eXtension. - * PH7 allow any complex expression to be associated with the static variable while - * the zend engine would allow only simple scalar value. - * Example - * static $myVar = "Welcome "." guest ".rand_str(3); //Valid under PH7/Generate error using the zend engine - * Refer to the official documentation for more information on this feature. - */ -static sxi32 PH7_CompileStatic(ph7_gen_state *pGen) { - ph7_vm_func_static_var sStatic; /* Structure describing the static variable */ - ph7_vm_func *pFunc; /* Enclosing function */ - GenBlock *pBlock; - SyString *pName; - char *zDup; - sxu32 nLine; - sxi32 rc; - /* Jump the static keyword */ - nLine = pGen->pIn->nLine; - pGen->pIn++; - /* Extract the enclosing function if any */ - pBlock = pGen->pCurrent; - while(pBlock) { - if(pBlock->iFlags & GEN_BLOCK_FUNC) { - break; - } - /* Point to the upper block */ - pBlock = pBlock->pParent; - } - if(pBlock == 0) { - /* Static statement,called outside of a function body,treat it as a simple variable. */ - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR) == 0) { - rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword"); - if(rc == SXERR_ABORT) { - return SXERR_ABORT; - } - goto Synchronize; - } - /* Compile the expression holding the variable */ - rc = PH7_CompileExpr(&(*pGen), 0, 0); - if(rc == SXERR_ABORT) { - return SXERR_ABORT; - } else if(rc != SXERR_EMPTY) { - /* Emit the POP instruction */ - PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_POP, 1, 0, 0, 0); - } - return SXRET_OK; - } - pFunc = (ph7_vm_func *)pBlock->pUserData; - /* Make sure we are dealing with a valid statement */ - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd || - (pGen->pIn[1].nType & (PH7_TK_ID | PH7_TK_KEYWORD)) == 0) { - rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword"); - if(rc == SXERR_ABORT) { - return SXERR_ABORT; - } - goto Synchronize; - } - pGen->pIn++; - /* Extract variable name */ - pName = &pGen->pIn->sData; - pGen->pIn++; /* Jump the var name */ - if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_SEMI/*';'*/ | PH7_TK_EQUAL/*'='*/)) == 0) { - rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "static: Unexpected token '%z'", &pGen->pIn->sData); - goto Synchronize; - } - /* Initialize the structure describing the static variable */ - SySetInit(&sStatic.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr)); - sStatic.nIdx = SXU32_HIGH; /* Not yet created */ - /* Duplicate variable name */ - zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte); - if(zDup == 0) { - PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, PH7 engine is running out of memory"); - return SXERR_ABORT; - } - SyStringInitFromBuf(&sStatic.sName, zDup, pName->nByte); - /* Check if we have an expression to compile */ - if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_EQUAL)) { - SySet *pInstrContainer; - /* - * Static variable can take any complex expression including function - * call as their initialization value. - * Example: - * static $var = foo(1,4+5,bar()); - */ - pGen->pIn++; /* Jump the equal '=' sign */ - /* Swap bytecode container */ - pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); - PH7_VmSetByteCodeContainer(pGen->pVm, &sStatic.aByteCode); - /* Compile the expression */ - rc = PH7_CompileExpr(&(*pGen), 0, 0); - /* Emit the done instruction */ - PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0); - /* Restore default bytecode container */ - PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer); - } - /* Finally save the compiled static variable in the appropriate container */ - SySetPut(&pFunc->aStatic, (const void *)&sStatic); - return SXRET_OK; -Synchronize: - /* Synchronize with the first semi-colon ';',so we can avoid compiling this erroneous - * statement. - */ - while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) { - pGen->pIn++; - } - return SXRET_OK; -} /* * Compile the var statement. - * Symisc Extension: - * var statement can be used outside of a class definition. */ static sxi32 PH7_CompileVar(ph7_gen_state *pGen) { sxu32 nLine = pGen->pIn->nLine; + sxbool bStatic = FALSE; + ph7_vm_func_static_var sStatic; + ph7_vm_func *pFunc; + GenBlock *pBlock; + SyString *pName; + sxu32 nKey, nType; + char *zDup; sxi32 rc; - /* Jump the 'var' keyword */ - pGen->pIn++; - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR/*'$'*/) == 0) { - rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "var: Expecting variable name"); - /* Synchronize with the first semi-colon */ - while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0) { - pGen->pIn++; - } - if(rc == SXERR_ABORT) { - return SXERR_ABORT; - } + nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData)); + if(nKey == PH7_KEYWORD_STATIC) { + bStatic = TRUE; + /* Jump the 'static' keyword' */ + pGen->pIn++; + nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData)); + } + if(nKey & PH7_KEYWORD_BOOL) { + nType = MEMOBJ_BOOL; + } else if(nKey & PH7_KEYWORD_CALLBACK) { + nType = MEMOBJ_CALL; + } else if(nKey & PH7_KEYWORD_CHAR) { + nType = MEMOBJ_CHAR; + } else if(nKey & PH7_KEYWORD_FLOAT) { + nType = MEMOBJ_REAL; + } else if(nKey & PH7_KEYWORD_INT) { + nType = MEMOBJ_INT; + } else if(nKey & PH7_KEYWORD_MIXED) { + nType = MEMOBJ_MIXED; + } else if(nKey & PH7_KEYWORD_OBJECT) { + nType = MEMOBJ_OBJ; + } else if(nKey & PH7_KEYWORD_RESOURCE) { + nType = MEMOBJ_RES; + } else if(nKey & PH7_KEYWORD_STRING) { + nType = MEMOBJ_STRING; + } else if(nKey & PH7_KEYWORD_VOID) { + nType = MEMOBJ_VOID; } else { - /* Compile the expression */ - rc = PH7_CompileExpr(&(*pGen), 0, 0); - if(rc == SXERR_ABORT) { - return SXERR_ABORT; - } else if(rc != SXERR_EMPTY) { + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, + "Unknown data type name '%z'", + &pGen->pIn->sData); + } + /* Jump the data type keyword */ + pGen->pIn++; + if((pGen->pIn->nType & PH7_TK_OSB) && &pGen->pIn[1] < pGen->pEnd && (pGen->pIn[1].nType & PH7_TK_CSB)) { + nType |= MEMOBJ_HASHMAP; + pGen->pIn += 2; + } + for(;;) { + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR/*'$'*/) == 0 || &pGen->pIn[1] >= pGen->pEnd || + (pGen->pIn[1].nType & (PH7_TK_ID | PH7_TK_KEYWORD)) == 0) { + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, + "Unexpected '%z', expecting variable", + &pGen->pIn->sData); + } + /* Extract variable name */ + pName = &pGen->pIn[1].sData; + if(bStatic) { + /* Extract the enclosing method/closure */ + pBlock = pGen->pCurrent; + while(pBlock) { + if(pBlock->iFlags & GEN_BLOCK_FUNC) { + break; + } + /* Point to the upper block */ + pBlock = pBlock->pParent; + } + pFunc = (ph7_vm_func *)pBlock->pUserData; + pGen->pIn += 2; /* Jump the dollar '$' sign and variable name */ + if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (PH7_TK_COMMA/*','*/ | PH7_TK_SEMI/*';'*/ | PH7_TK_EQUAL/*'='*/)) == 0) { + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData); + } + /* TODO: Check if static variable exists to avoid redeclaration */ + + /* Initialize the structure describing the static variable */ + SySetInit(&sStatic.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr)); + sStatic.nIdx = SXU32_HIGH; /* Not yet created */ + /* Duplicate variable name */ + zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte); + if(zDup == 0) { + PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, PH7 engine is running out of memory"); + } + SyStringInitFromBuf(&sStatic.sName, zDup, pName->nByte); + /* Check if we have an expression to compile */ + if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_EQUAL)) { + SySet *pInstrContainer; + pGen->pIn++; /* Jump the equal '=' sign */ + /* Swap bytecode container */ + pInstrContainer = PH7_VmGetByteCodeContainer(pGen->pVm); + PH7_VmSetByteCodeContainer(pGen->pVm, &sStatic.aByteCode); + /* Compile the expression */ + rc = PH7_CompileExpr(&(*pGen), EXPR_FLAG_COMMA_STATEMENT, 0); + if(rc == SXERR_EMPTY) { + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Static variable '%z' is missing default value", &pName); + } + /* Emit the done instruction */ + PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 1, 0, 0); + /* Restore default bytecode container */ + PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer); + } + /* Set static variable type */ + sStatic.iFlags = nType; + /* Finally save the compiled static variable in the appropriate container */ + SySetPut(&pFunc->aStatic, (const void *)&sStatic); + } else { + zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte); + if(zDup == 0) { + PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, PH7 engine is running out of memory"); + } + void *p3 = (void *) zDup; + /* Emit OP_DECLARE instruction */ + PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DECLARE, 0, nType, p3, 0); + /* Pop the l-value */ PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_POP, 1, 0, 0, 0); + /* Check if we have an expression to compile */ + if(pGen->pIn < pGen->pEnd && (pGen->pIn[2].nType & PH7_TK_EQUAL)) { + /* Compile the expression */ + rc = PH7_CompileExpr(&(*pGen), EXPR_FLAG_COMMA_STATEMENT, 0); + if(rc == SXERR_ABORT) { + return SXERR_ABORT; + } else if(rc == SXERR_EMPTY) { + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Variable '%z' is missing default value", &pName); + } else { + PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_POP, 1, 0, 0, 0); + } + } else { + pGen->pIn += 2; /* Jump the dollar '$' sign and variable name */ + } + } + if(pGen->pIn->nType == PH7_TK_SEMI) { + break; + } else if(pGen->pIn->nType & PH7_TK_COMMA) { + pGen->pIn++; + } else { + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData); } } return SXRET_OK; @@ -2845,7 +2783,7 @@ static sxi32 PH7_GenStateProcessArgValue(ph7_gen_state *pGen, ph7_vm_func_arg *p /* Compile the expression holding the argument value */ rc = PH7_CompileExpr(&(*pGen), 0, 0); /* Emit the done instruction */ - PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0); + PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 1, 0, 0); PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer); RE_SWAP_DELIMITER(pGen); if(rc == SXERR_ABORT) { @@ -2905,32 +2843,49 @@ static sxi32 PH7_GenStateCollectFuncArgs(ph7_vm_func *pFunc, ph7_gen_state *pGen if(pIn->nType & (PH7_TK_ID | PH7_TK_KEYWORD)) { if(pIn->nType & PH7_TK_KEYWORD) { sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pIn->pUserData)); - if(nKey & PH7_KEYWORD_ARRAY) { - sArg.nType = MEMOBJ_HASHMAP; - } else if(nKey & PH7_KEYWORD_BOOL) { + if(nKey & PH7_KEYWORD_BOOL) { sArg.nType = MEMOBJ_BOOL; - } else if(nKey & PH7_KEYWORD_INT) { - sArg.nType = MEMOBJ_INT; - } else if(nKey & PH7_KEYWORD_STRING) { - sArg.nType = MEMOBJ_STRING; + } else if(nKey & PH7_KEYWORD_CALLBACK) { + sArg.nType = MEMOBJ_CALL; + } else if(nKey & PH7_KEYWORD_CHAR) { + sArg.nType = MEMOBJ_CHAR; } else if(nKey & PH7_KEYWORD_FLOAT) { sArg.nType = MEMOBJ_REAL; + } else if(nKey & PH7_KEYWORD_INT) { + sArg.nType = MEMOBJ_INT; + } else if(nKey & PH7_KEYWORD_MIXED) { + sArg.nType = MEMOBJ_MIXED; + } else if(nKey & PH7_KEYWORD_OBJECT) { + sArg.nType = MEMOBJ_OBJ; + } else if(nKey & PH7_KEYWORD_RESOURCE) { + sArg.nType = MEMOBJ_RES; + } else if(nKey & PH7_KEYWORD_STRING) { + sArg.nType = MEMOBJ_STRING; + } else if(nKey & PH7_KEYWORD_VOID) { + sArg.nType = MEMOBJ_VOID; } else { - PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, - "Invalid argument type '%z',Automatic cast will not be performed", + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, + "Unknown data type name '%z'", &pIn->sData); } } else { SyString *pName = &pIn->sData; /* Class name */ char *zDup; - /* Argument must be a class instance,record that*/ + /* Argument must be a class instance, record that*/ zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte); if(zDup) { sArg.nType = SXU32_HIGH; /* 0xFFFFFFFF as sentinel */ SyStringInitFromBuf(&sArg.sClass, zDup, pName->nByte); + } else { + /* This should not happen, but fallback to object anyway */ + sArg.nType = MEMOBJ_OBJ; } } pIn++; + if((pIn->nType & PH7_TK_OSB) && &pIn[1] < pEnd && (pIn[1].nType & PH7_TK_CSB)) { + sArg.nType |= MEMOBJ_HASHMAP; + pIn += 2; + } } if(pIn >= pEnd) { rc = PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Missing argument name"); @@ -2947,6 +2902,9 @@ static sxi32 PH7_GenStateCollectFuncArgs(ph7_vm_func *pFunc, ph7_gen_state *pGen return rc; } pIn++; /* Jump the dollar sign */ + if(!sArg.nType) { + PH7_GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Argument '$%z' is of undefined data type", &pIn->sData); + } /* Copy argument name */ zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, SyStringData(&pIn->sData), SyStringLength(&pIn->sData)); if(zDup == 0) { @@ -2999,32 +2957,41 @@ static sxi32 PH7_GenStateCollectFuncArgs(ph7_vm_func *pFunc, ph7_gen_state *pGen /* Class name */ SyBlobAppend(&sSig, SyStringData(&sArg.sClass), SyStringLength(&sArg.sClass)); } else { - int c; - c = 'n'; /* cc warning */ + int c = 'n'; /* cc warning */ /* Type leading character */ - switch(sArg.nType) { - case MEMOBJ_HASHMAP: - /* Hashmap aka 'array' */ - c = 'h'; - break; - case MEMOBJ_INT: - /* Integer */ - c = 'i'; - break; - case MEMOBJ_BOOL: - /* Bool */ - c = 'b'; - break; - case MEMOBJ_REAL: - /* Float */ - c = 'f'; - break; - case MEMOBJ_STRING: - /* String */ - c = 's'; - break; - default: - break; + if(sArg.nType & MEMOBJ_BOOL) { + /* Bool */ + c = 'b'; + } else if(sArg.nType & MEMOBJ_CALL) { + /* Callback */ + c = 'a'; + } else if(sArg.nType & MEMOBJ_CHAR) { + /* Char */ + c = 'c'; + } else if(sArg.nType & MEMOBJ_INT) { + /* Integer */ + c = 'i'; + } else if(sArg.nType & MEMOBJ_MIXED) { + /* Mixed */ + c = 'm'; + } else if(sArg.nType & MEMOBJ_OBJ) { + /* Object */ + c = 'o'; + } else if(sArg.nType & MEMOBJ_REAL) { + /* Float */ + c = 'f'; + } else if(sArg.nType & MEMOBJ_RES) { + /* Resource */ + c = 'r'; + } else if(sArg.nType & MEMOBJ_STRING) { + /* String */ + c = 's'; + } else if(sArg.nType & MEMOBJ_VOID) { + /* Void */ + c = 'v'; + } + if(sArg.nType & MEMOBJ_HASHMAP) { + c = SyToUpper(c); } SyBlobAppend(&sSig, (const void *)&c, sizeof(char)); } @@ -3350,7 +3317,7 @@ loop: } pGen->pIn++; /* Jump the equal sign */ /* Allocate a new class attribute */ - pCons = PH7_NewClassAttr(pGen->pVm, pName, nLine, iProtection, iFlags); + pCons = PH7_NewClassAttr(pGen->pVm, pName, nLine, iProtection, iFlags, 0); if(pCons == 0) { PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 is running out of memory"); return SXERR_ABORT; @@ -3368,7 +3335,7 @@ loop: } } /* Emit the done instruction */ - PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_DONE, 1, 0, 0, 0); + PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_DONE, 1, 1, 0, 0); PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer); if(rc == SXERR_ABORT) { /* Don't worry about freeing memory, everything will be released shortly */ @@ -3429,7 +3396,7 @@ Synchronize: * Refer to the official documentation for more information on the powerful extension * introduced by the PH7 engine to the OO subsystem. */ -static sxi32 PH7_GenStateCompileClassAttr(ph7_gen_state *pGen, sxi32 iProtection, sxi32 iFlags, ph7_class *pClass) { +static sxi32 PH7_GenStateCompileClassAttr(ph7_gen_state *pGen, sxi32 iProtection, sxi32 iFlags, sxu32 nType, ph7_class *pClass) { sxu32 nLine = pGen->pIn->nLine; ph7_class_attr *pAttr; SyString *pName; @@ -3461,7 +3428,7 @@ loop: goto Synchronize; } /* Allocate a new class attribute */ - pAttr = PH7_NewClassAttr(pGen->pVm, pName, nLine, iProtection, iFlags); + pAttr = PH7_NewClassAttr(pGen->pVm, pName, nLine, iProtection, iFlags, nType); if(pAttr == 0) { PH7_GenCompileError(pGen, E_ERROR, nLine, "Fatal, PH7 engine is running out of memory"); return SXERR_ABORT; @@ -3482,7 +3449,7 @@ loop: } } /* Emit the done instruction */ - PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_DONE, 1, 0, 0, 0); + PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_DONE, 1, 1, 0, 0); PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer); } /* All done,install the attribute */ @@ -3529,6 +3496,7 @@ Synchronize: */ static sxi32 PH7_GenStateCompileClassMethod( ph7_gen_state *pGen, /* Code generator state */ + sxu32 nType, /* Method return data type */ sxi32 iProtection, /* Visibility level */ sxi32 iFlags, /* Configuration flags */ int doBody, /* TRUE to process method body */ @@ -3542,7 +3510,6 @@ static sxi32 PH7_GenStateCompileClassMethod( sxi32 rc; /* Extract visibility level */ iProtection = PH7_GetProtectionLevel(iProtection); - pGen->pIn++; /* Jump the 'function' keyword */ iFuncFlags = 0; if(pGen->pIn >= pGen->pEnd) { /* Invalid method name */ @@ -3658,6 +3625,8 @@ static sxi32 PH7_GenStateCompileClassMethod( return SXERR_CORRUPT; } } + /* Store method return data type */ + pMeth->sFunc.nType = nType; /* All done,install the method */ rc = PH7_ClassInstallMethod(pClass, pMeth); if(rc != SXRET_OK) { @@ -3802,7 +3771,7 @@ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) { } if((pGen->pIn->nType & PH7_TK_KEYWORD) == 0) { rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "Unexpected token '%z'.Expecting method signature or constant declaration inside interface '%z'", + "Unexpected token '%z'. Expecting method signature or constant declaration inside interface '%z'", &pGen->pIn->sData, pName); if(rc == SXERR_ABORT) { /* Error count limit reached,abort immediately */ @@ -3817,15 +3786,6 @@ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) { PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "interface: Access type must be public"); nKwrd = PH7_KEYWORD_PUBLIC; } - if(nKwrd != PH7_KEYWORD_PUBLIC && nKwrd != PH7_KEYWORD_FUNCTION && nKwrd != PH7_KEYWORD_CONST && nKwrd != PH7_KEYWORD_STATIC) { - rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "Expecting method signature or constant declaration inside interface '%z'", pName); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; - } - goto done; - } if(nKwrd == PH7_KEYWORD_PUBLIC) { /* Advance the stream cursor */ pGen->pIn++; @@ -3838,19 +3798,11 @@ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) { } goto done; } + /* Extract the keyword */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); - if(nKwrd != PH7_KEYWORD_FUNCTION && nKwrd != PH7_KEYWORD_CONST && nKwrd != PH7_KEYWORD_STATIC) { - rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "Expecting method signature or constant declaration inside interface '%z'", pName); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; - } - goto done; - } } if(nKwrd == PH7_KEYWORD_CONST) { - /* Parse constant */ + /* Process constant declaration */ rc = PH7_GenStateCompileClassConstant(&(*pGen), 0, 0, pClass); if(rc != SXRET_OK) { if(rc == SXERR_ABORT) { @@ -3865,8 +3817,7 @@ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) { iFlags |= PH7_CLASS_ATTR_STATIC; /* Advance the stream cursor */ pGen->pIn++; - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 - || SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_KEYWORD_FUNCTION) { + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0) { rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Expecting method signature inside interface '%z'", pName); if(rc == SXERR_ABORT) { @@ -3875,15 +3826,72 @@ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) { } goto done; } + /* Extract the keyword */ + nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); + if(nKwrd == PH7_KEYWORD_PRIVATE || nKwrd == PH7_KEYWORD_PROTECTED) { + /* Emit a warning and switch to public visibility */ + PH7_GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "interface: Access type must be public"); + nKwrd = PH7_KEYWORD_PUBLIC; + } + if(nKwrd == PH7_KEYWORD_PUBLIC) { + /* Advance the stream cursor */ + pGen->pIn++; + /* Extract the keyword */ + nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); + } } - /* Process method signature */ - rc = PH7_GenStateCompileClassMethod(&(*pGen), 0, FALSE/* Only method signature*/, iFlags, pClass); - if(rc != SXRET_OK) { + if((nKwrd & PH7_KEYWORD_TYPEDEF) == 0) { + rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, + "Unexpected token '%z', expecting data type for method signature inside interface '%z'", + &pGen->pIn->sData, pName); if(rc == SXERR_ABORT) { + /* Error count limit reached,abort immediately */ return SXERR_ABORT; } goto done; } + sxu32 nType; + sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData)); + if(nKey & PH7_KEYWORD_BOOL) { + nType = MEMOBJ_BOOL; + } else if(nKey & PH7_KEYWORD_CALLBACK) { + nType = MEMOBJ_CALL; + } else if(nKey & PH7_KEYWORD_CHAR) { + nType = MEMOBJ_CHAR; + } else if(nKey & PH7_KEYWORD_FLOAT) { + nType = MEMOBJ_REAL; + } else if(nKey & PH7_KEYWORD_INT) { + nType = MEMOBJ_INT; + } else if(nKey & PH7_KEYWORD_MIXED) { + nType = MEMOBJ_MIXED; + } else if(nKey & PH7_KEYWORD_OBJECT) { + nType = MEMOBJ_OBJ; + } else if(nKey & PH7_KEYWORD_RESOURCE) { + nType = MEMOBJ_RES; + } else if(nKey & PH7_KEYWORD_STRING) { + nType = MEMOBJ_STRING; + } else if(nKey & PH7_KEYWORD_VOID) { + nType = MEMOBJ_VOID; + } else { + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Invalid return data type '%z'", &pGen->pIn->sData); + } + pGen->pIn++; /* Jump the return data type */ + if(pGen->pIn->nType & PH7_TK_OSB && pGen->pIn[1].nType & PH7_TK_CSB) { + pGen->pIn += 2; + nType |= MEMOBJ_HASHMAP; + } + if(pGen->pIn->nType & PH7_TK_DOLLAR/*'$'*/) { + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Attributes cannot be declared inside interface '%z'", pName); + } else { + /* Process method signature */ + rc = PH7_GenStateCompileClassMethod(&(*pGen), nType, 0, iFlags, FALSE/* Only method signature*/, pClass); + if(rc != SXRET_OK) { + if(rc == SXERR_ABORT) { + return SXERR_ABORT; + } + goto done; + } + } } } /* Install the interface */ @@ -4070,9 +4078,9 @@ static sxi32 PH7_GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { /* End of class body */ break; } - if((pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_DOLLAR)) == 0) { + if((pGen->pIn->nType & PH7_TK_KEYWORD) == 0) { rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "Unexpected token '%z'. Expecting attribute declaration inside class '%z'", + "Unexpected token '%z'. Expecting attribute or method declaration inside class '%z'", &pGen->pIn->sData, pName); if(rc == SXERR_ABORT) { /* Error count limit reached,abort immediately */ @@ -4083,15 +4091,41 @@ static sxi32 PH7_GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { /* Assume public visibility */ iProtection = PH7_KEYWORD_PUBLIC; iAttrflags = 0; - if(pGen->pIn->nType & PH7_TK_KEYWORD) { - /* Extract the current keyword */ + /* Extract the current keyword */ + nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); + if(nKwrd == PH7_KEYWORD_PUBLIC || nKwrd == PH7_KEYWORD_PRIVATE || nKwrd == PH7_KEYWORD_PROTECTED) { + iProtection = nKwrd; + pGen->pIn++; /* Jump the visibility token */ + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0) { + rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, + "Unexpected token '%z'. Expecting attribute or method declaration inside class '%z'", + &pGen->pIn->sData, pName); + if(rc == SXERR_ABORT) { + /* Error count limit reached,abort immediately */ + return SXERR_ABORT; + } + goto done; + } + /* Extract the keyword */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); - if(nKwrd == PH7_KEYWORD_PUBLIC || nKwrd == PH7_KEYWORD_PRIVATE || nKwrd == PH7_KEYWORD_PROTECTED) { - iProtection = nKwrd; - pGen->pIn++; /* Jump the visibility token */ - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_DOLLAR)) == 0) { + } + if(nKwrd == PH7_KEYWORD_CONST) { + /* Process constant declaration */ + rc = PH7_GenStateCompileClassConstant(&(*pGen), iProtection, iAttrflags, pClass); + if(rc != SXRET_OK) { + if(rc == SXERR_ABORT) { + return SXERR_ABORT; + } + goto done; + } + } else { + if(nKwrd == PH7_KEYWORD_STATIC) { + /* Static method or attribute,record that */ + iAttrflags |= PH7_CLASS_ATTR_STATIC; + pGen->pIn++; /* Jump the static keyword */ + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0) { rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "Unexpected token '%z'. Expecting attribute declaration inside class '%z'", + "Unexpected token '%z',Expecting attribute or method declaration inside class '%z'", &pGen->pIn->sData, pName); if(rc == SXERR_ABORT) { /* Error count limit reached,abort immediately */ @@ -4099,163 +4133,122 @@ static sxi32 PH7_GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) { } goto done; } - if(pGen->pIn->nType & PH7_TK_DOLLAR) { - /* Attribute declaration */ - rc = PH7_GenStateCompileClassAttr(&(*pGen), iProtection, iAttrflags, pClass); - if(rc != SXRET_OK) { - if(rc == SXERR_ABORT) { - return SXERR_ABORT; - } - goto done; - } - continue; - } /* Extract the keyword */ nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); - } - if(nKwrd == PH7_KEYWORD_CONST) { - /* Process constant declaration */ - rc = PH7_GenStateCompileClassConstant(&(*pGen), iProtection, iAttrflags, pClass); - if(rc != SXRET_OK) { - if(rc == SXERR_ABORT) { - return SXERR_ABORT; - } - goto done; + if(nKwrd == PH7_KEYWORD_PUBLIC || nKwrd == PH7_KEYWORD_PRIVATE || nKwrd == PH7_KEYWORD_PROTECTED) { + iProtection = nKwrd; + pGen->pIn++; /* Jump the visibility token */ } - } else { - if(nKwrd == PH7_KEYWORD_STATIC) { - /* Static method or attribute,record that */ + /* Extract the keyword */ + nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); + } else if(nKwrd == PH7_KEYWORD_VIRTUAL) { + /* Virtual method,record that */ + iAttrflags |= PH7_CLASS_ATTR_VIRTUAL; + /* Advance the stream cursor */ + pGen->pIn++; + if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD)) { + nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); + if(nKwrd == PH7_KEYWORD_PUBLIC || nKwrd == PH7_KEYWORD_PRIVATE || nKwrd == PH7_KEYWORD_PROTECTED) { + iProtection = nKwrd; + pGen->pIn++; /* Jump the visibility token */ + } + } + if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && + SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_KEYWORD_STATIC) { + /* Static method */ iAttrflags |= PH7_CLASS_ATTR_STATIC; pGen->pIn++; /* Jump the static keyword */ - if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD)) { - /* Extract the keyword */ - nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); - if(nKwrd == PH7_KEYWORD_PUBLIC || nKwrd == PH7_KEYWORD_PRIVATE || nKwrd == PH7_KEYWORD_PROTECTED) { - iProtection = nKwrd; - pGen->pIn++; /* Jump the visibility token */ - } - } - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_DOLLAR)) == 0) { - rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "Unexpected token '%z',Expecting method,attribute or constant declaration inside class '%z'", - &pGen->pIn->sData, pName); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; - } - goto done; - } - if(pGen->pIn->nType & PH7_TK_DOLLAR) { - /* Attribute declaration */ - rc = PH7_GenStateCompileClassAttr(&(*pGen), iProtection, iAttrflags, pClass); - if(rc != SXRET_OK) { - if(rc == SXERR_ABORT) { - return SXERR_ABORT; - } - goto done; - } - continue; - } - /* Extract the keyword */ - nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); - } else if(nKwrd == PH7_KEYWORD_VIRTUAL) { - /* Virtual method,record that */ - iAttrflags |= PH7_CLASS_ATTR_VIRTUAL; - /* Advance the stream cursor */ - pGen->pIn++; - if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD)) { - nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); - if(nKwrd == PH7_KEYWORD_PUBLIC || nKwrd == PH7_KEYWORD_PRIVATE || nKwrd == PH7_KEYWORD_PROTECTED) { - iProtection = nKwrd; - pGen->pIn++; /* Jump the visibility token */ - } - } - if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && - SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_KEYWORD_STATIC) { - /* Static method */ - iAttrflags |= PH7_CLASS_ATTR_STATIC; - pGen->pIn++; /* Jump the static keyword */ - } - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 || - SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_KEYWORD_FUNCTION) { - rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "Unexpected token '%z',Expecting method declaration after 'virtual' keyword inside class '%z'", - &pGen->pIn->sData, pName); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; - } - goto done; - } - nKwrd = PH7_KEYWORD_FUNCTION; - } else if(nKwrd == PH7_KEYWORD_FINAL) { - /* final method ,record that */ - iAttrflags |= PH7_CLASS_ATTR_FINAL; - pGen->pIn++; /* Jump the final keyword */ - if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD)) { - /* Extract the keyword */ - nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); - if(nKwrd == PH7_KEYWORD_PUBLIC || nKwrd == PH7_KEYWORD_PRIVATE || nKwrd == PH7_KEYWORD_PROTECTED) { - iProtection = nKwrd; - pGen->pIn++; /* Jump the visibility token */ - } - } - if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && - SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_KEYWORD_STATIC) { - /* Static method */ - iAttrflags |= PH7_CLASS_ATTR_STATIC; - pGen->pIn++; /* Jump the static keyword */ - } - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 || - SX_PTR_TO_INT(pGen->pIn->pUserData) != PH7_KEYWORD_FUNCTION) { - rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "Unexpected token '%z',Expecting method declaration after 'final' keyword inside class '%z'", - &pGen->pIn->sData, pName); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; - } - goto done; - } - nKwrd = PH7_KEYWORD_FUNCTION; } - if(nKwrd != PH7_KEYWORD_FUNCTION && nKwrd != PH7_KEYWORD_VAR) { + nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 || + ((nKwrd & PH7_KEYWORD_TYPEDEF) == 0 && pGen->pIn[2].nType != PH7_TK_LPAREN)) { rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "Unexpected token '%z',Expecting method declaration inside class '%z'", + "Unexpected token '%z', expecting method declaration after 'virtual' keyword inside class '%z'", &pGen->pIn->sData, pName); if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ + /* Error count limit reached, abort immediately */ return SXERR_ABORT; } goto done; } - if(nKwrd == PH7_KEYWORD_VAR) { - pGen->pIn++; /* Jump the 'var' keyword */ - if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_DOLLAR/*'$'*/) == 0) { - rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, - "Expecting attribute declaration after 'var' keyword"); - if(rc == SXERR_ABORT) { - /* Error count limit reached,abort immediately */ - return SXERR_ABORT; - } - goto done; + } else if(nKwrd == PH7_KEYWORD_FINAL) { + /* final method ,record that */ + iAttrflags |= PH7_CLASS_ATTR_FINAL; + pGen->pIn++; /* Jump the final keyword */ + if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD)) { + /* Extract the keyword */ + nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); + if(nKwrd == PH7_KEYWORD_PUBLIC || nKwrd == PH7_KEYWORD_PRIVATE || nKwrd == PH7_KEYWORD_PROTECTED) { + iProtection = nKwrd; + pGen->pIn++; /* Jump the visibility token */ } - /* Attribute declaration */ - rc = PH7_GenStateCompileClassAttr(&(*pGen), iProtection, iAttrflags, pClass); - } else { - /* Process method declaration */ - rc = PH7_GenStateCompileClassMethod(&(*pGen), iProtection, iAttrflags, TRUE, pClass); } - if(rc != SXRET_OK) { + if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_KEYWORD) && + SX_PTR_TO_INT(pGen->pIn->pUserData) == PH7_KEYWORD_STATIC) { + /* Static method */ + iAttrflags |= PH7_CLASS_ATTR_STATIC; + pGen->pIn++; /* Jump the static keyword */ + } + nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData); + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 || + ((nKwrd & PH7_KEYWORD_TYPEDEF) == 0 && pGen->pIn[2].nType != PH7_TK_LPAREN)) { + rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, + "Unexpected token '%z', expecting method declaration after 'final' keyword inside class '%z'", + &pGen->pIn->sData, pName); if(rc == SXERR_ABORT) { + /* Error count limit reached, abort immediately */ return SXERR_ABORT; } goto done; } } - } else { - /* Attribute declaration */ - rc = PH7_GenStateCompileClassAttr(&(*pGen), iProtection, iAttrflags, pClass); + if((nKwrd & PH7_KEYWORD_TYPEDEF) == 0) { + rc = PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, + "Unexpected token '%z', expecting data type for attribute or method declaration inside class '%z'", + &pGen->pIn->sData, pName); + if(rc == SXERR_ABORT) { + /* Error count limit reached,abort immediately */ + return SXERR_ABORT; + } + goto done; + } + sxu32 nType; + sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData)); + if(nKey & PH7_KEYWORD_BOOL) { + nType = MEMOBJ_BOOL; + } else if(nKey & PH7_KEYWORD_CALLBACK) { + nType = MEMOBJ_CALL; + } else if(nKey & PH7_KEYWORD_CHAR) { + nType = MEMOBJ_CHAR; + } else if(nKey & PH7_KEYWORD_FLOAT) { + nType = MEMOBJ_REAL; + } else if(nKey & PH7_KEYWORD_INT) { + nType = MEMOBJ_INT; + } else if(nKey & PH7_KEYWORD_MIXED) { + nType = MEMOBJ_MIXED; + } else if(nKey & PH7_KEYWORD_OBJECT) { + nType = MEMOBJ_OBJ; + } else if(nKey & PH7_KEYWORD_RESOURCE) { + nType = MEMOBJ_RES; + } else if(nKey & PH7_KEYWORD_STRING) { + nType = MEMOBJ_STRING; + } else if(nKey & PH7_KEYWORD_VOID) { + nType = MEMOBJ_VOID; + } else { + PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Invalid return data type '%z'", &pGen->pIn->sData); + } + pGen->pIn++; /* Jump the return data type */ + if(pGen->pIn->nType & PH7_TK_OSB && pGen->pIn[1].nType & PH7_TK_CSB) { + pGen->pIn += 2; + nType |= MEMOBJ_HASHMAP; + } + if(pGen->pIn->nType & PH7_TK_DOLLAR/*'$'*/) { + /* Attribute declaration */ + rc = PH7_GenStateCompileClassAttr(&(*pGen), iProtection, iAttrflags, nType, pClass); + } else { + /* Process method declaration */ + rc = PH7_GenStateCompileClassMethod(&(*pGen), nType, iProtection, iAttrflags, TRUE, pClass); + } if(rc != SXRET_OK) { if(rc == SXERR_ABORT) { return SXERR_ABORT; @@ -4670,7 +4663,7 @@ static sxi32 PH7_GenStateCompileCaseExpr(ph7_gen_state *pGen, ph7_case_expr *pEx PH7_VmSetByteCodeContainer(pGen->pVm, &pExpr->aByteCode); rc = PH7_CompileExpr(&(*pGen), 0, 0); /* Emit the done instruction */ - PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0); + PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 1, 0, 0); PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer); /* Update token stream */ pGen->pIn = pEnd; @@ -4916,7 +4909,7 @@ static sxi32 PH7_GenStateEmitExprCode( } nJz = nJmp = 0; /* cc -O6 warning */ /* Phase#2: Emit the false jump */ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JZ, 0, 0, 0, &nJz); + PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMPZ, 0, 0, 0, &nJz); if(pNode->pLeft) { /* Phase#3: Compile the 'then' expression */ rc = PH7_GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags); @@ -5012,10 +5005,10 @@ static sxi32 PH7_GenStateEmitExprCode( if(pNode->pRight) { if(iVmOp == PH7_OP_LAND) { /* Emit the false jump so we can short-circuit the logical and */ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx); + PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMPZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx); } else if(iVmOp == PH7_OP_LOR) { /* Emit the true jump so we can short-circuit the logical or*/ - PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JNZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx); + PH7_VmEmitInstr(pGen->pVm, 0, 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 */) { iFlags |= EXPR_FLAG_LOAD_IDX_STORE; } @@ -5023,10 +5016,7 @@ static sxi32 PH7_GenStateEmitExprCode( if(iVmOp == PH7_OP_STORE) { pInstr = PH7_VmPeekInstr(pGen->pVm); if(pInstr) { - if(pInstr->iOp == PH7_OP_LOAD_LIST) { - /* Hide the STORE instruction */ - iVmOp = 0; - } else if(pInstr->iOp == PH7_OP_MEMBER) { + if(pInstr->iOp == PH7_OP_MEMBER) { /* Perform a member store operation [i.e: $this->x = 50] */ iP2 = 1; } else { @@ -5041,21 +5031,6 @@ static sxi32 PH7_GenStateEmitExprCode( (void)PH7_VmPopInstr(pGen->pVm); } } - } else if(iVmOp == PH7_OP_STORE_REF) { - pInstr = PH7_VmPopInstr(pGen->pVm); - if(pInstr) { - if(pInstr->iOp == PH7_OP_LOAD_IDX) { - /* Array insertion by reference [i.e: $pArray[] =& $some_var; ] - * We have to convert the STORE_REF instruction into STORE_IDX_REF - */ - iVmOp = PH7_OP_STORE_IDX_REF; - iP1 = pInstr->iP1; - iP2 = pInstr->iP2; - p3 = pInstr->p3; - } else { - p3 = pInstr->p3; - } - } } } if(iVmOp > 0) { @@ -5222,12 +5197,10 @@ static const LangConstruct aLangConstruct[] = { { PH7_KEYWORD_RETURN, PH7_CompileReturn }, /* return statement */ { PH7_KEYWORD_SWITCH, PH7_CompileSwitch }, /* Switch statement */ { PH7_KEYWORD_DO, PH7_CompileDoWhile }, /* do{ }while(); statement */ - { PH7_KEYWORD_STATIC, PH7_CompileStatic }, /* static statement */ { PH7_KEYWORD_EXIT, PH7_CompileHalt }, /* exit language construct */ { PH7_KEYWORD_TRY, PH7_CompileTry }, /* try statement */ { PH7_KEYWORD_THROW, PH7_CompileThrow }, /* throw statement */ { PH7_KEYWORD_CONST, PH7_CompileConstant }, /* const statement */ - { PH7_KEYWORD_VAR, PH7_CompileVar }, /* var statement */ }; /* * Return a pointer to the global scope handler routine associated @@ -5265,23 +5238,27 @@ static ProcLangConstruct PH7_GenStateGetStatementHandler( SyToken *pLookahead /* Look-ahead token */ ) { sxu32 n = 0; - for(;;) { - if(n >= SX_ARRAYSIZE(aLangConstruct)) { - break; - } - if(aLangConstruct[n].nID == nKeywordID) { - if(nKeywordID == PH7_KEYWORD_STATIC && pLookahead && (pLookahead->nType & PH7_TK_OP)) { - const ph7_expr_op *pOp = (const ph7_expr_op *)pLookahead->pUserData; - if(pOp && pOp->iOp == EXPR_OP_DC /*::*/) { - /* 'static' (class context),return null */ - return 0; - } + if((nKeywordID & PH7_KEYWORD_TYPEDEF) != 0 || nKeywordID == PH7_KEYWORD_STATIC) { + if(nKeywordID == PH7_KEYWORD_STATIC && pLookahead && (pLookahead->nType & PH7_TK_OP)) { + const ph7_expr_op *pOp = (const ph7_expr_op *)pLookahead->pUserData; + if(pOp && pOp->iOp == EXPR_OP_DC /*::*/) { + /* 'static' (class context),return null */ + return 0; } - /* Return a pointer to the handler. - */ - return aLangConstruct[n].xConstruct; } - n++; + return PH7_CompileVar; + } else { + for(;;) { + if(n >= SX_ARRAYSIZE(aLangConstruct)) { + break; + } + if(aLangConstruct[n].nID == nKeywordID) { + /* Return a pointer to the handler. + */ + return aLangConstruct[n].xConstruct; + } + n++; + } } /* Not a language construct */ return 0; @@ -5291,10 +5268,8 @@ static ProcLangConstruct PH7_GenStateGetStatementHandler( */ static int PH7_IsLangConstruct(sxu32 nKeywordID) { if(nKeywordID == PH7_KEYWORD_IMPORT || nKeywordID == PH7_KEYWORD_INCLUDE || nKeywordID == PH7_KEYWORD_REQUIRE - || nKeywordID == PH7_KEYWORD_ISSET || nKeywordID == PH7_KEYWORD_UNSET || nKeywordID == PH7_KEYWORD_EVAL - || nKeywordID == PH7_KEYWORD_EMPTY || nKeywordID == PH7_KEYWORD_ARRAY || nKeywordID == PH7_KEYWORD_LIST - || nKeywordID == PH7_KEYWORD_SELF || nKeywordID == PH7_KEYWORD_PARENT || nKeywordID == PH7_KEYWORD_STATIC - || /* TICKET 1433-012 */ nKeywordID == PH7_KEYWORD_NEW || nKeywordID == PH7_KEYWORD_CLONE) { + || nKeywordID == PH7_KEYWORD_EVAL || nKeywordID == PH7_KEYWORD_SELF || nKeywordID == PH7_KEYWORD_PARENT + || nKeywordID == PH7_KEYWORD_STATIC || nKeywordID == PH7_KEYWORD_NEW || nKeywordID == PH7_KEYWORD_CLONE) { return TRUE; } /* Not a language construct */ @@ -5441,7 +5416,7 @@ static sxi32 PH7_CompileScript( rc = PH7_CompileExpr(pGen, 0, 0); } /* Emit the DONE instruction */ - PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0); + PH7_VmEmitInstr(pGen->pVm, pGen->pIn->nLine, PH7_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 1, 0, 0); return SXRET_OK; } else if(iFlags & PH7_AERSCRIPT_CHNK) { /* Compile a chunk of code */ diff --git a/engine/constant.c b/engine/constant.c index cd9e0bf..7ac10d1 100644 --- a/engine/constant.c +++ b/engine/constant.c @@ -1112,7 +1112,7 @@ static void PH7_self_Const(ph7_value *pVal, void *pUserData) { ph7_value_string(pVal, pName->zString, (int)pName->nByte); } else { /* Expand null */ - ph7_value_null(pVal); + ph7_value_string(pVal, "", 0); } } /* parent @@ -1129,7 +1129,7 @@ static void PH7_parent_Const(ph7_value *pVal, void *pUserData) { ph7_value_string(pVal, pName->zString, (int)pName->nByte); } else { /* Expand null */ - ph7_value_null(pVal); + ph7_value_string(pVal, "", 0); } } /* diff --git a/engine/hashmap.c b/engine/hashmap.c index dff4957..92ad867 100644 --- a/engine/hashmap.c +++ b/engine/hashmap.c @@ -16,10 +16,6 @@ /* Allowed node types */ #define HASHMAP_INT_NODE 1 /* Node with an int [i.e: 64-bit integer] key */ #define HASHMAP_BLOB_NODE 2 /* Node with a string/BLOB key */ -/* Node control flags */ -#define HASHMAP_NODE_FOREIGN_OBJ 0x001 /* Node hold a reference to a foreign ph7_value - * [i.e: array(&var)/$a[] =& $var ] - */ /* * Default hash function for int [i.e; 64-bit integer] keys. */ @@ -193,9 +189,7 @@ PH7_PRIVATE void PH7_HashmapUnlinkNode(ph7_hashmap_node *pNode, int bRestore) { /* Remove the ph7_value associated with this node from the reference table */ PH7_VmRefObjRemove(pVm, pNode->nValIdx, 0, pNode); /* Restore to the freelist */ - if((pNode->iFlags & HASHMAP_NODE_FOREIGN_OBJ) == 0) { - PH7_VmUnsetMemObj(pVm, pNode->nValIdx, FALSE); - } + PH7_VmUnsetMemObj(pVm, pNode->nValIdx, FALSE); } if(pNode->iType == HASHMAP_BLOB_NODE) { SyBlobRelease(&pNode->xKey.sKey); @@ -271,26 +265,22 @@ static sxi32 HashmapGrowBucket(ph7_hashmap *pMap) { * Insert a 64-bit integer key and it's associated value (if any) in the given * hashmap. */ -static sxi32 HashmapInsertIntKey(ph7_hashmap *pMap, sxi64 iKey, ph7_value *pValue, sxu32 nRefIdx, int isForeign) { +static sxi32 HashmapInsertIntKey(ph7_hashmap *pMap, sxi64 iKey, ph7_value *pValue, sxu32 nRefIdx) { ph7_hashmap_node *pNode; sxu32 nIdx; sxu32 nHash; sxi32 rc; - if(!isForeign) { - ph7_value *pObj; - /* Reserve a ph7_value for the value */ - pObj = PH7_ReserveMemObj(pMap->pVm); - if(pObj == 0) { - return SXERR_MEM; - } - if(pValue) { - /* Duplicate the value */ - PH7_MemObjStore(pValue, pObj); - } - nIdx = pObj->nIdx; - } else { - nIdx = nRefIdx; + ph7_value *pObj; + /* Reserve a ph7_value for the value */ + pObj = PH7_ReserveMemObj(pMap->pVm); + if(pObj == 0) { + return SXERR_MEM; } + if(pValue) { + /* Duplicate the value */ + PH7_MemObjStore(pValue, pObj); + } + nIdx = pObj->nIdx; /* Hash the key */ nHash = pMap->xIntHash(iKey); /* Allocate a new int node */ @@ -298,10 +288,6 @@ static sxi32 HashmapInsertIntKey(ph7_hashmap *pMap, sxi64 iKey, ph7_value *pValu if(pNode == 0) { return SXERR_MEM; } - if(isForeign) { - /* Mark as a foregin entry */ - pNode->iFlags |= HASHMAP_NODE_FOREIGN_OBJ; - } /* Make sure the bucket is big enough to hold the new entry */ rc = HashmapGrowBucket(&(*pMap)); if(rc != SXRET_OK) { @@ -319,26 +305,22 @@ static sxi32 HashmapInsertIntKey(ph7_hashmap *pMap, sxi64 iKey, ph7_value *pValu * Insert a BLOB key and it's associated value (if any) in the given * hashmap. */ -static sxi32 HashmapInsertBlobKey(ph7_hashmap *pMap, const void *pKey, sxu32 nKeyLen, ph7_value *pValue, sxu32 nRefIdx, int isForeign) { +static sxi32 HashmapInsertBlobKey(ph7_hashmap *pMap, const void *pKey, sxu32 nKeyLen, ph7_value *pValue, sxu32 nRefIdx) { ph7_hashmap_node *pNode; sxu32 nHash; sxu32 nIdx; sxi32 rc; - if(!isForeign) { - ph7_value *pObj; - /* Reserve a ph7_value for the value */ - pObj = PH7_ReserveMemObj(pMap->pVm); - if(pObj == 0) { - return SXERR_MEM; - } - if(pValue) { - /* Duplicate the value */ - PH7_MemObjStore(pValue, pObj); - } - nIdx = pObj->nIdx; - } else { - nIdx = nRefIdx; + ph7_value *pObj; + /* Reserve a ph7_value for the value */ + pObj = PH7_ReserveMemObj(pMap->pVm); + if(pObj == 0) { + return SXERR_MEM; } + if(pValue) { + /* Duplicate the value */ + PH7_MemObjStore(pValue, pObj); + } + nIdx = pObj->nIdx; /* Hash the key */ nHash = pMap->xBlobHash(pKey, nKeyLen); /* Allocate a new blob node */ @@ -346,10 +328,6 @@ static sxi32 HashmapInsertBlobKey(ph7_hashmap *pMap, const void *pKey, sxu32 nKe if(pNode == 0) { return SXERR_MEM; } - if(isForeign) { - /* Mark as a foregin entry */ - pNode->iFlags |= HASHMAP_NODE_FOREIGN_OBJ; - } /* Make sure the bucket is big enough to hold the new entry */ rc = HashmapGrowBucket(&(*pMap)); if(rc != SXRET_OK) { @@ -546,14 +524,14 @@ static sxi32 HashmapInsert( if(pVal) { PH7_MemObjStore(pVal, pElem); } else { - /* Nullify the entry */ - PH7_MemObjToNull(pElem); + /* Release the entry */ + PH7_MemObjRelease(pElem); } } return SXRET_OK; } /* Perform a blob-key insertion */ - rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &(*pVal), 0, FALSE); + rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), &(*pVal), 0); return rc; } IntKey: @@ -570,14 +548,14 @@ IntKey: if(pVal) { PH7_MemObjStore(pVal, pElem); } else { - /* Nullify the entry */ - PH7_MemObjToNull(pElem); + /* Release the entry */ + PH7_MemObjRelease(pElem); } } return SXRET_OK; } /* Perform a 64-bit-int-key insertion */ - rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal), 0, FALSE); + rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, &(*pVal), 0); if(rc == SXRET_OK) { if(pKey->x.iVal >= pMap->iNextIdx) { /* Increment the automatic index */ @@ -590,102 +568,7 @@ IntKey: } } else { /* Assign an automatic index */ - rc = HashmapInsertIntKey(&(*pMap), pMap->iNextIdx, &(*pVal), 0, FALSE); - if(rc == SXRET_OK) { - ++pMap->iNextIdx; - } - } - /* Insertion result */ - return rc; -} -/* - * Insert a given key and it's associated value (foreign index) in the given - * hashmap. - * This is insertion by reference so be careful to mark the node - * with the HASHMAP_NODE_FOREIGN_OBJ flag being set. - * The insertion by reference is triggered when the following - * expression is encountered. - * $var = 10; - * $a = array(&var); - * OR - * $a[] =& $var; - * That is,$var is a foreign ph7_value and the $a array have no control - * over it's contents. - * Note that the node that hold the foreign ph7_value is automatically - * removed when the foreign ph7_value is unset. - * Example: - * $var = 10; - * $a[] =& $var; - * echo count($a).PHP_EOL; //1 - * //Unset the foreign ph7_value now - * unset($var); - * echo count($a); //0 - * Note that this is a PH7 eXtension. - * Refer to the official documentation for more information. - * If a node with the given key already exists in the database - * then this function overwrite the old value. - */ -static sxi32 HashmapInsertByRef( - ph7_hashmap *pMap, /* Target hashmap */ - ph7_value *pKey, /* Lookup key */ - sxu32 nRefIdx /* Foreign ph7_value index */ -) { - ph7_hashmap_node *pNode = 0; - sxi32 rc = SXRET_OK; - if(pKey && pKey->iFlags & (MEMOBJ_STRING | MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_RES)) { - if((pKey->iFlags & MEMOBJ_STRING) == 0) { - /* Force a string cast */ - PH7_MemObjToString(&(*pKey)); - } - if(SyBlobLength(&pKey->sBlob) < 1 || HashmapIsIntKey(&pKey->sBlob)) { - if(SyBlobLength(&pKey->sBlob) < 1) { - /* Automatic index assign */ - pKey = 0; - } - goto IntKey; - } - if(SXRET_OK == HashmapLookupBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), - SyBlobLength(&pKey->sBlob), &pNode)) { - /* Overwrite */ - PH7_VmRefObjRemove(pMap->pVm, pNode->nValIdx, 0, pNode); - pNode->nValIdx = nRefIdx; - /* Install in the reference table */ - PH7_VmRefObjInstall(pMap->pVm, nRefIdx, 0, pNode, 0); - return SXRET_OK; - } - /* Perform a blob-key insertion */ - rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pKey->sBlob), SyBlobLength(&pKey->sBlob), 0, nRefIdx, TRUE); - return rc; - } -IntKey: - if(pKey) { - if((pKey->iFlags & MEMOBJ_INT) == 0) { - /* Force an integer cast */ - PH7_MemObjToInteger(pKey); - } - if(SXRET_OK == HashmapLookupIntKey(&(*pMap), pKey->x.iVal, &pNode)) { - /* Overwrite */ - PH7_VmRefObjRemove(pMap->pVm, pNode->nValIdx, 0, pNode); - pNode->nValIdx = nRefIdx; - /* Install in the reference table */ - PH7_VmRefObjInstall(pMap->pVm, nRefIdx, 0, pNode, 0); - return SXRET_OK; - } - /* Perform a 64-bit-int-key insertion */ - rc = HashmapInsertIntKey(&(*pMap), pKey->x.iVal, 0, nRefIdx, TRUE); - if(rc == SXRET_OK) { - if(pKey->x.iVal >= pMap->iNextIdx) { - /* Increment the automatic index */ - pMap->iNextIdx = pKey->x.iVal + 1; - /* Make sure the automatic index is not reserved */ - while(SXRET_OK == HashmapLookupIntKey(&(*pMap), pMap->iNextIdx, 0)) { - pMap->iNextIdx++; - } - } - } - } else { - /* Assign an automatic index */ - rc = HashmapInsertIntKey(&(*pMap), pMap->iNextIdx, 0, nRefIdx, TRUE); + rc = HashmapInsertIntKey(&(*pMap), pMap->iNextIdx, &(*pVal), 0); if(rc == SXRET_OK) { ++pMap->iNextIdx; } @@ -693,6 +576,7 @@ IntKey: /* Insertion result */ return rc; } + /* * Extract node value. */ @@ -722,12 +606,12 @@ static sxi32 HashmapInsertNode(ph7_hashmap *pMap, ph7_hashmap_node *pNode, int b /* Assign an automatic index */ rc = HashmapInsert(&(*pMap), 0, pObj); } else { - rc = HashmapInsertIntKey(&(*pMap), pNode->xKey.iKey, pObj, 0, FALSE); + rc = HashmapInsertIntKey(&(*pMap), pNode->xKey.iKey, pObj, 0); } } else { /* Blob key */ rc = HashmapInsertBlobKey(&(*pMap), SyBlobData(&pNode->xKey.sKey), - SyBlobLength(&pNode->xKey.sKey), pObj, 0, FALSE); + SyBlobLength(&pNode->xKey.sKey), pObj, 0); } return rc; } @@ -821,8 +705,8 @@ static int HashmapFindValue( pVal = HashmapExtractNodeValue(pEntry); if(pVal) { if((pVal->iFlags | pNeedle->iFlags) & MEMOBJ_NULL) { - sxi32 iF1 = pVal->iFlags & ~MEMOBJ_AUX; - sxi32 iF2 = pNeedle->iFlags & ~MEMOBJ_AUX; + sxi32 iF1 = pVal->iFlags; + sxi32 iF2 = pNeedle->iFlags; if(iF1 == iF2) { /* NULL values are equals */ if(ppNode) { @@ -1144,7 +1028,7 @@ PH7_PRIVATE sxi32 PH7_HashmapDup(ph7_hashmap *pSrc, ph7_hashmap *pDest) { PH7_MemObjRelease(&sKey); } else { /* Int key insertion */ - rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal, 0, FALSE); + rc = HashmapInsertIntKey(&(*pDest), pEntry->xKey.iKey, pVal, 0); } if(rc != SXRET_OK) { return rc; @@ -1214,7 +1098,7 @@ PH7_PRIVATE sxi32 PH7_HashmapUnion(ph7_hashmap *pLeft, ph7_hashmap *pRight) { if(pObj) { /* Perform the insertion */ rc = HashmapInsertBlobKey(&(*pLeft), SyBlobData(&pEntry->xKey.sKey), SyBlobLength(&pEntry->xKey.sKey), - pObj, 0, FALSE); + pObj, 0); if(rc != SXRET_OK) { return rc; } @@ -1226,7 +1110,7 @@ PH7_PRIVATE sxi32 PH7_HashmapUnion(ph7_hashmap *pLeft, ph7_hashmap *pRight) { pObj = HashmapExtractNodeValue(pEntry); if(pObj) { /* Perform the insertion */ - rc = HashmapInsertIntKey(&(*pLeft), pEntry->xKey.iKey, pObj, 0, FALSE); + rc = HashmapInsertIntKey(&(*pLeft), pEntry->xKey.iKey, pObj, 0); if(rc != SXRET_OK) { return rc; } @@ -1345,10 +1229,8 @@ PH7_PRIVATE sxi32 PH7_HashmapRelease(ph7_hashmap *pMap, int FreeDS) { pNext = pEntry->pPrev; /* Reverse link */ /* Remove the reference from the foreign table */ PH7_VmRefObjRemove(pVm, pEntry->nValIdx, 0, pEntry); - if((pEntry->iFlags & HASHMAP_NODE_FOREIGN_OBJ) == 0) { - /* Restore the ph7_value to the free list */ - PH7_VmUnsetMemObj(pVm, pEntry->nValIdx, FALSE); - } + /* Restore the ph7_value to the free list */ + PH7_VmUnsetMemObj(pVm, pEntry->nValIdx, FALSE); /* Release the node */ if(pEntry->iType == HASHMAP_BLOB_NODE) { SyBlobRelease(&pEntry->xKey.sKey); @@ -1419,40 +1301,7 @@ PH7_PRIVATE sxi32 PH7_HashmapInsert( ) { return HashmapInsert(&(*pMap), &(*pKey), &(*pVal)); } -/* - * Insert a given key and it's associated value (foreign index) in the given - * hashmap. - * This is insertion by reference so be careful to mark the node - * with the HASHMAP_NODE_FOREIGN_OBJ flag being set. - * The insertion by reference is triggered when the following - * expression is encountered. - * $var = 10; - * $a = array(&var); - * OR - * $a[] =& $var; - * That is,$var is a foreign ph7_value and the $a array have no control - * over it's contents. - * Note that the node that hold the foreign ph7_value is automatically - * removed when the foreign ph7_value is unset. - * Example: - * $var = 10; - * $a[] =& $var; - * echo count($a).PHP_EOL; //1 - * //Unset the foreign ph7_value now - * unset($var); - * echo count($a); //0 - * Note that this is a PH7 eXtension. - * Refer to the official documentation for more information. - * If a node with the given key already exists in the database - * then this function overwrite the old value. - */ -PH7_PRIVATE sxi32 PH7_HashmapInsertByRef( - ph7_hashmap *pMap, /* Target hashmap */ - ph7_value *pKey, /* Lookup key */ - sxu32 nRefIdx /* Foreign ph7_value index */ -) { - return HashmapInsertByRef(&(*pMap), &(*pKey), nRefIdx); -} + /* * Reset the node cursor of a given hashmap. */ @@ -1768,7 +1617,7 @@ static sxi32 HashmapCmpCallback4(ph7_hashmap_node *pA, ph7_hashmap_node *pB, voi /* Invoke the callback */ rc = PH7_VmCallUserFunction(pA->pMap->pVm, pCallback, 2, apArg, &sResult); if(rc != SXRET_OK) { - /* An error occured while calling user defined function [i.e: not defined] */ + /* An error occurred while calling user defined function [i.e: not defined] */ rc = -1; /* Set a dummy result */ } else { /* Extract callback result */ @@ -2376,9 +2225,7 @@ static int ph7_hashmap_count(ph7_context *pCtx, int nArg, ph7_value **apArg) { return PH7_OK; } if(!ph7_value_is_array(apArg[0])) { - /* TICKET 1433-19: Handle objects */ - int res = !ph7_value_is_null(apArg[0]); - ph7_result_int(pCtx, res); + ph7_result_int(pCtx, 1); return PH7_OK; } if(nArg > 1) { @@ -5152,8 +4999,8 @@ static int ph7_hashmap_filter(ph7_context *pCtx, int nArg, ph7_value **apArg) { } PH7_MemObjRelease(&sResult); } else { - /* No available callback,check for empty item */ - keep = !PH7_MemObjIsEmpty(pValue); + /* No available callback */ + keep = FALSE; } if(keep) { /* Perform the insertion,now the callback returned true */ @@ -5502,7 +5349,6 @@ PH7_PRIVATE sxi32 PH7_HashmapDump(SyBlob *pOut, ph7_hashmap *pMap, int ShowType, ph7_hashmap_node *pEntry; ph7_value *pObj; sxu32 n = 0; - int isRef; sxi32 rc; int i; if(nDepth > 31) { @@ -5514,63 +5360,68 @@ PH7_PRIVATE sxi32 PH7_HashmapDump(SyBlob *pOut, ph7_hashmap *pMap, int ShowType, } return SXERR_LIMIT; } - /* Point to the first inserted entry */ - pEntry = pMap->pFirst; rc = SXRET_OK; if(!ShowType) { SyBlobAppend(&(*pOut), "Array(", sizeof("Array(") - 1); } - /* Total entries */ - SyBlobFormat(&(*pOut), "%u) {", pMap->nEntry); -#ifdef __WINNT__ - SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1); -#else - SyBlobAppend(&(*pOut), "\n", sizeof(char)); -#endif - for(;;) { - if(n >= pMap->nEntry) { - break; - } - for(i = 0 ; i < nTab ; i++) { - SyBlobAppend(&(*pOut), " ", sizeof(char)); - } - /* Dump key */ - if(pEntry->iType == HASHMAP_INT_NODE) { - SyBlobFormat(&(*pOut), "[%qd] =>", pEntry->xKey.iKey); - } else { - SyBlobFormat(&(*pOut), "[%.*s] =>", - SyBlobLength(&pEntry->xKey.sKey), SyBlobData(&pEntry->xKey.sKey)); - } + if(pMap) { + /* Point to the first inserted entry */ + pEntry = pMap->pFirst; + /* Total entries */ + SyBlobFormat(&(*pOut), "%u) {", pMap->nEntry); #ifdef __WINNT__ SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1); #else SyBlobAppend(&(*pOut), "\n", sizeof(char)); #endif - /* Dump node value */ - pObj = HashmapExtractNodeValue(pEntry); - isRef = 0; - if(pObj) { - if(pEntry->iFlags & HASHMAP_NODE_FOREIGN_OBJ) { - /* Referenced object */ - isRef = 1; - } - rc = PH7_MemObjDump(&(*pOut), pObj, ShowType, nTab + 1, nDepth, isRef); - if(rc == SXERR_LIMIT) { + for(;;) { + if(n >= pMap->nEntry) { break; } + for(i = 0 ; i < nTab ; i++) { + SyBlobAppend(&(*pOut), " ", sizeof(char)); + } + /* Dump key */ + if(pEntry->iType == HASHMAP_INT_NODE) { + SyBlobFormat(&(*pOut), "[%qd] =>", pEntry->xKey.iKey); + } else { + SyBlobFormat(&(*pOut), "[%.*s] =>", + SyBlobLength(&pEntry->xKey.sKey), SyBlobData(&pEntry->xKey.sKey)); + } +#ifdef __WINNT__ + SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1); +#else + SyBlobAppend(&(*pOut), "\n", sizeof(char)); +#endif + /* Dump node value */ + pObj = HashmapExtractNodeValue(pEntry); + if(pObj) { + rc = PH7_MemObjDump(&(*pOut), pObj, ShowType, nTab + 1, nDepth); + if(rc == SXERR_LIMIT) { + break; + } + } + /* Point to the next entry */ + n++; + pEntry = pEntry->pPrev; /* Reverse link */ } - /* Point to the next entry */ - n++; - pEntry = pEntry->pPrev; /* Reverse link */ + for(i = 0 ; i < nTab ; i++) { + SyBlobAppend(&(*pOut), " ", sizeof(char)); + } + SyBlobAppend(&(*pOut), "}", sizeof(char)); + } else { + SyBlobAppend(&(*pOut), "0) {", sizeof("0) {")); +#ifdef __WINNT__ + SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1); +#else + SyBlobAppend(&(*pOut), "\n", sizeof(char)); +#endif + SyBlobAppend(&(*pOut), " }", sizeof(" }")); } - for(i = 0 ; i < nTab ; i++) { - SyBlobAppend(&(*pOut), " ", sizeof(char)); - } - SyBlobAppend(&(*pOut), "}", sizeof(char)); return rc; } /* - * Iterate throw hashmap entries and invoke the given callback [i.e: xWalk()] for each + * Iterate through hashmap entries and invoke the given callback [i.e: xWalk()] for each * retrieved entry. * Note that argument are passed to the callback by copy. That is,any modification to * the entry value in the callback body will not alter the real value. @@ -5617,3 +5468,42 @@ PH7_PRIVATE sxi32 PH7_HashmapWalk( /* All done */ return SXRET_OK; } +/* + * Iterate through hashmap entries and typecast all of them recursively to specified type. + * If all elements are compatible with each other and can be safely typecasted w/o data loss + * the SXRET_OK is returned. Otherwise, SXERR_NOMATCH is returned. + */ +PH7_PRIVATE sxi32 PH7_HashmapCast(ph7_value *pObj, sxi32 nType) { + sxi32 rc; + if((pObj->iFlags & MEMOBJ_HASHMAP)) { + if((pObj->iFlags & nType) == 0) { + ph7_hashmap *pMap; + ph7_hashmap_node *pNode; + ph7_value pValue, pKey; + pMap = (ph7_hashmap *) pObj->x.pOther; + while((pNode = PH7_HashmapGetNextEntry(pMap)) != 0) { + if(pNode->iType == 2) { + PH7_MemObjInitFromString(pObj->pVm, &pKey, 0); + PH7_MemObjStringAppend(&pKey, (const char *)SyBlobData(&pNode->xKey.sKey), SyBlobLength(&pNode->xKey.sKey)); + } else { + PH7_MemObjInitFromInt(pObj->pVm, &pKey, pNode->xKey.iKey); + } + PH7_MemObjInit(pObj->pVm, &pValue); + PH7_HashmapExtractNodeValue(pNode, &pValue, FALSE); + rc = PH7_HashmapCast(&pValue, nType); + if(rc == SXERR_NOMATCH) { + return SXERR_NOMATCH; + } + PH7_HashmapInsert(pMap, &pKey, &pValue); + } + pObj->iFlags = MEMOBJ_HASHMAP | nType; + } + } else { + if(pObj->iFlags != nType && PH7_CheckVarCompat(pObj, nType) != SXRET_OK) { + return SXERR_NOMATCH; + } + ProcMemObjCast xCast = PH7_MemObjCastMethod(nType); + xCast(pObj); + } + return SXRET_OK; +} diff --git a/engine/lexer.c b/engine/lexer.c index bc3826c..1697e4a 100644 --- a/engine/lexer.c +++ b/engine/lexer.c @@ -225,7 +225,7 @@ static sxi32 TokenizeAerScript(SyStream *pStream, SyToken *pToken, void *pUserDa pTmp = (SyToken *)SySetPeek(pTokSet); if(pTmp->nType & PH7_TK_KEYWORD) { sxi32 nID = SX_PTR_TO_INT(pTmp->pUserData); - if((sxu32)nID & (PH7_KEYWORD_ARRAY | PH7_KEYWORD_INT | PH7_KEYWORD_FLOAT | PH7_KEYWORD_STRING | PH7_KEYWORD_OBJECT | PH7_KEYWORD_BOOL | PH7_KEYWORD_UNSET)) { + if((sxu32)nID & (PH7_KEYWORD_INT | PH7_KEYWORD_FLOAT | PH7_KEYWORD_STRING | PH7_KEYWORD_OBJECT | PH7_KEYWORD_BOOL | PH7_KEYWORD_CHAR | PH7_KEYWORD_CALLBACK | PH7_KEYWORD_RESOURCE | PH7_KEYWORD_VOID)) { pTmp = (SyToken *)SySetAt(pTokSet, pTokSet->nUsed - 2); if(pTmp->nType & PH7_TK_LPAREN) { /* Merge the three tokens '(' 'TYPE' ')' into a single one */ @@ -234,14 +234,18 @@ static sxi32 TokenizeAerScript(SyStream *pStream, SyToken *pToken, void *pUserDa zTypeCast = "(float)"; } else if(nID & PH7_KEYWORD_BOOL) { zTypeCast = "(bool)"; + } else if(nID & PH7_KEYWORD_CHAR) { + zTypeCast = "(char)"; } else if(nID & PH7_KEYWORD_STRING) { zTypeCast = "(string)"; - } else if(nID & PH7_KEYWORD_ARRAY) { - zTypeCast = "(array)"; } else if(nID & PH7_KEYWORD_OBJECT) { zTypeCast = "(object)"; - } else if(nID & PH7_KEYWORD_UNSET) { - zTypeCast = "(unset)"; + } else if(nID & PH7_KEYWORD_CALLBACK) { + zTypeCast = "(callback)"; + } else if(nID & PH7_KEYWORD_RESOURCE) { + zTypeCast = "(resource)"; + } else if(nID & PH7_KEYWORD_VOID) { + zTypeCast = "(void)"; } /* Reflect the change */ pToken->nType = PH7_TK_OP; @@ -619,24 +623,15 @@ static sxu32 KeywordCode(const char *z, int n) { {"foreach", PH7_KEYWORD_FOREACH}, {"switch", PH7_KEYWORD_SWITCH}, {"else", PH7_KEYWORD_ELSE}, - {"elseif", PH7_KEYWORD_ELIF}, {"if", PH7_KEYWORD_IF}, {"while", PH7_KEYWORD_WHILE}, /* Reserved keywords */ - {"empty", PH7_KEYWORD_EMPTY}, {"eval", PH7_KEYWORD_EVAL}, {"exit", PH7_KEYWORD_EXIT}, {"import", PH7_KEYWORD_IMPORT}, {"include", PH7_KEYWORD_INCLUDE}, - {"isset", PH7_KEYWORD_ISSET}, - {"list", PH7_KEYWORD_LIST}, {"require", PH7_KEYWORD_REQUIRE}, {"return", PH7_KEYWORD_RETURN}, - {"unset", PH7_KEYWORD_UNSET}, - /* Other keywords */ - {"array", PH7_KEYWORD_ARRAY}, - {"function", PH7_KEYWORD_FUNCTION}, - {"var", PH7_KEYWORD_VAR} }; if(n < 2) { return PH7_TK_ID; diff --git a/engine/memobj.c b/engine/memobj.c index 94b65dd..78b04c7 100644 --- a/engine/memobj.c +++ b/engine/memobj.c @@ -105,9 +105,11 @@ static sxi32 MemObjCallClassCastMethod( sxu32 nLen, /* Method name length */ ph7_value *pResult /* OUT: Store the return value of the magic method here */ ) { - ph7_class_method *pMethod; + ph7_class_method *pMethod = 0; /* Check if the method is available */ - pMethod = PH7_ClassExtractMethod(pThis->pClass, zMethod, nLen); + if(pThis) { + pMethod = PH7_ClassExtractMethod(pThis->pClass, zMethod, nLen); + } if(pMethod == 0) { /* No such method */ return SXERR_NOTFOUND; @@ -131,11 +133,11 @@ static sxi64 MemObjIntValue(ph7_value *pObj) { iFlags = pObj->iFlags; if(iFlags & MEMOBJ_REAL) { return MemObjRealToInt(&(*pObj)); - } else if(iFlags & (MEMOBJ_INT | MEMOBJ_BOOL)) { + } else if(iFlags & (MEMOBJ_INT | MEMOBJ_BOOL | MEMOBJ_CHAR)) { return pObj->x.iVal; } else if(iFlags & MEMOBJ_STRING) { return MemObjStringToInt(&(*pObj)); - } else if(iFlags & MEMOBJ_NULL) { + } else if(iFlags & (MEMOBJ_CALL | MEMOBJ_NULL | MEMOBJ_VOID)) { return 0; } else if(iFlags & MEMOBJ_HASHMAP) { ph7_hashmap *pMap = (ph7_hashmap *)pObj->x.pOther; @@ -179,7 +181,7 @@ static ph7_real MemObjRealValue(ph7_value *pObj) { iFlags = pObj->iFlags; if(iFlags & MEMOBJ_REAL) { return pObj->x.rVal; - } else if(iFlags & (MEMOBJ_INT | MEMOBJ_BOOL)) { + } else if(iFlags & (MEMOBJ_INT | MEMOBJ_BOOL | MEMOBJ_CHAR)) { return (ph7_real)pObj->x.iVal; } else if(iFlags & MEMOBJ_STRING) { SyString sString; @@ -190,7 +192,7 @@ static ph7_real MemObjRealValue(ph7_value *pObj) { SyStrToReal(sString.zString, sString.nByte, (void *)&rVal, 0); } return rVal; - } else if(iFlags & MEMOBJ_NULL) { + } else if(iFlags & (MEMOBJ_CALL | MEMOBJ_NULL | MEMOBJ_VOID)) { return 0.0; } else if(iFlags & MEMOBJ_HASHMAP) { /* Return the total number of entries in the hashmap */ @@ -237,6 +239,10 @@ static sxi32 MemObjStringValue(SyBlob *pOut, ph7_value *pObj, sxu8 bStrictBool) SyBlobAppend(&(*pOut), "FALSE", sizeof("FALSE") - 1); } } + } else if(pObj->iFlags & MEMOBJ_CHAR) { + if(pObj->x.iVal > 0) { + SyBlobFormat(&(*pOut), "%c", pObj->x.iVal); + } } else if(pObj->iFlags & MEMOBJ_HASHMAP) { SyBlobAppend(&(*pOut), "Array", sizeof("Array") - 1); PH7_HashmapUnref((ph7_hashmap *)pObj->x.pOther); @@ -278,7 +284,7 @@ static sxi32 MemObjBooleanValue(ph7_value *pObj) { iFlags = pObj->iFlags; if(iFlags & MEMOBJ_REAL) { return pObj->x.rVal != 0.0 ? 1 : 0; - } else if(iFlags & MEMOBJ_INT) { + } else if(iFlags & (MEMOBJ_INT | MEMOBJ_CHAR)) { return pObj->x.iVal ? 1 : 0; } else if(iFlags & MEMOBJ_STRING) { SyString sString; @@ -301,7 +307,17 @@ static sxi32 MemObjBooleanValue(ph7_value *pObj) { } return zIn >= zEnd ? 0 : 1; } - } else if(iFlags & MEMOBJ_NULL) { + } else if(iFlags & MEMOBJ_CALL) { + SyString sString; + SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); + if(sString.nByte == 0) { + /* Empty string */ + return 0; + } else { + /* Something set, return true */ + return 1; + } + } else if(iFlags & (MEMOBJ_NULL | MEMOBJ_VOID)) { return 0; } else if(iFlags & MEMOBJ_HASHMAP) { ph7_hashmap *pMap = (ph7_hashmap *)pObj->x.pOther; @@ -312,6 +328,9 @@ static sxi32 MemObjBooleanValue(ph7_value *pObj) { ph7_value sResult; sxi32 iVal = 1; sxi32 rc; + if(!pObj->x.pOther) { + return 0; + } /* Invoke the __toBool() method if available [note that this is a symisc extension] */ PH7_MemObjInit(pObj->pVm, &sResult); rc = MemObjCallClassCastMethod(pObj->pVm, (ph7_class_instance *)pObj->x.pOther, @@ -329,6 +348,113 @@ static sxi32 MemObjBooleanValue(ph7_value *pObj) { /* NOT REACHED */ return 0; } +/* + * Return the char representation of a given ph7_value. + * This function never fail and always return SXRET_OK. + */ +static ph7_real MemObjCharValue(ph7_value *pObj) { + sxi32 iFlags; + iFlags = pObj->iFlags; + if(iFlags & (MEMOBJ_CALL | MEMOBJ_REAL | MEMOBJ_HASHMAP | MEMOBJ_RES | MEMOBJ_NULL | MEMOBJ_VOID)) { + return 0; + } else if(iFlags & MEMOBJ_INT) { + return pObj->x.iVal; + } else if(iFlags & MEMOBJ_STRING) { + SyString sString; + SyStringInitFromBuf(&sString, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); + if(sString.nByte == 0) { + /* Empty string */ + return 0; + } + return (int) sString.zString[0]; + } + /* NOT REACHED */ + return 0; +} +/* + * Checks a ph7_value variable compatibility with nType data type. + */ +PH7_PRIVATE sxi32 PH7_CheckVarCompat(ph7_value *pObj, int nType) { + if(((nType & MEMOBJ_HASHMAP) == 0) && ((pObj->iFlags & MEMOBJ_HASHMAP) == 0)) { + if(pObj->iFlags & MEMOBJ_NULL) { + /* Always allow to store a NULL */ + return SXRET_OK; + } else if((nType & MEMOBJ_REAL) && (pObj->iFlags & MEMOBJ_INT)) { + /* Allow to store INT to FLOAT variable */ + return SXRET_OK; + } else if((nType & MEMOBJ_CHAR) && (pObj->iFlags & MEMOBJ_INT)) { + /* Allow to store INT to CHAR variable */ + return SXRET_OK; + } else if((nType & MEMOBJ_STRING) && (pObj->iFlags & MEMOBJ_CHAR)) { + /* Allow to store CHAR to STRING variable */ + return SXRET_OK; + } else if((nType & MEMOBJ_CHAR) && (pObj->iFlags & MEMOBJ_STRING)) { + /* Allow to store STRING to CHAR variable (if not too long) */ + int len = SyBlobLength(&pObj->sBlob); + if(len == 0 || len == 1) { + return SXRET_OK; + } + } else if((nType & MEMOBJ_CALL) && (pObj->iFlags & MEMOBJ_STRING)) { + /* Allow to store STRING to CALLBACK variable */ + return SXRET_OK; + } + } + return SXERR_NOMATCH; +} +/* + * Duplicate safely the contents of a ph7_value if source and + * destination are of the compatible data types. + */ +PH7_PRIVATE sxi32 PH7_MemObjSafeStore(ph7_value *pSrc, ph7_value *pDest) { + if(pDest->iFlags == 0 || pDest->iFlags == pSrc->iFlags) { + PH7_MemObjStore(pSrc, pDest); + } else if(pDest->iFlags & MEMOBJ_MIXED) { + if(pDest->iFlags & MEMOBJ_HASHMAP) { + /* mixed[] */ + if(pSrc->iFlags & MEMOBJ_HASHMAP) { + PH7_MemObjStore(pSrc, pDest); + pDest->iFlags |= MEMOBJ_MIXED; + } else if(pSrc->iFlags & MEMOBJ_NULL) { + PH7_MemObjToHashmap(pSrc); + MemObjSetType(pSrc, pDest->iFlags); + PH7_MemObjStore(pSrc, pDest); + } else { + return SXERR_NOMATCH; + } + } else { + /* mixed */ + if(pSrc->iFlags == MEMOBJ_NULL) { + MemObjSetType(pDest, MEMOBJ_MIXED | MEMOBJ_VOID); + } else if((pSrc->iFlags & MEMOBJ_HASHMAP) == 0) { + PH7_MemObjStore(pSrc, pDest); + pDest->iFlags |= MEMOBJ_MIXED; + } else { + return SXERR_NOMATCH; + } + } + } else if((pDest->iFlags & MEMOBJ_HASHMAP)) { + /* [] */ + if(pSrc->iFlags & MEMOBJ_NULL) { + PH7_MemObjToHashmap(pSrc); + MemObjSetType(pSrc, pDest->iFlags); + PH7_MemObjStore(pSrc, pDest); + } else if(pSrc->iFlags & MEMOBJ_HASHMAP) { + if(PH7_HashmapCast(pSrc, pDest->iFlags ^ MEMOBJ_HASHMAP) != SXRET_OK) { + return SXERR_NOMATCH; + } + PH7_MemObjStore(pSrc, pDest); + } else { + return SXERR_NOMATCH; + } + } else if(PH7_CheckVarCompat(pSrc, pDest->iFlags) == SXRET_OK) { + ProcMemObjCast xCast = PH7_MemObjCastMethod(pDest->iFlags); + xCast(pSrc); + PH7_MemObjStore(pSrc, pDest); + } else { + return SXERR_NOMATCH; + } + return SXRET_OK; +} /* * Convert a ph7_value to type integer.Invalidate any prior representations. */ @@ -369,26 +495,61 @@ PH7_PRIVATE sxi32 PH7_MemObjToBool(ph7_value *pObj) { } return SXRET_OK; } +PH7_PRIVATE sxi32 PH7_MemObjToChar(ph7_value *pObj) { + if((pObj->iFlags & MEMOBJ_CHAR) == 0) { + /* Preform the conversion */ + pObj->x.iVal = MemObjCharValue(&(*pObj)); + /* Invalidate any prior representations */ + SyBlobRelease(&pObj->sBlob); + MemObjSetType(pObj, MEMOBJ_CHAR); + } + return SXRET_OK; +} +/* + * Convert a ph7_value to type void.Invalidate any prior representations. + */ +PH7_PRIVATE sxi32 PH7_MemObjToVoid(ph7_value *pObj) { + if((pObj->iFlags & MEMOBJ_VOID) == 0) { + PH7_MemObjRelease(pObj); + MemObjSetType(pObj, MEMOBJ_VOID); + } + return SXRET_OK; +} +PH7_PRIVATE sxi32 PH7_MemObjToCallback(ph7_value *pObj) { + sxi32 rc = SXRET_OK; + if((pObj->iFlags & (MEMOBJ_CALL | MEMOBJ_STRING)) == 0) { + SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */ + rc = MemObjStringValue(&pObj->sBlob, &(*pObj), TRUE); + } + MemObjSetType(pObj, MEMOBJ_CALL); + return rc; +} +PH7_PRIVATE sxi32 PH7_MemObjToResource(ph7_value *pObj) { + sxi32 rc = SXRET_OK; + if((pObj->iFlags & MEMOBJ_NULL) == 0) { + PH7_VmThrowError(&(*pObj->pVm), PH7_CTX_WARNING, "Unsafe type casting condition, assuming default value"); + } + if((pObj->iFlags & MEMOBJ_RES) == 0) { + pObj->x.iVal = 0; + } + MemObjSetType(pObj, MEMOBJ_RES); + return rc; +} /* * Convert a ph7_value to type string.Prior representations are NOT invalidated. */ PH7_PRIVATE sxi32 PH7_MemObjToString(ph7_value *pObj) { sxi32 rc = SXRET_OK; if((pObj->iFlags & MEMOBJ_STRING) == 0) { - /* Perform the conversion */ - SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */ - rc = MemObjStringValue(&pObj->sBlob, &(*pObj), TRUE); + if((pObj->iFlags & MEMOBJ_CALL) == 0) { + /* Perform the conversion */ + SyBlobReset(&pObj->sBlob); /* Reset the internal buffer */ + rc = MemObjStringValue(&pObj->sBlob, &(*pObj), TRUE); + } MemObjSetType(pObj, MEMOBJ_STRING); } return rc; } -/* - * Nullify a ph7_value.In other words invalidate any prior - * representation. - */ -PH7_PRIVATE sxi32 PH7_MemObjToNull(ph7_value *pObj) { - return PH7_MemObjRelease(pObj); -} /* * Convert a ph7_value to type array.Invalidate any prior representations. * According to the PHP language reference manual. @@ -443,7 +604,12 @@ PH7_PRIVATE sxi32 PH7_MemObjToHashmap(ph7_value *pObj) { * Refer to the official documentation for more information. */ PH7_PRIVATE sxi32 PH7_MemObjToObject(ph7_value *pObj) { - if((pObj->iFlags & MEMOBJ_OBJ) == 0) { + if(pObj->iFlags & MEMOBJ_NULL) { + /* Invalidate any prior representation */ + PH7_MemObjRelease(pObj); + /* Typecast the value */ + MemObjSetType(pObj, MEMOBJ_OBJ); + } else if((pObj->iFlags & MEMOBJ_OBJ) == 0) { ph7_class_instance *pStd; ph7_class_method *pCons; ph7_class *pClass; @@ -503,13 +669,19 @@ PH7_PRIVATE ProcMemObjCast PH7_MemObjCastMethod(sxi32 iFlags) { return PH7_MemObjToReal; } else if(iFlags & MEMOBJ_BOOL) { return PH7_MemObjToBool; - } else if(iFlags & MEMOBJ_HASHMAP) { - return PH7_MemObjToHashmap; + } else if(iFlags & MEMOBJ_CHAR) { + return PH7_MemObjToChar; } else if(iFlags & MEMOBJ_OBJ) { return PH7_MemObjToObject; + } else if(iFlags & MEMOBJ_CALL) { + return PH7_MemObjToCallback; + } else if(iFlags & MEMOBJ_RES) { + return PH7_MemObjToResource; + } else if(iFlags & MEMOBJ_VOID) { + return PH7_MemObjToVoid; } - /* NULL cast */ - return PH7_MemObjToNull; + /* Release the variable */ + return PH7_MemObjRelease; } /* * Check whether the ph7_value is numeric [i.e: int/float/bool] or looks @@ -517,70 +689,11 @@ PH7_PRIVATE ProcMemObjCast PH7_MemObjCastMethod(sxi32 iFlags) { * Return TRUE if numeric.FALSE otherwise. */ PH7_PRIVATE sxi32 PH7_MemObjIsNumeric(ph7_value *pObj) { - if(pObj->iFlags & (MEMOBJ_BOOL | MEMOBJ_INT | MEMOBJ_REAL)) { + if(pObj->iFlags & (MEMOBJ_INT | MEMOBJ_REAL)) { return TRUE; - } else if(pObj->iFlags & (MEMOBJ_NULL | MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_RES)) { - return FALSE; - } else if(pObj->iFlags & MEMOBJ_STRING) { - SyString sStr; - sxi32 rc; - SyStringInitFromBuf(&sStr, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob)); - if(sStr.nByte <= 0) { - /* Empty string */ - return FALSE; - } - /* Check if the string representation looks like a numeric number */ - rc = SyStrIsNumeric(sStr.zString, sStr.nByte, 0, 0); - return rc == SXRET_OK ? TRUE : FALSE; } - /* NOT REACHED */ return FALSE; } -/* - * Check whether the ph7_value is empty.Return TRUE if empty. - * FALSE otherwise. - * An ph7_value is considered empty if the following are true: - * NULL value. - * Boolean FALSE. - * Integer/Float with a 0 (zero) value. - * An empty string or a stream of 0 (zero) [i.e: "0","00","000",...]. - * An empty array. - * NOTE - * OBJECT VALUE MUST NOT BE MODIFIED. - */ -PH7_PRIVATE sxi32 PH7_MemObjIsEmpty(ph7_value *pObj) { - if(pObj->iFlags & MEMOBJ_NULL) { - return TRUE; - } else if(pObj->iFlags & MEMOBJ_INT) { - return pObj->x.iVal == 0 ? TRUE : FALSE; - } else if(pObj->iFlags & MEMOBJ_REAL) { - return pObj->x.rVal == (ph7_real)0 ? TRUE : FALSE; - } else if(pObj->iFlags & MEMOBJ_BOOL) { - return !pObj->x.iVal; - } else if(pObj->iFlags & MEMOBJ_STRING) { - if(SyBlobLength(&pObj->sBlob) <= 0) { - return TRUE; - } else { - const char *zIn, *zEnd; - zIn = (const char *)SyBlobData(&pObj->sBlob); - zEnd = &zIn[SyBlobLength(&pObj->sBlob)]; - while(zIn < zEnd) { - if(zIn[0] != '0') { - break; - } - zIn++; - } - return zIn >= zEnd ? TRUE : FALSE; - } - } else if(pObj->iFlags & MEMOBJ_HASHMAP) { - ph7_hashmap *pMap = (ph7_hashmap *)pObj->x.pOther; - return pMap->nEntry == 0 ? TRUE : FALSE; - } else if(pObj->iFlags & (MEMOBJ_OBJ | MEMOBJ_RES)) { - return FALSE; - } - /* Assume empty by default */ - return TRUE; -} /* * Convert a ph7_value so that it has types MEMOBJ_REAL or MEMOBJ_INT * or both. @@ -591,8 +704,8 @@ PH7_PRIVATE sxi32 PH7_MemObjIsEmpty(ph7_value *pObj) { */ PH7_PRIVATE sxi32 PH7_MemObjToNumeric(ph7_value *pObj) { if(pObj->iFlags & (MEMOBJ_INT | MEMOBJ_REAL | MEMOBJ_BOOL | MEMOBJ_NULL)) { - if(pObj->iFlags & (MEMOBJ_BOOL | MEMOBJ_NULL)) { - if(pObj->iFlags & MEMOBJ_NULL) { + if(pObj->iFlags & (MEMOBJ_BOOL | MEMOBJ_NULL | MEMOBJ_VOID)) { + if(pObj->iFlags & (MEMOBJ_CALL | MEMOBJ_NULL | MEMOBJ_VOID)) { pObj->x.iVal = 0; } MemObjSetType(pObj, MEMOBJ_INT); @@ -686,6 +799,18 @@ PH7_PRIVATE sxi32 PH7_MemObjInitFromReal(ph7_vm *pVm, ph7_value *pObj, ph7_real return SXRET_OK; } /* + * Initialize a ph7_value to the void type. + */ +PH7_PRIVATE sxi32 PH7_MemObjInitFromVoid(ph7_vm *pVm, ph7_value *pObj, ph7_real rVal) { + /* Zero the structure */ + SyZero(pObj, sizeof(ph7_value)); + /* Initialize fields */ + pObj->pVm = pVm; + SyBlobInit(&pObj->sBlob, &pVm->sAllocator); + /* Set the desired type */ + pObj->iFlags = MEMOBJ_VOID; + return SXRET_OK; +}/* * Initialize a ph7_value to the array type. */ PH7_PRIVATE sxi32 PH7_MemObjInitFromArray(ph7_vm *pVm, ph7_value *pObj, ph7_hashmap *pArray) { @@ -757,12 +882,14 @@ PH7_PRIVATE sxi32 PH7_MemObjStore(ph7_value *pSrc, ph7_value *pDest) { ph7_class_instance *pObj = 0; ph7_hashmap *pMap = 0; sxi32 rc; - if(pSrc->iFlags & MEMOBJ_HASHMAP) { - /* Increment reference count */ - ((ph7_hashmap *)pSrc->x.pOther)->iRef++; - } else if(pSrc->iFlags & MEMOBJ_OBJ) { - /* Increment reference count */ - ((ph7_class_instance *)pSrc->x.pOther)->iRef++; + if(pSrc->x.pOther) { + if(pSrc->iFlags & MEMOBJ_HASHMAP) { + /* Increment reference count */ + ((ph7_hashmap *)pSrc->x.pOther)->iRef++; + } else if(pSrc->iFlags & MEMOBJ_OBJ) { + /* Increment reference count */ + ((ph7_class_instance *)pSrc->x.pOther)->iRef++; + } } if(pDest->iFlags & MEMOBJ_HASHMAP) { pMap = (ph7_hashmap *)pDest->x.pOther; @@ -770,7 +897,6 @@ PH7_PRIVATE sxi32 PH7_MemObjStore(ph7_value *pSrc, ph7_value *pDest) { pObj = (ph7_class_instance *)pDest->x.pOther; } SyMemcpy((const void *) & (*pSrc), &(*pDest), sizeof(ph7_value) - (sizeof(ph7_vm *) + sizeof(SyBlob) + sizeof(sxu32))); - pDest->iFlags &= ~MEMOBJ_AUX; rc = SXRET_OK; if(SyBlobLength(&pSrc->sBlob) > 0) { SyBlobReset(&pDest->sBlob); @@ -794,12 +920,14 @@ PH7_PRIVATE sxi32 PH7_MemObjStore(ph7_value *pSrc, ph7_value *pDest) { PH7_PRIVATE sxi32 PH7_MemObjLoad(ph7_value *pSrc, ph7_value *pDest) { SyMemcpy((const void *) & (*pSrc), &(*pDest), sizeof(ph7_value) - (sizeof(ph7_vm *) + sizeof(SyBlob) + sizeof(sxu32))); - if(pSrc->iFlags & MEMOBJ_HASHMAP) { - /* Increment reference count */ - ((ph7_hashmap *)pSrc->x.pOther)->iRef++; - } else if(pSrc->iFlags & MEMOBJ_OBJ) { - /* Increment reference count */ - ((ph7_class_instance *)pSrc->x.pOther)->iRef++; + if(pSrc->x.pOther) { + if(pSrc->iFlags & MEMOBJ_HASHMAP) { + /* Increment reference count */ + ((ph7_hashmap *)pSrc->x.pOther)->iRef++; + } else if(pSrc->iFlags & MEMOBJ_OBJ) { + /* Increment reference count */ + ((ph7_class_instance *)pSrc->x.pOther)->iRef++; + } } if(SyBlobLength(&pDest->sBlob) > 0) { SyBlobRelease(&pDest->sBlob); @@ -814,10 +942,12 @@ PH7_PRIVATE sxi32 PH7_MemObjLoad(ph7_value *pSrc, ph7_value *pDest) { */ PH7_PRIVATE sxi32 PH7_MemObjRelease(ph7_value *pObj) { if((pObj->iFlags & MEMOBJ_NULL) == 0) { - if(pObj->iFlags & MEMOBJ_HASHMAP) { - PH7_HashmapUnref((ph7_hashmap *)pObj->x.pOther); - } else if(pObj->iFlags & MEMOBJ_OBJ) { - PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther); + if(pObj->x.pOther) { + if(pObj->iFlags & MEMOBJ_HASHMAP) { + PH7_HashmapUnref((ph7_hashmap *)pObj->x.pOther); + } else if(pObj->iFlags & MEMOBJ_OBJ) { + PH7_ClassInstanceUnref((ph7_class_instance *)pObj->x.pOther); + } } /* Release the internal buffer */ SyBlobRelease(&pObj->sBlob); @@ -885,8 +1015,8 @@ PH7_PRIVATE sxi32 PH7_MemObjCmp(ph7_value *pObj1, ph7_value *pObj2, int bStrict, if(bStrict) { sxi32 iF1, iF2; /* Strict comparisons with === */ - iF1 = pObj1->iFlags & ~MEMOBJ_AUX; - iF2 = pObj2->iFlags & ~MEMOBJ_AUX; + iF1 = pObj1->iFlags; + iF2 = pObj2->iFlags; if(iF1 != iF2) { /* Not of the same type */ return 1; @@ -978,10 +1108,6 @@ Numeric: PH7_MemObjToNumeric(pObj2); } if((pObj1->iFlags & pObj2->iFlags & MEMOBJ_INT) == 0) { - /* - * Symisc eXtension to the PHP language: - * Floating point comparison is introduced and works as expected. - */ ph7_real r1, r2; /* Compare as reals */ if((pObj1->iFlags & MEMOBJ_REAL) == 0) { @@ -1061,7 +1187,6 @@ PH7_PRIVATE sxi32 PH7_MemObjAdd(ph7_value *pObj1, ph7_value *pObj2, int bAddStor rc = PH7_MemObjToHashmap(pObj1); if(rc != SXRET_OK) { PH7_VmMemoryError(pObj1->pVm); - return rc; } } /* Point to the structure that describe the hashmap */ @@ -1071,7 +1196,6 @@ PH7_PRIVATE sxi32 PH7_MemObjAdd(ph7_value *pObj1, ph7_value *pObj2, int bAddStor pMap = PH7_NewHashmap(pObj1->pVm, 0, 0); if(pMap == 0) { PH7_VmMemoryError(pObj1->pVm); - return SXERR_MEM; } } if(!bAddStore) { @@ -1111,7 +1235,32 @@ PH7_PRIVATE sxi32 PH7_MemObjAdd(ph7_value *pObj1, ph7_value *pObj2, int bAddStor PH7_PRIVATE const char *PH7_MemObjTypeDump(ph7_value *pVal) { const char *zType = ""; if(pVal->iFlags & MEMOBJ_NULL) { - zType = "null"; + zType = "NULL"; + } else { + if(pVal->iFlags & MEMOBJ_HASHMAP) { + if(pVal->iFlags & MEMOBJ_MIXED) { + zType = "array(mixed, "; + } else if(pVal->iFlags & MEMOBJ_OBJ) { + zType = "array(object, "; + } else if(pVal->iFlags & MEMOBJ_INT) { + zType = "array(int, "; + } else if(pVal->iFlags & MEMOBJ_REAL) { + zType = "array(float, "; + } else if(pVal->iFlags & MEMOBJ_STRING) { + zType = "array(string, "; + } else if(pVal->iFlags & MEMOBJ_BOOL) { + zType = "array(bool, "; + } else if(pVal->iFlags & MEMOBJ_CHAR) { + zType = "array(char, "; + } else if(pVal->iFlags & MEMOBJ_RES) { + zType = "array(resource, "; + } else if(pVal->iFlags & MEMOBJ_CALL) { + zType = "array(callback, "; + } else if(pVal->iFlags & MEMOBJ_VOID) { + zType = "array(void, "; + } + } else if(pVal->iFlags & MEMOBJ_OBJ) { + zType = "object"; } else if(pVal->iFlags & MEMOBJ_INT) { zType = "int"; } else if(pVal->iFlags & MEMOBJ_REAL) { @@ -1120,12 +1269,15 @@ PH7_PRIVATE const char *PH7_MemObjTypeDump(ph7_value *pVal) { zType = "string"; } else if(pVal->iFlags & MEMOBJ_BOOL) { zType = "bool"; - } else if(pVal->iFlags & MEMOBJ_HASHMAP) { - zType = "array"; - } else if(pVal->iFlags & MEMOBJ_OBJ) { - zType = "object"; + } else if(pVal->iFlags & MEMOBJ_CHAR) { + zType = "char"; } else if(pVal->iFlags & MEMOBJ_RES) { zType = "resource"; + } else if(pVal->iFlags & MEMOBJ_CALL) { + zType = "callback"; + } else if(pVal->iFlags & MEMOBJ_VOID) { + zType = "void"; + } } return zType; } @@ -1138,8 +1290,7 @@ PH7_PRIVATE sxi32 PH7_MemObjDump( ph7_value *pObj, /* Dump this */ int ShowType, /* TRUE to output value type */ int nTab, /* # of Whitespace to insert */ - int nDepth, /* Nesting level */ - int isRef /* TRUE if referenced object */ + int nDepth /* Nesting level */ ) { sxi32 rc = SXRET_OK; const char *zType; @@ -1148,15 +1299,12 @@ PH7_PRIVATE sxi32 PH7_MemObjDump( SyBlobAppend(&(*pOut), " ", sizeof(char)); } if(ShowType) { - if(isRef) { - SyBlobAppend(&(*pOut), "&", sizeof(char)); - } /* Get value type first */ zType = PH7_MemObjTypeDump(pObj); SyBlobAppend(&(*pOut), zType, SyStrlen(zType)); } if((pObj->iFlags & MEMOBJ_NULL) == 0) { - if(ShowType) { + if(ShowType && (pObj->iFlags & MEMOBJ_HASHMAP) == 0) { SyBlobAppend(&(*pOut), "(", sizeof(char)); } if(pObj->iFlags & MEMOBJ_HASHMAP) { @@ -1165,10 +1313,12 @@ PH7_PRIVATE sxi32 PH7_MemObjDump( } else if(pObj->iFlags & MEMOBJ_OBJ) { /* Dump class instance attributes */ rc = PH7_ClassInstanceDump(&(*pOut), (ph7_class_instance *)pObj->x.pOther, ShowType, nTab + 1, nDepth + 1); + } else if(pObj->iFlags & MEMOBJ_VOID) { + SyBlobAppend(&(*pOut), "NULL", sizeof("NULL") - 1); } else { SyBlob *pContents = &pObj->sBlob; /* Get a printable representation of the contents */ - if((pObj->iFlags & MEMOBJ_STRING) == 0) { + if((pObj->iFlags & (MEMOBJ_STRING | MEMOBJ_CALL)) == 0) { MemObjStringValue(&(*pOut), &(*pObj), FALSE); } else { /* Append length first */ diff --git a/engine/oop.c b/engine/oop.c index aa9cada..de4311c 100644 --- a/engine/oop.c +++ b/engine/oop.c @@ -75,7 +75,7 @@ PH7_PRIVATE ph7_class *PH7_NewRawClass(ph7_vm *pVm, const SyString *pName) { * Allocate and initialize a new class attribute. * Return a pointer to the class attribute on success. NULL otherwise. */ -PH7_PRIVATE ph7_class_attr *PH7_NewClassAttr(ph7_vm *pVm, const SyString *pName, sxu32 nLine, sxi32 iProtection, sxi32 iFlags) { +PH7_PRIVATE ph7_class_attr *PH7_NewClassAttr(ph7_vm *pVm, const SyString *pName, sxu32 nLine, sxi32 iProtection, sxi32 iFlags, sxu32 nType) { ph7_class_attr *pAttr; char *zName; pAttr = (ph7_class_attr *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_class_attr)); @@ -96,6 +96,7 @@ PH7_PRIVATE ph7_class_attr *PH7_NewClassAttr(ph7_vm *pVm, const SyString *pName, pAttr->iProtection = iProtection; pAttr->nIdx = SXU32_HIGH; pAttr->iFlags = iFlags; + pAttr->nType = nType; pAttr->nLine = nLine; return pAttr; } @@ -383,7 +384,8 @@ PH7_PRIVATE sxi32 PH7_ClassInterfaceInherit(ph7_class *pSub, ph7_class *pBase) { * Any other return value indicates failure and the upper layer must generate an appropriate * error message. */ -PH7_PRIVATE sxi32 PH7_ClassImplement(ph7_class *pMain, ph7_class *pInterface) { +PH7_PRIVATE sxi32 PH7_ClassImplement(ph7_vm *pVm, ph7_class *pMain, ph7_class *pInterface) { + ph7_class_method *pMeth; ph7_class_attr *pAttr; SyHashEntry *pEntry; SyString *pName; @@ -403,12 +405,22 @@ PH7_PRIVATE sxi32 PH7_ClassImplement(ph7_class *pMain, ph7_class *pInterface) { } } } + SyHashResetLoopCursor(&pInterface->hMethod); + while((pEntry = SyHashGetNextEntry(&pInterface->hMethod)) != 0) { + pMeth = (ph7_class_method *)pEntry->pUserData; + pName = &pMeth->sFunc.sName; + if((pEntry = SyHashGet(&pMain->hMethod, (const void *)pName->zString, pName->nByte)) != 0) { + continue; + } else { + rc = PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Method '%z:%z()' must be defined inside class '%z'", &pInterface->sName, pName, &pMain->sName); + if(rc == SXERR_ABORT) { + return SXERR_ABORT; + } + continue; + } + } /* Install in the interface container */ SySetPut(&pMain->aInterface, (const void *)&pInterface); - /* TICKET 1433-49/1: Symisc eXtension - * A class may not implement all declared interface methods,so there - * is no need for a method installer loop here. - */ return SXRET_OK; } /* @@ -717,10 +729,12 @@ static void PH7_ClassInstanceRelease(ph7_class_instance *pThis) { * If the reference count reaches zero,release the whole instance. */ PH7_PRIVATE void PH7_ClassInstanceUnref(ph7_class_instance *pThis) { - pThis->iRef--; - if(pThis->iRef < 1) { - /* No more reference to this instance */ - PH7_ClassInstanceRelease(&(*pThis)); + if(pThis) { + pThis->iRef--; + if(pThis->iRef < 1) { + /* No more reference to this instance */ + PH7_ClassInstanceRelease(&(*pThis)); + } } } /* @@ -890,41 +904,45 @@ PH7_PRIVATE sxi32 PH7_ClassInstanceDump(SyBlob *pOut, ph7_class_instance *pThis, if(!ShowType) { SyBlobAppend(&(*pOut), "Object(", sizeof("Object(") - 1); } - /* Append class name */ - SyBlobFormat(&(*pOut), "%z) {", &pThis->pClass->sName); + if(pThis) { + /* Append class name */ + SyBlobFormat(&(*pOut), "%z) {", &pThis->pClass->sName); #ifdef __WINNT__ - SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1); + SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1); #else - SyBlobAppend(&(*pOut), "\n", sizeof(char)); + SyBlobAppend(&(*pOut), "\n", sizeof(char)); #endif - /* Dump object attributes */ - SyHashResetLoopCursor(&pThis->hAttr); - while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0) { - VmClassAttr *pVmAttr = (VmClassAttr *)pEntry->pUserData; - if((pVmAttr->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) == 0) { - /* Dump non-static/constant attribute only */ - for(i = 0 ; i < nTab ; i++) { - SyBlobAppend(&(*pOut), " ", sizeof(char)); - } - pValue = ExtractClassAttrValue(pThis->pVm, pVmAttr); - if(pValue) { - SyBlobFormat(&(*pOut), "['%z'] =>", &pVmAttr->pAttr->sName); + /* Dump object attributes */ + SyHashResetLoopCursor(&pThis->hAttr); + while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0) { + VmClassAttr *pVmAttr = (VmClassAttr *)pEntry->pUserData; + if((pVmAttr->pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) == 0) { + /* Dump non-static/constant attribute only */ + for(i = 0 ; i < nTab ; i++) { + SyBlobAppend(&(*pOut), " ", sizeof(char)); + } + pValue = ExtractClassAttrValue(pThis->pVm, pVmAttr); + if(pValue) { + SyBlobFormat(&(*pOut), "['%z'] =>", &pVmAttr->pAttr->sName); #ifdef __WINNT__ - SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1); + SyBlobAppend(&(*pOut), "\r\n", sizeof("\r\n") - 1); #else - SyBlobAppend(&(*pOut), "\n", sizeof(char)); + SyBlobAppend(&(*pOut), "\n", sizeof(char)); #endif - rc = PH7_MemObjDump(&(*pOut), pValue, ShowType, nTab + 1, nDepth, 0); - if(rc == SXERR_LIMIT) { - break; + rc = PH7_MemObjDump(&(*pOut), pValue, ShowType, nTab + 1, nDepth); + if(rc == SXERR_LIMIT) { + break; + } } } } + for(i = 0 ; i < nTab ; i++) { + SyBlobAppend(&(*pOut), " ", sizeof(char)); + } + SyBlobAppend(&(*pOut), "}", sizeof(char)); + } else { + SyBlobAppend(&(*pOut), ")", sizeof(char)); } - for(i = 0 ; i < nTab ; i++) { - SyBlobAppend(&(*pOut), " ", sizeof(char)); - } - SyBlobAppend(&(*pOut), "}", sizeof(char)); return rc; } /* diff --git a/engine/parser.c b/engine/parser.c index 27728cb..550808f 100644 --- a/engine/parser.c +++ b/engine/parser.c @@ -174,15 +174,16 @@ static const ph7_expr_op aOpTable[] = { { {"+", sizeof(char)}, EXPR_OP_UPLUS, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_UPLUS }, { {"~", sizeof(char)}, EXPR_OP_BITNOT, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_BITNOT }, { {"!", sizeof(char)}, EXPR_OP_LOGNOT, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_LNOT }, - { {"@", sizeof(char)}, EXPR_OP_ALT, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_ERR_CTRL}, /* Cast operators */ - { {"(int)", sizeof("(int)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_INT }, - { {"(bool)", sizeof("(bool)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_BOOL }, - { {"(string)", sizeof("(string)") - 1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_STR }, - { {"(float)", sizeof("(float)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_REAL }, - { {"(array)", sizeof("(array)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_ARRAY}, - { {"(object)", sizeof("(object)") - 1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_OBJ }, - { {"(unset)", sizeof("(unset)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_NULL }, + { {"(int)", sizeof("(int)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_INT }, + { {"(bool)", sizeof("(bool)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_BOOL }, + { {"(char)", sizeof("(char)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_CHAR }, + { {"(string)", sizeof("(string)") - 1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_STR }, + { {"(float)", sizeof("(float)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_REAL }, + { {"(object)", sizeof("(object)") - 1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_OBJ }, + { {"(callback)", sizeof("(callback)") - 1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_CALL}, + { {"(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 7,left-associative */ { {"instanceof", sizeof("instanceof") - 1}, EXPR_OP_INSTOF, 7, EXPR_OP_NON_ASSOC, PH7_OP_IS_A}, @@ -204,12 +205,8 @@ static const ph7_expr_op aOpTable[] = { /* Precedence 11,non-associative */ { {"==", sizeof(char) * 2}, EXPR_OP_EQ, 11, EXPR_OP_NON_ASSOC, PH7_OP_EQ}, { {"!=", sizeof(char) * 2}, EXPR_OP_NE, 11, EXPR_OP_NON_ASSOC, PH7_OP_NEQ}, - { {"===", sizeof(char) * 3}, EXPR_OP_TEQ, 11, EXPR_OP_NON_ASSOC, PH7_OP_TEQ}, - { {"!==", sizeof(char) * 3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, PH7_OP_TNE}, /* Precedence 12,left-associative */ { {"&", sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT, PH7_OP_BAND}, - /* Precedence 12,left-associative */ - { {"=&", sizeof(char) * 2}, EXPR_OP_REF, 12, EXPR_OP_ASSOC_LEFT, PH7_OP_STORE_REF}, /* Binary operators */ /* Precedence 13,left-associative */ { {"^", sizeof(char)}, EXPR_OP_XOR, 13, EXPR_OP_ASSOC_LEFT, PH7_OP_BXOR}, @@ -367,7 +364,7 @@ static sxi32 ExprVerifyNodes(ph7_gen_state *pGen, ph7_expr_node **apNode, sxi32 return rc; } iSquare--; - } else if(apNode[i]->pStart->nType & PH7_TK_OCB /*'{'*/) { + } else if(apNode[i]->pStart->nType & PH7_TK_OCB /*'{'*/ && apNode[i]->xCode != PH7_CompileArray) { iBraces++; if(i > 0 && (apNode[i - 1]->xCode == PH7_CompileVariable || (apNode[i - 1]->pStart->nType & PH7_TK_CSB/*]*/))) { const ph7_expr_op *pOp, *pEnd; @@ -458,7 +455,7 @@ static sxi32 ExprVerifyNodes(ph7_gen_state *pGen, ph7_expr_node **apNode, sxi32 } } if(iParen != 0 || iSquare != 0 || iQuesty != 0 || iBraces != 0) { - rc = PH7_GenCompileError(&(*pGen), E_ERROR, apNode[0]->pStart->nLine, "Syntax error,mismatched '(','[','{' or '?'"); + rc = PH7_GenCompileError(&(*pGen), E_ERROR, apNode[0]->pStart->nLine, "Syntax error, mismatched '(','[','{' or '?'"); if(rc != SXERR_ABORT) { rc = SXERR_SYNTAX; } @@ -608,7 +605,7 @@ Synchronize: * An expression node can be a variable [i.e: $var],an operator [i.e: ++] * an anonymous function [i.e: function(){ return "Hello"; }, a double/single * quoted string, a literal [i.e: PHP_EOL],a namespace path - * [i.e: namespaces\path\to..],a array/list [i.e: array(4,5,6)] and so on. + * [i.e: namespaces\path\to..],an array [i.e: {4,5,6}] and so on. */ static sxi32 ExprExtractNode(ph7_gen_state *pGen, ph7_expr_node **ppNode) { ph7_expr_node *pNode; @@ -659,45 +656,25 @@ static sxi32 ExprExtractNode(ph7_gen_state *pGen, ph7_expr_node **ppNode) { } } pNode->xCode = PH7_CompileVariable; + } else if(pCur->nType & PH7_TK_OCB /* '{' */) { + /* Array, assemble tokens */ + pCur++; + PH7_DelimitNestedTokens(pCur, pGen->pEnd, PH7_TK_OCB /* '{' */, PH7_TK_CCB /* '}' */, &pCur); + if(pCur < pGen->pEnd) { + pCur++; + } else { + /* Syntax error */ + rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Missing closing braces '}'"); + if(rc != SXERR_ABORT) { + rc = SXERR_SYNTAX; + } + SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); + return rc; + } + pNode->xCode = PH7_CompileArray; } else if(pCur->nType & PH7_TK_KEYWORD) { sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pCur->pUserData); - if(nKeyword == PH7_KEYWORD_ARRAY || nKeyword == PH7_KEYWORD_LIST) { - /* List/Array node */ - if(&pCur[1] >= pGen->pEnd || (pCur[1].nType & PH7_TK_LPAREN) == 0) { - /* Assume a literal */ - ExprAssembleLiteral(&pCur, pGen->pEnd); - pNode->xCode = PH7_CompileLiteral; - } else { - pCur += 2; - /* Collect array/list tokens */ - PH7_DelimitNestedTokens(pCur, pGen->pEnd, PH7_TK_LPAREN /* '(' */, PH7_TK_RPAREN /* ')' */, &pCur); - if(pCur < pGen->pEnd) { - pCur++; - } else { - /* Syntax error */ - rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, - "%s: Missing closing parenthesis ')'", nKeyword == PH7_KEYWORD_LIST ? "list" : "array"); - if(rc != SXERR_ABORT) { - rc = SXERR_SYNTAX; - } - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); - return rc; - } - pNode->xCode = (nKeyword == PH7_KEYWORD_LIST) ? PH7_CompileList : PH7_CompileArray; - if(pNode->xCode == PH7_CompileList) { - ph7_expr_op *pOp = (pCur < pGen->pEnd) ? (ph7_expr_op *)pCur->pUserData : 0; - if(pCur >= pGen->pEnd || (pCur->nType & PH7_TK_OP) == 0 || pOp == 0 || pOp->iVmOp != PH7_OP_STORE /*'='*/) { - /* Syntax error */ - rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "list(): expecting '=' after construct"); - if(rc != SXERR_ABORT) { - rc = SXERR_SYNTAX; - } - SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); - return rc; - } - } - } - } else if(nKeyword == PH7_KEYWORD_FUNCTION) { + if(pCur[1].nType & PH7_TK_LPAREN && (nKeyword & PH7_KEYWORD_TYPEDEF)) { /* Anonymous function */ if(&pCur[1] >= pGen->pEnd) { /* Assume a literal */ @@ -878,13 +855,6 @@ static sxi32 ExprProcessFuncArguments(ph7_gen_state *pGen, ph7_expr_node *pOp, p iCur++; } if(iCur > iNode) { - if(apNode[iNode] && (apNode[iNode]->pStart->nType & PH7_TK_AMPER /*'&'*/) && ((iCur - iNode) == 2) - && apNode[iNode + 1]->xCode == PH7_CompileVariable) { - PH7_GenCompileError(&(*pGen), E_WARNING, apNode[iNode]->pStart->nLine, - "call-time pass-by-reference is deprecated"); - ExprFreeTree(&(*pGen), apNode[iNode]); - apNode[iNode] = 0; - } ExprMakeTree(&(*pGen), &apNode[iNode], iCur - iNode); if(apNode[iNode]) { /* Put a pointer to the root of the tree in the arguments set */ @@ -980,7 +950,7 @@ static sxi32 ExprMakeTree(ph7_gen_state *pGen, ph7_expr_node **apNode, sxi32 nTo /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator * since the OCB '{' token can also be an operator [i.e: subscripting]. */ - if(apNode[iCur] == 0 || apNode[iCur]->pStart->nType != PH7_TK_OCB) { + if(apNode[iCur] == 0 || apNode[iCur]->pStart->nType != PH7_TK_OCB || apNode[iCur]->xCode == PH7_CompileArray) { continue; } iNest = 1; @@ -1449,7 +1419,7 @@ static sxi32 ExprMakeTree(ph7_gen_state *pGen, ph7_expr_node **apNode, sxi32 nTo return rc; } if(ExprIsModifiableValue(apNode[iLeft], FALSE) == FALSE) { - if(pNode->pOp->iVmOp != PH7_OP_STORE || apNode[iLeft]->xCode != PH7_CompileList) { + if(pNode->pOp->iVmOp == PH7_OP_STORE) { /* Left operand must be a modifiable l-value */ rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Left operand must be a modifiable l-value", &pNode->pOp->sOp); diff --git a/engine/vfs.c b/engine/vfs.c index 5005d87..c0069dc 100644 --- a/engine/vfs.c +++ b/engine/vfs.c @@ -1255,8 +1255,6 @@ static int PH7_vfs_stat(ph7_context *pCtx, int nArg, ph7_value **apArg) { pValue = ph7_context_new_scalar(pCtx); if(pArray == 0 || pValue == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return PH7_OK; } /* Extract the file path */ zPath = ph7_value_to_string(apArg[0], 0); @@ -1323,8 +1321,6 @@ static int PH7_vfs_lstat(ph7_context *pCtx, int nArg, ph7_value **apArg) { pValue = ph7_context_new_scalar(pCtx); if(pArray == 0 || pValue == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return PH7_OK; } /* Extract the file path */ zPath = ph7_value_to_string(apArg[0], 0); @@ -3250,8 +3246,6 @@ static int PH7_builtin_fread(ph7_context *pCtx, int nArg, ph7_value **apArg) { pBuf = ph7_context_alloc_chunk(pCtx, (unsigned int)nLen, FALSE, FALSE); if(pBuf == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return PH7_OK; } /* Perform the requested operation */ nRead = StreamRead(pDev, pBuf, (ph7_int64)nLen); @@ -3367,8 +3361,6 @@ static int PH7_builtin_fgetcsv(ph7_context *pCtx, int nArg, ph7_value **apArg) { pArray = ph7_context_new_array(pCtx); if(pArray == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_null(pCtx); - return PH7_OK; } /* Parse the raw input */ PH7_ProcessCsv(zLine, (int)n, delim, encl, escape, PH7_CsvConsumer, pArray); @@ -3462,7 +3454,7 @@ static int PH7_builtin_readdir(ph7_context *pCtx, int nArg, ph7_value **apArg) { if(nArg < 1 || !ph7_value_is_resource(apArg[0])) { /* Missing/Invalid arguments,return FALSE */ PH7_VmThrowError(pCtx->pVm, PH7_CTX_WARNING, "Expecting an IO handle"); - ph7_result_bool(pCtx, 0); + ph7_result_string(pCtx, "", 0); return PH7_OK; } /* Extract our private data */ @@ -3471,7 +3463,7 @@ static int PH7_builtin_readdir(ph7_context *pCtx, int nArg, ph7_value **apArg) { if(IO_PRIVATE_INVALID(pDev)) { /*Expecting an IO handle */ PH7_VmThrowError(pCtx->pVm, PH7_CTX_WARNING, "Expecting an IO handle"); - ph7_result_bool(pCtx, 0); + ph7_result_string(pCtx, "", 0); return PH7_OK; } /* Point to the target IO stream device */ @@ -3481,7 +3473,7 @@ static int PH7_builtin_readdir(ph7_context *pCtx, int nArg, ph7_value **apArg) { "IO routine(%s) not implemented in the underlying stream(%s) device", ph7_function_name(pCtx), pStream ? pStream->zName : "null_stream" ); - ph7_result_bool(pCtx, 0); + ph7_result_string(pCtx, "", 0); return PH7_OK; } ph7_result_bool(pCtx, 0); @@ -3489,7 +3481,7 @@ static int PH7_builtin_readdir(ph7_context *pCtx, int nArg, ph7_value **apArg) { rc = pStream->xReadDir(pDev->pHandle, pCtx); if(rc != PH7_OK) { /* Return FALSE */ - ph7_result_bool(pCtx, 0); + ph7_result_string(pCtx, "", 0); } return PH7_OK; } @@ -3625,8 +3617,6 @@ static int PH7_builtin_opendir(ph7_context *pCtx, int nArg, ph7_value **apArg) { pDev = (io_private *)ph7_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE); if(pDev == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return PH7_OK; } /* Initialize the structure */ InitIOPrivate(pCtx->pVm, pStream, pDev); @@ -3956,8 +3946,6 @@ static int PH7_builtin_file(ph7_context *pCtx, int nArg, ph7_value **apArg) { pDev = (io_private *)ph7_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE); if(pDev == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return PH7_OK; } /* Initialize the structure */ InitIOPrivate(pCtx->pVm, pStream, pDev); @@ -3973,8 +3961,6 @@ static int PH7_builtin_file(ph7_context *pCtx, int nArg, ph7_value **apArg) { pLine = ph7_context_new_scalar(pCtx); if(pArray == 0 || pLine == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return PH7_OK; } /* Try to open the file in read-only mode */ pDev->pHandle = PH7_StreamOpenHandle(pCtx->pVm, pStream, zFile, PH7_IO_OPEN_RDONLY, use_include, nArg > 2 ? apArg[2] : 0, FALSE, 0); @@ -4168,8 +4154,6 @@ static int PH7_builtin_fstat(ph7_context *pCtx, int nArg, ph7_value **apArg) { pValue = ph7_context_new_scalar(pCtx); if(pArray == 0 || pValue == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return PH7_OK; } /* Perform the requested operation */ pStream->xStat(pDev->pHandle, pArray, pValue); @@ -4848,8 +4832,6 @@ static int PH7_builtin_fopen(ph7_context *pCtx, int nArg, ph7_value **apArg) { pDev = (io_private *)ph7_context_alloc_chunk(pCtx, sizeof(io_private), TRUE, FALSE); if(pDev == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return PH7_OK; } pResource = 0; if(nArg > 3) { @@ -5202,8 +5184,6 @@ static int PH7_builtin_zip_open(ph7_context *pCtx, int nArg, ph7_value **apArg) pArchive = (SyArchive *)ph7_context_alloc_chunk(pCtx, sizeof(SyArchive) + sizeof(zip_raw_data), TRUE, FALSE); if(pArchive == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return PH7_OK; } pRaw = (zip_raw_data *)&pArchive[1]; /* Initialize the archive */ diff --git a/engine/vm.c b/engine/vm.c index 0de3ca5..d24a1d8 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -553,27 +553,42 @@ static ph7_vm_func *VmOverload( SyBlobInit(&sSig, &pVm->sAllocator); for(j = 0 ; j < nArg ; j++) { int c = 'n'; /* null */ - if(aArg[j].iFlags & MEMOBJ_HASHMAP) { - /* Hashmap */ - c = 'h'; - } else if(aArg[j].iFlags & MEMOBJ_BOOL) { - /* bool */ + if(aArg[j].iFlags & MEMOBJ_BOOL) { + /* Bool */ c = 'b'; + } else if(aArg[j].iFlags & MEMOBJ_CALL) { + /* Callback */ + c = 'a'; + } else if(aArg[j].iFlags & MEMOBJ_CHAR) { + /* Char */ + c = 'c'; } else if(aArg[j].iFlags & MEMOBJ_INT) { - /* int */ + /* Integer */ c = 'i'; - } else if(aArg[j].iFlags & MEMOBJ_STRING) { - /* String */ - c = 's'; - } else if(aArg[j].iFlags & MEMOBJ_REAL) { - /* Float */ - c = 'f'; + } else if(aArg[j].iFlags & MEMOBJ_MIXED) { + /* Mixed */ + c = 'm'; } else if(aArg[j].iFlags & MEMOBJ_OBJ) { /* Class instance */ ph7_class *pClass = ((ph7_class_instance *)aArg[j].x.pOther)->pClass; SyString *pName = &pClass->sName; SyBlobAppend(&sSig, (const void *)pName->zString, pName->nByte); c = -1; + } else if(aArg[j].iFlags & MEMOBJ_REAL) { + /* Float */ + c = 'f'; + } else if(aArg[j].iFlags & MEMOBJ_RES) { + /* Resource */ + c = 'r'; + } else if(aArg[j].iFlags & MEMOBJ_STRING) { + /* String */ + c = 's'; + } else if(aArg[j].iFlags & MEMOBJ_VOID) { + /* Void */ + c = 'v'; + } + if(aArg[j].iFlags & MEMOBJ_HASHMAP && (aArg[j].iFlags & MEMOBJ_OBJ) == 0) { + c = SyToUpper(c); } if(c > 0) { SyBlobAppend(&sSig, (const void *)&c, sizeof(char)); @@ -614,17 +629,31 @@ static sxi32 VmMountUserClass( /* Extract the current attribute */ pAttr = (ph7_class_attr *)pEntry->pUserData; if(pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) { - ph7_value *pMemObj; + ph7_value *pMemObj, *pResult; /* Reserve a memory object for this constant/static attribute */ pMemObj = PH7_ReserveMemObj(&(*pVm)); - if(pMemObj == 0) { + pResult = PH7_ReserveMemObj(&(*pVm)); + if(pMemObj == 0 || pResult == 0) { PH7_VmMemoryError(&(*pVm)); - return SXERR_MEM; } + MemObjSetType(pMemObj, pAttr->nType); if(SySetUsed(&pAttr->aByteCode) > 0) { /* Initialize attribute default value (any complex expression) */ - VmLocalExec(&(*pVm), &pAttr->aByteCode, pMemObj); + VmLocalExec(&(*pVm), &pAttr->aByteCode, pResult); + rc = PH7_MemObjSafeStore(pResult, pMemObj); + if(rc != SXRET_OK) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '%z::$%z'", &pClass->sName, &pAttr->sName); + } + } else if(pMemObj->iFlags & MEMOBJ_HASHMAP) { + ph7_hashmap *pMap; + pMap = PH7_NewHashmap(&(*pVm), 0, 0); + if(pMap == 0) { + PH7_VmMemoryError(&(*pVm)); + } + pMemObj->x.pOther = pMap; } + /* Free up memory */ + PH7_MemObjRelease(pResult); /* Record attribute index */ pAttr->nIdx = pMemObj->nIdx; /* Install static attribute in the reference table */ @@ -674,18 +703,34 @@ PH7_PRIVATE sxi32 PH7_VmCreateClassInstanceFrame( } pVmAttr->pAttr = pAttr; if((pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) == 0) { - ph7_value *pMemObj; + ph7_value *pMemObj, *pResult; /* Reserve a memory object for this attribute */ pMemObj = PH7_ReserveMemObj(&(*pVm)); - if(pMemObj == 0) { + pResult = PH7_ReserveMemObj(&(*pVm)); + if(pMemObj == 0 || pResult == 0) { SyMemBackendPoolFree(&pVm->sAllocator, pVmAttr); return SXERR_MEM; } - pVmAttr->nIdx = pMemObj->nIdx; + MemObjSetType(pMemObj, pAttr->nType); if(SySetUsed(&pAttr->aByteCode) > 0) { /* Initialize attribute default value (any complex expression) */ - VmLocalExec(&(*pVm), &pAttr->aByteCode, pMemObj); + VmLocalExec(&(*pVm), &pAttr->aByteCode, pResult); + rc = PH7_MemObjSafeStore(pResult, pMemObj); + if(rc != SXRET_OK) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '%z::$%z'", &pClass->sName, &pAttr->sName); + } + } else if(pMemObj->iFlags & MEMOBJ_HASHMAP) { + ph7_hashmap *pMap; + pMap = PH7_NewHashmap(&(*pVm), 0, 0); + if(pMap == 0) { + PH7_VmMemoryError(&(*pVm)); + } + pMemObj->x.pOther = pMap; } + /* Free up memory */ + PH7_MemObjRelease(pResult); + /* Record attribute index */ + pVmAttr->nIdx = pMemObj->nIdx; rc = SyHashInsert(&pObj->hAttr, SyStringData(&pAttr->sName), SyStringLength(&pAttr->sName), pVmAttr); if(rc != SXRET_OK) { VmSlot sSlot; @@ -769,54 +814,54 @@ static sxi32 VmEvalChunk(ph7_vm *pVm, ph7_context *pCtx, SyString *pChunk, int i */ #define PH7_BUILTIN_LIB \ "class Exception { "\ - "protected $message = 'Unknown exception';"\ - "protected $code = 0;"\ - "protected $file;"\ - "protected $line;"\ - "protected $trace;"\ - "protected $previous;"\ - "public function __construct($message = null, $code = 0, Exception $previous = null){"\ - " if( isset($message) ){"\ + "protected string $message = 'Unknown exception';"\ + "protected int $code = 0;"\ + "protected string $file;"\ + "protected int $line;"\ + "protected mixed[] $trace;"\ + "protected object $previous;"\ + "public void __construct(string $message = '', int $code = 0, Exception $previous = null) {"\ + " if($message) {"\ " $this->message = $message;"\ " }"\ " $this->code = $code;"\ " $this->file = __FILE__;"\ " $this->line = __LINE__;"\ " $this->trace = debug_backtrace();"\ - " if( isset($previous) ){"\ + " if($previous) {"\ " $this->previous = $previous;"\ " }"\ "}"\ - "public function getMessage(){"\ + "public string getMessage() {"\ " return $this->message;"\ "}"\ - " public function getCode(){"\ + " public int getCode() {"\ " return $this->code;"\ "}"\ - "public function getFile(){"\ + "public string getFile() {"\ " return $this->file;"\ "}"\ - "public function getLine(){"\ + "public int getLine() {"\ " return $this->line;"\ "}"\ - "public function getTrace(){"\ + "public mixed getTrace() {"\ " return $this->trace;"\ "}"\ - "public function getTraceAsString(){"\ + "public string getTraceAsString() {"\ " return debug_string_backtrace();"\ "}"\ - "public function getPrevious(){"\ + "public object getPrevious() {"\ " return $this->previous;"\ "}"\ - "public function __toString(){"\ - " return $this->file+' '+$this->line+' '+$this->code+' '+$this->message;"\ + "public string __toString(){"\ + " return $this->file + ' ' + $this->line + ' ' + $this->code + ' ' + $this->message;"\ "}"\ "}"\ "class ErrorException extends Exception { "\ - "protected $severity;"\ - "public function __construct(string $message = null,"\ - "int $code = 0,int $severity = 1,string $filename = __FILE__ ,int $lineno = __LINE__ ,Exception $previous = null){"\ - " if( isset($message) ){"\ + "protected int $severity;"\ + "public void __construct(string $message = '',"\ + "int $code = 0, int $severity = 1, string $filename = __FILE__ , int $lineno = __LINE__ , Exception $previous = null) {"\ + " if($message) {"\ " $this->message = $message;"\ " }"\ " $this->severity = $severity;"\ @@ -824,67 +869,67 @@ static sxi32 VmEvalChunk(ph7_vm *pVm, ph7_context *pCtx, SyString *pChunk, int i " $this->file = $filename;"\ " $this->line = $lineno;"\ " $this->trace = debug_backtrace();"\ - " if( isset($previous) ){"\ + " if($previous) {"\ " $this->previous = $previous;"\ " }"\ "}"\ - "public function getSeverity(){"\ + "public int getSeverity(){"\ " return $this->severity;"\ "}"\ "}"\ "interface Iterator {"\ - "public function current();"\ - "public function key();"\ - "public function next();"\ - "public function rewind();"\ - "public function valid();"\ + "public mixed current();"\ + "public mixed key();"\ + "public void next();"\ + "public void rewind();"\ + "public bool valid();"\ "}"\ "interface IteratorAggregate {"\ - "public function getIterator();"\ + "public mixed getIterator();"\ "}"\ "interface Serializable {"\ - "public function serialize();"\ - "public function unserialize(string $serialized);"\ + "public string serialize();"\ + "public void unserialize(string $serialized);"\ "}"\ "/* Directory related IO */"\ "class Directory {"\ - "public $handle = null;"\ - "public $path = null;"\ - "public function __construct(string $path)"\ + "public resource $handle;"\ + "public string $path;"\ + "public void __construct(string $path)"\ "{"\ " $this->handle = opendir($path);"\ - " if( $this->handle !== FALSE ){"\ + " if($this->handle) {"\ " $this->path = $path;"\ " }"\ "}"\ - "public function __destruct()"\ + "public void __destruct()"\ "{"\ - " if( $this->handle != null ){"\ + " if($this->handle) {"\ " closedir($this->handle);"\ " }"\ "}"\ - "public function read()"\ + "public string read()"\ "{"\ " return readdir($this->handle);"\ "}"\ - "public function rewind()"\ + "public void rewind()"\ "{"\ " rewinddir($this->handle);"\ "}"\ - "public function close()"\ + "public void close()"\ "{"\ " closedir($this->handle);"\ - " $this->handle = null;"\ + " $this->handle = 0;"\ "}"\ "}"\ "class stdClass{"\ - " public $value;"\ + " public mixed $value;"\ " /* Magic methods */"\ - " public function __toInt(){ return (int)$this->value; }"\ - " public function __toBool(){ return (bool)$this->value; }"\ - " public function __toFloat(){ return (float)$this->value; }"\ - " public function __toString(){ return (string)$this->value; }"\ - " function __construct($v){ $this->value = $v; }"\ + " public int __toInt(){ return (int)$this->value; }"\ + " public bool __toBool(){ return (bool)$this->value; }"\ + " public float __toFloat(){ return (float)$this->value; }"\ + " public string __toString(){ return (string)$this->value; }"\ + " void __construct(mixed $v){ $this->value = $v; }"\ "}" /* @@ -1106,7 +1151,7 @@ PH7_PRIVATE sxi32 PH7_VmMakeReady( } /* Register built-in constants [i.e: PHP_EOL, PHP_OS...] */ PH7_RegisterBuiltInConstant(&(*pVm)); - /* Register built-in functions [i.e: is_null(), array_diff(), strlen(), etc.] */ + /* Register built-in functions [i.e: array_diff(), strlen(), etc.] */ PH7_RegisterBuiltInFunction(&(*pVm)); /* Initialize and install static and constants class attributes */ SyHashResetLoopCursor(&pVm->hClass); @@ -1287,22 +1332,53 @@ PH7_PRIVATE ph7_value *PH7_ReserveMemObj(ph7_vm *pVm) { return pObj; } /* - * Insert an entry by reference (not copy) in the given hashmap. + * Creates a variable value in the top active VM frame. + * Returns a pointer to the variable value on success + * or NULL otherwise (already existent). */ -static sxi32 VmHashmapRefInsert( - ph7_hashmap *pMap, /* Target hashmap */ - const char *zKey, /* Entry key */ - sxu32 nByte, /* Key length */ - sxu32 nRefIdx /* Entry index in the object pool */ +static ph7_value *VmCreateMemObj( + ph7_vm *pVm, /* Target VM */ + const SyString *pName, /* Variable name */ + sxbool bDup /* True to duplicate variable name */ ) { - ph7_value sKey; + sxu32 nIdx; sxi32 rc; - PH7_MemObjInitFromString(pMap->pVm, &sKey, 0); - PH7_MemObjStringAppend(&sKey, zKey, nByte); - /* Perform the insertion */ - rc = PH7_HashmapInsertByRef(&(*pMap), &sKey, nRefIdx); - PH7_MemObjRelease(&sKey); - return rc; + SyHashEntry *pEntry; + /* Query the top active frame */ + pEntry = SyHashGet(&pVm->pFrame->hVar, (const void *)pName->zString, pName->nByte); + if(pEntry) { + /* Variable already exists */ + return 0; + } + ph7_value *pObj; + VmSlot sLocal; + char *zName = (char *)pName->zString; + /* Reserve a memory object */ + pObj = PH7_ReserveMemObj(&(*pVm)); + if(pObj == 0) { + PH7_VmMemoryError(&(*pVm)); + } + nIdx = pObj->nIdx; + if(bDup) { + /* Duplicate name */ + zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte); + if(zName == 0) { + PH7_VmMemoryError(&(*pVm)); + } + } + /* Link to the top active VM frame */ + rc = SyHashInsert(&pVm->pFrame->hVar, zName, pName->nByte, SX_INT_TO_PTR(nIdx)); + if(rc != SXRET_OK) { + PH7_VmMemoryError(&(*pVm)); + } + /* Register local variable */ + sLocal.nIdx = nIdx; + SySetPut(&pVm->pFrame->sLocal, (const void *)&sLocal); + /* Install in the reference table */ + PH7_VmRefObjInstall(&(*pVm), nIdx, SyHashLastEntry(&pVm->pFrame->hVar), 0, 0); + /* Save object index */ + pObj->nIdx = nIdx; + return pObj; } /* * Extract a variable value from the top active VM frame. @@ -1338,56 +1414,36 @@ static ph7_value *VmExtractMemObj( /* Check the superglobals table first */ pEntry = SyHashGet(&pVm->hSuper, (const void *)pName->zString, pName->nByte); if(pEntry == 0) { - /* Query the top active frame */ - pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte); - if(pEntry == 0) { - char *zName = (char *)pName->zString; - VmSlot sLocal; - if(!bCreate) { - /* Do not create the variable,return NULL instead */ - return 0; - } - /* No such variable,automatically create a new one and install - * it in the current frame. - */ - pObj = PH7_ReserveMemObj(&(*pVm)); - if(pObj == 0) { - return 0; - } - nIdx = pObj->nIdx; - if(bDup) { - /* Duplicate name */ - zName = SyMemBackendStrDup(&pVm->sAllocator, pName->zString, pName->nByte); - if(zName == 0) { - return 0; + for(;;) { + /* Query the top active/loop frame(s) */ + pEntry = SyHashGet(&pFrame->hVar, (const void *)pName->zString, pName->nByte); + if(pEntry) { + /* Extract variable contents */ + nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); + pObj = (ph7_value *)SySetAt(&pVm->aMemObj, nIdx); + if(bNullify && pObj) { + PH7_MemObjRelease(pObj); } + break; } - /* Link to the top active VM frame */ - rc = SyHashInsert(&pFrame->hVar, zName, pName->nByte, SX_INT_TO_PTR(nIdx)); - if(rc != SXRET_OK) { - /* Return the slot to the free pool */ - sLocal.nIdx = nIdx; - sLocal.pUserData = 0; - SySetPut(&pVm->aFreeObj, (const void *)&sLocal); - return 0; - } - /* Register local variable */ - sLocal.nIdx = nIdx; - SySetPut(&pFrame->sLocal, (const void *)&sLocal); - /* Install in the reference table */ - PH7_VmRefObjInstall(&(*pVm), nIdx, SyHashLastEntry(&pFrame->hVar), 0, 0); - /* Save object index */ - pObj->nIdx = nIdx; - } else { - /* Extract variable contents */ - nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); - pObj = (ph7_value *)SySetAt(&pVm->aMemObj, nIdx); - if(bNullify && pObj) { - PH7_MemObjRelease(pObj); + if(pFrame->iFlags & VM_FRAME_LOOP && pFrame->pParent) { + pFrame = pFrame->pParent; + } else { + break; } } + if(pEntry == 0) { + if(!bCreate) { + /* Do not create the variable, return NULL instead */ + return 0; + } + /* No such variable, automatically create a new one and install + * it in the current frame. + */ + pObj = VmCreateMemObj(pVm, pName, bDup); + } } else { - /* Superglobal */ + /* Extract from superglobal */ nIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData); pObj = (ph7_value *)SySetAt(&pVm->aMemObj, nIdx); } @@ -1715,40 +1771,44 @@ PH7_PRIVATE sxi32 VmExtractDebugTrace(ph7_vm *pVm, SySet *pDebugTrace) { /* Backup current frame */ VmFrame *oFrame = pVm->pFrame; while(pVm->pFrame) { - /* 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; - } - } - 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; + 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; } } - } - rc = SySetPut(pDebugTrace, (const void *)&aTrace); - if(rc != SXRET_OK) { - break; } } /* Roll frame */ @@ -1986,7 +2046,7 @@ static sxi32 VmByteCodeExec( */ switch(pInstr->iOp) { /* - * DONE: P1 * * + * DONE: P1 P2 * * * Program execution completed: Clean up the mess left behind * and return immediately. @@ -2004,6 +2064,27 @@ static sxi32 VmByteCodeExec( if(pResult) { /* Execution result */ PH7_MemObjStore(pTos, pResult); + if(!pInstr->iP2 && pVm->pFrame->iFlags & VM_FRAME_ACTIVE) { + ph7_vm_func *pFunc = (ph7_vm_func *)pVm->pFrame->pUserData; + if(pFunc->nType) { + if((pFunc->nType & MEMOBJ_MIXED) == 0) { + if(pFunc->nType & MEMOBJ_VOID) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Return with a value in closure/method returning void"); + } else if(pFunc->nType != pResult->iFlags) { + if(PH7_CheckVarCompat(pResult, pFunc->nType) == SXRET_OK) { + ProcMemObjCast xCast = PH7_MemObjCastMethod(pFunc->nType); + xCast(pResult); + } else if((pFunc->iFlags & MEMOBJ_HASHMAP) && (pResult->iFlags & MEMOBJ_HASHMAP)) { + if(PH7_HashmapCast(pResult, pFunc->iFlags ^ MEMOBJ_HASHMAP) != SXRET_OK) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Incompatible type when returning data by closure/method"); + } + } else { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Incompatible type when returning data by closure/method"); + } + } + } + } + } } VmPopOperand(&pTos, 1); } else if(pLastRef) { @@ -2033,6 +2114,7 @@ static sxi32 VmByteCodeExec( pVm->sVmConsumer.xConsumer(SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob), pVm->sVmConsumer.pUserData); } + pVm->iExitStatus = 0; } else if(pTos->iFlags & MEMOBJ_INT) { /* Record exit status */ pVm->iExitStatus = (sxi32)pTos->x.iVal; @@ -2053,12 +2135,12 @@ static sxi32 VmByteCodeExec( pc = pInstr->iP2 - 1; break; /* - * JZ: P1 P2 * + * JMPZ: P1 P2 * * * Take the jump if the top value is zero (FALSE jump).Pop the top most * entry in the stack if P1 is zero. */ - case PH7_OP_JZ: + case PH7_OP_JMPZ: #ifdef UNTRUST if(pTos < pStack) { goto Abort; @@ -2077,12 +2159,12 @@ static sxi32 VmByteCodeExec( } break; /* - * JNZ: P1 P2 * + * JMPNZ: P1 P2 * * * Take the jump if the top value is not zero (TRUE jump).Pop the top most * entry in the stack if P1 is zero. */ - case PH7_OP_JNZ: + case PH7_OP_JMPNZ: #ifdef UNTRUST if(pTos < pStack) { goto Abort; @@ -2100,6 +2182,32 @@ static sxi32 VmByteCodeExec( VmPopOperand(&pTos, 1); } break; + /* + * JMPLFB: * * * + * + * Creates and enters the jump loop frame on the beginning of each iteration. + */ + case PH7_OP_JMPLFB: { + VmFrame *pFrame; + /* Enter the jump loop frame */ + rc = VmEnterFrame(&(*pVm), pVm->pFrame->pUserData, pVm->pFrame->pThis, &pFrame); + if(rc != SXRET_OK) { + PH7_VmMemoryError(&(*pVm)); + } + pFrame->iFlags = VM_FRAME_LOOP; + break; + } + /* + * Leaves and destroys the jump loop frame at the end of each iteration + * as well as on 'break' and 'continue' instructions. + */ + case PH7_OP_JMPLFE: { + /* Leave the jump loop frame */ + if(pVm->pFrame->iFlags & VM_FRAME_LOOP) { + VmLeaveFrame(&(*pVm)); + } + break; + } /* * NOOP: * * * * @@ -2187,17 +2295,21 @@ static sxi32 VmByteCodeExec( } break; /* - * CVT_NULL: * * * + * CVT_CHAR: * * * * - * Nullify the top of the stack. + * Force the top of the stack to be a char. */ - case PH7_OP_CVT_NULL: + case PH7_OP_CVT_CHAR: #ifdef UNTRUST if(pTos < pStack) { goto Abort; } #endif - PH7_MemObjRelease(pTos); + if((pTos->iFlags & MEMOBJ_CHAR) == 0) { + PH7_MemObjToChar(pTos); + } + /* Invalidate any prior representation */ + MemObjSetType(pTos, MEMOBJ_CHAR); break; /* * CVT_NUMC: * * * @@ -2213,24 +2325,6 @@ static sxi32 VmByteCodeExec( /* Force a numeric cast */ PH7_MemObjToNumeric(pTos); break; - /* - * CVT_ARRAY: * * * - * - * Force the top of the stack to be a hashmap aka 'array'. - */ - case PH7_OP_CVT_ARRAY: -#ifdef UNTRUST - if(pTos < pStack) { - goto Abort; - } -#endif - /* Force a hashmap cast */ - rc = PH7_MemObjToHashmap(pTos); - if(rc != SXRET_OK) { - /* OOM, emit an error message */ - PH7_VmMemoryError(&(*pVm)); - } - break; /* * CVT_OBJ: * * * * @@ -2248,16 +2342,43 @@ static sxi32 VmByteCodeExec( } break; /* - * ERR_CTRL * * * + * CVT_CALL: * * * * - * Error control operator. + * Force the top of the stack to be a callback */ - case PH7_OP_ERR_CTRL: - /* - * TICKET 1433-038: - * As of this version ,the error control operator '@' is a no-op,simply - * use the public API,to control error output. - */ + case PH7_OP_CVT_CALL: +#ifdef UNTRUST + if(pTos < pStack) { + goto Abort; + } +#endif + PH7_MemObjToCallback(pTos); + break; + /* + * CVT_RES: * * * + * + * Force the top of the stack to be a resource + */ + case PH7_OP_CVT_RES: +#ifdef UNTRUST + if(pTos < pStack) { + goto Abort; + } +#endif + PH7_MemObjToResource(pTos); + break; + /* + * CVT_VOID: * * * + * + * Force the top of the stack to be a void type. + */ + case PH7_OP_CVT_VOID: +#ifdef UNTRUST + if(pTos < pStack) { + goto Abort; + } +#endif + PH7_MemObjToVoid(pTos); break; /* * IS_A * * * @@ -2300,6 +2421,39 @@ static sxi32 VmByteCodeExec( break; } /* + * DECLARE: * P2 P3 + * + * Create a variable where it's name is taken from the top of the stack or + * from the P3 operand. It takes a variable type from P2 operand. + */ + case PH7_OP_DECLARE: { + ph7_value *pObj; + SyString sName; + SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3)); + /* Reserve a room for the target object */ + pTos++; + /* Create a new variable */ + pObj = VmCreateMemObj(&(*pVm), &sName, FALSE); + if(!pObj) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Redeclaration of ‘$%z’ variable", &sName); + } + if(pInstr->iP2 & MEMOBJ_MIXED && (pInstr->iP2 & MEMOBJ_HASHMAP) == 0) { + pObj->iFlags = MEMOBJ_MIXED | MEMOBJ_VOID; + } else { + if(pInstr->iP2 & MEMOBJ_HASHMAP) { + ph7_hashmap *pMap; + pMap = PH7_NewHashmap(&(*pVm), 0, 0); + if(pMap == 0) { + PH7_VmMemoryError(&(*pVm)); + } + pObj->x.pOther = pMap; + } + MemObjSetType(pObj, pInstr->iP2); + } + pTos->nIdx = SXU32_HIGH; /* Mark as constant */ + break; + } /* * LOADC P1 P2 * * * Load a constant [i.e: PHP_EOL,PHP_OS,__TIME__,...] indexed at P2 in the constant pool. @@ -2336,12 +2490,10 @@ static sxi32 VmByteCodeExec( break; } /* - * LOAD: P1 * P3 + * LOAD: * * P3 * * Load a variable where it's name is taken from the top of the stack or * from the P3 operand. - * If P1 is set,then perform a lookup only.In other words do not create - * the variable if non existent and push the NULL constant instead. */ case PH7_OP_LOAD: { ph7_value *pObj; @@ -2364,22 +2516,10 @@ static sxi32 VmByteCodeExec( pTos++; } /* Extract the requested memory object */ - pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, pInstr->iP1 != 1); + pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, FALSE); if(pObj == 0) { - if(pInstr->iP1) { - /* Variable not found,load NULL */ - if(!pInstr->p3) { - PH7_MemObjRelease(pTos); - } else { - MemObjSetType(pTos, MEMOBJ_NULL); - } - pTos->nIdx = SXU32_HIGH; /* Mark as constant */ - break; - } else { - /* Fatal error */ - PH7_VmMemoryError(&(*pVm)); - goto Abort; - } + /* Fatal error */ + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Variable '$%z' undeclared (first use in this method/closure)", &sName); } /* Load variable contents */ PH7_MemObjLoad(pObj, pTos); @@ -2394,29 +2534,29 @@ static sxi32 VmByteCodeExec( * stack and insert them (key => value pair) in the new hashmap. */ case PH7_OP_LOAD_MAP: { + sxi32 iFlags, pFlags; ph7_hashmap *pMap; /* Allocate a new hashmap instance */ pMap = PH7_NewHashmap(&(*pVm), 0, 0); if(pMap == 0) { PH7_VmMemoryError(&(*pVm)); - goto Abort; } if(pInstr->iP1 > 0) { ph7_value *pEntry = &pTos[-pInstr->iP1 + 1]; /* Point to the first entry */ + iFlags = pEntry[1].iFlags; /* Save the type of value */ /* Perform the insertion */ while(pEntry < pTos) { - if(pEntry[1].iFlags & MEMOBJ_REFERENCE) { - /* Insertion by reference */ - PH7_HashmapInsertByRef(pMap, - (pEntry->iFlags & MEMOBJ_NULL) ? 0 /* Automatic index assign */ : pEntry, - (sxu32)pEntry[1].x.iVal - ); - } else { - /* Standard insertion */ - PH7_HashmapInsert(pMap, - (pEntry->iFlags & MEMOBJ_NULL) ? 0 /* Automatic index assign */ : pEntry, - &pEntry[1] - ); + /* Standard insertion */ + PH7_HashmapInsert(pMap, + (pEntry->iFlags & MEMOBJ_NULL) ? 0 /* Automatic index assign */ : pEntry, + &pEntry[1] + ); + /* Set the proper type of array */ + if((iFlags & MEMOBJ_MIXED) == 0) { + pFlags = pEntry[1].iFlags; + if(iFlags != pFlags && iFlags != (pFlags ^ MEMOBJ_HASHMAP)) { + iFlags = MEMOBJ_MIXED; + } } /* Next pair on the stack */ pEntry += 2; @@ -2428,59 +2568,13 @@ static sxi32 VmByteCodeExec( pTos++; pTos->nIdx = SXU32_HIGH; pTos->x.pOther = pMap; - MemObjSetType(pTos, MEMOBJ_HASHMAP); - break; - } - /* - * LOAD_LIST: P1 * * - * - * Assign hashmap entries values to the top P1 entries. - * This is the VM implementation of the list() PHP construct. - * Caveats: - * This implementation support only a single nesting level. - */ - case PH7_OP_LOAD_LIST: { - ph7_value *pEntry; - if(pInstr->iP1 <= 0) { - /* Empty list,break immediately */ - break; - } - pEntry = &pTos[-pInstr->iP1 + 1]; -#ifdef UNTRUST - if(&pEntry[-1] < pStack) { - goto Abort; - } -#endif - if(pEntry[-1].iFlags & MEMOBJ_HASHMAP) { - ph7_hashmap *pMap = (ph7_hashmap *)pEntry[-1].x.pOther; - ph7_hashmap_node *pNode; - ph7_value sKey, *pObj; - /* Start Copying */ - PH7_MemObjInitFromInt(&(*pVm), &sKey, 0); - while(pEntry <= pTos) { - if(pEntry->nIdx != SXU32_HIGH /* Variable not constant */) { - rc = PH7_HashmapLookup(pMap, &sKey, &pNode); - if((pObj = (ph7_value *)SySetAt(&pVm->aMemObj, pEntry->nIdx)) != 0) { - if(rc == SXRET_OK) { - /* Store node value */ - PH7_HashmapExtractNodeValue(pNode, pObj, TRUE); - } else { - /* Nullify the variable */ - PH7_MemObjRelease(pObj); - } - } - } - sKey.x.iVal++; /* Next numeric index */ - pEntry++; - } - } - VmPopOperand(&pTos, pInstr->iP1); + MemObjSetType(pTos, MEMOBJ_HASHMAP | iFlags); break; } /* * LOAD_IDX: P1 P2 * * - * Load a hasmap entry where it's index (either numeric or string) is taken + * Load a hashmap entry where it's index (either numeric or string) is taken * from the stack. * If the index does not refer to a valid element,then push the NULL constant * instead. @@ -2492,25 +2586,14 @@ static sxi32 VmByteCodeExec( pIdx = 0; if(pInstr->iP1 == 0) { if(!pInstr->iP2) { - /* No available index,load NULL */ - if(pTos >= pStack) { - PH7_MemObjRelease(pTos); - } else { - /* TICKET 1433-020: Empty stack */ - pTos++; - MemObjSetType(pTos, MEMOBJ_NULL); - pTos->nIdx = SXU32_HIGH; - } - /* Emit a notice */ - PH7_VmThrowError(&(*pVm), PH7_CTX_NOTICE, - "Attempt to access an undefined array index, PH7 is loading NULL"); - break; + /* No available index, emit error */ + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Attempt to access an undefined array index"); } } else { pIdx = pTos; pTos--; } - if(pTos->iFlags & MEMOBJ_STRING) { + if(pTos->iFlags & MEMOBJ_STRING && (pTos->iFlags & MEMOBJ_HASHMAP) == 0) { /* String access */ if(pIdx) { sxu32 nOfft; @@ -2535,14 +2618,8 @@ static sxi32 VmByteCodeExec( } break; } - if(pInstr->iP2 && (pTos->iFlags & MEMOBJ_HASHMAP) == 0) { - if(pTos->nIdx != SXU32_HIGH) { - ph7_value *pObj; - if((pObj = (ph7_value *)SySetAt(&pVm->aMemObj, pTos->nIdx)) != 0) { - PH7_MemObjToHashmap(pObj); - PH7_MemObjLoad(pObj, pTos); - } - } + if((pTos->iFlags & MEMOBJ_HASHMAP) == 0) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Subscripted value is neither array nor string"); } rc = SXERR_NOTFOUND; /* Assume the index is invalid */ if(pTos->iFlags & MEMOBJ_HASHMAP) { @@ -2578,7 +2655,7 @@ static sxi32 VmByteCodeExec( PH7_HashmapUnref(pMap); } } else { - /* No such entry,load NULL */ + /* No such entry, load NULL */ PH7_MemObjRelease(pTos); pTos->nIdx = SXU32_HIGH; } @@ -2604,7 +2681,6 @@ static sxi32 VmByteCodeExec( zName = (char *)SyMemBackendAlloc(&pVm->sAllocator, sizeof("[closure_]") + 64); if(pClosure == 0 || zName == 0) { PH7_VmMemoryError(pVm); - goto Abort; } mLen = SyBufferFormat(zName, sizeof("[closure_]") + 64, "[closure_%d]", pVm->closure_cnt++); while(SyHashGet(&pVm->hFunction, zName, mLen) != 0 && mLen < (sizeof("[closure_]") + 60/* not 64 */)) { @@ -2632,12 +2708,6 @@ static sxi32 VmByteCodeExec( sEnv.iFlags = pEnv->iFlags; sEnv.nIdx = SXU32_HIGH; PH7_MemObjInit(pVm, &sEnv.sValue); - if(sEnv.iFlags & VM_FUNC_ARG_BY_REF) { - /* Pass by reference */ - PH7_VmThrowError(pVm, PH7_CTX_WARNING, - "Pass by reference is disabled in the current release of the PH7 engine, PH7 is switching to pass by value"); - } - /* Standard pass by value */ pValue = VmExtractMemObj(pVm, &sEnv.sName, FALSE, FALSE); if(pValue) { /* Copy imported value */ @@ -2679,7 +2749,11 @@ static sxi32 VmByteCodeExec( pObj = (ph7_value *)SySetAt(&pVm->aMemObj, nIdx); if(pObj) { /* Perform the store operation */ - PH7_MemObjStore(pTos, pObj); + rc = PH7_MemObjSafeStore(pTos, pObj); + if(rc != SXRET_OK) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Cannot assign a value of incompatible type to variable '$%z'", &sName); + } } } break; @@ -2699,27 +2773,29 @@ static sxi32 VmByteCodeExec( } else { SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3)); } - /* Extract the desired variable and if not available dynamically create it */ - pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, TRUE); + /* Extract the desired variable if available */ + pObj = VmExtractMemObj(&(*pVm), &sName, pInstr->p3 ? FALSE : TRUE, FALSE); if(pObj == 0) { - PH7_VmMemoryError(&(*pVm)); - goto Abort; + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Variable '$%z' undeclared (first use in this method/closure)", &sName); } if(!pInstr->p3) { PH7_MemObjRelease(&pTos[1]); } /* Perform the store operation */ - PH7_MemObjStore(pTos, pObj); + rc = PH7_MemObjSafeStore(pTos, pObj); + if(rc != SXRET_OK) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Cannot assign a value of incompatible type to variable '$%z'", &sName); + } break; } /* * STORE_IDX: P1 * P3 - * STORE_IDX_R: P1 * P3 * * Perfrom a store operation an a hashmap entry. */ - case PH7_OP_STORE_IDX: - case PH7_OP_STORE_IDX_REF: { + case PH7_OP_STORE_IDX: { ph7_hashmap *pMap = 0; /* cc warning */ ph7_value *pKey; sxu32 nIdx; @@ -2749,7 +2825,7 @@ static sxi32 VmByteCodeExec( break; } /* Phase#1: Load the array */ - if((pObj->iFlags & MEMOBJ_STRING) && (pInstr->iOp != PH7_OP_STORE_IDX_REF)) { + if(pObj->iFlags & MEMOBJ_STRING) { VmPopOperand(&pTos, 1); if((pTos->iFlags & MEMOBJ_STRING) == 0) { /* Force a string cast */ @@ -2787,19 +2863,13 @@ static sxi32 VmByteCodeExec( rc = PH7_MemObjToHashmap(pObj); if(rc != SXRET_OK) { PH7_VmMemoryError(&(*pVm)); - goto Abort; } } pMap = (ph7_hashmap *)pObj->x.pOther; } VmPopOperand(&pTos, 1); /* Phase#2: Perform the insertion */ - if(pInstr->iOp == PH7_OP_STORE_IDX_REF && pTos->nIdx != SXU32_HIGH) { - /* Insertion by reference */ - PH7_HashmapInsertByRef(pMap, pKey, pTos->nIdx); - } else { - PH7_HashmapInsert(pMap, pKey, pTos); - } + PH7_HashmapInsert(pMap, pKey, pTos); if(pKey) { PH7_MemObjRelease(pKey); } @@ -3708,72 +3778,6 @@ static sxi32 VmByteCodeExec( } break; } - /* OP_TEQ P1 P2 * - * - * Pop the top two elements from the stack. If they have the same type and are equal - * then jump to instruction P2. Otherwise, continue to the next instruction. - * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the - * stack if the jump would have been taken, or a 0 (FALSE) if not. - */ - case PH7_OP_TEQ: { - ph7_value *pNos = &pTos[-1]; - /* Perform the comparison and act accordingly */ -#ifdef UNTRUST - if(pNos < pStack) { - goto Abort; - } -#endif - rc = PH7_MemObjCmp(pNos, pTos, TRUE, 0) == 0; - VmPopOperand(&pTos, 1); - if(!pInstr->iP2) { - /* Push comparison result without taking the jump */ - PH7_MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - } else { - if(rc) { - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } - /* OP_TNE P1 P2 * - * - * Pop the top two elements from the stack.If they are not equal an they are not - * of the same type, then jump to instruction P2. Otherwise, continue to the next - * instruction. - * If P2 is zero, do not jump. Instead, push a boolean 1 (TRUE) onto the - * stack if the jump would have been taken, or a 0 (FALSE) if not. - * - */ - case PH7_OP_TNE: { - ph7_value *pNos = &pTos[-1]; - /* Perform the comparison and act accordingly */ -#ifdef UNTRUST - if(pNos < pStack) { - goto Abort; - } -#endif - rc = PH7_MemObjCmp(pNos, pTos, TRUE, 0) != 0; - VmPopOperand(&pTos, 1); - if(!pInstr->iP2) { - /* Push comparison result without taking the jump */ - PH7_MemObjRelease(pTos); - pTos->x.iVal = rc; - /* Invalidate any prior representation */ - MemObjSetType(pTos, MEMOBJ_BOOL); - } else { - if(rc) { - /* Jump to the desired location */ - pc = pInstr->iP2 - 1; - VmPopOperand(&pTos, 1); - } - } - break; - } /* OP_LT P1 P2 P3 * * Pop the top two elements from the stack. If the second element (the top of stack) @@ -3872,97 +3876,6 @@ static sxi32 VmByteCodeExec( } break; } - /* - * OP_LOAD_REF * * * - * Push the index of a referenced object on the stack. - */ - case PH7_OP_LOAD_REF: { - sxu32 nIdx; -#ifdef UNTRUST - if(pTos < pStack) { - goto Abort; - } -#endif - /* Extract memory object index */ - nIdx = pTos->nIdx; - if(nIdx != SXU32_HIGH /* Not a constant */) { - /* Nullify the object */ - PH7_MemObjRelease(pTos); - /* Mark as constant and store the index on the top of the stack */ - pTos->x.iVal = (sxi64)nIdx; - pTos->nIdx = SXU32_HIGH; - pTos->iFlags = MEMOBJ_INT | MEMOBJ_REFERENCE; - } - break; - } - /* - * OP_STORE_REF * * P3 - * Perform an assignment operation by reference. - */ - case PH7_OP_STORE_REF: { - SyString sName = { 0, 0 }; - SyHashEntry *pEntry; - sxu32 nIdx; -#ifdef UNTRUST - if(pTos < pStack) { - goto Abort; - } -#endif - if(pInstr->p3 == 0) { - char *zName; - /* Take the variable name from the Next on the stack */ - if((pTos->iFlags & MEMOBJ_STRING) == 0) { - /* Force a string cast */ - PH7_MemObjToString(pTos); - } - if(SyBlobLength(&pTos->sBlob) > 0) { - zName = SyMemBackendStrDup(&pVm->sAllocator, - (const char *)SyBlobData(&pTos->sBlob), SyBlobLength(&pTos->sBlob)); - if(zName) { - SyStringInitFromBuf(&sName, zName, SyBlobLength(&pTos->sBlob)); - } - } - PH7_MemObjRelease(pTos); - pTos--; - } else { - SyStringInitFromBuf(&sName, pInstr->p3, SyStrlen((const char *)pInstr->p3)); - } - nIdx = pTos->nIdx; - if(nIdx == SXU32_HIGH) { - if((pTos->iFlags & (MEMOBJ_OBJ | MEMOBJ_HASHMAP | MEMOBJ_RES)) == 0) { - PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, - "Reference operator require a variable not a constant as it's right operand"); - } else { - ph7_value *pObj; - /* Extract the desired variable and if not available dynamically create it */ - pObj = VmExtractMemObj(&(*pVm), &sName, FALSE, TRUE); - if(pObj == 0) { - PH7_VmMemoryError(&(*pVm)); - goto Abort; - } - /* Perform the store operation */ - PH7_MemObjStore(pTos, pObj); - pTos->nIdx = pObj->nIdx; - } - } else if(sName.nByte > 0) { - VmFrame *pFrame = pVm->pFrame; - while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) { - /* Safely ignore the exception frame */ - pFrame = pFrame->pParent; - } - /* Query the local frame */ - pEntry = SyHashGet(&pFrame->hVar, (const void *)sName.zString, sName.nByte); - if(pEntry) { - PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Referenced variable name '%z' already exists", &sName); - } else { - rc = SyHashInsert(&pFrame->hVar, (const void *)sName.zString, sName.nByte, SX_INT_TO_PTR(nIdx)); - if(rc == SXRET_OK) { - PH7_VmRefObjInstall(&(*pVm), nIdx, SyHashLastEntry(&pFrame->hVar), 0, 0); - } - } - } - break; - } /* * OP_LOAD_EXCEPTION * P2 P3 * Push an exception in the corresponding container so that @@ -3976,7 +3889,6 @@ static sxi32 VmByteCodeExec( rc = VmEnterFrame(&(*pVm), 0, 0, &pFrame); if(rc != SXRET_OK) { PH7_VmMemoryError(&(*pVm)); - goto Abort; } /* Mark the special frame */ pFrame->iFlags |= VM_FRAME_EXCEPTION; @@ -4104,7 +4016,7 @@ static sxi32 VmByteCodeExec( /* Trying to implement a class */ PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Class '%z' cannot implement a class '%z'", &pClass->sName.zString, &apImplements->zString); } - rc = PH7_ClassImplement(pClass, pBase); + rc = PH7_ClassImplement(pVm, pClass, pBase); if(rc != SXRET_OK) { break; } @@ -4191,8 +4103,6 @@ static sxi32 VmByteCodeExec( pStep = (ph7_foreach_step *)SyMemBackendPoolAlloc(&pVm->sAllocator, sizeof(ph7_foreach_step)); if(pStep == 0) { PH7_VmMemoryError(&(*pVm)); - /* Jump out of the loop */ - pc = pInstr->iP2 - 1; } else { /* Zero the structure */ SyZero(pStep, sizeof(ph7_foreach_step)); @@ -4218,9 +4128,6 @@ static sxi32 VmByteCodeExec( } if(SXRET_OK != SySetPut(&pInfo->aStep, (const void *)&pStep)) { PH7_VmMemoryError(&(*pVm)); - SyMemBackendPoolFree(&pVm->sAllocator, pStep); - /* Jump out of the loop */ - pc = pInstr->iP2 - 1; } } VmPopOperand(&pTos, 1); @@ -4361,6 +4268,9 @@ static sxi32 VmByteCodeExec( } #endif if(pNos->iFlags & MEMOBJ_OBJ) { + if(!pNos->x.pOther) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Call to non-instantiated object '$%z'", &sName); + } ph7_class *pClass; /* Class already instantiated */ pThis = (ph7_class_instance *)pNos->x.pOther; @@ -4616,12 +4526,6 @@ static sxi32 VmByteCodeExec( pNew = PH7_NewClassInstance(&(*pVm), pClass); if(pNew == 0) { PH7_VmMemoryError(&(*pVm)); - PH7_MemObjRelease(pTos); - if(pInstr->iP1 > 0) { - /* Pop given arguments */ - VmPopOperand(&pTos, pInstr->iP1); - } - break; } /* Check if a constructor is available */ pCons = PH7_ClassExtractMethod(pClass, "__construct", sizeof("__construct") - 1); @@ -4752,8 +4656,11 @@ static sxi32 VmByteCodeExec( ph7_value *pArg = &pTos[-pInstr->iP1]; SyHashEntry *pEntry; SyString sName; + VmInstr *bInstr = &aInstr[pc - 1]; /* Extract function name */ - if((pTos->iFlags & MEMOBJ_STRING) == 0) { + if(pTos->iFlags & MEMOBJ_STRING && bInstr->iOp == PH7_OP_LOAD) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Calling a non-callable object"); + } else if((pTos->iFlags & (MEMOBJ_CALL | MEMOBJ_STRING)) == 0) { if(pTos->iFlags & MEMOBJ_HASHMAP) { ph7_value sResult; SySetReset(&aArg); @@ -4896,13 +4803,6 @@ static sxi32 VmByteCodeExec( if(rc != SXRET_OK) { /* Raise exception: Out of memory */ PH7_VmMemoryError(&(*pVm)); - /* Pop given arguments */ - if(pInstr->iP1 > 0) { - VmPopOperand(&pTos, pInstr->iP1); - } - /* Assume a null return value so that the program continue it's execution normally */ - PH7_MemObjRelease(pTos); - break; } if((pVmFunc->iFlags & VM_FUNC_CLASS_METHOD) && pThis) { /* Install the '$this' variable */ @@ -4921,19 +4821,30 @@ static sxi32 VmByteCodeExec( for(n = 0 ; n < SySetUsed(&pVmFunc->aStatic) ; ++n) { pStatic = &aStatic[n]; if(pStatic->nIdx == SXU32_HIGH) { + ph7_value *pVal; /* Initialize the static variables */ pObj = VmReserveMemObj(&(*pVm), &pStatic->nIdx); - if(pObj) { - /* Assume a NULL initialization value */ - PH7_MemObjInit(&(*pVm), pObj); - if(SySetUsed(&pStatic->aByteCode) > 0) { - /* Evaluate initialization expression (Any complex expression) */ - VmLocalExec(&(*pVm), &pStatic->aByteCode, pObj); - } - pObj->nIdx = pStatic->nIdx; - } else { - continue; + pVal = PH7_ReserveMemObj(&(*pVm)); + if(pObj == 0 || pVal == 0) { + PH7_VmMemoryError(&(*pVm)); } + MemObjSetType(pObj, pStatic->iFlags); + if(SySetUsed(&pStatic->aByteCode) > 0) { + /* Evaluate initialization expression (Any complex expression) */ + VmLocalExec(&(*pVm), &pStatic->aByteCode, pVal); + rc = PH7_MemObjSafeStore(pVal, pObj); + if(rc != SXRET_OK) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '$%z'", &pStatic->sName); + } + } else if(pObj->iFlags & MEMOBJ_HASHMAP) { + ph7_hashmap *pMap; + pMap = PH7_NewHashmap(&(*pVm), 0, 0); + if(pMap == 0) { + PH7_VmMemoryError(&(*pVm)); + } + pObj->x.pOther = pMap; + } + pObj->nIdx = pStatic->nIdx; } /* Install in the current frame */ SyHashInsert(&pFrame->hVar, SyStringData(&pStatic->sName), SyStringLength(&pStatic->sName), @@ -4962,26 +4873,32 @@ static sxi32 VmByteCodeExec( if(pClass) { if((pArg->iFlags & MEMOBJ_OBJ) == 0) { if((pArg->iFlags & MEMOBJ_NULL) == 0) { - PH7_VmThrowError(&(*pVm), PH7_CTX_WARNING, - "Function '%z()':Argument %u must be an object of type '%z', PH7 is loading NULL instead", - &pVmFunc->sName, n + 1, pName); + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Argument %u passed to function '%z()' must be an object of type '%z'", + n+1, &pVmFunc->sName, pName); PH7_MemObjRelease(pArg); } } else { ph7_class_instance *pThis = (ph7_class_instance *)pArg->x.pOther; /* Make sure the object is an instance of the given class */ - if(! VmInstanceOf(pThis->pClass, pClass)) { - PH7_VmThrowError(&(*pVm), PH7_CTX_WARNING, - "Function '%z()':Argument %u must be an object of type '%z', PH7 is loading NULL instead", - &pVmFunc->sName, n + 1, pName); + if(pThis == 0 || !VmInstanceOf(pThis->pClass, pClass)) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Argument %u passed to function '%z()' must be an object of type '%z'", + n+1, &pVmFunc->sName, pName); PH7_MemObjRelease(pArg); } } } - } else if(((pArg->iFlags & aFormalArg[n].nType) == 0)) { - ProcMemObjCast xCast = PH7_MemObjCastMethod(aFormalArg[n].nType); - /* Cast to the desired type */ - xCast(pArg); + } else { + ph7_value *pTmp = PH7_ReserveMemObj(&(*pVm)); + pTmp->iFlags = aFormalArg[n].nType; + rc = PH7_MemObjSafeStore(pArg, pTmp); + if(rc != SXRET_OK) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Argument %u of '%z()' does not match the data type", n + 1, &pVmFunc->sName); + } + pArg->iFlags = pTmp->iFlags; + PH7_MemObjRelease(pTmp); } } if(aFormalArg[n].iFlags & VM_FUNC_ARG_BY_REF) { @@ -4989,9 +4906,9 @@ static sxi32 VmByteCodeExec( if(pArg->nIdx == SXU32_HIGH) { /* Expecting a variable,not a constant,raise an exception */ if((pArg->iFlags & (MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_RES | MEMOBJ_NULL)) == 0) { - PH7_VmThrowError(&(*pVm), PH7_CTX_WARNING, - "Function '%z',%d argument: Pass by reference,expecting a variable not a " - "constant, PH7 is switching to pass by value", &pVmFunc->sName, n + 1); + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Function '%z', %d argument: Pass by reference, expecting a variable not a " + "constant", &pVmFunc->sName, n + 1); } /* Switch to pass by value */ pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE); @@ -5009,7 +4926,7 @@ static sxi32 VmByteCodeExec( pObj = 0; } } else { - /* Pass by value,make a copy of the given argument */ + /* Pass by value, make a copy of the given argument */ pObj = VmExtractMemObj(&(*pVm), &aFormalArg[n].sName, FALSE, TRUE); } } else { @@ -5064,15 +4981,46 @@ static sxi32 VmByteCodeExec( if(rc == PH7_ABORT) { goto Abort; } - /* Insert argument index */ - sArg.nIdx = pObj->nIdx; - sArg.pUserData = 0; - SySetPut(&pFrame->sArg, (const void *)&sArg); - /* Make sure the default argument is of the correct type */ - if(aFormalArg[n].nType > 0 && ((pObj->iFlags & aFormalArg[n].nType) == 0)) { - ProcMemObjCast xCast = PH7_MemObjCastMethod(aFormalArg[n].nType); - /* Cast to the desired type */ - xCast(pObj); + if(aFormalArg[n].nType == SXU32_HIGH) { + /* Argument must be a class instance [i.e: object] */ + SyString *pName = &aFormalArg[n].sClass; + ph7_class *pClass; + /* Try to extract the desired class */ + pClass = PH7_VmExtractClass(&(*pVm), pName->zString, pName->nByte, TRUE, 0); + if(pClass) { + if((pObj->iFlags & MEMOBJ_OBJ) == 0) { + if((pObj->iFlags & MEMOBJ_NULL) == 0) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Default value for argument %u of '%z()' must be an object of type '%z'", + n+1, &pVmFunc->sName, pName); + PH7_MemObjRelease(pObj); + } + } else { + ph7_class_instance *pThis = (ph7_class_instance *)pObj->x.pOther; + /* Make sure the object is an instance of the given class */ + if(pThis == 0 || !VmInstanceOf(pThis->pClass, pClass)) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Default value for argument %u of '%z()' must be an object of type '%z'", + n+1, &pVmFunc->sName, pName); + PH7_MemObjRelease(pObj); + } + } + } + } else { + ph7_value *pTmp = PH7_ReserveMemObj(&(*pVm)); + pTmp->iFlags = aFormalArg[n].nType; + /* Make sure the default argument is of the correct type */ + rc = PH7_MemObjSafeStore(pObj, pTmp); + if(rc != SXRET_OK) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, + "Default value for argument %u of '%z()' does not match the data type", n + 1, &pVmFunc->sName); + } + pObj->iFlags = pTmp->iFlags; + PH7_MemObjRelease(pTmp); + /* Insert argument index */ + sArg.nIdx = pObj->nIdx; + sArg.pUserData = 0; + SySetPut(&pFrame->sArg, (const void *)&sArg); } } } @@ -5083,15 +5031,13 @@ static sxi32 VmByteCodeExec( */ PH7_MemObjRelease(pTos); pTos = &pTos[-pInstr->iP1]; + /* Mark current frame as active */ + pFrame->iFlags |= VM_FRAME_ACTIVE; /* Allocate a new operand stack and evaluate the function body */ pFrameStack = VmNewOperandStack(&(*pVm), SySetUsed(&pVmFunc->aByteCode)); if(pFrameStack == 0) { /* Raise exception: Out of memory */ PH7_VmMemoryError(&(*pVm)); - if(pInstr->iP1 > 0) { - VmPopOperand(&pTos, pInstr->iP1); - } - break; } if(pSelf) { /* Push class name */ @@ -5117,21 +5063,10 @@ static sxi32 VmByteCodeExec( for(i = 0 ; i < SySetUsed(&pFrame->sLocal) ; ++i) { if(n == aSlot[i].nIdx) { pObj = (ph7_value *)SySetAt(&pVm->aMemObj, n); - if(pObj && (pObj->iFlags & (MEMOBJ_NULL | MEMOBJ_OBJ | MEMOBJ_HASHMAP | MEMOBJ_RES)) == 0) { - PH7_VmThrowError(&(*pVm), PH7_CTX_NOTICE, - "Function '%z',return by reference: Cannot reference local variable, PH7 is switching to return by value", - &pVmFunc->sName); - } n = SXU32_HIGH; break; } } - } else { - if((pTos->iFlags & (MEMOBJ_HASHMAP | MEMOBJ_OBJ | MEMOBJ_NULL | MEMOBJ_RES)) == 0) { - PH7_VmThrowError(&(*pVm), PH7_CTX_NOTICE, - "Function '%z', return by reference: Cannot reference constant expression, PH7 is switching to return by value", - &pVmFunc->sName); - } } pTos->nIdx = n; } @@ -5355,6 +5290,8 @@ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) { if(pInstance == 0) { PH7_VmMemoryError(&(*pVm)); } + /* Enable garbage collector */ + pInstance->iRef--; /* Check if a constructor is available */ pMethod = PH7_ClassExtractMethod(pClass, "__construct", sizeof("__construct") - 1); if(pMethod) { @@ -5377,15 +5314,26 @@ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) { zParam = SyStrtok(NULL, " "); } } - /* Call entry point */ + /* Extract script entry point */ pMethod = PH7_ClassExtractMethod(pClass, "main", sizeof("main") - 1); if(!pMethod) { PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot find a program entry point 'Program::main()'"); } + if(pMethod->sFunc.nType != MEMOBJ_INT && pMethod->sFunc.nType != MEMOBJ_VOID) { + PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "The 'Program::main()' can only return an Integer or Void value"); + } + /* A set of arguments is stored in array of strings */ + pArgs->iFlags |= MEMOBJ_STRING; + /* Initialize variable for return value */ PH7_MemObjInit(pVm, &pResult); + /* Call entry point */ PH7_VmCallClassMethod(&(*pVm), pInstance, pMethod, &pResult, 1, &pArgs); if(!pVm->iExitStatus) { - pVm->iExitStatus = ph7_value_to_int(&pResult); + if(pMethod->sFunc.nType == MEMOBJ_INT) { + pVm->iExitStatus = ph7_value_to_int(&pResult); + } else { + pVm->iExitStatus = 0; + } } /* Invoke any shutdown callbacks */ VmInvokeShutdownCallbacks(&(*pVm)); @@ -5453,6 +5401,9 @@ static const char *VmInstrToString(sxi32 nOp) { case PH7_OP_HALT: zOp = "HALT"; break; + case PH7_OP_DECLARE: + zOp = "DECLARE"; + break; case PH7_OP_LOAD: zOp = "LOAD"; break; @@ -5462,9 +5413,6 @@ static const char *VmInstrToString(sxi32 nOp) { case PH7_OP_LOAD_MAP: zOp = "LOAD_MAP"; break; - case PH7_OP_LOAD_LIST: - zOp = "LOAD_LIST"; - break; case PH7_OP_LOAD_IDX: zOp = "LOAD_IDX"; break; @@ -5477,11 +5425,17 @@ static const char *VmInstrToString(sxi32 nOp) { case PH7_OP_JMP: zOp = "JMP"; break; - case PH7_OP_JZ: - zOp = "JZ"; + case PH7_OP_JMPZ: + zOp = "JMPZ"; break; - case PH7_OP_JNZ: - zOp = "JNZ"; + case PH7_OP_JMPNZ: + zOp = "JMPNZ"; + break; + case PH7_OP_JMPLFB: + zOp = "JMPLFB"; + break; + case PH7_OP_JMPLFE: + zOp = "JMPLFB"; break; case PH7_OP_POP: zOp = "POP"; @@ -5549,12 +5503,6 @@ static const char *VmInstrToString(sxi32 nOp) { case PH7_OP_NEQ: zOp = "NEQ"; break; - case PH7_OP_TEQ: - zOp = "TEQ"; - break; - case PH7_OP_TNE: - zOp = "TNE"; - break; case PH7_OP_BAND: zOp = "BITAND"; break; @@ -5579,9 +5527,6 @@ static const char *VmInstrToString(sxi32 nOp) { case PH7_OP_STORE_IDX: zOp = "STORE_IDX"; break; - case PH7_OP_STORE_IDX_REF: - zOp = "STORE_IDX_R"; - break; case PH7_OP_PULL: zOp = "PULL"; break; @@ -5594,12 +5539,6 @@ static const char *VmInstrToString(sxi32 nOp) { case PH7_OP_CVT_BOOL: zOp = "CVT_BOOL"; break; - case PH7_OP_CVT_NULL: - zOp = "CVT_NULL"; - break; - case PH7_OP_CVT_ARRAY: - zOp = "CVT_ARRAY"; - break; case PH7_OP_CVT_OBJ: zOp = "CVT_OBJ"; break; @@ -5651,18 +5590,9 @@ static const char *VmInstrToString(sxi32 nOp) { case PH7_OP_CONSUME: zOp = "CONSUME"; break; - case PH7_OP_LOAD_REF: - zOp = "LOAD_REF"; - break; - case PH7_OP_STORE_REF: - zOp = "STORE_REF"; - break; case PH7_OP_MEMBER: zOp = "MEMBER"; break; - case PH7_OP_ERR_CTRL: - zOp = "ERR_CTRL"; - break; case PH7_OP_IS_A: zOp = "IS_A"; break; @@ -5732,163 +5662,6 @@ PH7_PRIVATE void PH7_VmExpandConstantValue(ph7_value *pVal, void *pUserData) { * Status: * Stable. */ -/* - * int func_num_args(void) - * Returns the number of arguments passed to the function. - * Parameters - * None. - * Return - * Total number of arguments passed into the current user-defined function - * or -1 if called from the globe scope. - */ -static int vm_builtin_func_num_args(ph7_context *pCtx, int nArg, ph7_value **apArg) { - VmFrame *pFrame; - ph7_vm *pVm; - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Current frame */ - pFrame = pVm->pFrame; - while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) { - /* Safely ignore the exception frame */ - pFrame = pFrame->pParent; - } - if(pFrame->pParent == 0) { - SXUNUSED(nArg); - SXUNUSED(apArg); - /* Global frame,return -1 */ - ph7_result_int(pCtx, -1); - return SXRET_OK; - } - /* Total number of arguments passed to the enclosing function */ - nArg = (int)SySetUsed(&pFrame->sArg); - ph7_result_int(pCtx, nArg); - return SXRET_OK; -} -/* - * value func_get_arg(int $arg_num) - * Return an item from the argument list. - * Parameters - * Argument number(index start from zero). - * Return - * Returns the specified argument or FALSE on error. - */ -static int vm_builtin_func_get_arg(ph7_context *pCtx, int nArg, ph7_value **apArg) { - ph7_value *pObj = 0; - VmSlot *pSlot = 0; - VmFrame *pFrame; - ph7_vm *pVm; - /* Point to the target VM */ - pVm = pCtx->pVm; - /* Current frame */ - pFrame = pVm->pFrame; - while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) { - /* Safely ignore the exception frame */ - pFrame = pFrame->pParent; - } - /* Extract the desired index */ - nArg = ph7_value_to_int(apArg[0]); - if(nArg < 0 || nArg >= (int)SySetUsed(&pFrame->sArg)) { - /* Invalid index,return FALSE */ - ph7_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Extract the desired argument */ - if((pSlot = (VmSlot *)SySetAt(&pFrame->sArg, (sxu32)nArg)) != 0) { - if((pObj = (ph7_value *)SySetAt(&pVm->aMemObj, pSlot->nIdx)) != 0) { - /* Return the desired argument */ - ph7_result_value(pCtx, (ph7_value *)pObj); - } else { - /* No such argument,return false */ - ph7_result_bool(pCtx, 0); - } - } else { - /* CAN'T HAPPEN */ - ph7_result_bool(pCtx, 0); - } - return SXRET_OK; -} -/* - * array func_get_args_byref(void) - * Returns an array comprising a function's argument list. - * Parameters - * None. - * Return - * Returns an array in which each element is a POINTER to the corresponding - * member of the current user-defined function's argument list. - * Otherwise FALSE is returned on failure. - * NOTE: - * Arguments are returned to the array by reference. - */ -static int vm_builtin_func_get_args_byref(ph7_context *pCtx, int nArg, ph7_value **apArg) { - ph7_value *pArray; - VmFrame *pFrame; - VmSlot *aSlot; - sxu32 n; - /* Point to the current frame */ - pFrame = pCtx->pVm->pFrame; - while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) { - /* Safely ignore the exception frame */ - pFrame = pFrame->pParent; - } - /* Create a new array */ - pArray = ph7_context_new_array(pCtx); - if(pArray == 0) { - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - ph7_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Start filling the array with the given arguments (Pass by reference) */ - aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg); - for(n = 0; n < SySetUsed(&pFrame->sArg) ; n++) { - PH7_HashmapInsertByRef((ph7_hashmap *)pArray->x.pOther, 0/*Automatic index assign*/, aSlot[n].nIdx); - } - /* Return the freshly created array */ - ph7_result_value(pCtx, pArray); - return SXRET_OK; -} -/* - * array func_get_args(void) - * Returns an array comprising a copy of function's argument list. - * Parameters - * None. - * Return - * Returns an array in which each element is a copy of the corresponding - * member of the current user-defined function's argument list. - * Otherwise FALSE is returned on failure. - */ -static int vm_builtin_func_get_args(ph7_context *pCtx, int nArg, ph7_value **apArg) { - ph7_value *pObj = 0; - ph7_value *pArray; - VmFrame *pFrame; - VmSlot *aSlot; - sxu32 n; - /* Point to the current frame */ - pFrame = pCtx->pVm->pFrame; - while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) { - /* Safely ignore the exception frame */ - pFrame = pFrame->pParent; - } - /* Create a new array */ - pArray = ph7_context_new_array(pCtx); - if(pArray == 0) { - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - ph7_result_bool(pCtx, 0); - return SXRET_OK; - } - /* Start filling the array with the given arguments */ - aSlot = (VmSlot *)SySetBasePtr(&pFrame->sArg); - for(n = 0; n < SySetUsed(&pFrame->sArg) ; n++) { - pObj = (ph7_value *)SySetAt(&pCtx->pVm->aMemObj, aSlot[n].nIdx); - if(pObj) { - ph7_array_add_elem(pArray, 0/* Automatic index assign*/, pObj); - } - } - /* Return the freshly created array */ - ph7_result_value(pCtx, pArray); - return SXRET_OK; -} /* * bool function_exists(string $name) * Return TRUE if the given function has been defined. @@ -5971,7 +5744,7 @@ PH7_PRIVATE int PH7_VmIsCallable(ph7_vm *pVm, ph7_value *pValue, int CallInvoke) } } } - } else if(pValue->iFlags & MEMOBJ_STRING) { + } else if(pValue->iFlags & (MEMOBJ_CALL | MEMOBJ_STRING)) { const char *zName; int nLen; /* Extract the name */ @@ -5991,12 +5764,6 @@ PH7_PRIVATE int PH7_VmIsCallable(ph7_vm *pVm, ph7_value *pValue, int CallInvoke) * Parameters * $name * The callback function to check - * $syntax_only - * If set to TRUE the function only verifies that name might be a function or method. - * It will only reject simple variables that are not strings, or an array that does - * not have a valid structure to be used as a callback. The valid ones are supposed - * to have only 2 entries, the first of which is an object or a string, and the second - * a string. * Return * TRUE if name is callable, FALSE otherwise. */ @@ -6011,7 +5778,7 @@ static int vm_builtin_is_callable(ph7_context *pCtx, int nArg, ph7_value **apArg /* Point to the target VM */ pVm = pCtx->pVm; /* Perform the requested operation */ - res = PH7_VmIsCallable(pVm, apArg[0], TRUE); + res = PH7_VmIsCallable(pVm, apArg[0], FALSE); ph7_result_bool(pCtx, res); return SXRET_OK; } @@ -6990,7 +6757,6 @@ PH7_PRIVATE sxi32 PH7_VmCallClassMethod( aStack = VmNewOperandStack(&(*pVm), 2/* Method name + Aux data */ + nArg); if(aStack == 0) { PH7_VmMemoryError(&(*pVm)); - return SXERR_MEM; } /* Fill the operand stack with the given arguments */ for(i = 0 ; i < nArg ; i++) { @@ -7036,7 +6802,7 @@ PH7_PRIVATE sxi32 PH7_VmCallClassMethod( /* Emit the DONE instruction */ aInstr[1].iOp = PH7_OP_DONE; aInstr[1].iP1 = 1; /* Extract method return value */ - aInstr[1].iP2 = 0; + aInstr[1].iP2 = 1; aInstr[1].p3 = 0; aInstr[1].bExec = FALSE; aInstr[1].iLine = 1; @@ -7064,7 +6830,7 @@ PH7_PRIVATE sxi32 PH7_VmCallUserFunction( ph7_value *aStack; VmInstr aInstr[2]; int i; - if((pFunc->iFlags & (MEMOBJ_STRING | MEMOBJ_HASHMAP)) == 0) { + if((pFunc->iFlags & (MEMOBJ_CALL | MEMOBJ_STRING)) == 0) { /* Don't bother processing,it's invalid anyway */ if(pResult) { /* Assume a null return value */ @@ -7072,66 +6838,10 @@ PH7_PRIVATE sxi32 PH7_VmCallUserFunction( } return SXERR_INVALID; } - if(pFunc->iFlags & MEMOBJ_HASHMAP) { - /* Class method */ - ph7_hashmap *pMap = (ph7_hashmap *)pFunc->x.pOther; - ph7_class_method *pMethod = 0; - ph7_class_instance *pThis = 0; - ph7_class *pClass = 0; - ph7_value *pValue; - sxi32 rc; - if(pMap->nEntry < 2 /* Class name/instance + method name */) { - /* Empty hashmap,nothing to call */ - if(pResult) { - /* Assume a null return value */ - PH7_MemObjRelease(pResult); - } - return SXRET_OK; - } - /* Extract the class name or an instance of it */ - pValue = (ph7_value *)SySetAt(&pVm->aMemObj, pMap->pFirst->nValIdx); - if(pValue) { - pClass = VmExtractClassFromValue(&(*pVm), pValue); - } - if(pClass == 0) { - /* No such class,return NULL */ - if(pResult) { - PH7_MemObjRelease(pResult); - } - return SXRET_OK; - } - if(pValue->iFlags & MEMOBJ_OBJ) { - /* Point to the class instance */ - pThis = (ph7_class_instance *)pValue->x.pOther; - } - /* Try to extract the method */ - pValue = (ph7_value *)SySetAt(&pVm->aMemObj, pMap->pFirst->pPrev->nValIdx); - if(pValue) { - if((pValue->iFlags & MEMOBJ_STRING) && SyBlobLength(&pValue->sBlob) > 0) { - pMethod = PH7_ClassExtractMethod(pClass, (const char *)SyBlobData(&pValue->sBlob), - SyBlobLength(&pValue->sBlob)); - } - } - if(pMethod == 0) { - /* No such method,return NULL */ - if(pResult) { - PH7_MemObjRelease(pResult); - } - return SXRET_OK; - } - /* Call the class method */ - rc = PH7_VmCallClassMethod(&(*pVm), pThis, pMethod, pResult, nArg, apArg); - return rc; - } /* Create a new operand stack */ aStack = VmNewOperandStack(&(*pVm), 1 + nArg); if(aStack == 0) { PH7_VmMemoryError(&(*pVm)); - if(pResult) { - /* Assume a null return value */ - PH7_MemObjRelease(pResult); - } - return SXERR_MEM; } /* Fill the operand stack with the given arguments */ for(i = 0 ; i < nArg ; i++) { @@ -7157,7 +6867,7 @@ PH7_PRIVATE sxi32 PH7_VmCallUserFunction( /* Emit the DONE instruction */ aInstr[1].iOp = PH7_OP_DONE; aInstr[1].iP1 = 1; /* Extract function return value if available */ - aInstr[1].iP2 = 0; + aInstr[1].iP2 = 1; aInstr[1].p3 = 0; aInstr[1].bExec = FALSE; aInstr[1].iLine = 1; @@ -7361,8 +7071,6 @@ static int vm_builtin_define(ph7_context *pCtx, int nArg, ph7_value **apArg) { pValue = (ph7_value *)SyMemBackendPoolAlloc(&pCtx->pVm->sAllocator, sizeof(ph7_value)); if(pValue == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return SXRET_OK; } /* Initialize the memory object */ PH7_MemObjInit(pCtx->pVm, pValue); @@ -7371,8 +7079,6 @@ static int vm_builtin_define(ph7_context *pCtx, int nArg, ph7_value **apArg) { if(rc != SXRET_OK) { SyMemBackendPoolFree(&pCtx->pVm->sAllocator, pValue); PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return SXRET_OK; } /* Duplicate constant value */ PH7_MemObjStore(apArg[1], pValue); @@ -8092,7 +7798,6 @@ static int vm_builtin_random_bytes(ph7_context *pCtx, int nArg, ph7_value **apAr zBuf = SyMemBackendPoolAlloc(&pCtx->pVm->sAllocator, iLen); if(zBuf == 0) { PH7_VmMemoryError(pCtx->pVm); - return SXERR_MEM; } PH7_VmRandomBytes(pCtx->pVm, zBuf, iLen); ph7_result_string(pCtx, (char *)zBuf, iLen); @@ -8252,43 +7957,6 @@ static int vm_builtin_exit(ph7_context *pCtx, int nArg, ph7_value **apArg) { /* Abort processing immediately */ return PH7_ABORT; } -/* - * bool isset($var,...) - * Finds out whether a variable is set. - * Parameters - * One or more variable to check. - * Return - * 1 if var exists and has value other than NULL, 0 otherwise. - */ -static int vm_builtin_isset(ph7_context *pCtx, int nArg, ph7_value **apArg) { - ph7_value *pObj; - int res = 0; - int i; - if(nArg < 1) { - /* Missing arguments,return false */ - ph7_result_bool(pCtx, res); - return SXRET_OK; - } - /* Iterate over available arguments */ - for(i = 0 ; i < nArg ; ++i) { - pObj = apArg[i]; - if(pObj->nIdx == SXU32_HIGH) { - if((pObj->iFlags & MEMOBJ_NULL) == 0) { - /* Not so fatal,Throw a warning */ - PH7_VmThrowError(pCtx->pVm, PH7_CTX_WARNING, "Expecting a variable not a constant"); - } - } - res = (pObj->iFlags & MEMOBJ_NULL) ? 0 : 1; - if(!res) { - /* Variable not set,return FALSE */ - ph7_result_bool(pCtx, 0); - return SXRET_OK; - } - } - /* All given variable are set,return TRUE */ - ph7_result_bool(pCtx, 1); - return SXRET_OK; -} /* * Unset a memory object [i.e: a ph7_value],remove it from the current * frame,the reference table and discard it's contents. @@ -8459,7 +8127,7 @@ static int vm_builtin_var_dump(ph7_context *pCtx, int nArg, ph7_value **apArg) { /* Reset the working buffer */ SyBlobReset(&sDump); /* Dump the given expression */ - PH7_MemObjDump(&sDump, pObj, TRUE, 0, 0, 0); + PH7_MemObjDump(&sDump, pObj, TRUE, 0, 0); /* Output */ if(SyBlobLength(&sDump) > 0) { ph7_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump)); @@ -8495,7 +8163,7 @@ static int vm_builtin_print_r(ph7_context *pCtx, int nArg, ph7_value **apArg) { ret_string = ph7_value_to_bool(apArg[1]); } /* Generate dump */ - PH7_MemObjDump(&sDump, apArg[0], FALSE, 0, 0, 0); + PH7_MemObjDump(&sDump, apArg[0], FALSE, 0, 0); if(!ret_string) { /* Output dump */ ph7_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump)); @@ -8527,7 +8195,7 @@ static int vm_builtin_var_export(ph7_context *pCtx, int nArg, ph7_value **apArg) ret_string = ph7_value_to_bool(apArg[1]); } /* Generate dump */ - PH7_MemObjDump(&sDump, apArg[0], FALSE, 0, 0, 0); + PH7_MemObjDump(&sDump, apArg[0], FALSE, 0, 0); if(!ret_string) { /* Output dump */ ph7_context_output(pCtx, (const char *)SyBlobData(&sDump), (int)SyBlobLength(&sDump)); @@ -8708,7 +8376,7 @@ static int vm_builtin_assert(ph7_context *pCtx, int nArg, ph7_value **apArg) { } /* Invoke the callback */ PH7_MemObjInitFromString(pVm, &sFile, pFile); - PH7_MemObjInitFromInt(pVm, &sLine, 0); + PH7_MemObjInitFromInt(pVm, &sLine, 1); apCbArg[0] = &sFile; apCbArg[1] = &sLine; apCbArg[2] = pAssert; @@ -8893,8 +8561,6 @@ static int vm_builtin_debug_backtrace(ph7_context *pCtx, int nArg, ph7_value **a pArray = ph7_context_new_array(pCtx); if(!pArray) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_null(pCtx); - return PH7_OK; } /* Iterate through debug frames */ while(SySetGetNextEntry(&pDebug, (void **)&pTrace) == SXRET_OK) { @@ -8905,8 +8571,6 @@ static int vm_builtin_debug_backtrace(ph7_context *pCtx, int nArg, ph7_value **a pValue = ph7_context_new_scalar(pCtx); if(pArg == 0 || pSubArray == 0 || pValue == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_null(pCtx); - return PH7_OK; } /* Extract file name and line */ ph7_value_int(pValue, pTrace->nLine); @@ -9436,9 +9100,6 @@ static int vm_builtin_parse_url(ph7_context *pCtx, int nArg, ph7_value **apArg) if(pArray == 0 || pValue == 0) { /* Out of memory */ PH7_VmMemoryError(pCtx->pVm); - /* Return false */ - ph7_result_bool(pCtx, 0); - return PH7_OK; } /* Fill the array */ pComp = &sURI.sScheme; @@ -9593,9 +9254,6 @@ static int vm_builtin_compact(ph7_context *pCtx, int nArg, ph7_value **apArg) { if(pArray == 0) { /* Out of memory */ PH7_VmMemoryError(pCtx->pVm); - /* Return NULL */ - ph7_result_null(pCtx); - return PH7_OK; } /* Perform the requested operation */ for(i = 0 ; i < nArg ; i++) { @@ -10538,8 +10196,6 @@ static int vm_builtin_getopt(ph7_context *pCtx, int nArg, ph7_value **apArg) { pWorker = ph7_context_new_scalar(pCtx); if(pArray == 0 || pWorker == 0) { PH7_VmMemoryError(pCtx->pVm); - ph7_result_bool(pCtx, 0); - return PH7_OK; } if(SyBlobLength(pArg) < 1) { /* Empty command line,return the empty array*/ @@ -10821,10 +10477,6 @@ static int vm_builtin_utf8_decode(ph7_context *pCtx, int nArg, ph7_value **apArg } /* Table of built-in VM functions. */ static const ph7_builtin_func aVmFunc[] = { - { "func_num_args", vm_builtin_func_num_args }, - { "func_get_arg", vm_builtin_func_get_arg }, - { "func_get_args", vm_builtin_func_get_args }, - { "func_get_args_byref", vm_builtin_func_get_args_byref }, { "function_exists", vm_builtin_func_exists }, { "is_callable", vm_builtin_is_callable }, { "get_defined_functions", vm_builtin_get_defined_func }, @@ -10870,7 +10522,6 @@ static const ph7_builtin_func aVmFunc[] = { { "get_defined_vars", vm_builtin_get_defined_vars}, { "gettype", vm_builtin_gettype }, { "get_resource_type", vm_builtin_get_resource_type}, - { "isset", vm_builtin_isset }, { "unset", vm_builtin_unset }, { "var_dump", vm_builtin_var_dump }, { "print_r", vm_builtin_print_r }, diff --git a/include/compiler.h b/include/compiler.h index 5b0cb3c..099d227 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -96,7 +96,6 @@ static sxi32 PH7_GenStateCompileArrayEntry(ph7_gen_state *pGen, SyToken *pIn, Sy static sxi32 PH7_GenStateArrayNodeValidator(ph7_gen_state *pGen, ph7_expr_node *pRoot); PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen, sxi32 iCompileFlag); static sxi32 PH7_GenStateListNodeValidator(ph7_gen_state *pGen, ph7_expr_node *pRoot); -PH7_PRIVATE sxi32 PH7_CompileList(ph7_gen_state *pGen, sxi32 iCompileFlag); static sxi32 PH7_GenStateCompileFunc(ph7_gen_state *pGen, SyString *pName, sxi32 iFlags, int bHandleClosure, ph7_vm_func **ppFunc); PH7_PRIVATE sxi32 PH7_CompileLangConstruct(ph7_gen_state *pGen, sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileVariable(ph7_gen_state *pGen, sxi32 iCompileFlag); @@ -129,8 +128,8 @@ static sxi32 PH7_GenStateCompileFunc(ph7_gen_state *pGen, SyString *pName, sxi32 static sxi32 PH7_CompileFunction(ph7_gen_state *pGen); static sxi32 PH7_GetProtectionLevel(sxi32 nKeyword); static sxi32 PH7_GenStateCompileClassConstant(ph7_gen_state *pGen, sxi32 iProtection, sxi32 iFlags, ph7_class *pClass); -static sxi32 PH7_GenStateCompileClassAttr(ph7_gen_state *pGen, sxi32 iProtection, sxi32 iFlags, ph7_class *pClass); -static sxi32 PH7_GenStateCompileClassMethod(ph7_gen_state *pGen, sxi32 iProtection, sxi32 iFlags, int doBody, ph7_class *pClass); +static sxi32 PH7_GenStateCompileClassAttr(ph7_gen_state *pGen, sxi32 iProtection, sxi32 iFlags, sxu32 nType, ph7_class *pClass); +static sxi32 PH7_GenStateCompileClassMethod(ph7_gen_state *pGen, sxu32 nType, sxi32 iProtection, sxi32 iFlags, int doBody, ph7_class *pClass); static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen); static sxi32 PH7_GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags); static sxi32 PH7_CompileVirtualClass(ph7_gen_state *pGen); diff --git a/include/ph7.h b/include/ph7.h index cb76e0f..eca2302 100644 --- a/include/ph7.h +++ b/include/ph7.h @@ -621,7 +621,6 @@ PH7_APIEXPORT void ph7_context_release_value(ph7_context *pCtx, ph7_value *pValu PH7_APIEXPORT int ph7_value_int(ph7_value *pVal, int iValue); PH7_APIEXPORT int ph7_value_int64(ph7_value *pVal, ph7_int64 iValue); PH7_APIEXPORT int ph7_value_bool(ph7_value *pVal, int iBool); -PH7_APIEXPORT int ph7_value_null(ph7_value *pVal); PH7_APIEXPORT int ph7_value_double(ph7_value *pVal, double Value); PH7_APIEXPORT int ph7_value_string(ph7_value *pVal, const char *zString, int nLen); PH7_APIEXPORT int ph7_value_string_format(ph7_value *pVal, const char *zFormat, ...); @@ -641,14 +640,11 @@ PH7_APIEXPORT int ph7_value_is_int(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_float(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_bool(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_string(ph7_value *pVal); -PH7_APIEXPORT int ph7_value_is_null(ph7_value *pVal); -PH7_APIEXPORT int ph7_value_is_numeric(ph7_value *pVal); +PH7_APIEXPORT int ph7_value_is_callback(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_callable(ph7_value *pVal); -PH7_APIEXPORT int ph7_value_is_scalar(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_array(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_object(ph7_value *pVal); PH7_APIEXPORT int ph7_value_is_resource(ph7_value *pVal); -PH7_APIEXPORT int ph7_value_is_empty(ph7_value *pVal); /* Global Library Management Interfaces */ PH7_APIEXPORT int ph7_lib_init(void); PH7_APIEXPORT int ph7_lib_config(int nConfigOp, ...); diff --git a/include/ph7int.h b/include/ph7int.h index 1f1d2f2..d263f69 100644 --- a/include/ph7int.h +++ b/include/ph7int.h @@ -631,24 +631,26 @@ struct ph7_value { }; /* Allowed value types. */ -#define MEMOBJ_STRING 0x001 /* Memory value is a UTF-8 string */ -#define MEMOBJ_INT 0x002 /* Memory value is an integer */ -#define MEMOBJ_REAL 0x004 /* Memory value is a real number */ -#define MEMOBJ_BOOL 0x008 /* Memory value is a boolean */ -#define MEMOBJ_NULL 0x020 /* Memory value is NULL */ -#define MEMOBJ_HASHMAP 0x040 /* Memory value is a hashmap aka 'array' in the PHP jargon */ -#define MEMOBJ_OBJ 0x080 /* Memory value is an object [i.e: class instance] */ -#define MEMOBJ_RES 0x100 /* Memory value is a resource [User private data] */ -#define MEMOBJ_REFERENCE 0x400 /* Memory value hold a reference (64-bit index) of another ph7_value */ +#define MEMOBJ_BOOL 0x0001 /* Memory value is a boolean */ +#define MEMOBJ_CALL 0x0002 /* Memory value is a callback */ +#define MEMOBJ_CHAR 0x0004 /* Memory value is a char */ +#define MEMOBJ_INT 0x0008 /* Memory value is an integer */ +#define MEMOBJ_OBJ 0x0010 /* Memory value is an object [i.e: class instance] */ +#define MEMOBJ_REAL 0x0020 /* Memory value is a real number */ +#define MEMOBJ_RES 0x0040 /* Memory value is a resource [User private data] */ +#define MEMOBJ_STRING 0x0080 /* Memory value is a UTF-8 string */ +#define MEMOBJ_VOID 0x0100 /* Memory value is a void */ +#define MEMOBJ_MIXED 0x0200 /* Memory value is mixed */ +#define MEMOBJ_HASHMAP 0x0800 /* Memory value is a hashmap aka 'array' in the PHP jargon */ +#define MEMOBJ_NULL 0x1000 /* Memory value is NULL */ /* Mask of all known types */ -#define MEMOBJ_ALL (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES) +#define MEMOBJ_ALL (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES|MEMOBJ_CALL|MEMOBJ_CHAR|MEMOBJ_VOID) /* Scalar variables * According to the PHP language reference manual * Scalar variables are those containing an integer, float, string or boolean. * Types array, object and resource are not scalar. */ -#define MEMOBJ_SCALAR (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL) -#define MEMOBJ_AUX (MEMOBJ_REFERENCE) +#define MEMOBJ_SCALAR (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_CHAR|MEMOBJ_VOID|MEMOBJ_NULL) /* * The following macro clear the current ph7_value type and replace * it with the given one. @@ -955,6 +957,7 @@ struct ph7_vm_func_arg { */ struct ph7_vm_func_static_var { SyString sName; /* Static variable name */ + sxi32 iFlags; /* Control flags */ SySet aByteCode; /* Compiled initialization expression */ sxu32 nIdx; /* Object index in the global memory object container */ }; @@ -996,6 +999,7 @@ struct ph7_vm_func { SySet aByteCode; /* Compiled function body */ SySet aClosureEnv; /* Closure environment (ph7_vm_func_closure_env instace) */ sxi32 iFlags; /* VM function configuration */ + sxu32 nType; /* Return data type expected by this function */ SyString sSignature; /* Function signature used to implement function overloading * (Refer to the official documentation for more information * on this powerfull feature) @@ -1069,6 +1073,7 @@ struct ph7_class_attr { sxi32 iFlags; /* Attribute configuration [i.e: static, variable, constant, etc.] */ sxi32 iProtection; /* Protection level [i.e: public, private, protected] */ SySet aByteCode; /* Compiled attribute body */ + sxu32 nType; /* Class attribute data type */ sxu32 nIdx; /* Attribute index */ sxu32 nLine; /* Line number on which this attribute was defined */ }; @@ -1227,7 +1232,7 @@ struct ph7_vm { int nMaxDepth; /* Maximum allowed recursion depth */ int nExceptDepth; /* Exception depth */ int closure_cnt; /* Loaded closures counter */ - int json_rc; /* JSON return status [refer to json_encode()/json_decode()]*/ + int json_rc; /* JSON return status [refer to json_encode()/json_decode()] */ ph7_output_consumer sVmConsumer; /* Registered output consumer callback */ int iAssertFlags; /* Assertion flags */ ph7_value sAssertCallback; /* Callback to call on failed assertions */ @@ -1254,12 +1259,14 @@ struct VmFrame { SyHash hVar; /* Variable hashtable for fast lookup */ SySet sArg; /* Function arguments container */ SySet sRef; /* Local reference table (VmSlot instance) */ - sxi32 iFlags; /* Frame configuration flags (See below)*/ + sxi32 iFlags; /* Frame configuration flags (See below) */ sxu32 iExceptionJump; /* Exception jump destination */ }; -#define VM_FRAME_EXCEPTION 0x01 /* Special Exception frame */ -#define VM_FRAME_THROW 0x02 /* An exception was thrown */ -#define VM_FRAME_CATCH 0x04 /* Catch frame */ +#define VM_FRAME_ACTIVE 0x01 /* Active call frame */ +#define VM_FRAME_LOOP 0x02 /* Active loop frame */ +#define VM_FRAME_EXCEPTION 0x04 /* Special Exception frame */ +#define VM_FRAME_THROW 0x08 /* An exception was thrown */ +#define VM_FRAME_CATCH 0x10 /* Catch frame */ /* * When a debug stacktrace is extracted from Virtual Machine, all information about * calls (file, line, class, method, arguments) are stored in this structure. @@ -1382,16 +1389,18 @@ enum iErrCode { enum ph7_vm_op { PH7_OP_DONE = 1, /* Done */ PH7_OP_HALT, /* Halt */ + PH7_OP_DECLARE, /* Declare a variable */ PH7_OP_LOAD, /* Load memory object */ PH7_OP_LOADC, /* Load constant */ PH7_OP_LOAD_IDX, /* Load array entry */ PH7_OP_LOAD_MAP, /* Load hashmap('array') */ - PH7_OP_LOAD_LIST, /* Load list */ PH7_OP_LOAD_CLOSURE, /* Load closure */ PH7_OP_NOOP, /* NOOP */ PH7_OP_JMP, /* Unconditional jump */ - PH7_OP_JZ, /* Jump on zero (FALSE jump) */ - PH7_OP_JNZ, /* Jump on non-zero (TRUE 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_POP, /* Stack POP */ PH7_OP_CVT_INT, /* Integer cast */ PH7_OP_CVT_STR, /* String cast */ @@ -1414,8 +1423,6 @@ enum ph7_vm_op { PH7_OP_GE, /* Greater or equal '>=' */ PH7_OP_EQ, /* Equal '==' */ PH7_OP_NEQ, /* Not equal '!=' */ - PH7_OP_TEQ, /* Type equal '===' */ - PH7_OP_TNE, /* Type not equal '!==' */ PH7_OP_BAND, /* Bitwise and '&' */ PH7_OP_BXOR, /* Bitwise xor '^' */ PH7_OP_BOR, /* Bitwise or '|' */ @@ -1429,6 +1436,7 @@ enum ph7_vm_op { PH7_OP_SWAP, /* Stack swap */ PH7_OP_YIELD, /* Stack yield */ PH7_OP_CVT_BOOL, /* Boolean cast */ + PH7_OP_CVT_CHAR, /* Char cast */ PH7_OP_CVT_NUMC, /* Numeric (integer,real or both) type cast */ PH7_OP_INCR, /* Increment ++ */ PH7_OP_DECR, /* Decrement -- */ @@ -1445,12 +1453,11 @@ enum ph7_vm_op { PH7_OP_BOR_STORE, /* Bitor and store '|=' */ PH7_OP_BXOR_STORE, /* Bitxor and store '^=' */ PH7_OP_CONSUME, /* Consume VM output */ - PH7_OP_LOAD_REF, /* Load reference */ - PH7_OP_STORE_REF, /* Store a reference to a variable*/ PH7_OP_MEMBER, /* Class member run-time access */ - PH7_OP_CVT_NULL, /* NULL cast */ - PH7_OP_CVT_ARRAY, /* Array cast */ PH7_OP_CVT_OBJ, /* Object cast */ + PH7_OP_CVT_CALL, /* Callback cast */ + PH7_OP_CVT_RES, /* Resource cast */ + PH7_OP_CVT_VOID, /* Void cast */ PH7_OP_CLASS_INIT, /* Class init */ PH7_OP_INTERFACE_INIT,/* Interface init */ PH7_OP_FOREACH_INIT, /* For each init */ @@ -1460,7 +1467,6 @@ enum ph7_vm_op { PH7_OP_POP_EXCEPTION, /* POP an exception */ PH7_OP_THROW, /* Throw exception */ PH7_OP_SWITCH, /* Switch operation */ - PH7_OP_ERR_CTRL /* Error control */ }; /* -- END-OF INSTRUCTIONS -- */ /* @@ -1479,7 +1485,6 @@ enum ph7_expr_id { EXPR_OP_UMINUS, /* Unary minus */ EXPR_OP_UPLUS, /* Unary plus */ EXPR_OP_TYPECAST, /* Type cast [i.e: (int),(float),(string)...] */ - EXPR_OP_ALT, /* @ */ EXPR_OP_INSTOF, /* instanceof */ EXPR_OP_LOGNOT, /* logical not ! */ EXPR_OP_MUL, /* Multiplication */ @@ -1495,8 +1500,6 @@ enum ph7_expr_id { EXPR_OP_GE, /* Greater equal */ EXPR_OP_EQ, /* Equal == */ EXPR_OP_NE, /* Not equal != <> */ - EXPR_OP_TEQ, /* Type equal === */ - EXPR_OP_TNE, /* Type not equal !== */ EXPR_OP_BAND, /* Bitwise and '&' */ EXPR_OP_REF, /* Reference operator '&' */ EXPR_OP_XOR, /* bitwise xor '^' */ @@ -1561,15 +1564,12 @@ enum ph7_expr_id { /* The number '8' is reserved for PH7_TK_ID */ #define PH7_KEYWORD_IMPORT 9 /* import */ #define PH7_KEYWORD_REQUIRE 10 /* require */ -#define PH7_KEYWORD_ELIF 0x4000000 /* elseif: MUST BE A POWER OF TWO */ -#define PH7_KEYWORD_ELSE 0x8000000 /* else: MUST BE A POWER OF TWO */ +#define PH7_KEYWORD_ELSE 12 /* else */ #define PH7_KEYWORD_IF 13 /* if */ #define PH7_KEYWORD_FINAL 14 /* final */ -#define PH7_KEYWORD_LIST 15 /* list */ #define PH7_KEYWORD_STATIC 16 /* static */ #define PH7_KEYWORD_CASE 17 /* case */ #define PH7_KEYWORD_SELF 18 /* self */ -#define PH7_KEYWORD_FUNCTION 19 /* function */ #define PH7_KEYWORD_NAMESPACE 20 /* namespace */ #define PH7_KEYWORD_CLONE 0x80 /* clone: MUST BE A POWER OF TWO */ #define PH7_KEYWORD_NEW 0x100 /* new: MUST BE A POWER OF TWO */ @@ -1578,8 +1578,6 @@ enum ph7_expr_id { #define PH7_KEYWORD_USING 24 /* using */ #define PH7_KEYWORD_WHILE 26 /* while */ #define PH7_KEYWORD_EVAL 27 /* eval */ -#define PH7_KEYWORD_VAR 28 /* var */ -#define PH7_KEYWORD_ARRAY 0x200 /* array: MUST BE A POWER OF TWO */ #define PH7_KEYWORD_VIRTUAL 29 /* virtual */ #define PH7_KEYWORD_TRY 30 /* try */ #define PH7_KEYWORD_DEFAULT 31 /* default */ @@ -1590,9 +1588,7 @@ enum ph7_expr_id { #define PH7_KEYWORD_FINALLY 36 /* finally */ #define PH7_KEYWORD_IMPLEMENTS 39 /* implements */ #define PH7_KEYWORD_INCLUDE 41 /* include */ -#define PH7_KEYWORD_EMPTY 42 /* empty */ #define PH7_KEYWORD_INSTANCEOF 0x400 /* instanceof: MUST BE A POWER OF TWO */ -#define PH7_KEYWORD_ISSET 43 /* isset */ #define PH7_KEYWORD_PARENT 44 /* parent */ #define PH7_KEYWORD_PRIVATE 45 /* private */ #define PH7_KEYWORD_FOR 48 /* for */ @@ -1603,7 +1599,6 @@ enum ph7_expr_id { #define PH7_KEYWORD_CATCH 53 /* catch */ #define PH7_KEYWORD_RETURN 54 /* return */ #define PH7_KEYWORD_BREAK 55 /* break */ -#define PH7_KEYWORD_UNSET 0x800 /* unset: MUST BE A POWER OF TWO */ #define PH7_KEYWORD_VOID 0x1000 /* void: MUST BE A POWER OF TWO */ #define PH7_KEYWORD_CHAR 0x2000 /* char: MUST BE A POWER OF TWO */ #define PH7_KEYWORD_BOOL 0x4000 /* bool: MUST BE A POWER OF TWO */ @@ -1614,6 +1609,8 @@ enum ph7_expr_id { #define PH7_KEYWORD_CALLBACK 0x80000 /* callback: MUST BE A POWER OF TWO */ #define PH7_KEYWORD_RESOURCE 0x100000 /* resource: MUST BE A POWER OF TWO */ #define PH7_KEYWORD_MIXED 0x200000 /* mixed: MUST BE A POWER OF TWO */ + +#define PH7_KEYWORD_TYPEDEF (PH7_KEYWORD_VOID|PH7_KEYWORD_CHAR|PH7_KEYWORD_BOOL|PH7_KEYWORD_INT|PH7_KEYWORD_FLOAT|PH7_KEYWORD_STRING|PH7_KEYWORD_OBJECT|PH7_KEYWORD_CALLBACK|PH7_KEYWORD_RESOURCE|PH7_KEYWORD_MIXED) /* JSON encoding/decoding related definition */ enum json_err_code { JSON_ERROR_NONE = 0, /* No error has occurred. */ @@ -1624,7 +1621,7 @@ enum json_err_code { JSON_ERROR_UTF8 /* Malformed UTF-8 characters */ }; /* memobj.c function prototypes */ -PH7_PRIVATE sxi32 PH7_MemObjDump(SyBlob *pOut, ph7_value *pObj, int ShowType, int nTab, int nDepth, int isRef); +PH7_PRIVATE sxi32 PH7_MemObjDump(SyBlob *pOut, ph7_value *pObj, int ShowType, int nTab, int nDepth); PH7_PRIVATE const char *PH7_MemObjTypeDump(ph7_value *pVal); PH7_PRIVATE sxi32 PH7_MemObjAdd(ph7_value *pObj1, ph7_value *pObj2, int bAddStore); PH7_PRIVATE sxi32 PH7_MemObjCmp(ph7_value *pObj1, ph7_value *pObj2, int bStrict, int iNest); @@ -1646,10 +1643,13 @@ PH7_PRIVATE sxi32 PH7_MemObjIsEmpty(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToHashmap(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToObject(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToString(ph7_value *pObj); -PH7_PRIVATE sxi32 PH7_MemObjToNull(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToReal(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToInteger(ph7_value *pObj); PH7_PRIVATE sxi32 PH7_MemObjToBool(ph7_value *pObj); +PH7_PRIVATE sxi32 PH7_MemObjToCallback(ph7_value *pObj); +PH7_PRIVATE sxi32 PH7_MemObjToResource(ph7_value *pObj); +PH7_PRIVATE sxi32 PH7_CheckVarCompat(ph7_value *pObj, int nType); +PH7_PRIVATE sxi32 PH7_MemObjSafeStore(ph7_value *pSrc, ph7_value *pDest); PH7_PRIVATE sxi64 PH7_TokenValueToInt64(SyString *pData); /* lex.c function prototypes */ PH7_PRIVATE sxi32 PH7_TokenizeRawText(const char *zInput, sxu32 nLen, SySet *pOut); @@ -1719,7 +1719,6 @@ PH7_PRIVATE sxi32 PH7_CompileLiteral(ph7_gen_state *pGen, sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileSimpleString(ph7_gen_state *pGen, sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileString(ph7_gen_state *pGen, sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen, sxi32 iCompileFlag); -PH7_PRIVATE sxi32 PH7_CompileList(ph7_gen_state *pGen, sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_CompileClosure(ph7_gen_state *pGen, sxi32 iCompileFlag); PH7_PRIVATE sxi32 PH7_InitCodeGenerator(ph7_vm *pVm, ProcConsumer xErr, void *pErrData); PH7_PRIVATE sxi32 PH7_ResetCodeGenerator(ph7_vm *pVm, ProcConsumer xErr, void *pErrData); @@ -1736,7 +1735,6 @@ PH7_PRIVATE sxi32 PH7_HashmapRelease(ph7_hashmap *pMap, int FreeDS); PH7_PRIVATE void PH7_HashmapUnref(ph7_hashmap *pMap); PH7_PRIVATE sxi32 PH7_HashmapLookup(ph7_hashmap *pMap, ph7_value *pKey, ph7_hashmap_node **ppNode); PH7_PRIVATE sxi32 PH7_HashmapInsert(ph7_hashmap *pMap, ph7_value *pKey, ph7_value *pVal); -PH7_PRIVATE sxi32 PH7_HashmapInsertByRef(ph7_hashmap *pMap, ph7_value *pKey, sxu32 nRefIdx); PH7_PRIVATE sxi32 PH7_HashmapUnion(ph7_hashmap *pLeft, ph7_hashmap *pRight); PH7_PRIVATE void PH7_HashmapUnlinkNode(ph7_hashmap_node *pNode, int bRestore); PH7_PRIVATE sxi32 PH7_HashmapDup(ph7_hashmap *pSrc, ph7_hashmap *pDest); @@ -1748,6 +1746,7 @@ PH7_PRIVATE void PH7_HashmapExtractNodeKey(ph7_hashmap_node *pNode, ph7_value *p PH7_PRIVATE void PH7_RegisterHashmapFunctions(ph7_vm *pVm); PH7_PRIVATE sxi32 PH7_HashmapDump(SyBlob *pOut, ph7_hashmap *pMap, int ShowType, int nTab, int nDepth); PH7_PRIVATE sxi32 PH7_HashmapWalk(ph7_hashmap *pMap, int (*xWalk)(ph7_value *, ph7_value *, void *), void *pUserData); +PH7_PRIVATE sxi32 PH7_HashmapCast(ph7_value *pObj, sxi32 nType); PH7_PRIVATE int PH7_HashmapValuesToSet(ph7_hashmap *pMap, SySet *pOut); /* builtin.c function prototypes */ PH7_PRIVATE sxi32 PH7_InputFormat(int (*xConsumer)(ph7_context *, const char *, int, void *), @@ -1760,7 +1759,7 @@ PH7_PRIVATE sxi32 PH7_ParseIniString(ph7_context *pCtx, const char *zIn, sxu32 n /* oo.c function prototypes */ PH7_PRIVATE ph7_class_info *PH7_NewClassInfo(ph7_vm *pVm, const SyString *pName); PH7_PRIVATE ph7_class *PH7_NewRawClass(ph7_vm *pVm, const SyString *pName); -PH7_PRIVATE ph7_class_attr *PH7_NewClassAttr(ph7_vm *pVm, const SyString *pName, sxu32 nLine, sxi32 iProtection, sxi32 iFlags); +PH7_PRIVATE ph7_class_attr *PH7_NewClassAttr(ph7_vm *pVm, const SyString *pName, sxu32 nLine, sxi32 iProtection, sxi32 iFlags, sxu32 nType); PH7_PRIVATE ph7_class_method *PH7_NewClassMethod(ph7_vm *pVm, ph7_class *pClass, const SyString *pName, sxu32 nLine, sxi32 iProtection, sxi32 iFlags, sxi32 iFuncFlags); PH7_PRIVATE ph7_class_method *PH7_ClassExtractMethod(ph7_class *pClass, const char *zName, sxu32 nByte); @@ -1769,7 +1768,7 @@ PH7_PRIVATE sxi32 PH7_ClassInstallAttr(ph7_class *pClass, ph7_class_attr *pAttr) PH7_PRIVATE sxi32 PH7_ClassInstallMethod(ph7_class *pClass, ph7_class_method *pMeth); PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_vm *pVm, ph7_class *pSub, ph7_class *pBase); PH7_PRIVATE sxi32 PH7_ClassInterfaceInherit(ph7_class *pSub, ph7_class *pBase); -PH7_PRIVATE sxi32 PH7_ClassImplement(ph7_class *pMain, ph7_class *pInterface); +PH7_PRIVATE sxi32 PH7_ClassImplement(ph7_vm *pVm, ph7_class *pMain, ph7_class *pInterface); PH7_PRIVATE ph7_class_instance *PH7_NewClassInstance(ph7_vm *pVm, ph7_class *pClass); PH7_PRIVATE ph7_class_instance *PH7_CloneClassInstance(ph7_class_instance *pSrc); PH7_PRIVATE sxi32 PH7_ClassInstanceCmp(ph7_class_instance *pLeft, ph7_class_instance *pRight, int bStrict, int iNest); diff --git a/modules/json/json.c b/modules/json/json.c index 337450c..26e4e76 100644 --- a/modules/json/json.c +++ b/modules/json/json.c @@ -151,7 +151,7 @@ static sxi32 VmJsonEncode( ph7_context *pCtx = pData->pCtx; int iFlags = pData->iFlags; int nByte; - if(ph7_value_is_null(pIn) || ph7_value_is_resource(pIn)) { + if(ph7_value_is_void(pIn) || ph7_value_is_resource(pIn)) { /* null */ ph7_result_string(pCtx, "null", (int)sizeof("null") - 1); } else if(ph7_value_is_bool(pIn)) { @@ -620,7 +620,7 @@ static sxi32 VmJsonDecode( /* Reflect the JSON image */ if(pDecoder->pIn->nType & JSON_TK_NULL) { /* Nullify the value.*/ - ph7_value_null(pWorker); + ph7_value_void(pWorker); } else if(pDecoder->pIn->nType & (JSON_TK_TRUE | JSON_TK_FALSE)) { /* Boolean value */ ph7_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0); diff --git a/tests/anon_filter.aer b/tests/anon_filter.aer new file mode 100644 index 0000000..bf94872 --- /dev/null +++ b/tests/anon_filter.aer @@ -0,0 +1,28 @@ +class Program { + private callback $condition = bool(int $x) { return ($x > 100); }; + private int[] $numbers = {34, 56, 22, 1, 5, 67, 897, 123, 55, 101}; + + int[] filter(callback $condition, int[] $numbers) { + int $len = sizeof($numbers); + int[] $filtered; + for(int $i = 0; $i < $len; $i++) { + if($condition($numbers[$i])) { + $filtered[] = $numbers[$i]; + } + } + return $filtered; + } + + void main() { + int[] $filtered = $this->filter($this->condition, $this->numbers); + var_dump($filtered); + } + +} + + +/* + * Should output + +Array ( [0] => 897 [1] => 123 ) +*/ diff --git a/tests/anon_filter.exp b/tests/anon_filter.exp new file mode 100644 index 0000000..9e58c8a --- /dev/null +++ b/tests/anon_filter.exp @@ -0,0 +1,8 @@ +array(int, 3) { + [0] => + int(897) + [1] => + int(123) + [2] => + int(101) + } diff --git a/tests/arab_to_roman.aer b/tests/arab_to_roman.aer index b5a8cfa..df56df8 100644 --- a/tests/arab_to_roman.aer +++ b/tests/arab_to_roman.aer @@ -1,21 +1,22 @@ class Program { - private function num2Roman($num) { - $n = intval($num); - $result = ''; - $lookup = array('M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400, + private string num2Roman(int $num) { + int $n = $num; + string $result = ''; + int[] $lookup = {'M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400, 'C' => 100, 'XC' => 90, 'L' => 50, 'XL' => 40, - 'X' => 10, 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1); + 'X' => 10, 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1}; + int $matches; foreach($lookup as $roman => $value) { - $matches = intval($n / $value); + $matches = (int) ($n / $value); $result += str_repeat($roman, $matches); $n = $n % $value; } return $result; } - public function main() { + public void main() { print(' 7 => ' + $this->num2Roman(7) + "\n"); print(' 9 => ' + $this->num2Roman(9) + "\n"); print(' 11 => ' + $this->num2Roman(11) + "\n"); diff --git a/tests/assertion_test.aer b/tests/assertion_test.aer new file mode 100644 index 0000000..97b2d52 --- /dev/null +++ b/tests/assertion_test.aer @@ -0,0 +1,19 @@ +class Program { + + void test_assert(mixed $param) { + assert("is_bool($param);"); + } + + void main() { + callback $assert_fail = void(string $file, int $line, string $code) { + print("Assertion failed ...\n"); + }; + assert_options(ASSERT_ACTIVE, true); + assert_options(ASSERT_BAIL, false); + assert_options(ASSERT_WARNING, false); + assert_options(ASSERT_CALLBACK, $assert_fail); + $this->test_assert(true); + $this->test_assert(1); + } + +} diff --git a/tests/assertion_test.exp b/tests/assertion_test.exp new file mode 100644 index 0000000..8babf68 --- /dev/null +++ b/tests/assertion_test.exp @@ -0,0 +1 @@ +Assertion failed ... diff --git a/tests/bin_format.aer b/tests/bin_format.aer new file mode 100644 index 0000000..5157ec8 --- /dev/null +++ b/tests/bin_format.aer @@ -0,0 +1,17 @@ +class Program { + + string fmt_binary(int $x, int $numbits = 8) { + string $bin; + string $rtnval = ''; + $bin = decbin($x); + $bin = substr(str_repeat(0, $numbits), 0, $numbits - strlen($bin)) + $bin; + for($x = 0; $x < $numbits / 4; $x++) { + $rtnval += ' ' + substr($bin, $x * 4, 4); + } + return ltrim($rtnval); + } + + void main() { + var_dump($this->fmt_binary(2541)); + } +} diff --git a/tests/bin_format.exp b/tests/bin_format.exp new file mode 100644 index 0000000..78a4b4b --- /dev/null +++ b/tests/bin_format.exp @@ -0,0 +1 @@ +string(9 '0000 1001') diff --git a/tests/brainfuck_interpreter.aer b/tests/brainfuck_interpreter.aer new file mode 100644 index 0000000..7661499 --- /dev/null +++ b/tests/brainfuck_interpreter.aer @@ -0,0 +1,107 @@ +class Brainfuck { + private string $code; + private int $code_pointer; + private int[] $cells; + private int $pointer; + private string $input; + private int $input_pointer; + private int[] $buffer; + private string $output; + + public void __construct(string $code, string $input = NULL) { + $this->code = $code; + $this->input = $input; + } + + public void run() { + while ($this->code_pointer < strlen($this->code)) { + $this->interpret($this->code[$this->code_pointer]); + $this->code_pointer++; + } + print($this->output); + } + + private void interpret(string $command) { + if(!$this->cells[$this->pointer]) { + $this->cells[$this->pointer] = 0; + } + switch ($command) { + case '>' : + $this->pointer++; + break; + case '<' : + $this->pointer--; + break; + case '+' : + $this->cells[$this->pointer]++; + if($this->cells[$this->pointer] > 255) { + $this->cells[$this->pointer] = 0; + } + break; + case '-' : + $this->cells[$this->pointer]--; + if($this->cells[$this->pointer] < 0) { + $this->cells[$this->pointer] = 255; + } + break; + case '.' : + $this->output += chr($this->cells[$this->pointer]); + break; + case ',' : + $this->cells[$this->pointer] = $this->input[$this->input_pointer] ? ord($this->input[$this->input_pointer]) : 0; + $this->input_pointer++; + break; + case '[' : + if($this->cells[$this->pointer] == 0) { + int $delta = 1; + while($delta && $this->code_pointer++ < strlen($this->code)) { + switch ($this->code[$this->code_pointer]) { + case '[' : + $delta++; + break; + case ']' : + $delta--; + break; + } + } + } else { + $this->buffer[] = $this->code_pointer; + } + break; + case ']' : + $this->code_pointer = array_pop($this->buffer) - 1; + } + } + +} + + +class Program { + + void main() { + object $bf; + string $code; + resource $dir; + string $file; + string[] $files; + string $input; + $dir = opendir('tests/data/brainfuck'); + while($file = readdir($dir)) { + if($file == '.' || $file == '..') { + continue; + } + $files[] = $file; + } + sort($files); + unset($file); + foreach($files as $file) { + print('Executing "' + $file + '"' + "\n"); + $code = file_get_contents('tests/data/brainfuck/' + $file); + $bf = new Brainfuck($code, $input); + $bf->run(); + print("\n"); + } + closedir($dir); + } + +} diff --git a/tests/brainfuck_interpreter.exp b/tests/brainfuck_interpreter.exp new file mode 100644 index 0000000..3cd399c --- /dev/null +++ b/tests/brainfuck_interpreter.exp @@ -0,0 +1,39 @@ +Executing "array.bf" +EC +Executing "hello_world.bf" +Hello World! + +Executing "sierpinski.bf" + * + * * + * * + * * * * + * * + * * * * + * * * * + * * * * * * * * + * * + * * * * + * * * * + * * * * * * * * + * * * * + * * * * * * * * + * * * * * * * * + * * * * * * * * * * * * * * * * + * * + * * * * + * * * * + * * * * * * * * + * * * * + * * * * * * * * + * * * * * * * * + * * * * * * * * * * * * * * * * + * * * * + * * * * * * * * + * * * * * * * * + * * * * * * * * * * * * * * * * + * * * * * * * * + * * * * * * * * * * * * * * * * + * * * * * * * * * * * * * * * * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + diff --git a/tests/callback_function.aer b/tests/callback_function.aer new file mode 100644 index 0000000..f0992f0 --- /dev/null +++ b/tests/callback_function.aer @@ -0,0 +1,15 @@ +class Program { + + void main(string[] $args) { + callback $y = void() { + callback $a = 'printf'; + $a("I'm alive\n"); + var_dump($a); + }; + $y(); + var_dump($y); + string $a = 'printf'; + var_dump($a); + } + +} diff --git a/tests/callback_function.exp b/tests/callback_function.exp new file mode 100644 index 0000000..5a63841 --- /dev/null +++ b/tests/callback_function.exp @@ -0,0 +1,4 @@ +I'm alive +callback(6 'printf') +callback(11 '{closure_1}') +string(6 'printf') diff --git a/tests/center_text.aer b/tests/center_text.aer new file mode 100644 index 0000000..a231121 --- /dev/null +++ b/tests/center_text.aer @@ -0,0 +1,28 @@ +class Program { + + private string center_text(string $word){ + int $tot_width = 30; + char $symbol = '-'; + int $middle = (int) round($tot_width/2); + int $length_word = strlen($word); + int $middle_word = (int) round($length_word / 2); + int $last_position = $middle + $middle_word; + int $number_of_spaces = $middle - $middle_word; + string $result = sprintf("%'{$symbol}{$last_position}s", $word); + for(int $i = 0; $i < $number_of_spaces; $i++) { + $result += "$symbol"; + } + return $result; + } + + void main() { + string $str = 'Example text'; + print($this->center_text($str)); + } + +} + + +/* Should output +---------------This is some text------------ +*/ diff --git a/tests/center_text.exp b/tests/center_text.exp new file mode 100644 index 0000000..5bce2f6 --- /dev/null +++ b/tests/center_text.exp @@ -0,0 +1 @@ +---------Example text--------- diff --git a/tests/class_instance_arg.aer b/tests/class_instance_arg.aer new file mode 100644 index 0000000..fea973e --- /dev/null +++ b/tests/class_instance_arg.aer @@ -0,0 +1,19 @@ +class Test1 { +} + +class Test2 extends Test1 { +} + +class Program { + object $t; + + void test(Test1 $t = new Test2) { + $this->t = $t; + var_dump($this->t); + } + + void main() { + $this->test(); + } + +} diff --git a/tests/class_instance_arg.exp b/tests/class_instance_arg.exp new file mode 100644 index 0000000..08b5db2 --- /dev/null +++ b/tests/class_instance_arg.exp @@ -0,0 +1,2 @@ +object(Test2) { + } diff --git a/tests/closure_event.aer b/tests/closure_event.aer new file mode 100644 index 0000000..12627b3 --- /dev/null +++ b/tests/closure_event.aer @@ -0,0 +1,41 @@ +class Button { + public callback $OnBeforeClick; + public callback $OnAfterClick; + public string $Name; + + public void __construct() { + $this->Name = 'MyButton'; + } + + public void Click() { + $this->DoBeforeClick(); + print('Click!'); + $this->DoAfterClick(); + } + + private void DoBeforeClick() { + if($this->OnBeforeClick) { + callback $event = $this->OnBeforeClick; + $event($this); + } + } + + private void DoAfterClick() { + if($this->OnAfterClick) { + callback $event = $this->OnAfterClick; + $event($this); + } + } + +} + +class Program { + + void main() { + object $MyWidget = new Button(); + $MyWidget->OnBeforeClick = void(object $Sender) { print($Sender->Name + ' (Before Click)'); }; + $MyWidget->OnAfterClick = void(object $Sender) { print($Sender->Name + ' (After Click)'); }; + $MyWidget->Click(); + } + +} diff --git a/tests/closure_event.exp b/tests/closure_event.exp new file mode 100644 index 0000000..750a77a --- /dev/null +++ b/tests/closure_event.exp @@ -0,0 +1 @@ +MyButton (Before Click)Click!MyButton (After Click) diff --git a/tests/closure_test.aer b/tests/closure_test.aer new file mode 100644 index 0000000..6a753a4 --- /dev/null +++ b/tests/closure_test.aer @@ -0,0 +1,36 @@ +class Operations { + + public callback ops(int $x, int $y, string $op){ + switch($op) { + case 'ADD': + return int() using ($x, $y) { + return $x + $y; + }; + break; + case 'SUB': + return int() using ($x, $y) { + return $x - $y; + }; + break; + default: + return string() { + return 'Operation is not supported by class.'; + }; + } + } +} + +class Program { + + void main() { + callback $fn; + object $op = new Operations(); + $fn = $op->ops(6, 7, 'ADD'); + print($fn() + "\n"); + $fn = $op->ops(6, 2, 'SUB'); + print($fn() + "\n"); + $fn = $op->ops(6, 7, 'MUL'); + print($fn() + "\n"); + } + +} diff --git a/tests/closure_test.exp b/tests/closure_test.exp new file mode 100644 index 0000000..3ca1c19 --- /dev/null +++ b/tests/closure_test.exp @@ -0,0 +1,3 @@ +13 +4 +Operation is not supported by class. diff --git a/tests/complex_expressions.aer b/tests/complex_expressions.aer new file mode 100644 index 0000000..6f930fd --- /dev/null +++ b/tests/complex_expressions.aer @@ -0,0 +1,43 @@ +class Program { + private const MY_CONST = 12 ^ 0x3FD; + private callback $callback_test = void(){ print("Welcome everyone!\n");}; + private static int $value = 4 + 4 * 4; + + void callable(callback $callback = void(){ print("Hello world!\n");}) { + $callback(); + } + + void complexArgs(string $name = 'AerScript' + $this->someStr(), int $age = 10*2+5) { + print("Name = $name\n"); + print("Age = $age\n"); + } + + string someStr() { + return 'ABC'; + } + + bool someTrue() { + return true; + } + + bool someFalse() { + return false; + } + + int main() { + static float $f = 4 + 2.4 * 9.1; + var_dump($this->MY_CONST); + var_dump($this->callback_test); + var_dump($this->value); + var_dump($f); + $this->complexArgs(); + $this->complexArgs('Me'); + $this->callable(); + $this->callable(void(){ print("Welcome guest!\n");}); + $this->callable($this->callback_test); + $this->someTrue() || print("someTrue() failed\n"); + $this->someFalse() || print("someFalse() failed\n"); + return 0; + } + +} diff --git a/tests/complex_expressions.exp b/tests/complex_expressions.exp new file mode 100644 index 0000000..4f1520b --- /dev/null +++ b/tests/complex_expressions.exp @@ -0,0 +1,12 @@ +int(1009) +callback(11 '{closure_1}') +int(20) +float(25.84) +Name = AerScriptABC +Age = 25 +Name = Me +Age = 25 +Hello world! +Welcome guest! +Welcome everyone! +someFalse() failed diff --git a/tests/complex_expressions2.aer b/tests/complex_expressions2.aer new file mode 100644 index 0000000..759e79c --- /dev/null +++ b/tests/complex_expressions2.aer @@ -0,0 +1,16 @@ +class Program { + + void test(int $a = (int(int $a, int $b, int $c){return $a+$b+$c;})(14, 10+2, 15), int $b = 0, int $c = 98) { + print($a + PHP_EOL); + print($b + PHP_EOL); + print($c + PHP_EOL); + } + + void main() { + $this->test(); + $this->test(512); + $this->test(1024, 32); + $this->test(1000, 2000, 4000); + } + +} diff --git a/tests/complex_expressions2.exp b/tests/complex_expressions2.exp new file mode 100644 index 0000000..856b5af --- /dev/null +++ b/tests/complex_expressions2.exp @@ -0,0 +1,12 @@ +41 +0 +98 +512 +0 +98 +1024 +32 +98 +1000 +2000 +4000 diff --git a/tests/data/brainfuck/array.bf b/tests/data/brainfuck/array.bf new file mode 100644 index 0000000..3a7a503 --- /dev/null +++ b/tests/data/brainfuck/array.bf @@ -0,0 +1,22 @@ +>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++> +>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++> +>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++> +>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++> +>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++> +<<<<<<<<<< +first put the desired index in front of the array +++++ +we are done if there is a zero in the current tag cell +[ +decrement the current tag cell +- +move the value in the current tag cell to the next if it is not zero +[>>+<<-] +move to next tag cell +>> +we found it: print the desired element +]>. +now print another +<<<<<<<<< +++ +[-[>>+<<-]>>]>. \ No newline at end of file diff --git a/tests/data/brainfuck/hello_world.bf b/tests/data/brainfuck/hello_world.bf new file mode 100644 index 0000000..265e751 --- /dev/null +++ b/tests/data/brainfuck/hello_world.bf @@ -0,0 +1 @@ +++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. diff --git a/tests/data/brainfuck/sierpinski.bf b/tests/data/brainfuck/sierpinski.bf new file mode 100644 index 0000000..d638652 --- /dev/null +++ b/tests/data/brainfuck/sierpinski.bf @@ -0,0 +1,5 @@ +++++++++[>+>++++<<-]>++>>+<[-[>>+<<-]+>>]>+[ + -<<<[ + ->[+[-]+>++>>>-<<]<[<]>>++++++[<<+++++>>-]+<<++.[-]<< + ]>.>+[>>]>+ +] \ No newline at end of file diff --git a/tests/debug_backtrace.aer b/tests/debug_backtrace.aer index 4c50763..c715c14 100644 --- a/tests/debug_backtrace.aer +++ b/tests/debug_backtrace.aer @@ -1,12 +1,12 @@ class Program { - function main() { + void main() { $this->b($this->a('First A'), $this->a('Second A'), $this->a('Third A')); } - function a($p) { - $backtrace = debug_backtrace(); - if(isset($backtrace[0]['args'])) { + string a(string $p) { + mixed[] $backtrace = debug_backtrace(); + if(array_key_exists('args', $backtrace[0])) { var_export($backtrace[0]['args']); } else { print("Cannot aquire arguments\n"); @@ -14,7 +14,7 @@ class Program { return $p; } - function b($p1, $p2, $p3) { + void b(string $p1, string $p2, string $p3) { print("$p1, $p2, $p3"); } diff --git a/tests/exception_handler.aer b/tests/exception_handler.aer new file mode 100644 index 0000000..3d3a82f --- /dev/null +++ b/tests/exception_handler.aer @@ -0,0 +1,33 @@ +class ExceptionHandler { + + public static void printException(Exception $e) { + print('Uncaught ' + get_class($e) + ', code: ' + $e->getCode() + "\nMessage: " + htmlentities($e->getMessage()) + "\n"); + } + + public static void handleException(Exception $e) { + self::printException($e); + } + +} + +class NewException extends Exception { +} + +class Program { + + void main() { + callback $handler = void(Exception $e) { + ExceptionHandler::handleException($e); + }; + set_exception_handler($handler); + try { + throw new NewException("Catch me once", 1); + } catch(Exception $e) { + ExceptionHandler::handleException($e); + } + throw new Exception("Catch me twice", 2); + } + +} + + diff --git a/tests/exception_handler.exp b/tests/exception_handler.exp new file mode 100644 index 0000000..8310cc2 --- /dev/null +++ b/tests/exception_handler.exp @@ -0,0 +1,4 @@ +Uncaught NewException, code: 1 +Message: Catch me once +Uncaught Exception, code: 2 +Message: Catch me twice diff --git a/tests/extended_closure.aer b/tests/extended_closure.aer new file mode 100644 index 0000000..a84f94a --- /dev/null +++ b/tests/extended_closure.aer @@ -0,0 +1,24 @@ +class Dog { + public string $name; + public string $color; + + public void __construct(string $name, string $color) { + $this->name = $name; + $this->color = $color; + } + + public callback greet(string $greeting) { + return void() using ($greeting) { + print("$greeting, I am a {$this->color} dog named {$this->name}\n"); + }; + } +} + +class Program { + + void main() { + object $dog = new Dog('Alex', 'red'); + callback $c = $dog->greet('Hello'); + $c(); + } +} diff --git a/tests/extended_closure.exp b/tests/extended_closure.exp new file mode 100644 index 0000000..b6228a0 --- /dev/null +++ b/tests/extended_closure.exp @@ -0,0 +1 @@ +Hello, I am a red dog named Alex diff --git a/tests/factorial_test.aer b/tests/factorial_test.aer new file mode 100644 index 0000000..e99b87a --- /dev/null +++ b/tests/factorial_test.aer @@ -0,0 +1,15 @@ +class Program { + + int factorial(int $num) { + if($num == 0 || $num == 1) + return 1; + else + return $num * $this->factorial($num - 1); + } + + void main() { + int $num = 7; + print('Factorial of ', $num, ' is ', $this->factorial($num), '.'); + } + +} diff --git a/tests/factorial_test.exp b/tests/factorial_test.exp new file mode 100644 index 0000000..279b35a --- /dev/null +++ b/tests/factorial_test.exp @@ -0,0 +1 @@ +Factorial of 7 is 5040. diff --git a/tests/factory_objects.aer b/tests/factory_objects.aer new file mode 100644 index 0000000..c0a1c86 --- /dev/null +++ b/tests/factory_objects.aer @@ -0,0 +1,29 @@ +class Circle { + void draw() { + print("Circle\n"); + } +} + +class Square { + void draw() { + print("Square\n"); + } +} + +class Program { + + object ShapeFactoryMethod(string $shape) { + switch ($shape) { + case "Circle": + return new Circle(); + case "Square": + return new Square(); + } + } + + void main() { + $this->ShapeFactoryMethod("Circle")->draw(); + $this->ShapeFactoryMethod("Square")->draw(); + } + +} diff --git a/tests/factory_objects.exp b/tests/factory_objects.exp new file mode 100644 index 0000000..83a280e --- /dev/null +++ b/tests/factory_objects.exp @@ -0,0 +1,2 @@ +Circle +Square diff --git a/tests/hello_world.aer b/tests/hello_world.aer index 83c13b3..bc2918f 100644 --- a/tests/hello_world.aer +++ b/tests/hello_world.aer @@ -1,7 +1,12 @@ +#!/usr/local/bin/aer class Program { - public function main() { - print('Hello world!'); + /* + * Program entry point. + * It does not take any arguments. + */ + public void main() { + print('Hello world!'); // single line comment } } diff --git a/tests/int2alpha_test.aer b/tests/int2alpha_test.aer new file mode 100644 index 0000000..7f4a467 --- /dev/null +++ b/tests/int2alpha_test.aer @@ -0,0 +1,26 @@ +class Program { + + 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); + } + return $r; + } + + int alpha2num(string $a) { + int $r = 0; + int $l = strlen($a); + for(int $i = 0; $i < $l; $i++) { + $r += pow(26, $i) * (ord($a[$l - $i - 1]) - 0x40); + } + return (int) $r - 1; + } + + void main() { + import('math'); + var_dump($this->alpha2num("Salut"), $this->num2alpha(1723), $this->num2alpha(9854), $this->alpha2num("Base64")); + } + +} diff --git a/tests/int2alpha_test.exp b/tests/int2alpha_test.exp new file mode 100644 index 0000000..882e706 --- /dev/null +++ b/tests/int2alpha_test.exp @@ -0,0 +1,4 @@ +int(9293725) +string(3 'BNH') +string(3 'NOA') +int(39764075) diff --git a/tests/interface_test.aer b/tests/interface_test.aer new file mode 100644 index 0000000..c899ea7 --- /dev/null +++ b/tests/interface_test.aer @@ -0,0 +1,42 @@ +interface iVehicle { + + public void drive(); + public void stop(); + +} + +class Car implements iVehicle { + + public void drive() { + print("Driving...\n"); + } + + public void stop() { + print("Stopping...\n"); + } + +} + +class MyCar extends Car { + + public void drive() { + print("Driving my car...\n"); + } + +} + +class Program { + + public void driveCar(iVehicle $vehicle) { + $vehicle->drive(); + $vehicle->stop(); + } + + public void main() { + object $car = new Car(); + object $mycar = new MyCar(); + $this->driveCar($car); + $this->driveCar($mycar); + } + +} diff --git a/tests/interface_test.exp b/tests/interface_test.exp new file mode 100644 index 0000000..b026219 --- /dev/null +++ b/tests/interface_test.exp @@ -0,0 +1,4 @@ +Driving... +Stopping... +Driving my car... +Stopping... diff --git a/tests/ip_addr_enc_dec.aer b/tests/ip_addr_enc_dec.aer new file mode 100644 index 0000000..7ef807e --- /dev/null +++ b/tests/ip_addr_enc_dec.aer @@ -0,0 +1,22 @@ +class Program { + + string encode_ip(string $dotquad_ip) { + string[] $ip_sep = explode('.', $dotquad_ip); + return sprintf('%02x%02x%02x%02x', $ip_sep[0], $ip_sep[1], $ip_sep[2], $ip_sep[3]); + } + + string decode_ip(string $int_ip) { + string[] $hexipbang = explode('.', chunk_split($int_ip, 2, '.')); + return hexdec($hexipbang[0]) + '.' + hexdec($hexipbang[1]) + '.' + hexdec($hexipbang[2]) + '.' + hexdec($hexipbang[3]); + } + + void main() { + string $localhost = $this->encode_ip('127.0.0.1'); + print("127.0.0.1 ==> $localhost\n"); + string $router = $this->encode_ip('192.168.2.1'); + print("192.168.2.1 ==> $router\n"); + print("$localhost ==> ", $this->decode_ip($localhost) + PHP_EOL); + print("$router ==> ", $this->decode_ip($router) + PHP_EOL); + } + +} diff --git a/tests/ip_addr_enc_dec.exp b/tests/ip_addr_enc_dec.exp new file mode 100644 index 0000000..91c2428 --- /dev/null +++ b/tests/ip_addr_enc_dec.exp @@ -0,0 +1,4 @@ +127.0.0.1 ==> 7f000001 +192.168.2.1 ==> c0a80201 +7f000001 ==> 127.0.0.1 +c0a80201 ==> 192.168.2.1 diff --git a/tests/load_module.aer b/tests/load_module.aer index d2ad4ef..09a1f32 100644 --- a/tests/load_module.aer +++ b/tests/load_module.aer @@ -1,5 +1,5 @@ final class Program { - function main() { + void main() { var_dump(function_exists('dummy_function')); var_dump(import('dummy')); var_dump(function_exists('dummy_function')); diff --git a/tests/loop_do_while_test.aer b/tests/loop_do_while_test.aer new file mode 100644 index 0000000..e6641e7 --- /dev/null +++ b/tests/loop_do_while_test.aer @@ -0,0 +1,23 @@ +class Program { + + private int test() { + static int $x = 9; + return $x--; + } + + public void main() { + int $a; + do { + $a = $this->test(); + int $z = $a; + if($z == 6) { + continue; + } else if($z == 3) { + break; + } + print("$z\n"); + } while($a); + print("$a\n"); + } + +} diff --git a/tests/loop_do_while_test.exp b/tests/loop_do_while_test.exp new file mode 100644 index 0000000..a77242f --- /dev/null +++ b/tests/loop_do_while_test.exp @@ -0,0 +1,6 @@ +9 +8 +7 +5 +4 +3 diff --git a/tests/loop_for_test.aer b/tests/loop_for_test.aer new file mode 100644 index 0000000..372471c --- /dev/null +++ b/tests/loop_for_test.aer @@ -0,0 +1,20 @@ +class Program { + + public void main() { + int $c = 4; + for(int $a = 0; $a < 10; $a++) { + for(int $b = 0; $b < 10; $b++) { + int $c = 2; + if($b == 2) { + continue; + } + if($b == 6) { + break; + } + print("Hello variables: $a $b $c\n"); + } + } + print("Variable \$c: $c\n"); + } + +} diff --git a/tests/loop_for_test.exp b/tests/loop_for_test.exp new file mode 100644 index 0000000..1419a06 --- /dev/null +++ b/tests/loop_for_test.exp @@ -0,0 +1,51 @@ +Hello variables: 0 0 2 +Hello variables: 0 1 2 +Hello variables: 0 3 2 +Hello variables: 0 4 2 +Hello variables: 0 5 2 +Hello variables: 1 0 2 +Hello variables: 1 1 2 +Hello variables: 1 3 2 +Hello variables: 1 4 2 +Hello variables: 1 5 2 +Hello variables: 2 0 2 +Hello variables: 2 1 2 +Hello variables: 2 3 2 +Hello variables: 2 4 2 +Hello variables: 2 5 2 +Hello variables: 3 0 2 +Hello variables: 3 1 2 +Hello variables: 3 3 2 +Hello variables: 3 4 2 +Hello variables: 3 5 2 +Hello variables: 4 0 2 +Hello variables: 4 1 2 +Hello variables: 4 3 2 +Hello variables: 4 4 2 +Hello variables: 4 5 2 +Hello variables: 5 0 2 +Hello variables: 5 1 2 +Hello variables: 5 3 2 +Hello variables: 5 4 2 +Hello variables: 5 5 2 +Hello variables: 6 0 2 +Hello variables: 6 1 2 +Hello variables: 6 3 2 +Hello variables: 6 4 2 +Hello variables: 6 5 2 +Hello variables: 7 0 2 +Hello variables: 7 1 2 +Hello variables: 7 3 2 +Hello variables: 7 4 2 +Hello variables: 7 5 2 +Hello variables: 8 0 2 +Hello variables: 8 1 2 +Hello variables: 8 3 2 +Hello variables: 8 4 2 +Hello variables: 8 5 2 +Hello variables: 9 0 2 +Hello variables: 9 1 2 +Hello variables: 9 3 2 +Hello variables: 9 4 2 +Hello variables: 9 5 2 +Variable $c: 4 diff --git a/tests/loop_while_test.aer b/tests/loop_while_test.aer new file mode 100644 index 0000000..031a913 --- /dev/null +++ b/tests/loop_while_test.aer @@ -0,0 +1,23 @@ +class Program { + + private int test() { + static int $x = 9; + return $x--; + } + + public void main() { + int $a; + int $b = 2; + while($a = $this->test()) { + int $b = $a; + if($b == 6) { + continue; + } else if($b == 3) { + break; + } + print("$b\n"); + } + print("$b\n"); + } + +} diff --git a/tests/loop_while_test.exp b/tests/loop_while_test.exp new file mode 100644 index 0000000..222651f --- /dev/null +++ b/tests/loop_while_test.exp @@ -0,0 +1,6 @@ +9 +8 +7 +5 +4 +2 diff --git a/tests/luhn_verify.aer b/tests/luhn_verify.aer new file mode 100644 index 0000000..861d927 --- /dev/null +++ b/tests/luhn_verify.aer @@ -0,0 +1,44 @@ +class Luhn { + private string $number; + + string getNumber() { + return $this->number; + } + + void setNumber(string $number) { + $this->number = $number; + } + + bool validate() { + string $sum; + string $revNumber; + int $len; + + $revNumber = strrev($this->number); + $len = strlen($this->number); + for(int $i = 0; $i < $len; $i++) { + $sum += $i & 1 ? $revNumber[$i] * 2 : $revNumber[$i]; + } + return array_sum(str_split($sum)) % 10 == 0; + } +} + +class Program { + private const NUMBERS = {'3788803280', '6487308345', '5443489710530865', '5539266155200609', '4024007151066296', '4345234978'}; + + void main() { + int $i, $nums = sizeof($this->NUMBERS); + object $luhn = new Luhn(); + + while($i < $nums) { + $luhn->setNumber($this->NUMBERS[$i]); + if($luhn->validate()) { + print('The number ' + $luhn->getNumber() + ' has passed the Luhn validation.' + "\n"); + } else { + print('The number ' + $luhn->getNumber() + ' has NOT passed the Luhn validation.' + "\n"); + } + $i++; + } + } +} + diff --git a/tests/luhn_verify.exp b/tests/luhn_verify.exp new file mode 100644 index 0000000..6e8970a --- /dev/null +++ b/tests/luhn_verify.exp @@ -0,0 +1,6 @@ +The number 3788803280 has passed the Luhn validation. +The number 6487308345 has passed the Luhn validation. +The number 5443489710530865 has passed the Luhn validation. +The number 5539266155200609 has passed the Luhn validation. +The number 4024007151066296 has passed the Luhn validation. +The number 4345234978 has NOT passed the Luhn validation. diff --git a/tests/magic_method.aer b/tests/magic_method.aer new file mode 100644 index 0000000..76b888b --- /dev/null +++ b/tests/magic_method.aer @@ -0,0 +1,16 @@ +class Dog { + + void __invoke() { + print("I am a dog!\n"); + } +} + + +class Program { + + void main() { + object $dog = new Dog(); + $dog(); + } + +} diff --git a/tests/magic_method.exp b/tests/magic_method.exp new file mode 100644 index 0000000..07699fc --- /dev/null +++ b/tests/magic_method.exp @@ -0,0 +1 @@ +I am a dog! diff --git a/tests/multiple_inheritance.aer b/tests/multiple_inheritance.aer new file mode 100644 index 0000000..6fc5a52 --- /dev/null +++ b/tests/multiple_inheritance.aer @@ -0,0 +1,62 @@ +interface IntA { + + public void test_a(); + +} + +interface IntB { + + public void test_b(); + +} + +class TestA { + + public void test_a() { + print("Hello world from TestA::test_a().\n"); + } + +} + +class TestB { + + public void test_b() { + print("Hello world from TestB::test_b().\n"); + } + + +} + +class TestC { + + public void test_c() { + print("Hello world from TestC::test_c().\n"); + } + +} + +class TestD { + + public void test_a() { + print("Hello world from TestD::test_a().\n"); + } + +} + +class TestE { + + public void test_b() { + print("Hello world from TestE::test_b().\n"); + } + +} + +class Program extends TestE, TestD, TestC, TestB, TestA implements IntA, IntB { + + void main() { + $this->test_a(); + $this->test_b(); + $this->test_c(); + } + +} diff --git a/tests/multiple_inheritance.exp b/tests/multiple_inheritance.exp new file mode 100644 index 0000000..abc2844 --- /dev/null +++ b/tests/multiple_inheritance.exp @@ -0,0 +1,3 @@ +Hello world from TestD::test_a(). +Hello world from TestE::test_b(). +Hello world from TestC::test_c(). diff --git a/tests/operators_precedence.aer b/tests/operators_precedence.aer new file mode 100644 index 0000000..92a53e3 --- /dev/null +++ b/tests/operators_precedence.aer @@ -0,0 +1,20 @@ +class Program { + + string[] testArray() { + return {'Machine' => 'Turing'}; + } + + callback[] testCallback() { + return {'callme' => void() { print("Hello world\n"); }}; + } + + void main() { + callback[] $constr = {'print'}; + print($this->testArray()['Machine'] + "\n"); + $this->testCallback()['callme'](); + print(true ? "TRUE\n" : false ? "true\n" : "false\n"); + (void(string $name) { print("This should work too - $name.\n");})('AerScript'); + $constr[0]('Test finished.' + "\n"); + } + +} diff --git a/tests/operators_precedence.exp b/tests/operators_precedence.exp new file mode 100644 index 0000000..513c9a9 --- /dev/null +++ b/tests/operators_precedence.exp @@ -0,0 +1,5 @@ +Turing +Hello world +TRUE +This should work too - AerScript. +Test finished. diff --git a/tests/overloading_methods.aer b/tests/overloading_methods.aer new file mode 100644 index 0000000..aa2bfff --- /dev/null +++ b/tests/overloading_methods.aer @@ -0,0 +1,16 @@ +class Program { + + void count(int $a, int $b) { + print("Counting 2 integers: $a + $b = ", $a + $b, "\n"); + } + + void count(float $a, float $b) { + print("Counting 2 floats: $a + $b = ", $a + $b, "\n"); + } + + void main() { + $this->count(4.3, 5.7); + $this->count(6, 4); + } + +} diff --git a/tests/overloading_methods.exp b/tests/overloading_methods.exp new file mode 100644 index 0000000..bfc9055 --- /dev/null +++ b/tests/overloading_methods.exp @@ -0,0 +1,2 @@ +Counting 2 floats: 4.3 + 5.7 = 10 +Counting 2 integers: 6 + 4 = 10 diff --git a/tests/path_split.aer b/tests/path_split.aer new file mode 100644 index 0000000..487115e --- /dev/null +++ b/tests/path_split.aer @@ -0,0 +1,32 @@ +class Program { + + string[] parsePathComponents(string $path, bool $endSlash=true, bool $base=false) { + string[] $retArray; + $path = trim($path); + string $str, $temp; + char $char; + for(int $x = 0; $char = $path[$x]; $x++) { + if(!strstr('/', $char)) $temp += $char; + else if($temp) { + $str += $temp; + $retArray[$temp] = $str + ($endSlash ? '/' : ''); + $str += '/'; + $temp = ''; + } + } + ($base && $temp) ? $retArray[$temp] = $str + $temp : NULL; + return $retArray; + } + + void main() { + string $path = '/my//stupid//path/to///some/file.php'; + print_r($this->parsePathComponents($path)); + + $path = 'my/other//path/'; + print_r($this->parsePathComponents($path, false)); + + $path = '/my//other/path/to///file.php'; + print_r($this->parsePathComponents($path, true, true)); + } + +} diff --git a/tests/path_split.exp b/tests/path_split.exp new file mode 100644 index 0000000..c99eb2a --- /dev/null +++ b/tests/path_split.exp @@ -0,0 +1,32 @@ +Array(5) { + [my] => + my/ + [stupid] => + my/stupid/ + [path] => + my/stupid/path/ + [to] => + my/stupid/path/to/ + [some] => + my/stupid/path/to/some/ + } +Array(3) { + [my] => + my + [other] => + my/other + [path] => + my/other/path + } +Array(5) { + [my] => + my/ + [other] => + my/other/ + [path] => + my/other/path/ + [to] => + my/other/path/to/ + [file.php] => + my/other/path/to/file.php + } diff --git a/tests/printf_test.aer b/tests/printf_test.aer index e839044..955b91d 100644 --- a/tests/printf_test.aer +++ b/tests/printf_test.aer @@ -1,16 +1,16 @@ class Program { - private $s = 'monkey'; - private $t = 'many monkeys'; - private $n = 43951789; - private $u = -43951789; - private $c = 65; + private string $s = 'monkey'; + private string $t = 'many monkeys'; + private int $n = 43951789; + private int $u = -43951789; + private char $c = 65; - public function main() { + public void main() { $this->testMonkey(); $this->testNumbers(); } - private function testMonkey() { + private void testMonkey() { printf("[%s]\n", $this->s); printf("[%10s]\n", $this->s); printf("[%-10s]\n", $this->s); @@ -19,9 +19,10 @@ class Program { printf("[%10.10s]\n", $this->t); } - private function testNumbers() { + private void testNumbers() { printf("%%b = '%b'\n", $this->n); printf("%%c = '%c'\n", $this->c); + printf("%%d = '%d'\n", $this->c); printf("%%d = '%d'\n", $this->n); printf("%%e = '%e'\n", $this->n); printf("%%u = '%u'\n", $this->n); diff --git a/tests/printf_test.exp b/tests/printf_test.exp index a1d8c18..6b3128e 100644 --- a/tests/printf_test.exp +++ b/tests/printf_test.exp @@ -6,6 +6,7 @@ [many monke] %b = '10100111101010011010101101' %c = 'A' +%d = '65' %d = '43951789' %e = '4.395179e+07' %u = '43951789' diff --git a/tests/reference_test.aer b/tests/reference_test.aer new file mode 100644 index 0000000..6a04776 --- /dev/null +++ b/tests/reference_test.aer @@ -0,0 +1,13 @@ +class Program { + + void add_by_ref(int &$val) { + $val += 7; + } + + void main() { + int $num = 7; + $this->add_by_ref($num); + var_dump($num); + } + +} diff --git a/tests/reference_test.exp b/tests/reference_test.exp new file mode 100644 index 0000000..a7fce3e --- /dev/null +++ b/tests/reference_test.exp @@ -0,0 +1 @@ +int(14) diff --git a/tests/singleton_test.aer b/tests/singleton_test.aer index 03d9ddf..4e6ab85 100644 --- a/tests/singleton_test.aer +++ b/tests/singleton_test.aer @@ -1,32 +1,32 @@ final class Test { - private $value; + private int $value; - private function __construct() { + private void __construct() { } /* This is singleton */ - public function getInstance() { - static $instance; + public object getInstance() { + static object $instance; if(!$instance) { $instance = new Test(); } return $instance; } - public function get() { + public int get() { return $this->value; } - public function set($value = 0) { + public int set(int $value = 0) { $this->value = $value; } } final class Program { - public function main() { - $testA = Test::getInstance(); + public void main() { + object $testA = Test::getInstance(); $testA->set(5); - $testB = Test::getInstance(); + object $testB = Test::getInstance(); var_dump($testB->get()); } } /* class */ diff --git a/tests/static_var.aer b/tests/static_var.aer new file mode 100644 index 0000000..535e4ff --- /dev/null +++ b/tests/static_var.aer @@ -0,0 +1,24 @@ +class Program { + + string cycle(char $a, char $b, int $i = 0) { + static char[] $switches; + if($switches[$i]) + $switches[$i] = !$switches[$i]; + else + !($switches[$i] = true); + return ($switches[$i]) ? $a : $b; + } + + void main() { + for(int $i = 1; $i < 3; $i++) { + print($i + $this->cycle('a', 'b') + PHP_EOL); + for(int $j = 1; $j < 5; $j++) { + print(' ' + $j + $this->cycle('a', 'b', 1) + PHP_EOL); + for(int $k = 1; $k < 3; $k++) { + print(' ' + $k + $this->cycle('c', 'd', 2) + PHP_EOL); + } + } + } + } + +} diff --git a/tests/static_var.exp b/tests/static_var.exp new file mode 100644 index 0000000..7a256fb --- /dev/null +++ b/tests/static_var.exp @@ -0,0 +1,26 @@ +1a + 1a + 1c + 2d + 2b + 1c + 2d + 3a + 1c + 2d + 4b + 1c + 2d +2b + 1a + 1c + 2d + 2b + 1c + 2d + 3a + 1c + 2d + 4b + 1c + 2d diff --git a/tests/switch_block.aer b/tests/switch_block.aer new file mode 100644 index 0000000..e009a20 --- /dev/null +++ b/tests/switch_block.aer @@ -0,0 +1,20 @@ +class Program { + + void main() { + int $a = 1; + switch($a) { + case 0: + print("very bad\n"); + break; + case 1: + print("very good\n"); + case 2: + print("good\n"); + break; + default: + print("bad"); + break; + } + } + +} diff --git a/tests/switch_block.exp b/tests/switch_block.exp new file mode 100644 index 0000000..206cec8 --- /dev/null +++ b/tests/switch_block.exp @@ -0,0 +1,2 @@ +very good +good diff --git a/tests/text_normalize.aer b/tests/text_normalize.aer new file mode 100644 index 0000000..d5918bf --- /dev/null +++ b/tests/text_normalize.aer @@ -0,0 +1,21 @@ +class Program { + + string normalize(string $str) { + string[] $table = { + 'Š'=>'S', 'š'=>'s', 'Đ'=>'Dj', 'đ'=>'dj', 'Ž'=>'Z', 'ž'=>'z', 'Č'=>'C', 'č'=>'c', 'Ć'=>'C', 'ć'=>'c', + 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E', + 'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O', + 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ù'=>'U', 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'B', 'ß'=>'Ss', + 'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c', 'è'=>'e', 'é'=>'e', + 'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'o', 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o', + 'ô'=>'o', 'õ'=>'o', 'ö'=>'o', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ý'=>'y', 'ý'=>'y', 'þ'=>'b', + 'ÿ'=>'y', 'Ŕ'=>'R', 'ŕ'=>'r' + }; + return strtr($str, $table); + } + + void main() { + var_dump($this->normalize("ÿĆ Welcome ÂëÑ Žöø Ŕ")); + } + +} diff --git a/tests/text_normalize.exp b/tests/text_normalize.exp new file mode 100644 index 0000000..d84bca6 --- /dev/null +++ b/tests/text_normalize.exp @@ -0,0 +1 @@ +string(20 'yC Welcome AeN Zoo R') diff --git a/tests/tokenizer.aer b/tests/tokenizer.aer new file mode 100644 index 0000000..6d922af --- /dev/null +++ b/tests/tokenizer.aer @@ -0,0 +1,36 @@ +class StringTokenizer { + private string $token; + private string $delim; + + public void __construct(string $str, string $delim = ' ') { + $this->token = strtok($str, $delim); + $this->delim = $delim; + } + + public void __destruct() { + unset($this); + } + + public bool hasMoreTokens() { + return ($this->token != NULL); + } + + public string nextToken() { + string $current = $this->token; + $this->token = strtok($this->delim); + return $current; + } + +} + +class Program { + void main() { + string $str = "This is:@\t\n a TEST!"; + string $delim = " !@:\t\n"; + object $st = new StringTokenizer($str, $delim); + while ($st->hasMoreTokens()) { + print($st->nextToken(), "\n"); + } + unset($st); + } +} diff --git a/tests/tokenizer.exp b/tests/tokenizer.exp new file mode 100644 index 0000000..413f8b1 --- /dev/null +++ b/tests/tokenizer.exp @@ -0,0 +1,4 @@ +This +is +a +TEST diff --git a/tests/type_hinting.aer b/tests/type_hinting.aer new file mode 100644 index 0000000..03942e1 --- /dev/null +++ b/tests/type_hinting.aer @@ -0,0 +1,43 @@ +class Program { + private int $test = 7; + + void testChar(char $value) { + var_dump($value); + var_dump(is_char($value)); + } + + void testFloat(float $value) { + var_dump($value); + var_dump(is_float($value)); + } + + void testObject(object $value) { + var_dump($value); + var_dump(is_object($value)); + } + + void testString(string $value) { + var_dump($value); + var_dump(is_string($value)); + } + + void testVoid(void $value) { + var_dump($value); + var_dump(is_void($value)); + } + + void main() { + object $objval; + void $voidval; + $this->testChar('c'); + $this->testChar(NULL); + $this->testFloat(4); + $this->testFloat(56.3); + $this->testObject($objval); + $this->testObject($this); + $this->testString('sample text'); + $this->testString(NULL); + $this->testVoid($voidval); + $this->testVoid(NULL); + } +} diff --git a/tests/type_hinting.exp b/tests/type_hinting.exp new file mode 100644 index 0000000..4e9dfe8 --- /dev/null +++ b/tests/type_hinting.exp @@ -0,0 +1,23 @@ +char(c) +bool(TRUE) +char() +bool(TRUE) +float(4) +bool(TRUE) +float(56.3) +bool(TRUE) +object() +bool(TRUE) +object(Program) { + ['test'] => + int(7) + } +bool(TRUE) +string(11 'sample text') +bool(TRUE) +string(0 '') +bool(TRUE) +void(NULL) +bool(TRUE) +void(NULL) +bool(TRUE) diff --git a/tests/type_juggle.aer b/tests/type_juggle.aer index 2dd0ead..d4f920c 100644 --- a/tests/type_juggle.aer +++ b/tests/type_juggle.aer @@ -1,6 +1,7 @@ class Program { - function main() { + void main() { + mixed $foo; $foo = '0'; var_dump($foo); $foo += 2; diff --git a/tests/unicode_characters.aer b/tests/unicode_characters.aer index d164afe..1cc905e 100644 --- a/tests/unicode_characters.aer +++ b/tests/unicode_characters.aer @@ -1,7 +1,8 @@ class Unicode { - public function unicon($str, $to_uni = true) { - $cp = array('А' => 'А', 'а' => 'а', + public string unicon(string $str, bool $to_uni = true) { + string $cpp; + string[] $cp = {'А' => 'А', 'а' => 'а', "Б" => "Б", "б" => "б", "В" => "В", "в" => "в", "Г" => "Г", "г" => "г", @@ -33,7 +34,7 @@ class Unicode { "Ь" => "Ь", "ь" => "ь", "Э" => "Э", "э" => "э", "Ю" => "Ю", "ю" => "ю", - "Я" => "Я", "я" => "я"); + "Я" => "Я", "я" => "я"}; if($to_uni) { $str = strtr($str, $cp); } else { @@ -49,8 +50,8 @@ class Unicode { final class Program { - public function main() { - $unicode = new Unicode(); + public void main() { + object $unicode = new Unicode(); var_dump($unicode->unicon("ИфйжБЦ")); } diff --git a/tests/utf8_variables.aer b/tests/utf8_variables.aer index ecdeeeb..85ef3f3 100644 --- a/tests/utf8_variables.aer +++ b/tests/utf8_variables.aer @@ -1,33 +1,33 @@ class Program { - private $概要 = "AerScript Interpreter"; + private string $概要 = "AerScript Interpreter"; - public function main() { + public void main() { $this->ダウンロード(); var_dump($this->概要); var_dump($this->isUTF8('hello')); var_dump($this->isUTF8("すが、基本的な使い方は単純です。かしながら使い方を身につけていきましょう")); } - private function ダウンロード(){ + private void ダウンロード(){ print($this->概要 + "\n"); } - private function isUTF8($str) { - $b = 0; - $c = 0; - $bits = 0; - $len = strlen($str); - for($i = 0; $i < $len; $i++) { + private bool isUTF8(string $str) { + int $b = 0; + int $c = 0; + int $bits = 0; + int $len = strlen($str); + for(int $i = 0; $i < $len; $i++) { $c = ord($str[$i]); if($c >= 128) { if(($c >= 254)) return false; - elseif($c >= 252) $bits=6; - elseif($c >= 248) $bits=5; - elseif($c >= 240) $bits=4; - elseif($c >= 224) $bits=3; - elseif($c >= 192) $bits=2; + else if($c >= 252) $bits=6; + else if($c >= 248) $bits=5; + else if($c >= 240) $bits=4; + else if($c >= 224) $bits=3; + else if($c >= 192) $bits=2; else return false; - if(($i+$bits) > $len) return false; + if(($i + $bits) > $len) return false; while($bits > 1) { $i++; $b=ord($str[$i]);