Alamo
InputScraper.cpp
Go to the documentation of this file.
1#include "IO/InputScraper.H"
2#include "IO/JSON.H"
3#include "IO/ParmParse.H"
4
5#include <algorithm>
6#include <fstream>
7#include <iostream>
8#include <utility>
9
10#include "AMReX_ParallelDescriptor.H"
11
12namespace
13{
15GetChild(IO::InputScraper::InputNode &node, const std::string &name)
16{
17 for (auto &child : node.children)
18 if (child.name == name)
19 return child;
20
22 child.name = name;
23 child.full_name = node.full_name.empty() ? name : node.full_name + "." + name;
24 node.children.push_back(child);
25 return node.children.back();
26}
27
28bool
29SameConditions( const std::vector<IO::InputScraper::InputNode::Condition> &a,
30 const std::vector<IO::InputScraper::InputNode::Condition> &b)
31{
32 if (a.size() != b.size()) return false;
33 for (std::size_t i = 0; i < a.size(); i++)
34 if (a[i].path != b[i].path || a[i].value != b[i].value)
35 return false;
36 return true;
37}
38
39void
40WriteConditions(std::ostream &os,
41 const std::vector<IO::InputScraper::InputNode::Condition> &conditions,
42 int indent)
43{
44 os << "[";
45 for (std::size_t i = 0; i < conditions.size(); i++)
46 {
47 if (i) os << ",";
48 os << "\n";
49 IO::JSON::Indent(os, indent + 2);
50 os << "{\"path\": \"" << IO::JSON::Escape(conditions[i].path)
51 << "\", \"value\": \"" << IO::JSON::Escape(conditions[i].value) << "\"}";
52 }
53 if (!conditions.empty())
54 {
55 os << "\n";
56 IO::JSON::Indent(os, indent);
57 }
58 os << "]";
59}
60
61void
62WriteContextsField( std::ostream &os, bool &first, int indent,
63 const std::vector<std::vector<IO::InputScraper::InputNode::Condition>> &contexts)
64{
65 IO::JSON::Comma(os, first, indent);
66 os << "\"contexts\": [";
67 for (std::size_t i = 0; i < contexts.size(); i++)
68 {
69 if (i) os << ",";
70 os << "\n";
71 IO::JSON::Indent(os, indent + 2);
72 WriteConditions(os, contexts[i], indent + 2);
73 }
74 if (!contexts.empty())
75 {
76 os << "\n";
77 IO::JSON::Indent(os, indent);
78 }
79 os << "]";
80}
81
82void
83WriteConstraint(std::ostream &os,
85 int indent)
86{
87 bool first = true;
88 os << "{";
89 IO::JSON::WriteStringField(os, first, indent + 2, "kind", constraint.kind);
90 IO::JSON::WriteIntField(os, first, indent + 2, "count", constraint.count);
91 IO::JSON::WriteStringArrayField(os, first, indent + 2, "members", constraint.members);
92 if (!constraint.units.empty())
93 IO::JSON::WriteStringArrayField(os, first, indent + 2, "units", constraint.units);
94 if (!constraint.conditions.empty())
95 {
96 IO::JSON::Comma(os, first, indent + 2);
97 os << "\"conditions\": ";
98 WriteConditions(os, constraint.conditions, indent + 2);
99 }
100 IO::JSON::WriteSourceField(os, first, indent + 2, constraint.location);
101 os << "\n";
102 IO::JSON::Indent(os, indent);
103 os << "}";
104}
105
106void
107WriteTraversalIgnore( std::ostream &os,
109 int indent)
110{
111 bool first = true;
112 os << "{";
113 IO::JSON::WriteStringField(os, first, indent + 2, "note", ignore.note);
114 IO::JSON::WriteSourceField(os, first, indent + 2, ignore.location);
115 os << "\n";
116 IO::JSON::Indent(os, indent);
117 os << "}";
118}
119
120void
121WriteNode(std::ostream &os, const IO::InputScraper::InputNode &node, int indent)
122{
123 bool first = true;
124 os << "{";
125 IO::JSON::WriteStringField(os, first, indent + 2, "kind", node.kind);
126 IO::JSON::WriteStringField(os, first, indent + 2, "name", node.name);
127 IO::JSON::WriteStringField(os, first, indent + 2, "path", node.full_name);
128 if (!node.directive.empty())
129 IO::JSON::WriteStringField(os, first, indent + 2, "directive", node.directive);
130 if (!node.options.empty())
131 IO::JSON::WriteStringArrayField(os, first, indent + 2, "options", node.options);
132 if (node.required)
133 IO::JSON::WriteBoolField(os, first, indent + 2, "required", node.required);
134 if (node.has_default)
135 {
136 IO::JSON::WriteBoolField(os, first, indent + 2, "has_default", node.has_default);
137 if (node.has_default_value)
138 IO::JSON::WriteStringField(os, first, indent + 2, "default_value", node.default_value);
139 }
140 if (node.has_unnamed_default)
141 IO::JSON::WriteBoolField(os, first, indent + 2, "has_unnamed_default", true);
142 if (!node.directive.empty())
143 IO::JSON::WriteSourceField(os, first, indent + 2, node.location);
144 if (!node.contexts.empty())
145 WriteContextsField(os, first, indent + 2, node.contexts);
146 if (!node.constraints.empty())
147 {
148 IO::JSON::Comma(os, first, indent + 2);
149 os << "\"constraints\": [";
150 for (std::size_t i = 0; i < node.constraints.size(); i++)
151 {
152 if (i) os << ",";
153 os << "\n";
154 IO::JSON::Indent(os, indent + 4);
155 WriteConstraint(os, node.constraints[i], indent + 4);
156 }
157 os << "\n";
158 IO::JSON::Indent(os, indent + 2);
159 os << "]";
160 }
161 if (!node.children.empty())
162 {
163 IO::JSON::Comma(os, first, indent + 2);
164 os << "\"children\": [";
165 for (std::size_t i = 0; i < node.children.size(); i++)
166 {
167 if (i) os << ",";
168 os << "\n";
169 IO::JSON::Indent(os, indent + 4);
170 WriteNode(os, node.children[i], indent + 4);
171 }
172 os << "\n";
173 IO::JSON::Indent(os, indent + 2);
174 os << "]";
175 }
176 os << "\n";
177 IO::JSON::Indent(os, indent);
178 os << "}";
179}
180
181void
182WriteFlowNodes( std::ostream &os,
183 const std::vector<IO::InputScraper::FlowNode> &nodes,
184 int indent);
185
186void
187WriteFlowNode(std::ostream &os, const IO::InputScraper::FlowNode &node, int indent)
188{
189 bool first = true;
190 os << "{";
191 IO::JSON::WriteStringField(os, first, indent + 2, "kind", node.kind);
192 IO::JSON::WriteStringField(os, first, indent + 2, "input", node.input);
193 IO::JSON::WriteSourceField(os, first, indent + 2, node.location);
194 if (node.has_unnamed_default)
195 IO::JSON::WriteBoolField(os, first, indent + 2, "has_unnamed_default", true);
196 if (!node.branches.empty())
197 {
198 IO::JSON::Comma(os, first, indent + 2);
199 os << "\"branches\": [";
200 for (std::size_t i = 0; i < node.branches.size(); i++)
201 {
202 if (i) os << ",";
203 os << "\n";
204 IO::JSON::Indent(os, indent + 4);
205 os << "{";
206 bool branch_first = true;
207 IO::JSON::WriteStringField( os, branch_first, indent + 6,
208 "value", node.branches[i].value);
209 IO::JSON::Comma(os, branch_first, indent + 6);
210 os << "\"children\": ";
211 WriteFlowNodes(os, node.branches[i].children, indent + 6);
212 os << "\n";
213 IO::JSON::Indent(os, indent + 4);
214 os << "}";
215 }
216 os << "\n";
217 IO::JSON::Indent(os, indent + 2);
218 os << "]";
219 }
220 if (!node.children.empty())
221 {
222 IO::JSON::Comma(os, first, indent + 2);
223 os << "\"children\": ";
224 WriteFlowNodes(os, node.children, indent + 2);
225 }
226 os << "\n";
227 IO::JSON::Indent(os, indent);
228 os << "}";
229}
230
231void
232WriteFlowNodes( std::ostream &os,
233 const std::vector<IO::InputScraper::FlowNode> &nodes,
234 int indent)
235{
236 os << "[";
237 for (std::size_t i = 0; i < nodes.size(); i++)
238 {
239 if (i) os << ",";
240 os << "\n";
241 IO::JSON::Indent(os, indent + 2);
242 WriteFlowNode(os, nodes[i], indent + 2);
243 }
244 if (!nodes.empty())
245 {
246 os << "\n";
247 IO::JSON::Indent(os, indent);
248 }
249 os << "]";
250}
251}
252
253namespace IO
254{
255InputScraper::InputNode InputScraper::input_tree;
258std::string InputScraper::traversal_output_file = "alamo-inputs.schema.json";
259std::vector<InputScraper::InputNode::Condition> InputScraper::traversal_conditions;
260std::vector<InputScraper::FlowNode> InputScraper::input_flow;
261std::vector<InputScraper::FlowNode> *InputScraper::traversal_flow = &InputScraper::input_flow;
262std::vector<std::vector<InputScraper::FlowNode> *> InputScraper::traversal_flow_stack;
263std::vector<InputScraper::TraversalIgnore> InputScraper::traversal_ignores;
264
265InputScraper::InputNode &
266InputScraper::GetPath(InputNode &root, const std::string &path)
267{
268 InputNode *node = &root;
269 std::size_t start = 0;
270 while (start < path.size())
271 {
272 std::size_t end = path.find('.', start);
273 std::string part = path.substr(start, end - start);
274 if (!part.empty())
275 node = &GetChild(*node, part);
276 if (end == std::string::npos)
277 break;
278 start = end + 1;
279 }
280 return *node;
281}
282
283void
284InputScraper::AddContext(InputNode &node, const std::vector<InputNode::Condition> &conditions)
285{
286 for (const auto &context : node.contexts)
287 if (SameConditions(context, conditions))
288 return;
289 node.contexts.push_back(conditions);
290}
291
292void
293InputScraper::RecordFlowInput( const std::string &path,
294 const std::string &directive,
295 const std::vector<std::string> &options,
296 const std::source_location &location,
297 bool has_unnamed_default)
298{
300
301 FlowNode *flow_node = nullptr;
302 for (auto it = traversal_flow->rbegin(); it != traversal_flow->rend(); ++it)
303 {
304 if (it->input == path)
305 {
306 flow_node = &*it;
307 break;
308 }
309 }
310
311 if (!flow_node)
312 {
313 traversal_flow->push_back(FlowNode());
314 flow_node = &traversal_flow->back();
315 flow_node->input = path;
316 }
317 flow_node->location = location;
318 flow_node->has_unnamed_default =
319 flow_node->has_unnamed_default || has_unnamed_default;
320
321 if (KindForDirective(directive, options) != "switch") return;
322
323 flow_node->kind = "switch";
324 if (has_unnamed_default)
325 {
326 bool found = false;
327 for (const auto &branch : flow_node->branches)
328 if (branch.value.empty())
329 found = true;
330 if (!found)
331 flow_node->branches.push_back(FlowBranch());
332 }
333 for (const auto &option : options)
334 {
335 bool found = false;
336 for (const auto &branch : flow_node->branches)
337 if (branch.value == option)
338 found = true;
339 if (!found)
340 {
341 FlowBranch branch;
342 branch.value = option;
343 flow_node->branches.push_back(std::move(branch));
344 }
345 }
346}
347
348std::string
349InputScraper::KindForDirective( const std::string &directive,
350 const std::vector<std::string> &options)
351{
352 if (directive == "query_switch" || directive == "query_if" ||
353 directive == "query_if_else")
354 return "switch";
355 if (directive == "select" || directive == "select_default") return "switch";
356 if ((directive == "query" || directive == "query_required" || directive == "query_default") &&
357 options == std::vector<std::string>{"0", "1"})
358 return "switch";
359 if (directive == "select_enumerate") return "sequence";
360 if (directive == "query_enumerate" || directive == "queryarr_enumerate" || directive == "queryclass_enumerate") return "sequence";
361 if (directive == "queryclass") return "scope";
362 return "parameter";
363}
364
365std::string
366InputScraper::MergeKind(const std::string &old_kind, const std::string &new_kind)
367{
368 if (old_kind == "scope") return new_kind;
369 if (new_kind == "switch" || new_kind == "sequence") return new_kind;
370 return old_kind;
371}
372
373std::string
374InputScraper::DirectiveName(const std::source_location &location)
375{
376 std::string name = location.function_name();
377
378 std::size_t paren = name.find('(');
379 if (paren != std::string::npos)
380 name = name.substr(0, paren);
381
382 std::size_t scope = name.rfind("::");
383 if (scope != std::string::npos)
384 name = name.substr(scope + 2);
385
386 std::size_t space = name.rfind(' ');
387 if (space != std::string::npos)
388 name = name.substr(space + 1);
389
390 return name;
391}
392
393void
395{
396 traversal_mode = enabled;
397 traversal_conditions.clear();
398 if (enabled)
400}
401
402bool
407
408bool
413
416{
417 return input_tree;
418}
419
420void
422{
424 input_tree.kind = "scope";
425 input_flow.clear();
427 traversal_flow_stack.clear();
428 traversal_ignores.clear();
429}
430
431void
433{
434 traversal_output_file = std::move(path);
435}
436
437const std::string &
442
443void
445{
446 os << "{\n";
447 os << " \"schema_version\": 3,\n";
448 os << " \"format\": \"alamo.input_schema\",\n";
449 os << " \"root\": ";
450 WriteNode(os, input_tree, 2);
451 os << ",\n \"flow\": ";
452 WriteFlowNodes(os, input_flow, 2);
453 if (!traversal_ignores.empty())
454 {
455 os << ",\n \"traversal_ignored\": [";
456 for (std::size_t i = 0; i < traversal_ignores.size(); i++)
457 {
458 if (i) os << ",";
459 os << "\n";
460 IO::JSON::Indent(os, 4);
461 WriteTraversalIgnore(os, traversal_ignores[i], 4);
462 }
463 os << "\n ]";
464 }
465 os << "\n}\n";
466}
467
468void
470{
471 if (path.empty()) return;
472 if (!amrex::ParallelDescriptor::IOProcessor()) return;
473
474 std::ofstream os(path);
476}
477
478void
479InputScraper::PrintTraversalBranch(ParmParse &pp, std::string name, const std::string &value)
480{
481 if (!InTraversalMode()) return;
482 if (!amrex::ParallelDescriptor::IOProcessor()) return;
483
484 for (int i = 0; i < traversal_print_depth; i++)
485 std::cout << " ";
486
487 std::cout << "-> " << pp.full(name) << " = " << value << std::endl;
488}
489
490void
492 std::string name,
493 std::string directive,
494 const std::source_location &location,
495 std::vector<std::string> options,
496 std::optional<std::string> default_value,
497 bool has_unnamed_default)
498{
499 if (!InTraversalMode()) return;
500
501 InputNode &node = GetPath(input_tree, pp.full(name));
502 node.kind = MergeKind(node.kind, KindForDirective(directive, options));
503 node.directive = directive;
504 node.location = location;
505 if (!options.empty())
506 node.options = std::move(options);
507 node.required = node.required || directive.find("required") != std::string::npos;
508 node.has_default = node.has_default || directive.find("default") != std::string::npos || default_value.has_value();
509 node.has_unnamed_default = node.has_unnamed_default || has_unnamed_default;
510 if (default_value.has_value())
511 {
512 node.has_default_value = true;
513 node.default_value = std::move(*default_value);
514 }
516 RecordFlowInput(node.full_name, directive, node.options, location,
517 has_unnamed_default);
518
519 if (!amrex::ParallelDescriptor::IOProcessor()) return;
520
521 for (int i = 0; i < traversal_print_depth; i++)
522 std::cout << " ";
523
524 std::cout << node.full_name << " [" << node.directive << "]";
525 if (!node.options.empty())
526 {
527 std::cout << " {";
528 for (std::size_t i = 0; i < node.options.size(); i++)
529 {
530 if (i) std::cout << ", ";
531 std::cout << node.options[i];
532 }
533 std::cout << "}";
534 }
535 std::cout << std::endl;
536}
537
538void
540 const std::string &sequence_name,
541 const std::string &template_name)
542{
543 if (!InTraversalMode()) return;
544
545 const std::string sequence_path = pp.full(sequence_name);
546 const std::string template_path = pp.full(template_name);
547 const std::size_t dot = template_path.rfind('.');
548 const std::string parent_path =
549 dot == std::string::npos ? std::string() : template_path.substr(0, dot);
550 const std::string child_name =
551 dot == std::string::npos ? template_path : template_path.substr(dot + 1);
552
553 InputNode &parent = GetPath(input_tree, parent_path);
554 auto child = std::find_if( parent.children.begin(), parent.children.end(),
555 [&](const InputNode &node) { return node.name == child_name; });
556 if (child != parent.children.end())
557 {
558 InputNode item = std::move(*child);
559 parent.children.erase(child);
560 GetPath(input_tree, sequence_path).children.push_back(std::move(item));
561 }
562
563 if (!traversal_flow) return;
564 auto sequence = std::find_if(
565 traversal_flow->begin(), traversal_flow->end(),
566 [&](const FlowNode &node) { return node.input == sequence_path; });
567 if (sequence == traversal_flow->end()) return;
568
569 for (auto it = traversal_flow->begin(); it != traversal_flow->end();)
570 {
571 const bool template_root = it->input == template_path;
572 const bool template_child =
573 it->input.size() > template_path.size() &&
574 it->input.compare(0, template_path.size(), template_path) == 0 &&
575 it->input[template_path.size()] == '.';
576 if (!template_root && !template_child)
577 {
578 ++it;
579 continue;
580 }
581 if (template_child)
582 sequence->children.push_back(std::move(*it));
583 it = traversal_flow->erase(it);
584 }
585}
586
587void
589 std::string kind,
590 int count,
591 std::vector<std::string> members,
592 std::vector<std::string> units,
593 const std::source_location &location)
594{
595 if (!InTraversalMode()) return;
596
597 for (auto &member : members)
598 member = pp.full(member);
599
600 InputNode &scope = GetPath(input_tree, pp.getPrefix());
601 InputNode::Constraint constraint;
602 constraint.kind = std::move(kind);
603 constraint.count = count;
604 constraint.members = std::move(members);
605 constraint.units = std::move(units);
606 constraint.location = location;
607 constraint.conditions = traversal_conditions;
608 scope.constraints.push_back(std::move(constraint));
609}
610
611void
612InputScraper::RecordTraversalIgnore(const std::source_location &location,
613 std::string note)
614{
615 if (!InTraversalMode()) return;
616
617 const auto duplicate = std::find_if(
619 [&](const TraversalIgnore &ignore)
620 {
621 return std::string(ignore.location.file_name()) == location.file_name() &&
622 ignore.location.line() == location.line();
623 });
624 if (duplicate == traversal_ignores.end())
625 traversal_ignores.push_back({std::move(note), location});
626}
627
628void
629InputScraper::PushTraversalCondition(ParmParse &pp, std::string name, std::string value)
630{
631 if (!InTraversalMode()) return;
632 traversal_conditions.push_back({pp.full(name), std::move(value)});
633}
634
635void
641
642void
643InputScraper::PushTraversalBranch( ParmParse &pp, const std::string &name,
644 const std::string &value)
645{
647
648 const std::string path = pp.full(name);
649 FlowNode *switch_node = nullptr;
650 for (auto it = traversal_flow->rbegin(); it != traversal_flow->rend(); ++it)
651 {
652 if (it->input == path && it->kind == "switch")
653 {
654 switch_node = &*it;
655 break;
656 }
657 }
658
659 if (!switch_node)
660 {
661 traversal_flow->push_back(FlowNode());
662 switch_node = &traversal_flow->back();
663 switch_node->kind = "switch";
664 switch_node->input = path;
665 }
666
667 FlowBranch *selected_branch = nullptr;
668 for (auto &branch : switch_node->branches)
669 {
670 if (branch.value == value)
671 {
672 selected_branch = &branch;
673 break;
674 }
675 }
676 if (!selected_branch)
677 {
678 FlowBranch branch;
679 branch.value = value;
680 switch_node->branches.push_back(std::move(branch));
681 selected_branch = &switch_node->branches.back();
682 }
683
685 traversal_flow = &selected_branch->children;
686}
687
688void
690{
691 if (traversal_flow_stack.empty())
692 {
694 return;
695 }
697 traversal_flow_stack.pop_back();
698}
699
701{
702 if (InTraversalMode())
703 {
704 active = true;
705 PushTraversalCondition(pp, name, value);
706 PushTraversalBranch(pp, name, value);
708 }
709}
710
720}
TraversalBranchScope(ParmParse &pp, std::string name, std::string value)
static std::vector< FlowNode > input_flow
static void RecordInput(ParmParse &pp, std::string name, std::string directive, const std::source_location &location, std::vector< std::string > options={}, std::optional< std::string > default_value=std::nullopt, bool has_unnamed_default=false)
static void WriteInputTreeJsonFile(const std::string &path)
static void ClearInputTree()
static void SetTraversalMode(bool enabled)
static void CaptureSequenceTemplate(ParmParse &pp, const std::string &sequence_name, const std::string &template_name)
static void PrintTraversalBranch(ParmParse &pp, std::string name, const std::string &value)
static void RecordConstraint(ParmParse &pp, std::string kind, int count, std::vector< std::string > members, std::vector< std::string > units, const std::source_location &location)
static std::string DirectiveName(const std::source_location &location)
static std::string MergeKind(const std::string &old_kind, const std::string &new_kind)
static const InputNode & InputTree()
static void PopTraversalCondition()
static InputNode & GetPath(InputNode &root, const std::string &path)
static void AddContext(InputNode &node, const std::vector< InputNode::Condition > &conditions)
static std::vector< std::vector< FlowNode > * > traversal_flow_stack
static bool ShouldExecute()
static bool InTraversalMode()
static void WriteInputTreeJson(std::ostream &os)
static InputNode input_tree
static bool traversal_mode
static std::string traversal_output_file
static std::vector< TraversalIgnore > traversal_ignores
static std::string KindForDirective(const std::string &directive, const std::vector< std::string > &options={})
static void PushTraversalBranch(ParmParse &pp, const std::string &name, const std::string &value)
static std::vector< FlowNode > * traversal_flow
static void PopTraversalBranch()
static void SetTraversalOutputFile(std::string path)
static void RecordFlowInput(const std::string &path, const std::string &directive, const std::vector< std::string > &options, const std::source_location &location, bool has_unnamed_default)
static void PushTraversalCondition(ParmParse &pp, std::string name, std::string value)
static std::vector< InputNode::Condition > traversal_conditions
static void RecordTraversalIgnore(const std::source_location &location, std::string note)
static int traversal_print_depth
static const std::string & TraversalOutputFile()
static void WriteStringField(std::ostream &os, bool &first, int indent, const std::string &name, const std::string &value)
Definition JSON.H:46
static void Comma(std::ostream &os, bool &first, int indent)
Definition JSON.H:39
static void WriteBoolField(std::ostream &os, bool &first, int indent, const std::string &name, bool value)
Definition JSON.H:53
static void WriteSourceField(std::ostream &os, bool &first, int indent, const std::source_location &location)
Definition JSON.H:87
static std::string Escape(const std::string &value)
Definition JSON.H:14
static void WriteIntField(std::ostream &os, bool &first, int indent, const std::string &name, int value)
Definition JSON.H:60
static void Indent(std::ostream &os, int indent)
Definition JSON.H:34
static void WriteStringArrayField(std::ostream &os, bool &first, int indent, const std::string &name, const std::vector< std::string > &values)
Definition JSON.H:78
std::string getPrefix() const
Definition ParmParse.H:275
std::string full(std::string name)
Definition ParmParse.H:1758
std::vector< FlowNode > children
std::vector< FlowBranch > branches
std::source_location location
std::vector< FlowNode > children
std::vector< std::string > members
std::vector< std::string > units
std::vector< Condition > conditions
std::source_location location
std::vector< std::string > options
std::vector< InputNode > children
std::vector< std::vector< Condition > > contexts
std::vector< Constraint > constraints
std::source_location location