G:/ScriptBasic/source/commands/function.c

Go to the documentation of this file.
00001 /*function.c
00002 
00003 --GNU LGPL
00004 This library is free software; you can redistribute it and/or
00005 modify it under the terms of the GNU Lesser General Public
00006 License as published by the Free Software Foundation; either
00007 version 2.1 of the License, or (at your option) any later version.
00008 
00009 This library is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 Lesser General Public License for more details.
00013 
00014 You should have received a copy of the GNU Lesser General Public
00015 License along with this library; if not, write to the Free Software
00016 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018 */
00019 
00020 
00021 #include <stdlib.h>
00022 #include <stdio.h>
00023 
00024 #include "../command.h"
00025 
00026 /*POD
00027 =H Function and sub commands
00028 
00029 This code implements the start and the end of a function.
00030 
00031 When the program execution reaches the code that was generated from the code
00032 
00033 =verbatim
00034      fun(x,y,z)     
00035 =noverbatim
00036 
00037 if saves some of the state variables, like the function result pointer, local variable pointer,
00038 program counter, error resume and erroro goto values. The function evaluation does NOT allocate
00039 local variables, and does not evaluate the function arguments. Instead it calls the executor
00040 function recursively to perform execution starting at the node, which was generated from
00041 
00042 =verbatim
00043         FUNCTION fname(a,b,c)
00044 =noverbatim
00045 
00046 The code of the command should evaluate the arguments (it gets the expression list node in a
00047 class variable), and should allocate the local variables and put the values into the local
00048 variables.
00049 
00050 Note that the implementation of the command T<FUNCTION> is very sophisticated, and uses many
00051 features of the execution system, which is usually not needed by other commands. Note that for this
00052 reasons it accesses variables directly without using the hiding macros supplied via T<command.c>
00053 
00054 The code implementing T<FUNCTION> should access the global variables, the local variables
00055 of the caller and the local variables of the just starting function, at the same time.
00056 
00057 Also note that the parameter evaluation is special. It first checks the expression. If this is a normal,
00058 compound expression it does evaluate it the normal way and puts the result to the respective local variable.
00059 However if the expression is a variable then it generates a referring variable. Whenever value is assigned to
00060 a variable that holds a referring value the instruction T<LET> searches for the referred variable and the
00061 assignment is done using that variable.
00062 
00063 This means that all variables are passed by reference. If you want to pass a variable by value you should
00064 apply some tricks, like:
00065 
00066 =verbatim
00067 a=1
00068 call f(a+0)
00069 print a
00070 print
00071 call f(a)
00072 print a
00073 print
00074 function f(x)
00075  x=x+1
00076 end function
00077 =noverbatim
00078 
00079 CUT*/
00080 
00131 COMMAND(FUNCTION)
00132 #if NOTIMP_FUNCTION
00133 NOTIMPLEMENTED;
00134 #else
00135 
00136   IDENTICAL_COMMAND(FUNCTIONARG)
00137 
00138 #endif
00139 END
00140 
00141 /*POD
00142 =section FUNCTIONARG
00143 =H FUNCTIONARG
00144 
00145 This command implements the function head.
00146 
00147 CUT*/
00148 void COMMAND_FUNCTIONARG(pExecuteObject pEo
00149   ){
00150 #if NOTIMP_FUNCTIONARG
00151 NOTIMPLEMENTED;
00152 #else
00153   MortalList _ThisCommandMortals=NULL;
00154   pMortalList _pThisCommandMortals = &_ThisCommandMortals;
00155   unsigned long _ActualNode=pEo->ProgramCounter;
00156   int iErrorCode;
00157   NODE nItem,nExpression;
00158   unsigned long i;
00159   unsigned long NumberOfArguments;
00160   long Opcode;
00161   pFixSizeMemoryObject ItemResult;
00162 
00163 /* note that this code should allocate and put values into the function local variables, but
00164    should evaluate expressions and code using the caller local variables. Therefore we store
00165    the caller local variables pointer in CallerLocalVariables and whenever we need a call
00166    using the caller local variables we swap the two pointers for the shortest time possible.
00167 */
00168   pFixSizeMemoryObject CallerLocalVariables,SwapLVP;
00169 /* This macro is used to maintain readability when the variables are swapped. */
00170 #define NewFunLocalVariables CallerLocalVariables
00171 #define SWAP_LOCAL_VARIABLES SwapLVP = CallerLocalVariables; \
00172                              CallerLocalVariables = pEo->LocalVariables;\
00173                              pEo->LocalVariables = SwapLVP;
00174 
00175   nItem = pEo->CommandArray[pEo->ProgramCounter-1].Parameter.NodeList.actualm ;
00176   Opcode = pEo->CommandArray[nItem-1].OpCode;
00177   pEo->cLocalVariables = pEo->CommandArray[nItem-1].Parameter.CommandArgument.Argument.lLongValue;
00178   nItem = pEo->CommandArray[nItem-1].Parameter.CommandArgument.next;
00179   NumberOfArguments = pEo->CommandArray[nItem-1].Parameter.CommandArgument.Argument.lLongValue;
00180   nItem = pEo->CommandArray[nItem-1].Parameter.CommandArgument.next;
00181   nItem = pEo->CommandArray[nItem-1].Parameter.CommandArgument.Argument.lLongValue;
00182 
00183   if( ! pEo->fWeAreCallingFuction ){
00184     SETPROGRAMCOUNTER(CDR(nItem));
00185     return;
00186     }
00187 
00188   CallerLocalVariables = pEo->LocalVariables;
00189   if( pEo->cLocalVariables ){
00190     pEo->LocalVariables = memory_NewArray(pEo->pMo,1,pEo->cLocalVariables);
00191     if( pEo->LocalVariables == NULL ){
00192       pEo->fStop = fStopSTOP;
00193       return;
00194       }
00195     }else pEo->LocalVariables = NULL; /* it should have been null anyway */
00196 
00197   nItem = pEo->FunctionArgumentsNode;
00198   i = 0;
00199   while( nItem ){
00200     i++ ;
00201     nExpression = CAR(nItem);
00202     switch( OPCODE(nExpression) ){
00203 
00204       case eNTYPE_ARR:
00205         SWAP_LOCAL_VARIABLES;
00206         if( i <= NumberOfArguments ){
00207           NewFunLocalVariables->Value.aValue[i-1] = memory_NewRef(pEo->pMo);
00208           memory_SetRef(pEo->pMo,NewFunLocalVariables->Value.aValue+i-1,execute_LeftValueArray(pEo,nExpression,_pThisCommandMortals,&iErrorCode));
00209           }else execute_LeftValueArray(pEo,nExpression,_pThisCommandMortals,&iErrorCode);
00210         SWAP_LOCAL_VARIABLES;
00211         break;
00212 
00213       case eNTYPE_SAR:
00214         SWAP_LOCAL_VARIABLES;
00215         if( i <= NumberOfArguments ){
00216           NewFunLocalVariables->Value.aValue[i-1] = memory_NewRef(pEo->pMo);
00217           memory_SetRef(pEo->pMo,NewFunLocalVariables->Value.aValue+i-1,execute_LeftValueSarray(pEo,nExpression,_pThisCommandMortals,&iErrorCode));
00218           }else execute_LeftValueSarray(pEo,nExpression,_pThisCommandMortals,&iErrorCode);
00219         SWAP_LOCAL_VARIABLES;
00220         break;
00221 
00222       case eNTYPE_LVR:
00223         SWAP_LOCAL_VARIABLES;
00224         if( i <= NumberOfArguments ){
00225           NewFunLocalVariables->Value.aValue[i-1] = memory_NewRef(pEo->pMo);
00226           memory_SetRef(pEo->pMo,NewFunLocalVariables->Value.aValue+i-1,&(pEo->LocalVariables->Value.aValue[pEo->CommandArray[nExpression-1].Parameter.Variable.Serial-1]));
00227           }
00228         SWAP_LOCAL_VARIABLES;
00229         break;
00230 
00231       case eNTYPE_GVR:
00232         if( i <= NumberOfArguments ){
00233           pEo->LocalVariables->Value.aValue[i-1] = memory_NewRef(pEo->pMo);
00234           memory_SetRef(pEo->pMo,pEo->LocalVariables->Value.aValue+i-1,&(pEo->GlobalVariables->Value.aValue[pEo->CommandArray[nExpression-1].Parameter.Variable.Serial-1]));
00235           }
00236         break;
00237 
00238       default:
00239         SWAP_LOCAL_VARIABLES;
00240         ItemResult = EVALUATEEXPRESSION(nExpression);
00241         SWAP_LOCAL_VARIABLES;
00242         ASSERTOKE;
00243         if( ItemResult)
00244           memory_Immortalize(ItemResult,_pThisCommandMortals);
00245         if( i <= NumberOfArguments )
00246           pEo->LocalVariables->Value.aValue[i-1] = ItemResult;
00247         break;
00248       }
00249 
00250     nItem = CDR(nItem);
00251     }
00252 
00253   memory_ReleaseMortals(pEo->pMo,&_ThisCommandMortals);
00254   /* and finally we start to execute the function when executing the next command */
00255   pEo->lFunctionLevel++;
00256   /* some macros need this label */
00257 #endif
00258 _FunctionFinishLabel: ;
00259   }
00260 
00261 /*
00262 
00263 This structure is used to maintain the return gosub stack. This structure
00264 becomes empty when there is no any GOSUB/RETURN pairs pending. If some
00265 of the GOSUB-s did not return when the code finishes the memory is released
00266 when the segment is released. We do leak here memory at this level, because
00267 upper levels release this memory before exiting the interpreter.
00268 
00269 */
00270 typedef struct _GosubStack {
00271   struct _GosubStack *next;
00272   long lFunctionLevel;
00273   NODE nReturnNode;
00274   } GosubStack , *pGosubStack;
00275 
00276 #define GosubStackRoot ((pGosubStack)PARAMPTR(CMD_GOSUB))
00277 
00295 COMMAND(GOSUB)
00296 #if NOTIMP_GOSUB
00297 NOTIMPLEMENTED;
00298 #else
00299 
00300   pGosubStack pGSS;
00301 
00302   pGSS = ALLOC(sizeof(GosubStack));
00303   if( pGSS == NULL )ERROR(COMMAND_ERROR_MEMORY_LOW);
00304   pGSS->lFunctionLevel = pEo->lFunctionLevel;
00305   pGSS->nReturnNode = pEo->CommandArray[pEo->ProgramCounter-1].Parameter.NodeList.rest;
00306   pGSS->next = GosubStackRoot;
00307   GosubStackRoot = pGSS;
00308   SETPROGRAMCOUNTER(PARAMETERNODE);
00309 
00310 #endif
00311 END
00312 
00318 COMMAND(RETURNC)
00319 #if NOTIMP_RETURNC
00320 NOTIMPLEMENTED;
00321 #else
00322 
00323   pGosubStack pGSS;
00324 
00325   pGSS = GosubStackRoot;
00326   if( pGSS == NULL || pGSS->lFunctionLevel < pEo->lFunctionLevel )ERROR(COMMAND_ERROR_RETURN_WITHOUT_GOSUB);
00327   GosubStackRoot = GosubStackRoot->next;
00328   SETPROGRAMCOUNTER(pGSS->nReturnNode);
00329   FREE(pGSS);
00330 #endif
00331 END
00332 
00340 COMMAND(POP)
00341 #if NOTIMP_POP
00342 NOTIMPLEMENTED;
00343 #else
00344 
00345   pGosubStack pGSS;
00346 
00347   pGSS = GosubStackRoot;
00348   if( pGSS == NULL || pGSS->lFunctionLevel < pEo->lFunctionLevel )ERROR(COMMAND_ERROR_RETURN_WITHOUT_GOSUB);
00349   GosubStackRoot = GosubStackRoot->next;
00350   /* this is what POP does not do as opposed to RETURN */
00351 /*SETPROGRAMCOUNTER(pGSS->nReturnNode);*/
00352   FREE(pGSS);
00353 #endif
00354 END
00355 
00363 COMMAND(EXITFUNC)
00364 #if NOTIMP_EXITFUNC
00365 NOTIMPLEMENTED;
00366 #else
00367 
00368   pGosubStack pGSS;
00369 
00370   /* step back the function level because we are leaving the function */
00371   pEo->lFunctionLevel--;
00372 
00373   /* clean up the gosub stack */
00374   pGSS = GosubStackRoot;
00375   while( pGSS && pGSS->lFunctionLevel > pEo->lFunctionLevel ){
00376     GosubStackRoot = GosubStackRoot->next;
00377     FREE(pGSS);
00378     pGSS = GosubStackRoot;
00379     }
00380   pEo->fStop = fStopRETURN;
00381   
00382 #endif
00383 END
00384 
00385 COMMAND(ENDFUNC)
00386 #if NOTIMP_ENDFUNC
00387 NOTIMPLEMENTED;
00388 #else
00389 
00390   IDENTICAL_COMMAND(EXITFUNC)
00391   
00392 #endif
00393 END
00394 
00395 COMMAND(FLET)
00396 #if NOTIMP_FLET
00397 NOTIMPLEMENTED;
00398 #else
00399 
00400 
00401   VARIABLE ItemResult;
00402 
00403   /* here we get a mortal value as result. This should be mortal, because it is immortalized and
00404      the code later assumes that this is an immortal memory piece that is assigned only to this
00405      "variable"
00406   */
00407   ItemResult = _EVALUATEEXPRESSION_A(PARAMETERNODE);
00408   ASSERTOKE;
00409 
00410   if( ItemResult && TYPE(ItemResult) == VTYPE_ARRAY )ERROR(COMMAND_ERROR_NOARRAY);
00411 
00412   ItemResult = memory_DupMortalize(pEo->pMo,ItemResult,_pThisCommandMortals,&iErrorCode);
00413 
00414   if( pEo->pFunctionResult )
00415     memory_ReleaseVariable(pEo->pMo,pEo->pFunctionResult);
00416   if( ItemResult )IMMORTALIZE(ItemResult);
00417   pEo->pFunctionResult = ItemResult;
00418 
00419 #endif
00420 END
00421 
00434 COMMAND(ADDRESSF)
00435 #if NOTIMP_ADDRESSF
00436 NOTIMPLEMENTED;
00437 #else
00438 
00439   NODE z;
00440 
00441   USE_CALLER_MORTALS;
00442   z = PARAMETERLIST;
00443 
00444   if( OPCODE(CAR(z)) != eNTYPE_FUN )ERROR(COMMAND_ERROR_INVALID_ARGUMENT_FOR_FUNCTION_ADDRESS);
00445 
00446   RESULT = NEWMORTALLONG;
00447   ASSERTNULL(RESULT)
00448   LONGVALUE(RESULT) = CAR(pEo->CommandArray[z-1].Parameter.UserFunction.NodeId);
00449 
00450 #endif
00451 END
00452 
00547 COMMAND(ICALLFUN)
00548 #if NOTIMP_ICALLFUN
00549 NOTIMPLEMENTED;
00550 #else
00551 
00552   NODE nItem;
00553   VARIABLE ItemResult;
00554   pFixSizeMemoryObject ThisFunctionResultPointer;
00555   unsigned long SaveProgramCounter,SaveStepCounter;
00556   unsigned long SavefErrorGoto,SaveErrorGoto,SaveErrorResume;
00557   pFixSizeMemoryObject SaveLocalVariablesPointer;
00558   pFixSizeMemoryObject SaveFunctionResultPointer;
00559   long CommandOpCode;
00560 
00561   USE_CALLER_MORTALS;
00562 
00563   if( pEo->FunctionLevelLimit && pEo->lFunctionLevel > pEo->FunctionLevelLimit )
00564     ERROR(EXE_ERROR_TOO_DEEP_CALL);
00565 
00566   SaveLocalVariablesPointer = pEo->LocalVariables;
00567   SaveProgramCounter = pEo->ProgramCounter;
00568 
00569   nItem = PARAMETERLIST;
00570   ItemResult = CONVERT2LONG(EVALUATEEXPRESSION(CAR(nItem)));
00571   ASSERTOKE;
00572   pEo->ProgramCounter = LONGVALUE(ItemResult);
00573 
00574   CommandOpCode = pEo->CommandArray[pEo->CommandArray[pEo->ProgramCounter-1].Parameter.NodeList.actualm-1].OpCode;
00575   if( CommandOpCode < START_CMD || CommandOpCode > END_CMD ){
00576     ERROR(EXE_ERROR_USERFUN_UNDEFINED);
00577     }
00578   if( CommandOpCode != CMD_FUNCTION &&
00579       CommandOpCode != CMD_FUNCTIONARG &&
00580       CommandOpCode != CMD_SUB &&
00581       CommandOpCode != CMD_SUBARG ){
00582     ERROR(EXE_ERROR_USERFUN_UNDEFINED);
00583     }
00584 
00585   pEo->FunctionArgumentsNode = CDR(nItem);
00586   SaveFunctionResultPointer = pEo->pFunctionResult;
00587   pEo->pFunctionResult = NULL;
00588   SaveStepCounter = pEo->lStepCounter;
00589   pEo->lStepCounter = 0;
00590   pEo->fWeAreCallingFuction = 1;
00591   SaveErrorGoto = pEo->ErrorGoto;
00592   pEo->ErrorGoto = 0;
00593   SaveErrorResume = pEo->ErrorResume;
00594   pEo->ErrorResume = 0;
00595   SavefErrorGoto = pEo->fErrorGoto;
00596   pEo->fErrorGoto = ONERROR_NOTHING;
00597   if( pEo->pHookers->HOOK_ExecCall && 
00598       (iErrorCode = pEo->pHookers->HOOK_ExecCall(pEo)) )
00599     ERROR(iErrorCode);
00600   /* function entering code needs access to the caller local variables, therefore
00601      WE SHOULD NOT NULL pEo->LocalVariables */
00602   execute_Execute_r(pEo,&iErrorCode);
00603   if( pEo->pHookers->HOOK_ExecReturn &&
00604       (iErrorCode = pEo->pHookers->HOOK_ExecReturn(pEo)) )
00605     ERROR(iErrorCode);
00606 
00607   pEo->lStepCounter = SaveStepCounter;
00608   if( pEo->LocalVariables )/* this is null if the function did not have arguments and no local variables */
00609     memory_ReleaseVariable(pEo->pMo,pEo->LocalVariables);
00610   pEo->ProgramCounter = SaveProgramCounter;
00611   pEo->LocalVariables = SaveLocalVariablesPointer;
00612   ThisFunctionResultPointer = pEo->pFunctionResult;
00613   pEo->pFunctionResult = SaveFunctionResultPointer;
00614   /* Functions return their value as immortal values assigned to the very global
00615      variable pEo->pFunctionResult. Here this variable is restored to point to the
00616      saved value and the value returned should be mortalized.                   */
00617   if( ThisFunctionResultPointer && ! IsMortal(ThisFunctionResultPointer) )
00618     memory_Mortalize(ThisFunctionResultPointer,_pThisCommandMortals);
00619 
00620   pEo->ErrorGoto = SaveErrorGoto;
00621   pEo->fErrorGoto = SavefErrorGoto;
00622   pEo->ErrorResume = SaveErrorResume;
00623   if( iErrorCode )ERROR(iErrorCode);
00624 
00625   RESULT = ThisFunctionResultPointer;
00626 #endif
00627 END
00628 
00629 COMMAND(ICALL)
00630 #if NOTIMP_ICALL
00631 NOTIMPLEMENTED;
00632 #else
00633 
00634   VARIABLE ItemResult;
00635   unsigned long SaveProgramCounter,SaveStepCounter;
00636   unsigned long SavefErrorGoto,SaveErrorGoto,SaveErrorResume;
00637   pFixSizeMemoryObject SaveLocalVariablesPointer;
00638   pFixSizeMemoryObject SaveFunctionResultPointer;
00639   long CommandOpCode;
00640 
00641   if( pEo->FunctionLevelLimit && pEo->lFunctionLevel > pEo->FunctionLevelLimit )
00642     ERROR(EXE_ERROR_TOO_DEEP_CALL);
00643 
00644   SaveLocalVariablesPointer = pEo->LocalVariables;
00645   SaveProgramCounter = pEo->ProgramCounter;
00646 
00647   ItemResult = EVALUATEEXPRESSION(CAR(PARAMETERNODE));
00648   ASSERTOKE;
00649   pEo->ProgramCounter = GETLONGVALUE(ItemResult);
00650 
00651   CommandOpCode = pEo->CommandArray[pEo->CommandArray[pEo->ProgramCounter-1].Parameter.NodeList.actualm-1].OpCode;
00652   if( CommandOpCode < START_CMD || CommandOpCode > END_CMD ){
00653     ERROR(EXE_ERROR_USERFUN_UNDEFINED);
00654     }
00655   if( CommandOpCode != CMD_FUNCTION &&
00656       CommandOpCode != CMD_FUNCTIONARG &&
00657       CommandOpCode != CMD_SUB &&
00658       CommandOpCode != CMD_SUBARG ){
00659     ERROR(EXE_ERROR_USERFUN_UNDEFINED);
00660     }
00661 
00662   pEo->FunctionArgumentsNode = CDR(PARAMETERNODE);
00663   SaveFunctionResultPointer = pEo->pFunctionResult;
00664   pEo->pFunctionResult = NULL;
00665   SaveStepCounter = pEo->lStepCounter;
00666   pEo->lStepCounter = 0;
00667   pEo->fWeAreCallingFuction = 1;
00668   SaveErrorGoto = pEo->ErrorGoto;
00669   pEo->ErrorGoto = 0;
00670   SaveErrorResume = pEo->ErrorResume;
00671   pEo->ErrorResume = 0;
00672   SavefErrorGoto = pEo->fErrorGoto;
00673   pEo->fErrorGoto = ONERROR_NOTHING;
00674   if( pEo->pHookers->HOOK_ExecCall && 
00675       (iErrorCode = pEo->pHookers->HOOK_ExecCall(pEo)) )
00676     ERROR(iErrorCode);
00677   /* function entering code needs access to the caller local variables, therefore
00678      WE SHOULD NOT NULL pEo->LocalVariables */
00679   execute_Execute_r(pEo,&iErrorCode);
00680   if( pEo->pHookers->HOOK_ExecReturn &&
00681       (iErrorCode = pEo->pHookers->HOOK_ExecReturn(pEo)) )
00682     ERROR(iErrorCode);
00683 
00684   pEo->lStepCounter = SaveStepCounter;
00685   if( pEo->LocalVariables )/* this is null if the function did not have arguments and no local variables */
00686     memory_ReleaseVariable(pEo->pMo,pEo->LocalVariables);
00687   pEo->ProgramCounter = SaveProgramCounter;
00688   pEo->LocalVariables = SaveLocalVariablesPointer;
00689   memory_ReleaseVariable(pEo->pMo,pEo->pFunctionResult);
00690   pEo->pFunctionResult = SaveFunctionResultPointer;
00691 
00692   pEo->ErrorGoto = SaveErrorGoto;
00693   pEo->fErrorGoto = SavefErrorGoto;
00694   pEo->ErrorResume = SaveErrorResume;
00695   if( iErrorCode )ERROR(iErrorCode);
00696 
00697 #endif
00698 END
00699 
00712 COMMAND(CALL)
00713 #if NOTIMP_CALL
00714 NOTIMPLEMENTED;
00715 #else
00716 
00717 
00718   _EVALUATEEXPRESSION(PARAMETERNODE);
00719   ASSERTOKE;
00720 
00721 #endif
00722 END
00723 
00742 COMMAND(SUB)
00743 #if NOTIMP_SUB
00744 NOTIMPLEMENTED;
00745 #else
00746 
00747   IDENTICAL_COMMAND(FUNCTIONARG)
00748 
00749 #endif
00750 END
00751 
00752 /*POD
00753 =section SUBARG
00754 =H SUBARG
00755 
00756 Same as R<FUNCTIONARG>
00757 
00758 CUT*/
00759 COMMAND(SUBARG)
00760 #if NOTIMP_SUBARG
00761 NOTIMPLEMENTED;
00762 #else
00763 
00764   IDENTICAL_COMMAND(FUNCTIONARG)
00765 
00766 #endif
00767 END
00768 
00769 
00780 COMMAND(EXITSUB)
00781 #if NOTIMP_EXITSUB
00782 NOTIMPLEMENTED;
00783 #else
00784 
00785   IDENTICAL_COMMAND(EXITFUNC)
00786 
00787 #endif
00788 END
00789 
00790 
00791 COMMAND(ENDSUB)
00792 #if NOTIMP_ENDSUB
00793 NOTIMPLEMENTED;
00794 #else
00795 
00796   IDENTICAL_COMMAND(EXITFUNC)
00797 
00798 #endif
00799 END

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