1 /*
2 * Copyright © 2006 Ondra Kamenik
3 * Copyright © 2019 Dynare Team
4 *
5 * This file is part of Dynare.
6 *
7 * Dynare is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Dynare is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Dynare. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "atom_assignings.hh"
22 #include "location.hh"
23 #include "parser_exception.hh"
24
25 #include "utils/cc/exception.hh"
26
27 #include <limits>
28 #include <iostream>
29 #include <sstream>
30 #include <iomanip>
31
32 using namespace ogp;
33
AtomAssignings(const AtomAssignings & aa,ogp::StaticAtoms & a)34 AtomAssignings::AtomAssignings(const AtomAssignings &aa, ogp::StaticAtoms &a)
35 : atoms(a), expr(aa.expr, atoms), left_names(aa.left_names),
36 lname2expr(aa.lname2expr), order(aa.order)
37 {
38 }
39
40 /** A global symbol for passing info to the AtomAssignings from
41 * asgn_parse(). */
42 AtomAssignings *aparser;
43
44 /** The declaration of functions defined in asgn_ll.cc and asgn_tab.cc
45 * generated from assign.lex assign.y */
46 void *asgn__scan_string(const char *);
47 void asgn__destroy_buffer(void *);
48 void asgn_parse();
49 extern location_type asgn_lloc;
50
51 void
parse(const string & stream)52 AtomAssignings::parse(const string &stream)
53 {
54 asgn_lloc.off = 0;
55 asgn_lloc.ll = 0;
56 void *p = asgn__scan_string(stream.c_str());
57 aparser = this;
58 asgn_parse();
59 asgn__destroy_buffer(p);
60 }
61
62 void
error(string mes)63 AtomAssignings::error(string mes)
64 {
65 throw ParserException(std::move(mes), asgn_lloc.off);
66 }
67
68 void
add_assignment_to_double(string name,double val)69 AtomAssignings::add_assignment_to_double(string name, double val)
70 {
71 // if left hand side is a registered atom, insert it to tree
72 int t;
73 try
74 {
75 if (atoms.check(name))
76 t = expr.add_nulary(name);
77 else
78 t = -1;
79 }
80 catch (const ParserException &e)
81 {
82 t = -1;
83 }
84 // register left hand side in order
85 order.push_back(t);
86
87 // add the double to the tree
88 std::ostringstream buf;
89 buf << std::setprecision(std::numeric_limits<double>::max_digits10)
90 << val;
91 try
92 {
93 expr.parse(buf.str());
94 }
95 catch (const ParserException &e)
96 {
97 // should never happen
98 throw ParserException(string("Error parsing double ")+buf.str()+": "+e.message(), 0);
99 }
100
101 // register name of the left hand side and put to lname2expr
102 left_names.insert(name);
103 lname2expr.emplace(std::move(name), order.size()-1);
104 }
105
106 void
add_assignment(int asgn_off,const string & str,int name_len,int right_off,int right_len)107 AtomAssignings::add_assignment(int asgn_off, const string &str, int name_len,
108 int right_off, int right_len)
109 {
110 // the order of doing things here is important: since the
111 // FormulaParser requires that all references from the i-th tree
112 // refere to trees with index lass than i, so to capture also a
113 // nulary term for the left hand side, it must be inserted to the
114 // expression tree before the expression is parsed.
115
116 // find the name in the atoms
117 string name = str.substr(0, name_len);
118
119 // if left hand side is a registered atom, insert it to tree
120 int t;
121 try
122 {
123 t = atoms.check(name);
124 if (t == -1)
125 t = expr.add_nulary(name);
126 }
127 catch (const ParserException &e)
128 {
129 atoms.register_name(name);
130 t = expr.add_nulary(name);
131 }
132 // register left hand side in order
133 order.push_back(t);
134
135 // parse expression on the right
136 try
137 {
138 expr.parse(str.substr(right_off, right_len));
139 }
140 catch (const ParserException &e)
141 {
142 throw ParserException(e, asgn_off+right_off);
143 }
144
145 // register name of the left hand side and put to lname2expr
146 left_names.insert(name);
147 if (lname2expr.find(name) != lname2expr.end())
148 {
149 // Prevent the occurrence of #415
150 std::cerr << "Changing the value of " << name << " is not supported. Aborting." << std::endl;
151 exit(EXIT_FAILURE);
152 }
153 lname2expr[name] = order.size()-1;
154 }
155
156 void
apply_subst(const AtomSubstitutions::Toldnamemap & mm)157 AtomAssignings::apply_subst(const AtomSubstitutions::Toldnamemap &mm)
158 {
159 // go through all old variables and see what are their derived new
160 // variables
161 for (const auto &it : mm)
162 {
163 const string &oldname = it.first;
164 const AtomSubstitutions::Tshiftnameset &sset = it.second;
165 if (!sset.empty())
166 {
167 int told = atoms.index(oldname);
168 if (told < 0 && !atoms.get_name_storage().query(oldname))
169 atoms.register_name(oldname);
170 if (told == -1)
171 told = expr.add_nulary(oldname);
172 // at least one substitution here, so make an expression
173 expr.add_formula(told);
174 // say that this expression is not assigned to any atom
175 order.push_back(-1);
176 // now go through all new names derived from the old name and
177 // reference to the newly added formula
178 for (const auto &itt : sset)
179 {
180 const string &newname = itt.first;
181 left_names.insert(newname);
182 lname2expr.emplace(newname, expr.nformulas()-1);
183 }
184 }
185 }
186 }
187
188 void
print() const189 AtomAssignings::print() const
190 {
191 std::cout << "Atom Assignings\nExpressions:\n";
192 expr.print();
193 std::cout << "Left names:\n";
194 for (auto it : lname2expr)
195 std::cout << it.first << u8" ⇒ " << expr.formula(it.second) << " (t=" << order[it.second] << ")\n";
196 }
197
198 void
setValues(EvalTree & et) const199 AtomAsgnEvaluator::setValues(EvalTree &et) const
200 {
201 // set values of constants
202 aa.atoms.setValues(et);
203
204 // set values of variables to NaN or to user set values
205 double nan = std::numeric_limits<double>::quiet_NaN();
206 for (int i = 0; i < aa.atoms.nvar(); i++)
207 {
208 const string &ss = aa.atoms.name(i);
209 int t = aa.atoms.index(ss);
210 if (t >= 0)
211 {
212 auto it = user_values.find(t);
213 if (it == user_values.end())
214 et.set_nulary(t, nan);
215 else
216 et.set_nulary(t, it->second);
217 }
218 }
219 }
220
221 void
set_user_value(const string & name,double val)222 AtomAsgnEvaluator::set_user_value(const string &name, double val)
223 {
224 int t = aa.atoms.index(name);
225 if (t >= 0)
226 {
227 auto it = user_values.find(t);
228 if (it == user_values.end())
229 user_values.emplace(t, val);
230 else
231 it->second = val;
232 }
233 }
234
235 void
load(int i,double res)236 AtomAsgnEvaluator::load(int i, double res)
237 {
238 // set the value
239 operator[](i) = res;
240 // if i-th expression is atom, set its value to this EvalTree
241 int t = aa.order[i];
242 if (t >= 0)
243 etree.set_nulary(t, res);
244 }
245
246 double
get_value(const string & name) const247 AtomAsgnEvaluator::get_value(const string &name) const
248 {
249 auto it = aa.lname2expr.find(name);
250 if (it == aa.lname2expr.end())
251 return std::numeric_limits<double>::quiet_NaN();
252 else
253 return operator[](it->second);
254 }
255