// examples/parse translated by mycpp_main

#include "mycpp/examples/parse_preamble.h"
// BEGIN mycpp output

#include "mycpp/runtime.h"

namespace runtime {  // forward declare
  class TraversalState;
}

namespace format {  // forward declare
}

namespace j8_lite {  // forward declare
}

namespace ansi {  // forward declare
}

namespace pp_hnode {  // forward declare
  class BaseEncoder;
  class HNodeEncoder;
}

namespace pretty {  // forward declare
  class PrettyPrinter;
}

namespace parse {  // forward declare
  class Lexer;
  class ParseError;
  class Parser;
}

GLOBAL_STR(S_Aoo, "");
GLOBAL_STR(S_nfs, "\n");
GLOBAL_STR(S_yfk, "\u001b[0;0m");
GLOBAL_STR(S_aaF, "\u001b[1m");
GLOBAL_STR(S_sqm, "\u001b[31m");
GLOBAL_STR(S_eda, "\u001b[32m");
GLOBAL_STR(S_ysf, "\u001b[33m");
GLOBAL_STR(S_osl, "\u001b[34m");
GLOBAL_STR(S_vie, "\u001b[35m");
GLOBAL_STR(S_mmi, "\u001b[36m");
GLOBAL_STR(S_rpo, "\u001b[37m");
GLOBAL_STR(S_sCc, "\u001b[4m");
GLOBAL_STR(S_woy, "\u001b[7m");
GLOBAL_STR(S_yfw, " ");
GLOBAL_STR(S_utu, " $$ ");
GLOBAL_STR(S_ijB, "(");
GLOBAL_STR(S_zxb, "()");
GLOBAL_STR(S_anb, "(1+2)*3");
GLOBAL_STR(S_qey, "(a+b");
GLOBAL_STR(S_hxb, ")");
GLOBAL_STR(S_fFq, "*/");
GLOBAL_STR(S_jnE, "+");
GLOBAL_STR(S_jau, "+-");
GLOBAL_STR(S_gpk, "--");
GLOBAL_STR(S_vrA, "1");
GLOBAL_STR(S_sdy, "1*2+3");
GLOBAL_STR(S_nmE, "1+2");
GLOBAL_STR(S_eei, "1+2*3");
GLOBAL_STR(S_fyj, ":");
GLOBAL_STR(S_rFn, "Expected ");
GLOBAL_STR(S_gFh, "F");
GLOBAL_STR(S_wqv, "NULL BUG");
GLOBAL_STR(S_otA, "Other");
GLOBAL_STR(S_cor, "T");
GLOBAL_STR(S_pqw, "Unexpected token ");
GLOBAL_STR(S_Eax, "[");
GLOBAL_STR(S_xmu, "[]");
GLOBAL_STR(S_pcD, "]");
GLOBAL_STR(S_tci, "_");
GLOBAL_STR(S_gfw, "___ GC: after printing");
GLOBAL_STR(S_gCD, "a");
GLOBAL_STR(S_fhr, "a*b*3*4");
GLOBAL_STR(S_Bth, "a+b+c+d");
GLOBAL_STR(S_jng, "abc");
GLOBAL_STR(S_jFv, "b");
GLOBAL_STR(S_clt, "bar");
GLOBAL_STR(S_emj, "c");
GLOBAL_STR(S_Crn, "d");
GLOBAL_STR(S_ysz, "e");
GLOBAL_STR(S_lqB, "foo");
GLOBAL_STR(S_wkf, "not null");
GLOBAL_STR(S_xww, "zz");

namespace runtime {  // declare

extern int NO_SPID;
hnode::Record* NewRecord(BigStr* node_type);
hnode::Leaf* NewLeaf(BigStr* s, hnode_asdl::color_t e_color);
class TraversalState {
 public:
  TraversalState();
  Dict<int, bool>* seen{};
  Dict<int, int>* ref_count{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(TraversalState));
  }

  DISALLOW_COPY_AND_ASSIGN(TraversalState)
};

extern BigStr* TRUE_STR;
extern BigStr* FALSE_STR;

}  // declare namespace runtime

namespace format {  // declare

int _HNodeCount(hnode_asdl::hnode_t* h);
int _DocCount(pretty_asdl::doc_t* d);
void _HNodePrettyPrint(bool perf_stats, bool doc_debug, hnode_asdl::hnode_t* node, mylib::Writer* f, int max_width = 80);
void HNodePrettyPrint(hnode_asdl::hnode_t* node, mylib::Writer* f, int max_width = 80);

}  // declare namespace format

namespace j8_lite {  // declare

BigStr* EncodeString(BigStr* s, bool unquoted_ok = false);
BigStr* YshEncodeString(BigStr* s);
BigStr* MaybeShellEncode(BigStr* s);
BigStr* ShellEncode(BigStr* s);
BigStr* YshEncode(BigStr* s, bool unquoted_ok = false);

}  // declare namespace j8_lite

namespace ansi {  // declare

extern BigStr* RESET;
extern BigStr* BOLD;
extern BigStr* UNDERLINE;
extern BigStr* REVERSE;
extern BigStr* RED;
extern BigStr* GREEN;
extern BigStr* YELLOW;
extern BigStr* BLUE;
extern BigStr* MAGENTA;
extern BigStr* CYAN;
extern BigStr* WHITE;

}  // declare namespace ansi

namespace pp_hnode {  // declare

class BaseEncoder {
 public:
  BaseEncoder();
  void SetIndent(int indent);
  void SetUseStyles(bool use_styles);
  void SetMaxTabularWidth(int max_tabular_width);
  pretty_asdl::MeasuredDoc* _Styled(BigStr* style, pretty_asdl::MeasuredDoc* mdoc);
  pretty_asdl::MeasuredDoc* _StyledAscii(BigStr* style, BigStr* s);
  pretty_asdl::MeasuredDoc* _Surrounded(BigStr* left, pretty_asdl::MeasuredDoc* mdoc, BigStr* right);
  pretty_asdl::MeasuredDoc* _SurroundedAndPrefixed(BigStr* left, pretty_asdl::MeasuredDoc* prefix, BigStr* sep, pretty_asdl::MeasuredDoc* mdoc, BigStr* right);
  pretty_asdl::MeasuredDoc* _Join(List<pretty_asdl::MeasuredDoc*>* items, BigStr* sep, BigStr* space);
  pretty_asdl::MeasuredDoc* _Tabular(List<pretty_asdl::MeasuredDoc*>* items, BigStr* sep);
  int indent{};
  int max_tabular_width{};
  bool use_styles{};
  Dict<int, bool>* visiting{};
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(BaseEncoder, visiting));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(BaseEncoder));
  }

  DISALLOW_COPY_AND_ASSIGN(BaseEncoder)
};

