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