Commit dd7764f4 authored by sebastien's avatar sebastien
Browse files

v4 preprocessor/macro: added @for loops


git-svn-id: https://www.dynare.org/svn/dynare/dynare_v4@1724 ac1d8469-bf42-47a9-8791-bf33cf982152
parent 62e216c3
......@@ -51,7 +51,7 @@ class MacroDriver;
{
string *string_val;
int int_val;
MacroValue *mv;
const MacroValue *mv;
};
%{
......@@ -64,9 +64,9 @@ class MacroDriver;
#undef yylex
#define yylex driver.lexer->lex
#define TYPERR_CATCH(st, loc) try \
#define TYPERR_CATCH(statement, loc) try \
{ \
st; \
statement; \
} \
catch(MacroValue::TypeError &e) \
{ \
......@@ -75,7 +75,7 @@ class MacroDriver;
%}
%token DEFINE LINE
%token DEFINE LINE FOR IN
%token LPAREN RPAREN LBRACKET RBRACKET EQUAL EOL
%token <int_val> INTEGER
......@@ -98,24 +98,30 @@ class MacroDriver;
%start statement_list_or_nothing;
statement_list_or_nothing : /* empty */
| statement_list;
| statement_list
;
statement_list : statement EOL
| statement_list statement EOL;
| statement_list statement EOL
;
statement : expr
{ *driver.out_stream << $1->toString(); delete $1; }
{ *driver.out_stream << $1->toString(); }
| DEFINE NAME EQUAL expr
{ driver.set_variable(*$2, $4); delete $2; }
| FOR NAME IN expr
{ TYPERR_CATCH(driver.init_loop(*$2, $4), @$); delete $2; }
| LINE STRING INTEGER
/* Ignore @line declarations */
;
expr : INTEGER
{ $$ = new IntMV($1); }
{ $$ = new IntMV(driver, $1); }
| STRING
{ $$ = new StringMV(*$1); delete $1; }
{ $$ = new StringMV(driver, *$1); delete $1; }
| NAME
{ try
{
try
{
$$ = driver.get_variable(*$1);
}
......@@ -123,56 +129,58 @@ expr : INTEGER
{
error(@$, "Unknown variable: " + e.name);
}
delete $1; }
delete $1;
}
| LPAREN expr RPAREN
{ $$ = $2; }
| expr PLUS expr
{ TYPERR_CATCH($$ = *$1 + *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 + *$3, @$); }
| expr MINUS expr
{ TYPERR_CATCH($$ = *$1 - *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 - *$3, @$); }
| expr TIMES expr
{ TYPERR_CATCH($$ = *$1 * *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 * *$3, @$); }
| expr DIVIDE expr
{ TYPERR_CATCH($$ = *$1 / *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 / *$3, @$); }
| expr LESS expr
{ TYPERR_CATCH($$ = *$1 < *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 < *$3, @$); }
| expr GREATER expr
{ TYPERR_CATCH($$ = *$1 > *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 > *$3, @$); }
| expr LESS_EQUAL expr
{ TYPERR_CATCH($$ = *$1 <= *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 <= *$3, @$); }
| expr GREATER_EQUAL expr
{ TYPERR_CATCH($$ = *$1 >= *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 >= *$3, @$); }
| expr EQUAL_EQUAL expr
{ TYPERR_CATCH($$ = *$1 == *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 == *$3, @$); }
| expr EXCLAMATION_EQUAL expr
{ TYPERR_CATCH($$ = *$1 != *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 != *$3, @$); }
| expr LOGICAL_OR expr
{ TYPERR_CATCH($$ = *$1 || *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 || *$3, @$); }
| expr LOGICAL_AND expr
{ TYPERR_CATCH($$ = *$1 && *$3, @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = *$1 && *$3, @$); }
| MINUS expr %prec UMINUS
{ TYPERR_CATCH($$ = -*$2, @$); delete $2;}
{ TYPERR_CATCH($$ = -*$2, @$); }
| PLUS expr %prec UPLUS
{ TYPERR_CATCH($$ = +(*$2), @$); delete $2; }
{ TYPERR_CATCH($$ = +(*$2), @$); }
| EXCLAMATION expr
{ TYPERR_CATCH($$ = !*$2, @$); delete $2; }
{ TYPERR_CATCH($$ = !*$2, @$); }
| expr LBRACKET array_expr RBRACKET
{ TYPERR_CATCH($$ = (*$1)[*$3], @$)
{
TYPERR_CATCH($$ = (*$1)[*$3], @$)
catch(MacroValue::OutOfBoundsError)
{
error(@$, "Index out of bounds");
}
delete $1; delete $3; }
}
| LBRACKET array_expr RBRACKET
{ $$ = $2; }
| expr COLON expr
{ TYPERR_CATCH($$ = IntMV::new_range(*$1, *$3), @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = IntMV::new_range(driver, $1, $3), @$); }
;
array_expr : expr
{ $$ = $1->toArray(); delete $1; }
{ $$ = $1->toArray(); }
| array_expr COMMA expr
{ TYPERR_CATCH($$ = $3->append(*$1), @$); delete $1; delete $3; }
{ TYPERR_CATCH($$ = $3->append($1), @$); }
;
%%
......
......@@ -28,9 +28,9 @@ MacroDriver::MacroDriver() : trace_scanning(false), trace_parsing(false)
MacroDriver::~MacroDriver()
{
for(map<string, MacroValue *>::iterator it = env.begin();
it != env.end(); it++)
delete it->second;
for(set<const MacroValue *>::iterator it = values.begin();
it != values.end(); it++)
delete *it;
}
void
......@@ -40,6 +40,11 @@ MacroDriver::parse(const string &f, ostream &out)
out_stream = &out;
ifstream in(f.c_str(), ios::binary);
if (in.fail())
{
cerr << "ERROR: Could not open file: " << f << endl;
exit(-1);
}
lexer = new MacroFlex(&in, &out);
lexer->set_debug(trace_scanning);
......@@ -58,16 +63,66 @@ MacroDriver::error(const Macro::parser::location_type &l, const string &m) const
}
void
MacroDriver::set_variable(const string &name, MacroValue *value)
MacroDriver::set_variable(const string &name, const MacroValue *value)
{
env[name] = value;
}
MacroValue *
const MacroValue *
MacroDriver::get_variable(const string &name) const throw (UnknownVariable)
{
map<string, MacroValue *>::const_iterator it = env.find(name);
map<string, const MacroValue *>::const_iterator it = env.find(name);
if (it == env.end())
throw UnknownVariable(name);
return (it->second)->clone();
return it->second;
}
void
MacroDriver::init_loop(const string &name, const MacroValue *value) throw (MacroValue::TypeError)
{
const ArrayMV<int> *mv1 = dynamic_cast<const ArrayMV<int> *>(value);
const ArrayMV<string> *mv2 = dynamic_cast<const ArrayMV<string> *>(value);
if (!mv1 && !mv2)
throw MacroValue::TypeError("Argument of @for loop must be an array expression");
loop_stack.push(make_pair(name, make_pair(value, 0)));
}
bool
MacroDriver::iter_loop()
{
if (loop_stack.empty())
throw "No loop on which to iterate!";
int &i = loop_stack.top().second.second;
const MacroValue *mv = loop_stack.top().second.first;
string name = loop_stack.top().first;
const ArrayMV<int> *mv1 = dynamic_cast<const ArrayMV<int> *>(mv);
if (mv1)
{
if (i >= (int) mv1->values.size())
{
loop_stack.pop();
return false;
}
else
{
env[name] = new IntMV(*this, mv1->values[i++]);
return true;
}
}
else
{
const ArrayMV<string> *mv2 = dynamic_cast<const ArrayMV<string> *>(mv);
if (i >= (int) mv2->values.size())
{
loop_stack.pop();
return false;
}
else
{
env[name] = new StringMV(*this, mv2->values[i++]);
return true;
}
}
}
......@@ -28,6 +28,7 @@
#include <iostream>
#include <stack>
#include <map>
#include <set>
#include "MacroBison.hh"
#include "MacroValue.hh"
......@@ -47,14 +48,51 @@ using namespace std;
*/
class MacroFlex : public MacroFlexLexer
{
//! Used to backup all the information related to a given scanning context
class ScanContext
{
public:
istream *input;
struct yy_buffer_state *buffer;
const Macro::parser::location_type yylloc;
const string for_body;
const Macro::parser::location_type for_body_loc;
ScanContext(istream *input_arg, struct yy_buffer_state *buffer_arg,
Macro::parser::location_type &yylloc_arg, const string &for_body_arg,
Macro::parser::location_type &for_body_loc_arg) :
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 handle (possibly nested) includes
/*! Keeps track of buffer state and associated location, as they were just before switching to
included file.
Note that we could have used yypush_buffer_state() and yypop_buffer_state()
instead of a stack for buffer states, but those functions do not exist in Flex 2.5.4 */
stack<pair<struct yy_buffer_state *, Macro::parser::location_type> > include_stack;
//! The stack used to keep track of nested scanning contexts
stack<ScanContext> context_stack;
//! Input stream used for initialization of current scanning context
/*! Kept for deletion at end of current scanning buffer */
istream *input;
//! If current context is the body of a loop, contains the string of the loop body. Empty otherwise.
string for_body;
//! If current context is the body of a loop, contains the location of the beginning of the body
Macro::parser::location_type for_body_loc;
//! Temporary variable used in FOR_BODY mode
string for_body_tmp;
//! Temporary variable used in FOR_BODY mode
Macro::parser::location_type for_body_loc_tmp;
//! Temporary variable used in FOR_BODY mode. Keeps track of number of nested @for/@endfor
int nested_for_nb;
//! Set to true while parsing a FOR statement (only the statement, not the loop body)
bool reading_for_statement;
//! Output the @line declaration
void output_line(Macro::parser::location_type *yylloc);
//! Iterates over the loop body
/*! If loop is terminated, return false and do nothing.
Otherwise, set loop variable to its new value (through driver.iter_loop()),
and initialise a new scanning context with the loop body */
bool iter_loop(MacroDriver &driver, Macro::parser::location_type *yylloc);
public:
MacroFlex(istream* in = 0, ostream* out = 0);
......@@ -67,10 +105,16 @@ public:
//! Implements the macro expansion using a Flex scanner and a Bison parser
class MacroDriver
{
friend class MacroValue;
private:
set<const MacroValue *> values;
//! Environment: maps macro variables to their values
map<string, MacroValue *> env;
map<string, const MacroValue *> env;
//! Stack used to keep track of (possibly nested) loops
//! First element is loop variable name, second is the array over which iteration is done, and third is subscript to be used by next call of iter_loop() (beginning with 0) */
stack<pair<string, pair<const MacroValue *, int> > > loop_stack;
public:
//! Exception thrown when value of an unknown variable is requested
class UnknownVariable
......@@ -88,9 +132,6 @@ public:
//! Starts parsing a file, returns output in out
void parse(const string &f, ostream &out);
//! Pointer to keep track of the input file stream currently scanned
ifstream *ifs;
//! Name of main file being parsed
string file;
......@@ -113,12 +154,19 @@ public:
void error(const Macro::parser::location_type &l, const string &m) const;
//! Set a variable
/*! Pointer *value must not be altered nor deleted afterwards, since it is kept by this class */
void set_variable(const string &name, MacroValue *value);
void set_variable(const string &name, const MacroValue *value);
//! Get a variable
/*! Returns a newly allocated value (clone of the value stored in environment). */
MacroValue *get_variable(const string &name) const throw (UnknownVariable);
const MacroValue *get_variable(const string &name) const throw (UnknownVariable);
//! Initiate a for loop
/*! Does not set name = value[1]. You must call iter_loop() for that. */
void init_loop(const string &name, const MacroValue *value) throw (MacroValue::TypeError);
//! 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();
};
#endif // ! MACRO_DRIVER_HH
......@@ -47,9 +47,8 @@ typedef Macro::parser::token token;
%option case-insensitive noyywrap nounput batch debug never-interactive
%x INCLUDE
%x END_INCLUDE
%x MACRO
%x FOR_BODY
%{
// Increments location counter for every token read
......@@ -62,43 +61,54 @@ typedef Macro::parser::token token;
yylloc->step();
%}
<INITIAL>^@include[ \t]+\" BEGIN(INCLUDE);
<INCLUDE>[^\"\r\n]* {
driver.ifs = new ifstream(yytext, ios::binary);
if (driver.ifs->fail())
driver.error(*yylloc, "Could not open " + string(yytext));
// Save old buffer state and location
/* We don't use yypush_buffer_state(), since it doesn't exist in
Flex 2.5.4 (see Flex 2.5.33 info file - section 11 - for code
example with yypush_buffer_state()) */
<INITIAL>^@include[ \t]+\"[^\"\r\n]*\"[ \t]*(\r)?\n {
yylloc->lines(1);
yylloc->step();
include_stack.push(make_pair(YY_CURRENT_BUFFER, *yylloc));
// 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 = new string(yytext);
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
*yyout << "@line \"" << *yylloc->begin.filename << "\" 1" << endl;
output_line(yylloc);
// Switch to new buffer
yy_switch_to_buffer(yy_create_buffer(driver.ifs, YY_BUF_SIZE));
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
BEGIN(INITIAL);
}
<END_INCLUDE>\"[^\r\n]*(\r)?\n {
yylloc->lines(1);
yylloc->step();
*yyout << "@line \"" << *yylloc->begin.filename << "\" "
<< yylloc->begin.line << endl;
BEGIN(INITIAL);
}
<INITIAL>@ { BEGIN(MACRO); }
<MACRO>[ \t\r\f]+ { yylloc->step(); }
<MACRO>@ { BEGIN(INITIAL); return token::EOL; }
<MACRO>\n { BEGIN(INITIAL); yylloc->lines(1); yylloc->step(); *yyout << endl; return token::EOL; }
<MACRO>\n {
yylloc->lines(1);
yylloc->step();
if (reading_for_statement)
{
reading_for_statement = false;
for_body_tmp.erase();
for_body_loc_tmp = *yylloc;
nested_for_nb = 0;
BEGIN(FOR_BODY);
}
else
BEGIN(INITIAL);
*yyout << endl;
return token::EOL;
}
<MACRO>[0-9]+ {
yylval->int_val = atoi(yytext);
......@@ -133,31 +143,68 @@ 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>[A-Za-z_][A-Za-z0-9_]* {
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>. { 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 = for_body_tmp;
for_body_loc = for_body_loc_tmp;
iter_loop(driver, yylloc);
BEGIN(INITIAL);
}
}
<<EOF>> {
<INITIAL><<EOF>> {
// Quit lexer if end of main file
if (include_stack.empty())
if (context_stack.empty())
{
yyterminate();
}
// Else restore old flex buffer
/* We don't use yypop_buffer_state(), since it doesn't exist in
Flex 2.5.4 (see Flex 2.5.33 info file - section 11 - for code
example with yypop_buffer_state()) */
// Else clean current scanning context
yy_delete_buffer(YY_CURRENT_BUFFER);
yy_switch_to_buffer(include_stack.top().first);
// And restore old location
delete input;
delete yylloc->begin.filename;
*yylloc = include_stack.top().second;
// Remove top of stack
include_stack.pop();
BEGIN(END_INCLUDE);
// 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);
}
}
/* Ignore \r, because under Cygwin, outputting \n automatically adds another \r */
......@@ -171,8 +218,30 @@ typedef Macro::parser::token token;
%%
MacroFlex::MacroFlex(istream* in, ostream* out)
: MacroFlexLexer(in, out)
: MacroFlexLexer(in, out), input(in), reading_for_statement(false)
{
}
void
MacroFlex::output_line(Macro::parser::location_type *yylloc)
{
*yyout << endl << "@line \"" << *yylloc->begin.filename << "\" "
<< yylloc->begin.line << endl;
}
bool
MacroFlex::iter_loop(MacroDriver &driver, Macro::parser::location_type *yylloc)
{
if (!driver.iter_loop())
return false;
input = new stringstream(for_body);
*yylloc = for_body_loc;
yylloc->begin.filename = yylloc->end.filename = new string(*for_body_loc.begin.filename);
output_line(yylloc);
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
return true;
}
/* This implementation of MacroFlexLexer::yylex() is required to fill the
......
......@@ -17,109 +17,114 @@
* along with Dynare. If not, see <http://www.gnu.org/licenses/>.
*/
#include "MacroValue.hh"
#include "MacroDriver.hh"
MacroValue::MacroValue(MacroDriver &driver_arg) : driver(driver_arg)
{
driver.values.insert(this);
}
MacroValue::~MacroValue()
{
}
MacroValue *
const MacroValue *
MacroValue::operator+() const throw (TypeError)
{
throw TypeError("Unary operator + does not exist for this type");
}
MacroValue *
const MacroValue *
MacroValue::operator-(const MacroValue &mv) const throw (TypeError)
{
throw TypeError("Operator - does not exist for this type");
}
MacroValue *
const MacroValue *
MacroValue::operator-() const throw (TypeError)
{
throw TypeError("Unary operator - does not exist for this type");
}
MacroValue *
const MacroValue *
MacroValue::operator*(const MacroValue &mv) const throw (TypeError)
{
throw TypeError("Operator * does not exist for this type");
}
MacroValue *
const MacroValue *
MacroValue::operator/(const MacroValue &mv) const throw (TypeError)
{
throw TypeError("Operator / does not exist for this type");
}
MacroValue *
const MacroValue *
MacroValue::operator<(const MacroValue &mv) const throw (TypeError)
{
throw TypeError("Operator < does not exist for this type");
}
MacroValue *
const MacroValue *
MacroValue::operator>(const MacroValue &mv) const throw (TypeError)
{
throw TypeError("Operator > does not exist for this type");
}
MacroValue *
const MacroValue *
MacroValue::operator<=(const MacroValue &m