/* -*- 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
{ LSET, &SbiParser::LSet, N, Y, },
// LSET
{ NAME, &SbiParser::Name, N, Y, },
// NAME
{ 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
{
WHILE, &SbiParser::
While, N, Y, },
// WHILE
{ WITH, &SbiParser::With, N, Y, },
// WITH
{ WRITE, &SbiParser::Write, N, Y, },
// WRITE
};
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->IsVBASupport();
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 == GET || eTok == LET || eTok == SET) ? PROPERTY : eTok;
// #i109051
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 )
{
if (eTok == p->eExitTok)
{
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
eEndTok == ENDPROPERTY &&
(pProc && pProc->getPropertyMode() == PropertyMode::Get) &&
( eCurTok == ENDFUNC || eCurTok == 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 auto p = std::find_if(std::begin(StmntTable), std::end(StmntTable),
[
this](
auto element) {
return element.eTok == eCurTok; });
if (p != std::end(StmntTable))
{
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, u
"="_ustr );
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;
}
}
else if (
auto nLen = pDef->GetLen())
// Dim s As String * 123 ' 123 -> nLen
{
aGen.Gen(SbiOpcode::PAD_, nLen);
}
}
aGen.Gen( eOp );
}
}
void SbiParser::Assign()
{
SbiExpression aLvalue(
this, SbLVALUE );
TestToken( EQ );
SbiExpression aExpr(
this );
aLvalue.Gen();
aExpr.Gen();
if (SbiSymDef* pDef = aLvalue.GetRealVar())
{
if( pDef->GetConstDef() )
Error( ERRCODE_BASIC_DUPLICATE_DEF, pDef->GetName() );
if (
auto nLen = pDef->GetLen())
// Dim s As String * 123 ' 123 -> 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();
SbiSymDef aTypeDef( u
""_ustr );
TypeDecl( aTypeDef,
true );
aLvalue.Gen();
aGen.Gen( SbiOpcode::CREATE_, pDef->GetId(), aTypeDef.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, u
"0/1"_ustr );
break;
case PRIVATE:
{
OUString aString = SbiTokenizer::Symbol(Next());
if( !aString.equalsIgnoreAsciiCase(
"Module") )
{
Error( ERRCODE_BASIC_EXPECTED, u
"Module"_ustr );
}
break;
}
case COMPARE:
{
SbiToken eTok = Next();
if( eTok == BINARY )
{
}
else if( eTok == SYMBOL && GetSym().equalsIgnoreAsciiCase(
"text") )
{
}
else
{
Error( ERRCODE_BASIC_EXPECTED, u
"Text/Binary"_ustr );
}
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().IsVBASupport() )
{
aGen.GetModule().SetVBASupport( bVBASupportOn );
}
break;
}
}
Error( ERRCODE_BASIC_EXPECTED, u
"0/1"_ustr );
break;
default:
Error( ERRCODE_BASIC_BAD_OPTION, eCurTok );
}
}
static void addStringConst( SbiSymPool& rPool,
const OUString& pSym,
const OUStri
ng& 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#153543 - shell constants
// See https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/shell-constants
addNumericConst(aPublics, u"vbHide"_ustr, 0);
addNumericConst(aPublics, u"vbNormalFocus"_ustr, 1);
addNumericConst(aPublics, u"vbMinimizedFocus"_ustr, 2);
addNumericConst(aPublics, u"vbMaximizedFocus"_ustr, 3);
addNumericConst(aPublics, u"vbNormalNoFocus"_ustr, 4);
addNumericConst(aPublics, u"vbMinimizedNoFocus"_ustr, 6);
// tdf#131563 - add vba color constants
// See https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/color-constants
addNumericConst(aPublics, u"vbBlack"_ustr, 0x0);
addNumericConst(aPublics, u"vbRed"_ustr, 0xFF);
addNumericConst(aPublics, u"vbGreen"_ustr, 0xFF00);
addNumericConst(aPublics, u"vbYellow"_ustr, 0xFFFF);
addNumericConst(aPublics, u"vbBlue"_ustr, 0xFF0000);
addNumericConst(aPublics, u"vbMagenta"_ustr, 0xFF00FF);
addNumericConst(aPublics, u"vbCyan"_ustr, 0xFFFF00);
addNumericConst(aPublics, u"vbWhite"_ustr, 0xFFFFFF);
// #113063 Create constant RTL symbols
addStringConst( aPublics, u"vbCr"_ustr, u"\x0D"_ustr );
addStringConst( aPublics, u"vbCrLf"_ustr, u"\x0D\x0A"_ustr );
addStringConst( aPublics, u"vbFormFeed"_ustr, u"\x0C"_ustr );
addStringConst( aPublics, u"vbLf"_ustr, u"\x0A"_ustr );
#ifdef _WIN32
addStringConst( aPublics, "vbNewLine", "\x0D\x0A" );
#else
addStringConst( aPublics, u"vbNewLine"_ustr, u"\x0A"_ustr );
#endif
addStringConst( aPublics, u"vbNullString"_ustr, u""_ustr );
addStringConst( aPublics, u"vbTab"_ustr, u"\x09"_ustr );
addStringConst( aPublics, u"vbVerticalTab"_ustr, u"\x0B"_ustr );
addStringConst( aPublics, u"vbNullChar"_ustr, OUString(u'\0') );
}
// ERROR n
void SbiParser::ErrorStmnt()
{
SbiExpression aPar( this );
aPar.Gen();
aGen.Gen( SbiOpcode::ERROR_ );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */