Mpfw.h
// Mpfw.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2012
#pragma once
#ifndef Ceda_cxMacroExpander_Mpfw_H
#define Ceda_cxMacroExpander_Mpfw_H
#include "cxMacroExpander.h"
#include "LSToken.h"
#include "ExpressionParser.h"
#include "MacroExpand.h"
#include "Ceda/cxUtils/xstring.h"
#include "Ceda/cxUtils/xostream.h"
#include "Ceda/cxUtils/SubString.h"
#include "Ceda/cxUtils/AutoPtrExt.h"
#include <map>
#ifdef _MSC_VER
// non dll-interface struct 'X' used as base for dll-interface class 'Y'
#pragma warning(disable:4275)
#endif
/*
Mpfw (Macro Parser FrameWork)
-----------------------------
We want the following three parsers:
WorkspaceParser (to parse .xcws files)
ProjectParser (to parse .xcpj files)
ConfigParser (to parse .xcpp files)
to share as much infrastructure as possible. i.e. :
- Using the macro expander
- Using the ExpressionParser
- Support @import
- Support include paths to resolve @imports
- Record the set of files that have been brought into memory
- Error pin pointing
- $macro = "..." style macros, which can then be applied to string tokens
[see Macros.h]
Futhermore, we want both $macros and @macros to be available to the "next stage". i.e.
.xcpp macros are available to the workspace and projects
.xcws macros are available to the projects
The implication is that they should share the recorded set of source files - so that when an error
occurs in a macro, the original file in which the macro is defined can be used for error pin-
pointing.
*/
namespace ceda
{
///////////////////////////////////////////////////////////////////////////////////////////////////
// MpfwSourceFile
/*
cxBuild ScannedFile manages to avoid recording m_expanded and m_pieces. So why does MpfwSourceFile need this
state? Why can't it be local to a given call to MacroExpand?
Application can subclass MpfwSourceFile if convenient, in order to add additional state for each
imported file. In that case MpfwParser::CreateMpfwSourceFile() can be overridden to new the subclass
*/
struct cxMacroExpander_API MpfwSourceFile
{
virtual ~MpfwSourceFile() {}
bool GetFilenameAndLineNumber(const xchar* pos, xstring& filename, ssize_t& lineNumber) const;
void AdjustErrorPosition(LexScannerException& e)
{
m_pieces.AdjustErrorPosition(e,m_expanded);
}
///////// state //////////
xstring m_localPath; // We record a local path to the file; this is appropriate for display in the
// MSVC output window.
xstring m_buffer; // The contents of the file read into memory in a single contiguous buffer.
xstring m_expanded; // After macro expansion
OutputPieces m_pieces; // Allows position in m_expanded to be mapped back to source
// positions
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// MpfwSourceFileList
struct cxMacroExpander_API MpfwSourceFileList : public AutoCollectionOfPtr<xvector<MpfwSourceFile*> >
{
void AddSrcFile(/*takes*/ MpfwSourceFile* srcFile);
MpfwSourceFile* FindSourceFile(const xstring& localPath);
ssize_t GetSrcFileIndexContainingPosition(const xchar* pos) const;
bool GetFilenameAndLineNumber(const xchar* pos, xstring& localPath, ssize_t& lineNumber) const;
void WriteLexScannerError(xostream& os, const LexScannerException& e) const;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// MpfwFrame
class cxMacroExpander_API MpfwFrame
{
public:
MpfwFrame(MpfwSourceFileList& srcFileList);
////////////////////// state //////////////////////////
MpfwSourceFileList& m_srcFileList;
FileScopeVarFrame m_vf; // For macro expansion
// Associated with @importpath statement which is a bit like the /I path switch of the MSVC
// compiler
xvector<xstring> m_importPaths;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// MpfwParser
class cxMacroExpander_API MpfwParser : public ExpressionParser, public ImportHandler
{
public:
MpfwParser(MpfwFrame& frame);
/*
HandleImport() is called by the macro expander when it processes
an @import directive.
It is necessary to in turn run the macro expander on the given file, so
that the macro definitions become available in the containing file.
However it is NOT possible to actually run the parser on the imported file yet, because we
haven't even started the parser on the containing file yet!
*/
// Implementation of ImportHandler
virtual bool HandleImportPath(const xstring& importPath, const xchar* errorpos);
virtual bool HandleImport(VarFrame* vf, const xstring& importPath, const xchar* errorpos);
// Override if a subclass of MpfwSourceFile is to be used instead
virtual MpfwSourceFile* CreateMpfwSourceFile()
{
return new MpfwSourceFile;
}
virtual bool ResolveImportPath(const xstring& importPath, xstring& localPath) = 0;
virtual void ProcessImportedFile(MpfwSourceFile* srcFile) = 0;
MpfwSourceFile* AddFile(const xstring& localPath, SubString extra = SubString());
// Move this into ParserBase?
//void ThrowError(const xstring& str) { throw LexScannerException(str.c_str(), m_token); }
bool ParseImportDirective();
void ReadString(xstring& value);
void ParseStringSettingHelper(const xstring& name, xstring& value);
void ParseStringSetting(const xstring& name, xstring& value);
void ParsePathSetting(const xstring& name, xstring& value);
// Parse a macro definition of the form $macroname = "string value"
bool ParseMacroDef(xstring& name, xstring& value);
bool ParseMacroDef();
bool ApplyMacros(xstring& s);
xstring ParseQuotedString();
xstring ParseFileOrDirName();
///////////////// state ////////////////
MpfwFrame& m_frame;
};
} // namespace ceda
#endif // include guard