diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /basic/source/comp/parser.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'basic/source/comp/parser.cxx')
-rw-r--r-- | basic/source/comp/parser.cxx | 889 |
1 files changed, 889 insertions, 0 deletions
diff --git a/basic/source/comp/parser.cxx b/basic/source/comp/parser.cxx new file mode 100644 index 000000000..777db4071 --- /dev/null +++ b/basic/source/comp/parser.cxx @@ -0,0 +1,889 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <basic/sberrors.hxx> +#include <basic/sbxmeth.hxx> +#include <basic/sbmod.hxx> +#include <basic/sbstar.hxx> +#include <basic/sbx.hxx> +#include <parser.hxx> +#include <com/sun/star/script/ModuleType.hpp> +#include <rtl/character.hxx> + +struct SbiParseStack { // "Stack" for statement-blocks + SbiParseStack* pNext; // Chain + SbiExprNode* pWithVar; + SbiToken eExitTok; + sal_uInt32 nChain; // JUMP-Chain +}; + +namespace { + +struct SbiStatement { + SbiToken eTok; + void( SbiParser::*Func )(); + bool bMain; // true: OK outside the SUB + bool bSubr; // true: OK inside the SUB +}; + +} + +#define Y true +#define N false + +const SbiStatement StmntTable [] = { +{ ATTRIBUTE, &SbiParser::Attribute, Y, Y, }, // ATTRIBUTE +{ CALL, &SbiParser::Call, N, Y, }, // CALL +{ CLOSE, &SbiParser::Close, N, Y, }, // CLOSE +{ CONST_, &SbiParser::Dim, Y, Y, }, // CONST +{ DECLARE, &SbiParser::Declare, Y, N, }, // DECLARE +{ DEFBOOL, &SbiParser::DefXXX, Y, N, }, // DEFBOOL +{ DEFCUR, &SbiParser::DefXXX, Y, N, }, // DEFCUR +{ DEFDATE, &SbiParser::DefXXX, Y, N, }, // DEFDATE +{ DEFDBL, &SbiParser::DefXXX, Y, N, }, // DEFDBL +{ DEFERR, &SbiParser::DefXXX, Y, N, }, // DEFERR +{ DEFINT, &SbiParser::DefXXX, Y, N, }, // DEFINT +{ DEFLNG, &SbiParser::DefXXX, Y, N, }, // DEFLNG +{ DEFOBJ, &SbiParser::DefXXX, Y, N, }, // DEFOBJ +{ DEFSNG, &SbiParser::DefXXX, Y, N, }, // DEFSNG +{ DEFSTR, &SbiParser::DefXXX, Y, N, }, // DEFSTR +{ DEFVAR, &SbiParser::DefXXX, Y, N, }, // DEFVAR +{ DIM, &SbiParser::Dim, Y, Y, }, // DIM +{ DO, &SbiParser::DoLoop, N, Y, }, // DO +{ ELSE, &SbiParser::NoIf, N, Y, }, // ELSE +{ ELSEIF, &SbiParser::NoIf, N, Y, }, // ELSEIF +{ ENDIF, &SbiParser::NoIf, N, Y, }, // ENDIF +{ END, &SbiParser::Stop, N, Y, }, // END +{ ENUM, &SbiParser::Enum, Y, N, }, // TYPE +{ ERASE, &SbiParser::Erase, N, Y, }, // ERASE +{ ERROR_, &SbiParser::ErrorStmnt, N, Y, }, // ERROR +{ EXIT, &SbiParser::Exit, N, Y, }, // EXIT +{ FOR, &SbiParser::For, N, Y, }, // FOR +{ FUNCTION, &SbiParser::SubFunc, Y, N, }, // FUNCTION +{ GOSUB, &SbiParser::Goto, N, Y, }, // GOSUB +{ GLOBAL, &SbiParser::Dim, Y, N, }, // GLOBAL +{ GOTO, &SbiParser::Goto, N, Y, }, // GOTO +{ IF, &SbiParser::If, N, Y, }, // IF +{ IMPLEMENTS, &SbiParser::Implements, Y, N, }, // IMPLEMENTS +{ INPUT, &SbiParser::Input, N, Y, }, // INPUT +{ LET, &SbiParser::Assign, N, Y, }, // LET +{ LINE, &SbiParser::Line, N, Y, }, // LINE, -> LINE INPUT (#i92642) +{ LINEINPUT,&SbiParser::LineInput, N, Y, }, // LINE INPUT +{ LOOP, &SbiParser::BadBlock, N, Y, }, // LOOP +{ LSET, &SbiParser::LSet, N, Y, }, // LSET +{ NAME, &SbiParser::Name, N, Y, }, // NAME +{ NEXT, &SbiParser::BadBlock, N, Y, }, // NEXT +{ ON, &SbiParser::On, N, Y, }, // ON +{ OPEN, &SbiParser::Open, N, Y, }, // OPEN +{ OPTION, &SbiParser::Option, Y, N, }, // OPTION +{ PRINT, &SbiParser::Print, N, Y, }, // PRINT +{ PRIVATE, &SbiParser::Dim, Y, N, }, // PRIVATE +{ PROPERTY, &SbiParser::SubFunc, Y, N, }, // FUNCTION +{ PUBLIC, &SbiParser::Dim, Y, N, }, // PUBLIC +{ REDIM, &SbiParser::ReDim, N, Y, }, // DIM +{ RESUME, &SbiParser::Resume, N, Y, }, // RESUME +{ RETURN, &SbiParser::Return, N, Y, }, // RETURN +{ RSET, &SbiParser::RSet, N, Y, }, // RSET +{ SELECT, &SbiParser::Select, N, Y, }, // SELECT +{ SET, &SbiParser::Set, N, Y, }, // SET +{ STATIC, &SbiParser::Static, Y, Y, }, // STATIC +{ STOP, &SbiParser::Stop, N, Y, }, // STOP +{ SUB, &SbiParser::SubFunc, Y, N, }, // SUB +{ TYPE, &SbiParser::Type, Y, N, }, // TYPE +{ UNTIL, &SbiParser::BadBlock, N, Y, }, // UNTIL +{ WHILE, &SbiParser::While, N, Y, }, // WHILE +{ WEND, &SbiParser::BadBlock, N, Y, }, // WEND +{ WITH, &SbiParser::With, N, Y, }, // WITH +{ WRITE, &SbiParser::Write, N, Y, }, // WRITE + +{ NIL, nullptr, N, N } +}; + +SbiParser::SbiParser( StarBASIC* pb, SbModule* pm ) + : SbiTokenizer( pm->GetSource32(), pb ), + pStack(nullptr), + pProc(nullptr), + pWithVar(nullptr), + eEndTok(NIL), + bGblDefs(false), + bNewGblDefs(false), + bSingleLineIf(false), + bCodeCompleting(false), + aGlobals( aGblStrings, SbGLOBAL, this ), + aPublics( aGblStrings, SbPUBLIC, this ), + aRtlSyms( aGblStrings, SbRTL, this ), + aGen( *pm, this ), + nBase(0), + bExplicit(false) +{ + bClassModule = ( pm->GetModuleType() == css::script::ModuleType::CLASS ); + pPool = &aPublics; + for(SbxDataType & eDefType : eDefTypes) + eDefType = SbxVARIANT; // no explicit default type + + aPublics.SetParent( &aGlobals ); + aGlobals.SetParent( &aRtlSyms ); + + + nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 ); + + rTypeArray = new SbxArray; // array for user defined types + rEnumArray = new SbxArray; // array for Enum types + bVBASupportOn = pm->IsVBACompat(); + if ( bVBASupportOn ) + EnableCompatibility(); + +} + +SbiParser::~SbiParser() { } + +// part of the runtime-library? +SbiSymDef* SbiParser::CheckRTLForSym(const OUString& rSym, SbxDataType eType) +{ + SbxVariable* pVar = GetBasic()->GetRtl()->Find(rSym, SbxClassType::DontCare); + if (!pVar) + return nullptr; + + if (SbxMethod* pMethod = dynamic_cast<SbxMethod*>(pVar)) + { + SbiProcDef* pProc_ = aRtlSyms.AddProc( rSym ); + if (pMethod->IsRuntimeFunction()) + { + pProc_->SetType( pMethod->GetRuntimeFunctionReturnType() ); + } + else + { + pProc_->SetType( pVar->GetType() ); + } + return pProc_; + } + + + SbiSymDef* pDef = aRtlSyms.AddSym(rSym); + pDef->SetType(eType); + return pDef; +} + +// close global chain + +bool SbiParser::HasGlobalCode() +{ + if( bGblDefs && nGblChain ) + { + aGen.BackChain( nGblChain ); + aGen.Gen( SbiOpcode::LEAVE_ ); + nGblChain = 0; + } + return bGblDefs; +} + +void SbiParser::OpenBlock( SbiToken eTok, SbiExprNode* pVar ) +{ + SbiParseStack* p = new SbiParseStack; + p->eExitTok = eTok; + p->nChain = 0; + p->pWithVar = pWithVar; + p->pNext = pStack; + pStack = p; + pWithVar = pVar; + + // #29955 service the for-loop level + if( eTok == FOR ) + aGen.IncForLevel(); +} + +void SbiParser::CloseBlock() +{ + if( !pStack ) + return; + + SbiParseStack* p = pStack; + + // #29955 service the for-loop level + if( p->eExitTok == FOR ) + aGen.DecForLevel(); + + aGen.BackChain( p->nChain ); + pStack = p->pNext; + pWithVar = p->pWithVar; + delete p; +} + +// EXIT ... + +void SbiParser::Exit() +{ + SbiToken eTok = Next(); + for( SbiParseStack* p = pStack; p; p = p->pNext ) + { + SbiToken eExitTok = p->eExitTok; + if( eTok == eExitTok || + (eTok == PROPERTY && (eExitTok == GET || eExitTok == LET) ) ) // #i109051 + { + p->nChain = aGen.Gen( SbiOpcode::JUMP_, p->nChain ); + return; + } + } + if( pStack ) + Error( ERRCODE_BASIC_EXPECTED, pStack->eExitTok ); + else + Error( ERRCODE_BASIC_BAD_EXIT ); +} + +bool SbiParser::TestSymbol() +{ + Peek(); + if( eCurTok == SYMBOL ) + { + Next(); return true; + } + Error( ERRCODE_BASIC_SYMBOL_EXPECTED ); + return false; +} + + +bool SbiParser::TestToken( SbiToken t ) +{ + if( Peek() == t ) + { + Next(); return true; + } + else + { + Error( ERRCODE_BASIC_EXPECTED, t ); + return false; + } +} + + +bool SbiParser::TestComma() +{ + SbiToken eTok = Peek(); + if( IsEoln( eTok ) ) + { + Next(); + return false; + } + else if( eTok != COMMA ) + { + Error( ERRCODE_BASIC_EXPECTED, COMMA ); + return false; + } + Next(); + return true; +} + + +void SbiParser::TestEoln() +{ + if( !IsEoln( Next() ) ) + { + Error( ERRCODE_BASIC_EXPECTED, EOLN ); + while( !IsEoln( Next() ) ) {} + } +} + + +void SbiParser::StmntBlock( SbiToken eEnd ) +{ + SbiToken xe = eEndTok; + eEndTok = eEnd; + while( !bAbort && Parse() ) {} + eEndTok = xe; + if( IsEof() ) + { + Error( ERRCODE_BASIC_BAD_BLOCK, eEnd ); + bAbort = true; + } +} + +void SbiParser::SetCodeCompleting( bool b ) +{ + bCodeCompleting = b; +} + + +bool SbiParser::Parse() +{ + if( bAbort ) return false; + + EnableErrors(); + + bErrorIsSymbol = false; + Peek(); + bErrorIsSymbol = true; + + if( IsEof() ) + { + // AB #33133: If no sub has been created before, + // the global chain must be closed here! + // AB #40689: Due to the new static-handling there + // can be another nGblChain, so ask for it before. + if( bNewGblDefs && nGblChain == 0 ) + nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 ); + return false; + } + + + if( IsEoln( eCurTok ) ) + { + Next(); return true; + } + + if( !bSingleLineIf && MayBeLabel( true ) ) + { + // is a label + if( !pProc ) + Error( ERRCODE_BASIC_NOT_IN_MAIN, aSym ); + else + pProc->GetLabels().Define( aSym ); + Next(); Peek(); + + if( IsEoln( eCurTok ) ) + { + Next(); return true; + } + } + + // end of parsing? + if( eCurTok == eEndTok || + ( bVBASupportOn && // #i109075 + (eCurTok == ENDFUNC || eCurTok == ENDPROPERTY || eCurTok == ENDSUB) && + (eEndTok == ENDFUNC || eEndTok == ENDPROPERTY || eEndTok == ENDSUB) ) ) + { + Next(); + if( eCurTok != NIL ) + aGen.Statement(); + return false; + } + + // comment? + if( eCurTok == REM ) + { + Next(); return true; + } + + // In vba it's possible to do Error.foobar ( even if it results in + // a runtime error + if ( eCurTok == ERROR_ && IsVBASupportOn() ) // we probably need to define a subset of keywords where this madness applies e.g. if ( IsVBASupportOn() && SymbolCanBeRedined( eCurTok ) ) + { + SbiTokenizer tokens( *this ); + tokens.Next(); + if ( tokens.Peek() == DOT ) + { + eCurTok = SYMBOL; + ePush = eCurTok; + } + } + // if there's a symbol, it's either a variable (LET) + // or a SUB-procedure (CALL without brackets) + // DOT for assignments in the WITH-block: .A=5 + if( eCurTok == SYMBOL || eCurTok == DOT ) + { + if( !pProc ) + Error( ERRCODE_BASIC_EXPECTED, SUB ); + else + { + // for correct line and column... + Next(); + Push( eCurTok ); + aGen.Statement(); + Symbol(nullptr); + } + } + else + { + Next(); + + // statement parsers + + const SbiStatement* p; + for( p = StmntTable; p->eTok != NIL; p++ ) + if( p->eTok == eCurTok ) + break; + if( p->eTok != NIL ) + { + if( !pProc && !p->bMain ) + Error( ERRCODE_BASIC_NOT_IN_MAIN, eCurTok ); + else if( pProc && !p->bSubr ) + Error( ERRCODE_BASIC_NOT_IN_SUBR, eCurTok ); + else + { + // AB #41606/#40689: Due to the new static-handling there + // can be another nGblChain, so ask for it before. + if( bNewGblDefs && nGblChain == 0 && + ( eCurTok == SUB || eCurTok == FUNCTION || eCurTok == PROPERTY ) ) + { + nGblChain = aGen.Gen( SbiOpcode::JUMP_, 0 ); + bNewGblDefs = false; + } + // statement-opcode at the beginning of a sub, too, please + if( ( p->bSubr && (eCurTok != STATIC || Peek() == SUB || Peek() == FUNCTION ) ) || + eCurTok == SUB || eCurTok == FUNCTION ) + aGen.Statement(); + (this->*( p->Func ) )(); + ErrCode nSbxErr = SbxBase::GetError(); + if( nSbxErr ) + { + SbxBase::ResetError(); + Error( nSbxErr ); + } + } + } + else + Error( ERRCODE_BASIC_UNEXPECTED, eCurTok ); + } + + // test for the statement's end - + // might also be an ELSE, as there must not necessary be a : before the ELSE! + + if( !IsEos() ) + { + Peek(); + if( !IsEos() && eCurTok != ELSE ) + { + // if the parsing has been aborted, jump over to the ":" + Error( ERRCODE_BASIC_UNEXPECTED, eCurTok ); + while( !IsEos() ) Next(); + } + } + // The parser aborts at the end, the + // next token has not been fetched yet! + return true; +} + + +SbiExprNode* SbiParser::GetWithVar() +{ + if( pWithVar ) + return pWithVar; + + SbiParseStack* p = pStack; + while( p ) + { + // LoopVar can at the moment only be for with + if( p->pWithVar ) + return p->pWithVar; + p = p->pNext; + } + return nullptr; +} + + +// assignment or subroutine call + +void SbiParser::Symbol( const KeywordSymbolInfo* pKeywordSymbolInfo ) +{ + SbiExprMode eMode = bVBASupportOn ? EXPRMODE_STANDALONE : EXPRMODE_STANDARD; + SbiExpression aVar( this, SbSYMBOL, eMode, pKeywordSymbolInfo ); + + bool bEQ = ( Peek() == EQ ); + if( !bEQ && bVBASupportOn && aVar.IsBracket() ) + Error( ERRCODE_BASIC_EXPECTED, "=" ); + + RecursiveMode eRecMode = ( bEQ ? PREVENT_CALL : FORCE_CALL ); + bool bSpecialMidHandling = false; + SbiSymDef* pDef = aVar.GetRealVar(); + if( bEQ && pDef && pDef->GetScope() == SbRTL ) + { + OUString aRtlName = pDef->GetName(); + if( aRtlName.equalsIgnoreAsciiCase("Mid") ) + { + SbiExprNode* pExprNode = aVar.GetExprNode(); + if( pExprNode && pExprNode->GetNodeType() == SbxVARVAL ) + { + SbiExprList* pPar = pExprNode->GetParameters(); + short nParCount = pPar ? pPar->GetSize() : 0; + if( nParCount == 2 || nParCount == 3 ) + { + if( nParCount == 2 ) + pPar->addExpression( std::make_unique<SbiExpression>( this, -1, SbxLONG ) ); + + TestToken( EQ ); + pPar->addExpression( std::make_unique<SbiExpression>( this ) ); + + bSpecialMidHandling = true; + } + } + } + } + aVar.Gen( eRecMode ); + if( bSpecialMidHandling ) + return; + + if( !bEQ ) + { + aGen.Gen( SbiOpcode::GET_ ); + } + else + { + // so it must be an assignment! + if( !aVar.IsLvalue() ) + Error( ERRCODE_BASIC_LVALUE_EXPECTED ); + TestToken( EQ ); + SbiExpression aExpr( this ); + aExpr.Gen(); + SbiOpcode eOp = SbiOpcode::PUT_; + if( pDef ) + { + if( pDef->GetConstDef() ) + Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() ); + if( pDef->GetType() == SbxOBJECT ) + { + eOp = SbiOpcode::SET_; + if( pDef->GetTypeId() ) + { + aGen.Gen( SbiOpcode::SETCLASS_, pDef->GetTypeId() ); + return; + } + } + } + aGen.Gen( eOp ); + } +} + + +void SbiParser::Assign() +{ + SbiExpression aLvalue( this, SbLVALUE ); + TestToken( EQ ); + SbiExpression aExpr( this ); + aLvalue.Gen(); + aExpr.Gen(); + sal_uInt16 nLen = 0; + SbiSymDef* pDef = aLvalue.GetRealVar(); + { + if( pDef->GetConstDef() ) + Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() ); + nLen = aLvalue.GetRealVar()->GetLen(); + } + if( nLen ) + aGen.Gen( SbiOpcode::PAD_, nLen ); + aGen.Gen( SbiOpcode::PUT_ ); +} + +// assignments of an object-variable + +void SbiParser::Set() +{ + SbiExpression aLvalue( this, SbLVALUE ); + SbxDataType eType = aLvalue.GetType(); + if( eType != SbxOBJECT && eType != SbxEMPTY && eType != SbxVARIANT ) + Error( ERRCODE_BASIC_INVALID_OBJECT ); + TestToken( EQ ); + SbiSymDef* pDef = aLvalue.GetRealVar(); + if( pDef->GetConstDef() ) + Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() ); + + SbiToken eTok = Peek(); + if( eTok == NEW ) + { + Next(); + auto pTypeDef = std::make_unique<SbiSymDef>( OUString() ); + TypeDecl( *pTypeDef, true ); + + aLvalue.Gen(); + aGen.Gen( SbiOpcode::CREATE_, pDef->GetId(), pTypeDef->GetTypeId() ); + aGen.Gen( SbiOpcode::SETCLASS_, pDef->GetTypeId() ); + } + else + { + SbiExpression aExpr( this ); + aLvalue.Gen(); + aExpr.Gen(); + // It's a good idea to distinguish between + // set something = another & + // something = another + // ( it's necessary for vba objects where set is object + // specific and also doesn't involve processing default params ) + if( pDef->GetTypeId() ) + { + if ( bVBASupportOn ) + aGen.Gen( SbiOpcode::VBASETCLASS_, pDef->GetTypeId() ); + else + aGen.Gen( SbiOpcode::SETCLASS_, pDef->GetTypeId() ); + } + else + { + if ( bVBASupportOn ) + aGen.Gen( SbiOpcode::VBASET_ ); + else + aGen.Gen( SbiOpcode::SET_ ); + } + } +} + +// JSM 07.10.95 +void SbiParser::LSet() +{ + SbiExpression aLvalue( this, SbLVALUE ); + if( aLvalue.GetType() != SbxSTRING ) + { + Error( ERRCODE_BASIC_INVALID_OBJECT ); + } + TestToken( EQ ); + SbiSymDef* pDef = aLvalue.GetRealVar(); + if( pDef && pDef->GetConstDef() ) + { + Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() ); + } + SbiExpression aExpr( this ); + aLvalue.Gen(); + aExpr.Gen(); + aGen.Gen( SbiOpcode::LSET_ ); +} + +// JSM 07.10.95 +void SbiParser::RSet() +{ + SbiExpression aLvalue( this, SbLVALUE ); + if( aLvalue.GetType() != SbxSTRING ) + { + Error( ERRCODE_BASIC_INVALID_OBJECT ); + } + TestToken( EQ ); + SbiSymDef* pDef = aLvalue.GetRealVar(); + if( pDef && pDef->GetConstDef() ) + Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() ); + SbiExpression aExpr( this ); + aLvalue.Gen(); + aExpr.Gen(); + aGen.Gen( SbiOpcode::RSET_ ); +} + +// DEFINT, DEFLNG, DEFSNG, DEFDBL, DEFSTR and so on + +void SbiParser::DefXXX() +{ + sal_Unicode ch1, ch2; + SbxDataType t = SbxDataType( eCurTok - DEFINT + SbxINTEGER ); + + while( !bAbort ) + { + if( Next() != SYMBOL ) break; + ch1 = rtl::toAsciiUpperCase(aSym[0]); + ch2 = 0; + if( Peek() == MINUS ) + { + Next(); + if( Next() != SYMBOL ) Error( ERRCODE_BASIC_SYMBOL_EXPECTED ); + else + { + ch2 = rtl::toAsciiUpperCase(aSym[0]); + if( ch2 < ch1 ) + { + Error( ERRCODE_BASIC_SYNTAX ); + ch2 = 0; + } + } + } + if (!ch2) ch2 = ch1; + ch1 -= 'A'; ch2 -= 'A'; + for (; ch1 <= ch2; ch1++) eDefTypes[ ch1 ] = t; + if( !TestComma() ) break; + } +} + +// STOP/SYSTEM + +void SbiParser::Stop() +{ + aGen.Gen( SbiOpcode::STOP_ ); + Peek(); // #35694: only Peek(), so that EOL is recognized in Single-Line-If +} + +// IMPLEMENTS + +void SbiParser::Implements() +{ + if( !bClassModule ) + { + Error( ERRCODE_BASIC_UNEXPECTED, IMPLEMENTS ); + return; + } + + Peek(); + if( eCurTok != SYMBOL ) + { + Error( ERRCODE_BASIC_SYMBOL_EXPECTED ); + return; + } + + OUString aImplementedIface = aSym; + Next(); + if( Peek() == DOT ) + { + OUString aDotStr( '.' ); + while( Peek() == DOT ) + { + aImplementedIface += aDotStr; + Next(); + SbiToken ePeekTok = Peek(); + if( ePeekTok == SYMBOL || IsKwd( ePeekTok ) ) + { + Next(); + aImplementedIface += aSym; + } + else + { + Next(); + Error( ERRCODE_BASIC_SYMBOL_EXPECTED ); + break; + } + } + } + aIfaceVector.push_back( aImplementedIface ); +} + +void SbiParser::EnableCompatibility() +{ + if( !bCompatible ) + AddConstants(); + bCompatible = true; +} + +// OPTION + +void SbiParser::Option() +{ + switch( Next() ) + { + case BASIC_EXPLICIT: + bExplicit = true; break; + case BASE: + if( Next() == NUMBER && ( nVal == 0 || nVal == 1 ) ) + { + nBase = static_cast<short>(nVal); + break; + } + Error( ERRCODE_BASIC_EXPECTED, "0/1" ); + break; + case PRIVATE: + { + OUString aString = SbiTokenizer::Symbol(Next()); + if( !aString.equalsIgnoreAsciiCase("Module") ) + { + Error( ERRCODE_BASIC_EXPECTED, "Module" ); + } + break; + } + case COMPARE: + { + SbiToken eTok = Next(); + if( eTok == BINARY ) + { + } + else if( eTok == SYMBOL && GetSym().equalsIgnoreAsciiCase("text") ) + { + } + else + { + Error( ERRCODE_BASIC_EXPECTED, "Text/Binary" ); + } + break; + } + case COMPATIBLE: + EnableCompatibility(); + break; + + case CLASSMODULE: + bClassModule = true; + aGen.GetModule().SetModuleType( css::script::ModuleType::CLASS ); + break; + case VBASUPPORT: // Option VBASupport used to override the module mode ( in fact this must reset the mode + if( Next() == NUMBER ) + { + if ( nVal == 1 || nVal == 0 ) + { + bVBASupportOn = ( nVal == 1 ); + if ( bVBASupportOn ) + { + EnableCompatibility(); + } + // if the module setting is different + // reset it to what the Option tells us + if ( bVBASupportOn != aGen.GetModule().IsVBACompat() ) + { + aGen.GetModule().SetVBACompat( bVBASupportOn ); + } + break; + } + } + Error( ERRCODE_BASIC_EXPECTED, "0/1" ); + break; + default: + Error( ERRCODE_BASIC_BAD_OPTION, eCurTok ); + } +} + +static void addStringConst( SbiSymPool& rPool, const OUString& pSym, const OUString& rStr ) +{ + SbiConstDef* pConst = new SbiConstDef( pSym ); + pConst->SetType( SbxSTRING ); + pConst->Set( rStr ); + rPool.Add( pConst ); +} + +static void addNumericConst(SbiSymPool& rPool, const OUString& pSym, double nVal) +{ + SbiConstDef* pConst = new SbiConstDef(pSym); + pConst->Set(nVal, SbxDOUBLE); + rPool.Add(pConst); +} + +void SbiParser::AddConstants() +{ + // tdf#131563 - add vba color constants + // See https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/color-constants + addNumericConst(aPublics, "vbBlack", 0x0); + addNumericConst(aPublics, "vbRed", 0xFF); + addNumericConst(aPublics, "vbGreen", 0xFF00); + addNumericConst(aPublics, "vbYellow", 0xFFFF); + addNumericConst(aPublics, "vbBlue", 0xFF0000); + addNumericConst(aPublics, "vbMagenta", 0xFF00FF); + addNumericConst(aPublics, "vbCyan", 0xFFFF00); + addNumericConst(aPublics, "vbWhite", 0xFFFFFF); + + // #113063 Create constant RTL symbols + addStringConst( aPublics, "vbCr", "\x0D" ); + addStringConst( aPublics, "vbCrLf", "\x0D\x0A" ); + addStringConst( aPublics, "vbFormFeed", "\x0C" ); + addStringConst( aPublics, "vbLf", "\x0A" ); +#ifdef _WIN32 + addStringConst( aPublics, "vbNewLine", "\x0D\x0A" ); +#else + addStringConst( aPublics, "vbNewLine", "\x0A" ); +#endif + addStringConst( aPublics, "vbNullString", "" ); + addStringConst( aPublics, "vbTab", "\x09" ); + addStringConst( aPublics, "vbVerticalTab", "\x0B" ); + + addStringConst( aPublics, "vbNullChar", OUString(u'\0') ); +} + +// ERROR n + +void SbiParser::ErrorStmnt() +{ + SbiExpression aPar( this ); + aPar.Gen(); + aGen.Gen( SbiOpcode::ERROR_ ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |