
// recursive descent parser. refer to the wikipedia article.

void echo_comments(int level) {
  int idx;
  // echo out comments up to now
  if (comment_arr_idx > 0) {
    indent(level);
    write_comment_no_blank();
    idx = 1;
    while (idx < comment_arr_idx) {
      if (comment_arr[idx] == '\n') {
        writeln();
        indent(level);
        idx = idx + 1;
        write_comment_no_blank();
      }
      else
        write_char(comment_arr[idx]);
      idx = idx + 1;
    }
    writeln();
    comment_arr_idx = 0;
  }
  return;
}

void expect(int expected_token) {
  int token;
  token = get_token();
  if (token != expected_token)
    log_error(EXPECT_TOKEN, expected_token);
  return;
}

void translate_expression_to_code(int force_slow) {
  stack_idx = 0;
  output_queue_idx = 1;
  // if we were to use 0 it conflicts with 0 null cons val
  shunt(0);

  stack_idx = 0;
  cons_cell_idx = 1;
// TODO move this to global init
cons_cell[0] = 0;
  binary_tree = make_binary_tree(1);

  fixup_binary_tree_if_assignment(binary_tree);

  // by default
  is_simple_assignment_expression = 0;
  // by default
  is_simple_assignment_inc_dec_expression = 0;
  determine_is_simple_assignment_expression(binary_tree);
  if (is_simple_assignment_expression) {
    determine_is_simple_assignment_inc_dec_expression(binary_tree);
  }

  if (force_slow)
    fast_expression = 0;
  else
    fast_expression = get_fast_expression(binary_tree);

  set_fast_relop(binary_tree);

  if (is_pair(binary_tree))
    if (is_scheme_vector_set(binary_tree))
      display_scheme_vector_set(my_cdr(binary_tree));
    else
      display_binary_tree(binary_tree);
  else
    display_leaf_for_scheme(output_queue[binary_tree]);

  //writeln();

  stack_idx = 0;
  if (is_pair(binary_tree))
    traverse_binary_tree(binary_tree);
  else
    push(binary_tree);

  // fast_expression = 0;
  return;
}

void parse_expression(int start_idx, int force_slow) {
  int token;
  tokens_idx = start_idx;
  token = get_token();
  while (!
         (((token == close_paren_tok) && is_identifier(lookahead()))
          || ((token == close_bracket_tok) && is_identifier(lookahead()))
          || ((token == close_paren_tok) && (lookahead() == semi_colon_tok))
          || ((token == close_bracket_tok) && (lookahead() == semi_colon_tok))
          || (is_identifier(token) && (lookahead() == semi_colon_tok))
          || (is_identifier(token) && is_identifier(lookahead())))) {
    tokens[tokens_idx] = token;
    token = get_token();
    tokens_idx = tokens_idx + 1;
    if (tokens_idx >= MAX_TOKENS) {
      log_error(EXPRESSION_TOO_LONG, 0);
      cleanup();
    }
  }

  // store last id of expression
  tokens[tokens_idx] = token;
  tokens_idx = tokens_idx + 1;

  if (lookahead() == semi_colon_tok)
    // statement expression terminator
    dummy = get_token();

  stack_idx = 0;
  are_balanced_parens_and_brackets(0);

  filter_unary_minus();

  translate_expression_to_code(force_slow);
  return;
}

void display_top_level_vars() {
  int i;
  if (top_level_variable_idx != 0)
    for (i = 0; i < top_level_variable_idx; i++) {
      write_int(vector_ref(top_level_variable_position, i));
      write_char(' ');
      write_int(vector_ref(top_level_variable_size, i));
      write_char(' ');
      string_write(vector_ref(top_level_variable_name, i));
      writeln();
    }
  return;
}

