    /* -*- 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 <iosys.hxx>
    #include <memory>

    // test if there's an I/O channel

    bool SbiParser::Channel( bool bAlways )
    {
        bool bRes = false;
        Peek();
        if( IsHash() )
        {
            SbiExpression aExpr( this );
            while( Peek() == COMMA || Peek() == SEMICOLON )
                Next();
            aExpr.Gen();
            aGen.Gen( SbiOpcode::CHANNEL_ );
            bRes = true;
        }
        else if( bAlways )
            Error( ERRCODE_BASIC_EXPECTED, "#" );
        return bRes;
    }

    // it's tried that at object variables the Default-
    // Property is addressed for PRINT and WRITE

    void SbiParser::Print()
    {
        bool bChan = Channel();

        while( !bAbort )
        {
            if( !IsEoln( Peek() ) )
            {
                std::unique_ptr<SbiExpression> pExpr(new SbiExpression( this ));
                pExpr->Gen();
                pExpr.reset();
                Peek();
                aGen.Gen( eCurTok == COMMA ? SbiOpcode::PRINTF_ : SbiOpcode::BPRINT_ );
            }
            if( eCurTok == COMMA || eCurTok == SEMICOLON )
            {
                Next();
                if( IsEoln( Peek() ) ) break;
            }
            else
            {
                aGen.Gen( SbiOpcode::PRCHAR_, '\n' );
                break;
            }
        }
        if( bChan )
            aGen.Gen( SbiOpcode::CHAN0_ );
    }

    // WRITE #chan, expr, ...

    void SbiParser::Write()
    {
        bool bChan = Channel();

        while( !bAbort )
        {
            std::unique_ptr<SbiExpression> pExpr(new SbiExpression( this ));
            pExpr->Gen();
            pExpr.reset();
            aGen.Gen( SbiOpcode::BWRITE_ );
            if( Peek() == COMMA )
            {
                aGen.Gen( SbiOpcode::PRCHAR_, ',' );
                Next();
                if( IsEoln( Peek() ) ) break;
            }
            else
            {
                aGen.Gen( SbiOpcode::PRCHAR_, '\n' );
                break;
            }
        }
        if( bChan )
            aGen.Gen( SbiOpcode::CHAN0_ );
    }

    // #i92642 Handle LINE keyword outside ::Next()
    void SbiParser::Line()
    {
        // #i92642: Special handling to allow name as symbol
        if( Peek() == INPUT )
        {
            Next();
            LineInput();
        }
        else
        {
            aGen.Statement();

            KeywordSymbolInfo aInfo;
            aInfo.m_aKeywordSymbol = "line";
            aInfo.m_eSbxDataType = GetType();

            Symbol( &aInfo );
        }
    }

    // LINE INPUT [prompt], var$

    void SbiParser::LineInput()
    {
        Channel( true );
        std::unique_ptr<SbiExpression> pExpr(new SbiExpression( this, SbOPERAND ));
        if( !pExpr->IsVariable() )
            Error( ERRCODE_BASIC_VAR_EXPECTED );
        if( pExpr->GetType() != SbxVARIANT && pExpr->GetType() != SbxSTRING )
            Error( ERRCODE_BASIC_CONVERSION );
        pExpr->Gen();
        aGen.Gen( SbiOpcode::LINPUT_ );
        pExpr.reset();
        aGen.Gen( SbiOpcode::CHAN0_ );     // ResetChannel() not in StepLINPUT() anymore
    }

    // INPUT

    void SbiParser::Input()
    {
        aGen.Gen( SbiOpcode::RESTART_ );
        Channel( true );
        std::unique_ptr<SbiExpression> pExpr(new SbiExpression( this, SbOPERAND ));
        while( !bAbort )
        {
            if( !pExpr->IsVariable() )
                Error( ERRCODE_BASIC_VAR_EXPECTED );
            pExpr->Gen();
            aGen.Gen( SbiOpcode::INPUT_ );
            if( Peek() == COMMA )
            {
                Next();
                pExpr.reset(new SbiExpression( this, SbOPERAND ));
            }
            else break;
        }
        pExpr.reset();
        aGen.Gen( SbiOpcode::CHAN0_ );
    }

    // OPEN stringexpr FOR mode ACCESS access mode AS Channel [Len=n]

    void SbiParser::Open()
    {
        bInStatement = true;
        SbiExpression aFileName( this );
        SbiToken eTok;
        TestToken( FOR );
        StreamMode nMode = StreamMode::NONE;
        SbiStreamFlags nFlags = SbiStreamFlags::NONE;
        switch( Next() )
        {
            case INPUT:
                nMode = StreamMode::READ;  nFlags |= SbiStreamFlags::Input; break;
            case OUTPUT:
                nMode = StreamMode::WRITE | StreamMode::TRUNC; nFlags |= SbiStreamFlags::Output; break;
            case APPEND:
                nMode = StreamMode::WRITE; nFlags |= SbiStreamFlags::Append; break;
            case RANDOM:
                nMode = StreamMode::READ | StreamMode::WRITE; nFlags |= SbiStreamFlags::Random; break;
            case BINARY:
                nMode = StreamMode::READ | StreamMode::WRITE; nFlags |= SbiStreamFlags::Binary; break;
            default:
                Error( ERRCODE_BASIC_SYNTAX );
        }
        if( Peek() == ACCESS )
        {
            Next();
            eTok = Next();
            // influence only READ,WRITE-Flags in nMode
            nMode &= ~StreamMode(StreamMode::READ | StreamMode::WRITE);     // delete
            if( eTok == READ )
            {
                if( Peek() == WRITE )
                {
                    Next();
                    nMode |= (StreamMode::READ | StreamMode::WRITE);
                }
                else
                    nMode |= StreamMode::READ;
            }
            else if( eTok == WRITE )
                nMode |= StreamMode::WRITE;
            else
                Error( ERRCODE_BASIC_SYNTAX );
        }
        switch( Peek() )
        {
            case SHARED:
                Next(); nMode |= StreamMode::SHARE_DENYNONE; break;
            case LOCK:
                Next();
                eTok = Next();
                if( eTok == READ )
                {
                    if( Peek() == WRITE )
                    {
                        Next();
                        nMode |= StreamMode::SHARE_DENYALL;
                    }
                    else nMode |= StreamMode::SHARE_DENYREAD;
                }
                else if( eTok == WRITE )
                    nMode |= StreamMode::SHARE_DENYWRITE;
                else
                    Error( ERRCODE_BASIC_SYNTAX );
                break;
            default: break;
        }
        TestToken( AS );
        // channel number
        std::unique_ptr<SbiExpression> pChan(new SbiExpression( this ));
        if( !pChan )
            Error( ERRCODE_BASIC_SYNTAX );
        std::unique_ptr<SbiExpression> pLen;
        if( Peek() == SYMBOL )
        {
            Next();
            if( aSym.equalsIgnoreAsciiCase("LEN") )
            {
                TestToken( EQ );
                pLen.reset(new SbiExpression( this ));
            }
        }
        if( !pLen ) pLen.reset(new SbiExpression( this, 128, SbxINTEGER ));
        // the stack for the OPEN command looks as follows:
        // block length
        // channel number
        // file name
        pLen->Gen();
        if( pChan )
            pChan->Gen();
        aFileName.Gen();
        aGen.Gen( SbiOpcode::OPEN_, static_cast<sal_uInt32>(nMode), static_cast<sal_uInt32>(nFlags) );
        bInStatement = false;
    }

    // NAME file AS file

    void SbiParser::Name()
    {
        // #i92642: Special handling to allow name as symbol
        if( Peek() == EQ )
        {
            aGen.Statement();

            KeywordSymbolInfo aInfo;
            aInfo.m_aKeywordSymbol = "name";
            aInfo.m_eSbxDataType = GetType();

            Symbol( &aInfo );
            return;
        }
        SbiExpression aExpr1( this );
        TestToken( AS );
        SbiExpression aExpr2( this );
        aExpr1.Gen();
        aExpr2.Gen();
        aGen.Gen( SbiOpcode::RENAME_ );
    }

    // CLOSE [n,...]

    void SbiParser::Close()
    {
        Peek();
        if( IsEoln( eCurTok ) )
            aGen.Gen( SbiOpcode::CLOSE_, 0 );
        else
        for( ;; )
        {
            SbiExpression aExpr( this );
            while( Peek() == COMMA || Peek() == SEMICOLON )
                Next();
            aExpr.Gen();
            aGen.Gen( SbiOpcode::CHANNEL_ );
            aGen.Gen( SbiOpcode::CLOSE_, 1 );

            if( IsEoln( Peek() ) )
                break;
        }
    }

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

↑ V668 There is no sense in testing the 'pChan' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

↑ V668 There is no sense in testing the 'pChan' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.