// _gen/frontend/syntax.asdl.cc is generated by asdl_main.py

#include "_gen/frontend/syntax.asdl.h"
#include <assert.h>
#include "prebuilt/asdl/runtime.mycpp.h"  // generated code uses wrappers here
#include "_gen/core/value.asdl.h"  // "use" in ASDL 

// Generated code uses these types
using hnode_asdl::hnode;
using hnode_asdl::Field;
using hnode_asdl::color_e;

using id_kind_asdl::Id_str;

namespace syntax_asdl {


hnode_t* BoolParamBox::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("BoolParamBox"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(this->b ? runtime::TRUE_STR :
                                   runtime::FALSE_STR, color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("b"), x0));

  return out_node;
}


hnode_t* IntParamBox::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("IntParamBox"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(str(this->i), color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("i"), x0));

  return out_node;
}

BigStr* parse_result_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case parse_result_e::EmptyLine:
    v = "EmptyLine"; break;
  case parse_result_e::Eof:
    v = "Eof"; break;
  case parse_result_e::Node:
    v = "Node"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "parse_result.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

parse_result__EmptyLine* parse_result::EmptyLine =
&gparse_result__EmptyLine.obj;

GcGlobal<parse_result__EmptyLine> gparse_result__EmptyLine = 
  { ObjHeader::Global(parse_result_e::EmptyLine) };

parse_result__Eof* parse_result::Eof = &gparse_result__Eof.obj;

GcGlobal<parse_result__Eof> gparse_result__Eof = 
  { ObjHeader::Global(parse_result_e::Eof) };

hnode_t* parse_result__EmptyLine::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(parse_result_str(this->tag()));
  return out_node;
}


hnode_t* parse_result__Eof::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(parse_result_str(this->tag()));
  return out_node;
}


hnode_t* parse_result__Node::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(parse_result_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->cmd->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("cmd"), x0));

  return out_node;
}


hnode_t* parse_result_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case parse_result_e::EmptyLine: {
    parse_result__EmptyLine* obj = static_cast<parse_result__EmptyLine*>(this);
    return obj->PrettyTree(seen);
  }
  case parse_result_e::Eof: {
    parse_result__Eof* obj = static_cast<parse_result__Eof*>(this);
    return obj->PrettyTree(seen);
  }
  case parse_result_e::Node: {
    parse_result__Node* obj = static_cast<parse_result__Node*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* source_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case source_e::Interactive:
    v = "Interactive"; break;
  case source_e::Headless:
    v = "Headless"; break;
  case source_e::Unused:
    v = "Unused"; break;
  case source_e::CFlag:
    v = "CFlag"; break;
  case source_e::Stdin:
    v = "Stdin"; break;
  case source_e::MainFile:
    v = "MainFile"; break;
  case source_e::SourcedFile:
    v = "SourcedFile"; break;
  case source_e::ArgvWord:
    v = "ArgvWord"; break;
  case source_e::Variable:
    v = "Variable"; break;
  case source_e::VarRef:
    v = "VarRef"; break;
  case source_e::Alias:
    v = "Alias"; break;
  case source_e::Reparsed:
    v = "Reparsed"; break;
  case source_e::Synthetic:
    v = "Synthetic"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "source.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

source__Interactive* source::Interactive = &gsource__Interactive.obj;

GcGlobal<source__Interactive> gsource__Interactive = 
  { ObjHeader::Global(source_e::Interactive) };

source__Headless* source::Headless = &gsource__Headless.obj;

GcGlobal<source__Headless> gsource__Headless = 
  { ObjHeader::Global(source_e::Headless) };

source__CFlag* source::CFlag = &gsource__CFlag.obj;

GcGlobal<source__CFlag> gsource__CFlag = 
  { ObjHeader::Global(source_e::CFlag) };

hnode_t* source__Interactive::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  return out_node;
}


hnode_t* source__Headless::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  return out_node;
}


hnode_t* source__Unused::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = runtime::NewLeaf(this->comment, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("comment"), x0));

  return out_node;
}


hnode_t* source__CFlag::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  return out_node;
}


hnode_t* source__Stdin::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = runtime::NewLeaf(this->comment, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("comment"), x0));

  return out_node;
}


hnode_t* source__MainFile::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = runtime::NewLeaf(this->path, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("path"), x0));

  return out_node;
}


hnode_t* source__SourcedFile::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = runtime::NewLeaf(this->path, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("path"), x0));

  hnode_t* x1 = this->location->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("location"), x1));

  return out_node;
}


hnode_t* source__ArgvWord::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = runtime::NewLeaf(this->what, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("what"), x0));

  hnode_t* x1 = this->location->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("location"), x1));

  return out_node;
}


hnode_t* source__Variable::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = runtime::NewLeaf(this->var_name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("var_name"), x0));

  hnode_t* x1 = this->location->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("location"), x1));

  return out_node;
}


hnode_t* source__VarRef::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->orig_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("orig_tok"), x0));

  return out_node;
}


hnode_t* source__Alias::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = runtime::NewLeaf(this->argv0, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("argv0"), x0));

  hnode_t* x1 = this->argv0_loc->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("argv0_loc"), x1));

  return out_node;
}


hnode_t* source__Reparsed::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = runtime::NewLeaf(this->what, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("what"), x0));

  hnode_t* x1 = this->left_token->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left_token"), x1));

  hnode_t* x2 = this->right_token->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right_token"), x2));

  return out_node;
}


hnode_t* source__Synthetic::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(source_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = runtime::NewLeaf(this->s, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("s"), x0));

  return out_node;
}


hnode_t* source_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case source_e::Interactive: {
    source__Interactive* obj = static_cast<source__Interactive*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::Headless: {
    source__Headless* obj = static_cast<source__Headless*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::Unused: {
    source__Unused* obj = static_cast<source__Unused*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::CFlag: {
    source__CFlag* obj = static_cast<source__CFlag*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::Stdin: {
    source__Stdin* obj = static_cast<source__Stdin*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::MainFile: {
    source__MainFile* obj = static_cast<source__MainFile*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::SourcedFile: {
    source__SourcedFile* obj = static_cast<source__SourcedFile*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::ArgvWord: {
    source__ArgvWord* obj = static_cast<source__ArgvWord*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::Variable: {
    source__Variable* obj = static_cast<source__Variable*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::VarRef: {
    source__VarRef* obj = static_cast<source__VarRef*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::Alias: {
    source__Alias* obj = static_cast<source__Alias*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::Reparsed: {
    source__Reparsed* obj = static_cast<source__Reparsed*>(this);
    return obj->PrettyTree(seen);
  }
  case source_e::Synthetic: {
    source__Synthetic* obj = static_cast<source__Synthetic*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}

hnode_t* SourceLine::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("SourceLine"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(str(this->line_num), color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("line_num"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->content, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("content"), x1));

  hnode_t* x2 = this->src->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("src"), x2));

  return out_node;
}


hnode_t* Token::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("Token"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(Id_str(this->id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("id"), x0));

  hnode_t* x1 = Alloc<hnode::Leaf>(str(this->length), color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("length"), x1));

  hnode_t* x2 = Alloc<hnode::Leaf>(str(this->col), color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("col"), x2));

  if (this->line) {  // Optional
    hnode_t* x3 = this->line->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("line"), x3));
  }

  if (this->tval) {  // Optional
    hnode_t* x4 = runtime::NewLeaf(this->tval, color_e::StringConst);
    L->append(Alloc<Field>(StrFromC("tval"), x4));
  }

  return out_node;
}


hnode_t* CompoundWord::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("CompoundWord"));
  List<Field*>* L = out_node->fields;

  if (this->parts != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<word_part_t*> it(this->parts); !it.Done(); it.Next()) {
      word_part_t* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("parts"), x0));
  }

  return out_node;
}

BigStr* loc_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case loc_e::Missing:
    v = "Missing"; break;
  case loc_e::Token:
    v = "Token"; break;
  case loc_e::ArgWord:
    v = "ArgWord"; break;
  case loc_e::WordPart:
    v = "WordPart"; break;
  case loc_e::Word:
    v = "Word"; break;
  case loc_e::Arith:
    v = "Arith"; break;
  case loc_e::Command:
    v = "Command"; break;
  case loc_e::TokenTooLong:
    v = "TokenTooLong"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "loc.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

loc__Missing* loc::Missing = &gloc__Missing.obj;

GcGlobal<loc__Missing> gloc__Missing = 
  { ObjHeader::Global(loc_e::Missing) };

hnode_t* loc__Missing::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(loc_str(this->tag()));
  return out_node;
}


hnode_t* loc__WordPart::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(loc_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->p->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("p"), x0));

  return out_node;
}


hnode_t* loc__Word::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(loc_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->w->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("w"), x0));

  return out_node;
}


hnode_t* loc__Arith::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(loc_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->a->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("a"), x0));

  return out_node;
}


hnode_t* loc__Command::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(loc_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->c->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("c"), x0));

  return out_node;
}


hnode_t* loc__TokenTooLong::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(loc_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->line->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("line"), x0));

  hnode_t* x1 = Alloc<hnode::Leaf>(Id_str(this->id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("id"), x1));

  hnode_t* x2 = Alloc<hnode::Leaf>(str(this->length), color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("length"), x2));

  hnode_t* x3 = Alloc<hnode::Leaf>(str(this->col), color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("col"), x3));

  return out_node;
}


hnode_t* loc_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case loc_e::Missing: {
    loc__Missing* obj = static_cast<loc__Missing*>(this);
    return obj->PrettyTree(seen);
  }
  case loc_e::Token: {
    Token* obj = static_cast<Token*>(this);
    return obj->PrettyTree(seen);
  }
  case loc_e::ArgWord: {
    CompoundWord* obj = static_cast<CompoundWord*>(this);
    return obj->PrettyTree(seen);
  }
  case loc_e::WordPart: {
    loc__WordPart* obj = static_cast<loc__WordPart*>(this);
    return obj->PrettyTree(seen);
  }
  case loc_e::Word: {
    loc__Word* obj = static_cast<loc__Word*>(this);
    return obj->PrettyTree(seen);
  }
  case loc_e::Arith: {
    loc__Arith* obj = static_cast<loc__Arith*>(this);
    return obj->PrettyTree(seen);
  }
  case loc_e::Command: {
    loc__Command* obj = static_cast<loc__Command*>(this);
    return obj->PrettyTree(seen);
  }
  case loc_e::TokenTooLong: {
    loc__TokenTooLong* obj = static_cast<loc__TokenTooLong*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* debug_frame_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case debug_frame_e::Main:
    v = "Main"; break;
  case debug_frame_e::Source:
    v = "Source"; break;
  case debug_frame_e::Call:
    v = "Call"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "debug_frame.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* debug_frame__Main::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(debug_frame_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = runtime::NewLeaf(this->dollar0, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("dollar0"), x0));

  return out_node;
}


hnode_t* debug_frame__Source::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(debug_frame_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->call_tok) {  // Optional
    hnode_t* x0 = this->call_tok->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("call_tok"), x0));
  }

  hnode_t* x1 = runtime::NewLeaf(this->source_name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("source_name"), x1));

  return out_node;
}


hnode_t* debug_frame__Call::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(debug_frame_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->call_tok) {  // Optional
    hnode_t* x0 = this->call_tok->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("call_tok"), x0));
  }

  hnode_t* x1 = this->def_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("def_tok"), x1));

  hnode_t* x2 = runtime::NewLeaf(this->func_name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("func_name"), x2));

  return out_node;
}


hnode_t* debug_frame_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case debug_frame_e::Main: {
    debug_frame__Main* obj = static_cast<debug_frame__Main*>(this);
    return obj->PrettyTree(seen);
  }
  case debug_frame_e::Source: {
    debug_frame__Source* obj = static_cast<debug_frame__Source*>(this);
    return obj->PrettyTree(seen);
  }
  case debug_frame_e::Call: {
    debug_frame__Call* obj = static_cast<debug_frame__Call*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* bracket_op_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case bracket_op_e::WholeArray:
    v = "WholeArray"; break;
  case bracket_op_e::ArrayIndex:
    v = "ArrayIndex"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "bracket_op.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* bracket_op__WholeArray::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(bracket_op_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(Id_str(this->op_id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("op_id"), x0));

  return out_node;
}


hnode_t* bracket_op__ArrayIndex::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(bracket_op_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->expr->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("expr"), x0));

  return out_node;
}


hnode_t* bracket_op_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case bracket_op_e::WholeArray: {
    bracket_op__WholeArray* obj = static_cast<bracket_op__WholeArray*>(this);
    return obj->PrettyTree(seen);
  }
  case bracket_op_e::ArrayIndex: {
    bracket_op__ArrayIndex* obj = static_cast<bracket_op__ArrayIndex*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* suffix_op_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case suffix_op_e::Nullary:
    v = "Nullary"; break;
  case suffix_op_e::Unary:
    v = "Unary"; break;
  case suffix_op_e::Static:
    v = "Static"; break;
  case suffix_op_e::PatSub:
    v = "PatSub"; break;
  case suffix_op_e::Slice:
    v = "Slice"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "suffix_op.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* suffix_op__Unary::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(suffix_op_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x0));

  hnode_t* x1 = this->arg_word->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("arg_word"), x1));

  return out_node;
}


hnode_t* suffix_op__Static::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(suffix_op_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("tok"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->arg, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("arg"), x1));

  return out_node;
}


hnode_t* suffix_op__PatSub::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(suffix_op_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->pat->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("pat"), x0));

  hnode_t* x1 = this->replace->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("replace"), x1));

  hnode_t* x2 = Alloc<hnode::Leaf>(Id_str(this->replace_mode),
                                   color_e::UserType);
  L->append(Alloc<Field>(StrFromC("replace_mode"), x2));

  hnode_t* x3 = this->slash_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("slash_tok"), x3));

  return out_node;
}


hnode_t* suffix_op__Slice::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(suffix_op_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->begin->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("begin"), x0));

  if (this->length) {  // Optional
    hnode_t* x1 = this->length->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("length"), x1));
  }

  return out_node;
}


hnode_t* suffix_op_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case suffix_op_e::Nullary: {
    Token* obj = static_cast<Token*>(this);
    return obj->PrettyTree(seen);
  }
  case suffix_op_e::Unary: {
    suffix_op__Unary* obj = static_cast<suffix_op__Unary*>(this);
    return obj->PrettyTree(seen);
  }
  case suffix_op_e::Static: {
    suffix_op__Static* obj = static_cast<suffix_op__Static*>(this);
    return obj->PrettyTree(seen);
  }
  case suffix_op_e::PatSub: {
    suffix_op__PatSub* obj = static_cast<suffix_op__PatSub*>(this);
    return obj->PrettyTree(seen);
  }
  case suffix_op_e::Slice: {
    suffix_op__Slice* obj = static_cast<suffix_op__Slice*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}

hnode_t* BracedVarSub::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("BracedVarSub"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->token->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("token"), x1));

  hnode_t* x2 = runtime::NewLeaf(this->var_name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("var_name"), x2));

  if (this->prefix_op) {  // Optional
    hnode_t* x3 = this->prefix_op->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("prefix_op"), x3));
  }

  if (this->bracket_op) {  // Optional
    hnode_t* x4 = this->bracket_op->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("bracket_op"), x4));
  }

  if (this->suffix_op) {  // Optional
    hnode_t* x5 = this->suffix_op->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("suffix_op"), x5));
  }

  hnode_t* x6 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x6));

  return out_node;
}


hnode_t* DoubleQuoted::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("DoubleQuoted"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->parts != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<word_part_t*> it(this->parts); !it.Done(); it.Next()) {
      word_part_t* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("parts"), x1));
  }

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* SingleQuoted::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("SingleQuoted"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->sval, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("sval"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* SimpleVarSub::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("SimpleVarSub"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("tok"), x0));

  return out_node;
}


hnode_t* CommandSub::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("CommandSub"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left_token->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left_token"), x0));

  hnode_t* x1 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* ShArrayLiteral::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("ShArrayLiteral"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->words != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<word_t*> it(this->words); !it.Done(); it.Next()) {
      word_t* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("words"), x1));
  }

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* ArgList::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("ArgList"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->pos_args != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<expr_t*> it(this->pos_args); !it.Done(); it.Next()) {
      expr_t* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("pos_args"), x1));
  }

  if (this->semi_tok) {  // Optional
    hnode_t* x2 = this->semi_tok->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("semi_tok"), x2));
  }

  if (this->named_args != nullptr) {  // List
    hnode::Array* x3 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<NamedArg*> it(this->named_args); !it.Done(); it.Next()) {
      NamedArg* i3 = it.Value();
      hnode_t* h = (i3 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i3->PrettyTree(seen);
      x3->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("named_args"), x3));
  }

  if (this->semi_tok2) {  // Optional
    hnode_t* x4 = this->semi_tok2->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("semi_tok2"), x4));
  }

  if (this->block_expr) {  // Optional
    hnode_t* x5 = this->block_expr->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("block_expr"), x5));
  }

  hnode_t* x6 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x6));

  return out_node;
}


hnode_t* AssocPair::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("AssocPair"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->key->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("key"), x0));

  hnode_t* x1 = this->value->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("value"), x1));

  return out_node;
}

BigStr* word_part_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case word_part_e::ShArrayLiteral:
    v = "ShArrayLiteral"; break;
  case word_part_e::BashAssocLiteral:
    v = "BashAssocLiteral"; break;
  case word_part_e::Literal:
    v = "Literal"; break;
  case word_part_e::EscapedLiteral:
    v = "EscapedLiteral"; break;
  case word_part_e::SingleQuoted:
    v = "SingleQuoted"; break;
  case word_part_e::DoubleQuoted:
    v = "DoubleQuoted"; break;
  case word_part_e::SimpleVarSub:
    v = "SimpleVarSub"; break;
  case word_part_e::BracedVarSub:
    v = "BracedVarSub"; break;
  case word_part_e::ZshVarSub:
    v = "ZshVarSub"; break;
  case word_part_e::CommandSub:
    v = "CommandSub"; break;
  case word_part_e::TildeSub:
    v = "TildeSub"; break;
  case word_part_e::ArithSub:
    v = "ArithSub"; break;
  case word_part_e::BracedTuple:
    v = "BracedTuple"; break;
  case word_part_e::BracedRange:
    v = "BracedRange"; break;
  case word_part_e::ExtGlob:
    v = "ExtGlob"; break;
  case word_part_e::BashRegexGroup:
    v = "BashRegexGroup"; break;
  case word_part_e::Splice:
    v = "Splice"; break;
  case word_part_e::ExprSub:
    v = "ExprSub"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "word_part.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* word_part__BashAssocLiteral::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->pairs != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<AssocPair*> it(this->pairs); !it.Done(); it.Next()) {
      AssocPair* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("pairs"), x1));
  }

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* word_part__EscapedLiteral::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->token->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("token"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->ch, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("ch"), x1));

  return out_node;
}


hnode_t* word_part__ZshVarSub::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->ignored->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("ignored"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* word_part__TildeSub::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->name) {  // Optional
    hnode_t* x1 = this->name->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("name"), x1));
  }

  if (this->user_name) {  // Optional
    hnode_t* x2 = runtime::NewLeaf(this->user_name, color_e::StringConst);
    L->append(Alloc<Field>(StrFromC("user_name"), x2));
  }

  return out_node;
}


hnode_t* word_part__ArithSub::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->anode->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("anode"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* word_part__BracedTuple::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->words != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<CompoundWord*> it(this->words); !it.Done(); it.Next()) {
      CompoundWord* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("words"), x0));
  }

  return out_node;
}


hnode_t* word_part__BracedRange::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->blame_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("blame_tok"), x0));

  hnode_t* x1 = Alloc<hnode::Leaf>(Id_str(this->kind), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("kind"), x1));

  hnode_t* x2 = runtime::NewLeaf(this->start, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("start"), x2));

  hnode_t* x3 = runtime::NewLeaf(this->end, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("end"), x3));

  hnode_t* x4 = Alloc<hnode::Leaf>(str(this->step), color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("step"), x4));

  return out_node;
}


hnode_t* word_part__ExtGlob::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x0));

  if (this->arms != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<CompoundWord*> it(this->arms); !it.Done(); it.Next()) {
      CompoundWord* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("arms"), x1));
  }

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* word_part__BashRegexGroup::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->child) {  // Optional
    hnode_t* x1 = this->child->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("child"), x1));
  }

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* word_part__Splice::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->blame_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("blame_tok"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->var_name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("var_name"), x1));

  return out_node;
}


hnode_t* word_part__ExprSub::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* word_part_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case word_part_e::ShArrayLiteral: {
    ShArrayLiteral* obj = static_cast<ShArrayLiteral*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::BashAssocLiteral: {
    word_part__BashAssocLiteral* obj =
static_cast<word_part__BashAssocLiteral*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::Literal: {
    Token* obj = static_cast<Token*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::EscapedLiteral: {
    word_part__EscapedLiteral* obj =
static_cast<word_part__EscapedLiteral*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::SingleQuoted: {
    SingleQuoted* obj = static_cast<SingleQuoted*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::DoubleQuoted: {
    DoubleQuoted* obj = static_cast<DoubleQuoted*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::SimpleVarSub: {
    SimpleVarSub* obj = static_cast<SimpleVarSub*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::BracedVarSub: {
    BracedVarSub* obj = static_cast<BracedVarSub*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::ZshVarSub: {
    word_part__ZshVarSub* obj = static_cast<word_part__ZshVarSub*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::CommandSub: {
    CommandSub* obj = static_cast<CommandSub*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::TildeSub: {
    word_part__TildeSub* obj = static_cast<word_part__TildeSub*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::ArithSub: {
    word_part__ArithSub* obj = static_cast<word_part__ArithSub*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::BracedTuple: {
    word_part__BracedTuple* obj = static_cast<word_part__BracedTuple*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::BracedRange: {
    word_part__BracedRange* obj = static_cast<word_part__BracedRange*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::ExtGlob: {
    word_part__ExtGlob* obj = static_cast<word_part__ExtGlob*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::BashRegexGroup: {
    word_part__BashRegexGroup* obj =
static_cast<word_part__BashRegexGroup*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::Splice: {
    word_part__Splice* obj = static_cast<word_part__Splice*>(this);
    return obj->PrettyTree(seen);
  }
  case word_part_e::ExprSub: {
    word_part__ExprSub* obj = static_cast<word_part__ExprSub*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* rhs_word_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case rhs_word_e::Empty:
    v = "Empty"; break;
  case rhs_word_e::Compound:
    v = "Compound"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "rhs_word.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

rhs_word__Empty* rhs_word::Empty = &grhs_word__Empty.obj;

GcGlobal<rhs_word__Empty> grhs_word__Empty = 
  { ObjHeader::Global(rhs_word_e::Empty) };

hnode_t* rhs_word__Empty::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(rhs_word_str(this->tag()));
  return out_node;
}


hnode_t* rhs_word_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case rhs_word_e::Empty: {
    rhs_word__Empty* obj = static_cast<rhs_word__Empty*>(this);
    return obj->PrettyTree(seen);
  }
  case rhs_word_e::Compound: {
    CompoundWord* obj = static_cast<CompoundWord*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* word_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case word_e::Operator:
    v = "Operator"; break;
  case word_e::Compound:
    v = "Compound"; break;
  case word_e::BracedTree:
    v = "BracedTree"; break;
  case word_e::String:
    v = "String"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "word.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* word__BracedTree::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->parts != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<word_part_t*> it(this->parts); !it.Done(); it.Next()) {
      word_part_t* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("parts"), x0));
  }

  return out_node;
}


hnode_t* word__String::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(word_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(Id_str(this->id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("id"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->s, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("s"), x1));

  if (this->blame_loc) {  // Optional
    hnode_t* x2 = this->blame_loc->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("blame_loc"), x2));
  }

  return out_node;
}


hnode_t* word_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case word_e::Operator: {
    Token* obj = static_cast<Token*>(this);
    return obj->PrettyTree(seen);
  }
  case word_e::Compound: {
    CompoundWord* obj = static_cast<CompoundWord*>(this);
    return obj->PrettyTree(seen);
  }
  case word_e::BracedTree: {
    word__BracedTree* obj = static_cast<word__BracedTree*>(this);
    return obj->PrettyTree(seen);
  }
  case word_e::String: {
    word__String* obj = static_cast<word__String*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* sh_lhs_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case sh_lhs_e::Name:
    v = "Name"; break;
  case sh_lhs_e::IndexedName:
    v = "IndexedName"; break;
  case sh_lhs_e::UnparsedIndex:
    v = "UnparsedIndex"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "sh_lhs.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* sh_lhs__Name::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(sh_lhs_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  return out_node;
}


hnode_t* sh_lhs__IndexedName::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(sh_lhs_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  hnode_t* x2 = this->index->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("index"), x2));

  return out_node;
}


hnode_t* sh_lhs__UnparsedIndex::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(sh_lhs_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  hnode_t* x2 = runtime::NewLeaf(this->index, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("index"), x2));

  return out_node;
}


hnode_t* sh_lhs_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case sh_lhs_e::Name: {
    sh_lhs__Name* obj = static_cast<sh_lhs__Name*>(this);
    return obj->PrettyTree(seen);
  }
  case sh_lhs_e::IndexedName: {
    sh_lhs__IndexedName* obj = static_cast<sh_lhs__IndexedName*>(this);
    return obj->PrettyTree(seen);
  }
  case sh_lhs_e::UnparsedIndex: {
    sh_lhs__UnparsedIndex* obj = static_cast<sh_lhs__UnparsedIndex*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* arith_expr_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case arith_expr_e::EmptyZero:
    v = "EmptyZero"; break;
  case arith_expr_e::EmptyOne:
    v = "EmptyOne"; break;
  case arith_expr_e::VarSub:
    v = "VarSub"; break;
  case arith_expr_e::Word:
    v = "Word"; break;
  case arith_expr_e::UnaryAssign:
    v = "UnaryAssign"; break;
  case arith_expr_e::BinaryAssign:
    v = "BinaryAssign"; break;
  case arith_expr_e::Unary:
    v = "Unary"; break;
  case arith_expr_e::Binary:
    v = "Binary"; break;
  case arith_expr_e::TernaryOp:
    v = "TernaryOp"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "arith_expr.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

arith_expr__EmptyZero* arith_expr::EmptyZero = &garith_expr__EmptyZero.obj;

GcGlobal<arith_expr__EmptyZero> garith_expr__EmptyZero = 
  { ObjHeader::Global(arith_expr_e::EmptyZero) };

arith_expr__EmptyOne* arith_expr::EmptyOne = &garith_expr__EmptyOne.obj;

GcGlobal<arith_expr__EmptyOne> garith_expr__EmptyOne = 
  { ObjHeader::Global(arith_expr_e::EmptyOne) };

hnode_t* arith_expr__EmptyZero::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(arith_expr_str(this->tag()));
  return out_node;
}


hnode_t* arith_expr__EmptyOne::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(arith_expr_str(this->tag()));
  return out_node;
}


hnode_t* arith_expr__UnaryAssign::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(arith_expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(Id_str(this->op_id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("op_id"), x0));

  hnode_t* x1 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x1));

  return out_node;
}


hnode_t* arith_expr__BinaryAssign::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(arith_expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(Id_str(this->op_id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("op_id"), x0));

  hnode_t* x1 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* arith_expr__Unary::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(arith_expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(Id_str(this->op_id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("op_id"), x0));

  hnode_t* x1 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x1));

  return out_node;
}


hnode_t* arith_expr__Binary::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(arith_expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x0));

  hnode_t* x1 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* arith_expr__TernaryOp::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(arith_expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->cond->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("cond"), x0));

  hnode_t* x1 = this->true_expr->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("true_expr"), x1));

  hnode_t* x2 = this->false_expr->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("false_expr"), x2));

  return out_node;
}


hnode_t* arith_expr_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case arith_expr_e::EmptyZero: {
    arith_expr__EmptyZero* obj = static_cast<arith_expr__EmptyZero*>(this);
    return obj->PrettyTree(seen);
  }
  case arith_expr_e::EmptyOne: {
    arith_expr__EmptyOne* obj = static_cast<arith_expr__EmptyOne*>(this);
    return obj->PrettyTree(seen);
  }
  case arith_expr_e::VarSub: {
    Token* obj = static_cast<Token*>(this);
    return obj->PrettyTree(seen);
  }
  case arith_expr_e::Word: {
    CompoundWord* obj = static_cast<CompoundWord*>(this);
    return obj->PrettyTree(seen);
  }
  case arith_expr_e::UnaryAssign: {
    arith_expr__UnaryAssign* obj = static_cast<arith_expr__UnaryAssign*>(this);
    return obj->PrettyTree(seen);
  }
  case arith_expr_e::BinaryAssign: {
    arith_expr__BinaryAssign* obj =
static_cast<arith_expr__BinaryAssign*>(this);
    return obj->PrettyTree(seen);
  }
  case arith_expr_e::Unary: {
    arith_expr__Unary* obj = static_cast<arith_expr__Unary*>(this);
    return obj->PrettyTree(seen);
  }
  case arith_expr_e::Binary: {
    arith_expr__Binary* obj = static_cast<arith_expr__Binary*>(this);
    return obj->PrettyTree(seen);
  }
  case arith_expr_e::TernaryOp: {
    arith_expr__TernaryOp* obj = static_cast<arith_expr__TernaryOp*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* bool_expr_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case bool_expr_e::WordTest:
    v = "WordTest"; break;
  case bool_expr_e::Binary:
    v = "Binary"; break;
  case bool_expr_e::Unary:
    v = "Unary"; break;
  case bool_expr_e::LogicalNot:
    v = "LogicalNot"; break;
  case bool_expr_e::LogicalAnd:
    v = "LogicalAnd"; break;
  case bool_expr_e::LogicalOr:
    v = "LogicalOr"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "bool_expr.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* bool_expr__WordTest::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(bool_expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->w->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("w"), x0));

  return out_node;
}


hnode_t* bool_expr__Binary::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(bool_expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(Id_str(this->op_id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("op_id"), x0));

  hnode_t* x1 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* bool_expr__Unary::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(bool_expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(Id_str(this->op_id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("op_id"), x0));

  hnode_t* x1 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x1));

  return out_node;
}


hnode_t* bool_expr__LogicalNot::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(bool_expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x0));

  return out_node;
}


hnode_t* bool_expr__LogicalAnd::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(bool_expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x1));

  return out_node;
}


hnode_t* bool_expr__LogicalOr::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(bool_expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x1));

  return out_node;
}


hnode_t* bool_expr_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case bool_expr_e::WordTest: {
    bool_expr__WordTest* obj = static_cast<bool_expr__WordTest*>(this);
    return obj->PrettyTree(seen);
  }
  case bool_expr_e::Binary: {
    bool_expr__Binary* obj = static_cast<bool_expr__Binary*>(this);
    return obj->PrettyTree(seen);
  }
  case bool_expr_e::Unary: {
    bool_expr__Unary* obj = static_cast<bool_expr__Unary*>(this);
    return obj->PrettyTree(seen);
  }
  case bool_expr_e::LogicalNot: {
    bool_expr__LogicalNot* obj = static_cast<bool_expr__LogicalNot*>(this);
    return obj->PrettyTree(seen);
  }
  case bool_expr_e::LogicalAnd: {
    bool_expr__LogicalAnd* obj = static_cast<bool_expr__LogicalAnd*>(this);
    return obj->PrettyTree(seen);
  }
  case bool_expr_e::LogicalOr: {
    bool_expr__LogicalOr* obj = static_cast<bool_expr__LogicalOr*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* redir_loc_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case redir_loc_e::Fd:
    v = "Fd"; break;
  case redir_loc_e::VarName:
    v = "VarName"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "redir_loc.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* redir_loc__Fd::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(redir_loc_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(str(this->fd), color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("fd"), x0));

  return out_node;
}


