From 2518f6c83ca3475dee53628dc0188162510a2891 Mon Sep 17 00:00:00 2001 From: Houtan Bastani Date: Fri, 28 Aug 2015 12:38:25 +0200 Subject: [PATCH] preprocessor: macroprocessor: add @#includepath macro. #1039 --- doc/dynare.texi | 29 +++++++++++++-- preprocessor/macro/MacroDriver.hh | 8 +++-- preprocessor/macro/MacroFlex.ll | 60 +++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 4 deletions(-) diff --git a/doc/dynare.texi b/doc/dynare.texi index a2a4b0bfa..3b1519d22 100644 --- a/doc/dynare.texi +++ b/doc/dynare.texi @@ -845,12 +845,14 @@ information about the configuration file. Defines a macro-variable from the command line (the same effect as using the Macro directive @code{@@#define} in a model file, @pxref{Macro-processing language}). +@anchor{-I} @item -I@var{<>} Defines a path to search for files to be included by the macroprocessor (using the @ref{@@#include} command). Multiple @code{-I} flags can be passed on the command line. The paths will be searched in the order that the @code{-I} flags are passed and the -first matching file will be used. +first matching file will be used. The flags passed here take priority +over those passed to @ref{@@#includepath}. @item nostrict Allows Dynare to issue a warning and continue processing when @@ -8832,6 +8834,8 @@ of the line indicates that the directive is continued on the next line. The main directives are: @itemize @item +@code{@@#includepath}, paths to search for files that are to be included, +@item @code{@@#include}, for file inclusion, @item @code{@@#define}, for defining a macro-processor variable, @@ -8937,13 +8941,34 @@ constructed as explained above. @node Macro directives @subsection Macro directives +@anchor{@@#includepath} +@deffn {Macro directive} @@#includepath "@var{PATH}" +@deffnx {Macro directive} @@#includepath @var{MACRO_VARIABLE} +This directive adds the colon-separated paths contained in @var{PATH} +to the list of those to search when looking for a @code{.mod} file +specified by @ref{@@#include}. Note that these paths are added +@i{after} any paths passed using @ref{-I}. + +@examplehead + +@example +@@#include "/path/to/folder/containing/modfiles:/path/to/another/folder" +@@#include folders_containing_mod_files +@end example + +@end deffn + + @anchor{@@#include} @deffn {Macro directive} @@#include "@var{FILENAME}" @deffnx {Macro directive} @@#include @var{MACRO_VARIABLE} This directive simply includes the content of another file at the place where it is inserted. It is exactly equivalent to a copy/paste of the content of the included file. Note that it is possible to nest -includes (@i{i.e.} to include a file from an included file). +includes (@i{i.e.} to include a file from an included file). The file +will be searched for in the current directory. If it is not found, the +file will be searched for in the folders provided by @ref{-I} and +@ref{@@#includepath}. @examplehead diff --git a/preprocessor/macro/MacroDriver.hh b/preprocessor/macro/MacroDriver.hh index 92d90ee40..83828b2ed 100644 --- a/preprocessor/macro/MacroDriver.hh +++ b/preprocessor/macro/MacroDriver.hh @@ -79,7 +79,7 @@ private: //! Should we omit the @#line statements ? const bool no_line_macro; //! The paths to search when looking for .mod files - const vector path; + vector path; //! True iff current context is the body of a loop bool is_for_context; //! If current context is the body of a loop, contains the string of the loop body @@ -122,6 +122,10 @@ private: //! Restore last scanning context void restore_context(Macro::parser::location_type *yylloc); + //! pushes the colon-separated paths passed to @#includepath onto the path vector + void push_path(string *includepath, Macro::parser::location_type *yylloc, + MacroDriver &driver); + //! Saves current scanning context and create a new context with content of filename /*! Filename must be a newly allocated string which will be deleted by the lexer */ void create_include_context(string *filename, Macro::parser::location_type *yylloc, @@ -178,7 +182,7 @@ public: //! Starts parsing a file, returns output in out /*! \param no_line_macro should we omit the @#line statements ? */ void parse(const string &f, ostream &out, bool debug, bool no_line_macro, - map defines, const vector path); + map defines, vector path); //! Name of main file being parsed string file; diff --git a/preprocessor/macro/MacroFlex.ll b/preprocessor/macro/MacroFlex.ll index 994d2420f..9e694eb48 100644 --- a/preprocessor/macro/MacroFlex.ll +++ b/preprocessor/macro/MacroFlex.ll @@ -21,6 +21,9 @@ using namespace std; #include +#include +#include +#include #include "MacroDriver.hh" @@ -68,6 +71,47 @@ CONT \\\\ yylloc->step(); %} +^{SPC}*@#{SPC}*includepath{SPC}+\"([^\"\r\n:;|<>]*){1}(:[^\"\r\n:;|<>]*)*\"{SPC}*{EOL} { + yylloc->lines(1); + yylloc->step(); + + // Get path + string *includepath = new string(yytext); + int dblq_idx1 = includepath->find('"'); + int dblq_idx2 = includepath->find('"', dblq_idx1 + 1); + includepath->erase(dblq_idx2); + includepath->erase(0, dblq_idx1 + 1); + + push_path(includepath, yylloc, driver); + BEGIN(INITIAL); + } + +^{SPC}*@#{SPC}*includepath{SPC}+[^\"\r\n]*{SPC}*{EOL} { + yylloc->lines(1); + yylloc->step(); + + // Get variable name + string pathvar = string(yytext); + int dblq_idx1 = pathvar.find("includepath"); + pathvar.erase(0, dblq_idx1 + 11); + pathvar.erase(0, pathvar.find_first_not_of(" \t")); + size_t p = pathvar.find_last_not_of(" \t\n\r"); + if (string::npos != p) + pathvar.erase(p+1); + + string *includepath = NULL; + try + { + includepath = new string(driver.get_variable(pathvar)->toString()); + } + catch(MacroDriver::UnknownVariable(&e)) + { + driver.error(*yylloc, "Unknown variable: " + pathvar); + } + push_path(includepath, yylloc, driver); + BEGIN(INITIAL); + } + ^{SPC}*@#{SPC}*include{SPC}+\"[^\"\r\n]*\"{SPC}*{EOL} { yylloc->lines(1); yylloc->step(); @@ -383,6 +427,22 @@ MacroFlex::restore_context(Macro::parser::location_type *yylloc) output_line(yylloc); } +void +MacroFlex::push_path(string *includepath, Macro::parser::location_type *yylloc, + MacroDriver &driver) +{ + using namespace boost; + vector tokenizedPath; + split(tokenizedPath, *includepath, is_any_of(":"), token_compress_on); + for (vector::iterator it = tokenizedPath.begin(); + it != tokenizedPath.end(); it++ ) + if (!it->empty()) + { + trim(*it); + path.push_back(*it); + } +} + void MacroFlex::create_include_context(string *filename, Macro::parser::location_type *yylloc, MacroDriver &driver)