<commit_before>/*************************************************************************
 *
 *  $RCSfile: storagehelper.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: kz $ $Date: 2004-10-04 21:07:02 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _COM_SUN_STAR_EMBED_ELEMENTMODES_HPP_
#include <com/sun/star/embed/ElementModes.hpp>
#endif
#ifndef _COM_SUN_STAR_EMBED_XENCRYPTIONPROTECTEDSOURCE_HPP_
#include <com/sun/star/embed/XEncryptionProtectedSource.hpp>
#endif

#ifndef _COM_SUN_STAR_UCB_XSIMPLEFILEACCESS_HPP_
#include <com/sun/star/ucb/XSimpleFileAccess.hpp>
#endif

#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif

#include <comphelper/fileformat.h>
#include <comphelper/storagehelper.hxx>
#include <comphelper/processfactory.hxx>

using namespace ::com::sun::star;

namespace comphelper {

// ----------------------------------------------------------------------
uno::Reference< lang::XSingleServiceFactory > OStorageHelper::GetStorageFactory(
                            const uno::Reference< lang::XMultiServiceFactory >& xSF )
        throw ( uno::Exception )
{
    uno::Reference< lang::XMultiServiceFactory > xFactory = xSF.is() ? xSF : ::comphelper::getProcessServiceFactory();
    if ( !xFactory.is() )
        throw uno::RuntimeException();

    uno::Reference < lang::XSingleServiceFactory > xStorageFactory(
                    xFactory->createInstance ( ::rtl::OUString::createFromAscii( "com.sun.star.embed.StorageFactory" ) ),
                    uno::UNO_QUERY );

    if ( !xStorageFactory.is() )
        throw uno::RuntimeException();

    return xStorageFactory;
}

// ----------------------------------------------------------------------
uno::Reference< embed::XStorage > OStorageHelper::GetTemporaryStorage(
            const uno::Reference< lang::XMultiServiceFactory >& xFactory )
    throw ( uno::Exception )
{
    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( xFactory )->createInstance(),
                                                    uno::UNO_QUERY );
    if ( !xTempStorage.is() )
        throw uno::RuntimeException();

    return xTempStorage;
}

// ----------------------------------------------------------------------
uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromURL(
            const ::rtl::OUString& aURL,
            sal_Int32 nStorageMode,
            const uno::Reference< lang::XMultiServiceFactory >& xFactory )
    throw ( uno::Exception )
{
    uno::Sequence< uno::Any > aArgs( 2 );
    aArgs[0] <<= aURL;
    aArgs[1] <<= nStorageMode;

    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( xFactory )->createInstanceWithArguments( aArgs ),
                                                    uno::UNO_QUERY );
    if ( !xTempStorage.is() )
        throw uno::RuntimeException();

    return xTempStorage;
}

// ----------------------------------------------------------------------
uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromInputStream(
            const uno::Reference < io::XInputStream >& xStream,
            const uno::Reference< lang::XMultiServiceFactory >& xFactory )
        throw ( uno::Exception )
{
    uno::Sequence< uno::Any > aArgs( 2 );
    aArgs[0] <<= xStream;
    aArgs[1] <<= embed::ElementModes::READ;

    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( xFactory )->createInstanceWithArguments( aArgs ),
                                                    uno::UNO_QUERY );
    if ( !xTempStorage.is() )
        throw uno::RuntimeException();

    return xTempStorage;
}

// ----------------------------------------------------------------------
uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromStream(
            const uno::Reference < io::XStream >& xStream,
            sal_Int32 nStorageMode,
            const uno::Reference< lang::XMultiServiceFactory >& xFactory )
        throw ( uno::Exception )
{
    uno::Sequence< uno::Any > aArgs( 2 );
    aArgs[0] <<= xStream;
    aArgs[1] <<= nStorageMode;

    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( xFactory )->createInstanceWithArguments( aArgs ),
                                                    uno::UNO_QUERY );
    if ( !xTempStorage.is() )
        throw uno::RuntimeException();

    return xTempStorage;
}

// ----------------------------------------------------------------------
void OStorageHelper::CopyInputToOutput(
            const uno::Reference< io::XInputStream >& xInput,
            const uno::Reference< io::XOutputStream >& xOutput )
    throw ( uno::Exception )
{
    static const sal_Int32 nConstBufferSize = 32000;

    sal_Int32 nRead;
    uno::Sequence < sal_Int8 > aSequence ( nConstBufferSize );

    do
    {
        nRead = xInput->readBytes ( aSequence, nConstBufferSize );
        if ( nRead < nConstBufferSize )
        {
            uno::Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead );
            xOutput->writeBytes ( aTempBuf );
        }
        else
            xOutput->writeBytes ( aSequence );
    }
    while ( nRead == nConstBufferSize );
}

// ----------------------------------------------------------------------
uno::Reference< io::XInputStream > OStorageHelper::GetInputStreamFromURL(
            const ::rtl::OUString& aURL,
            const uno::Reference< lang::XMultiServiceFactory >& xSF )
    throw ( uno::Exception )
{
    uno::Reference< lang::XMultiServiceFactory > xFactory = xSF.is() ? xSF : ::comphelper::getProcessServiceFactory();
    if ( !xFactory.is() )
        throw uno::RuntimeException();

    uno::Reference < ::com::sun::star::ucb::XSimpleFileAccess > xTempAccess(
            xFactory->createInstance ( ::rtl::OUString::createFromAscii( "com.sun.star.ucb.SimpleFileAccess" ) ),
            uno::UNO_QUERY );

    if ( !xTempAccess.is() )
        throw uno::RuntimeException();

    uno::Reference< io::XInputStream > xInputStream = xTempAccess->openFileRead( aURL );
    if ( !xInputStream.is() )
        throw uno::RuntimeException();

    return xInputStream;
}

// ----------------------------------------------------------------------
void OStorageHelper::SetCommonStoragePassword(
            const uno::Reference< embed::XStorage >& xStorage,
            const ::rtl::OUString& aPass )
    throw ( uno::Exception )
{
    uno::Reference< embed::XEncryptionProtectedSource > xEncrSet( xStorage, uno::UNO_QUERY );
    if ( !xEncrSet.is() )
        throw io::IOException(); // TODO

    xEncrSet->setEncryptionPassword( aPass );
}

// ----------------------------------------------------------------------
sal_Int32 OStorageHelper::GetXStorageFormat(
            const uno::Reference< embed::XStorage >& xStorage )
        throw ( uno::Exception )
{
    uno::Reference< beans::XPropertySet > xStorProps( xStorage, uno::UNO_QUERY_THROW );

    ::rtl::OUString aMediaType;
    xStorProps->getPropertyValue( ::rtl::OUString::createFromAscii( "MediaType" ) ) >>= aMediaType;

    sal_Int32 nResult = 0;

    // TODO/LATER: the filter configuration could be used to detect it later, or batter a special service
    if ( aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.writer" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.writer.web" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.writer.global" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.draw" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.impress" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.calc" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.chart" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.math" ) ) )
    {
        nResult = SOFFICE_FILEFORMAT_60;
    }
    else if ( aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.text" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.text-web" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.text-global" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.drawing" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.presentation" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.spreadsheet" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.chart" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.math" ) ) )
    {
        nResult = SOFFICE_FILEFORMAT_8;
    }
    else
        throw uno::Exception();

    return nResult;
}

}

<commit_msg>INTEGRATION: CWS fwkp2fix02 (1.2.34); FILE MERGED 2004/11/12 12:14:17 mba 1.2.34.1: #i36981#: math objects couldn't be loaded<commit_after>/*************************************************************************
 *
 *  $RCSfile: storagehelper.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: obo $ $Date: 2004-11-17 15:36:09 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _COM_SUN_STAR_EMBED_ELEMENTMODES_HPP_
#include <com/sun/star/embed/ElementModes.hpp>
#endif
#ifndef _COM_SUN_STAR_EMBED_XENCRYPTIONPROTECTEDSOURCE_HPP_
#include <com/sun/star/embed/XEncryptionProtectedSource.hpp>
#endif

#ifndef _COM_SUN_STAR_UCB_XSIMPLEFILEACCESS_HPP_
#include <com/sun/star/ucb/XSimpleFileAccess.hpp>
#endif

#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif

#include <comphelper/fileformat.h>
#include <comphelper/storagehelper.hxx>
#include <comphelper/processfactory.hxx>

using namespace ::com::sun::star;

namespace comphelper {

// ----------------------------------------------------------------------
uno::Reference< lang::XSingleServiceFactory > OStorageHelper::GetStorageFactory(
                            const uno::Reference< lang::XMultiServiceFactory >& xSF )
        throw ( uno::Exception )
{
    uno::Reference< lang::XMultiServiceFactory > xFactory = xSF.is() ? xSF : ::comphelper::getProcessServiceFactory();
    if ( !xFactory.is() )
        throw uno::RuntimeException();

    uno::Reference < lang::XSingleServiceFactory > xStorageFactory(
                    xFactory->createInstance ( ::rtl::OUString::createFromAscii( "com.sun.star.embed.StorageFactory" ) ),
                    uno::UNO_QUERY );

    if ( !xStorageFactory.is() )
        throw uno::RuntimeException();

    return xStorageFactory;
}

// ----------------------------------------------------------------------
uno::Reference< embed::XStorage > OStorageHelper::GetTemporaryStorage(
            const uno::Reference< lang::XMultiServiceFactory >& xFactory )
    throw ( uno::Exception )
{
    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( xFactory )->createInstance(),
                                                    uno::UNO_QUERY );
    if ( !xTempStorage.is() )
        throw uno::RuntimeException();

    return xTempStorage;
}

// ----------------------------------------------------------------------
uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromURL(
            const ::rtl::OUString& aURL,
            sal_Int32 nStorageMode,
            const uno::Reference< lang::XMultiServiceFactory >& xFactory )
    throw ( uno::Exception )
{
    uno::Sequence< uno::Any > aArgs( 2 );
    aArgs[0] <<= aURL;
    aArgs[1] <<= nStorageMode;

    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( xFactory )->createInstanceWithArguments( aArgs ),
                                                    uno::UNO_QUERY );
    if ( !xTempStorage.is() )
        throw uno::RuntimeException();

    return xTempStorage;
}

// ----------------------------------------------------------------------
uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromInputStream(
            const uno::Reference < io::XInputStream >& xStream,
            const uno::Reference< lang::XMultiServiceFactory >& xFactory )
        throw ( uno::Exception )
{
    uno::Sequence< uno::Any > aArgs( 2 );
    aArgs[0] <<= xStream;
    aArgs[1] <<= embed::ElementModes::READ;

    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( xFactory )->createInstanceWithArguments( aArgs ),
                                                    uno::UNO_QUERY );
    if ( !xTempStorage.is() )
        throw uno::RuntimeException();

    return xTempStorage;
}

// ----------------------------------------------------------------------
uno::Reference< embed::XStorage > OStorageHelper::GetStorageFromStream(
            const uno::Reference < io::XStream >& xStream,
            sal_Int32 nStorageMode,
            const uno::Reference< lang::XMultiServiceFactory >& xFactory )
        throw ( uno::Exception )
{
    uno::Sequence< uno::Any > aArgs( 2 );
    aArgs[0] <<= xStream;
    aArgs[1] <<= nStorageMode;

    uno::Reference< embed::XStorage > xTempStorage( GetStorageFactory( xFactory )->createInstanceWithArguments( aArgs ),
                                                    uno::UNO_QUERY );
    if ( !xTempStorage.is() )
        throw uno::RuntimeException();

    return xTempStorage;
}

// ----------------------------------------------------------------------
void OStorageHelper::CopyInputToOutput(
            const uno::Reference< io::XInputStream >& xInput,
            const uno::Reference< io::XOutputStream >& xOutput )
    throw ( uno::Exception )
{
    static const sal_Int32 nConstBufferSize = 32000;

    sal_Int32 nRead;
    uno::Sequence < sal_Int8 > aSequence ( nConstBufferSize );

    do
    {
        nRead = xInput->readBytes ( aSequence, nConstBufferSize );
        if ( nRead < nConstBufferSize )
        {
            uno::Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead );
            xOutput->writeBytes ( aTempBuf );
        }
        else
            xOutput->writeBytes ( aSequence );
    }
    while ( nRead == nConstBufferSize );
}

// ----------------------------------------------------------------------
uno::Reference< io::XInputStream > OStorageHelper::GetInputStreamFromURL(
            const ::rtl::OUString& aURL,
            const uno::Reference< lang::XMultiServiceFactory >& xSF )
    throw ( uno::Exception )
{
    uno::Reference< lang::XMultiServiceFactory > xFactory = xSF.is() ? xSF : ::comphelper::getProcessServiceFactory();
    if ( !xFactory.is() )
        throw uno::RuntimeException();

    uno::Reference < ::com::sun::star::ucb::XSimpleFileAccess > xTempAccess(
            xFactory->createInstance ( ::rtl::OUString::createFromAscii( "com.sun.star.ucb.SimpleFileAccess" ) ),
            uno::UNO_QUERY );

    if ( !xTempAccess.is() )
        throw uno::RuntimeException();

    uno::Reference< io::XInputStream > xInputStream = xTempAccess->openFileRead( aURL );
    if ( !xInputStream.is() )
        throw uno::RuntimeException();

    return xInputStream;
}

// ----------------------------------------------------------------------
void OStorageHelper::SetCommonStoragePassword(
            const uno::Reference< embed::XStorage >& xStorage,
            const ::rtl::OUString& aPass )
    throw ( uno::Exception )
{
    uno::Reference< embed::XEncryptionProtectedSource > xEncrSet( xStorage, uno::UNO_QUERY );
    if ( !xEncrSet.is() )
        throw io::IOException(); // TODO

    xEncrSet->setEncryptionPassword( aPass );
}

// ----------------------------------------------------------------------
sal_Int32 OStorageHelper::GetXStorageFormat(
            const uno::Reference< embed::XStorage >& xStorage )
        throw ( uno::Exception )
{
    uno::Reference< beans::XPropertySet > xStorProps( xStorage, uno::UNO_QUERY_THROW );

    ::rtl::OUString aMediaType;
    xStorProps->getPropertyValue( ::rtl::OUString::createFromAscii( "MediaType" ) ) >>= aMediaType;

    sal_Int32 nResult = 0;

    // TODO/LATER: the filter configuration could be used to detect it later, or batter a special service
    if ( aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.writer" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.writer.web" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.writer.global" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.draw" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.impress" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.calc" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.chart" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/vnd.sun.xml.math" ) ) )
    {
        nResult = SOFFICE_FILEFORMAT_60;
    }
    else if ( aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.text" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.text-web" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.text-global" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.drawing" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.presentation" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.spreadsheet" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.chart" ) )
      || aMediaType.equalsIgnoreAsciiCase( ::rtl::OUString::createFromAscii( "application/x-vnd.oasis.openoffice.formula" ) ) )
    {
        nResult = SOFFICE_FILEFORMAT_8;
    }
    else
        throw uno::Exception();

    return nResult;
}

}

<|endoftext|>


<commit_before>#include "compiler.hh"
#include "tools.hh"

#include <iostream>

using namespace nolang;

Compiler::Compiler() :
    m_parameters(false)
{
}

Import *Compiler::addImportIdentifierSub(Import *imp, const std::string &cnts)
{
    if (imp == nullptr) imp = new Import(cnts);
    else imp->addSub(cnts);
    return imp;
}

Import *Compiler::addImportIdentifierAs(Import *imp, const std::string &cnts)
{
    if (imp == nullptr) imp = new Import(cnts);
    else imp->addAs(cnts);
    return imp;
}

void Compiler::iterateTree(mpc_ast_t *tree, std::function<void(mpc_ast_t *)> closure) {
    for (int c = 0; c < tree->children_num; ++c)
        closure(tree->children[c]);
}

bool Compiler::expect(mpc_ast_t *tree, std::string key, std::string val) const
{
    std::string tag = tree->tag;

    if (tag.find(key) == std::string::npos) return false;
    if (!val.empty() && tree->contents != val) return false;

    return true;
}

Import *Compiler::addImportAs(mpc_ast_t *tree)
{
    Import *imp = nullptr;

    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (expect(item, "identifier"))
            imp = addImportIdentifierSub(imp, item->contents);
    });

    return imp;
}

void Compiler::addImport(mpc_ast_t *tree)
{
    Import *imp = nullptr;

    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (expect(item, "identifier"))
            imp = addImportIdentifierAs(imp, item->contents);
        else if (expect(item, "namespacedef"))
            imp = addImportAs(item);
    });
    if (imp) m_imports.push_back(imp);
}

void Compiler::printError(std::string message, mpc_ast_t *item)
{
    std::cerr << "** ERROR: " << message << ": " << item->tag << ": '" << item->contents << "'\n";
}

void Compiler::addConstAssignment(mpc_ast_t *item)
{
    PureMethod tmp;
    Assignment *assignment = parseAssignment(item, &tmp);
    if (assignment && tmp.variables().size() == 1)
        m_consts.push_back(new Const(tmp.variables()[0], assignment));
    else printError("Invalid const", item);
}

void Compiler::addConst(mpc_ast_t *tree)
{
    bool wait_const = true;
    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (expect(item, "string", "const"))
            wait_const = false;
        else if (!wait_const & expect(item, "assignment"))
            addConstAssignment(item);
        else printError("Unknown node in const defination", item);
    });
}

NamespaceDef *Compiler::parseNamespaceDefStrings(mpc_ast_t *tree)
{
    NamespaceDef *def = new NamespaceDef();
    std::vector<std::string> res;
    bool cast = false;

    iterateTree(tree, [&] (mpc_ast_t *item) {
        std::string cnts = item->contents;
        if (expect(item, "identifier")) {
            if (cast) def->setCast(cnts);
            else res.push_back(cnts);
        } else if (cnts == "::") {
            cast = true;
        } else if (cnts == ".") { // FIXME
        } else printError("Unknown node in namespace defination", item);
    });
    if (!res.empty()) def->setValues(res);
    return def;
}

NamespaceDef *Compiler::parseNamespaceDef(mpc_ast_t *tree)
{
    NamespaceDef *res = parseNamespaceDefStrings(tree);
    if (!res->isValid()) {
        delete res;
        return nullptr;
    }
    return res;
}

MethodCall *Compiler::parseMethodCall(mpc_ast_t *tree)
{
    MethodCall *mcall = new MethodCall();

    bool wait_ns = true;
    bool wait_call_end = false;

    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (wait_ns && expect(item, "identifier")) {
            mcall->setNamespace(new NamespaceDef(item->contents));
            wait_ns = false;
        } else if (wait_ns && expect(item, "namespacedef")) {
            mcall->setNamespace(parseNamespaceDef(item));
            wait_ns = false;
        } else if (!wait_call_end && expect(item, "char", "("))
            wait_call_end = true;
        else if (wait_call_end && expect(item, "char", ")"))
            wait_call_end = false;
        else if (wait_call_end)
            mcall->addParameter(codegen(item));
        else printError("Unknown node in method call", item);
    });

    return mcall;
}

TypeIdent *Compiler::parseTypeIdent(mpc_ast_t *tree, PureMethod *m)
{
    std::string name;
    std::string type;
    bool wait_colon = true;
    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (expect(item, "identifier")) {
            if (wait_colon) name += item->contents;
            else type += item->contents;
        }
        else if (wait_colon && expect(item, "char", ":"))
            wait_colon = false;
    });
    m_last_indent = name;
    return new TypeIdent(name, type);
}

Assignment *Compiler::parseAssignmentTypeIdent(mpc_ast_t *item, PureMethod *m)
{
    TypeIdent *ident = parseTypeIdent(item, m);
    if (m) m->addVariable(ident);
    else delete ident;
    return new Assignment(m_last_indent);
}

Assignment *Compiler::parseAssignment(mpc_ast_t *tree, PureMethod *m)
{
    bool wait_for_ident = true;
    bool wait_for_assign = false;
#if 0
    std::cout << "/*\n";
    mpc_ast_print(tree);
    std::cout << "*/\n";
#endif
    Assignment *ass = nullptr;
    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (wait_for_ident && expect(item, "typeident")) {
            ass = parseAssignmentTypeIdent(item, m);
            wait_for_ident = false;
            wait_for_assign = true;
        } else if (wait_for_ident && expect(item, "namespacedef")) {
            NamespaceDef *def = parseNamespaceDef(item);
            if (def == nullptr) {
                throw std::string("Invalid NamespaceDef in assignment");
            }
            wait_for_ident = false;
            wait_for_assign = true;
            ass = new Assignment(def);
        } else if (wait_for_assign && expect(item, "char", "=")) {
            wait_for_assign = false;
        } else if (!wait_for_ident && !wait_for_assign) {
            // Now need to parse statements/expr...
            std::vector<Statement*> stmt = codegen(item, m);
            ass->addStatements(stmt);
        } else printError("Unknown node in assignment", item);
    });

    return ass;
}

void Compiler::parseStruct(mpc_ast_t *tree)
{
    Struct *s = nullptr;

    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (!s && expect(item, "identifier"))
            s = new Struct(item->contents);
        else if (s && expect(item, "typeident"))
            s->addData(parseTypeIdent(item, nullptr));
        else printError("Unknown node in struct", item);
    });
    m_structs.push_back(s);
}

