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

Go to the documentation of this file.
00001 /* while.c
00002 --GNU LGPL
00003 This library is free software; you can redistribute it and/or
00004 modify it under the terms of the GNU Lesser General Public
00005 License as published by the Free Software Foundation; either
00006 version 2.1 of the License, or (at your option) any later version.
00007 
00008 This library is distributed in the hope that it will be useful,
00009 but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011 Lesser General Public License for more details.
00012 
00013 You should have received a copy of the GNU Lesser General Public
00014 License along with this library; if not, write to the Free Software
00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00016 
00017 */
00018 
00019 #include <stdlib.h>
00020 #include <stdio.h>
00021 
00022 #include "../command.h"
00023 /*POD
00024 =H Implement the different looping constructs
00025 
00026 This file implements the various looping constructs. These include the followings:
00027 
00028 =verbatim
00029 while expression
00030 wend
00031 
00032 repeat
00033 until expression
00034 
00035 do while expression
00036 loop
00037 
00038 do until expression
00039 loop
00040 
00041 do
00042 loop while expression
00043 
00044 do
00045 loop until expression
00046 
00047 for i=expression to expression [step expression]
00048 next
00049 =noverbatim
00050 
00051 The different formats result the same, they are presented here to allow
00052 the user to use the one that fits his or her taste.
00053 
00054 The implementation of the 'for' loop is extremely sophisticated and tricky. Try to
00055 understand when you are really familiar with the code structure.
00056 
00057 CUT*/
00058 
00079 COMMAND(WHILE)
00080 #if NOTIMP_WHILE
00081 NOTIMPLEMENTED;
00082 #else
00083 
00084   NODE nItem;
00085   NODE nGoForward;
00086   VARIABLE ItemResult;
00087 
00088   nItem = PARAMETERNODE;
00089   ItemResult = EVALUATEEXPRESSION(nItem);
00090   ASSERTOKE;
00091   NEXTPARAMETER;
00092   /* we step forward to the node AFTER the while statement */
00093   nGoForward = CDR(PARAMETERNODE);
00094   if( memory_IsUndef(ItemResult) ){
00095     SETPROGRAMCOUNTER(nGoForward);
00096     RETURN;
00097     }
00098 
00099   switch( TYPE(ItemResult) ){
00100     case VTYPE_LONG:
00101       if( ! LONGVALUE(ItemResult) ){
00102         SETPROGRAMCOUNTER(nGoForward);
00103         RETURN;
00104         }
00105       break;
00106     case VTYPE_DOUBLE:
00107       if( ! DOUBLEVALUE(ItemResult) ){
00108         SETPROGRAMCOUNTER(nGoForward);
00109         RETURN;
00110         }
00111         break;
00112     case VTYPE_STRING:
00113       if( * STRINGVALUE(ItemResult) == (char)0 ){
00114         SETPROGRAMCOUNTER(nGoForward);
00115         RETURN;
00116         }
00117       break;
00118     case VTYPE_ARRAY:
00119       break;
00120     }
00121 
00122 #endif
00123 END
00124 
00155 COMMAND(DOUNTIL)
00156 #if NOTIMP_DOUNTIL
00157 NOTIMPLEMENTED;
00158 #else
00159 
00160 
00161   NODE nItem;
00162   NODE nGoForward;
00163   VARIABLE ItemResult;
00164 
00165   nItem = PARAMETERNODE;
00166   ItemResult = EVALUATEEXPRESSION(nItem);
00167   ASSERTOKE;
00168   NEXTPARAMETER;
00169 
00170   /* we step forward to the node AFTER the loop statement */
00171   nGoForward = CDR(PARAMETERNODE);
00172   if( memory_IsUndef(ItemResult) ){
00173     RETURN;
00174     }
00175 
00176    switch( TYPE(ItemResult) ){
00177       case VTYPE_LONG:
00178         if( LONGVALUE(ItemResult) ){
00179           SETPROGRAMCOUNTER(nGoForward);
00180           RETURN;
00181           }
00182         break;
00183       case VTYPE_DOUBLE:
00184         if( DOUBLEVALUE(ItemResult) ){
00185           SETPROGRAMCOUNTER(nGoForward);
00186           RETURN;
00187           }
00188         break;
00189       case VTYPE_STRING:
00190         if( * STRINGVALUE(ItemResult) != (char)0 ){
00191           SETPROGRAMCOUNTER(nGoForward);
00192           RETURN;
00193           }
00194         break;
00195       case VTYPE_ARRAY:
00196         break;
00197       }
00198 
00199 #endif
00200 END
00201 
00202 COMMAND(WEND)
00203 #if NOTIMP_WEND
00204 NOTIMPLEMENTED;
00205 #else
00206 
00207 
00208   SETPROGRAMCOUNTER(PARAMETERNODE);
00209 
00210 #endif
00211 END
00212 
00231 COMMAND(DOWHILE)
00232 #if NOTIMP_DOWHILE
00233 NOTIMPLEMENTED;
00234 #else
00235  IDENTICAL_COMMAND(WHILE)
00236 #endif
00237 END
00238 
00239 COMMAND(LOOP)
00240 #if NOTIMP_LOOP
00241 NOTIMPLEMENTED;
00242 #else
00243  IDENTICAL_COMMAND(WEND)
00244 #endif
00245 END
00246 
00266 COMMAND(REPEAT)
00267 #if NOTIMP_REPEAT
00268 NOTIMPLEMENTED;
00269 #else
00270 
00271  /* we do not do anything just start a loop */
00272 
00273 #endif
00274 END
00275 
00308 COMMAND(DO)
00309 #if NOTIMP_DO
00310 NOTIMPLEMENTED;
00311 #else
00312 
00313  /* we do not do anything just start a loop */
00314 #endif
00315 END
00316 
00317 COMMAND(LOOPWHILE)
00318 #if NOTIMP_LOOPWHILE
00319 NOTIMPLEMENTED;
00320 #else
00321   IDENTICAL_COMMAND(DOUNTIL)
00322 #endif
00323 END
00324 
00325 COMMAND(UNTIL)
00326 #if NOTIMP_UNTIL
00327 NOTIMPLEMENTED;
00328 #else
00329   IDENTICAL_COMMAND(WHILE)
00330 #endif
00331 END
00332 
00333 COMMAND(LOOPUNTIL)
00334 #if NOTIMP_LOOPUNTIL
00335 NOTIMPLEMENTED;
00336 #else
00337   IDENTICAL_COMMAND(WHILE)
00338 #endif
00339 END
00340 
00453 /*
00454 THIS IS DETAILS FOR C PROGRAMMERS We discuss here the specialties how FOR is implemented in the syntax analyzer!
00455 
00456 This command implements a 'for' loop. The 'for' loop is implemented with a slight trick. The syntax definition system of ScriptBasic does not allow optional parts of a command, like the STEP part of the for command. In such a case two different command should be defined, like
00457 
00458 =verbatim
00459 FOR: 'for' lval '=' expression 'to' expression nl
00460 FORS: 'for' lval '=' expression 'to' expression 'step' expression nl
00461 =noverbatim
00462 
00463 in the T<syntax.def> file. B<This does not work!>
00464 
00465 The reason is that an T<expression> is a terminal symbol which can not be parsed without side effects. Therefore the syntax of a line should be designed in a way which can assure that the line contains the very command by the time the parser starts to evaluate an expression.
00466 
00467 Assume that we use the syntax definition above and scriba tries to analyze a line
00468 
00469 =verbatim
00470 for i=1 to 100 step 2
00471 =noverbatim
00472 
00473 The syntax analyzer analyzed the left value before the '=' sign, two expressions when it realizes that
00474 the command does not fit the current syntax definition line, because a predefined symbol (step) is coming
00475 instead of the new line. At this stage the syntax analyzer drops the line and treats it syntactically
00476 incorrect, because the left value analysis and the expression analysis generated a lot of code which, 
00477 in the current of scriba can not be undone.
00478 
00479 I<What do we do then to implement a FOR loop?>
00480 
00481 A FOR loop head is defined as three consecutive commands:
00482 
00483 =itemize
00484 =item a T<FOR> command
00485 =item a T<FORTO> command
00486 =item and a T<STEP> command
00487 =noitemize
00488 (And yes, we have a NEXT command as well, but that is implemented normal.)
00489 
00490 The syntax definition lines in T<syntax.def> are:
00491 =verbatim
00492 FOR:     'for' lval '=' expression come_back(FORTO)
00493 FORTO:   'to' expression go_back(FORTO) go_forward(FOR) come_back(FOR)
00494 FORSTEP: 'step' expression nl
00495 =noverbatim
00496 
00497 This means that a
00498 
00499 =verbatim
00500 for i=1 to 100
00501 print
00502 =noverbatim
00503 
00504 code fragment is analyzed as
00505 
00506 =verbatim
00507 for i=1
00508 =noverbatim
00509 
00510 a for command,
00511 
00512 =verbatim
00513         to 100
00514 =noverbatim
00515 
00516 a to command. Then a single new line character comes, which is analyzed as an EMPTY command (see the syntax.def file!). And finally a
00517 print command.
00518 
00519 This also generates two or three commands in case you do or do not have a 'step' part. This helps implementing the initialization
00520 code, the increment code into two different command and therefore it is easy to distinguish between starting the loop and
00521 jumping from the end to the start. (Note that the other looping construct do not have initialization code inside them,
00522 and therefore they do not care if the execution is the first one entering the loop or we just jumped back to the loop head from
00523 the end.)
00524 
00525 When we jump back from the command T<NEXT> we always jump back to the command T<FORTO>.
00526 
00527 The sorry side of this solution is that one can write the following or similar code:
00528 
00529 =verbatim
00530 print "hello"
00531 for i=10
00532 print "here I am"
00533 =noverbatim
00534 
00535 or
00536 
00537 =verbatim
00538 print "hello"
00539 to 63
00540 print "here I am"
00541 next
00542 =noverbatim
00543 
00544 without getting syntax error. The error occurs when the execution starts.
00545 
00546 Let us hope two things:
00547 
00548 =itemize
00549 =item People doing nasty things with scriba using this "feature" won't exist.
00550 =item Unintentional syntax errors uncovered by this "feature" are very rare.
00551 =noitemize
00552 
00553 For more details see the code.
00554 */
00555 COMMAND(FOR)
00556 #if NOTIMP_FOR
00557 NOTIMPLEMENTED;
00558 #else
00559 
00560   VARIABLE vStartExpression,
00561            vEndExpression,
00562            vStepExpression;
00563   LEFTVALUE LetThisVariable;
00564   NODE nToStatement;
00565   NODE nStepStatement;
00566   NODE nLoopStart,nLoopEnd;
00567   int iGoingUp; /* are we stepping up or down */
00568   long refcount;
00569   int bEnterTheLoop;
00570 
00571   /* there should be a to statement following the for statement */
00572   nToStatement = CDR(pEo->ProgramCounter);
00573 
00574   if( nToStatement == 0 ){
00575     /* If this is the last statement then we can stop. */
00576     SETPROGRAMCOUNTER(0);
00577     RETURN;
00578     }
00579 
00580   nStepStatement = CDR(nToStatement);
00581   nToStatement = CAR(nToStatement);
00582 
00583   if( nStepStatement ){
00584     if( pEo->CommandArray[CAR(nStepStatement)-1].OpCode == CMD_FORSTEP ){
00585       nLoopStart = CDR(nStepStatement);
00586       nStepStatement = CAR(nStepStatement);
00587       }else{
00588       nLoopStart = nStepStatement;
00589       nStepStatement = 0; /* there is no step statement */
00590       }
00591     }else{
00592     nLoopStart = 0;
00593     }
00594 
00595   if( OPCODE(nToStatement) != CMD_FORTO ){
00596     pEo->fStop = fStopSTOP; /* at least at runtime we recognize the mistake */
00597     RETURN;
00598     }
00599 
00600   nLoopEnd = pEo->CommandArray[nToStatement-1].Parameter.CommandArgument.next;/* come_back(FORTO) */
00601   nLoopEnd = pEo->CommandArray[nLoopEnd    -1].Parameter.CommandArgument.next;
00602   nLoopEnd = pEo->CommandArray[nLoopEnd-1].Parameter.CommandArgument.Argument.pNode;
00603   if( nLoopEnd )nLoopEnd = CDR(nLoopEnd);
00604 
00605   LetThisVariable = EVALUATELEFTVALUE(PARAMETERNODE);
00606   ASSERTOKE;
00607   DEREFERENCE(LetThisVariable)
00608 
00609   NEXTPARAMETER;
00610   vStartExpression = EVALUATEEXPRESSION(PARAMETERNODE);
00611   ASSERTOKE;
00612   /* is the start value if undef then do NOT enter the loop by definition */
00613   if( memory_IsUndef(vStartExpression) ){
00614     SETPROGRAMCOUNTER(nLoopEnd);
00615     RETURN;
00616     }
00617   vStartExpression = CONVERT2NUMERIC(vStartExpression);
00618 
00619   vEndExpression = EVALUATEEXPRESSION(pEo->CommandArray[nToStatement-1].Parameter.CommandArgument.Argument.pNode);
00620   ASSERTOKE;
00621   vEndExpression = CONVERT2NUMERIC(vEndExpression);
00622 
00623   if( nStepStatement && OPCODE(nStepStatement) == CMD_FORSTEP ){/* if there is STEP part */
00624     vStepExpression = EVALUATEEXPRESSION(pEo->CommandArray[nStepStatement-1].Parameter.CommandArgument.Argument.pNode);
00625     ASSERTOKE;
00626     vStepExpression = CONVERT2NUMERIC(vStepExpression);
00627     switch( TYPE(vStepExpression) ){
00628       case VTYPE_LONG:
00629         iGoingUp = 0 < LONGVALUE(vStepExpression);
00630         break;
00631       case VTYPE_DOUBLE:
00632         iGoingUp = 0 < DOUBLEVALUE(vStepExpression);
00633         break;
00634       default: fprintf(stderr,"Internal error 667"); exit(667);
00635       }
00636     }else{
00637     iGoingUp = 1; /* by default, if there is no step statement we are going upward */
00638     }
00639 
00640   /*
00641      NOW HERE WE HAVE TO DECIDE IF WE HAVE TO START THE LOOP OR JUST JUMP OVER IT 
00642    */
00643   bEnterTheLoop = 0;
00644   if( TYPE(vStartExpression) == VTYPE_LONG && TYPE(vEndExpression) == VTYPE_LONG )
00645     bEnterTheLoop =  iGoingUp ? LONGVALUE(vStartExpression) <= LONGVALUE(vEndExpression)
00646                               : LONGVALUE(vStartExpression) >= LONGVALUE(vEndExpression);
00647 
00648   if( TYPE(vStartExpression) == VTYPE_LONG && TYPE(vEndExpression) == VTYPE_DOUBLE )
00649     bEnterTheLoop = iGoingUp ? ((double)LONGVALUE(vStartExpression)) <= DOUBLEVALUE(vEndExpression)
00650                              : ((double)LONGVALUE(vStartExpression)) >= DOUBLEVALUE(vEndExpression);
00651 
00652   if( TYPE(vStartExpression) == VTYPE_DOUBLE && TYPE(vEndExpression) == VTYPE_DOUBLE )
00653     bEnterTheLoop = iGoingUp ? DOUBLEVALUE(vStartExpression) <= DOUBLEVALUE(vEndExpression)
00654                              : DOUBLEVALUE(vStartExpression) >= DOUBLEVALUE(vEndExpression);
00655 
00656   if( TYPE(vStartExpression) == VTYPE_DOUBLE && TYPE(vEndExpression) == VTYPE_LONG )
00657     bEnterTheLoop =  iGoingUp ? DOUBLEVALUE(vStartExpression) <= (double)LONGVALUE(vEndExpression)
00658                               : DOUBLEVALUE(vStartExpression) >= (double)LONGVALUE(vEndExpression);
00659 
00660   if( bEnterTheLoop ){
00661     /* assign the start value to the loop variable only if we enter the loop, be definition */
00662     memory_ReplaceVariable(pEo->pMo,LetThisVariable,vStartExpression,_pThisCommandMortals,1);
00663     /* start the loop */
00664     SETPROGRAMCOUNTER(nLoopStart);
00665     RETURN;
00666     }
00667   /* We do not even step into the loop */
00668   SETPROGRAMCOUNTER(nLoopEnd);
00669 
00670 #endif
00671 END
00672 
00673 COMMAND(FORTO)
00674 #if NOTIMP_FORTO
00675 NOTIMPLEMENTED;
00676 #else
00677 
00678   LEFTVALUE LetThisVariable;
00679   VARIABLE vEndExpression;
00680   VARIABLE vStepExpression;
00681   VARIABLE vNewLoopValue;
00682   NODE nStepStatement;
00683   NODE nForStatement;
00684   NODE nLoopStart,nLoopEnd;
00685   int iGoingUp;
00686   int bContinueLoop;
00687   long refcount;
00688   int iError;
00689 
00690   vEndExpression = EVALUATEEXPRESSION(PARAMETERNODE);
00691   ASSERTOKE;
00692   vEndExpression = CONVERT2NUMERIC(vEndExpression);
00693 
00694   NEXTPARAMETER;
00695   nForStatement = CAR(PARAMETERNODE);
00696   NEXTPARAMETER;
00697   nLoopEnd = CDR(PARAMETERNODE);
00698 
00699   LetThisVariable = EVALUATELEFTVALUE( pEo->CommandArray[nForStatement-1].Parameter.CommandArgument.Argument.pNode );
00700   ASSERTOKE;
00701   DEREFERENCE(LetThisVariable)
00702   if( memory_IsUndef( *LetThisVariable) ||
00703       ( TYPE(*LetThisVariable) != VTYPE_LONG && TYPE(*LetThisVariable) != VTYPE_DOUBLE )  ){
00704     /* this may happen when the user sets the loop variable to be undef inside the loop */
00705     vNewLoopValue = CONVERT2NUMERIC(*LetThisVariable);
00706     if( vNewLoopValue == NULL )ERROR(COMMAND_ERROR_MEMORY_LOW);
00707     iError = memory_ReplaceVariable(pEo->pMo,LetThisVariable,vNewLoopValue,_pThisCommandMortals,0);
00708     if( iError )ERROR(iError);
00709     }
00710   nStepStatement = CDR(pEo->ProgramCounter);
00711 
00712   if( nStepStatement ){
00713     if( pEo->CommandArray[CAR(nStepStatement)-1].OpCode == CMD_FORSTEP ){
00714       nLoopStart = CDR(nStepStatement);
00715       nStepStatement = CAR(nStepStatement);
00716       }else{
00717       nLoopStart = nStepStatement;
00718       nStepStatement = 0; /* there is no step statement */
00719       }
00720     }else{
00721     nLoopStart = 0;
00722     }
00723 
00724   if( nStepStatement && OPCODE(nStepStatement) == CMD_FORSTEP ){
00725     vStepExpression = EVALUATEEXPRESSION(pEo->CommandArray[nStepStatement-1].Parameter.CommandArgument.Argument.pNode);
00726     ASSERTOKE;
00727     vStepExpression = CONVERT2NUMERIC(vStepExpression);
00728     switch( TYPE(vStepExpression) ){
00729       case VTYPE_LONG:
00730         iGoingUp = 0 < LONGVALUE(vStepExpression);
00731         switch( TYPE( (*LetThisVariable) ) ){
00732           case VTYPE_LONG:
00733             LONGVALUE( (*LetThisVariable) )+= LONGVALUE(vStepExpression);
00734             break;
00735           case VTYPE_DOUBLE:
00736             DOUBLEVALUE( (*LetThisVariable) )+= (double)LONGVALUE(vStepExpression);
00737           break;
00738           }
00739       break;
00740     case VTYPE_DOUBLE:
00741       iGoingUp = 0 < DOUBLEVALUE(vStepExpression);
00742         switch( TYPE( (*LetThisVariable) ) ){
00743           case VTYPE_LONG:
00744             /* this is the only tricky case when the loop variable was LONG and the step value is double
00745                in this case we have to convert the loop variable to double */
00746             (*LetThisVariable)->vType = VTYPE_DOUBLE;
00747             (*LetThisVariable)->Value.dValue = (double)(*LetThisVariable)->Value.lValue;
00748             /* do not break; here, flo on to the next case */
00749           case VTYPE_DOUBLE:
00750             DOUBLEVALUE( (*LetThisVariable) )+= DOUBLEVALUE(vStepExpression);
00751           break;
00752           }
00753       break;
00754       }
00755     }else{/* if there is no STEP at all */
00756     iGoingUp = 1; /* by default, if there is no step statement we are going upward */
00757     switch( TYPE( (*LetThisVariable) ) ){
00758       case VTYPE_LONG:
00759         LONGVALUE( (*LetThisVariable) )+= 1;
00760       break;
00761       case VTYPE_DOUBLE:
00762         DOUBLEVALUE( (*LetThisVariable) )+= 1.0;
00763         break;
00764         }
00765     }
00766 
00767   if( iGoingUp ){
00768     bContinueLoop = (TYPE(*LetThisVariable) == VTYPE_LONG ? LONGVALUE(*LetThisVariable) : DOUBLEVALUE(*LetThisVariable) )
00769           <=
00770                     (TYPE(vEndExpression) == VTYPE_LONG ? LONGVALUE(vEndExpression) : DOUBLEVALUE(vEndExpression) );
00771     }else{
00772     bContinueLoop = (TYPE(*LetThisVariable) == VTYPE_LONG ? LONGVALUE(*LetThisVariable) : DOUBLEVALUE(*LetThisVariable) )
00773           >=
00774                     (TYPE(vEndExpression) == VTYPE_LONG ? LONGVALUE(vEndExpression) : DOUBLEVALUE(vEndExpression) );
00775     }
00776   if( bContinueLoop ){ 
00777        /* continue the loop */
00778        SETPROGRAMCOUNTER(nLoopStart);
00779        }else{
00780        /* finish the loop  */
00781        SETPROGRAMCOUNTER(nLoopEnd);
00782        }
00783 
00784 #endif
00785 END
00786 
00787 COMMAND(FORSTEP)
00788 #if NOTIMP_FORSTEP
00789 NOTIMPLEMENTED;
00790 #else
00791 
00792 
00793  /* we should never ever get here */
00794 
00795 #endif
00796 END
00797 
00798 COMMAND(NEXT)
00799 #if NOTIMP_NEXT
00800 NOTIMPLEMENTED;
00801 #else
00802   IDENTICAL_COMMAND(WEND)
00803 #endif
00804 END
00805 
00806 COMMAND(NEXTI)
00807 #if NOTIMP_NEXTI
00808 NOTIMPLEMENTED;
00809 #else
00810   NEXTPARAMETER; /* we have to step over the symbol */
00811   SETPROGRAMCOUNTER(PARAMETERNODE);
00812 #endif
00813 END

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