void indent(int level) {
  int i;
  for (i = 0; i < level; i++)
    printf("  ");
  return;
}

// shunting yard routines. refer to the wikipedia
// article: "shunting yard algorithm".

int is_open_paren_or_open_bracket(int x) {
  return (x == open_paren_tok) || (x == open_bracket_tok);
}

int is_close_paren_or_close_bracket(int x) {
  return (x == close_paren_tok) || (x == close_bracket_tok);
}

int is_parenthesis(int x) {
  return is_open_paren_or_open_bracket(x)
    || is_close_paren_or_close_bracket(x);
}

int are_matching_parens(int x, int y) {
  return ((x == close_paren_tok) && (y == open_paren_tok))
    || ((x == close_bracket_tok) && (y == open_bracket_tok));
}

void insert_to_output_queue(int token, int annotation_1, int annotation_2,
                            int annotation_3) {
  output_queue[output_queue_idx] = token;
  output_queue_idx = output_queue_idx + 1;
  output_queue[output_queue_idx] = annotation_1;
  output_queue_idx = output_queue_idx + 1;
  output_queue[output_queue_idx] = annotation_2;
  output_queue_idx = output_queue_idx + 1;
  output_queue[output_queue_idx] = annotation_3;
  output_queue_idx = output_queue_idx + 1;
  // TODO check for overflow
  return;
}

void emit_display() {
  return;
}

int is_newline(int n) {
  if (n == 1)
    if (string[0] == '\n')
      return 1;
    else
      return 0;
  else
    return 0;	
}

int string_write_c_string(int n) {
  int i;
  int ch;
  int has_newline_at_end;
  has_newline_at_end = 0;
  if (string[n - 1] == '\n') {
    has_newline_at_end = 1;
    n = n - 1;
  }
  write_char('"');
  for (i = 0; i < n; i++) {
    ch = string[i];
    if ((ch == '\\') || (ch == '"'))
      // preceed with backslash
      write_char('\\');
    if (ch == '\n')
      // line feed
      write_char(' ');
    else
      write_char(ch);
  }
  write_char('"');
  return has_newline_at_end;
}

void emit_write_string(int n, int level) {
  int has_newline_at_end;
  if (is_newline(n)) {
    indent(level);
    printf("(newline)");
  }
  else {
    indent(level);
    printf("(display ");
    has_newline_at_end = string_write_c_string(n);
    printf(")");
    if (has_newline_at_end) {
      writeln();
      indent(level);
      printf("(newline)");
    }
  }
  return;
}

int precedence(int op) {
  int p1;
  int p2;
  if (op == assignment_tok)
    return 0;
  else if (op == or_tok)
    return 1;
  else if (op == and_tok)
    return 2;
  else if (op == not_tok)
    return 3;
  else
    if (((op == equal_tok) || (op == not_equals_tok) || (op == less_than_tok))
        || ((op == less_or_equal_tok) || (op == greater_or_equal_tok)
        || (op == greater_than_tok)))
    return 4;
  else if ((op == plus_tok) || (op == minus_tok) || (op == bit_or_tok))
    return 5;
  else {
    p1 = (op == times_tok) || (op == minus_tok) || (op == bit_or_tok);
    p2 = (op == bit_and_tok) || (op == shr_tok) || (op == shl_tok)
             || (op == bit_xor_tok);
    if (p1 || ((op == div_tok) || (op == mod_tok)) || p2)
      return 6;
    else if ((op == bit_not_tok) || (op == unary_minus_tok))
      return 7;
    else
      // shouldn't happen
      return 8;
  }
}

int is_right_associative(int op) {
  return (op == not_tok) || (op == bit_not_tok) || (op == unary_minus_tok);
}

int is_left_associative(int op) {
  return !is_right_associative(op);
}

int is_function_argument_separator(int x) {
  return (x == comma_tok);
}

void clear_operators_from_stack() {
  int element;
  int annotation_1;
  int annotation_2;
  int annotation_3;
  if (stack_idx > 0) {
    // non empty stack
    element = pop();
    annotation_1 = pop();
    annotation_2 = pop();
    annotation_3 = pop();
    if (is_parenthesis(element))
      log_error(MISMATCHED, 0);
    else {
      insert_to_output_queue(element, annotation_1, annotation_2,
                             annotation_3);
      clear_operators_from_stack();
    }
  }
  return;
}

void insert_4_pops_to_output_queue() {
  int pop1;
  int pop2;
  int pop3;
  int pop4;
  pop1 = pop();
  pop2 = pop();
  pop3 = pop();
  pop4 = pop();
  insert_to_output_queue(pop1, pop2, pop3, pop4);
  return;
}

void pop_ops_with_less_than_or_equal_precedence(int op) {
  int op2;
  if (stack_idx > 0)
    // non empty stack
    if (is_operator(tos())) {
      op2 = tos();
      if ((is_left_associative(op) && (precedence(op) <= precedence(op2)))
          || (is_right_associative(op)
              && (precedence(op) < precedence(op2)))) {
        insert_4_pops_to_output_queue();
        pop_ops_with_less_than_or_equal_precedence(op);
      }
    }
  return;
}

void pop_ops_until_left_paren() {
  int op;
  int annotation_1;
  int annotation_2;
  int annotation_3;
  if (stack_idx <= 0)
    // empty stack
    log_error(MISMATCHED, 0);
  else {
    op = pop();
    annotation_1 = pop();
    annotation_2 = pop();
    annotation_3 = pop();
    if (!(is_open_paren_or_open_bracket(op))) {
      // could also be a function name
      insert_to_output_queue(op, annotation_1, annotation_2, annotation_3);
      pop_ops_until_left_paren();
    }
  }
  return;
}

