#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char** clib_ArgV;
extern int Start_col, Curr_col, Curr_line_num;
extern char Inp_line[512];

char *Ignorables();
char *Ignorable_toks;
char **Token_sets();
char **Tok_set;
char Ignored_tokens[1024], *Ignored_toks;
int Tok;

void import_parser(){}

int clib_Parse(long *fn, long *mode){
  Tok_set = Token_sets();
  Ignorable_toks = Ignorables();
  Ignored_toks = Ignored_tokens; *Ignored_toks = 0;
  Init_scanner(fn, mode);
  Init_cbuilder();
  Tok = Next_token();
  Parse_goal();
  Skip(1);
};

int Match(char *Toks){
  do{
    if (Tok == 0) Note_error();
    if (strchr(Toks,Tok)) return 1;
    if (strpbrk(Toks,Ignored_tokens)) return 1;
    if (!strchr(Ignorable_toks,Tok)) return 0;
    *Ignored_toks = Tok; Ignored_toks += 1; *Ignored_toks = 0;
    Tok = Next_token();
  }while (1);
};

int Match1(int Tk){
  do{
    if (Tok == 0) Note_error();
    if (Tk == Tok || strchr(Ignored_tokens, Tk)) return 1;
    if (!strchr(Ignorable_toks,Tok)) return 0;
    *Ignored_toks = Tok; Ignored_toks += 1; *Ignored_toks = 0;
    Tok = Next_token();
  }while (1);
};

int Note_error(){
  int i;
  if (Tok == 1)
    fprintf(stderr, "%s(%i) ERROR: Source file ended too soon\n", clib_ArgV[1], Curr_line_num);
  else{
    fprintf(stderr, "%s(%i, %i) ERROR: Did not expect ", clib_ArgV[1], Curr_line_num, Start_col+1);
    for (i = Start_col; i < Curr_col; i++) fputc(Inp_line[i], stderr);
    fprintf(stderr, "\n");
  };
  exit(1);
};

int Skip(int Tk){
  char *p = strchr(Ignored_tokens,Tk);
  if (p != NULL){
    do {*p = 127; p = strchr(Ignored_tokens,Tk);} while (p != NULL);
    return;
  };
  while (Tk != Tok){
    if (!strchr(Ignorable_toks,Tok)) Note_error();
    Tok = Next_token();
  };
  Tok = Next_token();
  Ignored_toks = Ignored_tokens;  *Ignored_toks = 0; 
};

int Parse_comment(){
  if (!Match1(3)) Note_error();
  Build_token("comment", Inp_line, Start_col, Curr_col);
  Tok = Next_token();
  Ignored_toks = &Ignored_tokens[0]; *Ignored_toks = 0;
};

int Parse_identifier(){
  if (!Match1(4)) Note_error();
  Build_token("identifier", Inp_line, Start_col, Curr_col);
  Tok = Next_token();
  Ignored_toks = &Ignored_tokens[0]; *Ignored_toks = 0;
};

int Parse_intnum(){
  if (!Match1(5)) Note_error();
  Build_token("intnum", Inp_line, Start_col, Curr_col);
  Tok = Next_token();
  Ignored_toks = &Ignored_tokens[0]; *Ignored_toks = 0;
};

int Parse_string(){
  if (!Match1(6)) Note_error();
  Build_token("string", Inp_line, Start_col, Curr_col);
  Tok = Next_token();
  Ignored_toks = &Ignored_tokens[0]; *Ignored_toks = 0;
};

char *Ignorables(){
  char *igs = malloc(4);
  igs[0] = 2;
  igs[1] = 3;
  igs[2] = 35;
  igs[3] = 0;
  return igs;
};

int Parse_expression(){
  if (Match1(24)){
    Skip(24);
    Parse_express();
    Build_neg();
  }else if (Match(Tok_set[0])){
    Parse_express();
  }else{
    Note_error();
  };
};

