v4 preprocessor/macro:
* added @if/@then/@else * added @echo and @error git-svn-id: https://www.dynare.org/svn/dynare/dynare_v4@1738 ac1d8469-bf42-47a9-8791-bf33cf982152issue#70
parent
dd7764f484
commit
c04b4e4355
|
@ -75,7 +75,7 @@ class MacroDriver;
|
|||
|
||||
%}
|
||||
|
||||
%token DEFINE LINE FOR IN
|
||||
%token DEFINE LINE FOR IN IF ELSE ENDIF ECHO_DIR ERROR
|
||||
%token LPAREN RPAREN LBRACKET RBRACKET EQUAL EOL
|
||||
|
||||
%token <int_val> INTEGER
|
||||
|
@ -111,6 +111,12 @@ statement : expr
|
|||
{ driver.set_variable(*$2, $4); delete $2; }
|
||||
| FOR NAME IN expr
|
||||
{ TYPERR_CATCH(driver.init_loop(*$2, $4), @$); delete $2; }
|
||||
| IF expr
|
||||
{ TYPERR_CATCH(driver.begin_if($2), @$); }
|
||||
| ECHO_DIR expr
|
||||
{ TYPERR_CATCH(driver.echo(@$, $2), @$); }
|
||||
| ERROR expr
|
||||
{ TYPERR_CATCH(driver.error(@$, $2), @$); }
|
||||
| LINE STRING INTEGER
|
||||
/* Ignore @line declarations */
|
||||
;
|
||||
|
|
|
@ -126,3 +126,32 @@ MacroDriver::iter_loop()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroDriver::begin_if(const MacroValue *value) throw (MacroValue::TypeError)
|
||||
{
|
||||
const IntMV *ival = dynamic_cast<const IntMV *>(value);
|
||||
if (!ival)
|
||||
throw MacroValue::TypeError("Argument of @if must be an integer");
|
||||
last_if = (bool) ival->value;
|
||||
}
|
||||
|
||||
void
|
||||
MacroDriver::echo(const Macro::parser::location_type &l, const MacroValue *value) const throw (MacroValue::TypeError)
|
||||
{
|
||||
const StringMV *sval = dynamic_cast<const StringMV *>(value);
|
||||
if (!sval)
|
||||
throw MacroValue::TypeError("Argument of @echo must be a string");
|
||||
|
||||
cerr << "ECHO in macro-processor: " << l << ": " << sval->value << endl;
|
||||
}
|
||||
|
||||
void
|
||||
MacroDriver::error(const Macro::parser::location_type &l, const MacroValue *value) const throw (MacroValue::TypeError)
|
||||
{
|
||||
const StringMV *sval = dynamic_cast<const StringMV *>(value);
|
||||
if (!sval)
|
||||
throw MacroValue::TypeError("Argument of @error must be a string");
|
||||
|
||||
error(l, sval->value);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ using namespace std;
|
|||
*/
|
||||
class MacroFlex : public MacroFlexLexer
|
||||
{
|
||||
private:
|
||||
//! Used to backup all the information related to a given scanning context
|
||||
class ScanContext
|
||||
{
|
||||
|
@ -63,7 +64,7 @@ class MacroFlex : public MacroFlexLexer
|
|||
input(input_arg), buffer(buffer_arg), yylloc(yylloc_arg), for_body(for_body_arg),
|
||||
for_body_loc(for_body_loc_arg) { }
|
||||
};
|
||||
private:
|
||||
|
||||
//! The stack used to keep track of nested scanning contexts
|
||||
stack<ScanContext> context_stack;
|
||||
|
||||
|
@ -85,8 +86,38 @@ private:
|
|||
//! Set to true while parsing a FOR statement (only the statement, not the loop body)
|
||||
bool reading_for_statement;
|
||||
|
||||
//! Temporary variable used in THEN_BODY and ELSE_BODY modes. Keeps track of number of nested @if
|
||||
int nested_if_nb;
|
||||
//! Temporary variable used in THEN_BODY mode
|
||||
string then_body_tmp;
|
||||
//! Temporary variable used in THEN_BODY mode
|
||||
Macro::parser::location_type then_body_loc_tmp;
|
||||
//! Temporary variable used in ELSE_BODY mode
|
||||
string else_body_tmp;
|
||||
//! Temporary variable used in ELSE_BODY mode
|
||||
Macro::parser::location_type else_body_loc_tmp;
|
||||
//! Set to true while parsing an IF statement (only the statement, not the body)
|
||||
bool reading_if_statement;
|
||||
|
||||
//! Output the @line declaration
|
||||
void output_line(Macro::parser::location_type *yylloc);
|
||||
void output_line(Macro::parser::location_type *yylloc) const;
|
||||
|
||||
//! Save current scanning context
|
||||
void save_context(Macro::parser::location_type *yylloc);
|
||||
|
||||
//! Restore last scanning context
|
||||
void restore_context(Macro::parser::location_type *yylloc);
|
||||
|
||||
//! 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,
|
||||
MacroDriver &driver);
|
||||
|
||||
//! Saves current scanning context and create a new context based on the "then" body
|
||||
void create_then_context(Macro::parser::location_type *yylloc);
|
||||
|
||||
//! Saves current scanning context and create a new context based on the "else" body
|
||||
void create_else_context(Macro::parser::location_type *yylloc);
|
||||
|
||||
//! Iterates over the loop body
|
||||
/*! If loop is terminated, return false and do nothing.
|
||||
|
@ -107,6 +138,7 @@ class MacroDriver
|
|||
{
|
||||
friend class MacroValue;
|
||||
private:
|
||||
//! Stores all created macro values
|
||||
set<const MacroValue *> values;
|
||||
|
||||
//! Environment: maps macro variables to their values
|
||||
|
@ -141,6 +173,9 @@ public:
|
|||
//! Copy of output stream
|
||||
ostream *out_stream;
|
||||
|
||||
//! Used to store the value of the last @if condition
|
||||
bool last_if;
|
||||
|
||||
//! Trace scanning ?
|
||||
/*! If set to true before calling parse(), the flex scanner will dump a lot of debugging information. Defaults to false.
|
||||
*/
|
||||
|
@ -167,6 +202,15 @@ public:
|
|||
//! Iterate innermost loop
|
||||
/*! Returns false if iteration is no more possible (end of loop); in that case it destroys the pointer given to init_loop() */
|
||||
bool iter_loop();
|
||||
|
||||
//! Begins an @if statement
|
||||
void begin_if(const MacroValue *value) throw (MacroValue::TypeError);
|
||||
|
||||
//! Executes @echo directive
|
||||
void echo(const Macro::parser::location_type &l, const MacroValue *value) const throw (MacroValue::TypeError);
|
||||
|
||||
//! Executes @error directive
|
||||
void error(const Macro::parser::location_type &l, const MacroValue *value) const throw (MacroValue::TypeError);
|
||||
};
|
||||
|
||||
#endif // ! MACRO_DRIVER_HH
|
||||
|
|
|
@ -49,11 +49,17 @@ typedef Macro::parser::token token;
|
|||
|
||||
%x MACRO
|
||||
%x FOR_BODY
|
||||
%x THEN_BODY
|
||||
%x ELSE_BODY
|
||||
|
||||
%{
|
||||
// Increments location counter for every token read
|
||||
#define YY_USER_ACTION yylloc->columns(yyleng);
|
||||
%}
|
||||
|
||||
SPC [ \t]+
|
||||
EOL (\r)?\n
|
||||
|
||||
%%
|
||||
/* Code put at the beginning of yylex() */
|
||||
%{
|
||||
|
@ -61,39 +67,28 @@ typedef Macro::parser::token token;
|
|||
yylloc->step();
|
||||
%}
|
||||
|
||||
<INITIAL>^@include[ \t]+\"[^\"\r\n]*\"[ \t]*(\r)?\n {
|
||||
yylloc->lines(1);
|
||||
yylloc->step();
|
||||
// Save old buffer state and location
|
||||
context_stack.push(ScanContext(input, YY_CURRENT_BUFFER, *yylloc, for_body, for_body_loc));
|
||||
// Get filename
|
||||
string *filename = new string(yytext);
|
||||
int dblq_idx1 = filename->find('"');
|
||||
int dblq_idx2 = filename->find('"', dblq_idx1 + 1);
|
||||
filename->erase(dblq_idx2);
|
||||
filename->erase(0, dblq_idx1 + 1);
|
||||
// Open new file
|
||||
input = new ifstream(filename->c_str(), ios::binary);
|
||||
if (input->fail())
|
||||
driver.error(*yylloc, "Could not open " + *filename);
|
||||
// Reset location
|
||||
yylloc->begin.filename = yylloc->end.filename = filename;
|
||||
yylloc->begin.line = yylloc->end.line = 1;
|
||||
yylloc->begin.column = yylloc->end.column = 0;
|
||||
// We are not in a loop body
|
||||
for_body.erase();
|
||||
// Output @line information
|
||||
output_line(yylloc);
|
||||
// Switch to new buffer
|
||||
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
|
||||
BEGIN(INITIAL);
|
||||
<INITIAL>@{SPC}*include{SPC}+\"[^\"\r\n]*\"{SPC}*{EOL} {
|
||||
yylloc->lines(1);
|
||||
yylloc->step();
|
||||
|
||||
// Get filename
|
||||
string *filename = new string(yytext);
|
||||
int dblq_idx1 = filename->find('"');
|
||||
int dblq_idx2 = filename->find('"', dblq_idx1 + 1);
|
||||
filename->erase(dblq_idx2);
|
||||
filename->erase(0, dblq_idx1 + 1);
|
||||
|
||||
create_include_context(filename, yylloc, driver);
|
||||
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
|
||||
<INITIAL>@ { BEGIN(MACRO); }
|
||||
|
||||
<MACRO>[ \t\r\f]+ { yylloc->step(); }
|
||||
<MACRO>{SPC}+ { yylloc->step(); }
|
||||
<MACRO>@ { BEGIN(INITIAL); return token::EOL; }
|
||||
<MACRO>\n {
|
||||
<MACRO>\\\\{SPC}*{EOL} { yylloc->lines(1); yylloc->step(); }
|
||||
<MACRO>{EOL} {
|
||||
yylloc->lines(1);
|
||||
yylloc->step();
|
||||
if (reading_for_statement)
|
||||
|
@ -104,9 +99,19 @@ typedef Macro::parser::token token;
|
|||
nested_for_nb = 0;
|
||||
BEGIN(FOR_BODY);
|
||||
}
|
||||
else if (reading_if_statement)
|
||||
{
|
||||
reading_if_statement = false;
|
||||
then_body_tmp.erase();
|
||||
then_body_loc_tmp = *yylloc;
|
||||
nested_if_nb = 0;
|
||||
BEGIN(THEN_BODY);
|
||||
}
|
||||
else
|
||||
BEGIN(INITIAL);
|
||||
*yyout << endl;
|
||||
{
|
||||
*yyout << endl;
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
return token::EOL;
|
||||
}
|
||||
|
||||
|
@ -143,42 +148,116 @@ typedef Macro::parser::token token;
|
|||
|
||||
<MACRO>line { return token::LINE; }
|
||||
<MACRO>define { return token::DEFINE; }
|
||||
|
||||
<MACRO>for { reading_for_statement = true; return token::FOR; }
|
||||
<MACRO>in { return token::IN; }
|
||||
<MACRO>endfor { driver.error(*yylloc, "@endfor is not matched by a @for statement"); }
|
||||
|
||||
<MACRO>if { reading_if_statement = true; return token::IF; }
|
||||
<MACRO>else { driver.error(*yylloc, "@else is not matched by an @if statement"); }
|
||||
<MACRO>endif { driver.error(*yylloc, "@endif is not matched by an @if statement"); }
|
||||
|
||||
<MACRO>echo { return token::ECHO_DIR; }
|
||||
<MACRO>error { return token::ERROR; }
|
||||
|
||||
<MACRO>[A-Za-z_][A-Za-z0-9_]* {
|
||||
yylval->string_val = new string(yytext);
|
||||
return token::NAME;
|
||||
}
|
||||
yylval->string_val = new string(yytext);
|
||||
return token::NAME;
|
||||
}
|
||||
|
||||
<MACRO><<EOF>> { driver.error(*yylloc, "Unexpected end of file while parsing a macro expression"); }
|
||||
|
||||
<FOR_BODY>[\n]+ { yylloc->lines(yyleng); yylloc->step(); for_body_tmp.append(yytext); }
|
||||
<FOR_BODY>@for { nested_for_nb++; for_body_tmp.append(yytext); }
|
||||
<FOR_BODY>{EOL} { yylloc->lines(1); yylloc->step(); for_body_tmp.append(yytext); }
|
||||
<FOR_BODY>@{SPC}*for { nested_for_nb++; for_body_tmp.append(yytext); }
|
||||
<FOR_BODY>. { for_body_tmp.append(yytext); }
|
||||
<FOR_BODY><<EOF>> { driver.error(*yylloc, "Unexpected end of file: @for loop not matched by an @endfor"); }
|
||||
<FOR_BODY>@endfor[ \t]*(\r)?\n {
|
||||
if (nested_for_nb)
|
||||
{
|
||||
nested_for_nb--;
|
||||
for_body_tmp.append(yytext);
|
||||
}
|
||||
else
|
||||
{
|
||||
yylloc->lines(1);
|
||||
yylloc->step();
|
||||
// Save old buffer state and location
|
||||
context_stack.push(ScanContext(input, YY_CURRENT_BUFFER, *yylloc, for_body, for_body_loc));
|
||||
<FOR_BODY>@{SPC}*endfor{SPC}*{EOL} {
|
||||
yylloc->lines(1);
|
||||
yylloc->step();
|
||||
if (nested_for_nb)
|
||||
{
|
||||
/* This @endfor is not the end of the loop body,
|
||||
but only that of a nested @for loop */
|
||||
nested_for_nb--;
|
||||
for_body_tmp.append(yytext);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Save old buffer state and location
|
||||
save_context(yylloc);
|
||||
|
||||
for_body = for_body_tmp;
|
||||
for_body_loc = for_body_loc_tmp;
|
||||
|
||||
iter_loop(driver, yylloc);
|
||||
for_body = for_body_tmp;
|
||||
for_body_loc = for_body_loc_tmp;
|
||||
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
}
|
||||
iter_loop(driver, yylloc);
|
||||
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
}
|
||||
|
||||
<THEN_BODY>{EOL} { yylloc->lines(1); yylloc->step(); then_body_tmp.append(yytext); }
|
||||
<THEN_BODY>@{SPC}*if { nested_if_nb++; then_body_tmp.append(yytext); }
|
||||
<THEN_BODY>. { then_body_tmp.append(yytext); }
|
||||
<THEN_BODY><<EOF>> { driver.error(*yylloc, "Unexpected end of file: @if not matched by an @endif"); }
|
||||
<THEN_BODY>@{SPC}*else{SPC}*{EOL} {
|
||||
yylloc->lines(1);
|
||||
yylloc->step();
|
||||
if (nested_if_nb)
|
||||
then_body_tmp.append(yytext);
|
||||
else
|
||||
{
|
||||
else_body_tmp.erase();
|
||||
else_body_loc_tmp = *yylloc;
|
||||
BEGIN(ELSE_BODY);
|
||||
}
|
||||
}
|
||||
|
||||
<THEN_BODY>@{SPC}*endif{SPC}*{EOL} {
|
||||
yylloc->lines(1);
|
||||
yylloc->step();
|
||||
if (nested_if_nb)
|
||||
{
|
||||
/* This @endif is not the end of the @if we're parsing,
|
||||
but only that of a nested @if */
|
||||
nested_if_nb--;
|
||||
then_body_tmp.append(yytext);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (driver.last_if)
|
||||
create_then_context(yylloc);
|
||||
else
|
||||
output_line(yylloc);
|
||||
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
}
|
||||
|
||||
<ELSE_BODY>{EOL} { yylloc->lines(1); yylloc->step(); else_body_tmp.append(yytext); }
|
||||
<ELSE_BODY>@{SPC}*if { nested_if_nb++; else_body_tmp.append(yytext); }
|
||||
<ELSE_BODY>. { else_body_tmp.append(yytext); }
|
||||
<ELSE_BODY><<EOF>> { driver.error(*yylloc, "Unexpected end of file: @if not matched by an @endif"); }
|
||||
|
||||
<ELSE_BODY>@{SPC}*endif{SPC}*{EOL} {
|
||||
yylloc->lines(1);
|
||||
yylloc->step();
|
||||
if (nested_if_nb)
|
||||
{
|
||||
/* This @endif is not the end of the @if we're parsing,
|
||||
but only that of a nested @if */
|
||||
nested_if_nb--;
|
||||
else_body_tmp.append(yytext);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (driver.last_if)
|
||||
create_then_context(yylloc);
|
||||
else
|
||||
create_else_context(yylloc);
|
||||
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
}
|
||||
|
||||
<INITIAL><<EOF>> {
|
||||
// Quit lexer if end of main file
|
||||
|
@ -186,25 +265,16 @@ typedef Macro::parser::token token;
|
|||
{
|
||||
yyterminate();
|
||||
}
|
||||
|
||||
// Else clean current scanning context
|
||||
yy_delete_buffer(YY_CURRENT_BUFFER);
|
||||
delete input;
|
||||
delete yylloc->begin.filename;
|
||||
|
||||
// If we are not in a loop body, or if the loop has terminated, pop a context
|
||||
/* If we are not in a loop body, or if the loop has terminated,
|
||||
pop a context */
|
||||
if (for_body.empty() || !iter_loop(driver, yylloc))
|
||||
{
|
||||
// Restore old context
|
||||
input = context_stack.top().input;
|
||||
yy_switch_to_buffer(context_stack.top().buffer);
|
||||
*yylloc = context_stack.top().yylloc;
|
||||
for_body = context_stack.top().for_body;
|
||||
for_body_loc = context_stack.top().for_body_loc;
|
||||
// Remove top of stack
|
||||
context_stack.pop();
|
||||
// Dump @line instruction
|
||||
output_line(yylloc);
|
||||
}
|
||||
restore_context(yylloc);
|
||||
}
|
||||
|
||||
/* Ignore \r, because under Cygwin, outputting \n automatically adds another \r */
|
||||
|
@ -218,17 +288,82 @@ typedef Macro::parser::token token;
|
|||
%%
|
||||
|
||||
MacroFlex::MacroFlex(istream* in, ostream* out)
|
||||
: MacroFlexLexer(in, out), input(in), reading_for_statement(false)
|
||||
: MacroFlexLexer(in, out), input(in), reading_for_statement(false), reading_if_statement(false)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MacroFlex::output_line(Macro::parser::location_type *yylloc)
|
||||
MacroFlex::output_line(Macro::parser::location_type *yylloc) const
|
||||
{
|
||||
*yyout << endl << "@line \"" << *yylloc->begin.filename << "\" "
|
||||
<< yylloc->begin.line << endl;
|
||||
}
|
||||
|
||||
void
|
||||
MacroFlex::save_context(Macro::parser::location_type *yylloc)
|
||||
{
|
||||
context_stack.push(ScanContext(input, YY_CURRENT_BUFFER, *yylloc, for_body, for_body_loc));
|
||||
}
|
||||
|
||||
void
|
||||
MacroFlex::restore_context(Macro::parser::location_type *yylloc)
|
||||
{
|
||||
input = context_stack.top().input;
|
||||
yy_switch_to_buffer(context_stack.top().buffer);
|
||||
*yylloc = context_stack.top().yylloc;
|
||||
for_body = context_stack.top().for_body;
|
||||
for_body_loc = context_stack.top().for_body_loc;
|
||||
// Remove top of stack
|
||||
context_stack.pop();
|
||||
// Dump @line instruction
|
||||
output_line(yylloc);
|
||||
}
|
||||
|
||||
void
|
||||
MacroFlex::create_include_context(string *filename, Macro::parser::location_type *yylloc,
|
||||
MacroDriver &driver)
|
||||
{
|
||||
save_context(yylloc);
|
||||
// Open new file
|
||||
input = new ifstream(filename->c_str(), ios::binary);
|
||||
if (input->fail())
|
||||
driver.error(*yylloc, "Could not open " + *filename);
|
||||
// Reset location
|
||||
yylloc->begin.filename = yylloc->end.filename = filename;
|
||||
yylloc->begin.line = yylloc->end.line = 1;
|
||||
yylloc->begin.column = yylloc->end.column = 0;
|
||||
// We are not in a loop body
|
||||
for_body.clear();
|
||||
// Output @line information
|
||||
output_line(yylloc);
|
||||
// Switch to new buffer
|
||||
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
|
||||
}
|
||||
|
||||
void
|
||||
MacroFlex::create_then_context(Macro::parser::location_type *yylloc)
|
||||
{
|
||||
save_context(yylloc);
|
||||
input = new stringstream(then_body_tmp);
|
||||
*yylloc = then_body_loc_tmp;
|
||||
yylloc->begin.filename = yylloc->end.filename = new string(*then_body_loc_tmp.begin.filename);
|
||||
for_body.clear();
|
||||
output_line(yylloc);
|
||||
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
|
||||
}
|
||||
|
||||
void
|
||||
MacroFlex::create_else_context(Macro::parser::location_type *yylloc)
|
||||
{
|
||||
save_context(yylloc);
|
||||
input = new stringstream(else_body_tmp);
|
||||
*yylloc = else_body_loc_tmp;
|
||||
yylloc->begin.filename = yylloc->end.filename = new string(*else_body_loc_tmp.begin.filename);
|
||||
for_body.clear();
|
||||
output_line(yylloc);
|
||||
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
|
||||
}
|
||||
|
||||
bool
|
||||
MacroFlex::iter_loop(MacroDriver &driver, Macro::parser::location_type *yylloc)
|
||||
{
|
||||
|
|
|
@ -105,6 +105,7 @@ public:
|
|||
class IntMV : public MacroValue
|
||||
{
|
||||
friend class StringMV;
|
||||
friend class MacroDriver;
|
||||
private:
|
||||
//! Underlying integer value
|
||||
const int value;
|
||||
|
@ -154,6 +155,7 @@ public:
|
|||
//! Represents a string value in macro language
|
||||
class StringMV : public MacroValue
|
||||
{
|
||||
friend class MacroDriver;
|
||||
private:
|
||||
//! Underlying string value
|
||||
const string value;
|
||||
|
|
Loading…
Reference in New Issue