/* * Copyright © 2019-2022 Dynare Team * * This file is part of Dynare. * * Dynare is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Dynare is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Dynare. If not, see . */ #include "Directives.hh" #include "Driver.hh" #include using namespace macro; void Eval::interpret(ostream &output, Environment &env, [[maybe_unused]] vector &paths) { try { output << expr->eval(env)->to_string(); } catch (StackTrace &ex) { ex.push("Evaluation", location); error(ex); } catch (exception &e) { error(StackTrace("Evaluation", e.what(), location)); } } void Include::interpret(ostream &output, Environment &env, vector &paths) { using namespace filesystem; try { StringPtr msp = dynamic_pointer_cast(expr->eval(env)); if (!msp) throw StackTrace("File name does not evaluate to a string"); path filename = msp->to_string(); ifstream incfile(filename, ios::binary); if (incfile.fail()) { for (const auto &dir : paths) { incfile = ifstream(dir / filename, ios::binary); if (incfile.good()) break; } if (incfile.fail()) { ostringstream errmsg; errmsg << " * " << current_path().string() << endl; for (const auto &dir : paths) errmsg << " * " << absolute(dir).string() << endl; error(StackTrace("@#includepath", "Could not open " + filename.string() +". The following directories were searched:\n" + errmsg.str(), location)); } } Driver m; /* Calling `string()` method on filename and filename.stem() because of bug in GCC/MinGW 10.2 (shipped in Debian “Bullseye” 11), that fails to accept implicit conversion to string from filename::path. See https://en.cppreference.com/w/cpp/filesystem/path/native. */ m.parse(filename.string(), filename.stem().string(), incfile, false, {}, env, paths, output); } catch (StackTrace &ex) { ex.push("@#include", location); error(ex); } catch (exception &e) { error(StackTrace("@#include", e.what(), location)); } printLineInfo(output); } void IncludePath::interpret([[maybe_unused]] ostream &output, Environment &env, vector &paths) { using namespace filesystem; try { StringPtr msp = dynamic_pointer_cast(expr->eval(env)); if (!msp) throw StackTrace("File name does not evaluate to a string"); #ifdef _WIN32 /* Trim trailing slashes and backslashes in the path. This is a workaround for a GCC/MinGW bug present in version 10.2 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88881, that affects gcc-mingw-w64 in Debian “Bullseye” 11. It is fixed in GCC 10.3, and thus should be fixed in Debian “Bookworm” 12. See Madysson/estimation-codes#11. */ string ipstr = static_cast(*msp); while (ipstr.size() > 1 && (ipstr.back() == '/' || ipstr.back() == '\\')) ipstr.pop_back(); path ip{ipstr}; #else path ip = static_cast(*msp); #endif if (!is_directory(ip)) throw StackTrace(ip.string() + " does not evaluate to a valid directory"); if (!exists(ip)) warning(StackTrace("@#includepath", ip.string() + " does not exist", location)); paths.emplace_back(ip); } catch (StackTrace &ex) { ex.push("@#includepath", location); error(ex); } catch (exception &e) { error(StackTrace("@#includepath", e.what(), location)); } } void Define::interpret([[maybe_unused]] ostream &output, Environment &env, [[maybe_unused]] vector &paths) { try { if (var) env.define(var, value); else if (func) env.define(func, value); else throw StackTrace("LHS of can be either a variable or a function"); } catch (StackTrace &ex) { ex.push("@#define", location); error(ex); } catch (exception &e) { error(StackTrace("@#define", e.what(), location)); } } void Echo::interpret(ostream &output, Environment &env, [[maybe_unused]] vector &paths) { try { cout << "@#echo (" << getLocation() << "): " << expr->eval(env)->to_string() << endl; } catch (StackTrace &ex) { ex.push("@#echo", location); error(ex); } catch (exception &e) { error(StackTrace("@#echo", e.what(), location)); } printEndLineInfo(output); } void Error::interpret([[maybe_unused]] ostream &output, Environment &env, [[maybe_unused]] vector &paths) { try { throw StackTrace(expr->eval(env)->to_string()); } catch (StackTrace &ex) { ex.push("@#error", location); error(ex); } catch (exception &e) { error(StackTrace("@#error", e.what(), location)); } } void EchoMacroVars::interpret(ostream &output, Environment &env, [[maybe_unused]] vector &paths) { if (save) env.print(output, vars, location.begin.line, true); else env.print(cout, vars); printEndLineInfo(output); } void For::interpret(ostream &output, Environment &env, vector &paths) { ArrayPtr ap; try { ap = dynamic_pointer_cast(index_vals->eval(env)); if (!ap) throw StackTrace("The index must loop through an array"); } catch (StackTrace &ex) { ex.push("@#for", location); error(ex); } catch (exception &e) { error(StackTrace("@#for", e.what(), location)); } for (size_t i = 0; i < ap->size(); i++) { if (index_vec.size() == 1) env.define(index_vec.at(0), ap->at(i)); else { BaseTypePtr btp = dynamic_pointer_cast(ap->at(i)); if (!btp) error(StackTrace("@#for", "Unexpected error encountered in for loop", location)); if (btp->getType() == codes::BaseType::Tuple) { TuplePtr mtp = dynamic_pointer_cast(btp); if (index_vec.size() != mtp->size()) error(StackTrace("@#for", "Encountered tuple of size " + to_string(mtp->size()) + " but only have " + to_string(index_vec.size()) + " index variables", location)); else for (size_t j = 0; j < index_vec.size(); j++) env.define(index_vec.at(j), mtp->at(j)); } } bool printLine = true; for (const auto &statement : statements) { if (printLine) { statement->printLineInfo(output); printLine = false; } statement->interpret(output, env, paths); } } printEndLineInfo(output); } void If::interpret(ostream &output, Environment &env, vector &paths) { for (bool first_clause{true}; const auto &[expr, body] : expr_and_body) try { if ((ifdef || ifndef) && exchange(first_clause, false)) { VariablePtr vp = dynamic_pointer_cast(expr); if (!vp) error(StackTrace(ifdef ? "@#ifdef" : "@#ifndef", "The condition must be a variable name", location)); if ((ifdef && env.isVariableDefined(vp->getName())) || (ifndef && !env.isVariableDefined(vp->getName()))) { interpretBody(body, output, env, paths); break; } } else { auto tmp = expr->eval(env); RealPtr dp = dynamic_pointer_cast(tmp); BoolPtr bp = dynamic_pointer_cast(tmp); if (!bp && !dp) error(StackTrace("@#if", "The condition must evaluate to a boolean or a double", location)); if ((bp && *bp) || (dp && *dp)) { interpretBody(body, output, env, paths); break; } } } catch (StackTrace &ex) { ex.push("@#if", location); error(ex); } catch (exception &e) { error(StackTrace("@#if", e.what(), location)); } printEndLineInfo(output); } void If::interpretBody(const vector &body, ostream &output, Environment &env, vector &paths) { for (bool printLine{true}; const auto &statement : body) { if (exchange(printLine, false)) statement->printLineInfo(output); statement->interpret(output, env, paths); } }