// examples/control_flow translated by mycpp

// BEGIN mycpp output

#include "mycpp/runtime.h"

GLOBAL_STR(str0, "one");
GLOBAL_STR(str1, "two");
GLOBAL_STR(str2, "three");
GLOBAL_STR(str3, "other number");
GLOBAL_STR(str4, "f");
GLOBAL_STR(str5, "started with f");
GLOBAL_STR(str6, "");
GLOBAL_STR(str7, "foo");
GLOBAL_STR(str8, "bar");
GLOBAL_STR(str9, "error: %s");
GLOBAL_STR(str10, "result = %s");
GLOBAL_STR(str11, "");
GLOBAL_STR(str12, "");
GLOBAL_STR(str13, "fail");
GLOBAL_STR(str14, "ok");
GLOBAL_STR(str15, "ok");
GLOBAL_STR(str16, "ok");
GLOBAL_STR(str17, "num_exceptions = %d");
GLOBAL_STR(str18, "Ran %d iterations of try/except");

namespace control_flow {  // forward declare

  class ParseError;

}  // forward declare namespace control_flow

namespace control_flow {  // declare

void IfDemo(int i);
class ParseError {
 public:
  ParseError(BigStr* reason);
  BigStr* reason;

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

  DISALLOW_COPY_AND_ASSIGN(ParseError)
};

BigStr* f(BigStr* s);
void ExceptDemo();
void run_tests();
void run_benchmarks();

}  // declare namespace control_flow

namespace control_flow {  // define


void IfDemo(int i) {
  if (i == 1) {
    print(str0);
  }
  else {
    if (i == 2) {
      print(str1);
    }
    else {
      if (i == 3) {
        print(str2);
      }
      else {
        if (i == 4) {
          ;  // pass
        }
        else {
          print(str3);
        }
      }
    }
  }
}

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

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

  if (str_equals(s->at(0), str4)) {
    throw Alloc<ParseError>(str5);
  }
  return s;
}

void ExceptDemo() {
  BigStr* result = nullptr;
  List<BigStr*>* tmp = nullptr;
  StackRoot _root0(&result);
  StackRoot _root1(&tmp);

  result = str6;
  tmp = NewList<BigStr*>(std::initializer_list<BigStr*>{str7, str8});
  for (ListIter<BigStr*> it(tmp); !it.Done(); it.Next()) {
    BigStr* prog = it.Value();
    StackRoot _for(&prog  );
    try {
      result = f(prog);
    }
    catch (ParseError* e) {
      mylib::print_stderr(StrFormat("error: %s", e->reason));
      continue;
    }
    mylib::print_stderr(StrFormat("result = %s", result));
  }
}

void run_tests() {
  List<int>* tmp = nullptr;
  StackRoot _root0(&tmp);

  tmp = NewList<int>(std::initializer_list<int>{1, 2, 3, 4, 5});
  for (ListIter<int> it(tmp); !it.Done(); it.Next()) {
    int i = it.Value();
    IfDemo(i);
  }
  mylib::print_stderr(str11);
  ExceptDemo();
}

void run_benchmarks() {
  int n;
  BigStr* result = nullptr;
  int num_exceptions;
  int i;
  List<BigStr*>* cases = nullptr;
  StackRoot _root0(&result);
  StackRoot _root1(&cases);

  n = 100000;
  result = str12;
  num_exceptions = 0;
  i = 0;
  cases = NewList<BigStr*>(std::initializer_list<BigStr*>{str13, str14, str15, str16});
  while (i < n) {
    for (ListIter<BigStr*> it(cases); !it.Done(); it.Next()) {
      BigStr* prog = it.Value();
      StackRoot _for(&prog    );
      try {
        result = f(prog);
      }
      catch (ParseError* e) {
        num_exceptions += 1;
        continue;
      }
    }
    i += 1;
    mylib::MaybeCollect();
  }
  mylib::print_stderr(StrFormat("num_exceptions = %d", num_exceptions));
  mylib::print_stderr(StrFormat("Ran %d iterations of try/except", n));
}

}  // define namespace control_flow

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

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

  gHeap.CleanProcessExit();
}