class HNodeEncoder : public ::pp_hnode::BaseEncoder {
 public:
  HNodeEncoder();
  pretty_asdl::MeasuredDoc* HNode(hnode_asdl::hnode_t* h);
  pretty_asdl::MeasuredDoc* _Field(hnode_asdl::Field* field);
  pretty_asdl::MeasuredDoc* _HNode(hnode_asdl::hnode_t* h);

  BigStr* field_color{};
  BigStr* type_color{};
  
  static constexpr uint32_t field_mask() {
    return ::pp_hnode::BaseEncoder::field_mask()
         | maskbit(offsetof(HNodeEncoder, field_color))
         | maskbit(offsetof(HNodeEncoder, type_color));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(HNodeEncoder));
  }

  DISALLOW_COPY_AND_ASSIGN(HNodeEncoder)
};


}  // declare namespace pp_hnode

namespace pretty {  // declare

pretty_asdl::Measure* _EmptyMeasure();
pretty_asdl::Measure* _FlattenMeasure(pretty_asdl::Measure* measure);
pretty_asdl::Measure* _ConcatMeasure(pretty_asdl::Measure* m1, pretty_asdl::Measure* m2);
int _SuffixLen(pretty_asdl::Measure* measure);
pretty_asdl::MeasuredDoc* AsciiText(BigStr* string);
pretty_asdl::MeasuredDoc* _Break(BigStr* string);
pretty_asdl::MeasuredDoc* _Indent(int indent, pretty_asdl::MeasuredDoc* mdoc);
pretty_asdl::Measure* _Splice(List<pretty_asdl::MeasuredDoc*>* out, List<pretty_asdl::MeasuredDoc*>* mdocs);
pretty_asdl::MeasuredDoc* _Concat(List<pretty_asdl::MeasuredDoc*>* mdocs);
pretty_asdl::MeasuredDoc* _Group(pretty_asdl::MeasuredDoc* mdoc);
pretty_asdl::MeasuredDoc* _IfFlat(pretty_asdl::MeasuredDoc* flat_mdoc, pretty_asdl::MeasuredDoc* nonflat_mdoc);
pretty_asdl::MeasuredDoc* _Flat(pretty_asdl::MeasuredDoc* mdoc);
class PrettyPrinter {
 public:
  PrettyPrinter(int max_width);
  bool _Fits(int prefix_len, pretty_asdl::MeasuredDoc* group, pretty_asdl::Measure* suffix_measure);
  void PrintDoc(pretty_asdl::MeasuredDoc* document, mylib::BufWriter* buf);
  int max_width{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(0, sizeof(PrettyPrinter));
  }

  DISALLOW_COPY_AND_ASSIGN(PrettyPrinter)
};


}  // declare namespace pretty

namespace parse {  // declare

class Lexer {
 public:
  Lexer(BigStr* s);
  Tuple2<expr_asdl::tok_t, BigStr*> Read();
  void _MethodCallingOtherMethod();
  BigStr* s{};
  int i{};
  int n{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(Lexer));
  }

  DISALLOW_COPY_AND_ASSIGN(Lexer)
};

class ParseError {
 public:
  ParseError(BigStr* msg);
  BigStr* msg{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(ParseError));
  }

  DISALLOW_COPY_AND_ASSIGN(ParseError)
};

class Parser {
 public:
  Parser(parse::Lexer* lexer);
  void Next();
  void Eat(BigStr* tok_val);
  expr_asdl::expr_t* ParseFactor();
  expr_asdl::expr_t* ParseTerm();
  expr_asdl::expr_t* ParseExpr();
  expr_asdl::expr_t* Parse();
  parse::Lexer* lexer{};
  BigStr* tok_val{};
  expr_asdl::tok_t tok_type{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(Parser));
  }

  DISALLOW_COPY_AND_ASSIGN(Parser)
};

void TestParse();
void TestCreateNull();
void TestSubtype();
void TestLeafValue();
void run_tests();
void run_benchmarks();

}  // declare namespace parse

namespace runtime {  // define

using hnode_asdl::hnode;
using hnode_asdl::color_t;
using hnode_asdl::color_e;
int NO_SPID = -1;

hnode::Record* NewRecord(BigStr* node_type) {
  StackRoot _root0(&node_type);

  return Alloc<hnode::Record>(node_type, S_ijB, S_hxb, Alloc<List<hnode_asdl::Field*>>(), nullptr);
}

hnode::Leaf* NewLeaf(BigStr* s, hnode_asdl::color_t e_color) {
  StackRoot _root0(&s);

  if (s == nullptr) {
    return Alloc<hnode::Leaf>(S_tci, color_e::OtherConst);
  }
  else {
    return Alloc<hnode::Leaf>(s, e_color);
  }
}

TraversalState::TraversalState() {
  this->seen = Alloc<Dict<int, bool>>();
  this->ref_count = Alloc<Dict<int, int>>();
}
BigStr* TRUE_STR = S_cor;
BigStr* FALSE_STR = S_gFh;

}  // define namespace runtime