void parse_variable_definition(int top_level) {
  int name;
  int value;
  int value_name;
  expect(integer_tok);
  name = get_token();
  // default integer size
  value = 1;
  if (lookahead() == open_bracket_tok) {
    // array
    // get open bracket
    dummy = get_token();
    value = get_token();
    if (is_number(value)){
      value_name = 0;
      value = _atoi(value);
    }
    else {
      // saves token for use when c2s
      value_name = value;
      value = _atoi(vector_ref(constant_value, get_constant_value(value)));
      if (value == 0) {
        log_error(UNDEFINED_CONSTANT, name);
        // error default
        value = 1;
      }
    }
    expect(close_bracket_tok);
  }
  expect(semi_colon_tok);

  if (top_level) {
    vector_set(top_level_variable_name, top_level_variable_idx, name);
    vector_set(top_level_variable_size, top_level_variable_idx, value);
    vector_set(sorted_top_level_variable, top_level_variable_idx, top_level_variable_idx);
    if (top_level_variable_idx == 0)
      vector_set(top_level_variable_position, 0, 0);
    else
      vector_set(top_level_variable_position, top_level_variable_idx,
        vector_ref(top_level_variable_position, top_level_variable_idx - 1) +
        vector_ref(top_level_variable_size, top_level_variable_idx - 1));
    top_level_variable_idx = top_level_variable_idx + 1;
    if (is_out_of_range_vector(top_level_variable_name, top_level_variable_idx)) {
      top_level_variable_name = vector_resize(top_level_variable_name);
      top_level_variable_position = vector_resize(top_level_variable_position);
      top_level_variable_size = vector_resize(top_level_variable_size);
      sorted_top_level_variable = vector_resize(sorted_top_level_variable);
    }
    log_sorted_vector(
      0, top_level_variable_idx, top_level_variable_name,
      sorted_top_level_variable);
  }
  else {
    // variables local to a procedure or function
    vector_set(variable_name, variable_idx, name);
    vector_set(variable_size, variable_idx, value);
    if (variable_idx == 0)
      vector_set(variable_position, 0, 0);
    else
      vector_set(variable_position, variable_idx,
        vector_ref(variable_position, variable_idx - 1) +
        vector_ref(variable_size, variable_idx - 1));
    // it's not a reference type
    vector_set(variable_is_reference, variable_idx, 0);
    variable_idx = variable_idx + 1;
    if (is_out_of_range_vector(variable_name, variable_idx)) {
      variable_name = vector_resize(variable_name);
      variable_position = vector_resize(variable_position);
      variable_size = vector_resize(variable_size);
      variable_is_reference = vector_resize(variable_is_reference);
    }
  }
  if (top_level) {
    echo_comments(1);
    indent(1);
  }
  else {
    echo_comments(4);
    indent(4);
  }
  printf("(");
  string_write(name);
  write_char(' ');
  if (value > 1) {
    // array type
    printf("(make-vector ");
    if (value_name == 0)
      // numeric
      write_int(value);
      // array size
    else
      // symbolic size
      string_write(value_name);
    // initialize vector to zeros
    printf(" 0)");
  }
  else
    printf("0");
  write_char(')');
  writeln();
  return;
}

void parse_variable_definitions(int top_level) {
  if (!top_level)
    indent(3);
  printf("(let (");
  writeln();
  while (lookahead_var())
    parse_variable_definition(top_level);
  if (top_level)
    indent(1);
  else
    indent(4);
  printf(")");
  writeln();
  return;
}

void display_formal_parameters() {
  int i;
  if (formal_parameter_idx != 0)
    for (i = 0; i < formal_parameter_idx; i++) {
      string_write(formal_parameter_name[i]);
      writeln();
    }
  return;
}

void parse_formal_parameter() {
  int passing_type;
  int name;
  int value;
  expect(integer_tok);
  name = get_token();
  if (lookahead() == open_bracket_tok) {
    // reference var
    // get open bracket
    dummy = get_token();
    expect(close_bracket_tok);
    passing_type = 1;
  }
  else
    passing_type = 0;
  formal_parameter_name[formal_parameter_idx] = name;
  formal_parameter_idx = formal_parameter_idx + 1;
  if (formal_parameter_idx >= MAX_FORMAL_PARAMETERS) {
    // TODO limit number of formal parameters to 32? ARM limitation?
    log_error(MAX_ALLOWED_FORMAL_PARAMETERS, 0);
    cleanup();
  }

  string_write(name);

  // actual parameter is a variable local to a procedure or function

  // simple type size - its also 1 for array reference type
  value = 1;
  vector_set(variable_name, variable_idx, name);
  vector_set(variable_size, variable_idx, value);
  if (variable_idx == 0)
    vector_set(variable_position, 0, 0);
  else
    vector_set(variable_position, variable_idx,
      vector_ref(variable_position, variable_idx - 1) +
      vector_ref(variable_size, variable_idx - 1));
  if (passing_type == 1)
    // its a reference type
    vector_set(variable_is_reference, variable_idx, 1);
  else
    // it's not a reference type
    vector_set(variable_is_reference, variable_idx, 0);
  variable_idx = variable_idx + 1;
  if (is_out_of_range_vector(variable_name, variable_idx)) {
    variable_name = vector_resize(variable_name);
    variable_position = vector_resize(variable_position);
    variable_size = vector_resize(variable_size);
    variable_is_reference = vector_resize(variable_is_reference);
  }
  return;
}

void parse_formal_parameters() {
  // TODO move parsing of () here
  while (lookahead() != close_paren_tok) {
    parse_formal_parameter();
    if (lookahead() == comma_tok) {
      dummy = get_token();
      write_char(' ');
    }
  }
  return;
}

int is_forward() {
  return lookahead() == semi_colon_tok;
}

void parse_function() {
  int name;
  // int function type
  dummy = get_token();
  name = get_token();
  expect(open_paren_tok);
  writeln();
  indent(2);
  printf("(");
  string_write(name);
  writeln();
  indent(3);
  printf("(lambda (");
  parse_formal_parameters();
  expect(close_paren_tok);
  write_char(')');
  writeln();

  // log function name

  current_function = name;
  if (is_function(name) == 0) {
    vector_set(function_name, function_idx, name);
    // # of formals
    vector_set(function_value, function_idx, formal_parameter_idx);
    vector_set(sorted_function, function_idx, function_idx);
    function_idx = function_idx + 1;
    if (is_out_of_range_vector(function_name, function_idx)) {
      function_name = vector_resize(function_name);
      function_value = vector_resize(function_value);
      sorted_function = vector_resize(sorted_function);
    }
    log_sorted_vector(2, function_idx, function_name, sorted_function);
  }
  // else the function was previously defined in a forward
  if (!is_forward()) {
    expect(begin_tok);
    parse_block(name, 1);
    expect(end_tok);
  }
  else {
    // forward
    expect(semi_colon_tok);
    printf("__forward))");
    writeln();
  }
  formal_parameter_idx = 0;
  variable_idx = 0;
  return;
}

void parse_procedure() {
  int name;
  // void procedure type
  dummy = get_token();
  name = get_token();
  expect(open_paren_tok);
  writeln();
  indent(2);
  printf("(");
  string_write(name);
  writeln();
  indent(3);
  printf("(lambda (");
  parse_formal_parameters();
  expect(close_paren_tok);
  write_char(')');
  writeln();

  // log procedure name

  current_function = 0;
  if (is_procedure(name) == 0) {
    vector_set(procedure_name, procedure_idx, name);
    // # of formals
    vector_set(procedure_value, procedure_idx, formal_parameter_idx);
    vector_set(sorted_procedure, procedure_idx, procedure_idx);
    procedure_idx = procedure_idx + 1;
    if (is_out_of_range_vector(procedure_name, procedure_idx)) {
      procedure_name = vector_resize(procedure_name);
      procedure_value = vector_resize(procedure_value);
      sorted_procedure = vector_resize(sorted_procedure);
    }
    log_sorted_vector(4, procedure_idx, procedure_name, sorted_procedure);
  }
  // else the procedure was previously defined in a forward
  if (!is_forward()) {
    expect(begin_tok);
    parse_block(name, 0);
    expect(end_tok);
  }
  else {
    // forward
    expect(semi_colon_tok);
    printf("__forward))");
    writeln();
  }
  formal_parameter_idx = 0;
  variable_idx = 0;
  return;
}

void parse_procedures_and_functions() {
  while (lookahead_procedure() || lookahead_function()) {
    if (lookahead_procedure())
      parse_procedure();
    else
      parse_function();
    if (debug)
      display_formal_parameters();
  }
  return;
}

void parse_if_statement(int level) {
  int l0;
  int l1;
  indent(level);
  printf("(if (__test ");
  l0 = gen_label();
  if (lookahead() != open_paren_tok)
    expect(open_paren_tok);
  parse_expression(0, 0);
  printf(")");
  writeln();
  fast_expression = 0;
  parse_statement(level + 1);
  if (lookahead() == else_tok) {
    l1 = gen_label();
    // get else
    dummy = get_token();
    writeln();
    parse_statement(level + 1);
  }

  printf(")");
  return;
}

void push_case_label(int x) {
  vector_set(case_label, case_label_idx, x);
  case_label_idx = case_label_idx + 1;
  if (is_out_of_range_vector(case_label, case_label_idx))
    case_label = vector_resize(case_label);
  return;
}

void parse_break_statement(int lbl) {
  dummy = get_token();
  expect(semi_colon_tok);
  return;
}

void parse_case_list_element(int final_label, int level) {
  int l0;
  int number;
  if (lookahead() != end_tok) {
    expect(case_tok);
    number = get_token();
    if (is_number(number))
      number = _atoi(number);
    else
      number = _atoi(vector_ref(constant_value, get_constant_value(number)));

    expect(colon_tok);
    indent(level);
    printf("((");
    write_int(number);
    printf(")");
    writeln();
    l0 = gen_label();
    while ((lookahead() != end_tok) && (lookahead() != case_tok)) {
      if (lookahead() == break_tok)
        parse_break_statement(final_label);
      else {
        parse_statement(level + 1);
        if (lookahead() != break_tok)
          writeln();
      }
    }
    push_case_label(l0);
    printf(")");
    writeln();
  }
  return;
}

