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