void process_function_arg() {
  int op;
  if (stack_idx <= 0)
    // empty stack
    log_error(MISMATCHED2, 0);
  else {
    op = tos();
    if (!(is_open_paren_or_open_bracket(op))) {
      insert_4_pops_to_output_queue();
      process_function_arg();
    }
  }
  return;
}

int is_var(int x) {
  int found;
  if (is_parenthesis(x) || is_function_argument_separator(x)) {
    global_annotation = ANNOTATION_SEPARATOR;
    return 0;
  }
  else if (is_operator(x)) {
    global_annotation = ANNOTATION_OPERATOR;
    return 0;
  }
  else {
    found = find_local_var(x);
    if (found != 0) {
      global_annotation = ANNOTATION_LOCAL_VAR;
      return found;
    }
    else {
      global_annotation = ANNOTATION_GLOBAL_VAR;
      return find_top_level_var(x);
    }
  }
}

int rename_constants(int token){
  return token;
}

void shunt(int expression_idx) {
  int token;
  int found;
  int annotation_1;
  int annotation_2;
  int annotation_3;
  int is_reference;
  if (expression_idx == tokens_idx)
    // end of expression
    clear_operators_from_stack();
  else {
    token = tokens[expression_idx];
    token = rename_constants(token);
    found = 0;
    is_reference = 0;
    annotation_3 = 0;
                      // immediate constant or in the case of
                      // global vars when they are references
    if (is_number(token))
      global_annotation = ANNOTATION_NUMBER;
    else {
      if ((expression_idx == 0)
        // first token is function assignment
          && (tokens[0] == current_function)
          && (tokens[1] == assignment_tok)) {
        // treats token as a var of size one
        found = 1;
        global_annotation = ANNOTATION_RETURN;
      }
      else {
        found = is_var(token);
        // sets global annotation, returns size
        if (found == 0) {
          if (is_function(token) == 0)
            // sets global annotation
            dummy = is_procedure(token);
        }
        else if (global_annotation == ANNOTATION_LOCAL_VAR)
          is_reference = local_variable_is_reference(token);
        else if (global_annotation == ANNOTATION_GLOBAL_VAR)
          if ((found > 1) && (tokens[expression_idx + 1] != open_bracket_tok)) {
            // global var is an array name reference
            is_reference = 1;
            annotation_3 = 1;
          }
      }
    }
    annotation_1 = global_annotation;
    // variable size
    annotation_2 = found;
    if (is_open_paren_or_open_bracket(token)) {
      push(annotation_3);
      push(annotation_2);
      push(annotation_1);
      push(token);
    }
    else if (is_close_paren_or_close_bracket(token)) {
      pop_ops_until_left_paren();
      if (!empty_stack())
        if ((is_function(tos()) != 0) || (is_procedure(tos()) != 0)
            || (is_var(tos()) > 1) || ((is_var(tos()) == 1)
                                       && local_variable_is_reference(tos())))
          // function, procedure or array var or local array reference
          insert_4_pops_to_output_queue();
    }
    else if (is_function_argument_separator(token))
      process_function_arg();
    else if (is_operator(token)) {
      pop_ops_with_less_than_or_equal_precedence(token);
      push(annotation_3);
      push(annotation_2);
      push(annotation_1);
      push(token);
    }
    else
      if (is_number(token)
          || ((found == 1) && !is_reference) || ((found > 1) && is_reference))
      // number, string or simple type var (int)
      insert_to_output_queue(token, annotation_1, annotation_2, annotation_3);
    else if ((annotation_1 == ANNOTATION_FUNCTION) ||
             (annotation_1 == ANNOTATION_PROCEDURE)) {
      // function, procedure
      push(annotation_3);
      push(annotation_2);
      push(annotation_1);
      push(token);
    }
    else if ((found > 1) || is_reference) {
      // global array var or local array reference
      push(annotation_3);
      push(annotation_2);
      push(annotation_1);
      push(token);
    }
    else {
      // default to local var
      insert_to_output_queue(token, ANNOTATION_LOCAL_VAR, 1, 0);
    }
    shunt(expression_idx + 1);
  }
  return;
}

void are_balanced_parens_and_brackets(int expression_idx) {
  int token;
  int x;
  if (expression_idx == tokens_idx) {
    // end of expression
    if (stack_idx > 0)
      // non empty stack
      log_error(MISMATCHED, 0);
  }
  else {
    token = tokens[expression_idx];
    if (is_open_paren_or_open_bracket(token)) {
      push(token);
      are_balanced_parens_and_brackets(expression_idx + 1);
    }
    else if (is_close_paren_or_close_bracket(token)) {
      x = pop();
      if (!are_matching_parens(token, x))
        log_error(BAD_EXPRESSION, 0);
      else
        are_balanced_parens_and_brackets(expression_idx + 1);
    }
    else
      are_balanced_parens_and_brackets(expression_idx + 1);
  }
  return;
}

void filter_unary_minus() {
  int i;
  if (tokens[0] == minus_tok)
    tokens[0] = unary_minus_tok;
  for (i = 1; i < tokens_idx; i++)
    if ((is_operator(tokens[i - 1]) && (tokens[i] == minus_tok))
        || ((tokens[i - 1] == open_paren_tok) && (tokens[i] == minus_tok))
        || ((tokens[i - 1] == comma_tok) && (tokens[i] == minus_tok)))
      tokens[i] = unary_minus_tok;
  return;
}
