https://mooseframework.inl.gov
Parser.C
Go to the documentation of this file.
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 FuncParseEvaler::eval(hit::Field * n, const std::list<std::string> & args, hit::BraceExpander & exp)
38 {
39  std::string func_text;
40  for (auto & s : args)
41  func_text += s;
42  auto n_errs = exp.errors.size();
43 
44  FunctionParser fp;
45  fp.AddConstant("pi", libMesh::pi);
46  fp.AddConstant("e", std::exp(Real(1)));
47  std::vector<std::string> var_names;
48  auto ret = fp.ParseAndDeduceVariables(func_text, var_names);
49  if (ret != -1)
50  {
51  exp.errors.push_back(hit::errormsg(n, "fparse error: ", fp.ErrorMsg()));
52  return n->val();
53  }
54 
55  std::string errors;
56  std::vector<double> var_vals;
57  for (auto & var : var_names)
58  {
59  // recursively check all parent scopes for the needed variables
60  hit::Node * curr = n;
61  while ((curr = curr->parent()))
62  {
63  auto src = curr->find(var);
64  if (src && src != n && src->type() == hit::NodeType::Field)
65  {
66  exp.used.push_back(hit::pathJoin({curr->fullpath(), var}));
67  var_vals.push_back(curr->param<double>(var));
68  break;
69  }
70  }
71 
72  if (curr == nullptr)
73  exp.errors.push_back(hit::errormsg(
74  n, "\n no variable '", var, "' found for use in function parser expression"));
75  }
76 
77  if (exp.errors.size() != n_errs)
78  return n->val();
79 
80  std::stringstream ss;
81  ss << std::setprecision(17) << fp.Eval(var_vals.data());
82 
83  // change kind only (not val)
84  n->setVal(n->val(), hit::Field::Kind::Float);
85  return ss.str();
86 }
87 
88 std::string
90  const std::list<std::string> & args,
91  hit::BraceExpander & exp)
92 {
93  std::vector<std::string> argv;
94  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  if (argv.size() == 2)
98  {
99  n->setVal(n->val(), hit::Field::Kind::Float);
100  return argv[0];
101  }
102 
103  // conversion
104  if (argv.size() != 4 || (argv.size() >= 3 && argv[2] != "->"))
105  {
106  exp.errors.push_back(
107  hit::errormsg(n,
108  "units error: Expected 4 arguments ${units number from_unit -> to_unit} or "
109  "2 arguments ${units number unit}"));
110  return n->val();
111  }
112 
113  // get and check units
114  auto from_unit = MooseUnits(argv[1]);
115  auto to_unit = MooseUnits(argv[3]);
116  if (!from_unit.conformsTo(to_unit))
117  {
118  exp.errors.push_back(hit::errormsg(n,
119  "units error: ",
120  argv[1],
121  " (",
122  from_unit,
123  ") does not convert to ",
124  argv[3],
125  " (",
126  to_unit,
127  ")"));
128  return n->val();
129  }
130 
131  // parse number
132  Real num = MooseUtils::convert<Real>(argv[0]);
133 
134  // convert units
135  std::stringstream ss;
136  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  n->setVal(n->val(), hit::Field::Kind::Float);
152  return ss.str();
153 }
154 
155 Parser::Parser(const std::vector<std::string> & input_filenames,
156  const std::optional<std::vector<std::string>> & input_text)
157  : _root(nullptr),
158  _input_filenames(input_filenames),
159  _input_text(input_text),
160  _app_type(std::string())
161 {
162 }
163 
164 Parser::Parser(const std::string & input_filename, const std::optional<std::string> & input_text)
165  : Parser(std::vector<std::string>{input_filename},
166  input_text ? std::optional<std::vector<std::string>>({*input_text})
167  : std::optional<std::vector<std::string>>())
168 {
169 }
170 
171 void
172 DupParamWalker ::walk(const std::string & fullpath, const std::string & /*nodepath*/, hit::Node * n)
173 {
174  std::string prefix = n->type() == hit::NodeType::Field ? "parameter" : "section";
175 
176  if (_have.count(fullpath) > 0)
177  {
178  auto existing = _have[fullpath];
179  if (_duplicates.count(fullpath) == 0)
180  {
181  errors.push_back(
182  hit::errormsg(existing, prefix, " '", fullpath, "' supplied multiple times"));
183  _duplicates.insert(fullpath);
184  }
185  errors.push_back(hit::errormsg(n, prefix, " '", fullpath, "' supplied multiple times"));
186  }
187  _have[fullpath] = n;
188 }
189 
190 void
191 CompileParamWalker::walk(const std::string & fullpath,
192  const std::string & /*nodepath*/,
193  hit::Node * n)
194 {
195  if (n->type() == hit::NodeType::Field)
196  _map[fullpath] = n;
197 }
198 
199 void
200 OverrideParamWalker::walk(const std::string & fullpath,
201  const std::string & /*nodepath*/,
202  hit::Node * n)
203 {
204  const auto it = _map.find(fullpath);
205  if (it != _map.end())
206  warnings.push_back(hit::errormsg(n,
207  " Parameter '",
208  fullpath,
209  "' overrides the same parameter in ",
210  it->second->filename(),
211  ":",
212  it->second->line()));
213 }
214 
215 void
216 BadActiveWalker ::walk(const std::string & /*fullpath*/,
217  const std::string & /*nodepath*/,
218  hit::Node * section)
219 {
220  auto actives = section->find("active");
221  auto inactives = section->find("inactive");
222 
223  if (actives && inactives && actives->type() == hit::NodeType::Field &&
224  inactives->type() == hit::NodeType::Field && actives->parent() == inactives->parent())
225  {
226  errors.push_back(
227  hit::errormsg(section, "'active' and 'inactive' parameters both provided in section"));
228  return;
229  }
230 
231  // ensures we don't recheck deeper nesting levels
232  if (actives && actives->type() == hit::NodeType::Field && actives->parent() == section)
233  {
234  auto vars = section->param<std::vector<std::string>>("active");
235  std::string msg = "";
236  for (auto & var : vars)
237  {
238  if (!section->find(var))
239  msg += var + ", ";
240  }
241  if (msg.size() > 0)
242  {
243  msg = msg.substr(0, msg.size() - 2);
244  errors.push_back(hit::errormsg(section,
245  "variables listed as active (",
246  msg,
247  ") in section '",
248  section->fullpath(),
249  "' not found in input"));
250  }
251  }
252  // ensures we don't recheck deeper nesting levels
253  if (inactives && inactives->type() == hit::NodeType::Field && inactives->parent() == section)
254  {
255  auto vars = section->param<std::vector<std::string>>("inactive");
256  std::string msg = "";
257  for (auto & var : vars)
258  {
259  if (!section->find(var))
260  msg += var + ", ";
261  }
262  if (msg.size() > 0)
263  {
264  msg = msg.substr(0, msg.size() - 2);
265  errors.push_back(hit::errormsg(section,
266  "variables listed as inactive (",
267  msg,
268  ") in section '",
269  section->fullpath(),
270  "' not found in input"));
271  }
272  }
273 }
274 
275 class FindAppWalker : public hit::Walker
276 {
277 public:
278  void
279  walk(const std::string & /*fullpath*/, const std::string & /*nodepath*/, hit::Node * n) override
280  {
281  if (n && n->type() == hit::NodeType::Field && n->fullpath() == "Application/type")
282  _app_type = n->param<std::string>();
283  }
284  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 &
292 {
293  if (_input_filenames.empty())
294  mooseError("Parser::getLastInputFileName(): No inputs are set");
295  return _input_filenames.back();
296 }
297 
298 void
300 {
301  _root.reset();
302 
303  if (_input_text && _input_text->size() != getInputFileNames().size())
304  mooseError("Input text is not the same size as the input filenames");
305 
306  if (getInputFileNames().size() > 1)
307  mooseInfo("Merging inputs ", Moose::stringify(getInputFileNames()));
308 
309  CompileParamWalker::ParamMap override_map;
310  CompileParamWalker cpw(override_map);
311  OverrideParamWalker opw(override_map);
312 
313  std::string errmsg;
314  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  std::getenv("MOOSE_RELATIVE_FILEPATHS") ? std::getenv("MOOSE_RELATIVE_FILEPATHS") : "false";
319  const auto use_real_paths = use_rel_paths_str == "0" || use_rel_paths_str == "false";
320 
321  for (const auto i : index_range(getInputFileNames()))
322  {
323  const auto & input_filename = getInputFileNames()[i];
324  const auto corrected_filename =
325  use_real_paths ? MooseUtils::realpath(input_filename) : input_filename;
326 
327  // Parse the input text string if provided, otherwise read file from disk
328  std::string input;
329  std::string errmsg;
330  if (_input_text)
331  input = (*_input_text)[i];
332  else
333  {
334  MooseUtils::checkFileReadable(corrected_filename, true);
335  std::ifstream f(corrected_filename);
336  input = std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
337  }
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  std::stringstream input_errors;
344  std::unique_ptr<hit::Node> root(hit::parse(corrected_filename, input, &input_errors));
345  hit::explode(root.get());
346  DupParamWalker dw;
347  root->walk(&dw, hit::NodeType::Field);
348  if (!_root)
349  _root = std::move(root);
350  else
351  {
352  root->walk(&opw, hit::NodeType::Field);
353  hit::merge(root.get(), _root.get());
354  }
355 
356  if (!input_errors.str().empty())
357  throw hit::ParseError(input_errors.str());
358 
359  for (auto & msg : dw.errors)
360  errmsg += msg + "\n";
361 
362  dw_errmsg.push_back(errmsg);
363  _root->walk(&cpw, hit::NodeType::Field);
364  }
365  catch (hit::ParseError & err)
366  {
367  mooseError(err.what());
368  }
369  }
370 
371  // warn about overridden parameters in multiple inputs
372  if (!opw.warnings.empty())
373  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  BadActiveWalker bw;
378  _root->walk(&bw, hit::NodeType::Section);
379 
380  FindAppWalker fw;
381  _root->walk(&fw, hit::NodeType::Field);
382  if (fw.getApp())
383  setAppType(*fw.getApp());
384 
385  for (auto & msg : bw.errors)
386  errmsg += msg + "\n";
387 
388  // Print parse errors related to bad active early
389  if (errmsg.size() > 0)
390  mooseError(errmsg);
391 
392  for (auto & msg : dw_errmsg)
393  if (msg.size() > 0)
394  mooseError(msg);
395 }
const std::vector< std::string > _input_filenames
The input file names.
Definition: Parser.h:166
OStreamProxy err
void setAppType(const std::string &app_type)
Definition: Parser.h:149
void parse()
Parses the inputs.
Definition: Parser.C:299
const std::optional< std::vector< std::string > > _input_text
The optional input text contents (to support not reading by file)
Definition: Parser.h:169
std::vector< std::string > errors
Definition: Parser.h:67
auto exp(const T &)
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
ParamMap & _map
Definition: Parser.h:80
char ** vars
virtual std::string eval(hit::Field *n, const std::list< std::string > &args, hit::BraceExpander &exp)
Definition: Parser.C:89
void walk(const std::string &, const std::string &, hit::Node *n) override
Definition: Parser.C:279
const std::string & getLastInputFileName() const
Definition: Parser.C:291
void mooseInfoRepeated(Args &&... args)
Emit an informational message with the given stringified, concatenated args.
Definition: MooseError.h:377
const std::vector< std::string > & getInputFileNames() const
Definition: Parser.h:139
std::vector< std::string > warnings
Definition: Parser.h:89
std::string realpath(const std::string &path)
Wrapper around PetscGetRealPath, which is a cross-platform replacement for realpath.
Definition: MooseUtils.C:1231
std::set< std::string > _duplicates
Definition: Parser.h:57
virtual void walk(const std::string &fullpath, const std::string &, hit::Node *n) override
Definition: Parser.C:191
void mooseInfo(Args &&... args)
Emit an informational message with the given stringified, concatenated args.
Definition: MooseError.h:369
Parser(const std::vector< std::string > &input_filenames, const std::optional< std::vector< std::string >> &input_text={})
Constructor given a list of input files, given in input_filenames.
Definition: Parser.C:155
std::map< std::string, hit::Node * > _have
Definition: Parser.h:58
constexpr auto merge(std::index_sequence< first... >, std::index_sequence< second... >)
Merge two index sequences into one.
bool checkFileReadable(const std::string &filename, bool check_line_endings=false, bool throw_on_unreadable=true, bool check_for_git_lfs_pointer=true)
Checks to see if a file is readable (exists and permissions)
Definition: MooseUtils.C:250
std::string stringify(const T &t)
conversion to string
Definition: Conversion.h:64
std::map< std::string, hit::Node * > ParamMap
Definition: Parser.h:73
hit::Node * root()
Definition: Parser.h:134
Physical unit management class with runtime unit string parsing, unit checking, unit conversion...
Definition: Units.h:32
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const std::optional< std::string > & getApp()
Definition: Parser.C:284
virtual void walk(const std::string &fullpath, const std::string &, hit::Node *n) override
Definition: Parser.C:172
std::unique_ptr< hit::Node > _root
The root node, which owns the whole tree.
Definition: Parser.h:163
void walk(const std::string &fullpath, const std::string &, hit::Node *n) override
Definition: Parser.C:200
std::optional< std::string > _app_type
Definition: Parser.C:284
virtual std::string eval(hit::Field *n, const std::list< std::string > &args, hit::BraceExpander &exp)
Definition: Parser.C:37
auto index_range(const T &sizable)
Class for parsing input files.
Definition: Parser.h:100
std::vector< std::string > errors
Definition: Parser.h:54
virtual void walk(const std::string &, const std::string &, hit::Node *section) override
Definition: Parser.C:216
const CompileParamWalker::ParamMap & _map
Definition: Parser.h:92
const Real pi