int parse_case_list_elements(int n, int final_label, int level) {
  if (lookahead() == end_tok)
    return n - 1;
  else {
    parse_case_list_element(final_label, level);
    return parse_case_list_elements(n + 1, final_label, level);
  }
}

void parse_case_statement(int level) {
  int final_label;
  int computed_goto_label;
  int n;
  if (lookahead() != open_paren_tok)
    expect(open_paren_tok);
  indent(level);
  printf("(case");
  writeln();
  indent(level + 1);
  parse_expression(0, 1);
  fast_expression = 0;
  expect(begin_tok);
  writeln();
  final_label = gen_label();
  computed_goto_label = gen_label();
  n = parse_case_list_elements(1, final_label, level + 1);
  expect(end_tok);
  indent(level);
  printf(")");
  case_label_idx = case_label_idx - n;
  return;
}

void parse_while_statement(int level) {
  int l0;
  int l1;
  indent(level);
  printf("(let loop ()");
  writeln();
  indent(level + 1);
  printf("(if (__test ");
  if (lookahead() != open_paren_tok)
    expect(open_paren_tok);
  l0 = gen_label();
  l1 = gen_label();
  parse_expression(0, 0);
  fast_expression = 0;
  printf(")");
  writeln();
  indent(level + 2);
  printf("(begin");
  writeln();
  parse_statement(level + 3);
  writeln();
  indent(level + 3);
  printf("(loop))))");
  return;
}

void parse_repeat_statement(int level) {
  int l0;
  indent(level);
  printf("(let loop ()");
  writeln();
  indent(level + 1);
  printf("(begin");
  writeln();
  expect(begin_tok);
  l0 = gen_label();
  while (lookahead() != end_tok)
    parse_statement(level + 2);
  expect(end_tok);
  expect(while_tok);
  if (lookahead() != open_paren_tok)
    expect(open_paren_tok);
  indent(level + 2);
  printf("(if (__test ");
  parse_expression(0, 0);
  printf(")");
  writeln();
  fast_expression = 0;
  writeln();
  indent(level + 3);
  printf("(loop))))");
  writeln();
  return;
}

void parse_for_statement(int level) {
  int control_var;
  int incr;
  int l0;
  int l1;
  expect(open_paren_tok);
  if (lookahead() == semi_colon_tok) {
    // forever loop
    expect(semi_colon_tok);
    expect(semi_colon_tok);
    expect(close_paren_tok);
    l0 = gen_label();
    parse_statement(level + 1);
    return;
  }
  else {
    indent(level + 1);
    printf("(begin");
    writeln();
    indent(level + 1);
    l0 = gen_label();
    l1 = gen_label();
    parse_expression(0, 0);
    writeln();
    indent(level + 1);
    printf("(let loop ()");
    writeln();
    indent(level + 2);
    printf("(if (__test ");
    fast_expression = 0;
    control_var = output_queue[1];
    parse_expression(0, 0);
    printf(")");
    writeln();
    indent(level + 3);
    printf("(begin");
    writeln();
    dummy = get_token();
    if (dummy != control_var)
      log_error(CONTROL_VAR_MISMATCH, 0);
    incr = get_token();
    if (!((incr == succ_tok) || (incr == pred_tok)))
      log_error(EXPECTING_INCR_DECR, 0);
    expect(close_paren_tok);
    parse_statement(level + 4);
    tokens[0] = control_var;
    tokens[1] = assignment_tok;
    tokens[2] = control_var;
    if (incr == succ_tok)
      tokens[3] = plus_tok;
    else
      tokens[3] = minus_tok;
    tokens[4] = one_tok;
    tokens_idx = 5;
    writeln();
    indent(level + 4);
    translate_expression_to_code(0);
    writeln();
    indent(level + 4);
    printf("(loop)))))");
    writeln();
    return;
  }
}

void define_constant_name_value(int name, int value) {
  vector_set(constant_name, constant_idx, name);
  vector_set(constant_value, constant_idx, value);
  vector_set(sorted_constant, constant_idx, constant_idx);
  constant_idx = constant_idx + 1;
  if (is_out_of_range_vector(constant_name, constant_idx)) {
    constant_name = vector_resize(constant_name);
    constant_value = vector_resize(constant_value);
    sorted_constant = vector_resize(sorted_constant);
  }
  log_sorted_vector(2, constant_idx, constant_name, sorted_constant);
  return;
}

void parse_statement(int level) {
  int token;
  token = lookahead();
  echo_comments(level);
  if (token == begin_tok) {
    indent(level);
    printf("(begin");
    writeln();
    parse_compound_statement(level + 1);
    printf(")");
  }
  else if (token == if_tok) {
    dummy = get_token();
    parse_if_statement(level);
  }
  else if (token == switch_tok) {
    dummy = get_token();
    parse_case_statement(level);
  }
  else if (token == while_tok) {
    dummy = get_token();
    parse_while_statement(level);
  }
  else if (token == do_tok) {
    dummy = get_token();
    parse_repeat_statement(level);
  }
  else if (token == for_tok) {
    dummy = get_token();
    parse_for_statement(level);
  }
  else if (token == end_tok) {
    // do nothing
  }
  else if (token == return_tok) {
    dummy = get_token();
    if (lookahead() == semi_colon_tok) {
      // procedure return
      // get semicolon
      dummy = get_token();
      indent(level);
      printf("(return-from-procedure)");
    }
    else {
      // function return
      indent(level);
      printf("(return ");
      parse_expression(0, 0);
      printf(")");
    }
  }
  else if (token == printf_tok) {
    // transform printf into either write_int or write_string
    fast_expression = 0;
    dummy = get_token();
    if (lookahead() != open_paren_tok)
      expect(open_paren_tok);
    // (
    dummy = get_token();
    //string[] returned by way of lookahead get_token
    if (string[0] == '%') {
      // write_int
      dummy = get_token();
      // the null token returned while parsing a string
      // replace the next token (a comma) with an open paren
      // this will allow convenient parsing of %d expression
      indent(level);
      printf("(display ");
      token_ring[next_token_ring_idx()] = open_paren_tok;
      // force slow
      parse_expression(0, 1);
      printf(")");
      emit_display();
    }
    else {
      // write_string
      emit_write_string(string_idx, level);
      // the null token returned while parsing a string
      dummy = get_token();
      // )
      dummy = get_token();
      expect(semi_colon_tok);
    }
  }
  else if (is_identifier(token)) {
    indent(level);
    parse_expression(0, 0);
    fast_expression = 0;
  }
  else
    log_error(BAD_STATEMENT, 0);
  return;
}

void parse_statements(int level) {
  while (!(lookahead() == end_tok)) {
    parse_statement(level);
    if (!(lookahead() == end_tok))
      writeln();
  }
  return;
}

void parse_compound_statement(int level) {
  dummy = get_token();
  if (dummy != begin_tok)
    log_error(EXPECT_TOKEN, begin_tok);
  parse_statements(level);
  expect(end_tok);
  return;
}

void parse_block(int name, int functionp) {
  int block_has_vars;

  // formals offset is number of formal parameters

  block_has_vars = 0;
  if (lookahead_var()) {
    block_has_vars = 1;
    parse_variable_definitions(0);
  }

  parse_statements(3);

  //indent(3);
  if (block_has_vars)
    printf(")))");
  else
    printf("))");
  //writeln();

  return;
}

void parse_include() {
  expect(include_tok);
  expect(less_than_tok);
  dummy = get_token();
  while (dummy != greater_than_tok)
    dummy = get_token();
  return;
}

void parse_define() {
  int name;
  int value;
  expect(define_tok);
  name = get_token();
  value = get_token();
  if (!is_number(value)) {
    log_error(NUMBER_EXPECTED, value);
    value = zero_tok;
  }
  printf("(define ");
    string_write(name);
    printf(" ");
    if (is_hex(value))
      printf("%d", htoi(value));
    else
      string_write(value);
    printf(")");
    writeln();
  echo_comments(0);
  // for all targets
  define_constant_name_value(name, value);
  return;
}

void parse_includes_and_defines() {
  while (lookahead() == pound_tok) {
    dummy = get_token();
    if (lookahead() == include_tok)
      parse_include();
    else
      parse_define();
  }
  return;
}

void parse_program() {

  parse_includes_and_defines();

  top_level_variable_idx = 0;

  if (lookahead_var())
    parse_variable_definitions(1);

  if (debug)
    display_top_level_vars();

  indent(1);
  printf("(letrec (");
  writeln();

  if (lookahead_procedure() || lookahead_function())
    parse_procedures_and_functions();

  if (get_token() != 0)
    log_error(REST_OF_PROGRAM_IGNORED, 0);

  printf("    )");
  writeln();
  printf("    (main)");
  writeln();
  if (top_level_variable_idx == 0)
    // no global vars
    printf("  )");
  else
    printf("  ))");
  writeln();

  return;
}
