Logo Search packages:      
Sourcecode: warzone2100 version File versions

stack.c

/*
      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
*/
/*
 * Stack.c
 *
 * A stack for the script interpreter.
 */

#include <stdarg.h>
#include <string.h>


#include "lib/framework/frame.h"
#include "interp.h"
#include "stack.h"
#include "codeprint.h"
#include "script.h"

/* number of values in each stack chunk */
#define INIT_SIZE       30    //15
#define EXT_SIZE        10    //2

char STRSTACK[MAXSTACKLEN][MAXSTRLEN]; //simple string 'stack'
UDWORD CURSTACKSTR = 0;    //Points to the top of the string stack

/* store for a 'chunk' of the stack */
typedef struct _stack_chunk
{
      INTERP_VAL  *aVals;
      UDWORD            size;

      struct _stack_chunk           *psNext, *psPrev;
} STACK_CHUNK;

/* The first chunk of the stack */
static STACK_CHUNK            *psStackBase=NULL;

/* The current stack chunk */
static STACK_CHUNK            *psCurrChunk=NULL;

/* The current free entry on the current stack chunk */
static UDWORD                 currEntry=0;

/* Get rid of the top value without returning it */
static inline BOOL stackRemoveTop(void);


/* Check if the stack is empty */
BOOL stackEmpty(void)
{
      return (psCurrChunk->psPrev == NULL && currEntry == 0);
}


/* Allocate a new chunk for the stack */
static BOOL stackNewChunk(UDWORD size)
{
      /* see if a chunk has already been allocated */
      if (psCurrChunk->psNext != NULL)
      {
            psCurrChunk = psCurrChunk->psNext;
            currEntry = 0;
      }
      else
      {
            /* Allocate a new chunk */
            psCurrChunk->psNext = calloc(1, sizeof(*psCurrChunk->psNext));
            if (!psCurrChunk->psNext)
            {
                  return false;
            }
            psCurrChunk->psNext->aVals = calloc(size, sizeof(*psCurrChunk->psNext->aVals));
            if (!psCurrChunk->psNext->aVals)
            {
                  free(psCurrChunk->psNext);
                  psCurrChunk->psNext = NULL;
                  return false;
            }

            psCurrChunk->psNext->size = size;
            psCurrChunk->psNext->psPrev = psCurrChunk;
            psCurrChunk->psNext->psNext = NULL;
            psCurrChunk = psCurrChunk->psNext;
            currEntry = 0;
      }

      return true;
}


/* Push a value onto the stack */
BOOL stackPush(INTERP_VAL  *psVal)
{
      /* Store the value in the stack - psCurrChunk/currEntry always point to
      valid space */

      /* 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));
      }

      /* 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 */
                  debug(LOG_ERROR, "stackPush: Out of memory");
                  return false;
            }
      }

      return true;
}



/* Pop a value off the stack */
BOOL stackPop(INTERP_VAL  *psVal)
{
      if (stackEmpty())
      {
            debug(LOG_ERROR, "stackPop: stack empty");
            ASSERT( false, "stackPop: stack empty" );
            return false;
      }

      /* 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--;
      }

      /* copy the entire value off the stack */
      memcpy(psVal, &(psCurrChunk->aVals[currEntry]), sizeof(INTERP_VAL));

      return true;
}

/* Return pointer to the top value without poping it */
BOOL stackPeekTop(INTERP_VAL  **ppsVal)
{
      if ((psCurrChunk->psPrev == NULL) && (currEntry == 0))
      {
            debug(LOG_ERROR, "stackPeekTop: stack empty");
            ASSERT( false, "stackPeekTop: stack empty" );
            return false;
      }

      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]);
      }

      return true;
}


/* Pop a value off the stack, checking that the type matches what is passed in */
BOOL stackPopType(INTERP_VAL  *psVal)
{
      INTERP_VAL  *psTop;

      if ((psCurrChunk->psPrev == NULL) && (currEntry == 0))
      {
            debug(LOG_ERROR, "stackPopType: stack empty");
            ASSERT( false, "stackPopType: stack empty" );
            return false;
      }

      /* 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;

      //String support
      if(psVal->type == VAL_STRING)       //If we are about to assign something to a string variable (psVal)
      {
            if(psTop->type != VAL_STRING)       //if assigning a non-string value to a string variable, then convert the value
            {
                  /* Check for compatible types */
                  switch (psTop->type)
                  {
                        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);
                              return false;
                              break;
                  }
            }
            else  //assigning string value to a string variable - COPY string
            {
                  strcpy(psVal->v.sval, psTop->v.sval);
            }
      }
      else        // we are about to assign something to a non-string variable (psVal)
      {
            if (!interpCheckEquiv(psVal->type,psTop->type))
            {
                  debug(LOG_ERROR, "stackPopType: type mismatch: %d/%d", psVal->type, psTop->type);
                  ASSERT( !"type mismatch", "stackPopType: type mismatch: %d/%d", psVal->type, psTop->type);
                  return false;
            }

            /* copy the entire union off the stack */
            memcpy(&(psVal->v), &(psTop->v), sizeof(psTop->v));
      }

      return true;
}


