https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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 "MooseTypes.h"
14#include "CommandLine.h"
15#include "SystemInfo.h"
16#include "Parser.h"
17#include "Units.h"
18
19#include "libmesh/parallel.h"
20#include "libmesh/fparser.hh"
21
22// C++ includes
23#include <map>
24#include <fstream>
25#include <algorithm>
26#include <cstdlib>
27
28std::string
29FuncParseEvaler::eval(hit::Field * n, const std::list<std::string> & args, hit::BraceExpander & exp)
30{
31 std::string func_text;
32 for (auto & s : args)
33 func_text += s;
34 auto n_errs = exp.errors.size();
35
36 FunctionParser fp;
37 fp.AddConstant("pi", libMesh::pi);
38 fp.AddConstant("e", std::exp(Real(1)));
39 std::vector<std::string> var_names;
40 auto ret = fp.ParseAndDeduceVariables(func_text, var_names);
41 if (ret != -1)
42 {
43 exp.errors.emplace_back(
44 "fparse error: " + std::string(fp.ErrorMsg()) + " in '" + n->fullpath() + "'", n);
45 return n->val();
46 }
47
48 std::vector<double> var_vals;
49 for (auto & var : var_names)
50 {
51 // recursively check all parent scopes for the needed variables
52 hit::Node * curr = n;
53 while ((curr = curr->parent()))
54 {
55 auto src = curr->find(var);
56 if (src && src != n && src->type() == hit::NodeType::Field)
57 {
58 exp.used.push_back(hit::pathJoin({curr->fullpath(), var}));
59 var_vals.push_back(curr->param<double>(var));
60 break;
61 }
62 }
63
64 if (curr == nullptr)
65 exp.errors.emplace_back("no variable '" + var +
66 "' found for use in function parser expression in '" +
67 n->fullpath() + "'",
68 n);
69 }
70
71 if (exp.errors.size() != n_errs)
72 return n->val();
73
74 std::stringstream ss;
75 ss << std::setprecision(17) << fp.Eval(var_vals.data());
76
77 // change kind only (not val)
78 n->setVal(n->val(), hit::Field::Kind::Float);
79 return ss.str();
80}
81
82std::string
84 const std::list<std::string> & args,
85 hit::BraceExpander & exp)
86{
87 std::vector<std::string> argv;
88 argv.insert(argv.begin(), args.begin(), args.end());
89
90 // no conversion, the expression currently only documents the units and passes through the value
91 if (argv.size() == 2)
92 {
93 n->setVal(n->val(), hit::Field::Kind::Float);
94 return argv[0];
95 }
96
97 // conversion
98 if (argv.size() != 4 || (argv.size() >= 3 && argv[2] != "->"))
99 {
100 exp.errors.emplace_back("units error: Expected 4 arguments ${units number from_unit -> "
101 "to_unit} or 2 arguments ${units number unit} in '" +
102 n->fullpath() + "'",
103 n);
104 return n->val();
105 }
106
107 // get and check units
108 auto from_unit = MooseUnits(argv[1]);
109 auto to_unit = MooseUnits(argv[3]);
110 if (!from_unit.conformsTo(to_unit))
111 {
112 std::ostringstream err;
113 err << "units error: " << argv[1] << " (" << from_unit << ") does not convert to " << argv[3]
114 << " (" << to_unit << ") in '" << n->fullpath() << "'";
115 exp.errors.emplace_back(err.str(), n);
116 return n->val();
117 }
118
119 // parse number
120 Real num = MooseUtils::convert<Real>(argv[0]);
121
122 // convert units
123 std::stringstream ss;
124 ss << std::setprecision(17) << to_unit.convert(num, from_unit);
125
126#ifndef NDEBUG
127 mooseInfoRepeated(n->filename() + ':' + Moose::stringify(n->line()) + ':' +
128 Moose::stringify(n->column()) + ": Unit conversion ",
129 num,
130 ' ',
131 argv[1],
132 " -> ",
133 ss.str(),
134 ' ',
135 argv[3]);
136#endif
137
138 // change kind only (not val)
139 n->setVal(n->val(), hit::Field::Kind::Float);
140 return ss.str();
141}
142
143std::string
144EnumerateEvaler::eval(hit::Field * n, const std::list<std::string> & args, hit::BraceExpander & exp)
145{
146 std::vector<std::string> argv;
147 argv.insert(argv.begin(), args.begin(), args.end());
148
149 if (argv.size() != 3)
150 {
151 exp.errors.emplace_back(
152 "enumerate error: Expected 3 arguments ${enumerate prefix first_index last_index} in '" +
153 n->fullpath() + "'",
154 n);
155 return n->val();
156 }
157
158 const auto & prefix = argv[0];
159 std::array<int, 2> index;
160 for (const auto i : make_range(2))
161 {
162 const auto & arg = argv[i + 1];
163 if (arg.empty() || arg.find_first_not_of("0123456789") != std::string::npos)
164 {
165 exp.errors.emplace_back("enumerate error: index '" + arg +
166 "' is not a non-negative integer in '" + n->fullpath() + "'",
167 n);
168 return n->val();
169 }
170 index[i] = MooseUtils::convert<int>(arg);
171 }
172
173 if (index[1] < index[0])
174 {
175 exp.errors.emplace_back("enumerate error: last index " + argv[2] +
176 " is smaller than first index " + argv[1] + " in '" +
177 n->fullpath() + "'",
178 n);
179 return n->val();
180 }
181
182 std::vector<std::string> names;
183 for (const auto i : make_range(index[0], index[1] + 1))
184 names.push_back(prefix + std::to_string(i));
185
186 return MooseUtils::stringJoin(names);
187}
188
189std::string
190RepeatEvaler::eval(hit::Field * n, const std::list<std::string> & args, hit::BraceExpander & exp)
191{
192 std::vector<std::string> argv;
193 argv.insert(argv.begin(), args.begin(), args.end());
194
195 if (argv.size() != 2)
196 {
197 exp.errors.emplace_back(
198 "repeat error: Expected 2 arguments ${repeat name count} in '" + n->fullpath() + "'", n);
199 return n->val();
200 }
201
202 const auto & count_arg = argv[1];
203 if (count_arg.empty() || count_arg.find_first_not_of("0123456789") != std::string::npos)
204 {
205 exp.errors.emplace_back("repeat error: count '" + count_arg +
206 "' is not a non-negative integer in '" + n->fullpath() + "'",
207 n);
208 return n->val();
209 }
210
211 const std::vector<std::string> values(MooseUtils::convert<int>(count_arg), argv[0]);
213}
214
215Parser::Parser(const std::vector<std::string> & input_filenames,
216 const std::optional<std::vector<std::string>> & input_text /* = {} */)
217 : _root(nullptr),
218 _input_filenames(input_filenames),
219 _input_text(input_text ? *input_text : std::vector<std::string>()),
220 _cli_root(nullptr),
221 _throw_on_error(false)
222{
223 if (input_text && _input_filenames.size() != input_text->size())
224 mooseError("Parser: Input text not the same length as input filenames");
225}
226
227Parser::Parser(const std::string & input_filename,
228 const std::optional<std::string> & input_text /* = {} */)
229 : Parser(std::vector<std::string>{input_filename},
230 input_text ? std::optional<std::vector<std::string>>({*input_text})
231 : std::optional<std::vector<std::string>>())
232{
233}
234
235void
236DupParamWalker::walk(const std::string & fullpath, const std::string & /*nodepath*/, hit::Node * n)
237{
238 const auto it = _have.try_emplace(fullpath, n);
239 if (!it.second)
240 {
241 const std::string type = n->type() == hit::NodeType::Field ? "parameter" : "section";
242 const std::string error = type + " '" + fullpath + "' supplied multiple times";
243
244 // Don't warn multiple times (will happen if we find it three+ times)
245 const auto existing = it.first->second;
246 if (std::find_if(errors.begin(),
247 errors.end(),
248 [&existing](const auto & err)
249 { return err.node == existing; }) == errors.end())
250 errors.emplace_back(error, existing);
251
252 errors.emplace_back(error, n);
253 }
254}
255
256void
257CompileParamWalker::walk(const std::string & fullpath,
258 const std::string & /*nodepath*/,
259 hit::Node * n)
260{
261 if (n->type() == hit::NodeType::Field)
262 _map[fullpath] = n;
263}
264
265void
266OverrideParamWalker::walk(const std::string & fullpath,
267 const std::string & /*nodepath*/,
268 hit::Node * n)
269{
270 const auto it = _map.find(fullpath);
271 if (it != _map.end())
272 warnings.push_back(hit::errormsg(n,
273 " Parameter '",
274 fullpath,
275 "' overrides the same parameter in ",
276 it->second->filename(),
277 ":",
278 it->second->line()));
279}
280
281void
282BadActiveWalker ::walk(const std::string & fullpath,
283 const std::string & /*nodepath*/,
284 hit::Node * section)
285{
286 auto actives = section->find("active");
287 auto inactives = section->find("inactive");
288
289 if (actives && inactives && actives->type() == hit::NodeType::Field &&
290 inactives->type() == hit::NodeType::Field && actives->parent() == inactives->parent())
291 {
292 errors.emplace_back(
293 "'active' and 'inactive' parameters both provided in section '" + fullpath + "'", section);
294 return;
295 }
296
297 // ensures we don't recheck deeper nesting levels
298 if (actives && actives->type() == hit::NodeType::Field && actives->parent() == section)
299 {
300 auto vars = section->param<std::vector<std::string>>("active");
301 std::string msg = "";
302 for (auto & var : vars)
303 {
304 if (!section->find(var))
305 msg += var + ", ";
306 }
307 if (msg.size() > 0)
308 {
309 msg = msg.substr(0, msg.size() - 2);
310 errors.emplace_back("variables listed as active (" + msg + ") in section '" +
311 section->fullpath() + "' not found in input",
312 section);
313 }
314 }
315 // ensures we don't recheck deeper nesting levels
316 if (inactives && inactives->type() == hit::NodeType::Field && inactives->parent() == section)
317 {
318 auto vars = section->param<std::vector<std::string>>("inactive");
319 std::string msg = "";
320 for (auto & var : vars)
321 {
322 if (!section->find(var))
323 msg += var + ", ";
324 }
325 if (msg.size() > 0)
326 {
327 msg = msg.substr(0, msg.size() - 2);
328 errors.emplace_back("variables listed as inactive (" + msg + ") in section '" +
329 section->fullpath() + "' not found in input",
330 section);
331 }
332 }
333}
334
335class FindAppWalker : public hit::Walker
336{
337public:
338 void
339 walk(const std::string & /*fullpath*/, const std::string & /*nodepath*/, hit::Node * n) override
340 {
341 if (n && n->type() == hit::NodeType::Field && n->fullpath() == "Application/type")
342 _app_type = n->param<std::string>();
343 }
344 const std::optional<std::string> & getApp() { return _app_type; };
345
346private:
347 std::optional<std::string> _app_type;
348};
349
350void
351Parser::setCommandLineParams(const std::vector<std::string> & params)
352{
353 mooseAssert(!_command_line_params, "Already set");
354 _command_line_params = params;
355}
356
357const std::string &
359{
360 if (_input_filenames.empty())
361 mooseError("Parser::getLastInputFileName(): No inputs are set");
362 return _input_filenames.back();
363}
364
365Parser::Error::Error(const std::vector<hit::ErrorMessage> & error_messages)
366 : hit::Error(error_messages)
367{
368}
369
370void
372{
373 mooseAssert(!_root && !_cli_root, "Has already parsed");
374
375 if (getInputFileNames().size() > 1)
376 mooseInfo("Merging inputs ", Moose::stringify(getInputFileNames()));
377
378 // Correct filenames (default is to use real path)
379 const std::string use_rel_paths_str =
380 std::getenv("MOOSE_RELATIVE_FILEPATHS") ? std::getenv("MOOSE_RELATIVE_FILEPATHS") : "false";
381 const auto use_real_paths = use_rel_paths_str == "0" || use_rel_paths_str == "false";
382 std::vector<std::string> filenames;
383 for (const auto & filename : getInputFileNames())
384 filenames.push_back(use_real_paths ? MooseUtils::realpath(filename) : filename);
385
386 // Load each input file if text was not provided
387 if (_input_text.empty())
388 for (const auto & filename : filenames)
389 {
390 MooseUtils::checkFileReadable(filename, true);
391 std::ifstream f(filename);
392 _input_text.push_back(
393 std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>()));
394 }
395
396 CompileParamWalker::ParamMap override_map;
397 CompileParamWalker cpw(override_map);
398 OverrideParamWalker opw(override_map);
399
400 // Errors from the duplicate param walker, ran within each input
401 // independently first
402 std::vector<hit::ErrorMessage> dw_errors;
403
404 for (const auto i : index_range(getInputFileNames()))
405 {
406 const auto & filename = filenames[i];
407 const auto & input = getInputText()[i];
408
409 try
410 {
411 // provide stream to hit parse function to capture any syntax errors,
412 // set parser root node, then throw those errors if any were captured
413 std::vector<hit::ErrorMessage> syntax_errors;
414 std::unique_ptr<hit::Node> root(hit::parse(filename, input, &syntax_errors));
415
417 root->walk(&dw, hit::NodeType::Field);
418 appendErrorMessages(dw_errors, dw.errors);
419
420 if (!queryRoot())
421 _root = std::move(root);
422 else
423 {
424 root->walk(&opw, hit::NodeType::Field);
425 hit::merge(root.get(), &getRoot());
426 }
427
428 if (!syntax_errors.empty())
429 throw Parser::Error(syntax_errors);
430
431 getRoot().walk(&cpw, hit::NodeType::Field);
432 }
433 catch (hit::Error & err)
434 {
435 parseError(err.error_messages);
436 }
437 }
438
439 // warn about overridden parameters in multiple inputs
440 if (!opw.warnings.empty())
442
443 // If we don't have a root (allow no input files),
444 // create an empty one
445 if (!queryRoot())
446 _root.reset(hit::parse("EMPTY", ""));
447
448 {
450 getRoot().walk(&bw, hit::NodeType::Section);
451 if (bw.errors.size())
452 parseError(bw.errors);
453 }
454
455 {
456 FindAppWalker fw;
457 getRoot().walk(&fw, hit::NodeType::Field);
458 if (fw.getApp())
459 setAppType(*fw.getApp());
460 }
461
462 // Duplicate parameter errors (within each input file)
463 if (dw_errors.size())
464 parseError(dw_errors);
465
466 // Merge in command line HIT arguments
467 const auto joined_params =
469 try
470 {
471 _cli_root.reset(hit::parse("CLI_ARGS", joined_params));
472 hit::merge(&getCommandLineRoot(), &getRoot());
473 }
474 catch (hit::Error & err)
475 {
476 parseError(err.error_messages);
477 }
478
479 std::vector<hit::ErrorMessage> errors;
480
481 // expand ${bla} parameter values and mark/include variables
482 // used in expansion as "used" (obtained later by the Builder
483 // with getExtractedVars())
484 {
485 hit::RawEvaler raw;
486 hit::EnvEvaler env;
487 hit::ReplaceEvaler repl;
488 FuncParseEvaler fparse_ev;
489 UnitsConversionEvaler units_ev;
490 EnumerateEvaler enumerate_ev;
491 RepeatEvaler repeat_ev;
492 hit::BraceExpander exw;
493 exw.registerEvaler("raw", raw);
494 exw.registerEvaler("env", env);
495 exw.registerEvaler("fparse", fparse_ev);
496 exw.registerEvaler("replace", repl);
497 exw.registerEvaler("units", units_ev);
498 exw.registerEvaler("enumerate", enumerate_ev);
499 exw.registerEvaler("repeat", repeat_ev);
500 getRoot().walk(&exw);
501 for (auto & var : exw.used)
502 _extracted_vars.insert(var);
503 Parser::appendErrorMessages(errors, exw.errors);
504 }
505
506 // Collect duplicate parameters now that we've merged inputs
507 {
509 getRoot().walk(&dw, hit::NodeType::Field);
511 }
512
513 // Check bad active now that we've merged inputs
514 {
516 getRoot().walk(&bw, hit::NodeType::Section);
518 }
519
520 if (errors.size())
521 parseError(errors);
522}
523
524hit::Node &
526{
527 if (!queryRoot())
528 mooseError("Parser::getRoot(): root is not set");
529 return *queryRoot();
530}
531
532const hit::Node &
534{
536 mooseError("Parser::getCommandLineRoot(): command line root is not set");
537 return *queryCommandLineRoot();
538}
539
540hit::Node &
542{
543 return const_cast<hit::Node &>(std::as_const(*this).getCommandLineRoot());
544}
545
546void
547Parser::appendErrorMessages(std::vector<hit::ErrorMessage> & to,
548 const std::vector<hit::ErrorMessage> & from)
549{
550 to.insert(to.end(), from.begin(), from.end());
551}
552
553void
554Parser::appendErrorMessages(std::vector<hit::ErrorMessage> & to, const hit::Error & error)
555{
556 appendErrorMessages(to, error.error_messages);
557}
558
559std::string
560Parser::joinErrorMessages(const std::vector<hit::ErrorMessage> & error_messages)
561{
562 std::vector<std::string> values;
563 for (const auto & em : error_messages)
564 values.push_back(em.prefixed_message);
565 return MooseUtils::stringJoin(values, "\n");
566}
567
568void
569Parser::parseError(std::vector<hit::ErrorMessage> messages) const
570{
571 // Few things about command line arguments...
572 // 1. We don't care to add line and column context for CLI args, because
573 // it doesn't make sense. We go from the full CLI args and pull out
574 // the HIT parameters so "line" 1 might not even be command line
575 // argument 1. So, remove line/column context from all CLI args.
576 // 2. Whenever we have a parameter in input that then gets overridden
577 // by a command line argument, under the hood we're merging two
578 // different HIT trees. However, WASP doesn't currently update the
579 // "filename" context for the updated parameter. Which means that
580 // a param that is in input and then overridden by CLI will have
581 // its location as in input. Which isn't true. So we get around this
582 // by searching the independent CLI args tree for params that we have
583 // errors for. If the associated path is also in CLI args, we manually
584 // set its error to come from CLI args. This should be fixed in
585 // the future with a WASP update.
586 for (auto & em : messages)
587 if (em.node && queryCommandLineRoot())
588 if (getCommandLineRoot().find(em.node->fullpath()))
589 em = hit::ErrorMessage(em.message, "CLI_ARGS");
590
591 if (_throw_on_error)
592 throw Parser::Error(messages);
593 else
594 mooseError(joinErrorMessages(messages));
595}
void mooseInfoRepeated(Args &&... args)
Emit an informational message with the given stringified, concatenated args.
Definition MooseError.h:409
void mooseInfo(Args &&... args)
Emit an informational message with the given stringified, concatenated args.
Definition MooseError.h:401
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
std::array< Real, 2 > values
Definition MortarUtils.C:52
char ** vars
std::vector< hit::ErrorMessage > errors
Definition Parser.h:68
virtual void walk(const std::string &, const std::string &, hit::Node *section) override
Definition Parser.C:282
std::map< std::string, hit::Node * > ParamMap
Definition Parser.h:74
virtual void walk(const std::string &fullpath, const std::string &, hit::Node *n) override
Definition Parser.C:257
ParamMap & _map
Definition Parser.h:81
std::map< std::string, hit::Node * > _have
Definition Parser.h:59
virtual void walk(const std::string &fullpath, const std::string &, hit::Node *n) override
Definition Parser.C:236
std::vector< hit::ErrorMessage > errors
Definition Parser.h:56
virtual std::string eval(hit::Field *n, const std::list< std::string > &args, hit::BraceExpander &exp)
Definition Parser.C:144
std::optional< std::string > _app_type
Definition Parser.C:347
const std::optional< std::string > & getApp()
Definition Parser.C:344
void walk(const std::string &, const std::string &, hit::Node *n) override
Definition Parser.C:339
virtual std::string eval(hit::Field *n, const std::list< std::string > &args, hit::BraceExpander &exp)
Definition Parser.C:29
Physical unit management class with runtime unit string parsing, unit checking, unit conversion,...
Definition Units.h:33
const CompileParamWalker::ParamMap & _map
Definition Parser.h:93
std::vector< std::string > warnings
Definition Parser.h:90
void walk(const std::string &fullpath, const std::string &, hit::Node *n) override
Definition Parser.C:266
Class for parsing input files.
Definition Parser.h:102
std::set< std::string > _extracted_vars
Variables that have been extracted during brace expansion.
Definition Parser.h:266
static void appendErrorMessages(std::vector< hit::ErrorMessage > &to, const std::vector< hit::ErrorMessage > &from)
Helper for accumulating errors from a walker into an accumulation of errors.
Definition Parser.C:547
const std::string & getLastInputFileName() const
Definition Parser.C:358
void setAppType(const std::string &app_type)
Definition Parser.h:184
const hit::Node & getCommandLineRoot() const
Definition Parser.C:533
const hit::Node * queryRoot() const
Definition Parser.h:135
void setCommandLineParams(const std::vector< std::string > &params)
Sets the HIT parameters from the command line.
Definition Parser.C:351
const std::vector< std::string > & getInputText() const
Definition Parser.h:174
std::vector< std::string > _input_text
The input text (may be filled during parse())
Definition Parser.h:251
const std::vector< std::string > & getInputFileNames() const
Definition Parser.h:169
const std::vector< std::string > _input_filenames
The input file names.
Definition Parser.h:248
std::optional< std::vector< std::string > > _command_line_params
The command line HIT parameters (if any)
Definition Parser.h:263
std::unique_ptr< hit::Node > _cli_root
The root node for command line hit arguments.
Definition Parser.h:254
std::unique_ptr< hit::Node > _root
The root node, which owns the whole tree.
Definition Parser.h:245
void parseError(std::vector< hit::ErrorMessage > messages) const
Helper for throwing an error with the given messages.
Definition Parser.C:569
bool _throw_on_error
Whether or not to throw on error.
Definition Parser.h:260
hit::Node & getRoot()
Definition Parser.C:525
const hit::Node * queryCommandLineRoot() const
Definition Parser.h:152
void parse()
Parses the inputs.
Definition Parser.C:371
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:215
static std::string joinErrorMessages(const std::vector< hit::ErrorMessage > &error_messages)
Helper for combining error messages into a single, newline separated message.
Definition Parser.C:560
virtual std::string eval(hit::Field *n, const std::list< std::string > &args, hit::BraceExpander &exp)
Definition Parser.C:190
virtual std::string eval(hit::Field *n, const std::list< std::string > &args, hit::BraceExpander &exp)
Definition Parser.C:83
std::string realpath(const std::string &path)
std::string stringJoin(const std::vector< std::string > &values, const std::string &separator=" ")
Concatenates value into a single string separated by separator.
bool checkFileReadable(const std::string &filename, bool check_line_endings, bool throw_on_unreadable, bool check_for_git_lfs_pointer)
Definition MooseUtils.C:265
std::string stringify(const T &t)
conversion to string
Definition Conversion.h:64
Definition Moose.h:48
const Real pi
Error()=delete