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