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-bf33cf982152
issue#70
sebastien 2008-03-07 15:58:35 +00:00
parent dd7764f484
commit c04b4e4355
5 changed files with 288 additions and 72 deletions

View File

@ -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 */
;

View File

@ -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);
}

View File

@ -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

View File

@ -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)
{

View File

@ -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;