xref: /trunk/main/basic/source/comp/loops.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basic.hxx"
30 
31 #include "sbcomp.hxx"
32 
33 // Single-line IF und Multiline IF
34 
35 void SbiParser::If()
36 {
37     sal_uInt32 nEndLbl;
38     SbiToken eTok = NIL;
39     // Ende-Tokens ignorieren:
40     SbiExpression aCond( this );
41     aCond.Gen();
42     TestToken( THEN );
43     if( IsEoln( Next() ) )
44     {
45         // AB 13.5.1996: #27720# Am Ende jeden Blocks muss ein Jump zu ENDIF
46         // eingefuegt werden, damit bei ELSEIF nicht erneut die Bedingung
47         // ausgewertet wird. Die Tabelle nimmt alle Absprungstellen auf.
48 #define JMP_TABLE_SIZE 100
49         sal_uInt32 pnJmpToEndLbl[JMP_TABLE_SIZE];   // 100 ELSEIFs zulaessig
50         sal_uInt16 iJmp = 0;                        // aktueller Tabellen-Index
51 
52         // multiline IF
53         nEndLbl = aGen.Gen( _JUMPF, 0 );
54         eTok = Peek();
55         while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
56                 !bAbort && Parse() )
57         {
58             eTok = Peek();
59             if( IsEof() )
60             {
61                 Error( SbERR_BAD_BLOCK, IF ); bAbort = sal_True; return;
62             }
63         }
64         // ELSEIF?
65         while( eTok == ELSEIF )
66         {
67             // #27720# Bei erfolgreichem IF/ELSEIF auf ENDIF springen
68             if( iJmp >= JMP_TABLE_SIZE )
69             {
70                 Error( SbERR_PROG_TOO_LARGE );  bAbort = sal_True;  return;
71             }
72             pnJmpToEndLbl[iJmp++] = aGen.Gen( _JUMP, 0 );
73 
74             Next();
75             aGen.BackChain( nEndLbl );
76 
77             aGen.Statement();
78             SbiExpression* pCond = new SbiExpression( this );
79             pCond->Gen();
80             nEndLbl = aGen.Gen( _JUMPF, 0 );
81             delete pCond;
82             TestToken( THEN );
83             eTok = Peek();
84             while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
85                     !bAbort && Parse() )
86             {
87                 eTok = Peek();
88                 if( IsEof() )
89                 {
90                     Error( SbERR_BAD_BLOCK, ELSEIF );  bAbort = sal_True; return;
91                 }
92             }
93         }
94         if( eTok == ELSE )
95         {
96             Next();
97             sal_uInt32 nElseLbl = nEndLbl;
98             nEndLbl = aGen.Gen( _JUMP, 0 );
99             aGen.BackChain( nElseLbl );
100 
101             aGen.Statement();
102             StmntBlock( ENDIF );
103         }
104         else if( eTok == ENDIF )
105             Next();
106 
107         // #27720# Jmp-Tabelle abarbeiten
108         while( iJmp > 0 )
109         {
110             iJmp--;
111             aGen.BackChain( pnJmpToEndLbl[iJmp] );
112         }
113     }
114     else
115     {
116         // single line IF
117         bSingleLineIf = sal_True;
118         nEndLbl = aGen.Gen( _JUMPF, 0 );
119         Push( eCurTok );
120         while( !bAbort )
121         {
122             if( !Parse() ) break;
123             eTok = Peek();
124             if( eTok == ELSE || eTok == EOLN || eTok == REM )
125                 break;
126         }
127         if( eTok == ELSE )
128         {
129             Next();
130             sal_uInt32 nElseLbl = nEndLbl;
131             nEndLbl = aGen.Gen( _JUMP, 0 );
132             aGen.BackChain( nElseLbl );
133             while( !bAbort )
134             {
135                 if( !Parse() ) break;
136                 eTok = Peek();
137                 if( eTok == EOLN )
138                     break;
139             }
140         }
141         bSingleLineIf = sal_False;
142     }
143     aGen.BackChain( nEndLbl );
144 }
145 
146 // ELSE/ELSEIF/ENDIF ohne IF
147 
148 void SbiParser::NoIf()
149 {
150     Error( SbERR_NO_IF );
151     StmntBlock( ENDIF );
152 }
153 
154 // DO WHILE...LOOP
155 // DO ... LOOP WHILE
156 
157 void SbiParser::DoLoop()
158 {
159     sal_uInt32 nStartLbl = aGen.GetPC();
160     OpenBlock( DO );
161     SbiToken eTok = Next();
162     if( IsEoln( eTok ) )
163     {
164         // DO ... LOOP [WHILE|UNTIL expr]
165         StmntBlock( LOOP );
166         eTok = Next();
167         if( eTok == UNTIL || eTok == WHILE )
168         {
169             SbiExpression aExpr( this );
170             aExpr.Gen();
171             aGen.Gen( eTok == UNTIL ? _JUMPF : _JUMPT, nStartLbl );
172         } else
173             if (eTok == EOLN || eTok == REM)
174                 aGen.Gen (_JUMP, nStartLbl);
175             else
176                 Error( SbERR_EXPECTED, WHILE );
177     }
178     else
179     {
180         // DO [WHILE|UNTIL expr] ... LOOP
181         if( eTok == UNTIL || eTok == WHILE )
182         {
183             SbiExpression aCond( this );
184             aCond.Gen();
185         }
186         sal_uInt32 nEndLbl = aGen.Gen( eTok == UNTIL ? _JUMPT : _JUMPF, 0 );
187         StmntBlock( LOOP );
188         TestEoln();
189         aGen.Gen( _JUMP, nStartLbl );
190         aGen.BackChain( nEndLbl );
191     }
192     CloseBlock();
193 }
194 
195 // WHILE ... WEND
196 
197 void SbiParser::While()
198 {
199     SbiExpression aCond( this );
200     sal_uInt32 nStartLbl = aGen.GetPC();
201     aCond.Gen();
202     sal_uInt32 nEndLbl = aGen.Gen( _JUMPF, 0 );
203     StmntBlock( WEND );
204     aGen.Gen( _JUMP, nStartLbl );
205     aGen.BackChain( nEndLbl );
206 }
207 
208 // FOR var = expr TO expr STEP
209 
210 void SbiParser::For()
211 {
212     bool bForEach = ( Peek() == EACH );
213     if( bForEach )
214         Next();
215     SbiExpression aLvalue( this, SbOPERAND );
216     aLvalue.Gen();      // Variable auf dem Stack
217 
218     if( bForEach )
219     {
220         TestToken( _IN_ );
221         SbiExpression aCollExpr( this, SbOPERAND );
222         aCollExpr.Gen();    // Colletion var to for stack
223         TestEoln();
224         aGen.Gen( _INITFOREACH );
225     }
226     else
227     {
228         TestToken( EQ );
229         SbiExpression aStartExpr( this );
230         aStartExpr.Gen();   // Startausdruck auf dem Stack
231         TestToken( TO );
232         SbiExpression aStopExpr( this );
233         aStopExpr.Gen();    // Endausdruck auf dem Stack
234         if( Peek() == STEP )
235         {
236             Next();
237             SbiExpression aStepExpr( this );
238             aStepExpr.Gen();
239         }
240         else
241         {
242             SbiExpression aOne( this, 1, SbxINTEGER );
243             aOne.Gen();
244         }
245         TestEoln();
246         // Der Stack hat jetzt 4 Elemente: Variable, Start, Ende, Inkrement
247         // Startwert binden
248         aGen.Gen( _INITFOR );
249     }
250 
251     sal_uInt32 nLoop = aGen.GetPC();
252     // Test durchfuehren, evtl. Stack freigeben
253     sal_uInt32 nEndTarget = aGen.Gen( _TESTFOR, 0 );
254     OpenBlock( FOR );
255     StmntBlock( NEXT );
256     aGen.Gen( _NEXT );
257     aGen.Gen( _JUMP, nLoop );
258     // Kommen Variable nach NEXT?
259     if( Peek() == SYMBOL )
260     {
261         SbiExpression aVar( this, SbOPERAND );
262         if( aVar.GetRealVar() != aLvalue.GetRealVar() )
263             Error( SbERR_EXPECTED, aLvalue.GetRealVar()->GetName() );
264     }
265     aGen.BackChain( nEndTarget );
266     CloseBlock();
267 }
268 
269 // WITH .. END WITH
270 
271 void SbiParser::With()
272 {
273     SbiExpression aVar( this, SbOPERAND );
274 
275     // Letzten Knoten in der Objekt-Kette ueberpruefen
276     SbiExprNode *pNode = aVar.GetExprNode()->GetRealNode();
277     SbiSymDef* pDef = pNode->GetVar();
278     // Variant, AB 27.6.1997, #41090: bzw. empty -> mu� Object sein
279     if( pDef->GetType() == SbxVARIANT || pDef->GetType() == SbxEMPTY )
280         pDef->SetType( SbxOBJECT );
281     else if( pDef->GetType() != SbxOBJECT )
282         Error( SbERR_NEEDS_OBJECT );
283 
284     // Knoten auch auf SbxOBJECT setzen, damit spaeter Gen() klappt
285     pNode->SetType( SbxOBJECT );
286 
287     OpenBlock( NIL, aVar.GetExprNode() );
288     StmntBlock( ENDWITH );
289     CloseBlock();
290 }
291 
292 // LOOP/NEXT/WEND ohne Konstrukt
293 
294 void SbiParser::BadBlock()
295 {
296     if( eEndTok )
297         Error( SbERR_BAD_BLOCK, eEndTok );
298     else
299         Error( SbERR_BAD_BLOCK, "Loop/Next/Wend" );
300 }
301 
302 // On expr Goto/Gosub n,n,n...
303 
304 void SbiParser::OnGoto()
305 {
306     SbiExpression aCond( this );
307     aCond.Gen();
308     sal_uInt32 nLabelsTarget = aGen.Gen( _ONJUMP, 0 );
309     SbiToken eTok = Next();
310     if( eTok != GOTO && eTok != GOSUB )
311     {
312         Error( SbERR_EXPECTED, "GoTo/GoSub" );
313         eTok = GOTO;
314     }
315     // Label-Tabelle einlesen:
316     sal_uInt32 nLbl = 0;
317     do
318     {
319         SbiToken eTok2 = NIL;
320         eTok2 = Next(); // Label holen
321         if( MayBeLabel() )
322         {
323             sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
324             aGen.Gen( _JUMP, nOff );
325             nLbl++;
326         }
327         else Error( SbERR_LABEL_EXPECTED );
328     }
329     while( !bAbort && TestComma() );
330     if( eTok == GOSUB )
331         nLbl |= 0x8000;
332     aGen.Patch( nLabelsTarget, nLbl );
333 }
334 
335 // GOTO/GOSUB
336 
337 void SbiParser::Goto()
338 {
339     SbiOpcode eOp = eCurTok == GOTO ? _JUMP : _GOSUB;
340     Next();
341     if( MayBeLabel() )
342     {
343         sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
344         aGen.Gen( eOp, nOff );
345     }
346     else Error( SbERR_LABEL_EXPECTED );
347 }
348 
349 // RETURN [label]
350 
351 void SbiParser::Return()
352 {
353     Next();
354     if( MayBeLabel() )
355     {
356         sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
357         aGen.Gen( _RETURN, nOff );
358     }
359     else aGen.Gen( _RETURN, 0 );
360 }
361 
362 // SELECT CASE
363 
364 void SbiParser::Select()
365 {
366     TestToken( CASE );
367     SbiExpression aCase( this );
368     SbiToken eTok = NIL;
369     aCase.Gen();
370     aGen.Gen( _CASE );
371     TestEoln();
372     sal_uInt32 nNextTarget = 0;
373     sal_uInt32 nDoneTarget = 0;
374     sal_Bool bElse = sal_False;
375     // Die Cases einlesen:
376     while( !bAbort )
377     {
378         eTok = Next();
379         if( eTok == CASE )
380         {
381             if( nNextTarget )
382                 aGen.BackChain( nNextTarget ), nNextTarget = 0;
383             aGen.Statement();
384             // Jeden Case einlesen
385             sal_Bool bDone = sal_False;
386             sal_uInt32 nTrueTarget = 0;
387             if( Peek() == ELSE )
388             {
389                 // CASE ELSE
390                 Next();
391                 bElse = sal_True;
392             }
393             else while( !bDone )
394             {
395                 if( bElse )
396                     Error( SbERR_SYNTAX );
397                 SbiToken eTok2 = Peek();
398                 if( eTok2 == IS || ( eTok2 >= EQ && eTok2 <= GE ) )
399                 {   // CASE [IS] operator expr
400                     if( eTok2 == IS )
401                         Next();
402                     eTok2 = Peek();
403                     if( eTok2 < EQ || eTok2 > GE )
404                         Error( SbERR_SYNTAX );
405                     else Next();
406                     SbiExpression aCompare( this );
407                     aCompare.Gen();
408                     nTrueTarget = aGen.Gen(
409                         _CASEIS, nTrueTarget,
410                         sal::static_int_cast< sal_uInt16 >(
411                             SbxEQ + ( eTok2 - EQ ) ) );
412                 }
413                 else
414                 {   // CASE expr | expr TO expr
415                     SbiExpression aCase1( this );
416                     aCase1.Gen();
417                     if( Peek() == TO )
418                     {
419                         // CASE a TO b
420                         Next();
421                         SbiExpression aCase2( this );
422                         aCase2.Gen();
423                         nTrueTarget = aGen.Gen( _CASETO, nTrueTarget );
424                     }
425                     else
426                         // CASE a
427                         nTrueTarget = aGen.Gen( _CASEIS, nTrueTarget, SbxEQ );
428 
429                 }
430                 if( Peek() == COMMA ) Next();
431                 else TestEoln(), bDone = sal_True;
432             }
433             // Alle Cases abgearbeitet
434             if( !bElse )
435             {
436                 nNextTarget = aGen.Gen( _JUMP, nNextTarget );
437                 aGen.BackChain( nTrueTarget );
438             }
439             // den Statement-Rumpf bauen
440             while( !bAbort )
441             {
442                 eTok = Peek();
443                 if( eTok == CASE || eTok == ENDSELECT )
444                     break;
445                 if( !Parse() ) goto done;
446                 eTok = Peek();
447                 if( eTok == CASE || eTok == ENDSELECT )
448                     break;
449             }
450             if( !bElse )
451                 nDoneTarget = aGen.Gen( _JUMP, nDoneTarget );
452         }
453         else if( !IsEoln( eTok ) )
454             break;
455     }
456 done:
457     if( eTok != ENDSELECT )
458         Error( SbERR_EXPECTED, ENDSELECT );
459     if( nNextTarget )
460         aGen.BackChain( nNextTarget );
461     aGen.BackChain( nDoneTarget );
462     aGen.Gen( _ENDCASE );
463 }
464 
465 // ON Error/Variable
466 
467 #ifdef _MSC_VER
468 #pragma optimize("",off)
469 #endif
470 
471 void SbiParser::On()
472 {
473     SbiToken eTok = Peek();
474     String aString = SbiTokenizer::Symbol(eTok);
475     if (aString.EqualsIgnoreCaseAscii("ERROR"))
476     //if (!aString.ICompare("ERROR"))
477         eTok = _ERROR_; // Error kommt als SYMBOL
478     if( eTok != _ERROR_ && eTok != LOCAL ) OnGoto();
479     else
480     {
481         if( eTok == LOCAL ) Next();
482         Next (); // Kein TestToken mehr, da es sonst einen Fehler gibt
483 
484         Next(); // Token nach Error holen
485         if( eCurTok == GOTO )
486         {
487             // ON ERROR GOTO label|0
488             Next();
489             bool bError_ = false;
490             if( MayBeLabel() )
491             {
492                 if( eCurTok == NUMBER && !nVal )
493                     aGen.Gen( _STDERROR );
494                 else
495                 {
496                     sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
497                     aGen.Gen( _ERRHDL, nOff );
498                 }
499             }
500             else if( eCurTok == MINUS )
501             {
502                 Next();
503                 if( eCurTok == NUMBER && nVal == 1 )
504                     aGen.Gen( _STDERROR );
505                 else
506                     bError_ = true;
507             }
508             if( bError_ )
509                 Error( SbERR_LABEL_EXPECTED );
510         }
511         else if( eCurTok == RESUME )
512         {
513             TestToken( NEXT );
514             aGen.Gen( _NOERROR );
515         }
516         else Error( SbERR_EXPECTED, "GoTo/Resume" );
517     }
518 }
519 
520 #ifdef _MSC_VER
521 #pragma optimize("",off)
522 #endif
523 
524 // RESUME [0]|NEXT|label
525 
526 void SbiParser::Resume()
527 {
528     sal_uInt32 nLbl;
529 
530     switch( Next() )
531     {
532         case EOS:
533         case EOLN:
534             aGen.Gen( _RESUME, 0 );
535             break;
536         case NEXT:
537             aGen.Gen( _RESUME, 1 );
538             Next();
539             break;
540         case NUMBER:
541             if( !nVal )
542             {
543                 aGen.Gen( _RESUME, 0 );
544                 break;
545             } // fall thru
546         case SYMBOL:
547             if( MayBeLabel() )
548             {
549                 nLbl = pProc->GetLabels().Reference( aSym );
550                 aGen.Gen( _RESUME, nLbl );
551                 Next();
552                 break;
553             } // fall thru
554         default:
555             Error( SbERR_LABEL_EXPECTED );
556     }
557 }
558 
559