From 3b20d41a92db4267c0ba13278f3885aba67e6c47 Mon Sep 17 00:00:00 2001 From: Houtan Bastani Date: Fri, 30 Dec 2016 11:26:56 +0100 Subject: [PATCH] first iteration of fix for nested parenthesis in matlab. #1201 --- preprocessor/DynamicModel.cc | 11 ++-- preprocessor/ModelTree.cc | 115 +++++++++++++++++++++++++++++++---- preprocessor/ModelTree.hh | 6 +- preprocessor/StaticModel.cc | 11 ++-- 4 files changed, 119 insertions(+), 24 deletions(-) diff --git a/preprocessor/DynamicModel.cc b/preprocessor/DynamicModel.cc index 68c4d4e09..266987d5d 100644 --- a/preprocessor/DynamicModel.cc +++ b/preprocessor/DynamicModel.cc @@ -2344,11 +2344,12 @@ DynamicModel::writeDynamicModel(ostream &DynamicOutput, bool use_dll, bool julia if (output_type == oMatlabDynamicModel) { // Check that we don't have more than 32 nested parenthesis because Matlab does not suppor this. See Issue #1201 - testNestedParenthesis(model_output); - testNestedParenthesis(model_local_vars_output); - testNestedParenthesis(jacobian_output); - testNestedParenthesis(hessian_output); - testNestedParenthesis(third_derivatives_output); + map tmp_paren_vars; + fixNestedParenthesis(model_output, tmp_paren_vars); + fixNestedParenthesis(model_local_vars_output, tmp_paren_vars); + fixNestedParenthesis(jacobian_output, tmp_paren_vars); + fixNestedParenthesis(hessian_output, tmp_paren_vars); + fixNestedParenthesis(third_derivatives_output, tmp_paren_vars); DynamicOutput << "%" << endl << "% Model equations" << endl diff --git a/preprocessor/ModelTree.cc b/preprocessor/ModelTree.cc index 7eb3cf96c..b0df567c9 100644 --- a/preprocessor/ModelTree.cc +++ b/preprocessor/ModelTree.cc @@ -1293,25 +1293,116 @@ ModelTree::writeTemporaryTerms(const temporary_terms_t &tt, const temporary_term } void -ModelTree::testNestedParenthesis(const ostringstream &output) const +ModelTree::fixNestedParenthesis(ostringstream &output, map &tmp_paren_vars) const { string str = output.str(); int open = 0; - for (string::iterator it = str.begin(); it != str.end(); it++) + int first_open_paren = 0; + int matching_paren = 0; + bool hit_limit = false; + int i1 = 0; + map::iterator it; + for (int i = 0; i < str.length(); i++) { - if (*it == '(') - open++; - else if (*it == ')') - open--; - if (open > 32) + if (str.at(i) == '(') { - cout << "Error: A .m file created by Dynare will have more than 32 nested parenthesis. Matlab cannot support this. " - << "Please use the use_dll option of the model block to circumnavigate this problem." << endl - << " If you have not yet set up a compiler on your system, see the Matlab documentation for doing so." << endl - << " For Windows, see: https://www.mathworks.com/help/matlab/matlab_external/install-mingw-support-package.html" << endl; - exit(EXIT_FAILURE); + if (open == 0) + first_open_paren = i; + open++; + } + else if (str.at(i) == ')') + { + open--; + if (open == 0) + matching_paren = i; + } + if (open > 32) + hit_limit = true; + + if (hit_limit && open == 0) + { + cerr << "Warning: A .m file created by Dynare will have more than 32 nested parenthesis. Matlab cannot support this. " << endl + << " We are going to modify, albeit inefficiently, this output to have fewer than 32 nested parenthesis. " << endl + << " It would hence behoove you to use the use_dll option of the model block to circumnavigate this problem." << endl + << " If you have not yet set up a compiler on your system, see the Matlab documentation for doing so." << endl + << " For Windows, see: https://www.mathworks.com/help/matlab/matlab_external/install-mingw-support-package.html" << endl << endl; + + string str1 = str.substr(first_open_paren, matching_paren - first_open_paren + 1); + string repstr = ""; + string varname; + while (testNestedParenthesis(str1)) + { + int open_paren_idx = -1; + int match_paren_idx = -1; + int last_open_paren = -1; + for (int j = 0; j < str1.length(); j++) + { + if (str1.at(j) == '(') + { + // don't match, e.g. y(1) + size_t idx = str1.find_last_of("*/-+", j - 1); + if (j == 0 || (idx != string::npos && idx == j - 1)) + open_paren_idx = j; + last_open_paren = j; + } + else if (str1.at(j) == ')') + { + // don't match, e.g. y(1) + size_t idx = str1.find_last_not_of("0123456789", j - 1); + if (idx != string::npos && idx != last_open_paren) + match_paren_idx = j; + } + + if (open_paren_idx != -1 && match_paren_idx != -1) + { + string val = str1.substr(open_paren_idx, match_paren_idx - open_paren_idx + 1); + it = tmp_paren_vars.find(val); + if (it == tmp_paren_vars.end()) + { + varname = "paren32_tmp_var_" + to_string(i1++); + repstr = repstr + varname + " = " + val + ";\n"; + tmp_paren_vars[val] = varname; + } + else + varname = it->second; + str1.replace(open_paren_idx, match_paren_idx - open_paren_idx + 1, varname); + break; + } + } + } + it = tmp_paren_vars.find(str1); + if (it == tmp_paren_vars.end()) + { + varname = "paren32_tmp_var_" + to_string(i1++); + repstr = repstr + varname + " = " + str1 + ";\n"; + } + else + varname = it->second; + str.replace(first_open_paren, matching_paren - first_open_paren + 1, varname); + size_t insertLoc = str.find_last_of("\n", first_open_paren); + str.insert(insertLoc + 1, repstr); + hit_limit = false; + i = -1; + first_open_paren = matching_paren = open = 0; } } + output.str(str); +} + +bool +ModelTree::testNestedParenthesis(const string &str) const +{ + int open = 0; + for (int i = 0; i < str.length(); i++) + { + if (str.at(i) == '(') + open++; + else if (str.at(i) == ')') + open--; + if (open > 32) + return true; + } + return false; } void diff --git a/preprocessor/ModelTree.hh b/preprocessor/ModelTree.hh index edb9f084b..db014bc2f 100644 --- a/preprocessor/ModelTree.hh +++ b/preprocessor/ModelTree.hh @@ -190,8 +190,10 @@ protected: void compileTemporaryTerms(ostream &code_file, unsigned int &instruction_number, const temporary_terms_t &tt, map_idx_t map_idx, bool dynamic, bool steady_dynamic) const; //! Adds informations for simulation in a binary file void Write_Inf_To_Bin_File(const string &basename, int &u_count_int, bool &file_open, bool is_two_boundaries, int block_mfs) const; - //! Checks for the number of nested parenthesis, issues error if > 32. Issue #1201 - void testNestedParenthesis(const ostringstream &output) const; + //! Fixes output when there are more than 32 nested parens, Issue #1201 + void fixNestedParenthesis(ostringstream &output, map &tmp_paren_vars) const; + //! Tests if string contains more than 32 nested parens, Issue #1201 + bool testNestedParenthesis(const string &str) const; //! Writes model local variables /*! No temporary term is used in the output, so that local parameters declarations can be safely put before temporary terms declaration in the output files */ void writeModelLocalVariables(ostream &output, ExprNodeOutputType output_type, deriv_node_temp_terms_t &tef_terms) const; diff --git a/preprocessor/StaticModel.cc b/preprocessor/StaticModel.cc index ae59b626c..0428d0829 100644 --- a/preprocessor/StaticModel.cc +++ b/preprocessor/StaticModel.cc @@ -1399,11 +1399,12 @@ StaticModel::writeStaticModel(ostream &StaticOutput, bool use_dll, bool julia) c if (output_type == oMatlabStaticModel) { // Check that we don't have more than 32 nested parenthesis because Matlab does not suppor this. See Issue #1201 - testNestedParenthesis(model_output); - testNestedParenthesis(model_local_vars_output); - testNestedParenthesis(jacobian_output); - testNestedParenthesis(hessian_output); - testNestedParenthesis(third_derivatives_output); + map tmp_paren_vars; + fixNestedParenthesis(model_output, tmp_paren_vars); + fixNestedParenthesis(model_local_vars_output, tmp_paren_vars); + fixNestedParenthesis(jacobian_output, tmp_paren_vars); + fixNestedParenthesis(hessian_output, tmp_paren_vars); + fixNestedParenthesis(third_derivatives_output, tmp_paren_vars); StaticOutput << "residual = zeros( " << equations.size() << ", 1);" << endl << endl << "%" << endl