namespace format {  // define

using hnode_asdl::hnode;
using hnode_asdl::hnode_e;
using hnode_asdl::hnode_t;
using pretty_asdl::doc;
using pretty_asdl::doc_e;
using pretty_asdl::doc_t;
using pretty_asdl::MeasuredDoc;
using pretty_asdl::List_Measured;

int _HNodeCount(hnode_asdl::hnode_t* h) {
  hnode_asdl::hnode_t* UP_h = nullptr;
  int n;
  StackRoot _root0(&h);
  StackRoot _root1(&UP_h);

  UP_h = h;
  switch (h->tag()) {
    case hnode_e::AlreadySeen: {
      return 1;
    }
      break;
    case hnode_e::Leaf: {
      return 1;
    }
      break;
    case hnode_e::Array: {
      hnode::Array* h = static_cast<hnode::Array*>(UP_h);
      n = 1;
      for (ListIter<hnode_asdl::hnode_t*> it(h->children); !it.Done(); it.Next()) {
        hnode_asdl::hnode_t* child = it.Value();
        StackRoot _for(&child      );
        n += _HNodeCount(child);
      }
      return n;
    }
      break;
    case hnode_e::Record: {
      hnode::Record* h = static_cast<hnode::Record*>(UP_h);
      n = 1;
      for (ListIter<hnode_asdl::Field*> it(h->fields); !it.Done(); it.Next()) {
        hnode_asdl::Field* field = it.Value();
        StackRoot _for(&field      );
        n += _HNodeCount(field->val);
      }
      if (h->unnamed_fields != nullptr) {
        for (ListIter<hnode_asdl::hnode_t*> it(h->unnamed_fields); !it.Done(); it.Next()) {
          hnode_asdl::hnode_t* child = it.Value();
          StackRoot _for(&child        );
          n += _HNodeCount(child);
        }
      }
      return n;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

int _DocCount(pretty_asdl::doc_t* d) {
  pretty_asdl::doc_t* UP_d = nullptr;
  int n;
  StackRoot _root0(&d);
  StackRoot _root1(&UP_d);

  UP_d = d;
  switch (d->tag()) {
    case doc_e::Break: {
      return 1;
    }
      break;
    case doc_e::Text: {
      return 1;
    }
      break;
    case doc_e::Indent: {
      doc::Indent* d = static_cast<doc::Indent*>(UP_d);
      return (1 + _DocCount(d->mdoc->doc));
    }
      break;
    case doc_e::Group: {
      MeasuredDoc* d = static_cast<MeasuredDoc*>(UP_d);
      return (1 + _DocCount(d->doc));
    }
      break;
    case doc_e::Flat: {
      doc::Flat* d = static_cast<doc::Flat*>(UP_d);
      return (1 + _DocCount(d->mdoc->doc));
    }
      break;
    case doc_e::IfFlat: {
      doc::IfFlat* d = static_cast<doc::IfFlat*>(UP_d);
      return ((1 + _DocCount(d->flat_mdoc->doc)) + _DocCount(d->nonflat_mdoc->doc));
    }
      break;
    case doc_e::Concat: {
      List_Measured* d = static_cast<List_Measured*>(UP_d);
      n = 1;
      for (ListIter<pretty_asdl::MeasuredDoc*> it(d); !it.Done(); it.Next()) {
        pretty_asdl::MeasuredDoc* mdoc = it.Value();
        StackRoot _for(&mdoc      );
        n += _DocCount(mdoc->doc);
      }
      return n;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

void _HNodePrettyPrint(bool perf_stats, bool doc_debug, hnode_asdl::hnode_t* node, mylib::Writer* f, int max_width) {
  pp_hnode::HNodeEncoder* enc = nullptr;
  pretty_asdl::MeasuredDoc* d = nullptr;
  hnode_asdl::hnode_t* p = nullptr;
  pretty::PrettyPrinter* printer = nullptr;
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&f);
  StackRoot _root2(&enc);
  StackRoot _root3(&d);
  StackRoot _root4(&p);
  StackRoot _root5(&printer);
  StackRoot _root6(&buf);

  mylib::MaybeCollect();
  if (perf_stats) {
    mylib::print_stderr(StrFormat("___ HNODE COUNT %d", _HNodeCount(node)));
    mylib::print_stderr(S_Aoo);
  }
  enc = Alloc<pp_hnode::HNodeEncoder>();
  enc->SetUseStyles(f->isatty());
  enc->SetIndent(2);
  d = enc->HNode(node);
  mylib::MaybeCollect();
  if (perf_stats) {
    if (doc_debug) {
      p = d->PrettyTree(false);
      _HNodePrettyPrint(perf_stats, false, p, f);
    }
    mylib::print_stderr(StrFormat("___ DOC COUNT %d", _DocCount(d)));
    mylib::print_stderr(S_Aoo);
  }
  printer = Alloc<pretty::PrettyPrinter>(max_width);
  buf = Alloc<mylib::BufWriter>();
  printer->PrintDoc(d, buf);
  f->write(buf->getvalue());
  f->write(S_nfs);
  mylib::MaybeCollect();
  if (perf_stats) {
    mylib::print_stderr(S_gfw);
    mylib::PrintGcStats();
    mylib::print_stderr(S_Aoo);
  }
}

void HNodePrettyPrint(hnode_asdl::hnode_t* node, mylib::Writer* f, int max_width) {
  StackRoot _root0(&node);
  StackRoot _root1(&f);

  _HNodePrettyPrint(false, true, node, f, max_width);
}

}  // define namespace format

namespace j8_lite {  // define


BigStr* EncodeString(BigStr* s, bool unquoted_ok) {
  StackRoot _root0(&s);

  if ((unquoted_ok and fastfunc::CanOmitQuotes(s))) {
    return s;
  }
  return fastfunc::J8EncodeString(s, 1);
}

BigStr* YshEncodeString(BigStr* s) {
  StackRoot _root0(&s);

  return fastfunc::ShellEncodeString(s, 1);
}

BigStr* MaybeShellEncode(BigStr* s) {
  StackRoot _root0(&s);

  if (fastfunc::CanOmitQuotes(s)) {
    return s;
  }
  return fastfunc::ShellEncodeString(s, 0);
}

BigStr* ShellEncode(BigStr* s) {
  StackRoot _root0(&s);

  return fastfunc::ShellEncodeString(s, 0);
}

BigStr* YshEncode(BigStr* s, bool unquoted_ok) {
  StackRoot _root0(&s);

  if ((unquoted_ok and fastfunc::CanOmitQuotes(s))) {
    return s;
  }
  return fastfunc::ShellEncodeString(s, 1);
}

}  // define namespace j8_lite

namespace ansi {  // define

BigStr* RESET = S_yfk;
BigStr* BOLD = S_aaF;
BigStr* UNDERLINE = S_sCc;
BigStr* REVERSE = S_woy;
BigStr* RED = S_sqm;
BigStr* GREEN = S_eda;
BigStr* YELLOW = S_ysf;
BigStr* BLUE = S_osl;
BigStr* MAGENTA = S_vie;
BigStr* CYAN = S_mmi;
BigStr* WHITE = S_rpo;

}  // define namespace ansi

namespace pp_hnode {  // define

using hnode_asdl::hnode;
using hnode_asdl::hnode_e;
using hnode_asdl::hnode_t;
using hnode_asdl::Field;
using hnode_asdl::color_e;
using pretty_asdl::doc;
using pretty_asdl::MeasuredDoc;
using pretty_asdl::Measure;
using pretty::_Break;
using pretty::_Concat;
using pretty::_Flat;
using pretty::_Group;
using pretty::_IfFlat;
using pretty::_Indent;
using pretty::_EmptyMeasure;
using pretty::AsciiText;

BaseEncoder::BaseEncoder() {
  this->indent = 4;
  this->use_styles = true;
  this->max_tabular_width = 22;
  this->visiting = Alloc<Dict<int, bool>>();
}

void BaseEncoder::SetIndent(int indent) {
  this->indent = indent;
}

void BaseEncoder::SetUseStyles(bool use_styles) {
  this->use_styles = use_styles;
}

void BaseEncoder::SetMaxTabularWidth(int max_tabular_width) {
  this->max_tabular_width = max_tabular_width;
}

pretty_asdl::MeasuredDoc* BaseEncoder::_Styled(BigStr* style, pretty_asdl::MeasuredDoc* mdoc) {
  StackRoot _root0(&style);
  StackRoot _root1(&mdoc);

  if (this->use_styles) {
    return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{Alloc<MeasuredDoc>(Alloc<doc::Text>(style), _EmptyMeasure()), mdoc, Alloc<MeasuredDoc>(Alloc<doc::Text>(ansi::RESET), _EmptyMeasure())}));
  }
  else {
    return mdoc;
  }
}

pretty_asdl::MeasuredDoc* BaseEncoder::_StyledAscii(BigStr* style, BigStr* s) {
  pretty_asdl::Measure* measure = nullptr;
  StackRoot _root0(&style);
  StackRoot _root1(&s);
  StackRoot _root2(&measure);

  measure = Alloc<Measure>(len(s), -1);
  if (this->use_styles) {
    s = StrFormat("%s%s%s", style, s, ansi::RESET);
  }
  return Alloc<MeasuredDoc>(Alloc<doc::Text>(s), measure);
}

pretty_asdl::MeasuredDoc* BaseEncoder::_Surrounded(BigStr* left, pretty_asdl::MeasuredDoc* mdoc, BigStr* right) {
  StackRoot _root0(&left);
  StackRoot _root1(&mdoc);
  StackRoot _root2(&right);

  return _Group(_Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(left), _Indent(this->indent, _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{_Break(S_Aoo), mdoc}))), _Break(S_Aoo), AsciiText(right)})));
}

pretty_asdl::MeasuredDoc* BaseEncoder::_SurroundedAndPrefixed(BigStr* left, pretty_asdl::MeasuredDoc* prefix, BigStr* sep, pretty_asdl::MeasuredDoc* mdoc, BigStr* right) {
  StackRoot _root0(&left);
  StackRoot _root1(&prefix);
  StackRoot _root2(&sep);
  StackRoot _root3(&mdoc);
  StackRoot _root4(&right);

  return _Group(_Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(left), prefix, _Indent(this->indent, _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{_Break(sep), mdoc}))), _Break(S_Aoo), AsciiText(right)})));
}

pretty_asdl::MeasuredDoc* BaseEncoder::_Join(List<pretty_asdl::MeasuredDoc*>* items, BigStr* sep, BigStr* space) {
  List<pretty_asdl::MeasuredDoc*>* seq = nullptr;
  int i;
  StackRoot _root0(&items);
  StackRoot _root1(&sep);
  StackRoot _root2(&space);
  StackRoot _root3(&seq);

  seq = Alloc<List<pretty_asdl::MeasuredDoc*>>();
  i = 0;
  for (ListIter<pretty_asdl::MeasuredDoc*> it(items); !it.Done(); it.Next(), ++i) {
    pretty_asdl::MeasuredDoc* item = it.Value();
    StackRoot _for(&item  );
    if (i != 0) {
      seq->append(AsciiText(sep));
      seq->append(_Break(space));
    }
    seq->append(item);
  }
  return _Concat(seq);
}

pretty_asdl::MeasuredDoc* BaseEncoder::_Tabular(List<pretty_asdl::MeasuredDoc*>* items, BigStr* sep) {
  int max_flat_len;
  List<pretty_asdl::MeasuredDoc*>* seq = nullptr;
  int i;
  pretty_asdl::MeasuredDoc* non_tabular = nullptr;
  int sep_width;
  List<pretty_asdl::MeasuredDoc*>* tabular_seq = nullptr;
  int padding;
  pretty_asdl::MeasuredDoc* tabular = nullptr;
  StackRoot _root0(&items);
  StackRoot _root1(&sep);
  StackRoot _root2(&seq);
  StackRoot _root3(&non_tabular);
  StackRoot _root4(&tabular_seq);
  StackRoot _root5(&tabular);

  if (len(items) == 0) {
    return AsciiText(S_Aoo);
  }
  max_flat_len = 0;
  seq = Alloc<List<pretty_asdl::MeasuredDoc*>>();
  i = 0;
  for (ListIter<pretty_asdl::MeasuredDoc*> it(items); !it.Done(); it.Next(), ++i) {
    pretty_asdl::MeasuredDoc* item = it.Value();
    StackRoot _for(&item  );
    if (i != 0) {
      seq->append(AsciiText(sep));
      seq->append(_Break(S_yfw));
    }
    seq->append(item);
    max_flat_len = max(max_flat_len, item->measure->flat);
  }
  non_tabular = _Concat(seq);
  sep_width = len(sep);
  if (((max_flat_len + sep_width) + 1) <= this->max_tabular_width) {
    tabular_seq = Alloc<List<pretty_asdl::MeasuredDoc*>>();
    i = 0;
    for (ListIter<pretty_asdl::MeasuredDoc*> it(items); !it.Done(); it.Next(), ++i) {
      pretty_asdl::MeasuredDoc* item = it.Value();
      StackRoot _for(&item    );
      tabular_seq->append(_Flat(item));
      if (i != (len(items) - 1)) {
        padding = ((max_flat_len - item->measure->flat) + 1);
        tabular_seq->append(AsciiText(sep));
        tabular_seq->append(_Group(_Break(str_repeat(S_yfw, padding))));
      }
    }
    tabular = _Concat(tabular_seq);
    return _Group(_IfFlat(non_tabular, tabular));
  }
  else {
    return non_tabular;
  }
}

HNodeEncoder::HNodeEncoder() : ::pp_hnode::BaseEncoder() {
  this->type_color = ansi::YELLOW;
  this->field_color = ansi::MAGENTA;
}

pretty_asdl::MeasuredDoc* HNodeEncoder::HNode(hnode_asdl::hnode_t* h) {
  StackRoot _root0(&h);

  this->visiting->clear();
  return this->_HNode(h);
}

pretty_asdl::MeasuredDoc* HNodeEncoder::_Field(hnode_asdl::Field* field) {
  pretty_asdl::MeasuredDoc* name = nullptr;
  StackRoot _root0(&field);
  StackRoot _root1(&name);

  name = AsciiText(str_concat(field->name, S_fyj));
  return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{name, this->_HNode(field->val)}));
}

pretty_asdl::MeasuredDoc* HNodeEncoder::_HNode(hnode_asdl::hnode_t* h) {
  hnode_asdl::hnode_t* UP_h = nullptr;
  BigStr* color = nullptr;
  BigStr* s = nullptr;
  List<pretty_asdl::MeasuredDoc*>* children = nullptr;
  pretty_asdl::MeasuredDoc* type_name = nullptr;
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  List<pretty_asdl::MeasuredDoc*>* m = nullptr;
  pretty_asdl::MeasuredDoc* child = nullptr;
  StackRoot _root0(&h);
  StackRoot _root1(&UP_h);
  StackRoot _root2(&color);
  StackRoot _root3(&s);
  StackRoot _root4(&children);
  StackRoot _root5(&type_name);
  StackRoot _root6(&mdocs);
  StackRoot _root7(&m);
  StackRoot _root8(&child);

  UP_h = h;
  switch (h->tag()) {
    case hnode_e::AlreadySeen: {
      hnode::AlreadySeen* h = static_cast<hnode::AlreadySeen*>(UP_h);
      return pretty::AsciiText(StrFormat("...0x%s", mylib::hex_lower(h->heap_id)));
    }
      break;
    case hnode_e::Leaf: {
      hnode::Leaf* h = static_cast<hnode::Leaf*>(UP_h);
      switch (h->color) {
        case color_e::TypeName: {
          color = ansi::YELLOW;
        }
          break;
        case color_e::StringConst: {
          color = ansi::BOLD;
        }
          break;
        case color_e::OtherConst: {
          color = ansi::GREEN;
        }
          break;
        case color_e::External: {
          color = str_concat(ansi::BOLD, ansi::BLUE);
        }
          break;
        case color_e::UserType: {
          color = ansi::GREEN;
        }
          break;
        default: {
          assert(0);  // AssertionError
        }
      }
      s = j8_lite::EncodeString(h->s, true);
      return this->_StyledAscii(color, s);
    }
      break;
    case hnode_e::Array: {
      hnode::Array* h = static_cast<hnode::Array*>(UP_h);
      mylib::MaybeCollect();
      if (len(h->children) == 0) {
        return AsciiText(S_xmu);
      }
      children = Alloc<List<pretty_asdl::MeasuredDoc*>>();
      for (ListIter<hnode_asdl::hnode_t*> it(h->children); !it.Done(); it.Next()) {
        hnode_asdl::hnode_t* item = it.Value();
        children->append(this->_HNode(item));
      }
      return this->_Surrounded(S_Eax, this->_Tabular(children, S_Aoo), S_pcD);
    }
      break;
    case hnode_e::Record: {
      hnode::Record* h = static_cast<hnode::Record*>(UP_h);
      type_name = nullptr;
      if (len(h->node_type)) {
        type_name = this->_StyledAscii(this->type_color, h->node_type);
      }
      mdocs = nullptr;
      if ((h->unnamed_fields != nullptr and len(h->unnamed_fields))) {
        mdocs = Alloc<List<pretty_asdl::MeasuredDoc*>>();
        for (ListIter<hnode_asdl::hnode_t*> it(h->unnamed_fields); !it.Done(); it.Next()) {
          hnode_asdl::hnode_t* item = it.Value();
          mdocs->append(this->_HNode(item));
        }
      }
      else {
        if (len(h->fields) != 0) {
          mdocs = Alloc<List<pretty_asdl::MeasuredDoc*>>();
          for (ListIter<hnode_asdl::Field*> it(h->fields); !it.Done(); it.Next()) {
            hnode_asdl::Field* field = it.Value();
            mdocs->append(this->_Field(field));
          }
        }
      }
      if (mdocs == nullptr) {
        m = NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(h->left)});
        if (type_name != nullptr) {
          m->append(type_name);
        }
        m->append(AsciiText(h->right));
        return _Concat(m);
      }
      child = this->_Join(mdocs, S_Aoo, S_yfw);
      if (type_name != nullptr) {
        return this->_SurroundedAndPrefixed(h->left, type_name, S_yfw, child, h->right);
      }
      else {
        return this->_Surrounded(h->left, child, h->right);
      }
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

}  // define namespace pp_hnode

namespace pretty {  // define

using pretty_asdl::doc;
using pretty_asdl::doc_e;
using pretty_asdl::DocFragment;
using pretty_asdl::Measure;
using pretty_asdl::MeasuredDoc;
using pretty_asdl::List_Measured;
using mylib::BufWriter;

pretty_asdl::Measure* _EmptyMeasure() {
  return Alloc<Measure>(0, -1);
}

pretty_asdl::Measure* _FlattenMeasure(pretty_asdl::Measure* measure) {
  StackRoot _root0(&measure);

  return Alloc<Measure>(measure->flat, -1);
}

pretty_asdl::Measure* _ConcatMeasure(pretty_asdl::Measure* m1, pretty_asdl::Measure* m2) {
  StackRoot _root0(&m1);
  StackRoot _root1(&m2);

  if (m1->nonflat != -1) {
    return Alloc<Measure>((m1->flat + m2->flat), m1->nonflat);
  }
  else {
    if (m2->nonflat != -1) {
      return Alloc<Measure>((m1->flat + m2->flat), (m1->flat + m2->nonflat));
    }
    else {
      return Alloc<Measure>((m1->flat + m2->flat), -1);
    }
  }
}

int _SuffixLen(pretty_asdl::Measure* measure) {
  StackRoot _root0(&measure);

  if (measure->nonflat != -1) {
    return measure->nonflat;
  }
  else {
    return measure->flat;
  }
}

pretty_asdl::MeasuredDoc* AsciiText(BigStr* string) {
  StackRoot _root0(&string);

  return Alloc<MeasuredDoc>(Alloc<doc::Text>(string), Alloc<Measure>(len(string), -1));
}

pretty_asdl::MeasuredDoc* _Break(BigStr* string) {
  StackRoot _root0(&string);

  return Alloc<MeasuredDoc>(Alloc<doc::Break>(string), Alloc<Measure>(len(string), 0));
}

pretty_asdl::MeasuredDoc* _Indent(int indent, pretty_asdl::MeasuredDoc* mdoc) {
  StackRoot _root0(&mdoc);

  return Alloc<MeasuredDoc>(Alloc<doc::Indent>(indent, mdoc), mdoc->measure);
}

pretty_asdl::Measure* _Splice(List<pretty_asdl::MeasuredDoc*>* out, List<pretty_asdl::MeasuredDoc*>* mdocs) {
  pretty_asdl::Measure* measure = nullptr;
  pretty_asdl::List_Measured* child = nullptr;
  StackRoot _root0(&out);
  StackRoot _root1(&mdocs);
  StackRoot _root2(&measure);
  StackRoot _root3(&child);

  measure = _EmptyMeasure();
  for (ListIter<pretty_asdl::MeasuredDoc*> it(mdocs); !it.Done(); it.Next()) {
    pretty_asdl::MeasuredDoc* mdoc = it.Value();
    StackRoot _for(&mdoc  );
    switch (mdoc->doc->tag()) {
      case doc_e::Concat: {
        child = static_cast<List_Measured*>(mdoc->doc);
        _Splice(out, child);
      }
        break;
      default: {
        out->append(mdoc);
      }
    }
    measure = _ConcatMeasure(measure, mdoc->measure);
  }
  return measure;
}

pretty_asdl::MeasuredDoc* _Concat(List<pretty_asdl::MeasuredDoc*>* mdocs) {
  pretty_asdl::List_Measured* flattened = nullptr;
  pretty_asdl::Measure* measure = nullptr;
  StackRoot _root0(&mdocs);
  StackRoot _root1(&flattened);
  StackRoot _root2(&measure);

  flattened = List_Measured::New();
  measure = _Splice(flattened, mdocs);
  return Alloc<MeasuredDoc>(flattened, measure);
}

pretty_asdl::MeasuredDoc* _Group(pretty_asdl::MeasuredDoc* mdoc) {
  StackRoot _root0(&mdoc);

  return Alloc<MeasuredDoc>(mdoc, mdoc->measure);
}

pretty_asdl::MeasuredDoc* _IfFlat(pretty_asdl::MeasuredDoc* flat_mdoc, pretty_asdl::MeasuredDoc* nonflat_mdoc) {
  StackRoot _root0(&flat_mdoc);
  StackRoot _root1(&nonflat_mdoc);

  return Alloc<MeasuredDoc>(Alloc<doc::IfFlat>(flat_mdoc, nonflat_mdoc), Alloc<Measure>(flat_mdoc->measure->flat, nonflat_mdoc->measure->nonflat));
}

pretty_asdl::MeasuredDoc* _Flat(pretty_asdl::MeasuredDoc* mdoc) {
  StackRoot _root0(&mdoc);

  return Alloc<MeasuredDoc>(Alloc<doc::Flat>(mdoc), _FlattenMeasure(mdoc->measure));
}

PrettyPrinter::PrettyPrinter(int max_width) {
  this->max_width = max_width;
}

bool PrettyPrinter::_Fits(int prefix_len, pretty_asdl::MeasuredDoc* group, pretty_asdl::Measure* suffix_measure) {
  pretty_asdl::Measure* measure = nullptr;
  StackRoot _root0(&group);
  StackRoot _root1(&suffix_measure);
  StackRoot _root2(&measure);

  measure = _ConcatMeasure(_FlattenMeasure(group->measure), suffix_measure);
  return (prefix_len + _SuffixLen(measure)) <= this->max_width;
}

void PrettyPrinter::PrintDoc(pretty_asdl::MeasuredDoc* document, mylib::BufWriter* buf) {
  int prefix_len;
  List<pretty_asdl::DocFragment*>* fragments = nullptr;
  int max_stack;
  pretty_asdl::DocFragment* frag = nullptr;
  pretty_asdl::doc_t* UP_doc = nullptr;
  pretty_asdl::Measure* measure = nullptr;
  bool is_flat;
  pretty_asdl::MeasuredDoc* subdoc = nullptr;
  StackRoot _root0(&document);
  StackRoot _root1(&buf);
  StackRoot _root2(&fragments);
  StackRoot _root3(&frag);
  StackRoot _root4(&UP_doc);
  StackRoot _root5(&measure);
  StackRoot _root6(&subdoc);

  prefix_len = 0;
  fragments = NewList<pretty_asdl::DocFragment*>(std::initializer_list<pretty_asdl::DocFragment*>{Alloc<DocFragment>(_Group(document), 0, false, _EmptyMeasure())});
  max_stack = len(fragments);
  while (len(fragments) > 0) {
    max_stack = max(max_stack, len(fragments));
    frag = fragments->pop();
    UP_doc = frag->mdoc->doc;
    switch (UP_doc->tag()) {
      case doc_e::Text: {
        doc::Text* text = static_cast<doc::Text*>(UP_doc);
        buf->write(text->string);
        prefix_len += frag->mdoc->measure->flat;
      }
        break;
      case doc_e::Break: {
        doc::Break* break_ = static_cast<doc::Break*>(UP_doc);
        if (frag->is_flat) {
          buf->write(break_->string);
          prefix_len += frag->mdoc->measure->flat;
        }
        else {
          buf->write(S_nfs);
          buf->write_spaces(frag->indent);
          prefix_len = frag->indent;
        }
      }
        break;
      case doc_e::Indent: {
        doc::Indent* indented = static_cast<doc::Indent*>(UP_doc);
        fragments->append(Alloc<DocFragment>(indented->mdoc, (frag->indent + indented->indent), frag->is_flat, frag->measure));
      }
        break;
      case doc_e::Concat: {
        List_Measured* concat = static_cast<List_Measured*>(UP_doc);
        measure = frag->measure;
        for (ReverseListIter<pretty_asdl::MeasuredDoc*> it(concat); !it.Done(); it.Next()) {
          pretty_asdl::MeasuredDoc* mdoc = it.Value();
          StackRoot _for(&mdoc        );
          fragments->append(Alloc<DocFragment>(mdoc, frag->indent, frag->is_flat, measure));
          measure = _ConcatMeasure(mdoc->measure, measure);
        }
      }
        break;
      case doc_e::Group: {
        MeasuredDoc* group = static_cast<MeasuredDoc*>(UP_doc);
        is_flat = this->_Fits(prefix_len, group, frag->measure);
        fragments->append(Alloc<DocFragment>(group, frag->indent, is_flat, frag->measure));
      }
        break;
      case doc_e::IfFlat: {
        doc::IfFlat* if_flat = static_cast<doc::IfFlat*>(UP_doc);
        if (frag->is_flat) {
          subdoc = if_flat->flat_mdoc;
        }
        else {
          subdoc = if_flat->nonflat_mdoc;
        }
        fragments->append(Alloc<DocFragment>(subdoc, frag->indent, frag->is_flat, frag->measure));
      }
        break;
      case doc_e::Flat: {
        doc::Flat* flat_doc = static_cast<doc::Flat*>(UP_doc);
        fragments->append(Alloc<DocFragment>(flat_doc->mdoc, frag->indent, true, frag->measure));
      }
        break;
    }
  }
}

}  // define namespace pretty

namespace parse {  // define

using expr_asdl::expr;
using expr_asdl::expr_e;
using expr_asdl::expr_t;
using expr_asdl::tok_e;
using expr_asdl::tok_t;
using expr_asdl::CompoundWord;
using expr_asdl::Measure_v;
using expr_asdl::MeasuredDoc;
namespace fmt = format;

Lexer::Lexer(BigStr* s) {
  this->s = s;
  this->i = 0;
  this->n = len(s);
}

Tuple2<expr_asdl::tok_t, BigStr*> Lexer::Read() {
  BigStr* tok = nullptr;
  StackRoot _root0(&tok);

  if (this->i >= this->n) {
    return Tuple2<expr_asdl::tok_t, BigStr*>(tok_e::Eof, S_Aoo);
  }
  tok = this->s->at(this->i);
  this->i += 1;
  if (tok->isdigit()) {
    return Tuple2<expr_asdl::tok_t, BigStr*>(tok_e::Const, tok);
  }
  if (tok->isalpha()) {
    return Tuple2<expr_asdl::tok_t, BigStr*>(tok_e::Var, tok);
  }
  if (str_contains(S_jau, tok)) {
    return Tuple2<expr_asdl::tok_t, BigStr*>(tok_e::Op1, tok);
  }
  if (str_contains(S_fFq, tok)) {
    return Tuple2<expr_asdl::tok_t, BigStr*>(tok_e::Op2, tok);
  }
  if (str_contains(S_zxb, tok)) {
    return Tuple2<expr_asdl::tok_t, BigStr*>(tok_e::Paren, tok);
  }
  return Tuple2<expr_asdl::tok_t, BigStr*>(tok_e::Invalid, tok);
}

void Lexer::_MethodCallingOtherMethod() {
  this->Read();
}

ParseError::ParseError(BigStr* msg) {
  this->msg = msg;
}

Parser::Parser(parse::Lexer* lexer) {
  this->lexer = lexer;
  this->tok_type = tok_e::Eof;
  this->tok_val = S_Aoo;
}

void Parser::Next() {
  Tuple2<expr_asdl::tok_t, BigStr*> tup0 = this->lexer->Read();
  this->tok_type = tup0.at0();
  this->tok_val = tup0.at1();
}

void Parser::Eat(BigStr* tok_val) {
  StackRoot _root0(&tok_val);

  if (!(str_equals(this->tok_val, tok_val))) {
    throw Alloc<ParseError>(str_concat(S_rFn, tok_val));
  }
  this->Next();
}

expr_asdl::expr_t* Parser::ParseFactor() {
  expr::Var* n1 = nullptr;
  expr::Const* n2 = nullptr;
  expr_asdl::expr_t* n3 = nullptr;
  StackRoot _root0(&n1);
  StackRoot _root1(&n2);
  StackRoot _root2(&n3);

  if (this->tok_type == tok_e::Var) {
    n1 = Alloc<expr::Var>(this->tok_val);
    this->Next();
    return n1;
  }
  if (this->tok_type == tok_e::Const) {
    n2 = Alloc<expr::Const>(to_int(this->tok_val));
    this->Next();
    return n2;
  }
  if (this->tok_type == tok_e::Paren) {
    this->Eat(S_ijB);
    n3 = this->ParseExpr();
    this->Eat(S_hxb);
    return n3;
  }
  throw Alloc<ParseError>(str_concat(S_pqw, this->tok_val));
}

expr_asdl::expr_t* Parser::ParseTerm() {
  expr_asdl::expr_t* node = nullptr;
  BigStr* op = nullptr;
  expr_asdl::expr_t* n2 = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&op);
  StackRoot _root2(&n2);

  node = this->ParseFactor();
  while (this->tok_type == tok_e::Op2) {
    op = this->tok_val;
    this->Next();
    n2 = this->ParseFactor();
    node = Alloc<expr::Binary>(op, node, n2);
  }
  return node;
}

expr_asdl::expr_t* Parser::ParseExpr() {
  expr_asdl::expr_t* node = nullptr;
  BigStr* op = nullptr;
  expr_asdl::expr_t* n2 = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&op);
  StackRoot _root2(&n2);

  node = this->ParseTerm();
  while (this->tok_type == tok_e::Op1) {
    op = this->tok_val;
    this->Next();
    n2 = this->ParseTerm();
    node = Alloc<expr::Binary>(op, node, n2);
  }
  return node;
}

expr_asdl::expr_t* Parser::Parse() {
  this->Next();
  return this->ParseExpr();
}

