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