Macroprocessor: replace naked pointers by smart pointers

- use std::unique_ptr for MacroDriver::lexer
- use std::shared_ptr for MacroValue objects constructed by the parser
  This is made possible using Bison 3.0 variant value type.
  Using non-pointer type is not possible since MacroValue is polymorphic.
  Using std::unique_ptr is not possible since the value type must be
  copyable (see
  https://lists.gnu.org/archive/html/bug-bison/2015-03/msg00004.html).
  Define a new type MacroValuePtr == shared_ptr<const MacroValue> for code
  clarity.
  Use the following convention for shared_ptr arguments to functions:
  + if pass-by-value, means that the callee acquires ownership
  + if pass-by-const-reference, the callee does not acquire ownership
- naked pointers are still used for tracking the filename in Bison's location
  type, since we don't have control over that

Also, by the way:
- arrays can now contain elements of any type
- simplify MacroDriver::loop_stack using std::tuple
- toArray() method now fails on function objects
- no need to use const qualifiers, since all MacroValue objects are immutable
- use an exception for detecting division by zero
issue#70
Sébastien Villemot 2018-07-24 16:46:17 +02:00
parent 372005ed27
commit 5f1465f9d1
6 changed files with 514 additions and 782 deletions

View File

@ -18,8 +18,10 @@
*/
%language "c++"
%require "2.5"
%require "3.0"
%defines
%define api.value.type variant
%define parse.assert
%code top {
class MacroDriver;
@ -45,14 +47,6 @@ class MacroDriver;
#include "MacroValue.hh"
}
%union
{
string *string_val;
int int_val;
const MacroValue *mv;
vector<string> *vector_string_val;
};
%code {
#include "MacroDriver.hh"
@ -76,8 +70,8 @@ class MacroDriver;
%token DEFINE LINE FOR IN IF ELSE ENDIF ECHO_DIR ERROR IFDEF IFNDEF
%token LPAREN RPAREN LBRACKET RBRACKET EQUAL EOL LENGTH ECHOMACROVARS SAVE
%token <int_val> INTEGER
%token <string_val> NAME STRING
%token <int> INTEGER
%token <string> NAME STRING
%left COMMA
%left LOGICAL_OR
@ -90,8 +84,9 @@ class MacroDriver;
%left UMINUS UPLUS EXCLAMATION
%left LBRACKET
%type <vector_string_val> func_args
%type <mv> expr array_expr
%type <vector<string>> func_args
%type <MacroValuePtr> expr
%type <vector<MacroValuePtr>> comma_expr
%%
%start statement_list_or_nothing;
@ -107,15 +102,15 @@ statement_list : statement EOL
statement : expr
{ out << $1->toString(); }
| DEFINE NAME EQUAL expr
{ driver.set_variable(*$2, $4); delete $2; }
{ driver.set_variable($2, $4); }
| FOR NAME IN expr
{ TYPERR_CATCH(driver.init_loop(*$2, $4), @$); delete $2; }
{ TYPERR_CATCH(driver.init_loop($2, $4), @$); }
| IF expr
{ TYPERR_CATCH(driver.begin_if($2), @$); }
| IFDEF NAME
{ TYPERR_CATCH(driver.begin_ifdef(*$2), @$); delete $2; }
{ TYPERR_CATCH(driver.begin_ifdef($2), @$); }
| IFNDEF NAME
{ TYPERR_CATCH(driver.begin_ifndef(*$2), @$); delete $2; }
{ TYPERR_CATCH(driver.begin_ifndef($2), @$); }
| ECHO_DIR expr
{ TYPERR_CATCH(driver.echo(@$, $2), @$); }
| ERROR expr
@ -126,98 +121,96 @@ statement : expr
{ driver.printvars(@$, true); }
| ECHOMACROVARS LPAREN SAVE RPAREN
{ out << driver.printvars(@$, false); }
| DEFINE NAME LPAREN func_args { driver.push_args_into_func_env(*$4); } RPAREN EQUAL expr
| DEFINE NAME LPAREN func_args { driver.push_args_into_func_env($4); } RPAREN EQUAL expr
{
TYPERR_CATCH(driver.set_string_function(*$2, *$4, $8), @$);
TYPERR_CATCH(driver.set_string_function($2, $4, $8), @$);
driver.pop_func_env();
delete $2;
delete $4;
}
;
func_args : NAME
{ $$ = new vector<string>(); $$->push_back(*$1); delete $1; }
{ $$ = vector<string>{$1}; }
| func_args COMMA NAME
{ $$->push_back(*$3); delete $3; }
{ $1.push_back($3); $$ = $1; }
;
expr : INTEGER
{ $$ = new IntMV(driver, $1); }
{ $$ = make_shared<IntMV>($1); }
| STRING
{ $$ = new StringMV(driver, driver.replace_vars_in_str(*$1)); delete $1; }
{ $$ = make_shared<StringMV>(driver.replace_vars_in_str($1)); }
| NAME
{
try
{
$$ = driver.get_variable(*$1);
$$ = driver.get_variable($1);
}
catch(MacroDriver::UnknownVariable(&e))
{
error(@$, "Unknown variable: " + e.name);
}
delete $1;
}
| NAME LPAREN array_expr RPAREN
{ TYPERR_CATCH($$ = driver.eval_string_function(*$1, $3), @$); delete $1; }
| NAME LPAREN comma_expr RPAREN
{ TYPERR_CATCH($$ = driver.eval_string_function($1, $3), @$); }
| LENGTH LPAREN expr RPAREN
{ TYPERR_CATCH($$ = $3->length(), @$); }
| LPAREN expr RPAREN
{ $$ = $2; }
| expr PLUS expr
{ TYPERR_CATCH($$ = *$1 + *$3, @$); }
{ TYPERR_CATCH($$ = $1->plus($3), @$); }
| expr MINUS expr
{ TYPERR_CATCH($$ = *$1 - *$3, @$); }
{ TYPERR_CATCH($$ = $1->minus($3), @$); }
| expr TIMES expr
{ TYPERR_CATCH($$ = *$1 * *$3, @$); }
{ TYPERR_CATCH($$ = $1->times($3), @$); }
| expr DIVIDE expr
{
if (dynamic_cast<const IntMV *>($3) != NULL
&& ((IntMV *)$3)->get_int_value() == 0)
driver.error(@$, "Division by zero");
TYPERR_CATCH($$ = *$1 / *$3, @$);
TYPERR_CATCH($$ = $1->divide($3), @$)
catch(MacroValue::DivisionByZeroError)
{
error(@$, "Division by zero");
}
}
| expr LESS expr
{ TYPERR_CATCH($$ = *$1 < *$3, @$); }
{ TYPERR_CATCH($$ = $1->is_less($3), @$); }
| expr GREATER expr
{ TYPERR_CATCH($$ = *$1 > *$3, @$); }
{ TYPERR_CATCH($$ = $1->is_greater($3), @$); }
| expr LESS_EQUAL expr
{ TYPERR_CATCH($$ = *$1 <= *$3, @$); }
{ TYPERR_CATCH($$ = $1->is_less_equal($3), @$); }
| expr GREATER_EQUAL expr
{ TYPERR_CATCH($$ = *$1 >= *$3, @$); }
{ TYPERR_CATCH($$ = $1->is_greater_equal($3), @$); }
| expr EQUAL_EQUAL expr
{ TYPERR_CATCH($$ = *$1 == *$3, @$); }
{ $$ = $1->is_equal($3); }
| expr EXCLAMATION_EQUAL expr
{ TYPERR_CATCH($$ = *$1 != *$3, @$); }
{ $$ = $1->is_different($3); }
| expr LOGICAL_OR expr
{ TYPERR_CATCH($$ = *$1 || *$3, @$); }
{ TYPERR_CATCH($$ = $1->logical_or($3), @$); }
| expr LOGICAL_AND expr
{ TYPERR_CATCH($$ = *$1 && *$3, @$); }
{ TYPERR_CATCH($$ = $1->logical_and($3), @$); }
| MINUS expr %prec UMINUS
{ TYPERR_CATCH($$ = -*$2, @$); }
{ TYPERR_CATCH($$ = $2->unary_minus(), @$); }
| PLUS expr %prec UPLUS
{ TYPERR_CATCH($$ = +(*$2), @$); }
{ TYPERR_CATCH($$ = $2->unary_plus(), @$); }
| EXCLAMATION expr
{ TYPERR_CATCH($$ = !*$2, @$); }
| expr LBRACKET array_expr RBRACKET
{ TYPERR_CATCH($$ = $2->logical_not(), @$); }
| expr LBRACKET expr RBRACKET
{
TYPERR_CATCH($$ = (*$1)[*$3], @$)
TYPERR_CATCH($$ = $1->subscript($3), @$)
catch(MacroValue::OutOfBoundsError)
{
error(@$, "Index out of bounds");
}
}
| LBRACKET array_expr RBRACKET
{ $$ = $2; }
| LBRACKET comma_expr RBRACKET
{ $$ = make_shared<ArrayMV>($2); }
| expr COLON expr
{ TYPERR_CATCH($$ = IntMV::new_range(driver, $1, $3), @$); }
{ TYPERR_CATCH($$ = ArrayMV::range($1, $3), @$); }
| expr IN expr
{ TYPERR_CATCH($$ = $1->in($3), @$); }
{ TYPERR_CATCH($$ = $3->in($1), @$); }
;
array_expr : expr
{ $$ = $1->toArray(); }
| array_expr COMMA expr
{ TYPERR_CATCH($$ = $3->append($1), @$); }
comma_expr : expr
{ $$ = vector<MacroValuePtr>{$1}; }
| comma_expr COMMA expr
{ $1.push_back($3); $$ = $1; }
;
%%

View File

@ -26,15 +26,6 @@
#include "MacroDriver.hh"
MacroDriver::MacroDriver()
= default;
MacroDriver::~MacroDriver()
{
for (auto value : values)
delete value;
}
void
MacroDriver::parse(const string &f, const string &fb, const string &modfiletxt,
ostream &out, bool debug, bool no_line_macro_arg, map<string, string> defines,
@ -66,7 +57,7 @@ MacroDriver::parse(const string &f, const string &fb, const string &modfiletxt,
}
file_with_endl << modfiletxt << endl;
lexer = new MacroFlex(&file_with_endl, &out, no_line_macro, path);
lexer = make_unique<MacroFlex>(&file_with_endl, &out, no_line_macro, path);
lexer->set_debug(debug);
Macro::parser parser(*this, out);
@ -78,8 +69,6 @@ MacroDriver::parse(const string &f, const string &fb, const string &modfiletxt,
// Launch macro-processing
parser.parse();
delete lexer;
}
void
@ -93,7 +82,7 @@ string
MacroDriver::replace_vars_in_str(const string &s) const
{
if (s.find("@") == string::npos)
return string(s);
return s;
string retval(s);
smatch name;
@ -107,7 +96,7 @@ MacroDriver::replace_vars_in_str(const string &s) const
regex_search(macro, name, name_regex);
try
{
const MacroValue *mv = nullptr;
MacroValuePtr mv;
bool found_in_func_env = false;
for (unsigned i = func_env.size(); i-- > 0;)
{
@ -122,7 +111,7 @@ MacroDriver::replace_vars_in_str(const string &s) const
if (!found_in_func_env)
mv = get_variable(name.str());
if (mv != nullptr)
if (mv)
{
// mv will equal nullptr if we have
// @#define y = 1
@ -141,28 +130,27 @@ MacroDriver::replace_vars_in_str(const string &s) const
}
void
MacroDriver::set_string_function(const string &name, vector<string> &args, const MacroValue *value)
MacroDriver::set_string_function(const string &name, vector<string> &args, const MacroValuePtr &value)
{
auto *smv = dynamic_cast<const StringMV *>(value);
auto smv = dynamic_pointer_cast<StringMV>(value);
if (!smv)
throw MacroValue::TypeError("The definition of a macro function must evaluate to a string");
env[name] = new FuncMV(*this, args, *(const_cast<StringMV *>(smv)));
env[name] = make_shared<FuncMV>(args, smv->value);
}
const StringMV *
MacroDriver::eval_string_function(const string &name, const MacroValue *args)
MacroValuePtr
MacroDriver::eval_string_function(const string &name, const vector<MacroValuePtr> &args)
{
auto it = env.find(name);
if (it == env.end())
throw UnknownVariable(name);
const auto *fmv = dynamic_cast<const FuncMV *>(env[name]);
auto fmv = dynamic_pointer_cast<FuncMV>(it->second);
if (!fmv)
throw MacroValue::TypeError("You are using " + name + " as if it were a macro function");
vector<string> func_args = fmv->get_args();
if (func_args.size() != (size_t)dynamic_cast<const IntMV *>(args->length())->get_int_value())
if (fmv->args.size() != args.size())
{
cerr << "Macroprocessor: The evaluation of: " << name << " could not be completed" << endl
<< "because the number of arguments provided is different than the number of" << endl
@ -172,11 +160,11 @@ MacroDriver::eval_string_function(const string &name, const MacroValue *args)
int i = 0;
env_t func_env_map;
for (const auto it : func_args)
func_env_map[it] = args->at(i++);
for (const auto it : fmv->args)
func_env_map[it] = args[i++];
func_env.push_back(func_env_map);
StringMV *smv = new StringMV(*this, replace_vars_in_str(fmv->toString()));
auto smv = make_shared<StringMV>(replace_vars_in_str(fmv->toString()));
pop_func_env();
return smv;
}
@ -186,7 +174,7 @@ MacroDriver::push_args_into_func_env(const vector<string> &args)
{
env_t func_env_map;
for (const auto it : args)
func_env_map[it] = NULL;
func_env_map[it] = MacroValuePtr();
func_env.push_back(func_env_map);
}
@ -197,12 +185,12 @@ MacroDriver::pop_func_env()
}
void
MacroDriver::set_variable(const string &name, const MacroValue *value)
MacroDriver::set_variable(const string &name, MacroValuePtr value)
{
env[name] = value;
env[name] = move(value);
}
const MacroValue *
MacroValuePtr
MacroDriver::get_variable(const string &name) const noexcept(false)
{
auto it = env.find(name);
@ -212,13 +200,12 @@ MacroDriver::get_variable(const string &name) const noexcept(false)
}
void
MacroDriver::init_loop(const string &name, const MacroValue *value) noexcept(false)
MacroDriver::init_loop(const string &name, MacroValuePtr value) noexcept(false)
{
const auto *mv1 = dynamic_cast<const ArrayMV<int> *>(value);
const auto *mv2 = dynamic_cast<const ArrayMV<string> *>(value);
if (!mv1 && !mv2)
auto mv = dynamic_pointer_cast<ArrayMV>(value);
if (!mv)
throw MacroValue::TypeError("Argument of @#for loop must be an array expression");
loop_stack.emplace(name, make_pair(value, 0));
loop_stack.emplace(name, move(mv), 0);
}
bool
@ -227,44 +214,26 @@ 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;
int &i = get<2>(loop_stack.top());
auto mv = get<1>(loop_stack.top());
string &name = get<0>(loop_stack.top());
const auto *mv1 = dynamic_cast<const ArrayMV<int> *>(mv);
if (mv1)
if (i >= static_cast<int>(mv->values.size()))
{
if (i >= (int) mv1->values.size())
{
loop_stack.pop();
return false;
}
else
{
env[name] = new IntMV(*this, mv1->values[i++]);
return true;
}
loop_stack.pop();
return false;
}
else
{
const auto *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;
}
env[name] = mv->values[i++];
return true;
}
}
void
MacroDriver::begin_if(const MacroValue *value) noexcept(false)
MacroDriver::begin_if(const MacroValuePtr &value) noexcept(false)
{
const auto *ival = dynamic_cast<const IntMV *>(value);
auto ival = dynamic_pointer_cast<IntMV>(value);
if (!ival)
throw MacroValue::TypeError("Argument of @#if must be an integer");
last_if = (bool) ival->value;
@ -276,11 +245,11 @@ MacroDriver::begin_ifdef(const string &name)
try
{
get_variable(name);
begin_if(new IntMV(*this, 1));
begin_if(make_shared<IntMV>(1));
}
catch (UnknownVariable &)
{
begin_if(new IntMV(*this, 0));
begin_if(make_shared<IntMV>(0));
}
}
@ -290,18 +259,18 @@ MacroDriver::begin_ifndef(const string &name)
try
{
get_variable(name);
begin_if(new IntMV(*this, 0));
begin_if(make_shared<IntMV>(0));
}
catch (UnknownVariable &)
{
begin_if(new IntMV(*this, 1));
begin_if(make_shared<IntMV>(1));
}
}
void
MacroDriver::echo(const Macro::parser::location_type &l, const MacroValue *value) const noexcept(false)
MacroDriver::echo(const Macro::parser::location_type &l, const MacroValuePtr &value) const noexcept(false)
{
const auto *sval = dynamic_cast<const StringMV *>(value);
auto sval = dynamic_pointer_cast<StringMV>(value);
if (!sval)
throw MacroValue::TypeError("Argument of @#echo must be a string");
@ -309,9 +278,9 @@ MacroDriver::echo(const Macro::parser::location_type &l, const MacroValue *value
}
void
MacroDriver::error(const Macro::parser::location_type &l, const MacroValue *value) const noexcept(false)
MacroDriver::error(const Macro::parser::location_type &l, const MacroValuePtr &value) const noexcept(false)
{
const auto *sval = dynamic_cast<const StringMV *>(value);
auto sval = dynamic_pointer_cast<StringMV>(value);
if (!sval)
throw MacroValue::TypeError("Argument of @#error must be a string");
@ -328,7 +297,7 @@ MacroDriver::printvars(const Macro::parser::location_type &l, const bool tostdou
for (const auto & it : env)
{
cout << " ";
const auto *fmv = dynamic_cast<const FuncMV *>(it.second);
auto fmv = dynamic_pointer_cast<FuncMV>(it.second);
if (!fmv)
cout << it.first << " = " << it.second->print() << endl;
else

View File

@ -155,11 +155,8 @@ class MacroDriver
{
friend class MacroValue;
private:
//! Stores all created macro values
set<const MacroValue *> values;
// The map defining an environment
using env_t = map<string, const MacroValue *>;
using env_t = map<string, MacroValuePtr>;
//! Environment: maps macro variables to their values
env_t env;
@ -169,7 +166,7 @@ private:
//! 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;
stack<tuple<string, shared_ptr<ArrayMV>, int>> loop_stack;
public:
//! Exception thrown when value of an unknown variable is requested
class UnknownVariable
@ -181,12 +178,6 @@ public:
}
};
//! Constructor
MacroDriver();
//! Destructor
virtual
~MacroDriver();
//! Starts parsing a file, returns output in out
/*! \param no_line_macro should we omit the @#line statements ? */
void parse(const string &f, const string &fb, const string &modfiletxt, ostream &out, bool debug, bool no_line_macro_arg,
@ -202,7 +193,7 @@ public:
bool no_line_macro;
//! Reference to the lexer
class MacroFlex *lexer;
unique_ptr<MacroFlex> lexer;
//! Used to store the value of the last @#if condition
bool last_if;
@ -214,14 +205,14 @@ public:
string printvars(const Macro::parser::location_type &l, const bool save) const;
//! Set a variable
void set_variable(const string &name, const MacroValue *value);
void set_variable(const string &name, MacroValuePtr value);
//! Replace "@{x}" with the value of x (if it exists in the environment) in a string
//! Check for variable existence first in func_env before checking in env
string replace_vars_in_str(const string &s) const;
//! Set a function with arguments
void set_string_function(const string &name, vector<string> &args, const MacroValue *value);
void set_string_function(const string &name, vector<string> &args, const MacroValuePtr &value);
//! Push function arguments onto func_env stack setting equal to NULL
void push_args_into_func_env(const vector<string> &args);
@ -230,22 +221,21 @@ public:
void pop_func_env();
//! Evaluate a function with arguments
const StringMV *eval_string_function(const string &name, const MacroValue *args);
MacroValuePtr eval_string_function(const string &name, const vector<MacroValuePtr> &args);
//! Get a variable
/*! Returns a newly allocated value (clone of the value stored in environment). */
const MacroValue *get_variable(const string &name) const noexcept(false);
MacroValuePtr get_variable(const string &name) const noexcept(false);
//! 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) noexcept(false);
void init_loop(const string &name, MacroValuePtr value) noexcept(false);
//! 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) noexcept(false);
void begin_if(const MacroValuePtr &value) noexcept(false);
//! Begins an @#ifdef statement
void begin_ifdef(const string &name);
@ -254,10 +244,10 @@ public:
void begin_ifndef(const string &name);
//! Executes @#echo directive
void echo(const Macro::parser::location_type &l, const MacroValue *value) const noexcept(false);
void echo(const Macro::parser::location_type &l, const MacroValuePtr &value) const noexcept(false);
//! Executes @#error directive
void error(const Macro::parser::location_type &l, const MacroValue *value) const noexcept(false);
void error(const Macro::parser::location_type &l, const MacroValuePtr &value) const noexcept(false);
};
#endif // ! MACRO_DRIVER_HH

View File

@ -102,7 +102,7 @@ CONT \\\\
if (string::npos != p)
pathvar.erase(p+1);
string *includepath = NULL;
string *includepath = nullptr;
try
{
includepath = new string(driver.get_variable(pathvar)->toString());
@ -144,7 +144,7 @@ CONT \\\\
if (string::npos != p)
modvarname.erase(p+1);
string *filename = NULL;
string *filename = nullptr;
try
{
filename = new string(driver.get_variable(modvarname)->toString());
@ -204,7 +204,7 @@ CONT \\\\
<STMT,EXPR>{SPC}+ { yylloc->step(); }
<STMT,EXPR>[0-9]+ {
yylval->int_val = atoi(yytext);
yylval->build<int>(atoi(yytext));
return token::INTEGER;
}
<STMT,EXPR>\( { return token::LPAREN; }
@ -231,8 +231,9 @@ CONT \\\\
<STMT,EXPR>length { return token::LENGTH; }
<STMT,EXPR>\"[^\"]*\" {
yylval->string_val = new string(yytext + 1);
yylval->string_val->resize(yylval->string_val->length() - 1);
string s{yytext + 1};
s.resize(s.length() - 1);
yylval->build<string>(s);
return token::STRING;
}
@ -256,7 +257,7 @@ CONT \\\\
<STMT>error { return token::ERROR; }
<STMT,EXPR>[A-Za-z_][A-Za-z0-9_]* {
yylval->string_val = new string(yytext);
yylval->build<string>(yytext);
return token::NAME;
}

View File

@ -21,442 +21,464 @@
#include "MacroDriver.hh"
MacroValue::MacroValue(MacroDriver &driver_arg) : driver(driver_arg)
MacroValuePtr
MacroValue::plus(const MacroValuePtr &mv) noexcept(false)
{
driver.values.insert(this);
throw TypeError("Operator + does not exist for this type");
}
MacroValue::~MacroValue()
= default;
const MacroValue *
MacroValue::operator+() const noexcept(false)
MacroValuePtr
MacroValue::unary_plus() noexcept(false)
{
throw TypeError("Unary operator + does not exist for this type");
}
const MacroValue *
MacroValue::operator-(const MacroValue &mv) const noexcept(false)
MacroValuePtr
MacroValue::minus(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator - does not exist for this type");
}
const MacroValue *
MacroValue::operator-() const noexcept(false)
MacroValuePtr
MacroValue::unary_minus() noexcept(false)
{
throw TypeError("Unary operator - does not exist for this type");
}
const MacroValue *
MacroValue::operator*(const MacroValue &mv) const noexcept(false)
MacroValuePtr
MacroValue::times(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator * does not exist for this type");
}
const MacroValue *
MacroValue::operator/(const MacroValue &mv) const noexcept(false)
MacroValuePtr
MacroValue::divide(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator / does not exist for this type");
}
const MacroValue *
MacroValue::operator<(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
MacroValue::is_less(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator < does not exist for this type");
}
const MacroValue *
MacroValue::operator>(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
MacroValue::is_greater(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator > does not exist for this type");
}
const MacroValue *
MacroValue::operator<=(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
MacroValue::is_less_equal(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator <= does not exist for this type");
}
const MacroValue *
MacroValue::operator>=(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
MacroValue::is_greater_equal(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator >= does not exist for this type");
}
const MacroValue *
MacroValue::operator&&(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
MacroValue::is_different(const MacroValuePtr &mv)
{
if (is_equal(mv)->value)
return make_shared<IntMV>(0);
else
return make_shared<IntMV>(1);
}
shared_ptr<IntMV>
MacroValue::logical_and(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator && does not exist for this type");
}
const MacroValue *
MacroValue::operator||(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
MacroValue::logical_or(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator || does not exist for this type");
}
const MacroValue *
MacroValue::operator!() const noexcept(false)
shared_ptr<IntMV>
MacroValue::logical_not() noexcept(false)
{
throw TypeError("Operator ! does not exist for this type");
}
const MacroValue *
MacroValue::operator[](const MacroValue &mv) const noexcept(false)
MacroValuePtr
MacroValue::subscript(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator [] does not exist for this type");
}
const MacroValue *
MacroValue::length() const noexcept(false)
shared_ptr<IntMV>
MacroValue::length() noexcept(false)
{
throw TypeError("Length not supported for this type");
}
const MacroValue *
MacroValue::at(int i) const noexcept(false)
shared_ptr<IntMV>
MacroValue::in(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Length not supported for this type");
throw TypeError("Second argument of 'in' operator must be an array");
}
const MacroValue *
MacroValue::append(const MacroValue *mv) const noexcept(false)
{
throw TypeError("Cannot append an array at the end of another one. Should use concatenation.");
}
const MacroValue *
MacroValue::in(const MacroValue *array) const noexcept(false)
{
throw TypeError("First argument of 'in' operator cannot be an array");
}
const MacroValue *
MacroValue::new_base_value(MacroDriver &driver, int i)
{
return new IntMV(driver, i);
}
const MacroValue *
MacroValue::new_base_value(MacroDriver &driver, const string &s)
{
return new StringMV(driver, s);
}
IntMV::IntMV(MacroDriver &driver, int value_arg) : MacroValue(driver), value(value_arg)
IntMV::IntMV(int value_arg) : value{value_arg}
{
}
IntMV::~IntMV()
= default;
const MacroValue *
IntMV::operator+(const MacroValue &mv) const noexcept(false)
MacroValuePtr
IntMV::plus(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of + operator");
return new IntMV(driver, value + mv2->value);
return make_shared<IntMV>(value + mv2->value);
}
const MacroValue *
IntMV::operator+() const noexcept(false)
MacroValuePtr
IntMV::unary_plus() noexcept(false)
{
return this;
return make_shared<IntMV>(value);
}
const MacroValue *
IntMV::operator-(const MacroValue &mv) const noexcept(false)
MacroValuePtr
IntMV::minus(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of - operator");
return new IntMV(driver, value - mv2->value);
return make_shared<IntMV>(value - mv2->value);
}
const MacroValue *
IntMV::operator-() const noexcept(false)
MacroValuePtr
IntMV::unary_minus() noexcept(false)
{
return new IntMV(driver, -value);
return make_shared<IntMV>(-value);
}
const MacroValue *
IntMV::operator*(const MacroValue &mv) const noexcept(false)
MacroValuePtr
IntMV::times(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of * operator");
return new IntMV(driver, value * mv2->value);
return make_shared<IntMV>(value * mv2->value);
}
const MacroValue *
IntMV::operator/(const MacroValue &mv) const noexcept(false)
MacroValuePtr
IntMV::divide(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of / operator");
return new IntMV(driver, value / mv2->value);
if (!mv2->value)
throw DivisionByZeroError();
return make_shared<IntMV>(value / mv2->value);
}
const MacroValue *
IntMV::operator<(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
IntMV::is_less(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of < operator");
return new IntMV(driver, value < mv2->value);
return make_shared<IntMV>(value < mv2->value);
}
const MacroValue *
IntMV::operator>(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
IntMV::is_greater(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of > operator");
return new IntMV(driver, value > mv2->value);
return make_shared<IntMV>(value > mv2->value);
}
const MacroValue *
IntMV::operator<=(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
IntMV::is_less_equal(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of <= operator");
return new IntMV(driver, value <= mv2->value);
return make_shared<IntMV>(value <= mv2->value);
}
const MacroValue *
IntMV::operator>=(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
IntMV::is_greater_equal(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of >= operator");
return new IntMV(driver, value >= mv2->value);
return make_shared<IntMV>(value >= mv2->value);
}
const MacroValue *
IntMV::operator==(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
IntMV::is_equal(const MacroValuePtr &mv)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
return new IntMV(driver, 0);
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
return make_shared<IntMV>(0);
else
return new IntMV(driver, value == mv2->value);
return make_shared<IntMV>(value == mv2->value);
}
const MacroValue *
IntMV::operator!=(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
IntMV::logical_and(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
return new IntMV(driver, 1);
else
return new IntMV(driver, value != mv2->value);
}
const MacroValue *
IntMV::operator&&(const MacroValue &mv) const noexcept(false)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of && operator");
return new IntMV(driver, value && mv2->value);
return make_shared<IntMV>(value && mv2->value);
}
const MacroValue *
IntMV::operator||(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
IntMV::logical_or(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const IntMV *>(&mv);
if (mv2 == nullptr)
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of || operator");
return new IntMV(driver, value || mv2->value);
return make_shared<IntMV>(value || mv2->value);
}
const MacroValue *
IntMV::operator!() const noexcept(false)
shared_ptr<IntMV>
IntMV::logical_not() noexcept(false)
{
return new IntMV(driver, !value);
return make_shared<IntMV>(!value);
}
string
IntMV::toString() const
IntMV::toString()
{
return to_string(value);
}
string
IntMV::print() const
IntMV::print()
{
return toString();
}
const MacroValue *
IntMV::toArray() const
{
vector<int> v;
v.push_back(value);
return new ArrayMV<int>(driver, v);
}
const MacroValue *
IntMV::append(const MacroValue *array) const noexcept(false)
{
const auto *array2 = dynamic_cast<const ArrayMV<int> *>(array);
if (array2 == nullptr)
throw TypeError("Type mismatch for append operation");
vector<int> v(array2->values);
v.push_back(value);
return new ArrayMV<int>(driver, v);
}
const MacroValue *
IntMV::in(const MacroValue *array) const noexcept(false)
{
const auto *array2 = dynamic_cast<const ArrayMV<int> *>(array);
if (array2 == nullptr)
throw TypeError("Type mismatch for 'in' operator");
int result = 0;
for (int v : array2->values)
if (v == value)
{
result = 1;
break;
}
return new IntMV(driver, result);
}
const MacroValue *
IntMV::new_range(MacroDriver &driver, const MacroValue *mv1, const MacroValue *mv2) noexcept(false)
{
const auto *mv1i = dynamic_cast<const IntMV *>(mv1);
const auto *mv2i = dynamic_cast<const IntMV *>(mv2);
if (mv1i == nullptr || mv2i == nullptr)
throw TypeError("Arguments of range operator (:) must be integers");
int v1 = mv1i->value;
int v2 = mv2i->value;
vector<int> result;
for (; v1 <= v2; v1++)
result.push_back(v1);
return new ArrayMV<int>(driver, result);
}
StringMV::StringMV(MacroDriver &driver, string value_arg) :
MacroValue(driver), value(move(value_arg))
StringMV::StringMV(string value_arg) :
value{move(value_arg)}
{
}
StringMV::~StringMV()
= default;
const MacroValue *
StringMV::operator+(const MacroValue &mv) const noexcept(false)
MacroValuePtr
StringMV::plus(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const StringMV *>(&mv);
if (mv2 != nullptr)
return new StringMV(driver, value + mv2->value);
const auto *mv3 = dynamic_cast<const FuncMV *>(&mv);
if (mv3 != nullptr)
return new StringMV(driver, value + mv3->toString());
auto mv2 = dynamic_pointer_cast<StringMV>(mv);
if (mv2)
return make_shared<StringMV>(value + mv2->value);
throw TypeError("Type mismatch for operands of + operator");
}
const MacroValue *
StringMV::operator==(const MacroValue &mv) const noexcept(false)
shared_ptr<IntMV>
StringMV::is_equal(const MacroValuePtr &mv)
{
const auto *mv2 = dynamic_cast<const StringMV *>(&mv);
if (mv2 == nullptr)
return new IntMV(driver, 0);
auto mv2 = dynamic_pointer_cast<StringMV>(mv);
if (mv2)
return make_shared<IntMV>(value == mv2->value);
else
return new IntMV(driver, value == mv2->value);
return make_shared<IntMV>(0);
}
const MacroValue *
StringMV::operator!=(const MacroValue &mv) const noexcept(false)
MacroValuePtr
StringMV::subscript(const MacroValuePtr &mv) noexcept(false)
{
const auto *mv2 = dynamic_cast<const StringMV *>(&mv);
if (mv2 == nullptr)
return new IntMV(driver, 1);
else
return new IntMV(driver, value != mv2->value);
}
const MacroValue *
StringMV::operator[](const MacroValue &mv) const noexcept(false)
{
const auto *mv2 = dynamic_cast<const ArrayMV<int> *>(&mv);
if (mv2 == nullptr)
throw TypeError("Expression inside [] must be an integer array");
string result;
for (int v : mv2->values)
{
if (v < 1 || v > (int) value.length())
throw OutOfBoundsError();
char c = value.at(v - 1);
result.append(1, c);
}
return new StringMV(driver, result);
auto copy_element = [&](int i) {
if (i < 1 || i > static_cast<int>(value.length()))
throw OutOfBoundsError();
result.append(1, value.at(i - 1));
};
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
auto mv3 = dynamic_pointer_cast<ArrayMV>(mv);
if (mv2)
copy_element(mv2->value);
else if (mv3)
for (auto &v : mv3->values)
{
auto v2 = dynamic_pointer_cast<IntMV>(v);
if (!v2)
throw TypeError("Expression inside [] must be an integer or an integer array");
copy_element(v2->value);
}
else
throw TypeError("Expression inside [] must be an integer or an integer array");
return make_shared<StringMV>(result);
}
string
StringMV::toString() const
StringMV::toString()
{
return value;
}
string
StringMV::print() const
StringMV::print()
{
return "'" + value + "'";
}
const MacroValue *
StringMV::toArray() const
FuncMV::FuncMV(vector<string> args_arg, string body_arg) :
args{move(args_arg)}, body{move(body_arg)}
{
vector<string> v;
v.push_back(value);
return new ArrayMV<string>(driver, v);
}
const MacroValue *
StringMV::append(const MacroValue *array) const noexcept(false)
shared_ptr<IntMV>
FuncMV::is_equal(const MacroValuePtr &mv)
{
const auto *array2 = dynamic_cast<const ArrayMV<string> *>(array);
if (array2 == nullptr)
throw TypeError("Type mismatch for append operation");
auto mv2 = dynamic_pointer_cast<FuncMV>(mv);
if (!mv2 || body != mv2->body)
return make_shared<IntMV>(0);
vector<string> v(array2->values);
v.push_back(value);
return new ArrayMV<string>(driver, v);
if (args.size() == mv2->args.size())
for (size_t i = 0; i < args.size(); i++)
if (args[i] != mv2->args[i])
return make_shared<IntMV>(0);
return make_shared<IntMV>(1);
}
const MacroValue *
StringMV::in(const MacroValue *array) const noexcept(false)
{
const auto *array2 = dynamic_cast<const ArrayMV<string> *>(array);
if (array2 == nullptr)
throw TypeError("Type mismatch for 'in' operator");
int result = 0;
for (const auto &v : array2->values)
if (v == value)
{
result = 1;
break;
}
return new IntMV(driver, result);
}
template<>
string
ArrayMV<int>::print() const
FuncMV::toString()
{
return body;
}
string
FuncMV::print()
{
bool comma_flag = false;
string retval = "(";
for (auto it : args)
{
if (comma_flag)
retval += ", ";
retval += it;
comma_flag = true;
}
retval += ")";
return retval + " = '" + body + "'";
}
ArrayMV::ArrayMV(vector<MacroValuePtr> values_arg) : values{move(values_arg)}
{
}
MacroValuePtr
ArrayMV::plus(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<ArrayMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of + operator");
vector<MacroValuePtr> values_copy{values};
values_copy.insert(values_copy.end(), mv2->values.begin(), mv2->values.end());
return make_shared<ArrayMV>(values_copy);
}
MacroValuePtr
ArrayMV::minus(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<ArrayMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of - operator");
/* Highly inefficient algorithm for computing set difference
(but vector<T> is not suited for that...) */
vector<MacroValuePtr> new_values;
for (auto &it : values)
{
auto it2 = mv2->values.cbegin();
for (; it2 != mv2->values.cend(); ++it2)
if (it->is_different(*it2)->value)
break;
if (it2 == mv2->values.cend())
new_values.push_back(it);
}
return make_shared<ArrayMV>(new_values);
}
shared_ptr<IntMV>
ArrayMV::is_equal(const MacroValuePtr &mv)
{
auto mv2 = dynamic_pointer_cast<ArrayMV>(mv);
if (!mv2 || values.size() != mv2->values.size())
return make_shared<IntMV>(0);
auto it = values.cbegin();
auto it2 = mv2->values.cbegin();
while (it != values.cend())
{
if ((*it)->is_different(*it2)->value)
return make_shared<IntMV>(0);
++it;
++it2;
}
return make_shared<IntMV>(1);
}
MacroValuePtr
ArrayMV::subscript(const MacroValuePtr &mv) noexcept(false)
{
vector<MacroValuePtr> result;
auto copy_element = [&](int i) {
if (i < 1 || i > static_cast<int>(values.size()))
throw OutOfBoundsError();
result.push_back(values[i - 1]);
};
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
auto mv3 = dynamic_pointer_cast<ArrayMV>(mv);
if (mv2)
copy_element(mv2->value);
else if (mv3)
for (auto &v : mv3->values)
{
auto v2 = dynamic_pointer_cast<IntMV>(v);
if (!v2)
throw TypeError("Expression inside [] must be an integer or an integer array");
copy_element(v2->value);
}
else
throw TypeError("Expression inside [] must be an integer or an integer array");
if (result.size() > 1 || result.size() == 0)
return make_shared<ArrayMV>(result);
else
return result[0];
}
string
ArrayMV::toString()
{
ostringstream ss;
for (auto &v : values)
ss << v->toString();
return ss.str();
}
shared_ptr<IntMV>
ArrayMV::length() noexcept(false)
{
return make_shared<IntMV>(values.size());
}
string
ArrayMV::print()
{
ostringstream ss;
ss << "[";
@ -466,104 +488,40 @@ ArrayMV<int>::print() const
if (it != values.begin())
ss << ", ";
ss << *it;
ss << (*it)->print();
}
ss << "]";
return ss.str();
}
template<>
string
ArrayMV<string>::print() const
shared_ptr<ArrayMV>
ArrayMV::append(MacroValuePtr mv) noexcept(false)
{
ostringstream ss;
ss << "{";
for (auto it = values.begin();
it != values.end(); it++)
{
if (it != values.begin())
ss << ", ";
ss << "'" << *it << "'";
}
ss << "}";
return ss.str();
vector<MacroValuePtr> v{values};
v.push_back(move(mv));
return make_shared<ArrayMV>(v);
}
FuncMV::FuncMV(MacroDriver &driver, vector<string> &args_arg, StringMV &value_arg) :
MacroValue(driver), args(args_arg), value(value_arg)
shared_ptr<IntMV>
ArrayMV::in(const MacroValuePtr &mv) noexcept(false)
{
for (auto &v : values)
if (v->is_equal(mv)->value)
return make_shared<IntMV>(1);
return make_shared<IntMV>(0);
}
FuncMV::~FuncMV()
= default;
const MacroValue *
FuncMV::operator+(const MacroValue &mv) const noexcept(false)
shared_ptr<ArrayMV>
ArrayMV::range(const MacroValuePtr &mv1, const MacroValuePtr &mv2) noexcept(false)
{
const auto *mv2 = dynamic_cast<const FuncMV *>(&mv);
if (mv2 != nullptr)
return value + mv2->value;
auto mv1i = dynamic_pointer_cast<IntMV>(mv1);
auto mv2i = dynamic_pointer_cast<IntMV>(mv2);
if (!mv1i || !mv2i)
throw TypeError("Arguments of range operator (:) must be integers");
const auto *mv3 = dynamic_cast<const StringMV *>(&mv);
if (mv3 != nullptr)
return value + *mv3;
throw TypeError("Type mismatch for operands of + operator");
}
const MacroValue *
FuncMV::operator==(const MacroValue &mv) const noexcept(false)
{
const auto *mv2 = dynamic_cast<const FuncMV *>(&mv);
if (mv2 == nullptr)
return new IntMV(driver, 0);
if (value != mv2->value)
return new IntMV(driver, 0);
if (args.size() == mv2->args.size())
for (size_t i = 0; i < args.size(); i++)
if (args[i] != mv2->args[i])
return new IntMV(driver, 0);
return new IntMV(driver, 1);
}
const MacroValue *
FuncMV::operator!=(const MacroValue &mv) const noexcept(false)
{
if (dynamic_cast<const IntMV *>(*this == mv)->value == 1)
return new IntMV(driver, 0);
return new IntMV(driver, 1);
}
string
FuncMV::toString() const
{
return value.toString();
}
string
FuncMV::print() const
{
bool comma_flag = false;
string retval = "(";
for (const auto it : args)
{
if (comma_flag)
retval += ", ";
retval += it;
comma_flag = true;
}
retval += ")";
return retval + " = '" + value.toString() + "'";
}
const MacroValue *
FuncMV::toArray() const
{
vector<string> v;
v.push_back(value.toString());
return new ArrayMV<string>(driver, v);
vector<MacroValuePtr> result;
for (int v1 = mv1i->value, v2 = mv2i->value; v1 <= v2; v1++)
result.push_back(make_shared<IntMV>(v1));
return make_shared<ArrayMV>(result);
}

View File

@ -24,24 +24,29 @@
#include <utility>
#include <vector>
#include <sstream>
#include <memory>
using namespace std;
class MacroDriver;
class MacroValue;
class IntMV;
class ArrayMV;
using MacroValuePtr = shared_ptr<MacroValue>;
//! Base class for representing values in macro language
/*! All its derived types are immutable, so that we don't need to carry const
qualifiers everywhere in the code */
class MacroValue
{
protected:
//! Reference to enclosing MacroDriver
MacroDriver &driver;
public:
//! Exception thrown when type error occurs in macro language
class TypeError
{
public:
const string message;
TypeError(string message_arg) : message(move(message_arg))
TypeError(string message_arg) : message{move(message_arg)}
{
};
};
@ -49,345 +54,161 @@ public:
class OutOfBoundsError
{
};
MacroValue(MacroDriver &driver_arg);
virtual
~MacroValue();
//! Exception thrown when dividing by zero
class DivisionByZeroError
{
};
//! Applies + operator
virtual const MacroValue *operator+(const MacroValue &mv) const noexcept(false) = 0;
virtual MacroValuePtr plus(const MacroValuePtr &mv) noexcept(false);
//! Applies unary + operator
virtual const MacroValue *operator+() const noexcept(false);
virtual MacroValuePtr unary_plus() noexcept(false);
//! Applies - operator
virtual const MacroValue *operator-(const MacroValue &mv) const noexcept(false);
virtual MacroValuePtr minus(const MacroValuePtr &mv) noexcept(false);
//! Applies unary - operator
virtual const MacroValue *operator-() const noexcept(false);
virtual MacroValuePtr unary_minus() noexcept(false);
//! Applies * operator
virtual const MacroValue *operator*(const MacroValue &mv) const noexcept(false);
virtual MacroValuePtr times(const MacroValuePtr &mv) noexcept(false);
//! Applies / operator
virtual const MacroValue *operator/(const MacroValue &mv) const noexcept(false);
virtual MacroValuePtr divide(const MacroValuePtr &mv) noexcept(false);
//! Less comparison
/*! Returns an IntMV, equal to 0 or 1 */
virtual const MacroValue *operator<(const MacroValue &mv) const noexcept(false);
virtual shared_ptr<IntMV> is_less(const MacroValuePtr &mv) noexcept(false);
//! Greater comparision
/*! Returns an IntMV, equal to 0 or 1 */
virtual const MacroValue *operator>(const MacroValue &mv) const noexcept(false);
virtual shared_ptr<IntMV> is_greater(const MacroValuePtr &mv) noexcept(false);
//! Less or equal comparison
/*! Returns an IntMV, equal to 0 or 1 */
virtual const MacroValue *operator<=(const MacroValue &mv) const noexcept(false);
virtual shared_ptr<IntMV> is_less_equal(const MacroValuePtr &mv) noexcept(false);
//! Greater or equal comparison
/*! Returns an IntMV, equal to 0 or 1 */
virtual const MacroValue *operator>=(const MacroValue &mv) const noexcept(false);
virtual shared_ptr<IntMV> is_greater_equal(const MacroValuePtr &mv) noexcept(false);
//! Equal comparison
/*! Returns an IntMV, equal to 0 or 1 */
virtual const MacroValue *operator==(const MacroValue &mv) const noexcept(false) = 0;
virtual shared_ptr<IntMV> is_equal(const MacroValuePtr &mv) = 0;
//! Not equal comparison
/*! Returns an IntMV, equal to 0 or 1 */
virtual const MacroValue *operator!=(const MacroValue &mv) const noexcept(false) = 0;
shared_ptr<IntMV> is_different(const MacroValuePtr &mv);
//! Applies && operator
virtual const MacroValue *operator&&(const MacroValue &mv) const noexcept(false);
virtual shared_ptr<IntMV> logical_and(const MacroValuePtr &mv) noexcept(false);
//! Applies || operator
virtual const MacroValue *operator||(const MacroValue &mv) const noexcept(false);
virtual shared_ptr<IntMV> logical_or(const MacroValuePtr &mv) noexcept(false);
//! Applies unary ! operator
virtual const MacroValue *operator!() const noexcept(false);
virtual shared_ptr<IntMV> logical_not() noexcept(false);
//! Applies [] operator
virtual const MacroValue *operator[](const MacroValue &mv) const noexcept(false);
virtual MacroValuePtr subscript(const MacroValuePtr &mv) noexcept(false);
//! Converts value to string
virtual string toString() const = 0;
virtual string toString() = 0;
//! Converts value to be printed
virtual string print() const = 0;
//! Converts value to array form
virtual const MacroValue *toArray() const = 0;
virtual string print() = 0;
//! Gets length
virtual const MacroValue *length() const noexcept(false);
//! Returns element at location i
virtual const MacroValue *at(int i) const noexcept(false);
//! Appends value at the end of an array
/*! The argument must be an array. */
virtual const MacroValue *append(const MacroValue *array) const noexcept(false);
virtual shared_ptr<IntMV> length() noexcept(false);
//! Applies "in" operator
/*! The argument must be an array. Returns an IntMV, equal to 0 or 1 */
virtual const MacroValue *in(const MacroValue *array) const noexcept(false);
//! Returns a new IntMV
/*! Necessary for ArrayMV::operator[] (template issue) */
static const MacroValue *new_base_value(MacroDriver &driver, int i);
//! Returns a new StringMV
/*! Necessary for ArrayMV::operator[] (template issue) */
static const MacroValue *new_base_value(MacroDriver &driver, const string &s);
/*! The argument is the element to be tested for inclusion. Returns an IntMV, equal to 0 or 1 */
virtual shared_ptr<IntMV> in(const MacroValuePtr &mv) noexcept(false);
};
//! Represents an integer value in macro language
class IntMV : public MacroValue
{
friend class StringMV;
friend class FuncMV;
friend class MacroDriver;
private:
public:
IntMV(int value_arg);
//! Underlying integer value
const int value;
public:
IntMV(MacroDriver &driver, int value_arg);
~IntMV() override;
//! Computes arithmetic addition
const MacroValue *operator+(const MacroValue &mv) const noexcept(false) override;
MacroValuePtr plus(const MacroValuePtr &mv) noexcept(false) override;
//! Unary plus
/*! Returns itself */
const MacroValue *operator+() const noexcept(false) override;
MacroValuePtr unary_plus() noexcept(false) override;
//! Computes arithmetic substraction
const MacroValue *operator-(const MacroValue &mv) const noexcept(false) override;
MacroValuePtr minus(const MacroValuePtr &mv) noexcept(false) override;
//! Computes opposite
const MacroValue *operator-() const noexcept(false) override;
MacroValuePtr unary_minus() noexcept(false) override;
//! Computes arithmetic multiplication
const MacroValue *operator*(const MacroValue &mv) const noexcept(false) override;
MacroValuePtr times(const MacroValuePtr &mv) noexcept(false) override;
//! Computes arithmetic division
const MacroValue *operator/(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator<(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator>(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator<=(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator>=(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator==(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator!=(const MacroValue &mv) const noexcept(false) override;
MacroValuePtr divide(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_less(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_greater(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_less_equal(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_greater_equal(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_equal(const MacroValuePtr &mv) override;
//! Computes logical and
const MacroValue *operator&&(const MacroValue &mv) const noexcept(false) override;
shared_ptr<IntMV> logical_and(const MacroValuePtr &mv) noexcept(false) override;
//! Computes logical or
const MacroValue *operator||(const MacroValue &mv) const noexcept(false) override;
shared_ptr<IntMV> logical_or(const MacroValuePtr &mv) noexcept(false) override;
//! Computes logical negation
const MacroValue *operator!() const noexcept(false) override;
string toString() const override;
string print() const override;
//! Converts value to array form
/*! Returns an integer array containing a single value */
const MacroValue *toArray() const override;
//! Appends value at the end of an array
/*! The first argument must be an integer array. */
const MacroValue *append(const MacroValue *array) const noexcept(false) override;
const MacroValue *in(const MacroValue *array) const noexcept(false) override;
//! Creates a integer range
/*! Arguments must be of type IntMV.
Returns an integer array containing all integers between mv1 and mv2.
If mv2 < mv1, returns an empty range (for consistency with MATLAB).
*/
static const MacroValue *new_range(MacroDriver &driver, const MacroValue *mv1, const MacroValue *mv2) noexcept(false);
inline int
get_int_value() const
{
return value;
};
shared_ptr<IntMV> logical_not() noexcept(false) override;
string toString() override;
string print() override;
};
//! Represents a string value in macro language
class StringMV : public MacroValue
{
friend class MacroDriver;
private:
public:
StringMV(string value_arg);
//! Underlying string value
const string value;
public:
StringMV(MacroDriver &driver, string value_arg);
~StringMV() override;
//! Computes string concatenation
const MacroValue *operator+(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator==(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator!=(const MacroValue &mv) const noexcept(false) override;
MacroValuePtr plus(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_equal(const MacroValuePtr &mv) override;
//! Subscripting operator
/*! Argument must be an ArrayMV<int>. Indexes begin at 1. Returns a StringMV. */
const MacroValue *operator[](const MacroValue &mv) const noexcept(false) override;
MacroValuePtr subscript(const MacroValuePtr &mv) noexcept(false) override;
//! Returns underlying string value
string toString() const override;
string print() const override;
//! Converts value to array form
/*! Returns a string array containing a single value */
const MacroValue *toArray() const override;
//! Appends value at the end of an array
/*! The first argument must be a string array. Returns a string array. */
const MacroValue *append(const MacroValue *array) const noexcept(false) override;
const MacroValue *in(const MacroValue *array) const noexcept(false) override;
string toString() override;
string print() override;
};
class FuncMV : public MacroValue
{
friend class MacroDriver;
private:
public:
FuncMV(vector<string> args, string body_arg);
//! Function args & body
const vector<string> args;
const StringMV &value;
public:
FuncMV(MacroDriver &driver, vector<string> &args, StringMV &value_arg);
const string body;
~FuncMV() override;
//! Computes string concatenation
const MacroValue *operator+(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator==(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator!=(const MacroValue &mv) const noexcept(false) override;
string toString() const override;
string print() const override;
const MacroValue *toArray() const override;
inline const vector<string> &
get_args() const
{
return args;
}
shared_ptr<IntMV> is_equal(const MacroValuePtr &mv) override;
string toString() override;
string print() override;
};
//! Represents an array in macro language
template<typename T>
class ArrayMV : public MacroValue
{
friend class IntMV;
friend class StringMV;
friend class ArrayMV<string>; // Necessary for operator[] to access values of integer array when subscripting a string array
friend class MacroDriver;
private:
//! Underlying vector
const vector<T> values;
public:
ArrayMV(MacroDriver &driver, vector<T> values_arg);
~ArrayMV() override;
ArrayMV(vector<MacroValuePtr> values_arg);
//! Underlying vector
const vector<MacroValuePtr> values;
//! Computes array concatenation
/*! Both array must be of same type */
const MacroValue *operator+(const MacroValue &mv) const noexcept(false) override;
MacroValuePtr plus(const MacroValuePtr &mv) noexcept(false) override;
//! Returns an array in which the elements of the second array have been removed from the first
/*! It is close to a set difference operation, except that if an element appears two times in the first array, it will also be in the returned value (provided it is not in the second array) */
const MacroValue *operator-(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator==(const MacroValue &mv) const noexcept(false) override;
const MacroValue *operator!=(const MacroValue &mv) const noexcept(false) override;
MacroValuePtr minus(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_equal(const MacroValuePtr &mv) override;
//! Subscripting operator
/*! Argument must be an ArrayMV<int>. Indexes begin at 1.
If argument is a one-element array, returns an IntMV or StringMV.
Otherwise returns an array. */
const MacroValue *operator[](const MacroValue &mv) const noexcept(false) override;
MacroValuePtr subscript(const MacroValuePtr &mv) noexcept(false) override;
//! Returns a string containing the concatenation of string representations of elements
string toString() const override;
string print() const override;
//! Returns itself
const MacroValue *toArray() const override;
string toString() override;
string print() override;
//! Gets length
const MacroValue *length() const noexcept(false) override;
const MacroValue *at(int i) const noexcept(false) override;
shared_ptr<IntMV> length() noexcept(false) override;
shared_ptr<ArrayMV> append(MacroValuePtr mv) noexcept(false);
shared_ptr<IntMV> in(const MacroValuePtr &mv) noexcept(false) override;
/*! Arguments must be of type IntMV.
Returns an integer array containing all integers between mv1 and mv2.
If mv2 < mv1, returns an empty range (for consistency with MATLAB).
*/
static shared_ptr<ArrayMV> range(const MacroValuePtr &mv1, const MacroValuePtr &mv2) noexcept(false);
};
template<typename T>
ArrayMV<T>::ArrayMV(MacroDriver &driver, vector<T> values_arg) : MacroValue(driver), values(move(values_arg))
{
}
template<typename T>
ArrayMV<T>::~ArrayMV()
= default;
template<typename T>
const MacroValue *
ArrayMV<T>::operator+(const MacroValue &mv) const noexcept(false)
{
const auto *mv2 = dynamic_cast<const ArrayMV<T> *>(&mv);
if (mv2 == nullptr)
throw TypeError("Type mismatch for operands of + operator");
vector<T> values_copy(values);
values_copy.insert(values_copy.end(), mv2->values.begin(), mv2->values.end());
return new ArrayMV<T>(driver, values_copy);
}
template<typename T>
const MacroValue *
ArrayMV<T>::operator-(const MacroValue &mv) const noexcept(false)
{
const auto *mv2 = dynamic_cast<const ArrayMV<T> *>(&mv);
if (mv2 == nullptr)
throw TypeError("Type mismatch for operands of - operator");
/* Highly inefficient algorithm for computing set difference
(but vector<T> is not suited for that...) */
vector<T> new_values;
for (auto it = values.begin();
it != values.end(); it++)
{
typename vector<T>::const_iterator it2;
for (it2 = mv2->values.begin(); it2 != mv2->values.end(); it2++)
if (*it == *it2)
break;
if (it2 == mv2->values.end())
new_values.push_back(*it);
}
return new ArrayMV<T>(driver, new_values);
}
template<typename T>
const MacroValue *
ArrayMV<T>::operator==(const MacroValue &mv) const noexcept(false)
{
const auto *mv2 = dynamic_cast<const ArrayMV<T> *>(&mv);
if (mv2 == nullptr)
return new IntMV(driver, 0);
else
return new IntMV(driver, values == mv2->values);
}
template<typename T>
const MacroValue *
ArrayMV<T>::operator!=(const MacroValue &mv) const noexcept(false)
{
const auto *mv2 = dynamic_cast<const ArrayMV<T> *>(&mv);
if (mv2 == nullptr)
return new IntMV(driver, 1);
else
return new IntMV(driver, values != mv2->values);
}
template<typename T>
const MacroValue *
ArrayMV<T>::operator[](const MacroValue &mv) const noexcept(false)
{
const auto *mv2 = dynamic_cast<const ArrayMV<int> *>(&mv);
if (mv2 == nullptr)
throw TypeError("Expression inside [] must be an integer array");
vector<T> result;
for (int value : mv2->values)
{
if (value < 1 || value > (int) values.size())
throw OutOfBoundsError();
result.push_back(values[value - 1]);
}
if (result.size() > 1 || result.size() == 0)
return new ArrayMV<T>(driver, result);
else
return MacroValue::new_base_value(driver, result[0]);
}
template<typename T>
string
ArrayMV<T>::toString() const
{
ostringstream ss;
for (auto it = values.begin();
it != values.end(); it++)
ss << *it;
return ss.str();
}
template<typename T>
const MacroValue *
ArrayMV<T>::toArray() const
{
return this;
}
template<typename T>
const MacroValue *
ArrayMV<T>::length() const noexcept(false)
{
return new IntMV(driver, values.size());
}
template<typename T>
const MacroValue *
ArrayMV<T>::at(int i) const noexcept(false)
{
return new_base_value(driver, values[i]);
}
#endif