G:/ScriptBasic/source/execute.c

Go to the documentation of this file.
00001 /* 
00002 FILE:   execute.c
00003 HEADER: execute.h
00004 
00005 --GNU LGPL
00006 This library is free software; you can redistribute it and/or
00007 modify it under the terms of the GNU Lesser General Public
00008 License as published by the Free Software Foundation; either
00009 version 2.1 of the License, or (at your option) any later version.
00010 
00011 This library is distributed in the hope that it will be useful,
00012 but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014 Lesser General Public License for more details.
00015 
00016 You should have received a copy of the GNU Lesser General Public
00017 License along with this library; if not, write to the Free Software
00018 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 TO_HEADER:
00021 
00022 #include "conftree.h"
00023 #include "hookers.h"
00024 #include "thread.h"
00025 
00026 // This structure stores the information on loaded modules and also on modules
00027 // that were only tried to be loaded, but were not loaded.
00028 typedef struct _Module {
00029   char *pszModuleName; // the name of the module as supplied by the caller, either
00030                        // simple name or full path to the dll
00031   void *ModulePointer; // pointer returned by the system function LoadModule/dlopen
00032   void *ModuleInternalParameters; // the modules own pointer initialized to zero, and the module
00033                       // alters it as it likes
00034   int ModuleIsActive; // true if the module is active. It can not be unloaded if it is active.
00035   int ModuleIsStatic; // true if the module is statically linked to the interpreter.
00036   struct _Module *next; // the next module in the list of loaded modules
00037   }Module, *pModule;
00038 
00039 // note that the ExecuteObject has a pointer to the SupportTable, but the SupportTable
00040 // also has a pointer back to the ExecuteObject
00041 typedef struct _SupportTable *pSupportTable;
00042 #define PSUPPORTTABLE 1
00043 
00044 typedef struct _ExecuteObject {
00045   void *(*memory_allocating_function)(size_t);
00046   void (*memory_releasing_function)(void *);
00047   void *pMemorySegment; //this pointer is passed to the memory allocating functions
00048 
00049   pReportFunction report;
00050   void *reportptr; // this pointer is passed to the report function. The caller should set it.
00051   unsigned long fErrorFlags;
00052 
00053   ptConfigTree pConfig; // configuration data
00054 
00055   char *StringTable; // all the string constants of the program zero terminated each
00056 
00057   unsigned long cbStringTable; // all the bytes of StringTable including the zeroes
00058 
00059   pcNODE CommandArray;
00060   unsigned long StartNode;
00061   unsigned long CommandArraySize;
00062 
00063   long cGlobalVariables;
00064   pFixSizeMemoryObject GlobalVariables;
00065 
00066   long cLocalVariables;
00067   pFixSizeMemoryObject LocalVariables; // this variable is always stored and restored when a locality is entered
00068 
00069   unsigned long ProgramCounter;
00070   unsigned long NextProgramCounter;
00071   int fNextPC; // command sets it to TRUE when the NextProgramCounter was set (like in a jump)
00072 
00073 #define fStopRETURN 1
00074 #define fStopSTOP   2
00075   int fStop;
00076 
00077   unsigned long lStepCounter;  // counts the program steps within the function
00078   unsigned long lGlobalStepCounter; // counts the program steps in the total program
00079   long lFunctionLevel;        // the level in function call deepness
00080 
00081   // maximal values or zero if no limit exists to help avoid infinite loop s
00082   long GlobalStepLimit;       // the max number of steps allowed for the programs
00083   long LocalStepLimit;        // the max number of steps inside a function
00084   long FunctionLevelLimit;    // the maximal function call deepness
00085 
00086   int fWeAreCallingFuction; // This is true, when we are calling a function
00087 
00088   unsigned long ErrorCode;
00089   int fErrorGoto; // what type of value is in the ErrorGoto variable
00090 #define ONERROR_NOTHING    0
00091 #define ONERROR_GOTO       1
00092 #define ONERROR_RESUME     2
00093 #define ONERROR_RESUMENEXT 3
00094 
00095   unsigned long ErrorGoto;   // where to go when an error occures
00096   unsigned long ErrorResume; // where did the error occures, where to resume
00097   unsigned long LastError;   // the code of the the last error that happened
00098 
00099   unsigned long OperatorNode;     // the node number of the current operator
00100   pFixSizeMemoryObject pOpResult; // result of the operator function
00101   pFixSizeMemoryObject pFunctionResult; // result of the current function
00102   pMortalList pGlobalMortalList;   // the actually used mortal list
00103   unsigned long FunctionArgumentsNode; // the node of the expression list forming the argument list
00104 
00105   pMemoryObject pMo;
00106 
00107   CommandFunctionType *pCommandFunction;
00108 
00109   void *CommandParameter[NUM_CMD]; // a NULL initialized pointer for each type of command or function
00110   void (*(Finaliser[NUM_CMD]))(struct _ExecuteObject*);
00111                                    // a NULL initialized pointer for each type of command or function
00112                                    // if a function pointer is put into this variable it is called upon
00113                                    // finishing the execution of the program
00114   void **InstructionParameter;     // a NULL initialized pointer for each cNODE
00115 
00116   VersionInfo Ver;
00117 
00118   void *fpStdinFunction;  // pointer to standard input function when embedded
00119   void *fpStdouFunction;  // pointer to standard output function when embedded
00120   void *fpEnvirFunction;  // pointer to the environment variable retrieval function
00121   char *CmdLineArgument;  // pointer to the command line argument
00122 
00123   SymbolTable OptionsTable; // the options that the program can set using the statement option
00124 
00125   void *pEmbedder; // this can be used by the embedding program
00126 
00127   pSupportTable pST; // support table for the external functions
00128   pSupportTable pSTI;// support table inherited from a process global program object needed
00129                      // only for multi-thread supporting modules
00130   MUTEX mxModules;   // to lock the modules list if this is a process SB object otherwise not used
00131   pModule modules;   // list of the the loaded modules
00132   struct _ExecuteObject *pEPo;//the process objects execute structure in case this interpreter runs in MT env
00133 
00134   char *pszModuleError; // the error message returned by a module call
00135 
00136   pHookFunctions pHookers; // structure containing the hooker function pointers
00137 
00138 //  char *Argv0; // the name of the script executed
00139   LexNASymbol *pCSYMBOLS; // to help locate the command code based on command id string
00140   int fThreadedCommandTable; // true if the command table points to a copy (owned by the thread)
00141   char **CSymbolList;
00142   //unsigned long maxderef;
00143   }ExecuteObject
00144 #ifndef PEXECUTEOBJECT
00145   , *pExecuteObject
00146 #endif
00147   ;
00148 #define GETDOUBLEVALUE(x) execute_GetDoubleValue(pEo,(x))
00149 #define GETLONGVALUE(x)   execute_GetLongValue(pEo,(x))
00150 */
00151 
00152 #include <stdlib.h>
00153 #include <stdio.h>
00154 #include <string.h>
00155 #include <ctype.h>
00156 #include <math.h>
00157 #include <limits.h>
00158 
00159 #include "basext.h"
00160 #include "sym.h"
00161 #include "errcodes.h"
00162 #include "report.h"
00163 #include "lexer.h"
00164 #include "expression.h"
00165 #include "builder.h"
00166 #include "memory.h"
00167 #include "syntax.h"
00168 #include "execute.h"
00169 #include "myalloc.h"
00170 #include "modumana.h"
00171 
00172 #define REPORT(x) if( pEo->report )pEo->report(pEo->reportptr,"",0,x,REPORT_ERROR,NULL,NULL,&(pEo->fErrorFlags))
00173 
00174 #if BCC32
00175 #define pow10 _mypow10
00176 #endif
00177 
00178 static double pow10(double a)
00179 {
00180    int j,i;
00181    double pro,k;
00182 
00183    for( (i= a<0.0) && (a = -a) , j=(int)a , pro=1.0 , k=10; j ;
00184        j%2 && (pro *=k) , j /= 2 , k *= k )
00185       continue;
00186    i && (pro=1.0/pro);
00187    return pro;
00188 }
00189 
00190 
00191 /*POD
00192 
00193 This module contain the functions that execute the code resuled by the builder.
00194 
00195 CUT*/
00196 
00197 /*POD
00198 =H execute_GetCommandByName()
00199 
00200 The op-code of a command can easily be identified, because T<syntax.h> contains
00201 symbolic constant for it. This function can be used by external modules to
00202 get this opcode based on the name of the function. The argument T<pszCommandName>
00203 should be the name of the command, for example T<"ONERRORRESUMENEXT">. The third 
00204 argument is the hint for the function to help to find the value. It should always
00205 be the opcode of the command. The return value is the actual opcode of the command.
00206 For example:
00207 
00208 =verbatim
00209 i = execute_GetCommandByName(pEo,"ONERRORRESUMENEXT",CMD_ONERRORRESUMENEXT);
00210 =noverbatim
00211 
00212 will return T<CMD_ONERRORRESUMENEXT>.
00213 
00214 I<Why is this function all about then?>
00215 
00216 The reason is that the external module may not be sure that the code
00217 T<CMD_ONERRORRESUMENEXT> is the same when the external module is compiled
00218 and when it is loaded. External modules negotiate the interface version
00219 information with the calling interpreter, but the opcodes may silently changed
00220 from interpreter version to the next interpreter version and still supporting
00221 the same extension interface version.
00222 
00223 When an external module needs to know the opcode of a command of the calling
00224 interpreter it first calls this function telling:
00225 
00226 I<I need the code of the command ONERRORRESUMENEXT. I think that the code is
00227 CMD_ONERRORRESUMENEXT, but is it the real code?>
00228 
00229 The argument T<lCodeHint> is required only, because it speeds up search.
00230 
00231 If there is no function found for the given name the returnvalue is zero.
00232 
00233 /*FUNCTION*/
00234 long execute_GetCommandByName(pExecuteObject pEo,
00235                               char *pszCommandName,
00236                               long lCodeHint
00237   ){
00238 /*noverbatim
00239 CUT*/
00240   long DownCounter,UpCounter;
00241 
00242   if( lCodeHint < START_CMD )lCodeHint = START_CMD;
00243   if( lCodeHint >= END_EXEC )lCodeHint = END_EXEC-1;
00244 
00245   DownCounter = UpCounter = lCodeHint;
00246   while( DownCounter || UpCounter ){
00247     if( DownCounter && !strcmp(pszCommandName,pEo->CSymbolList[DownCounter-START_CMD]) )return DownCounter;
00248     if( UpCounter && !strcmp(pszCommandName,pEo->CSymbolList[UpCounter-START_CMD]) )return UpCounter;
00249     UpCounter ++;
00250     if( UpCounter == END_EXEC )UpCounter = 0;
00251     DownCounter --;
00252     if( DownCounter < START_CMD )DownCounter = 0;
00253     }
00254   return 0; 
00255   }
00256 
00257 /*POD
00258 =H execute_CopyCommandTable()
00259 
00260 The command table is a huge table containing pointers to functions. For example
00261 the T<CMD_LET>-th element of the table points to the function T<COMMAND_LET>
00262 implementing the assignment command.
00263 
00264 This table is usually treated as constant and is not moduified during run time.
00265 In case a module wants to reimplement a command it should alter this table.
00266 However the table is shared all concurrently running interpreter threads in
00267 a multi-thread variation of ScriptBasic.
00268 
00269 To avoid altering the command table of an independent interpreter threadthe module
00270 wanting altering the command table should call this function. This function allocates
00271 memory for a new copy of the command table and copies the original constant
00272 value to this new place. After the copy is done the T<ExecuteObject> will point to
00273 the copied command table and the extension is free to alter the table.
00274 
00275 In case the function is called more than once for the same interpreter thread
00276 only the first time is effective. Later the function returns without creating superfluous
00277 copies of the command table.
00278 /*FUNCTION*/
00279 int execute_CopyCommandTable(pExecuteObject pEo
00280   ){
00281 /*noverbatim
00282 CUT*/
00283   CommandFunctionType *p;
00284 
00285   /* it is already copied to the thread local place */
00286   if( pEo->fThreadedCommandTable )return COMMAND_ERROR_SUCCESS;
00287 
00288   p = ALLOC( sizeof( CommandFunctionType ) * (END_EXEC-START_CMD) );
00289   if( p == NULL )return COMMAND_ERROR_MEMORY_LOW;
00290   memcpy(p, pEo->pCommandFunction, sizeof( CommandFunctionType ) * (END_EXEC-START_CMD) );
00291   pEo->pCommandFunction = p;
00292   pEo->fThreadedCommandTable = 1;
00293   return COMMAND_ERROR_SUCCESS;
00294   }
00295 
00296 /*POD
00297 =H execute_InitStructure()
00298 
00299 /*FUNCTION*/
00300 int execute_InitStructure(pExecuteObject pEo,
00301                           pBuildObject pBo
00302   ){
00303 /*noverbatim
00304 CUT*/
00305   long maxmem;
00306   int iError;
00307 
00308   build_MagicCode(&(pEo->Ver));
00309   pEo->fpStdinFunction = NULL;
00310   pEo->fpStdouFunction = NULL;
00311   pEo->fpEnvirFunction = NULL;
00312   pEo->CmdLineArgument = NULL;
00313 
00314   if( cft_GetEx(pEo->pConfig,"maxstep",NULL,NULL,
00315                 &(pEo->GlobalStepLimit),NULL,NULL) )
00316     pEo->GlobalStepLimit = 0;
00317 
00318   if( cft_GetEx(pEo->pConfig,"maxlocalstep",NULL,NULL,
00319                 &(pEo->LocalStepLimit),NULL,NULL) )
00320     pEo->LocalStepLimit = 0;
00321 
00322   if( cft_GetEx(pEo->pConfig,"maxlevel",NULL,NULL,
00323                 &(pEo->FunctionLevelLimit),NULL,NULL) )
00324     pEo->FunctionLevelLimit = 0;
00325 
00326   pEo->CommandArray = pBo->CommandArray ;
00327   pEo->StartNode = pBo->StartNode;
00328   pEo->CommandArraySize = pBo->NodeCounter;
00329   pEo->StringTable = pBo->StringTable;
00330   pEo->cbStringTable = pBo->cbStringTable;
00331   pEo->cGlobalVariables = pBo->cGlobalVariables;
00332   pEo->lGlobalStepCounter = 0L;
00333   pEo->LastError = 0; /* there was no run time error so 
00334                          far and this led formerly the 
00335                          BASIC 'ERROR()' function random
00336                          on the start */
00337 
00338   pEo->pCommandFunction = CommandFunction;
00339   pEo->CSymbolList = COMMANDSYMBOLS;
00340   pEo->fThreadedCommandTable = 0;
00341   pEo->pFunctionResult = NULL;
00342   pEo->pGlobalMortalList = NULL;
00343   pEo->pST = NULL;
00344   pEo->pSTI = NULL;
00345   pEo->pEPo = NULL;
00346   pEo->modules = NULL;
00347 
00348   pEo->pMemorySegment = alloc_InitSegment(pEo->memory_allocating_function,pEo->memory_releasing_function);
00349 
00350   if( pEo->pMemorySegment == NULL )return COMMAND_ERROR_MEMORY_LOW;
00351 
00352 
00353   pEo->pMo = alloc_Alloc(sizeof(MemoryObject),pEo->pMemorySegment);
00354   if( pEo->pMo == NULL )return EXE_ERROR_MEMORY_LOW;
00355 
00356   if( cft_GetEx(pEo->pConfig,"maxderef",NULL,NULL,
00357                 &(pEo->pMo->maxderef),NULL,NULL) )
00358     pEo->pMo->maxderef = 1000;
00359   pEo->pMo->memory_allocating_function = pEo->memory_allocating_function;
00360   pEo->pMo->memory_releasing_function = pEo->memory_releasing_function;
00361   pEo->cbStringTable = 0L;
00362   pEo->OptionsTable = NULL;
00363   if( iError = memory_InitStructure(pEo->pMo) )return iError;
00364 
00365   if( cft_GetEx(pEo->pConfig,"maxmem",NULL,NULL,
00366                 &maxmem,NULL,NULL) == CFT_ERROR_SUCCESS )
00367     alloc_SegmentLimit(pEo->pMo->pMemorySegment,maxmem);
00368 
00369 
00370   memory_RegisterTypes(pEo->pMo);
00371   if( hook_Init(pEo, &(pEo->pHookers)) )return 1;
00372   if( modu_Preload(pEo) )return 1;
00373 
00374   pEo->GlobalVariables = NULL;
00375   pEo->InstructionParameter = NULL;
00376 
00377   return EXE_ERROR_SUCCESS;
00378   }
00379 
00380 /*POD
00381 =H execute_ReInitStructure()
00382 
00383 This function should be used if a code is executed repeatedly. The first
00384 initialization call is R<execute_InitStructure()> and consecutive executions
00385 should call this function.
00386 
00387 /*FUNCTION*/
00388 int execute_ReInitStructure(pExecuteObject pEo,
00389                             pBuildObject pBo
00390   ){
00391 /*noverbatim
00392 CUT*/
00393 
00394   pEo->lGlobalStepCounter = 0L;
00395   pEo->pFunctionResult = NULL;
00396 
00397   pEo->pST  = NULL;
00398   pEo->pSTI = NULL;
00399   pEo->pEPo = NULL;
00400   pEo->modules = NULL;
00401   if( modu_Preload(pEo) )return 1;
00402 
00403   return 0;
00404   }
00405 
00406 /*POD
00407 =H execute_Execute_r()
00408 
00409 This function executes a program fragment. The execution starts from the class variable
00410 T<ProgramCounter>. This function is called from the R<execute_Execute()> function which is the
00411 main entry point to the basic main program. This function is also called recursively from
00412 the function R<execute_Evaluate()> when a user defined function is to be executed.
00413 
00414 /*FUNCTION*/
00415 void execute_Execute_r(pExecuteObject pEo,
00416                        int *piErrorCode
00417   ){
00418 /*noverbatim
00419 CUT*/
00420   CommandFunctionType ThisCommandFunction;
00421   unsigned long CommandOpCode;
00422   unsigned long pc,npc;
00423 
00424   pEo->fStop = 0;
00425   pEo->lStepCounter = 0L;
00426   pEo->fErrorGoto = ONERROR_NOTHING; /* this is the default behaviour */
00427   while( pEo->ProgramCounter ){
00428     pEo->fNextPC = 0;
00429     if( pEo->ProgramCounter > pEo->CommandArraySize ){
00430       *piErrorCode = EXE_ERROR_INVALID_PC;
00431       return;
00432       }
00433 
00434     if( pEo->CommandArray[pEo->ProgramCounter-1].OpCode != eNTYPE_LST ){
00435       *piErrorCode = EXE_ERROR_INVALID_NODE;
00436       return;
00437       }
00438 
00439     pc = pEo->CommandArray[pEo->ProgramCounter-1].Parameter.NodeList.actualm;
00440     if( pc > pEo->CommandArraySize ){
00441       *piErrorCode = EXE_ERROR_INVALID_PC1;
00442       return;
00443       }
00444     if( pc ){
00445       CommandOpCode = pEo->CommandArray[pc-1].OpCode;
00446       if( CommandOpCode < START_CMD || CommandOpCode > END_CMD ){
00447         *piErrorCode = EXE_ERROR_INVALID_OPCODE;
00448         return;
00449         }
00450       }
00451     npc = pEo->CommandArray[pEo->ProgramCounter-1].Parameter.NodeList.rest ;
00452 
00453     if( pc ){
00454       ThisCommandFunction = pEo->pCommandFunction[CommandOpCode - START_CMD];
00455       if( ThisCommandFunction == NULL ){
00456         *piErrorCode = EXE_ERROR_NOT_IMPLEMENTED;
00457         return;
00458         }
00459       }
00460     pEo->ErrorCode = EXE_ERROR_SUCCESS;
00461     if( pEo->pHookers->HOOK_ExecBefore && (*piErrorCode = pEo->pHookers->HOOK_ExecBefore(pEo)) )return;
00462     if( pc )
00463       ThisCommandFunction(pEo);
00464     if( pEo->pHookers->HOOK_ExecAfter && (*piErrorCode = pEo->pHookers->HOOK_ExecAfter(pEo)) )return;
00465     pEo->fWeAreCallingFuction = 0;
00466     pEo->lStepCounter++;
00467     if( pEo->LocalStepLimit && pEo->lStepCounter > (unsigned)pEo->LocalStepLimit ){
00468       *piErrorCode = EXE_ERROR_TOO_LONG_RUN;
00469       return;
00470       }
00471     pEo->lGlobalStepCounter++;
00472     if( pEo->GlobalStepLimit && pEo->lGlobalStepCounter > (unsigned)pEo->GlobalStepLimit ){
00473       *piErrorCode = EXE_ERROR_TOO_LONG_RUN;
00474       return;
00475       }
00476 
00477     /* If the error code was set by the instruction then handle the on error goto statements */
00478     if( pEo->ErrorCode ){
00479       pEo->LastError = pEo->ErrorCode;
00480       if( pEo->fErrorGoto == ONERROR_RESUMENEXT ){
00481         pEo->fErrorGoto = ONERROR_NOTHING;
00482         pEo->ErrorResume = 0; /* we resume the exection, we won't go back to anywhere */
00483         pEo->LastError = 0;   /* it is resumed, there is no error                     */
00484         pEo->ErrorGoto = 0;   /* no error goto node */
00485         }else
00486       if( pEo->fErrorGoto == ONERROR_RESUME ){
00487         if( ! pEo->ErrorGoto ){
00488           *piErrorCode = pEo->ErrorCode;
00489           return;
00490           }
00491         pEo->ErrorResume = 0; /* we resume the exection, we won't go back to anywhere */
00492         pEo->LastError = 0;   /* it is resumed, there is no error                     */
00493         pEo->fErrorGoto = ONERROR_NOTHING; /* on error is switched off by default     */
00494         pEo->ErrorGoto = 0;   /* no error goto node */
00495         pEo->fNextPC = 1;
00496         pEo->NextProgramCounter = pEo->ErrorGoto;
00497         }else
00498       if( pEo->fErrorGoto == ONERROR_GOTO ){
00499         if( ! pEo->ErrorGoto ){
00500           *piErrorCode = pEo->ErrorCode;
00501           return;
00502           }
00503         pEo->ErrorResume = pEo->ProgramCounter;
00504         pEo->fNextPC = 1;
00505         pEo->NextProgramCounter = pEo->ErrorGoto;
00506         pEo->ErrorGoto = 0;
00507         }else{
00508         /* fErrorGoto should be ONERROR_NOTHING if we got here */
00509         *piErrorCode = pEo->ErrorCode;
00510         return;
00511         }
00512       }
00513 
00514     if( pEo->fStop ){
00515       if( pEo->fStop == fStopRETURN )pEo->fStop = 0;
00516       *piErrorCode = EXE_ERROR_SUCCESS;
00517       return;
00518       }
00519     if( pEo->fNextPC )
00520       pEo->ProgramCounter = pEo->NextProgramCounter;
00521     else
00522       pEo->ProgramCounter = npc;
00523     }
00524   *piErrorCode = EXE_ERROR_SUCCESS;
00525   return;
00526   }
00527 
00528 /*POD
00529 =H execute_InitExecute()
00530 
00531 /*FUNCTION*/
00532 void execute_InitExecute(pExecuteObject pEo,
00533                         int *piErrorCode
00534   ){
00535 /*noverbatim
00536 CUT*/
00537 #ifdef NULL_IS_NOT_ZERO
00538   unsigned long i;
00539 #endif
00540   *piErrorCode = 0;
00541   pEo->ProgramCounter = pEo->StartNode;
00542 
00543   pEo->LocalVariables = NULL;
00544   pEo->pszModuleError = NULL;
00545   pEo->cLocalVariables = 0;
00546   pEo->ErrorGoto = 0;
00547 
00548   if( pEo->GlobalVariables == NULL ){
00549     pEo->GlobalVariables = memory_NewArray(pEo->pMo,1,pEo->cGlobalVariables);
00550     if( pEo->GlobalVariables == NULL ){
00551       *piErrorCode = EXE_ERROR_MEMORY_LOW;
00552       return;
00553       }
00554     }
00555   pEo->fWeAreCallingFuction = 0;
00556   pEo->lFunctionLevel = 0;
00557   if( pEo->InstructionParameter == NULL ){
00558     pEo->InstructionParameter = alloc_Alloc(pEo->CommandArraySize*sizeof(void *),pEo->pMemorySegment);
00559     if( pEo->InstructionParameter == NULL ){
00560       *piErrorCode = EXE_ERROR_MEMORY_LOW;
00561       return;
00562       }
00563     memset(pEo->InstructionParameter,0,pEo->CommandArraySize*sizeof(void *));
00564     }
00565   memset(pEo->CommandParameter,0,NUM_CMD*sizeof(void *));
00566   memset(pEo->Finaliser,0,NUM_CMD*sizeof(void *));
00567   }
00568 
00569 /*POD
00570 =H execute_FinishExecute()
00571 
00572 /*FUNCTION*/
00573 void execute_FinishExecute(pExecuteObject pEo,
00574                            int *piErrorCode
00575   ){
00576 /*noverbatim
00577 CUT*/
00578   unsigned long i;
00579 
00580   for( i=0 ; i < NUM_CMD ; i++ ){
00581     if( pEo->Finaliser[i] )pEo->Finaliser[i](pEo);
00582     }
00583   modu_UnloadAllModules(pEo);
00584   }
00585 
00586 /*POD
00587 =H execute_Execute()
00588 
00589 This function was called from the basic T<main> function. This function performs inititalization
00590 that is needed before each execution of the code and calls R<execute_Execute_r()> to perform the execution.
00591 
00592 Note that R<execute_Execute_r()> is recursively calls itself.
00593 
00594 This function is obsolete and is not used anymore. This is kept in the source
00595 for the shake of old third party variations that may depend on this function.
00596 
00597 Use of this function in new applications is discouraged.
00598 
00599 /*FUNCTION*/
00600 void execute_Execute(pExecuteObject pEo,
00601                      int *piErrorCode
00602   ){
00603 /*noverbatim
00604 CUT*/
00605   execute_InitExecute(pEo,piErrorCode);
00606   if( *piErrorCode )return;
00607 
00608   execute_Execute_r(pEo,piErrorCode);
00609 
00610   if( *piErrorCode ){
00611     REPORT(*piErrorCode);
00612     }
00613 
00614   execute_FinishExecute(pEo,piErrorCode);
00615   }
00616 
00617 /*POD
00618 =H execute_ExecuteFunction()
00619 
00620 This function is used by the embedding layer (aka T<scriba_> functions) to execute a function.
00621 This function is not directly called by the execution of a ScriptBasic program. It may be
00622 used after the execution of the program by a special embeddign application that keeps the
00623 code and the global variables in memory and calls functions of the program.
00624 
00625 The function takes T<pEo> as the execution environment. T<StartNode> should be the node where the
00626 sub or function is defined. T<cArgs> should give the number of arguments. T<pArgs> should point
00627 to the argument array. T<pResult> will point to the result. If T<pResult> is T<NULL> the result is
00628 dropped. Otherwise the result is a mortal variable.
00629 
00630 Note that this code does not check the number of arguments you provide. There can be more arguments
00631 passed to the SUB than it has declared, therefore you can initialize the local variables of the sub.
00632 (You should know that arguments are local variables in ScriptBasic just as any other non-argument local
00633 variable.)
00634 
00635 The arguments should be normal immortal variables. They are passed to the SUB by reference and in case
00636 they are modified the old variable is going to be released.
00637 
00638 T<piErrorCode> returns the error code of the execution which is zero in case of no error.
00639 /*FUNCTION*/
00640 void execute_ExecuteFunction(pExecuteObject pEo,
00641                              unsigned long StartNode,
00642                              long cArgs,
00643                              pFixSizeMemoryObject *pArgs,
00644                              pFixSizeMemoryObject *pResult,
00645                              int *piErrorCode
00646   ){
00647 /*noverbatim
00648 CUT*/
00649   unsigned long nItem,pc;
00650   long i;
00651   long CommandOpCode;
00652 
00653   pEo->ProgramCounter = StartNode;
00654   pEo->pFunctionResult = NULL;
00655   pEo->lStepCounter = 0;
00656   pEo->fWeAreCallingFuction = 1;
00657   pEo->ErrorGoto = 0;
00658   pEo->ErrorResume = 0;
00659   pEo->fErrorGoto = ONERROR_NOTHING;
00660   pEo->LocalVariables = NULL;
00661   if( pResult )*pResult = NULL;
00662 
00663   if( pEo->CommandArray[pEo->ProgramCounter-1].OpCode != eNTYPE_LST ){
00664       *piErrorCode = EXE_ERROR_INVALID_NODE;
00665       return;
00666       }
00667 
00668   pc = pEo->CommandArray[pEo->ProgramCounter-1].Parameter.NodeList.actualm;
00669   if( pc > pEo->CommandArraySize ){
00670     *piErrorCode = EXE_ERROR_INVALID_PC1;
00671     return;
00672     }
00673   if( pc ){
00674     CommandOpCode = pEo->CommandArray[pc-1].OpCode;
00675     if( CommandOpCode < START_CMD || CommandOpCode > END_CMD ){
00676       *piErrorCode = EXE_ERROR_INVALID_OPCODE;
00677       return;
00678       }
00679     }
00680   if( CommandOpCode != CMD_FUNCTION &&
00681       CommandOpCode != CMD_FUNCTIONARG &&
00682       CommandOpCode != CMD_SUB &&
00683       CommandOpCode != CMD_SUBARG
00684     ){
00685     *piErrorCode = COMMAND_ERROR_INVALID_CODE;
00686     return;
00687     }
00688   nItem = pEo->CommandArray[pEo->ProgramCounter-1].Parameter.NodeList.actualm ;
00689   pEo->cLocalVariables = pEo->CommandArray[nItem-1].Parameter.CommandArgument.Argument.lLongValue;
00690 
00691   if( pEo->cLocalVariables ){
00692     pEo->LocalVariables = memory_NewArray(pEo->pMo,1,pEo->cLocalVariables);
00693     if( pEo->LocalVariables == NULL ){
00694       pEo->fStop = fStopSTOP;
00695       return;
00696       }
00697     }
00698 
00699   /* there can not be more arguments than local variables. The rest is ignored. */
00700   if( cArgs > pEo->cLocalVariables )cArgs = pEo->cLocalVariables;
00701   for( i=0 ; i < cArgs ; i++ ){
00702     pEo->LocalVariables->Value.aValue[i] = memory_NewRef(pEo->pMo);
00703     memory_SetRef(pEo->pMo,pEo->LocalVariables->Value.aValue+i,pArgs+i);
00704     }
00705 
00706   /* step over the function head */
00707   pEo->ProgramCounter = pEo->CommandArray[pEo->ProgramCounter-1].Parameter.NodeList.rest ;
00708 
00709   execute_Execute_r(pEo,piErrorCode);
00710 
00711 
00712   if( pEo->LocalVariables )/* this is null if the function did not have arguments and no local variables */
00713     memory_ReleaseVariable(pEo->pMo,pEo->LocalVariables);
00714 
00715   /* if the result is not needed by the caller we drop it. Otherwise it is the caller's responsibility to drop it. */
00716   if( pResult )
00717     *pResult = pEo->pFunctionResult;
00718   else 
00719     memory_ReleaseVariable(pEo->pMo,pEo->pFunctionResult);
00720   return;
00721   }
00722 
00723 /*POD
00724 =H execute_Evaluate()
00725 
00726 This function evaluates an expression. You should not get confused! This is not syntax analysis, caring
00727 operator precedences and grouping by nested parentheses. That has already been done during syntax analysis.
00728 This code performs the code that was generated from an expression.
00729 
00730 The result is usually a mortal memory value which is the final result of the expression. However this piece of
00731 code assumes that the caller is careful enough to handle the result as read only, and sometimes the return
00732 value is not mortal. In this case the return value is a memory object that a variable points to. Whenever the
00733 caller needs this value to perform an operation that does not alter the value it is OK. Duplicating the structure
00734 to create a mortal would be waste of time and memory. On the other hand sometimes operations modify their operands
00735 assuming that they are mortal values. They should be careful.
00736 
00737 Operators are actually created in the directory T<commands> and they use the macros defined in T<command.h> (created
00738 by T<headerer.pl> from T<command.c>). They help to avoid pitfalls.
00739 
00740 The argument T<iArrayAccepted> tells the function whether an array as a result is accepted or not. If a whole
00741 array is accepted as a result of the expression evaluation the array is returned. If the array is not an
00742 acceptable result, then the first element of the array is retuned in case the result is an array. If the result
00743 is NOT an array this parameter has no effect.
00744 
00745 /*FUNCTION*/
00746 pFixSizeMemoryObject execute_Evaluate(pExecuteObject pEo,
00747                                       unsigned long lExpressionRootNode,
00748                                       pMortalList pMyMortal,
00749                                       int *piErrorCode,
00750                                       int iArrayAccepted
00751   ){
00752 /*noverbatim
00753 CUT*/
00754   pFixSizeMemoryObject pVar;
00755   char *s;
00756   unsigned long slen,refcount;
00757   unsigned long SaveProgramCounter,SaveStepCounter;
00758   unsigned long SavefErrorGoto,SaveErrorGoto,SaveErrorResume;
00759   pFixSizeMemoryObject SaveLocalVariablesPointer;
00760   pFixSizeMemoryObject SaveFunctionResultPointer;
00761   pFixSizeMemoryObject ThisFunctionResultPointer;
00762   long OpCode;
00763   CommandFunctionType ThisCommandFunction;
00764   pMortalList pSaveMortalList;
00765 
00766   if( ! lExpressionRootNode ){
00767     *piErrorCode = EXE_ERROR_INTERNAL;
00768     return NULL;
00769     }
00770   if( pMyMortal == NULL ){
00771     *piErrorCode = EXE_ERROR_INTERNAL;
00772     return NULL;
00773     }
00774   *piErrorCode = EXE_ERROR_SUCCESS;
00775 #define ASSERT_NON_NULL(x) if( (x) == NULL ){ *piErrorCode = EXE_ERROR_MEMORY_LOW; return NULL; }
00776   switch( OpCode = pEo->CommandArray[lExpressionRootNode-1].OpCode ){
00777     case eNTYPE_ARR: /* array access                */
00778       pVar = execute_EvaluateArray(pEo,lExpressionRootNode,pMyMortal,piErrorCode);
00779       while( pVar && ( ((!iArrayAccepted) && pVar->vType == VTYPE_ARRAY) || pVar->vType == VTYPE_REF) ){
00780         /* when an array is referenced as scalar the first element is returned */
00781         while( pVar && (!iArrayAccepted) && pVar->vType == VTYPE_ARRAY )
00782           pVar = pVar->Value.aValue[0];
00783         while( pVar && pVar->vType == VTYPE_REF )
00784           pVar = *(pVar->Value.aValue);
00785         }
00786       return memory_SelfOrRealUndef(pVar);
00787     case eNTYPE_SAR: /* associative array access */
00788       pVar = execute_EvaluateSarray(pEo,lExpressionRootNode,pMyMortal,piErrorCode);
00789       while( pVar && ( ((!iArrayAccepted) && pVar->vType == VTYPE_ARRAY) || pVar->vType == VTYPE_REF) ){
00790         /* when an array is referenced as scalar the first element is returned */
00791         while( pVar && (!iArrayAccepted) && pVar->vType == VTYPE_ARRAY )
00792           pVar = pVar->Value.aValue[0];
00793         while( pVar && pVar->vType == VTYPE_REF )
00794           pVar = *(pVar->Value.aValue);
00795         }
00796       return memory_SelfOrRealUndef(pVar);
00797     case eNTYPE_FUN: /* function                    */
00798       if( pEo->FunctionLevelLimit && pEo->lFunctionLevel > pEo->FunctionLevelLimit ){
00799         *piErrorCode = EXE_ERROR_TOO_DEEP_CALL;
00800         return NULL;
00801         }
00802       SaveLocalVariablesPointer = pEo->LocalVariables;
00803       SaveProgramCounter = pEo->ProgramCounter;
00804       pEo->ProgramCounter = pEo->CommandArray[lExpressionRootNode-1].Parameter.UserFunction.NodeId;
00805       if( pEo->ProgramCounter == 0 ){
00806         *piErrorCode = EXE_ERROR_USERFUN_UNDEFINED;
00807         return NULL;
00808         }
00809       pEo->FunctionArgumentsNode = pEo->CommandArray[lExpressionRootNode-1].Parameter.UserFunction.Argument;
00810       SaveFunctionResultPointer = pEo->pFunctionResult;
00811       pEo->pFunctionResult = NULL;
00812       SaveStepCounter = pEo->lStepCounter;
00813       pEo->lStepCounter = 0;
00814       pEo->fWeAreCallingFuction = 1;
00815       SaveErrorGoto = pEo->ErrorGoto;
00816       pEo->ErrorGoto = 0;
00817       SaveErrorResume = pEo->ErrorResume;
00818       pEo->ErrorResume = 0;
00819       SavefErrorGoto = pEo->fErrorGoto;
00820       pEo->fErrorGoto = ONERROR_NOTHING;
00821       if( pEo->pHookers->HOOK_ExecCall && (*piErrorCode = pEo->pHookers->HOOK_ExecCall(pEo)) )return NULL;
00822       /* function entering code needs access to the caller local variables, therefore
00823          WE SHOULD NOT NULL pEo->LocalVariables */
00824       execute_Execute_r(pEo,piErrorCode);
00825       if( pEo->pHookers->HOOK_ExecReturn ){
00826         /* if there was already an error then there is no way to handle two different errors
00827            one coming from the execution system and one from the hook function. This way the
00828            hook function generated error (if any) is ignored. */
00829         if( *piErrorCode )
00830           pEo->pHookers->HOOK_ExecReturn(pEo);
00831          else
00832           *piErrorCode = pEo->pHookers->HOOK_ExecReturn(pEo);
00833         }
00834       pEo->lStepCounter = SaveStepCounter;
00835       if( pEo->LocalVariables )/* this is null if the function did not have arguments and no local variables */
00836         memory_ReleaseVariable(pEo->pMo,pEo->LocalVariables);
00837       pEo->ProgramCounter = SaveProgramCounter;
00838       pEo->LocalVariables = SaveLocalVariablesPointer;
00839       ThisFunctionResultPointer = pEo->pFunctionResult;
00840       pEo->pFunctionResult = SaveFunctionResultPointer;
00841       while( ThisFunctionResultPointer &&
00842              (!iArrayAccepted) && 
00843              ThisFunctionResultPointer->vType == VTYPE_ARRAY ){
00844         ThisFunctionResultPointer = ThisFunctionResultPointer->Value.aValue[0];
00845         }
00846       /* Functions return their value as immortal values assigned to the very global
00847          variable pEo->pFunctionResult. Here this variable is restored to point to the
00848          saved value and the value returned should be mortalized.                   */
00849       if( ThisFunctionResultPointer && 
00850           ThisFunctionResultPointer->vType != VTYPE_ARRAY &&
00851           ! IsMortal(ThisFunctionResultPointer) )
00852         memory_Mortalize(ThisFunctionResultPointer,pMyMortal);
00853 
00854       pEo->ErrorGoto = SaveErrorGoto;
00855       pEo->fErrorGoto = SavefErrorGoto;
00856       pEo->ErrorResume = SaveErrorResume;
00857       if( *piErrorCode )return NULL;
00858       return memory_SelfOrRealUndef(ThisFunctionResultPointer);
00859     case eNTYPE_LVR: /* local variable              */
00860       if( pEo->LocalVariables == NULL ){
00861         *piErrorCode = EXE_ERROR_NO_LOCAL;
00862         return NULL;
00863         }
00864       pVar = pEo->LocalVariables->Value.aValue[pEo->CommandArray[lExpressionRootNode-1].Parameter.Variable.Serial-1];
00865       while( pVar && ( ((!iArrayAccepted) && pVar->vType == VTYPE_ARRAY) || pVar->vType == VTYPE_REF) ){
00866         /* when an array is referenced as scalar the first element is returned */
00867         while( pVar && (!iArrayAccepted) && pVar->vType == VTYPE_ARRAY )
00868           pVar = pVar->Value.aValue[0];
00869         refcount = 0;
00870         while( pVar && pVar->vType == VTYPE_REF ){
00871           pVar = *(pVar->Value.aValue);
00872           if( refcount++ > pEo->pMo->maxderef ){
00873             *piErrorCode = COMMAND_ERROR_CIRCULAR;
00874             return NULL;
00875             }
00876           }
00877         }
00878       return memory_SelfOrRealUndef(pVar);
00879     case eNTYPE_GVR: /* global variable             */
00880       pVar = pEo->GlobalVariables->Value.aValue[pEo->CommandArray[lExpressionRootNode-1].Parameter.Variable.Serial-1];
00881       while( pVar && ( ((!iArrayAccepted) && pVar->vType == VTYPE_ARRAY) || pVar->vType == VTYPE_REF) ){
00882         /* when an array is referenced as scalar the first element is returned */
00883         while( pVar && (!iArrayAccepted) && pVar->vType == VTYPE_ARRAY )
00884           pVar = pVar->Value.aValue[0];
00885         refcount = 0;
00886         while( pVar && pVar->vType == VTYPE_REF ){
00887           pVar = *(pVar->Value.aValue);
00888           if( refcount++ > pEo->pMo->maxderef ){
00889             *piErrorCode = COMMAND_ERROR_CIRCULAR;
00890             return NULL;
00891             }
00892           }
00893         }
00894       return memory_SelfOrRealUndef(pVar);
00895     case eNTYPE_DBL: /* constant double             */
00896       if( pEo->InstructionParameter[lExpressionRootNode-1] == NULL ){
00897         pVar = pEo->InstructionParameter[lExpressionRootNode-1] = memory_NewDouble(pEo->pMo);
00898         ASSERT_NON_NULL(pVar);
00899         pVar->Value.dValue = pEo->CommandArray[lExpressionRootNode-1].Parameter.Constant.dValue;
00900         }else
00901         pVar = pEo->InstructionParameter[lExpressionRootNode-1];
00902       return memory_SelfOrRealUndef(pVar);
00903 
00904     case eNTYPE_LNG: /* constant long               */
00905       if( pEo->InstructionParameter[lExpressionRootNode-1] == NULL ){
00906         pVar = pEo->InstructionParameter[lExpressionRootNode-1] = memory_NewLong(pEo->pMo);
00907         ASSERT_NON_NULL(pVar);
00908         pVar->Value.lValue = pEo->CommandArray[lExpressionRootNode-1].Parameter.Constant.lValue;
00909         }else
00910         pVar = pEo->InstructionParameter[lExpressionRootNode-1];
00911       return memory_SelfOrRealUndef(pVar);
00912 
00913     case eNTYPE_STR: /* constant string             */
00914       s = pEo->StringTable+pEo->CommandArray[lExpressionRootNode-1].Parameter.Constant.sValue;
00915       memcpy(&slen, s-sizeof(long), sizeof(long));
00916       pVar = memory_NewMortalCString(pEo->pMo,slen,pMyMortal);
00917       ASSERT_NON_NULL(pVar);
00918       pVar->Value.pValue = s;
00919       return memory_SelfOrRealUndef(pVar);
00920 
00921     case eNTYPE_LST: /* list member (invalid)       */
00922       *piErrorCode = EXE_ERROR_INVALID_EXPRESSION_NODE;
00923       return NULL;
00924     case eNTYPE_CRG: /* command arguments (invalid) */
00925       *piErrorCode = EXE_ERROR_INVALID_EXPRESSION_NODE1;
00926       return NULL;
00927     default: /* operators and built in functions    */
00928       ThisCommandFunction = pEo->pCommandFunction[OpCode - START_CMD];
00929       if( ThisCommandFunction == NULL ){
00930         *piErrorCode = EXE_ERROR_NOT_IMPLEMENTED;
00931         return NULL;
00932         }
00933       pEo->OperatorNode = lExpressionRootNode;
00934       pEo->pOpResult = NULL;
00935       pSaveMortalList = pEo->pGlobalMortalList;
00936       pEo->pGlobalMortalList = pMyMortal;
00937       ThisCommandFunction(pEo);
00938       pEo->pGlobalMortalList = pSaveMortalList;
00939       *piErrorCode = pEo->ErrorCode;
00940       return memory_SelfOrRealUndef(pEo->pOpResult);
00941     }
00942   *piErrorCode = EXE_ERROR_INTERNAL;
00943   return NULL;
00944   }
00945 
00946 /*POD
00947 =H execute_LeftValue()
00948 
00949 This function evaluate a left value. A left value is a special expression that value can be assigned, and therefore
00950 they usually stand on the left side of the assignment operator. That is the reason for the name.
00951 
00952 When an expression is evaluates a pointer to a memory object is returned. Whenever a left value is evaluated a pointer
00953 to the variable is returned. If any code assignes value to the variable pointed by the return value of this function
00954 it should release the memory object that the left value points currently.
00955 /*FUNCTION*/
00956 pFixSizeMemoryObject *execute_LeftValue(pExecuteObject pEo,
00957                                         unsigned long lExpressionRootNode,
00958                                         pMortalList pMyMortal,
00959                                         int *piErrorCode,
00960                                         int iArrayAccepted
00961   ){
00962 /*noverbatim
00963 CUT*/
00964   pFixSizeMemoryObject *ppVar;
00965   long OpCode;
00966 
00967   *piErrorCode = EXE_ERROR_SUCCESS;
00968 
00969   switch( OpCode = pEo->CommandArray[lExpressionRootNode-1].OpCode ){
00970 
00971     case eNTYPE_ARR: /* array access                */
00972       return execute_LeftValueArray(pEo,lExpressionRootNode,pMyMortal,piErrorCode);
00973 
00974     case eNTYPE_SAR: /* associative array access */
00975       return execute_LeftValueSarray(pEo,lExpressionRootNode,pMyMortal,piErrorCode);
00976 
00977     case eNTYPE_LVR: /* local variable              */
00978       if( pEo->LocalVariables == NULL ){
00979         *piErrorCode = EXE_ERROR_NO_LOCAL;
00980         return NULL;
00981         }
00982       ppVar = &(pEo->LocalVariables->Value.aValue[pEo->CommandArray[lExpressionRootNode-1].Parameter.Variable.Serial-1]);
00983       /* when an array is referenced as scalar the first element is returned */
00984       while( (!iArrayAccepted) && *ppVar && (*ppVar)->vType == VTYPE_ARRAY )
00985         ppVar = &((*ppVar)->Value.aValue[0]);
00986       return ppVar;
00987 
00988     case eNTYPE_GVR: /* global variable             */
00989       ppVar = &(pEo->GlobalVariables->Value.aValue[pEo->CommandArray[lExpressionRootNode-1].Parameter.Variable.Serial-1]);
00990       /* when an array is referenced as scalar the first element is returned */
00991       while( (!iArrayAccepted) && *ppVar && (*ppVar)->vType == VTYPE_ARRAY )
00992         ppVar = &((*ppVar)->Value.aValue[0]);
00993       return ppVar;
00994 
00995     case eNTYPE_FUN: /* function                    */
00996       *piErrorCode = EXE_ERROR_INVALID_LVALNODE0;
00997       return NULL;
00998 
00999     case eNTYPE_DBL: /* constant double             */
01000       *piErrorCode = EXE_ERROR_INVALID_LVALNODE1;
01001       return NULL;
01002 
01003     case eNTYPE_LNG: /* constant long               */
01004       *piErrorCode = EXE_ERROR_INVALID_LVALNODE2;
01005       return NULL;
01006 
01007     case eNTYPE_STR: /* constant string             */
01008       *piErrorCode = EXE_ERROR_INVALID_LVALNODE3;
01009       return NULL;
01010 
01011     case eNTYPE_LST: /* list member (invalid)       */
01012       *piErrorCode = EXE_ERROR_INVALID_LVALNODE4;
01013       return NULL;
01014 
01015     case eNTYPE_CRG: /* command arguments (invalid) */
01016       *piErrorCode = EXE_ERROR_INVALID_LVALNODE5;
01017       return NULL;
01018     default: /* operators and built in functions    */
01019       *piErrorCode = EXE_ERROR_INVALID_LVALNODE6;
01020       return NULL;
01021     }
01022   *piErrorCode = EXE_ERROR_INTERNAL;
01023   return NULL;
01024   }
01025 
01026 /*POD
01027 =H execute_EvaluateArray()
01028 
01029 This function should be used to evaluate an array access to get the actual
01030 value. This is called by R<execute_Evaluate()>.
01031 
01032 An array is stored in the expression as an operator with many operands. The first
01033 operand is a local or global variable, the rest of the operators are the indices.
01034 
01035 Accessing a variable holding scalar value with array indices automatically converts
01036 the variable to array. Accessing an array variable without indices gets the "first"
01037 element of the array.
01038 /*FUNCTION*/
01039 pFixSizeMemoryObject execute_EvaluateArray(pExecuteObject pEo,
01040                                       unsigned long lExpressionRootNode,
01041                                       pMortalList pMyMortal,
01042                                       int *piErrorCode
01043   ){
01044 /*noverbatim
01045 CUT*/
01046 
01047   return *execute_LeftValueArray(pEo,lExpressionRootNode,pMyMortal,piErrorCode);
01048   }
01049 
01050 
01051 /*POD
01052 =H execute_EvaluateSarray()
01053 
01054 This function should be used to evaluate an array access to get the actual
01055 value. This is called by R<execute_Evaluate()>.
01056 
01057 An array is stored in the expression as an operator with many operands. The first
01058 operand is a local or global variable, the rest of the operators are the indices.
01059 
01060 Associative arrays are normal arrays, only the access mode is different. When accessing
01061 an array using the fom T<a{key}> then the access searches for the value T<key> in the 
01062 evenly indexed elements of the array and gives the next index element of the array. This
01063 if
01064 
01065 =verbatim
01066 a[0] = "kakukk"
01067 a[1] = "birka"
01068 a[2] = "kurta"
01069 a[3] = "mamus"
01070 =noverbatim
01071 
01072 then T<a{"kakukk"}> is "birka". T<a{"birka"}> is T<undef>. T<a{"kurta"}> is "mamus".
01073 
01074 /*FUNCTION*/
01075 pFixSizeMemoryObject execute_EvaluateSarray(pExecuteObject pEo,
01076                                       unsigned long lExpressionRootNode,
01077                                       pMortalList pMyMortal,
01078                                       int *piErrorCode
01079   ){
01080 /*noverbatim
01081 CUT*/
01082 
01083   return *execute_LeftValueSarray(pEo,lExpressionRootNode,pMyMortal,piErrorCode);
01084   }
01085 
01086 /*POD
01087 =H execute_LeftValueArray()
01088 
01089 This function evaluates an array access left value. This function is also called by R<execute_EvaluateArray()>
01090 and the result pointer is dereferenced.
01091 
01092 /*FUNCTION*/
01093 pFixSizeMemoryObject *execute_LeftValueArray(pExecuteObject pEo,
01094                                              unsigned long lExpressionRootNode,
01095                                              pMortalList pMyMortal,
01096                                              int *piErrorCode
01097   ){
01098 /*noverbatim
01099 CUT*/
01100 
01101   long OpCode;
01102   long lIndex,lMinIndex,lMaxIndex;
01103   unsigned long nVariable, nIndex;
01104   unsigned long nHead;
01105   pFixSizeMemoryObject *ppVar,pVar;
01106   unsigned long __refcount_;
01107 
01108   nHead = pEo->CommandArray[lExpressionRootNode-1].Parameter.Arguments.Argument;
01109   nVariable = pEo->CommandArray[nHead-1].Parameter.NodeList.actualm;
01110   OpCode = pEo->CommandArray[nVariable-1].OpCode;
01111 
01112   nHead = pEo->CommandArray[nHead-1].Parameter.NodeList.rest;
01113 
01114   switch( OpCode ){
01115     case eNTYPE_LVR: /* local variable              */
01116       if( pEo->LocalVariables == NULL ){
01117         *piErrorCode = EXE_ERROR_NO_LOCAL;
01118         return NULL;
01119         }
01120       ppVar = &(pEo->LocalVariables->Value.aValue[pEo->CommandArray[nVariable-1].Parameter.Variable.Serial-1]);
01121       break;
01122     case eNTYPE_GVR: /* global variable             */
01123       ppVar = &(pEo->GlobalVariables->Value.aValue[pEo->CommandArray[nVariable-1].Parameter.Variable.Serial-1]);
01124       break;
01125     case eNTYPE_ARR:
01126       ppVar = execute_LeftValueArray(pEo,nVariable,pMyMortal,piErrorCode);
01127       break;
01128     case eNTYPE_SAR:
01129       ppVar = execute_LeftValueSarray(pEo,nVariable,pMyMortal,piErrorCode);
01130       break;
01131     default:
01132       /* the syntax analyzer should not generate anything else than this */
01133       *piErrorCode =EXE_ERROR_INTERNAL;
01134       return NULL;
01135     }
01136 
01137   /* if this value is a reference value then */
01138   __refcount_ = pEo->pMo->maxderef;
01139   while( *ppVar && (*ppVar)->vType == VTYPE_REF && __refcount_-- )
01140     ppVar = (*ppVar)->Value.aValue;
01141   if( *ppVar && (*ppVar)->vType == VTYPE_REF ){
01142     *piErrorCode = COMMAND_ERROR_CIRCULAR;
01143     return NULL;
01144     }
01145   while( nHead ){
01146     nIndex = pEo->CommandArray[nHead-1].Parameter.NodeList.actualm;
01147     nHead  = pEo->CommandArray[nHead-1].Parameter.NodeList.rest;
01148     pVar = execute_Evaluate(pEo,
01149                             nIndex,
01150                             pMyMortal,
01151                             piErrorCode,0);
01152     if( *piErrorCode )return NULL;
01153 
01154     if( pVar )
01155       lIndex = GETLONGVALUE(pVar);
01156     else
01157       lIndex = 0;
01158 
01159     /* If the variable is NOT an array the covert it to array on the fly.
01160        The referenced index is the only one and gets the scalar value. */
01161     if( !*ppVar || (*ppVar)->vType != VTYPE_ARRAY ){
01162       if( *ppVar ){
01163         if( 0 < lIndex )lMinIndex = 0; else lMinIndex = lIndex;
01164         if( 0 > lIndex )lMaxIndex = 0; else lMaxIndex = lIndex;
01165         }else lMinIndex = lMaxIndex = lIndex;
01166       pVar = memory_NewArray(pEo->pMo, /* memory class */
01167                              lMinIndex,   /* min index */
01168                              lMaxIndex);  /* max index */
01169       if( pVar == NULL ){
01170         *piErrorCode = EXE_ERROR_MEMORY_LOW;
01171         return NULL;
01172         }
01173       if( *ppVar ){/* if the variable was defined */
01174         pVar->Value.aValue[-lMinIndex] = *ppVar;
01175         }
01176       *ppVar = pVar;
01177       }
01178     memory_CheckArrayIndex(pEo->pMo,(*ppVar),lIndex);
01179     ppVar = (*ppVar)->Value.aValue+lIndex-(*ppVar)->ArrayLowLimit;
01180 
01181     }
01182   return ppVar;
01183   }
01184 
01185 /* stringcompare two string values. The values SHOULD be string.
01186 */
01187 static int STRCMP(VARIABLE Op1, VARIABLE Op2, int iCase){
01188   unsigned long n;
01189   char *a,*b;
01190   char ca,cb;
01191 
01192   if( memory_IsUndef(Op1) && memory_IsUndef(Op2) )return 0;
01193   if( memory_IsUndef(Op1) )return 1;
01194   if( memory_IsUndef(Op2) )return -1;
01195   iCase &= 1;/* only the lowest bit is about case sensitivity */
01196   n = STRLEN(Op1);
01197   if( n > STRLEN(Op2) ) n= STRLEN(Op2);
01198   a = STRINGVALUE(Op1);
01199   b = STRINGVALUE(Op2);
01200   while( n-- ){
01201     ca = *a;
01202     cb = *b;
01203     if( iCase ){
01204       if( isupper(ca) )ca = tolower(ca);
01205       if( isupper(cb) )cb = tolower(cb);
01206       }
01207     if( ca != cb )return ( (ca)-(cb) );
01208     a++;
01209     b++;
01210     }
01211   if( STRLEN(Op1) == STRLEN(Op2) )return 0;
01212   if( STRLEN(Op1) > STRLEN(Op2) )return 1;
01213   return -1;
01214   }
01215 
01216 
01217 /*POD
01218 =H execute_LeftValueSarray()
01219 
01220 This function evaluates an associative array access left value.
01221 This function is also called by R<execute_EvaluateSarray()> and the result
01222 pointer is dereferenced.
01223 
01224 /*FUNCTION*/
01225 pFixSizeMemoryObject *execute_LeftValueSarray(pExecuteObject pEo,
01226                                               unsigned long lExpressionRootNode,
01227                                               pMortalList pMyMortal,
01228                                               int *piErrorCode
01229   ){
01230 /*noverbatim
01231 CUT*/
01232 
01233   long OpCode;
01234   long lIndex;
01235   int KeyIsFound;
01236   unsigned long nVariable, nIndex;
01237   unsigned long nHead;
01238   pFixSizeMemoryObject *ppVar,pVar,vIndex,vCurrentKey;
01239   unsigned long __refcount_;
01240   int iCase = options_Get(pEo,"compare")&1;
01241 
01242   nHead = pEo->CommandArray[lExpressionRootNode-1].Parameter.Arguments.Argument;
01243   nVariable = pEo->CommandArray[nHead-1].Parameter.NodeList.actualm;
01244   OpCode = pEo->CommandArray[nVariable-1].OpCode;
01245 
01246   nHead = pEo->CommandArray[nHead-1].Parameter.NodeList.rest;
01247 
01248   switch( OpCode ){
01249     case eNTYPE_LVR: /* local variable              */
01250       if( pEo->LocalVariables == NULL ){
01251         *piErrorCode = EXE_ERROR_NO_LOCAL;
01252         return NULL;
01253         }
01254       ppVar = &(pEo->LocalVariables->Value.aValue[pEo->CommandArray[nVariable-1].Parameter.Variable.Serial-1]);
01255       break;
01256     case eNTYPE_GVR: /* global variable             */
01257       ppVar = &(pEo->GlobalVariables->Value.aValue[pEo->CommandArray[nVariable-1].Parameter.Variable.Serial-1]);
01258       break;
01259     case eNTYPE_ARR:
01260       ppVar = execute_LeftValueArray(pEo,nVariable,pMyMortal,piErrorCode);
01261       break;
01262     case eNTYPE_SAR:
01263       ppVar = execute_LeftValueSarray(pEo,nVariable,pMyMortal,piErrorCode);
01264       break;
01265     default:
01266       /* the syntax analyzer should not generate anything else than this */
01267       *piErrorCode =EXE_ERROR_INTERNAL;
01268       return NULL;
01269     }
01270 
01271   /* if this value is a reference value then */
01272   __refcount_ = pEo->pMo->maxderef;
01273   while( *ppVar && (*ppVar)->vType == VTYPE_REF &&__refcount_-- )
01274     ppVar = (*ppVar)->Value.aValue;
01275 
01276   if( *ppVar && (*ppVar)->vType == VTYPE_REF ){
01277     *piErrorCode = COMMAND_ERROR_CIRCULAR;
01278     return NULL;
01279     }
01280 
01281   while( nHead ){
01282     nIndex = pEo->CommandArray[nHead-1].Parameter.NodeList.actualm;
01283     nHead  = pEo->CommandArray[nHead-1].Parameter.NodeList.rest;
01284     vIndex = execute_Evaluate(pEo,
01285                             nIndex,
01286                             pMyMortal,
01287                             piErrorCode,0);
01288     if( *piErrorCode )return NULL;
01289 
01290     /* If the variable is NOT an array the covert it to array on the fly. */
01291     if( !*ppVar || (*ppVar)->vType != VTYPE_ARRAY ){
01292       if( *ppVar ){/* if the variable has some value */
01293         pVar = memory_NewArray(pEo->pMo, /* memory class */
01294                                0,        /* min index */
01295                                3);       /* max index */
01296         if( pVar == NULL ){
01297           *piErrorCode = EXE_ERROR_MEMORY_LOW;
01298           return NULL;
01299           }
01300         /* the value is stored on the index 0 associated with the value undef */
01301         pVar->Value.aValue[0] = *ppVar;
01302         pVar->Value.aValue[1] = NULL;
01303         pVar->Value.aValue[2] = memory_DupVar(pEo->pMo,vIndex,pMyMortal,piErrorCode);
01304         memory_Immortalize(pVar->Value.aValue[2],pMyMortal);
01305         if( *piErrorCode )return NULL;
01306         pVar->Value.aValue[3] = NULL;
01307         lIndex = 3;
01308         }else{/* if the variable does not have any value */
01309         pVar = memory_NewArray(pEo->pMo, /* memory class */
01310                                0,        /* min index */
01311                                1);       /* max index */
01312         if( pVar == NULL ){
01313           *piErrorCode = EXE_ERROR_MEMORY_LOW;
01314           return NULL;
01315           }
01316         pVar->Value.aValue[0] = memory_DupVar(pEo->pMo,vIndex,pMyMortal,piErrorCode);
01317         memory_Immortalize(pVar->Value.aValue[0],pMyMortal);
01318         if( *piErrorCode )return NULL;
01319         pVar->Value.aValue[1] = NULL;
01320         lIndex = 1;
01321         }
01322       *ppVar = pVar;
01323       }else{/* the variable is already an array */
01324       KeyIsFound = 0;
01325       for( lIndex = (*ppVar)->ArrayLowLimit ; lIndex < (*ppVar)->ArrayHighLimit ; lIndex += 2 ){
01326          vCurrentKey = (*ppVar)->Value.aValue[lIndex-(*ppVar)->ArrayLowLimit];
01327          /* if this value is a reference value then *//*TODO: limit dereferencing */
01328          while( vCurrentKey && vCurrentKey->vType == VTYPE_REF )vCurrentKey = *vCurrentKey->Value.aValue;
01329          if( memory_IsUndef(vCurrentKey) && memory_IsUndef(vIndex) ){
01330            lIndex++;
01331            KeyIsFound = 1;
01332            goto KEY_IS_FOUND;
01333            }
01334          if( memory_IsUndef(vCurrentKey) )continue;
01335          if( memory_IsUndef(vIndex) )continue;
01336          if( vCurrentKey->vType != vIndex->vType )continue;
01337          switch( vIndex->vType ){
01338            case VTYPE_LONG:
01339              if( vIndex->Value.lValue == vCurrentKey->Value.lValue ){
01340                lIndex++;
01341                KeyIsFound = 1;
01342                goto KEY_IS_FOUND;
01343                }
01344                break;
01345            case VTYPE_DOUBLE:
01346              if( vIndex->Value.dValue == vCurrentKey->Value.dValue ){
01347                lIndex++;
01348                KeyIsFound = 1;
01349                goto KEY_IS_FOUND;
01350                }
01351                break;
01352            case VTYPE_STRING:
01353              if( !STRCMP(vIndex,vCurrentKey,iCase) ){
01354                lIndex++;
01355                KeyIsFound = 1;
01356                goto KEY_IS_FOUND;
01357                }
01358                break;
01359            case VTYPE_ARRAY:
01360                /* if this is an array it will not match anything */
01361                break;
01362            default:
01363              *piErrorCode = EXE_ERROR_INTERNAL;
01364              return NULL;
01365            }
01366          }
01367 KEY_IS_FOUND:
01368       if( ! KeyIsFound ){
01369         /* The key was not found in the array. Append it to the array with undef value. */
01370         memory_CheckArrayIndex(pEo->pMo,*ppVar,(*ppVar)->ArrayHighLimit+2);
01371         /* note that CheckArrayIndex also modifies the ArrayXxxLimit values */
01372         (*ppVar)->Value.aValue[(*ppVar)->ArrayHighLimit-(*ppVar)->ArrayLowLimit] = NULL;
01373         (*ppVar)->Value.aValue[(*ppVar)->ArrayHighLimit-(*ppVar)->ArrayLowLimit-1] = memory_DupVar(pEo->pMo,vIndex,pMyMortal,piErrorCode);
01374         memory_Immortalize((*ppVar)->Value.aValue[(*ppVar)->ArrayHighLimit-(*ppVar)->ArrayLowLimit-1],pMyMortal);
01375         lIndex = (*ppVar)->ArrayHighLimit;
01376         }
01377       }
01378     memory_CheckArrayIndex(pEo->pMo,(*ppVar),lIndex);
01379     ppVar = (*ppVar)->Value.aValue+lIndex-(*ppVar)->ArrayLowLimit;
01380     }
01381   return ppVar;
01382   }
01383 
01384 
01385 /*POD
01386 =H execute_Convert2String()
01387 
01388 This functionconverts a variable to string. When the variable is already a string then it returns the pointer to the
01389 variable. When the variable is long or double T<sprintf> is used to convert the number to string.
01390 
01391 When the conversion from number to string is done the result is always a newly allocated mortal. In other words
01392 this conversion routine is safe, not modifying the argument memory object.
01393 /*FUNCTION*/
01394 pFixSizeMemoryObject execute_Convert2String(pExecuteObject pEo,
01395                                           pFixSizeMemoryObject pVar,
01396                                           pMortalList pMyMortal
01397   ){
01398 /*noverbatim
01399 CUT*/
01400   char buffer[256]; /* this size should be enough to represent a number in string format */
01401 
01402   while( pVar && pVar->vType == VTYPE_ARRAY )
01403     pVar = pVar->Value.aValue[pVar->ArrayLowLimit];
01404 
01405   /* undef is converted to a zero length string */  
01406   if( memory_IsUndef(pVar) ){
01407     pVar = memory_NewMortalString(pEo->pMo,0,pMyMortal);
01408     if( pVar == NULL )return NULL;
01409     return pVar;
01410     }
01411 
01412   execute_DereferenceS(pEo->pMo->maxderef,&pVar);
01413 
01414   switch( pVar->vType ){
01415     default: return NULL;
01416     case VTYPE_LONG: 
01417       sprintf(buffer,"%ld",pVar->Value.lValue);
01418       break;
01419     case VTYPE_STRING:
01420       return pVar;
01421     case VTYPE_DOUBLE:
01422       sprintf(buffer,"%lf",pVar->Value.dValue);
01423       break;
01424     }
01425   pVar = memory_NewMortalString(pEo->pMo,strlen(buffer),pMyMortal);
01426   if( pVar == NULL )return NULL;
01427   memcpy(pVar->Value.pValue,buffer,strlen(buffer));
01428   return pVar;
01429   }
01430 
01431 
01432 /*POD
01433 =H execute_Convert2Long()
01434 
01435 This function should be used to convert a variable to long. The conversion is
01436 usually done in place. However strings can not be converted into long in place, because
01437 they have different size. In such a case a new variable is created. If the mortal list T<pMyMortal>
01438 is T<NULL> then the new variable in not mortal. In such a case care should be taken
01439 to release the original variable.
01440 
01441 Usually there is a mortal list and a new mortal variable is generated. In such a case
01442 the original value is also a mortal and is automatically released after the command
01443 executing the conversion is finished.
01444 
01445 Note that strings are converted to long in two steps. The first step converts the string to
01446 T<double> and then this value is converted to long in-place.
01447 
01448 
01449 /*FUNCTION*/
01450 pFixSizeMemoryObject execute_Convert2Long(pExecuteObject pEo,
01451                                           pFixSizeMemoryObject pVar,
01452                                           pMortalList pMyMortal
01453   ){
01454 /*noverbatim
01455 CUT*/
01456   char *s;
01457   int lintpart;
01458   double intpart,fracpart,exppart,man;
01459   int i,esig,isig;
01460   unsigned long sLen;
01461 
01462   while( pVar && pVar->vType == VTYPE_ARRAY )
01463     pVar = pVar->Value.aValue[pVar->ArrayLowLimit];
01464 
01465   if( memory_IsUndef(pVar) ){
01466     pVar = memory_NewMortalLong(pEo->pMo,pMyMortal);
01467     if( pVar == NULL )return NULL;
01468     pVar->Value.lValue = 0;
01469     return pVar;
01470     }
01471 
01472   execute_DereferenceS(pEo->pMo->maxderef,&pVar);
01473 
01474   switch( pVar->vType ){
01475     default: return NULL;
01476     case VTYPE_LONG: return pVar; /* it is already long */
01477     case VTYPE_STRING:
01478       s = (char *)pVar->Value.pValue;
01479       sLen = pVar->Size;
01480       while( isspace( *s ) && sLen ){
01481         s++; /*leading spaces don't matter*/
01482         sLen--;
01483         }
01484       isig = 1; esig =1;
01485       if( *s == '-' )isig = -1;
01486       if( sLen )
01487         if( *s == '-' || *s == '+' ){ s++; sLen--; }
01488       for( lintpart = 0 ; sLen && isdigit(*s) ; s++,sLen-- ){
01489         lintpart *= 10;
01490         lintpart += *s -'0';
01491         }
01492       if( (!sLen) || (*s != '.' && *s != 'e' && *s != 'E') ){
01493         pVar = memory_NewMortalLong(pEo->pMo,pMyMortal);
01494         if( pVar == NULL )return NULL;
01495         pVar->Value.lValue = isig*lintpart;
01496         return pVar;
01497         }
01498       intpart = lintpart;
01499       fracpart = 0.0;
01500       if( sLen && *s == '.' ){
01501         s++;     /* step over the decimal dot */
01502         sLen --;
01503         i = 0; /* this is not an integer anymore */
01504         fracpart = 0.0; /* fractional part */
01505         man = 1.0;      /* actual mantissa */
01506         for(  ; isdigit(*s) && sLen ; s++, sLen-- )
01507           fracpart += (man *= 0.1) * (*s-'0');
01508         }
01509       if( sLen && (*s == 'E' || *s == 'e') ){
01510         i = 0; /* this is not an integer anymore if it has exponential part */
01511         s++; /* step over the character E */
01512         sLen --;
01513         if( *s == '-' )esig=-1; else esig = 1;
01514         if( sLen )
01515           if( *s == '+' || *s == '-'){ s++; sLen--; } /* step over the exponential sign */
01516         for( exppart=0.0 , i = 0 ; sLen && isdigit(*s) ; s++, sLen-- )
01517           exppart = 10*exppart + *s-'0';
01518         }else exppart = 0.0;
01519       pVar = memory_NewMortalLong(pEo->pMo,pMyMortal);
01520       if( pVar == NULL )return NULL;
01521       pVar->Value.lValue = (long)(isig*(intpart + fracpart)*pow10(esig*exppart));
01522       return pVar;
01523     case VTYPE_DOUBLE:
01524       pVar->vType = VTYPE_LONG;
01525       pVar->Value.lValue = (long)pVar->Value.dValue;
01526       return pVar;
01527     }
01528   }
01529 
01530 /*POD
01531 =H execute_Convert2LongS()
01532 
01533 This is the safe version of the conversion function R<execute_Convert2Long()>.
01534 
01535 This function ALWAYS create a new variable and does NOT convert a
01536 double to long in place. This function is called by the extensions,
01537 because extensions tend to be more laisy regarding conversion and
01538 many converts arguments in place and thus introduce side effect.
01539 
01540 To solve this problem we have introduced this function and have
01541 set the support table to point to this function.
01542 /*FUNCTION*/
01543 pFixSizeMemoryObject execute_Convert2LongS(pExecuteObject pEo,
01544                                            pFixSizeMemoryObject pVar,
01545                                            pMortalList pMyMortal
01546   ){
01547 /*noverbatim
01548 CUT*/
01549   pFixSizeMemoryObject pVarr;
01550 
01551   while( pVar && pVar->vType == VTYPE_ARRAY )
01552     pVar = pVar->Value.aValue[pVar->ArrayLowLimit];
01553 
01554   if( memory_IsUndef(pVar) ){
01555     pVar = memory_NewMortalLong(pEo->pMo,pMyMortal);
01556     if( pVar == NULL )return NULL;
01557     pVar->Value.lValue = 0;
01558     return pVar;
01559     }
01560 
01561   execute_DereferenceS(pEo->pMo->maxderef,&pVar);
01562 
01563   switch( pVar->vType ){
01564     default: return NULL;
01565     case VTYPE_LONG: return pVar; /* it is already long */
01566     case VTYPE_STRING:
01567       /* strings are NOT converted to long in place by
01568          the original functions as well, so we can use it */
01569       return execute_Convert2Long(pEo,pVar,pMyMortal);
01570 
01571     case VTYPE_DOUBLE:
01572       pVarr = memory_NewMortalLong(pEo->pMo,pMyMortal);
01573       if( pVarr == NULL )return NULL;
01574       pVarr->vType = VTYPE_LONG;
01575       pVarr->Value.lValue = (long)pVar->Value.dValue;
01576       return pVarr;
01577     }
01578   }
01579 
01580 
01581 /*POD
01582 =H execute_Convert2Double()
01583 
01584 This function should be used to convert a variable to double. The conversion is
01585 usually done in place. However strings can not be converted into double in place, because
01586 they have different size. In such a case a new variable is created. If the mortal list
01587 is T<NULL> then the new variable in not mortal. In such a case care should be taken
01588 to release the original variable.
01589 
01590 Usually there is a mortal list and a new mortal variable is generated. In such a case
01591 the original value is also a mortal and is automatically released after the command
01592 executing the conversion is finished.
01593 
01594 /*FUNCTION*/
01595 pFixSizeMemoryObject execute_Convert2Double(pExecuteObject pEo,
01596                                             pFixSizeMemoryObject pVar,
01597                                             pMortalList pMyMortal
01598   ){
01599 /*noverbatim
01600 CUT*/
01601   char *s;
01602   double intpart,fracpart,exppart,man;
01603   int i,esig,isig;
01604   unsigned long sLen;
01605 
01606   while( pVar && pVar->vType == VTYPE_ARRAY )
01607     pVar = pVar->Value.aValue[pVar->ArrayLowLimit];
01608 
01609   if( memory_IsUndef(pVar) ){
01610     pVar = memory_NewMortalDouble(pEo->pMo,pMyMortal);
01611     if( pVar == NULL )return NULL;
01612     pVar->Value.dValue = 0.0;
01613     return pVar;
01614     }
01615 
01616   execute_DereferenceS(pEo->pMo->maxderef,&pVar);
01617 
01618   switch( pVar->vType ){
01619     default: return NULL;
01620     case VTYPE_LONG:
01621       pVar->vType = VTYPE_DOUBLE;
01622       pVar->Value.dValue = (double)pVar->Value.lValue;
01623       return pVar;
01624     case VTYPE_DOUBLE:
01625       return pVar;
01626     case VTYPE_STRING:
01627       s = (char *)pVar->Value.pValue;
01628       sLen = pVar->Size;
01629       while( isspace( *s ) && sLen ){
01630         s++; /*leading spaces don't matter*/
01631         sLen--;
01632         }
01633       isig = 1; esig =1;
01634       if( *s == '-' )isig = -1;
01635       if( sLen )
01636         if( *s == '-' || *s == '+' ){ s++; sLen--; }
01637       for( intpart = 0 ; sLen && isdigit(*s) ; s++,sLen-- ){
01638         intpart *= 10;
01639         intpart += *s -'0';
01640         }
01641       fracpart = 0.0;
01642       if( sLen && *s == '.' ){
01643         s++;     /* step over the decimal dot */
01644         sLen --;
01645         i = 0; /* this is not an integer anymore */
01646         fracpart = 0.0; /* fractional part */
01647         man = 1.0;      /* actual mantissa */
01648         for(  ; isdigit(*s) && sLen ; s++, sLen-- )
01649           fracpart += (man *= 0.1) * (*s-'0');
01650         }
01651       if( sLen && (*s == 'E' || *s == 'e') ){
01652         i = 0; /* this is not an integer anymore if it has exponential part */
01653         s++; /* step over the character E */
01654         sLen --;
01655         if( *s == '-' )esig=-1; else esig = 1;
01656         if( sLen )
01657           if( *s == '+' || *s == '-'){ s++; sLen--; } /* step over the exponential sign */
01658         for( exppart=0.0 , i = 0 ; sLen && isdigit(*s) ; s++, sLen-- )
01659           exppart = 10*exppart + *s-'0';
01660         }else exppart = 0.0;
01661     pVar = memory_NewMortalDouble(pEo->pMo,pMyMortal);
01662     if( pVar == NULL )return NULL;
01663     pVar->Value.dValue = isig*(intpart + fracpart)*pow10(esig*exppart);
01664     return pVar;
01665     }
01666   }
01667 
01668 /*POD
01669 =H execute_Convert2DoubleS()
01670 
01671 This is the safe version of the conversion function R<execute_Convert2Double()>.
01672 
01673 This function ALWAYS create a new variable and does NOT convert a
01674 long to double in place. This function is called by the extensions,
01675 because extensions tend to be more laisy regarding conversion and
01676 many converts arguments in place and thus introduce side effect.
01677 
01678 To solve this problem we have introduced this function and have
01679 set the support table to point to this function.
01680 
01681 /*FUNCTION*/
01682 pFixSizeMemoryObject execute_Convert2DoubleS(pExecuteObject pEo,
01683                                              pFixSizeMemoryObject pVar,
01684                                              pMortalList pMyMortal
01685   ){
01686 /*noverbatim
01687 CUT*/
01688   pFixSizeMemoryObject pVarr;
01689 
01690   while( pVar && pVar->vType == VTYPE_ARRAY )
01691     pVar = pVar->Value.aValue[pVar->ArrayLowLimit];
01692 
01693   if( memory_IsUndef(pVar) ){
01694     pVar = memory_NewMortalDouble(pEo->pMo,pMyMortal);
01695     if( pVar == NULL )return NULL;
01696     pVar->Value.dValue = 0.0;
01697     return pVar;
01698     }
01699 
01700   execute_DereferenceS(pEo->pMo->maxderef,&pVar);
01701 
01702   switch( pVar->vType ){
01703     default: return NULL;
01704     case VTYPE_LONG:
01705       pVarr = memory_NewMortalDouble(pEo->pMo,pMyMortal);
01706       if( pVarr == NULL )return NULL;
01707       pVarr->vType = VTYPE_DOUBLE;
01708       pVarr->Value.dValue = (double)pVar->Value.lValue;
01709       return pVarr;
01710     case VTYPE_DOUBLE:
01711       return pVar;
01712     case VTYPE_STRING:
01713       /* strings are not converted in place by default */
01714       return execute_Convert2Double(pEo,pVar,pMyMortal);
01715     }
01716   }
01717 
01718 
01719 /*POD
01720 =H execute_Convert2Numeric()
01721 
01722 
01723 This function should be used to convert a variable to numeric type.
01724 
01725 The conversion results a double or long variable. If the source variable
01726 was already a long or double the function does nothing but results the
01727 source variable.
01728 
01729 T<undef> is converted to long zero.
01730 
01731 The function calls R<execute_Convert2Long> and R<execute_Convert2Double> thus
01732 all other parameters are treated according to that.
01733 
01734 /*FUNCTION*/
01735 pFixSizeMemoryObject execute_Convert2Numeric(pExecuteObject pEo,
01736                                              pFixSizeMemoryObject pVar,
01737                                              pMortalList pMyMortal
01738   ){
01739 /*noverbatim
01740 CUT*/
01741 
01742   while( pVar && pVar->vType == VTYPE_ARRAY )
01743     pVar = pVar->Value.aValue[pVar->ArrayLowLimit];
01744 
01745   if( memory_IsUndef(pVar) ){
01746     pVar = memory_NewMortalLong(pEo->pMo,pMyMortal);
01747     if( pVar == NULL )return NULL;
01748     pVar->Value.lValue = 0;
01749     return pVar;
01750     }
01751 
01752   execute_DereferenceS(pEo->pMo->maxderef,&pVar);
01753 
01754   switch( pVar->vType ){
01755     default: return NULL;
01756     case VTYPE_LONG:
01757       return pVar;
01758     case VTYPE_DOUBLE:
01759       return pVar;
01760     case VTYPE_STRING:
01761       if( ISSTRINGINTEGER(pVar) )
01762         return execute_Convert2Long(pEo,pVar,pMyMortal);
01763       else
01764         return execute_Convert2Double(pEo,pVar,pMyMortal);
01765     }
01766   }
01767 
01768 /*POD
01769 =H execute_Dereference()
01770 
01771 This function recursively follows variable references and returns
01772 the original variable that was referenced by the original variable.
01773 
01774 A reference variable is a special variable that does not hold value
01775 itself but rather a pointer to another variable. Such reference variables
01776 are used when arguments are passed by reference to BASIC subroutines.
01777 
01778 Calling this function the caller can get the original variable and the
01779 valéue of the original variable rather than a reference.
01780 /*FUNCTION*/
01781 pFixSizeMemoryObject execute_Dereference(pExecuteObject pEo,
01782                                          pFixSizeMemoryObject p,
01783                                          int *piErrorCode
01784   ){
01785 /*noverbatim
01786 See also R<execute_DereferenceS()>.
01787 CUT*/
01788   unsigned long refcount;
01789 
01790   if( *piErrorCode )return p;
01791   refcount = pEo->pMo->maxderef;
01792   while( p && TYPE(p) == VTYPE_REF ){
01793     p = *(p->Value.aValue);
01794     if( ! refcount-- ){
01795       *piErrorCode = COMMAND_ERROR_CIRCULAR;
01796       return NULL;
01797       }
01798     }
01799   return p;  
01800   }
01801 
01802 /*POD
01803 =H execute_DereferenceS()
01804 
01805 This function does the same as R<execute_Dereference()> except that it has
01806 different arguments fitted to support external modules and T<besXXX>
01807 macros.
01808 
01809 /*FUNCTION*/
01810 int execute_DereferenceS(unsigned long refcount,
01811                          pFixSizeMemoryObject *p
01812   ){
01813 /*noverbatim
01814 See also R<execute_Dereference()>.
01815 
01816 If the argument is referencing an T<undef> value then this function
01817 converts the argument to be a real T<NULL> to allow external modules
01818 to compare T<besDEREFERENCE>d variables against T<NULL>.
01819 
01820 The subroutine is also error prone handling T<NULL> pointer as argument,
01821 though it should never be happen if the external module programmer
01822 uses the macro T<besDEREFERENCE>.
01823 CUT*/
01824   while( p && *p && TYPE(*p) == VTYPE_REF ){
01825     *p = *((*p)->Value.aValue);
01826     if( ! refcount-- )return COMMAND_ERROR_CIRCULAR;
01827     }
01828   /* extension modules like to chack undef against NULL pointer, let them do */
01829   if( p && *p && TYPE(*p) == VTYPE_UNDEF )*p = NULL;
01830   return EXE_ERROR_SUCCESS;  
01831   }
01832 
01833 /*POD
01834 =H execute_GetDoubleValue()
01835 
01836 Use this function whenever you want to access the B<value> of a variable as a T<double>.
01837 Formerly ScriptBasic in such situation converted the variable to double calling
01838 R<execute_Convert2Double()> and then used the macro T<DOUBLEVALUE>. This method is faster
01839 because this does not create a new mortal variable but returns directly the
01840 double value.
01841 
01842 The macro T<GETDOUBLEVALUE> can be used to call this function with the default
01843 execution environment variable T<pEo>
01844 
01845 Note however that the macro T<GETDOUBLEVALUE> and T<DOUBLEVALUE> are not 
01846 interchangeable. T<GETDOUBLEVALUE> is returnig a T<double> while 
01847 T<DOUBLEVALUE> is a left value available to store a T<double>.
01848 
01849 /*FUNCTION*/
01850 double execute_GetDoubleValue(pExecuteObject pEo,
01851                               pFixSizeMemoryObject pVar
01852   ){
01853 /*noverbatim
01854 CUT*/
01855   char *s;
01856   double intpart,fracpart,exppart,man;
01857   int i,esig,isig;
01858   unsigned long sLen;
01859 
01860   while( pVar && pVar->vType == VTYPE_ARRAY )
01861     pVar = pVar->Value.aValue[pVar->ArrayLowLimit];
01862 
01863   if( memory_IsUndef(pVar) )return 0.0;
01864 
01865   execute_DereferenceS(pEo->pMo->maxderef,&pVar);
01866 
01867   switch( pVar->vType ){
01868     default: return 0.0;
01869     case VTYPE_LONG:
01870       return (double)pVar->Value.lValue;
01871     case VTYPE_DOUBLE:
01872       return pVar->Value.dValue;
01873     case VTYPE_STRING:
01874       s = (char *)pVar->Value.pValue;
01875       sLen = pVar->Size;
01876       while( isspace( *s ) && sLen ){
01877         s++; /*leading spaces don't matter*/
01878         sLen--;
01879         }
01880       isig = 1; esig =1;
01881       if( *s == '-' )isig = -1;
01882       if( sLen )
01883         if( *s == '-' || *s == '+' ){ s++; sLen--; }
01884       for( intpart = 0 ; sLen && isdigit(*s) ; s++,sLen-- ){
01885         intpart *= 10;
01886         intpart += *s -'0';
01887         }
01888       fracpart = 0.0;
01889       if( sLen && *s == '.' ){
01890         s++;     /* step over the decimal dot */
01891         sLen --;
01892         i = 0; /* this is not an integer anymore */
01893         fracpart = 0.0; /* fractional part */
01894         man = 1.0;      /* actual mantissa */
01895         for(  ; isdigit(*s) && sLen ; s++, sLen-- )
01896           fracpart += (man *= 0.1) * (*s-'0');
01897         }
01898       if( sLen && (*s == 'E' || *s == 'e') ){
01899         i = 0; /* this is not an integer anymore if it has exponential part */
01900         s++; /* step over the character E */
01901         sLen --;
01902         if( *s == '-' )esig=-1; else esig = 1;
01903         if( sLen )
01904           if( *s == '+' || *s == '-'){ s++; sLen--; } /* step over the exponential sign */
01905         for( exppart=0.0 , i = 0 ; sLen && isdigit(*s) ; s++, sLen-- )
01906           exppart = 10*exppart + *s-'0';
01907         }else exppart = 0.0;
01908     return isig*(intpart + fracpart)*pow10(esig*exppart);
01909     }
01910   }
01911 
01912 /*POD
01913 =H execute_GetLongValue()
01914 
01915 Use this function whenever you want to access the B<value> of a variable as a T<long>.
01916 Formerly ScriptBasic in such situation converted the variable to long calling
01917 R<execute_Convert2Long()> and then used the macro T<LONGVALUE>. This method is faster
01918 because this does not create a new mortal variable but returns directly the
01919 long value.
01920 
01921 The macro T<GETLONGVALUE> can be used to call this function with the default
01922 execution environment variable T<pEo>
01923 
01924 Note however that the macro T<GETLONGVALUE> and T<LONGVALUE> are not 
01925 interchangeable. T<GETLONGVALUE> is returnig a T<long> while 
01926 T<LONGVALUE> is a left value available to store a T<long>.
01927 
01928 /*FUNCTION*/
01929 long execute_GetLongValue(pExecuteObject pEo,
01930                           pFixSizeMemoryObject pVar
01931   ){
01932 /*noverbatim
01933 
01934 Please also note that the result of converting a string variable to LONG and then
01935 accessing its longvalue may not result the same number as calling this function.
01936 The reason is that conversion of a string to a LONG variable is done in two steps.
01937 First it converts the string to a T<double> and then it rounds the T<double> value
01938 to T<long>. On the other hand this function converts a string diretly to T<long>.
01939 
01940 For example the string T<"3.7"> becomes 4 when converted to long and 3 when getting the
01941 value as a long.
01942 
01943 CUT*/
01944   char *s;
01945   long lintpart;
01946   double intpart,fracpart,exppart,man;
01947   int i,esig,isig;
01948   unsigned long sLen;
01949 
01950   while( pVar && pVar->vType == VTYPE_ARRAY )
01951     pVar = pVar->Value.aValue[pVar->ArrayLowLimit];
01952 
01953   if( memory_IsUndef(pVar) )return 0;
01954 
01955   execute_DereferenceS(pEo->pMo->maxderef,&pVar);
01956 
01957   switch( pVar->vType ){
01958     default: return 0;
01959     case VTYPE_LONG: return pVar->Value.lValue;
01960     case VTYPE_STRING:
01961       s = (char *)pVar->Value.pValue;
01962       sLen = pVar->Size;
01963       while( isspace( *s ) && sLen ){
01964         s++; /*leading spaces don't matter*/
01965         sLen--;
01966         }
01967       isig = 1;
01968       if( *s == '-' )isig = -1;
01969       if( sLen )
01970         if( *s == '-' || *s == '+' ){ s++; sLen--; }
01971       for( lintpart = 0 ; sLen && isdigit(*s) ; s++,sLen-- ){
01972         lintpart *= 10;
01973         lintpart += *s -'0';
01974         }
01975       /* if there is no fractional part then return it */
01976       if( (!sLen) || (*s != '.' && *s != 'e' && *s != 'E') )return isig*lintpart;
01977       intpart = lintpart;
01978       fracpart = 0.0;
01979       if( sLen && *s == '.' ){
01980         s++;     /* step over the decimal dot */
01981         sLen --;
01982         i = 0; /* this is not an integer anymore */
01983         fracpart = 0.0; /* fractional part */
01984         man = 1.0;      /* actual mantissa */
01985         for(  ; isdigit(*s) && sLen ; s++, sLen-- )
01986           fracpart += (man *= 0.1) * (*s-'0');
01987         }
01988       if( sLen && (*s == 'E' || *s == 'e') ){
01989         i = 0; /* this is not an integer anymore if it has exponential part */
01990         s++; /* step over the character E */
01991         sLen --;
01992         if( *s == '-' )esig=-1; else esig = 1;
01993         if( sLen )
01994           if( *s == '+' || *s == '-'){ s++; sLen--; } /* step over the exponential sign */
01995         for( exppart=0.0 , i = 0 ; sLen && isdigit(*s) ; s++, sLen-- )
01996           exppart = 10*exppart + *s-'0';
01997         }else exppart = 0.0;
01998     return (long)(isig*(intpart + fracpart)*pow10(esig*exppart));
01999     case VTYPE_DOUBLE:
02000       return (long)pVar->Value.dValue;
02001     }
02002   }
02003 
02004 
02005 /*POD
02006 =H execute_IsStringInteger()
02007 
02008 This function should be used to check a string before converting it to numeric value.
02009 If the string contains only digits it should be converted to T<long>. If the string contains
02010 other characters then it should be converted to double. This function decides what characters
02011 the string contains.
02012 
02013 /*FUNCTION*/
02014 int execute_IsStringInteger(pFixSizeMemoryObject pVar
02015   ){
02016 /*noverbatim
02017 CUT*/
02018   char *s;
02019   double mantissa,fracpart,frac;
02020   long sLen;
02021   long fraclen,fracreallen;
02022   long exponent;
02023   int sig,esig;
02024 
02025   if( memory_IsUndef(pVar) || pVar->vType != VTYPE_STRING )return 0;
02026   s = (char *)pVar->Value.pValue;
02027   sLen = pVar->Size;
02028   while( isspace(*s) && sLen ){
02029     s++; /* leading spaces dont matter */
02030     sLen--;
02031     }
02032   sig = 1;
02033   if( sLen )
02034     if( *s == '+' || *s == '-' ){
02035       sig = *s == '+';
02036       s++;
02037       sLen--;
02038       }
02039   /* calculate the mantissa, to check the actual size against LONG_MAX and LONG_MIN*/
02040   mantissa = 0.0;
02041   fracreallen = 0;
02042   while( sLen && isdigit(*s) ){
02043     mantissa *= 10;
02044     mantissa += (double)*s - '0';
02045     /* Calculate the number of zeroes before the fractional dot. This was 1000.0E-3 will be recognized as integer. */
02046     if( '0' == *s )fracreallen --;
02047     s++;
02048     sLen--;
02049     }
02050 
02051   /* if there are no more charaters after the digits */
02052   if( sLen == 0 )
02053     return sig ? mantissa <= LONG_MAX : mantissa <= -(LONG_MIN);
02054 
02055   /* if this is not a correct number */
02056   if( *s != '.' && *s != 'e' && *s != 'E' )return 1;
02057 
02058   fraclen = 0;     /* the number of digits in the fractional part */
02059   fracpart = 0.0;  /* the fractional part of the number. we need this to check the final result against LONG_MAX and LONG_MIN*/
02060   frac = 0.1;      /* the magnitude of the actual fractional digit. This is divided by 10 for each new digit */
02061   if( *s == '.' ){
02062     s++;
02063     sLen --;
02064     while( sLen && isdigit(*s) ){
02065       fracpart += frac * ( *s - '0' );
02066       frac /= 10.0;
02067       fraclen ++;
02068       if( *s != '0' )fracreallen = fraclen;
02069       s++;
02070       sLen--;
02071       }
02072     }
02073   esig = 1;
02074   exponent = 0;
02075   if( sLen && ( *s == 'e' || *s == 'E' ) ){
02076     sLen--;
02077     s++;
02078     if( sLen && ( *s == '+' || *s == '-' ) ){
02079       if( *s == '-' )esig = -1;
02080       sLen--;
02081       s++;
02082       }
02083     while( sLen && isdigit(*s) ){
02084       exponent *= 10;
02085       exponent += *s -'0';
02086       s++;
02087       sLen--;
02088       }
02089     }
02090   /* if there is non-zero fractional part */
02091   /* note that fracreallen can be negative */
02092   if( fracreallen > esig * exponent )return 0;
02093   
02094   /* here we can be sure that the string is an integer number, but still it may be larger than LONG_MAX or smaller than LONG_MIN */
02095   mantissa += fracpart;
02096   mantissa *= pow10(esig *exponent);
02097   return sig ? mantissa <= LONG_MAX : mantissa <= -(LONG_MIN);
02098   }
02099 
02100 /*POD
02101 =H execute_IsInteger()
02102 
02103 This function checks that a variable being long, double or string can be
02104 converted to long without loosing information.
02105 
02106 /*FUNCTION*/
02107 int execute_IsInteger(pFixSizeMemoryObject pVar
02108   ){
02109 /*noverbatim
02110 CUT*/
02111   long lTest;
02112 
02113   /* Although the function GetLongValue converts an undef value to zero 
02114      it should not actually.   */
02115   if( memory_IsUndef(pVar) )return 0;
02116 
02117   switch( TYPE(pVar) ){
02118     case VTYPE_LONG: return 1; /* obvious */
02119 
02120     case VTYPE_DOUBLE: /* return floor(DOUBLEVALUE(pVar)) == DOUBLEVALUE(pVar);*/
02121       lTest = (long)DOUBLEVALUE(pVar);
02122       return DOUBLEVALUE(pVar) == (double)lTest;
02123 
02124     case VTYPE_STRING: return ISSTRINGINTEGER(pVar);
02125     default: return 0;
02126     }
02127 
02128   }

Generated on Sun Mar 12 23:56:27 2006 for ScriptBasic by  doxygen 1.4.6-NO