nss: upgrade to release 3.73
[LibreOffice.git] / basic / source / comp / loops.cxx
blob07aac44943a69505162603ba76b5c51c02071c4a
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 <memory>
24 #include <basic/sberrors.hxx>
26 // Single-line IF and Multiline IF
28 void SbiParser::If()
30 sal_uInt32 nEndLbl;
31 SbiToken eTok = NIL;
32 // ignore end-tokens
33 SbiExpression aCond( this );
34 aCond.Gen();
35 TestToken( THEN );
36 if( IsEoln( Next() ) )
38 // At the end of each block a jump to ENDIF must be inserted,
39 // so that the condition is not evaluated again at ELSEIF.
40 // The table collects all jump points.
41 #define JMP_TABLE_SIZE 100
42 sal_uInt32 pnJmpToEndLbl[JMP_TABLE_SIZE]; // 100 ELSEIFs allowed
43 sal_uInt16 iJmp = 0; // current table index
45 // multiline IF
46 nEndLbl = aGen.Gen( SbiOpcode::JUMPF_, 0 );
47 eTok = Peek();
48 while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
49 !bAbort && Parse() )
51 eTok = Peek();
52 if( IsEof() )
54 Error( ERRCODE_BASIC_BAD_BLOCK, IF ); bAbort = true; return;
57 while( eTok == ELSEIF )
59 // jump to ENDIF in case of a successful IF/ELSEIF
60 if( iJmp >= JMP_TABLE_SIZE )
62 Error( ERRCODE_BASIC_PROG_TOO_LARGE ); bAbort = true; return;
64 pnJmpToEndLbl[iJmp++] = aGen.Gen( SbiOpcode::JUMP_, 0 );
66 Next();
67 aGen.BackChain( nEndLbl );
69 aGen.Statement();
70 auto pCond = std::make_unique<SbiExpression>( this );
71 pCond->Gen();
72 nEndLbl = aGen.Gen( SbiOpcode::JUMPF_, 0 );
73 pCond.reset();
74 TestToken( THEN );
75 eTok = Peek();
76 while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
77 !bAbort && Parse() )
79 eTok = Peek();
80 if( IsEof() )
82 Error( ERRCODE_BASIC_BAD_BLOCK, ELSEIF ); bAbort = true; return;
86 if( eTok == ELSE )
88 Next();
89 sal_uInt32 nElseLbl = nEndLbl;
90 nEndLbl = aGen.Gen( SbiOpcode::JUMP_, 0 );
91 aGen.BackChain( nElseLbl );
93 aGen.Statement();
94 StmntBlock( ENDIF );
96 else if( eTok == ENDIF )
97 Next();
100 while( iJmp > 0 )
102 iJmp--;
103 aGen.BackChain( pnJmpToEndLbl[iJmp] );
106 else
108 // single line IF
109 bSingleLineIf = true;
110 nEndLbl = aGen.Gen( SbiOpcode::JUMPF_, 0 );
111 Push( eCurTok );
112 // tdf#128263: update push positions to correctly restore in Next()
113 nPLine = nLine;
114 nPCol1 = nCol1;
115 nPCol2 = nCol2;
117 while( !bAbort )
119 if( !Parse() ) break;
120 eTok = Peek();
121 if( eTok == ELSE || eTok == EOLN || eTok == REM )
122 break;
124 if( eTok == ELSE )
126 Next();
127 sal_uInt32 nElseLbl = nEndLbl;
128 nEndLbl = aGen.Gen( SbiOpcode::JUMP_, 0 );
129 aGen.BackChain( nElseLbl );
130 while( !bAbort )
132 if( !Parse() ) break;
133 eTok = Peek();
134 if( eTok == EOLN || eTok == REM )
135 break;
138 bSingleLineIf = false;
140 aGen.BackChain( nEndLbl );
143 // ELSE/ELSEIF/ENDIF without IF
145 void SbiParser::NoIf()
147 Error( ERRCODE_BASIC_NO_IF );
148 StmntBlock( ENDIF );
151 // DO WHILE...LOOP
152 // DO ... LOOP WHILE
154 void SbiParser::DoLoop()
156 sal_uInt32 nStartLbl = aGen.GetPC();
157 OpenBlock( DO );
158 SbiToken eTok = Next();
159 if( IsEoln( eTok ) )
161 // DO ... LOOP [WHILE|UNTIL expr]
162 StmntBlock( LOOP );
163 eTok = Next();
164 if( eTok == UNTIL || eTok == WHILE )
166 SbiExpression aExpr( this );
167 aExpr.Gen();
168 aGen.Gen( eTok == UNTIL ? SbiOpcode::JUMPF_ : SbiOpcode::JUMPT_, nStartLbl );
169 } else
170 if (eTok == EOLN || eTok == REM)
171 aGen.Gen (SbiOpcode::JUMP_, nStartLbl);
172 else
173 Error( ERRCODE_BASIC_EXPECTED, WHILE );
175 else
177 // DO [WHILE|UNTIL expr] ... LOOP
178 if( eTok == UNTIL || eTok == WHILE )
180 SbiExpression aCond( this );
181 aCond.Gen();
183 sal_uInt32 nEndLbl = aGen.Gen( eTok == UNTIL ? SbiOpcode::JUMPT_ : SbiOpcode::JUMPF_, 0 );
184 StmntBlock( LOOP );
185 TestEoln();
186 aGen.Gen( SbiOpcode::JUMP_, nStartLbl );
187 aGen.BackChain( nEndLbl );
189 CloseBlock();
192 // WHILE ... WEND
194 void SbiParser::While()
196 SbiExpression aCond( this );
197 sal_uInt32 nStartLbl = aGen.GetPC();
198 aCond.Gen();
199 sal_uInt32 nEndLbl = aGen.Gen( SbiOpcode::JUMPF_, 0 );
200 StmntBlock( WEND );
201 aGen.Gen( SbiOpcode::JUMP_, nStartLbl );
202 aGen.BackChain( nEndLbl );
205 // FOR var = expr TO expr STEP
207 void SbiParser::For()
209 bool bForEach = ( Peek() == EACH );
210 if( bForEach )
211 Next();
212 SbiExpression aLvalue( this, SbOPERAND );
213 aLvalue.Gen(); // variable on the Stack
215 if( bForEach )
217 TestToken( IN_ );
218 SbiExpression aCollExpr( this, SbOPERAND );
219 aCollExpr.Gen(); // Collection var to for stack
220 TestEoln();
221 aGen.Gen( SbiOpcode::INITFOREACH_ );
223 else
225 TestToken( EQ );
226 SbiExpression aStartExpr( this );
227 aStartExpr.Gen();
228 TestToken( TO );
229 SbiExpression aStopExpr( this );
230 aStopExpr.Gen();
231 if( Peek() == STEP )
233 Next();
234 SbiExpression aStepExpr( this );
235 aStepExpr.Gen();
237 else
239 SbiExpression aOne( this, 1, SbxINTEGER );
240 aOne.Gen();
242 TestEoln();
243 // The stack has all 4 elements now: variable, start, end, increment
244 // bind start value
245 aGen.Gen( SbiOpcode::INITFOR_ );
248 sal_uInt32 nLoop = aGen.GetPC();
249 // do tests, maybe free the stack
250 sal_uInt32 nEndTarget = aGen.Gen( SbiOpcode::TESTFOR_, 0 );
251 OpenBlock( FOR );
252 StmntBlock( NEXT );
253 aGen.Gen( SbiOpcode::NEXT_ );
254 aGen.Gen( SbiOpcode::JUMP_, nLoop );
255 // are there variables after NEXT?
256 if( Peek() == SYMBOL )
258 SbiExpression aVar( this, SbOPERAND );
259 if( aVar.GetRealVar() != aLvalue.GetRealVar() )
260 Error( ERRCODE_BASIC_EXPECTED, aLvalue.GetRealVar()->GetName() );
262 aGen.BackChain( nEndTarget );
263 CloseBlock();
266 // WITH .. END WITH
268 void SbiParser::With()
270 SbiExpression aVar( this, SbOPERAND );
272 SbiExprNode *pNode = aVar.GetExprNode()->GetRealNode();
273 if (!pNode)
274 return;
275 SbiSymDef* pDef = pNode->GetVar();
276 // Variant, from 27.6.1997, #41090: empty -> must be Object
277 if( pDef->GetType() == SbxVARIANT || pDef->GetType() == SbxEMPTY )
278 pDef->SetType( SbxOBJECT );
279 else if( pDef->GetType() != SbxOBJECT )
280 Error( ERRCODE_BASIC_NEEDS_OBJECT );
283 pNode->SetType( SbxOBJECT );
285 OpenBlock( NIL, aVar.GetExprNode() );
286 StmntBlock( ENDWITH );
287 CloseBlock();
290 // LOOP/NEXT/WEND without construct
292 void SbiParser::BadBlock()
294 if( eEndTok )
295 Error( ERRCODE_BASIC_BAD_BLOCK, eEndTok );
296 else
297 Error( ERRCODE_BASIC_BAD_BLOCK, "Loop/Next/Wend" );
300 // On expr Goto/Gosub n,n,n...
302 void SbiParser::OnGoto()
304 SbiExpression aCond( this );
305 aCond.Gen();
306 sal_uInt32 nLabelsTarget = aGen.Gen( SbiOpcode::ONJUMP_, 0 );
307 SbiToken eTok = Next();
308 if( eTok != GOTO && eTok != GOSUB )
310 Error( ERRCODE_BASIC_EXPECTED, "GoTo/GoSub" );
311 eTok = GOTO;
314 sal_uInt32 nLbl = 0;
317 Next(); // get label
318 if( MayBeLabel() )
320 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
321 aGen.Gen( SbiOpcode::JUMP_, nOff );
322 nLbl++;
324 else Error( ERRCODE_BASIC_LABEL_EXPECTED );
326 while( !bAbort && TestComma() );
327 if( eTok == GOSUB )
328 nLbl |= 0x8000;
329 aGen.Patch( nLabelsTarget, nLbl );
332 // GOTO/GOSUB
334 void SbiParser::Goto()
336 SbiOpcode eOp = eCurTok == GOTO ? SbiOpcode::JUMP_ : SbiOpcode::GOSUB_;
337 Next();
338 if( MayBeLabel() )
340 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
341 aGen.Gen( eOp, nOff );
343 else Error( ERRCODE_BASIC_LABEL_EXPECTED );
346 // RETURN [label]
348 void SbiParser::Return()
350 Next();
351 if( MayBeLabel() )
353 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
354 aGen.Gen( SbiOpcode::RETURN_, nOff );
356 else aGen.Gen( SbiOpcode::RETURN_, 0 );
359 // SELECT CASE
361 void SbiParser::Select()
363 TestToken( CASE );
364 SbiExpression aCase( this );
365 SbiToken eTok = NIL;
366 aCase.Gen();
367 aGen.Gen( SbiOpcode::CASE_ );
368 TestEoln();
369 sal_uInt32 nNextTarget = 0;
370 sal_uInt32 nDoneTarget = 0;
371 bool bElse = false;
373 while( !bAbort )
375 eTok = Next();
376 if( eTok == CASE )
378 if( nNextTarget )
380 aGen.BackChain( nNextTarget );
381 nNextTarget = 0;
383 aGen.Statement();
385 bool bDone = false;
386 sal_uInt32 nTrueTarget = 0;
387 if( Peek() == ELSE )
389 // CASE ELSE
390 Next();
391 bElse = true;
393 else while( !bDone )
395 if( bElse )
396 Error( ERRCODE_BASIC_SYNTAX );
397 SbiToken eTok2 = Peek();
398 if( eTok2 == IS || ( eTok2 >= EQ && eTok2 <= GE ) )
399 { // CASE [IS] operator expr
400 if( eTok2 == IS )
401 Next();
402 eTok2 = Peek();
403 if( eTok2 < EQ || eTok2 > GE )
404 Error( ERRCODE_BASIC_SYNTAX );
405 else Next();
406 SbiExpression aCompare( this );
407 aCompare.Gen();
408 nTrueTarget = aGen.Gen(
409 SbiOpcode::CASEIS_, nTrueTarget,
410 sal::static_int_cast< sal_uInt16 >(
411 SbxEQ + ( eTok2 - EQ ) ) );
413 else
414 { // CASE expr | expr TO expr
415 SbiExpression aCase1( this );
416 aCase1.Gen();
417 if( Peek() == TO )
419 // CASE a TO b
420 Next();
421 SbiExpression aCase2( this );
422 aCase2.Gen();
423 nTrueTarget = aGen.Gen( SbiOpcode::CASETO_, nTrueTarget );
425 else
426 // CASE a
427 nTrueTarget = aGen.Gen( SbiOpcode::CASEIS_, nTrueTarget, SbxEQ );
430 if( Peek() == COMMA ) Next();
431 else
433 TestEoln();
434 bDone = true;
438 if( !bElse )
440 nNextTarget = aGen.Gen( SbiOpcode::JUMP_, nNextTarget );
441 aGen.BackChain( nTrueTarget );
443 // build the statement body
444 while( !bAbort )
446 eTok = Peek();
447 if( eTok == CASE || eTok == ENDSELECT )
448 break;
449 if( !Parse() ) goto done;
450 eTok = Peek();
451 if( eTok == CASE || eTok == ENDSELECT )
452 break;
454 if( !bElse )
455 nDoneTarget = aGen.Gen( SbiOpcode::JUMP_, nDoneTarget );
457 else if( !IsEoln( eTok ) )
458 break;
460 done:
461 if( eTok != ENDSELECT )
462 Error( ERRCODE_BASIC_EXPECTED, ENDSELECT );
463 if( nNextTarget )
464 aGen.BackChain( nNextTarget );
465 aGen.BackChain( nDoneTarget );
466 aGen.Gen( SbiOpcode::ENDCASE_ );
469 // ON Error/Variable
471 void SbiParser::On()
473 SbiToken eTok = Peek();
474 OUString aString = SbiTokenizer::Symbol(eTok);
475 if (aString.equalsIgnoreAsciiCase("ERROR"))
477 eTok = ERROR_; // Error comes as SYMBOL
479 if( eTok != ERROR_ && eTok != LOCAL )
481 OnGoto();
483 else
485 if( eTok == LOCAL )
487 Next();
489 Next (); // no more TestToken, as there'd be an error otherwise
491 Next(); // get token after error
492 if( eCurTok == GOTO )
494 // ON ERROR GOTO label|0
495 Next();
496 bool bError_ = false;
497 if( MayBeLabel() )
499 if( eCurTok == NUMBER && !nVal )
501 aGen.Gen( SbiOpcode::STDERROR_ );
503 else
505 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
506 aGen.Gen( SbiOpcode::ERRHDL_, nOff );
509 else if( eCurTok == MINUS )
511 Next();
512 if( eCurTok == NUMBER && nVal == 1 )
514 aGen.Gen( SbiOpcode::STDERROR_ );
516 else
518 bError_ = true;
521 if( bError_ )
523 Error( ERRCODE_BASIC_LABEL_EXPECTED );
526 else if( eCurTok == RESUME )
528 TestToken( NEXT );
529 aGen.Gen( SbiOpcode::NOERROR_ );
531 else Error( ERRCODE_BASIC_EXPECTED, "GoTo/Resume" );
535 // RESUME [0]|NEXT|label
537 void SbiParser::Resume()
539 sal_uInt32 nLbl;
541 switch( Next() )
543 case EOS:
544 case EOLN:
545 aGen.Gen( SbiOpcode::RESUME_, 0 );
546 break;
547 case NEXT:
548 aGen.Gen( SbiOpcode::RESUME_, 1 );
549 Next();
550 break;
551 case NUMBER:
552 if( !nVal )
554 aGen.Gen( SbiOpcode::RESUME_, 0 );
555 break;
557 [[fallthrough]];
558 case SYMBOL:
559 if( MayBeLabel() )
561 nLbl = pProc->GetLabels().Reference( aSym );
562 aGen.Gen( SbiOpcode::RESUME_, nLbl );
563 Next();
564 break;
566 [[fallthrough]];
567 default:
568 Error( ERRCODE_BASIC_LABEL_EXPECTED );
572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */