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 "parser.hxx"
22 : #include <boost/scoped_ptr.hpp>
23 :
24 : // Single-line IF and Multiline IF
25 :
26 399 : void SbiParser::If()
27 : {
28 : sal_uInt32 nEndLbl;
29 399 : SbiToken eTok = NIL;
30 : // ignore end-tokens
31 399 : SbiExpression aCond( this );
32 399 : aCond.Gen();
33 399 : TestToken( THEN );
34 399 : 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 289 : sal_uInt16 iJmp = 0; // current table index
42 :
43 : // multiline IF
44 289 : nEndLbl = aGen.Gen( _JUMPF, 0 );
45 289 : eTok = Peek();
46 3456 : while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
47 2878 : !bAbort && Parse() )
48 : {
49 863 : eTok = Peek();
50 863 : if( IsEof() )
51 : {
52 0 : Error( SbERR_BAD_BLOCK, IF ); bAbort = true; return;
53 : }
54 : }
55 578 : 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 289 : if( eTok == ELSE )
85 : {
86 124 : Next();
87 124 : sal_uInt32 nElseLbl = nEndLbl;
88 124 : nEndLbl = aGen.Gen( _JUMP, 0 );
89 124 : aGen.BackChain( nElseLbl );
90 :
91 124 : aGen.Statement();
92 124 : StmntBlock( ENDIF );
93 : }
94 165 : else if( eTok == ENDIF )
95 165 : Next();
96 :
97 :
98 578 : while( iJmp > 0 )
99 : {
100 0 : iJmp--;
101 0 : aGen.BackChain( pnJmpToEndLbl[iJmp] );
102 : }
103 : }
104 : else
105 : {
106 : // single line IF
107 110 : bSingleLineIf = true;
108 110 : nEndLbl = aGen.Gen( _JUMPF, 0 );
109 110 : Push( eCurTok );
110 220 : while( !bAbort )
111 : {
112 110 : if( !Parse() ) break;
113 110 : eTok = Peek();
114 110 : if( eTok == ELSE || eTok == EOLN || eTok == REM )
115 : break;
116 : }
117 110 : 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 110 : bSingleLineIf = false;
132 : }
133 399 : 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 1 : void SbiParser::DoLoop()
148 : {
149 1 : sal_uInt32 nStartLbl = aGen.GetPC();
150 1 : OpenBlock( DO );
151 1 : SbiToken eTok = Next();
152 1 : 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 1 : if( eTok == UNTIL || eTok == WHILE )
172 : {
173 1 : SbiExpression aCond( this );
174 1 : aCond.Gen();
175 : }
176 1 : sal_uInt32 nEndLbl = aGen.Gen( eTok == UNTIL ? _JUMPT : _JUMPF, 0 );
177 1 : StmntBlock( LOOP );
178 1 : TestEoln();
179 1 : aGen.Gen( _JUMP, nStartLbl );
180 1 : aGen.BackChain( nEndLbl );
181 : }
182 1 : CloseBlock();
183 1 : }
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 41 : void SbiParser::For()
201 : {
202 41 : bool bForEach = ( Peek() == EACH );
203 41 : if( bForEach )
204 2 : Next();
205 41 : SbiExpression aLvalue( this, SbOPERAND );
206 41 : aLvalue.Gen(); // variable on the Stack
207 :
208 41 : if( bForEach )
209 : {
210 2 : TestToken( _IN_ );
211 2 : SbiExpression aCollExpr( this, SbOPERAND );
212 2 : aCollExpr.Gen(); // Colletion var to for stack
213 2 : TestEoln();
214 2 : aGen.Gen( _INITFOREACH );
215 : }
216 : else
217 : {
218 39 : TestToken( EQ );
219 39 : SbiExpression aStartExpr( this );
220 39 : aStartExpr.Gen();
221 39 : TestToken( TO );
222 78 : SbiExpression aStopExpr( this );
223 39 : aStopExpr.Gen();
224 39 : if( Peek() == STEP )
225 : {
226 0 : Next();
227 0 : SbiExpression aStepExpr( this );
228 0 : aStepExpr.Gen();
229 : }
230 : else
231 : {
232 39 : SbiExpression aOne( this, 1, SbxINTEGER );
233 39 : aOne.Gen();
234 : }
235 39 : TestEoln();
236 : // The stack has all 4 elements now: variable, start, end, increment
237 : // bind start value
238 78 : aGen.Gen( _INITFOR );
239 : }
240 :
241 41 : sal_uInt32 nLoop = aGen.GetPC();
242 : // do tests, maybe free the stack
243 41 : sal_uInt32 nEndTarget = aGen.Gen( _TESTFOR, 0 );
244 41 : OpenBlock( FOR );
245 41 : StmntBlock( NEXT );
246 41 : aGen.Gen( _NEXT );
247 41 : aGen.Gen( _JUMP, nLoop );
248 : // are there variables after NEXT?
249 41 : if( Peek() == SYMBOL )
250 : {
251 37 : SbiExpression aVar( this, SbOPERAND );
252 37 : if( aVar.GetRealVar() != aLvalue.GetRealVar() )
253 0 : Error( SbERR_EXPECTED, aLvalue.GetRealVar()->GetName() );
254 : }
255 41 : aGen.BackChain( nEndTarget );
256 41 : CloseBlock();
257 41 : }
258 :
259 : // WITH .. END WITH
260 :
261 16 : void SbiParser::With()
262 : {
263 16 : SbiExpression aVar( this, SbOPERAND );
264 :
265 16 : SbiExprNode *pNode = aVar.GetExprNode()->GetRealNode();
266 16 : SbiSymDef* pDef = pNode->GetVar();
267 : // Variant, from 27.6.1997, #41090: empty -> must be Object
268 16 : if( pDef->GetType() == SbxVARIANT || pDef->GetType() == SbxEMPTY )
269 1 : pDef->SetType( SbxOBJECT );
270 15 : else if( pDef->GetType() != SbxOBJECT )
271 0 : Error( SbERR_NEEDS_OBJECT );
272 :
273 :
274 16 : pNode->SetType( SbxOBJECT );
275 :
276 16 : OpenBlock( NIL, aVar.GetExprNode() );
277 16 : StmntBlock( ENDWITH );
278 16 : CloseBlock();
279 16 : }
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 3 : void SbiParser::Goto()
326 : {
327 3 : SbiOpcode eOp = eCurTok == GOTO ? _JUMP : _GOSUB;
328 3 : Next();
329 3 : if( MayBeLabel() )
330 : {
331 3 : sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
332 3 : aGen.Gen( eOp, nOff );
333 : }
334 0 : else Error( SbERR_LABEL_EXPECTED );
335 3 : }
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 8 : void SbiParser::Select()
353 : {
354 8 : TestToken( CASE );
355 8 : SbiExpression aCase( this );
356 8 : SbiToken eTok = NIL;
357 8 : aCase.Gen();
358 8 : aGen.Gen( _CASE );
359 8 : TestEoln();
360 8 : sal_uInt32 nNextTarget = 0;
361 8 : sal_uInt32 nDoneTarget = 0;
362 8 : bool bElse = false;
363 :
364 81 : while( !bAbort )
365 : {
366 73 : eTok = Next();
367 73 : if( eTok == CASE )
368 : {
369 65 : if( nNextTarget )
370 57 : aGen.BackChain( nNextTarget ), nNextTarget = 0;
371 65 : aGen.Statement();
372 :
373 65 : bool bDone = false;
374 65 : sal_uInt32 nTrueTarget = 0;
375 65 : if( Peek() == ELSE )
376 : {
377 : // CASE ELSE
378 6 : Next();
379 6 : bElse = true;
380 : }
381 179 : else while( !bDone )
382 : {
383 61 : if( bElse )
384 0 : Error( SbERR_SYNTAX );
385 61 : SbiToken eTok2 = Peek();
386 61 : 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 61 : SbiExpression aCase1( this );
404 61 : aCase1.Gen();
405 61 : 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 61 : nTrueTarget = aGen.Gen( _CASEIS, nTrueTarget, SbxEQ );
416 :
417 : }
418 61 : if( Peek() == COMMA ) Next();
419 59 : else TestEoln(), bDone = true;
420 : }
421 :
422 65 : if( !bElse )
423 : {
424 59 : nNextTarget = aGen.Gen( _JUMP, nNextTarget );
425 59 : aGen.BackChain( nTrueTarget );
426 : }
427 : // build the statement body
428 206 : while( !bAbort )
429 : {
430 141 : eTok = Peek();
431 141 : if( eTok == CASE || eTok == ENDSELECT )
432 : break;
433 141 : if( !Parse() ) goto done;
434 141 : eTok = Peek();
435 141 : if( eTok == CASE || eTok == ENDSELECT )
436 : break;
437 : }
438 65 : if( !bElse )
439 59 : nDoneTarget = aGen.Gen( _JUMP, nDoneTarget );
440 : }
441 8 : else if( !IsEoln( eTok ) )
442 8 : break;
443 : }
444 : done:
445 8 : if( eTok != ENDSELECT )
446 0 : Error( SbERR_EXPECTED, ENDSELECT );
447 8 : if( nNextTarget )
448 2 : aGen.BackChain( nNextTarget );
449 8 : aGen.BackChain( nDoneTarget );
450 8 : aGen.Gen( _ENDCASE );
451 8 : }
452 :
453 : // ON Error/Variable
454 :
455 55 : void SbiParser::On()
456 : {
457 55 : SbiToken eTok = Peek();
458 55 : OUString aString = SbiTokenizer::Symbol(eTok);
459 55 : if (aString.equalsIgnoreAsciiCase("ERROR"))
460 : {
461 49 : eTok = _ERROR_; // Error comes as SYMBOL
462 : }
463 55 : if( eTok != _ERROR_ && eTok != LOCAL )
464 : {
465 0 : OnGoto();
466 : }
467 : else
468 : {
469 55 : if( eTok == LOCAL )
470 : {
471 6 : Next();
472 : }
473 55 : Next (); // no more TestToken, as there'd be an error otherwise
474 :
475 55 : Next(); // get token after error
476 55 : if( eCurTok == GOTO )
477 : {
478 : // ON ERROR GOTO label|0
479 53 : Next();
480 53 : bool bError_ = false;
481 53 : if( MayBeLabel() )
482 : {
483 53 : if( eCurTok == NUMBER && !nVal )
484 : {
485 1 : aGen.Gen( _STDERROR );
486 : }
487 : else
488 : {
489 52 : sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
490 52 : 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 53 : if( bError_ )
506 : {
507 0 : Error( SbERR_LABEL_EXPECTED );
508 : }
509 : }
510 2 : else if( eCurTok == RESUME )
511 : {
512 2 : TestToken( NEXT );
513 2 : aGen.Gen( _NOERROR );
514 : }
515 0 : else Error( SbERR_EXPECTED, "GoTo/Resume" );
516 55 : }
517 55 : }
518 :
519 : // RESUME [0]|NEXT|label
520 :
521 4 : void SbiParser::Resume()
522 : {
523 : sal_uInt32 nLbl;
524 :
525 4 : switch( Next() )
526 : {
527 : case EOS:
528 : case EOLN:
529 0 : aGen.Gen( _RESUME, 0 );
530 0 : break;
531 : case NEXT:
532 4 : aGen.Gen( _RESUME, 1 );
533 4 : Next();
534 4 : break;
535 : case NUMBER:
536 0 : if( !nVal )
537 : {
538 0 : aGen.Gen( _RESUME, 0 );
539 0 : break;
540 : } // fall through
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 through
549 : default:
550 0 : Error( SbERR_LABEL_EXPECTED );
551 : }
552 4 : }
553 :
554 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|