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