int Parse_conditional_statement(){
  if (Match1(37)){
    Skip(37);
    Build_exit();
  }else if (Match1(11)){
    Skip(11);
    if (Match(Tok_set[1])){
      Parse_expression();
      Build_return_val();
    }else if (Match1(2)){
      /*Eol already skipped*/
      Build_return();
    }else{
      Build_return();
    };
  }else if (Match1(4)){
    Parse_name();
    if (Match(Tok_set[2])){
      Parse_assignment();
    }else if (Match(Tok_set[3])){
      Parse_declaration();
    }else{
      Build_procedure_call();
    };
  }else{
    Note_error();
  };
  if (Match1(29)){
    Skip(29);
    Parse_expression();
    Build_conditional_statement();
  };
};

int Parse_relation(){
  Parse_arith_expr();
  if (Match1(18)){
    Skip(18);
    Parse_arith_expr();
    Build_ne();
  }else if (Match1(39)){
    Skip(39);
    Parse_arith_expr();
    Build_eq();
  }else if (Match1(38)){
    Skip(38);
    Parse_arith_expr();
    Build_lt();
  }else if (Match1(59)){
    Skip(59);
    Parse_arith_expr();
    Build_le();
  }else if (Match1(40)){
    Skip(40);
    Parse_arith_expr();
    Build_gt();
  }else if (Match1(60)){
    Skip(60);
    Parse_arith_expr();
    Build_ge();
  };
};

int Parse_unsubscripted_name(){
  Parse_identifier();
  if (Match1(19)){
    Parse_parameter_values();
    Build_parameterized_name();
  }else if (Match1(33)){
    Skip(33);
    Parse_identifier();
    if (Match1(19)){
      Parse_parameter_values();
      Build_class_name();
    }else{
      Build_simple_class_name();
    };
  }else{
    Build_simple_name();
  };
};

int Parse_module(){
  Build_start_list();
  Parse_statements();
  Build_add_to_list();
  while (Match(Tok_set[4])){
    if (Match1(32)){
      Parse_procedure();
      Build_add_to_list();
    }else{
      Parse_function();
      Build_add_to_list();
    };
  };
  Build_module();
};

int Parse_conjunction(){
  Parse_negation();
  while (Match1(30)){
    Skip(30);
    Parse_negation();
    Build_and();
  };
};

int Parse_expressions(){
  Build_start_list();
  while (Match(Tok_set[1])){
    Parse_expression();
    Build_add_to_list();
    if (Match1(23)){
      Skip(23);
    };
  };
};

int Parse_repeat_loop(){
  Skip(7);
  Parse_statements();
  if (Match1(26)){
    Skip(26);
    Parse_expression();
    Build_repeat_until();
  }else if (Match1(36)){
    Skip(36);
    Build_repeat_loop();
  }else{
    Note_error();
  };
};

int Parse_while_loop(){
  Skip(9);
  Parse_expression();
  Parse_statements();
  Skip(36);
  Build_while_loop();
};

int Parse_name(){
  Parse_unsubscripted_name();
  if (Match1(19)){
    Skip(19);
    Parse_expression();
    Skip(20);
    Build_subscripted_name();
  };
};

int Parse_arith_expr(){
  Parse_term();
  while (Match(Tok_set[5])){
    if (Match1(22)){
      Skip(22);
      Parse_term();
      Build_plus();
    }else if (Match1(24)){
      Skip(24);
      Parse_term();
      Build_min();
    }else{
      Skip(61);
      Parse_term();
      Build_concat();
    };
  };
};

int Parse_negation(){
  if (Match1(47)){
    Skip(47);
    Parse_relation();
    Build_not();
  }else if (Match(Tok_set[6])){
    Parse_relation();
  }else{
    Note_error();
  };
};

int Parse_for_loop(){
  Skip(44);
  Parse_identifier();
  Skip(49);
  Skip(50);
  Parse_expression();
  Skip(51);
  Parse_expression();
  Skip(52);
  Parse_statements();
  Skip(36);
  Build_for_loop();
};