std::vector<Statement*> Compiler::codegenRecurse(mpc_ast_t *tree, PureMethod *m, int level)
{
    std::vector<Statement*> rdata;
    iterateTree(tree, [&] (mpc_ast_t *item) {
        std::vector<Statement*> st = codegen(item, m, level);
        for (auto s : st) {
            if (s->type() == "EOS") {
                rdata.push_back(s);
                m_blocks.push_back(rdata);
                rdata = std::vector<Statement*>();
            } else {
                rdata.push_back(s);
            }
        }
    });
    return rdata;
}

std::vector<Statement*> Compiler::codegen(mpc_ast_t *tree, PureMethod *m, int level)
{
    std::vector<Statement*> rdata;

    std::string tag = tree->tag;
    std::string cnts = tree->contents;
    bool recurse = true;

    if (tag == ">") {
        //std::cout << "ROOT\n";
    } else if (expect(tree, "comment")) {
        // Ignore comments
        recurse = false;
    } else if (expect(tree, "methoddef")) {
        // New method
        parseMethod(tree, level);
        recurse = false;
        level = 0;
    } else if (expect(tree, "struct")) {
        parseStruct(tree);
        recurse = false;
    } else if (expect(tree, "indent")) {
        int new_level = cnts.length();
        if (new_level != level) {
            if (m && !m_blocks.empty()) {
                m->addBlock(m_blocks);
                m_blocks = std::vector<std::vector<Statement*>>();
            }
        }
        // SKIP and recurse
    } else if (expect(tree, "methodcall")) {
        rdata.push_back(parseMethodCall(tree));
        recurse = false;
    } else if (expect(tree, "assignment")) {
        rdata.push_back(parseAssignment(tree, m));
        recurse = false;
    } else if (expect(tree, "number")) {
        rdata.push_back(new NumberValue(cnts));
    } else if (expect(tree, "termop")) {
        rdata.push_back(new Op(cnts));
    } else if (expect(tree, "factorop")) {
        rdata.push_back(new Op(cnts));
    } else if (expect(tree, "string")) {
        rdata.push_back(new StringValue(cnts));
    } else if (expect(tree, "typeident")) {
        TypeIdent *ident = parseTypeIdent(tree, m);
        if (m_parameters) {
            rdata.push_back(ident);
        } else if (m != nullptr) {
            m->addVariable(ident);
        } else {
            rdata.push_back(ident);
        }
        recurse = false;
    } else if (expect(tree, "namespacedef")) {
        if (cnts == "false" || cnts == "true") {
            rdata.push_back(new Boolean(cnts));
        } else {
            NamespaceDef *def = parseNamespaceDef(tree);
            if (def != nullptr) {
                rdata.push_back(def);
            } else if (!cnts.empty()) {
                rdata.push_back(new Identifier(cnts));
            }
        }
        recurse = false;
    } else if (expect(tree, "identifier")) {
        // FIXME Some idenfiers are special/reserved words
        if (cnts == "false" || cnts == "true") {
            rdata.push_back(new Boolean(cnts));
        } else {
            rdata.push_back(new Identifier(cnts));
        }
    } else if (expect(tree, "import")) {
        addImport(tree);
        recurse = false;
    } else if (expect(tree, "const")) {
        addConst(tree);
        recurse = false;
    } else if (expect(tree, "newline")) {
        rdata.push_back(new EOS());
    } else if (expect(tree, "comparator")) {
        rdata.push_back(new Comparator(cnts));
    } else if (expect(tree, "char", "(") || expect(tree, "char", ")")) {
        rdata.push_back(new Braces(cnts));
    } else if (expect(tree, "ows") || expect(tree, "ws")) {
    } else printError("Unknown node in statement", tree);

    if (recurse)
        rdata = applyToVector<Statement*>(rdata, codegenRecurse(tree, m, level));

    return rdata;
}

void Compiler::parseParamDef(mpc_ast_t *tree, PureMethod *m, int level)
{
    int numparams = 0;
    for (int c = 0; c < tree->children_num; ++c) {
        std::string tag = tree->children[c]->tag;
        std::string cnts = tree->children[c]->contents;
        if (expect(tree->children[c], "typeident")) {
            m_parameters = true;
            auto res = codegen(tree->children[c], m, level + 1);
            m_parameters = false;
            for (auto r: res ){
                if (r->type() == "TypeIdent") {
                    m->addParameter(static_cast<TypeIdent*>(r));
                } else {
                    throw std::string("Invalid parameter definition: " + r->code());
                }
            }
        } else if (expect(tree->children[c], "char") && cnts == ",") {
            // FIXME?
            numparams += 1;
        } else {
            std::cerr << "** ERROR: Unknown node in parameter: " << tag << ": '" << cnts << "'\n";
        }
    }
}

void Compiler::parseArgs(mpc_ast_t *tree, PureMethod *m, int level)
{
    int open = 0;
    for (int c = 0; c < tree->children_num; ++c) {
        std::string cnts = tree->children[c]->contents;
        if (expect(tree->children[c], "char")) {
            if (cnts == "(") open++;
            else if (cnts == ")") open--;
            else {
                throw std::string("Unexpected char: " + cnts);
            }
        } else if (open > 0 && expect(tree->children[c], "paramdef")) {
            parseParamDef(tree->children[c], m, level + 1);
        } else {
            std::string tag = tree->children[c]->tag;
            std::cerr << "** ERROR: Unknown node in arguments: " << tag << ": '" << cnts << "'\n";
        }
    }
}

void Compiler::parseMethodRet(mpc_ast_t *tree, PureMethod *m, int level)
{
    auto r = codegen(tree, m, level);

    if (r.size() > 1) {
        throw std::string("Expected one return type, got " + std::to_string(r.size()) + " for '" + m->name() + "'");
    }

    if (r.size() == 1) {
        if (r[0]->type() != "Identifier") {
            throw std::string("Expected identifier as return type, got " + r[0]->type() + " for '" + m->name() + "'");
        }
        m->setReturnType(TypeDef(r[0]->code()));
    }
}

void Compiler::parseMethod(mpc_ast_t *tree, int level)
{
    PureMethod *m = new PureMethod();
    bool waitName = true;
    bool waitBody = false;
    for (int c = 0; c < tree->children_num; ++c) {
        std::string tag = tree->children[c]->tag;
        std::string cnts = tree->children[c]->contents;
        if (waitName && tag.find("pure") != std::string::npos) {
            m->setPure();
        } else if (waitName && tag.find("identifier") != std::string::npos) {
            m->setName(cnts);
            waitName = false;
        } else if (!waitName && expect(tree->children[c], "args")) {
            parseArgs(tree->children[c], m, level + 1);
        } else if (!waitName && expect(tree->children[c], "methodret")) {
            parseMethodRet(tree->children[c], m, level + 1);
        } else if (tag.find("ows") != std::string::npos) {
            // Optional whitespace
        } else if (!waitBody && tag.find("string") != std::string::npos && cnts == "=>") {
            // Body should follow
            waitBody = true;
        } else if (waitBody && tag.find("body") != std::string::npos) {
            m->setBody(codegen(tree->children[c], m, level));
            if (!m_blocks.empty()) {
                m->addBlock(m_blocks);
                //m_blocks = std::vector<std::string>();
                m_blocks = std::vector<std::vector<Statement*>>();
            }
        } else if (tag.find("newline") != std::string::npos || tag.find("ws") != std::string::npos) {
        //} else if (cnts.length() == 0) {
            // SKIP
        } else {
            std::cerr << "** ERROR: Unknown node in method: " << tag << ": '" << cnts << "'\n";
        }
    }
    m_methods[m->name()] = m;
}
<commit_msg>Refactor argument and parameter parsing<commit_after>#include "compiler.hh"
#include "tools.hh"

#include <iostream>

using namespace nolang;

Compiler::Compiler() :
    m_parameters(false)
{
}

Import *Compiler::addImportIdentifierSub(Import *imp, const std::string &cnts)
{
    if (imp == nullptr) imp = new Import(cnts);
    else imp->addSub(cnts);
    return imp;
}

Import *Compiler::addImportIdentifierAs(Import *imp, const std::string &cnts)
{
    if (imp == nullptr) imp = new Import(cnts);
    else imp->addAs(cnts);
    return imp;
}

void Compiler::iterateTree(mpc_ast_t *tree, std::function<void(mpc_ast_t *)> closure) {
    for (int c = 0; c < tree->children_num; ++c)
        closure(tree->children[c]);
}

bool Compiler::expect(mpc_ast_t *tree, std::string key, std::string val) const
{
    std::string tag = tree->tag;

    if (tag.find(key) == std::string::npos) return false;
    if (!val.empty() && tree->contents != val) return false;

    return true;
}

Import *Compiler::addImportAs(mpc_ast_t *tree)
{
    Import *imp = nullptr;

    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (expect(item, "identifier"))
            imp = addImportIdentifierSub(imp, item->contents);
    });

    return imp;
}

void Compiler::addImport(mpc_ast_t *tree)
{
    Import *imp = nullptr;

    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (expect(item, "identifier"))
            imp = addImportIdentifierAs(imp, item->contents);
        else if (expect(item, "namespacedef"))
            imp = addImportAs(item);
    });
    if (imp) m_imports.push_back(imp);
}

void Compiler::printError(std::string message, mpc_ast_t *item)
{
    std::cerr << "** ERROR: " << message << ": " << item->tag << ": '" << item->contents << "'\n";
}

void Compiler::addConstAssignment(mpc_ast_t *item)
{
    PureMethod tmp;
    Assignment *assignment = parseAssignment(item, &tmp);
    if (assignment && tmp.variables().size() == 1)
        m_consts.push_back(new Const(tmp.variables()[0], assignment));
    else printError("Invalid const", item);
}

void Compiler::addConst(mpc_ast_t *tree)
{
    bool wait_const = true;
    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (expect(item, "string", "const"))
            wait_const = false;
        else if (!wait_const & expect(item, "assignment"))
            addConstAssignment(item);
        else printError("Unknown node in const defination", item);
    });
}

