Skip to content

Commit dcd3623

Browse files
authored
Merge pull request idaholab#32707 from bwspenc/skip_include
Add option to prevent expanding include files in hit format
2 parents 7429443 + b6f41ec commit dcd3623

3 files changed

Lines changed: 217 additions & 95 deletions

File tree

framework/contrib/hit/include/hit/parse.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ enum class NodeType
7676
Section, /// Represents hit sections (i.e. "[pathname]...[../]").
7777
Comment, /// Represents comments that are not directly part of the actual hit document.
7878
Field, /// Represents field-value pairs (i.e. paramname=val).
79+
Include, /// Represents !include directives.
7980
Blank, /// Represents a blank line
8081
Other, /// Represents any other type of node
8182
};
@@ -90,6 +91,14 @@ enum class TraversalOrder
9091

9192
class Node;
9293

94+
// structure with options that can be used to control behavior when parsing
95+
// currently only contains option for formatter to not expand include files
96+
// this is passed into parse() and buildHITTree() and may be expanded later
97+
struct ParseOptions
98+
{
99+
bool expand_includes = true;
100+
};
101+
93102
/**
94103
* Struct that contains the context for an error message.
95104
*
@@ -510,6 +519,19 @@ class Blank : public Node
510519
virtual Node * clone(bool /*absolute_path = false*/) override { return new Blank(); };
511520
};
512521

522+
/// Include represents a !include directive in the input.
523+
class Include : public Node
524+
{
525+
public:
526+
Include(std::shared_ptr<wasp::DefaultHITInterpreter> dhi, wasp::HITNodeView hnv);
527+
528+
virtual std::string render(int indent = 0,
529+
const std::string & indent_text = default_indent,
530+
int maxlen = 0) const override;
531+
532+
virtual Node * clone(bool absolute_path = false) override;
533+
};
534+
513535
/// Section represents a hit section including the section header path and all entries inside
514536
/// the section (e.g. fields/parameters, subsections, etc.).
515537
class Section : public Node
@@ -597,7 +619,8 @@ class Field : public Node
597619
/// on parse failure and the root is returned instead of this function throwing an Error.
598620
Node * parse(const std::string & fname,
599621
const std::string & input,
600-
std::vector<ErrorMessage> * syntax_errors = nullptr);
622+
std::vector<ErrorMessage> * syntax_errors = nullptr,
623+
const ParseOptions & options = ParseOptions{});
601624

602625
/// parses the file checking for errors but does not return any node tree.
603626
inline void
@@ -769,6 +792,7 @@ void buildHITTree(std::shared_ptr<wasp::DefaultHITInterpreter> interpreter,
769792
wasp::HITNodeView hnv_parent,
770793
Node * hit_parent,
771794
std::string & previous_file,
772-
std::size_t & previous_line);
795+
std::size_t & previous_line,
796+
const ParseOptions & options = ParseOptions{});
773797

774798
} // namespace hit

framework/contrib/hit/src/hit/parse.cc

Lines changed: 146 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,8 @@ Node::type() const
375375
return NodeType::Section;
376376
else if (_hnv.type() == wasp::KEYED_VALUE || _hnv.type() == wasp::ARRAY)
377377
return NodeType::Field;
378+
else if (_hnv.type() == wasp::FILE)
379+
return NodeType::Include;
378380
return NodeType::Other;
379381
}
380382

@@ -554,6 +556,26 @@ Comment::setInline(bool is_inline)
554556
_isinline = is_inline;
555557
}
556558

559+
Include::Include(std::shared_ptr<wasp::DefaultHITInterpreter> dhi, wasp::HITNodeView hnv)
560+
: Node(dhi, hnv)
561+
{
562+
}
563+
564+
std::string
565+
Include::render(int indent, const std::string & indent_text, int /*maxlen*/) const
566+
{
567+
return "\n" + strRepeat(indent_text, indent) + _hnv.data();
568+
}
569+
570+
Node *
571+
Include::clone(bool absolute_path)
572+
{
573+
auto n = new Include(_dhi, _hnv);
574+
if (absolute_path)
575+
n->setOverridePath(fullpath());
576+
return n;
577+
}
578+
557579
Section::Section(const std::string & path) : Node(path) {}
558580

559581
Section::Section(std::shared_ptr<wasp::DefaultHITInterpreter> dhi, wasp::HITNodeView hnv)
@@ -1024,7 +1046,8 @@ Field::strVal() const
10241046
Node *
10251047
parse(const std::string & fname,
10261048
const std::string & input,
1027-
std::vector<ErrorMessage> * syntax_errors /* = nullptr */)
1049+
std::vector<ErrorMessage> * syntax_errors /* = nullptr */,
1050+
const ParseOptions & options /* = ParseOptions{} */)
10281051
{
10291052
std::stringstream input_errors;
10301053
std::shared_ptr<wasp::DefaultHITInterpreter> interpreter =
@@ -1056,7 +1079,8 @@ parse(const std::string & fname,
10561079
{
10571080
std::string starting_file = interpreter->root().node_pool()->stream_name();
10581081
std::size_t starting_line = interpreter->root().line();
1059-
buildHITTree(interpreter, interpreter->root(), root.get(), starting_file, starting_line);
1082+
buildHITTree(
1083+
interpreter, interpreter->root(), root.get(), starting_file, starting_line, options);
10601084
}
10611085

10621086
return root.release();
@@ -1186,7 +1210,10 @@ Formatter::Formatter(const std::string & fname, const std::string & hit_config)
11861210
std::string
11871211
Formatter::format(const std::string & fname, const std::string & input)
11881212
{
1189-
std::unique_ptr<hit::Node> root(hit::parse(fname, input));
1213+
// formatting should parse the document without included content expanded
1214+
ParseOptions options;
1215+
options.expand_includes = false;
1216+
std::unique_ptr<hit::Node> root(hit::parse(fname, input, nullptr, options));
11901217
format(root.get());
11911218
return root->render(0, indent_string, line_length);
11921219
}
@@ -1311,112 +1338,138 @@ buildHITTree(std::shared_ptr<wasp::DefaultHITInterpreter> interpreter,
13111338
wasp::HITNodeView hnv_parent,
13121339
Node * hit_parent,
13131340
std::string & previous_file,
1314-
std::size_t & previous_line)
1341+
std::size_t & previous_line,
1342+
const ParseOptions & options)
13151343
{
13161344
if (hnv_parent.is_null())
13171345
return;
13181346

1319-
for (const auto & hnv_child : hnv_parent)
1347+
// do range-based traversal in normal conditions to expand included files
1348+
// do index-based traversal for format to not descend into included files
1349+
auto walk_children = [](wasp::HITNodeView hnv_parent, bool expand_includes, auto visit)
13201350
{
1321-
// create and add comment node as terminal leaf but do not recurse deeper
1322-
if (hnv_child.type() == wasp::COMMENT)
1323-
{
1324-
// add blank line if needed between previous node line and this node line
1325-
if (hnv_child.node_pool()->stream_name() == previous_file &&
1326-
hnv_child.line() > previous_line + 1)
1327-
hit_parent->addChild(new Blank());
1328-
1329-
auto hit_child = new Comment(interpreter, hnv_child);
1330-
if (hnv_child.node_pool()->stream_name() == previous_file &&
1331-
hnv_child.line() == previous_line && hit_parent->children().size() > 0)
1332-
{
1333-
hit_child->setInline(true);
1334-
hit_parent->children().back()->addChild(hit_child);
1335-
}
1336-
else
1337-
{
1338-
hit_child->setInline(false);
1339-
hit_parent->addChild(hit_child);
1340-
}
1341-
previous_file = hnv_child.node_pool()->stream_name();
1342-
previous_line = hnv_child.last_line();
1343-
}
1344-
1345-
// create and add field node depending on conflicts but recurse no deeper
1346-
else if (hnv_child.type() == wasp::KEYED_VALUE || hnv_child.type() == wasp::ARRAY)
1347-
{
1348-
// check if tree has field conflict and overrides need to be considered
1349-
if (auto found_field = hit_parent->find(hnv_child.name());
1350-
found_field && found_field->type() == NodeType::Field)
1351+
if (expand_includes)
1352+
for (const auto & hnv_child : hnv_parent)
1353+
visit(hnv_child);
1354+
else
1355+
for (std::size_t i = 0, count = hnv_parent.child_count(); i < count; i++)
1356+
visit(hnv_parent.child_at(i));
1357+
};
1358+
1359+
walk_children(
1360+
hnv_parent,
1361+
options.expand_includes,
1362+
[&](const auto & hnv_child)
13511363
{
1352-
// capture any override settings used for both existing and new field
1353-
bool override_for_old_node = wasp::is_override(found_field->getNodeView());
1354-
bool override_for_new_node = wasp::is_override(hnv_child);
1355-
1356-
// use overrides of conflicting fields to decide which has precedence
1357-
if (!override_for_old_node && !override_for_new_node)
1364+
// create and add comment node as terminal leaf but do not recurse deeper
1365+
if (hnv_child.type() == wasp::COMMENT)
13581366
{
1359-
// neither has override so add new next to existing for error later
1360-
hit_parent->addChild(new Field(interpreter, hnv_child));
1367+
// add blank line if needed between previous node line and this node line
1368+
if (hnv_child.node_pool()->stream_name() == previous_file &&
1369+
hnv_child.line() > previous_line + 1)
1370+
hit_parent->addChild(new Blank());
1371+
1372+
auto hit_child = new Comment(interpreter, hnv_child);
1373+
if (hnv_child.node_pool()->stream_name() == previous_file &&
1374+
hnv_child.line() == previous_line && hit_parent->children().size() > 0)
1375+
{
1376+
hit_child->setInline(true);
1377+
hit_parent->children().back()->addChild(hit_child);
1378+
}
1379+
else
1380+
{
1381+
hit_child->setInline(false);
1382+
hit_parent->addChild(hit_child);
1383+
}
13611384
previous_file = hnv_child.node_pool()->stream_name();
13621385
previous_line = hnv_child.last_line();
13631386
}
1364-
else if (!override_for_old_node && override_for_new_node)
1387+
1388+
// create and add field node depending on conflicts but recurse no deeper
1389+
else if (hnv_child.type() == wasp::KEYED_VALUE || hnv_child.type() == wasp::ARRAY)
13651390
{
1366-
// only new field has override so remove existing field and replace
1367-
const auto & hit_siblings = hit_parent->children();
1368-
for (std::size_t index = 0; index < hit_siblings.size(); index++)
1369-
if (hit_siblings[index] == found_field)
1391+
// check if tree has field conflict and overrides need to be considered
1392+
if (auto found_field = hit_parent->find(hnv_child.name());
1393+
found_field && found_field->type() == NodeType::Field)
1394+
{
1395+
// capture any override settings used for both existing and new field
1396+
bool override_for_old_node = wasp::is_override(found_field->getNodeView());
1397+
bool override_for_new_node = wasp::is_override(hnv_child);
1398+
1399+
// use overrides of conflicting fields to decide which has precedence
1400+
if (!override_for_old_node && !override_for_new_node)
1401+
{
1402+
// neither has override so add new next to existing for error later
1403+
hit_parent->addChild(new Field(interpreter, hnv_child));
1404+
previous_file = hnv_child.node_pool()->stream_name();
1405+
previous_line = hnv_child.last_line();
1406+
}
1407+
else if (!override_for_old_node && override_for_new_node)
13701408
{
1371-
hit_parent->insertChild(index, new Field(interpreter, hnv_child));
1372-
delete found_field;
1373-
break;
1409+
// only new field has override so remove existing field and replace
1410+
const auto & hit_siblings = hit_parent->children();
1411+
for (std::size_t index = 0; index < hit_siblings.size(); index++)
1412+
if (hit_siblings[index] == found_field)
1413+
{
1414+
hit_parent->insertChild(index, new Field(interpreter, hnv_child));
1415+
delete found_field;
1416+
break;
1417+
}
13741418
}
1419+
else if (override_for_old_node && override_for_new_node)
1420+
// both fields have override and this is not allowed so throw error
1421+
throw Error("'" + found_field->fullpath() +
1422+
"' specified more than once with override syntax",
1423+
found_field);
1424+
}
1425+
else
1426+
{
1427+
// no conflict so add blank line if necessary then add new field node
1428+
if (hnv_child.node_pool()->stream_name() == previous_file &&
1429+
hnv_child.line() > previous_line + 1)
1430+
hit_parent->addChild(new Blank());
1431+
hit_parent->addChild(new Field(interpreter, hnv_child));
1432+
previous_file = hnv_child.node_pool()->stream_name();
1433+
previous_line = hnv_child.last_line();
1434+
}
13751435
}
1376-
else if (override_for_old_node && override_for_new_node)
1377-
// both fields have override and this is not allowed so throw error
1378-
throw Error("'" + found_field->fullpath() +
1379-
"' specified more than once with override syntax",
1380-
found_field);
1381-
}
1382-
else
1383-
{
1384-
// no conflict so add blank line if necessary then add new field node
1385-
if (hnv_child.node_pool()->stream_name() == previous_file &&
1386-
hnv_child.line() > previous_line + 1)
1387-
hit_parent->addChild(new Blank());
1388-
hit_parent->addChild(new Field(interpreter, hnv_child));
1389-
previous_file = hnv_child.node_pool()->stream_name();
1390-
previous_line = hnv_child.last_line();
1391-
}
1392-
}
13931436

1394-
// create and add section node if not found in tree then recurse children
1395-
else if (hnv_child.type() == wasp::OBJECT || hnv_child.type() == wasp::DOCUMENT_ROOT)
1396-
{
1397-
// recurse using section if found and do not create or add anything new
1398-
if (auto hit_child = hit_parent->find(hnv_child.name());
1399-
hit_child && hit_child->type() == NodeType::Section)
1400-
buildHITTree(interpreter, hnv_child, hit_child, previous_file, previous_line);
1437+
else if (hnv_child.type() == wasp::FILE)
1438+
{
1439+
if (hnv_child.node_pool()->stream_name() == previous_file &&
1440+
hnv_child.line() > previous_line + 1)
1441+
hit_parent->addChild(new Blank());
1442+
hit_parent->addChild(new Include(interpreter, hnv_child));
1443+
previous_file = hnv_child.node_pool()->stream_name();
1444+
previous_line = hnv_child.last_line();
1445+
}
14011446

1402-
// create and add new section node if not found then recurse using node
1403-
else
1404-
{
1405-
// add blank line if needed between previous node line and this node line
1406-
if (hnv_child.node_pool()->stream_name() == previous_file &&
1407-
hnv_child.line() > previous_line + 1)
1408-
hit_parent->addChild(new Blank());
1409-
1410-
hit_child = new Section(interpreter, hnv_child);
1411-
hit_parent->addChild(hit_child);
1412-
previous_file = hnv_child.node_pool()->stream_name();
1413-
previous_line = hnv_child.line();
1414-
buildHITTree(interpreter, hnv_child, hit_child, previous_file, previous_line);
1415-
previous_file = hnv_child.node_pool()->stream_name();
1416-
previous_line = hnv_child.last_line();
1417-
}
1418-
}
1419-
}
1447+
// create and add section node if not found in tree then recurse children
1448+
else if (hnv_child.type() == wasp::OBJECT || hnv_child.type() == wasp::DOCUMENT_ROOT)
1449+
{
1450+
// recurse using section if found and do not create or add anything new
1451+
if (auto hit_child = hit_parent->find(hnv_child.name());
1452+
hit_child && hit_child->type() == NodeType::Section)
1453+
buildHITTree(interpreter, hnv_child, hit_child, previous_file, previous_line, options);
1454+
1455+
// create and add new section node if not found then recurse using node
1456+
else
1457+
{
1458+
// add blank line if needed between previous node line and this node line
1459+
if (hnv_child.node_pool()->stream_name() == previous_file &&
1460+
hnv_child.line() > previous_line + 1)
1461+
hit_parent->addChild(new Blank());
1462+
1463+
hit_child = new Section(interpreter, hnv_child);
1464+
hit_parent->addChild(hit_child);
1465+
previous_file = hnv_child.node_pool()->stream_name();
1466+
previous_line = hnv_child.line();
1467+
buildHITTree(interpreter, hnv_child, hit_child, previous_file, previous_line, options);
1468+
previous_file = hnv_child.node_pool()->stream_name();
1469+
previous_line = hnv_child.last_line();
1470+
}
1471+
}
1472+
});
14201473
}
14211474

14221475
} // namespace hit

0 commit comments

Comments
 (0)