Line data Source code
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 "sbcomp.hxx"
22 :
23 : // Single-line IF and Multiline IF
24 :
25 2 : void SbiParser::If()
26 : {
27 : sal_uInt32 nEndLbl;
28 2 : SbiToken eTok = NIL;
29 : // ignore end-tokens
30 2 : SbiExpression aCond( this );
31 2 : aCond.Gen();
32 2 : TestToken( THEN );
33 2 : if( IsEoln( Next() ) )
34 : {
35 : // At the end of each block a jump to ENDIF must be inserted,
36 : // so that the condition is not evaluated again at ELSEIF.
37 : // The table collects all jump points.
38 : #define JMP_TABLE_SIZE 100
39 : sal_uInt32 pnJmpToEndLbl[JMP_TABLE_SIZE]; // 100 ELSEIFs allowed
40 2 : sal_uInt16 iJmp = 0; // current table index
41 :
42 : // multiline IF
43 2 : nEndLbl = aGen.Gen( _JUMPF, 0 );
44 2 : eTok = Peek();
45 16 : while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
46 8 : !bAbort && Parse() )
47 : {
48 4 : eTok = Peek();
49 4 : if( IsEof() )
50 : {
51 0 : Error( SbERR_BAD_BLOCK, IF ); bAbort = sal_True; return;
52 : }
53 : }
54 4 : while( eTok == ELSEIF )
55 : {
56 : // jump to ENDIF in case of a successful IF/ELSEIF
57 0 : if( iJmp >= JMP_TABLE_SIZE )
58 : {
59 0 : Error( SbERR_PROG_TOO_LARGE ); bAbort = sal_True; return;
60 : }
61 0 : pnJmpToEndLbl[iJmp++] = aGen.Gen( _JUMP, 0 );
62 :
63 0 : Next();
64 0 : aGen.BackChain( nEndLbl );
65 :
66 0 : aGen.Statement();
67 0 : SbiExpression* pCond = new SbiExpression( this );
68 0 : pCond->Gen();
69 0 : nEndLbl = aGen.Gen( _JUMPF, 0 );
70 0 : delete pCond;
71 0 : TestToken( THEN );
72 0 : eTok = Peek();
73 0 : while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
74 0 : !bAbort && Parse() )
75 : {
76 0 : eTok = Peek();
77 0 : if( IsEof() )
78 : {
79 0 : Error( SbERR_BAD_BLOCK, ELSEIF ); bAbort = sal_True; return;
80 : }
81 : }
82 : }
83 2 : if( eTok == ELSE )
84 : {
85 2 : Next();
86 2 : sal_uInt32 nElseLbl = nEndLbl;
87 2 : nEndLbl = aGen.Gen( _JUMP, 0 );
88 2 : aGen.BackChain( nElseLbl );
89 :
90 2 : aGen.Statement();
91 2 : StmntBlock( ENDIF );
92 : }
93 0 : else if( eTok == ENDIF )
94 0 : Next();
95 :
96 :
97 4 : while( iJmp > 0 )
98 : {
99 0 : iJmp--;
100 0 : aGen.BackChain( pnJmpToEndLbl[iJmp] );
101 : }
102 : }
103 : else
104 : {
105 : // single line IF
106 0 : bSingleLineIf = true;
107 0 : nEndLbl = aGen.Gen( _JUMPF, 0 );
108 0 : Push( eCurTok );
109 0 : while( !bAbort )
110 : {
111 0 : if( !Parse() ) break;
112 0 : eTok = Peek();
113 0 : if( eTok == ELSE || eTok == EOLN || eTok == REM )
114 0 : break;
115 : }
116 0 : if( eTok == ELSE )
117 : {
118 0 : Next();
119 0 : sal_uInt32 nElseLbl = nEndLbl;
120 0 : nEndLbl = aGen.Gen( _JUMP, 0 );
121 0 : aGen.BackChain( nElseLbl );
122 0 : while( !bAbort )
123 : {
124 0 : if( !Parse() ) break;
125 0 : eTok = Peek();
126 0 : if( eTok == EOLN )
127 0 : break;
128 : }
129 : }
130 0 : bSingleLineIf = false;
131 : }
132 2 : aGen.BackChain( nEndLbl );
133 : }
134 :
135 : // ELSE/ELSEIF/ENDIF without IF
136 :
137 0 : void SbiParser::NoIf()
138 : {
139 0 : Error( SbERR_NO_IF );
140 0 : StmntBlock( ENDIF );
141 0 : }
142 :
143 : // DO WHILE...LOOP
144 : // DO ... LOOP WHILE
145 :
146 0 : void SbiParser::DoLoop()
147 : {
148 0 : sal_uInt32 nStartLbl = aGen.GetPC();
149 0 : OpenBlock( DO );
150 0 : SbiToken eTok = Next();
151 0 : if( IsEoln( eTok ) )
152 : {
153 : // DO ... LOOP [WHILE|UNTIL expr]
154 0 : StmntBlock( LOOP );
155 0 : eTok = Next();
156 0 : if( eTok == UNTIL || eTok == WHILE )
157 : {
158 0 : SbiExpression aExpr( this );
159 0 : aExpr.Gen();
160 0 : aGen.Gen( eTok == UNTIL ? _JUMPF : _JUMPT, nStartLbl );
161 : } else
162 0 : if (eTok == EOLN || eTok == REM)
163 0 : aGen.Gen (_JUMP, nStartLbl);
164 : else
165 0 : Error( SbERR_EXPECTED, WHILE );
166 : }
167 : else
168 : {
169 : // DO [WHILE|UNTIL expr] ... LOOP
170 0 : if( eTok == UNTIL || eTok == WHILE )
171 : {
172 0 : SbiExpression aCond( this );
173 0 : aCond.Gen();
174 : }
175 0 : sal_uInt32 nEndLbl = aGen.Gen( eTok == UNTIL ? _JUMPT : _JUMPF, 0 );
176 0 : StmntBlock( LOOP );
177 0 : TestEoln();
178 0 : aGen.Gen( _JUMP, nStartLbl );
179 0 : aGen.BackChain( nEndLbl );
180 : }
181 0 : CloseBlock();
182 0 : }
183 :
184 : // WHILE ... WEND
185 :
186 0 : void SbiParser::While()
187 : {
188 0 : SbiExpression aCond( this );
189 0 : sal_uInt32 nStartLbl = aGen.GetPC();
190 0 : aCond.Gen();
191 0 : sal_uInt32 nEndLbl = aGen.Gen( _JUMPF, 0 );
192 0 : StmntBlock( WEND );
193 0 : aGen.Gen( _JUMP, nStartLbl );
194 0 : aGen.BackChain( nEndLbl );
195 0 : }
196 :
197 : // FOR var = expr TO expr STEP
198 :
199 0 : void SbiParser::For()
200 : {
201 0 : bool bForEach = ( Peek() == EACH );
202 0 : if( bForEach )
203 0 : Next();
204 0 : SbiExpression aLvalue( this, SbOPERAND );
205 0 : aLvalue.Gen(); // variable on the Stack
206 :
207 0 : if( bForEach )
208 : {
209 0 : TestToken( _IN_ );
210 0 : SbiExpression aCollExpr( this, SbOPERAND );
211 0 : aCollExpr.Gen(); // Colletion var to for stack
212 0 : TestEoln();
213 0 : aGen.Gen( _INITFOREACH );
214 : }
215 : else
216 : {
217 0 : TestToken( EQ );
218 0 : SbiExpression aStartExpr( this );
219 0 : aStartExpr.Gen();
220 0 : TestToken( TO );
221 0 : SbiExpression aStopExpr( this );
222 0 : aStopExpr.Gen();
223 0 : if( Peek() == STEP )
224 : {
225 0 : Next();
226 0 : SbiExpression aStepExpr( this );
227 0 : aStepExpr.Gen();
228 : }
229 : else
230 : {
231 0 : SbiExpression aOne( this, 1, SbxINTEGER );
232 0 : aOne.Gen();
233 : }
234 0 : TestEoln();
235 : // The stack has all 4 elements now: variable, start, end, increment
236 : // bind start value
237 0 : aGen.Gen( _INITFOR );
238 : }
239 :
240 0 : sal_uInt32 nLoop = aGen.GetPC();
241 : // do tests, maybe free the stack
242 0 : sal_uInt32 nEndTarget = aGen.Gen( _TESTFOR, 0 );
243 0 : OpenBlock( FOR );
244 0 : StmntBlock( NEXT );
245 0 : aGen.Gen( _NEXT );
246 0 : aGen.Gen( _JUMP, nLoop );
247 : // are there variables after NEXT?
248 0 : if( Peek() == SYMBOL )
249 : {
250 0 : SbiExpression aVar( this, SbOPERAND );
251 0 : if( aVar.GetRealVar() != aLvalue.GetRealVar() )
252 0 : Error( SbERR_EXPECTED, aLvalue.GetRealVar()->GetName() );
253 : }
254 0 : aGen.BackChain( nEndTarget );
255 0 : CloseBlock();
256 0 : }
257 :
258 : // WITH .. END WITH
259 :
260 0 : void SbiParser::With()
261 : {
262 0 : SbiExpression aVar( this, SbOPERAND );
263 :
264 0 : SbiExprNode *pNode = aVar.GetExprNode()->GetRealNode();
265 0 : SbiSymDef* pDef = pNode->GetVar();
266 : // Variant, from 27.6.1997, #41090: empty -> must be Object
267 0 : if( pDef->GetType() == SbxVARIANT || pDef->GetType() == SbxEMPTY )
268 0 : pDef->SetType( SbxOBJECT );
269 0 : else if( pDef->GetType() != SbxOBJECT )
270 0 : Error( SbERR_NEEDS_OBJECT );
271 :
272 :
273 0 : pNode->SetType( SbxOBJECT );
274 :
275 0 : OpenBlock( NIL, aVar.GetExprNode() );
276 0 : StmntBlock( ENDWITH );
277 0 : CloseBlock();
278 0 : }
279 :
280 : // LOOP/NEXT/WEND without construct
281 :
282 0 : void SbiParser::BadBlock()
283 : {
284 0 : if( eEndTok )
285 0 : Error( SbERR_BAD_BLOCK, eEndTok );
286 : else
287 0 : Error( SbERR_BAD_BLOCK, "Loop/Next/Wend" );
288 0 : }
289 :
290 : // On expr Goto/Gosub n,n,n...
291 :
292 0 : void SbiParser::OnGoto()
293 : {
294 0 : SbiExpression aCond( this );
295 0 : aCond.Gen();
296 0 : sal_uInt32 nLabelsTarget = aGen.Gen( _ONJUMP, 0 );
297 0 : SbiToken eTok = Next();
298 0 : if( eTok != GOTO && eTok != GOSUB )
299 : {
300 0 : Error( SbERR_EXPECTED, "GoTo/GoSub" );
301 0 : eTok = GOTO;
302 : }
303 :
304 0 : sal_uInt32 nLbl = 0;
305 0 : do
306 : {
307 0 : Next(); // get label
308 0 : if( MayBeLabel() )
309 : {
310 0 : sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
311 0 : aGen.Gen( _JUMP, nOff );
312 0 : nLbl++;
313 : }
314 0 : else Error( SbERR_LABEL_EXPECTED );
315 : }
316 0 : while( !bAbort && TestComma() );
317 0 : if( eTok == GOSUB )
318 0 : nLbl |= 0x8000;
319 0 : aGen.Patch( nLabelsTarget, nLbl );
320 0 : }
321 :
322 : // GOTO/GOSUB
323 :
324 0 : void SbiParser::Goto()
325 : {
326 0 : SbiOpcode eOp = eCurTok == GOTO ? _JUMP : _GOSUB;
327 0 : Next();
328 0 : if( MayBeLabel() )
329 : {
330 0 : sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
331 0 : aGen.Gen( eOp, nOff );
332 : }
333 0 : else Error( SbERR_LABEL_EXPECTED );
334 0 : }
335 :
336 : // RETURN [label]
337 :
338 0 : void SbiParser::Return()
339 : {
340 0 : Next();
341 0 : if( MayBeLabel() )
342 : {
343 0 : sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
344 0 : aGen.Gen( _RETURN, nOff );
345 : }
346 0 : else aGen.Gen( _RETURN, 0 );
347 0 : }
348 :
349 : // SELECT CASE
350 :
351 0 : void SbiParser::Select()
352 : {
353 0 : TestToken( CASE );
354 0 : SbiExpression aCase( this );
355 0 : SbiToken eTok = NIL;
356 0 : aCase.Gen();
357 0 : aGen.Gen( _CASE );
358 0 : TestEoln();
359 0 : sal_uInt32 nNextTarget = 0;
360 0 : sal_uInt32 nDoneTarget = 0;
361 0 : sal_Bool bElse = sal_False;
362 :
363 0 : while( !bAbort )
364 : {
365 0 : eTok = Next();
366 0 : if( eTok == CASE )
367 : {
368 0 : if( nNextTarget )
369 0 : aGen.BackChain( nNextTarget ), nNextTarget = 0;
370 0 : aGen.Statement();
371 :
372 0 : sal_Bool bDone = sal_False;
373 0 : sal_uInt32 nTrueTarget = 0;
374 0 : if( Peek() == ELSE )
375 : {
376 : // CASE ELSE
377 0 : Next();
378 0 : bElse = sal_True;
379 : }
380 0 : else while( !bDone )
381 : {
382 0 : if( bElse )
383 0 : Error( SbERR_SYNTAX );
384 0 : SbiToken eTok2 = Peek();
385 0 : if( eTok2 == IS || ( eTok2 >= EQ && eTok2 <= GE ) )
386 : { // CASE [IS] operator expr
387 0 : if( eTok2 == IS )
388 0 : Next();
389 0 : eTok2 = Peek();
390 0 : if( eTok2 < EQ || eTok2 > GE )
391 0 : Error( SbERR_SYNTAX );
392 0 : else Next();
393 0 : SbiExpression aCompare( this );
394 0 : aCompare.Gen();
395 : nTrueTarget = aGen.Gen(
396 : _CASEIS, nTrueTarget,
397 : sal::static_int_cast< sal_uInt16 >(
398 0 : SbxEQ + ( eTok2 - EQ ) ) );
399 : }
400 : else
401 : { // CASE expr | expr TO expr
402 0 : SbiExpression aCase1( this );
403 0 : aCase1.Gen();
404 0 : if( Peek() == TO )
405 : {
406 : // CASE a TO b
407 0 : Next();
408 0 : SbiExpression aCase2( this );
409 0 : aCase2.Gen();
410 0 : nTrueTarget = aGen.Gen( _CASETO, nTrueTarget );
411 : }
412 : else
413 : // CASE a
414 0 : nTrueTarget = aGen.Gen( _CASEIS, nTrueTarget, SbxEQ );
415 :
416 : }
417 0 : if( Peek() == COMMA ) Next();
418 0 : else TestEoln(), bDone = sal_True;
419 : }
420 :
421 0 : if( !bElse )
422 : {
423 0 : nNextTarget = aGen.Gen( _JUMP, nNextTarget );
424 0 : aGen.BackChain( nTrueTarget );
425 : }
426 : // build the statement body
427 0 : while( !bAbort )
428 : {
429 0 : eTok = Peek();
430 0 : if( eTok == CASE || eTok == ENDSELECT )
431 0 : break;
432 0 : if( !Parse() ) goto done;
433 0 : eTok = Peek();
434 0 : if( eTok == CASE || eTok == ENDSELECT )
435 0 : break;
436 : }
437 0 : if( !bElse )
438 0 : nDoneTarget = aGen.Gen( _JUMP, nDoneTarget );
439 : }
440 0 : else if( !IsEoln( eTok ) )
441 0 : break;
442 : }
443 : done:
444 0 : if( eTok != ENDSELECT )
445 0 : Error( SbERR_EXPECTED, ENDSELECT );
446 0 : if( nNextTarget )
447 0 : aGen.BackChain( nNextTarget );
448 0 : aGen.BackChain( nDoneTarget );
449 0 : aGen.Gen( _ENDCASE );
450 0 : }
451 :
452 : // ON Error/Variable
453 :
454 : #ifdef _MSC_VER
455 : #pragma optimize("",off)
456 : #endif
457 :
458 0 : void SbiParser::On()
459 : {
460 0 : SbiToken eTok = Peek();
461 0 : OUString aString = SbiTokenizer::Symbol(eTok);
462 0 : if (aString.equalsIgnoreAsciiCase("ERROR"))
463 : {
464 0 : eTok = _ERROR_; // Error comes as SYMBOL
465 : }
466 0 : if( eTok != _ERROR_ && eTok != LOCAL )
467 : {
468 0 : OnGoto();
469 : }
470 : else
471 : {
472 0 : if( eTok == LOCAL )
473 : {
474 0 : Next();
475 : }
476 0 : Next (); // no more TestToken, as there'd be an error otherwise
477 :
478 0 : Next(); // get token after error
479 0 : if( eCurTok == GOTO )
480 : {
481 : // ON ERROR GOTO label|0
482 0 : Next();
483 0 : bool bError_ = false;
484 0 : if( MayBeLabel() )
485 : {
486 0 : if( eCurTok == NUMBER && !nVal )
487 : {
488 0 : aGen.Gen( _STDERROR );
489 : }
490 : else
491 : {
492 0 : sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
493 0 : aGen.Gen( _ERRHDL, nOff );
494 : }
495 : }
496 0 : else if( eCurTok == MINUS )
497 : {
498 0 : Next();
499 0 : if( eCurTok == NUMBER && nVal == 1 )
500 : {
501 0 : aGen.Gen( _STDERROR );
502 : }
503 : else
504 : {
505 0 : bError_ = true;
506 : }
507 : }
508 0 : if( bError_ )
509 : {
510 0 : Error( SbERR_LABEL_EXPECTED );
511 : }
512 : }
513 0 : else if( eCurTok == RESUME )
514 : {
515 0 : TestToken( NEXT );
516 0 : aGen.Gen( _NOERROR );
517 : }
518 0 : else Error( SbERR_EXPECTED, "GoTo/Resume" );
519 0 : }
520 0 : }
521 :
522 : #ifdef _MSC_VER
523 : #pragma optimize("",off)
524 : #endif
525 :
526 : // RESUME [0]|NEXT|label
527 :
528 0 : void SbiParser::Resume()
529 : {
530 : sal_uInt32 nLbl;
531 :
532 0 : switch( Next() )
533 : {
534 : case EOS:
535 : case EOLN:
536 0 : aGen.Gen( _RESUME, 0 );
537 0 : break;
538 : case NEXT:
539 0 : aGen.Gen( _RESUME, 1 );
540 0 : Next();
541 0 : break;
542 : case NUMBER:
543 0 : if( !nVal )
544 : {
545 0 : aGen.Gen( _RESUME, 0 );
546 0 : break;
547 : } // fall thru
548 : case SYMBOL:
549 0 : if( MayBeLabel() )
550 : {
551 0 : nLbl = pProc->GetLabels().Reference( aSym );
552 0 : aGen.Gen( _RESUME, nLbl );
553 0 : Next();
554 0 : break;
555 : } // fall thru
556 : default:
557 0 : Error( SbERR_LABEL_EXPECTED );
558 : }
559 0 : }
560 :
561 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|