1 /*!
2  * \file  mfront/src/ModelDSLCommon.cxx
3  * \brief
4  * \author Thomas Helfer
5  * \brief 11 jun 2010
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 <sstream>
15 #include <stdexcept>
16 #include <algorithm>
17 #include "TFEL/Raise.hxx"
18 #include "TFEL/Glossary/Glossary.hxx"
19 #include "TFEL/Glossary/GlossaryEntry.hxx"
20 #include "TFEL/Utilities/StringAlgorithms.hxx"
21 #include "TFEL/UnicodeSupport/UnicodeSupport.hxx"
22 #include "MFront/DSLUtilities.hxx"
23 #include "MFront/MFrontLogStream.hxx"
24 #include "MFront/MFrontDebugMode.hxx"
25 #include "MFront/ModelDSLCommon.hxx"
26 #include "MFront/TargetsDescription.hxx"
27 #include "MFront/ModelInterfaceFactory.hxx"
28 
29 // fixing a bug on current glibc++ cygwin versions (19/08/2015)
30 #if defined __CYGWIN__ && (!defined _GLIBCXX_USE_C99)
31 #include <sstream>
32 namespace std {
33   template <typename T>
to_string(const T & v)34   std::string to_string(const T& v) {
35     std::ostringstream s;
36     s << v;
37     return s.str();
38   }
39 }
40 #endif /* defined __CYGWIN__ &&  (!defined _GLIBCXX_USE_C99) */
41 
42 namespace mfront {
43 
is(const VariableDescriptionContainer & vc,const std::string & vn)44   static bool is(const VariableDescriptionContainer& vc,
45                  const std::string& vn) {
46     for (const auto& v : vc) {
47       if (v.name == vn) {
48         return true;
49       }
50       if (v.hasAttribute(VariableDescription::depth)) {
51         const auto d =
52             v.getAttribute<unsigned short>(VariableDescription::depth);
53         for (unsigned short i = 1; i != d + 1; ++i) {
54           if (v.name + '_' + std::to_string(i) == vn) {
55             return true;
56           }
57         }
58       }
59     }
60     return false;
61   }  // end of ModelDSLCommon::is()
62 
isValidModelName(const std::string & n)63   bool isValidModelName(const std::string& n) {
64     return tfel::utilities::CxxTokenizer::isValidIdentifier(n, false);
65   }
66 
ModelDSLCommon()67   ModelDSLCommon::ModelDSLCommon() {
68     this->reserveName("dt");
69     this->reserveName("\u0394t");
70     for (const auto& v : DSLBase::getDefaultReservedNames()) {
71       this->reserveName(v);
72     }
73   }
74 
getMaterialKnowledgeIdentifier() const75   std::string ModelDSLCommon::getMaterialKnowledgeIdentifier() const {
76     return this->md.className;
77   }  // end of ModelDSLCommon::getMaterialKnowledgeIdentifier
78 
getMaterialName() const79   std::string ModelDSLCommon::getMaterialName() const {
80     return this->md.material;
81   }  // end of ModelDSLCommon::getMaterialName(
82 
getOverridableVariableNameByExternalName(const std::string &) const83   std::string ModelDSLCommon::getOverridableVariableNameByExternalName(
84       const std::string&) const {
85 #pragma message("unimplemented")
86     tfel::raise(
87         "ModelDSLCommon::getOverridableVariableNameByExternalName: "
88         "unimplemented feature");
89     return "";
90   }  // end of ModelDSLCommon::getOverridableVariableNameByExternalName
91 
overrideByAParameter(const std::string & n,const double v)92   void ModelDSLCommon::overrideByAParameter(const std::string& n,
93                                             const double v) {
94 #pragma message("unimplemented")
95   }  // end of ModelDSLCommon::overrideByAParameter
96 
endsInputFileProcessing()97   void ModelDSLCommon::endsInputFileProcessing() {
98   }  // end of ModelDSLCommon::endsInputFileProcessing
99 
reserveName(const std::string & n)100   void ModelDSLCommon::reserveName(const std::string& n) {
101     this->md.reserveName(n);
102   }
103 
isNameReserved(const std::string & n) const104   bool ModelDSLCommon::isNameReserved(const std::string& n) const {
105     return this->md.isNameReserved(n);
106   }
107 
getTargetType() const108   AbstractDSL::DSLTarget ModelDSLCommon::getTargetType() const {
109     return MODELDSL;
110   }
111 
getClassName() const112   std::string ModelDSLCommon::getClassName() const {
113     return this->md.className;
114   }  // end of ModelDSLCommon::getClassName
115 
addStaticVariableDescription(const StaticVariableDescription & v)116   void ModelDSLCommon::addStaticVariableDescription(
117       const StaticVariableDescription& v) {
118     this->md.staticVars.push_back(v);
119   }  // end of ModelDSLCommon::addStaticVariableDescription
120 
getIntegerConstant(const std::string & n) const121   int ModelDSLCommon::getIntegerConstant(const std::string& n) const {
122     for (const auto& v : this->md.staticVars) {
123       if (v.name == n) {
124         if (v.type != "int") {
125           this->throwRuntimeError("ModelDSLCommon::getIntegerConstant",
126                                   "invalid type for variable '" + n + "'");
127         }
128         return v.value;
129       }
130     }
131     this->throwRuntimeError("ModelDSLCommon::getIntegerConstant",
132                             "unknown variable '" + n + "'");
133   }  // end of ModelDSLCommon::getIntegerConstant
134 
setMaterial(const std::string & m)135   void ModelDSLCommon::setMaterial(const std::string& m) {
136     if (!this->md.material.empty()) {
137       this->throwRuntimeError("ModelDSLCommon::setMaterial",
138                               "material name alreay defined");
139     }
140     if (!isValidMaterialName(m)) {
141       this->throwRuntimeError("ModelDSLCommon::setMaterial",
142                               "invalid material name ('" + m + "')");
143     }
144     this->md.material = m;
145     if (!this->md.modelName.empty()) {
146       this->md.className = this->md.material + "_" + this->md.modelName;
147     }
148   }  // end of ModelDSLCommon::setMaterial
149 
treatLibrary()150   void ModelDSLCommon::treatLibrary() {
151     const auto& l = this->readOnlyOneToken();
152     if (!isValidLibraryName(l)) {
153       this->throwRuntimeError("ModelDSLCommon::treatLibrary",
154                               "invalid library name");
155     }
156     if (!this->md.library.empty()) {
157       this->throwRuntimeError("ModelDSLCommon::treatLibrary",
158                               "library name already registred");
159     }
160     this->md.library = l;
161   }  // end of ModelDSLCommon::treatLibrary
162 
setMaterialKnowledgeIdentifier(const std::string & i)163   void ModelDSLCommon::setMaterialKnowledgeIdentifier(const std::string& i) {
164     if (!this->md.className.empty()) {
165       this->throwRuntimeError("ModelDSLCommon::setMaterialKnowledgeIdentifier",
166                               "model name already defined");
167     }
168     if (!isValidModelName(i)) {
169       this->throwRuntimeError("ModelDSLCommon::setMaterialKnowledgeIdentifier",
170                               "invalid model name");
171     }
172     this->md.modelName = i;
173     if (!this->md.material.empty()) {
174       this->md.className = this->md.material + "_" + this->md.modelName;
175     } else {
176       this->md.className = i;
177     }
178   }  // end of ModelDSLCommon::setMaterialKnowledgeIdentifier
179 
treatModel()180   void ModelDSLCommon::treatModel() {
181     const auto& m  = this->readOnlyOneToken();
182     if (!isValidModelName(m)) {
183       this->throwRuntimeError("ModelDSLCommon::treatModel",
184                               "invalid model name");
185     }
186     if (this->overriden_implementation_name.empty()) {
187       this->setMaterialKnowledgeIdentifier(m);
188     }
189   }  // end of ModelDSLCommon::treatModel
190 
setInterfaces(const std::set<std::string> & inames)191   void ModelDSLCommon::setInterfaces(const std::set<std::string>& inames) {
192     using namespace std;
193     auto& mbif = ModelInterfaceFactory::getModelInterfaceFactory();
194     for (const auto& i : inames) {
195       if (this->interfaces.count(i) == 0) {
196         this->interfaces.insert({i, mbif.getInterface(i)});
197       }
198     }
199   }  // end of ModelDSLCommon::setInterfaces
200 
generateOutputFiles()201   void ModelDSLCommon::generateOutputFiles() {
202     if (this->interfaces.empty()) {
203       this->throwRuntimeError("ModelDSLCommon::generateOutputFiles",
204                               "no interface defined");
205     }
206     //! generating sources du to external material properties and models
207     for (const auto& em : this->externalMFrontFiles) {
208       this->callMFront(em.second, {em.first});
209     }
210     //! generating sources by the interfaces
211     for (const auto& i : this->interfaces) {
212       i.second->writeOutputFiles(this->fd, this->md);
213     }
214   }  // end of ModelDSLCommon::generateOutputFiles
215 
treatUnknownKeyword()216   void ModelDSLCommon::treatUnknownKeyword() {
217     TokensContainer::const_iterator p2;
218     auto treated = false;
219     --(this->current);
220     const auto key = this->current->value;
221     ++(this->current);
222     this->checkNotEndOfFile("ModelDSLCommon::treatUnknownKeyword");
223     if (this->current->value == "[") {
224       ++(this->current);
225       this->checkNotEndOfFile("ModelDSLCommon::treatUnknownKeyword");
226       auto s = std::vector<std::string>{};
227       while (this->current->value != "]") {
228         this->checkNotEndOfFile("ModelDSLCommon::treatUnknownKeyword");
229         const auto t = [this]() -> std::string {
230           if (this->current->flag == tfel::utilities::Token::String) {
231             return this->current->value.substr(1,
232                                                this->current->value.size() - 2);
233           }
234           return this->current->value;
235         }();
236         ++(this->current);
237         this->checkNotEndOfFile("ModelDSLCommon::treatUnknownKeyword");
238         if (std::find(s.begin(), s.end(), t) == s.end()) {
239           s.push_back(t);
240         }
241         if (this->current->value != "]") {
242           this->readSpecifiedToken("ModelDSLCommon::treatUnknownKeyword", ",");
243           this->checkNotEndOfFile("ModelDSLCommon::treatUnknownKeyword");
244           if (this->current->value == "]") {
245             this->throwRuntimeError("ModelDSLCommon::treatUnknownKeyword",
246                                     "unexpected token ']'");
247           }
248         }
249       }
250       ++(this->current);
251       for (auto& i : this->interfaces) {
252         auto p =
253             i.second->treatKeyword(key, s, this->current, this->tokens.end());
254         if (p.first) {
255           if (treated) {
256             if (p2 != p.second) {
257               this->throwRuntimeError("ModelDSLCommon::treatUnknownKeyword",
258                                       "the keyword '" + key +
259                                           "' has been treated "
260                                           "by two interfaces/analysers but "
261                                           "results were differents");
262             }
263           }
264           p2 = p.second;
265           treated = true;
266         }
267       }
268       if (!treated) {
269         this->ignoreKeyWord(key);
270         return;
271       }
272     } else {
273       for (const auto& i : this->interfaces) {
274         auto p =
275             i.second->treatKeyword(key, {}, this->current, this->tokens.end());
276         if (p.first) {
277           if (treated) {
278             if (p2 != p.second) {
279               this->throwRuntimeError("ModelDSLCommon::treatUnknownKeyword",
280                                       "the keyword '" + key +
281                                           "' has been treated "
282                                           "by two interfaces/analysers but "
283                                           "results were differents");
284             }
285           }
286           p2 = p.second;
287           treated = true;
288         }
289       }
290     }
291     if (!treated) {
292       DSLBase::treatUnknownKeyword();
293     }
294     this->current = p2;
295   }  // end of ModelDSLCommon::treatUnknownKeyword
296 
treatDomain()297   void ModelDSLCommon::treatDomain() {
298     auto throw_if = [this](const bool b, const std::string& m) {
299       if (b) {
300         this->throwRuntimeError("ModelDSLCommon::treatDomain", m);
301       }
302     };
303     throw_if(!this->md.domains.empty(), "domain defined");
304     this->checkNotEndOfFile("ModelDSLCommon::treatDomain");
305     throw_if(
306         this->current->flag != tfel::utilities::Token::String,
307         "Expected to read a string (read '" + this->current->value + "').");
308     throw_if(this->current->value.size() < 2, "domain name too short.");
309     this->md.domains.insert(
310         this->current->value.substr(1, this->current->value.size() - 2));
311     ++(this->current);
312     this->readSpecifiedToken("ModelDSLCommon::treatDomain", ";");
313   }  // end of ModelDSLCommon::treatDomain()
314 
treatDomains()315   void ModelDSLCommon::treatDomains() {
316     auto throw_if = [this](const bool b, const std::string& m) {
317       if (b) {
318         this->throwRuntimeError("ModelDSLCommon::treatDomains", m);
319       }
320     };
321     throw_if(!this->md.domains.empty(), "domains defined");
322     for (const auto& d :
323          this->readArrayOfString("ModelDSLCommon::treatDomains")) {
324       throw_if(!this->md.domains.insert(d).second,
325                "domain " + d + " already defined.");
326     }
327     throw_if(this->md.domains.empty(), "@Domains does not set any domain");
328     this->readSpecifiedToken("ModelDSLCommon::treatDomain", ";");
329   }  // end of ModelDSLCommon::treatDomains()
330 
isInputVariable(const std::string & v) const331   bool ModelDSLCommon::isInputVariable(const std::string& v) const {
332     return is(this->md.inputs, v);
333   }  // end of ModelDSLCommon::isInputVariable()
334 
isOutputVariable(const std::string & v) const335   bool ModelDSLCommon::isOutputVariable(const std::string& v) const {
336     return is(this->md.outputs, v);
337   }  // end of ModelDSLCommon::isInputVariable()
338 
treatFunction()339   void ModelDSLCommon::treatFunction() {
340     auto throw_if = [this](const bool b, const std::string& m) {
341       if (b) {
342         this->throwRuntimeError("ModelDSLCommon::treatFunction", m);
343       }
344     };
345     auto isStaticMemberName = [](const ModelDescription& d,
346                                  const std::string& n) -> bool {
347       for (const auto& v : d.staticMemberNames) {
348         if (v == n) {
349           return true;
350         }
351       }
352       return false;
353     };
354     auto isMemberName = [](const ModelDescription& d,
355                            const std::string& n) -> bool {
356       for (const auto& v : d.memberNames) {
357         if (v == n) {
358           return true;
359         }
360       }
361       return false;
362     };
363     ModelDescription::Function f;
364     unsigned int openedBrackets = 0;
365     unsigned int openedParenthesis = 0;
366     f.useTimeIncrement = false;
367     this->md.registerMemberName("functor" +
368                                 std::to_string(this->md.functions.size()));
369     this->checkNotEndOfFile("ModelDSLCommon::treatFunction");
370     f.name = this->current->value;
371     throw_if(!this->isValidIdentifier(f.name),
372              "function name '" + f.name + "' is not valid");
373     this->md.registerMemberName(f.name);
374     this->reserveName(f.name + ".Domain");
375     this->reserveName(f.name + ".Domains");
376     f.line = this->current->line;
377     ++(this->current);
378     this->readSpecifiedToken("ModelDSLCommon::treatFunction", "{");
379     ++openedBrackets;
380     this->checkNotEndOfFile("ModelDSLCommon::treatFunction",
381                             "expected body of function '" + f.name + "'.");
382     auto currentLine = this->current->line;
383     auto newInstruction = true;
384     auto newLine = true;
385     if (getDebugMode()) {
386       f.body += "#line " + std::to_string(currentLine) + " \"" +
387                 this->fd.fileName + "\"\n";
388     }
389     for (; (this->current != this->tokens.end()) && (openedBrackets != 0);
390          ++(this->current)) {
391       if (this->current->line != currentLine) {
392         currentLine = this->current->line;
393         f.body += "\n";
394         if (getDebugMode()) {
395           f.body += "#line " + std::to_string(currentLine) + " \"" +
396                     this->fd.fileName + "\"\n";
397         }
398         newLine = true;
399       }
400       if (this->current->value == "{") {
401         ++openedBrackets;
402         f.body += "{";
403         newInstruction = true;
404       } else if (this->current->value == "}") {
405         --openedBrackets;
406         if (openedBrackets != 0) {
407           f.body += "}";
408         }
409       } else if (this->current->value == "(") {
410         ++openedParenthesis;
411         f.body += "(";
412       } else if (this->current->value == ")") {
413         throw_if(openedParenthesis == 0, "unbalanced parenthesis");
414         --openedParenthesis;
415         f.body += ")";
416       } else if (this->current->value == ";") {
417         f.body += ";";
418         newInstruction = true;
419       } else {
420         if (!newLine) {
421           f.body += " ";
422         }
423         const auto c = tfel::unicode::getMangledString(this->current->value);
424         if (isStaticMemberName(this->md, c)) {
425           f.body +=
426               "(" + this->md.className + ":: " + c + ")";
427         } else if (isMemberName(this->md, c)) {
428           bool treated = false;
429           if (newInstruction) {
430             auto op = std::string{};
431             ++(this->current);
432             throw_if(this->current == tokens.end(),
433                      "unexpected end of file while reading "
434                      "body of function '" +
435                          f.name + "'");
436             auto modifier = false;
437             if (this->current->value == "=") {
438               op = "=";
439               modifier = true;
440             } else if (this->current->value == "+=") {
441               op = "+=";
442               modifier = true;
443             } else if (this->current->value == "-=") {
444               op = "-=";
445               modifier = true;
446             } else if (this->current->value == "*=") {
447               op = "*=";
448               modifier = true;
449             } else if (this->current->value == "/=") {
450               op = "/=";
451               modifier = true;
452             }
453             if (modifier) {
454               bool found = false;
455               for (auto p = this->md.outputs.begin();
456                    (p != this->md.outputs.end()) && (!found);) {
457                 if (p->name == c) {
458                   found = true;
459                 } else {
460                   ++p;
461                 }
462               }
463               throw_if(!found, "trying to modify variable '" + c +
464                                    "' in the body of function '" + f.name +
465                                    "'");
466               f.body += c + " " + op + " ";
467               f.modifiedVariables.insert(c);
468               auto p6 = f.depths.find(c);
469               if (p6 == f.depths.end()) {
470                 f.depths[c] = 0;
471               }
472               treated = true;
473             } else {
474               --(this->current);
475             }
476           }
477           if (!treated) {
478             // treating the case of local parameters
479             for (auto p = this->md.parameters.begin();
480                  (p != this->md.parameters.end()) && (!treated); ++p) {
481               if (p->name == c) {
482                 treated = true;
483                 f.parameters.insert(c);
484                 f.body += "(this->" + c + ")";
485               }
486             }
487             // treating the case of local parameters
488             for (auto p = this->md.constantMaterialProperties.begin();
489                  (p != this->md.constantMaterialProperties.end()) && (!treated);
490                  ++p) {
491               if (p->name == c) {
492                 treated = true;
493                 f.constantMaterialProperties.insert(c);
494                 f.body += "(this->" + c + ")";
495               }
496             }
497             if (!treated) {
498               if (this->isInputVariable(c)) {
499                 f.usedVariables.insert(c);
500                 auto dv = this->md.decomposeVariableName(c);
501                 auto p6 = f.depths.find(dv.first);
502                 if (p6 != f.depths.end()) {
503                   if (dv.second > p6->second) {
504                     f.depths[dv.first] = dv.second;
505                   }
506                 } else {
507                   f.depths[dv.first] = dv.second;
508                 }
509               } else if (this->isOutputVariable(c)) {
510                 f.usedVariables.insert(c);
511                 auto dv = this->md.decomposeVariableName(c);
512                 auto p6 = f.depths.find(dv.first);
513                 if (p6 != f.depths.end()) {
514                   if (dv.second > p6->second) {
515                     f.depths[dv.first] = dv.second;
516                   }
517                 } else {
518                   f.depths[dv.first] = dv.second;
519                 }
520               }
521               f.body += c;
522             }
523           }
524         } else {
525           if ((c == "dt")||(c=="\u0394t")) {
526             f.useTimeIncrement = true;
527             f.body += "dt";
528           } else {
529             f.body += c;
530           }
531         }
532         newInstruction = false;
533       }
534       newLine = false;
535     }
536     throw_if((this->current == tokens.end()) && (openedBrackets != 0),
537              "unexpected end of file while reading body of function '" +
538                  f.name + "'");
539     throw_if(
540         openedBrackets != 0,
541         "parenthesis still opened at the end of function '" + f.name + "'");
542     throw_if(f.modifiedVariables.empty(),
543              "function " + f.name + " does not change any variable.");
544     for (const auto& df : this->md.functions) {
545       throw_if(df.name == f.name, "function " + f.name + " already declared.");
546     }
547     for (auto p2 = f.modifiedVariables.begin(); p2 != f.modifiedVariables.end();
548          ++p2) {
549       auto p3 = f.usedVariables.find(*p2);
550       if (p3 != f.usedVariables.end()) {
551         f.usedVariables.erase(*p3);
552       }
553     }
554     this->md.functions.push_back(f);
555   }  // end of ModelDSLCommon::treatFunction()
556 
treatOutput()557   void ModelDSLCommon::treatOutput() {
558     if (!this->md.functions.empty()) {
559       this->throwRuntimeError("ModelDSLCommon::treatInput",
560                               "outputs must be declared before "
561                               "declaring functions");
562     }
563     VariableDescriptionContainer noutputs;
564     this->readVarList(noutputs, "real", false);
565     for (const auto& v : noutputs) {
566       if (!v.symbolic_form.empty()) {
567         this->reserveName(v.symbolic_form);
568       }
569       this->md.registerMemberName(v.name);
570       this->md.outputs.push_back(v);
571     }
572   }  // end of ModelDSLCommon::treatOutput()
573 
treatInput()574   void ModelDSLCommon::treatInput() {
575     if (!this->md.functions.empty()) {
576       this->throwRuntimeError("ModelDSLCommon::treatInput",
577                               "inputs must be declared before "
578                               "declaring functions");
579     }
580     VariableDescriptionContainer ninputs;
581     this->readVarList(ninputs, "real", false);
582     for (const auto& v : ninputs) {
583       if (!v.symbolic_form.empty()) {
584         this->reserveName(v.symbolic_form);
585       }
586       this->md.registerMemberName(v.name);
587       this->md.inputs.push_back(v);
588     }
589   }  // end of ModelDSLCommon::treatInput()
590 
treatOutputMethod()591   void ModelDSLCommon::treatOutputMethod() {
592     if (!this->md.functions.empty()) {
593       this->throwRuntimeError("ModelDSLCommon::treatOutputMethod: ",
594                               "variable methods must be called "
595                               "before declaring functions");
596     }
597     this->readSpecifiedToken("ModelDSLCommon::treatOutputMethod", ".");
598     this->checkNotEndOfFile("ModelDSLCommon::treatOutputMethod",
599                             "Expected method name.");
600     const auto mn = this->current->value;
601     if ((mn != "setGlossaryName") && (mn != "setEntryName") &&
602         (mn != "setDefaultInitialValue") && (mn != "setDepth")) {
603       this->throwRuntimeError("ModelDSLCommon::treatOutputMethod",
604                               "Unknown method (valid methods for fields are "
605                               "setGlossaryName, setEntryName, setDepth and "
606                               "setDefaultInitialValue,"
607                               "read '" +
608                                   mn + "').");
609     }
610     ++(this->current);
611     this->readSpecifiedToken("ModelDSLCommon::treatOutputMethod", "(");
612     if (mn == "setGlossaryName") {
613       const auto gn = this->readString("ModelDSLCommon::treatOutputMethod");
614       this->md.setGlossaryName(this->currentVar, gn);
615     } else if (mn == "setEntryName") {
616       const auto en = this->readString("ModelDSLCommon::treatOutputMethod");
617       this->md.setEntryName(this->currentVar, en);
618     } else if (mn == "setDefaultInitialValue") {
619       this->checkNotEndOfFile("ModelDSLCommon::treatOutputMethod",
620                               "Expected intial value.");
621       const auto value = this->readDouble();
622       auto& v = this->md.outputs.getVariable(this->currentVar);
623       v.setAttribute(VariableDescription::initialValue, value, false);
624     } else if (mn == "setDepth") {
625       this->checkNotEndOfFile("ModelDSLCommon::treatOutputMethod",
626                               "Expected depth value.");
627       unsigned short value;
628       std::istringstream converter(this->current->value);
629       converter >> value;
630       if (!converter || (!converter.eof())) {
631         this->throwRuntimeError(
632             "ModelDSLCommon::treatOutputMethod",
633             "Could not read depth value of field '" + this->currentVar + "'");
634       }
635       auto& v = this->md.outputs.getVariable(this->currentVar);
636       v.setAttribute(VariableDescription::depth, value, false);
637       for (unsigned short i = 1; i <= value; ++i) {
638         const auto vn = this->currentVar + "_" + std::to_string(i);
639         this->md.registerMemberName(vn);
640         this->md.registerMemberName("f_" + vn);
641         this->md.registerMemberName("ff_" + vn);
642       }
643       ++(this->current);
644     } else {
645       this->throwRuntimeError("ModelDSLCommon::treatOutputMethod",
646                               "Internal error (untreated method '" + mn + "')");
647     }
648     this->readSpecifiedToken("ModelDSLCommon::treatOutputMethod", ")");
649     this->readSpecifiedToken("ModelDSLCommon::treatOutputMethod", ";");
650   }  // end of ModelDSLCommon::treatOutputMethod
651 
treatInputMethod()652   void ModelDSLCommon::treatInputMethod() {
653     if (!this->md.functions.empty()) {
654       this->throwRuntimeError("ModelDSLCommon::treatInputMethod",
655                               "input methods must be called "
656                               "before declaring functions");
657     }
658     this->readSpecifiedToken("ModelDSLCommon::treatInputMethod", ".");
659     this->checkNotEndOfFile("ModelDSLCommon::treatInputMethod",
660                             "Expected method name.");
661     const auto mn = this->current->value;
662     if ((mn != "setGlossaryName") && (mn != "setEntryName") &&
663         (mn != "setDepth")) {
664       this->throwRuntimeError(
665           "ModelDSLCommon::treatInputMethod",
666           "Unknown method (valid methods for input fields are "
667           "setGlossaryName, setEntryName, setDepth"
668           ", read '" +
669               mn + "')");
670     }
671     ++(this->current);
672     this->readSpecifiedToken("ModelDSLCommon::treatInputMethod", "(");
673     if (mn == "setGlossaryName") {
674       const auto gn = this->readString("ModelDSLCommon::treatInputMethod");
675       this->md.setGlossaryName(this->currentVar, gn);
676     } else if (mn == "setEntryName") {
677       const auto en = this->readString("ModelDSLCommon::treatInputMethod");
678       this->md.setEntryName(this->currentVar, en);
679     } else if (mn == "setDepth") {
680       this->checkNotEndOfFile("ModelDSLCommon::treatInputMethod",
681                               "Expected depth value.");
682       unsigned short value;
683       std::istringstream converter(this->current->value);
684       converter >> value;
685       if (!converter || (!converter.eof())) {
686         this->throwRuntimeError(
687             "ModelDSLCommon::treatInputMethod",
688             "Could not read initial value of field '" + this->currentVar + "'");
689       }
690       auto& v = this->md.inputs.getVariable(this->currentVar);
691       v.setAttribute(VariableDescription::depth, value, false);
692       for (unsigned short i = 1; i <= value; ++i) {
693         const auto vn = this->currentVar + "_" + std::to_string(i);
694         this->md.registerMemberName(vn);
695         this->md.registerMemberName("f_" + vn);
696         this->md.registerMemberName("ff_" + vn);
697       }
698       ++(this->current);
699     } else {
700       this->throwRuntimeError("ModelDSLCommon::treatInputMethod",
701                               "Internal error (untreated method '" + mn + "')");
702     }
703     this->readSpecifiedToken("ModelDSLCommon::treatInputMethod", ")");
704     this->readSpecifiedToken("ModelDSLCommon::treatInputMethod", ";");
705   }  // end of ModelDSLCommon::treatInputMethod
706 
treatParameter()707   void ModelDSLCommon::treatParameter() {
708     auto endOfTreatment = false;
709     while ((this->current != this->tokens.end()) && (!endOfTreatment)) {
710       if (!this->isValidIdentifier(this->current->value)) {
711         this->throwRuntimeError("DSLBase::handleParameter : ",
712                                 "variable given is not valid (read '" +
713                                     this->current->value + "').");
714       }
715       const auto n = this->current->value;
716       const auto lineNumber = this->current->line;
717       VariableDescription v("real", n, 1u, lineNumber);
718       ++(this->current);
719       this->checkNotEndOfFile("DSLBase::handleParameter");
720       auto value = this->readInitialisationValue<double>(n, false);
721       if (value.first) {
722         const auto op = this->overriding_parameters.find(n);
723         if (op == this->overriding_parameters.end()) {
724           v.setAttribute(VariableDescription::defaultValue, value.second,
725                          false);
726         } else {
727           v.setAttribute(VariableDescription::defaultValue, op->second, false);
728         }
729       }
730       if (this->current->value == ",") {
731         ++(this->current);
732       } else if (this->current->value == ";") {
733         endOfTreatment = true;
734         ++(this->current);
735       } else {
736         this->throwRuntimeError("DSLBase::handleParameter",
737                                 ", or ; expected after '" + n + "'");
738       }
739       this->md.registerMemberName(v.name);
740       this->md.parameters.push_back(v);
741     }
742     if (!endOfTreatment) {
743       --(this->current);
744       this->throwRuntimeError("DSLBase::handleParameter",
745                               "Expected ';' before end of file");
746     }
747   }  // end of ModelDSLCommon::treatParameter()
748 
treatLocalParameter()749   void ModelDSLCommon::treatLocalParameter() {
750     VariableDescriptionContainer gp;
751     this->readVarList(gp, false);
752     for (const auto& v : gp) {
753       this->md.registerMemberName(v.name);
754       this->md.parameters.push_back(v);
755     }
756   }  // end of ModelDSLCommon::treatLocalParameter()
757 
treatParameterMethod()758   void ModelDSLCommon::treatParameterMethod() {
759     auto throw_if = [this](const bool b, const std::string& m) {
760       if (b) {
761         this->throwRuntimeError("ModelDSLCommon::treatParameterMethod", m);
762       }
763     };
764     this->readSpecifiedToken("ModelDSLCommon::treatParameterMethod", ".");
765     this->checkNotEndOfFile("ModelDSLCommon::treatParameterMethod",
766                             "Expected method name.");
767     const auto mn = this->current->value;
768     throw_if((mn != "setGlossaryName") && (mn != "setEntryName") &&
769                  (mn != "setDefaultValue"),
770              "unknown method (valid methods for local parameters are "
771              "setGlossaryName, setEntryName and setDefaultValue"
772              ", read " +
773                  mn + ").");
774     ++(this->current);
775     this->readSpecifiedToken("ModelDSLCommon::treatParameterMethod", "(");
776     if (mn == "setGlossaryName") {
777       const auto gn = this->readString("ModelDSLCommon::treatInputMethod");
778       this->md.setGlossaryName(this->currentVar, gn);
779     } else if (mn == "setEntryName") {
780       const auto en = this->readString("ModelDSLCommon::treatInputMethod");
781       this->md.setEntryName(this->currentVar, en);
782     } else if (mn == "setDefaultValue") {
783       this->readDefaultValue();
784     }
785     this->readSpecifiedToken("ModelDSLCommon::treatParameterMethod", ")");
786     this->readSpecifiedToken("ModelDSLCommon::treatParameterMethod", ";");
787   }  // end of ModelDSLCommon::treatParameterMethod
788 
treatConstantMaterialProperty()789   void ModelDSLCommon::treatConstantMaterialProperty() {
790     VariableDescriptionContainer cmp;
791     this->readVarList(cmp, "real", false);
792     for (const auto& mp : cmp) {
793       this->md.registerMemberName(mp.name);
794       this->md.constantMaterialProperties.push_back(mp);
795     }
796   }  // end of ModelDSLCommon::treatConstantMaterialProperty()
797 
treatConstantMaterialPropertyMethod()798   void ModelDSLCommon::treatConstantMaterialPropertyMethod() {
799     auto throw_if = [this](const bool b, const std::string& m) {
800       if (b) {
801         this->throwRuntimeError(
802             "ModelDSLCommon::treatConstantMaterialPropertyMethod", m);
803       }
804     };
805     this->readSpecifiedToken(
806         "ModelDSLCommon::treatConstantMaterialPropertyMethod", ".");
807     this->checkNotEndOfFile(
808         "ModelDSLCommon::treatConstantMaterialPropertyMethod",
809         "Expected method name.");
810     const auto mn = this->current->value;
811     ++(this->current);
812     this->readSpecifiedToken(
813         "ModelDSLCommon::treatConstantMaterialPropertyMethod", "(");
814     if (mn == "setGlossaryName") {
815       const auto gn = this->readString(
816           "ModelDSLCommon::treatConstantMaterialPropertyMethod");
817       this->md.setGlossaryName(this->currentVar, gn);
818     } else if (mn == "setEntryName") {
819       const auto en = this->readString(
820           "ModelDSLCommon::treatConstantMaterialPropertyMethod");
821       this->md.setEntryName(this->currentVar, en);
822     } else {
823       throw_if(
824           true,
825           "unknown method (valid methods for constant material properties are "
826           "setGlossaryName and setEntryName, read " +
827               mn + ").");
828     }
829     this->readSpecifiedToken(
830         "ModelDSLCommon::treatConstantMaterialPropertyMethod", ")");
831     this->readSpecifiedToken(
832         "ModelDSLCommon::treatConstantMaterialPropertyMethod", ";");
833   }  // end of ModelDSLCommon::treatConstantMaterialPropertyMethod
834 
readDefaultValue()835   void ModelDSLCommon::readDefaultValue() {
836     auto throw_if = [this](const bool b, const std::string& m) {
837       if (b) {
838         this->throwRuntimeError("ModelDSLCommon::readDefaultValue", m);
839       }
840     };
841     auto& v = this->md.parameters.getVariable(this->currentVar);
842     this->checkNotEndOfFile("ModelDSLCommon::readDefaultValue",
843                             "Expected default value.");
844     if (v.type == "DoubleArray") {
845       const auto values =
846           this->readArrayOfDouble("ModelDSLCommon::readDefaultValue");
847       v.setAttribute(VariableDescription::defaultValue, values, false);
848     } else if (v.type == "StringArray") {
849       const auto values =
850           this->readArrayOfDouble("ModelDSLCommon::readDefaultValue");
851       v.setAttribute(VariableDescription::defaultValue, values, false);
852     } else if ((v.type == "double") || (v.type == "real")) {
853       const auto value = this->readDouble();
854       const auto op = this->overriding_parameters.find(v.name);
855       if (op == this->overriding_parameters.end()) {
856         v.setAttribute(VariableDescription::defaultValue, value, false);
857       } else {
858         v.setAttribute(VariableDescription::defaultValue, op->second, false);
859       }
860     } else if (v.type == "string") {
861       const auto value = this->readString("ModelDSLCommon::readDefaultValue");
862       v.setAttribute(VariableDescription::defaultValue, value, false);
863     } else if (v.type == "bool") {
864       const auto b = this->readBooleanValue("ModelDSLCommon::readDefaultValue");
865       v.setAttribute(VariableDescription::defaultValue, b, false);
866     } else {
867       throw_if(true, "type '" + v.type + "' is not supported.");
868     }
869   }  // end of ModelDSLCommon::readDefaultValue
870 
treatBounds()871   void ModelDSLCommon::treatBounds() {
872     const auto b = mfront::readVariableBounds(this->current, this->end());
873     if (this->md.outputs.contains(b.first)) {
874       this->md.outputs.getVariable(b.first).setBounds(b.second);
875     } else if (this->md.inputs.contains(b.first)) {
876       this->md.inputs.getVariable(b.first).setBounds(b.second);
877     } else {
878       this->throwRuntimeError("ModelDSLCommon::treatBounds",
879                               "no variable named '" + b.first + "'");
880     }
881     this->readSpecifiedToken("ModelDSLCommon::treatBounds", ";");
882   }  // end of ModelDSLCommon::treatBounds
883 
treatPhysicalBounds()884   void ModelDSLCommon::treatPhysicalBounds() {
885     const auto b = mfront::readVariableBounds(this->current, this->end());
886     if (this->md.outputs.contains(b.first)) {
887       this->md.outputs.getVariable(b.first).setPhysicalBounds(b.second);
888     } else if (this->md.inputs.contains(b.first)) {
889       this->md.inputs.getVariable(b.first).setPhysicalBounds(b.second);
890     } else {
891       this->throwRuntimeError("ModelDSLCommon::treatPhysicalBounds",
892                               "no variable named '" + b.first + "'");
893     }
894     this->readSpecifiedToken("ModelDSLCommon::treatPhysicalBounds", ";");
895   }  // end of ModelDSLCommon::treatPhysicalBounds
896 
addMaterialLaw(const std::string & m)897   void ModelDSLCommon::addMaterialLaw(const std::string& m) {
898     this->md.addMaterialLaw(m);
899   }  // end of ModelDSLCommon::addMaterialLaw
900 
appendToIncludes(const std::string & c)901   void ModelDSLCommon::appendToIncludes(const std::string& c) {
902     this->md.appendToIncludes(c);
903   }  // end of ModelDSLCommon::appendToIncludes
904 
appendToMembers(const std::string & c)905   void ModelDSLCommon::appendToMembers(const std::string& c) {
906     this->md.appendToMembers(c);
907   }  // end of ModelDSLCommon::appendToMembers
908 
appendToPrivateCode(const std::string & c)909   void ModelDSLCommon::appendToPrivateCode(const std::string& c) {
910     this->md.appendToPrivateCode(c);
911   }  // end of ModelDSLCommon::appendToPrivateCode
912 
appendToSources(const std::string & c)913   void ModelDSLCommon::appendToSources(const std::string& c) {
914     this->md.appendToSources(c);
915   }  // end of ModelDSLCommon::appendToSources
916 
917   ModelDSLCommon::~ModelDSLCommon() = default;
918 
919 }  // end of namespace mfront
920