MacroExpand.h

// MacroExpand.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2006

#pragma once
#ifndef Ceda_cxMacroExpander_MacroExpand_H
#define Ceda_cxMacroExpander_MacroExpand_H

#include "cxMacroExpander.h"
#include "LSVariable.h"
#include "Ceda/cxUtils/SubString.h"

namespace ceda
{
/*
Macro expand the given input string (given as a SubString).  The expansion is performed in the 
context of the given VarFrame.  This actually represents a stack of frames because each VarFrame
stores a pointer to its parent frame (or NULL if there is no parent).  Each frame provides a 
set of macros, implemented as an STL map keyed by the name of the macro to support fast lookup.  
Searching of a macro in a VarFrame involves first searching the local map before recursing into the
parent frame.  This has the effect of implementing the "hiding rule" - ie where each frame hides 
all the macros with the same name in ancestor frames.

Note that vf cannot be NULL.  TODO: Use a reference then!

The srcIndent represents the indentation of the input string.  After each linefeed it is assumed 
that srcIndent spaces should be consumed from the input (ie without being sent to the output).  
Note that no source indent is assumed on the first line.

The dstIndent represents the required indentation on the output string.  ie the source string, 
considered as a block of text (without its source indent), is indented by dstIndent as it is
written to the output string.  Note that no destination indent is applied on the first line - ie
an indent is only applied after each linefeed.

Import directives
-----------------

The input string may contain import/include directives.  It is assumed that importing a file has 
the effect of adding additional local names to the VarFrame.  This is done dynamically - a VarFrame
has a concept of mounting any number of additional VarFrames that contribute to the local 
namespace.  This however can lead to ambiguities during name lookup.  Ambiguities are regarded as 
fatal errors.

The client receives a notification of an import directive.  This provides an opportunity to add 
additional VarFrames to the main VarFrame.
*/

/*
TODO:

*   Document all the pre-processor directives.
    
    @def
    @if() ... @else
    @[]
    @()
    @quote()
    @import
    @str
    @unstr
    @while
    @let
    @@

    types : bool, int, double, char, string

*   Allow output to be an ostream

*   Allow for call back to client for import directive.
    Client is reponsible for opening file, processing it, adjusting vf of calling frame
*/

struct LexScannerException;

///////////////////////////////////////////////////////////////////////////////////////////////////
// ImportHandler

struct ImportHandler
{
    // Handle an 'importpath' directive.  By default returns false to indicate the directive is not
    // supported
    virtual bool HandleImportPath(const xstring& importPath, const xchar* errorpos) { return false; }
    
    virtual bool HandleImport(VarFrame* vf, const xstring& importPath, const xchar* errorpos) = 0;
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// OutputPiece

struct OutputPiece
{
    OutputPiece() : out(0) {}
    
    // The size of the piece in characters.  Never returns 0.
    ssize_t size() const
    {
        ssize_t s = out ? out : in.size();
        cxAssert(s > 0);
        return s;
    }

    SubString in;           // Must alias a non-empty part of one of the original source files
                            // or if empty corresponds to the output of pure white space characters
                            // which are only an artefact of formatting requirements.
                            
    ssize_t out;            // 0 means 'in' is copied verbatim.  Otherwise 'out' characters
                            // were calculated from 'in'.
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// OutputPieces

class cxMacroExpander_API OutputPieces
{
public:
    ssize_t size() const;
    
    // Map the given position within the output of the macro expansion string to a corresponding 
    // source position within one of the input source code files.
    const xchar* MapOutputPositionToAddress(ssize_t pos) const;
    
    void swap(OutputPieces& rhs) { m_pieces.swap(rhs.m_pieces); }
    
    void push_back(const OutputPiece& op) { m_pieces.push_back(op); }

    void AdjustErrorPosition(LexScannerException& e, SubString input) const;

private:
    xvector<OutputPiece> m_pieces;
    
    friend class OutputPiecesMonotonePosMapper;
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// OutputPiecesMonotonePosMapper
/*
OutputPieces::MapOutputPositionToAddress() is relatively expensive because it involves a scan 
through the xvector<OutputPiece>.

Allows for efficient calls to MapOutputPositionToAddress() in the case of monotone increasing 
positions, by making use of where it was last up to.
*/
class cxMacroExpander_API OutputPiecesMonotonePosMapper
{
public:
    OutputPiecesMonotonePosMapper(const OutputPieces& op);
    const xchar* MapOutputPositionToAddress(ssize_t pos);

private:
    const OutputPieces& m_op;
    ssize_t m_index;                // Index position in the xvector<OutputPiece>
    ssize_t m_offset;               // total of sizes of the xvector<OutputPiece> up to but not
                                    // including m_index.
};

///////////////////////////////////////////////////////////////////////////////////////////////////
cxMacroExpander_API void MacroExpand(
    ImportHandler* importHandler, 
    VarFrame* vf,
    SubString input,
    ssize_t srcIndent, 
    ssize_t dstIndent, 
    xstring& output);

// Version which provides additional information about 'output' allowing for positions in 'output' 
// to be mapped back to the source files.
cxMacroExpander_API void MacroExpand(
    ImportHandler* importHandler, 
    VarFrame* vf,
    SubString input,
    ssize_t srcIndent, 
    ssize_t dstIndent, 
    xstring& output, 
    OutputPieces& pieces);

} // namespace ceda

#endif // include guard