blob: a6f92c4a27092c4460af9b42e20b54fda7b6fc32 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basic.hxx"
#include "sbcomp.hxx"
// Single-line IF und Multiline IF
void SbiParser::If()
{
sal_uInt32 nEndLbl;
SbiToken eTok = NIL;
// Ende-Tokens ignorieren:
SbiExpression aCond( this );
aCond.Gen();
TestToken( THEN );
if( IsEoln( Next() ) )
{
// AB 13.5.1996: #27720# Am Ende jeden Blocks muss ein Jump zu ENDIF
// eingefuegt werden, damit bei ELSEIF nicht erneut die Bedingung
// ausgewertet wird. Die Tabelle nimmt alle Absprungstellen auf.
#define JMP_TABLE_SIZE 100
sal_uInt32 pnJmpToEndLbl[JMP_TABLE_SIZE]; // 100 ELSEIFs zulaessig
sal_uInt16 iJmp = 0; // aktueller Tabellen-Index
// multiline IF
nEndLbl = aGen.Gen( _JUMPF, 0 );
eTok = Peek();
while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
!bAbort && Parse() )
{
eTok = Peek();
if( IsEof() )
{
Error( SbERR_BAD_BLOCK, IF ); bAbort = sal_True; return;
}
}
// ELSEIF?
while( eTok == ELSEIF )
{
// #27720# Bei erfolgreichem IF/ELSEIF auf ENDIF springen
if( iJmp >= JMP_TABLE_SIZE )
{
Error( SbERR_PROG_TOO_LARGE ); bAbort = sal_True; return;
}
pnJmpToEndLbl[iJmp++] = aGen.Gen( _JUMP, 0 );
Next();
aGen.BackChain( nEndLbl );
aGen.Statement();
SbiExpression* pCond = new SbiExpression( this );
pCond->Gen();
nEndLbl = aGen.Gen( _JUMPF, 0 );
delete pCond;
TestToken( THEN );
eTok = Peek();
while( !( eTok == ELSEIF || eTok == ELSE || eTok == ENDIF ) &&
!bAbort && Parse() )
{
eTok = Peek();
if( IsEof() )
{
Error( SbERR_BAD_BLOCK, ELSEIF ); bAbort = sal_True; return;
}
}
}
if( eTok == ELSE )
{
Next();
sal_uInt32 nElseLbl = nEndLbl;
nEndLbl = aGen.Gen( _JUMP, 0 );
aGen.BackChain( nElseLbl );
aGen.Statement();
StmntBlock( ENDIF );
}
else if( eTok == ENDIF )
Next();
// #27720# Jmp-Tabelle abarbeiten
while( iJmp > 0 )
{
iJmp--;
aGen.BackChain( pnJmpToEndLbl[iJmp] );
}
}
else
{
// single line IF
bSingleLineIf = sal_True;
nEndLbl = aGen.Gen( _JUMPF, 0 );
Push( eCurTok );
while( !bAbort )
{
if( !Parse() ) break;
eTok = Peek();
if( eTok == ELSE || eTok == EOLN || eTok == REM )
break;
}
if( eTok == ELSE )
{
Next();
sal_uInt32 nElseLbl = nEndLbl;
nEndLbl = aGen.Gen( _JUMP, 0 );
aGen.BackChain( nElseLbl );
while( !bAbort )
{
if( !Parse() ) break;
eTok = Peek();
if( eTok == EOLN )
break;
}
}
bSingleLineIf = sal_False;
}
aGen.BackChain( nEndLbl );
}
// ELSE/ELSEIF/ENDIF ohne IF
void SbiParser::NoIf()
{
Error( SbERR_NO_IF );
StmntBlock( ENDIF );
}
// DO WHILE...LOOP
// DO ... LOOP WHILE
void SbiParser::DoLoop()
{
sal_uInt32 nStartLbl = aGen.GetPC();
OpenBlock( DO );
SbiToken eTok = Next();
if( IsEoln( eTok ) )
{
// DO ... LOOP [WHILE|UNTIL expr]
StmntBlock( LOOP );
eTok = Next();
if( eTok == UNTIL || eTok == WHILE )
{
SbiExpression aExpr( this );
aExpr.Gen();
aGen.Gen( eTok == UNTIL ? _JUMPF : _JUMPT, nStartLbl );
} else
if (eTok == EOLN || eTok == REM)
aGen.Gen (_JUMP, nStartLbl);
else
Error( SbERR_EXPECTED, WHILE );
}
else
{
// DO [WHILE|UNTIL expr] ... LOOP
if( eTok == UNTIL || eTok == WHILE )
{
SbiExpression aCond( this );
aCond.Gen();
}
sal_uInt32 nEndLbl = aGen.Gen( eTok == UNTIL ? _JUMPT : _JUMPF, 0 );
StmntBlock( LOOP );
TestEoln();
aGen.Gen( _JUMP, nStartLbl );
aGen.BackChain( nEndLbl );
}
CloseBlock();
}
// WHILE ... WEND
void SbiParser::While()
{
SbiExpression aCond( this );
sal_uInt32 nStartLbl = aGen.GetPC();
aCond.Gen();
sal_uInt32 nEndLbl = aGen.Gen( _JUMPF, 0 );
StmntBlock( WEND );
aGen.Gen( _JUMP, nStartLbl );
aGen.BackChain( nEndLbl );
}
// FOR var = expr TO expr STEP
void SbiParser::For()
{
bool bForEach = ( Peek() == EACH );
if( bForEach )
Next();
SbiExpression aLvalue( this, SbOPERAND );
aLvalue.Gen(); // Variable auf dem Stack
if( bForEach )
{
TestToken( _IN_ );
SbiExpression aCollExpr( this, SbOPERAND );
aCollExpr.Gen(); // Colletion var to for stack
TestEoln();
aGen.Gen( _INITFOREACH );
}
else
{
TestToken( EQ );
SbiExpression aStartExpr( this );
aStartExpr.Gen(); // Startausdruck auf dem Stack
TestToken( TO );
SbiExpression aStopExpr( this );
aStopExpr.Gen(); // Endausdruck auf dem Stack
if( Peek() == STEP )
{
Next();
SbiExpression aStepExpr( this );
aStepExpr.Gen();
}
else
{
SbiExpression aOne( this, 1, SbxINTEGER );
aOne.Gen();
}
TestEoln();
// Der Stack hat jetzt 4 Elemente: Variable, Start, Ende, Inkrement
// Startwert binden
aGen.Gen( _INITFOR );
}
sal_uInt32 nLoop = aGen.GetPC();
// Test durchfuehren, evtl. Stack freigeben
sal_uInt32 nEndTarget = aGen.Gen( _TESTFOR, 0 );
OpenBlock( FOR );
StmntBlock( NEXT );
aGen.Gen( _NEXT );
aGen.Gen( _JUMP, nLoop );
// Kommen Variable nach NEXT?
if( Peek() == SYMBOL )
{
SbiExpression aVar( this, SbOPERAND );
if( aVar.GetRealVar() != aLvalue.GetRealVar() )
Error( SbERR_EXPECTED, aLvalue.GetRealVar()->GetName() );
}
aGen.BackChain( nEndTarget );
CloseBlock();
}
// WITH .. END WITH
void SbiParser::With()
{
SbiExpression aVar( this, SbOPERAND );
// Letzten Knoten in der Objekt-Kette ueberpruefen
SbiExprNode *pNode = aVar.GetExprNode()->GetRealNode();
SbiSymDef* pDef = pNode->GetVar();
// Variant, AB 27.6.1997, #41090: bzw. empty -> muß Object sein
if( pDef->GetType() == SbxVARIANT || pDef->GetType() == SbxEMPTY )
pDef->SetType( SbxOBJECT );
else if( pDef->GetType() != SbxOBJECT )
Error( SbERR_NEEDS_OBJECT );
// Knoten auch auf SbxOBJECT setzen, damit spaeter Gen() klappt
pNode->SetType( SbxOBJECT );
OpenBlock( NIL, aVar.GetExprNode() );
StmntBlock( ENDWITH );
CloseBlock();
}
// LOOP/NEXT/WEND ohne Konstrukt
void SbiParser::BadBlock()
{
if( eEndTok )
Error( SbERR_BAD_BLOCK, eEndTok );
else
Error( SbERR_BAD_BLOCK, "Loop/Next/Wend" );
}
// On expr Goto/Gosub n,n,n...
void SbiParser::OnGoto()
{
SbiExpression aCond( this );
aCond.Gen();
sal_uInt32 nLabelsTarget = aGen.Gen( _ONJUMP, 0 );
SbiToken eTok = Next();
if( eTok != GOTO && eTok != GOSUB )
{
Error( SbERR_EXPECTED, "GoTo/GoSub" );
eTok = GOTO;
}
// Label-Tabelle einlesen:
sal_uInt32 nLbl = 0;
do
{
SbiToken eTok2 = NIL;
eTok2 = Next(); // Label holen
if( MayBeLabel() )
{
sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
aGen.Gen( _JUMP, nOff );
nLbl++;
}
else Error( SbERR_LABEL_EXPECTED );
}
while( !bAbort && TestComma() );
if( eTok == GOSUB )
nLbl |= 0x8000;
aGen.Patch( nLabelsTarget, nLbl );
}
// GOTO/GOSUB
void SbiParser::Goto()
{
SbiOpcode eOp = eCurTok == GOTO ? _JUMP : _GOSUB;
Next();
if( MayBeLabel() )
{
sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
aGen.Gen( eOp, nOff );
}
else Error( SbERR_LABEL_EXPECTED );
}
// RETURN [label]
void SbiParser::Return()
{
Next();
if( MayBeLabel() )
{
sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
aGen.Gen( _RETURN, nOff );
}
else aGen.Gen( _RETURN, 0 );
}
// SELECT CASE
void SbiParser::Select()
{
TestToken( CASE );
SbiExpression aCase( this );
SbiToken eTok = NIL;
aCase.Gen();
aGen.Gen( _CASE );
TestEoln();
sal_uInt32 nNextTarget = 0;
sal_uInt32 nDoneTarget = 0;
sal_Bool bElse = sal_False;
// Die Cases einlesen:
while( !bAbort )
{
eTok = Next();
if( eTok == CASE )
{
if( nNextTarget )
aGen.BackChain( nNextTarget ), nNextTarget = 0;
aGen.Statement();
// Jeden Case einlesen
sal_Bool bDone = sal_False;
sal_uInt32 nTrueTarget = 0;
if( Peek() == ELSE )
{
// CASE ELSE
Next();
bElse = sal_True;
}
else while( !bDone )
{
if( bElse )
Error( SbERR_SYNTAX );
SbiToken eTok2 = Peek();
if( eTok2 == IS || ( eTok2 >= EQ && eTok2 <= GE ) )
{ // CASE [IS] operator expr
if( eTok2 == IS )
Next();
eTok2 = Peek();
if( eTok2 < EQ || eTok2 > GE )
Error( SbERR_SYNTAX );
else Next();
SbiExpression aCompare( this );
aCompare.Gen();
nTrueTarget = aGen.Gen(
_CASEIS, nTrueTarget,
sal::static_int_cast< sal_uInt16 >(
SbxEQ + ( eTok2 - EQ ) ) );
}
else
{ // CASE expr | expr TO expr
SbiExpression aCase1( this );
aCase1.Gen();
if( Peek() == TO )
{
// CASE a TO b
Next();
SbiExpression aCase2( this );
aCase2.Gen();
nTrueTarget = aGen.Gen( _CASETO, nTrueTarget );
}
else
// CASE a
nTrueTarget = aGen.Gen( _CASEIS, nTrueTarget, SbxEQ );
}
if( Peek() == COMMA ) Next();
else TestEoln(), bDone = sal_True;
}
// Alle Cases abgearbeitet
if( !bElse )
{
nNextTarget = aGen.Gen( _JUMP, nNextTarget );
aGen.BackChain( nTrueTarget );
}
// den Statement-Rumpf bauen
while( !bAbort )
{
eTok = Peek();
if( eTok == CASE || eTok == ENDSELECT )
break;
if( !Parse() ) goto done;
eTok = Peek();
if( eTok == CASE || eTok == ENDSELECT )
break;
}
if( !bElse )
nDoneTarget = aGen.Gen( _JUMP, nDoneTarget );
}
else if( !IsEoln( eTok ) )
break;
}
done:
if( eTok != ENDSELECT )
Error( SbERR_EXPECTED, ENDSELECT );
if( nNextTarget )
aGen.BackChain( nNextTarget );
aGen.BackChain( nDoneTarget );
aGen.Gen( _ENDCASE );
}
// ON Error/Variable
#ifdef _MSC_VER
#pragma optimize("",off)
#endif
void SbiParser::On()
{
SbiToken eTok = Peek();
String aString = SbiTokenizer::Symbol(eTok);
if (aString.EqualsIgnoreCaseAscii("ERROR"))
//if (!aString.ICompare("ERROR"))
eTok = _ERROR_; // Error kommt als SYMBOL
if( eTok != _ERROR_ && eTok != LOCAL ) OnGoto();
else
{
if( eTok == LOCAL ) Next();
Next (); // Kein TestToken mehr, da es sonst einen Fehler gibt
Next(); // Token nach Error holen
if( eCurTok == GOTO )
{
// ON ERROR GOTO label|0
Next();
bool bError_ = false;
if( MayBeLabel() )
{
if( eCurTok == NUMBER && !nVal )
aGen.Gen( _STDERROR );
else
{
sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
aGen.Gen( _ERRHDL, nOff );
}
}
else if( eCurTok == MINUS )
{
Next();
if( eCurTok == NUMBER && nVal == 1 )
aGen.Gen( _STDERROR );
else
bError_ = true;
}
if( bError_ )
Error( SbERR_LABEL_EXPECTED );
}
else if( eCurTok == RESUME )
{
TestToken( NEXT );
aGen.Gen( _NOERROR );
}
else Error( SbERR_EXPECTED, "GoTo/Resume" );
}
}
#ifdef _MSC_VER
#pragma optimize("",off)
#endif
// RESUME [0]|NEXT|label
void SbiParser::Resume()
{
sal_uInt32 nLbl;
switch( Next() )
{
case EOS:
case EOLN:
aGen.Gen( _RESUME, 0 );
break;
case NEXT:
aGen.Gen( _RESUME, 1 );
Next();
break;
case NUMBER:
if( !nVal )
{
aGen.Gen( _RESUME, 0 );
break;
} // fall thru
case SYMBOL:
if( MayBeLabel() )
{
nLbl = pProc->GetLabels().Reference( aSym );
aGen.Gen( _RESUME, nLbl );
Next();
break;
} // fall thru
default:
Error( SbERR_LABEL_EXPECTED );
}
}