hnode_t* redir_loc__VarName::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(redir_loc_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x0));

  return out_node;
}


hnode_t* redir_loc_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case redir_loc_e::Fd: {
    redir_loc__Fd* obj = static_cast<redir_loc__Fd*>(this);
    return obj->PrettyTree(seen);
  }
  case redir_loc_e::VarName: {
    redir_loc__VarName* obj = static_cast<redir_loc__VarName*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* redir_param_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case redir_param_e::Word:
    v = "Word"; break;
  case redir_param_e::HereDoc:
    v = "HereDoc"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "redir_param.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* redir_param__HereDoc::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(redir_param_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->here_begin->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("here_begin"), x0));

  if (this->here_end_tok) {  // Optional
    hnode_t* x1 = this->here_end_tok->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("here_end_tok"), x1));
  }

  if (this->stdin_parts != nullptr) {  // List
    hnode::Array* x2 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<word_part_t*> it(this->stdin_parts); !it.Done(); it.Next()) {
      word_part_t* i2 = it.Value();
      hnode_t* h = (i2 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i2->PrettyTree(seen);
      x2->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("stdin_parts"), x2));
  }

  return out_node;
}


hnode_t* redir_param_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case redir_param_e::Word: {
    CompoundWord* obj = static_cast<CompoundWord*>(this);
    return obj->PrettyTree(seen);
  }
  case redir_param_e::HereDoc: {
    redir_param__HereDoc* obj = static_cast<redir_param__HereDoc*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}

hnode_t* Redir::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("Redir"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x0));

  hnode_t* x1 = this->loc->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("loc"), x1));

  hnode_t* x2 = this->arg->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("arg"), x2));

  return out_node;
}

BigStr* assign_op_str(assign_op_e tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case assign_op_e::Equal:
    v = "Equal"; break;
  case assign_op_e::PlusEqual:
    v = "PlusEqual"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "assign_op.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* AssignPair::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("AssignPair"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->lhs->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("lhs"), x1));

  hnode_t* x2 = Alloc<hnode::Leaf>(assign_op_str(this->op), color_e::TypeName);
  L->append(Alloc<Field>(StrFromC("op"), x2));

  hnode_t* x3 = this->rhs->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("rhs"), x3));

  return out_node;
}


hnode_t* EnvPair::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("EnvPair"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  hnode_t* x2 = this->val->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("val"), x2));

  return out_node;
}

BigStr* condition_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case condition_e::Shell:
    v = "Shell"; break;
  case condition_e::YshExpr:
    v = "YshExpr"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "condition.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* condition__Shell::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(condition_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->commands != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<command_t*> it(this->commands); !it.Done(); it.Next()) {
      command_t* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("commands"), x0));
  }

  return out_node;
}


hnode_t* condition__YshExpr::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(condition_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->e->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("e"), x0));

  return out_node;
}


hnode_t* condition_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case condition_e::Shell: {
    condition__Shell* obj = static_cast<condition__Shell*>(this);
    return obj->PrettyTree(seen);
  }
  case condition_e::YshExpr: {
    condition__YshExpr* obj = static_cast<condition__YshExpr*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}

hnode_t* CaseArm::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("CaseArm"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->pattern->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("pattern"), x1));

  hnode_t* x2 = this->middle->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("middle"), x2));

  if (this->action != nullptr) {  // List
    hnode::Array* x3 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<command_t*> it(this->action); !it.Done(); it.Next()) {
      command_t* i3 = it.Value();
      hnode_t* h = (i3 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i3->PrettyTree(seen);
      x3->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("action"), x3));
  }

  if (this->right) {  // Optional
    hnode_t* x4 = this->right->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("right"), x4));
  }

  return out_node;
}

BigStr* case_arg_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case case_arg_e::Word:
    v = "Word"; break;
  case case_arg_e::YshExpr:
    v = "YshExpr"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "case_arg.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* case_arg__Word::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(case_arg_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->w->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("w"), x0));

  return out_node;
}


hnode_t* case_arg__YshExpr::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(case_arg_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->e->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("e"), x0));

  return out_node;
}


hnode_t* case_arg_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case case_arg_e::Word: {
    case_arg__Word* obj = static_cast<case_arg__Word*>(this);
    return obj->PrettyTree(seen);
  }
  case case_arg_e::YshExpr: {
    case_arg__YshExpr* obj = static_cast<case_arg__YshExpr*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}

hnode_t* EggexFlag::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("EggexFlag"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(this->negated ? runtime::TRUE_STR :
                                   runtime::FALSE_STR, color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("negated"), x0));

  hnode_t* x1 = this->flag->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("flag"), x1));

  return out_node;
}


hnode_t* Eggex::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("Eggex"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->regex->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("regex"), x1));

  if (this->flags != nullptr) {  // List
    hnode::Array* x2 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<EggexFlag*> it(this->flags); !it.Done(); it.Next()) {
      EggexFlag* i2 = it.Value();
      hnode_t* h = (i2 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i2->PrettyTree(seen);
      x2->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("flags"), x2));
  }

  if (this->trans_pref) {  // Optional
    hnode_t* x3 = this->trans_pref->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("trans_pref"), x3));
  }

  if (this->canonical_flags) {  // Optional
    hnode_t* x4 = runtime::NewLeaf(this->canonical_flags, color_e::StringConst);
    L->append(Alloc<Field>(StrFromC("canonical_flags"), x4));
  }

  return out_node;
}

BigStr* pat_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case pat_e::Else:
    v = "Else"; break;
  case pat_e::Words:
    v = "Words"; break;
  case pat_e::YshExprs:
    v = "YshExprs"; break;
  case pat_e::Eggex:
    v = "Eggex"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "pat.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

pat__Else* pat::Else = &gpat__Else.obj;

GcGlobal<pat__Else> gpat__Else = 
  { ObjHeader::Global(pat_e::Else) };

hnode_t* pat__Else::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(pat_str(this->tag()));
  return out_node;
}


hnode_t* pat__Words::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(pat_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->words != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<word_t*> it(this->words); !it.Done(); it.Next()) {
      word_t* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("words"), x0));
  }

  return out_node;
}


hnode_t* pat__YshExprs::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(pat_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->exprs != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<expr_t*> it(this->exprs); !it.Done(); it.Next()) {
      expr_t* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("exprs"), x0));
  }

  return out_node;
}


hnode_t* pat_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case pat_e::Else: {
    pat__Else* obj = static_cast<pat__Else*>(this);
    return obj->PrettyTree(seen);
  }
  case pat_e::Words: {
    pat__Words* obj = static_cast<pat__Words*>(this);
    return obj->PrettyTree(seen);
  }
  case pat_e::YshExprs: {
    pat__YshExprs* obj = static_cast<pat__YshExprs*>(this);
    return obj->PrettyTree(seen);
  }
  case pat_e::Eggex: {
    Eggex* obj = static_cast<Eggex*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}

hnode_t* IfArm::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("IfArm"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->keyword->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("keyword"), x0));

  hnode_t* x1 = this->cond->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("cond"), x1));

  if (this->then_kw) {  // Optional
    hnode_t* x2 = this->then_kw->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("then_kw"), x2));
  }

  if (this->action != nullptr) {  // List
    hnode::Array* x3 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<command_t*> it(this->action); !it.Done(); it.Next()) {
      command_t* i3 = it.Value();
      hnode_t* h = (i3 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i3->PrettyTree(seen);
      x3->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("action"), x3));
  }

  if (this->then_tok) {  // Optional
    hnode_t* x4 = this->then_tok->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("then_tok"), x4));
  }

  return out_node;
}

BigStr* for_iter_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case for_iter_e::Args:
    v = "Args"; break;
  case for_iter_e::Words:
    v = "Words"; break;
  case for_iter_e::YshExpr:
    v = "YshExpr"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "for_iter.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

for_iter__Args* for_iter::Args = &gfor_iter__Args.obj;

GcGlobal<for_iter__Args> gfor_iter__Args = 
  { ObjHeader::Global(for_iter_e::Args) };

hnode_t* for_iter__Args::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(for_iter_str(this->tag()));
  return out_node;
}


hnode_t* for_iter__Words::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(for_iter_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->words != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<word_t*> it(this->words); !it.Done(); it.Next()) {
      word_t* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("words"), x0));
  }

  return out_node;
}


hnode_t* for_iter__YshExpr::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(for_iter_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->e->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("e"), x0));

  hnode_t* x1 = this->blame->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("blame"), x1));

  return out_node;
}


hnode_t* for_iter_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case for_iter_e::Args: {
    for_iter__Args* obj = static_cast<for_iter__Args*>(this);
    return obj->PrettyTree(seen);
  }
  case for_iter_e::Words: {
    for_iter__Words* obj = static_cast<for_iter__Words*>(this);
    return obj->PrettyTree(seen);
  }
  case for_iter_e::YshExpr: {
    for_iter__YshExpr* obj = static_cast<for_iter__YshExpr*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}

hnode_t* BraceGroup::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("BraceGroup"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->doc_token) {  // Optional
    hnode_t* x1 = this->doc_token->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("doc_token"), x1));
  }

  if (this->children != nullptr) {  // List
    hnode::Array* x2 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<command_t*> it(this->children); !it.Done(); it.Next()) {
      command_t* i2 = it.Value();
      hnode_t* h = (i2 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i2->PrettyTree(seen);
      x2->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("children"), x2));
  }

  hnode_t* x3 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x3));

  return out_node;
}


hnode_t* Param::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("Param"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->blame_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("blame_tok"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  if (this->type) {  // Optional
    hnode_t* x2 = this->type->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("type"), x2));
  }

  if (this->default_val) {  // Optional
    hnode_t* x3 = this->default_val->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("default_val"), x3));
  }

  return out_node;
}


hnode_t* RestParam::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("RestParam"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->blame_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("blame_tok"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  return out_node;
}


hnode_t* ParamGroup::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("ParamGroup"));
  List<Field*>* L = out_node->fields;

  if (this->params != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<Param*> it(this->params); !it.Done(); it.Next()) {
      Param* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("params"), x0));
  }

  if (this->rest_of) {  // Optional
    hnode_t* x1 = this->rest_of->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("rest_of"), x1));
  }

  return out_node;
}

BigStr* proc_sig_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case proc_sig_e::Open:
    v = "Open"; break;
  case proc_sig_e::Closed:
    v = "Closed"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "proc_sig.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

proc_sig__Open* proc_sig::Open = &gproc_sig__Open.obj;

GcGlobal<proc_sig__Open> gproc_sig__Open = 
  { ObjHeader::Global(proc_sig_e::Open) };

hnode_t* proc_sig__Open::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(proc_sig_str(this->tag()));
  return out_node;
}


hnode_t* proc_sig__Closed::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(proc_sig_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->word) {  // Optional
    hnode_t* x0 = this->word->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("word"), x0));
  }

  if (this->positional) {  // Optional
    hnode_t* x1 = this->positional->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("positional"), x1));
  }

  if (this->named) {  // Optional
    hnode_t* x2 = this->named->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("named"), x2));
  }

  if (this->block_param) {  // Optional
    hnode_t* x3 = this->block_param->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("block_param"), x3));
  }

  return out_node;
}


hnode_t* proc_sig_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case proc_sig_e::Open: {
    proc_sig__Open* obj = static_cast<proc_sig__Open*>(this);
    return obj->PrettyTree(seen);
  }
  case proc_sig_e::Closed: {
    proc_sig__Closed* obj = static_cast<proc_sig__Closed*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}

hnode_t* Proc::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("Proc"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->keyword->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("keyword"), x0));

  hnode_t* x1 = this->name->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  hnode_t* x2 = this->sig->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("sig"), x2));

  hnode_t* x3 = this->body->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("body"), x3));

  return out_node;
}


hnode_t* Func::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("Func"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->keyword->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("keyword"), x0));

  hnode_t* x1 = this->name->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  if (this->positional) {  // Optional
    hnode_t* x2 = this->positional->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("positional"), x2));
  }

  if (this->named) {  // Optional
    hnode_t* x3 = this->named->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("named"), x3));
  }

  hnode_t* x4 = this->body->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("body"), x4));

  return out_node;
}


hnode_t* LiteralBlock::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("LiteralBlock"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->brace_group->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("brace_group"), x0));

  if (this->lines != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<SourceLine*> it(this->lines); !it.Done(); it.Next()) {
      SourceLine* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("lines"), x1));
  }

  return out_node;
}


hnode_t* ParsedAssignment::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("ParsedAssignment"));
  List<Field*>* L = out_node->fields;

  if (this->left) {  // Optional
    hnode_t* x0 = this->left->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("left"), x0));
  }

  if (this->close) {  // Optional
    hnode_t* x1 = this->close->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("close"), x1));
  }

  hnode_t* x2 = Alloc<hnode::Leaf>(str(this->part_offset), color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("part_offset"), x2));

  hnode_t* x3 = this->w->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("w"), x3));

  return out_node;
}

BigStr* command_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case command_e::NoOp:
    v = "NoOp"; break;
  case command_e::Redirect:
    v = "Redirect"; break;
  case command_e::Simple:
    v = "Simple"; break;
  case command_e::ExpandedAlias:
    v = "ExpandedAlias"; break;
  case command_e::Sentence:
    v = "Sentence"; break;
  case command_e::ShAssignment:
    v = "ShAssignment"; break;
  case command_e::ControlFlow:
    v = "ControlFlow"; break;
  case command_e::Pipeline:
    v = "Pipeline"; break;
  case command_e::AndOr:
    v = "AndOr"; break;
  case command_e::DoGroup:
    v = "DoGroup"; break;
  case command_e::BraceGroup:
    v = "BraceGroup"; break;
  case command_e::Subshell:
    v = "Subshell"; break;
  case command_e::DParen:
    v = "DParen"; break;
  case command_e::DBracket:
    v = "DBracket"; break;
  case command_e::ForEach:
    v = "ForEach"; break;
  case command_e::ForExpr:
    v = "ForExpr"; break;
  case command_e::WhileUntil:
    v = "WhileUntil"; break;
  case command_e::If:
    v = "If"; break;
  case command_e::Case:
    v = "Case"; break;
  case command_e::ShFunction:
    v = "ShFunction"; break;
  case command_e::TimeBlock:
    v = "TimeBlock"; break;
  case command_e::CommandList:
    v = "CommandList"; break;
  case command_e::VarDecl:
    v = "VarDecl"; break;
  case command_e::BareDecl:
    v = "BareDecl"; break;
  case command_e::Mutation:
    v = "Mutation"; break;
  case command_e::Expr:
    v = "Expr"; break;
  case command_e::Proc:
    v = "Proc"; break;
  case command_e::Func:
    v = "Func"; break;
  case command_e::Retval:
    v = "Retval"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "command.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

command__NoOp* command::NoOp = &gcommand__NoOp.obj;

GcGlobal<command__NoOp> gcommand__NoOp = 
  { ObjHeader::Global(command_e::NoOp) };

hnode_t* command__NoOp::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  return out_node;
}


hnode_t* command__Redirect::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x0));

  if (this->redirects != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<Redir*> it(this->redirects); !it.Done(); it.Next()) {
      Redir* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("redirects"), x1));
  }

  return out_node;
}


hnode_t* command__Simple::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->blame_tok) {  // Optional
    hnode_t* x0 = this->blame_tok->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("blame_tok"), x0));
  }

  if (this->more_env != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<EnvPair*> it(this->more_env); !it.Done(); it.Next()) {
      EnvPair* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("more_env"), x1));
  }

  if (this->words != nullptr) {  // List
    hnode::Array* x2 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<word_t*> it(this->words); !it.Done(); it.Next()) {
      word_t* i2 = it.Value();
      hnode_t* h = (i2 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i2->PrettyTree(seen);
      x2->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("words"), x2));
  }

  if (this->typed_args) {  // Optional
    hnode_t* x3 = this->typed_args->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("typed_args"), x3));
  }

  if (this->block) {  // Optional
    hnode_t* x4 = this->block->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("block"), x4));
  }

  hnode_t* x5 = Alloc<hnode::Leaf>(this->is_last_cmd ? runtime::TRUE_STR :
                                   runtime::FALSE_STR, color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("is_last_cmd"), x5));

  return out_node;
}


hnode_t* command__ExpandedAlias::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x0));

  if (this->more_env != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<EnvPair*> it(this->more_env); !it.Done(); it.Next()) {
      EnvPair* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("more_env"), x1));
  }

  return out_node;
}


hnode_t* command__Sentence::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x0));

  hnode_t* x1 = this->terminator->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("terminator"), x1));

  return out_node;
}


hnode_t* command__ShAssignment::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->pairs != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<AssignPair*> it(this->pairs); !it.Done(); it.Next()) {
      AssignPair* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("pairs"), x1));
  }

  return out_node;
}


hnode_t* command__ControlFlow::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->keyword->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("keyword"), x0));

  if (this->arg_word) {  // Optional
    hnode_t* x1 = this->arg_word->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("arg_word"), x1));
  }

  return out_node;
}


hnode_t* command__Pipeline::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->negated) {  // Optional
    hnode_t* x0 = this->negated->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("negated"), x0));
  }

  if (this->children != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<command_t*> it(this->children); !it.Done(); it.Next()) {
      command_t* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("children"), x1));
  }

  if (this->ops != nullptr) {  // List
    hnode::Array* x2 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<Token*> it(this->ops); !it.Done(); it.Next()) {
      Token* i2 = it.Value();
      hnode_t* h = (i2 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i2->PrettyTree(seen);
      x2->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("ops"), x2));
  }

  return out_node;
}


hnode_t* command__AndOr::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->children != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<command_t*> it(this->children); !it.Done(); it.Next()) {
      command_t* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("children"), x0));
  }

  if (this->ops != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<Token*> it(this->ops); !it.Done(); it.Next()) {
      Token* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("ops"), x1));
  }

  return out_node;
}


hnode_t* command__DoGroup::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->children != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<command_t*> it(this->children); !it.Done(); it.Next()) {
      command_t* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("children"), x1));
  }

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* command__Subshell::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  hnode_t* x3 = Alloc<hnode::Leaf>(this->is_last_cmd ? runtime::TRUE_STR :
                                   runtime::FALSE_STR, color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("is_last_cmd"), x3));

  return out_node;
}


hnode_t* command__DParen::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* command__DBracket::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->expr->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("expr"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* command__ForEach::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->keyword->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("keyword"), x0));

  if (this->iter_names != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<BigStr*> it(this->iter_names); !it.Done(); it.Next()) {
      BigStr* i1 = it.Value();
      x1->children->append(runtime::NewLeaf(i1, color_e::StringConst));
    }
    L->append(Alloc<Field>(StrFromC("iter_names"), x1));
  }

  hnode_t* x2 = this->iterable->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("iterable"), x2));

  if (this->semi_tok) {  // Optional
    hnode_t* x3 = this->semi_tok->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("semi_tok"), x3));
  }

  hnode_t* x4 = this->body->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("body"), x4));

  return out_node;
}


hnode_t* command__ForExpr::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->keyword->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("keyword"), x0));

  if (this->init) {  // Optional
    hnode_t* x1 = this->init->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("init"), x1));
  }

  if (this->cond) {  // Optional
    hnode_t* x2 = this->cond->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("cond"), x2));
  }

  if (this->update) {  // Optional
    hnode_t* x3 = this->update->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("update"), x3));
  }

  if (this->body) {  // Optional
    hnode_t* x4 = this->body->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("body"), x4));
  }

  return out_node;
}


hnode_t* command__WhileUntil::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->keyword->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("keyword"), x0));

  hnode_t* x1 = this->cond->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("cond"), x1));

  hnode_t* x2 = this->body->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("body"), x2));

  return out_node;
}


hnode_t* command__If::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->if_kw->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("if_kw"), x0));

  if (this->arms != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<IfArm*> it(this->arms); !it.Done(); it.Next()) {
      IfArm* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("arms"), x1));
  }

  if (this->else_kw) {  // Optional
    hnode_t* x2 = this->else_kw->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("else_kw"), x2));
  }

  if (this->else_action != nullptr) {  // List
    hnode::Array* x3 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<command_t*> it(this->else_action); !it.Done(); it.Next()) {
      command_t* i3 = it.Value();
      hnode_t* h = (i3 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i3->PrettyTree(seen);
      x3->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("else_action"), x3));
  }

  if (this->fi_kw) {  // Optional
    hnode_t* x4 = this->fi_kw->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("fi_kw"), x4));
  }

  return out_node;
}


hnode_t* command__Case::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->case_kw->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("case_kw"), x0));

  hnode_t* x1 = this->to_match->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("to_match"), x1));

  hnode_t* x2 = this->arms_start->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("arms_start"), x2));

  if (this->arms != nullptr) {  // List
    hnode::Array* x3 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<CaseArm*> it(this->arms); !it.Done(); it.Next()) {
      CaseArm* i3 = it.Value();
      hnode_t* h = (i3 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i3->PrettyTree(seen);
      x3->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("arms"), x3));
  }

  hnode_t* x4 = this->arms_end->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("arms_end"), x4));

  return out_node;
}


hnode_t* command__ShFunction::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->keyword) {  // Optional
    hnode_t* x0 = this->keyword->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("keyword"), x0));
  }

  hnode_t* x1 = this->name_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("name_tok"), x1));

  hnode_t* x2 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x2));

  hnode_t* x3 = this->body->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("body"), x3));

  return out_node;
}


hnode_t* command__TimeBlock::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->keyword->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("keyword"), x0));

  hnode_t* x1 = this->pipeline->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("pipeline"), x1));

  return out_node;
}


hnode_t* command__CommandList::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->children != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<command_t*> it(this->children); !it.Done(); it.Next()) {
      command_t* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("children"), x0));
  }

  return out_node;
}


hnode_t* command__VarDecl::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->keyword) {  // Optional
    hnode_t* x0 = this->keyword->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("keyword"), x0));
  }

  if (this->lhs != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<NameType*> it(this->lhs); !it.Done(); it.Next()) {
      NameType* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("lhs"), x1));
  }

  if (this->rhs) {  // Optional
    hnode_t* x2 = this->rhs->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("rhs"), x2));
  }

  return out_node;
}


hnode_t* command__BareDecl::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->lhs->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("lhs"), x0));

  hnode_t* x1 = this->rhs->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("rhs"), x1));

  return out_node;
}


hnode_t* command__Mutation::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->keyword->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("keyword"), x0));

  if (this->lhs != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<y_lhs_t*> it(this->lhs); !it.Done(); it.Next()) {
      y_lhs_t* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("lhs"), x1));
  }

  hnode_t* x2 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x2));

  hnode_t* x3 = this->rhs->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("rhs"), x3));

  return out_node;
}


hnode_t* command__Expr::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->keyword->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("keyword"), x0));

  hnode_t* x1 = this->e->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("e"), x1));

  return out_node;
}


hnode_t* command__Retval::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(command_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->keyword->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("keyword"), x0));

  hnode_t* x1 = this->val->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("val"), x1));

  return out_node;
}


hnode_t* command_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case command_e::NoOp: {
    command__NoOp* obj = static_cast<command__NoOp*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::Redirect: {
    command__Redirect* obj = static_cast<command__Redirect*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::Simple: {
    command__Simple* obj = static_cast<command__Simple*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::ExpandedAlias: {
    command__ExpandedAlias* obj = static_cast<command__ExpandedAlias*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::Sentence: {
    command__Sentence* obj = static_cast<command__Sentence*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::ShAssignment: {
    command__ShAssignment* obj = static_cast<command__ShAssignment*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::ControlFlow: {
    command__ControlFlow* obj = static_cast<command__ControlFlow*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::Pipeline: {
    command__Pipeline* obj = static_cast<command__Pipeline*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::AndOr: {
    command__AndOr* obj = static_cast<command__AndOr*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::DoGroup: {
    command__DoGroup* obj = static_cast<command__DoGroup*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::BraceGroup: {
    BraceGroup* obj = static_cast<BraceGroup*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::Subshell: {
    command__Subshell* obj = static_cast<command__Subshell*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::DParen: {
    command__DParen* obj = static_cast<command__DParen*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::DBracket: {
    command__DBracket* obj = static_cast<command__DBracket*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::ForEach: {
    command__ForEach* obj = static_cast<command__ForEach*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::ForExpr: {
    command__ForExpr* obj = static_cast<command__ForExpr*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::WhileUntil: {
    command__WhileUntil* obj = static_cast<command__WhileUntil*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::If: {
    command__If* obj = static_cast<command__If*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::Case: {
    command__Case* obj = static_cast<command__Case*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::ShFunction: {
    command__ShFunction* obj = static_cast<command__ShFunction*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::TimeBlock: {
    command__TimeBlock* obj = static_cast<command__TimeBlock*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::CommandList: {
    command__CommandList* obj = static_cast<command__CommandList*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::VarDecl: {
    command__VarDecl* obj = static_cast<command__VarDecl*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::BareDecl: {
    command__BareDecl* obj = static_cast<command__BareDecl*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::Mutation: {
    command__Mutation* obj = static_cast<command__Mutation*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::Expr: {
    command__Expr* obj = static_cast<command__Expr*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::Proc: {
    Proc* obj = static_cast<Proc*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::Func: {
    Func* obj = static_cast<Func*>(this);
    return obj->PrettyTree(seen);
  }
  case command_e::Retval: {
    command__Retval* obj = static_cast<command__Retval*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* glob_part_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case glob_part_e::Literal:
    v = "Literal"; break;
  case glob_part_e::Operator:
    v = "Operator"; break;
  case glob_part_e::CharClass:
    v = "CharClass"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "glob_part.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* glob_part__Literal::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(glob_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(Id_str(this->id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("id"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->s, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("s"), x1));

  return out_node;
}


hnode_t* glob_part__Operator::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(glob_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(Id_str(this->op_id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("op_id"), x0));

  return out_node;
}


hnode_t* glob_part__CharClass::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(glob_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(this->negated ? runtime::TRUE_STR :
                                   runtime::FALSE_STR, color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("negated"), x0));

  if (this->strs != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<BigStr*> it(this->strs); !it.Done(); it.Next()) {
      BigStr* i1 = it.Value();
      x1->children->append(runtime::NewLeaf(i1, color_e::StringConst));
    }
    L->append(Alloc<Field>(StrFromC("strs"), x1));
  }

  return out_node;
}


hnode_t* glob_part_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case glob_part_e::Literal: {
    glob_part__Literal* obj = static_cast<glob_part__Literal*>(this);
    return obj->PrettyTree(seen);
  }
  case glob_part_e::Operator: {
    glob_part__Operator* obj = static_cast<glob_part__Operator*>(this);
    return obj->PrettyTree(seen);
  }
  case glob_part_e::CharClass: {
    glob_part__CharClass* obj = static_cast<glob_part__CharClass*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* printf_part_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case printf_part_e::Literal:
    v = "Literal"; break;
  case printf_part_e::Percent:
    v = "Percent"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "printf_part.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* printf_part__Percent::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(printf_part_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->flags != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<Token*> it(this->flags); !it.Done(); it.Next()) {
      Token* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("flags"), x0));
  }

  if (this->width) {  // Optional
    hnode_t* x1 = this->width->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("width"), x1));
  }

  if (this->precision) {  // Optional
    hnode_t* x2 = this->precision->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("precision"), x2));
  }

  hnode_t* x3 = this->type->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("type"), x3));

  return out_node;
}


hnode_t* printf_part_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case printf_part_e::Literal: {
    Token* obj = static_cast<Token*>(this);
    return obj->PrettyTree(seen);
  }
  case printf_part_e::Percent: {
    printf_part__Percent* obj = static_cast<printf_part__Percent*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* expr_context_str(expr_context_e tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case expr_context_e::Load:
    v = "Load"; break;
  case expr_context_e::Store:
    v = "Store"; break;
  case expr_context_e::Del:
    v = "Del"; break;
  case expr_context_e::AugLoad:
    v = "AugLoad"; break;
  case expr_context_e::AugStore:
    v = "AugStore"; break;
  case expr_context_e::Param:
    v = "Param"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "expr_context.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* TypeExpr::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("TypeExpr"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("tok"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  if (this->params != nullptr) {  // List
    hnode::Array* x2 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<TypeExpr*> it(this->params); !it.Done(); it.Next()) {
      TypeExpr* i2 = it.Value();
      hnode_t* h = (i2 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i2->PrettyTree(seen);
      x2->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("params"), x2));
  }

  return out_node;
}


hnode_t* NameType::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("NameType"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  if (this->typ) {  // Optional
    hnode_t* x2 = this->typ->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("typ"), x2));
  }

  return out_node;
}


hnode_t* Comprehension::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("Comprehension"));
  List<Field*>* L = out_node->fields;

  if (this->lhs != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<NameType*> it(this->lhs); !it.Done(); it.Next()) {
      NameType* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("lhs"), x0));
  }

  hnode_t* x1 = this->iter->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("iter"), x1));

  if (this->cond) {  // Optional
    hnode_t* x2 = this->cond->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("cond"), x2));
  }

  return out_node;
}


hnode_t* NamedArg::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("NamedArg"));
  List<Field*>* L = out_node->fields;

  if (this->name) {  // Optional
    hnode_t* x0 = this->name->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("name"), x0));
  }

  hnode_t* x1 = this->value->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("value"), x1));

  return out_node;
}


hnode_t* Subscript::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("Subscript"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->obj->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("obj"), x1));

  hnode_t* x2 = this->index->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("index"), x2));

  return out_node;
}


hnode_t* Attribute::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("Attribute"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->obj->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("obj"), x0));

  hnode_t* x1 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x1));

  hnode_t* x2 = this->attr->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("attr"), x2));

  hnode_t* x3 = runtime::NewLeaf(this->attr_name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("attr_name"), x3));

  hnode_t* x4 = Alloc<hnode::Leaf>(expr_context_str(this->ctx),
                                   color_e::TypeName);
  L->append(Alloc<Field>(StrFromC("ctx"), x4));

  return out_node;
}

BigStr* y_lhs_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case y_lhs_e::Var:
    v = "Var"; break;
  case y_lhs_e::Subscript:
    v = "Subscript"; break;
  case y_lhs_e::Attribute:
    v = "Attribute"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "y_lhs.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* y_lhs_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case y_lhs_e::Var: {
    Token* obj = static_cast<Token*>(this);
    return obj->PrettyTree(seen);
  }
  case y_lhs_e::Subscript: {
    Subscript* obj = static_cast<Subscript*>(this);
    return obj->PrettyTree(seen);
  }
  case y_lhs_e::Attribute: {
    Attribute* obj = static_cast<Attribute*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* place_op_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case place_op_e::Subscript:
    v = "Subscript"; break;
  case place_op_e::Attribute:
    v = "Attribute"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "place_op.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* place_op__Subscript::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(place_op_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x0));

  hnode_t* x1 = this->index->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("index"), x1));

  return out_node;
}


hnode_t* place_op__Attribute::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(place_op_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x0));

  hnode_t* x1 = this->attr->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("attr"), x1));

  return out_node;
}


hnode_t* place_op_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case place_op_e::Subscript: {
    place_op__Subscript* obj = static_cast<place_op__Subscript*>(this);
    return obj->PrettyTree(seen);
  }
  case place_op_e::Attribute: {
    place_op__Attribute* obj = static_cast<place_op__Attribute*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* expr_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case expr_e::Var:
    v = "Var"; break;
  case expr_e::Const:
    v = "Const"; break;
  case expr_e::Place:
    v = "Place"; break;
  case expr_e::ShArrayLiteral:
    v = "ShArrayLiteral"; break;
  case expr_e::Eggex:
    v = "Eggex"; break;
  case expr_e::SimpleVarSub:
    v = "SimpleVarSub"; break;
  case expr_e::BracedVarSub:
    v = "BracedVarSub"; break;
  case expr_e::CommandSub:
    v = "CommandSub"; break;
  case expr_e::SingleQuoted:
    v = "SingleQuoted"; break;
  case expr_e::DoubleQuoted:
    v = "DoubleQuoted"; break;
  case expr_e::Literal:
    v = "Literal"; break;
  case expr_e::Lambda:
    v = "Lambda"; break;
  case expr_e::Unary:
    v = "Unary"; break;
  case expr_e::Binary:
    v = "Binary"; break;
  case expr_e::Compare:
    v = "Compare"; break;
  case expr_e::FuncCall:
    v = "FuncCall"; break;
  case expr_e::IfExp:
    v = "IfExp"; break;
  case expr_e::Tuple:
    v = "Tuple"; break;
  case expr_e::List:
    v = "List"; break;
  case expr_e::Dict:
    v = "Dict"; break;
  case expr_e::Implicit:
    v = "Implicit"; break;
  case expr_e::ListComp:
    v = "ListComp"; break;
  case expr_e::DictComp:
    v = "DictComp"; break;
  case expr_e::GeneratorExp:
    v = "GeneratorExp"; break;
  case expr_e::Range:
    v = "Range"; break;
  case expr_e::Slice:
    v = "Slice"; break;
  case expr_e::Subscript:
    v = "Subscript"; break;
  case expr_e::Attribute:
    v = "Attribute"; break;
  case expr_e::Spread:
    v = "Spread"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "expr.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

expr__Implicit* expr::Implicit = &gexpr__Implicit.obj;

GcGlobal<expr__Implicit> gexpr__Implicit = 
  { ObjHeader::Global(expr_e::Implicit) };

hnode_t* expr__Var::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  return out_node;
}


hnode_t* expr__Const::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->c->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("c"), x0));

  hnode_t* x1 = this->val->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("val"), x1));

  return out_node;
}


hnode_t* expr__Place::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->blame_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("blame_tok"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->var_name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("var_name"), x1));

  if (this->ops != nullptr) {  // List
    hnode::Array* x2 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<place_op_t*> it(this->ops); !it.Done(); it.Next()) {
      place_op_t* i2 = it.Value();
      hnode_t* h = (i2 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i2->PrettyTree(seen);
      x2->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("ops"), x2));
  }

  return out_node;
}


hnode_t* expr__Literal::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->inner->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("inner"), x0));

  return out_node;
}


hnode_t* expr__Lambda::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->params != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<NameType*> it(this->params); !it.Done(); it.Next()) {
      NameType* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("params"), x0));
  }

  hnode_t* x1 = this->body->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("body"), x1));

  return out_node;
}


hnode_t* expr__Unary::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x0));

  hnode_t* x1 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x1));

  return out_node;
}


hnode_t* expr__Binary::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x0));

  hnode_t* x1 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x1));

  hnode_t* x2 = this->right->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("right"), x2));

  return out_node;
}


hnode_t* expr__Compare::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->ops != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<Token*> it(this->ops); !it.Done(); it.Next()) {
      Token* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("ops"), x1));
  }

  if (this->comparators != nullptr) {  // List
    hnode::Array* x2 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<expr_t*> it(this->comparators); !it.Done(); it.Next()) {
      expr_t* i2 = it.Value();
      hnode_t* h = (i2 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i2->PrettyTree(seen);
      x2->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("comparators"), x2));
  }

  return out_node;
}


hnode_t* expr__FuncCall::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->func->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("func"), x0));

  hnode_t* x1 = this->args->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("args"), x1));

  return out_node;
}


hnode_t* expr__IfExp::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->test->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("test"), x0));

  hnode_t* x1 = this->body->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("body"), x1));

  hnode_t* x2 = this->orelse->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("orelse"), x2));

  return out_node;
}


hnode_t* expr__Tuple::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->elts != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<expr_t*> it(this->elts); !it.Done(); it.Next()) {
      expr_t* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("elts"), x1));
  }

  hnode_t* x2 = Alloc<hnode::Leaf>(expr_context_str(this->ctx),
                                   color_e::TypeName);
  L->append(Alloc<Field>(StrFromC("ctx"), x2));

  return out_node;
}


hnode_t* expr__List::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->elts != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<expr_t*> it(this->elts); !it.Done(); it.Next()) {
      expr_t* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("elts"), x1));
  }

  hnode_t* x2 = Alloc<hnode::Leaf>(expr_context_str(this->ctx),
                                   color_e::TypeName);
  L->append(Alloc<Field>(StrFromC("ctx"), x2));

  return out_node;
}


hnode_t* expr__Dict::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  if (this->keys != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<expr_t*> it(this->keys); !it.Done(); it.Next()) {
      expr_t* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("keys"), x1));
  }

  if (this->values != nullptr) {  // List
    hnode::Array* x2 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<expr_t*> it(this->values); !it.Done(); it.Next()) {
      expr_t* i2 = it.Value();
      hnode_t* h = (i2 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i2->PrettyTree(seen);
      x2->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("values"), x2));
  }

  return out_node;
}


hnode_t* expr__Implicit::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  return out_node;
}


hnode_t* expr__ListComp::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->elt->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("elt"), x1));

  if (this->generators != nullptr) {  // List
    hnode::Array* x2 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<Comprehension*> it(this->generators); !it.Done(); it.Next()) {
      Comprehension* i2 = it.Value();
      hnode_t* h = (i2 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i2->PrettyTree(seen);
      x2->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("generators"), x2));
  }

  return out_node;
}


hnode_t* expr__DictComp::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->key->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("key"), x1));

  hnode_t* x2 = this->value->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("value"), x2));

  if (this->generators != nullptr) {  // List
    hnode::Array* x3 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<Comprehension*> it(this->generators); !it.Done(); it.Next()) {
      Comprehension* i3 = it.Value();
      hnode_t* h = (i3 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i3->PrettyTree(seen);
      x3->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("generators"), x3));
  }

  return out_node;
}


hnode_t* expr__GeneratorExp::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->elt->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("elt"), x0));

  if (this->generators != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<Comprehension*> it(this->generators); !it.Done(); it.Next()) {
      Comprehension* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("generators"), x1));
  }

  return out_node;
}


hnode_t* expr__Range::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->lower->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("lower"), x0));

  hnode_t* x1 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x1));

  hnode_t* x2 = this->upper->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("upper"), x2));

  return out_node;
}


hnode_t* expr__Slice::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->lower) {  // Optional
    hnode_t* x0 = this->lower->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("lower"), x0));
  }

  hnode_t* x1 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x1));

  if (this->upper) {  // Optional
    hnode_t* x2 = this->upper->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("upper"), x2));
  }

  return out_node;
}


hnode_t* expr__Spread::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(expr_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->left->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("left"), x0));

  hnode_t* x1 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x1));

  return out_node;
}


hnode_t* expr_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case expr_e::Var: {
    expr__Var* obj = static_cast<expr__Var*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Const: {
    expr__Const* obj = static_cast<expr__Const*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Place: {
    expr__Place* obj = static_cast<expr__Place*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::ShArrayLiteral: {
    ShArrayLiteral* obj = static_cast<ShArrayLiteral*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Eggex: {
    Eggex* obj = static_cast<Eggex*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::SimpleVarSub: {
    SimpleVarSub* obj = static_cast<SimpleVarSub*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::BracedVarSub: {
    BracedVarSub* obj = static_cast<BracedVarSub*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::CommandSub: {
    CommandSub* obj = static_cast<CommandSub*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::SingleQuoted: {
    SingleQuoted* obj = static_cast<SingleQuoted*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::DoubleQuoted: {
    DoubleQuoted* obj = static_cast<DoubleQuoted*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Literal: {
    expr__Literal* obj = static_cast<expr__Literal*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Lambda: {
    expr__Lambda* obj = static_cast<expr__Lambda*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Unary: {
    expr__Unary* obj = static_cast<expr__Unary*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Binary: {
    expr__Binary* obj = static_cast<expr__Binary*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Compare: {
    expr__Compare* obj = static_cast<expr__Compare*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::FuncCall: {
    expr__FuncCall* obj = static_cast<expr__FuncCall*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::IfExp: {
    expr__IfExp* obj = static_cast<expr__IfExp*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Tuple: {
    expr__Tuple* obj = static_cast<expr__Tuple*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::List: {
    expr__List* obj = static_cast<expr__List*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Dict: {
    expr__Dict* obj = static_cast<expr__Dict*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Implicit: {
    expr__Implicit* obj = static_cast<expr__Implicit*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::ListComp: {
    expr__ListComp* obj = static_cast<expr__ListComp*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::DictComp: {
    expr__DictComp* obj = static_cast<expr__DictComp*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::GeneratorExp: {
    expr__GeneratorExp* obj = static_cast<expr__GeneratorExp*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Range: {
    expr__Range* obj = static_cast<expr__Range*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Slice: {
    expr__Slice* obj = static_cast<expr__Slice*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Subscript: {
    Subscript* obj = static_cast<Subscript*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Attribute: {
    Attribute* obj = static_cast<Attribute*>(this);
    return obj->PrettyTree(seen);
  }
  case expr_e::Spread: {
    expr__Spread* obj = static_cast<expr__Spread*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}

hnode_t* PosixClass::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("PosixClass"));
  List<Field*>* L = out_node->fields;

  if (this->negated) {  // Optional
    hnode_t* x0 = this->negated->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("negated"), x0));
  }

  hnode_t* x1 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  return out_node;
}


hnode_t* PerlClass::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("PerlClass"));
  List<Field*>* L = out_node->fields;

  if (this->negated) {  // Optional
    hnode_t* x0 = this->negated->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("negated"), x0));
  }

  hnode_t* x1 = runtime::NewLeaf(this->name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  return out_node;
}


hnode_t* CharCode::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("CharCode"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->blame_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("blame_tok"), x0));

  hnode_t* x1 = Alloc<hnode::Leaf>(str(this->i), color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("i"), x1));

  hnode_t* x2 = Alloc<hnode::Leaf>(this->u_braced ? runtime::TRUE_STR :
                                   runtime::FALSE_STR, color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("u_braced"), x2));

  return out_node;
}


hnode_t* CharRange::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(StrFromC("CharRange"));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->start->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("start"), x0));

  hnode_t* x1 = this->end->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("end"), x1));

  return out_node;
}

BigStr* class_literal_term_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case class_literal_term_e::PosixClass:
    v = "PosixClass"; break;
  case class_literal_term_e::PerlClass:
    v = "PerlClass"; break;
  case class_literal_term_e::CharRange:
    v = "CharRange"; break;
  case class_literal_term_e::CharCode:
    v = "CharCode"; break;
  case class_literal_term_e::SingleQuoted:
    v = "SingleQuoted"; break;
  case class_literal_term_e::Splice:
    v = "Splice"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "class_literal_term.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* class_literal_term__Splice::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node =
runtime::NewRecord(class_literal_term_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->name->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("name"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->var_name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("var_name"), x1));

  return out_node;
}


hnode_t* class_literal_term_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case class_literal_term_e::PosixClass: {
    PosixClass* obj = static_cast<PosixClass*>(this);
    return obj->PrettyTree(seen);
  }
  case class_literal_term_e::PerlClass: {
    PerlClass* obj = static_cast<PerlClass*>(this);
    return obj->PrettyTree(seen);
  }
  case class_literal_term_e::CharRange: {
    CharRange* obj = static_cast<CharRange*>(this);
    return obj->PrettyTree(seen);
  }
  case class_literal_term_e::CharCode: {
    CharCode* obj = static_cast<CharCode*>(this);
    return obj->PrettyTree(seen);
  }
  case class_literal_term_e::SingleQuoted: {
    SingleQuoted* obj = static_cast<SingleQuoted*>(this);
    return obj->PrettyTree(seen);
  }
  case class_literal_term_e::Splice: {
    class_literal_term__Splice* obj =
static_cast<class_literal_term__Splice*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* char_class_term_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case char_class_term_e::PosixClass:
    v = "PosixClass"; break;
  case char_class_term_e::PerlClass:
    v = "PerlClass"; break;
  case char_class_term_e::CharRange:
    v = "CharRange"; break;
  case char_class_term_e::CharCode:
    v = "CharCode"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "char_class_term.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* char_class_term_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case char_class_term_e::PosixClass: {
    PosixClass* obj = static_cast<PosixClass*>(this);
    return obj->PrettyTree(seen);
  }
  case char_class_term_e::PerlClass: {
    PerlClass* obj = static_cast<PerlClass*>(this);
    return obj->PrettyTree(seen);
  }
  case char_class_term_e::CharRange: {
    CharRange* obj = static_cast<CharRange*>(this);
    return obj->PrettyTree(seen);
  }
  case char_class_term_e::CharCode: {
    CharCode* obj = static_cast<CharCode*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* re_repeat_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case re_repeat_e::Op:
    v = "Op"; break;
  case re_repeat_e::Range:
    v = "Range"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "re_repeat.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* re_repeat__Range::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_repeat_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->left) {  // Optional
    hnode_t* x0 = this->left->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("left"), x0));
  }

  hnode_t* x1 = runtime::NewLeaf(this->lower, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("lower"), x1));

  hnode_t* x2 = runtime::NewLeaf(this->upper, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("upper"), x2));

  if (this->right) {  // Optional
    hnode_t* x3 = this->right->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("right"), x3));
  }

  return out_node;
}


hnode_t* re_repeat_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case re_repeat_e::Op: {
    Token* obj = static_cast<Token*>(this);
    return obj->PrettyTree(seen);
  }
  case re_repeat_e::Range: {
    re_repeat__Range* obj = static_cast<re_repeat__Range*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}
BigStr* re_str(int tag, bool dot) {
  char buf[32];
  const char* v = nullptr;
  switch (tag) {
  case re_e::Primitive:
    v = "Primitive"; break;
  case re_e::PosixClass:
    v = "PosixClass"; break;
  case re_e::PerlClass:
    v = "PerlClass"; break;
  case re_e::CharClassLiteral:
    v = "CharClassLiteral"; break;
  case re_e::CharClass:
    v = "CharClass"; break;
  case re_e::Splice:
    v = "Splice"; break;
  case re_e::SingleQuoted:
    v = "SingleQuoted"; break;
  case re_e::Repeat:
    v = "Repeat"; break;
  case re_e::Seq:
    v = "Seq"; break;
  case re_e::Alt:
    v = "Alt"; break;
  case re_e::Group:
    v = "Group"; break;
  case re_e::Capture:
    v = "Capture"; break;
  case re_e::Backtracking:
    v = "Backtracking"; break;
  case re_e::LiteralChars:
    v = "LiteralChars"; break;
  default:
    assert(0);
  }
  if (dot) {
    snprintf(buf, 32, "re.%s", v);
    return StrFromC(buf);
  } else {
    return StrFromC(v);
  }
}

hnode_t* re__Primitive::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->blame_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("blame_tok"), x0));

  hnode_t* x1 = Alloc<hnode::Leaf>(Id_str(this->id), color_e::UserType);
  L->append(Alloc<Field>(StrFromC("id"), x1));

  return out_node;
}


hnode_t* re__CharClassLiteral::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(this->negated ? runtime::TRUE_STR :
                                   runtime::FALSE_STR, color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("negated"), x0));

  if (this->terms != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<class_literal_term_t*> it(this->terms); !it.Done();
         it.Next()) {
      class_literal_term_t* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("terms"), x1));
  }

  return out_node;
}


hnode_t* re__CharClass::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(this->negated ? runtime::TRUE_STR :
                                   runtime::FALSE_STR, color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("negated"), x0));

  if (this->terms != nullptr) {  // List
    hnode::Array* x1 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<char_class_term_t*> it(this->terms); !it.Done(); it.Next()) {
      char_class_term_t* i1 = it.Value();
      hnode_t* h = (i1 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i1->PrettyTree(seen);
      x1->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("terms"), x1));
  }

  return out_node;
}


hnode_t* re__Splice::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->name->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("name"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->var_name, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("var_name"), x1));

  return out_node;
}


hnode_t* re__Repeat::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x0));

  hnode_t* x1 = this->op->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("op"), x1));

  return out_node;
}


hnode_t* re__Seq::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->children != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<re_t*> it(this->children); !it.Done(); it.Next()) {
      re_t* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("children"), x0));
  }

  return out_node;
}


hnode_t* re__Alt::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_str(this->tag()));
  List<Field*>* L = out_node->fields;

  if (this->children != nullptr) {  // List
    hnode::Array* x0 = Alloc<hnode::Array>(Alloc<List<hnode_t*>>());
    for (ListIter<re_t*> it(this->children); !it.Done(); it.Next()) {
      re_t* i0 = it.Value();
      hnode_t* h = (i0 == nullptr) ? Alloc<hnode::Leaf>(StrFromC("_"),
                    color_e::OtherConst) : i0->PrettyTree(seen);
      x0->children->append(h);
    }
    L->append(Alloc<Field>(StrFromC("children"), x0));
  }

  return out_node;
}


hnode_t* re__Group::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x0));

  return out_node;
}


hnode_t* re__Capture::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x0));

  if (this->name) {  // Optional
    hnode_t* x1 = this->name->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("name"), x1));
  }

  if (this->func_name) {  // Optional
    hnode_t* x2 = this->func_name->PrettyTree(seen);
    L->append(Alloc<Field>(StrFromC("func_name"), x2));
  }

  return out_node;
}


hnode_t* re__Backtracking::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = Alloc<hnode::Leaf>(this->negated ? runtime::TRUE_STR :
                                   runtime::FALSE_STR, color_e::OtherConst);
  L->append(Alloc<Field>(StrFromC("negated"), x0));

  hnode_t* x1 = this->name->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("name"), x1));

  hnode_t* x2 = this->child->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("child"), x2));

  return out_node;
}


hnode_t* re__LiteralChars::PrettyTree(Dict<int, bool>* seen) {
  seen = seen ? seen : Alloc<Dict<int, bool>>();
  int heap_id = ObjectId(this);
  if (dict_contains(seen, heap_id)) {
    return Alloc<hnode::AlreadySeen>(heap_id);
  }
  seen->set(heap_id, true);
  hnode::Record* out_node = runtime::NewRecord(re_str(this->tag()));
  List<Field*>* L = out_node->fields;

  hnode_t* x0 = this->blame_tok->PrettyTree(seen);
  L->append(Alloc<Field>(StrFromC("blame_tok"), x0));

  hnode_t* x1 = runtime::NewLeaf(this->s, color_e::StringConst);
  L->append(Alloc<Field>(StrFromC("s"), x1));

  return out_node;
}


hnode_t* re_t::PrettyTree(Dict<int, bool>* seen) {
  switch (this->tag()) {
  case re_e::Primitive: {
    re__Primitive* obj = static_cast<re__Primitive*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::PosixClass: {
    PosixClass* obj = static_cast<PosixClass*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::PerlClass: {
    PerlClass* obj = static_cast<PerlClass*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::CharClassLiteral: {
    re__CharClassLiteral* obj = static_cast<re__CharClassLiteral*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::CharClass: {
    re__CharClass* obj = static_cast<re__CharClass*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::Splice: {
    re__Splice* obj = static_cast<re__Splice*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::SingleQuoted: {
    SingleQuoted* obj = static_cast<SingleQuoted*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::Repeat: {
    re__Repeat* obj = static_cast<re__Repeat*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::Seq: {
    re__Seq* obj = static_cast<re__Seq*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::Alt: {
    re__Alt* obj = static_cast<re__Alt*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::Group: {
    re__Group* obj = static_cast<re__Group*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::Capture: {
    re__Capture* obj = static_cast<re__Capture*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::Backtracking: {
    re__Backtracking* obj = static_cast<re__Backtracking*>(this);
    return obj->PrettyTree(seen);
  }
  case re_e::LiteralChars: {
    re__LiteralChars* obj = static_cast<re__LiteralChars*>(this);
    return obj->PrettyTree(seen);
  }
  default:
    assert(0);
  }
}

}  // namespace syntax_asdl