LibreOffice Module basic (master) 1
loops.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
21#include <parser.hxx>
22#include <memory>
23
24#include <basic/sberrors.hxx>
25
26// Single-line IF and Multiline IF
27
29{
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() ) )
37 {
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
44
45 // multiline IF
46 nEndLbl = aGen.Gen( SbiOpcode::JUMPF_, 0 );
47 eTok = Peek();
48 while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
49 !bAbort && Parse() )
50 {
51 eTok = Peek();
52 if( IsEof() )
53 {
54 Error( ERRCODE_BASIC_BAD_BLOCK, IF ); bAbort = true; return;
55 }
56 }
57 while( eTok == ELSEIF )
58 {
59 // jump to ENDIF in case of a successful IF/ELSEIF
60 if( iJmp >= JMP_TABLE_SIZE )
61 {
62 Error( ERRCODE_BASIC_PROG_TOO_LARGE ); bAbort = true; return;
63 }
64 pnJmpToEndLbl[iJmp++] = aGen.Gen( SbiOpcode::JUMP_, 0 );
65
66 Next();
67 aGen.BackChain( nEndLbl );
68
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() )
78 {
79 eTok = Peek();
80 if( IsEof() )
81 {
82 Error( ERRCODE_BASIC_BAD_BLOCK, ELSEIF ); bAbort = true; return;
83 }
84 }
85 }
86 if( eTok == ELSE )
87 {
88 Next();
89 sal_uInt32 nElseLbl = nEndLbl;
90 nEndLbl = aGen.Gen( SbiOpcode::JUMP_, 0 );
91 aGen.BackChain( nElseLbl );
92
95 }
96 else if( eTok == ENDIF )
97 Next();
98
99
100 while( iJmp > 0 )
101 {
102 iJmp--;
103 aGen.BackChain( pnJmpToEndLbl[iJmp] );
104 }
105 }
106 else
107 {
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;
116
117 while( !bAbort )
118 {
119 if( !Parse() ) break;
120 eTok = Peek();
121 if( eTok == ELSE || eTok == EOLN || eTok == REM )
122 break;
123 }
124 if( eTok == ELSE )
125 {
126 Next();
127 sal_uInt32 nElseLbl = nEndLbl;
128 nEndLbl = aGen.Gen( SbiOpcode::JUMP_, 0 );
129 aGen.BackChain( nElseLbl );
130 while( !bAbort )
131 {
132 if( !Parse() ) break;
133 eTok = Peek();
134 if( eTok == EOLN || eTok == REM )
135 break;
136 }
137 }
138 bSingleLineIf = false;
139 }
140 aGen.BackChain( nEndLbl );
141}
142
143// ELSE/ELSEIF/ENDIF without IF
144
146{
148 StmntBlock( ENDIF );
149}
150
151// DO WHILE...LOOP
152// DO ... LOOP WHILE
153
155{
156 sal_uInt32 nStartLbl = aGen.GetPC();
157 OpenBlock( DO );
158 SbiToken eTok = Next();
159 if( IsEoln( eTok ) )
160 {
161 // DO ... LOOP [WHILE|UNTIL expr]
162 StmntBlock( LOOP );
163 eTok = Next();
164 if( eTok == UNTIL || eTok == WHILE )
165 {
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
174 }
175 else
176 {
177 // DO [WHILE|UNTIL expr] ... LOOP
178 if( eTok == UNTIL || eTok == WHILE )
179 {
180 SbiExpression aCond( this );
181 aCond.Gen();
182 }
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 );
188 }
189 CloseBlock();
190}
191
192// WHILE ... WEND
193
195{
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 );
203}
204
205// FOR var = expr TO expr STEP
206
208{
209 bool bForEach = ( Peek() == EACH );
210 if( bForEach )
211 Next();
212 SbiExpression aLvalue( this, SbOPERAND );
213 aLvalue.Gen(); // variable on the Stack
214
215 if( bForEach )
216 {
217 TestToken( IN_ );
218 SbiExpression aCollExpr( this, SbOPERAND );
219 aCollExpr.Gen(); // Collection var to for stack
220 TestEoln();
222 }
223 else
224 {
225 TestToken( EQ );
226 SbiExpression aStartExpr( this );
227 aStartExpr.Gen();
228 TestToken( TO );
229 SbiExpression aStopExpr( this );
230 aStopExpr.Gen();
231 if( Peek() == STEP )
232 {
233 Next();
234 SbiExpression aStepExpr( this );
235 aStepExpr.Gen();
236 }
237 else
238 {
239 SbiExpression aOne( this, 1, SbxINTEGER );
240 aOne.Gen();
241 }
242 TestEoln();
243 // The stack has all 4 elements now: variable, start, end, increment
244 // bind start value
246 }
247
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 );
254 aGen.Gen( SbiOpcode::JUMP_, nLoop );
255 // are there variables after NEXT?
256 if( Peek() == SYMBOL )
257 {
258 SbiExpression aVar( this, SbOPERAND );
259 if( aVar.GetRealVar() != aLvalue.GetRealVar() )
261 }
262 aGen.BackChain( nEndTarget );
263 CloseBlock();
264}
265
266// WITH .. END WITH
267
269{
270 SbiExpression aVar( this, SbOPERAND );
271
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 )
281
282
283 pNode->SetType( SbxOBJECT );
284
285 OpenBlock( NIL, aVar.GetExprNode() );
287 CloseBlock();
288}
289
290// LOOP/NEXT/WEND without construct
291
293{
294 if( eEndTok )
296 else
297 Error( ERRCODE_BASIC_BAD_BLOCK, "Loop/Next/Wend" );
298}
299
300// On expr Goto/Gosub n,n,n...
301
303{
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 )
309 {
310 Error( ERRCODE_BASIC_EXPECTED, "GoTo/GoSub" );
311 eTok = GOTO;
312 }
313
314 sal_uInt32 nLbl = 0;
315 do
316 {
317 Next(); // get label
318 if( MayBeLabel() )
319 {
320 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
321 aGen.Gen( SbiOpcode::JUMP_, nOff );
322 nLbl++;
323 }
325 }
326 while( !bAbort && TestComma() );
327 if( eTok == GOSUB )
328 nLbl |= 0x8000;
329 aGen.Patch( nLabelsTarget, nLbl );
330}
331
332// GOTO/GOSUB
333
335{
337 Next();
338 if( MayBeLabel() )
339 {
340 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
341 aGen.Gen( eOp, nOff );
342 }
344}
345
346// RETURN [label]
347
349{
350 Next();
351 if( MayBeLabel() )
352 {
353 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
354 aGen.Gen( SbiOpcode::RETURN_, nOff );
355 }
356 else aGen.Gen( SbiOpcode::RETURN_, 0 );
357}
358
359// SELECT CASE
360
362{
363 TestToken( CASE );
364 SbiExpression aCase( this );
365 SbiToken eTok = NIL;
366 aCase.Gen();
368 TestEoln();
369 sal_uInt32 nNextTarget = 0;
370 sal_uInt32 nDoneTarget = 0;
371 bool bElse = false;
372
373 while( !bAbort )
374 {
375 eTok = Next();
376 if( eTok == CASE )
377 {
378 if( nNextTarget )
379 {
380 aGen.BackChain( nNextTarget );
381 nNextTarget = 0;
382 }
383 aGen.Statement();
384
385 bool bDone = false;
386 sal_uInt32 nTrueTarget = 0;
387 if( Peek() == ELSE )
388 {
389 // CASE ELSE
390 Next();
391 bElse = true;
392 }
393 else while( !bDone )
394 {
395 if( bElse )
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 )
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 ) ) );
412 }
413 else
414 { // CASE expr | expr TO expr
415 SbiExpression aCase1( this );
416 aCase1.Gen();
417 if( Peek() == TO )
418 {
419 // CASE a TO b
420 Next();
421 SbiExpression aCase2( this );
422 aCase2.Gen();
423 nTrueTarget = aGen.Gen( SbiOpcode::CASETO_, nTrueTarget );
424 }
425 else
426 // CASE a
427 nTrueTarget = aGen.Gen( SbiOpcode::CASEIS_, nTrueTarget, SbxEQ );
428
429 }
430 if( Peek() == COMMA ) Next();
431 else
432 {
433 TestEoln();
434 bDone = true;
435 }
436 }
437
438 if( !bElse )
439 {
440 nNextTarget = aGen.Gen( SbiOpcode::JUMP_, nNextTarget );
441 aGen.BackChain( nTrueTarget );
442 }
443 // build the statement body
444 while( !bAbort )
445 {
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;
453 }
454 if( !bElse )
455 nDoneTarget = aGen.Gen( SbiOpcode::JUMP_, nDoneTarget );
456 }
457 else if( !IsEoln( eTok ) )
458 break;
459 }
460done:
461 if( eTok != ENDSELECT )
463 if( nNextTarget )
464 aGen.BackChain( nNextTarget );
465 aGen.BackChain( nDoneTarget );
467}
468
469// ON Error/Variable
470
472{
473 SbiToken eTok = Peek();
474 OUString aString = SbiTokenizer::Symbol(eTok);
475 if (aString.equalsIgnoreAsciiCase("ERROR"))
476 {
477 eTok = ERROR_; // Error comes as SYMBOL
478 }
479 if( eTok != ERROR_ && eTok != LOCAL )
480 {
481 OnGoto();
482 }
483 else
484 {
485 if( eTok == LOCAL )
486 {
487 Next();
488 }
489 Next (); // no more TestToken, as there'd be an error otherwise
490
491 Next(); // get token after error
492 if( eCurTok == GOTO )
493 {
494 // ON ERROR GOTO label|0
495 Next();
496 bool bError_ = false;
497 if( MayBeLabel() )
498 {
499 if( eCurTok == NUMBER && !nVal )
500 {
502 }
503 else
504 {
505 sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
506 aGen.Gen( SbiOpcode::ERRHDL_, nOff );
507 }
508 }
509 else if( eCurTok == MINUS )
510 {
511 Next();
512 if( eCurTok == NUMBER && nVal == 1 )
513 {
515 }
516 else
517 {
518 bError_ = true;
519 }
520 }
521 if( bError_ )
522 {
524 }
525 }
526 else if( eCurTok == RESUME )
527 {
528 TestToken( NEXT );
530 }
531 else Error( ERRCODE_BASIC_EXPECTED, "GoTo/Resume" );
532 }
533}
534
535// RESUME [0]|NEXT|label
536
538{
539 sal_uInt32 nLbl;
540
541 switch( Next() )
542 {
543 case EOS:
544 case EOLN:
546 break;
547 case NEXT:
549 Next();
550 break;
551 case NUMBER:
552 if( !nVal )
553 {
555 break;
556 }
557 [[fallthrough]];
558 case SYMBOL:
559 if( MayBeLabel() )
560 {
561 nLbl = pProc->GetLabels().Reference( aSym );
562 aGen.Gen( SbiOpcode::RESUME_, nLbl );
563 Next();
564 break;
565 }
566 [[fallthrough]];
567 default:
569 }
570}
571
572/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void Patch(sal_uInt32 o, sal_uInt32 v)
Definition: codegen.hxx:43
sal_uInt32 Gen(SbiOpcode)
Definition: codegen.cxx:88
void BackChain(sal_uInt32 off)
Definition: codegen.hxx:44
sal_uInt32 GetPC() const
Definition: codegen.cxx:49
void Statement()
Definition: codegen.cxx:56
SbiSymDef * GetVar()
Definition: exprnode.cxx:119
void SetType(SbxDataType eTp)
Definition: expr.hxx:152
SbiExprNode * GetRealNode()
Definition: exprnode.cxx:137
void Gen(RecursiveMode eRecMode=UNDEFINED)
Definition: exprgen.cxx:260
SbiSymDef * GetRealVar()
Definition: expr.hxx:209
SbiExprNode * GetExprNode()
Definition: expr.hxx:210
void BadBlock()
Definition: loops.cxx:292
void On()
Definition: loops.cxx:471
void While()
Definition: loops.cxx:194
bool TestComma()
Definition: parser.cxx:275
void StmntBlock(SbiToken)
Definition: parser.cxx:303
bool Parse()
Definition: parser.cxx:322
void Resume()
Definition: loops.cxx:537
bool TestToken(SbiToken)
Definition: parser.cxx:261
void TestEoln()
Definition: parser.cxx:293
void OnGoto()
Definition: loops.cxx:302
void Select()
Definition: loops.cxx:361
void OpenBlock(SbiToken, SbiExprNode *=nullptr)
Definition: parser.cxx:196
void If()
Definition: loops.cxx:28
void For()
Definition: loops.cxx:207
void Goto()
Definition: loops.cxx:334
void Return()
Definition: loops.cxx:348
bool bSingleLineIf
Definition: parser.hxx:42
SbiProcDef * pProc
Definition: parser.hxx:36
void DoLoop()
Definition: loops.cxx:154
void CloseBlock()
Definition: parser.cxx:211
void NoIf()
Definition: loops.cxx:145
SbiToken eEndTok
Definition: parser.hxx:38
void With()
Definition: loops.cxx:268
SbiCodeGen aGen
Definition: parser.hxx:68
SbiSymPool & GetLabels()
Definition: symtbl.hxx:178
bool bAbort
Definition: scanner.hxx:59
double nVal
Definition: scanner.hxx:48
OUString aSym
Definition: scanner.hxx:45
sal_Int32 nCol2
Definition: scanner.hxx:55
sal_Int32 nCol1
Definition: scanner.hxx:55
sal_Int32 nLine
Definition: scanner.hxx:54
SbxDataType GetType() const
Definition: symtbl.hxx:115
const OUString & GetName()
Definition: symtbl.cxx:321
virtual void SetType(SbxDataType)
Definition: symtbl.cxx:331
sal_uInt32 Reference(const OUString &)
Definition: symtbl.cxx:256
void Push(SbiToken)
Definition: token.cxx:227
sal_uInt16 nPCol1
Definition: token.hxx:105
bool IsEof() const
Definition: token.hxx:113
bool MayBeLabel(bool=false)
Definition: token.cxx:545
SbiToken Next()
Definition: token.cxx:309
sal_uInt16 nPLine
Definition: token.hxx:105
static bool IsEoln(SbiToken t)
Definition: token.hxx:127
void Error(ErrCode c)
Definition: token.hxx:123
SbiToken Peek()
Definition: token.cxx:248
SbiToken eCurTok
Definition: token.hxx:103
const OUString & Symbol(SbiToken)
Definition: token.cxx:266
sal_uInt16 nPCol2
Definition: token.hxx:105
@ SbOPERAND
Definition: expr.hxx:57
#define JMP_TABLE_SIZE
SbiOpcode
Definition: opcodes.hxx:25
#define ERRCODE_BASIC_EXPECTED
Definition: sberrors.hxx:125
#define ERRCODE_BASIC_PROG_TOO_LARGE
Definition: sberrors.hxx:151
#define ERRCODE_BASIC_LABEL_EXPECTED
Definition: sberrors.hxx:128
#define ERRCODE_BASIC_SYNTAX
Definition: sberrors.hxx:25
#define ERRCODE_BASIC_BAD_BLOCK
Definition: sberrors.hxx:139
#define ERRCODE_BASIC_NEEDS_OBJECT
Definition: sberrors.hxx:108
#define ERRCODE_BASIC_NO_IF
Definition: sberrors.hxx:145
@ SbxEQ
Definition: sbxdef.hxx:115
@ SbxOBJECT
Definition: sbxdef.hxx:47
@ SbxEMPTY
Definition: sbxdef.hxx:38
@ SbxVARIANT
Definition: sbxdef.hxx:51
@ SbxINTEGER
Definition: sbxdef.hxx:40
SbiToken
Definition: token.hxx:30
@ NUMBER
Definition: token.hxx:78
@ EACH
Definition: token.hxx:52
@ IF
Definition: token.hxx:55
@ ENDWITH
Definition: token.hxx:65
@ WEND
Definition: token.hxx:64
@ WHILE
Definition: token.hxx:64
@ RESUME
Definition: token.hxx:60
@ GE
Definition: token.hxx:73
@ ENDIF
Definition: token.hxx:65
@ LOOP
Definition: token.hxx:56
@ NIL
Definition: token.hxx:31
@ IS
Definition: token.hxx:75
@ REM
Definition: token.hxx:60
@ TO
Definition: token.hxx:62
@ ELSEIF
Definition: token.hxx:52
@ GOSUB
Definition: token.hxx:54
@ IN_
Definition: token.hxx:55
@ ENDSELECT
Definition: token.hxx:65
@ ELSE
Definition: token.hxx:52
@ EOLN
Definition: token.hxx:69
@ CASE
Definition: token.hxx:39
@ EQ
Definition: token.hxx:73
@ UNTIL
Definition: token.hxx:63
@ FOR
Definition: token.hxx:53
@ NEXT
Definition: token.hxx:57
@ THEN
Definition: token.hxx:62
@ DO
Definition: token.hxx:40
@ STEP
Definition: token.hxx:61
@ LOCAL
Definition: token.hxx:56
@ GOTO
Definition: token.hxx:54
@ SYMBOL
Definition: token.hxx:78
@ EOS
Definition: token.hxx:69
@ MINUS
Definition: token.hxx:72
@ COMMA
Definition: token.hxx:33