forked from amazingfate/loongoffice
In order to support the correct data type of numeric constants, booleans, and default values for strings, the strategy to transport the data type character to the runtime has been changed. The type character will be added after the literal and the string termination symbol in order to keep compatibility. This allows to retrieve the correct type in StepLOADNC and in StepPARAM. Any legacy written images are still possible to process, since if there is a suffix type character where the data type character was directly written after the numeric constant, it will be processed in StepLOADNC. Without this fix, an optional parameter of type Variant, would still include the suffixe type character, and will not be converted to the correct type in StepPARAM. Change-Id: I86c8192c6dc28457053fa7b23a073420e45407b2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/119945 Tested-by: Jenkins Reviewed-by: Andreas Heinisch <andreas.heinisch@yahoo.de>
534 lines
13 KiB
C++
534 lines
13 KiB
C++
/* -*- 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 <memory>
|
|
#include <parser.hxx>
|
|
|
|
#include <osl/diagnose.h>
|
|
|
|
#include <stdio.h>
|
|
#include <rtl/character.hxx>
|
|
#include <basic/sberrors.hxx>
|
|
|
|
// All symbol names are laid down int the symbol-pool's stringpool, so that
|
|
// all symbols are handled in the same case. On saving the code-image, the
|
|
// global stringpool with the respective symbols is also saved.
|
|
// The local stringpool holds all the symbols that don't move to the image
|
|
// (labels, constant names etc.).
|
|
|
|
SbiStringPool::SbiStringPool( )
|
|
{}
|
|
|
|
SbiStringPool::~SbiStringPool()
|
|
{}
|
|
|
|
OUString SbiStringPool::Find( sal_uInt32 n ) const
|
|
{
|
|
if( n == 0 || n > aData.size() )
|
|
return OUString();
|
|
else
|
|
return aData[n - 1];
|
|
}
|
|
|
|
short SbiStringPool::Add( const OUString& rVal )
|
|
{
|
|
sal_uInt32 n = aData.size();
|
|
for( sal_uInt32 i = 0; i < n; ++i )
|
|
{
|
|
OUString& p = aData[i];
|
|
if( p == rVal )
|
|
return i+1;
|
|
}
|
|
|
|
aData.push_back(rVal);
|
|
return static_cast<short>(++n);
|
|
}
|
|
|
|
short SbiStringPool::Add(double n, SbxDataType t)
|
|
{
|
|
size_t size = 0;
|
|
const size_t aBufLength = 40;
|
|
char buf[aBufLength]{};
|
|
|
|
// tdf#143707 - add the type character after the null termination of the string in order to
|
|
// keep compatibility. After the type character has been added, the buffer contains the value
|
|
// of the double n, the string termination symbol, and the type character.
|
|
switch( t )
|
|
{
|
|
// tdf#142460 - properly handle boolean values in string pool
|
|
case SbxBOOL:
|
|
size = snprintf(buf, sizeof(buf), "%d", static_cast<short>(n)) + 1;
|
|
buf[size++] = 'b';
|
|
break;
|
|
// tdf#131296 - store numeric value including its type character
|
|
// See GetSuffixType in basic/source/comp/scanner.cxx for type characters
|
|
case SbxINTEGER:
|
|
size = snprintf(buf, sizeof(buf), "%d", static_cast<short>(n)) + 1;
|
|
buf[size++] = '%';
|
|
break;
|
|
case SbxLONG:
|
|
size = snprintf(buf, sizeof(buf), "%" SAL_PRIdINT32, static_cast<sal_Int32>(n)) + 1;
|
|
buf[size++] = '&';
|
|
break;
|
|
case SbxSINGLE:
|
|
size = snprintf(buf, sizeof(buf), "%.6g", static_cast<float>(n)) + 1;
|
|
buf[size++] = '!';
|
|
break;
|
|
case SbxDOUBLE:
|
|
size = snprintf(buf, sizeof(buf), "%.16g", n) + 1;
|
|
buf[size++] = '#';
|
|
break;
|
|
case SbxCURRENCY:
|
|
size = snprintf(buf, sizeof(buf), "%.16g", n) + 1;
|
|
buf[size++] = '@';
|
|
break;
|
|
default: assert(false); break; // should not happen
|
|
}
|
|
|
|
// tdf#143707 - add the content of the buffer to the string pool inclding its calculated length
|
|
return Add(OUString::createFromAscii(std::string_view(buf, size)));
|
|
}
|
|
|
|
SbiSymPool::SbiSymPool( SbiStringPool& r, SbiSymScope s, SbiParser* pP ) :
|
|
rStrings(r),
|
|
pParent(nullptr),
|
|
pParser(pP),
|
|
eScope(s),
|
|
nProcId(0),
|
|
nCur(0)
|
|
{
|
|
}
|
|
|
|
SbiSymPool::~SbiSymPool()
|
|
{}
|
|
|
|
|
|
SbiSymDef* SbiSymPool::First()
|
|
{
|
|
nCur = sal_uInt16(-1);
|
|
return Next();
|
|
}
|
|
|
|
SbiSymDef* SbiSymPool::Next()
|
|
{
|
|
if (m_Data.size() <= ++nCur)
|
|
return nullptr;
|
|
else
|
|
return m_Data[ nCur ].get();
|
|
}
|
|
|
|
|
|
SbiSymDef* SbiSymPool::AddSym( const OUString& rName )
|
|
{
|
|
SbiSymDef* p = new SbiSymDef( rName );
|
|
p->nPos = m_Data.size();
|
|
p->nId = rStrings.Add( rName );
|
|
p->nProcId = nProcId;
|
|
p->pIn = this;
|
|
m_Data.insert( m_Data.begin() + p->nPos, std::unique_ptr<SbiSymDef>(p) );
|
|
return p;
|
|
}
|
|
|
|
SbiProcDef* SbiSymPool::AddProc( const OUString& rName )
|
|
{
|
|
SbiProcDef* p = new SbiProcDef( pParser, rName );
|
|
p->nPos = m_Data.size();
|
|
p->nId = rStrings.Add( rName );
|
|
// procs are always local
|
|
p->nProcId = 0;
|
|
p->pIn = this;
|
|
m_Data.insert( m_Data.begin() + p->nPos, std::unique_ptr<SbiProcDef>(p) );
|
|
return p;
|
|
}
|
|
|
|
// adding an externally constructed symbol definition
|
|
|
|
void SbiSymPool::Add( SbiSymDef* pDef )
|
|
{
|
|
if( !(pDef && pDef->pIn != this) )
|
|
return;
|
|
|
|
if( pDef->pIn )
|
|
{
|
|
#ifdef DBG_UTIL
|
|
|
|
pParser->Error( ERRCODE_BASIC_INTERNAL_ERROR, "Dbl Pool" );
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
pDef->nPos = m_Data.size();
|
|
if( !pDef->nId )
|
|
{
|
|
// A unique name must be created in the string pool
|
|
// for static variables (Form ProcName:VarName)
|
|
OUString aName( pDef->aName );
|
|
if( pDef->IsStatic() )
|
|
{
|
|
aName = pParser->aGblStrings.Find( nProcId )
|
|
+ ":"
|
|
+ pDef->aName;
|
|
}
|
|
pDef->nId = rStrings.Add( aName );
|
|
}
|
|
|
|
if( !pDef->GetProcDef() )
|
|
{
|
|
pDef->nProcId = nProcId;
|
|
}
|
|
pDef->pIn = this;
|
|
m_Data.insert( m_Data.begin() + pDef->nPos, std::unique_ptr<SbiSymDef>(pDef) );
|
|
}
|
|
|
|
|
|
SbiSymDef* SbiSymPool::Find( const OUString& rName, bool bSearchInParents )
|
|
{
|
|
sal_uInt16 nCount = m_Data.size();
|
|
for( sal_uInt16 i = 0; i < nCount; i++ )
|
|
{
|
|
SbiSymDef &r = *m_Data[ nCount - i - 1 ];
|
|
if( ( !r.nProcId || ( r.nProcId == nProcId)) &&
|
|
( r.aName.equalsIgnoreAsciiCase(rName)))
|
|
{
|
|
return &r;
|
|
}
|
|
}
|
|
if( bSearchInParents && pParent )
|
|
{
|
|
return pParent->Find( rName );
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
// find via position (from 0)
|
|
|
|
SbiSymDef* SbiSymPool::Get( sal_uInt16 n )
|
|
{
|
|
if (m_Data.size() <= n)
|
|
{
|
|
return nullptr;
|
|
}
|
|
else
|
|
{
|
|
return m_Data[ n ].get();
|
|
}
|
|
}
|
|
|
|
sal_uInt32 SbiSymPool::Define( const OUString& rName )
|
|
{
|
|
SbiSymDef* p = Find( rName );
|
|
if( p )
|
|
{
|
|
if( p->IsDefined() )
|
|
{
|
|
pParser->Error( ERRCODE_BASIC_LABEL_DEFINED, rName );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
p = AddSym( rName );
|
|
}
|
|
return p->Define();
|
|
}
|
|
|
|
sal_uInt32 SbiSymPool::Reference( const OUString& rName )
|
|
{
|
|
SbiSymDef* p = Find( rName );
|
|
if( !p )
|
|
{
|
|
p = AddSym( rName );
|
|
}
|
|
// to be sure
|
|
pParser->aGen.GenStmnt();
|
|
return p->Reference();
|
|
}
|
|
|
|
|
|
void SbiSymPool::CheckRefs()
|
|
{
|
|
for (std::unique_ptr<SbiSymDef> & r : m_Data)
|
|
{
|
|
if( !r->IsDefined() )
|
|
{
|
|
pParser->Error( ERRCODE_BASIC_UNDEF_LABEL, r->GetName() );
|
|
}
|
|
}
|
|
}
|
|
|
|
SbiSymDef::SbiSymDef( const OUString& rName ) :
|
|
aName(rName),
|
|
eType(SbxEMPTY),
|
|
pIn(nullptr),
|
|
nLen(0),
|
|
nDims(0),
|
|
nId(0),
|
|
nTypeId(0),
|
|
nProcId(0),
|
|
nPos(0),
|
|
nChain(0),
|
|
bNew(false),
|
|
bChained(false),
|
|
bByVal(false),
|
|
bOpt(false),
|
|
bStatic(false),
|
|
bAs(false),
|
|
bGlobal(false),
|
|
bParamArray(false),
|
|
bWithEvents(false),
|
|
bWithBrackets(false),
|
|
nDefaultId(0),
|
|
nFixedStringLength(-1)
|
|
{
|
|
}
|
|
|
|
SbiSymDef::~SbiSymDef()
|
|
{
|
|
}
|
|
|
|
SbiProcDef* SbiSymDef::GetProcDef()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
SbiConstDef* SbiSymDef::GetConstDef()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
const OUString& SbiSymDef::GetName()
|
|
{
|
|
if( pIn )
|
|
{
|
|
aName = pIn->rStrings.Find( nId );
|
|
}
|
|
return aName;
|
|
}
|
|
|
|
|
|
void SbiSymDef::SetType( SbxDataType t )
|
|
{
|
|
if( t == SbxVARIANT && pIn )
|
|
{
|
|
//See if there have been any deftype statements to set the default type
|
|
//of a variable based on its starting letter
|
|
sal_Unicode cu = aName[0];
|
|
if( cu < 256 )
|
|
{
|
|
unsigned char ch = static_cast<unsigned char>(cu);
|
|
if( ch == '_' )
|
|
{
|
|
ch = 'Z';
|
|
}
|
|
int ch2 = rtl::toAsciiUpperCase( ch );
|
|
int nIndex = ch2 - 'A';
|
|
if (nIndex >= 0 && nIndex < N_DEF_TYPES)
|
|
t = pIn->pParser->eDefTypes[nIndex];
|
|
}
|
|
}
|
|
eType = t;
|
|
}
|
|
|
|
// construct a backchain, if not yet defined
|
|
// the value that shall be stored as an operand is returned
|
|
|
|
sal_uInt32 SbiSymDef::Reference()
|
|
{
|
|
if( !bChained )
|
|
{
|
|
sal_uInt32 n = nChain;
|
|
nChain = pIn->pParser->aGen.GetOffset();
|
|
return n;
|
|
}
|
|
else return nChain;
|
|
}
|
|
|
|
|
|
sal_uInt32 SbiSymDef::Define()
|
|
{
|
|
sal_uInt32 n = pIn->pParser->aGen.GetPC();
|
|
pIn->pParser->aGen.GenStmnt();
|
|
if( nChain )
|
|
{
|
|
pIn->pParser->aGen.BackChain( nChain );
|
|
}
|
|
nChain = n;
|
|
bChained = true;
|
|
return nChain;
|
|
}
|
|
|
|
// A symbol definition may have its own pool. This is the case
|
|
// for objects and procedures (local variable)
|
|
|
|
SbiSymPool& SbiSymDef::GetPool()
|
|
{
|
|
if( !pPool )
|
|
{
|
|
pPool = std::make_unique<SbiSymPool>( pIn->pParser->aGblStrings, SbLOCAL, pIn->pParser );// is dumped
|
|
}
|
|
return *pPool;
|
|
}
|
|
|
|
SbiSymScope SbiSymDef::GetScope() const
|
|
{
|
|
return pIn ? pIn->GetScope() : SbLOCAL;
|
|
}
|
|
|
|
|
|
// The procedure definition has three pools:
|
|
// 1) aParams: is filled by the definition. Contains the
|
|
// parameters' names, like they're used inside the body.
|
|
// The first element is the return value.
|
|
// 2) pPool: all local variables
|
|
// 3) aLabels: labels
|
|
|
|
SbiProcDef::SbiProcDef( SbiParser* pParser, const OUString& rName,
|
|
bool bProcDecl )
|
|
: SbiSymDef( rName )
|
|
, aParams( pParser->aGblStrings, SbPARAM, pParser ) // is dumped
|
|
, aLabels( pParser->aLclStrings, SbLOCAL, pParser ) // is not dumped
|
|
, mbProcDecl( bProcDecl )
|
|
{
|
|
aParams.SetParent( &pParser->aPublics );
|
|
pPool = std::make_unique<SbiSymPool>( pParser->aGblStrings, SbLOCAL, pParser );
|
|
pPool->SetParent( &aParams );
|
|
nLine1 =
|
|
nLine2 = 0;
|
|
mePropMode = PropertyMode::NONE;
|
|
bPublic = true;
|
|
bCdecl = false;
|
|
bStatic = false;
|
|
// For return values the first element of the parameter
|
|
// list is always defined with name and type of the proc
|
|
aParams.AddSym( aName );
|
|
}
|
|
|
|
SbiProcDef::~SbiProcDef()
|
|
{}
|
|
|
|
SbiProcDef* SbiProcDef::GetProcDef()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
void SbiProcDef::SetType( SbxDataType t )
|
|
{
|
|
SbiSymDef::SetType( t );
|
|
aParams.Get( 0 )->SetType( eType );
|
|
}
|
|
|
|
// match with a forward-declaration
|
|
// if the match is OK, pOld is replaced by this in the pool
|
|
// pOld is deleted in any case!
|
|
|
|
void SbiProcDef::Match( SbiProcDef* pOld )
|
|
{
|
|
SbiSymDef *pn=nullptr;
|
|
// parameter 0 is the function name
|
|
sal_uInt16 i;
|
|
for( i = 1; i < aParams.GetSize(); i++ )
|
|
{
|
|
SbiSymDef* po = pOld->aParams.Get( i );
|
|
pn = aParams.Get( i );
|
|
// no type matching - that is done during running
|
|
// but is it maybe called with too little parameters?
|
|
if( !po && !pn->IsOptional() && !pn->IsParamArray() )
|
|
{
|
|
break;
|
|
}
|
|
pOld->aParams.Next();
|
|
}
|
|
|
|
if( pn && i < aParams.GetSize() && pOld->pIn )
|
|
{
|
|
// mark the whole line
|
|
pOld->pIn->GetParser()->SetCol1( 0 );
|
|
pOld->pIn->GetParser()->Error( ERRCODE_BASIC_BAD_DECLARATION, aName );
|
|
}
|
|
|
|
if( !pIn && pOld->pIn )
|
|
{
|
|
// Replace old entry with the new one
|
|
nPos = pOld->nPos;
|
|
nId = pOld->nId;
|
|
pIn = pOld->pIn;
|
|
|
|
// don't delete pOld twice, if it's stored in m_Data
|
|
if (pOld == pIn->m_Data[nPos].get())
|
|
pOld = nullptr;
|
|
pIn->m_Data[nPos].reset(this);
|
|
}
|
|
delete pOld;
|
|
}
|
|
|
|
void SbiProcDef::setPropertyMode( PropertyMode ePropMode )
|
|
{
|
|
mePropMode = ePropMode;
|
|
if( mePropMode == PropertyMode::NONE )
|
|
return;
|
|
|
|
// Prop name = original scanned procedure name
|
|
maPropName = aName;
|
|
|
|
// CompleteProcName includes "Property xxx "
|
|
// to avoid conflicts with other symbols
|
|
OUString aCompleteProcName = "Property ";
|
|
switch( mePropMode )
|
|
{
|
|
case PropertyMode::Get: aCompleteProcName += "Get "; break;
|
|
case PropertyMode::Let: aCompleteProcName += "Let "; break;
|
|
case PropertyMode::Set: aCompleteProcName += "Set "; break;
|
|
case PropertyMode::NONE: OSL_FAIL( "Illegal PropertyMode PropertyMode::NONE" ); break;
|
|
}
|
|
aCompleteProcName += aName;
|
|
aName = aCompleteProcName;
|
|
}
|
|
|
|
|
|
SbiConstDef::SbiConstDef( const OUString& rName )
|
|
: SbiSymDef( rName )
|
|
{
|
|
nVal = 0; eType = SbxINTEGER;
|
|
}
|
|
|
|
void SbiConstDef::Set( double n, SbxDataType t )
|
|
{
|
|
aVal.clear(); nVal = n; eType = t;
|
|
}
|
|
|
|
void SbiConstDef::Set( const OUString& n )
|
|
{
|
|
aVal = n; nVal = 0; eType = SbxSTRING;
|
|
}
|
|
|
|
SbiConstDef::~SbiConstDef()
|
|
{}
|
|
|
|
SbiConstDef* SbiConstDef::GetConstDef()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|