Version 4.0.2.1, tag libreoffice-4.0.2.1
[LibreOffice.git] / basic / source / comp / loops.cxx
blobac536484e2c84821b36541a93e905f715f43bee6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "sbcomp.hxx"
23 // Single-line IF and Multiline IF
25 void SbiParser::If()
27 sal_uInt32 nEndLbl;
28 SbiToken eTok = NIL;
29 // ignore end-tokens
30 SbiExpression aCond( this );
31 aCond.Gen();
32 TestToken( THEN );
33 if( IsEoln( Next() ) )
35 // At the end of each block a jump to ENDIF must be inserted,
36 // so that the condition is not evaluated again at ELSEIF.
37 // The table collects all jump points.
38 #define JMP_TABLE_SIZE 100
39 sal_uInt32 pnJmpToEndLbl[JMP_TABLE_SIZE]; // 100 ELSEIFs allowed
40 sal_uInt16 iJmp = 0; // current table index
42 // multiline IF
43 nEndLbl = aGen.Gen( _JUMPF, 0 );
44 eTok = Peek();
45 while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
46 !bAbort && Parse() )
48 eTok = Peek();
49 if( IsEof() )
51 Error( SbERR_BAD_BLOCK, IF ); bAbort = sal_True; return;
54 while( eTok == ELSEIF )
56 // jump to ENDIF in case of a successful IF/ELSEIF
57 if( iJmp >= JMP_TABLE_SIZE )
59 Error( SbERR_PROG_TOO_LARGE ); bAbort = sal_True; return;
61 pnJmpToEndLbl[iJmp++] = aGen.Gen( _JUMP, 0 );
63 Next();
64 aGen.BackChain( nEndLbl );
66 aGen.Statement();
67 SbiExpression* pCond = new SbiExpression( this );
68 pCond->Gen();
69 nEndLbl = aGen.Gen( _JUMPF, 0 );
70 delete pCond;
71 TestToken( THEN );
72 eTok = Peek();
73 while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
74 !bAbort && Parse() )
76 eTok = Peek();
77 if( IsEof() )
79 Error( SbERR_BAD_BLOCK, ELSEIF ); bAbort = sal_True; return;
83 if( eTok == ELSE )
85 Next();
86 sal_uInt32 nElseLbl = nEndLbl;
87 nEndLbl = aGen.Gen( _JUMP, 0 );
88 aGen.BackChain( nElseLbl );
90 aGen.Statement();
91 StmntBlock( ENDIF );
93 else if( eTok == ENDIF )
94 Next();
97 while( iJmp > 0 )
99 iJmp--;
100 aGen.BackChain( pnJmpToEndLbl[iJmp] );
103 else
105 // single line IF
106 bSingleLineIf = true;
107 nEndLbl = aGen.Gen( _JUMPF, 0 );
108 Push( eCurTok );
109 while( !bAbort )
111 if( !Parse() ) break;
112 eTok = Peek();
113 if( eTok == ELSE || eTok == EOLN || eTok == REM )
114 break;
116 if( eTok == ELSE )
118 Next();
119 sal_uInt32 nElseLbl = nEndLbl;
120 nEndLbl = aGen.Gen( _JUMP, 0 );
121 aGen.BackChain( nElseLbl );
122 while( !bAbort )
124 if( !Parse() ) break;
125 eTok = Peek();
126 if( eTok == EOLN )
127 break;
130 bSingleLineIf = false;
132 aGen.BackChain( nEndLbl );
135 // ELSE/ELSEIF/ENDIF without IF
137 void SbiParser::NoIf()
139 Error( SbERR_NO_IF );
140 StmntBlock( ENDIF );
143 // DO WHILE...LOOP
144 // DO ... LOOP WHILE
146 void SbiParser::DoLoop()
148 sal_uInt32 nStartLbl = aGen.GetPC();
149 OpenBlock( DO );
150 SbiToken eTok = Next();
151 if( IsEoln( eTok ) )
153 // DO ... LOOP [WHILE|UNTIL expr]
154 StmntBlock( LOOP );
155 eTok = Next();
156 if( eTok == UNTIL || eTok == WHILE )
158 SbiExpression aExpr( this );
159 aExpr.Gen();
160 aGen.Gen( eTok == UNTIL ? _JUMPF : _JUMPT, nStartLbl );
161 } else
162 if (eTok == EOLN || eTok == REM)
163 aGen.Gen (_JUMP, nStartLbl);
164 else
165 Error( SbERR_EXPECTED, WHILE );
167 else
169 // DO [WHILE|UNTIL expr] ... LOOP
170 if( eTok == UNTIL || eTok == WHILE )
172 SbiExpression aCond( this );
173 aCond.Gen();
175 sal_uInt32 nEndLbl = aGen.Gen( eTok == UNTIL ? _JUMPT : _JUMPF, 0 );
176 StmntBlock( LOOP );
177 TestEoln();
178 aGen.Gen( _JUMP, nStartLbl );
179 aGen.BackChain( nEndLbl );
181 CloseBlock();
184 // WHILE ... WEND
186 void SbiParser::While()
188 SbiExpression aCond( this );
189 sal_uInt32 nStartLbl = aGen.GetPC();
190 aCond.Gen();
191 sal_uInt32 nEndLbl = aGen.Gen( _JUMPF, 0 );
192 StmntBlock( WEND );
193 aGen.Gen( _JUMP, nStartLbl );
194 aGen.BackChain( nEndLbl );
197 // FOR var = expr TO expr STEP
199 void SbiParser::For()
201 bool bForEach = ( Peek() == EACH );
202 if( bForEach )
203 Next();
204 SbiExpression aLvalue( this, SbOPERAND );
205 aLvalue.Gen(); // variable on the Stack
207 if( bForEach )
209 TestToken( _IN_ );
210 SbiExpression aCollExpr( this, SbOPERAND );
211 aCollExpr.Gen(); // Colletion var to for stack
212 TestEoln();
213 aGen.Gen( _INITFOREACH );
215 else
217 TestToken( EQ );
218 SbiExpression aStartExpr( this );
219 aStartExpr.Gen();
220 TestToken( TO );
221 SbiExpression aStopExpr( this );
222 aStopExpr.Gen();
223 if( Peek() == STEP )
225 Next();
226 SbiExpression aStepExpr( this );
227 aStepExpr.Gen();
229 else
231 SbiExpression aOne( this, 1, SbxINTEGER );
232 aOne.Gen();
234 TestEoln();
235 // The stack has all 4 elements now: variable, start, end, increment
236 // bind start value
237 aGen.Gen( _INITFOR );
240 sal_uInt32 nLoop = aGen.GetPC();
241 // do tests, maybe free the stack
242 sal_uInt32 nEndTarget = aGen.Gen( _TESTFOR, 0 );
243 OpenBlock( FOR );
244 StmntBlock( NEXT );
245 aGen.Gen( _NEXT );
246 aGen.Gen( _JUMP, nLoop );
247 // are there variables after NEXT?
248 if( Peek() == SYMBOL )
250 SbiExpression aVar( this, SbOPERAND );
251 if( aVar.GetRealVar() != aLvalue.GetRealVar() )
252 Error( SbERR_EXPECTED, aLvalue.GetRealVar()->GetName() );
254 aGen.BackChain( nEndTarget );
255 CloseBlock();
258 // WITH .. END WITH
260 void SbiParser::With()
262 SbiExpression aVar( this, SbOPERAND );
264 SbiExprNode *pNode = aVar.GetExprNode()->GetRealNode();
265 SbiSymDef* pDef = pNode->GetVar();
266 // Variant, from 27.6.1997, #41090: empty -> must be Object
267 if( pDef->GetType() == SbxVARIANT || pDef->GetType() == SbxEMPTY )
268 pDef->SetType( SbxOBJECT );
269 else if( pDef->GetType() != SbxOBJECT )
270 Error( SbERR_NEEDS_OBJECT );
273 pNode->SetType( SbxOBJECT );
275 OpenBlock( NIL, aVar.GetExprNode() );
276 StmntBlock( ENDWITH );
277 CloseBlock();
280 // LOOP/NEXT/WEND without construct
282 void SbiParser::BadBlock()
284 if( eEndTok )
285 Error( SbERR_BAD_BLOCK, eEndTok );
286 else
287 Error( SbERR_BAD_BLOCK, "Loop/Next/Wend" );
290 // On expr Goto/Gosub n,n,n...
292 void SbiParser::OnGoto()
294 SbiExpression aCond( this );
295 aCond.Gen();
296 sal_uInt32 nLabelsTarget = aGen.Gen( _ONJUMP, 0 );
297 SbiToken eTok = Next();
298 if( eTok != GOTO && eTok != GOSUB )
300 Error( SbERR_EXPECTED, "GoTo/GoSub" );
301 eTok = GOTO;
304 sal_uInt32 nLbl = 0;
307 Next(); // get label
308 if( MayBeLabel() )
310 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
311 aGen.Gen( _JUMP, nOff );
312 nLbl++;
314 else Error( SbERR_LABEL_EXPECTED );
316 while( !bAbort && TestComma() );
317 if( eTok == GOSUB )
318 nLbl |= 0x8000;
319 aGen.Patch( nLabelsTarget, nLbl );
322 // GOTO/GOSUB
324 void SbiParser::Goto()
326 SbiOpcode eOp = eCurTok == GOTO ? _JUMP : _GOSUB;
327 Next();
328 if( MayBeLabel() )
330 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
331 aGen.Gen( eOp, nOff );
333 else Error( SbERR_LABEL_EXPECTED );
336 // RETURN [label]
338 void SbiParser::Return()
340 Next();
341 if( MayBeLabel() )
343 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
344 aGen.Gen( _RETURN, nOff );
346 else aGen.Gen( _RETURN, 0 );
349 // SELECT CASE
351 void SbiParser::Select()
353 TestToken( CASE );
354 SbiExpression aCase( this );
355 SbiToken eTok = NIL;
356 aCase.Gen();
357 aGen.Gen( _CASE );
358 TestEoln();
359 sal_uInt32 nNextTarget = 0;
360 sal_uInt32 nDoneTarget = 0;
361 sal_Bool bElse = sal_False;
363 while( !bAbort )
365 eTok = Next();
366 if( eTok == CASE )
368 if( nNextTarget )
369 aGen.BackChain( nNextTarget ), nNextTarget = 0;
370 aGen.Statement();
372 sal_Bool bDone = sal_False;
373 sal_uInt32 nTrueTarget = 0;
374 if( Peek() == ELSE )
376 // CASE ELSE
377 Next();
378 bElse = sal_True;
380 else while( !bDone )
382 if( bElse )
383 Error( SbERR_SYNTAX );
384 SbiToken eTok2 = Peek();
385 if( eTok2 == IS || ( eTok2 >= EQ && eTok2 <= GE ) )
386 { // CASE [IS] operator expr
387 if( eTok2 == IS )
388 Next();
389 eTok2 = Peek();
390 if( eTok2 < EQ || eTok2 > GE )
391 Error( SbERR_SYNTAX );
392 else Next();
393 SbiExpression aCompare( this );
394 aCompare.Gen();
395 nTrueTarget = aGen.Gen(
396 _CASEIS, nTrueTarget,
397 sal::static_int_cast< sal_uInt16 >(
398 SbxEQ + ( eTok2 - EQ ) ) );
400 else
401 { // CASE expr | expr TO expr
402 SbiExpression aCase1( this );
403 aCase1.Gen();
404 if( Peek() == TO )
406 // CASE a TO b
407 Next();
408 SbiExpression aCase2( this );
409 aCase2.Gen();
410 nTrueTarget = aGen.Gen( _CASETO, nTrueTarget );
412 else
413 // CASE a
414 nTrueTarget = aGen.Gen( _CASEIS, nTrueTarget, SbxEQ );
417 if( Peek() == COMMA ) Next();
418 else TestEoln(), bDone = sal_True;
421 if( !bElse )
423 nNextTarget = aGen.Gen( _JUMP, nNextTarget );
424 aGen.BackChain( nTrueTarget );
426 // build the statement body
427 while( !bAbort )
429 eTok = Peek();
430 if( eTok == CASE || eTok == ENDSELECT )
431 break;
432 if( !Parse() ) goto done;
433 eTok = Peek();
434 if( eTok == CASE || eTok == ENDSELECT )
435 break;
437 if( !bElse )
438 nDoneTarget = aGen.Gen( _JUMP, nDoneTarget );
440 else if( !IsEoln( eTok ) )
441 break;
443 done:
444 if( eTok != ENDSELECT )
445 Error( SbERR_EXPECTED, ENDSELECT );
446 if( nNextTarget )
447 aGen.BackChain( nNextTarget );
448 aGen.BackChain( nDoneTarget );
449 aGen.Gen( _ENDCASE );
452 // ON Error/Variable
454 #ifdef _MSC_VER
455 #pragma optimize("",off)
456 #endif
458 void SbiParser::On()
460 SbiToken eTok = Peek();
461 OUString aString = SbiTokenizer::Symbol(eTok);
462 if (aString.equalsIgnoreAsciiCase("ERROR"))
464 eTok = _ERROR_; // Error comes as SYMBOL
466 if( eTok != _ERROR_ && eTok != LOCAL )
468 OnGoto();
470 else
472 if( eTok == LOCAL )
474 Next();
476 Next (); // no more TestToken, as there'd be an error otherwise
478 Next(); // get token after error
479 if( eCurTok == GOTO )
481 // ON ERROR GOTO label|0
482 Next();
483 bool bError_ = false;
484 if( MayBeLabel() )
486 if( eCurTok == NUMBER && !nVal )
488 aGen.Gen( _STDERROR );
490 else
492 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
493 aGen.Gen( _ERRHDL, nOff );
496 else if( eCurTok == MINUS )
498 Next();
499 if( eCurTok == NUMBER && nVal == 1 )
501 aGen.Gen( _STDERROR );
503 else
505 bError_ = true;
508 if( bError_ )
510 Error( SbERR_LABEL_EXPECTED );
513 else if( eCurTok == RESUME )
515 TestToken( NEXT );
516 aGen.Gen( _NOERROR );
518 else Error( SbERR_EXPECTED, "GoTo/Resume" );
522 #ifdef _MSC_VER
523 #pragma optimize("",off)
524 #endif
526 // RESUME [0]|NEXT|label
528 void SbiParser::Resume()
530 sal_uInt32 nLbl;
532 switch( Next() )
534 case EOS:
535 case EOLN:
536 aGen.Gen( _RESUME, 0 );
537 break;
538 case NEXT:
539 aGen.Gen( _RESUME, 1 );
540 Next();
541 break;
542 case NUMBER:
543 if( !nVal )
545 aGen.Gen( _RESUME, 0 );
546 break;
547 } // fall thru
548 case SYMBOL:
549 if( MayBeLabel() )
551 nLbl = pProc->GetLabels().Reference( aSym );
552 aGen.Gen( _RESUME, nLbl );
553 Next();
554 break;
555 } // fall thru
556 default:
557 Error( SbERR_LABEL_EXPECTED );
561 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */