xref: /trunk/main/basic/source/comp/loops.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basic.hxx"
30 
31 #include "sbcomp.hxx"
32 
33 // Single-line IF und Multiline IF
34 
35 void SbiParser::If()
36 {
37 	sal_uInt32 nEndLbl;
38 	SbiToken eTok = NIL;
39 	// Ende-Tokens ignorieren:
40 	SbiExpression aCond( this );
41 	aCond.Gen();
42 	TestToken( THEN );
43 	if( IsEoln( Next() ) )
44 	{
45 		// AB 13.5.1996: #27720# Am Ende jeden Blocks muss ein Jump zu ENDIF
46 		// eingefuegt werden, damit bei ELSEIF nicht erneut die Bedingung
47 		// ausgewertet wird. Die Tabelle nimmt alle Absprungstellen auf.
48 #define JMP_TABLE_SIZE 100
49 		sal_uInt32 pnJmpToEndLbl[JMP_TABLE_SIZE];	// 100 ELSEIFs zulaessig
50 		sal_uInt16 iJmp = 0;						// aktueller Tabellen-Index
51 
52 		// multiline IF
53 		nEndLbl = aGen.Gen( _JUMPF, 0 );
54 		eTok = Peek();
55 		while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
56 				!bAbort && Parse() )
57 		{
58 			eTok = Peek();
59 			if( IsEof() )
60 			{
61 				Error( SbERR_BAD_BLOCK, IF ); bAbort = sal_True; return;
62 			}
63 		}
64 		// ELSEIF?
65 		while( eTok == ELSEIF )
66 		{
67 			// #27720# Bei erfolgreichem IF/ELSEIF auf ENDIF springen
68 			if( iJmp >=	JMP_TABLE_SIZE )
69 			{
70 				Error( SbERR_PROG_TOO_LARGE );	bAbort = sal_True; 	return;
71 			}
72 			pnJmpToEndLbl[iJmp++] = aGen.Gen( _JUMP, 0 );
73 
74 			Next();
75 			aGen.BackChain( nEndLbl );
76 
77 			aGen.Statement();
78 			SbiExpression* pCond = new SbiExpression( this );
79 			pCond->Gen();
80 			nEndLbl = aGen.Gen( _JUMPF, 0 );
81 			delete pCond;
82 			TestToken( THEN );
83 			eTok = Peek();
84 			while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
85 					!bAbort && Parse() )
86 			{
87 				eTok = Peek();
88 				if( IsEof() )
89 				{
90 					Error( SbERR_BAD_BLOCK, ELSEIF );  bAbort = sal_True; return;
91 				}
92 			}
93 		}
94 		if( eTok == ELSE )
95 		{
96 			Next();
97 			sal_uInt32 nElseLbl = nEndLbl;
98 			nEndLbl = aGen.Gen( _JUMP, 0 );
99 			aGen.BackChain( nElseLbl );
100 
101 			aGen.Statement();
102 			StmntBlock( ENDIF );
103 		}
104 		else if( eTok == ENDIF )
105 			Next();
106 
107 		// #27720# Jmp-Tabelle abarbeiten
108 		while( iJmp > 0 )
109 		{
110 			iJmp--;
111 			aGen.BackChain( pnJmpToEndLbl[iJmp] );
112 		}
113 	}
114 	else
115 	{
116 		// single line IF
117 		bSingleLineIf = sal_True;
118 		nEndLbl = aGen.Gen( _JUMPF, 0 );
119 		Push( eCurTok );
120 		while( !bAbort )
121 		{
122 			if( !Parse() ) break;
123 			eTok = Peek();
124 			if( eTok == ELSE || eTok == EOLN || eTok == REM )
125 				break;
126 		}
127 		if( eTok == ELSE )
128 		{
129 			Next();
130 			sal_uInt32 nElseLbl = nEndLbl;
131 			nEndLbl = aGen.Gen( _JUMP, 0 );
132 			aGen.BackChain( nElseLbl );
133 			while( !bAbort )
134 			{
135 				if( !Parse() ) break;
136 				eTok = Peek();
137 				if( eTok == EOLN )
138 					break;
139 			}
140 		}
141 		bSingleLineIf = sal_False;
142 	}
143 	aGen.BackChain( nEndLbl );
144 }
145 
146 // ELSE/ELSEIF/ENDIF ohne IF
147 
148 void SbiParser::NoIf()
149 {
150 	Error( SbERR_NO_IF );
151 	StmntBlock( ENDIF );
152 }
153 
154 // DO WHILE...LOOP
155 // DO ... LOOP WHILE
156 
157 void SbiParser::DoLoop()
158 {
159 	sal_uInt32 nStartLbl = aGen.GetPC();
160 	OpenBlock( DO );
161 	SbiToken eTok = Next();
162 	if( IsEoln( eTok ) )
163 	{
164 		// DO ... LOOP [WHILE|UNTIL expr]
165 		StmntBlock( LOOP );
166 		eTok = Next();
167 		if( eTok == UNTIL || eTok == WHILE )
168 		{
169 			SbiExpression aExpr( this );
170 			aExpr.Gen();
171 			aGen.Gen( eTok == UNTIL ? _JUMPF : _JUMPT, nStartLbl );
172 		} else
173 			if (eTok == EOLN || eTok == REM)
174 				aGen.Gen (_JUMP, nStartLbl);
175 			else
176 				Error( SbERR_EXPECTED, WHILE );
177 	}
178 	else
179 	{
180 		// DO [WHILE|UNTIL expr] ... LOOP
181 		if( eTok == UNTIL || eTok == WHILE )
182 		{
183 			SbiExpression aCond( this );
184 			aCond.Gen();
185 		}
186 		sal_uInt32 nEndLbl = aGen.Gen( eTok == UNTIL ? _JUMPT : _JUMPF, 0 );
187 		StmntBlock( LOOP );
188 		TestEoln();
189 		aGen.Gen( _JUMP, nStartLbl );
190 		aGen.BackChain( nEndLbl );
191 	}
192 	CloseBlock();
193 }
194 
195 // WHILE ... WEND
196 
197 void SbiParser::While()
198 {
199 	SbiExpression aCond( this );
200 	sal_uInt32 nStartLbl = aGen.GetPC();
201 	aCond.Gen();
202 	sal_uInt32 nEndLbl = aGen.Gen( _JUMPF, 0 );
203 	StmntBlock( WEND );
204 	aGen.Gen( _JUMP, nStartLbl );
205 	aGen.BackChain( nEndLbl );
206 }
207 
208 // FOR var = expr TO expr STEP
209 
210 void SbiParser::For()
211 {
212 	bool bForEach = ( Peek() == EACH );
213 	if( bForEach )
214 		Next();
215 	SbiExpression aLvalue( this, SbOPERAND );
216 	aLvalue.Gen();		// Variable auf dem Stack
217 
218 	if( bForEach )
219 	{
220 		TestToken( _IN_ );
221 		SbiExpression aCollExpr( this, SbOPERAND );
222 		aCollExpr.Gen();	// Colletion var to for stack
223 		TestEoln();
224 		aGen.Gen( _INITFOREACH );
225 	}
226 	else
227 	{
228 		TestToken( EQ );
229 		SbiExpression aStartExpr( this );
230 		aStartExpr.Gen();	// Startausdruck auf dem Stack
231 		TestToken( TO );
232 		SbiExpression aStopExpr( this );
233 		aStopExpr.Gen();	// Endausdruck auf dem Stack
234 		if( Peek() == STEP )
235 		{
236 			Next();
237 			SbiExpression aStepExpr( this );
238 			aStepExpr.Gen();
239 		}
240 		else
241 		{
242 			SbiExpression aOne( this, 1, SbxINTEGER );
243 			aOne.Gen();
244 		}
245 		TestEoln();
246 		// Der Stack hat jetzt 4 Elemente: Variable, Start, Ende, Inkrement
247 		// Startwert binden
248 		aGen.Gen( _INITFOR );
249 	}
250 
251 	sal_uInt32 nLoop = aGen.GetPC();
252 	// Test durchfuehren, evtl. Stack freigeben
253 	sal_uInt32 nEndTarget = aGen.Gen( _TESTFOR, 0 );
254 	OpenBlock( FOR );
255 	StmntBlock( NEXT );
256 	aGen.Gen( _NEXT );
257 	aGen.Gen( _JUMP, nLoop );
258 	// Kommen Variable nach NEXT?
259 	if( Peek() == SYMBOL )
260 	{
261 		SbiExpression aVar( this, SbOPERAND );
262 		if( aVar.GetRealVar() != aLvalue.GetRealVar() )
263 			Error( SbERR_EXPECTED, aLvalue.GetRealVar()->GetName() );
264 	}
265 	aGen.BackChain( nEndTarget );
266 	CloseBlock();
267 }
268 
269 // WITH .. END WITH
270 
271 void SbiParser::With()
272 {
273 	SbiExpression aVar( this, SbOPERAND );
274 
275 	// Letzten Knoten in der Objekt-Kette ueberpruefen
276 	SbiExprNode *pNode = aVar.GetExprNode()->GetRealNode();
277 	SbiSymDef* pDef = pNode->GetVar();
278 	// Variant, AB 27.6.1997, #41090: bzw. empty -> mu� Object sein
279 	if( pDef->GetType() == SbxVARIANT || pDef->GetType() == SbxEMPTY )
280 		pDef->SetType( SbxOBJECT );
281 	else if( pDef->GetType() != SbxOBJECT )
282 		Error( SbERR_NEEDS_OBJECT );
283 
284 	// Knoten auch auf SbxOBJECT setzen, damit spaeter Gen() klappt
285 	pNode->SetType( SbxOBJECT );
286 
287 	OpenBlock( NIL, aVar.GetExprNode() );
288 	StmntBlock( ENDWITH );
289 	CloseBlock();
290 }
291 
292 // LOOP/NEXT/WEND ohne Konstrukt
293 
294 void SbiParser::BadBlock()
295 {
296 	if( eEndTok )
297 		Error( SbERR_BAD_BLOCK, eEndTok );
298 	else
299 		Error( SbERR_BAD_BLOCK, "Loop/Next/Wend" );
300 }
301 
302 // On expr Goto/Gosub n,n,n...
303 
304 void SbiParser::OnGoto()
305 {
306 	SbiExpression aCond( this );
307 	aCond.Gen();
308 	sal_uInt32 nLabelsTarget = aGen.Gen( _ONJUMP, 0 );
309 	SbiToken eTok = Next();
310 	if( eTok != GOTO && eTok != GOSUB )
311 	{
312 		Error( SbERR_EXPECTED, "GoTo/GoSub" );
313 		eTok = GOTO;
314 	}
315 	// Label-Tabelle einlesen:
316 	sal_uInt32 nLbl = 0;
317 	do
318 	{
319 		SbiToken eTok2 = NIL;
320 		eTok2 = Next();	// Label holen
321 		if( MayBeLabel() )
322 		{
323 			sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
324 			aGen.Gen( _JUMP, nOff );
325 			nLbl++;
326 		}
327 		else Error( SbERR_LABEL_EXPECTED );
328 	}
329 	while( !bAbort && TestComma() );
330 	if( eTok == GOSUB )
331 		nLbl |= 0x8000;
332 	aGen.Patch( nLabelsTarget, nLbl );
333 }
334 
335 // GOTO/GOSUB
336 
337 void SbiParser::Goto()
338 {
339 	SbiOpcode eOp = eCurTok == GOTO ? _JUMP : _GOSUB;
340 	Next();
341 	if( MayBeLabel() )
342 	{
343 		sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
344 		aGen.Gen( eOp, nOff );
345 	}
346 	else Error( SbERR_LABEL_EXPECTED );
347 }
348 
349 // RETURN [label]
350 
351 void SbiParser::Return()
352 {
353 	Next();
354 	if( MayBeLabel() )
355 	{
356 		sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
357 		aGen.Gen( _RETURN, nOff );
358 	}
359 	else aGen.Gen( _RETURN, 0 );
360 }
361 
362 // SELECT CASE
363 
364 void SbiParser::Select()
365 {
366 	TestToken( CASE );
367 	SbiExpression aCase( this );
368 	SbiToken eTok = NIL;
369 	aCase.Gen();
370 	aGen.Gen( _CASE );
371 	TestEoln();
372 	sal_uInt32 nNextTarget = 0;
373 	sal_uInt32 nDoneTarget = 0;
374 	sal_Bool bElse = sal_False;
375 	// Die Cases einlesen:
376 	while( !bAbort )
377 	{
378 		eTok = Next();
379 		if( eTok == CASE )
380 		{
381 			if( nNextTarget )
382 				aGen.BackChain( nNextTarget ), nNextTarget = 0;
383 			aGen.Statement();
384 			// Jeden Case einlesen
385 			sal_Bool bDone = sal_False;
386 			sal_uInt32 nTrueTarget = 0;
387 			if( Peek() == ELSE )
388 			{
389 				// CASE ELSE
390 				Next();
391 				bElse = sal_True;
392 			}
393 			else while( !bDone )
394 			{
395 				if( bElse )
396 					Error( SbERR_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( SbERR_SYNTAX );
405 					else Next();
406 					SbiExpression aCompare( this );
407 					aCompare.Gen();
408 					nTrueTarget = aGen.Gen(
409                         _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( _CASETO, nTrueTarget );
424 					}
425 					else
426 						// CASE a
427 						nTrueTarget = aGen.Gen( _CASEIS, nTrueTarget, SbxEQ );
428 
429 				}
430 				if( Peek() == COMMA ) Next();
431 				else TestEoln(), bDone = sal_True;
432 			}
433 			// Alle Cases abgearbeitet
434 			if( !bElse )
435 			{
436 				nNextTarget = aGen.Gen( _JUMP, nNextTarget );
437 				aGen.BackChain( nTrueTarget );
438 			}
439 			// den Statement-Rumpf bauen
440 			while( !bAbort )
441 			{
442 				eTok = Peek();
443 				if( eTok == CASE || eTok == ENDSELECT )
444 					break;
445 				if( !Parse() ) goto done;
446 				eTok = Peek();
447 				if( eTok == CASE || eTok == ENDSELECT )
448 					break;
449 			}
450 			if( !bElse )
451 				nDoneTarget = aGen.Gen( _JUMP, nDoneTarget );
452 		}
453 		else if( !IsEoln( eTok ) )
454 			break;
455 	}
456 done:
457 	if( eTok != ENDSELECT )
458 		Error( SbERR_EXPECTED, ENDSELECT );
459 	if( nNextTarget )
460 		aGen.BackChain( nNextTarget );
461 	aGen.BackChain( nDoneTarget );
462 	aGen.Gen( _ENDCASE );
463 }
464 
465 // ON Error/Variable
466 
467 #ifdef _MSC_VER
468 #pragma optimize("",off)
469 #endif
470 
471 void SbiParser::On()
472 {
473 	SbiToken eTok = Peek();
474 	String aString = SbiTokenizer::Symbol(eTok);
475 	if (aString.EqualsIgnoreCaseAscii("ERROR"))
476 	//if (!aString.ICompare("ERROR"))
477 		eTok = _ERROR_;	// Error kommt als SYMBOL
478 	if( eTok != _ERROR_ && eTok != LOCAL ) OnGoto();
479 	else
480 	{
481 		if( eTok == LOCAL ) Next();
482 		Next (); // Kein TestToken mehr, da es sonst einen Fehler gibt
483 
484 		Next();	// Token nach Error holen
485 		if( eCurTok == GOTO )
486 		{
487 			// ON ERROR GOTO label|0
488 			Next();
489 			bool bError_ = false;
490 			if( MayBeLabel() )
491 			{
492 				if( eCurTok == NUMBER && !nVal )
493 					aGen.Gen( _STDERROR );
494 				else
495 				{
496 					sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
497 					aGen.Gen( _ERRHDL, nOff );
498 				}
499 			}
500 			else if( eCurTok == MINUS )
501 			{
502 				Next();
503 				if( eCurTok == NUMBER && nVal == 1 )
504 					aGen.Gen( _STDERROR );
505 				else
506 					bError_ = true;
507 			}
508 			if( bError_ )
509 				Error( SbERR_LABEL_EXPECTED );
510 		}
511 		else if( eCurTok == RESUME )
512 		{
513 			TestToken( NEXT );
514 			aGen.Gen( _NOERROR );
515 		}
516 		else Error( SbERR_EXPECTED, "GoTo/Resume" );
517 	}
518 }
519 
520 #ifdef _MSC_VER
521 #pragma optimize("",off)
522 #endif
523 
524 // RESUME [0]|NEXT|label
525 
526 void SbiParser::Resume()
527 {
528 	sal_uInt32 nLbl;
529 
530 	switch( Next() )
531 	{
532 		case EOS:
533 		case EOLN:
534 			aGen.Gen( _RESUME, 0 );
535 			break;
536 		case NEXT:
537 			aGen.Gen( _RESUME, 1 );
538 			Next();
539 			break;
540 		case NUMBER:
541 			if( !nVal )
542 			{
543 				aGen.Gen( _RESUME, 0 );
544 				break;
545 			} // fall thru
546 		case SYMBOL:
547 			if( MayBeLabel() )
548 			{
549 				nLbl = pProc->GetLabels().Reference( aSym );
550 				aGen.Gen( _RESUME, nLbl );
551 				Next();
552 				break;
553 			} // fall thru
554 		default:
555 			Error( SbERR_LABEL_EXPECTED );
556 	}
557 }
558 
559