NamespaceDef *Compiler::parseNamespaceDefStrings(mpc_ast_t *tree)
{
    NamespaceDef *def = new NamespaceDef();
    std::vector<std::string> res;
    bool cast = false;

    iterateTree(tree, [&] (mpc_ast_t *item) {
        std::string cnts = item->contents;
        if (expect(item, "identifier")) {
            if (cast) def->setCast(cnts);
            else res.push_back(cnts);
        } else if (cnts == "::") {
            cast = true;
        } else if (cnts == ".") { // FIXME
        } else printError("Unknown node in namespace defination", item);
    });
    if (!res.empty()) def->setValues(res);
    return def;
}

NamespaceDef *Compiler::parseNamespaceDef(mpc_ast_t *tree)
{
    NamespaceDef *res = parseNamespaceDefStrings(tree);
    if (!res->isValid()) {
        delete res;
        return nullptr;
    }
    return res;
}

MethodCall *Compiler::parseMethodCall(mpc_ast_t *tree)
{
    MethodCall *mcall = new MethodCall();

    bool wait_ns = true;
    bool wait_call_end = false;

    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (wait_ns && expect(item, "identifier")) {
            mcall->setNamespace(new NamespaceDef(item->contents));
            wait_ns = false;
        } else if (wait_ns && expect(item, "namespacedef")) {
            mcall->setNamespace(parseNamespaceDef(item));
            wait_ns = false;
        } else if (!wait_call_end && expect(item, "char", "("))
            wait_call_end = true;
        else if (wait_call_end && expect(item, "char", ")"))
            wait_call_end = false;
        else if (wait_call_end)
            mcall->addParameter(codegen(item));
        else printError("Unknown node in method call", item);
    });

    return mcall;
}

TypeIdent *Compiler::parseTypeIdent(mpc_ast_t *tree, PureMethod *m)
{
    std::string name;
    std::string type;
    bool wait_colon = true;
    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (expect(item, "identifier")) {
            if (wait_colon) name += item->contents;
            else type += item->contents;
        }
        else if (wait_colon && expect(item, "char", ":"))
            wait_colon = false;
    });
    m_last_indent = name;
    return new TypeIdent(name, type);
}

Assignment *Compiler::parseAssignmentTypeIdent(mpc_ast_t *item, PureMethod *m)
{
    TypeIdent *ident = parseTypeIdent(item, m);
    if (m) m->addVariable(ident);
    else delete ident;
    return new Assignment(m_last_indent);
}

Assignment *Compiler::parseAssignment(mpc_ast_t *tree, PureMethod *m)
{
    bool wait_for_ident = true;
    bool wait_for_assign = false;
#if 0
    std::cout << "/*\n";
    mpc_ast_print(tree);
    std::cout << "*/\n";
#endif
    Assignment *ass = nullptr;
    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (wait_for_ident && expect(item, "typeident")) {
            ass = parseAssignmentTypeIdent(item, m);
            wait_for_ident = false;
            wait_for_assign = true;
        } else if (wait_for_ident && expect(item, "namespacedef")) {
            NamespaceDef *def = parseNamespaceDef(item);
            if (def == nullptr) {
                throw std::string("Invalid NamespaceDef in assignment");
            }
            wait_for_ident = false;
            wait_for_assign = true;
            ass = new Assignment(def);
        } else if (wait_for_assign && expect(item, "char", "=")) {
            wait_for_assign = false;
        } else if (!wait_for_ident && !wait_for_assign) {
            // Now need to parse statements/expr...
            std::vector<Statement*> stmt = codegen(item, m);
            ass->addStatements(stmt);
        } else printError("Unknown node in assignment", item);
    });

    return ass;
}

void Compiler::parseStruct(mpc_ast_t *tree)
{
    Struct *s = nullptr;

    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (!s && expect(item, "identifier"))
            s = new Struct(item->contents);
        else if (s && expect(item, "typeident"))
            s->addData(parseTypeIdent(item, nullptr));
        else printError("Unknown node in struct", item);
    });
    m_structs.push_back(s);
}

std::vector<Statement*> Compiler::codegenRecurse(mpc_ast_t *tree, PureMethod *m, int level)
{
    std::vector<Statement*> rdata;
    iterateTree(tree, [&] (mpc_ast_t *item) {
        std::vector<Statement*> st = codegen(item, m, level);
        for (auto s : st) {
            if (s->type() == "EOS") {
                rdata.push_back(s);
                m_blocks.push_back(rdata);
                rdata = std::vector<Statement*>();
            } else {
                rdata.push_back(s);
            }
        }
    });
    return rdata;
}

std::vector<Statement*> Compiler::codegen(mpc_ast_t *tree, PureMethod *m, int level)
{
    std::vector<Statement*> rdata;

    std::string tag = tree->tag;
    std::string cnts = tree->contents;
    bool recurse = true;

    if (tag == ">") {
        //std::cout << "ROOT\n";
    } else if (expect(tree, "comment")) {
        // Ignore comments
        recurse = false;
    } else if (expect(tree, "methoddef")) {
        // New method
        parseMethod(tree, level);
        recurse = false;
        level = 0;
    } else if (expect(tree, "struct")) {
        parseStruct(tree);
        recurse = false;
    } else if (expect(tree, "indent")) {
        int new_level = cnts.length();
        if (new_level != level) {
            if (m && !m_blocks.empty()) {
                m->addBlock(m_blocks);
                m_blocks = std::vector<std::vector<Statement*>>();
            }
        }
        // SKIP and recurse
    } else if (expect(tree, "methodcall")) {
        rdata.push_back(parseMethodCall(tree));
        recurse = false;
    } else if (expect(tree, "assignment")) {
        rdata.push_back(parseAssignment(tree, m));
        recurse = false;
    } else if (expect(tree, "number")) {
        rdata.push_back(new NumberValue(cnts));
    } else if (expect(tree, "termop")) {
        rdata.push_back(new Op(cnts));
    } else if (expect(tree, "factorop")) {
        rdata.push_back(new Op(cnts));
    } else if (expect(tree, "string")) {
        rdata.push_back(new StringValue(cnts));
    } else if (expect(tree, "typeident")) {
        TypeIdent *ident = parseTypeIdent(tree, m);
        if (m_parameters) {
            rdata.push_back(ident);
        } else if (m != nullptr) {
            m->addVariable(ident);
        } else {
            rdata.push_back(ident);
        }
        recurse = false;
    } else if (expect(tree, "namespacedef")) {
        if (cnts == "false" || cnts == "true") {
            rdata.push_back(new Boolean(cnts));
        } else {
            NamespaceDef *def = parseNamespaceDef(tree);
            if (def != nullptr) {
                rdata.push_back(def);
            } else if (!cnts.empty()) {
                rdata.push_back(new Identifier(cnts));
            }
        }
        recurse = false;
    } else if (expect(tree, "identifier")) {
        // FIXME Some idenfiers are special/reserved words
        if (cnts == "false" || cnts == "true") {
            rdata.push_back(new Boolean(cnts));
        } else {
            rdata.push_back(new Identifier(cnts));
        }
    } else if (expect(tree, "import")) {
        addImport(tree);
        recurse = false;
    } else if (expect(tree, "const")) {
        addConst(tree);
        recurse = false;
    } else if (expect(tree, "newline")) {
        rdata.push_back(new EOS());
    } else if (expect(tree, "comparator")) {
        rdata.push_back(new Comparator(cnts));
    } else if (expect(tree, "char", "(") || expect(tree, "char", ")")) {
        rdata.push_back(new Braces(cnts));
    } else if (expect(tree, "ows") || expect(tree, "ws")) {
    } else printError("Unknown node in statement", tree);

    if (recurse)
        rdata = applyToVector<Statement*>(rdata, codegenRecurse(tree, m, level));

    return rdata;
}

void Compiler::parseParamDef(mpc_ast_t *tree, PureMethod *m, int level)
{
    iterateTree(tree, [&] (mpc_ast_t *item) {
        std::string cnts = item->contents;
        if (expect(item, "typeident")) {
            m_parameters = true;
            auto res = codegen(item, m, level + 1);
            m_parameters = false;
            for (auto r : res){
                if (r->type() == "TypeIdent")
                    m->addParameter(static_cast<TypeIdent*>(r));
                else
                    throw std::string("Invalid parameter definition: " + r->code());
            }
        } else if (expect(item, "char") && cnts == ",") {
            // FIXME Can ',' separate in params something else than next param?
        } else printError("Unknown node in parameter", item);
    });
}

void Compiler::parseArgs(mpc_ast_t *tree, PureMethod *m, int level)
{
    int open = 0;
    iterateTree(tree, [&] (mpc_ast_t *item) {
        if (expect(item, "char")) {
            std::string cnts = item->contents;
            if (cnts == "(") open++;
            else if (cnts == ")") open--;
            else {
                throw std::string("Unexpected char: " + cnts);
            }
        } else if (open > 0 && expect(item, "paramdef"))
            parseParamDef(item, m, level + 1);
        else printError("Unknown node in arguments", item);
    });
}

void Compiler::parseMethodRet(mpc_ast_t *tree, PureMethod *m, int level)
{
    auto r = codegen(tree, m, level);

    if (r.size() > 1) {
        throw std::string("Expected one return type, got " + std::to_string(r.size()) + " for '" + m->name() + "'");
    }

    if (r.size() == 1) {
        if (r[0]->type() != "Identifier") {
            throw std::string("Expected identifier as return type, got " + r[0]->type() + " for '" + m->name() + "'");
        }
        m->setReturnType(TypeDef(r[0]->code()));
    }
}

