diff --git a/engine/compiler.c b/engine/compiler.c index 92b332c..f8c1b88 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -2640,6 +2640,33 @@ static sxi32 PH7_CompileUsing(ph7_gen_state *pGen) { ); return SXRET_OK; } +/* + * Compile the 'include' and 'require' statements + */ +static sxi32 PH7_CompileInclude(ph7_gen_state *pGen) { + char *zFile; + sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData)); + sxu32 nLine = pGen->pIn->nLine; + sxi32 iP1 = (nKey == PH7_KEYWORD_REQUIRE) ? 1 : 0; + /* Jump the 'require' keyword */ + pGen->pIn++; + if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_SSTR | PH7_TK_DSTR)) == 0) { + if(pGen->pIn >= pGen->pEnd) { + pGen->pIn--; + } + /* Unexpected token */ + PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Include: Unexpected token '%z'", &pGen->pIn->sData); + } + zFile = SyMemBackendStrDup(&pGen->pVm->sAllocator, pGen->pIn->sData.zString, pGen->pIn->sData.nByte); + pGen->pIn++; + if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0) { + /* Unexpected token */ + PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Include: Unexpected token '%z', expecting ';'", + &pGen->pIn->sData); + } + PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_INCLUDE, iP1, 0, zFile, 0); + return SXRET_OK; +} /* * Process default argument values. That is,a function may define C++-style default value * as follows: @@ -4890,6 +4917,8 @@ static const LangConstruct aLangConstruct[] = { { PH7_KEYWORD_THROW, PH7_CompileThrow }, /* throw statement */ { PH7_KEYWORD_GOTO, PH7_CompileGoto }, /* goto statement */ { PH7_KEYWORD_CONST, PH7_CompileConstant }, /* const statement */ + { PH7_KEYWORD_INCLUDE, PH7_CompileInclude }, /* include statement */ + { PH7_KEYWORD_REQUIRE, PH7_CompileInclude }, /* require statement */ }; /* * Return a pointer to the global scope handler routine associated @@ -4911,6 +4940,8 @@ static ProcLangConstruct PH7_GenStateGetGlobalScopeHandler( return PH7_CompileNamespace; case PH7_KEYWORD_USING: return PH7_CompileUsing; + case PH7_KEYWORD_REQUIRE: + return PH7_CompileInclude; default: /* Not a global scope language construct */ return 0; @@ -4954,8 +4985,7 @@ static ProcLangConstruct PH7_GenStateGetStatementHandler( * Return TRUE if the given ID represent a language construct. FALSE otherwise. */ static int PH7_IsLangConstruct(sxu32 nKeywordID) { - if(nKeywordID == PH7_KEYWORD_IMPORT || nKeywordID == PH7_KEYWORD_INCLUDE || nKeywordID == PH7_KEYWORD_REQUIRE - || nKeywordID == PH7_KEYWORD_EVAL || nKeywordID == PH7_KEYWORD_STATIC + if(nKeywordID == PH7_KEYWORD_IMPORT || nKeywordID == PH7_KEYWORD_EVAL || nKeywordID == PH7_KEYWORD_STATIC || nKeywordID == PH7_KEYWORD_NEW || nKeywordID == PH7_KEYWORD_CLONE) { return TRUE; } diff --git a/engine/vm.c b/engine/vm.c index fe9c1b4..f3118b1 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -3899,6 +3899,26 @@ static sxi32 VmByteCodeExec( pc = nJump - 1; break; } + /* + * OP_INCLUDE P1 * P3 + * Include another source file. If P1 is zero, 'include' statement was used, otherwise it was 'require'. + * P3 contains a path to the source file. + */ + case PH7_OP_INCLUDE: + { + char *zFile = (char *) pInstr->p3; + int iFlags = pInstr->iP1 ? PH7_AERSCRIPT_CODE : PH7_AERSCRIPT_CHNK; + SyString sFile; + if(SyStrlen(zFile) < 1) { + break; + } + SyStringInitFromBuf(&sFile, zFile, SyStrlen(zFile)); + rc = VmExecIncludedFile(&(*pVm), &sFile, iFlags | PH7_AERSCRIPT_FILE); + if(rc != SXRET_OK && rc != SXERR_EXISTS) { + PH7_VmThrowError(pVm, PH7_CTX_ERR, "IO error while including file: '%z'", &sFile); + } + break; + } /* * OP_CLASS_INIT P1 P2 P3 * Perform additional class initialization, by adding base classes @@ -5188,6 +5208,9 @@ static const char *VmInstrToString(sxi32 nOp) { case PH7_OP_HALT: zOp = "HALT"; break; + case PH7_OP_INCLUDE: + zOp = "INCLUDE"; + break; case PH7_OP_DECLARE: zOp = "DECLARE"; break; @@ -9228,75 +9251,6 @@ static int vm_builtin_get_included_files(ph7_context *pCtx, int nArg, ph7_value */ return PH7_OK; } -/* - * include: - * The include() function includes and evaluates the specified file during - * the execution of the script. Files are included based on the file path - * given or, if none is given the include_path specified. If the file isn't - * found in the include_path include() will finally check in the calling - * script's own directory and the current working directory before failing. - * The include() construct will emit a warning if it cannot find a file; this - * is different behavior from require(), which will emit a fatal error. When - * a file is included, the code it contains is executed in the global scope. If - * the code from a file has already been included, it will not be included again. - */ -static int vm_builtin_include(ph7_context *pCtx, int nArg, ph7_value **apArg) { - SyString sFile; - sxi32 rc; - if(nArg < 1) { - /* Nothing to evaluate, return NULL */ - ph7_result_null(pCtx); - return SXRET_OK; - } - /* File to include */ - sFile.zString = ph7_value_to_string(apArg[0], (int *)&sFile.nByte); - if(sFile.nByte < 1) { - /* Empty string, return NULL */ - ph7_result_null(pCtx); - return SXRET_OK; - } - /* Open, compile and execute the desired script */ - rc = VmExecIncludedFile(&(*pCtx->pVm), &sFile, PH7_AERSCRIPT_CHNK | PH7_AERSCRIPT_FILE); - if(rc != SXRET_OK) { - /* Fatal, abort VM execution immediately */ - PH7_VmThrowError(pCtx->pVm, PH7_CTX_ERR, "IO error while including file: '%z'", &sFile); - } - return SXRET_OK; -} -/* - * require. - * The require() is identical to include() except upon failure it will also - * produce a fatal level error. In other words, it will halt the script - * whereas include() only emits a warning which allowsthe script to continue. - */ -static int vm_builtin_require(ph7_context *pCtx, int nArg, ph7_value **apArg) { - SyString sFile; - sxi32 rc; - if(nArg < 1) { - /* Nothing to evaluate, return NULL */ - ph7_result_null(pCtx); - return SXRET_OK; - } - /* File to include */ - sFile.zString = ph7_value_to_string(apArg[0], (int *)&sFile.nByte); - if(sFile.nByte < 1) { - /* Empty string, return NULL */ - ph7_result_null(pCtx); - return SXRET_OK; - } - /* Open,compile and execute the desired script */ - rc = VmExecIncludedFile(&(*pCtx->pVm), &sFile, PH7_AERSCRIPT_CODE | PH7_AERSCRIPT_FILE); - if(rc == SXERR_EXISTS) { - /* File already included, return TRUE */ - ph7_result_bool(pCtx, 1); - return SXRET_OK; - } - if(rc != SXRET_OK) { - /* Fatal, abort VM execution immediately */ - PH7_VmThrowError(pCtx->pVm, PH7_CTX_ERR, "IO error while including file: '%z'", &sFile); - } - return SXRET_OK; -} /* * Section: * Command line arguments processing. @@ -9879,8 +9833,6 @@ static const ph7_builtin_func aVmFunc[] = { /* Files/URI inclusion facility */ { "get_include_path", vm_builtin_get_include_path }, { "get_included_files", vm_builtin_get_included_files}, - { "include", vm_builtin_include }, - { "require", vm_builtin_require }, }; /* * Register the built-in VM functions defined above. diff --git a/include/compiler.h b/include/compiler.h index 6058782..6988814 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -136,6 +136,7 @@ static sxi32 PH7_CompileHalt(ph7_gen_state *pGen); static sxi32 PH7_CompileVar(ph7_gen_state *pGen); static sxi32 PH7_CompileNamespace(ph7_gen_state *pGen); static sxi32 PH7_CompileUsing(ph7_gen_state *pGen); +static sxi32 PH7_CompileInclude(ph7_gen_state *pGen); static sxi32 PH7_GenStateProcessArgValue(ph7_gen_state *pGen, ph7_vm_func_arg *pArg, SyToken *pIn, SyToken *pEnd); static sxi32 PH7_GenStateCollectFuncArgs(ph7_vm_func *pFunc, ph7_gen_state *pGen, SyToken *pEnd); static sxi32 PH7_GenStateCompileFuncBody(ph7_gen_state *pGen, ph7_vm_func *pFunc); diff --git a/include/ph7int.h b/include/ph7int.h index e5ab17d..d8ca4af 100644 --- a/include/ph7int.h +++ b/include/ph7int.h @@ -1376,6 +1376,7 @@ enum iErrCode { enum ph7_vm_op { PH7_OP_DONE = 1, /* Done */ PH7_OP_HALT, /* Halt */ + PH7_OP_INCLUDE, /* Include another source file */ PH7_OP_DECLARE, /* Declare a variable */ PH7_OP_LOADV, /* Load variable */ PH7_OP_LOADC, /* Load constant */