/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2010 Warzone 2100 Project Warzone 2100 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Warzone 2100 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ %{ /* * script.y * * The yacc grammar for the scipt files. */ #include "lib/framework/frame.h" #include "lib/framework/frameresource.h" #include "lib/framework/string_ext.h" #include "lib/script/interpreter.h" #include "lib/script/parse.h" #include "lib/script/script.h" #include /* this will give us a more detailed error output */ #define YYERROR_VERBOSE true /* Script defines stack */ extern int scr_lex(void); extern int scr_lex_destroy(void); /* Error return codes for code generation functions */ typedef enum _code_error { CE_OK, // No error CE_MEMORY, // Out of memory CE_PARSE // A parse error occured } CODE_ERROR; /* Turn off a couple of warnings that the yacc generated code gives */ /* Pointer to the compiled code */ static SCRIPT_CODE *psFinalProg=NULL; /* Pointer to current block of compiled code */ static CODE_BLOCK *psCurrBlock=NULL; /* Pointer to current block of conditional code */ static COND_BLOCK *psCondBlock=NULL; /* Pointer to current block of object variable code */ static OBJVAR_BLOCK *psObjVarBlock=NULL; /* Pointer to current block of compiled parameter code */ static PARAM_BLOCK *psCurrPBlock=NULL; /* Any errors occured? */ static BOOL bError=false; //String support //----------------------------- char msg[MAXSTRLEN]; extern char STRSTACK[MAXSTACKLEN][MAXSTRLEN]; // just a simple string "stack" extern UDWORD CURSTACKSTR; //Current string index /* Pointer into the current code block */ static INTERP_VAL *ip; /* Pointer to current parameter declaration block */ //static PARAM_DECL *psCurrParamDecl=NULL; /* Pointer to current trigger subdeclaration */ static TRIGGER_DECL *psCurrTDecl=NULL; /* Pointer to current variable subdeclaration */ static VAR_DECL *psCurrVDecl=NULL; /* Pointer to the current variable identifier declaration */ static VAR_IDENT_DECL *psCurrVIdentDecl=NULL; /* Pointer to the current array access block */ static ARRAY_BLOCK *psCurrArrayBlock=NULL; /* Return code from code generation functions */ static CODE_ERROR codeRet; /* The list of global variables */ static VAR_SYMBOL *psGlobalVars=NULL; /* The list of global arrays */ static VAR_SYMBOL *psGlobalArrays=NULL; #define maxEventsLocalVars 1200 static VAR_SYMBOL *psLocalVarsB[maxEventsLocalVars]; /* local var storage */ static UDWORD numEventLocalVars[maxEventsLocalVars]; /* number of declard local vars for each event */ EVENT_SYMBOL *psCurEvent = NULL; /* stores current event: for local var declaration */ /* The current object variable context */ static INTERP_TYPE objVarContext = (INTERP_TYPE)0; /* Control whether debug info is generated */ static BOOL genDebugInfo = true; /* Currently defined triggers */ static TRIGGER_SYMBOL *psTriggers; static UDWORD numTriggers; /* Currently defined events */ static EVENT_SYMBOL *psEvents; static UDWORD numEvents; /* This is true when local variables are being defined. * (So local variables can have the same name as global ones) */ static BOOL localVariableDef=false; /* The identifier for the current script function being defined */ //static char *pCurrFuncIdent=NULL; /* A temporary store for a line number - used when * generating debugging info for functions, conditionals and loops. */ static UDWORD debugLine; /* The table of user types */ TYPE_SYMBOL *asScrTypeTab; /* The table of instinct function type definitions */ FUNC_SYMBOL *asScrInstinctTab; /* The table of external variables and their access functions */ VAR_SYMBOL *asScrExternalTab; /* The table of object variables and their access functions. */ VAR_SYMBOL *asScrObjectVarTab; /* The table of constant variables */ CONST_SYMBOL *asScrConstantTab; /* The table of callback triggers */ CALLBACK_SYMBOL *asScrCallbackTab; /* Used for additional debug output */ void script_debug(const char *pFormat, ...); /**************************************************************************************** * * Code Block Macros * * These macros are used to allocate and free the different types of code * block used within the compiler */ /* What the macro should do if it has an allocation error. * This is different depending on whether the macro is used * in a function, or in a rule body. * * This definition is used within the code generation functions * and is then changed for use within a rule body. */ #define ALLOC_ERROR_ACTION return CE_MEMORY /* Macro to allocate a program structure, size is in _bytes_ */ #define ALLOC_PROG(psProg, codeSize, pAICode, numGlobs, numArys, numTrigs, numEvnts) \ (psProg) = (SCRIPT_CODE *)malloc(sizeof(SCRIPT_CODE)); \ if ((psProg) == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psProg)->pCode = (INTERP_VAL *)malloc((codeSize) * sizeof(INTERP_VAL)); \ if ((psProg)->pCode == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ if (numGlobs > 0) \ { \ (psProg)->pGlobals = (INTERP_TYPE *)malloc(sizeof(INTERP_TYPE) * (numGlobs)); \ if ((psProg)->pGlobals == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ } \ else \ { \ (psProg)->pGlobals = NULL; \ } \ if (numArys > 0) \ { \ (psProg)->psArrayInfo = (ARRAY_DATA *)malloc(sizeof(ARRAY_DATA) * (numArys)); \ if ((psProg)->psArrayInfo == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ } \ else \ { \ (psProg)->psArrayInfo = NULL; \ } \ (psProg)->numArrays = (UWORD)(numArys); \ if ((numTrigs) > 0) \ { \ (psProg)->pTriggerTab = malloc(sizeof(UWORD) * ((numTrigs) + 1)); \ if ((psProg)->pTriggerTab == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psProg)->psTriggerData = malloc(sizeof(TRIGGER_DATA) * (numTrigs)); \ if ((psProg)->psTriggerData == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ } \ else \ { \ (psProg)->pTriggerTab = NULL; \ (psProg)->psTriggerData = NULL; \ } \ (psProg)->pEventTab = malloc(sizeof(UWORD) * ((numEvnts) + 1)); \ if ((psProg)->pEventTab == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psProg)->pEventLinks = malloc(sizeof(SWORD) * (numEvnts)); \ if ((psProg)->pEventLinks == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psProg)->numGlobals = (UWORD)(numGlobs); \ (psProg)->numTriggers = (UWORD)(numTriggers); \ (psProg)->numEvents = (UWORD)(numEvnts); \ (psProg)->size = (codeSize) * sizeof(INTERP_VAL); /* Macro to allocate a code block, blockSize - number of INTERP_VALs we need*/ #define ALLOC_BLOCK(psBlock, num) \ (psBlock) = (CODE_BLOCK *)malloc(sizeof(CODE_BLOCK)); \ if ((psBlock) == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psBlock)->pCode = (INTERP_VAL *)malloc((num) * sizeof(INTERP_VAL)); \ if ((psBlock)->pCode == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ free((psBlock)); \ ALLOC_ERROR_ACTION; \ } \ (psBlock)->size = (num); \ (psBlock)->debugEntries = 0 /* Macro to free a code block */ #define FREE_BLOCK(psBlock) \ free((psBlock)->pCode); \ free((psBlock)) /* Macro to allocate a parameter block */ #define ALLOC_PBLOCK(psBlock, num, paramSize) \ (psBlock) = (PARAM_BLOCK *)malloc(sizeof(PARAM_BLOCK)); \ if ((psBlock) == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psBlock)->pCode = (INTERP_VAL *)malloc((num) * sizeof(INTERP_VAL)); \ if ((psBlock)->pCode == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ free((psBlock)); \ ALLOC_ERROR_ACTION; \ } \ (psBlock)->aParams = (INTERP_TYPE *)malloc(sizeof(INTERP_TYPE) * (paramSize)); \ if ((psBlock)->aParams == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ free((psBlock)->pCode); \ free((psBlock)); \ ALLOC_ERROR_ACTION; \ } \ (psBlock)->size = (num); \ (psBlock)->numParams = (paramSize) /* Macro to free a parameter block */ #define FREE_PBLOCK(psBlock) \ free((psBlock)->pCode); \ free((psBlock)->aParams); \ free((psBlock)) /* Macro to allocate a parameter declaration block */ #define ALLOC_PARAMDECL(psPDecl, num) \ (psPDecl) = (PARAM_DECL *)malloc(sizeof(PARAM_DECL)); \ if ((psPDecl) == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psPDecl)->aParams = (INTERP_TYPE *)malloc(sizeof(INTERP_TYPE) * (num)); \ if ((psPDecl)->aParams == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psPDecl)->numParams = (num) /* Macro to free a parameter declaration block */ #define FREE_PARAMDECL(psPDecl) \ free((psPDecl)->aParams); \ free((psPDecl)) /* Macro to allocate a conditional block */ #define ALLOC_CONDBLOCK(psCB, num, numBlocks) \ (psCB) = (COND_BLOCK *)malloc(sizeof(COND_BLOCK)); \ if ((psCB) == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psCB)->aOffsets = (UDWORD *)malloc(sizeof(SDWORD) * (num)); \ if ((psCB)->aOffsets == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psCB)->pCode = (INTERP_VAL *)malloc((numBlocks) * sizeof(INTERP_VAL)); \ if ((psCB)->pCode == NULL) \ { \ debug(LOG_ERROR, "Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psCB)->size = (numBlocks); \ (psCB)->numOffsets = (num) /* Macro to free a conditional block */ #define FREE_CONDBLOCK(psCB) \ free((psCB)->aOffsets); \ free((psCB)->pCode); \ free(psCB) /* Macro to free a code block */ #define FREE_USERBLOCK(psBlock) \ free((psBlock)->pCode); \ free((psBlock)) /* Macro to allocate an object variable block */ #define ALLOC_OBJVARBLOCK(psOV, blockSize, psVar) \ (psOV) = (OBJVAR_BLOCK *)malloc(sizeof(OBJVAR_BLOCK)); \ if ((psOV) == NULL) \ { \ scr_error("Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psOV)->pCode = (INTERP_VAL *)malloc((blockSize) * sizeof(INTERP_VAL)); \ if ((psOV)->pCode == NULL) \ { \ scr_error("Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psOV)->size = (blockSize); \ (psOV)->psObjVar = (psVar) /* Macro to free an object variable block */ #define FREE_OBJVARBLOCK(psOV) \ free((psOV)->pCode); \ free(psOV) /* Macro to allocate an array variable block */ #define ALLOC_ARRAYBLOCK(psAV, blockSize, psVar) \ (psAV) = (ARRAY_BLOCK *)malloc(sizeof(ARRAY_BLOCK)); \ if ((psAV) == NULL) \ { \ scr_error("Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psAV)->pCode = (INTERP_VAL *)malloc((blockSize) * sizeof(INTERP_VAL)); \ if ((psAV)->pCode == NULL) \ { \ scr_error("Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psAV)->size = (blockSize); \ (psAV)->dimensions = 1; \ (psAV)->psArrayVar = (psVar) /* Macro to free an object variable block */ #define FREE_ARRAYBLOCK(psAV) \ free((psAV)->pCode); \ free((psAV)) /* Allocate a trigger subdecl */ #define ALLOC_TSUBDECL(psTSub, blockType, blockSize, blockTime) \ (psTSub) = malloc(sizeof(TRIGGER_DECL)); \ if ((psTSub) == NULL) \ { \ scr_error("Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psTSub)->type = (blockType); \ (psTSub)->time = (blockTime); \ if ((blockSize) > 0) \ { \ (psTSub)->pCode = (INTERP_VAL *)malloc((blockSize) * sizeof(INTERP_VAL)); \ if ((psTSub)->pCode == NULL) \ { \ scr_error("Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ (psTSub)->size = (blockSize); \ } \ else \ { \ (psTSub)->pCode = NULL; \ (psTSub)->size = 0; \ } /* Free a trigger subdecl */ #define FREE_TSUBDECL(psTSub) \ if ((psTSub)->pCode) \ { \ free((psTSub)->pCode); \ } \ free(psTSub) /* Allocate a variable declaration block */ #define ALLOC_VARDECL(psDcl) \ (psDcl)=malloc(sizeof(VAR_DECL)); \ if ((psDcl) == NULL) \ { \ scr_error("Out of memory"); \ ALLOC_ERROR_ACTION; \ } /* Free a variable declaration block */ #define FREE_VARDECL(psDcl) \ free(psDcl) /* Allocate a variable declaration block */ static inline CODE_ERROR do_ALLOC_VARIDENTDECL(VAR_IDENT_DECL** psDcl, const char* ident, unsigned int dim) { // Allocate memory *psDcl = malloc(sizeof(VAR_IDENT_DECL)); if (*psDcl == NULL) { scr_error("Out of memory"); ALLOC_ERROR_ACTION; } // Copy over the "ident" string (if it's there) if (ident != NULL) { (*psDcl)->pIdent = strdup(ident); if ((*psDcl)->pIdent == NULL) { scr_error("Out of memory"); ALLOC_ERROR_ACTION; } } else { (*psDcl)->pIdent = NULL; } (*psDcl)->dimensions = dim; return CE_OK; } #define ALLOC_VARIDENTDECL(psDcl, ident, dim) \ { \ CODE_ERROR err = do_ALLOC_VARIDENTDECL(&psDcl, ident, dim); \ if (err != CE_OK) \ return err; \ } /* Free a variable declaration block */ static void freeVARIDENTDECL(VAR_IDENT_DECL* psDcl) { free(psDcl->pIdent); free(psDcl); } /**************************************************************************************** * * Code block manipulation macros. * * These are used to copy chunks of code from one block to another * or to insert opcodes or other values into a code block. * All the macros use the ip parameter. This is a pointer into the code * block that is incremented by the macro. This ensures that it always points * to the next free space in the code block. */ /* Macro to store an opcode in a code block, opcode type is '-1' */ #define PUT_OPCODE(ip, opcode) \ (ip)->type = VAL_OPCODE; \ (ip)->v.ival = (opcode) << OPCODE_SHIFT; \ (ip)++ /* Macro to put a packed opcode in a code block */ #define PUT_PKOPCODE(ip, opcode, data) \ (ip)->type = VAL_PKOPCODE; \ (ip)->v.ival = ((SDWORD)(data)) & OPCODE_DATAMASK; \ (ip)->v.ival = (((SDWORD)(opcode)) << OPCODE_SHIFT) | ((ip)->v.ival); \ (ip)++ /* Special macro for floats, could be a bit tricky */ #define PUT_DATA_FLOAT(ip, data) \ ip->type = VAL_FLOAT; \ ip->v.fval = (float)(data); \ ip++ /* Macros to store a value in a code block */ #define PUT_DATA_BOOL(ip, value) \ (ip)->type = VAL_BOOL; \ (ip)->v.bval = (BOOL)(value); \ (ip)++ #define PUT_DATA_INT(ip, value) \ (ip)->type = VAL_INT; \ (ip)->v.ival = (SDWORD)(value); \ (ip)++ #define PUT_DATA_STRING(ip, value) \ (ip)->type = VAL_STRING; \ (ip)->v.sval = (char *)(value); \ (ip)++ #define PUT_OBJECT_CONST(ip, value, constType) \ (ip)->type = (constType); \ (ip)->v.oval = (value); \ (ip)++ #define PUT_STRING(ip, value) \ ((INTERP_VAL *)(ip))->type = VAL_STRING; \ ((INTERP_VAL *)(ip))->v.sval = (value); \ ((INTERP_VAL *)(ip)) += 1 /* Macro to store an external function index in a code block */ #define PUT_FUNC_EXTERN(ip, func) \ (ip)->type = VAL_FUNC_EXTERN; \ (ip)->v.pFuncExtern = (SCRIPT_FUNC)func; \ (ip)++ /* Macro to store an internal (in-script, actually an event) function index in a code block */ #define PUT_EVENT(ip, func) \ (ip)->type = VAL_EVENT; \ (ip)->v.ival = (SDWORD)func; \ (ip)++ /* Macro to store trigger */ #define PUT_TRIGGER(ip, func) \ (ip)->type = VAL_TRIGGER; \ (ip)->v.ival = (SDWORD)func; \ (ip)++ /* Macro to store a function pointer in a code block */ #define PUT_VARFUNC(ip, func) \ (ip)->type = VAL_OBJ_GETSET; \ (ip)->v.pObjGetSet = (SCRIPT_VARFUNC)func; \ (ip)++ /* Macro to store a variable index number in a code block - NOT USED */ #define PUT_INDEX(ip, index) \ (ip)->type = -1; \ (ip)->v.ival = (SDWORD)(index); \ (ip)++ /* Macro to copy a code block into another code block */ #define PUT_BLOCK(ip, psOldBlock) \ memcpy(ip, (psOldBlock)->pCode, (psOldBlock)->size * sizeof(INTERP_VAL)); \ (ip) = (INTERP_VAL *)( (ip) + (psOldBlock)->size) /*********************************************************************************** * * Debugging information macros * * These macros are only used to generate debugging information for scripts. */ /* Macro to allocate debugging info for a CODE_BLOCK or a COND_BLOCK */ #define ALLOC_DEBUG(psBlock, num) \ if (genDebugInfo) \ { \ (psBlock)->psDebug = (SCRIPT_DEBUG *)malloc(sizeof(SCRIPT_DEBUG) * (num)); \ if ((psBlock)->psDebug == NULL) \ { \ scr_error("Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ memset((psBlock)->psDebug, 0, sizeof(SCRIPT_DEBUG) * (num));\ (psBlock)->debugEntries = (UWORD)(num); \ } \ else \ { \ (psBlock)->psDebug = NULL; \ (psBlock)->debugEntries = 0; \ } /* Macro to free debugging info */ #define FREE_DEBUG(psBlock) \ if (genDebugInfo) \ free((psBlock)->psDebug) /* Macro to copy the debugging information from one block to another */ #define PUT_DEBUG(psFinal, psBlock) \ if (genDebugInfo) \ { \ memcpy((psFinal)->psDebug, (psBlock)->psDebug, \ sizeof(SCRIPT_DEBUG) * (psBlock)->debugEntries); \ (psFinal)->debugEntries = (psBlock)->debugEntries; \ } /* Macro to combine the debugging information in two blocks into a third block */ static UDWORD _dbEntry; static SCRIPT_DEBUG *_psCurr; #define COMBINE_DEBUG(psFinal, psBlock1, psBlock2) \ if (genDebugInfo) \ { \ memcpy((psFinal)->psDebug, (psBlock1)->psDebug, \ sizeof(SCRIPT_DEBUG) * (psBlock1)->debugEntries); \ _baseOffset = (psBlock1)->size / sizeof(UDWORD); \ for(_dbEntry = 0; _dbEntry < (psBlock2)->debugEntries; _dbEntry++) \ { \ _psCurr = (psFinal)->psDebug + (psBlock1)->debugEntries + _dbEntry; \ _psCurr->line = (psBlock2)->psDebug[_dbEntry].line; \ _psCurr->offset = (psBlock2)->psDebug[_dbEntry].offset + _baseOffset; \ } \ (psFinal)->debugEntries = (psBlock1)->debugEntries + (psBlock2)->debugEntries; \ } /* Macro to append some debugging information onto a block, given the instruction offset of the debugging information already in the destination block */ #define APPEND_DEBUG(psFinal, baseOffset, psBlock) \ if (genDebugInfo) \ { \ for(_dbEntry = 0; _dbEntry < (psBlock)->debugEntries; _dbEntry++) \ { \ _psCurr = (psFinal)->psDebug + (psFinal)->debugEntries + _dbEntry; \ _psCurr->line = (psBlock)->psDebug[_dbEntry].line; \ _psCurr->offset = (psBlock)->psDebug[_dbEntry].offset + (baseOffset); \ } \ (psFinal)->debugEntries = (UWORD)((psFinal)->debugEntries + (psBlock)->debugEntries); \ } /* Macro to store a label in the debug info */ #define DEBUG_LABEL(psBlock, offset, pString) \ if (genDebugInfo) \ { \ (psBlock)->psDebug[offset].pLabel = malloc(strlen(pString)+1); \ if (!(psBlock)->psDebug[offset].pLabel) \ { \ scr_error("Out of memory"); \ ALLOC_ERROR_ACTION; \ } \ strcpy((psBlock)->psDebug[offset].pLabel, pString); \ } /*************************************************************************************** * * Code generation functions * * These functions are used within rule bodies to generate code. */ /* Macro to deal with the errors returned by code generation functions. * Used within the rule body. */ #define CHECK_CODE_ERROR(error) \ if ((error) == CE_MEMORY) \ { \ YYABORT; \ } \ else if ((error) == CE_PARSE) \ { \ YYERROR; \ } void script_debug(const char *pFormat, ...) { char buffer[500]; va_list pArgs; va_start(pArgs, pFormat); vsnprintf(buffer, sizeof(buffer), pFormat, pArgs); va_end(pArgs); debug(LOG_SCRIPT, "%s", buffer); } /* Generate the code for a function call, checking the parameter * types match. */ static CODE_ERROR scriptCodeFunction(FUNC_SYMBOL *psFSymbol, // The function being called PARAM_BLOCK *psPBlock, // The functions parameters BOOL expContext, // Whether the function is being // called in an expression context CODE_BLOCK **ppsCBlock) // The generated code block { UDWORD size, i; INTERP_VAL *ip; BOOL typeError = false; char aErrorString[255]; INTERP_TYPE type1,type2; ASSERT( psFSymbol != NULL, "ais_CodeFunction: Invalid function symbol pointer" ); ASSERT( psPBlock != NULL, "scriptCodeFunction: Invalid param block pointer" ); ASSERT( (psPBlock->size == 0) || psPBlock->pCode != NULL, "scriptCodeFunction: Invalid parameter code pointer" ); ASSERT( ppsCBlock != NULL, "scriptCodeFunction: Invalid generated code block pointer" ); /* Check the parameter types match what the function needs */ for(i=0; (inumParams) && (inumParams); i++) { /* if (psFSymbol->aParams[i] != VAL_VOID && psFSymbol->aParams[i] != psPBlock->aParams[i])*/ //TODO: string support if(psFSymbol->aParams[i] != VAL_STRING) // string - allow mixed types if string is parameter type //TODO: tweak this { type1 = psFSymbol->aParams[i]; type2 = psPBlock->aParams[i]; if (!interpCheckEquiv(type1, type2)) { debug(LOG_ERROR, "scriptCodeFunction: Type mismatch for parameter %d (%d/%d)", i, psFSymbol->aParams[i], psPBlock->aParams[i]); snprintf(aErrorString, sizeof(aErrorString), "Type mismatch for parameter %d", i); scr_error("%s", aErrorString); typeError = true; } } //else //{ // debug(LOG_SCRIPT, "scriptCodeFunction: %s takes string as parameter %d (provided: %d)", psFSymbol->pIdent, i, psPBlock->aParams[i]); //} } /* Check the number of parameters matches that expected */ if (psFSymbol->numParams != psPBlock->numParams) { snprintf(aErrorString, sizeof(aErrorString), "Expected %d parameters", psFSymbol->numParams); scr_error("%s", aErrorString); *ppsCBlock = NULL; return CE_PARSE; } if (typeError) { /* Report the error here so all the */ /* type mismatches are reported */ *ppsCBlock = NULL; return CE_PARSE; } //size = psPBlock->size + sizeof(OPCODE) + sizeof(SCRIPT_FUNC); size = psPBlock->size + 1 + 1; //size + opcode + sizeof(SCRIPT_FUNC) if (!expContext && (psFSymbol->type != VAL_VOID)) { //size += sizeof(OPCODE); size += 1; //+ 1 additional opcode to pop value if needed } ALLOC_BLOCK(*ppsCBlock, size); ip = (*ppsCBlock)->pCode; (*ppsCBlock)->type = psFSymbol->type; /* Copy in the code for the parameters */ PUT_BLOCK(ip, psPBlock); FREE_PBLOCK(psPBlock); /* Make the function call */ if (psFSymbol->script) { /* function defined in this script */ // PUT_OPCODE(ip, OP_FUNC); // PUT_SCRIPTFUNC(ip, psFSymbol); ASSERT(false, "wrong function type call"); } else { /* call an instinct function */ PUT_OPCODE(ip, OP_CALL); PUT_FUNC_EXTERN(ip, psFSymbol->pFunc); } if (!expContext && (psFSymbol->type != VAL_VOID)) { /* Clear the return value from the stack */ PUT_OPCODE(ip, OP_POP); } return CE_OK; } /* Function call: Check the parameter types match, assumes param count matched */ static UDWORD checkFuncParamTypes(EVENT_SYMBOL *psFSymbol, // The function being called PARAM_BLOCK *psPBlock) // The generated code block { UDWORD i; //debug(LOG_SCRIPT,"checkFuncParamTypes"); /* Check the parameter types match what the function needs */ for(i=0; (inumParams) && (inumParams); i++) { //TODO: string support //if(psFSymbol->aParams[i] != VAL_STRING) // string - allow mixed types if string is parameter type //{ if (!interpCheckEquiv(psFSymbol->aParams[i], psPBlock->aParams[i])) { debug(LOG_ERROR, "checkFuncParamTypes: Type mismatch for parameter %d ('1' based) in Function '%s' (provided type: %d, expected: %d)", (i+1), psFSymbol->pIdent, psPBlock->aParams[i], psFSymbol->aParams[i]); scr_error("Parameter type mismatch"); return i+1; } //} } //debug(LOG_SCRIPT,"END checkFuncParamTypes"); return 0; //all ok } #ifdef UNUSED /* * function call */ static CODE_ERROR scriptCodeCallFunction(FUNC_SYMBOL *psFSymbol, // The function being called PARAM_BLOCK *psPBlock, // The functions parameters CODE_BLOCK **ppsCBlock) // The generated code block { UDWORD size; INTERP_VAL *ip; //debug(LOG_SCRIPT, "scriptCodeCallFunction"); ASSERT( psFSymbol != NULL, "scriptCodeCallFunction: Invalid function symbol pointer" ); ASSERT( psPBlock != NULL, "scriptCodeCallFunction: Invalid param block pointer" ); ASSERT( (psPBlock->size == 0) || psPBlock->pCode != NULL, "scriptCodeCallFunction: Invalid parameter code pointer" ); ASSERT( ppsCBlock != NULL, "scriptCodeCallFunction: Invalid generated code block pointer" ); //size = psPBlock->size + sizeof(OPCODE) + sizeof(SCRIPT_FUNC); size = psPBlock->size + 1 + 1; //size + opcode + SCRIPT_FUNC ALLOC_BLOCK(*ppsCBlock, size); ip = (*ppsCBlock)->pCode; (*ppsCBlock)->type = psFSymbol->type; /* Copy in the code for the parameters */ PUT_BLOCK(ip, psPBlock); FREE_PBLOCK(psPBlock); /* Make the function call */ PUT_OPCODE(ip, OP_CALL); PUT_FUNC_EXTERN(ip, psFSymbol->pFunc); //debug(LOG_SCRIPT, "END scriptCodeCallFunction"); return CE_OK; } #endif /* Generate the code for a parameter callback, checking the parameter * types match. */ static CODE_ERROR scriptCodeCallbackParams( CALLBACK_SYMBOL *psCBSymbol, // The callback being called PARAM_BLOCK *psPBlock, // The callbacks parameters TRIGGER_DECL **ppsTDecl) // The generated code block { UDWORD size, i; INTERP_VAL *ip; BOOL typeError = false; char aErrorString[255]; ASSERT( psPBlock != NULL, "scriptCodeCallbackParams: Invalid param block pointer" ); ASSERT( (psPBlock->size == 0) || psPBlock->pCode != NULL, "scriptCodeCallbackParams: Invalid parameter code pointer" ); ASSERT( ppsTDecl != NULL, "scriptCodeCallbackParams: Invalid generated code block pointer" ); ASSERT( psCBSymbol->pFunc != NULL, "scriptCodeCallbackParams: Expected function pointer for callback symbol" ); /* Check the parameter types match what the function needs */ for(i=0; (inumParams) && (inumParams); i++) { if (!interpCheckEquiv(psCBSymbol->aParams[i], psPBlock->aParams[i])) { snprintf(aErrorString, sizeof(aErrorString), "Type mismatch for parameter %d", i); scr_error("%s", aErrorString); typeError = true; } } /* Check the number of parameters matches that expected */ if (psPBlock->numParams == 0) { scr_error("Expected parameters to callback"); *ppsTDecl = NULL; return CE_PARSE; } else if (psCBSymbol->numParams != psPBlock->numParams) { snprintf(aErrorString, sizeof(aErrorString), "Expected %d parameters", psCBSymbol->numParams); scr_error("%s", aErrorString); *ppsTDecl = NULL; return CE_PARSE; } if (typeError) { /* Return the error here so all the */ /* type mismatches are reported */ *ppsTDecl = NULL; return CE_PARSE; } //size = psPBlock->size + sizeof(OPCODE) + sizeof(SCRIPT_FUNC); size = psPBlock->size + 1 + 1; //size + opcode + SCRIPT_FUNC ALLOC_TSUBDECL(*ppsTDecl, psCBSymbol->type, size, 0); ip = (*ppsTDecl)->pCode; /* Copy in the code for the parameters */ PUT_BLOCK(ip, psPBlock); FREE_PBLOCK(psPBlock); /* call the instinct function */ PUT_OPCODE(ip, OP_CALL); PUT_FUNC_EXTERN(ip, psCBSymbol->pFunc); return CE_OK; } /* Generate code for assigning a value to a variable */ static CODE_ERROR scriptCodeAssignment(VAR_SYMBOL *psVariable, // The variable to assign to CODE_BLOCK *psValue, // The code for the value to // assign CODE_BLOCK **ppsBlock) // Generated code { SDWORD size; ASSERT( psVariable != NULL, "scriptCodeAssignment: Invalid variable symbol pointer" ); ASSERT( psValue != NULL, "scriptCodeAssignment: Invalid value code block pointer" ); ASSERT( psValue->pCode != NULL, "scriptCodeAssignment: Invalid value code pointer" ); ASSERT( ppsBlock != NULL, "scriptCodeAssignment: Invalid generated code block pointer" ); //size = psValue->size + sizeof(OPCODE); size = psValue->size + 1; //1 - for assignment opcode if (psVariable->storage == ST_EXTERN) { // Check there is a set function if (psVariable->set == NULL) { scr_error("No set function for external variable"); return CE_PARSE; } //size += sizeof(SCRIPT_VARFUNC); size += 1; //1 - for set func pointer } ALLOC_BLOCK(*ppsBlock, size); ip = (*ppsBlock)->pCode; /* Copy in the code for the expression */ PUT_BLOCK(ip, psValue); FREE_BLOCK(psValue); /* Code to get the value from the stack into the variable */ switch (psVariable->storage) { case ST_PUBLIC: case ST_PRIVATE: PUT_PKOPCODE(ip, OP_POPGLOBAL, psVariable->index); break; case ST_LOCAL: PUT_PKOPCODE(ip, OP_POPLOCAL, psVariable->index); break; case ST_EXTERN: PUT_PKOPCODE(ip, OP_VARCALL, psVariable->index); PUT_VARFUNC(ip, psVariable->set); break; case ST_OBJECT: scr_error("Cannot use member variables in this context"); return CE_PARSE; break; default: scr_error("Unknown storage type"); return CE_PARSE; break; } return CE_OK; } /* Generate code for assigning a value to an object variable */ static CODE_ERROR scriptCodeObjAssignment(OBJVAR_BLOCK *psVariable,// The variable to assign to CODE_BLOCK *psValue, // The code for the value to // assign CODE_BLOCK **ppsBlock) // Generated code { ASSERT( psVariable != NULL, "scriptCodeObjAssignment: Invalid object variable block pointer" ); ASSERT( psVariable->pCode != NULL, "scriptCodeObjAssignment: Invalid object variable code pointer" ); ASSERT( psVariable->psObjVar != NULL, "scriptCodeObjAssignment: Invalid object variable symbol pointer" ); ASSERT( psValue != NULL, "scriptCodeObjAssignment: Invalid value code block pointer" ); ASSERT( psValue->pCode != NULL, "scriptCodeObjAssignment: Invalid value code pointer" ); ASSERT( ppsBlock != NULL, "scriptCodeObjAssignment: Invalid generated code block pointer" ); // Check there is an access function for the variable if (psVariable->psObjVar->set == NULL) { scr_error("No set function for object variable"); return CE_PARSE; } //ALLOC_BLOCK(*ppsBlock, psVariable->size + psValue->size + sizeof(OPCODE) + sizeof(SCRIPT_VARFUNC)); ALLOC_BLOCK(*ppsBlock, psVariable->size + psValue->size + 1 + 1); //size + size + opcode + 'sizeof(SCRIPT_VARFUNC)' ip = (*ppsBlock)->pCode; /* Copy in the code for the value */ PUT_BLOCK(ip, psValue); FREE_BLOCK(psValue); /* Copy in the code for the object */ PUT_BLOCK(ip, psVariable); /* Code to get the value from the stack into the variable */ PUT_PKOPCODE(ip, OP_VARCALL, psVariable->psObjVar->index); PUT_VARFUNC(ip, (psVariable->psObjVar->set)); /* Free the variable block */ FREE_OBJVARBLOCK(psVariable); return CE_OK; } /* Generate code for getting a value from an object variable */ static CODE_ERROR scriptCodeObjGet(OBJVAR_BLOCK *psVariable,// The variable to get from CODE_BLOCK **ppsBlock) // Generated code { ASSERT( psVariable != NULL, "scriptCodeObjAssignment: Invalid object variable block pointer" ); ASSERT( psVariable->pCode != NULL, "scriptCodeObjAssignment: Invalid object variable code pointer" ); ASSERT( psVariable->psObjVar != NULL, "scriptCodeObjAssignment: Invalid object variable symbol pointer" ); ASSERT( ppsBlock != NULL, "scriptCodeObjAssignment: Invalid generated code block pointer" ); // Check there is an access function for the variable if (psVariable->psObjVar->get == NULL) { scr_error("No get function for object variable"); return CE_PARSE; } //ALLOC_BLOCK(*ppsBlock, psVariable->size + sizeof(OPCODE) + sizeof(SCRIPT_VARFUNC)); ALLOC_BLOCK(*ppsBlock, psVariable->size + 1 + 1); //size + opcode + SCRIPT_VARFUNC ip = (*ppsBlock)->pCode; /* Copy in the code for the object */ PUT_BLOCK(ip, psVariable); (*ppsBlock)->type = psVariable->psObjVar->type; /* Code to get the value from the object onto the stack */ PUT_PKOPCODE(ip, OP_VARCALL, psVariable->psObjVar->index); PUT_VARFUNC(ip, psVariable->psObjVar->get); /* Free the variable block */ FREE_OBJVARBLOCK(psVariable); return CE_OK; } /* Generate code for assigning a value to an array variable */ static CODE_ERROR scriptCodeArrayAssignment(ARRAY_BLOCK *psVariable,// The variable to assign to CODE_BLOCK *psValue, // The code for the value to // assign CODE_BLOCK **ppsBlock) // Generated code { // SDWORD elementDWords, i; // UBYTE *pElement; ASSERT( psVariable != NULL, "scriptCodeObjAssignment: Invalid object variable block pointer" ); ASSERT( psVariable->pCode != NULL, "scriptCodeObjAssignment: Invalid object variable code pointer" ); ASSERT( psVariable->psArrayVar != NULL, "scriptCodeObjAssignment: Invalid object variable symbol pointer" ); ASSERT( psValue != NULL, "scriptCodeObjAssignment: Invalid value code block pointer" ); ASSERT( psValue->pCode != NULL, "scriptCodeObjAssignment: Invalid value code pointer" ); ASSERT( ppsBlock != NULL, "scriptCodeObjAssignment: Invalid generated code block pointer" ); // Check this is an array if (psVariable->psArrayVar->dimensions == 0) { scr_error("Not an array variable"); return CE_PARSE; } // calculate the number of DWORDs needed to store the number of elements for each dimension of the array // elementDWords = (psVariable->psArrayVar->dimensions - 1)/4 + 1; // ALLOC_BLOCK(*ppsBlock, psVariable->size + psValue->size + sizeof(OPCODE) + elementDWords*4); //ALLOC_BLOCK(*ppsBlock, psVariable->size + psValue->size + sizeof(OPCODE)); ALLOC_BLOCK(*ppsBlock, psVariable->size + psValue->size + 1); //size + size + opcode ip = (*ppsBlock)->pCode; /* Copy in the code for the value */ PUT_BLOCK(ip, psValue); FREE_BLOCK(psValue); /* Copy in the code for the array index */ PUT_BLOCK(ip, psVariable); /* Code to get the value from the stack into the variable */ PUT_PKOPCODE(ip, OP_POPARRAYGLOBAL, ((psVariable->psArrayVar->dimensions << ARRAY_DIMENSION_SHIFT) & ARRAY_DIMENSION_MASK) | (psVariable->psArrayVar->index & ARRAY_BASE_MASK) ); // store the size of each dimension /* pElement = (UBYTE *)ip; for(i=0; ipsArrayVar->dimensions; i++) { *pElement = (UBYTE)psVariable->psArrayVar->elements[i]; pElement += 1; }*/ /* Free the variable block */ FREE_ARRAYBLOCK(psVariable); return CE_OK; } /* Generate code for getting a value from an array variable */ static CODE_ERROR scriptCodeArrayGet(ARRAY_BLOCK *psVariable,// The variable to get from CODE_BLOCK **ppsBlock) // Generated code { // SDWORD elementDWords, i; // UBYTE *pElement; ASSERT( psVariable != NULL, "scriptCodeObjAssignment: Invalid object variable block pointer" ); ASSERT( psVariable->pCode != NULL, "scriptCodeObjAssignment: Invalid object variable code pointer" ); ASSERT( psVariable->psArrayVar != NULL, "scriptCodeObjAssignment: Invalid object variable symbol pointer" ); ASSERT( ppsBlock != NULL, "scriptCodeObjAssignment: Invalid generated code block pointer" ); // Check this is an array if (psVariable->psArrayVar->dimensions == 0) { scr_error("Not an array variable"); return CE_PARSE; } // calculate the number of DWORDs needed to store the number of elements for each dimension of the array // elementDWords = (psVariable->psArrayVar->dimensions - 1)/4 + 1; // ALLOC_BLOCK(*ppsBlock, psVariable->size + sizeof(OPCODE) + elementDWords*4); //ALLOC_BLOCK(*ppsBlock, psVariable->size + sizeof(OPCODE)); ALLOC_BLOCK(*ppsBlock, psVariable->size + 1); //size + opcode ip = (*ppsBlock)->pCode; /* Copy in the code for the array index */ PUT_BLOCK(ip, psVariable); (*ppsBlock)->type = psVariable->psArrayVar->type; /* Code to get the value from the array onto the stack */ PUT_PKOPCODE(ip, OP_PUSHARRAYGLOBAL, ((psVariable->psArrayVar->dimensions << ARRAY_DIMENSION_SHIFT) & ARRAY_DIMENSION_MASK) | (psVariable->psArrayVar->index & ARRAY_BASE_MASK) ); // store the size of each dimension /* pElement = (UBYTE *)ip; for(i=0; ipsArrayVar->dimensions; i++) { *pElement = (UBYTE)psVariable->psArrayVar->elements[i]; pElement += 1; }*/ /* Free the variable block */ FREE_ARRAYBLOCK(psVariable); return CE_OK; } /* Generate the final code block for conditional statements */ static CODE_ERROR scriptCodeConditional( COND_BLOCK *psCondBlock, // The intermediate conditional code CODE_BLOCK **ppsBlock) // The final conditional code { UDWORD i; ASSERT( psCondBlock != NULL, "scriptCodeConditional: Invalid conditional code block pointer" ); ASSERT( psCondBlock->pCode != NULL, "scriptCodeConditional: Invalid conditional code pointer" ); ASSERT( ppsBlock != NULL, "scriptCodeConditional: Invalid generated code block pointer" ); /* Allocate the final block */ ALLOC_BLOCK(*ppsBlock, psCondBlock->size); ALLOC_DEBUG(*ppsBlock, psCondBlock->debugEntries); ip = (*ppsBlock)->pCode; /* Copy the code over */ PUT_BLOCK(ip, psCondBlock); /* Copy the debugging information */ PUT_DEBUG(*ppsBlock, psCondBlock); /* Now set the offsets of jumps in the conditional to the correct value */ for(i = 0; i < psCondBlock->numOffsets; i++) { ip = (*ppsBlock)->pCode + psCondBlock->aOffsets[i]; // *ip = ((*ppsBlock)->size / sizeof(UDWORD)) - (ip - (*ppsBlock)->pCode); ip->type = VAL_PKOPCODE; ip->v.ival = (*ppsBlock)->size - (ip - (*ppsBlock)->pCode); ip->v.ival = (OP_JUMP << OPCODE_SHIFT) | ( (ip->v.ival) & OPCODE_DATAMASK ); } /* Free the original code */ FREE_DEBUG(psCondBlock); FREE_CONDBLOCK(psCondBlock); return CE_OK; } /* Generate code for function parameters */ static CODE_ERROR scriptCodeParameter(CODE_BLOCK *psParam, // Code for the parameter INTERP_TYPE type, // Parameter type PARAM_BLOCK **ppsBlock) // Generated code { ASSERT( psParam != NULL, "scriptCodeParameter: Invalid parameter code block pointer" ); ASSERT( psParam->pCode != NULL, "scriptCodeParameter: Invalid parameter code pointer" ); ASSERT( ppsBlock != NULL, "scriptCodeParameter: Invalid generated code block pointer" ); RULE(" scriptCodeParameter(): type: %d", type); ALLOC_PBLOCK(*ppsBlock, psParam->size, 1); ip = (*ppsBlock)->pCode; /* Copy in the code for the parameter */ PUT_BLOCK(ip, psParam); FREE_BLOCK(psParam); (*ppsBlock)->aParams[0] = type; return CE_OK; } /* Generate code for binary operators (e.g. 2 + 2) */ static CODE_ERROR scriptCodeBinaryOperator(CODE_BLOCK *psFirst, // Code for first parameter CODE_BLOCK *psSecond, // Code for second parameter OPCODE opcode, // Operator function CODE_BLOCK **ppsBlock) // Generated code { ALLOC_BLOCK(*ppsBlock, psFirst->size + psSecond->size + 1); //size + size + binary opcode ip = (*ppsBlock)->pCode; /* Copy the already generated bits of code into the code block */ PUT_BLOCK(ip, psFirst); PUT_BLOCK(ip, psSecond); /* Now put an add operator into the code */ PUT_PKOPCODE(ip, OP_BINARYOP, opcode); /* Free the two code blocks that have been copied */ FREE_BLOCK(psFirst); FREE_BLOCK(psSecond); return CE_OK; } /* check if the arguments in the function definition body match the argument types and names from function declaration (if there was any) */ static BOOL checkFuncParamType(UDWORD argIndex, UDWORD argType) { VAR_SYMBOL *psCurr; SDWORD i,j; if(psCurEvent == NULL) { debug(LOG_ERROR, "checkFuncParamType() - psCurEvent == NULL"); return false; } if(argIndex < psCurEvent->numParams) { /* find the argument by the index */ i=psCurEvent->index; j=0; for(psCurr = psLocalVarsB[i]; psCurr != NULL; psCurr = psCurr->psNext) { if((psCurEvent->numParams - j - 1)==argIndex) /* got to the right argument */ { if(argType != psCurr->type) { debug(LOG_ERROR, "Argument type with index %d in event '%s' doesn't match function declaration (%d/%d)",argIndex,psCurEvent->pIdent,argType,psCurr->type); return false; } else { //debug(LOG_SCRIPT, "arg matched "); return true; } } j++; } } else { debug(LOG_ERROR, "checkFuncParamType() - argument %d has wrong argument index, event: '%s'", argIndex, psCurEvent->pIdent); return false; } return false; } /* Generate code for accessing an object variable. The variable symbol is * stored with the code for the object value so that this block can be used for * both setting and retrieving an object value. */ static CODE_ERROR scriptCodeObjectVariable(CODE_BLOCK *psObjCode, // Code for the object value VAR_SYMBOL *psVar, // The object variable symbol OBJVAR_BLOCK **ppsBlock) // Generated code { ASSERT( psObjCode != NULL, "scriptCodeObjectVariable: Invalid object code block pointer" ); ASSERT( psObjCode->pCode != NULL, "scriptCodeObjectVariable: Invalid object code pointer" ); ASSERT( psVar != NULL, "scriptCodeObjectVariable: Invalid variable symbol pointer" ); ASSERT( ppsBlock != NULL, "scriptCodeObjectVariable: Invalid generated code block pointer" ); ALLOC_OBJVARBLOCK(*ppsBlock, psObjCode->size, psVar); ip = (*ppsBlock)->pCode; /* Copy the already generated bit of code into the code block */ PUT_BLOCK(ip, psObjCode); FREE_BLOCK(psObjCode); /* Check the variable is the correct type */ if (psVar->storage != ST_OBJECT) { scr_error("Only object variables are valid in this context"); return CE_PARSE; } return CE_OK; } /* Generate code for accessing an array variable. The variable symbol is * stored with the code for the object value so that this block can be used for * both setting and retrieving an array value. */ static CODE_ERROR scriptCodeArrayVariable(ARRAY_BLOCK *psArrayCode, // Code for the array index VAR_SYMBOL *psVar, // The array variable symbol ARRAY_BLOCK **ppsBlock) // Generated code { ASSERT( psArrayCode != NULL, "scriptCodeObjectVariable: Invalid object code block pointer" ); ASSERT( psArrayCode->pCode != NULL, "scriptCodeObjectVariable: Invalid object code pointer" ); ASSERT( psVar != NULL, "scriptCodeObjectVariable: Invalid variable symbol pointer" ); ASSERT( ppsBlock != NULL, "scriptCodeObjectVariable: Invalid generated code block pointer" ); /* ALLOC_ARRAYBLOCK(*ppsBlock, psArrayCode->size, psVar); ip = (*ppsBlock)->pCode; // Copy the already generated bit of code into the code block PUT_BLOCK(ip, psArrayCode); FREE_BLOCK(psArrayCode);*/ // Check the variable is the correct type if (psVar->dimensions != psArrayCode->dimensions) { scr_error("Invalid number of array dimensions for this variable"); return CE_PARSE; } psArrayCode->psArrayVar = psVar; *ppsBlock = psArrayCode; return CE_OK; } /* Generate code for a constant */ static CODE_ERROR scriptCodeConstant(CONST_SYMBOL *psConst, // The object variable symbol CODE_BLOCK **ppsBlock) // Generated code { ASSERT( psConst != NULL, "scriptCodeConstant: Invalid constant symbol pointer" ); ASSERT( ppsBlock != NULL, "scriptCodeConstant: Invalid generated code block pointer" ); //ALLOC_BLOCK(*ppsBlock, sizeof(OPCODE) + sizeof(UDWORD)); ALLOC_BLOCK(*ppsBlock, 1 + 1); //OP_PUSH opcode + variable value ip = (*ppsBlock)->pCode; (*ppsBlock)->type = psConst->type; /* Put the value onto the stack */ switch (psConst->type) { case VAL_FLOAT: RULE(" scriptCodeConstant: pushing float"); PUT_PKOPCODE(ip, OP_PUSH, VAL_FLOAT); PUT_DATA_FLOAT(ip, psConst->fval); break; case VAL_BOOL: RULE(" scriptCodeConstant: pushing bool"); PUT_PKOPCODE(ip, OP_PUSH, VAL_BOOL); PUT_DATA_BOOL(ip, psConst->bval); break; case VAL_INT: RULE(" scriptCodeConstant: pushing int"); PUT_PKOPCODE(ip, OP_PUSH, VAL_INT); PUT_DATA_INT(ip, psConst->ival); break; case VAL_STRING: RULE(" scriptCodeConstant: pushing string"); PUT_PKOPCODE(ip, OP_PUSH, VAL_STRING); PUT_DATA_STRING(ip, psConst->sval); break; default: /* Object/user constants */ RULE(" scriptCodeConstant: pushing object/ user var (type: %d)", psConst->type); PUT_PKOPCODE(ip, OP_PUSH, psConst->type); PUT_OBJECT_CONST(ip, psConst->oval, psConst->type); //TODO: differentiate types break; } return CE_OK; } /* Generate code for getting a variables value */ static CODE_ERROR scriptCodeVarGet(VAR_SYMBOL *psVariable, // The object variable symbol CODE_BLOCK **ppsBlock) // Generated code { SDWORD size = 1; //1 - for opcode if (psVariable->storage == ST_EXTERN) { // Check there is a set function if (psVariable->get == NULL) { scr_error("No get function for external variable"); return CE_PARSE; } size++; } ALLOC_BLOCK(*ppsBlock, size); ip = (*ppsBlock)->pCode; (*ppsBlock)->type = psVariable->type; /* Code to get the value onto the stack */ switch (psVariable->storage) { case ST_PUBLIC: case ST_PRIVATE: PUT_PKOPCODE(ip, OP_PUSHGLOBAL, psVariable->index); break; case ST_LOCAL: PUT_PKOPCODE(ip, OP_PUSHLOCAL, psVariable->index); //opcode + event index break; case ST_EXTERN: PUT_PKOPCODE(ip, OP_VARCALL, psVariable->index); PUT_VARFUNC(ip, psVariable->get); break; case ST_OBJECT: scr_error("Cannot use member variables in this context"); return CE_PARSE; break; default: scr_error("Unknown storage type"); return CE_PARSE; break; } return CE_OK; } /* Code Increment/Decrement operators */ static CODE_ERROR scriptCodeIncDec(VAR_SYMBOL *psVariable, // The object variable symbol CODE_BLOCK **ppsBlock, OPCODE op_dec_inc) { ALLOC_BLOCK(*ppsBlock, 1 + 1 + 1); //OP_PUSHREF opcode + variable index + inc opcode ip = (*ppsBlock)->pCode; (*ppsBlock)->type = psVariable->type; /* Put variable index */ switch (psVariable->storage) { case ST_PRIVATE: PUT_PKOPCODE(ip, OP_PUSHREF, psVariable->type | VAL_REF); PUT_DATA_INT(ip, psVariable->index); break; case ST_LOCAL: PUT_PKOPCODE(ip, OP_PUSHLOCALREF, psVariable->type | VAL_REF); PUT_DATA_INT(ip, psVariable->index); break; default: scr_error("Wrong variable storage type for increment/decrement operator"); break; } /* Put inc/dec opcode */ PUT_PKOPCODE(ip, OP_UNARYOP, op_dec_inc); return CE_OK; } /* Generate code for getting a variables value */ static CODE_ERROR scriptCodeVarRef(VAR_SYMBOL *psVariable, // The object variable symbol PARAM_BLOCK **ppsBlock) // Generated code { SDWORD size; //size = sizeof(OPCODE) + sizeof(SDWORD); size = 1 + 1; //OP_PUSHREF opcode + variable index ALLOC_PBLOCK(*ppsBlock, size, 1); ip = (*ppsBlock)->pCode; (*ppsBlock)->aParams[0] = psVariable->type | VAL_REF; /* Code to get the value onto the stack */ switch (psVariable->storage) { case ST_PUBLIC: case ST_PRIVATE: PUT_PKOPCODE(ip, OP_PUSHREF, (*ppsBlock)->aParams[0]); PUT_DATA_INT(ip, psVariable->index); //TODO: add new macro break; case ST_LOCAL: PUT_PKOPCODE(ip, OP_PUSHLOCALREF, (*ppsBlock)->aParams[0]); PUT_DATA_INT(ip, psVariable->index); break; case ST_EXTERN: scr_error("Cannot use external variables in this context"); return CE_PARSE; break; case ST_OBJECT: scr_error("Cannot use member variables in this context"); return CE_PARSE; break; default: scr_error("Unknown storage type: %d", psVariable->storage); return CE_PARSE; break; } return CE_OK; } #ifdef UNUSED /* Generate the code for a trigger and store it in the trigger list */ static CODE_ERROR scriptCodeTrigger(char *pIdent, CODE_BLOCK *psCode) { CODE_BLOCK *psNewBlock; UDWORD line; char *pDummy; // Have to add the exit code to the end of the event //ALLOC_BLOCK(psNewBlock, psCode->size + sizeof(OPCODE)); ALLOC_BLOCK(psNewBlock, psCode->size + 1); //size + opcode ip = psNewBlock->pCode; PUT_BLOCK(ip, psCode); PUT_OPCODE(ip, OP_EXIT); // Add the debug info ALLOC_DEBUG(psNewBlock, psCode->debugEntries + 1); PUT_DEBUG(psNewBlock, psCode); if (genDebugInfo) { /* Add debugging info for the EXIT instruction */ scriptGetErrorData((SDWORD *)&line, &pDummy); psNewBlock->psDebug[psNewBlock->debugEntries].line = line; psNewBlock->psDebug[psNewBlock->debugEntries].offset = ip - psNewBlock->pCode; psNewBlock->debugEntries ++; } FREE_BLOCK(psCode); // Create the trigger /* if (!scriptAddTrigger(pIdent, psNewBlock)) { return CE_MEMORY; }*/ return CE_OK; } #endif #ifdef UNUSED /* Generate the code for an event and store it in the event list */ static CODE_ERROR scriptCodeEvent(EVENT_SYMBOL *psEvent, TRIGGER_SYMBOL *psTrig, CODE_BLOCK *psCode) { CODE_BLOCK *psNewBlock; UDWORD line; char *pDummy; // Have to add the exit code to the end of the event //ALLOC_BLOCK(psNewBlock, psCode->size + sizeof(OPCODE)); ALLOC_BLOCK(psNewBlock, psCode->size + 1); //size + opcode ip = psNewBlock->pCode; PUT_BLOCK(ip, psCode); PUT_OPCODE(ip, OP_EXIT); // Add the debug info ALLOC_DEBUG(psNewBlock, psCode->debugEntries + 1); PUT_DEBUG(psNewBlock, psCode); if (genDebugInfo) { /* Add debugging info for the EXIT instruction */ scriptGetErrorData((SDWORD *)&line, &pDummy); psNewBlock->psDebug[psNewBlock->debugEntries].line = line; psNewBlock->psDebug[psNewBlock->debugEntries].offset = ip - psNewBlock->pCode; psNewBlock->debugEntries ++; } FREE_BLOCK(psCode); // Create the event if (!scriptDefineEvent(psEvent, psNewBlock, psTrig->index)) { return CE_MEMORY; } return CE_OK; } #endif #ifdef UNUSED /* Store the types of a list of variables into a code block. * The order of the list is reversed so that the type of the * first variable defined is stored first. */ static void scriptStoreVarTypes(VAR_SYMBOL *psVar) { if (psVar != NULL) { /* Recurse down the list to get to the end of it */ scriptStoreVarTypes(psVar->psNext); /* Now store the current variable */ PUT_INDEX(ip, psVar->type); } } #endif /* Change the error action for the ALLOC macro's to what it * should be inside a rule body. * * NOTE: DO NOT USE THE ALLOC MACRO'S INSIDE ANY FUNCTIONS * ONCE ALLOC_ERROR_ACTION IS SET TO THIS VALUE. * ALL FUNCTIONS THAT USE THESE MACROS MUST BE PLACED * BEFORE THIS #define. */ #undef ALLOC_ERROR_ACTION #define ALLOC_ERROR_ACTION YYABORT %} %name-prefix="scr_" %union { /* Types returned by the lexer */ BOOL bval; float fval; SDWORD ival; char *sval; INTERP_TYPE tval; STORAGE_TYPE stype; VAR_SYMBOL *vSymbol; CONST_SYMBOL *cSymbol; FUNC_SYMBOL *fSymbol; TRIGGER_SYMBOL *tSymbol; EVENT_SYMBOL *eSymbol; CALLBACK_SYMBOL *cbSymbol; /* Types only returned by rules */ CODE_BLOCK *cblock; COND_BLOCK *condBlock; OBJVAR_BLOCK *objVarBlock; ARRAY_BLOCK *arrayBlock; PARAM_BLOCK *pblock; PARAM_DECL *pdecl; TRIGGER_DECL *tdecl; UDWORD integer_val; VAR_DECL *vdecl; VAR_IDENT_DECL *videcl; } /* key words */ %token lexFUNCTION %token TRIGGER %token EVENT %token WAIT %token EVERY %token INACTIVE %token INITIALISE %token LINK %token REF %token RET %token _VOID /* %token COND */ %token WHILE %token IF %token ELSE %token EXIT %token PAUSE /* boolean operators */ %token BOOLEQUAL %token NOTEQUAL %token GREATEQUAL %token LESSEQUAL %token GREATER %token LESS %token _AND %token _OR %token _NOT %token _INC %token _DEC %left _AND _OR %left BOOLEQUAL NOTEQUAL GREATEQUAL LESSEQUAL GREATER LESS %nonassoc _NOT %left '-' '+' '&' %left '*' '/' %right TO_INT_CAST %right TO_FLOAT_CAST %nonassoc UMINUS %left _INC _DEC /* value tokens */ %token BOOLEAN_T %token FLOAT_T %token INTEGER %token QTEXT /* Text with double quotes surrounding it */ %token TYPE /* A type specifier */ %token STORAGE /* Variable storage type */ %token IDENT /* An undefined identifier */ %token VAR /* A defined variable */ %token BOOL_VAR /* A defined boolean variable */ %token NUM_VAR /* A defined numeric (int) variable */ %token FLOAT_VAR /* A defined numeric (float) variable */ %token OBJ_VAR /* A defined object pointer variable */ %token STRING_VAR /* A defined string variable */ %token VAR_ARRAY /* A defined variable array */ %token BOOL_ARRAY /* A defined boolean variable array */ %token NUM_ARRAY /* A defined numeric (int) variable array */ %token FLOAT_ARRAY /* A defined numeric (float) variable array */ %token OBJ_ARRAY /* A defined object pointer variable array */ %token BOOL_OBJVAR /* A defined boolean object member variable */ %token NUM_OBJVAR /* A defined numeric object member variable */ %token USER_OBJVAR /* A defined user type object member variable */ %token OBJ_OBJVAR /* A defined object pointer object member variable */ %token BOOL_CONSTANT /* A defined boolean constant */ %token NUM_CONSTANT /* A defined numeric constant */ %token USER_CONSTANT /* A defined object pointer constant */ %token OBJ_CONSTANT /* A defined object pointer constant */ %token STRING_CONSTANT /* A defined string constant */ %token FUNC /* A defined function */ %token BOOL_FUNC /* A defined boolean function */ %token NUM_FUNC /* A defined numeric (int) function */ %token FLOAT_FUNC /* A defined numeric (float) function */ %token USER_FUNC /* A defined user defined type function */ %token OBJ_FUNC /* A defined object pointer function */ %token STRING_FUNC /* A defined string function */ /* custom, in-script defined functions (events actually) */ %token VOID_FUNC_CUST /* A defined function */ %token BOOL_FUNC_CUST /* A defined boolean function */ %token NUM_FUNC_CUST /* A defined numeric (int) function */ %token FLOAT_FUNC_CUST /* A defined numeric (float) function */ %token USER_FUNC_CUST /* A defined user defined type function */ %token OBJ_FUNC_CUST /* A defined object pointer function */ %token STRING_FUNC_CUST /* A defined string function */ %token TRIG_SYM /* A defined trigger */ %token EVENT_SYM /* A defined event */ %token CALLBACK_SYM /* A callback trigger */ /* rule types */ %type expression %type boolexp %type stringexp %type floatexp %type return_exp %type return_statement %type objexp %type objexp_dot %type userexp %type inc_dec_exp %type num_objvar %type bool_objvar %type user_objvar %type obj_objvar %type array_index %type array_index_list %type num_array_var %type bool_array_var %type user_array_var %type obj_array_var %type statement_list %type statement %type assignment %type func_call %type param_list /* %type braced_param_list */ /* for jump(), to make optional braced parameter list for function calls */ %type parameter %type var_ref %type conditional %type cond_clause %type terminal_cond %type cond_clause_list %type loop %type array_sub_decl %type array_sub_decl_list %type variable_ident %type variable_decl_head %type variable_decl %type var_line %type trigger_subdecl %type funcbody_var_def_body %type funcbody_var_def %type void_funcbody_var_def /*%type func_decl_ret_type */ /* for function declarations and definitions */ %type funcvar_decl_types %type event_subdecl %type func_subdecl /* function declaration */ %type function_def %type void_function_def %type void_func_subdecl /* void function declaration */ %type function_declaration %type void_function_declaration %type function_type %% /* The final accepting rule, must always be the first rule, if not explicitly defined as accepting rule */ accept_script: script { unsigned int i, numArrays; SDWORD size = 0, debug_i = 0, totalArraySize = 0; UDWORD base; VAR_SYMBOL *psCurr; TRIGGER_SYMBOL *psTrig; EVENT_SYMBOL *psEvent; UDWORD numVars; RULE("script: var_list"); // Calculate the code size for(psTrig = psTriggers; psTrig; psTrig = psTrig->psNext) { // Add the trigger code size size += psTrig->size; debug_i += psTrig->debugEntries; } for(psEvent = psEvents; psEvent; psEvent = psEvent->psNext) { // Add the trigger code size size += psEvent->size; debug_i += psEvent->debugEntries; } // Allocate the program numVars = psGlobalVars ? psGlobalVars->index+1 : 0; numArrays = psGlobalArrays ? psGlobalArrays->index+1 : 0; for(psCurr=psGlobalArrays; psCurr; psCurr=psCurr->psNext) { unsigned int arraySize = 1, dimension; for(dimension = 0; dimension < psCurr->dimensions; dimension++) { arraySize *= psCurr->elements[dimension]; } totalArraySize += arraySize; } ALLOC_PROG(psFinalProg, size, psCurrBlock->pCode, numVars, numArrays, numTriggers, numEvents); //store local vars //allocate array for holding an array of local vars for each event psFinalProg->ppsLocalVars = (INTERP_TYPE **)malloc(sizeof(INTERP_TYPE*) * numEvents); psFinalProg->ppsLocalVarVal = NULL; psFinalProg->numLocalVars = (UDWORD *)malloc(sizeof(UDWORD) * numEvents); //how many local vars each event has psFinalProg->numParams = (UDWORD *)malloc(sizeof(UDWORD) * numEvents); //how many arguments each event has for(psEvent = psEvents, i = 0; psEvent; psEvent = psEvent->psNext, i++) { psEvent->numLocalVars = numEventLocalVars[i]; psFinalProg->numLocalVars[i] = numEventLocalVars[i]; //remember how many local vars this event has psFinalProg->numParams[i] = psEvent->numParams; //remember how many parameters this event has if(numEventLocalVars[i] > 0) { unsigned int j; INTERP_TYPE * pCurEvLocalVars = (INTERP_TYPE*)malloc(sizeof(INTERP_TYPE) * numEventLocalVars[i]); for(psCurr = psLocalVarsB[i], j = 0; psCurr != NULL; psCurr = psCurr->psNext, j++) { pCurEvLocalVars[numEventLocalVars[i] - j - 1] = psCurr->type; //save type, order is reversed } psFinalProg->ppsLocalVars[i] = pCurEvLocalVars; } else { psFinalProg->ppsLocalVars[i] = NULL; //this event has no local vars } } ALLOC_DEBUG(psFinalProg, debug_i); psFinalProg->debugEntries = 0; ip = psFinalProg->pCode; // Add the trigger code for(psTrig = psTriggers, i = 0; psTrig; psTrig = psTrig->psNext, i++) { // Store the trigger offset psFinalProg->pTriggerTab[i] = (UWORD)(ip - psFinalProg->pCode); if (psTrig->pCode != NULL) { // Store the label DEBUG_LABEL(psFinalProg, psFinalProg->debugEntries, psTrig->pIdent); // Store debug info APPEND_DEBUG(psFinalProg, ip - psFinalProg->pCode, psTrig); // Store the code PUT_BLOCK(ip, psTrig); psFinalProg->psTriggerData[i].code = true; } else { psFinalProg->psTriggerData[i].code = false; } // Store the data psFinalProg->psTriggerData[i].type = (UWORD)psTrig->type; psFinalProg->psTriggerData[i].time = psTrig->time; } // Note the end of the final trigger psFinalProg->pTriggerTab[i] = (UWORD)(ip - psFinalProg->pCode); // Add the event code for(psEvent = psEvents, i = 0; psEvent; psEvent = psEvent->psNext, i++) { // Check the event was declared and has a code body if (psEvent->pCode == NULL) { scr_error("Event %s declared without being defined", psEvent->pIdent); YYABORT; } // Store the event offset psFinalProg->pEventTab[i] = (UWORD)(ip - psFinalProg->pCode); // Store the trigger link psFinalProg->pEventLinks[i] = (SWORD)(psEvent->trigger); // Store the label DEBUG_LABEL(psFinalProg, psFinalProg->debugEntries, psEvent->pIdent); // Store debug info APPEND_DEBUG(psFinalProg, ip - psFinalProg->pCode, psEvent); // Store the code PUT_BLOCK(ip, psEvent); } // Note the end of the final event psFinalProg->pEventTab[i] = (UWORD)(ip - psFinalProg->pCode); // Allocate debug info for the variables if necessary if (genDebugInfo) { if (numVars > 0) { psFinalProg->psVarDebug = malloc(sizeof(VAR_DEBUG) * numVars); if (psFinalProg->psVarDebug == NULL) { scr_error("Out of memory"); YYABORT; } } else { psFinalProg->psVarDebug = NULL; } if (numArrays > 0) { psFinalProg->psArrayDebug = malloc(sizeof(ARRAY_DEBUG) * numArrays); if (psFinalProg->psArrayDebug == NULL) { scr_error("Out of memory"); YYABORT; } } else { psFinalProg->psArrayDebug = NULL; } } else { psFinalProg->psVarDebug = NULL; psFinalProg->psArrayDebug = NULL; } /* Now set the types for the global variables */ for(psCurr = psGlobalVars; psCurr != NULL; psCurr = psCurr->psNext) { unsigned int i = psCurr->index; psFinalProg->pGlobals[i] = psCurr->type; if (genDebugInfo) { psFinalProg->psVarDebug[i].pIdent = strdup(psCurr->pIdent); if (psFinalProg->psVarDebug[i].pIdent == NULL) { scr_error("Out of memory"); YYABORT; } psFinalProg->psVarDebug[i].storage = psCurr->storage; } } /* Now store the array info */ psFinalProg->arraySize = totalArraySize; for(psCurr = psGlobalArrays; psCurr != NULL; psCurr = psCurr->psNext) { unsigned int i = psCurr->index, dimension; psFinalProg->psArrayInfo[i].type = (UBYTE)psCurr->type; psFinalProg->psArrayInfo[i].dimensions = (UBYTE)psCurr->dimensions; for(dimension = 0; dimension < psCurr->dimensions; dimension++) { psFinalProg->psArrayInfo[i].elements[dimension] = (UBYTE)psCurr->elements[dimension]; } if (genDebugInfo) { psFinalProg->psArrayDebug[i].pIdent = strdup(psCurr->pIdent); if (psFinalProg->psArrayDebug[i].pIdent == NULL) { scr_error("Out of memory"); YYABORT; } psFinalProg->psArrayDebug[i].storage = psCurr->storage; } } // calculate the base index of each array base = psFinalProg->numGlobals; for(i=0; ipsArrayInfo[i].base = base; for(dimension = 0; dimension < psFinalProg->psArrayInfo[i].dimensions; dimension++) { arraySize *= psFinalProg->psArrayInfo[i].elements[dimension]; } base += arraySize; } RULE("END script: var_list"); } ; script: var_list trigger_list { RULE("script:var_list trigger_list"); } | script event_list { RULE("script:script event_list"); } | script var_list { RULE("script:script var_list"); } | script trigger_list { RULE("script:script trigger_list"); } ; var_list: /* NULL token */ { RULE("var_list: NULL"); } | var_line { RULE("var_list: var_line"); FREE_VARDECL($1); } | var_list var_line { FREE_VARDECL($2); } ; /************************************************************************************** * * Variable and function declarations */ var_line: variable_decl ';' { /* remember that local var declaration is over */ localVariableDef = false; //debug(LOG_SCRIPT, "localVariableDef = false 0"); $$ = $1; } ; variable_decl_head: STORAGE TYPE { //debug(LOG_SCRIPT, "variable_decl_head: STORAGE TYPE"); ALLOC_VARDECL(psCurrVDecl); psCurrVDecl->storage = $1; psCurrVDecl->type = $2; /* allow local vars to have the same names as global vars (don't check global vars) */ if($1 == ST_LOCAL) { localVariableDef = true; //debug(LOG_SCRIPT, "localVariableDef = true 0"); } $$ = psCurrVDecl; //debug(LOG_SCRIPT, "END variable_decl_head: STORAGE TYPE (TYPE=%d)", $2); } | STORAGE TRIGGER { ALLOC_VARDECL(psCurrVDecl); psCurrVDecl->storage = $1; psCurrVDecl->type = VAL_TRIGGER; $$ = psCurrVDecl; } | STORAGE EVENT { ALLOC_VARDECL(psCurrVDecl); psCurrVDecl->storage = $1; psCurrVDecl->type = VAL_EVENT; $$ = psCurrVDecl; } ; array_sub_decl: '[' INTEGER ']' { if ($2 <= 0 || $2 >= VAR_MAX_ELEMENTS) { scr_error("Invalid array size %d", $2); YYABORT; } ALLOC_VARIDENTDECL(psCurrVIdentDecl, NULL, 1); psCurrVIdentDecl->elements[0] = $2; $$ = psCurrVIdentDecl; } ; array_sub_decl_list: array_sub_decl { $$ = $1; } | array_sub_decl_list '[' INTEGER ']' { if ($1->dimensions >= VAR_MAX_DIMENSIONS) { scr_error("Too many dimensions for array"); YYABORT; } if ($3 <= 0 || $3 >= VAR_MAX_ELEMENTS) { scr_error("Invalid array size %d", $3); YYABORT; } $1->elements[$1->dimensions] = $3; $1->dimensions += 1; $$ = $1; } ; variable_ident: IDENT { ALLOC_VARIDENTDECL(psCurrVIdentDecl, $1, 0); $$ = psCurrVIdentDecl; } | IDENT array_sub_decl_list { $2->pIdent = strdup($1); if ($2->pIdent == NULL) { scr_error("Out of memory"); YYABORT; } $$ = $2; } ; variable_decl: variable_decl_head variable_ident { if (!scriptAddVariable($1, $2)) { /* Out of memory - error already given */ YYABORT; } freeVARIDENTDECL($2); /* return the variable type */ $$ = $1; } | variable_decl ',' variable_ident { if (!scriptAddVariable($1, $3)) { /* Out of memory - error already given */ YYABORT; } freeVARIDENTDECL($3); /* return the variable type */ $$ = $1; } ; /************************************************************************************** * * Trigger declarations */ trigger_list: /* NULL token */ | trigger_decl | trigger_list trigger_decl ; trigger_subdecl: boolexp ',' INTEGER { ALLOC_TSUBDECL(psCurrTDecl, TR_CODE, $1->size, $3); ip = psCurrTDecl->pCode; PUT_BLOCK(ip, $1); FREE_BLOCK($1); $$ = psCurrTDecl; } | WAIT ',' INTEGER { ALLOC_TSUBDECL(psCurrTDecl, TR_WAIT, 0, $3); $$ = psCurrTDecl; } | EVERY ',' INTEGER { ALLOC_TSUBDECL(psCurrTDecl, TR_EVERY, 0, $3); $$ = psCurrTDecl; } | INITIALISE { ALLOC_TSUBDECL(psCurrTDecl, TR_INIT, 0, 0); $$ = psCurrTDecl; } | CALLBACK_SYM { if ($1->numParams != 0) { scr_error("Expected parameters for callback trigger"); YYABORT; } ALLOC_TSUBDECL(psCurrTDecl, $1->type, 0, 0); $$ = psCurrTDecl; } | CALLBACK_SYM ',' param_list { RULE("trigger_subdecl: CALLBACK_SYM ',' param_list"); codeRet = scriptCodeCallbackParams($1, $3, &psCurrTDecl); CHECK_CODE_ERROR(codeRet); $$ = psCurrTDecl; } ; trigger_decl: TRIGGER IDENT '(' trigger_subdecl ')' ';' { SDWORD line; char *pDummy; scriptGetErrorData(&line, &pDummy); if (!scriptAddTrigger($2, $4, (UDWORD)line)) { YYABORT; } FREE_TSUBDECL($4); } ; /************************************************************************************** * * Event declarations */ event_list: event_decl | event_list event_decl ; event_subdecl: EVENT IDENT { EVENT_SYMBOL *psEvent; RULE("event_subdecl: EVENT IDENT"); if (!scriptDeclareEvent($2, &psEvent,0)) { YYABORT; } psCurEvent = psEvent; $$ = psEvent; //debug(LOG_SCRIPT, "END event_subdecl: EVENT IDENT"); } | EVENT EVENT_SYM { RULE("EVENT EVENT_SYM"); psCurEvent = $2; $$ = $2; //debug(LOG_SCRIPT, "END EVENT EVENT_SYM"); } ; function_def: lexFUNCTION TYPE function_type { RULE("function_def: lexFUNCTION TYPE function_type"); psCurEvent = $3; /* check if this event was declared as function before */ if(!$3->bFunction) { debug(LOG_ERROR, "'%s' was declared as event before and can't be redefined to function", $3->pIdent); scr_error("Wrong event definition"); YYABORT; } $$ = $3; } ; function_type: VOID_FUNC_CUST | BOOL_FUNC_CUST | NUM_FUNC_CUST | USER_FUNC_CUST | OBJ_FUNC_CUST | STRING_FUNC_CUST | FLOAT_FUNC_CUST ; /* function declaration rules */ func_subdecl: lexFUNCTION TYPE IDENT { EVENT_SYMBOL *psEvent; RULE("func_subdecl: lexFUNCTION TYPE IDENT"); /* allow local vars to have the same names as global vars (don't check global vars) */ localVariableDef = true; //debug(LOG_SCRIPT, "localVariableDef = true 1"); if (!scriptDeclareEvent($3, &psEvent,0)) { YYABORT; } psEvent->retType = $2; psCurEvent = psEvent; psCurEvent->bFunction = true; $$ = psEvent; //debug(LOG_SCRIPT, "END func_subdecl:lexFUNCTION TYPE IDENT. "); } ; /* void function declaration rules */ void_func_subdecl: lexFUNCTION _VOID IDENT /* declaration of a function */ { EVENT_SYMBOL *psEvent; RULE("void_func_subdecl: lexFUNCTION _VOID IDENT"); /* allow local vars to have the same names as global vars (don't check global vars) */ localVariableDef = true; //debug(LOG_SCRIPT, "localVariableDef = true 1"); if (!scriptDeclareEvent($3, &psEvent,0)) { YYABORT; } psEvent->retType = VAL_VOID; psCurEvent = psEvent; psCurEvent->bFunction = true; $$ = psEvent; //debug(LOG_SCRIPT, "END func_subdecl:lexFUNCTION TYPE IDENT. "); } ; void_function_def: lexFUNCTION _VOID function_type /* definition of a function that was declated before */ { //debug(LOG_SCRIPT, "func_subdecl:lexFUNCTION EVENT_SYM "); psCurEvent = $3; /* check if this event was declared as function before */ if(!$3->bFunction) { debug(LOG_ERROR, "'%s' was declared as event before and can't be redefined to function", $3->pIdent); scr_error("Wrong event definition"); YYABORT; } /* psCurEvent->bFunction = true; */ /* psEvent->retType = $2; */ $$ = $3; //debug(LOG_SCRIPT, "func_subdecl:lexFUNCTION EVENT_SYM. "); } ; funcvar_decl_types: TYPE NUM_VAR { $$=$1; } | TYPE FLOAT_VAR { $$=$1; } | TYPE BOOL_VAR { $$=$1; } | TYPE STRING_VAR { $$=$1; } | TYPE OBJ_VAR { $$=$1; } | TYPE OBJ_OBJVAR { $$=$1; } | TYPE OBJ_ARRAY { $$=$1; } | TYPE VAR /* STRUCTURESTAT, RESEARCHSTAT etc */ { $$=$1; } ; funcbody_var_def_body: funcvar_decl_types { if(!checkFuncParamType(0, $1)) { scr_error("Wrong event argument definition in '%s'", psCurEvent->pIdent); YYABORT; } //debug(LOG_SCRIPT, "funcbody_var_def_body=%d \n", $1); $$=1; } | funcbody_var_def_body ',' funcvar_decl_types { if(!checkFuncParamType($1, $3)) { scr_error("Wrong event argument definition"); YYABORT; } //debug(LOG_SCRIPT, "funcbody_var_def_body2=%d \n", $3); $$=$1+1; } ; /* function was declared before */ funcbody_var_def: function_def '(' funcbody_var_def_body ')' { RULE("funcbody_var_def: '(' funcvar_decl_types ')'"); /* remember that local var declaration is over */ localVariableDef = false; if(psCurEvent == NULL) { scr_error("psCurEvent == NULL"); YYABORT; } if(!psCurEvent->bDeclared) { debug(LOG_ERROR, "Event %s's definition doesn't match with declaration.", psCurEvent->pIdent); scr_error("Wrong event definition:\n event %s's definition doesn't match with declaration", psCurEvent->pIdent); YYABORT; } /* check if number of parameter in body defenition match number of params in the declaration */ if($3 != psCurEvent->numParams) { scr_error("Wrong number of arguments in function definition (or declaration-definition argument type/names mismatch) \n in event: '%s'", psCurEvent->pIdent); YYABORT; } $$ = $1; } | function_def '(' ')' { RULE( "funcbody_var_def: funcbody_var_def_body '(' ')'"); /* remember that local var declaration is over */ localVariableDef = false; if(psCurEvent == NULL) { scr_error("psCurEvent == NULL"); YYABORT; } if(!psCurEvent->bDeclared) { debug(LOG_ERROR, "Event %s's definition doesn't match with declaration.", psCurEvent->pIdent); scr_error("Wrong event definition:\n event %s's definition doesn't match with declaration", psCurEvent->pIdent); YYABORT; } /* check if number of parameter in body defenition match number of params in the declaration */ if(0 != psCurEvent->numParams) { scr_error("Wrong number of arguments in function definition (or declaration-definition argument type/names mismatch) \n in event: '%s'", psCurEvent->pIdent); YYABORT; } $$ = $1; } ; /* void function was declared before */ void_funcbody_var_def: void_function_def '(' funcbody_var_def_body ')' { RULE( "funcbody_var_def: '(' funcvar_decl_types ')'"); /* remember that local var declaration is over */ localVariableDef = false; if(psCurEvent == NULL) { scr_error("psCurEvent == NULL"); YYABORT; } if(!psCurEvent->bDeclared) { debug(LOG_ERROR, "Event %s's definition doesn't match with declaration.", psCurEvent->pIdent); scr_error("Wrong event definition:\n event %s's definition doesn't match with declaration", psCurEvent->pIdent); YYABORT; } /* check if number of parameter in body defenition match number of params in the declaration */ if($3 != psCurEvent->numParams) { scr_error("Wrong number of arguments in function definition (or declaration-definition argument type/names mismatch) \n in event: '%s'", psCurEvent->pIdent); YYABORT; } $$ = $1; } | void_function_def '(' ')' { RULE( "funcbody_var_def: funcbody_var_def_body '(' ')'"); /* remember that local var declaration is over */ localVariableDef = false; if(psCurEvent == NULL) { scr_error("psCurEvent == NULL"); YYABORT; } if(!psCurEvent->bDeclared) { debug(LOG_ERROR, "Event %s's definition doesn't match with declaration.", psCurEvent->pIdent); scr_error("Wrong event definition:\n event %s's definition doesn't match with declaration", psCurEvent->pIdent); YYABORT; } /* check if number of parameter in body defenition match number of params in the declaration */ if(0 != psCurEvent->numParams) { scr_error("Wrong number of arguments in function definition (or declaration-definition argument type/names mismatch) \n in event: '%s'", psCurEvent->pIdent); YYABORT; } $$ = $1; } ; /* event declaration with parameters, like: event myEvent(int Arg1, string Arg2); */ argument_decl_head: TYPE variable_ident { RULE( "argument_decl_head: TYPE variable_ident"); /* handle type part */ ALLOC_VARDECL(psCurrVDecl); psCurrVDecl->storage =ST_LOCAL; /* can only be local */ psCurrVDecl->type = $1; /* handle ident part */ if (!scriptAddVariable(psCurrVDecl, $2)) { YYABORT; } freeVARIDENTDECL($2); FREE_VARDECL(psCurrVDecl); /* return the variable type */ /* $$ = psCurrVDecl; */ /* not needed? */ if(psCurEvent == NULL) debug(LOG_ERROR, "argument_decl_head 0: - psCurEvent == NULL"); psCurEvent->numParams = psCurEvent->numParams + 1; /* remember a parameter was declared */ /* remember parameter type */ psCurEvent->aParams[0] = $1; //debug(LOG_SCRIPT, "argument_decl_head 0. "); } | argument_decl_head ',' TYPE variable_ident { //debug(LOG_SCRIPT, "argument_decl_head 1 "); /* handle type part */ ALLOC_VARDECL(psCurrVDecl); psCurrVDecl->storage =ST_LOCAL; /* can only be local */ psCurrVDecl->type = $3; /* remember parameter type */ psCurEvent->aParams[psCurEvent->numParams] = $3; //debug(LOG_SCRIPT, "argument_decl_head 10 "); /* handle ident part */ if (!scriptAddVariable(psCurrVDecl, $4)) { YYABORT; } //debug(LOG_SCRIPT, "argument_decl_head 11 "); freeVARIDENTDECL($4); FREE_VARDECL(psCurrVDecl); /* return the variable type */ /* $$ = psCurrVDecl; */ /* not needed? */ if(psCurEvent == NULL) debug(LOG_ERROR, "argument_decl_head 0: - psCurEvent == NULL"); psCurEvent->numParams = psCurEvent->numParams + 1; /* remember a parameter was declared */ //debug(LOG_SCRIPT, "argument_decl_head 1. "); } ; argument_decl: '(' ')' | '(' argument_decl_head ')' { /* remember that local var declaration is over */ localVariableDef = false; //debug(LOG_SCRIPT, "localVariableDef = false 1"); } ; function_declaration: func_subdecl '(' argument_decl_head ')' /* function was not declared before */ { RULE( "function_declaration: func_subdecl '(' argument_decl_head ')'"); /* remember that local var declaration is over */ localVariableDef = false; //debug(LOG_SCRIPT, "localVariableDef = false 2"); if(psCurEvent->bDeclared) /* can only occur if different (new) var names are used in the event definition that don't match with declaration*/ { scr_error("Wrong event definition: \nEvent %s's definition doesn't match with declaration", psCurEvent->pIdent); YYABORT; } $$ = $1; } | func_subdecl '(' ')' { RULE( "function_declaration: func_subdecl '(' ')'"); /* remember that local var declaration is over */ localVariableDef = false; //debug(LOG_SCRIPT, "localVariableDef = false 3"); if($1->numParams > 0 && psCurEvent->bDeclared) /* can only occur if no parameters or different (new) var names are used in the event definition that don't match with declaration */ { scr_error("Wrong event definition: \nEvent %s's definition doesn't match with declaration", psCurEvent->pIdent); YYABORT; } $$ = $1; } ; void_function_declaration: void_func_subdecl '(' argument_decl_head ')' /* function was not declared before */ { /* remember that local var declaration is over */ localVariableDef = false; //debug(LOG_SCRIPT, "localVariableDef = false 2"); if(psCurEvent->bDeclared) /* can only occur if different (new) var names are used in the event definition that don't match with declaration*/ { scr_error("Wrong event definition: \nEvent %s's definition doesn't match with declaration", psCurEvent->pIdent); YYABORT; } $$ = $1; } | void_func_subdecl '(' ')' { RULE( "void_function_declaration: void_func_subdecl '(' ')'"); /* remember that local var declaration is over */ localVariableDef = false; //debug(LOG_SCRIPT, "localVariableDef = false 3"); if($1->numParams > 0 && psCurEvent->bDeclared) /* can only occur if no parameters or different (new) var names are used in the event definition that don't match with declaration */ { scr_error("Wrong event definition: \nEvent %s's definition doesn't match with declaration", psCurEvent->pIdent); YYABORT; } $$ = $1; } ; return_statement_void: RET ';' ; return_statement: return_statement_void { RULE( "return_statement: return_statement_void"); if(psCurEvent == NULL) /* no events declared or defined yet */ { scr_error("return statement outside of function"); YYABORT; } if(!psCurEvent->bFunction) { scr_error("return statement inside of an event '%s'", psCurEvent->pIdent); YYABORT; } if(psCurEvent->retType != VAL_VOID) { scr_error("wrong return statement syntax for a non-void function '%s'", psCurEvent->pIdent); YYABORT; } /* Allocate code block for exit instruction */ ALLOC_BLOCK(psCurrBlock, 1); //1 for opcode ip = psCurrBlock->pCode; PUT_OPCODE(ip, OP_EXIT); psCurrBlock->type = VAL_VOID; /* make return statement of type VOID manually */ $$ = psCurrBlock; } | RET return_exp ';' { RULE( "return_statement: RET return_exp ';'"); if(psCurEvent == NULL) /* no events declared or defined yet */ { debug(LOG_ERROR, "return statement outside of function"); YYABORT; } if(!psCurEvent->bFunction) { debug(LOG_ERROR, "return statement inside of an event '%s'", psCurEvent->pIdent); YYABORT; } if(psCurEvent->retType != $2->type) { if(!interpCheckEquiv(psCurEvent->retType, $2->type)) { debug(LOG_ERROR, "return type mismatch"); debug(LOG_ERROR, "wrong return statement syntax for function '%s' (%d - %d)", psCurEvent->pIdent, psCurEvent->retType, $2->type); YYABORT; } } /* Allocate code block for exit instruction */ ALLOC_BLOCK(psCurrBlock, $2->size + 1); //+1 for OPCODE ip = psCurrBlock->pCode; PUT_BLOCK(ip, $2); PUT_OPCODE(ip, OP_EXIT); /* store the type of the exp */ psCurrBlock->type = $2->type; FREE_BLOCK($2); $$ = psCurrBlock; //debug(LOG_SCRIPT, "END RET return_exp ';'"); } ; statement_list: /* NULL token */ { RULE( "statement_list: NULL"); // Allocate a dummy code block ALLOC_BLOCK(psCurrBlock, 1); psCurrBlock->size = 0; $$ = psCurrBlock; } | statement { RULE("statement_list: statement"); $$ = $1; } | statement_list statement { RULE("statement_list: statement_list statement"); ALLOC_BLOCK(psCurrBlock, $1->size + $2->size); ALLOC_DEBUG(psCurrBlock, $1->debugEntries + $2->debugEntries); ip = psCurrBlock->pCode; /* Copy the two code blocks */ PUT_BLOCK(ip, $1); PUT_BLOCK(ip, $2); PUT_DEBUG(psCurrBlock, $1); APPEND_DEBUG(psCurrBlock, $1->size / sizeof(INTERP_VAL), $2); ASSERT($1 != NULL, "$1 == NULL"); ASSERT($1->psDebug != NULL, "psDebug == NULL"); FREE_DEBUG($1); FREE_DEBUG($2); FREE_BLOCK($1); FREE_BLOCK($2); /* Return the code block */ $$ = psCurrBlock; } ; event_decl: event_subdecl ';' { RULE( "event_decl: event_subdecl ';'"); psCurEvent->bDeclared = true; } | func_subdecl argument_decl ';' /* event (function) declaration can now include parameter declaration (optional) */ { //debug(LOG_SCRIPT, "localVariableDef = false new "); localVariableDef = false; psCurEvent->bDeclared = true; } | void_func_subdecl argument_decl ';' /* event (function) declaration can now include parameter declaration (optional) */ { RULE( "event_decl: void_func_subdecl argument_decl ';'"); //debug(LOG_SCRIPT, "localVariableDef = false new "); localVariableDef = false; psCurEvent->bDeclared = true; } | event_subdecl '(' TRIG_SYM ')' '{' var_list statement_list '}' /* 16.08.05 - local vars support */ { RULE( "event_decl: event_subdecl '(' TRIG_SYM ')'"); /* make sure this event is not declared as function */ if(psCurEvent->bFunction) { debug(LOG_ERROR, "Event '%s' is declared as function and can't have a trigger assigned", psCurEvent->pIdent); scr_error("Wrong event definition"); YYABORT; } /* pop required number of paramerets passed to this event (if any) */ /* popArguments($1, $7, &psCurrBlock); */ /* if (!scriptDefineEvent($1, $6, $3->index)) */ if (!scriptDefineEvent($1, $7, $3->index)) { YYABORT; } /* end of event */ psCurEvent = NULL; FREE_DEBUG($7); FREE_BLOCK($7); RULE( "END event_decl: event_subdecl '(' TRIG_SYM ')'"); } | event_subdecl '(' trigger_subdecl ')' { // Get the line for the implicit trigger declaration char *pDummy; RULE( "event_decl:event_subdecl '(' trigger_subdecl ')' "); /* make sure this event is not declared as function */ if(psCurEvent->bFunction) { debug(LOG_ERROR, "Event '%s' is declared as function and can't have a trigger assigned", psCurEvent->pIdent); scr_error("Wrong event definition"); YYABORT; } scriptGetErrorData((SDWORD *)&debugLine, &pDummy); } '{' var_list statement_list '}' /* local vars support */ { RULE("event_decl: '{' var_list statement_list '}'"); // Create a trigger for this event if (!scriptAddTrigger("", $3, debugLine)) { YYABORT; } FREE_TSUBDECL($3); /* if (!scriptDefineEvent($1, $7, numTriggers - 1)) */ if (!scriptDefineEvent($1, $8, numTriggers - 1)) { YYABORT; } /* end of event */ psCurEvent = NULL; FREE_DEBUG($8); FREE_BLOCK($8); RULE( "END event_decl:event_subdecl '(' trigger_subdecl ')' ."); } /* local vars */ | event_subdecl '(' INACTIVE ')' '{' var_list statement_list '}' { RULE( "event_subdecl '(' INACTIVE ')' '{' var_list statement_list '}'"); /* make sure this event is not declared as function */ if(psCurEvent->bFunction) { debug(LOG_ERROR, "Event '%s' is declared as function and can't have a trigger assigned", psCurEvent->pIdent); scr_error("Wrong event definition"); YYABORT; } if (!scriptDefineEvent($1, $7, -1)) { YYABORT; } /* end of event */ psCurEvent = NULL; FREE_DEBUG($7); FREE_BLOCK($7); RULE( "END event_subdecl '(' INACTIVE ')' '{' var_list statement_list '}'"); } /* VOID function that was NOT declared before */ | void_function_declaration '{' var_list statement_list '}' { RULE( "void_function_declaration '{' var_list statement_list '}'"); /* stays the same if no params (just gets copied) */ ALLOC_BLOCK(psCurrBlock, $4->size + 1 + $1->numParams); /* statement_list + opcode + numParams */ ip = psCurrBlock->pCode; /* pop required number of paramerets passed to this event (if any) */ popArguments(&ip, $1->numParams); /* Copy the two code blocks */ PUT_BLOCK(ip, $4); PUT_OPCODE(ip, OP_EXIT); /* must exit after return */ ALLOC_DEBUG(psCurrBlock, $4->debugEntries); PUT_DEBUG(psCurrBlock, $4); if (!scriptDefineEvent($1, psCurrBlock, -1)) { YYABORT; } FREE_DEBUG($4); FREE_BLOCK($4); /* end of event */ psCurEvent = NULL; /* free block since code was copied in scriptDefineEvent() */ FREE_DEBUG(psCurrBlock); FREE_BLOCK(psCurrBlock); RULE( "END void_function_declaration '{' var_list statement_list '}'"); } /* void function that was declared before */ | void_funcbody_var_def '{' var_list statement_list '}' { RULE( "void_funcbody_var_def '{' var_list statement_list '}'"); /* stays the same if no params (just gets copied) */ ALLOC_BLOCK(psCurrBlock, $4->size + 1 + $1->numParams); /* statements + opcode + numparams */ ip = psCurrBlock->pCode; /* pop required number of paramerets passed to this event (if any) */ popArguments(&ip, $1->numParams); /* Copy the old (main) code and free it */ PUT_BLOCK(ip, $4); PUT_OPCODE(ip, OP_EXIT); /* must exit after return */ /* copy debug info */ ALLOC_DEBUG(psCurrBlock, $4->debugEntries); PUT_DEBUG(psCurrBlock, $4); if (!scriptDefineEvent($1, psCurrBlock, -1)) { YYABORT; } FREE_DEBUG($4); FREE_BLOCK($4); FREE_DEBUG(psCurrBlock); FREE_BLOCK(psCurrBlock); /* end of event */ psCurEvent = NULL; RULE( "END void_func_subdecl '(' funcbody_var_def_body ')' '{' var_list statement_list '}'"); } /* function that was NOT declared before (atleast must look like this)!!! */ | function_declaration '{' var_list statement_list return_statement '}' { RULE( "function_declaration '{' var_list statement_list return_statement '}'"); /* stays the same if no params (just gets copied) */ //ALLOC_BLOCK(psCurrBlock, $4->size + $5->size + sizeof(OPCODE) + (sizeof(OPCODE) * $1->numParams)); /* statement_list + expression + EXIT + numParams */ ALLOC_BLOCK(psCurrBlock, $4->size + $5->size + 1 + $1->numParams); /* statement_list + return_expr + EXIT opcode + numParams */ ip = psCurrBlock->pCode; /* pop required number of paramerets passed to this event (if any) */ popArguments(&ip, $1->numParams); /* Copy the two code blocks */ PUT_BLOCK(ip, $4); PUT_BLOCK(ip, $5); PUT_OPCODE(ip, OP_EXIT); /* must exit after return */ ALLOC_DEBUG(psCurrBlock, $4->debugEntries); PUT_DEBUG(psCurrBlock, $4); if (!scriptDefineEvent($1, psCurrBlock, -1)) { YYABORT; } FREE_DEBUG($4); FREE_BLOCK($4); FREE_BLOCK($5); /* end of event */ psCurEvent = NULL; /* free block since code was copied in scriptDefineEvent() */ FREE_DEBUG(psCurrBlock); FREE_BLOCK(psCurrBlock); RULE( "END function_declaration '{' var_list statement_list return_statement '}'"); } /* function that WAS declared before */ | funcbody_var_def '{' var_list statement_list return_statement '}' { RULE( "func_subdecl '(' funcbody_var_def_body ')' '{' var_list statement_list return_statement '}'"); /* stays the same if no params (just gets copied) */ //ALLOC_BLOCK(psCurrBlock, $4->size + $5->size + sizeof(OPCODE) + (sizeof(OPCODE) * $1->numParams)); ALLOC_BLOCK(psCurrBlock, $4->size + $5->size + 1 + $1->numParams); /* statements + return expr + opcode + numParams */ ip = psCurrBlock->pCode; /* pop required number of paramerets passed to this event (if any) */ popArguments(&ip, $1->numParams); /* Copy the old (main) code and free it */ PUT_BLOCK(ip, $4); PUT_BLOCK(ip, $5); PUT_OPCODE(ip, OP_EXIT); /* must exit after return */ /* copy debug info */ ALLOC_DEBUG(psCurrBlock, $4->debugEntries); PUT_DEBUG(psCurrBlock, $4); RULE( "debug entries %d", $4->debugEntries); if (!scriptDefineEvent($1, psCurrBlock, -1)) { YYABORT; } FREE_DEBUG($4); FREE_BLOCK($4); FREE_BLOCK($5); FREE_DEBUG(psCurrBlock); FREE_BLOCK(psCurrBlock); /* end of event */ psCurEvent = NULL; RULE( "END func_subdecl '(' funcbody_var_def_body ')' '{' var_list statement_list return_statement '}'"); } /* function that WAS declared before with a single return statement */ | funcbody_var_def '{' var_list return_statement '}' { RULE( "funcbody_var_def '{' var_list return_statement '}'"); /* stays the same if no params (just gets copied) */ ALLOC_BLOCK(psCurrBlock, $4->size + 1 + $1->numParams); /* return expr + opcode + numParams */ ip = psCurrBlock->pCode; /* pop required number of paramerets passed to this event (if any) */ popArguments(&ip, $1->numParams); /* Copy the old (main) code and free it */ PUT_BLOCK(ip, $4); PUT_OPCODE(ip, OP_EXIT); /* must exit after return */ if (!scriptDefineEvent($1, psCurrBlock, -1)) { YYABORT; } FREE_BLOCK($4); FREE_BLOCK(psCurrBlock); psCurEvent = NULL; RULE( "END funcbody_var_def '{' var_list return_statement '}'"); } /* function with a single return statement that was NOT declared before */ | function_declaration '{' var_list return_statement '}' { RULE( "function_declaration '{' var_list statement_list return_statement '}'"); /* stays the same if no params (just gets copied) */ ALLOC_BLOCK(psCurrBlock, $4->size + 1 + $1->numParams); /* return_expr + EXIT opcode + numParams */ ip = psCurrBlock->pCode; /* pop required number of paramerets passed to this event (if any) */ popArguments(&ip, $1->numParams); /* Copy code block */ PUT_BLOCK(ip, $4); PUT_OPCODE(ip, OP_EXIT); /* must exit after return */ if (!scriptDefineEvent($1, psCurrBlock, -1)) { YYABORT; } FREE_BLOCK($4); psCurEvent = NULL; FREE_BLOCK(psCurrBlock); RULE( "END function_declaration '{' var_list statement_list return_statement '}'"); } ; /************************************************************************************** * * Statements */ statement: assignment ';' { UDWORD line; char *pDummy; RULE("statement: assignment"); /* Put in debugging info */ if (genDebugInfo) { ALLOC_DEBUG($1, 1); $1->psDebug[0].offset = 0; scriptGetErrorData((SDWORD *)&line, &pDummy); $1->psDebug[0].line = line; } $$ = $1; } | inc_dec_exp ';' { UDWORD line; char *pDummy; RULE("statement: inc_dec_exp"); /* Put in debugging info */ if (genDebugInfo) { ALLOC_DEBUG($1, 1); $1->psDebug[0].offset = 0; scriptGetErrorData((SDWORD *)&line, &pDummy); $1->psDebug[0].line = line; } $$ = $1; } | func_call ';' { UDWORD line; char *pDummy; RULE("statement: func_call ';'"); /* Put in debugging info */ if (genDebugInfo) { ALLOC_DEBUG($1, 1); $1->psDebug[0].offset = 0; scriptGetErrorData((SDWORD *)&line, &pDummy); $1->psDebug[0].line = line; } $$ = $1; } | VOID_FUNC_CUST '(' param_list ')' ';' { UDWORD line,paramNumber; char *pDummy; RULE( "statement: VOID_FUNC_CUST '(' param_list ')' ';'"); /* allow to call EVENTs to reuse the code only if no actual parameters are specified in function call, like "myEvent();" */ if(!$1->bFunction && $3->numParams > 0) { scr_error("Can't pass any parameters in an event call:\nEvent: '%s'", $1->pIdent); return CE_PARSE; } if($3->numParams != $1->numParams) { scr_error("Wrong number of arguments for function call: '%s'. Expected %d parameters instead of %d.", $1->pIdent, $1->numParams, $3->numParams); return CE_PARSE; } /* check if right parameters were passed */ paramNumber = checkFuncParamTypes($1, $3); if(paramNumber > 0) { debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter %d.", $1->pIdent, paramNumber); YYABORT; } /* Allocate the code block */ ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1); //paramList + opcode + event index ALLOC_DEBUG(psCurrBlock, 1); ip = psCurrBlock->pCode; if($3->numParams > 0) /* if any parameters declared */ { /* Copy in the code for the parameters */ PUT_BLOCK(ip, $3); } FREE_PBLOCK($3); /* Store the instruction */ PUT_OPCODE(ip, OP_FUNC); PUT_EVENT(ip,$1->index); //Put event index /* Add the debugging information */ if (genDebugInfo) { psCurrBlock->psDebug[0].offset = 0; scriptGetErrorData((SDWORD *)&line, &pDummy); psCurrBlock->psDebug[0].line = line; } $$ = psCurrBlock; } | EVENT_SYM '(' param_list ')' ';' /* event as a void function to reuse the code */ { UDWORD line; char *pDummy; RULE( "statement: EVENT_SYM '(' param_list ')' ';'"); /* allow to call EVENTs to reuse the code only if no actual parameters are specified in function call, like "myEvent();" */ if(!$1->bFunction && $3->numParams > 0) { scr_error("Can't pass any parameters in an event call:\nEvent: '%s'", $1->pIdent); return CE_PARSE; } if($3->numParams != $1->numParams) { scr_error("Wrong number of arguments for function call: '%s'. Expected %d parameters instead of %d.", $1->pIdent, $1->numParams, $3->numParams); return CE_PARSE; } /* Allocate the code block */ //ALLOC_BLOCK(psCurrBlock, $3->size + sizeof(OPCODE) + sizeof(UDWORD)); //Params + Opcode + event index ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1); //Params + Opcode + event index ALLOC_DEBUG(psCurrBlock, 1); ip = psCurrBlock->pCode; if($3->numParams > 0) /* if any parameters declared */ { /* Copy in the code for the parameters */ PUT_BLOCK(ip, $3); } FREE_PBLOCK($3); /* Store the instruction */ PUT_OPCODE(ip, OP_FUNC); PUT_EVENT(ip,$1->index); //Put event index as VAL_EVENT /* Add the debugging information */ if (genDebugInfo) { psCurrBlock->psDebug[0].offset = 0; scriptGetErrorData((SDWORD *)&line, &pDummy); psCurrBlock->psDebug[0].line = line; } $$ = psCurrBlock; } | conditional { $$ = $1; } | loop { $$ = $1; } | EXIT ';' { UDWORD line; char *pDummy; /* Allocate the code block */ ALLOC_BLOCK(psCurrBlock, 1); //1 - Exit opcode ALLOC_DEBUG(psCurrBlock, 1); ip = psCurrBlock->pCode; /* Store the instruction */ PUT_OPCODE(ip, OP_EXIT); /* Add the debugging information */ if (genDebugInfo) { psCurrBlock->psDebug[0].offset = 0; scriptGetErrorData((SDWORD *)&line, &pDummy); psCurrBlock->psDebug[0].line = line; } $$ = psCurrBlock; } | return_statement { UDWORD line; char *pDummy; RULE( "statement: return_statement"); if(psCurEvent == NULL) { scr_error("'return' outside of function body"); YYABORT; } if(!psCurEvent->bFunction) { debug(LOG_ERROR, "'return' can only be used in functions, not in events, event: '%s'", psCurEvent->pIdent); scr_error("'return' in event"); YYABORT; } if($1->type != psCurEvent->retType) { debug(LOG_ERROR, "'return' type doesn't match with function return type, function: '%s' (%d / %d)", psCurEvent->pIdent, $1->type, psCurEvent->retType); scr_error("'return' type mismatch 0"); YYABORT; } ALLOC_BLOCK(psCurrBlock, $1->size + 1); //return statement + EXIT opcode ALLOC_DEBUG(psCurrBlock, 1); ip = psCurrBlock->pCode; PUT_BLOCK(ip, $1); PUT_OPCODE(ip, OP_EXIT); FREE_BLOCK($1); if (genDebugInfo) { psCurrBlock->psDebug[0].offset = 0; scriptGetErrorData((SDWORD *)&line, &pDummy); psCurrBlock->psDebug[0].line = line; } $$ = psCurrBlock; } | PAUSE '(' INTEGER ')' ';' { UDWORD line; char *pDummy; // can only have a positive pause if ($3 < 0) { scr_error("Invalid pause time"); YYABORT; } /* Allocate the code block */ ALLOC_BLOCK(psCurrBlock, 1); //1 - for OP_PAUSE ALLOC_DEBUG(psCurrBlock, 1); ip = psCurrBlock->pCode; /* Store the instruction */ PUT_PKOPCODE(ip, OP_PAUSE, $3); /* Add the debugging information */ if (genDebugInfo) { psCurrBlock->psDebug[0].offset = 0; scriptGetErrorData((SDWORD *)&line, &pDummy); psCurrBlock->psDebug[0].line = line; } $$ = psCurrBlock; } ; /* function return type */ return_exp: expression { RULE("return_exp: expression"); /* Just pass the code up the tree */ $1->type = VAL_INT; $$ = $1; } | floatexp { RULE("return_exp: floatexp"); /* Just pass the code up the tree */ $1->type = VAL_FLOAT; $$ = $1; } | stringexp { RULE( "return_exp: stringexp"); /* Just pass the code up the tree */ $1->type = VAL_STRING; $$ = $1; } | boolexp { RULE( "return_exp: boolexp"); /* Just pass the code up the tree */ $1->type = VAL_BOOL; $$ = $1; } | objexp /* example: "group", "feature", "structure.health", "droid.numkills" */ { RULE( "return_exp: objexp"); /* Just pass the code up the tree */ /* $1->type = */ $$ = $1; } | userexp /* templates, bodies etc */ { RULE( "return_exp: userexp"); /* Just pass the code up the tree */ /* $1->type = */ $$ = $1; } ; /* Variable assignment statements, eg: * foo = 4 + bar ; */ assignment: NUM_VAR '=' expression { RULE( "assignment: NUM_VAR '=' expression"); codeRet = scriptCodeAssignment($1, $3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | BOOL_VAR '=' boolexp { RULE( "assignment: BOOL_VAR '=' boolexp"); codeRet = scriptCodeAssignment($1, $3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | FLOAT_VAR '=' floatexp { RULE( "assignment: FLOAT_VAR '=' floatexp"); codeRet = scriptCodeAssignment($1, $3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | STRING_VAR '=' stringexp { RULE("assignment: STRING_VAR '=' stringexp"); codeRet = scriptCodeAssignment($1, $3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | OBJ_VAR '=' objexp { RULE("assignment: OBJ_VAR '=' objexp"); if (!interpCheckEquiv($1->type, $3->type)) { scr_error("User type mismatch for assignment (%d - %d) 4", $1->type, $3->type); YYABORT; } codeRet = scriptCodeAssignment($1, $3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | VAR '=' userexp { RULE( "assignment: VAR '=' userexp"); if (!interpCheckEquiv($1->type, $3->type)) { scr_error("User type mismatch for assignment (%d - %d) 3", $1->type, $3->type); YYABORT; } codeRet = scriptCodeAssignment($1, (CODE_BLOCK *)$3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | num_objvar '=' expression { RULE( "assignment: num_objvar '=' expression"); codeRet = scriptCodeObjAssignment($1, $3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | bool_objvar '=' boolexp { RULE( "assignment: bool_objvar '=' boolexp"); codeRet = scriptCodeObjAssignment($1, $3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | user_objvar '=' userexp { RULE( "assignment: user_objvar '=' userexp"); if (!interpCheckEquiv($1->psObjVar->type,$3->type)) { scr_error("User type mismatch for assignment (%d - %d) 2", $1->psObjVar->type, $3->type); YYABORT; } codeRet = scriptCodeObjAssignment($1, (CODE_BLOCK *)$3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | obj_objvar '=' objexp { RULE( "assignment: obj_objvar '=' objexp"); if (!interpCheckEquiv($1->psObjVar->type, $3->type)) { scr_error("User type mismatch for assignment (%d - %d) 1", $1->psObjVar->type, $3->type); YYABORT; } codeRet = scriptCodeObjAssignment($1, $3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | num_array_var '=' expression { RULE( "assignment: num_array_var '=' expression"); codeRet = scriptCodeArrayAssignment($1, $3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | bool_array_var '=' boolexp { RULE( "assignment: bool_array_var '=' boolexp"); codeRet = scriptCodeArrayAssignment($1, $3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | user_array_var '=' userexp { RULE( "assignment: user_array_var '=' userexp"); if (!interpCheckEquiv($1->psArrayVar->type,$3->type)) { scr_error("User type mismatch for assignment (%d - %d) 0", $1->psArrayVar->type, $3->type); YYABORT; } codeRet = scriptCodeArrayAssignment($1, (CODE_BLOCK *)$3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | obj_array_var '=' objexp { RULE( "assignment: obj_array_var '=' objexp"); if (!interpCheckEquiv($1->psArrayVar->type, $3->type)) { scr_error("User type mismatch for assignment"); YYABORT; } codeRet = scriptCodeArrayAssignment($1, $3, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } ; /* Function calls, eg: * DisplayGlobals() ; * * If the function returns a value in a statement context * code is generated to remove that value from the stack. */ func_call: NUM_FUNC '(' param_list ')' { RULE( "func_call: NUM_FUNC '(' param_list ')'"); /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | BOOL_FUNC '(' param_list ')' { RULE( "func_call: BOOL_FUNC '(' param_list ')'"); /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | USER_FUNC '(' param_list ')' { RULE( "func_call: USER_FUNC '(' param_list ')'"); /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | OBJ_FUNC '(' param_list ')' { RULE( "func_call: OBJ_FUNC '(' param_list ')'"); /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | FUNC '(' param_list ')' { RULE("func_call: FUNC '(' param_list ')'"); /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | STRING_FUNC '(' param_list ')' { RULE( "func_call: STRING_FUNC '(' param_list ')'"); /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | FLOAT_FUNC '(' param_list ')' { RULE( "func_call: FLOAT_FUNC '(' param_list ')'"); /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, false, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } ; /* * Parse parameter lists eg: * 5, foo, (bar + 4.2) / 3, foobar and barfoo * * These rules return PARAM_BLOCKS which contain the type of each * parameter as well as the code for the parameter. */ param_list: /* NULL token */ { /* create a dummy pblock containing nothing */ //ALLOC_PBLOCK(psCurrPBlock, sizeof(UDWORD), 1); ALLOC_PBLOCK(psCurrPBlock, 1, 1); //1 is enough psCurrPBlock->size = 0; psCurrPBlock->numParams = 0; $$ = psCurrPBlock; } | parameter { RULE("param_list: parameter"); $$ = $1; } | param_list ',' parameter { RULE("param_list: param_list ',' parameter"); ALLOC_PBLOCK(psCurrPBlock, $1->size + $3->size, $1->numParams + $3->numParams); ip = psCurrPBlock->pCode; /* Copy in the code for the parameters */ PUT_BLOCK(ip, $1); PUT_BLOCK(ip, $3); /* Copy the parameter types */ memcpy(psCurrPBlock->aParams, $1->aParams, $1->numParams * sizeof(INTERP_TYPE)); memcpy(psCurrPBlock->aParams + $1->numParams, $3->aParams, $3->numParams * sizeof(INTERP_TYPE)); /* Free the old pblocks */ FREE_PBLOCK($1); FREE_PBLOCK($3); /* return the pblock */ $$ = psCurrPBlock; } ; parameter: expression { RULE("parameter: expression"); /* Generate the code for the parameter */ codeRet = scriptCodeParameter($1, VAL_INT, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } | boolexp { RULE("parameter: boolexp (boolexp size: %d)", $1->size); /* Generate the code for the parameter */ codeRet = scriptCodeParameter($1, VAL_BOOL, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } | floatexp { RULE("parameter: floatexp"); /* Generate the code for the parameter */ codeRet = scriptCodeParameter($1, VAL_FLOAT, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } | stringexp { RULE("parameter: stringexp"); /* Generate the code for the parameter */ codeRet = scriptCodeParameter($1, VAL_STRING, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } | userexp { RULE("parameter: userexp"); /* Generate the code for the parameter */ codeRet = scriptCodeParameter((CODE_BLOCK *)$1, $1->type, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } | objexp { RULE( "parameter: objexp"); /* Generate the code for the parameter */ codeRet = scriptCodeParameter($1, $1->type, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } | var_ref { /* just pass the variable reference up the tree */ $$ = $1; } ; var_ref: REF NUM_VAR { codeRet = scriptCodeVarRef($2, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } | REF FLOAT_VAR { codeRet = scriptCodeVarRef($2, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } | REF BOOL_VAR { codeRet = scriptCodeVarRef($2, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } | REF STRING_VAR { codeRet = scriptCodeVarRef($2, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } | REF VAR { codeRet = scriptCodeVarRef($2, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } | REF OBJ_VAR { codeRet = scriptCodeVarRef($2, &psCurrPBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrPBlock; } ; /* * Conditional statements, can be : * */ conditional: cond_clause_list { RULE( "conditional: cond_clause_list"); codeRet = scriptCodeConditional($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); $$ = psCurrBlock; } | cond_clause_list ELSE terminal_cond { RULE( "conditional: cond_clause_list ELSE terminal_cond"); ALLOC_CONDBLOCK(psCondBlock, $1->numOffsets + $3->numOffsets, $1->size + $3->size); ALLOC_DEBUG(psCondBlock, $1->debugEntries + $3->debugEntries); ip = psCondBlock->pCode; /* Store the two blocks of code */ PUT_BLOCK(ip, $1); PUT_BLOCK(ip, $3); /* Copy over the offset information */ /* (There isn't any in the terminal_cond) */ memcpy(psCondBlock->aOffsets, $1->aOffsets, $1->numOffsets * sizeof(SDWORD)); psCondBlock->numOffsets = $1->numOffsets; /* Put in the debugging information */ PUT_DEBUG(psCondBlock, $1); APPEND_DEBUG(psCondBlock, $1->size, $3); /* Free the code blocks */ FREE_DEBUG($1); FREE_DEBUG($3); FREE_CONDBLOCK($1); FREE_CONDBLOCK($3); /* Do the final processing of the conditional */ codeRet = scriptCodeConditional(psCondBlock, &psCurrBlock); CHECK_CODE_ERROR(codeRet); $$ = psCurrBlock; } ; cond_clause_list: cond_clause { RULE( "cond_clause_list: cond_clause"); $$ = $1; } | cond_clause_list ELSE cond_clause { RULE( "cond_clause_list: cond_clause_list ELSE cond_clause"); ALLOC_CONDBLOCK(psCondBlock, $1->numOffsets + $3->numOffsets, $1->size + $3->size); ALLOC_DEBUG(psCondBlock, $1->debugEntries + $3->debugEntries); ip = psCondBlock->pCode; /* Store the two blocks of code */ PUT_BLOCK(ip, $1); PUT_BLOCK(ip, $3); /* Copy over the offset information */ memcpy(psCondBlock->aOffsets, $1->aOffsets, $1->numOffsets * sizeof(SDWORD)); psCondBlock->aOffsets[$1->numOffsets] = $3->aOffsets[0] + $1->size; psCondBlock->numOffsets = $1->numOffsets + 1; /* Put in the debugging information */ PUT_DEBUG(psCondBlock, $1); APPEND_DEBUG(psCondBlock, $1->size, $3); /* Free the code blocks */ FREE_DEBUG($1); FREE_DEBUG($3); FREE_CONDBLOCK($1); FREE_CONDBLOCK($3); $$ = psCondBlock; } ; terminal_cond: '{' statement_list '}' { RULE( "terminal_cond: '{' statement_list '}'"); /* Allocate the block */ ALLOC_CONDBLOCK(psCondBlock, 1, $2->size); ALLOC_DEBUG(psCondBlock, $2->debugEntries); ip = psCondBlock->pCode; /* Put in the debugging information */ PUT_DEBUG(psCondBlock, $2); /* Store the statements */ PUT_BLOCK(ip, $2); FREE_DEBUG($2); FREE_BLOCK($2); $$ = psCondBlock; } ; cond_clause: IF '(' boolexp ')' { char *pDummy; RULE( "cond_clause: IF '(' boolexp ')' '{' statement_list '}'"); /* Get the line number for the end of the boolean expression */ /* and store it in debugLine. */ scriptGetErrorData((SDWORD *)&debugLine, &pDummy); } '{' statement_list '}' { /* Allocate the block */ ALLOC_CONDBLOCK(psCondBlock, 1, //1 offset $3->size + $7->size + 1 + 1); //size + size + OP_JUMPFALSE + OP_JUMP ALLOC_DEBUG(psCondBlock, $7->debugEntries + 1); ip = psCondBlock->pCode; /* Store the boolean expression code */ PUT_BLOCK(ip, $3); FREE_BLOCK($3); /* Put in the jump to the end of the block if the */ /* condition is false */ PUT_PKOPCODE(ip, OP_JUMPFALSE, $7->size + 2); /* Put in the debugging information */ if (genDebugInfo) { psCondBlock->debugEntries = 1; psCondBlock->psDebug->line = debugLine; psCondBlock->psDebug->offset = 0; APPEND_DEBUG(psCondBlock, ip - psCondBlock->pCode, $7); } /* Store the statements */ PUT_BLOCK(ip, $7); FREE_DEBUG($7); FREE_BLOCK($7); /* Store the location that has to be filled in */ //TODO: don't want to store pointers as ints psCondBlock->aOffsets[0] = (UDWORD)(ip - psCondBlock->pCode); /* Put in a jump to skip the rest of the conditional */ /* The correct offset will be set once the whole */ /* conditional has been parsed */ /* The jump should be to the instruction after the */ /* entire conditonal block */ PUT_PKOPCODE(ip, OP_JUMP, 0); $$ = psCondBlock; } /* 'Monoblock' */ | IF '(' boolexp ')' { char *pDummy; RULE( "cond_clause: IF '(' boolexp ')' statement"); /* Get the line number for the end of the boolean expression */ /* and store it in debugLine. */ scriptGetErrorData((SDWORD *)&debugLine, &pDummy); } statement { /* Allocate the block */ ALLOC_CONDBLOCK(psCondBlock, 1, $3->size + $6->size + 1 + 1); //size + size + opcode + opcode ALLOC_DEBUG(psCondBlock, $6->debugEntries + 1); ip = psCondBlock->pCode; /* Store the boolean expression code */ PUT_BLOCK(ip, $3); FREE_BLOCK($3); /* Put in the jump to the end of the block if the */ /* condition is false */ PUT_PKOPCODE(ip, OP_JUMPFALSE, $6->size + 2); /* Put in the debugging information */ if (genDebugInfo) { psCondBlock->debugEntries = 1; psCondBlock->psDebug->line = debugLine; psCondBlock->psDebug->offset = 0; APPEND_DEBUG(psCondBlock, ip - psCondBlock->pCode, $6); } /* Store the statements */ PUT_BLOCK(ip, $6); FREE_DEBUG($6); FREE_BLOCK($6); /* Store the location that has to be filled in */ psCondBlock->aOffsets[0] = (UDWORD)(ip - psCondBlock->pCode); /* Put in a jump to skip the rest of the conditional */ /* The correct offset will be set once the whole */ /* conditional has been parsed */ /* The jump should be to the instruction after the */ /* entire conditonal block */ PUT_PKOPCODE(ip, OP_JUMP, 0); $$ = psCondBlock; } ; /* * while loops, e.g. * * while (i < 5) * { * .... * i = i + 1; * } */ loop: WHILE '(' boolexp ')' { char *pDummy; /* Get the line number for the end of the boolean expression */ /* and store it in debugLine. */ scriptGetErrorData((SDWORD *)&debugLine, &pDummy); } '{' statement_list '}' { RULE("loop: WHILE '(' boolexp ')'"); //ALLOC_BLOCK(psCurrBlock, $3->size + $7->size + sizeof(OPCODE) * 2); ALLOC_BLOCK(psCurrBlock, $3->size + $7->size + 1 + 1); //size + size + opcode + opcode ALLOC_DEBUG(psCurrBlock, $7->debugEntries + 1); ip = psCurrBlock->pCode; /* Copy in the loop expression */ PUT_BLOCK(ip, $3); FREE_BLOCK($3); /* Now a conditional jump out of the loop if the */ /* expression is false. */ PUT_PKOPCODE(ip, OP_JUMPFALSE, $7->size + 2); /* Now put in the debugging information */ if (genDebugInfo) { psCurrBlock->debugEntries = 1; psCurrBlock->psDebug->line = debugLine; psCurrBlock->psDebug->offset = 0; APPEND_DEBUG(psCurrBlock, ip - psCurrBlock->pCode, $7); } /* Copy in the body of the loop */ PUT_BLOCK(ip, $7); FREE_DEBUG($7); FREE_BLOCK($7); /* Put in a jump back to the start of the loop expression */ PUT_PKOPCODE(ip, OP_JUMP, (SWORD)( -(SWORD)(psCurrBlock->size) + 1)); $$ = psCurrBlock; } ; /* Increment/Decrement expressions */ inc_dec_exp: NUM_VAR _INC { RULE("expression: NUM_VAR++"); scriptCodeIncDec($1, &psCurrBlock, OP_INC); /* Return the code block */ $$ = psCurrBlock; } | NUM_VAR _DEC { RULE("expression: NUM_VAR--"); scriptCodeIncDec($1, &psCurrBlock, OP_DEC); /* Return the code block */ $$ = psCurrBlock; } ; /************************************************************************************** * * Mathematical expressions */ expression: expression '+' expression { codeRet = scriptCodeBinaryOperator($1, $3, OP_ADD, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | expression '-' expression { codeRet = scriptCodeBinaryOperator($1, $3, OP_SUB, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | expression '*' expression { codeRet = scriptCodeBinaryOperator($1, $3, OP_MUL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | expression '/' expression { codeRet = scriptCodeBinaryOperator($1, $3, OP_DIV, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | '-' expression %prec UMINUS { //ALLOC_BLOCK(psCurrBlock, $2->size + sizeof(OPCODE)); ALLOC_BLOCK(psCurrBlock, $2->size + 1); //size + unary minus opcode ip = psCurrBlock->pCode; /* Copy the already generated bits of code into the code block */ PUT_BLOCK(ip, $2); /* Now put a negation operator into the code */ PUT_PKOPCODE(ip, OP_UNARYOP, OP_NEG); /* Free the two code blocks that have been copied */ FREE_BLOCK($2); /* Return the code block */ $$ = psCurrBlock; } | '(' expression ')' { /* Just pass the code up the tree */ $$ = $2; } | TO_INT_CAST floatexp { RULE("expression: (int) floatexp"); /* perform cast */ $2->type = VAL_INT; //ALLOC_BLOCK(psCurrBlock, $4->size + sizeof(OPCODE)); ALLOC_BLOCK(psCurrBlock, $2->size + 1); //size + opcode ip = psCurrBlock->pCode; PUT_BLOCK(ip, $2); PUT_OPCODE(ip, OP_TO_INT); FREE_BLOCK($2); // free floatexp $$ = psCurrBlock; } | NUM_FUNC '(' param_list ')' { RULE( "expression: NUM_FUNC '(' param_list ')'"); /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | NUM_FUNC_CUST '(' param_list ')' { UDWORD paramNumber; RULE( "expression: NUM_FUNC_CUST '(' param_list ')'"); /* if($4->numParams != $3->numParams) */ if($3->numParams != $1->numParams) { debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of %d.", $1->pIdent, $1->numParams, $3->numParams); scr_error("Wrong number of arguments in function call"); return CE_PARSE; } if(!$1->bFunction) { debug(LOG_ERROR, "'%s' is not a function", $1->pIdent); scr_error("Can't call an event"); return CE_PARSE; } /* make sure function has a return type */ if($1->retType != VAL_INT) { debug(LOG_ERROR, "'%s' does not return an integer value", $1->pIdent); scr_error("assignment type conflict"); return CE_PARSE; } /* check if right parameters were passed */ paramNumber = checkFuncParamTypes($1, $3); if(paramNumber > 0) { debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter %d.", $1->pIdent, paramNumber); YYABORT; } /* Allocate the code block */ ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1); //Params + Opcode + event index ip = psCurrBlock->pCode; if($3->numParams > 0) /* if any parameters declared */ { /* Copy in the code for the parameters */ PUT_BLOCK(ip, $3); } FREE_PBLOCK($3); /* Store the instruction */ PUT_OPCODE(ip, OP_FUNC); PUT_EVENT(ip,$1->index); //Put event index $$ = psCurrBlock; } | NUM_VAR { RULE("expression: NUM_VAR"); codeRet = scriptCodeVarGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | NUM_CONSTANT { RULE("expression: NUM_CONSTANT"); codeRet = scriptCodeConstant($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | num_objvar { RULE( "expression: num_objvar"); RULE( "type=%d", $1->psObjVar->type); codeRet = scriptCodeObjGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | num_array_var { codeRet = scriptCodeArrayGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | INTEGER { RULE("expression: INTEGER"); //ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD)); ALLOC_BLOCK(psCurrBlock, 1 + 1); //opcode + integer value ip = psCurrBlock->pCode; /* Code to store the value on the stack */ PUT_PKOPCODE(ip, OP_PUSH, VAL_INT); PUT_DATA_INT(ip, $1); /* Return the code block */ $$ = psCurrBlock; } ; floatexp: floatexp '+' floatexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_ADD, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | floatexp '-' floatexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_SUB, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | floatexp '*' floatexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_MUL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | floatexp '/' floatexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_DIV, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | TO_FLOAT_CAST expression { RULE("floatexp: (float) expression"); /* perform cast */ $2->type = VAL_FLOAT; ALLOC_BLOCK(psCurrBlock, $2->size + 1); //size + opcode ip = psCurrBlock->pCode; PUT_BLOCK(ip, $2); PUT_OPCODE(ip, OP_TO_FLOAT); FREE_BLOCK($2); // free 'expression' $$ = psCurrBlock; } | '-' floatexp %prec UMINUS { ALLOC_BLOCK(psCurrBlock, $2->size + 1); //size + opcode ip = psCurrBlock->pCode; /* Copy the already generated bits of code into the code block */ PUT_BLOCK(ip, $2); /* Now put a negation operator into the code */ PUT_PKOPCODE(ip, OP_UNARYOP, OP_NEG); /* Free the code block that have been copied */ FREE_BLOCK($2); /* Return the code block */ $$ = psCurrBlock; } | '(' floatexp ')' { /* Just pass the code up the tree */ $$ = $2; } | FLOAT_FUNC '(' param_list ')' { RULE("floatexp: FLOAT_FUNC '(' param_list ')'"); /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | FLOAT_FUNC_CUST '(' param_list ')' { UDWORD line,paramNumber; char *pDummy; RULE("floatexp: FLOAT_FUNC_CUST '(' param_list ')'"); if($3->numParams != $1->numParams) { debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of %d.", $1->pIdent, $1->numParams, $3->numParams); scr_error("Wrong number of arguments in function call"); return CE_PARSE; } if(!$1->bFunction) { debug(LOG_ERROR, "'%s' is not a function", $1->pIdent); scr_error("Can't call an event"); return CE_PARSE; } /* make sure function has a return type */ if($1->retType != VAL_FLOAT) { debug(LOG_ERROR, "'%s' does not return a float value", $1->pIdent); scr_error("assignment type conflict"); return CE_PARSE; } /* check if right parameters were passed */ paramNumber = checkFuncParamTypes($1, $3); if(paramNumber > 0) { debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter %d.", $1->pIdent, paramNumber); YYABORT; } /* Allocate the code block */ ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1); //Params + Opcode + event index ALLOC_DEBUG(psCurrBlock, 1); ip = psCurrBlock->pCode; if($3->numParams > 0) /* if any parameters declared */ { /* Copy in the code for the parameters */ PUT_BLOCK(ip, $3); } FREE_PBLOCK($3); /* Store the instruction */ PUT_OPCODE(ip, OP_FUNC); PUT_EVENT(ip,$1->index); //Put event/function index /* Add the debugging information */ if (genDebugInfo) { psCurrBlock->psDebug[0].offset = 0; scriptGetErrorData((SDWORD *)&line, &pDummy); psCurrBlock->psDebug[0].line = line; } $$ = psCurrBlock; } | FLOAT_VAR { RULE( "floatexp: FLOAT_VAR"); codeRet = scriptCodeVarGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | FLOAT_T { RULE( "floatexp: FLOAT_T"); //ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(float)); ALLOC_BLOCK(psCurrBlock, 1 + 1); //opcode + float ip = psCurrBlock->pCode; /* Code to store the value on the stack */ PUT_PKOPCODE(ip, OP_PUSH, VAL_FLOAT); PUT_DATA_FLOAT(ip, $1); /* Return the code block */ $$ = psCurrBlock; } ; /*************************************************************************************** * * String expressions */ stringexp: stringexp '&' stringexp { RULE("stringexp: stringexp '&' stringexp"); codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; //debug(LOG_SCRIPT, "END stringexp: stringexp '&' stringexp"); } | stringexp '&' expression { RULE( "stringexp: stringexp '&' expression"); codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | expression '&' stringexp { RULE( "stringexp: expression '&' stringexp"); codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | stringexp '&' boolexp { RULE( "stringexp: stringexp '&' boolexp"); codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | boolexp '&' stringexp { RULE( "stringexp: boolexp '&' stringexp"); codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | stringexp '&' floatexp { RULE( "stringexp: stringexp '&' floatexp"); codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | floatexp '&' stringexp { RULE( "stringexp: floatexp '&' stringexp"); codeRet = scriptCodeBinaryOperator($1, $3, OP_CONC, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | '(' stringexp ')' { /* Just pass the code up the tree */ $$ = $2; } | STRING_FUNC '(' param_list ')' { RULE("stringexp: STRING_FUNC '(' param_list ')'"); /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | STRING_FUNC_CUST '(' param_list ')' { RULE("stringexp: STRING_FUNC_CUST '(' param_list ')'"); if($3->numParams != $1->numParams) { debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of %d.", $1->pIdent, $1->numParams, $3->numParams); scr_error("Wrong number of arguments in function call"); return CE_PARSE; } if(!$1->bFunction) { debug(LOG_ERROR, "'%s' is not a function", $1->pIdent); scr_error("Can't call an event"); return CE_PARSE; } /* make sure function has a return type */ if($1->retType != VAL_STRING) { debug(LOG_ERROR, "'%s' does not return a string value", $1->pIdent); scr_error("assignment type conflict"); return CE_PARSE; } /* Allocate the code block */ ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1); //Params + Opcode + event index ip = psCurrBlock->pCode; if($3->numParams > 0) /* if any parameters declared */ { /* Copy in the code for the parameters */ PUT_BLOCK(ip, $3); } FREE_PBLOCK($3); /* Store the instruction */ PUT_OPCODE(ip, OP_FUNC); PUT_EVENT(ip,$1->index); //Put event index $$ = psCurrBlock; } | STRING_VAR { RULE("stringexpr: STRING_VAR"); codeRet = scriptCodeVarGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | QTEXT { RULE("QTEXT: '%s'", yyvsp[0].sval); //ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD)); ALLOC_BLOCK(psCurrBlock, 1 + 1); //opcode + string pointer ip = psCurrBlock->pCode; /* Code to store the value on the stack */ PUT_PKOPCODE(ip, OP_PUSH, VAL_STRING); PUT_DATA_STRING(ip, STRSTACK[CURSTACKSTR]); /* Return the code block */ $$ = psCurrBlock; /* Manage string stack */ sstrcpy(STRSTACK[CURSTACKSTR], yyvsp[0].sval); CURSTACKSTR = CURSTACKSTR + 1; /* Increment 'pointer' to the top of the string stack */ if(CURSTACKSTR >= MAXSTACKLEN) { scr_error("Can't store more than %d strings", MAXSTACKLEN); } //debug(LOG_SCRIPT, "END QTEXT found"); } | expression { RULE( "stringexp: expression"); /* Just pass the code up the tree */ $$ = $1; } ; /*************************************************************************************** * * Boolean expressions */ boolexp: boolexp _AND boolexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_AND, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | boolexp _OR boolexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_OR, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | boolexp BOOLEQUAL boolexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | boolexp NOTEQUAL boolexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | _NOT boolexp { //ALLOC_BLOCK(psCurrBlock, $2->size + sizeof(OPCODE)); ALLOC_BLOCK(psCurrBlock, $2->size + 1); //size + opcode ip = psCurrBlock->pCode; /* Copy the already generated bits of code into the code block */ PUT_BLOCK(ip, $2); /* Now put a negation operator into the code */ PUT_PKOPCODE(ip, OP_UNARYOP, OP_NOT); /* Free the two code blocks that have been copied */ FREE_BLOCK($2); /* Return the code block */ $$ = psCurrBlock; } | '(' boolexp ')' { /* Just pass the code up the tree */ $$ = $2; } | BOOL_FUNC '(' param_list ')' { RULE("boolexp: BOOL_FUNC '(' param_list ')'"); /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | BOOL_FUNC_CUST '(' param_list ')' { UDWORD paramNumber; RULE("boolexp: BOOL_FUNC_CUST '(' param_list ')'"); if($3->numParams != $1->numParams) { debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of %d.", $1->pIdent, $1->numParams, $3->numParams); scr_error("Wrong number of arguments in function call"); return CE_PARSE; } if(!$1->bFunction) { debug(LOG_ERROR, "'%s' is not a function", $1->pIdent); scr_error("Can't call an event"); return CE_PARSE; } /* make sure function has a return type */ if($1->retType != VAL_BOOL) { debug(LOG_ERROR, "'%s' does not return a boolean value", $1->pIdent); scr_error("assignment type conflict"); return CE_PARSE; } /* check if right parameters were passed */ paramNumber = checkFuncParamTypes($1, $3); if(paramNumber > 0) { debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter %d.", $1->pIdent, paramNumber); YYABORT; } /* Allocate the code block */ ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1); //Params + Opcode + event index ip = psCurrBlock->pCode; if($3->numParams > 0) /* if any parameters declared */ { /* Copy in the code for the parameters */ PUT_BLOCK(ip, $3); } FREE_PBLOCK($3); /* Store the instruction */ PUT_OPCODE(ip, OP_FUNC); PUT_EVENT(ip,$1->index); //Put event/function index $$ = psCurrBlock; } | BOOL_VAR { RULE("boolexp: BOOL_VAR"); codeRet = scriptCodeVarGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | BOOL_CONSTANT { RULE("boolexp: BOOL_CONSTANT"); codeRet = scriptCodeConstant($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | bool_objvar { codeRet = scriptCodeObjGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | bool_array_var { codeRet = scriptCodeArrayGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | BOOLEAN_T { RULE("boolexp: BOOLEAN_T"); //ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD)); ALLOC_BLOCK(psCurrBlock, 1 + 1); //opcode + value ip = psCurrBlock->pCode; /* Code to store the value on the stack */ PUT_PKOPCODE(ip, OP_PUSH, VAL_BOOL); PUT_DATA_BOOL(ip, $1); /* Return the code block */ $$ = psCurrBlock; } | expression BOOLEQUAL expression { codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | floatexp BOOLEQUAL floatexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | stringexp BOOLEQUAL stringexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | userexp BOOLEQUAL userexp { if (!interpCheckEquiv($1->type,$3->type)) { scr_error("Type mismatch for equality"); YYABORT; } codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | objexp BOOLEQUAL objexp { if (!interpCheckEquiv($1->type,$3->type)) { scr_error("Type mismatch for equality"); YYABORT; } codeRet = scriptCodeBinaryOperator($1, $3, OP_EQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | expression NOTEQUAL expression { codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | floatexp NOTEQUAL floatexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | stringexp NOTEQUAL stringexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | userexp NOTEQUAL userexp { if (!interpCheckEquiv($1->type,$3->type)) { scr_error("Type mismatch for inequality"); YYABORT; } codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | objexp NOTEQUAL objexp { if (!interpCheckEquiv($1->type,$3->type)) { scr_error("Type mismatch for inequality"); YYABORT; } codeRet = scriptCodeBinaryOperator($1, $3, OP_NOTEQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | expression LESSEQUAL expression { codeRet = scriptCodeBinaryOperator($1, $3, OP_LESSEQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | floatexp LESSEQUAL floatexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_LESSEQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | expression GREATEQUAL expression { codeRet = scriptCodeBinaryOperator($1, $3, OP_GREATEREQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | floatexp GREATEQUAL floatexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_GREATEREQUAL, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | expression GREATER expression { codeRet = scriptCodeBinaryOperator($1, $3, OP_GREATER, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | floatexp GREATER floatexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_GREATER, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | expression LESS expression { codeRet = scriptCodeBinaryOperator($1, $3, OP_LESS, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | floatexp LESS floatexp { codeRet = scriptCodeBinaryOperator($1, $3, OP_LESS, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } ; /************************************************************************************* * * User variable expressions */ userexp: VAR { codeRet = scriptCodeVarGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | USER_CONSTANT { RULE("userexp: USER_CONSTANT"); codeRet = scriptCodeConstant($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | user_objvar { codeRet = scriptCodeObjGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | user_array_var { RULE("userexp: user_array_var"); codeRet = scriptCodeArrayGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | USER_FUNC '(' param_list ')' { /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | USER_FUNC_CUST '(' param_list ')' { UDWORD line,paramNumber; char *pDummy; if($3->numParams != $1->numParams) { debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of %d.", $1->pIdent, $1->numParams, $3->numParams); scr_error("Wrong number of arguments in function call"); return CE_PARSE; } if(!$1->bFunction) { debug(LOG_ERROR, "'%s' is not a function", $1->pIdent); scr_error("Can't call an event"); return CE_PARSE; } /* check if right parameters were passed */ paramNumber = checkFuncParamTypes($1, $3); if(paramNumber > 0) { debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter %d.", $1->pIdent, paramNumber); YYABORT; } /* Allocate the code block */ ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1); //Params + Opcode + event index ALLOC_DEBUG(psCurrBlock, 1); ip = psCurrBlock->pCode; if($3->numParams > 0) /* if any parameters declared */ { /* Copy in the code for the parameters */ PUT_BLOCK(ip, $3); FREE_PBLOCK($3); } /* Store the instruction */ PUT_OPCODE(ip, OP_FUNC); PUT_EVENT(ip,$1->index); //Put event index /* Add the debugging information */ if (genDebugInfo) { psCurrBlock->psDebug[0].offset = 0; scriptGetErrorData((SDWORD *)&line, &pDummy); psCurrBlock->psDebug[0].line = line; } /* remember objexp type for further stuff, like myVar = objFunc(); to be able to check type equivalency */ psCurrBlock->type = $1->retType; $$ = psCurrBlock; } | TRIG_SYM { //ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD)); ALLOC_BLOCK(psCurrBlock, 1 + 1); //opcode + trigger index ip = psCurrBlock->pCode; /* Code to store the value on the stack */ PUT_PKOPCODE(ip, OP_PUSH, VAL_TRIGGER); //TODO: don't need to put VAL_TRIGGER here, since stored in ip->type now PUT_TRIGGER(ip, $1->index); psCurrBlock->type = VAL_TRIGGER; /* Return the code block */ $$ = psCurrBlock; } | INACTIVE { //ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD)); ALLOC_BLOCK(psCurrBlock, 1 + 1); //opcode + '-1' for 'inactive' ip = psCurrBlock->pCode; /* Code to store the value on the stack */ PUT_PKOPCODE(ip, OP_PUSH, VAL_TRIGGER); PUT_TRIGGER(ip, -1); psCurrBlock->type = VAL_TRIGGER; /* Return the code block */ $$ = psCurrBlock; } | EVENT_SYM { //ALLOC_BLOCK(psCurrBlock, sizeof(OPCODE) + sizeof(UDWORD)); ALLOC_BLOCK(psCurrBlock, 1 + 1); //opcode + event index ip = psCurrBlock->pCode; /* Code to store the value on the stack */ PUT_PKOPCODE(ip, OP_PUSH, VAL_EVENT); PUT_EVENT(ip, $1->index); psCurrBlock->type = VAL_EVENT; /* Return the code block */ $$ = psCurrBlock; } ; /************************************************************************************* * * Object expressions. */ objexp: OBJ_VAR { codeRet = scriptCodeVarGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | OBJ_CONSTANT { RULE("objexp: OBJ_CONSTANT"); codeRet = scriptCodeConstant($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | OBJ_FUNC '(' param_list ')' { /* Generate the code for the function call */ codeRet = scriptCodeFunction($1, $3, true, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | OBJ_FUNC_CUST '(' param_list ')' { UDWORD paramNumber; if($3->numParams != $1->numParams) { debug(LOG_ERROR, "Wrong number of arguments for function call: '%s'. Expected %d parameters instead of %d.", $1->pIdent, $1->numParams, $3->numParams); scr_error("Wrong number of arguments in function call"); return CE_PARSE; } if(!$1->bFunction) { debug(LOG_ERROR, "'%s' is not a function", $1->pIdent); scr_error("Can't call an event"); return CE_PARSE; } /* make sure function has a return type */ /* if($1->retType != OBJ_VAR) */ if(asScrTypeTab[$1->retType - VAL_USERTYPESTART].accessType != AT_OBJECT) { scr_error("'%s' does not return an object value (%d )", $1->pIdent, $1->retType); return CE_PARSE; } /* check if right parameters were passed */ paramNumber = checkFuncParamTypes($1, $3); if(paramNumber > 0) { debug(LOG_ERROR, "Parameter mismatch in function call: '%s'. Mismatch in parameter %d.", $1->pIdent, paramNumber); YYABORT; } /* Allocate the code block */ ALLOC_BLOCK(psCurrBlock, $3->size + 1 + 1); //Params + Opcode + event index ip = psCurrBlock->pCode; if($3->numParams > 0) /* if any parameters declared */ { /* Copy in the code for the parameters */ PUT_BLOCK(ip, $3); } FREE_PBLOCK($3); /* Store the instruction */ PUT_OPCODE(ip, OP_FUNC); PUT_EVENT(ip,$1->index); //Put event index /* remember objexp type for further stuff, like myVar = objFunc(); to be able to check type equivalency */ psCurrBlock->type = $1->retType; $$ = psCurrBlock; } | obj_objvar { codeRet = scriptCodeObjGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } | obj_array_var { codeRet = scriptCodeArrayGet($1, &psCurrBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrBlock; } ; /* * Seperate rule for the dot after an object so the lookup context * for object variables can be set */ objexp_dot: objexp '.' { RULE( "objexp_dot: objexp '.', type=%d", $1->type); // Store the object type for the variable lookup objVarContext = $1->type; } | userexp '.' { RULE( "objexp_dot: userexp '.', type=%d", $1->type); // Store the object type for the variable lookup objVarContext = $1->type; } ; /* * Object member variable references. */ num_objvar: objexp_dot NUM_OBJVAR { RULE( "num_objvar: objexp_dot NUM_OBJVAR"); codeRet = scriptCodeObjectVariable($1, $2, &psObjVarBlock); CHECK_CODE_ERROR(codeRet); // reset the object type objVarContext = 0; /* Return the code block */ $$ = psObjVarBlock; } ; bool_objvar: objexp_dot BOOL_OBJVAR { codeRet = scriptCodeObjectVariable($1, $2, &psObjVarBlock); CHECK_CODE_ERROR(codeRet); // reset the object type objVarContext = 0; /* Return the code block */ $$ = psObjVarBlock; } ; user_objvar: objexp_dot USER_OBJVAR { codeRet = scriptCodeObjectVariable($1, $2, &psObjVarBlock); CHECK_CODE_ERROR(codeRet); // reset the object type objVarContext = 0; /* Return the code block */ $$ = psObjVarBlock; } ; obj_objvar: objexp_dot OBJ_OBJVAR { codeRet = scriptCodeObjectVariable($1, $2, &psObjVarBlock); CHECK_CODE_ERROR(codeRet); // reset the object type objVarContext = 0; /* Return the code block */ $$ = psObjVarBlock; } ; /***************************************************************************************************** * * Array expressions * */ array_index: '[' expression ']' { ALLOC_ARRAYBLOCK(psCurrArrayBlock, $2->size, NULL); ip = psCurrArrayBlock->pCode; /* Copy the index expression code into the code block */ PUT_BLOCK(ip, $2); FREE_BLOCK($2); $$ = psCurrArrayBlock; } ; array_index_list: array_index { $$ = $1; } | array_index_list '[' expression ']' { ALLOC_ARRAYBLOCK(psCurrArrayBlock, $1->size + $3->size, NULL); ip = psCurrArrayBlock->pCode; /* Copy the index expression code into the code block */ psCurrArrayBlock->dimensions = $1->dimensions + 1; PUT_BLOCK(ip, $1); PUT_BLOCK(ip, $3); FREE_ARRAYBLOCK($1); FREE_ARRAYBLOCK($3); $$ = psCurrArrayBlock; } ; num_array_var: NUM_ARRAY array_index_list { codeRet = scriptCodeArrayVariable($2, $1, &psCurrArrayBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrArrayBlock; } ; bool_array_var: BOOL_ARRAY array_index_list { codeRet = scriptCodeArrayVariable($2, $1, &psCurrArrayBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrArrayBlock; } ; obj_array_var: OBJ_ARRAY array_index_list { codeRet = scriptCodeArrayVariable($2, $1, &psCurrArrayBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrArrayBlock; } ; user_array_var: VAR_ARRAY array_index_list { RULE("user_array_var: VAR_ARRAY array_index_list"); codeRet = scriptCodeArrayVariable($2, $1, &psCurrArrayBlock); CHECK_CODE_ERROR(codeRet); /* Return the code block */ $$ = psCurrArrayBlock; } ; %% // Reset all the symbol tables static void scriptResetTables(void) { VAR_SYMBOL *psCurr, *psNext; TRIGGER_SYMBOL *psTCurr, *psTNext; EVENT_SYMBOL *psECurr, *psENext; SDWORD i; /* start with global vars definition */ localVariableDef = false; //debug(LOG_SCRIPT, "localVariableDef = false 4"); /* Reset the global variable symbol table */ for(psCurr = psGlobalVars; psCurr != NULL; psCurr = psNext) { psNext = psCurr->psNext; free(psCurr); } psGlobalVars = NULL; /* Reset the global variable symbol table */ psCurEvent = NULL; for(i=0; ipsNext; free(psCurr); } psLocalVarsB[i] = NULL; } /* Reset the global array symbol table */ for(psCurr = psGlobalArrays; psCurr != NULL; psCurr = psNext) { psNext = psCurr->psNext; free(psCurr); } psGlobalArrays = NULL; // Reset the trigger table for(psTCurr = psTriggers; psTCurr; psTCurr = psTNext) { psTNext = psTCurr->psNext; if (psTCurr->psDebug) { free(psTCurr->psDebug); } if (psTCurr->pCode) { free(psTCurr->pCode); } free(psTCurr->pIdent); free(psTCurr); } psTriggers = NULL; numTriggers = 0; // Reset the event table for(psECurr = psEvents; psECurr; psECurr = psENext) { psENext = psECurr->psNext; if (psECurr->psDebug) { free(psECurr->psDebug); } free(psECurr->pIdent); free(psECurr->pCode); free(psECurr); } psEvents = NULL; numEvents = 0; } /* Compile a script program */ SCRIPT_CODE* scriptCompile(PHYSFS_file* fileHandle, SCR_DEBUGTYPE debugType) { /* Feed flex with initial input buffer */ scriptSetInputFile(fileHandle); scriptResetTables(); psFinalProg = NULL; if (debugType == SCR_DEBUGINFO) { genDebugInfo = true; } else { genDebugInfo = false; } if (scr_parse() != 0 || bError) { scr_lex_destroy(); return NULL; } scr_lex_destroy(); scriptResetTables(); return psFinalProg; } /* A simple error reporting routine */ void scr_error(const char *pMessage, ...) { int line; char *text; va_list args; char aBuff[1024]; va_start(args, pMessage); vsnprintf(aBuff, sizeof(aBuff), pMessage, args); va_end(args); scriptGetErrorData(&line, &text); bError = true; #ifdef DEBUG debug( LOG_ERROR, "script parse error:\n%s at %s:%d\nToken: %d, Text: '%s'\n", aBuff, GetLastResourceFilename(), line, scr_char, text ); ASSERT( false, "script parse error:\n%s at %s:%d\nToken: %d, Text: '%s'\n", aBuff, GetLastResourceFilename(), line, scr_char, text ); #else //DBERROR(("script parse error:\n%s at line %d\nToken: %d, Text: '%s'\n", // pMessage, line, scr_char, text)); debug( LOG_ERROR, "script parse error:\n'%s' at %s:%d\nToken: %d, Text: '%s'\n", aBuff, GetLastResourceFilename(), line, scr_char, text ); abort(); #endif } /* Look up a type symbol */ BOOL scriptLookUpType(const char *pIdent, INTERP_TYPE *pType) { UDWORD i; //debug(LOG_SCRIPT, "scriptLookUpType"); if (asScrTypeTab) { for(i=0; asScrTypeTab[i].typeID != 0; i++) { if (strcmp(asScrTypeTab[i].pIdent, pIdent) == 0) { *pType = asScrTypeTab[i].typeID; return true; } } } //debug(LOG_SCRIPT, "END scriptLookUpType"); return false; } /* pop passed arguments (if any) */ BOOL popArguments(INTERP_VAL **ip_temp, SDWORD numParams) { SDWORD i; /* code to pop passed params right before the main code begins */ for(i = numParams-1; i >= 0 ; i--) { PUT_PKOPCODE(*ip_temp, OP_POPLOCAL, i); //pop parameters into first i local params } return true; } /* Add a new variable symbol. * If localVariableDef is true a local variable symbol is defined, * otherwise a global variable symbol is defined. */ BOOL scriptAddVariable(VAR_DECL *psStorage, VAR_IDENT_DECL *psVarIdent) { VAR_SYMBOL *psNew; unsigned int i; char* ident; /* Allocate the memory for the symbol structure plus the symbol name */ psNew = (VAR_SYMBOL *)malloc(sizeof(VAR_SYMBOL) + strlen(psVarIdent->pIdent) + 1); if (psNew == NULL) { scr_error("Out of memory"); return false; } // This ensures we only need to call free() on the VAR_SYMBOL* pointer and not on its members ident = (char*)(psNew + 1); strcpy(ident, psVarIdent->pIdent); psNew->pIdent = ident; psNew->type = psStorage->type; psNew->storage = psStorage->storage; psNew->dimensions = psVarIdent->dimensions; for(i=0; idimensions; i++) { psNew->elements[i] = psVarIdent->elements[i]; } if (psNew->dimensions == 0) { if(psStorage->storage != ST_LOCAL) //if not a local var { if (psGlobalVars == NULL) { psNew->index = 0; } else { psNew->index = psGlobalVars->index + 1; } /* Add the symbol to the list */ psNew->psNext = psGlobalVars; psGlobalVars = psNew; } else //local var { if(psCurEvent == NULL) debug(LOG_ERROR, "Can't declare local variables before defining an event"); //debug(LOG_SCRIPT, "local variable declared for event %d, type=%d \n", psCurEvent->index, psNew->type); //debug(LOG_SCRIPT, "%s \n", psNew->pIdent); if (psLocalVarsB[psCurEvent->index] == NULL) { psNew->index = 0; } else { psNew->index = psLocalVarsB[psCurEvent->index]->index + 1; } numEventLocalVars[psCurEvent->index] = numEventLocalVars[psCurEvent->index] + 1; psNew->psNext = psLocalVarsB[psCurEvent->index]; psLocalVarsB[psCurEvent->index] = psNew; //debug(LOG_SCRIPT, "local variable declared. "); } } else { if (psGlobalArrays == NULL) { psNew->index = 0; } else { psNew->index = psGlobalArrays->index + 1; } psNew->psNext = psGlobalArrays; psGlobalArrays = psNew; } return true; } /* Look up a variable symbol */ BOOL scriptLookUpVariable(const char *pIdent, VAR_SYMBOL **ppsSym) { VAR_SYMBOL *psCurr; UDWORD i; //debug(LOG_SCRIPT, "scriptLookUpVariable"); /* See if the symbol is an object variable */ if (asScrObjectVarTab && objVarContext != 0) { for(psCurr = asScrObjectVarTab; psCurr->pIdent != NULL; psCurr++) { if (interpCheckEquiv(psCurr->objType, objVarContext) && strcmp(psCurr->pIdent, pIdent) == 0) { *ppsSym = psCurr; return true; } } } /* See if the symbol is an external variable */ if (asScrExternalTab) { for(psCurr = asScrExternalTab; psCurr->pIdent != NULL; psCurr++) { if (strcmp(psCurr->pIdent, pIdent) == 0) { //debug(LOG_SCRIPT, "scriptLookUpVariable: extern"); *ppsSym = psCurr; return true; } } } /* check local vars if we are inside of an event */ if(psCurEvent != NULL) { if(psCurEvent->index >= maxEventsLocalVars) debug(LOG_ERROR, "psCurEvent->index (%d) >= maxEventsLocalVars", psCurEvent->index); i = psCurEvent->index; if(psLocalVarsB[i] != NULL) //any vars stored for this event { int line; char *text; //debug(LOG_SCRIPT, "now checking event %s; index = %d\n", psCurEvent->pIdent, psCurEvent->index); scriptGetErrorData(&line, &text); for(psCurr = psLocalVarsB[i]; psCurr != NULL; psCurr = psCurr->psNext) { if(psCurr->pIdent == NULL) { debug(LOG_ERROR, "psCurr->pIdent == NULL"); debug(LOG_ERROR, "psCurr->index = %d", psCurr->index); } //debug(LOG_SCRIPT, "start comparing, num local vars=%d, at line %d\n", numEventLocalVars[i], line); //debug(LOG_SCRIPT, "current var=%s\n", psCurr->pIdent); //debug(LOG_SCRIPT, "passed string=%s\n", pIdent); //debug(LOG_SCRIPT, "comparing %s with %s \n", psCurr->pIdent, pIdent); if (strcmp(psCurr->pIdent, pIdent) == 0) { //debug(LOG_SCRIPT, "scriptLookUpVariable - local var found, type=%d\n", psCurr->type); *ppsSym = psCurr; return true; } } } } /* See if the symbol is in the global variable list. * This is not checked for when local variables are being defined. * This allows local variables to have the same name as global ones. */ if (!localVariableDef) { for(psCurr = psGlobalVars; psCurr != NULL; psCurr = psCurr->psNext) { if (strcmp(psCurr->pIdent, pIdent) == 0) { *ppsSym = psCurr; return true; } } for(psCurr = psGlobalArrays; psCurr != NULL; psCurr = psCurr->psNext) { if (strcmp(psCurr->pIdent, pIdent) == 0) { *ppsSym = psCurr; return true; } } } /* Failed to find the variable */ *ppsSym = NULL; //debug(LOG_SCRIPT, "END scriptLookUpVariable"); return false; } /* Add a new trigger symbol */ BOOL scriptAddTrigger(const char *pIdent, TRIGGER_DECL *psDecl, UDWORD line) { TRIGGER_SYMBOL *psTrigger, *psCurr, *psPrev; // Allocate the trigger psTrigger = malloc(sizeof(TRIGGER_SYMBOL)); if (!psTrigger) { scr_error("Out of memory"); return false; } psTrigger->pIdent = malloc(strlen(pIdent) + 1); if (!psTrigger->pIdent) { scr_error("Out of memory"); return false; } strcpy(psTrigger->pIdent, pIdent); if (psDecl->size > 0) { psTrigger->pCode = (INTERP_VAL *)malloc(psDecl->size * sizeof(INTERP_VAL)); if (!psTrigger->pCode) { scr_error("Out of memory"); return false; } memcpy(psTrigger->pCode, psDecl->pCode, psDecl->size * sizeof(INTERP_VAL)); } else { psTrigger->pCode = NULL; } psTrigger->size = psDecl->size; psTrigger->type = psDecl->type; psTrigger->time = psDecl->time; psTrigger->index = numTriggers++; psTrigger->psNext = NULL; // Add debug info if (genDebugInfo) { psTrigger->psDebug = malloc(sizeof(SCRIPT_DEBUG)); psTrigger->psDebug[0].offset = 0; psTrigger->psDebug[0].line = line; psTrigger->debugEntries = 1; } else { psTrigger->debugEntries = 0; psTrigger->psDebug = NULL; } // Store the trigger psPrev = NULL; for(psCurr = psTriggers; psCurr; psCurr = psCurr->psNext) { psPrev = psCurr; } if (psPrev) { psPrev->psNext = psTrigger; } else { psTriggers = psTrigger; } return true; } /* Lookup a trigger symbol */ BOOL scriptLookUpTrigger(const char *pIdent, TRIGGER_SYMBOL **ppsTrigger) { TRIGGER_SYMBOL *psCurr; //debug(LOG_SCRIPT, "scriptLookUpTrigger"); for(psCurr = psTriggers; psCurr; psCurr=psCurr->psNext) { if (strcmp(pIdent, psCurr->pIdent) == 0) { //debug(LOG_SCRIPT, "scriptLookUpTrigger: found"); *ppsTrigger = psCurr; return true; } } //debug(LOG_SCRIPT, "END scriptLookUpTrigger"); return false; } /* Lookup a callback trigger symbol */ BOOL scriptLookUpCallback(const char *pIdent, CALLBACK_SYMBOL **ppsCallback) { CALLBACK_SYMBOL *psCurr; //debug(LOG_SCRIPT, "scriptLookUpCallback"); if (!asScrCallbackTab) { return false; } for(psCurr = asScrCallbackTab; psCurr->type != 0; psCurr += 1) { if (strcmp(pIdent, psCurr->pIdent) == 0) { //debug(LOG_SCRIPT, "scriptLookUpCallback: found"); *ppsCallback = psCurr; return true; } } //debug(LOG_SCRIPT, "END scriptLookUpCallback: found"); return false; } /* Add a new event symbol */ BOOL scriptDeclareEvent(const char *pIdent, EVENT_SYMBOL **ppsEvent, SDWORD numArgs) { EVENT_SYMBOL *psEvent, *psCurr, *psPrev; // Allocate the event psEvent = malloc(sizeof(EVENT_SYMBOL)); if (!psEvent) { scr_error("Out of memory"); return false; } psEvent->pIdent = malloc(strlen(pIdent) + 1); if (!psEvent->pIdent) { scr_error("Out of memory"); return false; } strcpy(psEvent->pIdent, pIdent); psEvent->pCode = NULL; psEvent->size = 0; psEvent->psDebug = NULL; psEvent->debugEntries = 0; psEvent->index = numEvents++; psEvent->psNext = NULL; /* remember how many params this event has */ psEvent->numParams = numArgs; psEvent->bFunction = false; psEvent->bDeclared = false; psEvent->retType = VAL_VOID; /* functions can return a value */ // Add the event to the list psPrev = NULL; for(psCurr = psEvents; psCurr; psCurr = psCurr->psNext) { psPrev = psCurr; } if (psPrev) { psPrev->psNext = psEvent; } else { psEvents = psEvent; } *ppsEvent = psEvent; return true; } // Add the code to a defined event BOOL scriptDefineEvent(EVENT_SYMBOL *psEvent, CODE_BLOCK *psCode, SDWORD trigger) { ASSERT(psCode != NULL, "scriptDefineEvent: psCode == NULL"); ASSERT(psCode->size > 0, "Event '%s' is empty, please add at least 1 statement", psEvent->pIdent); // events with arguments can't have a trigger assigned ASSERT(!(psEvent->numParams > 0 && trigger >= 0), "Events with parameters can't have a trigger assigned, event: '%s' ", psEvent->pIdent); // Store the event code psEvent->pCode = (INTERP_VAL *)malloc(psCode->size * sizeof(INTERP_VAL)); if (!psEvent->pCode) { scr_error("Out of memory"); return false; } memcpy(psEvent->pCode, psCode->pCode, psCode->size * sizeof(INTERP_VAL)); psEvent->size = psCode->size; psEvent->trigger = trigger; // Add debug info if (genDebugInfo && (psCode->debugEntries > 0)) { psEvent->psDebug = malloc(sizeof(SCRIPT_DEBUG) * psCode->debugEntries); if (!psEvent->psDebug) { scr_error("Out of memory"); return false; } memcpy(psEvent->psDebug, psCode->psDebug, sizeof(SCRIPT_DEBUG) * psCode->debugEntries); psEvent->debugEntries = psCode->debugEntries; } else { psEvent->debugEntries = 0; psEvent->psDebug = NULL; } //debug(LOG_SCRIPT, "before define event"); /* store local vars */ if(psEvent->index >= maxEventsLocalVars) debug(LOG_ERROR, "scriptDefineEvent - psEvent->index >= maxEventsLocalVars"); return true; } /* Lookup an event symbol */ BOOL scriptLookUpEvent(const char *pIdent, EVENT_SYMBOL **ppsEvent) { EVENT_SYMBOL *psCurr; //debug(LOG_SCRIPT, "scriptLookUpEvent"); for(psCurr = psEvents; psCurr; psCurr=psCurr->psNext) { if (strcmp(pIdent, psCurr->pIdent) == 0) { //debug(LOG_SCRIPT, "scriptLookUpEvent:found"); *ppsEvent = psCurr; return true; } } //debug(LOG_SCRIPT, "END scriptLookUpEvent"); return false; } /* Look up a constant variable symbol */ BOOL scriptLookUpConstant(const char *pIdent, CONST_SYMBOL **ppsSym) { CONST_SYMBOL *psCurr; //debug(LOG_SCRIPT, "scriptLookUpConstant"); /* Scan the Constant list */ if (asScrConstantTab) { for(psCurr = asScrConstantTab; psCurr->type != VAL_VOID; psCurr++) { if (strcmp(psCurr->pIdent, pIdent) == 0) { *ppsSym = psCurr; return true; } } } //debug(LOG_SCRIPT, "END scriptLookUpConstant"); return false; } /* Look up a function symbol */ BOOL scriptLookUpFunction(const char *pIdent, FUNC_SYMBOL **ppsSym) { UDWORD i; //debug(LOG_SCRIPT, "scriptLookUpFunction"); /* See if the function is defined as an instinct function */ if (asScrInstinctTab) { for(i = 0; asScrInstinctTab[i].pFunc != NULL; i++) { if (strcmp(asScrInstinctTab[i].pIdent, pIdent) == 0) { *ppsSym = asScrInstinctTab + i; return true; } } } /* Failed to find the indentifier */ *ppsSym = NULL; //debug(LOG_SCRIPT, "END scriptLookUpFunction"); return false; } /* Look up a function symbol defined in script */ BOOL scriptLookUpCustomFunction(const char *pIdent, EVENT_SYMBOL **ppsSym) { EVENT_SYMBOL *psCurr; //debug(LOG_SCRIPT, "scriptLookUpCustomFunction"); /* See if the function is defined as a script function */ for(psCurr = psEvents; psCurr; psCurr = psCurr->psNext) { if(psCurr->bFunction) /* event defined as function */ { if (strcmp(psCurr->pIdent, pIdent) == 0) { //debug(LOG_SCRIPT, "scriptLookUpCustomFunction: %s is a custom function", pIdent); *ppsSym = psCurr; return true; } } } /* Failed to find the indentifier */ *ppsSym = NULL; //debug(LOG_SCRIPT, "END scriptLookUpCustomFunction"); return false; } /* Set the type table */ void scriptSetTypeTab(TYPE_SYMBOL *psTypeTab) { #ifdef DEBUG SDWORD i; INTERP_TYPE type; for(i=0, type=VAL_USERTYPESTART; psTypeTab[i].typeID != 0; i++) { ASSERT( psTypeTab[i].typeID == type, "scriptSetTypeTab: ID's must be >= VAL_USERTYPESTART and sequential" ); type = type + 1; } #endif asScrTypeTab = psTypeTab; } /* Set the function table */ void scriptSetFuncTab(FUNC_SYMBOL *psFuncTab) { asScrInstinctTab = psFuncTab; } /* Set the object variable table */ void scriptSetObjectTab(VAR_SYMBOL *psObjTab) { asScrObjectVarTab = psObjTab; } /* Set the external variable table */ void scriptSetExternalTab(VAR_SYMBOL *psExtTab) { asScrExternalTab = psExtTab; } /* Set the constant table */ void scriptSetConstTab(CONST_SYMBOL *psConstTab) { asScrConstantTab = psConstTab; } /* Set the callback table */ void scriptSetCallbackTab(CALLBACK_SYMBOL *psCallTab) { #ifdef DEBUG SDWORD i; TRIGGER_TYPE type; for(i=0, type=TR_CALLBACKSTART; psCallTab[i].type != 0; i++) { ASSERT( psCallTab[i].type == type, "scriptSetCallbackTab: ID's must be >= VAL_CALLBACKSTART and sequential" ); type = type + 1; } #endif asScrCallbackTab = psCallTab; }