void Compiler::parseMethod(mpc_ast_t *tree, int level)
{
    PureMethod *m = new PureMethod();
    bool waitName = true;
    bool waitBody = false;
    for (int c = 0; c < tree->children_num; ++c) {
        std::string tag = tree->children[c]->tag;
        std::string cnts = tree->children[c]->contents;
        if (waitName && tag.find("pure") != std::string::npos) {
            m->setPure();
        } else if (waitName && tag.find("identifier") != std::string::npos) {
            m->setName(cnts);
            waitName = false;
        } else if (!waitName && expect(tree->children[c], "args")) {
            parseArgs(tree->children[c], m, level + 1);
        } else if (!waitName && expect(tree->children[c], "methodret")) {
            parseMethodRet(tree->children[c], m, level + 1);
        } else if (tag.find("ows") != std::string::npos) {
            // Optional whitespace
        } else if (!waitBody && tag.find("string") != std::string::npos && cnts == "=>") {
            // Body should follow
            waitBody = true;
        } else if (waitBody && tag.find("body") != std::string::npos) {
            m->setBody(codegen(tree->children[c], m, level));
            if (!m_blocks.empty()) {
                m->addBlock(m_blocks);
                //m_blocks = std::vector<std::string>();
                m_blocks = std::vector<std::vector<Statement*>>();
            }
        } else if (tag.find("newline") != std::string::npos || tag.find("ws") != std::string::npos) {
        //} else if (cnts.length() == 0) {
            // SKIP
        } else {
            std::cerr << "** ERROR: Unknown node in method: " << tag << ": '" << cnts << "'\n";
        }
    }
    m_methods[m->name()] = m;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017, Rauli Laine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <plorth/context.hpp>
#include <plorth/value-word.hpp>

namespace plorth
{
  using source_iterator = unistring::const_iterator;

  static bool skip_whitespace(source_iterator&, const source_iterator&);
  static ref<value> compile_value(context*,
                                  source_iterator&,
                                  const source_iterator&);

  ref<quote> context::compile(const unistring& source)
  {
    std::vector<ref<class value>> values;
    ref<class value> value;
    auto it = std::begin(source);
    const auto end = std::end(source);

    while (it != end)
    {
      if (skip_whitespace(it, end))
      {
        break;
      }
      else if (!(value = compile_value(this, it, end)))
      {
        return ref<quote>();
      }
      values.push_back(value);
    }

    return m_runtime->compiled_quote(values);
  }

  /**
   * Skips whitespace and comments from source code.
   *
   * \return True if end of input has been reached, false otherwise.
   */
  static bool skip_whitespace(source_iterator& it, const source_iterator& end)
  {
    while (it != end)
    {
      // Skip line comments.
      if (*it == '#')
      {
        while (++it != end)
        {
          if (*it == '\n' || *it == '\r')
          {
            break;
          }
        }
      }
      else if (!std::isspace(*it))
      {
        return false;
      } else {
        ++it;
      }
    }

    return true;
  }

  static bool compile_escape_sequence(context* ctx,
                                      source_iterator& it,
                                      const source_iterator& end,
                                      unistring& buffer)
  {
    if (it >= end)
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing escape sequence."
      );

      return false;
    }

    switch (*it++)
    {
    case 'b':
      buffer.append(1, 010);
      break;

    case 't':
      buffer.append(1, 011);
      break;

    case 'n':
      buffer.append(1, 012);
      break;

    case 'f':
      buffer.append(1, 014);
      break;

    case 'r':
      buffer.append(1, 015);
      break;

    case '"':
    case '\'':
    case '\\':
    case '/':
      buffer.append(1, *(it - 1));
      break;

    case 'u':
      {
        unichar result = 0;

        for (int i = 0; i < 4; ++i)
        {
          if (it >= end)
          {
            ctx->error(
              error::code_syntax,
              U"Unterminated escape sequence."
            );

            return false;
          }
          else if (!std::isxdigit(*it))
          {
            ctx->error(
              error::code_syntax,
              U"Illegal Unicode hex escape sequence."
            );

            return false;
          }

          if (*it >= 'A' && *it <= 'F')
          {
            result = result * 16 + (*it++ - 'A' + 10);
          }
          else if (*it >= 'a' && *it <= 'f')
          {
            result = result * 16 + (*it++ - 'a' + 10);
          } else {
            result = result * 16 + (*it++ - '0');
          }
        }

        if (!unichar_validate(result))
        {
          ctx->error(
            error::code_syntax,
            U"Illegal Unicode hex escape sequence."
          );

          return false;
        }

        buffer.append(1, result);
      }
      break;

    default:
      ctx->error(
        error::code_syntax,
        U"Illegal escape sequence in string literal."
      );

      return false;
    }

    return true;
  }

  static ref<string> compile_string(context* ctx,
                                    source_iterator& it,
                                    const source_iterator& end)
  {
    unichar separator;
    unistring buffer;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing string."
      );

      return ref<string>();
    }

    if (*it != '"' && *it != '\'')
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing string."
      );

      return ref<string>();
    }

    separator = *it++;

    for (;;)
    {
      if (it >= end)
      {
        ctx->error(
          error::code_syntax,
          unistring(U"Unterminated string; Missing `") + separator + U"'"
        );

        return ref<string>();
      }
      else if (*it == separator)
      {
        ++it;
        break;
      }
      else if (*it != '\\')
      {
        buffer.append(1, *it++);
      }
      else if (!compile_escape_sequence(ctx, ++it, end, buffer))
      {
        return ref<string>();
      }
    }

    return ctx->runtime()->string(buffer);
  }

  static ref<quote> compile_quote(context* ctx,
                                  source_iterator& it,
                                  const source_iterator& end)
  {
    std::vector<ref<value>> values;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing quote."
      );

      return ref<quote>();
    }

    if (*it != '(')
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing quote."
      );

      return ref<quote>();
    }

    ++it;

    for (;;)
    {
      if (skip_whitespace(it, end))
      {
        ctx->error(error::code_syntax, U"Unterminated quote; Missing `)'.");

        return ref<quote>();
      }
      else if (*it == ')')
      {
        ++it;
        break;
      } else {
        const auto value = compile_value(ctx, it, end);

        if (!value)
        {
          return ref<quote>();
        }
        values.push_back(value);
      }
    }

    return ctx->runtime()->compiled_quote(values);
  }

  static ref<array> compile_array(context* ctx,
                                  source_iterator& it,
                                  const source_iterator& end)
  {
    std::vector<ref<value>> elements;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing array."
      );

      return ref<array>();
    }

    if (*it != '[')
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing array."
      );

      return ref<array>();
    }

    ++it;

    for (;;)
    {
      if (skip_whitespace(it, end))
      {
        ctx->error(
          error::code_syntax,
          U"Unterminated array; Missing `]'."
        );

        return ref<array>();
      }
      else if (*it == ']')
      {
        ++it;
        break;
      } else {
        const auto value = compile_value(ctx, it, end);

        if (!value)
        {
          return ref<array>();
        }
        elements.push_back(value);
        if (skip_whitespace(it, end) || (*it != ',' && *it != ']'))
        {
          ctx->error(
            error::code_syntax,
            U"Unterminated array; Missing `]'."
          );

          return ref<array>();
        }
        else if (*it == ',')
        {
          ++it;
        }
      }
    }

    return ctx->runtime()->array(elements.data(), elements.size());
  }

  static ref<object> compile_object(context* ctx,
                                    source_iterator& it,
                                    const source_iterator& end)
  {
    object::container_type properties;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing object."
      );

      return ref<object>();
    }

    if (*it != '{')
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing object."
      );

      return ref<object>();
    }

    ++it;

    for (;;)
    {
      if (skip_whitespace(it, end))
      {
        ctx->error(
          error::code_syntax,
          U"Unterminated object; Missing `}'."
        );

        return ref<object>();
      }
      else if (*it == '}')
      {
        ++it;
        break;
      } else {
        ref<string> key;
        ref<class value> value;

        if (!(key = compile_string(ctx, it, end)))
        {
          return ref<object>();
        }

        if (skip_whitespace(it, end))
        {
          ctx->error(
            error::code_syntax,
            U"Unterminated object; Missing `}'."
          );

          return ref<object>();
        }

        if (*it != ':')
        {
          ctx->error(
            error::code_syntax,
            U"Missing `:' after property key."
          );

          return ref<object>();
        }

        if (!(value = compile_value(ctx, ++it, end)))
        {
          return ref<object>();
        }

        properties[key->to_string()] = value;

        if (skip_whitespace(it, end) || (*it != ',' && *it != '}'))
        {
          ctx->error(
            error::code_syntax,
            U"Unterminated object; Missing `}'."
          );

          return ref<object>();
        }
        else if (*it == ',')
        {
          ++it;
        }
      }
    }

    return ctx->runtime()->value<object>(properties);
  }

  static ref<symbol> compile_symbol(context* ctx,
                                    source_iterator& it,
                                    const source_iterator& end)
  {
    unistring buffer;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing symbol."
      );

      return ref<symbol>();
    }

    if (!unichar_isword(*it))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing symbol."
      );

      return ref<symbol>();
    }

    buffer.append(1, *it++);
    while (it != end && unichar_isword(*it))
    {
      buffer.append(1, *it++);
    }

    return ctx->runtime()->value<symbol>(buffer);
  }

  static ref<word> compile_word(context* ctx,
                                source_iterator& it,
                                const source_iterator& end)
  {
    const auto& runtime = ctx->runtime();
    ref<class symbol> symbol;
    std::vector<ref<value>> values;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing word."
        );

      return ref<word>();
    }

    if (*it != ':')
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing word."
        );

      return ref<word>();
    }

    ++it;

    if (!(symbol = compile_symbol(ctx, it, end)))
    {
      return ref<word>();
    }

    for (;;)
    {
      if (skip_whitespace(it, end))
      {
        ctx->error(error::code_syntax, U"Unterminated word; Missing `;'.");

        return ref<word>();
      }
      else if (*it == ';')
      {
        ++it;
        break;
      } else {
        const auto value = compile_value(ctx, it, end);

        if (!value)
        {
          return ref<word>();
        }
        values.push_back(value);
      }
    }

    return runtime->value<word>(symbol, runtime->compiled_quote(values));
  }

  static ref<value> compile_value(context* ctx,
                                  source_iterator& it,
                                  const source_iterator& end)
  {
    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing value."
      );

      return ref<value>();
    }
    switch (*it)
    {
    case '"':
    case '\'':
      return compile_string(ctx, it, end);

    case '(':
      return compile_quote(ctx, it, end);

    case '[':
      return compile_array(ctx, it, end);

    case '{':
      return compile_object(ctx, it, end);

    case ':':
      return compile_word(ctx, it, end);

    default:
      return compile_symbol(ctx, it, end);
    }
  }

  #if 0
  ref<quote> context::compile(const unistring& source)
  {
    std::vector<token> tokens;
    auto it = std::begin(source);
    const auto end = std::end(source);
    unistring buffer;

    while (it != end)
    {
      unichar c = *it++;

retry_switch:
      if (!unichar_isword(c))
      {
        error(error::code_syntax, U"Unexpected input.");

        return ref<quote>();
      }

      buffer.assign(1, c);

      while (it != end)
      {
        if (!unichar_isword(c = *it++))
        {
          tokens.push_back(token(token::type_word, buffer));
          goto retry_switch;
        }
        buffer.append(1, c);
      }

      tokens.push_back(token(token::type_word, buffer));
    }

    return m_runtime->compiled_quote(tokens);
  }

  static bool compile_string_literal(context* ctx,
                                     const unichar separator,
                                     unistring::const_iterator& it,
                                     const unistring::const_iterator& end,
                                     std::vector<token>& tokens,
                                     unistring& buffer)
  {
    buffer.clear();
    for (;;)
    {
      unichar c;

      if (it >= end)
      {
        ctx->error(error::code_syntax, U"Unterminated string literal.");

        return false;
      }

      if ((c = *it++) == separator)
      {
        tokens.push_back(token(token::type_string, buffer));

        return true;
      }
      else if (c != '\\')
      {
        buffer.append(1, c);
        continue;
      }

      if (it >= end)
      {
        ctx->error(error::code_syntax, U"Unterminated string literal.");

        return false;
      }

      switch (c = *it++)
      {
      }
    }
  }
  #endif
}
<commit_msg>Remove code that has been commented out<commit_after>/*
 * Copyright (c) 2017, Rauli Laine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <plorth/context.hpp>
#include <plorth/value-word.hpp>

namespace plorth
{
  using source_iterator = unistring::const_iterator;

  static bool skip_whitespace(source_iterator&, const source_iterator&);
  static ref<value> compile_value(context*,
                                  source_iterator&,
                                  const source_iterator&);

  ref<quote> context::compile(const unistring& source)
  {
    std::vector<ref<class value>> values;
    ref<class value> value;
    auto it = std::begin(source);
    const auto end = std::end(source);

    while (it != end)
    {
      if (skip_whitespace(it, end))
      {
        break;
      }
      else if (!(value = compile_value(this, it, end)))
      {
        return ref<quote>();
      }
      values.push_back(value);
    }

    return m_runtime->compiled_quote(values);
  }

  /**
   * Skips whitespace and comments from source code.
   *
   * \return True if end of input has been reached, false otherwise.
   */
  static bool skip_whitespace(source_iterator& it, const source_iterator& end)
  {
    while (it != end)
    {
      // Skip line comments.
      if (*it == '#')
      {
        while (++it != end)
        {
          if (*it == '\n' || *it == '\r')
          {
            break;
          }
        }
      }
      else if (!std::isspace(*it))
      {
        return false;
      } else {
        ++it;
      }
    }

    return true;
  }

  static bool compile_escape_sequence(context* ctx,
                                      source_iterator& it,
                                      const source_iterator& end,
                                      unistring& buffer)
  {
    if (it >= end)
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing escape sequence."
      );

      return false;
    }

    switch (*it++)
    {
    case 'b':
      buffer.append(1, 010);
      break;

    case 't':
      buffer.append(1, 011);
      break;

    case 'n':
      buffer.append(1, 012);
      break;

    case 'f':
      buffer.append(1, 014);
      break;

    case 'r':
      buffer.append(1, 015);
      break;

    case '"':
    case '\'':
    case '\\':
    case '/':
      buffer.append(1, *(it - 1));
      break;

    case 'u':
      {
        unichar result = 0;

        for (int i = 0; i < 4; ++i)
        {
          if (it >= end)
          {
            ctx->error(
              error::code_syntax,
              U"Unterminated escape sequence."
            );

            return false;
          }
          else if (!std::isxdigit(*it))
          {
            ctx->error(
              error::code_syntax,
              U"Illegal Unicode hex escape sequence."
            );

            return false;
          }

          if (*it >= 'A' && *it <= 'F')
          {
            result = result * 16 + (*it++ - 'A' + 10);
          }
          else if (*it >= 'a' && *it <= 'f')
          {
            result = result * 16 + (*it++ - 'a' + 10);
          } else {
            result = result * 16 + (*it++ - '0');
          }
        }

        if (!unichar_validate(result))
        {
          ctx->error(
            error::code_syntax,
            U"Illegal Unicode hex escape sequence."
          );

          return false;
        }

        buffer.append(1, result);
      }
      break;

    default:
      ctx->error(
        error::code_syntax,
        U"Illegal escape sequence in string literal."
      );

      return false;
    }

    return true;
  }

  static ref<string> compile_string(context* ctx,
                                    source_iterator& it,
                                    const source_iterator& end)
  {
    unichar separator;
    unistring buffer;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing string."
      );

      return ref<string>();
    }

    if (*it != '"' && *it != '\'')
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing string."
      );

      return ref<string>();
    }

    separator = *it++;

    for (;;)
    {
      if (it >= end)
      {
        ctx->error(
          error::code_syntax,
          unistring(U"Unterminated string; Missing `") + separator + U"'"
        );

        return ref<string>();
      }
      else if (*it == separator)
      {
        ++it;
        break;
      }
      else if (*it != '\\')
      {
        buffer.append(1, *it++);
      }
      else if (!compile_escape_sequence(ctx, ++it, end, buffer))
      {
        return ref<string>();
      }
    }

    return ctx->runtime()->string(buffer);
  }

  static ref<quote> compile_quote(context* ctx,
                                  source_iterator& it,
                                  const source_iterator& end)
  {
    std::vector<ref<value>> values;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing quote."
      );

      return ref<quote>();
    }

    if (*it != '(')
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing quote."
      );

      return ref<quote>();
    }

    ++it;

    for (;;)
    {
      if (skip_whitespace(it, end))
      {
        ctx->error(error::code_syntax, U"Unterminated quote; Missing `)'.");

        return ref<quote>();
      }
      else if (*it == ')')
      {
        ++it;
        break;
      } else {
        const auto value = compile_value(ctx, it, end);

        if (!value)
        {
          return ref<quote>();
        }
        values.push_back(value);
      }
    }

    return ctx->runtime()->compiled_quote(values);
  }

  static ref<array> compile_array(context* ctx,
                                  source_iterator& it,
                                  const source_iterator& end)
  {
    std::vector<ref<value>> elements;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing array."
      );

      return ref<array>();
    }

    if (*it != '[')
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing array."
      );

      return ref<array>();
    }

    ++it;

    for (;;)
    {
      if (skip_whitespace(it, end))
      {
        ctx->error(
          error::code_syntax,
          U"Unterminated array; Missing `]'."
        );

        return ref<array>();
      }
      else if (*it == ']')
      {
        ++it;
        break;
      } else {
        const auto value = compile_value(ctx, it, end);

        if (!value)
        {
          return ref<array>();
        }
        elements.push_back(value);
        if (skip_whitespace(it, end) || (*it != ',' && *it != ']'))
        {
          ctx->error(
            error::code_syntax,
            U"Unterminated array; Missing `]'."
          );

          return ref<array>();
        }
        else if (*it == ',')
        {
          ++it;
        }
      }
    }

    return ctx->runtime()->array(elements.data(), elements.size());
  }

  static ref<object> compile_object(context* ctx,
                                    source_iterator& it,
                                    const source_iterator& end)
  {
    object::container_type properties;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing object."
      );

      return ref<object>();
    }

    if (*it != '{')
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing object."
      );

      return ref<object>();
    }

    ++it;

    for (;;)
    {
      if (skip_whitespace(it, end))
      {
        ctx->error(
          error::code_syntax,
          U"Unterminated object; Missing `}'."
        );

        return ref<object>();
      }
      else if (*it == '}')
      {
        ++it;
        break;
      } else {
        ref<string> key;
        ref<class value> value;

        if (!(key = compile_string(ctx, it, end)))
        {
          return ref<object>();
        }

        if (skip_whitespace(it, end))
        {
          ctx->error(
            error::code_syntax,
            U"Unterminated object; Missing `}'."
          );

          return ref<object>();
        }

        if (*it != ':')
        {
          ctx->error(
            error::code_syntax,
            U"Missing `:' after property key."
          );

          return ref<object>();
        }

        if (!(value = compile_value(ctx, ++it, end)))
        {
          return ref<object>();
        }

        properties[key->to_string()] = value;

        if (skip_whitespace(it, end) || (*it != ',' && *it != '}'))
        {
          ctx->error(
            error::code_syntax,
            U"Unterminated object; Missing `}'."
          );

          return ref<object>();
        }
        else if (*it == ',')
        {
          ++it;
        }
      }
    }

    return ctx->runtime()->value<object>(properties);
  }

  static ref<symbol> compile_symbol(context* ctx,
                                    source_iterator& it,
                                    const source_iterator& end)
  {
    unistring buffer;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing symbol."
      );

      return ref<symbol>();
    }

    if (!unichar_isword(*it))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing symbol."
      );

      return ref<symbol>();
    }

    buffer.append(1, *it++);
    while (it != end && unichar_isword(*it))
    {
      buffer.append(1, *it++);
    }

    return ctx->runtime()->value<symbol>(buffer);
  }

  static ref<word> compile_word(context* ctx,
                                source_iterator& it,
                                const source_iterator& end)
  {
    const auto& runtime = ctx->runtime();
    ref<class symbol> symbol;
    std::vector<ref<value>> values;

    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing word."
        );

      return ref<word>();
    }

    if (*it != ':')
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected input; Missing word."
        );

      return ref<word>();
    }

    ++it;

    if (!(symbol = compile_symbol(ctx, it, end)))
    {
      return ref<word>();
    }

    for (;;)
    {
      if (skip_whitespace(it, end))
      {
        ctx->error(error::code_syntax, U"Unterminated word; Missing `;'.");

        return ref<word>();
      }
      else if (*it == ';')
      {
        ++it;
        break;
      } else {
        const auto value = compile_value(ctx, it, end);

        if (!value)
        {
          return ref<word>();
        }
        values.push_back(value);
      }
    }

    return runtime->value<word>(symbol, runtime->compiled_quote(values));
  }

  static ref<value> compile_value(context* ctx,
                                  source_iterator& it,
                                  const source_iterator& end)
  {
    if (skip_whitespace(it, end))
    {
      ctx->error(
        error::code_syntax,
        U"Unexpected end of input; Missing value."
      );

      return ref<value>();
    }
    switch (*it)
    {
    case '"':
    case '\'':
      return compile_string(ctx, it, end);

    case '(':
      return compile_quote(ctx, it, end);

    case '[':
      return compile_array(ctx, it, end);

    case '{':
      return compile_object(ctx, it, end);

    case ':':
      return compile_word(ctx, it, end);

    default:
      return compile_symbol(ctx, it, end);
    }
  }
}
<|endoftext|>


<commit_before>#include "JEBString/CodePoints/CodePointString.hpp"

#include "JEBString/Unicode/CaseInsensitive.hpp"
#include "JEBString/Unicode/UnicodePredicates.hpp"

namespace JEBString { namespace EncodedStrings {

using CodePoints::makeForwardIterator;
using CodePoints::makeReverseIterator;

template <typename It1, typename Enc1, typename It2, typename Enc2>
int32_t caseInsensitiveCompare(EncodedRange<It1, Enc1> str,
                               EncodedRange<It2, Enc2> cmp)
{
    return caseInsensitiveCompare(makeForwardIterator(str),
                                  makeForwardIterator(cmp));
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
bool caseInsensitiveEqual(EncodedRange<It1, Enc1> str,
                          EncodedRange<It2, Enc2> cmp)
{
    return caseInsensitiveEqual(makeForwardIterator(str),
                                makeForwardIterator(cmp));
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
bool caseInsensitiveLess(EncodedRange<It1, Enc1> str,
                         EncodedRange<It2, Enc2> cmp)
{
    return caseInsensitiveLess(makeForwardIterator(str),
                               makeForwardIterator(cmp));
}

template <typename It, typename Enc>
bool contains(EncodedRange<It, Enc> str, uint32_t chr)
{
    auto it = makeForwardIterator(str);
    return advanceUntil(it, [=](uint32_t c){return c == chr;});
}

template <typename InpIt, typename Enc1, typename OutIt, typename Enc2>
void copy(EncodedRange<InpIt, Enc1> src, Encoder<OutIt, Enc2> dst)
{
    if (src.encoding().encoding == dst.encoding().encoding)
        std::copy(begin(src), end(src), dst.iterator());
    else
        copy(makeForwardIterator(src), dst);
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
bool endsWith(EncodedRange<It1, Enc1> str, EncodedRange<It2, Enc2> cmp,
              FindFlags_t flags /*= FindFlags::Defaults*/)
{
    return startsWith(makeReverseIterator(str), makeReverseIterator(cmp),
                      flags);
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
EncodedRange<It1, Enc1> find(EncodedRange<It1, Enc1> str,
                             EncodedRange<It2, Enc2> cmp,
                             FindFlags_t flags /*= FindFlags::Defaults*/)
{
    auto it = makeForwardIterator(str);
    return makeEncodedRange(CodePoints::find(
            it, makeForwardIterator(cmp), flags));
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
EncodedRange<It1, Enc1> findNext(EncodedRange<It1, Enc1>& str,
                                 EncodedRange<It2, Enc2> cmp,
                                 FindFlags_t flags /*= FindFlags::Defaults*/)
{
    auto it = makeForwardIterator(str);
    auto result = makeEncodedRange(CodePoints::find(
            it, makeForwardIterator(cmp), flags));
    str.setBegin(end(result));
    return result;
}

template <typename It, typename Enc, typename UnaryFunc>
void forEachCodePoint(EncodedRange<It, Enc> str, UnaryFunc func)
{
    auto it = makeForwardIterator(str);
    uint32_t c;
    while (it.next(c))
        func(c);
}

template <typename It, typename Enc, typename UnaryFunc>
EncodedRange<It, Enc> forEachLine(
        EncodedRange<It, Enc> str, UnaryFunc tokenFunc,
        size_t maxParts /*= 0*/,
        SplitFlags_t flags /*= SplitFlags::Defaults*/)
{
    return makeEncodedRange(forEachLine(
            makeForwardIterator(str), tokenFunc, maxParts, flags));
}

template <typename It, typename Enc, typename UnaryFunc>
void forEachLower(EncodedRange<It, Enc> str, UnaryFunc func)
{
    forEachCodePoint(str, [&](uint32_t c){func(Unicode::lower(c));});
}

template <typename It, typename Enc, typename UnaryFunc>
void forEachTitle(EncodedRange<It, Enc> str, UnaryFunc func)
{
    bool capNext = true;
    auto it = makeForwardIterator(str);
    uint32_t c;
    while (it.next(c))
    {
        if (!Unicode::isCasedLetter(c))
        {
            func(c);
            capNext = !Unicode::isLetter(c);
        }
        else if (capNext)
        {
            func(Unicode::title(c));
            capNext = false;
        }
        else
        {
            func(Unicode::lower(c));
        }
    }
}

template <typename It, typename Enc, typename UnaryPred,
          typename UnaryFunc>
EncodedRange<It, Enc> forEachToken(
        EncodedRange<It, Enc> str, UnaryPred delimiterPred,
        UnaryFunc tokenFunc,
        size_t maxParts /*= 0*/,
        SplitFlags_t flags /*= SplitFlags::Defaults*/)
{
    return makeEncodedRange(forEachToken(
            makeForwardIterator(str), delimiterPred,
            tokenFunc, maxParts, flags));
}

template <typename It1, typename Enc1, typename It2, typename Enc2,
          typename UnaryFunc>
EncodedRange<It1, Enc1> forEachToken(
        EncodedRange<It1, Enc1> str, EncodedRange<It2, Enc2> delimiter,
        UnaryFunc tokenFunc,
        size_t maxParts /*= 0*/,
        SplitFlags_t flags /*= SplitFlags::Defaults*/)
{
    return makeEncodedRange(forEachToken(
            makeForwardIterator(str), makeForwardIterator(delimiter),
            tokenFunc, maxParts, flags));
}

template <typename It, typename Enc, typename UnaryFunc>
void forEachUpper(EncodedRange<It, Enc> str, UnaryFunc func)
{
    forEachCodePoint(str, [&](uint32_t c){func(Unicode::upper(c));});
}

template <typename It, typename Enc>
bool isAlphaNumeric(EncodedRange<It, Enc> str)
{
    auto it = makeForwardIterator(str);
    return !empty(str) && advanceWhile(it, Unicode::isAlphaNumeric);
}

template <typename It, typename Enc>
EncodedRange<It, Enc> nextLine(EncodedRange<It, Enc>& str)
{
    auto it = makeForwardIterator(str);
    auto line = makeEncodedRange(nextLine(it));
    it.setBegin(begin(it));
    return line;
}

template <typename It, typename Enc, typename UnaryPred>
EncodedRange<It, Enc> nextToken(EncodedRange<It, Enc>& str, UnaryPred pred)
{
    auto it = makeForwardIterator(str);
    auto token = makeEncodedRange(nextToken(it, pred));
    str.setBegin(end(it));
    return token;
}

template <typename It, typename Enc>
It nthCharacter(EncodedRange<It, Enc> str, ptrdiff_t n)
{
    if (n >= 0)
    {
        auto it = makeForwardIterator(str);
        advanceCharacters(it, n);
        return begin(it);
    }
    else
    {
        auto it = makeReverseIterator(str);
        advanceCharacters(it, -n);
        return end(it);
    }
}

template <typename It, typename Enc, typename UnaryPred>
EncodedRange<It, Enc> prevToken(EncodedRange<It, Enc>& str, UnaryPred pred)
{
    auto it = makeReverseIterator(str);
    auto token = makeEncodedRange(nextToken(it, pred));
    str.setEnd(end(it));
    return token;
}

template <typename String, typename It1, typename Enc1,
          typename It2, typename Enc2, typename It3, typename Enc3>
String& replace(String& dst,
                EncodedRange<It1, Enc1> str,
                EncodedRange<It2, Enc2> cmp,
                EncodedRange<It3, Enc3> subst,
                size_t max /*= 0*/,
                FindFlags_t flags /*= FindFlags::Defaults*/)
{
    auto first = begin(str);
    auto match = findNext(str, cmp, flags);
    while (!empty(match))
    {
        dst.insert(end(dst), first, begin(match));
        dst.insert(end(dst), begin(subst), end(subst));
        first = begin(str);
        if (--max == 0)
            break;
        match = findNext(str, cmp, flags);
    }
    dst.insert(end(dst), first, end(str));
    return dst;
}

template <typename InpIt, typename Enc1, typename OutIt, typename Enc2>
void reverse(EncodedRange<InpIt, Enc1> src, Encoder<OutIt, Enc2> dst)
{
    auto it = makeReverseIterator(src);
    while (advanceCharacter(it))
    {
        src.setBegin(end(it));
        copy(src, dst);
        src.setEnd(src.begin());
    }
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
bool startsWith(EncodedRange<It1, Enc1> str, EncodedRange<It2, Enc2> cmp,
                FindFlags_t flags /*= FindFlags::Defaults*/)
{
    return startsWith(makeForwardIterator(str),
                      makeForwardIterator(cmp),
                      flags);
}

template <typename It, typename Enc>
size_t stringLength(EncodedRange<It, Enc> str)
{
    auto it = makeForwardIterator(str);
    return advanceCharacters(it, std::numeric_limits<ptrdiff_t>::max());
}

template <typename It, typename Enc>
EncodedRange<It, Enc> substring(
        EncodedRange<It, Enc> str,
        ptrdiff_t first,
        ptrdiff_t last /*= std::numeric_limits<long>::max()*/)
{
    if (0 <= first)
    {
        str.setBegin(nthCharacter(str, first));
        if (last < 0)
            str.setEnd(nthCharacter(str, last));
        else if (first <= last)
            str.setEnd(nthCharacter(str, last - first));
        else
            str.setEnd(str.begin());
    }
    else if (0 <= last)
    {
        auto newBegin = nthCharacter(str, first);
        if (std::distance(begin(str), newBegin) <= last)
            str.setEnd(nthCharacter(str, last));
        else
            str.setEnd(newBegin);
        str.setBegin(newBegin);
    }
    else
    {
        str.setEnd(nthCharacter(str, last));
        if (first < last)
            str.setBegin(nthCharacter(str, first - last));
        else
            str.setBegin(str.end());
    }
    return str;
}

template <typename It, typename Enc, typename UnaryPred>
EncodedRange<It, Enc> trim(EncodedRange<It, Enc> str, UnaryPred trimChar)
{
    return trimBack(trimFront(str, trimChar), trimChar);
}

template <typename It, typename Enc, typename UnaryPred>
EncodedRange<It, Enc> trimFront(EncodedRange<It, Enc> str,
                                UnaryPred trimChar)
{
    auto first = makeForwardIterator(str);
    advanceWhile(first, trimChar);
    return makeEncodedRange(first);
}

template <typename It, typename Enc, typename UnaryPred>
EncodedRange<It, Enc> trimBack(EncodedRange<It, Enc> str,
                               UnaryPred trimChar)
{
    auto last = makeReverseIterator(str);
    advanceWhile(last, trimChar);
    return makeEncodedRange(last);
}

}}
<commit_msg>Added missing include.<commit_after>#include "JEBString/CodePoints/CodePointAlgorithms.hpp"
#include "JEBString/CodePoints/CodePointString.hpp"
#include "JEBString/Unicode/CaseInsensitive.hpp"
#include "JEBString/Unicode/UnicodePredicates.hpp"

namespace JEBString { namespace EncodedStrings {

using CodePoints::makeForwardIterator;
using CodePoints::makeReverseIterator;

template <typename It1, typename Enc1, typename It2, typename Enc2>
int32_t caseInsensitiveCompare(EncodedRange<It1, Enc1> str,
                               EncodedRange<It2, Enc2> cmp)
{
    return caseInsensitiveCompare(makeForwardIterator(str),
                                  makeForwardIterator(cmp));
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
bool caseInsensitiveEqual(EncodedRange<It1, Enc1> str,
                          EncodedRange<It2, Enc2> cmp)
{
    return caseInsensitiveEqual(makeForwardIterator(str),
                                makeForwardIterator(cmp));
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
bool caseInsensitiveLess(EncodedRange<It1, Enc1> str,
                         EncodedRange<It2, Enc2> cmp)
{
    return caseInsensitiveLess(makeForwardIterator(str),
                               makeForwardIterator(cmp));
}

template <typename It, typename Enc>
bool contains(EncodedRange<It, Enc> str, uint32_t chr)
{
    auto it = makeForwardIterator(str);
    return CodePoints::advanceUntil(it, [=](uint32_t c){return c == chr;});
}

template <typename InpIt, typename Enc1, typename OutIt, typename Enc2>
void copy(EncodedRange<InpIt, Enc1> src, Encoder<OutIt, Enc2> dst)
{
    if (src.encoding().encoding == dst.encoding().encoding)
        std::copy(begin(src), end(src), dst.iterator());
    else
        copy(makeForwardIterator(src), dst);
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
bool endsWith(EncodedRange<It1, Enc1> str, EncodedRange<It2, Enc2> cmp,
              FindFlags_t flags /*= FindFlags::Defaults*/)
{
    return startsWith(makeReverseIterator(str), makeReverseIterator(cmp),
                      flags);
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
EncodedRange<It1, Enc1> find(EncodedRange<It1, Enc1> str,
                             EncodedRange<It2, Enc2> cmp,
                             FindFlags_t flags /*= FindFlags::Defaults*/)
{
    auto it = makeForwardIterator(str);
    return makeEncodedRange(CodePoints::find(
            it, makeForwardIterator(cmp), flags));
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
EncodedRange<It1, Enc1> findNext(EncodedRange<It1, Enc1>& str,
                                 EncodedRange<It2, Enc2> cmp,
                                 FindFlags_t flags /*= FindFlags::Defaults*/)
{
    auto it = makeForwardIterator(str);
    auto result = makeEncodedRange(CodePoints::find(
            it, makeForwardIterator(cmp), flags));
    str.setBegin(end(result));
    return result;
}

template <typename It, typename Enc, typename UnaryFunc>
void forEachCodePoint(EncodedRange<It, Enc> str, UnaryFunc func)
{
    auto it = makeForwardIterator(str);
    uint32_t c;
    while (it.next(c))
        func(c);
}

template <typename It, typename Enc, typename UnaryFunc>
EncodedRange<It, Enc> forEachLine(
        EncodedRange<It, Enc> str, UnaryFunc tokenFunc,
        size_t maxParts /*= 0*/,
        SplitFlags_t flags /*= SplitFlags::Defaults*/)
{
    return makeEncodedRange(forEachLine(
            makeForwardIterator(str), tokenFunc, maxParts, flags));
}

template <typename It, typename Enc, typename UnaryFunc>
void forEachLower(EncodedRange<It, Enc> str, UnaryFunc func)
{
    forEachCodePoint(str, [&](uint32_t c){func(Unicode::lower(c));});
}

template <typename It, typename Enc, typename UnaryFunc>
void forEachTitle(EncodedRange<It, Enc> str, UnaryFunc func)
{
    bool capNext = true;
    auto it = makeForwardIterator(str);
    uint32_t c;
    while (it.next(c))
    {
        if (!Unicode::isCasedLetter(c))
        {
            func(c);
            capNext = !Unicode::isLetter(c);
        }
        else if (capNext)
        {
            func(Unicode::title(c));
            capNext = false;
        }
        else
        {
            func(Unicode::lower(c));
        }
    }
}

template <typename It, typename Enc, typename UnaryPred,
          typename UnaryFunc>
EncodedRange<It, Enc> forEachToken(
        EncodedRange<It, Enc> str, UnaryPred delimiterPred,
        UnaryFunc tokenFunc,
        size_t maxParts /*= 0*/,
        SplitFlags_t flags /*= SplitFlags::Defaults*/)
{
    return makeEncodedRange(forEachToken(
            makeForwardIterator(str), delimiterPred,
            tokenFunc, maxParts, flags));
}

template <typename It1, typename Enc1, typename It2, typename Enc2,
          typename UnaryFunc>
EncodedRange<It1, Enc1> forEachToken(
        EncodedRange<It1, Enc1> str, EncodedRange<It2, Enc2> delimiter,
        UnaryFunc tokenFunc,
        size_t maxParts /*= 0*/,
        SplitFlags_t flags /*= SplitFlags::Defaults*/)
{
    return makeEncodedRange(forEachToken(
            makeForwardIterator(str), makeForwardIterator(delimiter),
            tokenFunc, maxParts, flags));
}

template <typename It, typename Enc, typename UnaryFunc>
void forEachUpper(EncodedRange<It, Enc> str, UnaryFunc func)
{
    forEachCodePoint(str, [&](uint32_t c){func(Unicode::upper(c));});
}

template <typename It, typename Enc>
bool isAlphaNumeric(EncodedRange<It, Enc> str)
{
    auto it = makeForwardIterator(str);
    return !empty(str) && advanceWhile(it, Unicode::isAlphaNumeric);
}

template <typename It, typename Enc>
EncodedRange<It, Enc> nextLine(EncodedRange<It, Enc>& str)
{
    auto it = makeForwardIterator(str);
    auto line = makeEncodedRange(nextLine(it));
    it.setBegin(begin(it));
    return line;
}

template <typename It, typename Enc, typename UnaryPred>
EncodedRange<It, Enc> nextToken(EncodedRange<It, Enc>& str, UnaryPred pred)
{
    auto it = makeForwardIterator(str);
    auto token = makeEncodedRange(nextToken(it, pred));
    str.setBegin(end(it));
    return token;
}

template <typename It, typename Enc>
It nthCharacter(EncodedRange<It, Enc> str, ptrdiff_t n)
{
    if (n >= 0)
    {
        auto it = makeForwardIterator(str);
        advanceCharacters(it, n);
        return begin(it);
    }
    else
    {
        auto it = makeReverseIterator(str);
        advanceCharacters(it, -n);
        return end(it);
    }
}

template <typename It, typename Enc, typename UnaryPred>
EncodedRange<It, Enc> prevToken(EncodedRange<It, Enc>& str, UnaryPred pred)
{
    auto it = makeReverseIterator(str);
    auto token = makeEncodedRange(nextToken(it, pred));
    str.setEnd(end(it));
    return token;
}

template <typename String, typename It1, typename Enc1,
          typename It2, typename Enc2, typename It3, typename Enc3>
String& replace(String& dst,
                EncodedRange<It1, Enc1> str,
                EncodedRange<It2, Enc2> cmp,
                EncodedRange<It3, Enc3> subst,
                size_t max /*= 0*/,
                FindFlags_t flags /*= FindFlags::Defaults*/)
{
    auto first = begin(str);
    auto match = findNext(str, cmp, flags);
    while (!empty(match))
    {
        dst.insert(end(dst), first, begin(match));
        dst.insert(end(dst), begin(subst), end(subst));
        first = begin(str);
        if (--max == 0)
            break;
        match = findNext(str, cmp, flags);
    }
    dst.insert(end(dst), first, end(str));
    return dst;
}

template <typename InpIt, typename Enc1, typename OutIt, typename Enc2>
void reverse(EncodedRange<InpIt, Enc1> src, Encoder<OutIt, Enc2> dst)
{
    auto it = makeReverseIterator(src);
    while (advanceCharacter(it))
    {
        src.setBegin(end(it));
        copy(src, dst);
        src.setEnd(src.begin());
    }
}

template <typename It1, typename Enc1, typename It2, typename Enc2>
bool startsWith(EncodedRange<It1, Enc1> str, EncodedRange<It2, Enc2> cmp,
                FindFlags_t flags /*= FindFlags::Defaults*/)
{
    return startsWith(makeForwardIterator(str),
                      makeForwardIterator(cmp),
                      flags);
}

template <typename It, typename Enc>
size_t stringLength(EncodedRange<It, Enc> str)
{
    auto it = makeForwardIterator(str);
    return advanceCharacters(it, std::numeric_limits<ptrdiff_t>::max());
}

template <typename It, typename Enc>
EncodedRange<It, Enc> substring(
        EncodedRange<It, Enc> str,
        ptrdiff_t first,
        ptrdiff_t last /*= std::numeric_limits<long>::max()*/)
{
    if (0 <= first)
    {
        str.setBegin(nthCharacter(str, first));
        if (last < 0)
            str.setEnd(nthCharacter(str, last));
        else if (first <= last)
            str.setEnd(nthCharacter(str, last - first));
        else
            str.setEnd(str.begin());
    }
    else if (0 <= last)
    {
        auto newBegin = nthCharacter(str, first);
        if (std::distance(begin(str), newBegin) <= last)
            str.setEnd(nthCharacter(str, last));
        else
            str.setEnd(newBegin);
        str.setBegin(newBegin);
    }
    else
    {
        str.setEnd(nthCharacter(str, last));
        if (first < last)
            str.setBegin(nthCharacter(str, first - last));
        else
            str.setBegin(str.end());
    }
    return str;
}

template <typename It, typename Enc, typename UnaryPred>
EncodedRange<It, Enc> trim(EncodedRange<It, Enc> str, UnaryPred trimChar)
{
    return trimBack(trimFront(str, trimChar), trimChar);
}

template <typename It, typename Enc, typename UnaryPred>
EncodedRange<It, Enc> trimFront(EncodedRange<It, Enc> str,
                                UnaryPred trimChar)
{
    auto first = makeForwardIterator(str);
    advanceWhile(first, trimChar);
    return makeEncodedRange(first);
}

template <typename It, typename Enc, typename UnaryPred>
EncodedRange<It, Enc> trimBack(EncodedRange<It, Enc> str,
                               UnaryPred trimChar)
{
    auto last = makeReverseIterator(str);
    advanceWhile(last, trimChar);
    return makeEncodedRange(last);
}

}}
<|endoftext|>