bump product version to 5.0.4.1
[LibreOffice.git] / basic / source / comp / loops.cxx
blobb545ae7e69aeb2d15ef0b26a51fb0d20a9a2ad3c
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 "parser.hxx"
22 #include <boost/scoped_ptr.hpp>
24 // Single-line IF and Multiline IF
26 void SbiParser::If()
28 sal_uInt32 nEndLbl;
29 SbiToken eTok = NIL;
30 // ignore end-tokens
31 SbiExpression aCond( this );
32 aCond.Gen();
33 TestToken( THEN );
34 if( IsEoln( Next() ) )
36 // At the end of each block a jump to ENDIF must be inserted,
37 // so that the condition is not evaluated again at ELSEIF.
38 // The table collects all jump points.
39 #define JMP_TABLE_SIZE 100
40 sal_uInt32 pnJmpToEndLbl[JMP_TABLE_SIZE]; // 100 ELSEIFs allowed
41 sal_uInt16 iJmp = 0; // current table index
43 // multiline IF
44 nEndLbl = aGen.Gen( _JUMPF, 0 );
45 eTok = Peek();
46 while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
47 !bAbort && Parse() )
49 eTok = Peek();
50 if( IsEof() )
52 Error( SbERR_BAD_BLOCK, IF ); bAbort = true; return;
55 while( eTok == ELSEIF )
57 // jump to ENDIF in case of a successful IF/ELSEIF
58 if( iJmp >= JMP_TABLE_SIZE )
60 Error( SbERR_PROG_TOO_LARGE ); bAbort = true; return;
62 pnJmpToEndLbl[iJmp++] = aGen.Gen( _JUMP, 0 );
64 Next();
65 aGen.BackChain( nEndLbl );
67 aGen.Statement();
68 boost::scoped_ptr<SbiExpression> pCond(new SbiExpression( this ));
69 pCond->Gen();
70 nEndLbl = aGen.Gen( _JUMPF, 0 );
71 pCond.reset();
72 TestToken( THEN );
73 eTok = Peek();
74 while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
75 !bAbort && Parse() )
77 eTok = Peek();
78 if( IsEof() )
80 Error( SbERR_BAD_BLOCK, ELSEIF ); bAbort = true; return;
84 if( eTok == ELSE )
86 Next();
87 sal_uInt32 nElseLbl = nEndLbl;
88 nEndLbl = aGen.Gen( _JUMP, 0 );
89 aGen.BackChain( nElseLbl );
91 aGen.Statement();
92 StmntBlock( ENDIF );
94 else if( eTok == ENDIF )
95 Next();
98 while( iJmp > 0 )
100 iJmp--;
101 aGen.BackChain( pnJmpToEndLbl[iJmp] );
104 else
106 // single line IF
107 bSingleLineIf = true;
108 nEndLbl = aGen.Gen( _JUMPF, 0 );
109 Push( eCurTok );
110 while( !bAbort )
112 if( !Parse() ) break;
113 eTok = Peek();
114 if( eTok == ELSE || eTok == EOLN || eTok == REM )
115 break;
117 if( eTok == ELSE )
119 Next();
120 sal_uInt32 nElseLbl = nEndLbl;
121 nEndLbl = aGen.Gen( _JUMP, 0 );
122 aGen.BackChain( nElseLbl );
123 while( !bAbort )
125 if( !Parse() ) break;
126 eTok = Peek();
127 if( eTok == EOLN )
128 break;
131 bSingleLineIf = false;
133 aGen.BackChain( nEndLbl );
136 // ELSE/ELSEIF/ENDIF without IF
138 void SbiParser::NoIf()
140 Error( SbERR_NO_IF );
141 StmntBlock( ENDIF );
144 // DO WHILE...LOOP
145 // DO ... LOOP WHILE
147 void SbiParser::DoLoop()
149 sal_uInt32 nStartLbl = aGen.GetPC();
150 OpenBlock( DO );
151 SbiToken eTok = Next();
152 if( IsEoln( eTok ) )
154 // DO ... LOOP [WHILE|UNTIL expr]
155 StmntBlock( LOOP );
156 eTok = Next();
157 if( eTok == UNTIL || eTok == WHILE )
159 SbiExpression aExpr( this );
160 aExpr.Gen();
161 aGen.Gen( eTok == UNTIL ? _JUMPF : _JUMPT, nStartLbl );
162 } else
163 if (eTok == EOLN || eTok == REM)
164 aGen.Gen (_JUMP, nStartLbl);
165 else
166 Error( SbERR_EXPECTED, WHILE );
168 else
170 // DO [WHILE|UNTIL expr] ... LOOP
171 if( eTok == UNTIL || eTok == WHILE )
173 SbiExpression aCond( this );
174 aCond.Gen();
176 sal_uInt32 nEndLbl = aGen.Gen( eTok == UNTIL ? _JUMPT : _JUMPF, 0 );
177 StmntBlock( LOOP );
178 TestEoln();
179 aGen.Gen( _JUMP, nStartLbl );
180 aGen.BackChain( nEndLbl );
182 CloseBlock();
185 // WHILE ... WEND
187 void SbiParser::While()
189 SbiExpression aCond( this );
190 sal_uInt32 nStartLbl = aGen.GetPC();
191 aCond.Gen();
192 sal_uInt32 nEndLbl = aGen.Gen( _JUMPF, 0 );
193 StmntBlock( WEND );
194 aGen.Gen( _JUMP, nStartLbl );
195 aGen.BackChain( nEndLbl );
198 // FOR var = expr TO expr STEP
200 void SbiParser::For()
202 bool bForEach = ( Peek() == EACH );
203 if( bForEach )
204 Next();
205 SbiExpression aLvalue( this, SbOPERAND );
206 aLvalue.Gen(); // variable on the Stack
208 if( bForEach )
210 TestToken( _IN_ );
211 SbiExpression aCollExpr( this, SbOPERAND );
212 aCollExpr.Gen(); // Colletion var to for stack
213 TestEoln();
214 aGen.Gen( _INITFOREACH );
216 else
218 TestToken( EQ );
219 SbiExpression aStartExpr( this );
220 aStartExpr.Gen();
221 TestToken( TO );
222 SbiExpression aStopExpr( this );
223 aStopExpr.Gen();
224 if( Peek() == STEP )
226 Next();
227 SbiExpression aStepExpr( this );
228 aStepExpr.Gen();
230 else
232 SbiExpression aOne( this, 1, SbxINTEGER );
233 aOne.Gen();
235 TestEoln();
236 // The stack has all 4 elements now: variable, start, end, increment
237 // bind start value
238 aGen.Gen( _INITFOR );
241 sal_uInt32 nLoop = aGen.GetPC();
242 // do tests, maybe free the stack
243 sal_uInt32 nEndTarget = aGen.Gen( _TESTFOR, 0 );
244 OpenBlock( FOR );
245 StmntBlock( NEXT );
246 aGen.Gen( _NEXT );
247 aGen.Gen( _JUMP, nLoop );
248 // are there variables after NEXT?
249 if( Peek() == SYMBOL )
251 SbiExpression aVar( this, SbOPERAND );
252 if( aVar.GetRealVar() != aLvalue.GetRealVar() )
253 Error( SbERR_EXPECTED, aLvalue.GetRealVar()->GetName() );
255 aGen.BackChain( nEndTarget );
256 CloseBlock();
259 // WITH .. END WITH
261 void SbiParser::With()
263 SbiExpression aVar( this, SbOPERAND );
265 SbiExprNode *pNode = aVar.GetExprNode()->GetRealNode();
266 SbiSymDef* pDef = pNode->GetVar();
267 // Variant, from 27.6.1997, #41090: empty -> must be Object
268 if( pDef->GetType() == SbxVARIANT || pDef->GetType() == SbxEMPTY )
269 pDef->SetType( SbxOBJECT );
270 else if( pDef->GetType() != SbxOBJECT )
271 Error( SbERR_NEEDS_OBJECT );
274 pNode->SetType( SbxOBJECT );
276 OpenBlock( NIL, aVar.GetExprNode() );
277 StmntBlock( ENDWITH );
278 CloseBlock();
281 // LOOP/NEXT/WEND without construct
283 void SbiParser::BadBlock()
285 if( eEndTok )
286 Error( SbERR_BAD_BLOCK, eEndTok );
287 else
288 Error( SbERR_BAD_BLOCK, "Loop/Next/Wend" );
291 // On expr Goto/Gosub n,n,n...
293 void SbiParser::OnGoto()
295 SbiExpression aCond( this );
296 aCond.Gen();
297 sal_uInt32 nLabelsTarget = aGen.Gen( _ONJUMP, 0 );
298 SbiToken eTok = Next();
299 if( eTok != GOTO && eTok != GOSUB )
301 Error( SbERR_EXPECTED, "GoTo/GoSub" );
302 eTok = GOTO;
305 sal_uInt32 nLbl = 0;
308 Next(); // get label
309 if( MayBeLabel() )
311 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
312 aGen.Gen( _JUMP, nOff );
313 nLbl++;
315 else Error( SbERR_LABEL_EXPECTED );
317 while( !bAbort && TestComma() );
318 if( eTok == GOSUB )
319 nLbl |= 0x8000;
320 aGen.Patch( nLabelsTarget, nLbl );
323 // GOTO/GOSUB
325 void SbiParser::Goto()
327 SbiOpcode eOp = eCurTok == GOTO ? _JUMP : _GOSUB;
328 Next();
329 if( MayBeLabel() )
331 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
332 aGen.Gen( eOp, nOff );
334 else Error( SbERR_LABEL_EXPECTED );
337 // RETURN [label]
339 void SbiParser::Return()
341 Next();
342 if( MayBeLabel() )
344 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
345 aGen.Gen( _RETURN, nOff );
347 else aGen.Gen( _RETURN, 0 );
350 // SELECT CASE
352 void SbiParser::Select()
354 TestToken( CASE );
355 SbiExpression aCase( this );
356 SbiToken eTok = NIL;
357 aCase.Gen();
358 aGen.Gen( _CASE );
359 TestEoln();
360 sal_uInt32 nNextTarget = 0;
361 sal_uInt32 nDoneTarget = 0;
362 bool bElse = false;
364 while( !bAbort )
366 eTok = Next();
367 if( eTok == CASE )
369 if( nNextTarget )
370 aGen.BackChain( nNextTarget ), nNextTarget = 0;
371 aGen.Statement();
373 bool bDone = false;
374 sal_uInt32 nTrueTarget = 0;
375 if( Peek() == ELSE )
377 // CASE ELSE
378 Next();
379 bElse = true;
381 else while( !bDone )
383 if( bElse )
384 Error( SbERR_SYNTAX );
385 SbiToken eTok2 = Peek();
386 if( eTok2 == IS || ( eTok2 >= EQ && eTok2 <= GE ) )
387 { // CASE [IS] operator expr
388 if( eTok2 == IS )
389 Next();
390 eTok2 = Peek();
391 if( eTok2 < EQ || eTok2 > GE )
392 Error( SbERR_SYNTAX );
393 else Next();
394 SbiExpression aCompare( this );
395 aCompare.Gen();
396 nTrueTarget = aGen.Gen(
397 _CASEIS, nTrueTarget,
398 sal::static_int_cast< sal_uInt16 >(
399 SbxEQ + ( eTok2 - EQ ) ) );
401 else
402 { // CASE expr | expr TO expr
403 SbiExpression aCase1( this );
404 aCase1.Gen();
405 if( Peek() == TO )
407 // CASE a TO b
408 Next();
409 SbiExpression aCase2( this );
410 aCase2.Gen();
411 nTrueTarget = aGen.Gen( _CASETO, nTrueTarget );
413 else
414 // CASE a
415 nTrueTarget = aGen.Gen( _CASEIS, nTrueTarget, SbxEQ );
418 if( Peek() == COMMA ) Next();
419 else TestEoln(), bDone = true;
422 if( !bElse )
424 nNextTarget = aGen.Gen( _JUMP, nNextTarget );
425 aGen.BackChain( nTrueTarget );
427 // build the statement body
428 while( !bAbort )
430 eTok = Peek();
431 if( eTok == CASE || eTok == ENDSELECT )
432 break;
433 if( !Parse() ) goto done;
434 eTok = Peek();
435 if( eTok == CASE || eTok == ENDSELECT )
436 break;
438 if( !bElse )
439 nDoneTarget = aGen.Gen( _JUMP, nDoneTarget );
441 else if( !IsEoln( eTok ) )
442 break;
444 done:
445 if( eTok != ENDSELECT )
446 Error( SbERR_EXPECTED, ENDSELECT );
447 if( nNextTarget )
448 aGen.BackChain( nNextTarget );
449 aGen.BackChain( nDoneTarget );
450 aGen.Gen( _ENDCASE );
453 // ON Error/Variable
455 void SbiParser::On()
457 SbiToken eTok = Peek();
458 OUString aString = SbiTokenizer::Symbol(eTok);
459 if (aString.equalsIgnoreAsciiCase("ERROR"))
461 eTok = _ERROR_; // Error comes as SYMBOL
463 if( eTok != _ERROR_ && eTok != LOCAL )
465 OnGoto();
467 else
469 if( eTok == LOCAL )
471 Next();
473 Next (); // no more TestToken, as there'd be an error otherwise
475 Next(); // get token after error
476 if( eCurTok == GOTO )
478 // ON ERROR GOTO label|0
479 Next();
480 bool bError_ = false;
481 if( MayBeLabel() )
483 if( eCurTok == NUMBER && !nVal )
485 aGen.Gen( _STDERROR );
487 else
489 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
490 aGen.Gen( _ERRHDL, nOff );
493 else if( eCurTok == MINUS )
495 Next();
496 if( eCurTok == NUMBER && nVal == 1 )
498 aGen.Gen( _STDERROR );
500 else
502 bError_ = true;
505 if( bError_ )
507 Error( SbERR_LABEL_EXPECTED );
510 else if( eCurTok == RESUME )
512 TestToken( NEXT );
513 aGen.Gen( _NOERROR );
515 else Error( SbERR_EXPECTED, "GoTo/Resume" );
519 // RESUME [0]|NEXT|label
521 void SbiParser::Resume()
523 sal_uInt32 nLbl;
525 switch( Next() )
527 case EOS:
528 case EOLN:
529 aGen.Gen( _RESUME, 0 );
530 break;
531 case NEXT:
532 aGen.Gen( _RESUME, 1 );
533 Next();
534 break;
535 case NUMBER:
536 if( !nVal )
538 aGen.Gen( _RESUME, 0 );
539 break;
540 } // fall through
541 case SYMBOL:
542 if( MayBeLabel() )
544 nLbl = pProc->GetLabels().Reference( aSym );
545 aGen.Gen( _RESUME, nLbl );
546 Next();
547 break;
548 } // fall through
549 default:
550 Error( SbERR_LABEL_EXPECTED );
554 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */