8 #include <unordered_set> 18 std::ofstream &stream,
19 const std::string &doc,
20 const std::string &indent =
"",
21 const std::string &annotation =
"" 23 stream << indent <<
"/**";
24 if (!annotation.empty()) {
25 stream <<
" " << annotation;
28 auto word = std::ostringstream();
29 auto line = std::ostringstream();
30 line << indent <<
" *";
31 bool line_empty =
true;
35 line <<
" " << word.str();
38 }
else if (c ==
' ') {
39 line <<
" " << word.str();
44 flush = !line_empty && line.str().size() + word.str().size() > 79;
47 stream << line.str() << std::endl;
49 line << indent <<
" *";
53 if (!word.str().empty()) {
54 line <<
" " << word.str();
58 stream << line.str() << std::endl;
60 stream << indent <<
" */" << std::endl;
67 std::ofstream &header,
72 std::vector<std::string> variants;
73 for (
auto &node : nodes) {
74 if (node->derived.empty()) {
75 variants.push_back(node->title_case_name);
80 format_doc(header,
"Enumeration of all node types.");
81 header <<
"enum class NodeType {" << std::endl;
82 for (
size_t i = 0; i < variants.size(); i++) {
83 header <<
" " << variants[i];
84 if (i < variants.size() - 1) {
89 header <<
"};" << std::endl << std::endl;
97 std::ofstream &header,
98 std::ofstream &source,
99 const std::string &clsname,
103 for (
int constant = 0; constant < 2; constant++) {
104 std::string doc =
"Interprets this node to a node of type " 106 +
". Returns null if it has the wrong type.";
109 if (!allowed) header <<
"virtual ";
110 if (constant) header <<
"const ";
113 if (constant) header <<
" const";
114 if (allowed) header <<
" override";
115 header <<
";" << std::endl << std::endl;
117 if (constant) source <<
"const ";
120 if (constant) source <<
" const";
121 source <<
" {" << std::endl;
123 source <<
" return dynamic_cast<";
124 if (constant) source <<
"const ";
127 source <<
" return nullptr;" << std::endl;
129 source <<
"}" << std::endl << std::endl;
137 std::ofstream &header,
138 std::ofstream &source,
141 const std::string &support_ns
144 format_doc(header,
"Main class for all nodes.");
145 header <<
"class Node : public Base {" << std::endl;
146 header <<
"public:" << std::endl << std::endl;
148 format_doc(header,
"Returns the `NodeType` of this node.",
" ");
149 header <<
" virtual NodeType type() const = 0;" << std::endl << std::endl;
151 format_doc(header,
"Returns a shallow copy of this node.",
" ");
152 header <<
" virtual One<Node> copy() const = 0;" << std::endl << std::endl;
154 format_doc(header,
"Returns a deep copy of this node.",
" ");
155 header <<
" virtual One<Node> clone() const = 0;" << std::endl << std::endl;
157 format_doc(header,
"Value-based equality operator. Ignores annotations!",
" ");
158 header <<
" virtual bool equals(const Node& rhs) const = 0;" << std::endl << std::endl;
160 format_doc(header,
"Pointer-based equality operator.",
" ");
161 header <<
" virtual bool operator==(const Node& rhs) const = 0;" << std::endl << std::endl;
163 format_doc(header,
"Pointer-based inequality operator.",
" ");
164 header <<
" inline bool operator!=(const Node& rhs) const {" << std::endl;
165 header <<
" return !(*this == rhs);" << std::endl;
166 header <<
" }" << std::endl << std::endl;
168 header <<
"protected:" << std::endl << std::endl;
169 format_doc(header,
"Internal helper method for visiter pattern.",
" ");
170 header <<
" virtual void visit_internal(VisitorBase &visitor, void *retval=nullptr) = 0;" << std::endl << std::endl;
172 header <<
"public:" << std::endl << std::endl;
173 format_doc(header,
"Visit this object.",
" ");
174 header <<
" template <typename T>" << std::endl;
175 header <<
" T visit(Visitor<T> &visitor);" << std::endl << std::endl;
189 format_doc(header,
"Writes a debug dump of this node to the given stream.",
" ");
190 header <<
" void dump(std::ostream &out=std::cout, int indent=0);" << std::endl << std::endl;
191 format_doc(source,
"Writes a debug dump of this node to the given stream.");
192 source <<
"void Node::dump(std::ostream &out, int indent) {" << std::endl;
193 source <<
" auto dumper = Dumper(out, indent);" << std::endl;
194 source <<
" visit(dumper);" << std::endl;
195 source <<
"}" << std::endl << std::endl;
197 format_doc(header,
"Alternate debug dump that represents links and node uniqueness via sequence number tags.",
" ");
198 header <<
" void dump_seq(std::ostream &out=std::cout, int indent=0);" << std::endl << std::endl;
199 format_doc(source,
"Alternate debug dump that represents links and node uniqueness via sequence number tags.");
200 source <<
"void Node::dump_seq(std::ostream &out, int indent) {" << std::endl;
201 source <<
" " << support_ns <<
"::base::PointerMap ids;" << std::endl;
202 source <<
" ids.enable_exceptions = false;" << std::endl;
203 source <<
" ids.add_ref(*this);" << std::endl;
204 source <<
" find_reachable(ids);" << std::endl;
205 source <<
" auto dumper = Dumper(out, indent, &ids);" << std::endl;
206 source <<
" visit(dumper);" << std::endl;
207 source <<
"}" << std::endl << std::endl;
209 for (
auto &node : nodes) {
214 format_doc(header,
"Serializes this node to the given map.",
" ");
215 header <<
" virtual void serialize(" << std::endl;
216 header <<
" " << support_ns <<
"::cbor::MapWriter &map," << std::endl;
217 header <<
" const " << support_ns <<
"::base::PointerMap &ids" << std::endl;
218 header <<
" ) const = 0;" << std::endl << std::endl;
220 format_doc(header,
"Deserializes the given node.",
" ");
221 header <<
" static std::shared_ptr<Node> deserialize(" << std::endl;
222 header <<
" const " << support_ns <<
"::cbor::MapReader &map," << std::endl;
223 header <<
" " << support_ns <<
"::base::IdentifierMap &ids" << std::endl;
224 header <<
" );" << std::endl << std::endl;
225 format_doc(source,
"Writes a debug dump of this node to the given stream.");
226 source <<
"std::shared_ptr<Node> Node::deserialize(" << std::endl;
227 source <<
" const " << support_ns <<
"::cbor::MapReader &map," << std::endl;
228 source <<
" " << support_ns <<
"::base::IdentifierMap &ids" << std::endl;
229 source <<
") {" << std::endl;
230 source <<
" auto type = map.at(\"@t\").as_string();" << std::endl;
231 for (
auto &node : nodes) {
232 if (node->derived.empty()) {
233 source <<
" if (type == \"" << node->title_case_name <<
"\") ";
234 source <<
"return " << node->title_case_name <<
"::deserialize(map, ids);" << std::endl;
237 source <<
" throw std::runtime_error(\"Schema validation failed: unexpected node type \" + type);" << std::endl;
238 source <<
"}" << std::endl << std::endl;
241 header <<
"};" << std::endl << std::endl;
250 std::ofstream &source,
255 source <<
"return " << node.
title_case_name <<
"::deserialize(map, ids);" << std::endl;
257 for (
auto &derived : node.
derived) {
267 std::ofstream &header,
268 std::ofstream &source,
276 if (!node.
doc.empty()) {
281 header << node.
parent->title_case_name;
285 header <<
" {" << std::endl;
286 header <<
"public:" << std::endl << std::endl;
289 for (
auto &field : node.
fields) {
290 if (!field.doc.empty()) {
294 switch (field.type) {
295 case Maybe: header <<
"Maybe<" << field.node_type->title_case_name <<
"> ";
break;
296 case One: header <<
"One<" << field.node_type->title_case_name <<
"> ";
break;
297 case Any: header <<
"Any<" << field.node_type->title_case_name <<
"> ";
break;
298 case Many: header <<
"Many<" << field.node_type->title_case_name <<
"> ";
break;
299 case OptLink: header <<
"OptLink<" << field.node_type->title_case_name <<
"> ";
break;
300 case Link: header <<
"Link<" << field.node_type->title_case_name <<
"> ";
break;
301 case Prim: header << field.prim_type <<
" ";
break;
303 header << field.name <<
";" << std::endl << std::endl;
307 if (!all_fields.empty()) {
311 for (
auto &field : all_fields) {
318 switch (field.type) {
319 case Maybe: header <<
"Maybe<" << field.node_type->title_case_name <<
"> ";
break;
320 case One: header <<
"One<" << field.node_type->title_case_name <<
"> ";
break;
321 case Any: header <<
"Any<" << field.node_type->title_case_name <<
"> ";
break;
322 case Many: header <<
"Many<" << field.node_type->title_case_name <<
"> ";
break;
323 case OptLink: header <<
"OptLink<" << field.node_type->title_case_name <<
"> ";
break;
324 case Link: header <<
"Link<" << field.node_type->title_case_name <<
"> ";
break;
325 case Prim: header << field.prim_type <<
" ";
break;
327 header <<
"&" << field.name <<
" = ";
328 switch (field.type) {
329 case Maybe: header <<
"Maybe<" << field.node_type->title_case_name <<
">()";
break;
330 case One: header <<
"One<" << field.node_type->title_case_name <<
">()";
break;
331 case Any: header <<
"Any<" << field.node_type->title_case_name <<
">()";
break;
332 case Many: header <<
"Many<" << field.node_type->title_case_name <<
">()";
break;
333 case OptLink: header <<
"OptLink<" << field.node_type->title_case_name <<
">()";
break;
334 case Link: header <<
"Link<" << field.node_type->title_case_name <<
">()";
break;
338 header <<
");" << std::endl << std::endl;
343 for (
auto &field : all_fields) {
350 switch (field.type) {
351 case Maybe: source <<
"Maybe<" << field.node_type->title_case_name <<
"> ";
break;
352 case One: source <<
"One<" << field.node_type->title_case_name <<
"> ";
break;
353 case Any: source <<
"Any<" << field.node_type->title_case_name <<
"> ";
break;
354 case Many: source <<
"Many<" << field.node_type->title_case_name <<
"> ";
break;
355 case OptLink: source <<
"OptLink<" << field.node_type->title_case_name <<
"> ";
break;
356 case Link: source <<
"Link<" << field.node_type->title_case_name <<
"> ";
break;
357 case Prim: source << field.prim_type <<
" ";
break;
359 source <<
"&" << field.name;
361 source <<
")" << std::endl <<
" : ";
364 source << node.
parent->title_case_name <<
"(";
366 for (
auto &field : node.
parent->all_fields()) {
372 source << field.name;
377 for (
auto &field : node.
fields) {
383 source << field.name <<
"(" << field.name <<
")";
385 source << std::endl <<
"{}" << std::endl << std::endl;
390 std::string doc =
"Registers all reachable nodes with the given PointerMap.";
392 header <<
" void find_reachable(" << support_ns <<
"::base::PointerMap &map) const override;" << std::endl << std::endl;
395 source <<
"::find_reachable(" << support_ns <<
"::base::PointerMap &map) const {" << std::endl;
396 source <<
" (void)map;" << std::endl;
397 for (
auto &field : all_fields) {
398 auto type = (field.type ==
Prim) ? field.ext_type : field.type;
406 source <<
" " << field.name <<
".find_reachable(map);" << std::endl;
412 source <<
"}" << std::endl << std::endl;
414 doc =
"Returns whether this `" + node.
title_case_name +
"` is complete/fully defined.";
416 header <<
" void check_complete(const " << support_ns <<
"::base::PointerMap &map) const override;" << std::endl << std::endl;
419 source <<
"::check_complete(const " << support_ns <<
"::base::PointerMap &map) const {" << std::endl;
420 source <<
" (void)map;" << std::endl;
422 source <<
" throw " << support_ns <<
"::base::NotWellFormed(\"" << node.
title_case_name <<
" error node in tree\");" << std::endl;
424 for (
auto &field : all_fields) {
425 auto type = (field.type ==
Prim) ? field.ext_type : field.type;
433 source <<
" " << field.name <<
".check_complete(map);" << std::endl;
440 source <<
"}" << std::endl << std::endl;
445 auto doc =
"Returns the `NodeType` of this node.";
447 header <<
" NodeType type() const override;" << std::endl << std::endl;
450 source <<
"::type() const {" << std::endl;
451 source <<
" return NodeType::" << node.
title_case_name <<
";" << std::endl;
452 source <<
"}" << std::endl << std::endl;
457 auto doc =
"Helper method for visiting nodes.";
458 header <<
"protected:" << std::endl << std::endl;
460 header <<
" void visit_internal(VisitorBase &visitor, void *retval) override;" << std::endl << std::endl;
461 header <<
"public:" << std::endl << std::endl;
464 source <<
"::visit_internal(VisitorBase &visitor, void *retval) {" << std::endl;
466 source <<
"(*this, retval);" << std::endl;
467 source <<
"}" << std::endl << std::endl;
475 auto doc =
"Returns a shallow copy of this node.";
477 header <<
" One<Node> copy() const override;" << std::endl << std::endl;
480 source <<
"::copy() const {" << std::endl;
481 source <<
" return ";
486 source <<
"}" << std::endl << std::endl;
491 auto doc =
"Returns a deep copy of this node.";
493 header <<
" One<Node> clone() const override;" << std::endl << std::endl;
496 source <<
"::clone() const {" << std::endl;
497 source <<
" auto node = ";
502 for (
auto &field : all_fields) {
503 auto type = (field.type ==
Prim) ? field.ext_type : field.type;
505 source <<
" node->" << field.name <<
" = this->" << field.name <<
".clone();" << std::endl;
508 source <<
" return node;" << std::endl;
509 source <<
"}" << std::endl << std::endl;
514 auto doc =
"Value-based equality operator. Ignores annotations!";
516 header <<
" bool equals(const Node &rhs) const override;" << std::endl << std::endl;
519 source <<
"::equals(const Node &rhs) const {" << std::endl;
520 source <<
" if (rhs.type() != NodeType::" << node.
title_case_name <<
") return false;" << std::endl;
521 if (!all_fields.empty()) {
522 source <<
" auto rhsc = dynamic_cast<const " << node.
title_case_name <<
"&>(rhs);" << std::endl;
523 for (
auto &field : all_fields) {
524 if (field.type ==
Prim && field.ext_type ==
Prim) {
525 source <<
" if (this->" << field.name <<
" != rhsc." << field.name <<
") return false;" << std::endl;
527 source <<
" if (!this->" << field.name <<
".equals(rhsc." << field.name <<
")) return false;" << std::endl;
531 source <<
" return true;" << std::endl;
532 source <<
"}" << std::endl << std::endl;
534 doc =
"Pointer-based equality operator.";
536 header <<
" bool operator==(const Node &rhs) const override;" << std::endl << std::endl;
539 source <<
"::operator==(const Node &rhs) const {" << std::endl;
540 source <<
" if (rhs.type() != NodeType::" << node.
title_case_name <<
") return false;" << std::endl;
541 if (!all_fields.empty()) {
542 source <<
" auto rhsc = dynamic_cast<const " << node.
title_case_name <<
"&>(rhs);" << std::endl;
543 for (
auto &field : all_fields) {
544 source <<
" if (this->" << field.name <<
" != rhsc." << field.name <<
") return false;" << std::endl;
547 source <<
" return true;" << std::endl;
548 source <<
"}" << std::endl << std::endl;
554 format_doc(header,
"Serializes this node to the given map.",
" ");
555 header <<
" void serialize(" << std::endl;
556 header <<
" " << support_ns <<
"::cbor::MapWriter &map," << std::endl;
557 header <<
" const " << support_ns <<
"::base::PointerMap &ids" << std::endl;
558 header <<
" ) const override;" << std::endl << std::endl;
559 format_doc(source,
"Serializes this node to the given map.");
560 source <<
"void " << node.
title_case_name <<
"::serialize(" << std::endl;
561 source <<
" " << support_ns <<
"::cbor::MapWriter &map," << std::endl;
562 source <<
" const " << support_ns <<
"::base::PointerMap &ids" << std::endl;
563 source <<
") const {" << std::endl;
564 source <<
" (void)ids;" << std::endl;
565 source <<
" map.append_string(\"@t\", \"" << node.
title_case_name <<
"\");" << std::endl;
567 for (
const auto &field : all_fields) {
573 source <<
"submap = map.append_map(\"" << field.name <<
"\");" << std::endl;
574 if (field.type ==
Prim && field.ext_type ==
Prim) {
575 source <<
" " << spec.
serialize_fn <<
"<" << field.prim_type <<
">";
576 source <<
"(" << field.name <<
", submap);" << std::endl;
578 source <<
" " << field.name <<
".serialize(submap, ids);" << std::endl;
580 source <<
" submap.close();" << std::endl;
582 source <<
" serialize_annotations(map);" << std::endl;
583 source <<
"}" << std::endl << std::endl;
585 format_doc(header,
"Deserializes the given node.",
" ");
587 header <<
"deserialize(const " << support_ns <<
"::cbor::MapReader &map, " << support_ns <<
"::base::IdentifierMap &ids);" << std::endl << std::endl;
588 format_doc(source,
"Writes a debug dump of this node to the given stream.");
590 source << node.
title_case_name <<
"::deserialize(const " << support_ns <<
"::cbor::MapReader &map, " << support_ns <<
"::base::IdentifierMap &ids) {" << std::endl;
591 source <<
" (void)ids;" << std::endl;
592 source <<
" auto type = map.at(\"@t\").as_string();" << std::endl;
593 source <<
" if (type != \"" << node.
title_case_name <<
"\") {" << std::endl;
594 source <<
" throw std::runtime_error(\"Schema validation failed: unexpected node type \" + type);" << std::endl;
595 source <<
" }" << std::endl;
596 source <<
" auto node = std::make_shared<" << node.
title_case_name <<
">(" << std::endl;
597 std::vector<Field> links{};
599 for (
const auto &field : all_fields) {
603 source <<
"," << std::endl;
607 EdgeType type = (field.type !=
Prim) ? field.type : field.ext_type;
608 if (field.type !=
Prim) {
609 switch (field.type) {
610 case Maybe: source <<
"Maybe";
break;
611 case One: source <<
"One";
break;
612 case Any: source <<
"Any";
break;
613 case Many: source <<
"Many";
break;
614 case OptLink: source <<
"OptLink";
break;
615 case Link: source <<
"Link";
break;
616 default: source <<
"<?>";
break;
618 source <<
"<" << field.node_type->title_case_name <<
">(";
619 source <<
"map.at(\"" << field.name <<
"\").as_map(), ids)";
620 }
else if (field.ext_type !=
Prim) {
621 source << field.prim_type <<
"(";
622 source <<
"map.at(\"" << field.name <<
"\").as_map(), ids)";
625 source <<
"(map.at(\"" << field.name <<
"\").as_map())";
628 links.push_back(field);
632 source <<
" );" << std::endl;
634 for (
const auto &link : links) {
640 source <<
"link = map.at(\"" << link.name <<
"\").as_map().at(\"@l\");" << std::endl;
641 source <<
" if (!link.is_null()) {" << std::endl;
642 source <<
" ids.register_link(node->" << link.name <<
", link.as_int());" << std::endl;
643 source <<
" }" << std::endl;
645 source <<
" node->deserialize_annotations(map);" << std::endl;
646 source <<
" return node;" << std::endl;
647 source <<
"}" << std::endl << std::endl;
649 format_doc(header,
"Deserializes the given node.",
" ");
651 header <<
"deserialize(const " << support_ns <<
"::cbor::MapReader &map, " << support_ns <<
"::base::IdentifierMap &ids);" << std::endl << std::endl;
652 format_doc(source,
"Writes a debug dump of this node to the given stream.");
654 source << node.
title_case_name <<
"::deserialize(const " << support_ns <<
"::cbor::MapReader &map, " << support_ns <<
"::base::IdentifierMap &ids) {" << std::endl;
655 source <<
" auto type = map.at(\"@t\").as_string();" << std::endl;
656 for (
auto &derived : node.
derived) {
659 source <<
" throw std::runtime_error(\"Schema validation failed: unexpected node type \" + type);" << std::endl;
660 source <<
"}" << std::endl << std::endl;
665 header <<
"};" << std::endl << std::endl;
672 std::ofstream &header,
673 std::ofstream &source,
680 "Internal class for implementing the visitor pattern.");
681 header <<
"class VisitorBase {" << std::endl;
682 header <<
"public:" << std::endl << std::endl;
685 format_doc(header,
"Virtual destructor for proper cleanup.",
" ");
686 header <<
" virtual ~VisitorBase() = default;" << std::endl << std::endl;
689 header <<
"protected:" << std::endl << std::endl;
690 header <<
" friend class Node;" << std::endl;
691 for (
auto &node : nodes) {
692 header <<
" friend class " << node->title_case_name <<
";" << std::endl;
698 format_doc(header,
"Internal visitor function for nodes of any type.",
" ");
699 header <<
" virtual void raw_visit_node(Node &node, void *retval) = 0;" << std::endl << std::endl;
702 for (
auto &node : nodes) {
703 format_doc(header,
"Internal visitor function for `" + node->title_case_name +
"` nodes.",
" ");
704 header <<
" virtual void raw_visit_" << node->snake_case_name;
705 header <<
"(" << node->title_case_name <<
" &node, void *retval) = 0;" << std::endl << std::endl;
708 header <<
"};" << std::endl << std::endl;
715 std::ofstream &header,
716 std::ofstream &source,
723 "Base class for the visitor pattern for the tree.\n\n" 724 "To operate on the tree, derive from this class, describe your " 725 "operation by overriding the appropriate visit functions. and then " 726 "call `node->visit(your_visitor)`. The default implementations for " 727 "the node-specific functions fall back to the more generic functions, " 728 "eventually leading to `visit_node()`, which must be implemented with " 729 "the desired behavior for unknown nodes.");
730 header <<
"template <typename T>" << std::endl;
731 header <<
"class Visitor : public VisitorBase {" << std::endl;
732 header <<
"protected:" << std::endl << std::endl;
735 format_doc(header,
"Internal visitor function for nodes of any type.",
" ");
736 header <<
" void raw_visit_node(Node &node, void *retval) override;" << std::endl << std::endl;
739 for (
auto &node : nodes) {
740 format_doc(header,
"Internal visitor function for `" + node->title_case_name +
"` nodes.",
" ");
741 header <<
" void raw_visit_" << node->snake_case_name;
742 header <<
"(" << node->title_case_name <<
" &node, void *retval) override;" << std::endl << std::endl;
745 header <<
"public:" << std::endl << std::endl;
748 format_doc(header,
"Fallback function for nodes of any type.",
" ");
749 header <<
" virtual T visit_node(Node &node) = 0;" << std::endl << std::endl;
752 for (
auto &node : nodes) {
754 if (node->derived.empty()) {
755 doc =
"Visitor function for `" + node->title_case_name +
"` nodes.";
757 doc =
"Fallback function for `" + node->title_case_name +
"` nodes.";
760 header <<
" virtual T visit_" << node->snake_case_name;
761 header <<
"(" << node->title_case_name <<
" &node) {" << std::endl;
763 header <<
" return visit_" << node->parent->snake_case_name <<
"(node);" << std::endl;
765 header <<
" return visit_node(node);" << std::endl;
767 header <<
" }" << std::endl << std::endl;
770 header <<
"};" << std::endl << std::endl;
773 format_doc(header,
"Internal visitor function for nodes of any type.",
" ");
774 header <<
" template <typename T>" << std::endl;
775 header <<
" void Visitor<T>::raw_visit_node(Node &node, void *retval) {" << std::endl;
776 header <<
" if (retval == nullptr) {" << std::endl;
777 header <<
" this->visit_node(node);" << std::endl;
778 header <<
" } else {" << std::endl;
779 header <<
" *((T*)retval) = this->visit_node(node);" << std::endl;
780 header <<
" };" << std::endl;
781 header <<
" }" << std::endl << std::endl;
783 format_doc(header,
"Internal visitor function for nodes of any type.",
" ");
784 header <<
" template <>" << std::endl;
785 header <<
" void Visitor<void>::raw_visit_node(Node &node, void *retval);" << std::endl << std::endl;
787 format_doc(source,
"Internal visitor function for nodes of any type.");
788 source <<
"template <>" << std::endl;
789 source <<
"void Visitor<void>::raw_visit_node(Node &node, void *retval) {" << std::endl;
790 source <<
" (void)retval;" << std::endl;
791 source <<
" this->visit_node(node);" << std::endl;
792 source <<
"}" << std::endl << std::endl;
795 for (
auto &node : nodes) {
796 format_doc(header,
"Internal visitor function for `" + node->title_case_name +
"` nodes.",
" ");
797 header <<
" template <typename T>" << std::endl;
798 header <<
" void Visitor<T>::raw_visit_" << node->snake_case_name;
799 header <<
"(" << node->title_case_name <<
" &node, void *retval) {" << std::endl;
800 header <<
" if (retval == nullptr) {" << std::endl;
801 header <<
" this->visit_" << node->snake_case_name <<
"(node);" << std::endl;
802 header <<
" } else {" << std::endl;
803 header <<
" *((T*)retval) = this->visit_" << node->snake_case_name <<
"(node);" << std::endl;
804 header <<
" };" << std::endl;
805 header <<
" }" << std::endl << std::endl;
807 format_doc(header,
"Internal visitor function for `" + node->title_case_name +
"` nodes.",
" ");
808 header <<
" template <>" << std::endl;
809 header <<
" void Visitor<void>::raw_visit_" << node->snake_case_name;
810 header <<
"(" << node->title_case_name <<
" &node, void *retval);" << std::endl << std::endl;
812 format_doc(source,
"Internal visitor function for `" + node->title_case_name +
"` nodes.");
813 source <<
"template <>" << std::endl;
814 source <<
"void Visitor<void>::raw_visit_" << node->snake_case_name;
815 source <<
"(" << node->title_case_name <<
" &node, void *retval) {" << std::endl;
816 source <<
" (void)retval;" << std::endl;
817 source <<
" this->visit_" << node->snake_case_name <<
"(node);" << std::endl;
818 source <<
"}" << std::endl << std::endl;
827 std::ofstream &header,
828 std::ofstream &source,
835 "Visitor base class defaulting to DFS pre-order traversal.\n\n" 836 "The visitor functions for nodes with subnode fields default to DFS " 837 "traversal in addition to falling back to more generic node types." 838 "Links and OptLinks are *not* followed." 840 header <<
"class RecursiveVisitor : public Visitor<void> {" << std::endl;
841 header <<
"public:" << std::endl << std::endl;
844 for (
auto &node : nodes) {
845 auto doc =
"Recursive traversal for `" + node->title_case_name +
"` nodes.";
847 header <<
" void visit_" << node->snake_case_name;
848 header <<
"(" << node->title_case_name <<
" &node) override;" << std::endl << std::endl;
850 source <<
"void RecursiveVisitor::visit_" << node->snake_case_name;
851 source <<
"(" << node->title_case_name <<
" &node) {" << std::endl;
853 source <<
" visit_" << node->parent->snake_case_name <<
"(node);" << std::endl;
855 source <<
" visit_node(node);" << std::endl;
857 for (
auto &field : node->fields) {
858 if (field.node_type) {
860 source <<
" node." << field.name <<
".visit(*this);" << std::endl;
864 source <<
"}" << std::endl << std::endl;
867 header <<
"};" << std::endl << std::endl;
874 std::ofstream &header,
875 std::ofstream &source,
877 std::string &source_location,
878 std::string &support_ns
882 format_doc(header,
"Visitor class that debug-dumps a tree to a stream");
883 header <<
"class Dumper : public RecursiveVisitor {" << std::endl;
884 header <<
"protected:" << std::endl << std::endl;
885 format_doc(header,
"Output stream to dump to.",
" ");
886 header <<
" std::ostream &out;" << std::endl << std::endl;
887 format_doc(header,
"Current indentation level.",
" ");
888 header <<
" int indent = 0;" << std::endl << std::endl;
889 format_doc(header,
"When non-null, the print node IDs from here instead of link contents.",
" ");
890 header <<
" " << support_ns <<
"::base::PointerMap *ids;" << std::endl;
891 format_doc(header,
"Whether we're printing the contents of a link.",
" ");
892 header <<
" bool in_link = false;" << std::endl << std::endl;
895 format_doc(header,
"Writes the current indentation level's worth of spaces.",
" ");
896 header <<
" void write_indent();" << std::endl << std::endl;
897 format_doc(source,
"Writes the current indentation level's worth of spaces.");
898 source <<
"void Dumper::write_indent() {" << std::endl;
899 source <<
" for (int i = 0; i < indent; i++) {" << std::endl;
900 source <<
" out << \" \";" << std::endl;
901 source <<
" }" << std::endl;
902 source <<
"}" << std::endl << std::endl;
905 header <<
"public:" << std::endl << std::endl;
906 format_doc(header,
"Construct a dumping visitor.",
" ");
907 header <<
" Dumper(std::ostream &out, int indent=0, ";
908 header << support_ns <<
"::base::PointerMap *ids = nullptr) : ";
909 header <<
"out(out), indent(indent), ids(ids) {};" << std::endl << std::endl;
913 header <<
" void visit_node(Node &node) override;" << std::endl;
915 source <<
"void Dumper::visit_node(Node &node) {" << std::endl;
916 source <<
" (void)node;" << std::endl;
917 source <<
" write_indent();" << std::endl;
918 source <<
" out << \"!Node()\" << std::endl;" << std::endl;
919 source <<
"}" << std::endl << std::endl;
922 for (
auto &node : nodes) {
923 bool first_prim =
true;
924 auto doc =
"Dumps a `" + node->title_case_name +
"` node.";
926 header <<
" void visit_" << node->snake_case_name;
927 header <<
"(" << node->title_case_name <<
" &node) override;" << std::endl << std::endl;
929 source <<
"void Dumper::visit_" << node->snake_case_name;
930 source <<
"(" << node->title_case_name <<
" &node) {" << std::endl;
931 source <<
" write_indent();" << std::endl;
932 auto attributes = node->all_fields();
933 source <<
" out << \"" << node->title_case_name <<
"\";" << std::endl;
934 source <<
" if (ids != nullptr) {" << std::endl;
935 source <<
" out << \"@\" << ids->get_ref(node);" << std::endl;
936 source <<
" }" << std::endl;
937 source <<
" out << \"(\";" << std::endl;
938 if (!source_location.empty()) {
939 source <<
" if (auto loc = node.get_annotation_ptr<" << source_location <<
">()) {" << std::endl;
940 source <<
" out << \" # \" << *loc;" << std::endl;
941 source <<
" }" << std::endl;
943 source <<
" out << std::endl;" << std::endl;
944 if (!attributes.empty()) {
945 source <<
" indent++;" << std::endl;
946 for (
auto &attrib : attributes) {
947 source <<
" write_indent();" << std::endl;
948 source <<
" out << \"" << attrib.name;
949 if (attrib.ext_type ==
Link || attrib.ext_type ==
OptLink) {
954 source <<
"\";" << std::endl;
955 switch (attrib.ext_type) {
960 source <<
" if (node." << attrib.name <<
".empty()) {" << std::endl;
961 if (attrib.ext_type ==
One || attrib.ext_type ==
Link) {
962 source <<
" out << \"!MISSING\" << std::endl;" << std::endl;
964 source <<
" out << \"-\" << std::endl;" << std::endl;
966 if (attrib.ext_type ==
Link || attrib.ext_type ==
OptLink) {
967 source <<
" } else if (ids != nullptr && ids->get(node." << attrib.name <<
") != (size_t)-1) {" << std::endl;
968 auto type = attrib.node_type ? attrib.node_type->title_case_name : attrib.prim_type;
969 source <<
" out << \"" << type <<
"@\" << ids->get(node." << attrib.name <<
") << std::endl;" << std::endl;
971 source <<
" } else {" << std::endl;
972 source <<
" out << \"<\" << std::endl;" << std::endl;
973 source <<
" indent++;" << std::endl;
974 if (attrib.ext_type ==
Link || attrib.ext_type ==
OptLink) {
975 source <<
" if (!in_link) {" << std::endl;
976 source <<
" in_link = true;" << std::endl;
977 if (attrib.type ==
Prim) {
978 source <<
" if (!node." << attrib.name <<
".empty()) {" << std::endl;
979 source <<
" node." << attrib.name <<
"->dump(out, indent);" << std::endl;
980 source <<
" }" << std::endl;
982 source <<
" node." << attrib.name <<
".visit(*this);" << std::endl;
984 source <<
" in_link = false;" << std::endl;
985 source <<
" } else {" << std::endl;
986 source <<
" write_indent();" << std::endl;
987 source <<
" out << \"...\" << std::endl;" << std::endl;
988 source <<
" }" << std::endl;
989 }
else if (attrib.type ==
Prim) {
990 source <<
" if (!node." << attrib.name <<
".empty()) {" << std::endl;
991 source <<
" node." << attrib.name <<
"->dump(out, indent);" << std::endl;
992 source <<
" }" << std::endl;
994 source <<
" node." << attrib.name <<
".visit(*this);" << std::endl;
996 source <<
" indent--;" << std::endl;
997 source <<
" write_indent();" << std::endl;
998 source <<
" out << \">\" << std::endl;" << std::endl;
999 source <<
" }" << std::endl;
1004 source <<
" if (node." << attrib.name <<
".empty()) {" << std::endl;
1005 if (attrib.ext_type ==
Many) {
1006 source <<
" out << \"!MISSING\" << std::endl;" << std::endl;
1008 source <<
" out << \"[]\" << std::endl;" << std::endl;
1010 source <<
" } else {" << std::endl;
1011 source <<
" out << \"[\" << std::endl;" << std::endl;
1012 source <<
" indent++;" << std::endl;
1013 source <<
" for (auto &sptr : node." << attrib.name <<
") {" << std::endl;
1014 source <<
" if (!sptr.empty()) {" << std::endl;
1015 if (attrib.type ==
Prim) {
1016 source <<
" sptr->dump(out, indent);" << std::endl;
1018 source <<
" sptr->visit(*this);" << std::endl;
1020 source <<
" } else {" << std::endl;
1021 source <<
" write_indent();" << std::endl;
1022 source <<
" out << \"!NULL\" << std::endl;" << std::endl;
1023 source <<
" }" << std::endl;
1024 source <<
" }" << std::endl;
1025 source <<
" indent--;" << std::endl;
1026 source <<
" write_indent();" << std::endl;
1027 source <<
" out << \"]\" << std::endl;" << std::endl;
1028 source <<
" }" << std::endl;
1033 source <<
" std::stringstream ss;" << std::endl;
1034 source <<
" size_t pos;" << std::endl;
1037 source <<
" ss.str(\"\");" << std::endl;
1038 source <<
" ss.clear();" << std::endl;
1040 source <<
" ss << node." << attrib.name <<
";" << std::endl;
1041 source <<
" pos = ss.str().find_last_not_of(\" \\n\\r\\t\");" << std::endl;
1042 source <<
" if (pos != std::string::npos) {" << std::endl;
1043 source <<
" ss.str(ss.str().erase(pos+1));" << std::endl;
1044 source <<
" }" << std::endl;
1045 source <<
" if (ss.str().find('\\n') == std::string::npos) {" << std::endl;
1046 source <<
" out << ss.str() << std::endl;" << std::endl;
1047 source <<
" } else {" << std::endl;
1048 source <<
" out << \"" << attrib.prim_type <<
"<<\" << std::endl;" << std::endl;
1049 source <<
" indent++;" << std::endl;
1050 source <<
" std::string s;" << std::endl;
1051 source <<
" while (!ss.eof()) {" << std::endl;
1052 source <<
" std::getline(ss, s);" << std::endl;
1053 source <<
" write_indent();" << std::endl;
1054 source <<
" out << s << std::endl;" << std::endl;
1055 source <<
" }" << std::endl;
1056 source <<
" indent--;" << std::endl;
1057 source <<
" write_indent();" << std::endl;
1058 source <<
" out << \">>\" << std::endl;" << std::endl;
1059 source <<
" }" << std::endl;
1064 source <<
" indent--;" << std::endl;
1065 source <<
" write_indent();" << std::endl;
1067 source <<
" out << \")\" << std::endl;" << std::endl;
1068 source <<
"}" << std::endl << std::endl;
1071 header <<
"};" << std::endl << std::endl;
1078 const std::string &header_filename,
1079 const std::string &source_filename,
1082 auto nodes = specification.
nodes;
1085 auto header = std::ofstream(header_filename);
1086 if (!header.is_open()) {
1087 std::cerr <<
"Failed to open header file for writing" << std::endl;
1090 auto source = std::ofstream(source_filename);
1091 if (!source.is_open()) {
1092 std::cerr <<
"Failed to open source file for writing" << std::endl;
1098 auto sep_pos = header_filename.rfind(
'/');
1099 auto backslash_pos = header_filename.rfind(
'\\');
1100 if (backslash_pos != std::string::npos && backslash_pos > sep_pos) {
1101 sep_pos = backslash_pos;
1103 auto header_basename = header_filename.substr(sep_pos + 1);
1106 std::string include_guard = header_basename;
1108 include_guard.begin(), include_guard.end(), include_guard.begin(),
1109 [](
unsigned char c){
1110 if (std::isalnum(c)) {
1111 return std::toupper(c);
1113 return static_cast<int>(
'_');
1121 header << std::endl;
1123 header <<
"#pragma once" << std::endl;
1124 header << std::endl;
1125 header <<
"#include <iostream>" << std::endl;
1126 for (
auto &include : specification.
includes) {
1127 header <<
"#" << include << std::endl;
1129 header << std::endl;
1130 for (
size_t i = 0; i < specification.
namespaces.size(); i++) {
1132 header << std::endl;
1136 header <<
"/**" << std::endl;
1137 header <<
" * \\dot" << std::endl;
1138 header <<
" * digraph example {" << std::endl;
1139 header <<
" * node [shape=record, fontname=Helvetica, fontsize=10];" << std::endl;
1140 std::ostringstream ns;
1141 for (
auto &name : specification.
namespaces) {
1144 for (
auto &node : nodes) {
1145 header <<
" * " << node->title_case_name;
1146 header <<
" [ label=\"" << node->title_case_name;
1147 header <<
"\" URL=\"\\ref " << ns.str() << node->title_case_name;
1149 if (!node->derived.empty()) {
1150 header <<
", style=dotted";
1152 header <<
"];" << std::endl;
1154 for (
auto &node : nodes) {
1156 header <<
" * " << node->parent->title_case_name;
1157 header <<
" -> " << node->title_case_name;
1158 header <<
" [ arrowhead=open, style=dotted ];" << std::endl;
1162 for (
auto &node : nodes) {
1163 for (
auto field : node->fields) {
1165 if (field.node_type) {
1166 header <<
" * " << node->title_case_name;
1167 header <<
" -> " << field.node_type->title_case_name;
1170 std::string full_name = field.prim_type;
1171 auto pos = full_name.find(
"<");
1172 if (pos != std::string::npos) {
1173 full_name = full_name.substr(pos + 1);
1175 pos = full_name.rfind(
">");
1176 if (pos != std::string::npos) {
1177 full_name = full_name.substr(0, pos);
1179 std::string brief_name = full_name;
1180 pos = brief_name.rfind(
"::");
1181 if (pos != std::string::npos) {
1182 pos = brief_name.rfind(
"::", pos - 1);
1183 if (pos != std::string::npos) {
1184 brief_name = brief_name.substr(pos + 2);
1187 header <<
" * prim" << prim_id;
1188 header <<
" [ label=\"" << brief_name;
1189 header <<
"\" URL=\"\\ref " << full_name;
1190 header <<
"\"];" << std::endl;
1191 header <<
" * " << node->title_case_name;
1192 header <<
" -> prim" << prim_id;
1193 typ = field.ext_type;
1196 header <<
" [ label=\"" << field.name;
1198 case Any: header <<
"*\", arrowhead=open, style=bold, ";
break;
1199 case OptLink: header <<
"@?\", arrowhead=open, style=dashed, ";
break;
1200 case Maybe: header <<
"?\", arrowhead=open, style=solid, ";
break;
1201 case Many: header <<
"+\", arrowhead=normal, style=bold, ";
break;
1202 case Link: header <<
"@\", arrowhead=normal, style=dashed, ";
break;
1203 default: header <<
"\", arrowhead=normal, style=solid, ";
break;
1205 header <<
"fontname=Helvetica, fontsize=10];" << std::endl;
1208 header <<
" * }" << std::endl;
1209 header <<
" * \\enddot" << std::endl;
1210 header <<
" */" << std::endl;
1213 header <<
"namespace " << specification.
namespaces[i] <<
" {" << std::endl;
1215 header << std::endl;
1221 header <<
"// Base classes used to construct the tree." << std::endl;
1222 header <<
"using Base = " << tree_namespace <<
"Base;" << std::endl;
1223 header <<
"template <class T> using Maybe = " << tree_namespace <<
"Maybe<T>;" << std::endl;
1224 header <<
"template <class T> using One = " << tree_namespace <<
"One<T>;" << std::endl;
1225 header <<
"template <class T> using Any = " << tree_namespace <<
"Any<T>;" << std::endl;
1226 header <<
"template <class T> using Many = " << tree_namespace <<
"Many<T>;" << std::endl;
1227 header <<
"template <class T> using OptLink = " << tree_namespace <<
"OptLink<T>;" << std::endl;
1228 header <<
"template <class T> using Link = " << tree_namespace <<
"Link<T>;" << std::endl;
1229 header << std::endl;
1235 source << std::endl;
1238 source <<
"#" << include << std::endl;
1241 source <<
"#include \"" << specification.
header_fname <<
"\"" << std::endl;
1243 source <<
"#include \"" << header_basename <<
"\"" << std::endl;
1245 source << std::endl;
1246 for (
auto &name : specification.
namespaces) {
1247 source <<
"namespace " << name <<
" {" << std::endl;
1249 source << std::endl;
1252 header <<
"// Forward declarations for all classes." << std::endl;
1253 header <<
"class Node;" << std::endl;
1254 for (
auto &node : nodes) {
1255 header <<
"class " << node->title_case_name <<
";" << std::endl;
1257 header <<
"class VisitorBase;" << std::endl;
1258 header <<
"template <typename T = void>" << std::endl;
1259 header <<
"class Visitor;" << std::endl;
1260 header <<
"class RecursiveVisitor;" << std::endl;
1261 header <<
"class Dumper;" << std::endl;
1262 header << std::endl;
1277 std::unordered_set<std::string> generated;
1278 for (
auto node : nodes) {
1279 if (generated.count(node->snake_case_name)) {
1282 auto ancestors =
Nodes();
1284 ancestors.push_back(node);
1285 node = node->parent;
1287 for (
auto node_it = ancestors.rbegin(); node_it != ancestors.rend(); node_it++) {
1289 if (generated.count(node->snake_case_name)) {
1292 generated.insert(node->snake_case_name);
1306 header <<
"template <typename T>" << std::endl;
1307 header <<
"T Node::visit(Visitor<T> &visitor) {" << std::endl;
1308 header <<
" T retval;" << std::endl;
1309 header <<
" this->visit_internal(visitor, &retval);" << std::endl;
1310 header <<
" return retval;" << std::endl;
1311 header <<
"}" << std::endl << std::endl;
1314 header <<
"template <>" << std::endl;
1315 header <<
"void Node::visit(Visitor<void> &visitor);" << std::endl << std::endl;
1318 source <<
"template <>" << std::endl;
1319 source <<
"void Node::visit(Visitor<void> &visitor) {" << std::endl;
1320 source <<
" this->visit_internal(visitor);" << std::endl;
1321 source <<
"}" << std::endl << std::endl;
1324 format_doc(header,
"Stream << overload for tree nodes (writes debug dump).");
1325 header <<
"std::ostream &operator<<(std::ostream &os, const Node &object);" << std::endl << std::endl;
1326 format_doc(source,
"Stream << overload for tree nodes (writes debug dump).");
1327 source <<
"std::ostream &operator<<(std::ostream &os, const Node &object) {" << std::endl;
1328 source <<
" const_cast<Node&>(object).dump(os);" << std::endl;
1329 source <<
" return os;" << std::endl;
1330 source <<
"}" << std::endl << std::endl;
1333 for (
auto name_it = specification.
namespaces.rbegin(); name_it != specification.
namespaces.rend(); name_it++) {
1334 header <<
"} // namespace " << *name_it << std::endl;
1335 source <<
"} // namespace " << *name_it << std::endl;
1337 header << std::endl;
1338 source << 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.