2007-01-15 12:09:25 -08:00
|
|
|
/*
|
|
|
|
This file is part of Warzone 2100.
|
|
|
|
Copyright (C) 1999-2004 Eidos Interactive
|
|
|
|
Copyright (C) 2005-2007 Warzone Resurrection 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
|
|
|
|
*/
|
2007-06-28 10:47:08 -07:00
|
|
|
/*
|
|
|
|
* Stack.c
|
|
|
|
*
|
|
|
|
* A stack for the script interpreter.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
2006-11-06 06:40:07 -08:00
|
|
|
#include <string.h>
|
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2006-06-02 12:34:58 -07:00
|
|
|
#include "lib/framework/frame.h"
|
2007-06-28 10:47:08 -07:00
|
|
|
#include "interp.h"
|
|
|
|
#include "stack.h"
|
|
|
|
#include "codeprint.h"
|
|
|
|
#include "script.h"
|
|
|
|
|
|
|
|
/* number of values in each stack chunk */
|
2006-12-26 08:39:07 -08:00
|
|
|
#define INIT_SIZE 30 //15
|
|
|
|
#define EXT_SIZE 10 //2
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2006-08-25 13:38:27 -07:00
|
|
|
char STRSTACK[MAXSTACKLEN][MAXSTRLEN]; //simple string 'stack'
|
|
|
|
UDWORD CURSTACKSTR = 0; //Points to the top of the string stack
|
2006-08-19 06:26:11 -07:00
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
/* store for a 'chunk' of the stack */
|
|
|
|
typedef struct _stack_chunk
|
|
|
|
{
|
|
|
|
INTERP_VAL *aVals;
|
|
|
|
UDWORD size;
|
|
|
|
|
2008-03-27 11:38:25 -07:00
|
|
|
struct _stack_chunk *psNext, *psPrev;
|
2007-06-28 10:47:08 -07:00
|
|
|
} STACK_CHUNK;
|
|
|
|
|
|
|
|
/* The first chunk of the stack */
|
2008-03-27 11:38:25 -07:00
|
|
|
static STACK_CHUNK *psStackBase=NULL;
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
/* The current stack chunk */
|
2008-03-27 11:38:25 -07:00
|
|
|
static STACK_CHUNK *psCurrChunk=NULL;
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
/* The current free entry on the current stack chunk */
|
2008-03-27 11:38:25 -07:00
|
|
|
static UDWORD currEntry=0;
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2008-03-27 11:38:25 -07:00
|
|
|
/* Get rid of the top value without returning it */
|
|
|
|
static inline BOOL stackRemoveTop(void);
|
2006-12-16 06:59:50 -08:00
|
|
|
|
2006-08-25 13:38:27 -07:00
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
/* Check if the stack is empty */
|
|
|
|
BOOL stackEmpty(void)
|
|
|
|
{
|
2008-03-25 17:27:56 -07:00
|
|
|
return (psCurrChunk->psPrev == NULL && currEntry == 0);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Allocate a new chunk for the stack */
|
|
|
|
static BOOL stackNewChunk(UDWORD size)
|
|
|
|
{
|
2006-11-16 06:30:29 -08:00
|
|
|
/* see if a chunk has already been allocated */
|
2007-06-28 10:47:08 -07:00
|
|
|
if (psCurrChunk->psNext != NULL)
|
|
|
|
{
|
|
|
|
psCurrChunk = psCurrChunk->psNext;
|
|
|
|
currEntry = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Allocate a new chunk */
|
2008-05-25 08:40:56 -07:00
|
|
|
psCurrChunk->psNext = calloc(1, sizeof(*psCurrChunk->psNext));
|
2007-06-28 10:47:08 -07:00
|
|
|
if (!psCurrChunk->psNext)
|
|
|
|
{
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2008-05-25 08:40:56 -07:00
|
|
|
psCurrChunk->psNext->aVals = calloc(size, sizeof(*psCurrChunk->psNext->aVals));
|
2007-06-28 10:47:08 -07:00
|
|
|
if (!psCurrChunk->psNext->aVals)
|
|
|
|
{
|
2007-04-15 03:43:05 -07:00
|
|
|
free(psCurrChunk->psNext);
|
2007-06-12 10:56:34 -07:00
|
|
|
psCurrChunk->psNext = NULL;
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
psCurrChunk->psNext->size = size;
|
|
|
|
psCurrChunk->psNext->psPrev = psCurrChunk;
|
|
|
|
psCurrChunk->psNext->psNext = NULL;
|
|
|
|
psCurrChunk = psCurrChunk->psNext;
|
|
|
|
currEntry = 0;
|
|
|
|
}
|
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
2008-03-25 17:27:56 -07:00
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
/* Push a value onto the stack */
|
2008-03-27 11:38:25 -07:00
|
|
|
BOOL stackPush(INTERP_VAL *psVal)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
|
|
|
/* Store the value in the stack - psCurrChunk/currEntry always point to
|
2006-11-16 06:30:29 -08:00
|
|
|
valid space */
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2008-03-27 11:38:25 -07:00
|
|
|
/* String support: Copy the string, otherwise the stack will operate directly on the
|
|
|
|
original string (like & opcode will actually store the result in the first
|
|
|
|
variable which would point to the original string) */
|
|
|
|
if(psVal->type == VAL_STRING) //pushing string
|
|
|
|
{
|
|
|
|
/* strings should already have memory allocated */
|
|
|
|
if(psCurrChunk->aVals[currEntry].type != VAL_STRING) //needs to have memory allocated for string
|
|
|
|
psCurrChunk->aVals[currEntry].v.sval = (char*)malloc(MAXSTRLEN);
|
|
|
|
|
|
|
|
strcpy(psCurrChunk->aVals[currEntry].v.sval, psVal->v.sval); //copy string to stack
|
|
|
|
psCurrChunk->aVals[currEntry].type = VAL_STRING;
|
|
|
|
}
|
|
|
|
else /* pushing non-string */
|
|
|
|
{
|
|
|
|
/* free stack var allocated string memory, if stack var used to be of type VAL_STRING */
|
|
|
|
if(psCurrChunk->aVals[currEntry].type == VAL_STRING)
|
|
|
|
{
|
|
|
|
free(psCurrChunk->aVals[currEntry].v.sval); //don't need it anymore
|
|
|
|
psCurrChunk->aVals[currEntry].v.sval = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy type/data as union */
|
|
|
|
memcpy(&(psCurrChunk->aVals[currEntry]), psVal, sizeof(INTERP_VAL));
|
|
|
|
}
|
2006-08-19 06:26:11 -07:00
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
/* Now update psCurrChunk and currEntry */
|
|
|
|
currEntry++;
|
|
|
|
if (currEntry == psCurrChunk->size)
|
|
|
|
{
|
|
|
|
/* At the end of this chunk, need a new one */
|
|
|
|
if (!stackNewChunk(EXT_SIZE))
|
|
|
|
{
|
|
|
|
/* Out of memory */
|
2006-08-19 06:26:11 -07:00
|
|
|
debug(LOG_ERROR, "stackPush: Out of memory");
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-27 11:38:25 -07:00
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
/* Pop a value off the stack */
|
2008-03-27 11:38:25 -07:00
|
|
|
BOOL stackPop(INTERP_VAL *psVal)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2008-03-25 17:27:56 -07:00
|
|
|
if (stackEmpty())
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2006-08-19 06:26:11 -07:00
|
|
|
debug(LOG_ERROR, "stackPop: stack empty");
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackPop: stack empty" );
|
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* move the stack pointer down one */
|
2008-03-27 11:38:25 -07:00
|
|
|
if (currEntry == 0)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2008-03-27 11:38:25 -07:00
|
|
|
/* have to move onto the previous chunk. */
|
|
|
|
psCurrChunk = psCurrChunk->psPrev;
|
|
|
|
currEntry = psCurrChunk->size -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
currEntry--;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
2006-11-16 06:30:29 -08:00
|
|
|
/* copy the entire value off the stack */
|
2008-03-27 11:38:25 -07:00
|
|
|
memcpy(psVal, &(psCurrChunk->aVals[currEntry]), sizeof(INTERP_VAL));
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
2006-12-26 08:39:07 -08:00
|
|
|
/* Return pointer to the top value without poping it */
|
2008-03-27 11:38:25 -07:00
|
|
|
BOOL stackPeekTop(INTERP_VAL **ppsVal)
|
2006-12-26 08:39:07 -08:00
|
|
|
{
|
|
|
|
if ((psCurrChunk->psPrev == NULL) && (currEntry == 0))
|
|
|
|
{
|
|
|
|
debug(LOG_ERROR, "stackPeekTop: stack empty");
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackPeekTop: stack empty" );
|
|
|
|
return false;
|
2006-12-26 08:39:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (currEntry == 0)
|
|
|
|
{
|
|
|
|
STACK_CHUNK *pPrevChunck;
|
|
|
|
|
|
|
|
/* have to move onto the previous chunk */
|
|
|
|
pPrevChunck = psCurrChunk->psPrev;
|
|
|
|
|
|
|
|
/* Return top value */
|
|
|
|
*ppsVal = &(pPrevChunck->aVals[pPrevChunck->size - 1]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Return top value of the current chunk */
|
|
|
|
*ppsVal = &(psCurrChunk->aVals[currEntry - 1]);
|
|
|
|
}
|
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2006-12-26 08:39:07 -08:00
|
|
|
}
|
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
/* Pop a value off the stack, checking that the type matches what is passed in */
|
2008-03-27 11:38:25 -07:00
|
|
|
BOOL stackPopType(INTERP_VAL *psVal)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2008-03-27 11:38:25 -07:00
|
|
|
INTERP_VAL *psTop;
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
if ((psCurrChunk->psPrev == NULL) && (currEntry == 0))
|
|
|
|
{
|
2006-08-15 11:38:51 -07:00
|
|
|
debug(LOG_ERROR, "stackPopType: stack empty");
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackPopType: stack empty" );
|
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* move the stack pointer down one */
|
|
|
|
if (currEntry == 0)
|
|
|
|
{
|
|
|
|
/* have to move onto the previous chunk. */
|
|
|
|
psCurrChunk = psCurrChunk->psPrev;
|
|
|
|
currEntry = psCurrChunk->size -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
currEntry--;
|
|
|
|
}
|
|
|
|
|
|
|
|
psTop = psCurrChunk->aVals + currEntry;
|
2006-08-15 11:38:51 -07:00
|
|
|
|
2006-08-19 06:26:11 -07:00
|
|
|
//String support
|
|
|
|
if(psVal->type == VAL_STRING) //If we are about to assign something to a string variable (psVal)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2006-11-16 06:30:29 -08:00
|
|
|
if(psTop->type != VAL_STRING) //if assigning a non-string value to a string variable, then convert the value
|
2006-08-19 06:26:11 -07:00
|
|
|
{
|
|
|
|
/* Check for compatible types */
|
2006-11-16 06:30:29 -08:00
|
|
|
switch (psTop->type)
|
2006-08-19 06:26:11 -07:00
|
|
|
{
|
2006-11-16 06:30:29 -08:00
|
|
|
case VAL_INT: /* int->string */
|
|
|
|
sprintf(psVal->v.sval, "%d", psTop->v.ival);
|
|
|
|
break;
|
|
|
|
case VAL_BOOL: /* bool->string */
|
|
|
|
sprintf(psVal->v.sval, "%d", psTop->v.bval);
|
|
|
|
break;
|
|
|
|
case VAL_FLOAT: /* float->string */
|
|
|
|
sprintf(psVal->v.sval, "%f", psTop->v.fval);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
debug(LOG_ERROR, "stackPopType: trying to assign an incompatible data type to a string variable (type: %d)", psTop->type);
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
2006-08-19 06:26:11 -07:00
|
|
|
}
|
|
|
|
}
|
2006-11-16 06:30:29 -08:00
|
|
|
else //assigning string value to a string variable - COPY string
|
2006-08-19 06:26:11 -07:00
|
|
|
{
|
2006-11-16 06:30:29 -08:00
|
|
|
strcpy(psVal->v.sval, psTop->v.sval);
|
2006-08-19 06:26:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else // we are about to assign something to a non-string variable (psVal)
|
|
|
|
{
|
|
|
|
if (!interpCheckEquiv(psVal->type,psTop->type))
|
|
|
|
{
|
2007-04-01 09:22:01 -07:00
|
|
|
debug(LOG_ERROR, "stackPopType: type mismatch: %d/%d", psVal->type, psTop->type);
|
2007-06-04 09:49:58 -07:00
|
|
|
ASSERT( !"type mismatch", "stackPopType: type mismatch: %d/%d", psVal->type, psTop->type);
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2006-08-19 06:26:11 -07:00
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2006-11-16 06:30:29 -08:00
|
|
|
/* copy the entire union off the stack */
|
|
|
|
memcpy(&(psVal->v), &(psTop->v), sizeof(psTop->v));
|
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Pop a number of values off the stack checking their types
|
|
|
|
* This is used by instinct functions to get their parameters
|
|
|
|
*/
|
2008-03-24 15:09:31 -07:00
|
|
|
BOOL stackPopParams(unsigned int numParams, ...)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2008-03-24 15:09:31 -07:00
|
|
|
va_list args;
|
|
|
|
unsigned int i, index;
|
|
|
|
STACK_CHUNK *psCurr;
|
2006-08-19 06:26:11 -07:00
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
va_start(args, numParams);
|
|
|
|
|
|
|
|
// Find the position of the first parameter, and set
|
|
|
|
// the stack top to it
|
2008-03-24 15:09:31 -07:00
|
|
|
if (numParams <= currEntry)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
|
|
|
// parameters are all on current chunk
|
|
|
|
currEntry = currEntry - numParams;
|
|
|
|
psCurr = psCurrChunk;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Have to work down the previous chunks to find the first param
|
2008-03-24 15:09:31 -07:00
|
|
|
unsigned int params = numParams - currEntry;
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
for(psCurr = psCurrChunk->psPrev; psCurr != NULL; psCurr = psCurr->psPrev)
|
|
|
|
{
|
|
|
|
if (params <= psCurr->size)
|
|
|
|
{
|
|
|
|
// found the first param
|
|
|
|
currEntry = psCurr->size - params;
|
|
|
|
psCurrChunk = psCurr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
params -= psCurr->size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-08-19 06:26:11 -07:00
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
if (!psCurr)
|
|
|
|
{
|
2008-03-24 15:09:31 -07:00
|
|
|
debug( LOG_ERROR, "stackPopParams: not enough parameters on stack" );
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackPopParams: not enough parameters on stack" );
|
2008-03-24 15:09:31 -07:00
|
|
|
va_end(args);
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get the values, checking their types
|
|
|
|
index = currEntry;
|
2008-03-24 15:09:31 -07:00
|
|
|
for (i = 0; i < numParams; i++)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2008-03-24 15:09:31 -07:00
|
|
|
INTERP_VAL *psVal = psCurr->aVals + index;
|
|
|
|
INTERP_TYPE type = va_arg(args, INTERP_TYPE);
|
|
|
|
void * pData = va_arg(args, void *);
|
2006-08-19 06:26:11 -07:00
|
|
|
|
2008-03-24 15:09:31 -07:00
|
|
|
switch (type)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2008-03-24 15:09:31 -07:00
|
|
|
case VAL_BOOL:
|
|
|
|
*(BOOL*)pData = psVal->v.bval;
|
|
|
|
break;
|
|
|
|
case VAL_INT:
|
|
|
|
*(int*)pData = psVal->v.ival;
|
|
|
|
break;
|
|
|
|
case VAL_FLOAT:
|
|
|
|
*(float*)pData = psVal->v.fval;
|
|
|
|
break;
|
|
|
|
case VAL_STRING:
|
|
|
|
switch (psVal->type)
|
2006-12-16 06:59:50 -08:00
|
|
|
{
|
2008-03-24 15:09:31 -07:00
|
|
|
case VAL_BOOL:
|
|
|
|
sprintf((char*)pData, "%d", psVal->v.bval);
|
|
|
|
break;
|
|
|
|
case VAL_INT:
|
|
|
|
sprintf((char*)pData, "%d", psVal->v.ival);
|
|
|
|
break;
|
|
|
|
case VAL_FLOAT:
|
|
|
|
sprintf((char*)pData, "%f", psVal->v.fval);
|
|
|
|
break;
|
|
|
|
case VAL_STRING:
|
|
|
|
strcpy((char*)pData, psVal->v.sval);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(false,
|
|
|
|
"StackPopParam - wrong data type being converted to string (type: %s)",
|
2008-03-26 07:57:35 -07:00
|
|
|
scriptTypeToString(psVal->type));
|
2008-03-24 15:09:31 -07:00
|
|
|
break;
|
2006-12-16 06:59:50 -08:00
|
|
|
}
|
2008-03-24 15:09:31 -07:00
|
|
|
break;
|
|
|
|
default: // anything (including references)
|
|
|
|
if (scriptTypeIsPointer(psVal->type))
|
2006-12-16 06:59:50 -08:00
|
|
|
{
|
2008-03-24 15:09:31 -07:00
|
|
|
if (psVal->type >= VAL_REF) // if it's a reference
|
|
|
|
{
|
|
|
|
INTERP_VAL *refVal = psVal->v.oval; // oval is a pointer to INTERP_VAL in this case
|
|
|
|
|
|
|
|
/* doublecheck types */
|
|
|
|
ASSERT(interpCheckEquiv(type & ~VAL_REF, refVal->type),
|
|
|
|
"stackPopParams: type mismatch for a reference (expected %s, got %s)",
|
2008-03-26 07:57:35 -07:00
|
|
|
scriptTypeToString(type & ~VAL_REF), scriptTypeToString(refVal->type));
|
2008-03-24 15:09:31 -07:00
|
|
|
// type of provided container and type of the INTERP_VAL pointed by psVal->v.oval
|
|
|
|
|
|
|
|
*(void**)pData = &(refVal->v); /* get pointer to the union */
|
|
|
|
}
|
|
|
|
else // some pointer type
|
|
|
|
{
|
|
|
|
*(void**)pData = psVal->v.oval;
|
|
|
|
}
|
2006-12-16 06:59:50 -08:00
|
|
|
}
|
2008-03-24 15:09:31 -07:00
|
|
|
else
|
2006-11-16 06:30:29 -08:00
|
|
|
{
|
2008-03-24 15:09:31 -07:00
|
|
|
*(int*)pData = psVal->v.ival;
|
2006-11-16 06:30:29 -08:00
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
2008-03-24 15:09:31 -07:00
|
|
|
index++;
|
2007-06-28 10:47:08 -07:00
|
|
|
if (index >= psCurr->size)
|
|
|
|
{
|
|
|
|
psCurr = psCurr->psNext;
|
|
|
|
index = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
va_end(args);
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-11-16 06:30:29 -08:00
|
|
|
/* Push a value onto the stack without using a value structure
|
|
|
|
NOTE: result->type is _not_ set yet - use 'type' instead
|
|
|
|
*/
|
|
|
|
BOOL stackPushResult(INTERP_TYPE type, INTERP_VAL *result)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2006-11-16 06:30:29 -08:00
|
|
|
/* assign type, wasn't done before */
|
|
|
|
result->type = type;
|
2006-09-19 09:07:06 -07:00
|
|
|
|
2006-11-16 06:30:29 -08:00
|
|
|
/* String support: Copy the string, otherwise the stack will operate directly on the
|
|
|
|
original string (like & opcode will actually store the result in the first
|
|
|
|
variable which would point to the original string) */
|
|
|
|
if(result->type == VAL_STRING) //pushing string
|
2006-09-19 09:07:06 -07:00
|
|
|
{
|
2006-11-16 06:30:29 -08:00
|
|
|
/* strings should already have memory allocated */
|
|
|
|
if(psCurrChunk->aVals[currEntry].type != VAL_STRING) //needs to have memory allocated for string
|
2007-04-15 03:43:05 -07:00
|
|
|
psCurrChunk->aVals[currEntry].v.sval = (char*)malloc(MAXSTRLEN);
|
2006-11-16 06:30:29 -08:00
|
|
|
|
|
|
|
strcpy(psCurrChunk->aVals[currEntry].v.sval, result->v.sval); //copy string to stack
|
|
|
|
psCurrChunk->aVals[currEntry].type = VAL_STRING;
|
2006-09-19 09:07:06 -07:00
|
|
|
}
|
2006-11-16 06:30:29 -08:00
|
|
|
else /* pushing non-string */
|
2006-09-19 09:07:06 -07:00
|
|
|
{
|
2006-11-16 06:30:29 -08:00
|
|
|
/* free stack var allocated string memory, if stack var used to be of type VAL_STRING */
|
|
|
|
if(psCurrChunk->aVals[currEntry].type == VAL_STRING)
|
2007-06-12 10:56:34 -07:00
|
|
|
{
|
2007-04-15 03:43:05 -07:00
|
|
|
free(psCurrChunk->aVals[currEntry].v.sval); //don't need it anymore
|
2007-06-12 10:56:34 -07:00
|
|
|
psCurrChunk->aVals[currEntry].v.sval = NULL;
|
|
|
|
}
|
2006-11-16 06:30:29 -08:00
|
|
|
|
|
|
|
/* copy type/data as union */
|
|
|
|
memcpy(&(psCurrChunk->aVals[currEntry]), result, sizeof(INTERP_VAL));
|
2006-09-19 09:07:06 -07:00
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
// Now update psCurrChunk and currEntry
|
|
|
|
currEntry++;
|
|
|
|
if (currEntry == psCurrChunk->size)
|
|
|
|
{
|
|
|
|
// At the end of this chunk, need a new one
|
|
|
|
if (!stackNewChunk(EXT_SIZE))
|
|
|
|
{
|
|
|
|
// Out of memory
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Look at a value on the stack without removing it.
|
|
|
|
* index is how far down the stack to look.
|
|
|
|
* Index 0 is the top entry on the stack.
|
|
|
|
*/
|
|
|
|
BOOL stackPeek(INTERP_VAL *psVal, UDWORD index)
|
|
|
|
{
|
2008-03-27 11:38:25 -07:00
|
|
|
STACK_CHUNK *psCurr;
|
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
if (index < currEntry)
|
|
|
|
{
|
|
|
|
/* Looking at entry on current chunk */
|
|
|
|
memcpy(psVal, &(psCurrChunk->aVals[currEntry - index - 1]), sizeof(INTERP_VAL));
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Have to work down the previous chunks to find the entry */
|
|
|
|
index -= currEntry;
|
|
|
|
|
|
|
|
for(psCurr = psCurrChunk->psPrev; psCurr != NULL; psCurr = psCurr->psPrev)
|
|
|
|
{
|
|
|
|
if (index < psCurr->size)
|
|
|
|
{
|
|
|
|
/* found the entry */
|
|
|
|
memcpy(psVal, &(psCurr->aVals[psCurr->size - 1 - index]), sizeof(INTERP_VAL));
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2008-03-27 11:38:25 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
index -= psCurr->size;
|
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we got here the index is off the bottom of the stack */
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackPeek: index too large" );
|
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Print the top value on the stack */
|
|
|
|
void stackPrintTop(void)
|
|
|
|
{
|
2008-03-25 17:45:03 -07:00
|
|
|
INTERP_VAL sVal;
|
2007-06-28 10:47:08 -07:00
|
|
|
if (stackPeek(&sVal, 0))
|
|
|
|
{
|
2008-03-25 17:45:03 -07:00
|
|
|
cpPrintVal(sVal);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-08-22 07:28:49 -07:00
|
|
|
debug( LOG_NEVER, "STACK EMPTY" );
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Do binary operations on the top of the stack
|
|
|
|
* This effectively pops two values and pushes the result
|
|
|
|
*/
|
|
|
|
BOOL stackBinaryOp(OPCODE opcode)
|
|
|
|
{
|
|
|
|
STACK_CHUNK *psChunk;
|
|
|
|
INTERP_VAL *psV1, *psV2;
|
|
|
|
|
|
|
|
// Get the parameters
|
|
|
|
if (psCurrChunk->psPrev == NULL && currEntry < 2)
|
|
|
|
{
|
2006-08-19 06:26:11 -07:00
|
|
|
debug(LOG_ERROR, "stackBinaryOp: not enough entries on stack");
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackBinaryOp: not enough entries on stack" );
|
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (currEntry > 1)
|
|
|
|
{
|
|
|
|
psV1 = psCurrChunk->aVals + currEntry - 2;
|
|
|
|
psV2 = psCurrChunk->aVals + currEntry - 1;
|
|
|
|
currEntry -= 1;
|
|
|
|
}
|
|
|
|
else if (currEntry == 1)
|
|
|
|
{
|
|
|
|
// One value is on the previous chunk
|
|
|
|
psChunk = psCurrChunk->psPrev;
|
|
|
|
psV1 = psChunk->aVals + psChunk->size - 1;
|
|
|
|
psV2 = psCurrChunk->aVals + currEntry - 1;
|
|
|
|
currEntry -= 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// both on the previous chunk, pop to the previous chunk
|
|
|
|
psCurrChunk = psCurrChunk->psPrev;
|
|
|
|
currEntry = psCurrChunk->size - 1;
|
|
|
|
psV1 = psCurrChunk->aVals + psCurrChunk->size - 2;
|
|
|
|
psV2 = psCurrChunk->aVals + psCurrChunk->size - 1;
|
|
|
|
}
|
|
|
|
|
2006-11-16 06:30:29 -08:00
|
|
|
if(opcode != OP_CONC) //string - Don't check if OP_CONC, since types can be mixed here
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2006-08-19 06:26:11 -07:00
|
|
|
if (!interpCheckEquiv(psV1->type, psV2->type))
|
|
|
|
{
|
|
|
|
debug(LOG_ERROR, "stackBinaryOp: type mismatch");
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackBinaryOp: type mismatch" );
|
|
|
|
return false;
|
2006-08-19 06:26:11 -07:00
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2006-11-17 09:07:03 -08:00
|
|
|
/* find out if the result will be a float. Both or neither arguments are floats - should be taken care of by bison*/
|
|
|
|
if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT)
|
|
|
|
{
|
|
|
|
ASSERT(psV1->type == VAL_FLOAT && psV2->type == VAL_FLOAT, "Can't implicitly convert float->int (type1: %d, type2: %d)", psV1->type , psV2->type );
|
|
|
|
}
|
2006-11-16 06:30:29 -08:00
|
|
|
}
|
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
// do the operation
|
|
|
|
switch (opcode)
|
|
|
|
{
|
|
|
|
case OP_ADD:
|
2006-11-16 06:30:29 -08:00
|
|
|
if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT)
|
|
|
|
{
|
|
|
|
psV1->type = VAL_FLOAT;
|
|
|
|
psV1->v.fval = psV1->v.fval + psV2->v.fval;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
psV1->v.ival = psV1->v.ival + psV2->v.ival;
|
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
break;
|
|
|
|
case OP_SUB:
|
2006-11-16 06:30:29 -08:00
|
|
|
if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT)
|
|
|
|
{
|
|
|
|
psV1->type = VAL_FLOAT;
|
|
|
|
psV1->v.fval = psV1->v.fval - psV2->v.fval;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
psV1->v.ival = psV1->v.ival - psV2->v.ival;
|
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
break;
|
|
|
|
case OP_MUL:
|
2006-11-16 06:30:29 -08:00
|
|
|
if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT)
|
|
|
|
{
|
|
|
|
psV1->type = VAL_FLOAT;
|
|
|
|
psV1->v.fval = psV1->v.fval * psV2->v.fval;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
psV1->v.ival = psV1->v.ival * psV2->v.ival;
|
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
break;
|
|
|
|
case OP_DIV:
|
2006-11-16 06:30:29 -08:00
|
|
|
if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT)
|
|
|
|
{
|
|
|
|
if(psV2->v.fval != 0.0f)
|
|
|
|
{
|
|
|
|
psV1->type = VAL_FLOAT;
|
|
|
|
psV1->v.fval = psV1->v.fval / psV2->v.fval;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-05-23 07:29:11 -07:00
|
|
|
debug(LOG_ERROR, "stackBinaryOp: division by zero (float)");
|
|
|
|
return false;
|
2006-11-16 06:30:29 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(psV2->v.ival != 0)
|
|
|
|
{
|
|
|
|
psV1->v.ival = psV1->v.ival / psV2->v.ival;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-05-23 07:29:11 -07:00
|
|
|
debug(LOG_ERROR, "stackBinaryOp: division by zero (integer)");
|
|
|
|
return false;
|
2006-11-16 06:30:29 -08:00
|
|
|
}
|
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
break;
|
|
|
|
case OP_AND:
|
|
|
|
psV1->v.bval = psV1->v.bval && psV2->v.bval;
|
|
|
|
break;
|
|
|
|
case OP_OR:
|
|
|
|
psV1->v.bval = psV1->v.bval || psV2->v.bval;
|
|
|
|
break;
|
|
|
|
case OP_EQUAL:
|
2006-12-16 06:59:50 -08:00
|
|
|
if(psV1->type == VAL_FLOAT && psV2->type == VAL_FLOAT){
|
2006-12-02 15:27:00 -08:00
|
|
|
psV1->v.bval = psV1->v.fval == psV2->v.fval;
|
2006-12-16 06:59:50 -08:00
|
|
|
}else if(psV1->type == VAL_STRING && psV2->type == VAL_STRING){
|
|
|
|
psV1->v.bval = (strcasecmp(psV1->v.sval,psV2->v.sval) == 0); /* case-insensitive */
|
2006-12-02 15:27:00 -08:00
|
|
|
}else{
|
|
|
|
psV1->v.bval = psV1->v.ival == psV2->v.ival;
|
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
psV1->type = VAL_BOOL;
|
|
|
|
break;
|
|
|
|
case OP_NOTEQUAL:
|
2006-12-16 06:59:50 -08:00
|
|
|
if(psV1->type == VAL_FLOAT && psV2->type == VAL_FLOAT){
|
2006-12-02 15:27:00 -08:00
|
|
|
psV1->v.bval = psV1->v.fval != psV2->v.fval;
|
2006-12-16 06:59:50 -08:00
|
|
|
}else if(psV1->type == VAL_STRING && psV2->type == VAL_STRING){
|
|
|
|
psV1->v.bval = (strcasecmp(psV1->v.sval,psV2->v.sval) != 0); /* case-insensitive */
|
2006-12-02 15:27:00 -08:00
|
|
|
}else{
|
|
|
|
psV1->v.bval = psV1->v.ival != psV2->v.ival;
|
|
|
|
}
|
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
psV1->type = VAL_BOOL;
|
|
|
|
break;
|
|
|
|
case OP_GREATEREQUAL:
|
2006-12-02 15:27:00 -08:00
|
|
|
if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT){
|
|
|
|
psV1->v.bval = psV1->v.fval >= psV2->v.fval;
|
|
|
|
}else{
|
|
|
|
psV1->v.bval = psV1->v.ival >= psV2->v.ival;
|
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
psV1->type = VAL_BOOL;
|
|
|
|
break;
|
|
|
|
case OP_LESSEQUAL:
|
2006-12-02 15:27:00 -08:00
|
|
|
if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT){
|
|
|
|
psV1->v.bval = psV1->v.fval <= psV2->v.fval;
|
|
|
|
}else{
|
|
|
|
psV1->v.bval = psV1->v.ival <= psV2->v.ival;
|
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
psV1->type = VAL_BOOL;
|
|
|
|
break;
|
|
|
|
case OP_GREATER:
|
2006-12-02 15:27:00 -08:00
|
|
|
if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT){
|
|
|
|
psV1->v.bval = psV1->v.fval > psV2->v.fval;
|
|
|
|
}else{
|
|
|
|
psV1->v.bval = psV1->v.ival > psV2->v.ival;
|
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
psV1->type = VAL_BOOL;
|
|
|
|
break;
|
|
|
|
case OP_LESS:
|
2006-12-02 15:27:00 -08:00
|
|
|
if(psV1->type == VAL_FLOAT || psV2->type == VAL_FLOAT){
|
|
|
|
psV1->v.bval = psV1->v.fval < psV2->v.fval;
|
|
|
|
}else{
|
|
|
|
psV1->v.bval = psV1->v.ival < psV2->v.ival;
|
|
|
|
}
|
2007-06-28 10:47:08 -07:00
|
|
|
psV1->type = VAL_BOOL;
|
|
|
|
break;
|
2006-11-16 06:30:29 -08:00
|
|
|
case OP_CONC: //String concatenation
|
2006-08-19 06:26:11 -07:00
|
|
|
{
|
2006-09-03 13:32:01 -07:00
|
|
|
char tempstr1[MAXSTRLEN];
|
|
|
|
char tempstr2[MAXSTRLEN];
|
2006-08-19 06:26:11 -07:00
|
|
|
|
|
|
|
/* Check first value if it's compatible with Strings */
|
2006-11-16 06:30:29 -08:00
|
|
|
switch (psV1->type)
|
2006-08-19 06:26:11 -07:00
|
|
|
{
|
2006-11-16 06:30:29 -08:00
|
|
|
case VAL_INT: //first value isn't string, but can be converted to string
|
2007-10-24 14:11:29 -07:00
|
|
|
snprintf(tempstr1, sizeof(tempstr1), "%d", psV1->v.ival); //int->string
|
2006-11-16 06:30:29 -08:00
|
|
|
psV1->type = VAL_STRING; //Mark as string
|
2007-04-15 03:43:05 -07:00
|
|
|
psV1->v.sval = (char*)malloc(MAXSTRLEN); //allocate space for the string, since the result (string) of concatenation will be saved here
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
2006-08-19 06:26:11 -07:00
|
|
|
|
2006-11-16 06:30:29 -08:00
|
|
|
case VAL_BOOL:
|
2007-10-24 14:11:29 -07:00
|
|
|
snprintf(tempstr1, sizeof(tempstr1), "%d",psV1->v.bval); //bool->string
|
2006-11-16 06:30:29 -08:00
|
|
|
psV1->type = VAL_STRING; //Mark as string
|
2007-04-15 03:43:05 -07:00
|
|
|
psV1->v.sval = (char*)malloc(MAXSTRLEN); //allocate space for the string, since the result (string) of concatenation will be saved here
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VAL_FLOAT:
|
2007-10-24 14:11:29 -07:00
|
|
|
snprintf(tempstr1, sizeof(tempstr1), "%f", psV1->v.fval); //float->string
|
2006-11-16 06:30:29 -08:00
|
|
|
psV1->type = VAL_STRING; //Mark as string
|
2007-04-15 03:43:05 -07:00
|
|
|
psV1->v.sval = (char*)malloc(MAXSTRLEN); //allocate space for the string, since the result (string) of concatenation will be saved here
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VAL_STRING:
|
2008-05-25 06:46:49 -07:00
|
|
|
sstrcpy(tempstr1, psV1->v.sval);
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
debug(LOG_ERROR, "stackBinaryOp: OP_CONC: first parameter is not compatible with Strings");
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
2006-08-19 06:26:11 -07:00
|
|
|
}
|
|
|
|
|
2006-10-24 06:05:40 -07:00
|
|
|
/* Check second value if it's compatible with Strings */
|
2006-11-16 06:30:29 -08:00
|
|
|
switch (psV2->type)
|
2006-08-19 06:26:11 -07:00
|
|
|
{
|
2006-11-16 06:30:29 -08:00
|
|
|
case VAL_INT:
|
2007-10-24 14:11:29 -07:00
|
|
|
snprintf(tempstr2, sizeof(tempstr2), "%d", psV2->v.ival); //int->string
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VAL_BOOL:
|
2007-10-24 14:11:29 -07:00
|
|
|
snprintf(tempstr2, sizeof(tempstr2), "%d", psV2->v.bval); //bool->string
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VAL_FLOAT:
|
2007-10-24 14:11:29 -07:00
|
|
|
snprintf(tempstr2, sizeof(tempstr2), "%f", psV2->v.fval); //float->string
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VAL_STRING:
|
2008-05-25 06:46:49 -07:00
|
|
|
sstrcpy(tempstr2, psV2->v.sval);
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
debug(LOG_ERROR, "stackBinaryOp: OP_CONC: first parameter is not compatible with Strings");
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
2006-08-19 06:26:11 -07:00
|
|
|
}
|
|
|
|
|
2008-05-25 06:46:49 -07:00
|
|
|
sstrcat(tempstr1, tempstr2);
|
2007-10-24 14:11:29 -07:00
|
|
|
|
2006-08-19 06:26:11 -07:00
|
|
|
strcpy(psV1->v.sval,tempstr1); //Assign
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
default:
|
2006-08-19 06:26:11 -07:00
|
|
|
debug(LOG_ERROR, "stackBinaryOp: unknown opcode");
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackBinaryOp: unknown opcode" );
|
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Perform a unary operation on the top of the stack
|
|
|
|
* This effectively pops a value and pushes the result
|
|
|
|
*/
|
|
|
|
BOOL stackUnaryOp(OPCODE opcode)
|
|
|
|
{
|
|
|
|
STACK_CHUNK *psChunk;
|
|
|
|
INTERP_VAL *psVal;
|
|
|
|
|
|
|
|
// Get the value
|
|
|
|
if (psCurrChunk->psPrev == NULL && currEntry == 0)
|
|
|
|
{
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackUnaryOp: not enough entries on stack" );
|
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (currEntry > 0)
|
|
|
|
{
|
|
|
|
psVal = psCurrChunk->aVals + currEntry - 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Value is on the previous chunk
|
|
|
|
psChunk = psCurrChunk->psPrev;
|
|
|
|
psVal = psChunk->aVals + psChunk->size - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do the operation
|
|
|
|
switch (opcode)
|
|
|
|
{
|
2006-12-02 15:27:00 -08:00
|
|
|
case OP_INC:
|
|
|
|
switch (psVal->type)
|
|
|
|
{
|
2006-12-06 10:57:17 -08:00
|
|
|
case (VAL_REF | VAL_INT):
|
2006-12-16 06:59:50 -08:00
|
|
|
|
2007-01-08 05:40:12 -08:00
|
|
|
psVal = (INTERP_VAL*)psVal->v.oval; //get variable
|
2006-12-16 06:59:50 -08:00
|
|
|
|
|
|
|
ASSERT(psVal->type == VAL_INT, "Invalid type for increment opcode: %d", psVal->type);
|
|
|
|
|
|
|
|
psVal->v.ival++;
|
2006-12-06 10:57:17 -08:00
|
|
|
|
|
|
|
/* Just get rid of the variable pointer, since already increased it */
|
2008-03-27 11:38:25 -07:00
|
|
|
if (!stackRemoveTop())
|
2006-12-06 10:57:17 -08:00
|
|
|
{
|
|
|
|
debug( LOG_ERROR, "stackUnaryOpcode: OP_INC: could not pop" );
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2006-12-06 10:57:17 -08:00
|
|
|
}
|
|
|
|
|
2006-12-02 15:27:00 -08:00
|
|
|
break;
|
|
|
|
default:
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackUnaryOp: invalid type for OP_INC (type: %d)", psVal->type );
|
|
|
|
return false;
|
2006-12-02 15:27:00 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OP_DEC:
|
|
|
|
switch (psVal->type)
|
|
|
|
{
|
2006-12-06 10:57:17 -08:00
|
|
|
case (VAL_REF | VAL_INT):
|
2006-12-16 06:59:50 -08:00
|
|
|
|
2007-01-08 05:40:12 -08:00
|
|
|
psVal = (INTERP_VAL*)psVal->v.oval; //get variable
|
2006-12-16 06:59:50 -08:00
|
|
|
|
|
|
|
ASSERT(psVal->type == VAL_INT, "Invalid type for decrement opcode: %d", psVal->type);
|
|
|
|
|
|
|
|
psVal->v.ival--;
|
2006-12-06 10:57:17 -08:00
|
|
|
|
|
|
|
/* Just get rid of the variable pointer, since already decreased it */
|
2008-03-27 11:38:25 -07:00
|
|
|
if (!stackRemoveTop())
|
2006-12-06 10:57:17 -08:00
|
|
|
{
|
|
|
|
debug( LOG_ERROR, "stackUnaryOpcode: OP_DEC: could not pop" );
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2006-12-06 10:57:17 -08:00
|
|
|
}
|
2006-12-02 15:27:00 -08:00
|
|
|
break;
|
|
|
|
default:
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackUnaryOp: invalid type for OP_DEC (type: %d)", psVal->type );
|
|
|
|
return false;
|
2006-12-02 15:27:00 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
case OP_NEG:
|
|
|
|
switch (psVal->type)
|
|
|
|
{
|
|
|
|
case VAL_INT:
|
|
|
|
psVal->v.ival = - psVal->v.ival;
|
|
|
|
break;
|
2006-12-02 15:27:00 -08:00
|
|
|
case VAL_FLOAT:
|
|
|
|
psVal->v.fval = - psVal->v.fval;
|
|
|
|
break;
|
2007-06-28 10:47:08 -07:00
|
|
|
default:
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackUnaryOp: invalid type for negation (type: %d)", psVal->type );
|
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OP_NOT:
|
|
|
|
switch (psVal->type)
|
|
|
|
{
|
|
|
|
case VAL_BOOL:
|
|
|
|
psVal->v.bval = !psVal->v.bval;
|
|
|
|
break;
|
|
|
|
default:
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackUnaryOp: invalid type for NOT (type: %d)", psVal->type );
|
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT( false, "stackUnaryOp: unknown opcode (opcode: %d)", opcode );
|
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
2008-03-25 17:27:56 -07:00
|
|
|
BOOL stackCastTop(INTERP_TYPE neededType)
|
2006-11-16 06:30:29 -08:00
|
|
|
{
|
2008-03-25 17:27:56 -07:00
|
|
|
INTERP_VAL *pTop;
|
2006-11-16 06:30:29 -08:00
|
|
|
|
2008-03-25 17:27:56 -07:00
|
|
|
ASSERT(neededType == VAL_INT || neededType == VAL_FLOAT,
|
|
|
|
"stackCast: can't cast to %d", neededType);
|
2006-11-24 17:26:05 -08:00
|
|
|
|
2006-12-26 08:39:07 -08:00
|
|
|
if (!stackPeekTop(&pTop) || pTop==NULL)
|
2006-11-16 06:30:29 -08:00
|
|
|
{
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT(false, "castTop: failed to peek stack");
|
|
|
|
return false;
|
2006-11-16 06:30:29 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* see if we can cast this data type */
|
2006-12-26 08:39:07 -08:00
|
|
|
switch (pTop->type)
|
2006-11-16 06:30:29 -08:00
|
|
|
{
|
2008-03-25 17:27:56 -07:00
|
|
|
case VAL_BOOL:
|
|
|
|
switch (neededType)
|
|
|
|
{
|
|
|
|
case VAL_FLOAT: /* casting from bool to float */
|
2006-11-16 06:30:29 -08:00
|
|
|
|
2008-03-25 17:27:56 -07:00
|
|
|
pTop->v.fval = (float)pTop->v.bval;
|
|
|
|
pTop->type = VAL_FLOAT;
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
2008-03-25 17:27:56 -07:00
|
|
|
case VAL_INT:
|
|
|
|
switch (neededType)
|
|
|
|
{
|
|
|
|
case VAL_FLOAT: /* casting from int to float */
|
|
|
|
pTop->v.fval = (float)pTop->v.ival;
|
|
|
|
pTop->type = VAL_FLOAT;
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
2008-03-25 17:27:56 -07:00
|
|
|
case VAL_FLOAT:
|
|
|
|
switch (neededType)
|
|
|
|
{
|
|
|
|
case VAL_INT: /* casting from float to int */
|
|
|
|
pTop->v.ival = (int)pTop->v.fval;
|
|
|
|
pTop->type = VAL_INT;
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2006-11-16 06:30:29 -08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-25 17:27:56 -07:00
|
|
|
debug(LOG_ERROR, "can't cast from %s to %s",
|
2008-03-26 07:57:35 -07:00
|
|
|
scriptTypeToString(pTop->type), scriptTypeToString(neededType));
|
2008-03-25 17:27:56 -07:00
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2006-11-16 06:30:29 -08:00
|
|
|
}
|
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
/* Initialise the stack */
|
|
|
|
BOOL stackInitialise(void)
|
|
|
|
{
|
2008-05-25 08:40:56 -07:00
|
|
|
psStackBase = calloc(1, sizeof(*psStackBase));
|
2007-06-28 10:47:08 -07:00
|
|
|
if (psStackBase == NULL)
|
|
|
|
{
|
2006-08-22 07:28:49 -07:00
|
|
|
debug( LOG_ERROR, "Out of memory" );
|
|
|
|
abort();
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2008-05-25 08:40:56 -07:00
|
|
|
psStackBase->aVals = calloc(INIT_SIZE, sizeof(*psStackBase->aVals));
|
2007-06-28 10:47:08 -07:00
|
|
|
if (!psStackBase->aVals)
|
|
|
|
{
|
2006-08-22 07:28:49 -07:00
|
|
|
debug( LOG_ERROR, "Out of memory" );
|
|
|
|
abort();
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
psStackBase->size = INIT_SIZE;
|
|
|
|
psStackBase->psPrev = NULL;
|
|
|
|
psStackBase->psNext = NULL;
|
|
|
|
psCurrChunk = psStackBase;
|
|
|
|
|
2006-08-19 06:26:11 -07:00
|
|
|
//string support
|
|
|
|
CURSTACKSTR = 0; //initialize string 'stack'
|
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-27 11:38:25 -07:00
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
/* Shutdown the stack */
|
|
|
|
void stackShutDown(void)
|
|
|
|
{
|
2008-03-27 11:38:25 -07:00
|
|
|
STACK_CHUNK *psCurr, *psNext;
|
|
|
|
UDWORD i;
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2008-03-27 11:38:25 -07:00
|
|
|
if ((psCurrChunk != psStackBase) && (currEntry != 0))
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2006-08-22 07:28:49 -07:00
|
|
|
debug( LOG_NEVER, "stackShutDown: stack is not empty on shutdown" );
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for(psCurr = psStackBase; psCurr != NULL; psCurr = psNext)
|
|
|
|
{
|
|
|
|
psNext = psCurr->psNext;
|
2008-03-27 11:38:25 -07:00
|
|
|
|
|
|
|
/* Free strings */
|
|
|
|
for(i=0; i< psCurr->size; i++) //go through all values on this chunk
|
|
|
|
{
|
|
|
|
if(psCurr->aVals[i].type == VAL_STRING)
|
|
|
|
{
|
|
|
|
if(psCurr->aVals[i].v.sval != NULL) //FIXME: seems to be causing problems sometimes
|
|
|
|
{
|
|
|
|
debug(LOG_WZ, "freeing '%s' ", psCurr->aVals[i].v.sval);
|
|
|
|
free(psCurr->aVals[i].v.sval);
|
|
|
|
psCurr->aVals[i].v.sval = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
debug(LOG_SCRIPT, "stackShutDown: VAL_STRING with null pointer");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-15 03:43:05 -07:00
|
|
|
free(psCurr->aVals);
|
|
|
|
free(psCurr);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-27 11:38:25 -07:00
|
|
|
/* Get rid of the top value without returning it */
|
|
|
|
static inline BOOL stackRemoveTop(void)
|
2006-12-16 06:59:50 -08:00
|
|
|
{
|
2008-03-27 11:38:25 -07:00
|
|
|
if ((psCurrChunk->psPrev == NULL) && (currEntry == 0))
|
2006-12-16 06:59:50 -08:00
|
|
|
{
|
2008-03-27 11:38:25 -07:00
|
|
|
debug(LOG_ERROR, "stackRemoveTop: stack empty");
|
|
|
|
ASSERT( false, "stackRemoveTop: stack empty" );
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2006-12-16 06:59:50 -08:00
|
|
|
}
|
|
|
|
|
2008-03-27 11:38:25 -07:00
|
|
|
/* move the stack pointer down one */
|
|
|
|
if (currEntry == 0)
|
2006-12-16 06:59:50 -08:00
|
|
|
{
|
2008-03-27 11:38:25 -07:00
|
|
|
/* have to move onto the previous chunk. */
|
|
|
|
psCurrChunk = psCurrChunk->psPrev;
|
|
|
|
currEntry = psCurrChunk->size -1;
|
2006-12-16 06:59:50 -08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-03-27 11:38:25 -07:00
|
|
|
currEntry--;
|
2008-03-26 06:52:31 -07:00
|
|
|
}
|
2008-03-27 11:38:25 -07:00
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2006-12-16 06:59:50 -08:00
|
|
|
}
|
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
/* Reset the stack to an empty state */
|
|
|
|
void stackReset(void)
|
|
|
|
{
|
2008-03-27 11:38:25 -07:00
|
|
|
ASSERT( ((psCurrChunk == psStackBase) && (currEntry == 0)),
|
2006-08-23 05:58:48 -07:00
|
|
|
"stackReset: stack is not empty" );
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
psCurrChunk = psStackBase;
|
|
|
|
currEntry = 0;
|
|
|
|
}
|