/* Pop a number of values off the stack checking their types
 * This is used by instinct functions to get their parameters
 */
BOOL stackPopParams(unsigned int numParams, ...)
{
      va_list args;
      unsigned int i, index;
      STACK_CHUNK *psCurr;

      // Find the position of the first parameter, and set
      // the stack top to it
      if (numParams <= currEntry)
      {
            // parameters are all on current chunk
            currEntry = currEntry - numParams;
            psCurr = psCurrChunk;
      }
      else
      {
            // Have to work down the previous chunks to find the first param
            unsigned int params = numParams - currEntry;

            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;
                  }
            }
      }

      if (!psCurr)
      {
            debug( LOG_ERROR, "stackPopParams: not enough parameters on stack" );
            ASSERT( false, "stackPopParams: not enough parameters on stack" );
            return false;
      }

      va_start(args, numParams);

      // Get the values, checking their types
      index = currEntry;
      for (i = 0; i < numParams; i++)
      {
            INTERP_VAL *psVal = psCurr->aVals + index;
            INTERP_TYPE type = va_arg(args, INTERP_TYPE);
            void * pData = va_arg(args, void *);

            switch (type)
            {
                  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)
                        {
                              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)",
                                          scriptTypeToString(psVal->type));
                                    break;
                        }
                        break;
                  default: // anything (including references)
                        if (scriptTypeIsPointer(psVal->type))
                        {
                              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)",
                                          scriptTypeToString(type & ~VAL_REF), scriptTypeToString(refVal->type));
                                    // 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;
                              }
                        }
                        else
                        {
                              *(int*)pData = psVal->v.ival;
                        }
            }

            index++;
            if (index >= psCurr->size)
            {
                  psCurr = psCurr->psNext;
                  index = 0;
            }
      }

      va_end(args);
      return true;
}


/* 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)
{
      /* assign type, wasn't done before */
      result->type = type;

      /* 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
      {
            /* 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, result->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]), result, sizeof(INTERP_VAL));
      }

      // 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
                  return false;
            }
      }

      return true;
}


/* 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)
{
      STACK_CHUNK       *psCurr;

      if (index < currEntry)
      {
            /* Looking at entry on current chunk */
            memcpy(psVal, &(psCurrChunk->aVals[currEntry - index - 1]), sizeof(INTERP_VAL));
            return true;
      }
      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));
                        return true;
                  }
                  else
                  {
                        index -= psCurr->size;
                  }
            }
      }

      /* If we got here the index is off the bottom of the stack */
      ASSERT( false, "stackPeek: index too large" );
      return false;
}


/* Print the top value on the stack */
void stackPrintTop(void)
{
      INTERP_VAL sVal;
      if (stackPeek(&sVal, 0))
      {
            cpPrintVal(sVal);
      }
      else
      {
            debug( LOG_NEVER, "STACK EMPTY" );
      }
}


/* 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)
      {
            debug(LOG_ERROR, "stackBinaryOp: not enough entries on stack");
            ASSERT( false, "stackBinaryOp: not enough entries on stack" );
            return false;
      }

      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;
      }

      if(opcode != OP_CONC)         //string - Don't check if OP_CONC, since types can be mixed here
      {
            if (!interpCheckEquiv(psV1->type, psV2->type))
            {
                  debug(LOG_ERROR, "stackBinaryOp: type mismatch");
                  ASSERT( false, "stackBinaryOp: type mismatch" );
                  return false;
            }

            /* 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 );
            }
      }

      // do the operation
      switch (opcode)
      {
      case OP_ADD:
            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;
            }
            break;
      case OP_SUB:
            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;
            }
            break;
      case OP_MUL:
            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;
            }
            break;
      case OP_DIV:
            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
                  {
                        debug(LOG_ERROR, "stackBinaryOp: division by zero (float)");
                        return false;
                  }
            }
            else
            {
                  if(psV2->v.ival != 0)
                  {
                        psV1->v.ival = psV1->v.ival / psV2->v.ival;
                  }
                  else
                  {
                        debug(LOG_ERROR, "stackBinaryOp: division by zero (integer)");
                        return false;
                  }
            }
            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:
            if(psV1->type == VAL_FLOAT && psV2->type == VAL_FLOAT){
                  psV1->v.bval = psV1->v.fval == psV2->v.fval;
            }else if(psV1->type == VAL_STRING && psV2->type == VAL_STRING){
                  psV1->v.bval = (strcasecmp(psV1->v.sval,psV2->v.sval) == 0);      /* case-insensitive */
            }else{
                  psV1->v.bval = psV1->v.ival == psV2->v.ival;
            }
            psV1->type = VAL_BOOL;
            break;
      case OP_NOTEQUAL:
            if(psV1->type == VAL_FLOAT && psV2->type == VAL_FLOAT){
                  psV1->v.bval = psV1->v.fval != psV2->v.fval;
            }else if(psV1->type == VAL_STRING && psV2->type == VAL_STRING){
                  psV1->v.bval = (strcasecmp(psV1->v.sval,psV2->v.sval) != 0);      /* case-insensitive */
            }else{
                  psV1->v.bval = psV1->v.ival != psV2->v.ival;
            }

            psV1->type = VAL_BOOL;
            break;
      case OP_GREATEREQUAL:
            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;
            }
            psV1->type = VAL_BOOL;
            break;
      case OP_LESSEQUAL:
            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;
            }
            psV1->type = VAL_BOOL;
            break;
      case OP_GREATER:
            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;
            }
            psV1->type = VAL_BOOL;
            break;
      case OP_LESS:
            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;
            }
            psV1->type = VAL_BOOL;
            break;
      case OP_CONC:     //String concatenation
            {
                  char tempstr1[MAXSTRLEN];
                  char tempstr2[MAXSTRLEN];

                  /* Check first value if it's compatible with Strings */
                  switch (psV1->type)
                  {
                  case VAL_INT:           //first value isn't string, but can be converted to string
                        snprintf(tempstr1, sizeof(tempstr1), "%d", psV1->v.ival);   //int->string
                        psV1->type = VAL_STRING;                  //Mark as string
                        psV1->v.sval =  (char*)malloc(MAXSTRLEN);                   //allocate space for the string, since the result (string) of concatenation will be saved here
                        break;

                  case VAL_BOOL:
                        snprintf(tempstr1, sizeof(tempstr1), "%d",psV1->v.bval);    //bool->string
                        psV1->type = VAL_STRING;                  //Mark as string
                        psV1->v.sval =  (char*)malloc(MAXSTRLEN);                   //allocate space for the string, since the result (string) of concatenation will be saved here
                        break;

                  case VAL_FLOAT:
                        snprintf(tempstr1, sizeof(tempstr1), "%f", psV1->v.fval);   //float->string
                        psV1->type = VAL_STRING;                  //Mark as string
                        psV1->v.sval =  (char*)malloc(MAXSTRLEN);                   //allocate space for the string, since the result (string) of concatenation will be saved here
                        break;

                  case VAL_STRING:
                        sstrcpy(tempstr1, psV1->v.sval);
                        break;

                  default:
                        debug(LOG_ERROR, "stackBinaryOp: OP_CONC: first parameter is not compatible with Strings");
                        return false;
                        break;
                  }

                  /* Check second value if it's compatible with Strings */
                  switch (psV2->type)
                  {
                  case VAL_INT:
                        snprintf(tempstr2, sizeof(tempstr2), "%d", psV2->v.ival);         //int->string
                        break;

                  case VAL_BOOL:
                        snprintf(tempstr2, sizeof(tempstr2), "%d", psV2->v.bval);         //bool->string
                        break;

                  case VAL_FLOAT:
                        snprintf(tempstr2, sizeof(tempstr2), "%f", psV2->v.fval);         //float->string
                        break;

                  case VAL_STRING:
                        sstrcpy(tempstr2, psV2->v.sval);
                        break;

                  default:
                        debug(LOG_ERROR, "stackBinaryOp: OP_CONC: first parameter is not compatible with Strings");
                        return false;
                        break;
                  }

                  sstrcat(tempstr1, tempstr2);

                  strcpy(psV1->v.sval,tempstr1);            //Assign
            }
            break;

      default:
            debug(LOG_ERROR, "stackBinaryOp: unknown opcode");
            ASSERT( false, "stackBinaryOp: unknown opcode" );
            return false;
            break;
      }

      return true;
}


