1 /*!
2  * \file   MFrontUtilities.cxx
3  * \brief
4  * \author Thomas Helfer
5  * \date   15 août 2015
6  * \copyright Copyright (C) 2006-2018 CEA/DEN, EDF R&D. All rights
7  * reserved.
8  * This project is publicly released under either the GNU GPL Licence
9  * or the CECILL-A licence. A copy of thoses licences are delivered
10  * with the sources of TFEL. CEA or EDF may also distribute this
11  * project under specific licensing conditions.
12  */
13 
14 #include <cctype>
15 #include <sstream>
16 #include <ostream>
17 #include <cstring>
18 #include <stdexcept>
19 #include <algorithm>
20 #include "TFEL/Raise.hxx"
21 #include "TFEL/UnicodeSupport/UnicodeSupport.hxx"
22 #include "TFEL/Utilities/StringAlgorithms.hxx"
23 #include "MFront/VariableBoundsDescription.hxx"
24 #include "MFront/MFrontUtilities.hxx"
25 
26 namespace mfront{
27 
insert_if(std::vector<std::string> & d,const std::string & v)28   void insert_if(std::vector<std::string>& d, const std::string& v) {
29     if(v.empty()){
30       return;
31     }
32     if(std::find(d.begin(),d.end(),v)==d.end()){
33       d.push_back(v);
34     }
35   }
36 
insert_if(std::vector<std::string> & d,const char * const v)37   void insert_if(std::vector<std::string>& d, const char* const v) {
38     if(v==nullptr){
39       return;
40     }
41     if(strlen(v)==0){
42       return;
43     }
44     if(std::find(d.begin(),d.end(),v)==d.end()){
45       d.emplace_back(v);
46     }
47   }
48 
write(std::ostream & os,const std::vector<std::string> & v,const std::string & id)49   void write(std::ostream& os,
50 	     const std::vector<std::string>& v,
51 	     const std::string& id){
52     using tfel::utilities::replace_all;
53     if(v.empty()){
54       return;
55     }
56     os << id << " : {\n";
57     for(auto p = v.begin();p!=v.end();){
58       os << "\"" << replace_all(*p,"\"","\\\"")
59 	 << "\"";
60       if(++p!=v.end()){
61 	os << ",\n";
62       } else {
63 	os << "\n";
64       }
65     }
66     os << "};\n";
67   } // end of write
68 
69   template<>
read(tfel::utilities::CxxTokenizer::const_iterator & p,const tfel::utilities::CxxTokenizer::const_iterator pe)70   double read(tfel::utilities::CxxTokenizer::const_iterator& p,
71 	      const tfel::utilities::CxxTokenizer::const_iterator pe)
72   {
73     auto c = p;
74     auto r = tfel::utilities::CxxTokenizer::readDouble(c,pe);
75     p=c;
76     return r;
77   } // end of read
78 
79   template<>
80   std::string
read(tfel::utilities::CxxTokenizer::const_iterator & p,const tfel::utilities::CxxTokenizer::const_iterator pe)81   read(tfel::utilities::CxxTokenizer::const_iterator& p,
82        const tfel::utilities::CxxTokenizer::const_iterator pe)
83   {
84     auto c = p;
85     const auto r = tfel::utilities::CxxTokenizer::readString(c,pe);
86     p=c;
87     const auto v = tfel::utilities::replace_all(r,"\\\"","\"");
88     return v;
89   } // end of read
90 
91   template<>
92   std::vector<std::string>
read(tfel::utilities::CxxTokenizer::const_iterator & p,const tfel::utilities::CxxTokenizer::const_iterator pe)93   read(tfel::utilities::CxxTokenizer::const_iterator& p,
94        const tfel::utilities::CxxTokenizer::const_iterator pe)
95   {
96     auto c = p;
97     auto r = tfel::utilities::CxxTokenizer::readStringArray(c,pe);
98     for(auto&s : r){
99       s = tfel::utilities::replace_all(s,"\\\"","\"");
100     }
101     p=c;
102     return r;
103   } // end of read
104 
readVariableBounds(tfel::utilities::CxxTokenizer::const_iterator & p,const tfel::utilities::CxxTokenizer::const_iterator pe)105   std::pair<std::string, VariableBoundsDescription> readVariableBounds(
106       tfel::utilities::CxxTokenizer::const_iterator& p,
107       const tfel::utilities::CxxTokenizer::const_iterator pe) {
108     using tfel::utilities::CxxTokenizer;
109     const std::string m = "mfront::readVariableBounds";
110     auto throw_if = [&m](const bool b, const std::string& msg) {
111       tfel::raise_if(b, m + ": " + msg);
112     };
113     VariableBoundsDescription b;
114     CxxTokenizer::checkNotEndOfLine(m, p, pe);
115     auto n = tfel::unicode::getMangledString(p->value);
116     ++p;
117     CxxTokenizer::checkNotEndOfLine(m, "Expected '[' or '(' or 'in'.", p, pe);
118     if (p->value == "[") {
119       CxxTokenizer::readSpecifiedToken(m, "[", p, pe);
120       CxxTokenizer::checkNotEndOfLine(m, p, pe);
121       std::string position(p->value);
122       std::istringstream converter(position);
123       unsigned int pos;
124       converter >> pos;
125       throw_if(!converter || (!converter.eof()),
126                "could not read position number for variable '" + n + "'");
127       ++p;
128       CxxTokenizer::checkNotEndOfLine(m, p, pe);
129       CxxTokenizer::readSpecifiedToken(m, "]", p, pe);
130       n += '[' + position + ']';
131     }
132     CxxTokenizer::checkNotEndOfLine(m, "Expected '(' or 'in'.", p, pe);
133     if (p->value == "(") {
134       CxxTokenizer::readSpecifiedToken(m, "(", p, pe);
135       CxxTokenizer::checkNotEndOfLine(m, p, pe);
136       if (p->value != "*") {
137         unsigned int component;
138         std::istringstream converter(p->value);
139         converter >> component;
140         throw_if(!converter || (!converter.eof()),
141                  "could not read component number for variable '" + n + "'");
142         b.component = component;
143       }
144       ++(p);
145       CxxTokenizer::checkNotEndOfLine(m, p, pe);
146       CxxTokenizer::readSpecifiedToken(m, ")", p, pe);
147     }
148     CxxTokenizer::readSpecifiedToken(m, "in", p, pe);
149     CxxTokenizer::checkNotEndOfLine(
150         m, "Expected '\u211D\u208A', '\u211D\u208B', ']' or '['.", p, pe);
151     if (p->value == "\u211D\u208A") {
152       b.lowerBound = 0.;
153       b.boundsType = VariableBoundsDescription::LOWER;
154     } else if (p->value == "\u211D\u208B") {
155       b.upperBound = 0.;
156       b.boundsType = VariableBoundsDescription::UPPER;
157     } else {
158       if (p->value == "]") {
159         ++p;
160         CxxTokenizer::checkNotEndOfLine(m, "Expected '*'.", p, pe);
161         throw_if(p->value != "*", "Expected '*' (read '" + p->value + "')");
162         b.boundsType = VariableBoundsDescription::UPPER;
163         ++p;
164       } else if (p->value == "[") {
165         ++p;
166         CxxTokenizer::checkNotEndOfLine(
167             m, "Expected lower bound value for variable '" + n + "'", p, pe);
168         b.lowerBound = mfront::read<double>(p, pe);
169         b.boundsType = VariableBoundsDescription::LOWERANDUPPER;
170       } else {
171         throw_if(true, "Expected ']' or '[' (read '" + p->value + "')");
172       }
173       CxxTokenizer::readSpecifiedToken(m, ":", p, pe);
174       CxxTokenizer::checkNotEndOfLine(
175           m, "expected upper bound value for variable '" + n + "'", p, pe);
176       if (p->value == "*") {
177         throw_if(b.boundsType == VariableBoundsDescription::UPPER,
178                  "Upper and lower values bounds are both infinity. "
179                  "This is inconsistent.");
180         b.boundsType = VariableBoundsDescription::LOWER;
181         ++p;
182         CxxTokenizer::readSpecifiedToken(m, "[", p, pe);
183       } else {
184         b.upperBound = mfront::read<double>(p, pe);
185         if (b.boundsType == VariableBoundsDescription::LOWERANDUPPER) {
186           throw_if(b.lowerBound > b.upperBound,
187                    "Lower bound value is greater than upper "
188                    "bound value for variable '" +
189                        n + "'");
190         }
191         CxxTokenizer::readSpecifiedToken(m, "]", p, pe);
192       }
193     }
194     return std::make_pair(n, b);
195   }  // end of readVariableBounds
196 
197   std::tuple<std::string, bool, unsigned short>
extractVariableNameAndArrayPosition(const std::string & n)198   extractVariableNameAndArrayPosition(const std::string& n) {
199     auto throw_if = [](const bool c, const std::string& m) {
200       tfel::raise_if(c, "mfront::extractVariableNameAndArrayPosition: " + m);
201     };
202     unsigned short i = 0;
203     auto p = n.cbegin();
204     auto pe = n.cend();
205     while ((p != pe) && (*p != '[')) {
206       ++p;
207     }
208     if (p == pe) {
209       return std::make_tuple(n, false, i);
210     }
211     auto r = std::string{n.cbegin(), p};
212     ++p;
213     throw_if(p == pe, "unexpected end of string 'n'");
214     throw_if(!std::isdigit(*p), "unexpected a digit 'n'");
215     while ((p != pe) && (std::isdigit(*p))) {
216       i *= 10;
217       i += *p - '0';
218       ++p;
219     }
220     throw_if(p == pe, "unexpected end of string '" + n + "'");
221     throw_if(*p != ']', "invalid variable name '" + n + "'");
222     ++p;
223     throw_if(p != pe, "invalid variable name '" + n + "'");
224     return std::make_tuple(r, true, i);
225   } // end of extractVariableNameAndArrayPosition
226 
227 } // end of namespace mfront
228