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