/* 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)
      {
            ASSERT( false, "stackUnaryOp: not enough entries on stack" );
            return false;
      }

      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)
      {
      case OP_INC:
            switch (psVal->type)
            {
            case (VAL_REF | VAL_INT):

                  psVal = (INTERP_VAL*)psVal->v.oval;       //get variable

                  ASSERT(psVal->type == VAL_INT, "Invalid type for increment opcode: %d", psVal->type);

                  psVal->v.ival++;

                  /* Just get rid of the variable pointer, since already increased it */
                  if (!stackRemoveTop())
                  {
                        debug( LOG_ERROR, "stackUnaryOpcode: OP_INC: could not pop" );
                        return false;
                  }

                  break;
            default:
                  ASSERT( false, "stackUnaryOp: invalid type for OP_INC (type: %d)", psVal->type );
                  return false;
                  break;
            }
            break;
      case OP_DEC:
            switch (psVal->type)
            {
            case (VAL_REF | VAL_INT):

                  psVal = (INTERP_VAL*)psVal->v.oval;       //get variable

                  ASSERT(psVal->type == VAL_INT, "Invalid type for decrement opcode: %d", psVal->type);

                  psVal->v.ival--;

                  /* Just get rid of the variable pointer, since already decreased it */
                  if (!stackRemoveTop())
                  {
                        debug( LOG_ERROR, "stackUnaryOpcode: OP_DEC: could not pop" );
                        return false;
                  }
                  break;
            default:
                  ASSERT( false, "stackUnaryOp: invalid type for OP_DEC (type: %d)", psVal->type );
                  return false;
                  break;
            }
            break;

      case OP_NEG:
            switch (psVal->type)
            {
            case VAL_INT:
                  psVal->v.ival = - psVal->v.ival;
                  break;
            case VAL_FLOAT:
                  psVal->v.fval = - psVal->v.fval;
                  break;
            default:
                  ASSERT( false, "stackUnaryOp: invalid type for negation (type: %d)", psVal->type );
                  return false;
                  break;
            }
            break;
      case OP_NOT:
            switch (psVal->type)
            {
            case VAL_BOOL:
                  psVal->v.bval = !psVal->v.bval;
                  break;
            default:
                  ASSERT( false, "stackUnaryOp: invalid type for NOT (type: %d)", psVal->type );
                  return false;
                  break;
            }
            break;
      default:
            ASSERT( false, "stackUnaryOp: unknown opcode (opcode: %d)", opcode );
            return false;
            break;
      }

      return true;
}

BOOL stackCastTop(INTERP_TYPE neededType)
{
      INTERP_VAL *pTop;

      ASSERT(neededType == VAL_INT || neededType == VAL_FLOAT,
            "stackCast: can't cast to %d", neededType);

      if (!stackPeekTop(&pTop) || pTop==NULL)
      {
            ASSERT(false, "castTop: failed to peek stack");
            return false;
      }

      /* see if we can cast this data type */
      switch (pTop->type)
      {
            case VAL_BOOL:
                  switch (neededType)
                  {
                        case VAL_FLOAT:         /* casting from bool to float */

                              pTop->v.fval = (float)pTop->v.bval;
                              pTop->type = VAL_FLOAT;
                              return true;
                        default:
                              break;
                  }
                  break;
            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;
                  }
                  break;
            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;
                  }
                  break;
            default:
                  break;
      }

      debug(LOG_ERROR, "can't cast from %s to %s",
            scriptTypeToString(pTop->type), scriptTypeToString(neededType));

      return false;
}


/* Initialise the stack */
BOOL stackInitialise(void)
{
      psStackBase = calloc(1, sizeof(*psStackBase));
      if (psStackBase == NULL)
      {
            debug( LOG_ERROR, "Out of memory" );
            abort();
            return false;
      }
      psStackBase->aVals = calloc(INIT_SIZE, sizeof(*psStackBase->aVals));
      if (!psStackBase->aVals)
      {
            debug( LOG_ERROR, "Out of memory" );
            abort();
            return false;
      }

      psStackBase->size = INIT_SIZE;
      psStackBase->psPrev = NULL;
      psStackBase->psNext = NULL;
      psCurrChunk = psStackBase;

      //string support
      CURSTACKSTR = 0;        //initialize string 'stack'

      return true;
}



/* Shutdown the stack */
void stackShutDown(void)
{
      STACK_CHUNK       *psCurr, *psNext;
      UDWORD                        i;

      if ((psCurrChunk != psStackBase) && (currEntry != 0))
      {
            debug( LOG_NEVER, "stackShutDown: stack is not empty on shutdown" );
      }

      for(psCurr = psStackBase; psCurr != NULL; psCurr = psNext)
      {
            psNext = psCurr->psNext;

            /* 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");
                        }
                  }
            }

            free(psCurr->aVals);
            free(psCurr);
      }
}

/* Get rid of the top value without returning it */
static inline BOOL stackRemoveTop(void)
{
      if ((psCurrChunk->psPrev == NULL) && (currEntry == 0))
      {
            debug(LOG_ERROR, "stackRemoveTop: stack empty");
            ASSERT( false, "stackRemoveTop: stack empty" );
            return false;
      }

      /* 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--;
      }

      return true;
}


/* Reset the stack to an empty state */
void stackReset(void)
{
      ASSERT( ((psCurrChunk == psStackBase) && (currEntry == 0)),
            "stackReset: stack is not empty" );

      psCurrChunk = psStackBase;
      currEntry = 0;
}

Generated by  Doxygen 1.6.0   Back to index