int Parse_goal(){
  Parse_module();
};

int Parse_selection(){
  Skip(46);
  Parse_expression();
  Parse_statements();
  Build_if();
  while (Match1(8)){
    Skip(8);
    Parse_expression();
    Parse_statements();
    Build_elsif();
  };
  if (Match1(21)){
    Skip(21);
    Parse_statements();
    Build_else();
  };
  Skip(34);
  Build_endif();
};

int Parse_parameter_identifiers(){
  Build_start_list();
  if (Match1(19)){
    Skip(19);
    while (Match1(4)){
      Parse_identifier();
      Build_add_to_list();
      if (Match1(23)){
        Skip(23);
      };
    };
    Skip(20);
  };
};

int Parse_function(){
  Skip(25);
  Parse_identifier();
  Build_func_id();
  Parse_parameter_identifiers();
  Skip(56);
  Parse_expression();
  Parse_statements();
  Build_func();
};

int Parse_declaration(){
  if (Match1(41)){
    Skip(41);
    if (Match1(4)){
      Parse_identifier();
      Build_object_declaration();
      if (Match1(57)){
        Skip(57);
        Parse_expression();
        Build_object_initialization();
      };
    }else if (Match1(45)){
      Skip(45);
      Skip(48);
      Parse_identifier();
      Skip(57);
      Skip(58);
      Build_type_declaration();
    }else if (Match1(42)){
      Skip(42);
      Skip(31);
      Parse_identifier();
      Skip(55);
      Parse_identifier();
      Build_map_declaration();
    }else{
      Note_error();
    };
  }else if (Match1(23)){
    Skip(23);
    Parse_identifiers();
    Skip(41);
    Parse_identifier();
    Build_multi_object_declaration();
  }else{
    Note_error();
  };
};

int Parse_statement(){
  if (Match1(16)){
    Parse_export();
  }else if (Match1(10)){
    Parse_import();
  }else if (Match1(46)){
    Parse_selection();
  }else if (Match(Tok_set[7])){
    Parse_iteration();
  }else if (Match(Tok_set[8])){
    Parse_conditional_statement();
  }else{
    Note_error();
  };
  if (Match1(43)){
    Skip(43);
  };
};

int Parse_export(){
  Skip(16);
  Parse_identifiers();
  Build_export();
};

int Parse_assignment(){
  if (Match1(57)){
    Skip(57);
    Parse_expression();
    Build_assign();
  }else if (Match1(13)){
    Skip(13);
    Build_lr_name();
    Parse_expression();
    Build_plus();
    Build_assign();
  }else if (Match1(15)){
    Skip(15);
    Build_lr_name();
    Parse_expression();
    Build_min();
    Build_assign();
  }else if (Match1(17)){
    Skip(17);
    Build_lr_name();
    Parse_expression();
    Build_div();
    Build_assign();
  }else if (Match1(12)){
    Skip(12);
    Build_lr_name();
    Parse_expression();
    Build_mult();
    Build_assign();
  }else{
    Note_error();
  };
};

int Parse_parameter_values(){
  Skip(19);
  Build_start_list();
  while (Match(Tok_set[1])){
    Parse_expression();
    if (Match1(23)){
      Skip(23);
    };
    Build_add_to_list();
  };
  Build_parameter_values();
  Skip(20);
};

int Parse_statements(){
  Build_start_list();
  while (Match(Tok_set[9])){
    Parse_statement();
    Build_add_statement();
  };
};

int Parse_procedure(){
  Skip(32);
  Parse_identifier();
  Build_proc_id();
  Parse_parameter_identifiers();
  Parse_statements();
  Build_proc();
};

int Parse_identifiers(){
  Build_start_list();
  Parse_identifier();
  Build_add_to_list();
  while (Match(Tok_set[10])){
    if (Match1(2)){
      /*Eol already skipped*/ break;
    }else{
      if (Match1(23)){
        Skip(23);
      };
      Parse_identifier();
      Build_add_to_list();
    };
  };
};

