1 /*! 2 * \file mfront/src/DSLBase.cxx 3 * \brief 4 * 5 * \author Thomas Helfer 6 * \date 04 jun 2007 7 * \copyright Copyright (C) 2006-2018 CEA/DEN, EDF R&D. All rights 8 * reserved. 9 * This project is publicly released under either the GNU GPL Licence 10 * or the CECILL-A licence. A copy of thoses licences are delivered 11 * with the sources of TFEL. CEA or EDF may also distribute this 12 * project under specific licensing conditions. 13 */ 14 15 #include <cctype> 16 #include <iterator> 17 #include <sstream> 18 #include <stdexcept> 19 #include <algorithm> 20 21 #ifdef MFRONT_HAVE_MADNEX 22 #include "Madnex/MFrontImplementation.hxx" 23 #endif /* MFRONT_HAVE_MADNEX */ 24 25 #include "TFEL/Raise.hxx" 26 #include "TFEL/Math/IntegerEvaluator.hxx" 27 #include "TFEL/UnicodeSupport/UnicodeSupport.hxx" 28 #include "TFEL/Utilities/StringAlgorithms.hxx" 29 30 #include "MFront/MFront.hxx" 31 #include "MFront/PedanticMode.hxx" 32 #include "MFront/SupportedTypes.hxx" 33 #include "MFront/DSLBase.hxx" 34 #include "MFront/SearchPathsHandler.hxx" 35 #include "MFront/DSLUtilities.hxx" 36 #include "MFront/MFrontUtilities.hxx" 37 #include "MFront/MFrontDebugMode.hxx" 38 #include "MFront/MFrontLogStream.hxx" 39 #include "MFront/MFrontMaterialPropertyInterface.hxx" 40 #include "MFront/StaticVariableDescription.hxx" 41 #include "MFront/MaterialPropertyDSL.hxx" 42 43 // fixing a bug on current glibc++ cygwin versions (19/08/2015) 44 #if defined __CYGWIN__ && (!defined _GLIBCXX_USE_C99) 45 #include <sstream> 46 namespace std { 47 template <typename T> to_string(const T & v)48 std::string to_string(const T& v) { 49 std::ostringstream s; 50 s << v; 51 return s.str(); 52 } 53 } 54 #endif /* defined __CYGWIN__ && (!defined _GLIBCXX_USE_C99) */ 55 56 namespace mfront { 57 isInteger(const std::string & s)58 static bool isInteger(const std::string& s) { 59 const auto s2 = [&s]() -> std::string { 60 if (s.empty()) { 61 return s; 62 } 63 if ((s.back() == 'u') || (s.back() == 'U') || (s.back() == 'l') || (s.back() == 'L')) { 64 return s.substr(0, s.size() - 1); 65 } else if ((tfel::utilities::ends_with(s, "ul")) || (tfel::utilities::ends_with(s, "uL")) || 66 (tfel::utilities::ends_with(s, "lu")) || (tfel::utilities::ends_with(s, "Lu")) || 67 (tfel::utilities::ends_with(s, "Ul")) || (tfel::utilities::ends_with(s, "UL")) || 68 (tfel::utilities::ends_with(s, "lU")) || (tfel::utilities::ends_with(s, "LU"))) { 69 return s.substr(0, s.size() - 2); 70 } 71 return s; 72 }(); 73 if (s2.empty()) { 74 return false; 75 } 76 for (const auto c : s2) { 77 if (!std::isdigit(c)) { 78 return false; 79 } 80 } 81 return true; 82 } 83 isValidMaterialName(const std::string & n)84 bool isValidMaterialName(const std::string& n) { 85 return tfel::utilities::CxxTokenizer::isValidIdentifier(n, true); 86 } 87 isValidLibraryName(const std::string & n)88 bool isValidLibraryName(const std::string& n) { 89 return tfel::utilities::CxxTokenizer::isValidIdentifier(n, true); 90 } 91 92 DSLBase::VariableModifier::~VariableModifier() = default; 93 94 DSLBase::WordAnalyser::~WordAnalyser() = default; 95 96 DSLBase::CodeBlockParserOptions::CodeBlockParserOptions() = default; 97 98 DSLBase::CodeBlockParserOptions::~CodeBlockParserOptions() noexcept = default; 99 DSLBase()100 DSLBase::DSLBase() { 101 this->addSeparator("\u2297"); 102 this->addSeparator("\u22C5"); 103 } // end of DSLBase::DSLBase 104 getDefaultReservedNames()105 std::vector<std::string> DSLBase::getDefaultReservedNames() { 106 auto names = std::vector<std::string>{}; 107 // names of the c++ standard 108 names.insert(names.end(), 109 {"std", "cout", "cerr", "endl", "cos", "sin", "tan", "acos", "asin", 110 "atan", "atan2", "cosh", "sinh", "tanh", "acosh", "asinh", "atanh", "exp", 111 "frexp", "ldexp", "log", "log10", "modf", "exp2", "expm1", "ilogb", "log1p", 112 "log2", "logb", "scalbn", "scalbln", "pow", "sqrt", "cbrt", "hypot", "erf", 113 "erfc", "tgamma", "lgamma", "abs", "string", "ofstream", "ostringstream", "ifstream"}); 114 // tfel namespaces 115 names.insert(names.end(), {"tfel", "math", "material", "utilities", "exception", "glossary"}); 116 for (const auto& v : SupportedTypes::getTypeFlags()) { 117 names.push_back(v.first); 118 } 119 names.insert(names.end(), {"policy", "errno", "mfront_errno", "mfront_errno_old"}); 120 return names; 121 } 122 getTemporaryVariableName(std::vector<std::string> & tmpnames,const std::string & p) const123 std::string DSLBase::getTemporaryVariableName( 124 std::vector<std::string>& tmpnames, const std::string& p) const { 125 if (!this->isValidIdentifier(p)) { 126 this->throwRuntimeError("DSLBase::getTemporaryVariableName", 127 "invalid variable prefix '" + p + "'"); 128 } 129 for (size_type i = 0; i != std::numeric_limits<size_type>::max(); ++i) { 130 const auto c = p + std::to_string(i); 131 if (!this->isNameReserved(c)) { 132 if (std::find(tmpnames.begin(), tmpnames.end(), c) == tmpnames.end()) { 133 tmpnames.push_back(c); 134 return c; 135 } 136 } 137 } 138 this->throwRuntimeError("DSLBase::getTemporaryVariableName", 139 "unable to find a temporary variable"); 140 } 141 openFile(const std::string & f,const std::vector<std::string> & ecmds,const std::map<std::string,std::string> & s)142 void DSLBase::openFile(const std::string& f, 143 const std::vector<std::string>& ecmds, 144 const std::map<std::string, std::string>& s) { 145 if (tfel::utilities::starts_with(f, "madnex:")) { 146 #ifdef MFRONT_HAVE_MADNEX 147 const auto path = decomposeImplementationPathInMadnexFile(f); 148 const auto& material = std::get<2>(path); 149 const auto& name = std::get<3>(path); 150 const auto impl = madnex::getMFrontImplementation( 151 std::get<0>(path), std::get<1>(path), material, name); 152 this->overrideMaterialKnowledgeIdentifier(name); 153 if (!material.empty()) { 154 this->overrideMaterialName(material); 155 } 156 if (!impl.metadata.author.empty()) { 157 this->overrideAuthorName(impl.metadata.author); 158 } 159 if (!impl.metadata.date.empty()) { 160 this->overrideDate(impl.metadata.date); 161 } 162 if (!impl.metadata.description.empty()) { 163 this->overrideDescription(impl.metadata.description); 164 } 165 for (const auto& p: impl.parameters) { 166 this->overrideByAParameter(p.first, p.second); 167 } 168 CxxTokenizer::parseString(impl.source); 169 #else /* MFRONT_HAVE_MADNEX */ 170 tfel::raise("DSLBase::openFile: madnex support was not enabled"); 171 #endif /* MFRONT_HAVE_MADNEX */ 172 } else { 173 CxxTokenizer::openFile(f); 174 } 175 // substitutions 176 const auto pe = s.end(); 177 for (auto& t : this->tokens) { 178 auto p = s.find(t.value); 179 if (p != pe) { 180 t.value = p->second; 181 } 182 } 183 // treating external commands 184 for (const auto& c : ecmds) { 185 CxxTokenizer t; 186 try { 187 t.parseString(c); 188 } catch (std::exception& e) { 189 tfel::raise( 190 "DSLBase::openFile : " 191 "error while parsing external command " 192 "'" + 193 c + "'\n" + std::string(e.what())); 194 } 195 this->tokens.insert(this->tokens.begin(), t.begin(), t.end()); 196 } 197 } // end of DSLBase::openFile 198 getFileDescription() const199 const FileDescription& DSLBase::getFileDescription() const { 200 return this->fd; 201 } // end of DSLBase::getFileDescription 202 getTargetsDescription() const203 const TargetsDescription& DSLBase::getTargetsDescription() const { 204 return this->td; 205 } // end of DSLBase::getTargetsDescription 206 207 DSLBase::~DSLBase() = default; 208 readNextBlock(CodeBlock & res1,CodeBlock & res2,const CodeBlockParserOptions & o1,const CodeBlockParserOptions & o2)209 void DSLBase::readNextBlock(CodeBlock& res1, 210 CodeBlock& res2, 211 const CodeBlockParserOptions& o1, 212 const CodeBlockParserOptions& o2) { 213 auto pb = this->current; 214 res1 = this->readNextBlock(o1); 215 this->current = pb; 216 res2 = this->readNextBlock(o2); 217 } // end of DSLBase::readNextBlock 218 readNextBlock(const CodeBlockParserOptions & options)219 CodeBlock DSLBase::readNextBlock(const CodeBlockParserOptions& options) { 220 using tfel::utilities::Token; 221 auto addSpaceBetweenToken = [this]( 222 std::string& r, const TokensContainer::const_iterator c, 223 const TokensContainer::const_iterator n) { 224 if ((n == this->tokens.end()) || (n->line != c->line)) { 225 return; 226 } 227 const auto csize = [&c] { 228 if ((c->flag == Token::String) || (c->flag == Token::Char)) { 229 return c->value.size() + 2; 230 } 231 return c->value.size(); 232 }(); 233 if (n->offset > c->offset + csize) { 234 const auto d = n->offset - csize - c->offset; 235 r += std::string(d, ' '); 236 } 237 }; 238 const auto& smn = options.smn; 239 const auto& mn = options.mn; 240 const auto& delim1 = options.delim1; 241 const auto& delim2 = options.delim2; 242 const auto addThisPtr = options.qualifyMemberVariables; 243 const auto addClassName = options.qualifyStaticVariables; 244 const auto allowSemiColon = options.allowSemiColon; 245 const auto registerLine = options.registerLine; 246 auto demangle = [&options](const Token& t) { 247 if (t.flag != Token::Standard) { 248 return t.value; 249 } 250 const auto p = options.symbols.find(t.value); 251 if (p != options.symbols.end()) { 252 return p->second; 253 } 254 return tfel::unicode::getMangledString(t.value); 255 }; 256 auto modifier = options.modifier; 257 auto analyser = options.analyser; 258 CodeBlock b; 259 if (!this->currentComment.empty()) { 260 b.description += this->currentComment; 261 } 262 auto& res = b.code; 263 unsigned int openedBlock = 0; 264 this->readSpecifiedToken("DSLBase::readNextBlock", delim1); 265 this->checkNotEndOfFile("DSLBase::readNextBlock", "Expected a '" + delim2 + "'."); 266 if ((this->current->value == ";") && (!allowSemiColon)) { 267 this->throwRuntimeError("DSLBase::readNextBlock", 268 "read ';' before the end of block.\n" 269 "Number of block opened : " + 270 std::to_string(openedBlock)); 271 } 272 if (this->current->value == delim1) { 273 ++openedBlock; 274 } 275 if (this->current->value == delim2) { 276 ++(this->current); 277 return b; 278 } 279 auto currentLine = this->current->line; 280 if ((registerLine) && (!getDebugMode())) { 281 res = "#line "; 282 res += std::to_string(currentLine); 283 res += " \""; 284 res += this->fd.fileName; 285 res += "\"\n"; 286 } 287 if (!this->current->comment.empty()) { 288 if (!b.description.empty()) { 289 b.description += '\n'; 290 } 291 b.description += this->current->comment; 292 } 293 auto currentValue = demangle(*(this->current)); 294 if (analyser != nullptr) { 295 analyser->exe(b, currentValue); 296 } 297 if (smn.find(currentValue) != smn.end()) { 298 b.staticMembers.insert(currentValue); 299 const auto previous = std::prev(this->current); 300 if ((previous->value != "->") && (previous->value != ".") && 301 (previous->value != "::")) { 302 if (addClassName) { 303 res += this->getClassName(); 304 res += "::"; 305 } 306 } 307 res += currentValue; 308 } else if (mn.find(currentValue) != mn.end()) { 309 b.members.insert(currentValue); 310 auto cv = std::string{}; 311 auto previous = std::prev(this->current); 312 if ((previous->value == "->") || (previous->value == ".") || 313 (previous->value == "::")) { 314 cv = currentValue; 315 } else { 316 if (modifier != nullptr) { 317 cv = modifier->exe(currentValue, addThisPtr); 318 } else { 319 if (addThisPtr) { 320 cv = "this->" + currentValue; 321 } else { 322 cv = currentValue; 323 } 324 } 325 } 326 previous = std::prev(this->current); 327 if (previous->value == "*") { 328 res += "(" + cv+ ")"; 329 } else { 330 res += cv; 331 } 332 } else { 333 res += currentValue; 334 } 335 addSpaceBetweenToken(res, this->current, std::next(this->current)); 336 ++(this->current); 337 while ((this->current != this->tokens.end()) && (!((this->current->value == delim2) && (openedBlock == 0)))) { 338 currentValue = demangle(*(this->current)); 339 if (currentLine != this->current->line) { 340 currentLine = this->current->line; 341 if ((registerLine) && (!getDebugMode())) { 342 res += "\n"; 343 res += "#line "; 344 res += std::to_string(currentLine); 345 res += " \""; 346 res += this->fd.fileName; 347 res += "\"\n"; 348 } else { 349 res += "\n"; 350 } 351 } 352 if ((currentValue == ";") && (!allowSemiColon)) { 353 this->throwRuntimeError("DSLBase::readNextBlock", 354 "read ';' before the end of block.\n" 355 "Number of block opened : " + 356 std::to_string(openedBlock)); 357 } 358 if (!this->current->comment.empty()) { 359 if (!b.description.empty()) { 360 b.description += '\n'; 361 } 362 b.description += this->current->comment; 363 } 364 if (analyser != nullptr) { 365 analyser->exe(b, currentValue); 366 } 367 if (smn.find(currentValue) != smn.end()) { 368 b.staticMembers.insert(currentValue); 369 const auto previous = std::prev(this->current); 370 if ((previous->value != "->") && (previous->value != ".") && (previous->value != "::")) { 371 if (addClassName) { 372 res += this->getClassName(); 373 res += "::"; 374 } 375 } 376 res += currentValue; 377 } else if (mn.find(currentValue) != mn.end()) { 378 b.members.insert(currentValue); 379 auto cv = std::string{}; 380 const auto previous = std::prev(this->current); 381 if ((previous->value == "->") || (previous->value == ".") || (previous->value == "::")) { 382 cv = currentValue; 383 } else { 384 if (modifier != nullptr) { 385 cv = modifier->exe(currentValue, addThisPtr); 386 } else { 387 if (addThisPtr) { 388 if (previous->value == "*") { 389 cv = "(this->" + currentValue + ')'; 390 } else { 391 cv = "this->" + currentValue; 392 } 393 } else { 394 cv = currentValue; 395 } 396 } 397 } 398 res += cv; 399 } else { 400 res += currentValue; 401 } 402 addSpaceBetweenToken(res, this->current, std::next(this->current)); 403 if (currentValue == delim1) { 404 ++openedBlock; 405 } 406 if (currentValue == delim2) { 407 --openedBlock; 408 } 409 ++(this->current); 410 } 411 if (this->current == this->tokens.end()) { 412 --(this->current); 413 this->throwRuntimeError("DSLBase::readNextBlock", 414 "Expected the end of a block.\n" 415 "Number of block opened : " + 416 std::to_string(openedBlock)); 417 } 418 ++(this->current); 419 return b; 420 } // end of DSLBase::readNextBlock 421 throwRuntimeError(const std::string & m,const std::string & e) const422 void DSLBase::throwRuntimeError(const std::string& m, 423 const std::string& e) const { 424 auto msg = m; 425 if (!e.empty()) { 426 msg += ": " + e; 427 } 428 if (!this->tokens.empty()) { 429 auto t = this->current; 430 if (t == this->tokens.end()) { 431 --t; 432 } 433 msg += "\nError at line " + std::to_string(t->line); 434 } 435 tfel::raise(msg); 436 } // end of DSLBase::throwRuntimeError 437 treatImport()438 void DSLBase::treatImport() { 439 const auto m = "DSLBase::treatImport"; 440 const auto oFileName = this->fd.fileName; 441 this->checkNotEndOfFile(m); 442 const auto& files = this->readStringOrArrayOfString(m); 443 this->checkNotEndOfFile(m); 444 this->readSpecifiedToken(m, ";"); 445 TokensContainer oFileTokens; 446 oFileTokens.swap(this->tokens); 447 const auto ocurrent = this->current; 448 for (const auto& f : files) { 449 this->importFile(SearchPathsHandler::search(f), std::vector<std::string>(), {}); 450 } 451 this->fd.fileName = oFileName; 452 this->tokens.swap(oFileTokens); 453 this->current = ocurrent; 454 } 455 checkNotEndOfFile(const std::string & m,const std::string & e) const456 void DSLBase::checkNotEndOfFile(const std::string& m, const std::string& e) const { 457 if (this->current == this->tokens.end()) { 458 auto msg = std::string{}; 459 msg += "unexpected end of file."; 460 if (!e.empty()) { 461 msg += "\n" + e; 462 } 463 if (!this->tokens.empty()) { 464 const auto previous = std::prev(this->current); 465 msg += "\nError at line " + std::to_string(previous->line); 466 } 467 this->throwRuntimeError(m, msg); 468 } 469 } // end of DSLBase::checkNotEndOfFile 470 readUnsignedShort(const std::string & m)471 unsigned short DSLBase::readUnsignedShort(const std::string& m) { 472 this->checkNotEndOfFile(m, "Cannot read unsigned short value."); 473 unsigned short value; 474 std::istringstream flux(current->value); 475 flux >> value; 476 if ((flux.fail()) || (!flux.eof())) { 477 this->throwRuntimeError(m, "Failed to read unsigned short value."); 478 } 479 ++(this->current); 480 return value; 481 } // end of DSLBase::readUnsignedShort 482 readSpecifiedToken(const std::string & m,const std::string & v)483 void DSLBase::readSpecifiedToken(const std::string& m, const std::string& v) { 484 this->checkNotEndOfFile(m, "expected '" + v + "'."); 485 if (this->current->value != v) { 486 this->throwRuntimeError(m, "expected '" + v + 487 "', " 488 "read '" + 489 this->current->value + 490 "'.\n" 491 "Error at line: " + 492 std::to_string(this->current->line)); 493 } 494 ++(this->current); 495 } // end of DSLBase::readSpecifiedToken 496 readUntilEndOfInstruction()497 std::string DSLBase::readUntilEndOfInstruction() { 498 auto res = std::string{}; 499 while ((this->current != this->tokens.end()) && (this->current->value != ";")) { 500 if (!this->current->value.empty()) { 501 if (this->current->value[0] == '@') { 502 this->throwRuntimeError("DSLBase::readUntilEndOfInstruction", "no word beginning with '@' are allowed here"); 503 } 504 res += this->current->value; 505 res += " "; 506 } 507 ++(this->current); 508 } 509 this->checkNotEndOfFile("DSLBase::readUntilEndOfInstruction", "Missing ';' delimiter."); 510 if (!res.empty()) { 511 res.erase(res.length() - 1); 512 } 513 ++(this->current); 514 return res; 515 } 516 readOnlyOneToken()517 std::string DSLBase::readOnlyOneToken() { 518 this->checkNotEndOfFile("DSLBase::readOnlyOneToken", "Expected a word."); 519 if (this->current->value == ";") { 520 this->throwRuntimeError("DSLBase::readOnlyOneToken", "no word read"); 521 } 522 const auto res = this->current->value; 523 ++(this->current); 524 this->readSpecifiedToken("DSLBase::readOnlyOneToken", ";"); 525 return res; 526 } // end of DSLBase::readOnlyOneToken 527 readVariableBounds()528 std::pair<std::string, VariableBoundsDescription> DSLBase::readVariableBounds() { 529 return mfront::readVariableBounds(this->current, this->end()); 530 } // end of DSLBase::readVariableBounds 531 registerIntegerConstant(const std::string & n,const size_t l,const int v)532 void DSLBase::registerIntegerConstant(const std::string& n, const size_t l, const int v) { 533 if (!this->isValidIdentifier(n)) { 534 this->throwRuntimeError("DSLBase::registerIntegerConstant", "Variable name '" + n + "' is not valid."); 535 } 536 this->addStaticVariableDescription(StaticVariableDescription("int", n, l, v)); 537 } // end of DSLBase::registerIntegerConstant 538 treatIntegerConstant()539 void DSLBase::treatIntegerConstant() { 540 this->checkNotEndOfFile("DSLBase::treatIntegerConstant", "Cannot read type of static variable."); 541 const auto name = this->current->value; 542 const auto line = this->current->line; 543 ++(this->current); 544 const auto value = this->readInitialisationValue<int>(name, true); 545 this->readSpecifiedToken("DSLBase::treatIntegerConstant", ";"); 546 this->registerIntegerConstant(name, line, value.second); 547 } // end of DSLBase::treatIntegerConstant 548 readArrayOfVariablesSize(const std::string & n,const bool b)549 unsigned int DSLBase::readArrayOfVariablesSize(const std::string& n, 550 const bool b) { 551 auto throw_if = [this](const bool c, const std::string& m) { 552 if (c) { 553 this->throwRuntimeError("DSLBase::readArrayOfVariablesSize", m); 554 } 555 }; 556 this->checkNotEndOfFile("DSLBase::readArrayOfVariablesSize"); 557 unsigned int asize = 1u; 558 if (this->current->value == "[") { 559 throw_if(!b, "variable '" + n + "' can't be declared an array"); 560 auto array_size = std::string{}; 561 ++(this->current); 562 this->checkNotEndOfFile("DSLBase::readArrayOfVariablesSize"); 563 while (this->current->value != "]") { 564 throw_if(((this->current->flag != tfel::utilities::Token::Standard) && 565 (this->current->flag != tfel::utilities::Token::Number)) || 566 (this->current->value == ";"), 567 "invalid array size for '" + n + "'"); 568 array_size += this->current->value; 569 ++(this->current); 570 this->checkNotEndOfFile("DSLBase::readArrayOfVariablesSize"); 571 } 572 throw_if(array_size.empty(), "empty array size for '" + n + "'"); 573 tfel::math::IntegerEvaluator ev(array_size); 574 const auto& vars = ev.getVariablesNames(); 575 for (const auto& v : vars) { 576 ev.setVariableValue(v, this->getIntegerConstant(v)); 577 } 578 const auto iv = ev.getValue(); 579 throw_if(iv <= 0, "invalid array size for '" + n + "'"); 580 asize = static_cast<unsigned int>(iv); 581 this->readSpecifiedToken("DSLBase::readArrayOfVariablesSize", "]"); 582 this->checkNotEndOfFile("DSLBase::readArrayOfVariablesSize"); 583 } 584 return asize; 585 } // end of DSLBase::readArrayOfVariablesSize 586 readVarList(VariableDescriptionContainer & cont,const std::string & type,const bool allowArray)587 void DSLBase::readVarList(VariableDescriptionContainer& cont, 588 const std::string& type, 589 const bool allowArray) { 590 auto throw_if = [this](const bool b, const std::string& m) { 591 if (b) { 592 this->throwRuntimeError("DSLBase::readVarList", m); 593 } 594 }; 595 auto endComment = std::string{}; 596 auto endOfTreatment = false; 597 while ((this->current != this->tokens.end()) && (!endOfTreatment)) { 598 const auto sname = this->current->value; 599 const auto vname = tfel::unicode::getMangledString(sname); 600 throw_if(!this->isValidIdentifier(vname), 601 "variable given is not valid (read '" + sname + "')."); 602 auto lineNumber = this->current->line; 603 ++(this->current); 604 this->checkNotEndOfFile("DSLBase::readVarList"); 605 const auto asize = this->readArrayOfVariablesSize(sname, allowArray); 606 if (this->current->value == ",") { 607 ++(this->current); 608 } else if (this->current->value == ";") { 609 endOfTreatment = true; 610 endComment = this->current->comment; 611 ++(this->current); 612 } else { 613 throw_if(true, "',' or ';' expected after '" + sname + "'"); 614 } 615 if (vname == sname) { 616 cont.push_back(VariableDescription(type, vname, asize, lineNumber)); 617 } else { 618 cont.push_back( 619 VariableDescription(type, sname, vname, asize, lineNumber)); 620 } 621 if (!this->currentComment.empty()) { 622 cont.back().description = this->currentComment; 623 } 624 } 625 if (!endComment.empty()) { 626 for (auto& c : cont) { 627 if (!c.description.empty()) { 628 c.description += ' '; 629 } 630 c.description += endComment; 631 } 632 } 633 if (!endOfTreatment) { 634 --(this->current); 635 throw_if(true, "expected ';' before end of file"); 636 } 637 } 638 readType()639 std::pair<std::string, bool> DSLBase::readType() { 640 auto throw_if = [this](const bool b, const std::string& m) { 641 if (b) { 642 this->throwRuntimeError("DSLBase::readType", m); 643 } 644 }; 645 auto type = this->current->value; 646 throw_if(!this->isValidIdentifier(type, false), "given type '" + type + "' is not valid."); 647 ++(this->current); 648 this->checkNotEndOfFile("DSLBase::readType"); 649 while (this->current->value == "::") { 650 ++(this->current); 651 this->checkNotEndOfFile("DSLBase::readType"); 652 const auto t = this->current->value; 653 throw_if(!this->isValidIdentifier(t, false), "given type '" + t + "' is not valid."); 654 type += "::" + t; 655 ++(this->current); 656 this->checkNotEndOfFile("DSLBase::readType"); 657 } 658 if (this->current->value == "<") { 659 bool b = false; 660 type += "<"; 661 this->checkNotEndOfFile("DSLBase::readType"); 662 ++(this->current); 663 this->checkNotEndOfFile("DSLBase::readType"); 664 bool c = true; 665 while (c) { 666 if (isInteger(this->current->value)) { 667 type += this->current->value; 668 ++(this->current); 669 } else { 670 const auto r = this->readType(); 671 type += r.first; 672 b = r.second; 673 if (!b) { 674 c = false; 675 } 676 } 677 if (c) { 678 this->checkNotEndOfFile("DSLBase::readType"); 679 if (this->current->value == ",") { 680 this->readSpecifiedToken("DSLBase::readType", ","); 681 type += ","; 682 } else { 683 c = false; 684 } 685 } 686 } 687 if (b) { 688 if (this->current->value == ">>") { 689 ++(this->current); 690 return {type + '>', false}; 691 } else { 692 this->readSpecifiedToken("DSLBase::readType", ">"); 693 } 694 type += ">"; 695 } 696 } 697 return {type, true}; 698 } // end of DSLBase::readType 699 readVarList(VariableDescriptionContainer & cont,const bool allowArray)700 void DSLBase::readVarList(VariableDescriptionContainer& cont, const bool allowArray) { 701 this->checkNotEndOfFile("DSLBase::readVarList", "Cannot read type of varName.\n"); 702 const auto r = this->readType(); 703 if (!r.second) { 704 this->throwRuntimeError("DSLBase::readVarList", "unbalanced '>'"); 705 } 706 this->readVarList(cont, r.first, allowArray); 707 } // end of DSLBase::readVarList 708 readList(const std::string & m,const std::string & db,const std::string & de,const bool b)709 std::vector<tfel::utilities::Token> DSLBase::readList(const std::string& m, 710 const std::string& db, 711 const std::string& de, 712 const bool b) { 713 std::vector<tfel::utilities::Token> t; 714 this->readList(t, m, db, de, b); 715 return t; 716 } // end of DSLBase::readList 717 readList(std::vector<tfel::utilities::Token> & l,const std::string & m,const std::string & db,const std::string & de,const bool b)718 void DSLBase::readList(std::vector<tfel::utilities::Token>& l, 719 const std::string& m, 720 const std::string& db, 721 const std::string& de, 722 const bool b) { 723 l.clear(); 724 this->checkNotEndOfFile(m, "Expected '" + db + "'"); 725 if (this->current == this->tokens.end()) { 726 if (b) { 727 return; 728 } 729 } 730 this->checkNotEndOfFile(m, "Expected '" + db + "'"); 731 if (this->current->value != db) { 732 return; 733 } 734 this->readSpecifiedToken(m, db); 735 this->checkNotEndOfFile(m, "Expected '" + de + "'"); 736 while (this->current->value != de) { 737 l.push_back(*(this->current)); 738 ++(this->current); 739 this->checkNotEndOfFile(m, "Expected '" + de + "'"); 740 if (!((this->current->value == de) || (this->current->value == ","))) { 741 this->throwRuntimeError(m, "Expected ',' or '" + de + 742 "'," 743 " read '" + 744 this->current->value + "'"); 745 } 746 if (this->current->value == ",") { 747 ++(this->current); 748 this->checkNotEndOfFile(m, "Expected '" + de + "'"); 749 if (this->current->value == de) { 750 this->throwRuntimeError(m, "Expected a new item"); 751 } 752 } 753 } 754 ++(this->current); 755 } // end of DSLBase::readList 756 readArrayOfString(const std::string & m)757 std::vector<std::string> DSLBase::readArrayOfString(const std::string& m) { 758 auto r = std::vector<std::string>{}; 759 auto as = std::vector<tfel::utilities::Token>{}; 760 this->readList(as, m, "{", "}", false); 761 r.reserve(as.size()); 762 for (const auto& t : as) { 763 if (t.flag != tfel::utilities::Token::String) { 764 this->throwRuntimeError(m, "Expected a string"); 765 } 766 r.push_back(t.value.substr(1, t.value.size() - 2)); 767 } 768 return r; 769 } // end of DSLBase::readArrayOfString 770 readArrayOfDouble(const std::string & m)771 std::vector<double> DSLBase::readArrayOfDouble(const std::string& m) { 772 auto r = std::vector<double>{}; 773 auto as = std::vector<tfel::utilities::Token>{}; 774 this->readList(as, m, "{", "}", false); 775 r.reserve(as.size()); 776 for (const auto& t : as) { 777 r.push_back(tfel::utilities::convert<double>(t.value)); 778 } 779 return r; 780 } // end of DSLBase::readArrayOfDouble 781 readBooleanValue(const std::string & m)782 bool DSLBase::readBooleanValue(const std::string& m) { 783 bool b = false; 784 this->checkNotEndOfFile(m, "Expected a boolean value"); 785 if (this->current->value == "true") { 786 b = true; 787 } else if (this->current->value == "false") { 788 b = false; 789 } else { 790 this->throwRuntimeError(m, 791 "Expected to read 'true' or 'false' " 792 "(read '" + 793 this->current->value + "')"); 794 } 795 ++(this->current); 796 return b; 797 } // end of DSLBase::readBooleanValue 798 readString(const std::string & m)799 std::string DSLBase::readString(const std::string& m) { 800 this->checkNotEndOfFile(m, "Expected a string or '{'"); 801 if (this->current->flag != tfel::utilities::Token::String) { 802 this->throwRuntimeError(m, "Expected a string"); 803 } 804 const auto& r = this->current->value.substr(1, this->current->value.size() - 2); 805 ++(this->current); 806 return r; 807 } // end of DSLBase::readString 808 readStringOrArrayOfString(const std::string & m)809 std::vector<std::string> DSLBase::readStringOrArrayOfString(const std::string& m) { 810 this->checkNotEndOfFile(m, "Expected a string or '{'"); 811 if (this->current->value == "{") { 812 return this->readArrayOfString(m); 813 } 814 return {1u, this->readString(m)}; 815 } // end of DSLBase::readStringOrArrayOfString 816 treatLink()817 void DSLBase::treatLink() { 818 const auto nlink = readStringOrArrayOfString("DSLBase::treatLink"); 819 this->readSpecifiedToken("DSLBase::treatLink", ";"); 820 for (const auto& l : nlink) { 821 insert_if(this->ldflags, l); 822 } 823 } // end of DSLBase::treatLink 824 callMFront(const std::vector<std::string> & interfaces,const std::vector<std::string> & files)825 void DSLBase::callMFront(const std::vector<std::string>& interfaces, 826 const std::vector<std::string>& files) { 827 MFront m; 828 for (const auto& i : interfaces) { 829 m.setInterface(i); 830 } 831 for (const auto& f : files) { 832 mergeTargetsDescription(this->td, m.treatFile(f), false); 833 } 834 } // end of DSLBase::callMFront 835 treatMFront()836 void DSLBase::treatMFront() { 837 this->readSpecifiedToken("DSLBase::treatMfront", "{"); 838 const auto vfiles = this->readStringOrArrayOfString("DSLBase::treatMfront"); 839 auto vinterfaces = std::vector<std::string>{}; 840 this->checkNotEndOfFile("DSLBase::treatMfront", "Expected '}'"); 841 if (!((this->current->value == "}") || (this->current->value == ","))) { 842 this->throwRuntimeError("DSLBase::treatMfront", "Expected ',' or '}', read '" + this->current->value + "'"); 843 } 844 if (this->current->value == ",") { 845 ++(this->current); 846 vinterfaces = this->readStringOrArrayOfString("DSLBase::treatMfront"); 847 } 848 this->readSpecifiedToken("DSLBase::treatMfront", "}"); 849 this->readSpecifiedToken("DSLBase::treatMfront", ";"); 850 for (const auto& f : vfiles) { 851 this->externalMFrontFiles.insert({f, vinterfaces}); 852 } 853 } // end of DSLBase::treatMfront 854 readSpecifiedValue(const std::string & file,const std::string & value)855 std::string DSLBase::readSpecifiedValue(const std::string& file, const std::string& value) { 856 std::vector<std::string> values(1, value); 857 return this->readSpecifiedValues(file, values)[1]; 858 } // end of DSLBase::readSpecifiedValue 859 readSpecifiedValues(const std::string & file,const std::string & value1,const std::string & value2)860 std::vector<std::string> DSLBase::readSpecifiedValues(const std::string& file, 861 const std::string& value1, 862 const std::string& value2) { 863 return this->readSpecifiedValues(file, {value1, value2}); 864 } // end of DSLBase::readSpecifiedValues 865 readSpecifiedValues(const std::string & file,const std::vector<std::string> & values)866 std::vector<std::string> DSLBase::readSpecifiedValues(const std::string& file, 867 const std::vector<std::string>& values) { 868 auto throw_if = [](const bool b, const std::string& m, const unsigned int l) { 869 tfel::raise_if(b, "DSLBase::readSpecifiedValues : " + m + 870 "\n" 871 "Error at line " + 872 std::to_string(l)); 873 }; 874 tfel::utilities::CxxTokenizer cfile; 875 auto res = std::vector<std::string>(values.size()); 876 cfile.openFile(file); 877 cfile.stripComments(); 878 auto pt = cfile.begin(); 879 const auto pte = cfile.end(); 880 while (pt != pte) { 881 const auto p = find(values.begin(), values.end(), pt->value); 882 if (p != values.end()) { 883 if (pt != cfile.begin()) { 884 auto ptp = std::prev(pt); 885 throw_if(ptp->value != ";", "the keyword '" + *p + 886 "' does not " 887 "begin a new instruction.", 888 pt->line); 889 } 890 throw_if(++pt == pte, "unexepected end of file '" + file + "' (expected " + *p + ").\n", (--pt)->line); 891 const auto value = pt->value; 892 throw_if(pt->value == ";", "unexepected token ';' (exepected " + *p + ")", pt->line); 893 throw_if(++pt == pte, "unexepected end of file '" + file + "' (expected ';').\n", (--pt)->line); 894 throw_if(pt->value != ";", "unexepected token '" + pt->value + "' (exepected ';')", pt->line); 895 res[static_cast<std::vector<std::string>::size_type>(p - values.begin())] = value; 896 } 897 ++pt; 898 } 899 return res; 900 } // end of DSLBase::readSpecifiedValues 901 handleMaterialPropertyDescription(const std::string & f)902 std::shared_ptr<MaterialPropertyDescription> DSLBase::handleMaterialPropertyDescription(const std::string& f) { 903 // getting informations the source files 904 MaterialPropertyDSL mp; 905 try { 906 MFrontMaterialPropertyInterface minterface; 907 const auto& path = SearchPathsHandler::search(f); 908 mp.setInterfaces({"mfront"}); 909 mp.analyseFile(path); 910 const auto t = mp.getTargetsDescription(); 911 if (!t.specific_targets.empty()) { 912 this->throwRuntimeError("DSLBase::handleMaterialPropertyDescription", "error while treating file '" + f + 913 "'.\n" 914 "Specific targets are not supported"); 915 } 916 const auto& mpd = mp.getMaterialPropertyDescription(); 917 const auto& mname = minterface.getFunctionName(mpd); 918 this->reserveName(mname); 919 this->reserveName(mname + "_checkBounds"); 920 this->reserveName(mname + "_bounds_check_status"); 921 this->appendToIncludes("#include\"" + minterface.getHeaderFileName(mpd.material, mpd.law) + ".hxx\""); 922 this->addMaterialLaw(mname); 923 this->atds.push_back(std::move(t)); 924 this->externalMFrontFiles.insert({path, {"mfront"}}); 925 } catch (std::exception& e) { 926 this->throwRuntimeError("DSLBase::handleMaterialPropertyDescription", 927 "error while treating file '" + f + "'\n" + std::string(e.what())); 928 } catch (...) { 929 this->throwRuntimeError("DSLBase::handleMaterialPropertyDescription", "error while treating file '" + f + "'"); 930 } 931 const auto& m = mp.getMaterialPropertyDescription(); 932 return std::make_shared<MaterialPropertyDescription>(m); 933 } // end of DSLBase::handleMaterialLaw 934 treatMaterialLaw()935 void DSLBase::treatMaterialLaw() { 936 const auto vfiles = this->readStringOrArrayOfString("DSLBase::treatMaterialLaw"); 937 this->readSpecifiedToken("DSLBase::treatMaterialLaw", ";"); 938 for (const auto& f : vfiles) { 939 this->handleMaterialPropertyDescription(f); 940 } 941 } // end of DSLBase::treatMaterialLaw 942 treatLonelySeparator()943 void DSLBase::treatLonelySeparator() { 944 if (getPedanticMode()) { 945 getLogStream() << this->fd.fileName << ":" << this->current->line << ":" << this->current->offset 946 << ": warning: extra ‘;’ [-pedantic]\n"; 947 } 948 } // end of DSLBase::treatLonelySperator 949 overrideMaterialKnowledgeIdentifier(const std::string & i)950 void DSLBase::overrideMaterialKnowledgeIdentifier(const std::string& i) { 951 if (!this->overriden_implementation_name.empty()) { 952 this->throwRuntimeError("DSLBase::overrideMaterialKnowledgeIdentifier", 953 "the implementation name is already overriden"); 954 } 955 this->overriden_implementation_name = i; 956 this->setMaterialKnowledgeIdentifier(i); 957 } // end of DSLBase::overrideMaterialKnowledgeIdentifier 958 overrideMaterialName(const std::string & m)959 void DSLBase::overrideMaterialName(const std::string& m) { 960 if (!this->overriden_material.empty()) { 961 this->throwRuntimeError("DSLBase::overrideMaterialName", 962 "the material name is already overriden"); 963 } 964 this->overriden_material = m; 965 this->setMaterial(m); 966 } // end of DSLBase::overrideMaterialName 967 overrideAuthorName(const std::string & a)968 void DSLBase::overrideAuthorName(const std::string& a) { 969 if (!this->overriden_author.empty()) { 970 this->throwRuntimeError("DSLBase::overrideAuthorName", 971 "the author is already overriden"); 972 } 973 this->overriden_author = a; 974 this->setAuthor(a); 975 } // end of DSLBase::overrideAuthorName 976 overrideDate(const std::string & d)977 void DSLBase::overrideDate(const std::string& d) { 978 if (!this->overriden_date.empty()) { 979 this->throwRuntimeError("DSLBase::overrideDate", 980 "the date is already overriden"); 981 } 982 this->overriden_date = d; 983 this->setDate(d); 984 } // end of DSLBase::overrideAuthorName 985 overrideDescription(const std::string & d)986 void DSLBase::overrideDescription(const std::string& d) { 987 if (!this->overriden_description.empty()) { 988 this->throwRuntimeError("DSLBase::overrideDescription", 989 "the description is already overriden"); 990 } 991 this->overriden_description = d; 992 this->setDescription(d); 993 } // end of DSLBase::overrideAuthorName 994 treatMaterial()995 void DSLBase::treatMaterial() { 996 const auto& m = this->readOnlyOneToken(); 997 if (this->overriden_material.empty()) { 998 this->setMaterial(m); 999 } 1000 } // end of DSLBase::treatMaterial 1001 treatAuthor()1002 void DSLBase::treatAuthor() { 1003 const auto author = this->readUntilEndOfInstruction(); 1004 if (this->overriden_author.empty()) { 1005 this->setAuthor(author); 1006 } 1007 } // end of DSLBase::treatAuthor 1008 setAuthor(const std::string & a)1009 void DSLBase::setAuthor(const std::string& a) { 1010 if (!this->fd.authorName.empty()) { 1011 this->throwRuntimeError("DSLBase::setAuthor", "author already specified"); 1012 } 1013 this->fd.authorName = a; 1014 } // end of DSLBase::setAuthor 1015 treatDate()1016 void DSLBase::treatDate() { 1017 const auto date = this->readUntilEndOfInstruction(); 1018 if (this->overriden_date.empty()) { 1019 this->setDate(date); 1020 } 1021 } // end of DSLBase::treatDate 1022 setDate(const std::string & d)1023 void DSLBase::setDate(const std::string& d) { 1024 if (!this->fd.date.empty()) { 1025 this->throwRuntimeError("DSLBase::setDate", "date already specified"); 1026 } 1027 this->fd.date = d; 1028 } // end of DSLBase::setDate 1029 treatDescription()1030 void DSLBase::treatDescription() { 1031 this->readSpecifiedToken("DSLBase::treatDescription", "{"); 1032 this->checkNotEndOfFile("DSLBase::treatDescription"); 1033 auto description = std::string{}; 1034 description += "* "; 1035 auto currentLine = this->current->line; 1036 unsigned int openedBrackets = 1u; 1037 while ((this->current != this->tokens.end()) && (!((this->current->value == "}") && (openedBrackets == 1u)))) { 1038 if (this->current->value == "{") { 1039 const auto previous = std::prev(this->current); 1040 if ((previous->value.size() > 0) && (previous->value[previous->value.size() - 1] != '\\')) { 1041 ++openedBrackets; 1042 } 1043 } 1044 if (this->current->value == "}") { 1045 const auto previous = std::prev(this->current); 1046 if ((previous->value.size() > 0) && (previous->value[previous->value.size() - 1] != '\\')) { 1047 --openedBrackets; 1048 } 1049 } 1050 if (currentLine != this->current->line) { 1051 while (currentLine != this->current->line) { 1052 description += "\n* "; 1053 ++currentLine; 1054 } 1055 } 1056 if (this->current->flag == tfel::utilities::Token::String) { 1057 description += this->current->value.substr(1, this->current->value.size() - 2u); 1058 } else { 1059 description += this->current->value; 1060 } 1061 description += " "; 1062 ++(this->current); 1063 } 1064 if (this->current == this->tokens.end()) { 1065 --(this->current); 1066 this->throwRuntimeError("DSLBase::treatDescription", "File ended before the end of description."); 1067 } 1068 ++(this->current); 1069 if (this->overriden_description.empty()) { 1070 this->setDescription(description); 1071 } 1072 } // end of DSLBase::treatDescription 1073 setDescription(const std::string & d)1074 void DSLBase::setDescription(const std::string& d) { 1075 if (!this->fd.description.empty()) { 1076 this->throwRuntimeError("DSLBase::setDescription", "date already specified"); 1077 } 1078 this->fd.description= d; 1079 } // end of DSLBase::setDescription 1080 treatUnknownKeyword()1081 void DSLBase::treatUnknownKeyword() { 1082 --(this->current); 1083 this->throwRuntimeError("DSLBase::treatUnknownKeyword", "unknown keyword (read '" + this->current->value + "')"); 1084 } // end of DSLBase::treatUnknownKeyword 1085 treatIncludes()1086 void DSLBase::treatIncludes() { 1087 CodeBlockParserOptions options; 1088 this->appendToIncludes(this->readNextBlock(options).code); 1089 } 1090 treatSources()1091 void DSLBase::treatSources() { 1092 CodeBlockParserOptions options; 1093 this->appendToSources(this->readNextBlock(options).code); 1094 } // end of DSLBase::treatSources 1095 treatMembers()1096 void DSLBase::treatMembers() { 1097 CodeBlockParserOptions options; 1098 options.qualifyStaticVariables = true; 1099 options.qualifyMemberVariables = true; 1100 this->appendToMembers(this->readNextBlock(options).code); 1101 } 1102 treatPrivate()1103 void DSLBase::treatPrivate() { 1104 CodeBlockParserOptions options; 1105 options.qualifyStaticVariables = true; 1106 options.qualifyMemberVariables = true; 1107 this->appendToPrivateCode(this->readNextBlock(options).code); 1108 } // end of DSLBase::treatPrivate 1109 treatParser()1110 void DSLBase::treatParser() { this->readUntilEndOfInstruction(); } // end of DSLBase::treatParser 1111 treatStaticVar()1112 void DSLBase::treatStaticVar() { 1113 this->checkNotEndOfFile("DSLBase::treatStaticVar", "Cannot read type of static variable."); 1114 const auto type = this->current->value; 1115 if (!this->isValidIdentifier(type, false)) { 1116 --(this->current); 1117 this->throwRuntimeError("DSLBase::treatStaticVar", "type given is not valid."); 1118 } 1119 ++(this->current); 1120 this->checkNotEndOfFile("DSLBase::treatStaticVar", "Cannot read variable name."); 1121 const auto sname = this->current->value; 1122 const auto vname = tfel::unicode::getMangledString(sname); 1123 if (!this->isValidIdentifier(vname)) { 1124 this->throwRuntimeError("DSLBase::treatStaticVar", 1125 "Variable name '" + sname + "' is not valid."); 1126 } 1127 const auto line = this->current->line; 1128 ++(this->current); 1129 this->checkNotEndOfFile("DSLBase::treatStaticVar", "Expected to read value of variable."); 1130 const auto value = this->readInitialisationValue<long double>(sname, true); 1131 this->readSpecifiedToken("DSLBase::treatStaticVar", ";"); 1132 this->addStaticVariableDescription( 1133 StaticVariableDescription(type, sname, vname, line, value.second)); 1134 } // end of DSLBase::treatStaticVar 1135 ignoreKeyWord(const std::string & key)1136 void DSLBase::ignoreKeyWord(const std::string& key) { 1137 this->checkNotEndOfFile("DSLBase::ignoreKeyWord", "error while treating keyword '" + key + "' "); 1138 while ((this->current->value != "{") && (this->current->value != ";")) { 1139 ++(this->current); 1140 this->checkNotEndOfFile("DSLBase::ignoreKeyWord", "error while treating keyword '" + key + "' "); 1141 } 1142 if (this->current->value == "{") { 1143 unsigned short openedBrackets = 1; 1144 while (!((this->current->value == "}") && (openedBrackets == 0))) { 1145 ++(this->current); 1146 this->checkNotEndOfFile("DSLBase::ignoreKeyWord", "error while treating keyword '" + key + "' "); 1147 if (this->current->value == "{") { 1148 ++openedBrackets; 1149 } 1150 if (this->current->value == "}") { 1151 --openedBrackets; 1152 } 1153 } 1154 const auto next = std::next(this->current); 1155 if (next != this->tokens.end()) { 1156 if (next->value == ";") { 1157 current = next; 1158 } 1159 } 1160 } 1161 ++(this->current); 1162 } // end of DSLBase::ignoreKeyWord 1163 readDouble()1164 double DSLBase::readDouble() { 1165 this->checkNotEndOfFile("DSLBase::readDouble"); 1166 return CxxTokenizer::readDouble(this->current, this->tokens.end()); 1167 } // end of DSLBase::readDouble 1168 completeTargetsDescription()1169 void DSLBase::completeTargetsDescription() { 1170 for (auto& l : this->td.libraries) { 1171 l.ldflags.insert(l.ldflags.end(), this->ldflags.begin(), this->ldflags.end()); 1172 } 1173 for (const auto& t : this->atds) { 1174 for (const auto& al : t.libraries) { 1175 for (auto& l : this->td.libraries) { 1176 if (l.name != al.name) { 1177 insert_if(l.deps, al.name); 1178 } 1179 } 1180 } 1181 } 1182 for (const auto& t : this->atds) { 1183 mergeTargetsDescription(this->td, t, false); 1184 } 1185 this->ldflags.clear(); 1186 this->atds.clear(); 1187 } // end of DSLBase::completeTargetsDescription() 1188 1189 } // end of namespace mfront 1190