LCOV - code coverage report
Current view: top level - src/parser - Parser.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 177 188 94.1 %
Date: 2025-07-17 01:28:37 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : // MOOSE includes
      11             : #include "MooseUtils.h"
      12             : #include "MooseInit.h"
      13             : #include "InputFileFormatter.h"
      14             : #include "YAMLFormatter.h"
      15             : #include "MooseTypes.h"
      16             : #include "CommandLine.h"
      17             : #include "SystemInfo.h"
      18             : #include "Parser.h"
      19             : #include "Units.h"
      20             : 
      21             : #include "libmesh/parallel.h"
      22             : #include "libmesh/fparser.hh"
      23             : 
      24             : // Regular expression includes
      25             : #include "pcrecpp.h"
      26             : 
      27             : // C++ includes
      28             : #include <string>
      29             : #include <map>
      30             : #include <fstream>
      31             : #include <iomanip>
      32             : #include <algorithm>
      33             : #include <cstdlib>
      34             : #include <filesystem>
      35             : 
      36             : std::string
      37        3909 : FuncParseEvaler::eval(hit::Field * n, const std::list<std::string> & args, hit::BraceExpander & exp)
      38             : {
      39        3909 :   std::string func_text;
      40       10208 :   for (auto & s : args)
      41        6299 :     func_text += s;
      42        3909 :   auto n_errs = exp.errors.size();
      43             : 
      44        3909 :   FunctionParser fp;
      45        3909 :   fp.AddConstant("pi", libMesh::pi);
      46        3909 :   fp.AddConstant("e", std::exp(Real(1)));
      47        3909 :   std::vector<std::string> var_names;
      48        3909 :   auto ret = fp.ParseAndDeduceVariables(func_text, var_names);
      49        3909 :   if (ret != -1)
      50             :   {
      51           0 :     exp.errors.push_back(hit::errormsg(n, "fparse error: ", fp.ErrorMsg()));
      52           0 :     return n->val();
      53             :   }
      54             : 
      55        3909 :   std::string errors;
      56        3909 :   std::vector<double> var_vals;
      57        9899 :   for (auto & var : var_names)
      58             :   {
      59             :     // recursively check all parent scopes for the needed variables
      60        5990 :     hit::Node * curr = n;
      61       15419 :     while ((curr = curr->parent()))
      62             :     {
      63       15418 :       auto src = curr->find(var);
      64       15418 :       if (src && src != n && src->type() == hit::NodeType::Field)
      65             :       {
      66       17967 :         exp.used.push_back(hit::pathJoin({curr->fullpath(), var}));
      67        5989 :         var_vals.push_back(curr->param<double>(var));
      68        5989 :         break;
      69             :       }
      70             :     }
      71             : 
      72        5990 :     if (curr == nullptr)
      73           1 :       exp.errors.push_back(hit::errormsg(
      74             :           n, "\n    no variable '", var, "' found for use in function parser expression"));
      75             :   }
      76             : 
      77        3909 :   if (exp.errors.size() != n_errs)
      78           1 :     return n->val();
      79             : 
      80        3908 :   std::stringstream ss;
      81        3908 :   ss << std::setprecision(17) << fp.Eval(var_vals.data());
      82             : 
      83             :   // change kind only (not val)
      84        3908 :   n->setVal(n->val(), hit::Field::Kind::Float);
      85        3908 :   return ss.str();
      86        9898 : }
      87             : 
      88             : std::string
      89          72 : UnitsConversionEvaler::eval(hit::Field * n,
      90             :                             const std::list<std::string> & args,
      91             :                             hit::BraceExpander & exp)
      92             : {
      93          72 :   std::vector<std::string> argv;
      94          72 :   argv.insert(argv.begin(), args.begin(), args.end());
      95             : 
      96             :   // no conversion, the expression currently only documents the units and passes through the value
      97          72 :   if (argv.size() == 2)
      98             :   {
      99          48 :     n->setVal(n->val(), hit::Field::Kind::Float);
     100          48 :     return argv[0];
     101             :   }
     102             : 
     103             :   // conversion
     104          24 :   if (argv.size() != 4 || (argv.size() >= 3 && argv[2] != "->"))
     105             :   {
     106           0 :     exp.errors.push_back(
     107           0 :         hit::errormsg(n,
     108             :                       "units error: Expected 4 arguments ${units number from_unit -> to_unit} or "
     109             :                       "2 arguments  ${units number unit}"));
     110           0 :     return n->val();
     111             :   }
     112             : 
     113             :   // get and check units
     114          24 :   auto from_unit = MooseUnits(argv[1]);
     115          24 :   auto to_unit = MooseUnits(argv[3]);
     116          24 :   if (!from_unit.conformsTo(to_unit))
     117             :   {
     118           0 :     exp.errors.push_back(hit::errormsg(n,
     119             :                                        "units error: ",
     120           0 :                                        argv[1],
     121             :                                        " (",
     122             :                                        from_unit,
     123             :                                        ") does not convert to ",
     124           0 :                                        argv[3],
     125             :                                        " (",
     126             :                                        to_unit,
     127             :                                        ")"));
     128           0 :     return n->val();
     129             :   }
     130             : 
     131             :   // parse number
     132          24 :   Real num = MooseUtils::convert<Real>(argv[0]);
     133             : 
     134             :   // convert units
     135          24 :   std::stringstream ss;
     136          24 :   ss << std::setprecision(17) << to_unit.convert(num, from_unit);
     137             : 
     138             : #ifndef NDEBUG
     139             :   mooseInfoRepeated(n->filename() + ':' + Moose::stringify(n->line()) + ':' +
     140             :                         Moose::stringify(n->column()) + ": Unit conversion ",
     141             :                     num,
     142             :                     ' ',
     143             :                     argv[1],
     144             :                     " -> ",
     145             :                     ss.str(),
     146             :                     ' ',
     147             :                     argv[3]);
     148             : #endif
     149             : 
     150             :   // change kind only (not val)
     151          24 :   n->setVal(n->val(), hit::Field::Kind::Float);
     152          24 :   return ss.str();
     153          72 : }
     154             : 
     155       62800 : Parser::Parser(const std::vector<std::string> & input_filenames,
     156       62800 :                const std::optional<std::vector<std::string>> & input_text)
     157       62800 :   : _root(nullptr),
     158       62800 :     _input_filenames(input_filenames),
     159       62800 :     _input_text(input_text),
     160       62800 :     _app_type(std::string())
     161             : {
     162       62800 : }
     163             : 
     164       11404 : Parser::Parser(const std::string & input_filename, const std::optional<std::string> & input_text)
     165       34212 :   : Parser(std::vector<std::string>{input_filename},
     166       22822 :            input_text ? std::optional<std::vector<std::string>>({*input_text})
     167       11404 :                       : std::optional<std::vector<std::string>>())
     168             : {
     169       22815 : }
     170             : 
     171             : void
     172     4664334 : DupParamWalker ::walk(const std::string & fullpath, const std::string & /*nodepath*/, hit::Node * n)
     173             : {
     174     4664334 :   std::string prefix = n->type() == hit::NodeType::Field ? "parameter" : "section";
     175             : 
     176     4664334 :   if (_have.count(fullpath) > 0)
     177             :   {
     178          18 :     auto existing = _have[fullpath];
     179          18 :     if (_duplicates.count(fullpath) == 0)
     180             :     {
     181          18 :       errors.push_back(
     182          36 :           hit::errormsg(existing, prefix, " '", fullpath, "' supplied multiple times"));
     183          18 :       _duplicates.insert(fullpath);
     184             :     }
     185          18 :     errors.push_back(hit::errormsg(n, prefix, " '", fullpath, "' supplied multiple times"));
     186             :   }
     187     4664334 :   _have[fullpath] = n;
     188     4664334 : }
     189             : 
     190             : void
     191     2293544 : CompileParamWalker::walk(const std::string & fullpath,
     192             :                          const std::string & /*nodepath*/,
     193             :                          hit::Node * n)
     194             : {
     195     2293544 :   if (n->type() == hit::NodeType::Field)
     196     2293544 :     _map[fullpath] = n;
     197     2293544 : }
     198             : 
     199             : void
     200        1161 : OverrideParamWalker::walk(const std::string & fullpath,
     201             :                           const std::string & /*nodepath*/,
     202             :                           hit::Node * n)
     203             : {
     204        1161 :   const auto it = _map.find(fullpath);
     205        1161 :   if (it != _map.end())
     206          22 :     warnings.push_back(hit::errormsg(n,
     207             :                                      " Parameter '",
     208             :                                      fullpath,
     209             :                                      "' overrides the same parameter in ",
     210          22 :                                      it->second->filename(),
     211             :                                      ":",
     212          22 :                                      it->second->line()));
     213        1161 : }
     214             : 
     215             : void
     216     2180676 : BadActiveWalker ::walk(const std::string & /*fullpath*/,
     217             :                        const std::string & /*nodepath*/,
     218             :                        hit::Node * section)
     219             : {
     220     2180676 :   auto actives = section->find("active");
     221     2180676 :   auto inactives = section->find("inactive");
     222             : 
     223       26012 :   if (actives && inactives && actives->type() == hit::NodeType::Field &&
     224     2206688 :       inactives->type() == hit::NodeType::Field && actives->parent() == inactives->parent())
     225             :   {
     226           4 :     errors.push_back(
     227           8 :         hit::errormsg(section, "'active' and 'inactive' parameters both provided in section"));
     228           4 :     return;
     229             :   }
     230             : 
     231             :   // ensures we don't recheck deeper nesting levels
     232     2180672 :   if (actives && actives->type() == hit::NodeType::Field && actives->parent() == section)
     233             :   {
     234       25984 :     auto vars = section->param<std::vector<std::string>>("active");
     235       25984 :     std::string msg = "";
     236       67681 :     for (auto & var : vars)
     237             :     {
     238       41697 :       if (!section->find(var))
     239           9 :         msg += var + ", ";
     240             :     }
     241       25984 :     if (msg.size() > 0)
     242             :     {
     243           9 :       msg = msg.substr(0, msg.size() - 2);
     244           9 :       errors.push_back(hit::errormsg(section,
     245             :                                      "variables listed as active (",
     246             :                                      msg,
     247             :                                      ") in section '",
     248          18 :                                      section->fullpath(),
     249             :                                      "' not found in input"));
     250             :     }
     251       25984 :   }
     252             :   // ensures we don't recheck deeper nesting levels
     253     2180672 :   if (inactives && inactives->type() == hit::NodeType::Field && inactives->parent() == section)
     254             :   {
     255        2944 :     auto vars = section->param<std::vector<std::string>>("inactive");
     256        2944 :     std::string msg = "";
     257        6597 :     for (auto & var : vars)
     258             :     {
     259        3653 :       if (!section->find(var))
     260           8 :         msg += var + ", ";
     261             :     }
     262        2944 :     if (msg.size() > 0)
     263             :     {
     264           8 :       msg = msg.substr(0, msg.size() - 2);
     265           8 :       errors.push_back(hit::errormsg(section,
     266             :                                      "variables listed as inactive (",
     267             :                                      msg,
     268             :                                      ") in section '",
     269          16 :                                      section->fullpath(),
     270             :                                      "' not found in input"));
     271             :     }
     272        2944 :   }
     273             : }
     274             : 
     275             : class FindAppWalker : public hit::Walker
     276             : {
     277             : public:
     278             :   void
     279     2289983 :   walk(const std::string & /*fullpath*/, const std::string & /*nodepath*/, hit::Node * n) override
     280             :   {
     281     2289983 :     if (n && n->type() == hit::NodeType::Field && n->fullpath() == "Application/type")
     282          19 :       _app_type = n->param<std::string>();
     283     2289983 :   }
     284       62243 :   const std::optional<std::string> & getApp() { return _app_type; };
     285             : 
     286             : private:
     287             :   std::optional<std::string> _app_type;
     288             : };
     289             : 
     290             : const std::string &
     291       38892 : Parser::getLastInputFileName() const
     292             : {
     293       38892 :   if (_input_filenames.empty())
     294           0 :     mooseError("Parser::getLastInputFileName(): No inputs are set");
     295       38892 :   return _input_filenames.back();
     296             : }
     297             : 
     298             : void
     299       62248 : Parser::parse()
     300             : {
     301       62248 :   _root.reset();
     302             : 
     303       62248 :   if (_input_text && _input_text->size() != getInputFileNames().size())
     304           0 :     mooseError("Input text is not the same size as the input filenames");
     305             : 
     306       62248 :   if (getInputFileNames().size() > 1)
     307         188 :     mooseInfo("Merging inputs ", Moose::stringify(getInputFileNames()));
     308             : 
     309       62248 :   CompileParamWalker::ParamMap override_map;
     310       62248 :   CompileParamWalker cpw(override_map);
     311       62248 :   OverrideParamWalker opw(override_map);
     312             : 
     313       62248 :   std::string errmsg;
     314       62248 :   std::vector<std::string> dw_errmsg;
     315             : 
     316             :   // Whether or not to use real filepaths (from env)
     317             :   const std::string use_rel_paths_str =
     318       62248 :       std::getenv("MOOSE_RELATIVE_FILEPATHS") ? std::getenv("MOOSE_RELATIVE_FILEPATHS") : "false";
     319       62248 :   const auto use_real_paths = use_rel_paths_str == "0" || use_rel_paths_str == "false";
     320             : 
     321      124687 :   for (const auto i : index_range(getInputFileNames()))
     322             :   {
     323       62463 :     const auto & input_filename = getInputFileNames()[i];
     324             :     const auto corrected_filename =
     325       62463 :         use_real_paths ? MooseUtils::realpath(input_filename) : input_filename;
     326             : 
     327             :     // Parse the input text string if provided, otherwise read file from disk
     328       62463 :     std::string input;
     329       62463 :     std::string errmsg;
     330       62463 :     if (_input_text)
     331          15 :       input = (*_input_text)[i];
     332             :     else
     333             :     {
     334       62448 :       MooseUtils::checkFileReadable(corrected_filename, true);
     335       62440 :       std::ifstream f(corrected_filename);
     336       62440 :       input = std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
     337       62440 :     }
     338             : 
     339             :     try
     340             :     {
     341             :       // provide stream to hit parse function to capture any syntax errors,
     342             :       // set parser root node, then throw those errors if any were captured
     343       62455 :       std::stringstream input_errors;
     344       62455 :       std::unique_ptr<hit::Node> root(hit::parse(corrected_filename, input, &input_errors));
     345       62455 :       hit::explode(root.get());
     346       62455 :       DupParamWalker dw;
     347       62455 :       root->walk(&dw, hit::NodeType::Field);
     348       62455 :       if (!_root)
     349       62240 :         _root = std::move(root);
     350             :       else
     351             :       {
     352         215 :         root->walk(&opw, hit::NodeType::Field);
     353         215 :         hit::merge(root.get(), _root.get());
     354             :       }
     355             : 
     356       62455 :       if (!input_errors.str().empty())
     357          16 :         throw hit::ParseError(input_errors.str());
     358             : 
     359       62475 :       for (auto & msg : dw.errors)
     360          36 :         errmsg += msg + "\n";
     361             : 
     362       62439 :       dw_errmsg.push_back(errmsg);
     363       62439 :       _root->walk(&cpw, hit::NodeType::Field);
     364       62487 :     }
     365          16 :     catch (hit::ParseError & err)
     366             :     {
     367          16 :       mooseError(err.what());
     368           1 :     }
     369       62442 :   }
     370             : 
     371             :   // warn about overridden parameters in multiple inputs
     372       62224 :   if (!opw.warnings.empty())
     373          22 :     mooseInfo(Moose::stringify(opw.warnings), "\n");
     374             : 
     375             :   // do as much error checking as early as possible so that errors are more useful instead
     376             :   // of surprising and disconnected from what caused them.
     377       62224 :   BadActiveWalker bw;
     378       62224 :   _root->walk(&bw, hit::NodeType::Section);
     379             : 
     380       62224 :   FindAppWalker fw;
     381       62224 :   _root->walk(&fw, hit::NodeType::Field);
     382       62224 :   if (fw.getApp())
     383          19 :     setAppType(*fw.getApp());
     384             : 
     385       62229 :   for (auto & msg : bw.errors)
     386           5 :     errmsg += msg + "\n";
     387             : 
     388             :   // Print parse errors related to bad active early
     389       62224 :   if (errmsg.size() > 0)
     390           5 :     mooseError(errmsg);
     391             : 
     392      124644 :   for (auto & msg : dw_errmsg)
     393       62434 :     if (msg.size() > 0)
     394           9 :       mooseError(msg);
     395       62232 : }

Generated by: LCOV version 1.14