int Parse_import(){
  Skip(10);
  Parse_identifiers();
  Build_import();
};

int Parse_expr(){
  if (Match1(4)){
    Parse_name();
  }else if (Match1(5)){
    Parse_intnum();
    Build_integer();
  }else if (Match1(6)){
    Parse_string();
    Build_string();
  }else if (Match1(50)){
    Skip(50);
    Parse_expressions();
    Build_list_literal();
    Skip(52);
  }else if (Match1(19)){
    Skip(19);
    Parse_expression();
    Skip(20);
  }else if (Match1(62)){
    Skip(62);
    Parse_expression();
    Skip(63);
  }else{
    Note_error();
  };
};

int Parse_term(){
  Parse_expr();
  while (Match(Tok_set[11])){
    if (Match1(27)){
      Skip(27);
      Parse_expr();
      Build_mult();
    }else{
      Skip(28);
      Parse_expr();
      Build_div();
    };
  };
};

int Parse_iteration(){
  if (Match1(44)){
    Parse_for_loop();
  }else if (Match1(7)){
    Parse_repeat_loop();
  }else if (Match1(9)){
    Parse_while_loop();
  }else{
    Note_error();
  };
};

int Parse_express(){
  Parse_conjunction();
  while (Match1(54)){
    Skip(54);
    Parse_conjunction();
    Build_or();
  };
};

char **Token_sets(){
  char** ts = malloc(48);
  ts[3] = malloc(3);
  ts[3][0] = 23;
  ts[3][1] = 41;
  ts[3][2] = 0;
  ts[0] = malloc(8);
  ts[0][0] = 50;
  ts[0][1] = 19;
  ts[0][2] = 4;
  ts[0][3] = 5;
  ts[0][4] = 6;
  ts[0][5] = 62;
  ts[0][6] = 47;
  ts[0][7] = 0;
  ts[5] = malloc(4);
  ts[5][0] = 22;
  ts[5][1] = 24;
  ts[5][2] = 61;
  ts[5][3] = 0;
  ts[2] = malloc(6);
  ts[2][0] = 17;
  ts[2][1] = 57;
  ts[2][2] = 12;
  ts[2][3] = 13;
  ts[2][4] = 15;
  ts[2][5] = 0;
  ts[6] = malloc(7);
  ts[6][0] = 50;
  ts[6][1] = 19;
  ts[6][2] = 4;
  ts[6][3] = 5;
  ts[6][4] = 6;
  ts[6][5] = 62;
  ts[6][6] = 0;
  ts[8] = malloc(4);
  ts[8][0] = 4;
  ts[8][1] = 37;
  ts[8][2] = 11;
  ts[8][3] = 0;
  ts[11] = malloc(3);
  ts[11][0] = 27;
  ts[11][1] = 28;
  ts[11][2] = 0;
  ts[9] = malloc(10);
  ts[9][0] = 16;
  ts[9][1] = 4;
  ts[9][2] = 37;
  ts[9][3] = 7;
  ts[9][4] = 9;
  ts[9][5] = 10;
  ts[9][6] = 11;
  ts[9][7] = 44;
  ts[9][8] = 46;
  ts[9][9] = 0;
  ts[1] = malloc(9);
  ts[1][0] = 50;
  ts[1][1] = 19;
  ts[1][2] = 4;
  ts[1][3] = 5;
  ts[1][4] = 6;
  ts[1][5] = 24;
  ts[1][6] = 62;
  ts[1][7] = 47;
  ts[1][8] = 0;
  ts[4] = malloc(3);
  ts[4][0] = 32;
  ts[4][1] = 25;
  ts[4][2] = 0;
  ts[7] = malloc(4);
  ts[7][0] = 7;
  ts[7][1] = 9;
  ts[7][2] = 44;
  ts[7][3] = 0;
  ts[10] = malloc(4);
  ts[10][0] = 2;
  ts[10][1] = 4;
  ts[10][2] = 23;
  ts[10][3] = 0;
  return ts;
};
