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