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