tree-gen
C++ code generator for tree structures
tree-gen-cpp.cpp
Go to the documentation of this file.
1 
5 #include <fstream>
6 #include <iostream>
7 #include <cctype>
8 #include <fmt/format.h>
9 #include <unordered_set>
10 #include "tree-gen-cpp.hpp"
11 
12 namespace tree_gen {
13 namespace cpp {
14 
19  std::ofstream &stream,
20  const std::string &doc,
21  const std::string &indent = "",
22  const std::string &annotation = ""
23 ) {
24  stream << indent << "/**";
25  if (!annotation.empty()) {
26  stream << " " << annotation;
27  }
28  stream << std::endl;
29  auto word = std::ostringstream();
30  auto line = std::ostringstream();
31  line << indent << " *";
32  bool line_empty = true;
33  for (char c : doc) {
34  bool flush = false;
35  if (c == '\n') {
36  line << " " << word.str();
37  word.str("");
38  flush = true;
39  } else if (c == ' ') {
40  line << " " << word.str();
41  line_empty = false;
42  word.str("");
43  } else {
44  word << c;
45  flush = !line_empty && line.str().size() + word.str().size() > 79;
46  }
47  if (flush) {
48  stream << line.str() << std::endl;
49  line.str("");
50  line << indent << " *";
51  line_empty = true;
52  }
53  }
54  if (!word.str().empty()) {
55  line << " " << word.str();
56  line_empty = false;
57  }
58  if (!line_empty) {
59  stream << line.str() << std::endl;
60  }
61  stream << indent << " */" << std::endl;
62 }
63 
68  std::ofstream &header,
69  Nodes &nodes
70 ) {
71 
72  // Gather the leaf types.
73  std::vector<std::string> variants;
74  for (auto &node : nodes) {
75  if (node->derived.empty()) {
76  variants.push_back(node->title_case_name);
77  }
78  }
79 
80  // Print the enum.
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) {
86  header << ",";
87  }
88  header << std::endl;
89  }
90  header << "};" << std::endl << std::endl;
91 
92 }
93 
98  std::ofstream &header,
99  std::ofstream &source,
100  const std::string &clsname,
101  Node &into,
102  bool allowed
103 ) {
104  for (int constant = 0; constant < 2; constant++) {
105  std::string doc = "Interprets this node to a node of type "
106  + into.title_case_name
107  + ". Returns null if it has the wrong type.";
108  format_doc(header, doc, " ");
109  header << " ";
110  if (!allowed) header << "virtual ";
111  if (constant) header << "const ";
112  header << into.title_case_name << " *";
113  header << "as_" << into.snake_case_name << "()";
114  if (constant) header << " const";
115  if (allowed) header << " override";
116  header << ";" << std::endl << std::endl;
117  format_doc(source, doc);
118  if (constant) source << "const ";
119  source << into.title_case_name << " *";
120  source << clsname << "::as_" << into.snake_case_name << "()";
121  if (constant) source << " const";
122  source << " {" << std::endl;
123  if (allowed) {
124  source << " return dynamic_cast<";
125  if (constant) source << "const ";
126  source << into.title_case_name << "*>(this);" << std::endl;
127  } else {
128  source << " return nullptr;" << std::endl;
129  }
130  source << "}" << std::endl << std::endl;
131  }
132 }
133 
138  std::ofstream &header,
139  std::ofstream &source,
140  Nodes &nodes,
141  bool with_serdes,
142  const std::string &support_ns
143 ) {
144 
145  format_doc(header, "Main class for all nodes.");
146  header << "class Node : public Base {" << std::endl;
147  header << "public:" << std::endl << std::endl;
148 
149  format_doc(header, "Returns the `NodeType` of this node.", " ");
150  header << " virtual NodeType type() const = 0;" << std::endl << std::endl;
151 
152  format_doc(header, "Returns a shallow copy of this node.", " ");
153  header << " virtual One<Node> copy() const = 0;" << std::endl << std::endl;
154 
155  format_doc(header, "Returns a deep copy of this node.", " ");
156  header << " virtual One<Node> clone() const = 0;" << std::endl << std::endl;
157 
158  format_doc(header, "Value-based equality operator. Ignores annotations!", " ");
159  header << " virtual bool equals(const Node& rhs) const = 0;" << std::endl << std::endl;
160 
161  format_doc(header, "Pointer-based equality operator.", " ");
162  header << " virtual bool operator==(const Node& rhs) const = 0;" << std::endl << std::endl;
163 
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;
168 
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;
172 
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;
177 
178  /*header << " T visit(Visitor<T> &visitor) {" << std::endl;
179  header << " T retval;" << std::endl;
180  header << " this->visit_internal(visitor, &retval);" << std::endl;
181  header << " return retval;" << std::endl;
182  header << " }" << std::endl << std::endl;
183 
184  format_doc(header, "Visit this object.", " "); TODO
185  header << " template <>" << std::endl;
186  header << " void visit<void>(Visitor &visitor) {" << std::endl;
187  header << " this->visit_internal(visitor);" << std::endl;
188  header << " }" << std::endl << std::endl;*/
189 
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;
197 
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;
209 
210  for (auto &node : nodes) {
211  generate_typecast_function(header, source, "Node", *node, false);
212  }
213 
214  if (with_serdes) {
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;
220 
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;
236  }
237  }
238  source << " throw std::runtime_error(\"Schema validation failed: unexpected node type \" + type);" << std::endl;
239  source << "}" << std::endl << std::endl;
240  }
241 
242  header << "};" << std::endl << std::endl;
243 
244 }
245 
251  std::ofstream &source,
252  Node &node
253 ) {
254  if (node.derived.empty()) {
255  source << " if (type == \"" << node.title_case_name << "\") ";
256  source << "return " << node.title_case_name << "::deserialize(map, ids);" << std::endl;
257  } else {
258  for (auto &derived : node.derived) {
259  generate_deserialize_mux(source, *(derived.lock()));
260  }
261  }
262 }
263 
268  std::ofstream &header,
269  std::ofstream &source,
270  Specification &spec,
271  Node &node
272 ) {
273  const auto all_fields = node.all_fields();
274  const auto &support_ns = spec.support_namespace;
275 
276  // Print class header.
277  if (!node.doc.empty()) {
278  format_doc(header, node.doc);
279  }
280  header << "class " << node.title_case_name << " : public ";
281  if (node.parent) {
282  header << node.parent->title_case_name;
283  } else {
284  header << "Node";
285  }
286  header << " {" << std::endl;
287  header << "public:" << std::endl << std::endl;
288 
289  // Print fields.
290  for (auto &field : node.fields) {
291  if (!field.doc.empty()) {
292  format_doc(header, field.doc, " ");
293  }
294  header << " ";
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;
303  }
304  header << field.name << ";" << std::endl << std::endl;
305  }
306 
307  // Print constructors.
308  if (!all_fields.empty()) {
309  format_doc(header, "Constructor.", " ");
310  header << " " << node.title_case_name << "(";
311  bool first = true;
312  for (auto &field : all_fields) {
313  if (first) {
314  first = false;
315  } else {
316  header << ", ";
317  }
318  header << "const ";
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;
327  }
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;
336  case Prim: header << spec.initialize_function << "<" << field.prim_type << ">()"; break;
337  }
338  }
339  header << ");" << std::endl << std::endl;
340 
341  format_doc(source, "Constructor.", "");
342  source << node.title_case_name << "::" << node.title_case_name << "(";
343  first = true;
344  for (auto &field : all_fields) {
345  if (first) {
346  first = false;
347  } else {
348  source << ", ";
349  }
350  source << "const ";
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;
359  }
360  source << "&" << field.name;
361  }
362  source << ")" << std::endl << " : ";
363  first = true;
364  if (node.parent) {
365  source << node.parent->title_case_name << "(";
366  first = true;
367  for (auto &field : node.parent->all_fields()) {
368  if (first) {
369  first = false;
370  } else {
371  source << ", ";
372  }
373  source << field.name;
374  }
375  source << ")";
376  first = false;
377  }
378  for (auto &field : node.fields) {
379  if (first) {
380  first = false;
381  } else {
382  source << ", ";
383  }
384  source << field.name << "(" << field.name << ")";
385  }
386  source << std::endl << "{}" << std::endl << std::endl;
387  }
388 
389  // Print find_reachable and check_complete functions.
390  if (node.derived.empty()) {
391  std::string doc = "Registers all reachable nodes with the given PointerMap.";
392  format_doc(header, doc, " ");
393  header << " void find_reachable(" << support_ns << "::base::PointerMap &map) const override;" << std::endl << std::endl;
394  format_doc(source, doc);
395  source << "void " << node.title_case_name;
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;
400  switch (type) {
401  case Maybe:
402  case One:
403  case Any:
404  case Many:
405  case OptLink:
406  case Link:
407  source << " " << field.name << ".find_reachable(map);" << std::endl;
408  break;
409  default:
410  break;
411  }
412  }
413  source << "}" << std::endl << std::endl;
414 
415  doc = "Returns whether this `" + node.title_case_name + "` is complete/fully defined.";
416  format_doc(header, doc, " ");
417  header << " void check_complete(const " << support_ns << "::base::PointerMap &map) const override;" << std::endl << std::endl;
418  format_doc(source, doc);
419  source << "void " << node.title_case_name;
420  source << "::check_complete(const " << support_ns << "::base::PointerMap &map) const {" << std::endl;
421  source << " (void)map;" << std::endl;
422  if (node.is_error_marker) {
423  source << " throw " << support_ns << "::base::NotWellFormed(\"" << node.title_case_name << " error node in tree\");" << std::endl;
424  } else {
425  for (auto &field : all_fields) {
426  auto type = (field.type == Prim) ? field.ext_type : field.type;
427  switch (type) {
428  case Maybe:
429  case One:
430  case Any:
431  case Many:
432  case OptLink:
433  case Link:
434  source << " " << field.name << ".check_complete(map);" << std::endl;
435  break;
436  default:
437  break;
438  }
439  }
440  }
441  source << "}" << std::endl << std::endl;
442  }
443 
444  // Print type() function.
445  if (node.derived.empty()) {
446  auto doc = "Returns the `NodeType` of this node.";
447  format_doc(header, doc, " ");
448  header << " NodeType type() const override;" << std::endl << std::endl;
449  format_doc(source, doc);
450  source << "NodeType " << node.title_case_name;
451  source << "::type() const {" << std::endl;
452  source << " return NodeType::" << node.title_case_name << ";" << std::endl;
453  source << "}" << std::endl << std::endl;
454  }
455 
456  // Print visitor function.
457  if (node.derived.empty()) {
458  auto doc = "Helper method for visiting nodes.";
459  header << "protected:" << std::endl << std::endl;
460  format_doc(header, doc, " ");
461  header << " void visit_internal(VisitorBase &visitor, void *retval) override;" << std::endl << std::endl;
462  header << "public:" << std::endl << std::endl;
463  format_doc(source, doc);
464  source << "void " << node.title_case_name;
465  source << "::visit_internal(VisitorBase &visitor, void *retval) {" << std::endl;
466  source << " visitor.raw_visit_" << node.snake_case_name;
467  source << "(*this, retval);" << std::endl;
468  source << "}" << std::endl << std::endl;
469  }
470 
471  // Print conversion function.
472  generate_typecast_function(header, source, node.title_case_name, node, true);
473 
474  // Print copy method.
475  if (node.derived.empty()) {
476  auto doc = "Returns a shallow copy of this node.";
477  format_doc(header, doc, " ");
478  header << " One<Node> copy() const override;" << std::endl << std::endl;
479  format_doc(source, doc);
480  source << "One<Node> " << node.title_case_name;
481  source << "::copy() const {" << std::endl;
482  source << " return ";
483  if (!spec.tree_namespace.empty()) {
484  source << spec.tree_namespace << "::";
485  }
486  source << "make<" << node.title_case_name << ">(*this);" << std::endl;
487  source << "}" << std::endl << std::endl;
488  }
489 
490  // Print clone method.
491  if (node.derived.empty()) {
492  auto doc = "Returns a deep copy of this node.";
493  format_doc(header, doc, " ");
494  header << " One<Node> clone() const override;" << std::endl << std::endl;
495  format_doc(source, doc);
496  source << "One<Node> " << node.title_case_name;
497  source << "::clone() const {" << std::endl;
498  source << " auto node = ";
499  if (!spec.tree_namespace.empty()) {
500  source << spec.tree_namespace << "::";
501  }
502  source << "make<" << node.title_case_name << ">(*this);" << std::endl;
503  for (auto &field : all_fields) {
504  auto type = (field.type == Prim) ? field.ext_type : field.type;
505  if (type == Maybe || type == One || type == Any || type == Many) {
506  source << " node->" << field.name << " = this->" << field.name << ".clone();" << std::endl;
507  }
508  }
509  source << " return node;" << std::endl;
510  source << "}" << std::endl << std::endl;
511  }
512 
513  // Print equality operator.
514  if (node.derived.empty()) {
515  auto doc = "Value-based equality operator. Ignores annotations!";
516  format_doc(header, doc, " ");
517  header << " bool equals(const Node &rhs) const override;" << std::endl << std::endl;
518  format_doc(source, doc);
519  source << "bool " << node.title_case_name;
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;
527  } else {
528  source << " if (!this->" << field.name << ".equals(rhsc." << field.name << ")) return false;" << std::endl;
529  }
530  }
531  }
532  source << " return true;" << std::endl;
533  source << "}" << std::endl << std::endl;
534 
535  doc = "Pointer-based equality operator.";
536  format_doc(header, doc, " ");
537  header << " bool operator==(const Node &rhs) const override;" << std::endl << std::endl;
538  format_doc(source, doc);
539  source << "bool " << node.title_case_name;
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;
546  }
547  }
548  source << " return true;" << std::endl;
549  source << "}" << std::endl << std::endl;
550  }
551 
552  // Print serdes methods.
553  if (!spec.serialize_fn.empty()) {
554  if (node.derived.empty()) {
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;
567  bool first = true;
568  for (const auto &field : all_fields) {
569  source << " ";
570  if (first) {
571  source << "auto ";
572  first = false;
573  }
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;
578  } else {
579  source << " " << field.name << ".serialize(submap, ids);" << std::endl;
580  }
581  source << " submap.close();" << std::endl;
582  }
583  source << " serialize_annotations(map);" << std::endl;
584  source << "}" << std::endl << std::endl;
585 
586  format_doc(header, "Deserializes the given node.", " ");
587  header << " static std::shared_ptr<" << node.title_case_name << "> ";
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.");
590  source << "std::shared_ptr<" << node.title_case_name << "> ";
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{};
599  first = true;
600  for (const auto &field : all_fields) {
601  if (first) {
602  first = false;
603  } else {
604  source << "," << std::endl;
605  }
606  source << " ";
607 
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;
618  }
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)";
624  } else {
625  source << spec.deserialize_fn << "<" << field.prim_type << ">";
626  source << "(map.at(\"" << field.name << "\").as_map())";
627  }
628  if (type == OptLink || type == Link) {
629  links.push_back(field);
630  }
631  }
632  source << std::endl;
633  source << " );" << std::endl;
634  first = true;
635  for (const auto &link : links) {
636  source << " ";
637  if (first) {
638  first = false;
639  source << "auto ";
640  }
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;
645  }
646  source << " node->deserialize_annotations(map);" << std::endl;
647  source << " return node;" << std::endl;
648  source << "}" << std::endl << std::endl;
649  } else {
650  format_doc(header, "Deserializes the given node.", " ");
651  header << " static std::shared_ptr<" << node.title_case_name << "> ";
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.");
654  source << "std::shared_ptr<" << node.title_case_name << "> ";
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) {
658  generate_deserialize_mux(source, *(derived.lock()));
659  }
660  source << " throw std::runtime_error(\"Schema validation failed: unexpected node type \" + type);" << std::endl;
661  source << "}" << std::endl << std::endl;
662  }
663  }
664 
665  // Print class footer.
666  header << "};" << std::endl << std::endl;
667 }
668 
673  std::ofstream &header,
674  std::ofstream &source,
675  Nodes &nodes
676 ) {
677 
678  // Print class header.
679  format_doc(
680  header,
681  "Internal class for implementing the visitor pattern.");
682  header << "class VisitorBase {" << std::endl;
683  header << "public:" << std::endl << std::endl;
684 
685  // Virtual destructor.
686  format_doc(header, "Virtual destructor for proper cleanup.", " ");
687  header << " virtual ~VisitorBase() = default;" << std::endl << std::endl;
688 
689  // Raw visit methods are protected.
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;
694  }
695  header << std::endl;
696 
697 
698  // Fallback for any kind of node.
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;
701 
702  // Functions for all node types.
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;
707  }
708 
709  header << "};" << std::endl << std::endl;
710 }
711 
716  std::ofstream &header,
717  std::ofstream &source,
718  Nodes &nodes
719 ) {
720 
721  // Print class header.
722  format_doc(
723  header,
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;
734 
735  // Internal function for any kind of node.
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;
738 
739  // Internal functions for all node types.
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;
744  }
745 
746  header << "public:" << std::endl << std::endl;
747 
748  // Fallback for any kind of node.
749  format_doc(header, "Fallback function for nodes of any type.", " ");
750  header << " virtual T visit_node(Node &node) = 0;" << std::endl << std::endl;
751 
752  // Functions for all node types.
753  for (auto &node : nodes) {
754  std::string doc;
755  if (node->derived.empty()) {
756  doc = "Visitor function for `" + node->title_case_name + "` nodes.";
757  } else {
758  doc = "Fallback function for `" + node->title_case_name + "` nodes.";
759  }
760  format_doc(header, doc, " ");
761  header << " virtual T visit_" << node->snake_case_name;
762  header << "(" << node->title_case_name << " &node) {" << std::endl;
763  if (node->parent) {
764  header << " return visit_" << node->parent->snake_case_name << "(node);" << std::endl;
765  } else {
766  header << " return visit_node(node);" << std::endl;
767  }
768  header << " }" << std::endl << std::endl;
769  }
770 
771  header << "};" << std::endl << std::endl;
772 
773  // Internal function for any kind of node.
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;
783 
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;
787 
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;
794 
795  // Internal functions for all node types.
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;
807 
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;
812 
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;
820  }
821 
822 }
823 
828  std::ofstream &header,
829  std::ofstream &source,
830  Nodes &nodes
831 ) {
832 
833  // Print class header.
834  format_doc(
835  header,
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."
840  );
841  header << "class RecursiveVisitor : public Visitor<void> {" << std::endl;
842  header << "public:" << std::endl << std::endl;
843 
844  // Functions for all node types.
845  for (auto &node : nodes) {
846  auto doc = "Recursive traversal for `" + node->title_case_name + "` nodes.";
847  format_doc(header, doc, " ");
848  header << " void visit_" << node->snake_case_name;
849  header << "(" << node->title_case_name << " &node) override;" << std::endl << std::endl;
850  format_doc(source, doc);
851  source << "void RecursiveVisitor::visit_" << node->snake_case_name;
852  source << "(" << node->title_case_name << " &node) {" << std::endl;
853  if (node->parent) {
854  source << " visit_" << node->parent->snake_case_name << "(node);" << std::endl;
855  } else {
856  source << " visit_node(node);" << std::endl;
857  }
858  for (auto &field : node->fields) {
859  if (field.node_type) {
860  if (field.type != Link && field.type != OptLink) {
861  source << " node." << field.name << ".visit(*this);" << std::endl;
862  }
863  }
864  }
865  source << "}" << std::endl << std::endl;
866  }
867 
868  header << "};" << std::endl << std::endl;
869 }
870 
875  std::ofstream &header,
876  std::ofstream &source,
877  Nodes &nodes,
878  std::string &source_location,
879  std::string &support_ns
880 ) {
881 
882  // Print class header.
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;
894 
895  // Print function that prints indentation level.
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;
904 
905  // Write constructor.
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;
911 
912  // Print fallback function.
913  format_doc(header, "Dumps a `Node`.", " ");
914  header << " void visit_node(Node &node) override;" << std::endl;
915  format_doc(source, "Dumps a `Node`.");
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;
921 
922  // Functions for all node types.
923  for (auto &node : nodes) {
924  bool first_prim = true;
925  auto doc = "Dumps a `" + node->title_case_name + "` node.";
926  format_doc(header, doc, " ");
927  header << " void visit_" << node->snake_case_name;
928  header << "(" << node->title_case_name << " &node) override;" << std::endl << std::endl;
929  format_doc(source, doc);
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;
943  }
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) {
951  source << " --> ";
952  } else {
953  source << ": ";
954  }
955  source << "\";" << std::endl;
956  switch (attrib.ext_type) {
957  case Maybe:
958  case One:
959  case OptLink:
960  case Link:
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;
964  } else {
965  source << " out << \"-\" << std::endl;" << std::endl;
966  }
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;
971  }
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;
982  } else {
983  source << " node." << attrib.name << ".visit(*this);" << std::endl;
984  }
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;
994  } else {
995  source << " node." << attrib.name << ".visit(*this);" << std::endl;
996  }
997  source << " indent--;" << std::endl;
998  source << " write_indent();" << std::endl;
999  source << " out << \">\" << std::endl;" << std::endl;
1000  source << " }" << std::endl;
1001  break;
1002 
1003  case Any:
1004  case Many:
1005  source << " if (node." << attrib.name << ".empty()) {" << std::endl;
1006  if (attrib.ext_type == Many) {
1007  source << " out << \"!MISSING\" << std::endl;" << std::endl;
1008  } else {
1009  source << " out << \"[]\" << std::endl;" << std::endl;
1010  }
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;
1018  } else {
1019  source << " sptr->visit(*this);" << std::endl;
1020  }
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;
1030  break;
1031 
1032  case Prim:
1033  if (first_prim) {
1034  source << " std::stringstream ss;" << std::endl;
1035  source << " size_t pos;" << std::endl;
1036  first_prim = false;
1037  } else {
1038  source << " ss.str(\"\");" << std::endl;
1039  source << " ss.clear();" << std::endl;
1040  }
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;
1061  break;
1062 
1063  }
1064  }
1065  source << " indent--;" << std::endl;
1066  source << " write_indent();" << std::endl;
1067  }
1068  source << " out << \")\" << std::endl;" << std::endl;
1069  source << "}" << std::endl << std::endl;
1070  }
1071 
1072  header << "};" << std::endl << std::endl;
1073 }
1074 
1079  const std::string &header_filename,
1080  const std::string &source_filename,
1081  Specification &specification
1082 ) {
1083  auto nodes = specification.nodes;
1084 
1085  // Open the output files.
1086  auto header = std::ofstream(header_filename);
1087  if (!header.is_open()) {
1088  std::cerr << "Failed to open header file for writing" << std::endl;
1089  std::exit(1);
1090  }
1091  auto source = std::ofstream(source_filename);
1092  if (!source.is_open()) {
1093  std::cerr << "Failed to open source file for writing" << std::endl;
1094  std::exit(1);
1095  }
1096 
1097  // Strip the path from the header filename such that it can be used for the
1098  // include guard and the #include directive in the source file.
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;
1103  }
1104  auto header_basename = header_filename.substr(sep_pos + 1);
1105 
1106  // Generate the include guard name.
1107  std::string include_guard = header_basename;
1108  std::transform(
1109  include_guard.begin(), include_guard.end(), include_guard.begin(),
1110  [](unsigned char c){
1111  if (std::isalnum(c)) {
1112  return std::toupper(c);
1113  } else {
1114  return static_cast<int>('_');
1115  }
1116  }
1117  );
1118 
1119  // Header for the header file.
1120  if (!specification.header_doc.empty()) {
1121  format_doc(header, specification.header_doc, "", "\\file");
1122  header << std::endl;
1123  }
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;
1130  }
1131  header << std::endl;
1132  for (size_t i = 0; i < specification.namespaces.size(); i++) {
1133  if (i == specification.namespaces.size() - 1 && !specification.namespace_doc.empty()) {
1134  header << std::endl;
1135  format_doc(header, specification.namespace_doc);
1136 
1137  // Generate dot graph for the tree for the doxygen documentation.
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) {
1144  ns << name << "::";
1145  }
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;
1150  header << "\"";
1151  if (!node->derived.empty()) {
1152  header << ", style=dotted";
1153  }
1154  header << "];" << std::endl;
1155  }
1156  for (auto &node : nodes) {
1157  if (node->parent) {
1158  header << " * " << node->parent->title_case_name;
1159  header << " -> " << node->title_case_name;
1160  header << " [ arrowhead=open, style=dotted ];" << std::endl;
1161  }
1162  }
1163  int prim_id = 0;
1164  for (auto &node : nodes) {
1165  for (auto field : node->fields) {
1166  EdgeType typ;
1167  if (field.node_type) {
1168  header << " * " << node->title_case_name;
1169  header << " -> " << field.node_type->title_case_name;
1170  typ = field.type;
1171  } else {
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);
1176  }
1177  pos = full_name.rfind(">");
1178  if (pos != std::string::npos) {
1179  full_name = full_name.substr(0, pos);
1180  }
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);
1187  }
1188  }
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;
1196  prim_id++;
1197  }
1198  header << " [ label=\"" << field.name;
1199  switch (typ) {
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;
1206  }
1207  header << "fontname=Helvetica, fontsize=10];" << std::endl;
1208  }
1209  }
1210  header << " * }" << std::endl;
1211  header << " * \\enddot" << std::endl;
1212  header << " */" << std::endl;
1213 
1214  }
1215  header << "namespace " << specification.namespaces[i] << " {" << std::endl;
1216  }
1217  header << std::endl;
1218 
1219  // Determine the namespace that the base and edge classes are defined in.
1220  // If it's not the current namespace, pull the types into it using typedefs.
1221  if (!specification.tree_namespace.empty()) {
1222  auto tree_namespace = specification.tree_namespace + "::";
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;
1232  }
1233 
1234  // Header for the source file.
1235  if (!specification.source_doc.empty()) {
1236  format_doc(source, specification.source_doc, "", "\\file");
1237  source << std::endl;
1238  }
1239  for (auto &include : specification.src_includes) {
1240  source << "#" << include << std::endl;
1241  }
1242  if (!specification.header_fname.empty()) {
1243  source << "#include \"" << specification.header_fname << "\"" << std::endl;
1244  } else {
1245  source << "#include \"" << header_basename << "\"" << std::endl;
1246  }
1247  source << std::endl;
1248  for (auto &name : specification.namespaces) {
1249  source << "namespace " << name << " {" << std::endl;
1250  }
1251  source << std::endl;
1252 
1253  // Generate forward references for all the classes.
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;
1258  }
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;
1265 
1266  // Generate the NodeType enum.
1267  generate_enum(header, nodes);
1268 
1269  // Generate the base class.
1271  header,
1272  source,
1273  nodes,
1274  !specification.serialize_fn.empty(),
1275  specification.support_namespace
1276  );
1277 
1278  // Generate the node classes.
1279  std::unordered_set<std::string> generated;
1280  for (auto node : nodes) {
1281  if (generated.count(node->snake_case_name)) {
1282  continue;
1283  }
1284  auto ancestors = Nodes();
1285  while (node) {
1286  ancestors.push_back(node);
1287  node = node->parent;
1288  }
1289  for (auto node_it = ancestors.rbegin(); node_it != ancestors.rend(); node_it++) {
1290  node = *node_it;
1291  if (generated.count(node->snake_case_name)) {
1292  continue;
1293  }
1294  generated.insert(node->snake_case_name);
1295  generate_node_class(header, source, specification, *node);
1296  }
1297  }
1298 
1299  // Generate the visitor classes.
1300  generate_visitor_base_class(header, source, nodes);
1301  generate_visitor_class(header, source, nodes);
1302  generate_recursive_visitor_class(header, source, nodes);
1303  generate_dumper_class(header, source, nodes, specification.source_location, specification.support_namespace);
1304 
1305  // Generate the templated visit method and its specialization for void
1306  // return type.
1307  format_doc(header, "Visit this object.");
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;
1314 
1315  format_doc(header, "Visit this object.");
1316  header << "template <>" << std::endl;
1317  header << "void Node::visit(Visitor<void> &visitor);" << std::endl << std::endl;
1318 
1319  format_doc(source, "Visit this object.");
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;
1324 
1325  // Overload the stream write operator.
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;
1333 
1334  // Close the namespaces.
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;
1338  }
1339  header << std::endl;
1340  source << std::endl;
1341 
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;
1346  }
1347 }
1348 
1349 } // namespace cpp
1350 } // namespace tree_gen
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.
Definition: tree-gen.cpp:16
Exactly one node.
Definition: tree-gen.hpp:645
std::string support_namespace
The namespace to take support stuff like tree::cbor from.
Definition: tree-gen.hpp:914
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.
Definition: tree-gen.hpp:785
Represents a type of AST node.
Definition: tree-gen.hpp:733
Struct containing everything needed for a complete specification.
Definition: tree-gen.hpp:850
std::vector< std::string > src_includes
The include statements to stick at the top of the source file.
Definition: tree-gen.hpp:889
Zero or one nodes.
Definition: tree-gen.hpp:640
std::vector< std::weak_ptr< Node > > derived
Node types derived from this one.
Definition: tree-gen.hpp:758
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.
Definition: tree-gen.hpp:884
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.
Definition: tree-gen.hpp:899
std::string tree_namespace
The namespace to take the tree base types from.
Definition: tree-gen.hpp:909
std::string header_fname
Explicit filename for the header, in case it will not end up in the same directory that the source is...
Definition: tree-gen.hpp:874
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.
Definition: tree-gen.hpp:904
std::string initialize_function
The initialization function to use for default values of members.
Definition: tree-gen.hpp:919
std::string title_case_name
Name in TitleCase.
Definition: tree-gen.hpp:743
std::string header_doc
Header file documentation.
Definition: tree-gen.hpp:868
Link to zero or one nodes elsewhere in the tree.
Definition: tree-gen.hpp:660
EdgeType
Types of edges between nodes and primitives.
Definition: tree-gen.hpp:635
Primitive type.
Definition: tree-gen.hpp:670
std::string source_location
Annotation object used for source location info, or empty if source locations are not used or are not...
Definition: tree-gen.hpp:949
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.
Definition: tree-gen.hpp:748
One or more nodes.
Definition: tree-gen.hpp:655
std::string serialize_fn
The serialization function to use when serializing primitives.
Definition: tree-gen.hpp:925
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.
Definition: tree-gen.hpp:773
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.
Definition: tree-gen.hpp:763
std::string source_doc
Source file documentation.
Definition: tree-gen.hpp:863
std::string snake_case_name
Name in snake_case.
Definition: tree-gen.hpp:738
std::string deserialize_fn
The serialization function to use when deserializing primitives.
Definition: tree-gen.hpp:936
Zero or more nodes.
Definition: tree-gen.hpp:650
std::shared_ptr< Node > parent
The node type this is derived from, if any.
Definition: tree-gen.hpp:753
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.
Definition: tree-gen.hpp:954
Link to exactly one node elsewhere in the tree.
Definition: tree-gen.hpp:665