1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: loops.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_basic.hxx"
36 // Single-line IF und Multiline IF
42 // Ende-Tokens ignorieren:
43 SbiExpression
aCond( this );
46 if( IsEoln( Next() ) )
48 // AB 13.5.1996: #27720# Am Ende jeden Blocks muss ein Jump zu ENDIF
49 // eingefuegt werden, damit bei ELSEIF nicht erneut die Bedingung
50 // ausgewertet wird. Die Tabelle nimmt alle Absprungstellen auf.
51 #define JMP_TABLE_SIZE 100
52 UINT32 pnJmpToEndLbl
[JMP_TABLE_SIZE
]; // 100 ELSEIFs zulaessig
53 USHORT iJmp
= 0; // aktueller Tabellen-Index
56 nEndLbl
= aGen
.Gen( _JUMPF
, 0 );
58 while( !( eTok
== ELSEIF
|| eTok
== ELSE
|| eTok
== ENDIF
) &&
64 Error( SbERR_BAD_BLOCK
, IF
); bAbort
= TRUE
; return;
68 while( eTok
== ELSEIF
)
70 // #27720# Bei erfolgreichem IF/ELSEIF auf ENDIF springen
71 if( iJmp
>= JMP_TABLE_SIZE
)
73 Error( SbERR_PROG_TOO_LARGE
); bAbort
= TRUE
; return;
75 pnJmpToEndLbl
[iJmp
++] = aGen
.Gen( _JUMP
, 0 );
78 aGen
.BackChain( nEndLbl
);
81 SbiExpression
* pCond
= new SbiExpression( this );
83 nEndLbl
= aGen
.Gen( _JUMPF
, 0 );
87 while( !( eTok
== ELSEIF
|| eTok
== ELSE
|| eTok
== ENDIF
) &&
93 Error( SbERR_BAD_BLOCK
, ELSEIF
); bAbort
= TRUE
; return;
100 UINT32 nElseLbl
= nEndLbl
;
101 nEndLbl
= aGen
.Gen( _JUMP
, 0 );
102 aGen
.BackChain( nElseLbl
);
107 else if( eTok
== ENDIF
)
110 // #27720# Jmp-Tabelle abarbeiten
114 aGen
.BackChain( pnJmpToEndLbl
[iJmp
] );
120 bSingleLineIf
= TRUE
;
121 nEndLbl
= aGen
.Gen( _JUMPF
, 0 );
125 if( !Parse() ) break;
127 if( eTok
== ELSE
|| eTok
== EOLN
|| eTok
== REM
)
133 UINT32 nElseLbl
= nEndLbl
;
134 nEndLbl
= aGen
.Gen( _JUMP
, 0 );
135 aGen
.BackChain( nElseLbl
);
138 if( !Parse() ) break;
144 bSingleLineIf
= FALSE
;
146 aGen
.BackChain( nEndLbl
);
149 // ELSE/ELSEIF/ENDIF ohne IF
151 void SbiParser::NoIf()
153 Error( SbERR_NO_IF
);
160 void SbiParser::DoLoop()
162 UINT32 nStartLbl
= aGen
.GetPC();
164 SbiToken eTok
= Next();
167 // DO ... LOOP [WHILE|UNTIL expr]
170 if( eTok
== UNTIL
|| eTok
== WHILE
)
172 SbiExpression
aExpr( this );
174 aGen
.Gen( eTok
== UNTIL
? _JUMPF
: _JUMPT
, nStartLbl
);
176 if (eTok
== EOLN
|| eTok
== REM
)
177 aGen
.Gen (_JUMP
, nStartLbl
);
179 Error( SbERR_EXPECTED
, WHILE
);
183 // DO [WHILE|UNTIL expr] ... LOOP
184 if( eTok
== UNTIL
|| eTok
== WHILE
)
186 SbiExpression
aCond( this );
189 UINT32 nEndLbl
= aGen
.Gen( eTok
== UNTIL
? _JUMPT
: _JUMPF
, 0 );
192 aGen
.Gen( _JUMP
, nStartLbl
);
193 aGen
.BackChain( nEndLbl
);
200 void SbiParser::While()
202 SbiExpression
aCond( this );
203 UINT32 nStartLbl
= aGen
.GetPC();
205 UINT32 nEndLbl
= aGen
.Gen( _JUMPF
, 0 );
207 aGen
.Gen( _JUMP
, nStartLbl
);
208 aGen
.BackChain( nEndLbl
);
211 // FOR var = expr TO expr STEP
213 void SbiParser::For()
215 bool bForEach
= ( Peek() == EACH
);
218 SbiExpression
aLvalue( this, SbOPERAND
);
219 aLvalue
.Gen(); // Variable auf dem Stack
224 SbiExpression
aCollExpr( this, SbOPERAND
);
225 aCollExpr
.Gen(); // Colletion var to for stack
227 aGen
.Gen( _INITFOREACH
);
232 SbiExpression
aStartExpr( this );
233 aStartExpr
.Gen(); // Startausdruck auf dem Stack
235 SbiExpression
aStopExpr( this );
236 aStopExpr
.Gen(); // Endausdruck auf dem Stack
240 SbiExpression
aStepExpr( this );
245 SbiExpression
aOne( this, 1, SbxINTEGER
);
249 // Der Stack hat jetzt 4 Elemente: Variable, Start, Ende, Inkrement
251 aGen
.Gen( _INITFOR
);
254 UINT32 nLoop
= aGen
.GetPC();
255 // Test durchfuehren, evtl. Stack freigeben
256 UINT32 nEndTarget
= aGen
.Gen( _TESTFOR
, 0 );
260 aGen
.Gen( _JUMP
, nLoop
);
261 // Kommen Variable nach NEXT?
262 if( Peek() == SYMBOL
)
264 SbiExpression
aVar( this, SbOPERAND
);
265 if( aVar
.GetRealVar() != aLvalue
.GetRealVar() )
266 Error( SbERR_EXPECTED
, aLvalue
.GetRealVar()->GetName() );
268 aGen
.BackChain( nEndTarget
);
274 void SbiParser::With()
276 SbiExpression
aVar( this, SbOPERAND
);
278 // Letzten Knoten in der Objekt-Kette ueberpruefen
279 SbiExprNode
*pNode
= aVar
.GetExprNode()->GetRealNode();
280 SbiSymDef
* pDef
= pNode
->GetVar();
281 // Variant, AB 27.6.1997, #41090: bzw. empty -> muß Object sein
282 if( pDef
->GetType() == SbxVARIANT
|| pDef
->GetType() == SbxEMPTY
)
283 pDef
->SetType( SbxOBJECT
);
284 else if( pDef
->GetType() != SbxOBJECT
)
285 Error( SbERR_NEEDS_OBJECT
);
287 // Knoten auch auf SbxOBJECT setzen, damit spaeter Gen() klappt
288 pNode
->SetType( SbxOBJECT
);
290 OpenBlock( NIL
, aVar
.GetExprNode() );
291 StmntBlock( ENDWITH
);
295 // LOOP/NEXT/WEND ohne Konstrukt
297 void SbiParser::BadBlock()
300 Error( SbERR_BAD_BLOCK
, eEndTok
);
302 Error( SbERR_BAD_BLOCK
, "Loop/Next/Wend" );
305 // On expr Goto/Gosub n,n,n...
307 void SbiParser::OnGoto()
309 SbiExpression
aCond( this );
311 UINT32 nLabelsTarget
= aGen
.Gen( _ONJUMP
, 0 );
312 SbiToken eTok
= Next();
313 if( eTok
!= GOTO
&& eTok
!= GOSUB
)
315 Error( SbERR_EXPECTED
, "GoTo/GoSub" );
318 // Label-Tabelle einlesen:
322 SbiToken eTok2
= NIL
;
323 eTok2
= Next(); // Label holen
326 UINT32 nOff
= pProc
->GetLabels().Reference( aSym
);
327 aGen
.Gen( _JUMP
, nOff
);
330 else Error( SbERR_LABEL_EXPECTED
);
332 while( !bAbort
&& TestComma() );
335 aGen
.Patch( nLabelsTarget
, nLbl
);
340 void SbiParser::Goto()
342 SbiOpcode eOp
= eCurTok
== GOTO
? _JUMP
: _GOSUB
;
346 UINT32 nOff
= pProc
->GetLabels().Reference( aSym
);
347 aGen
.Gen( eOp
, nOff
);
349 else Error( SbERR_LABEL_EXPECTED
);
354 void SbiParser::Return()
359 UINT32 nOff
= pProc
->GetLabels().Reference( aSym
);
360 aGen
.Gen( _RETURN
, nOff
);
362 else aGen
.Gen( _RETURN
, 0 );
367 void SbiParser::Select()
370 SbiExpression
aCase( this );
375 UINT32 nNextTarget
= 0;
376 UINT32 nDoneTarget
= 0;
378 // Die Cases einlesen:
385 aGen
.BackChain( nNextTarget
), nNextTarget
= 0;
387 // Jeden Case einlesen
389 UINT32 nTrueTarget
= 0;
399 Error( SbERR_SYNTAX
);
400 SbiToken eTok2
= Peek();
401 if( eTok2
== IS
|| ( eTok2
>= EQ
&& eTok2
<= GE
) )
402 { // CASE [IS] operator expr
406 if( eTok2
< EQ
|| eTok2
> GE
)
407 Error( SbERR_SYNTAX
);
409 SbiExpression
aCompare( this );
411 nTrueTarget
= aGen
.Gen(
412 _CASEIS
, nTrueTarget
,
413 sal::static_int_cast
< UINT16
>(
414 SbxEQ
+ ( eTok2
- EQ
) ) );
417 { // CASE expr | expr TO expr
418 SbiExpression
aCase1( this );
424 SbiExpression
aCase2( this );
426 nTrueTarget
= aGen
.Gen( _CASETO
, nTrueTarget
);
430 nTrueTarget
= aGen
.Gen( _CASEIS
, nTrueTarget
, SbxEQ
);
433 if( Peek() == COMMA
) Next();
434 else TestEoln(), bDone
= TRUE
;
436 // Alle Cases abgearbeitet
439 nNextTarget
= aGen
.Gen( _JUMP
, nNextTarget
);
440 aGen
.BackChain( nTrueTarget
);
442 // den Statement-Rumpf bauen
446 if( eTok
== CASE
|| eTok
== ENDSELECT
)
448 if( !Parse() ) goto done
;
450 if( eTok
== CASE
|| eTok
== ENDSELECT
)
454 nDoneTarget
= aGen
.Gen( _JUMP
, nDoneTarget
);
456 else if( !IsEoln( eTok
) )
460 if( eTok
!= ENDSELECT
)
461 Error( SbERR_EXPECTED
, ENDSELECT
);
463 aGen
.BackChain( nNextTarget
);
464 aGen
.BackChain( nDoneTarget
);
465 aGen
.Gen( _ENDCASE
);
471 #pragma optimize("",off)
476 SbiToken eTok
= Peek();
477 String aString
= SbiTokenizer::Symbol(eTok
);
478 if (aString
.EqualsIgnoreCaseAscii("ERROR"))
479 //if (!aString.ICompare("ERROR"))
480 eTok
= _ERROR_
; // Error kommt als SYMBOL
481 if( eTok
!= _ERROR_
&& eTok
!= LOCAL
) OnGoto();
484 if( eTok
== LOCAL
) Next();
485 Next (); // Kein TestToken mehr, da es sonst einen Fehler gibt
487 Next(); // Token nach Error holen
488 if( eCurTok
== GOTO
)
490 // ON ERROR GOTO label|0
492 bool bError_
= false;
495 if( eCurTok
== NUMBER
&& !nVal
)
496 aGen
.Gen( _STDERROR
);
499 UINT32 nOff
= pProc
->GetLabels().Reference( aSym
);
500 aGen
.Gen( _ERRHDL
, nOff
);
503 else if( eCurTok
== MINUS
)
506 if( eCurTok
== NUMBER
&& nVal
== 1 )
507 aGen
.Gen( _STDERROR
);
512 Error( SbERR_LABEL_EXPECTED
);
514 else if( eCurTok
== RESUME
)
517 aGen
.Gen( _NOERROR
);
519 else Error( SbERR_EXPECTED
, "GoTo/Resume" );
524 #pragma optimize("",off)
527 // RESUME [0]|NEXT|label
529 void SbiParser::Resume()
537 aGen
.Gen( _RESUME
, 0 );
540 aGen
.Gen( _RESUME
, 1 );
546 aGen
.Gen( _RESUME
, 0 );
552 nLbl
= pProc
->GetLabels().Reference( aSym
);
553 aGen
.Gen( _RESUME
, nLbl
);
558 Error( SbERR_LABEL_EXPECTED
);