void TestParse() {
  parse::Lexer* lex = nullptr;
  expr_asdl::tok_t tok_type;
  BigStr* tok_val = nullptr;
  List<BigStr*>* CASES = nullptr;
  parse::Parser* p = nullptr;
  expr_asdl::expr_t* node = nullptr;
  hnode_asdl::hnode_t* htree = nullptr;
  mylib::Writer* f = nullptr;
  expr_asdl::expr_t* UP_node = nullptr;
  StackRoot _root0(&lex);
  StackRoot _root1(&tok_val);
  StackRoot _root2(&CASES);
  StackRoot _root3(&p);
  StackRoot _root4(&node);
  StackRoot _root5(&htree);
  StackRoot _root6(&f);
  StackRoot _root7(&UP_node);

  lex = Alloc<Lexer>(S_jng);
  while (true) {
    Tuple2<expr_asdl::tok_t, BigStr*> tup1 = lex->Read();
    tok_type = tup1.at0();
    tok_val = tup1.at1();
    if (tok_type == tok_e::Eof) {
      break;
    }
    mylib::print_stderr(StrFormat("tok_val %s", tok_val));
  }
  CASES = NewList<BigStr*>(std::initializer_list<BigStr*>{S_nmE, S_eei, S_sdy, S_anb, S_Bth, S_fhr, S_vrA, S_gCD, S_ijB, S_hxb, S_qey, S_yfw, S_utu});
  for (ListIter<BigStr*> it(CASES); !it.Done(); it.Next()) {
    BigStr* expr_ = it.Value();
    StackRoot _for(&expr_  );
    lex = Alloc<Lexer>(expr_);
    p = Alloc<Parser>(lex);
    mylib::print_stderr(S_Aoo);
    mylib::print_stderr(S_gpk);
    mylib::print_stderr(StrFormat("%s =>", expr_));
    node = nullptr;
    try {
      node = p->Parse();
    }
    catch (ParseError* e) {
      mylib::print_stderr(StrFormat("Parse error: %s", e->msg));
      continue;
    }
    htree = node->PrettyTree(false);
    f = mylib::Stdout();
    fmt::HNodePrettyPrint(htree, f);
    UP_node = node;
    switch (UP_node->tag()) {
      case expr_e::Const: {
        expr::Const* node = static_cast<expr::Const*>(UP_node);
        mylib::print_stderr(StrFormat("Const %d", node->i));
      }
        break;
      case expr_e::Var: {
        expr::Var* node = static_cast<expr::Var*>(UP_node);
        mylib::print_stderr(StrFormat("Var %s", node->name));
      }
        break;
      default: {
        mylib::print_stderr(S_otA);
      }
    }
  }
}

void TestCreateNull() {
  expr::Const* c = nullptr;
  expr::Var* v = nullptr;
  expr::Binary* b = nullptr;
  hnode_asdl::hnode_t* htree = nullptr;
  mylib::Writer* f = nullptr;
  StackRoot _root0(&c);
  StackRoot _root1(&v);
  StackRoot _root2(&b);
  StackRoot _root3(&htree);
  StackRoot _root4(&f);

  c = expr::Const::CreateNull(true);
  mylib::print_stderr(StrFormat("c.i %d", c->i));
  v = expr::Var::CreateNull(true);
  mylib::print_stderr(StrFormat("v.name %r", v->name));
  b = expr::Binary::CreateNull(true);
  mylib::print_stderr(StrFormat("b.op %r", b->op));
  b->op = S_jnE;
  b->left = c;
  b->right = v;
  htree = b->PrettyTree(false);
  f = mylib::Stdout();
  fmt::HNodePrettyPrint(htree, f);
}

void TestSubtype() {
  expr_asdl::CompoundWord* c = nullptr;
  BigStr* s1 = nullptr;
  List<BigStr*>* strs = nullptr;
  expr_asdl::CompoundWord* c3 = nullptr;
  expr_asdl::CompoundWord* c4 = nullptr;
  StackRoot _root0(&c);
  StackRoot _root1(&s1);
  StackRoot _root2(&strs);
  StackRoot _root3(&c3);
  StackRoot _root4(&c4);

  c = CompoundWord::New();
  c->append(S_lqB);
  c->append(S_clt);
  mylib::print_stderr(StrFormat("len(c) = %d", len(c)));
  s1 = c->at(1);
  mylib::print_stderr(StrFormat("s1 = %r", s1));
  c->set(1, S_xww);
  mylib::print_stderr(StrFormat("c[1] = %r", c->at(1)));
  for (ListIter<BigStr*> it(c); !it.Done(); it.Next()) {
    BigStr* s = it.Value();
    StackRoot _for(&s  );
    mylib::print_stderr(StrFormat("s = %r", s));
  }
  strs = NewList<BigStr*>(std::initializer_list<BigStr*>{S_gCD, S_jFv, S_emj, S_Crn});
  c3 = static_cast<CompoundWord*>(strs);
  mylib::print_stderr(StrFormat("len(c3) = %d", len(c3)));
  c4 = CompoundWord::Take(strs);
  mylib::print_stderr(StrFormat("len(c4) = %d", len(c4)));
  mylib::print_stderr(StrFormat("len(strs) = %d", len(strs)));
  if (c->at(0) == nullptr) {
    mylib::print_stderr(S_wqv);
  }
  else {
    mylib::print_stderr(S_wkf);
  }
  mylib::print_stderr(StrFormat("c4[0] = %s", c4->at(0)));
  strs->append(S_ysz);
  mylib::print_stderr(StrFormat("len(strs) = %d", len(strs)));
  for (ListIter<BigStr*> it(c4); !it.Done(); it.Next()) {
    BigStr* s = it.Value();
    StackRoot _for(&s  );
    print(StrFormat("s = %r", s));
  }
}

void TestLeafValue() {
  mylib::Writer* f = nullptr;
  int n;
  expr_asdl::Measure_v* m = nullptr;
  expr_asdl::MeasuredDoc* d = nullptr;
  hnode_asdl::hnode_t* tree = nullptr;
  StackRoot _root0(&f);
  StackRoot _root1(&m);
  StackRoot _root2(&d);
  StackRoot _root3(&tree);

  f = mylib::Stdout();
  n = 10;
  for (int i = 0; i < n; ++i) {
    m = Alloc<Measure_v>(i, (i + 1));
    d = Alloc<MeasuredDoc>(StrFormat("s%d", i), m);
    tree = d->PrettyTree(false);
    fmt::HNodePrettyPrint(tree, f);
  }
}

void run_tests() {
  TestParse();
  TestCreateNull();
  TestSubtype();
  TestLeafValue();
}

void run_benchmarks() {
  int n;
  int result;
  int i;
  parse::Lexer* lex = nullptr;
  parse::Parser* p = nullptr;
  expr_asdl::expr_t* tree = nullptr;
  StackRoot _root0(&lex);
  StackRoot _root1(&p);
  StackRoot _root2(&tree);

  n = 100000;
  result = 0;
  i = 0;
  while (i < n) {
    lex = Alloc<Lexer>(S_fhr);
    p = Alloc<Parser>(lex);
    tree = p->Parse();
    i += 1;
    mylib::MaybeCollect();
  }
  mylib::print_stderr(StrFormat("result = %d", result));
  mylib::print_stderr(StrFormat("iterations = %d", n));
}

}  // define namespace parse

int main(int argc, char **argv) {
  gHeap.Init();

  char* b = getenv("BENCHMARK");
  if (b && strlen(b)) {  // match Python's logic
    fprintf(stderr, "Benchmarking...\n");
    parse::run_benchmarks();
  } else {
    parse::run_tests();
  }

  gHeap.CleanProcessExit();
}