1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
24 #include <basic/sberrors.hxx>
26 // Single-line IF and Multiline IF
33 SbiExpression
aCond( this );
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
46 nEndLbl
= aGen
.Gen( SbiOpcode::JUMPF_
, 0 );
48 while( !( eTok
== ELSEIF
|| eTok
== ELSE
|| eTok
== ENDIF
) &&
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 );
67 aGen
.BackChain( nEndLbl
);
70 auto pCond
= std::make_unique
<SbiExpression
>( this );
72 nEndLbl
= aGen
.Gen( SbiOpcode::JUMPF_
, 0 );
76 while( !( eTok
== ELSEIF
|| eTok
== ELSE
|| eTok
== ENDIF
) &&
82 Error( ERRCODE_BASIC_BAD_BLOCK
, ELSEIF
); bAbort
= true; return;
89 sal_uInt32 nElseLbl
= nEndLbl
;
90 nEndLbl
= aGen
.Gen( SbiOpcode::JUMP_
, 0 );
91 aGen
.BackChain( nElseLbl
);
96 else if( eTok
== ENDIF
)
103 aGen
.BackChain( pnJmpToEndLbl
[iJmp
] );
109 bSingleLineIf
= true;
110 nEndLbl
= aGen
.Gen( SbiOpcode::JUMPF_
, 0 );
112 // tdf#128263: update push positions to correctly restore in Next()
119 if( !Parse() ) break;
121 if( eTok
== ELSE
|| eTok
== EOLN
|| eTok
== REM
)
127 sal_uInt32 nElseLbl
= nEndLbl
;
128 nEndLbl
= aGen
.Gen( SbiOpcode::JUMP_
, 0 );
129 aGen
.BackChain( nElseLbl
);
132 if( !Parse() ) break;
134 if( eTok
== EOLN
|| eTok
== REM
)
138 bSingleLineIf
= false;
140 aGen
.BackChain( nEndLbl
);
143 // ELSE/ELSEIF/ENDIF without IF
145 void SbiParser::NoIf()
147 Error( ERRCODE_BASIC_NO_IF
);
154 void SbiParser::DoLoop()
156 sal_uInt32 nStartLbl
= aGen
.GetPC();
158 SbiToken eTok
= Next();
161 // DO ... LOOP [WHILE|UNTIL expr]
164 if( eTok
== UNTIL
|| eTok
== WHILE
)
166 SbiExpression
aExpr( this );
168 aGen
.Gen( eTok
== UNTIL
? SbiOpcode::JUMPF_
: SbiOpcode::JUMPT_
, nStartLbl
);
170 if (eTok
== EOLN
|| eTok
== REM
)
171 aGen
.Gen (SbiOpcode::JUMP_
, nStartLbl
);
173 Error( ERRCODE_BASIC_EXPECTED
, WHILE
);
177 // DO [WHILE|UNTIL expr] ... LOOP
178 if( eTok
== UNTIL
|| eTok
== WHILE
)
180 SbiExpression
aCond( this );
183 sal_uInt32 nEndLbl
= aGen
.Gen( eTok
== UNTIL
? SbiOpcode::JUMPT_
: SbiOpcode::JUMPF_
, 0 );
186 aGen
.Gen( SbiOpcode::JUMP_
, nStartLbl
);
187 aGen
.BackChain( nEndLbl
);
194 void SbiParser::While()
196 SbiExpression
aCond( this );
197 sal_uInt32 nStartLbl
= aGen
.GetPC();
199 sal_uInt32 nEndLbl
= aGen
.Gen( SbiOpcode::JUMPF_
, 0 );
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
);
212 SbiExpression
aLvalue( this, SbOPERAND
);
213 aLvalue
.Gen(); // variable on the Stack
218 SbiExpression
aCollExpr( this, SbOPERAND
);
219 aCollExpr
.Gen(); // Collection var to for stack
221 aGen
.Gen( SbiOpcode::INITFOREACH_
);
226 SbiExpression
aStartExpr( this );
229 SbiExpression
aStopExpr( this );
234 SbiExpression
aStepExpr( this );
239 SbiExpression
aOne( this, 1, SbxINTEGER
);
243 // The stack has all 4 elements now: variable, start, end, increment
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 );
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
);
268 void SbiParser::With()
270 SbiExpression
aVar( this, SbOPERAND
);
272 SbiExprNode
*pNode
= aVar
.GetExprNode()->GetRealNode();
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
);
290 // LOOP/NEXT/WEND without construct
292 void SbiParser::BadBlock()
295 Error( ERRCODE_BASIC_BAD_BLOCK
, eEndTok
);
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 );
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" );
320 sal_uInt32 nOff
= pProc
->GetLabels().Reference( aSym
);
321 aGen
.Gen( SbiOpcode::JUMP_
, nOff
);
324 else Error( ERRCODE_BASIC_LABEL_EXPECTED
);
326 while( !bAbort
&& TestComma() );
329 aGen
.Patch( nLabelsTarget
, nLbl
);
334 void SbiParser::Goto()
336 SbiOpcode eOp
= eCurTok
== GOTO
? SbiOpcode::JUMP_
: SbiOpcode::GOSUB_
;
340 sal_uInt32 nOff
= pProc
->GetLabels().Reference( aSym
);
341 aGen
.Gen( eOp
, nOff
);
343 else Error( ERRCODE_BASIC_LABEL_EXPECTED
);
348 void SbiParser::Return()
353 sal_uInt32 nOff
= pProc
->GetLabels().Reference( aSym
);
354 aGen
.Gen( SbiOpcode::RETURN_
, nOff
);
356 else aGen
.Gen( SbiOpcode::RETURN_
, 0 );
361 void SbiParser::Select()
364 SbiExpression
aCase( this );
367 aGen
.Gen( SbiOpcode::CASE_
);
369 sal_uInt32 nNextTarget
= 0;
370 sal_uInt32 nDoneTarget
= 0;
380 aGen
.BackChain( nNextTarget
);
386 sal_uInt32 nTrueTarget
= 0;
396 Error( ERRCODE_BASIC_SYNTAX
);
397 SbiToken eTok2
= Peek();
398 if( eTok2
== IS
|| ( eTok2
>= EQ
&& eTok2
<= GE
) )
399 { // CASE [IS] operator expr
403 if( eTok2
< EQ
|| eTok2
> GE
)
404 Error( ERRCODE_BASIC_SYNTAX
);
406 SbiExpression
aCompare( this );
408 nTrueTarget
= aGen
.Gen(
409 SbiOpcode::CASEIS_
, nTrueTarget
,
410 sal::static_int_cast
< sal_uInt16
>(
411 SbxEQ
+ ( eTok2
- EQ
) ) );
414 { // CASE expr | expr TO expr
415 SbiExpression
aCase1( this );
421 SbiExpression
aCase2( this );
423 nTrueTarget
= aGen
.Gen( SbiOpcode::CASETO_
, nTrueTarget
);
427 nTrueTarget
= aGen
.Gen( SbiOpcode::CASEIS_
, nTrueTarget
, SbxEQ
);
430 if( Peek() == COMMA
) Next();
440 nNextTarget
= aGen
.Gen( SbiOpcode::JUMP_
, nNextTarget
);
441 aGen
.BackChain( nTrueTarget
);
443 // build the statement body
447 if( eTok
== CASE
|| eTok
== ENDSELECT
)
449 if( !Parse() ) goto done
;
451 if( eTok
== CASE
|| eTok
== ENDSELECT
)
455 nDoneTarget
= aGen
.Gen( SbiOpcode::JUMP_
, nDoneTarget
);
457 else if( !IsEoln( eTok
) )
461 if( eTok
!= ENDSELECT
)
462 Error( ERRCODE_BASIC_EXPECTED
, ENDSELECT
);
464 aGen
.BackChain( nNextTarget
);
465 aGen
.BackChain( nDoneTarget
);
466 aGen
.Gen( SbiOpcode::ENDCASE_
);
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
)
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
496 bool bError_
= false;
499 if( eCurTok
== NUMBER
&& !nVal
)
501 aGen
.Gen( SbiOpcode::STDERROR_
);
505 sal_uInt32 nOff
= pProc
->GetLabels().Reference( aSym
);
506 aGen
.Gen( SbiOpcode::ERRHDL_
, nOff
);
509 else if( eCurTok
== MINUS
)
512 if( eCurTok
== NUMBER
&& nVal
== 1 )
514 aGen
.Gen( SbiOpcode::STDERROR_
);
523 Error( ERRCODE_BASIC_LABEL_EXPECTED
);
526 else if( eCurTok
== RESUME
)
529 aGen
.Gen( SbiOpcode::NOERROR_
);
531 else Error( ERRCODE_BASIC_EXPECTED
, "GoTo/Resume" );
535 // RESUME [0]|NEXT|label
537 void SbiParser::Resume()
545 aGen
.Gen( SbiOpcode::RESUME_
, 0 );
548 aGen
.Gen( SbiOpcode::RESUME_
, 1 );
554 aGen
.Gen( SbiOpcode::RESUME_
, 0 );
561 nLbl
= pProc
->GetLabels().Reference( aSym
);
562 aGen
.Gen( SbiOpcode::RESUME_
, nLbl
);
568 Error( ERRCODE_BASIC_LABEL_EXPECTED
);
572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */