Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  loops.cxx   Sprache: 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 <parser.hxx>
#include <memory>

#include <basic/sberrors.hxx>
#include <basic/sbmod.hxx>

#include <rtl/ustrbuf.hxx>

static bool EndsIfBranch(SbiToken eTok)
{
    return eTok == ELSEIF || eTok == ELSE || eTok == ENDIF;
}

// Single-line IF and Multiline IF

void SbiParser::If()
{
    sal_uInt32 nEndLbl;
    SbiToken eTok = NIL;
    // ignore end-tokens
    SbiExpression aCond( this );
    aCond.Gen();
    TestToken( THEN );
    if( IsEoln( Next() ) )
    {
        // At the end of each block a jump to ENDIF must be inserted,
        // so that the condition is not evaluated again at ELSEIF.
        // The table collects all jump points.
        constexpr sal_uInt16 JMP_TABLE_SIZE = 100;
        sal_uInt32 pnJmpToEndLbl[JMP_TABLE_SIZE];   // 100 ELSEIFs allowed
        sal_uInt16 iJmp = 0;                        // current table index

        // multiline IF
        nEndLbl = aGen.Gen( SbiOpcode::JUMPF_, 0 );
        eTok = Peek();
        while (!EndsIfBranch(eTok) && !bAbort && Parse())
        {
            eTok = Peek();
            if( IsEof() )
            {
                Error( ERRCODE_BASIC_BAD_BLOCK, ENDIF ); bAbort = truereturn;
            }
        }
        while( eTok == ELSEIF )
        {
            // jump to ENDIF in case of a successful IF/ELSEIF
            if( iJmp >= JMP_TABLE_SIZE )
            {
                Error( ERRCODE_BASIC_PROG_TOO_LARGE );  bAbort = true;  return;
            }
            pnJmpToEndLbl[iJmp++] = aGen.Gen( SbiOpcode::JUMP_, 0 );

            Next();
            aGen.BackChain( nEndLbl );

            aGen.Statement();
            auto pCond = std::make_unique<SbiExpression>( this );
            pCond->Gen();
            nEndLbl = aGen.Gen( SbiOpcode::JUMPF_, 0 );
            pCond.reset();
            TestToken( THEN );
            eTok = Peek();
            while (!EndsIfBranch(eTok) && !bAbort && Parse())
            {
                eTok = Peek();
                if( IsEof() )
                {
                    Error( ERRCODE_BASIC_BAD_BLOCK, ENDIF );  bAbort = truereturn;
                }
            }
        }
        if( eTok == ELSE )
        {
            Next();
            sal_uInt32 nElseLbl = nEndLbl;
            nEndLbl = aGen.Gen( SbiOpcode::JUMP_, 0 );
            aGen.BackChain( nElseLbl );

            aGen.Statement();
            StmntBlock( ENDIF );
        }
        else if( eTok == ENDIF )
            Next();
        else
        {
            Error(ERRCODE_BASIC_BAD_BLOCK, ENDIF);
            bAbort = true;
            return;
        }

        while( iJmp > 0 )
        {
            iJmp--;
            aGen.BackChain( pnJmpToEndLbl[iJmp] );
        }
    }
    else
    {
        // single line IF
        bSingleLineIf = true;
        nEndLbl = aGen.Gen( SbiOpcode::JUMPF_, 0 );
        Push( eCurTok );
        // tdf#128263: update push positions to correctly restore in Next()
        nPLine = nLine;
        nPCol1 = nCol1;
        nPCol2 = nCol2;

        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( SbiOpcode::JUMP_, 0 );
            aGen.BackChain( nElseLbl );
            while( !bAbort )
            {
                if( !Parse() ) break;
                eTok = Peek();
                if( eTok == EOLN || eTok == REM )
                    break;
            }
        }
        bSingleLineIf = false;
    }
    aGen.BackChain( nEndLbl );
}

// ELSE/ELSEIF/ENDIF without IF

void SbiParser::NoIf()
{
    Error( ERRCODE_BASIC_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 ? SbiOpcode::JUMPF_ : SbiOpcode::JUMPT_, nStartLbl );
        } else
            if (eTok == EOLN || eTok == REM)
                aGen.Gen (SbiOpcode::JUMP_, nStartLbl);
            else
                Error( ERRCODE_BASIC_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 ? SbiOpcode::JUMPT_ : SbiOpcode::JUMPF_, 0 );
        StmntBlock( LOOP );
        TestEoln();
        aGen.Gen( SbiOpcode::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( SbiOpcode::JUMPF_, 0 );
    StmntBlock( WEND );
    aGen.Gen( SbiOpcode::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 );
    if (!aLvalue.IsVariable())
    {
        bAbort = true;
        return// the error is already set in SbiExpression ctor
    }
    aLvalue.Gen();      // variable on the Stack

    if( bForEach )
    {
        TestToken( IN_ );
        SbiExpression aCollExpr( this, SbOPERAND );
        aCollExpr.Gen();    // Collection var to for stack
        TestEoln();
        aGen.Gen( SbiOpcode::INITFOREACH_ );
    }
    else
    {
        TestToken( EQ );
        SbiExpression aStartExpr( this );
        aStartExpr.Gen();
        TestToken( TO );
        SbiExpression aStopExpr( this );
        aStopExpr.Gen();
        if( Peek() == STEP )
        {
            Next();
            SbiExpression aStepExpr( this );
            aStepExpr.Gen();
        }
        else
        {
            SbiExpression aOne( this, 1, SbxINTEGER );
            aOne.Gen();
        }
        TestEoln();
        // The stack has all 4 elements now: variable, start, end, increment
        // bind start value
        aGen.Gen( SbiOpcode::INITFOR_ );
    }

    sal_uInt32 nLoop = aGen.GetPC();
    // do tests, maybe free the stack
    sal_uInt32 nEndTarget = aGen.Gen( SbiOpcode::TESTFOR_, 0 );
    OpenBlock( FOR );
    StmntBlock( NEXT );
    aGen.Gen( SbiOpcode::NEXT_ );
    aGen.Gen( SbiOpcode::JUMP_, nLoop );
    // are there variables after NEXT?
    if( Peek() == SYMBOL )
    {
        SbiExpression aVar( this, SbOPERAND );
        if( aVar.GetRealVar() != aLvalue.GetRealVar() )
            Error( ERRCODE_BASIC_EXPECTED, aLvalue.GetRealVar()->GetName() );
    }
    aGen.BackChain( nEndTarget );
    CloseBlock();
}

// WITH .. END WITH

namespace
{
// Generate a '{_with_library.module_offset} = rVar'
// Use the {_with_library.module_offset} in OpenBlock
// The name of the variable can't be used by user: a name like [{_with_library.module_offset}]
// is valid, but not without the square brackets
struct WithLocalVar
{
    WithLocalVar(SbiParser& rParser, SbiExpression& rVar)
        : m_rParser(rParser)
        , m_aWithParent(createLocalVar(rParser))
    {
        // Assignment
        m_aWithParent.Gen();
        rVar.Gen();
        m_rParser.aGen.Gen(SbiOpcode::PUTC_);
    }

    ~WithLocalVar()
    {
        // {_with_library.module_offset} = Nothing
        m_aWithParent.Gen();
        m_rParser.aGen.Gen(SbiOpcode::RTL_, m_rParser.aGblStrings.Add(u"Nothing"_ustr), SbxOBJECT);
        m_rParser.aGen.Gen(SbiOpcode::PUTC_);
    }

    static SbiExpression createLocalVar(SbiParser& rParser)
    {
        // Create the unique name
        OUStringBuffer moduleName(rParser.aGen.GetModule().GetName());
        for (auto parent = rParser.aGen.GetModule().GetParent(); parent;
             parent = parent->GetParent())
            moduleName.insert(0, parent->GetName() + ".");

        OUString uniqueName
            = "{_with_" + moduleName + "_" + OUString::number(rParser.aGen.GetOffset()) + "}";
        while (rParser.pPool->Find(uniqueName) != nullptr)
        {
            static sal_Int64 unique_suffix;
            uniqueName = "{_with_" + moduleName + "_" + OUString::number(rParser.aGen.GetOffset())
                         + "_" + OUString::number(unique_suffix++) + "}";
        }
        SbiSymDef* pWithParentDef = new SbiSymDef(uniqueName);
        pWithParentDef->SetType(SbxOBJECT);
        rParser.pPool->Add(pWithParentDef);

        // DIM local variable: work with Option Explicit
        rParser.aGen.Gen(SbiOpcode::LOCAL_, pWithParentDef->GetId(), pWithParentDef->GetType());

        return SbiExpression(&rParser, *pWithParentDef);
    }

    SbiParser& m_rParser;
    SbiExpression m_aWithParent;
};
}

void SbiParser::With()
{
    SbiExpression aVar( this, SbOPERAND );

    SbiExprNode *pNode = aVar.GetExprNode()->GetRealNode();
    if (!pNode)
        return;
    SbiSymDef* pDef = pNode->GetVar();
    // Variant, from 27.6.1997, #41090: empty -> must be Object
    if( pDef->GetType() == SbxVARIANT || pDef->GetType() == SbxEMPTY )
        pDef->SetType( SbxOBJECT );
    else if( pDef->GetType() != SbxOBJECT )
        Error( ERRCODE_BASIC_NEEDS_OBJECT );

    pNode->SetType( SbxOBJECT );

    std::optional<WithLocalVar> oLocalVar;
    if (pDef->GetProcDef())
        oLocalVar.emplace(*this, aVar);

    OpenBlock(NIL, oLocalVar ? oLocalVar->m_aWithParent.GetExprNode() : aVar.GetExprNode());
    StmntBlock( ENDWITH );
    CloseBlock();
}

// On expr Goto/Gosub n,n,n...

void SbiParser::OnGoto()
{
    SbiExpression aCond( this );
    aCond.Gen();
    sal_uInt32 nLabelsTarget = aGen.Gen( SbiOpcode::ONJUMP_, 0 );
    SbiToken eTok = Next();
    if( eTok != GOTO && eTok != GOSUB )
    {
        Error( ERRCODE_BASIC_EXPECTED, u"GoTo/GoSub"_ustr );
        eTok = GOTO;
    }

    sal_uInt32 nLbl = 0;
    do
    {
        Next(); // get label
        if( MayBeLabel() )
        {
            sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
            aGen.Gen( SbiOpcode::JUMP_, nOff );
            nLbl++;
        }
        else Error( ERRCODE_BASIC_LABEL_EXPECTED );
    }
    while( !bAbort && TestComma() );
    if( eTok == GOSUB )
        nLbl |= 0x8000;
    aGen.Patch( nLabelsTarget, nLbl );
}

// GOTO/GOSUB

void SbiParser::Goto()
{
    SbiOpcode eOp = eCurTok == GOTO ? SbiOpcode::JUMP_ : SbiOpcode::GOSUB_;
    Next();
    if( MayBeLabel() )
    {
        sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
        aGen.Gen( eOp, nOff );
    }
    else Error( ERRCODE_BASIC_LABEL_EXPECTED );
}

// RETURN [label]

void SbiParser::Return()
{
    Next();
    if( MayBeLabel() )
    {
        sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
        aGen.Gen( SbiOpcode::RETURN_, nOff );
    }
    else aGen.Gen( SbiOpcode::RETURN_, 0 );
}

// SELECT CASE

void SbiParser::Select()
{
    TestToken( CASE );
    SbiExpression aCase( this );
    SbiToken eTok = NIL;
    aCase.Gen();
    aGen.Gen( SbiOpcode::CASE_ );
    TestEoln();
    sal_uInt32 nNextTarget = 0;
    sal_uInt32 nDoneTarget = 0;
    bool bElse = false;

    while( !bAbort )
    {
        eTok = Next();
        if( eTok == CASE )
        {
            if( nNextTarget )
            {
                aGen.BackChain( nNextTarget );
                nNextTarget = 0;
            }
            aGen.Statement();

            bool bDone = false;
            sal_uInt32 nTrueTarget = 0;
            if( Peek() == ELSE )
            {
                // CASE ELSE
                Next();
                bElse = true;
            }
            else while( !bDone )
            {
                if( bElse )
                    Error( ERRCODE_BASIC_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( ERRCODE_BASIC_SYNTAX );
                    else Next();
                    SbiExpression aCompare( this );
                    aCompare.Gen();
                    nTrueTarget = aGen.Gen(
                        SbiOpcode::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( SbiOpcode::CASETO_, nTrueTarget );
                    }
                    else
                        // CASE a
                        nTrueTarget = aGen.Gen( SbiOpcode::CASEIS_, nTrueTarget, SbxEQ );

                }
                if( Peek() == COMMA ) Next();
                else
                {
                    TestEoln();
                    bDone = true;
                }
            }

            if( !bElse )
            {
                nNextTarget = aGen.Gen( SbiOpcode::JUMP_, nNextTarget );
                aGen.BackChain( nTrueTarget );
            }
            // build the statement body
            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( SbiOpcode::JUMP_, nDoneTarget );
        }
        else if( !IsEoln( eTok ) )
            break;
    }
done:
    if( eTok != ENDSELECT )
        Error( ERRCODE_BASIC_EXPECTED, ENDSELECT );
    if( nNextTarget )
        aGen.BackChain( nNextTarget );
    aGen.BackChain( nDoneTarget );
    aGen.Gen( SbiOpcode::ENDCASE_ );
}

// ON Error/Variable

void SbiParser::On()
{
    SbiToken eTok = Peek();
    OUString aString = SbiTokenizer::Symbol(eTok);
    if (aString.equalsIgnoreAsciiCase("ERROR"))
    {
        eTok = ERROR_; // Error comes as SYMBOL
    }
    if( eTok != ERROR_ && eTok != LOCAL )
    {
        OnGoto();
    }
    else
    {
        if( eTok == LOCAL )
        {
            Next();
        }
        Next (); // no more TestToken, as there'd be an error otherwise

        Next(); // get token after error
        if( eCurTok == GOTO )
        {
            // ON ERROR GOTO label|0
            Next();
            bool bError_ = false;
            if( MayBeLabel() )
            {
                if( eCurTok == NUMBER && !nVal )
                {
                    aGen.Gen( SbiOpcode::STDERROR_ );
                }
                else
                {
                    sal_uInt32 nOff = pProc->GetLabels().Reference( aSym );
                    aGen.Gen( SbiOpcode::ERRHDL_, nOff );
                }
            }
            else if( eCurTok == MINUS )
            {
                Next();
                if( eCurTok == NUMBER && nVal == 1 )
                {
                    aGen.Gen( SbiOpcode::STDERROR_ );
                }
                else
                {
                    bError_ = true;
                }
            }
            if( bError_ )
            {
                Error( ERRCODE_BASIC_LABEL_EXPECTED );
            }
        }
        else if( eCurTok == RESUME )
        {
            TestToken( NEXT );
            aGen.Gen( SbiOpcode::NOERROR_ );
        }
        else Error( ERRCODE_BASIC_EXPECTED, u"GoTo/Resume"_ustr );
    }
}

// RESUME [0]|NEXT|label

void SbiParser::Resume()
{
    sal_uInt32 nLbl;

    switch( Next() )
    {
        case EOS:
        case EOLN:
            aGen.Gen( SbiOpcode::RESUME_, 0 );
            break;
        case NEXT:
            aGen.Gen( SbiOpcode::RESUME_, 1 );
            Next();
            break;
        case NUMBER:
            if( !nVal )
            {
                aGen.Gen( SbiOpcode::RESUME_, 0 );
                break;
            }
            [[fallthrough]];
        case SYMBOL:
            if( MayBeLabel() )
            {
                nLbl = pProc->GetLabels().Reference( aSym );
                aGen.Gen( SbiOpcode::RESUME_, nLbl );
                Next();
                break;
            }
            [[fallthrough]];
        default:
            Error( ERRCODE_BASIC_LABEL_EXPECTED );
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

91%


¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge