8 #include <fmt/format.h> 9 #include <unordered_set> 19 std::ofstream &stream,
20 const std::string &doc,
21 const std::string &indent =
"",
22 const std::string &annotation =
"" 24 stream << indent <<
"/**";
25 if (!annotation.empty()) {
26 stream <<
" " << annotation;
29 auto word = std::ostringstream();
30 auto line = std::ostringstream();
31 line << indent <<
" *";
32 bool line_empty =
true;
36 line <<
" " << word.str();
39 }
else if (c ==
' ') {
40 line <<
" " << word.str();
45 flush = !line_empty && line.str().size() + word.str().size() > 79;
48 stream << line.str() << std::endl;
50 line << indent <<
" *";
54 if (!word.str().empty()) {
55 line <<
" " << word.str();
59 stream << line.str() << std::endl;
61 stream << indent <<
" */" << std::endl;
68 std::ofstream &header,
73 std::vector<std::string> variants;
74 for (
auto &node : nodes) {
75 if (node->derived.empty()) {
76 variants.push_back(node->title_case_name);
81 format_doc(header,
"Enumeration of all node types.");
82 header <<
"enum class NodeType {" << std::endl;
83 for (
size_t i = 0; i < variants.size(); i++) {
84 header <<
" " << variants[i];
85 if (i < variants.size() - 1) {
90 header <<
"};" << std::endl << std::endl;
98 std::ofstream &header,
99 std::ofstream &source,
100 const std::string &clsname,
104 for (
int constant = 0; constant < 2; constant++) {
105 std::string doc =
"Interprets this node to a node of type " 107 +
". Returns null if it has the wrong type.";
110 if (!allowed) header <<
"virtual ";
111 if (constant) header <<
"const ";
114 if (constant) header <<
" const";
115 if (allowed) header <<
" override";
116 header <<
";" << std::endl << std::endl;
118 if (constant) source <<
"const ";
121 if (constant) source <<
" const";
122 source <<
" {" << std::endl;
124 source <<
" return dynamic_cast<";
125 if (constant) source <<
"const ";
128 source <<
" return nullptr;" << std::endl;
130 source <<
"}" << std::endl << std::endl;
138 std::ofstream &header,
139 std::ofstream &source,
142 const std::string &support_ns
145 format_doc(header,
"Main class for all nodes.");
146 header <<
"class Node : public Base {" << std::endl;
147 header <<
"public:" << std::endl << std::endl;
149 format_doc(header,
"Returns the `NodeType` of this node.",
" ");
150 header <<
" virtual NodeType type() const = 0;" << std::endl << std::endl;
152 format_doc(header,
"Returns a shallow copy of this node.",
" ");
153 header <<
" virtual One<Node> copy() const = 0;" << std::endl << std::endl;
155 format_doc(header,
"Returns a deep copy of this node.",
" ");
156 header <<
" virtual One<Node> clone() const = 0;" << std::endl << std::endl;
158 format_doc(header,
"Value-based equality operator. Ignores annotations!",
" ");
159 header <<
" virtual bool equals(const Node& rhs) const = 0;" << std::endl << std::endl;
161 format_doc(header,
"Pointer-based equality operator.",
" ");
162 header <<
" virtual bool operator==(const Node& rhs) const = 0;" << std::endl << std::endl;
164 format_doc(header,
"Pointer-based inequality operator.",
" ");
165 header <<
" inline bool operator!=(const Node& rhs) const {" << std::endl;
166 header <<
" return !(*this == rhs);" << std::endl;
167 header <<
" }" << std::endl << std::endl;
169 header <<
"protected:" << std::endl << std::endl;
170 format_doc(header,
"Internal helper method for visitor pattern.",
" ");
171 header <<
" virtual void visit_internal(VisitorBase &visitor, void *retval=nullptr) = 0;" << std::endl << std::endl;
173 header <<
"public:" << std::endl << std::endl;
174 format_doc(header,
"Visit this object.",
" ");
175 header <<
" template <typename T>" << std::endl;
176 header <<
" T visit(Visitor<T> &visitor);" << std::endl << std::endl;
190 format_doc(header,
"Writes a debug dump of this node to the given stream.",
" ");
191 header <<
" void dump(std::ostream &out=std::cout, int indent=0);" << std::endl << std::endl;
192 format_doc(source,
"Writes a debug dump of this node to the given stream.");
193 source <<
"void Node::dump(std::ostream &out, int indent) {" << std::endl;
194 source <<
" auto dumper = Dumper(out, indent);" << std::endl;
195 source <<
" visit(dumper);" << std::endl;
196 source <<
"}" << std::endl << std::endl;
198 format_doc(header,
"Alternate debug dump that represents links and node uniqueness via sequence number tags.",
" ");
199 header <<
" void dump_seq(std::ostream &out=std::cout, int indent=0);" << std::endl << std::endl;
200 format_doc(source,
"Alternate debug dump that represents links and node uniqueness via sequence number tags.");
201 source <<
"void Node::dump_seq(std::ostream &out, int indent) {" << std::endl;
202 source <<
" " << support_ns <<
"::base::PointerMap ids;" << std::endl;
203 source <<
" ids.enable_exceptions = false;" << std::endl;
204 source <<
" ids.add_ref(*this);" << std::endl;
205 source <<
" find_reachable(ids);" << std::endl;
206 source <<
" auto dumper = Dumper(out, indent, &ids);" << std::endl;
207 source <<
" visit(dumper);" << std::endl;
208 source <<
"}" << std::endl << std::endl;
210 for (
auto &node : nodes) {
215 format_doc(header,
"Serializes this node to the given map.",
" ");
216 header <<
" virtual void serialize(" << std::endl;
217 header <<
" " << support_ns <<
"::cbor::MapWriter &map," << std::endl;
218 header <<
" const " << support_ns <<
"::base::PointerMap &ids" << std::endl;
219 header <<
" ) const = 0;" << std::endl << std::endl;
221 format_doc(header,
"Deserializes the given node.",
" ");
222 header <<
" static std::shared_ptr<Node> deserialize(" << std::endl;
223 header <<
" const " << support_ns <<
"::cbor::MapReader &map," << std::endl;
224 header <<
" " << support_ns <<
"::base::IdentifierMap &ids" << std::endl;
225 header <<
" );" << std::endl << std::endl;
226 format_doc(source,
"Writes a debug dump of this node to the given stream.");
227 source <<
"std::shared_ptr<Node> Node::deserialize(" << std::endl;
228 source <<
" const " << support_ns <<
"::cbor::MapReader &map," << std::endl;
229 source <<
" " << support_ns <<
"::base::IdentifierMap &ids" << std::endl;
230 source <<
") {" << std::endl;
231 source <<
" auto type = map.at(\"@t\").as_string();" << std::endl;
232 for (
auto &node : nodes) {
233 if (node->derived.empty()) {
234 source <<
" if (type == \"" << node->title_case_name <<
"\") ";
235 source <<
"return " << node->title_case_name <<
"::deserialize(map, ids);" << std::endl;
238 source <<
" throw std::runtime_error(\"Schema validation failed: unexpected node type \" + type);" << std::endl;
239 source <<
"}" << std::endl << std::endl;
242 header <<
"};" << std::endl << std::endl;
251 std::ofstream &source,
256 source <<
"return " << node.
title_case_name <<
"::deserialize(map, ids);" << std::endl;
258 for (
auto &derived : node.
derived) {
268 std::ofstream &header,
269 std::ofstream &source,
277 if (!node.
doc.empty()) {
282 header << node.
parent->title_case_name;
286 header <<
" {" << std::endl;
287 header <<
"public:" << std::endl << std::endl;
290 for (
auto &field : node.
fields) {
291 if (!field.doc.empty()) {
295 switch (field.type) {
296 case Maybe: header <<
"Maybe<" << field.node_type->title_case_name <<
"> ";
break;
297 case One: header <<
"One<" << field.node_type->title_case_name <<
"> ";
break;
298 case Any: header <<
"Any<" << field.node_type->title_case_name <<
"> ";
break;
299 case Many: header <<
"Many<" << field.node_type->title_case_name <<
"> ";
break;
300 case OptLink: header <<
"OptLink<" << field.node_type->title_case_name <<
"> ";
break;
301 case Link: header <<
"Link<" << field.node_type->title_case_name <<
"> ";
break;
302 case Prim: header << field.prim_type <<
" ";
break;
304 header << field.name <<
";" << std::endl << std::endl;
308 if (!all_fields.empty()) {
312 for (
auto &field : all_fields) {
319 switch (field.type) {
320 case Maybe: header <<
"Maybe<" << field.node_type->title_case_name <<
"> ";
break;
321 case One: header <<
"One<" << field.node_type->title_case_name <<
"> ";
break;
322 case Any: header <<
"Any<" << field.node_type->title_case_name <<
"> ";
break;
323 case Many: header <<
"Many<" << field.node_type->title_case_name <<
"> ";
break;
324 case OptLink: header <<
"OptLink<" << field.node_type->title_case_name <<
"> ";
break;
325 case Link: header <<
"Link<" << field.node_type->title_case_name <<
"> ";
break;
326 case Prim: header << field.prim_type <<
" ";
break;
328 header <<
"&" << field.name <<
" = ";
329 switch (field.type) {
330 case Maybe: header <<
"Maybe<" << field.node_type->title_case_name <<
">()";
break;
331 case One: header <<
"One<" << field.node_type->title_case_name <<
">()";
break;
332 case Any: header <<
"Any<" << field.node_type->title_case_name <<
">()";
break;
333 case Many: header <<
"Many<" << field.node_type->title_case_name <<
">()";
break;
334 case OptLink: header <<
"OptLink<" << field.node_type->title_case_name <<
">()";
break;
335 case Link: header <<
"Link<" << field.node_type->title_case_name <<
">()";
break;
339 header <<
");" << std::endl << std::endl;
344 for (
auto &field : all_fields) {
351 switch (field.type) {
352 case Maybe: source <<
"Maybe<" << field.node_type->title_case_name <<
"> ";
break;
353 case One: source <<
"One<" << field.node_type->title_case_name <<
"> ";
break;
354 case Any: source <<
"Any<" << field.node_type->title_case_name <<
"> ";
break;
355 case Many: source <<
"Many<" << field.node_type->title_case_name <<
"> ";
break;
356 case OptLink: source <<
"OptLink<" << field.node_type->title_case_name <<
"> ";
break;
357 case Link: source <<
"Link<" << field.node_type->title_case_name <<
"> ";
break;
358 case Prim: source << field.prim_type <<
" ";
break;
360 source <<
"&" << field.name;
362 source <<
")" << std::endl <<
" : ";
365 source << node.
parent->title_case_name <<
"(";
367 for (
auto &field : node.
parent->all_fields()) {
373 source << field.name;
378 for (
auto &field : node.
fields) {
384 source << field.name <<
"(" << field.name <<
")";
386 source << std::endl <<
"{}" << std::endl << std::endl;
391 std::string doc =
"Registers all reachable nodes with the given PointerMap.";
393 header <<
" void find_reachable(" << support_ns <<
"::base::PointerMap &map) const override;" << std::endl << std::endl;
396 source <<
"::find_reachable(" << support_ns <<
"::base::PointerMap &map) const {" << std::endl;
397 source <<
" (void)map;" << std::endl;
398 for (
auto &field : all_fields) {
399 auto type = (field.type ==
Prim) ? field.ext_type : field.type;
407 source <<
" " << field.name <<
".find_reachable(map);" << std::endl;
413 source <<
"}" << std::endl << std::endl;
415 doc =
"Returns whether this `" + node.
title_case_name +
"` is complete/fully defined.";
417 header <<
" void check_complete(const " << support_ns <<
"::base::PointerMap &map) const override;" << std::endl << std::endl;
420 source <<
"::check_complete(const " << support_ns <<
"::base::PointerMap &map) const {" << std::endl;
421 source <<
" (void)map;" << std::endl;
423 source <<
" throw " << support_ns <<
"::base::NotWellFormed(\"" << node.
title_case_name <<
" error node in tree\");" << std::endl;
425 for (
auto &field : all_fields) {
426 auto type = (field.type ==
Prim) ? field.ext_type : field.type;
434 source <<
" " << field.name <<
".check_complete(map);" << std::endl;
441 source <<
"}" << std::endl << std::endl;
446 auto doc =
"Returns the `NodeType` of this node.";
448 header <<
" NodeType type() const override;" << std::endl << std::endl;
451 source <<
"::type() const {" << std::endl;
452 source <<
" return NodeType::" << node.
title_case_name <<
";" << std::endl;
453 source <<
"}" << std::endl << std::endl;
458 auto doc =
"Helper method for visiting nodes.";
459 header <<
"protected:" << std::endl << std::endl;
461 header <<
" void visit_internal(VisitorBase &visitor, void *retval) override;" << std::endl << std::endl;
462 header <<
"public:" << std::endl << std::endl;
465 source <<
"::visit_internal(VisitorBase &visitor, void *retval) {" << std::endl;
467 source <<
"(*this, retval);" << std::endl;
468 source <<
"}" << std::endl << std::endl;
476 auto doc =
"Returns a shallow copy of this node.";
478 header <<
" One<Node> copy() const override;" << std::endl << std::endl;
481 source <<
"::copy() const {" << std::endl;
482 source <<
" return ";
487 source <<
"}" << std::endl << std::endl;
492 auto doc =
"Returns a deep copy of this node.";
494 header <<
" One<Node> clone() const override;" << std::endl << std::endl;
497 source <<
"::clone() const {" << std::endl;
498 source <<
" auto node = ";
503 for (
auto &field : all_fields) {
504 auto type = (field.type ==
Prim) ? field.ext_type : field.type;
506 source <<
" node->" << field.name <<
" = this->" << field.name <<
".clone();" << std::endl;
509 source <<
" return node;" << std::endl;
510 source <<
"}" << std::endl << std::endl;
515 auto doc =
"Value-based equality operator. Ignores annotations!";
517 header <<
" bool equals(const Node &rhs) const override;" << std::endl << std::endl;
520 source <<
"::equals(const Node &rhs) const {" << std::endl;
521 source <<
" if (rhs.type() != NodeType::" << node.
title_case_name <<
") return false;" << std::endl;
522 if (!all_fields.empty()) {
523 source <<
" auto rhsc = dynamic_cast<const " << node.
title_case_name <<
"&>(rhs);" << std::endl;
524 for (
auto &field : all_fields) {
525 if (field.type ==
Prim && field.ext_type ==
Prim) {
526 source <<
" if (this->" << field.name <<
" != rhsc." << field.name <<
") return false;" << std::endl;
528 source <<
" if (!this->" << field.name <<
".equals(rhsc." << field.name <<
")) return false;" << std::endl;
532 source <<
" return true;" << std::endl;
533 source <<
"}" << std::endl << std::endl;
535 doc =
"Pointer-based equality operator.";
537 header <<
" bool operator==(const Node &rhs) const override;" << std::endl << std::endl;
540 source <<
"::operator==(const Node &rhs) const {" << std::endl;
541 source <<
" if (rhs.type() != NodeType::" << node.
title_case_name <<
") return false;" << std::endl;
542 if (!all_fields.empty()) {
543 source <<
" auto rhsc = dynamic_cast<const " << node.
title_case_name <<
"&>(rhs);" << std::endl;
544 for (
auto &field : all_fields) {
545 source <<
" if (this->" << field.name <<
" != rhsc." << field.name <<
") return false;" << std::endl;
548 source <<
" return true;" << std::endl;
549 source <<
"}" << std::endl << std::endl;
555 format_doc(header,
"Serializes this node to the given map.",
" ");
556 header <<
" void serialize(" << std::endl;
557 header <<
" " << support_ns <<
"::cbor::MapWriter &map," << std::endl;
558 header <<
" const " << support_ns <<
"::base::PointerMap &ids" << std::endl;
559 header <<
" ) const override;" << std::endl << std::endl;
560 format_doc(source,
"Serializes this node to the given map.");
561 source <<
"void " << node.
title_case_name <<
"::serialize(" << std::endl;
562 source <<
" " << support_ns <<
"::cbor::MapWriter &map," << std::endl;
563 source <<
" const " << support_ns <<
"::base::PointerMap &ids" << std::endl;
564 source <<
") const {" << std::endl;
565 source <<
" (void)ids;" << std::endl;
566 source <<
" map.append_string(\"@t\", \"" << node.
title_case_name <<
"\");" << std::endl;
568 for (
const auto &field : all_fields) {
574 source <<
"submap = map.append_map(\"" << field.name <<
"\");" << std::endl;
575 if (field.type ==
Prim && field.ext_type ==
Prim) {
576 source <<
" " << spec.
serialize_fn <<
"<" << field.prim_type <<
">";
577 source <<
"(" << field.name <<
", submap);" << std::endl;
579 source <<
" " << field.name <<
".serialize(submap, ids);" << std::endl;
581 source <<
" submap.close();" << std::endl;
583 source <<
" serialize_annotations(map);" << std::endl;
584 source <<
"}" << std::endl << std::endl;
586 format_doc(header,
"Deserializes the given node.",
" ");
588 header <<
"deserialize(const " << support_ns <<
"::cbor::MapReader &map, " << support_ns <<
"::base::IdentifierMap &ids);" << std::endl << std::endl;
589 format_doc(source,
"Writes a debug dump of this node to the given stream.");
591 source << node.
title_case_name <<
"::deserialize(const " << support_ns <<
"::cbor::MapReader &map, " << support_ns <<
"::base::IdentifierMap &ids) {" << std::endl;
592 source <<
" (void)ids;" << std::endl;
593 source <<
" auto type = map.at(\"@t\").as_string();" << std::endl;
594 source <<
" if (type != \"" << node.
title_case_name <<
"\") {" << std::endl;
595 source <<
" throw std::runtime_error(\"Schema validation failed: unexpected node type \" + type);" << std::endl;
596 source <<
" }" << std::endl;
597 source <<
" auto node = std::make_shared<" << node.
title_case_name <<
">(" << std::endl;
598 std::vector<Field> links{};
600 for (
const auto &field : all_fields) {
604 source <<
"," << std::endl;
608 EdgeType type = (field.type !=
Prim) ? field.type : field.ext_type;
609 if (field.type !=
Prim) {
610 switch (field.type) {
611 case Maybe: source <<
"Maybe";
break;
612 case One: source <<
"One";
break;
613 case Any: source <<
"Any";
break;
614 case Many: source <<
"Many";
break;
615 case OptLink: source <<
"OptLink";
break;
616 case Link: source <<
"Link";
break;
617 default: source <<
"<?>";
break;
619 source <<
"<" << field.node_type->title_case_name <<
">(";
620 source <<
"map.at(\"" << field.name <<
"\").as_map(), ids)";
621 }
else if (field.ext_type !=
Prim) {
622 source << field.prim_type <<
"(";
623 source <<
"map.at(\"" << field.name <<
"\").as_map(), ids)";
626 source <<
"(map.at(\"" << field.name <<
"\").as_map())";
629 links.push_back(field);
633 source <<
" );" << std::endl;
635 for (
const auto &link : links) {
641 source <<
"link = map.at(\"" << link.name <<
"\").as_map().at(\"@l\");" << std::endl;
642 source <<
" if (!link.is_null()) {" << std::endl;
643 source <<
" ids.register_link(node->" << link.name <<
", link.as_int());" << std::endl;
644 source <<
" }" << std::endl;
646 source <<
" node->deserialize_annotations(map);" << std::endl;
647 source <<
" return node;" << std::endl;
648 source <<
"}" << std::endl << std::endl;
650 format_doc(header,
"Deserializes the given node.",
" ");
652 header <<
"deserialize(const " << support_ns <<
"::cbor::MapReader &map, " << support_ns <<
"::base::IdentifierMap &ids);" << std::endl << std::endl;
653 format_doc(source,
"Writes a debug dump of this node to the given stream.");
655 source << node.
title_case_name <<
"::deserialize(const " << support_ns <<
"::cbor::MapReader &map, " << support_ns <<
"::base::IdentifierMap &ids) {" << std::endl;
656 source <<
" auto type = map.at(\"@t\").as_string();" << std::endl;
657 for (
auto &derived : node.
derived) {
660 source <<
" throw std::runtime_error(\"Schema validation failed: unexpected node type \" + type);" << std::endl;
661 source <<
"}" << std::endl << std::endl;
666 header <<
"};" << std::endl << std::endl;
673 std::ofstream &header,
674 std::ofstream &source,
681 "Internal class for implementing the visitor pattern.");
682 header <<
"class VisitorBase {" << std::endl;
683 header <<
"public:" << std::endl << std::endl;
686 format_doc(header,
"Virtual destructor for proper cleanup.",
" ");
687 header <<
" virtual ~VisitorBase() = default;" << std::endl << std::endl;
690 header <<
"protected:" << std::endl << std::endl;
691 header <<
" friend class Node;" << std::endl;
692 for (
auto &node : nodes) {
693 header <<
" friend class " << node->title_case_name <<
";" << std::endl;
699 format_doc(header,
"Internal visitor function for nodes of any type.",
" ");
700 header <<
" virtual void raw_visit_node(Node &node, void *retval) = 0;" << std::endl << std::endl;
703 for (
auto &node : nodes) {
704 format_doc(header,
"Internal visitor function for `" + node->title_case_name +
"` nodes.",
" ");
705 header <<
" virtual void raw_visit_" << node->snake_case_name;
706 header <<
"(" << node->title_case_name <<
" &node, void *retval) = 0;" << std::endl << std::endl;
709 header <<
"};" << std::endl << std::endl;
716 std::ofstream &header,
717 std::ofstream &source,
724 "Base class for the visitor pattern for the tree.\n\n" 725 "To operate on the tree, derive from this class, describe your " 726 "operation by overriding the appropriate visit functions. and then " 727 "call `node->visit(your_visitor)`. The default implementations for " 728 "the node-specific functions fall back to the more generic functions, " 729 "eventually leading to `visit_node()`, which must be implemented with " 730 "the desired behavior for unknown nodes.");
731 header <<
"template <typename T>" << std::endl;
732 header <<
"class Visitor : public VisitorBase {" << std::endl;
733 header <<
"protected:" << std::endl << std::endl;
736 format_doc(header,
"Internal visitor function for nodes of any type.",
" ");
737 header <<
" void raw_visit_node(Node &node, void *retval) override;" << std::endl << std::endl;
740 for (
auto &node : nodes) {
741 format_doc(header,
"Internal visitor function for `" + node->title_case_name +
"` nodes.",
" ");
742 header <<
" void raw_visit_" << node->snake_case_name;
743 header <<
"(" << node->title_case_name <<
" &node, void *retval) override;" << std::endl << std::endl;
746 header <<
"public:" << std::endl << std::endl;
749 format_doc(header,
"Fallback function for nodes of any type.",
" ");
750 header <<
" virtual T visit_node(Node &node) = 0;" << std::endl << std::endl;
753 for (
auto &node : nodes) {
755 if (node->derived.empty()) {
756 doc =
"Visitor function for `" + node->title_case_name +
"` nodes.";
758 doc =
"Fallback function for `" + node->title_case_name +
"` nodes.";
761 header <<
" virtual T visit_" << node->snake_case_name;
762 header <<
"(" << node->title_case_name <<
" &node) {" << std::endl;
764 header <<
" return visit_" << node->parent->snake_case_name <<
"(node);" << std::endl;
766 header <<
" return visit_node(node);" << std::endl;
768 header <<
" }" << std::endl << std::endl;
771 header <<
"};" << std::endl << std::endl;
774 format_doc(header,
"Internal visitor function for nodes of any type.",
" ");
775 header <<
" template <typename T>" << std::endl;
776 header <<
" void Visitor<T>::raw_visit_node(Node &node, void *retval) {" << std::endl;
777 header <<
" if (retval == nullptr) {" << std::endl;
778 header <<
" this->visit_node(node);" << std::endl;
779 header <<
" } else {" << std::endl;
780 header <<
" *((T*)retval) = this->visit_node(node);" << std::endl;
781 header <<
" };" << std::endl;
782 header <<
" }" << std::endl << std::endl;
784 format_doc(header,
"Internal visitor function for nodes of any type.",
" ");
785 header <<
" template <>" << std::endl;
786 header <<
" void Visitor<void>::raw_visit_node(Node &node, void *retval);" << std::endl << std::endl;
788 format_doc(source,
"Internal visitor function for nodes of any type.");
789 source <<
"template <>" << std::endl;
790 source <<
"void Visitor<void>::raw_visit_node(Node &node, void *retval) {" << std::endl;
791 source <<
" (void)retval;" << std::endl;
792 source <<
" this->visit_node(node);" << std::endl;
793 source <<
"}" << std::endl << std::endl;
796 for (
auto &node : nodes) {
797 format_doc(header,
"Internal visitor function for `" + node->title_case_name +
"` nodes.",
" ");
798 header <<
" template <typename T>" << std::endl;
799 header <<
" void Visitor<T>::raw_visit_" << node->snake_case_name;
800 header <<
"(" << node->title_case_name <<
" &node, void *retval) {" << std::endl;
801 header <<
" if (retval == nullptr) {" << std::endl;
802 header <<
" this->visit_" << node->snake_case_name <<
"(node);" << std::endl;
803 header <<
" } else {" << std::endl;
804 header <<
" *((T*)retval) = this->visit_" << node->snake_case_name <<
"(node);" << std::endl;
805 header <<
" };" << std::endl;
806 header <<
" }" << std::endl << std::endl;
808 format_doc(header,
"Internal visitor function for `" + node->title_case_name +
"` nodes.",
" ");
809 header <<
" template <>" << std::endl;
810 header <<
" void Visitor<void>::raw_visit_" << node->snake_case_name;
811 header <<
"(" << node->title_case_name <<
" &node, void *retval);" << std::endl << std::endl;
813 format_doc(source,
"Internal visitor function for `" + node->title_case_name +
"` nodes.");
814 source <<
"template <>" << std::endl;
815 source <<
"void Visitor<void>::raw_visit_" << node->snake_case_name;
816 source <<
"(" << node->title_case_name <<
" &node, void *retval) {" << std::endl;
817 source <<
" (void)retval;" << std::endl;
818 source <<
" this->visit_" << node->snake_case_name <<
"(node);" << std::endl;
819 source <<
"}" << std::endl << std::endl;
828 std::ofstream &header,
829 std::ofstream &source,
836 "Visitor base class defaulting to DFS pre-order traversal.\n\n" 837 "The visitor functions for nodes with subnode fields default to DFS " 838 "traversal in addition to falling back to more generic node types." 839 "Links and OptLinks are *not* followed." 841 header <<
"class RecursiveVisitor : public Visitor<void> {" << std::endl;
842 header <<
"public:" << std::endl << std::endl;
845 for (
auto &node : nodes) {
846 auto doc =
"Recursive traversal for `" + node->title_case_name +
"` nodes.";
848 header <<
" void visit_" << node->snake_case_name;
849 header <<
"(" << node->title_case_name <<
" &node) override;" << std::endl << std::endl;
851 source <<
"void RecursiveVisitor::visit_" << node->snake_case_name;
852 source <<
"(" << node->title_case_name <<
" &node) {" << std::endl;
854 source <<
" visit_" << node->parent->snake_case_name <<
"(node);" << std::endl;
856 source <<
" visit_node(node);" << std::endl;
858 for (
auto &field : node->fields) {
859 if (field.node_type) {
861 source <<
" node." << field.name <<
".visit(*this);" << std::endl;
865 source <<
"}" << std::endl << std::endl;
868 header <<
"};" << std::endl << std::endl;
875 std::ofstream &header,
876 std::ofstream &source,
878 std::string &source_location,
879 std::string &support_ns
883 format_doc(header,
"Visitor class that debug-dumps a tree to a stream");
884 header <<
"class Dumper : public RecursiveVisitor {" << std::endl;
885 header <<
"protected:" << std::endl << std::endl;
886 format_doc(header,
"Output stream to dump to.",
" ");
887 header <<
" std::ostream &out;" << std::endl << std::endl;
888 format_doc(header,
"Current indentation level.",
" ");
889 header <<
" int indent = 0;" << std::endl << std::endl;
890 format_doc(header,
"When non-null, the print node IDs from here instead of link contents.",
" ");
891 header <<
" " << support_ns <<
"::base::PointerMap *ids;" << std::endl;
892 format_doc(header,
"Whether we're printing the contents of a link.",
" ");
893 header <<
" bool in_link = false;" << std::endl << std::endl;
896 format_doc(header,
"Writes the current indentation level's worth of spaces.",
" ");
897 header <<
" void write_indent();" << std::endl << std::endl;
898 format_doc(source,
"Writes the current indentation level's worth of spaces.");
899 source <<
"void Dumper::write_indent() {" << std::endl;
900 source <<
" for (int i = 0; i < indent; i++) {" << std::endl;
901 source <<
" out << \" \";" << std::endl;
902 source <<
" }" << std::endl;
903 source <<
"}" << std::endl << std::endl;
906 header <<
"public:" << std::endl << std::endl;
907 format_doc(header,
"Construct a dumping visitor.",
" ");
908 header <<
" Dumper(std::ostream &out, int indent=0, ";
909 header << support_ns <<
"::base::PointerMap *ids = nullptr) : ";
910 header <<
"out(out), indent(indent), ids(ids) {};" << std::endl << std::endl;
914 header <<
" void visit_node(Node &node) override;" << std::endl;
916 source <<
"void Dumper::visit_node(Node &node) {" << std::endl;
917 source <<
" (void)node;" << std::endl;
918 source <<
" write_indent();" << std::endl;
919 source <<
" out << \"!Node()\" << std::endl;" << std::endl;
920 source <<
"}" << std::endl << std::endl;
923 for (
auto &node : nodes) {
924 bool first_prim =
true;
925 auto doc =
"Dumps a `" + node->title_case_name +
"` node.";
927 header <<
" void visit_" << node->snake_case_name;
928 header <<
"(" << node->title_case_name <<
" &node) override;" << std::endl << std::endl;
930 source <<
"void Dumper::visit_" << node->snake_case_name;
931 source <<
"(" << node->title_case_name <<
" &node) {" << std::endl;
932 source <<
" write_indent();" << std::endl;
933 auto attributes = node->all_fields();
934 source <<
" out << \"" << node->title_case_name <<
"\";" << std::endl;
935 source <<
" if (ids != nullptr) {" << std::endl;
936 source <<
" out << \"@\" << ids->get_ref(node);" << std::endl;
937 source <<
" }" << std::endl;
938 source <<
" out << \"(\";" << std::endl;
939 if (!source_location.empty()) {
940 source <<
" if (auto loc = node.get_annotation_ptr<" << source_location <<
">()) {" << std::endl;
941 source <<
" out << \" # \" << *loc;" << std::endl;
942 source <<
" }" << std::endl;
944 source <<
" out << std::endl;" << std::endl;
945 if (!attributes.empty()) {
946 source <<
" indent++;" << std::endl;
947 for (
auto &attrib : attributes) {
948 source <<
" write_indent();" << std::endl;
949 source <<
" out << \"" << attrib.name;
950 if (attrib.ext_type ==
Link || attrib.ext_type ==
OptLink) {
955 source <<
"\";" << std::endl;
956 switch (attrib.ext_type) {
961 source <<
" if (node." << attrib.name <<
".empty()) {" << std::endl;
962 if (attrib.ext_type ==
One || attrib.ext_type ==
Link) {
963 source <<
" out << \"!MISSING\" << std::endl;" << std::endl;
965 source <<
" out << \"-\" << std::endl;" << std::endl;
967 if (attrib.ext_type ==
Link || attrib.ext_type ==
OptLink) {
968 source <<
" } else if (ids != nullptr && ids->get(node." << attrib.name <<
") != (size_t)-1) {" << std::endl;
969 auto type = attrib.node_type ? attrib.node_type->title_case_name : attrib.prim_type;
970 source <<
" out << \"" << type <<
"@\" << ids->get(node." << attrib.name <<
") << std::endl;" << std::endl;
972 source <<
" } else {" << std::endl;
973 source <<
" out << \"<\" << std::endl;" << std::endl;
974 source <<
" indent++;" << std::endl;
975 if (attrib.ext_type ==
Link || attrib.ext_type ==
OptLink) {
976 source <<
" if (!in_link) {" << std::endl;
977 source <<
" in_link = true;" << std::endl;
978 if (attrib.type ==
Prim) {
979 source <<
" if (!node." << attrib.name <<
".empty()) {" << std::endl;
980 source <<
" node." << attrib.name <<
"->dump(out, indent);" << std::endl;
981 source <<
" }" << std::endl;
983 source <<
" node." << attrib.name <<
".visit(*this);" << std::endl;
985 source <<
" in_link = false;" << std::endl;
986 source <<
" } else {" << std::endl;
987 source <<
" write_indent();" << std::endl;
988 source <<
" out << \"...\" << std::endl;" << std::endl;
989 source <<
" }" << std::endl;
990 }
else if (attrib.type ==
Prim) {
991 source <<
" if (!node." << attrib.name <<
".empty()) {" << std::endl;
992 source <<
" node." << attrib.name <<
"->dump(out, indent);" << std::endl;
993 source <<
" }" << std::endl;
995 source <<
" node." << attrib.name <<
".visit(*this);" << std::endl;
997 source <<
" indent--;" << std::endl;
998 source <<
" write_indent();" << std::endl;
999 source <<
" out << \">\" << std::endl;" << std::endl;
1000 source <<
" }" << std::endl;
1005 source <<
" if (node." << attrib.name <<
".empty()) {" << std::endl;
1006 if (attrib.ext_type ==
Many) {
1007 source <<
" out << \"!MISSING\" << std::endl;" << std::endl;
1009 source <<
" out << \"[]\" << std::endl;" << std::endl;
1011 source <<
" } else {" << std::endl;
1012 source <<
" out << \"[\" << std::endl;" << std::endl;
1013 source <<
" indent++;" << std::endl;
1014 source <<
" for (auto &sptr : node." << attrib.name <<
") {" << std::endl;
1015 source <<
" if (!sptr.empty()) {" << std::endl;
1016 if (attrib.type ==
Prim) {
1017 source <<
" sptr->dump(out, indent);" << std::endl;
1019 source <<
" sptr->visit(*this);" << std::endl;
1021 source <<
" } else {" << std::endl;
1022 source <<
" write_indent();" << std::endl;
1023 source <<
" out << \"!NULL\" << std::endl;" << std::endl;
1024 source <<
" }" << std::endl;
1025 source <<
" }" << std::endl;
1026 source <<
" indent--;" << std::endl;
1027 source <<
" write_indent();" << std::endl;
1028 source <<
" out << \"]\" << std::endl;" << std::endl;
1029 source <<
" }" << std::endl;
1034 source <<
" std::stringstream ss;" << std::endl;
1035 source <<
" size_t pos;" << std::endl;
1038 source <<
" ss.str(\"\");" << std::endl;
1039 source <<
" ss.clear();" << std::endl;
1041 source <<
" ss << node." << attrib.name <<
";" << std::endl;
1042 source <<
" pos = ss.str().find_last_not_of(\" \\n\\r\\t\");" << std::endl;
1043 source <<
" if (pos != std::string::npos) {" << std::endl;
1044 source <<
" ss.str(ss.str().erase(pos+1));" << std::endl;
1045 source <<
" }" << std::endl;
1046 source <<
" if (ss.str().find('\\n') == std::string::npos) {" << std::endl;
1047 source <<
" out << ss.str() << std::endl;" << std::endl;
1048 source <<
" } else {" << std::endl;
1049 source <<
" out << \"" << attrib.prim_type <<
"<<\" << std::endl;" << std::endl;
1050 source <<
" indent++;" << std::endl;
1051 source <<
" std::string s;" << std::endl;
1052 source <<
" while (!ss.eof()) {" << std::endl;
1053 source <<
" std::getline(ss, s);" << std::endl;
1054 source <<
" write_indent();" << std::endl;
1055 source <<
" out << s << std::endl;" << std::endl;
1056 source <<
" }" << std::endl;
1057 source <<
" indent--;" << std::endl;
1058 source <<
" write_indent();" << std::endl;
1059 source <<
" out << \">>\" << std::endl;" << std::endl;
1060 source <<
" }" << std::endl;
1065 source <<
" indent--;" << std::endl;
1066 source <<
" write_indent();" << std::endl;
1068 source <<
" out << \")\" << std::endl;" << std::endl;
1069 source <<
"}" << std::endl << std::endl;
1072 header <<
"};" << std::endl << std::endl;
1079 const std::string &header_filename,
1080 const std::string &source_filename,
1083 auto nodes = specification.
nodes;
1086 auto header = std::ofstream(header_filename);
1087 if (!header.is_open()) {
1088 std::cerr <<
"Failed to open header file for writing" << std::endl;
1091 auto source = std::ofstream(source_filename);
1092 if (!source.is_open()) {
1093 std::cerr <<
"Failed to open source file for writing" << std::endl;
1099 auto sep_pos = header_filename.rfind(
'/');
1100 auto backslash_pos = header_filename.rfind(
'\\');
1101 if (backslash_pos != std::string::npos && backslash_pos > sep_pos) {
1102 sep_pos = backslash_pos;
1104 auto header_basename = header_filename.substr(sep_pos + 1);
1107 std::string include_guard = header_basename;
1109 include_guard.begin(), include_guard.end(), include_guard.begin(),
1110 [](
unsigned char c){
1111 if (std::isalnum(c)) {
1112 return std::toupper(c);
1114 return static_cast<int>(
'_');
1122 header << std::endl;
1124 header <<
"#pragma once" << std::endl;
1125 header << std::endl;
1126 header <<
"#include <iostream>" << std::endl;
1127 header <<
"#include <fmt/ostream.h>" << std::endl;
1128 for (
auto &include : specification.
includes) {
1129 header <<
"#" << include << std::endl;
1131 header << std::endl;
1132 for (
size_t i = 0; i < specification.
namespaces.size(); i++) {
1134 header << std::endl;
1138 header <<
"/**" << std::endl;
1139 header <<
" * \\dot" << std::endl;
1140 header <<
" * digraph example {" << std::endl;
1141 header <<
" * node [shape=record, fontname=Helvetica, fontsize=10];" << std::endl;
1142 std::ostringstream ns;
1143 for (
auto &name : specification.
namespaces) {
1146 for (
auto &node : nodes) {
1147 header <<
" * " << node->title_case_name;
1148 header <<
" [ label=\"" << node->title_case_name;
1149 header <<
"\" URL=\"\\ref " << ns.str() << node->title_case_name;
1151 if (!node->derived.empty()) {
1152 header <<
", style=dotted";
1154 header <<
"];" << std::endl;
1156 for (
auto &node : nodes) {
1158 header <<
" * " << node->parent->title_case_name;
1159 header <<
" -> " << node->title_case_name;
1160 header <<
" [ arrowhead=open, style=dotted ];" << std::endl;
1164 for (
auto &node : nodes) {
1165 for (
auto field : node->fields) {
1167 if (field.node_type) {
1168 header <<
" * " << node->title_case_name;
1169 header <<
" -> " << field.node_type->title_case_name;
1172 std::string full_name = field.prim_type;
1173 auto pos = full_name.find(
"<");
1174 if (pos != std::string::npos) {
1175 full_name = full_name.substr(pos + 1);
1177 pos = full_name.rfind(
">");
1178 if (pos != std::string::npos) {
1179 full_name = full_name.substr(0, pos);
1181 std::string brief_name = full_name;
1182 pos = brief_name.rfind(
"::");
1183 if (pos != std::string::npos) {
1184 pos = brief_name.rfind(
"::", pos - 1);
1185 if (pos != std::string::npos) {
1186 brief_name = brief_name.substr(pos + 2);
1189 header <<
" * prim" << prim_id;
1190 header <<
" [ label=\"" << brief_name;
1191 header <<
"\" URL=\"\\ref " << full_name;
1192 header <<
"\"];" << std::endl;
1193 header <<
" * " << node->title_case_name;
1194 header <<
" -> prim" << prim_id;
1195 typ = field.ext_type;
1198 header <<
" [ label=\"" << field.name;
1200 case Any: header <<
"*\", arrowhead=open, style=bold, ";
break;
1201 case OptLink: header <<
"@?\", arrowhead=open, style=dashed, ";
break;
1202 case Maybe: header <<
"?\", arrowhead=open, style=solid, ";
break;
1203 case Many: header <<
"+\", arrowhead=normal, style=bold, ";
break;
1204 case Link: header <<
"@\", arrowhead=normal, style=dashed, ";
break;
1205 default: header <<
"\", arrowhead=normal, style=solid, ";
break;
1207 header <<
"fontname=Helvetica, fontsize=10];" << std::endl;
1210 header <<
" * }" << std::endl;
1211 header <<
" * \\enddot" << std::endl;
1212 header <<
" */" << std::endl;
1215 header <<
"namespace " << specification.
namespaces[i] <<
" {" << std::endl;
1217 header << std::endl;
1223 header <<
"// Base classes used to construct the tree." << std::endl;
1224 header <<
"using Base = " << tree_namespace <<
"Base;" << std::endl;
1225 header <<
"template <class T> using Maybe = " << tree_namespace <<
"Maybe<T>;" << std::endl;
1226 header <<
"template <class T> using One = " << tree_namespace <<
"One<T>;" << std::endl;
1227 header <<
"template <class T> using Any = " << tree_namespace <<
"Any<T>;" << std::endl;
1228 header <<
"template <class T> using Many = " << tree_namespace <<
"Many<T>;" << std::endl;
1229 header <<
"template <class T> using OptLink = " << tree_namespace <<
"OptLink<T>;" << std::endl;
1230 header <<
"template <class T> using Link = " << tree_namespace <<
"Link<T>;" << std::endl;
1231 header << std::endl;
1237 source << std::endl;
1240 source <<
"#" << include << std::endl;
1243 source <<
"#include \"" << specification.
header_fname <<
"\"" << std::endl;
1245 source <<
"#include \"" << header_basename <<
"\"" << std::endl;
1247 source << std::endl;
1248 for (
auto &name : specification.
namespaces) {
1249 source <<
"namespace " << name <<
" {" << std::endl;
1251 source << std::endl;
1254 header <<
"// Forward declarations for all classes." << std::endl;
1255 header <<
"class Node;" << std::endl;
1256 for (
auto &node : nodes) {
1257 header <<
"class " << node->title_case_name <<
";" << std::endl;
1259 header <<
"class VisitorBase;" << std::endl;
1260 header <<
"template <typename T = void>" << std::endl;
1261 header <<
"class Visitor;" << std::endl;
1262 header <<
"class RecursiveVisitor;" << std::endl;
1263 header <<
"class Dumper;" << std::endl;
1264 header << std::endl;
1279 std::unordered_set<std::string> generated;
1280 for (
auto node : nodes) {
1281 if (generated.count(node->snake_case_name)) {
1284 auto ancestors =
Nodes();
1286 ancestors.push_back(node);
1287 node = node->parent;
1289 for (
auto node_it = ancestors.rbegin(); node_it != ancestors.rend(); node_it++) {
1291 if (generated.count(node->snake_case_name)) {
1294 generated.insert(node->snake_case_name);
1308 header <<
"template <typename T>" << std::endl;
1309 header <<
"T Node::visit(Visitor<T> &visitor) {" << std::endl;
1310 header <<
" T retval;" << std::endl;
1311 header <<
" this->visit_internal(visitor, &retval);" << std::endl;
1312 header <<
" return retval;" << std::endl;
1313 header <<
"}" << std::endl << std::endl;
1316 header <<
"template <>" << std::endl;
1317 header <<
"void Node::visit(Visitor<void> &visitor);" << std::endl << std::endl;
1320 source <<
"template <>" << std::endl;
1321 source <<
"void Node::visit(Visitor<void> &visitor) {" << std::endl;
1322 source <<
" this->visit_internal(visitor);" << std::endl;
1323 source <<
"}" << std::endl << std::endl;
1326 format_doc(header,
"Stream << overload for tree nodes (writes debug dump).");
1327 header <<
"std::ostream &operator<<(std::ostream &os, const Node &object);" << std::endl << std::endl;
1328 format_doc(source,
"Stream << overload for tree nodes (writes debug dump).");
1329 source <<
"std::ostream &operator<<(std::ostream &os, const Node &object) {" << std::endl;
1330 source <<
" const_cast<Node&>(object).dump(os);" << std::endl;
1331 source <<
" return os;" << std::endl;
1332 source <<
"}" << std::endl << std::endl;
1335 for (
auto name_it = specification.
namespaces.rbegin(); name_it != specification.
namespaces.rend(); name_it++) {
1336 header <<
"} // namespace " << *name_it << std::endl;
1337 source <<
"} // namespace " << *name_it << std::endl;
1339 header << std::endl;
1340 source << std::endl;
1342 format_doc(header,
"std::ostream support via fmt (uses operator<<).");
1343 auto namespaces = fmt::format(
"{}::", fmt::join(specification.
namespaces,
"::"));
1344 for (
auto &node : nodes) {
1345 header <<
"template <> struct fmt::formatter<" << namespaces << node->title_case_name <<
"> : ostream_formatter {};" << std::endl;
void generate_visitor_class(std::ofstream &header, std::ofstream &source, Nodes &nodes)
Generate the templated visitor class.
std::vector< Field > all_fields() const
Gathers all child nodes, including those in parent classes.
std::string support_namespace
The namespace to take support stuff like tree::cbor from.
void generate_deserialize_mux(std::ofstream &source, Node &node)
Recursive function to print a muxing if statement for all node classes derived from the given node cl...
std::vector< std::shared_ptr< Node > > Nodes
List of nodes.
Represents a type of AST node.
Struct containing everything needed for a complete specification.
std::vector< std::string > src_includes
The include statements to stick at the top of the source file.
std::vector< std::weak_ptr< Node > > derived
Node types derived from this one.
Namespace for the tree-gen program.
void generate_dumper_class(std::ofstream &header, std::ofstream &source, Nodes &nodes, std::string &source_location, std::string &support_ns)
Generate the dumper class.
std::vector< std::string > includes
The include statements to stick at the top of the header file.
void generate(const std::string &header_filename, const std::string &source_filename, Specification &specification)
Generate the complete C++ code (source and header).
std::string namespace_doc
Namespace documentation.
std::string tree_namespace
The namespace to take the tree base types from.
std::string header_fname
Explicit filename for the header, in case it will not end up in the same directory that the source is...
void generate_node_class(std::ofstream &header, std::ofstream &source, Specification &spec, Node &node)
Generates the class for the given node.
std::vector< std::string > namespaces
The C++ namespaces to use.
std::string initialize_function
The initialization function to use for default values of members.
std::string title_case_name
Name in TitleCase.
std::string header_doc
Header file documentation.
Link to zero or one nodes elsewhere in the tree.
EdgeType
Types of edges between nodes and primitives.
std::string source_location
Annotation object used for source location info, or empty if source locations are not used or are not...
void generate_recursive_visitor_class(std::ofstream &header, std::ofstream &source, Nodes &nodes)
Generate the recursive visitor class.
Header file for tree-gen-cpp.cpp.
std::string doc
Class documentation.
std::string serialize_fn
The serialization function to use when serializing primitives.
void generate_visitor_base_class(std::ofstream &header, std::ofstream &source, Nodes &nodes)
Generate the visitor base class.
bool is_error_marker
Whether this node represents a recovered parse error.
void generate_base_class(std::ofstream &header, std::ofstream &source, Nodes &nodes, bool with_serdes, const std::string &support_ns)
Generates the base class for the nodes.
std::vector< Field > fields
Child nodes.
std::string source_doc
Source file documentation.
std::string snake_case_name
Name in snake_case.
std::string deserialize_fn
The serialization function to use when deserializing primitives.
std::shared_ptr< Node > parent
The node type this is derived from, if any.
void format_doc(std::ofstream &stream, const std::string &doc, const std::string &indent="", const std::string &annotation="")
Formats a C++ docstring.
void generate_typecast_function(std::ofstream &header, std::ofstream &source, const std::string &clsname, Node &into, bool allowed)
Generates an as_<type> function.
void generate_enum(std::ofstream &header, Nodes &nodes)
Generates the node type enumeration.
Nodes nodes
All the nodes.
Link to exactly one node elsewhere in the tree.