/* -*- 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 <string.h>
#include <rtl/character.hxx>
#include <o3tl/make_unique.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 )
{
    char buf[ 40 ];
    switch( t )
    {
        case SbxINTEGER: snprintf( buf, sizeof(buf), "%d", static_cast<short>(n) ); break;
        case SbxLONG:    snprintf( buf, sizeof(buf), "%ld", static_cast<long>(n) ); break;
        case SbxSINGLE:  snprintf( buf, sizeof(buf), "%.6g", static_cast<float>(n) ); break;
        case SbxDOUBLE:  snprintf( buf, sizeof(buf), "%.16g", n ); break;
        default: break;
    }
    return Add( OUString::createFromAscii( buf ) );
}
 
SbiSymPool::SbiSymPool( SbiStringPool& r, SbiSymScope s, SbiParser* pP ) : rStrings( r ), pParser( pP )
{
    eScope   = s;
    pParent  = nullptr;
    nCur     =
    nProcId  = 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 )
    {
        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 )
{
    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( 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;
    nDims    = 0;
    nTypeId  = 0;
    nProcId  = 0;
    nId      = 0;
    nPos     = 0;
    nLen     = 0;
    nChain   = 0;
    bAs      =
    bNew     =
    bStatic  =
    bOpt     =
    bParamArray =
    bWithEvents =
    bWithBrackets =
    bByVal   =
    bChained =
    bGlobal  = false;
    pIn      = nullptr;
    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 = o3tl::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 = o3tl::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( std::unique_ptr<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;
        std::unique_ptr<SbiSymDef> tmp(this);
        std::swap(pIn->m_Data[nPos], tmp);
        tmp.release();
    }
}
 
void SbiProcDef::setPropertyMode( PropertyMode ePropMode )
{
    mePropMode = ePropMode;
    if( mePropMode != PropertyMode::NONE )
    {
        // 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: */

V530 The return value of function 'release' is required to be utilized.

V614 Potentially uninitialized buffer 'buf' used. Consider checking the first actual argument of the 'createFromAscii' function.

V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.

V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.

V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.

V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.