From 5f1465f9d17096baec1df4f29b48b2edd95681cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= Date: Tue, 24 Jul 2018 16:46:17 +0200 Subject: [PATCH] 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 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 --- src/macro/MacroBison.yy | 107 +++--- src/macro/MacroDriver.cc | 113 +++---- src/macro/MacroDriver.hh | 32 +- src/macro/MacroFlex.ll | 13 +- src/macro/MacroValue.cc | 680 ++++++++++++++++++--------------------- src/macro/MacroValue.hh | 351 +++++--------------- 6 files changed, 514 insertions(+), 782 deletions(-) diff --git a/src/macro/MacroBison.yy b/src/macro/MacroBison.yy index aac8aaca..ea6bae15 100644 --- a/src/macro/MacroBison.yy +++ b/src/macro/MacroBison.yy @@ -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 *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 INTEGER -%token NAME STRING +%token INTEGER +%token NAME STRING %left COMMA %left LOGICAL_OR @@ -90,8 +84,9 @@ class MacroDriver; %left UMINUS UPLUS EXCLAMATION %left LBRACKET -%type func_args -%type expr array_expr +%type > func_args +%type expr +%type > 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(); $$->push_back(*$1); delete $1; } + { $$ = vector{$1}; } | func_args COMMA NAME - { $$->push_back(*$3); delete $3; } + { $1.push_back($3); $$ = $1; } ; expr : INTEGER - { $$ = new IntMV(driver, $1); } + { $$ = make_shared($1); } | STRING - { $$ = new StringMV(driver, driver.replace_vars_in_str(*$1)); delete $1; } + { $$ = make_shared(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($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($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{$1}; } + | comma_expr COMMA expr + { $1.push_back($3); $$ = $1; } ; %% diff --git a/src/macro/MacroDriver.cc b/src/macro/MacroDriver.cc index 9efff6fd..61f412c9 100644 --- a/src/macro/MacroDriver.cc +++ b/src/macro/MacroDriver.cc @@ -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 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(&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 &args, const MacroValue *value) +MacroDriver::set_string_function(const string &name, vector &args, const MacroValuePtr &value) { - auto *smv = dynamic_cast(value); + auto smv = dynamic_pointer_cast(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(smv))); + env[name] = make_shared(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 &args) { auto it = env.find(name); if (it == env.end()) throw UnknownVariable(name); - const auto *fmv = dynamic_cast(env[name]); + auto fmv = dynamic_pointer_cast(it->second); if (!fmv) throw MacroValue::TypeError("You are using " + name + " as if it were a macro function"); - vector func_args = fmv->get_args(); - if (func_args.size() != (size_t)dynamic_cast(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(replace_vars_in_str(fmv->toString())); pop_func_env(); return smv; } @@ -186,7 +174,7 @@ MacroDriver::push_args_into_func_env(const vector &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 *>(value); - const auto *mv2 = dynamic_cast *>(value); - if (!mv1 && !mv2) + auto mv = dynamic_pointer_cast(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 *>(mv); - if (mv1) + if (i >= static_cast(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 *>(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(value); + auto ival = dynamic_pointer_cast(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(1)); } catch (UnknownVariable &) { - begin_if(new IntMV(*this, 0)); + begin_if(make_shared(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(0)); } catch (UnknownVariable &) { - begin_if(new IntMV(*this, 1)); + begin_if(make_shared(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(value); + auto sval = dynamic_pointer_cast(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(value); + auto sval = dynamic_pointer_cast(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(it.second); + auto fmv = dynamic_pointer_cast(it.second); if (!fmv) cout << it.first << " = " << it.second->print() << endl; else diff --git a/src/macro/MacroDriver.hh b/src/macro/MacroDriver.hh index 8c659fd1..db05b06e 100644 --- a/src/macro/MacroDriver.hh +++ b/src/macro/MacroDriver.hh @@ -155,11 +155,8 @@ class MacroDriver { friend class MacroValue; private: - //! Stores all created macro values - set values; - // The map defining an environment - using env_t = map; + using env_t = map; //! 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>> loop_stack; + stack, 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 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 &args, const MacroValue *value); + void set_string_function(const string &name, vector &args, const MacroValuePtr &value); //! Push function arguments onto func_env stack setting equal to NULL void push_args_into_func_env(const vector &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 &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 diff --git a/src/macro/MacroFlex.ll b/src/macro/MacroFlex.ll index 5a271774..2b3b34b4 100644 --- a/src/macro/MacroFlex.ll +++ b/src/macro/MacroFlex.ll @@ -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 \\\\ {SPC}+ { yylloc->step(); } [0-9]+ { - yylval->int_val = atoi(yytext); + yylval->build(atoi(yytext)); return token::INTEGER; } \( { return token::LPAREN; } @@ -231,8 +231,9 @@ CONT \\\\ length { return token::LENGTH; } \"[^\"]*\" { - 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(s); return token::STRING; } @@ -256,7 +257,7 @@ CONT \\\\ error { return token::ERROR; } [A-Za-z_][A-Za-z0-9_]* { - yylval->string_val = new string(yytext); + yylval->build(yytext); return token::NAME; } diff --git a/src/macro/MacroValue.cc b/src/macro/MacroValue.cc index aee8f55e..c44f80c1 100644 --- a/src/macro/MacroValue.cc +++ b/src/macro/MacroValue.cc @@ -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 +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 +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 +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 +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 +MacroValue::is_different(const MacroValuePtr &mv) +{ + if (is_equal(mv)->value) + return make_shared(0); + else + return make_shared(1); +} + +shared_ptr +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 +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 +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 +MacroValue::length() noexcept(false) { throw TypeError("Length not supported for this type"); } -const MacroValue * -MacroValue::at(int i) const noexcept(false) +shared_ptr +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(&mv); - if (mv2 == nullptr) + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) throw TypeError("Type mismatch for operands of + operator"); - return new IntMV(driver, value + mv2->value); + return make_shared(value + mv2->value); } -const MacroValue * -IntMV::operator+() const noexcept(false) +MacroValuePtr +IntMV::unary_plus() noexcept(false) { - return this; + return make_shared(value); } -const MacroValue * -IntMV::operator-(const MacroValue &mv) const noexcept(false) +MacroValuePtr +IntMV::minus(const MacroValuePtr &mv) noexcept(false) { - const auto *mv2 = dynamic_cast(&mv); - if (mv2 == nullptr) + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) throw TypeError("Type mismatch for operands of - operator"); - return new IntMV(driver, value - mv2->value); + return make_shared(value - mv2->value); } -const MacroValue * -IntMV::operator-() const noexcept(false) +MacroValuePtr +IntMV::unary_minus() noexcept(false) { - return new IntMV(driver, -value); + return make_shared(-value); } -const MacroValue * -IntMV::operator*(const MacroValue &mv) const noexcept(false) +MacroValuePtr +IntMV::times(const MacroValuePtr &mv) noexcept(false) { - const auto *mv2 = dynamic_cast(&mv); - if (mv2 == nullptr) + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) throw TypeError("Type mismatch for operands of * operator"); - return new IntMV(driver, value * mv2->value); + return make_shared(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(&mv); - if (mv2 == nullptr) + auto mv2 = dynamic_pointer_cast(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(value / mv2->value); } -const MacroValue * -IntMV::operator<(const MacroValue &mv) const noexcept(false) +shared_ptr +IntMV::is_less(const MacroValuePtr &mv) noexcept(false) { - const auto *mv2 = dynamic_cast(&mv); - if (mv2 == nullptr) + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) throw TypeError("Type mismatch for operands of < operator"); - return new IntMV(driver, value < mv2->value); + return make_shared(value < mv2->value); } -const MacroValue * -IntMV::operator>(const MacroValue &mv) const noexcept(false) +shared_ptr +IntMV::is_greater(const MacroValuePtr &mv) noexcept(false) { - const auto *mv2 = dynamic_cast(&mv); - if (mv2 == nullptr) + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) throw TypeError("Type mismatch for operands of > operator"); - return new IntMV(driver, value > mv2->value); + return make_shared(value > mv2->value); } -const MacroValue * -IntMV::operator<=(const MacroValue &mv) const noexcept(false) +shared_ptr +IntMV::is_less_equal(const MacroValuePtr &mv) noexcept(false) { - const auto *mv2 = dynamic_cast(&mv); - if (mv2 == nullptr) + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) throw TypeError("Type mismatch for operands of <= operator"); - return new IntMV(driver, value <= mv2->value); + return make_shared(value <= mv2->value); } -const MacroValue * -IntMV::operator>=(const MacroValue &mv) const noexcept(false) +shared_ptr +IntMV::is_greater_equal(const MacroValuePtr &mv) noexcept(false) { - const auto *mv2 = dynamic_cast(&mv); - if (mv2 == nullptr) + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) throw TypeError("Type mismatch for operands of >= operator"); - return new IntMV(driver, value >= mv2->value); + return make_shared(value >= mv2->value); } -const MacroValue * -IntMV::operator==(const MacroValue &mv) const noexcept(false) +shared_ptr +IntMV::is_equal(const MacroValuePtr &mv) { - const auto *mv2 = dynamic_cast(&mv); - if (mv2 == nullptr) - return new IntMV(driver, 0); + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) + return make_shared(0); else - return new IntMV(driver, value == mv2->value); + return make_shared(value == mv2->value); } -const MacroValue * -IntMV::operator!=(const MacroValue &mv) const noexcept(false) +shared_ptr +IntMV::logical_and(const MacroValuePtr &mv) noexcept(false) { - const auto *mv2 = dynamic_cast(&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(&mv); - if (mv2 == nullptr) + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) throw TypeError("Type mismatch for operands of && operator"); - return new IntMV(driver, value && mv2->value); + return make_shared(value && mv2->value); } -const MacroValue * -IntMV::operator||(const MacroValue &mv) const noexcept(false) +shared_ptr +IntMV::logical_or(const MacroValuePtr &mv) noexcept(false) { - const auto *mv2 = dynamic_cast(&mv); - if (mv2 == nullptr) + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) throw TypeError("Type mismatch for operands of || operator"); - return new IntMV(driver, value || mv2->value); + return make_shared(value || mv2->value); } -const MacroValue * -IntMV::operator!() const noexcept(false) +shared_ptr +IntMV::logical_not() noexcept(false) { - return new IntMV(driver, !value); + return make_shared(!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 v; - v.push_back(value); - return new ArrayMV(driver, v); -} - -const MacroValue * -IntMV::append(const MacroValue *array) const noexcept(false) -{ - const auto *array2 = dynamic_cast *>(array); - if (array2 == nullptr) - throw TypeError("Type mismatch for append operation"); - - vector v(array2->values); - v.push_back(value); - return new ArrayMV(driver, v); -} - -const MacroValue * -IntMV::in(const MacroValue *array) const noexcept(false) -{ - const auto *array2 = dynamic_cast *>(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(mv1); - const auto *mv2i = dynamic_cast(mv2); - if (mv1i == nullptr || mv2i == nullptr) - throw TypeError("Arguments of range operator (:) must be integers"); - - int v1 = mv1i->value; - int v2 = mv2i->value; - - vector result; - for (; v1 <= v2; v1++) - result.push_back(v1); - return new ArrayMV(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(&mv); - if (mv2 != nullptr) - return new StringMV(driver, value + mv2->value); - - const auto *mv3 = dynamic_cast(&mv); - if (mv3 != nullptr) - return new StringMV(driver, value + mv3->toString()); + auto mv2 = dynamic_pointer_cast(mv); + if (mv2) + return make_shared(value + mv2->value); throw TypeError("Type mismatch for operands of + operator"); } -const MacroValue * -StringMV::operator==(const MacroValue &mv) const noexcept(false) +shared_ptr +StringMV::is_equal(const MacroValuePtr &mv) { - const auto *mv2 = dynamic_cast(&mv); - if (mv2 == nullptr) - return new IntMV(driver, 0); + auto mv2 = dynamic_pointer_cast(mv); + if (mv2) + return make_shared(value == mv2->value); else - return new IntMV(driver, value == mv2->value); + return make_shared(0); } -const MacroValue * -StringMV::operator!=(const MacroValue &mv) const noexcept(false) +MacroValuePtr +StringMV::subscript(const MacroValuePtr &mv) noexcept(false) { - const auto *mv2 = dynamic_cast(&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 *>(&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(value.length())) + throw OutOfBoundsError(); + result.append(1, value.at(i - 1)); + }; + + auto mv2 = dynamic_pointer_cast(mv); + auto mv3 = dynamic_pointer_cast(mv); + + if (mv2) + copy_element(mv2->value); + else if (mv3) + for (auto &v : mv3->values) + { + auto v2 = dynamic_pointer_cast(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(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 args_arg, string body_arg) : + args{move(args_arg)}, body{move(body_arg)} { - vector v; - v.push_back(value); - return new ArrayMV(driver, v); } -const MacroValue * -StringMV::append(const MacroValue *array) const noexcept(false) +shared_ptr +FuncMV::is_equal(const MacroValuePtr &mv) { - const auto *array2 = dynamic_cast *>(array); - if (array2 == nullptr) - throw TypeError("Type mismatch for append operation"); + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2 || body != mv2->body) + return make_shared(0); - vector v(array2->values); - v.push_back(value); - return new ArrayMV(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(0); + + return make_shared(1); } -const MacroValue * -StringMV::in(const MacroValue *array) const noexcept(false) -{ - const auto *array2 = dynamic_cast *>(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::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 values_arg) : values{move(values_arg)} +{ +} + +MacroValuePtr +ArrayMV::plus(const MacroValuePtr &mv) noexcept(false) +{ + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) + throw TypeError("Type mismatch for operands of + operator"); + + vector values_copy{values}; + values_copy.insert(values_copy.end(), mv2->values.begin(), mv2->values.end()); + return make_shared(values_copy); +} + +MacroValuePtr +ArrayMV::minus(const MacroValuePtr &mv) noexcept(false) +{ + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2) + throw TypeError("Type mismatch for operands of - operator"); + + /* Highly inefficient algorithm for computing set difference + (but vector is not suited for that...) */ + vector 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(new_values); +} + +shared_ptr +ArrayMV::is_equal(const MacroValuePtr &mv) +{ + auto mv2 = dynamic_pointer_cast(mv); + if (!mv2 || values.size() != mv2->values.size()) + return make_shared(0); + + auto it = values.cbegin(); + auto it2 = mv2->values.cbegin(); + while (it != values.cend()) + { + if ((*it)->is_different(*it2)->value) + return make_shared(0); + ++it; + ++it2; + } + return make_shared(1); +} + +MacroValuePtr +ArrayMV::subscript(const MacroValuePtr &mv) noexcept(false) +{ + vector result; + + auto copy_element = [&](int i) { + if (i < 1 || i > static_cast(values.size())) + throw OutOfBoundsError(); + result.push_back(values[i - 1]); + }; + + auto mv2 = dynamic_pointer_cast(mv); + auto mv3 = dynamic_pointer_cast(mv); + + if (mv2) + copy_element(mv2->value); + else if (mv3) + for (auto &v : mv3->values) + { + auto v2 = dynamic_pointer_cast(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(result); + else + return result[0]; +} + +string +ArrayMV::toString() +{ + ostringstream ss; + for (auto &v : values) + ss << v->toString(); + return ss.str(); +} + +shared_ptr +ArrayMV::length() noexcept(false) +{ + return make_shared(values.size()); +} + +string +ArrayMV::print() { ostringstream ss; ss << "["; @@ -466,104 +488,40 @@ ArrayMV::print() const if (it != values.begin()) ss << ", "; - ss << *it; + ss << (*it)->print(); } ss << "]"; return ss.str(); } -template<> -string -ArrayMV::print() const +shared_ptr +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 v{values}; + v.push_back(move(mv)); + return make_shared(v); } -FuncMV::FuncMV(MacroDriver &driver, vector &args_arg, StringMV &value_arg) : - MacroValue(driver), args(args_arg), value(value_arg) +shared_ptr +ArrayMV::in(const MacroValuePtr &mv) noexcept(false) { + for (auto &v : values) + if (v->is_equal(mv)->value) + return make_shared(1); + + return make_shared(0); } -FuncMV::~FuncMV() -= default; - -const MacroValue * -FuncMV::operator+(const MacroValue &mv) const noexcept(false) +shared_ptr +ArrayMV::range(const MacroValuePtr &mv1, const MacroValuePtr &mv2) noexcept(false) { - const auto *mv2 = dynamic_cast(&mv); - if (mv2 != nullptr) - return value + mv2->value; + auto mv1i = dynamic_pointer_cast(mv1); + auto mv2i = dynamic_pointer_cast(mv2); + if (!mv1i || !mv2i) + throw TypeError("Arguments of range operator (:) must be integers"); - const auto *mv3 = dynamic_cast(&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(&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(*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 v; - v.push_back(value.toString()); - return new ArrayMV(driver, v); + vector result; + for (int v1 = mv1i->value, v2 = mv2i->value; v1 <= v2; v1++) + result.push_back(make_shared(v1)); + return make_shared(result); } diff --git a/src/macro/MacroValue.hh b/src/macro/MacroValue.hh index 176d801f..b8e782d0 100644 --- a/src/macro/MacroValue.hh +++ b/src/macro/MacroValue.hh @@ -24,24 +24,29 @@ #include #include #include +#include using namespace std; class MacroDriver; +class MacroValue; +class IntMV; +class ArrayMV; + +using MacroValuePtr = shared_ptr; //! 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 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 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 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 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 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 is_different(const MacroValuePtr &mv); //! Applies && operator - virtual const MacroValue *operator&&(const MacroValue &mv) const noexcept(false); + virtual shared_ptr logical_and(const MacroValuePtr &mv) noexcept(false); //! Applies || operator - virtual const MacroValue *operator||(const MacroValue &mv) const noexcept(false); + virtual shared_ptr logical_or(const MacroValuePtr &mv) noexcept(false); //! Applies unary ! operator - virtual const MacroValue *operator!() const noexcept(false); + virtual shared_ptr 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 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 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 is_less(const MacroValuePtr &mv) noexcept(false) override; + shared_ptr is_greater(const MacroValuePtr &mv) noexcept(false) override; + shared_ptr is_less_equal(const MacroValuePtr &mv) noexcept(false) override; + shared_ptr is_greater_equal(const MacroValuePtr &mv) noexcept(false) override; + shared_ptr is_equal(const MacroValuePtr &mv) override; //! Computes logical and - const MacroValue *operator&&(const MacroValue &mv) const noexcept(false) override; + shared_ptr logical_and(const MacroValuePtr &mv) noexcept(false) override; //! Computes logical or - const MacroValue *operator||(const MacroValue &mv) const noexcept(false) override; + shared_ptr 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 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 is_equal(const MacroValuePtr &mv) override; //! Subscripting operator /*! Argument must be an ArrayMV. 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 args, string body_arg); //! Function args & body const vector args; - const StringMV &value; -public: - FuncMV(MacroDriver &driver, vector &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 & - get_args() const - { - return args; - } + shared_ptr is_equal(const MacroValuePtr &mv) override; + string toString() override; + string print() override; }; //! Represents an array in macro language -template class ArrayMV : public MacroValue { - friend class IntMV; - friend class StringMV; - friend class ArrayMV; // Necessary for operator[] to access values of integer array when subscripting a string array - friend class MacroDriver; -private: - //! Underlying vector - const vector values; public: - ArrayMV(MacroDriver &driver, vector values_arg); - - ~ArrayMV() override; + ArrayMV(vector values_arg); + + //! Underlying vector + const vector 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 is_equal(const MacroValuePtr &mv) override; //! Subscripting operator /*! Argument must be an ArrayMV. 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 length() noexcept(false) override; + shared_ptr append(MacroValuePtr mv) noexcept(false); + shared_ptr 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 range(const MacroValuePtr &mv1, const MacroValuePtr &mv2) noexcept(false); }; -template -ArrayMV::ArrayMV(MacroDriver &driver, vector values_arg) : MacroValue(driver), values(move(values_arg)) -{ -} - -template -ArrayMV::~ArrayMV() -= default; - -template -const MacroValue * -ArrayMV::operator+(const MacroValue &mv) const noexcept(false) -{ - const auto *mv2 = dynamic_cast *>(&mv); - if (mv2 == nullptr) - throw TypeError("Type mismatch for operands of + operator"); - - vector values_copy(values); - values_copy.insert(values_copy.end(), mv2->values.begin(), mv2->values.end()); - return new ArrayMV(driver, values_copy); -} - -template -const MacroValue * -ArrayMV::operator-(const MacroValue &mv) const noexcept(false) -{ - const auto *mv2 = dynamic_cast *>(&mv); - if (mv2 == nullptr) - throw TypeError("Type mismatch for operands of - operator"); - - /* Highly inefficient algorithm for computing set difference - (but vector is not suited for that...) */ - vector new_values; - for (auto it = values.begin(); - it != values.end(); it++) - { - typename vector::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(driver, new_values); -} - -template -const MacroValue * -ArrayMV::operator==(const MacroValue &mv) const noexcept(false) -{ - const auto *mv2 = dynamic_cast *>(&mv); - if (mv2 == nullptr) - return new IntMV(driver, 0); - else - return new IntMV(driver, values == mv2->values); -} - -template -const MacroValue * -ArrayMV::operator!=(const MacroValue &mv) const noexcept(false) -{ - const auto *mv2 = dynamic_cast *>(&mv); - if (mv2 == nullptr) - return new IntMV(driver, 1); - else - return new IntMV(driver, values != mv2->values); -} - -template -const MacroValue * -ArrayMV::operator[](const MacroValue &mv) const noexcept(false) -{ - const auto *mv2 = dynamic_cast *>(&mv); - if (mv2 == nullptr) - throw TypeError("Expression inside [] must be an integer array"); - vector 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(driver, result); - else - return MacroValue::new_base_value(driver, result[0]); -} - -template -string -ArrayMV::toString() const -{ - ostringstream ss; - for (auto it = values.begin(); - it != values.end(); it++) - ss << *it; - return ss.str(); -} - -template -const MacroValue * -ArrayMV::toArray() const -{ - return this; -} - -template -const MacroValue * -ArrayMV::length() const noexcept(false) -{ - return new IntMV(driver, values.size()); -} - -template -const MacroValue * -ArrayMV::at(int i) const noexcept(false) -{ - return new_base_value(driver, values[i]); -} - #endif