#include "otalk.h"
#include "scanner.h"
#include <string.h>

#define current_tag() (TOKEN_TAG(current_token))

t_token new_token(t_tag tag)
{
  t_token result;

  result = (t_token)mymalloc(sizeof(struct t_token_def));

  TOKEN_TAG(result)   = tag;
  TOKEN_INDEX(result) = 0;

  return result;
}

void add_string(t_token token, char* extra)
{
  char* new = (char*)mymalloc(strlen(extra) + strlen(TOKEN_NAME(token))+1);

  strcpy(new,TOKEN_NAME(token));
  strcat(new,extra);
        
  free(TOKEN_NAME(token));
  TOKEN_NAME(token) = new;
}

char next_char_skip_whitespace(FILE* stream)
{
  int a_char;

  a_char = fgetc(stream);
  while(((a_char != EOF) && isspace(a_char))  || a_char == '#') {
    if (a_char == '#') {
      while ((a_char != EOF) && (a_char != '\n'))
	a_char = fgetc(stream);
    }
    else
      a_char = fgetc(stream);
  }
  return a_char;
}

char next_char(FILE* stream)
{
  return fgetc(stream);
}

char unget_char(char c,FILE* stream)
{
  return ungetc(c,stream);
}

t_token new_token_with_name(char* name)
{
  t_token result;

  result = new_token(tag_name);
  TOKEN_NAME(result) = (char*)mymalloc(strlen(name)+1);
  strcpy(TOKEN_NAME(result),name);

  return result;
}

t_token next_token(FILE* stream)
{
  t_token result;
  char next;
  char buffer[80];

  next = next_char_skip_whitespace(stream);
  
  switch(next) {
  case '+' : return new_token(tag_plus);
  case '&' : return new_token(tag_and);
  case '-' : return new_token(tag_minus);
  case '*' : return new_token(tag_mul);
  case '/' : return new_token(tag_div);
  case '(' : return new_token(tag_open_par);
  case ')' : return new_token(tag_close_par);
  case '[' : return new_token(tag_blockopen_par);
  case ']' : return new_token(tag_blockclose_par);
  case '.' : return new_token(tag_dot);
  case ',' : return new_token(tag_comma);
  case '!' : return new_token(tag_method_sep);
  case ';' : return new_token(tag_semicolon);
  case '^' : return new_token(tag_return);
  case '=' : 
    {
      char c = next_char(stream);
      if (c=='=') {
	return new_token(tag_equal);
      } else {
	unget_char(c,stream);
	return new_token(tag_assign);    
      }
    }
  case '|' : 
    {
      char c = next_char(stream);
      if (c=='|') {
	return new_token(tag_or);
      } else {
	unget_char(c,stream);
	return new_token(tag_pipe);    
      }
    }
  case '<' : 
    {
      char c = next_char(stream);
      if (c=='=') {
	return new_token(tag_less_eq);
      } else {
	unget_char(c,stream);
	return new_token(tag_less);    
      }
    }
  case '>' : 
    {
      char c = next_char(stream);
      if (c=='=') {
	return new_token(tag_greater_eq);
      } else {
	unget_char(c,stream);
	return new_token(tag_greater);    
      }
    }
  default:
    if (isdigit(next)) {
      int       count = 0;

      result = new_token(tag_number);

      while (isdigit(next)) {
	buffer[count++] = next;
	next = next_char(stream);
      }
      unget_char(next,stream);
      buffer[count]=0;

      sscanf(buffer,"%d",&TOKEN_NUMBER(result));

      return result;
    } 
    else if (isalpha(next) || next == '_' || next == ':') {
      int       count = 0;

      if (next == ':') {
	result = new_token(tag_symbol);
	next = next_char(stream);
      }
      else
	result = new_token(tag_name);

      while (isalpha(next) || isdigit(next) || next == '_') {
	buffer[count++] = next;
	next = next_char(stream);
      }
      unget_char(next,stream);
      buffer[count]=0;
      if (!strcmp("if",buffer)) {
	TOKEN_TAG(result) = tag_if;
	return result;
      }
      if (!strcmp("then",buffer)) {
	TOKEN_TAG(result) = tag_then;
	return result;
      }
      if (!strcmp("else",buffer)) {
	TOKEN_TAG(result) = tag_else;
	return result;
      }
      if (!strcmp("endif",buffer)) {
	TOKEN_TAG(result) = tag_endif;
	return result;
      }
      if (!strcmp("while",buffer)) {
	TOKEN_TAG(result) = tag_while;
	return result;
      }
      if (!strcmp("end",buffer)) {
	TOKEN_TAG(result) = tag_end;
	return result;
      }

      TOKEN_NAME(result) = (char*)mymalloc(strlen(buffer)+1);
      strcpy(TOKEN_NAME(result),buffer);

      if (!strcmp("errorhandler",buffer)) {
	TOKEN_TAG(result) = tag_errorhandler;
	return result;
      }

      return result;
    } else if (next == '"') {
      int       count = 0;

      result = new_token(tag_string);
      next = next_char(stream);
      while (next != '"') {
	if (next == '\\') {
	  while (next == '\\') {
	    next = next_char(stream);
	    if (isalpha(next)) {
	      switch(next) {
	      case 'n': next = '\n'; break;
	      case 'f': next = '\f'; break;
	      default:
	      }
	    }
	    buffer[count++] = next;
	    next = next_char(stream);
	  }
	}
	else {
	  buffer[count++] = next;
	  next = next_char(stream);
	}
      }
      buffer[count]=0;
      /*printf("Buffer: %s\n",buffer);*/
      TOKEN_NAME(result) = (char*)mymalloc(strlen(buffer)+1);
      strcpy(TOKEN_NAME(result),buffer);

      return result;
    }
  }
  return new_token(tag_eof);
};




t_node new_node(t_token token, t_node left, t_node right)
{
  t_node result;

  result = (t_node)mymalloc(sizeof(struct t_node_def));

  NODE_TOKEN(result) = token;
  NODE_LEFT(result)  = left;
  NODE_RIGHT(result) = right;

  return result;
}

t_node node_from_token(t_token token)
{
  return new_node(token,NULL,NULL);
}

t_node new_vector_node_aux(int length,t_token token)
{
  t_node result = node_from_token(token);

  NODE_LENGTH(result) = length;
  NODE_VECTOR(result) = (t_node*)mymalloc(sizeof(t_node) * length);
  NODE_FILL(result) = 0;

  return result;
}

t_node new_vector_node(int length)
{
  return new_vector_node_aux(length,new_token(tag_vector));
}

t_node new_method_vector_node(int length)
{
  return new_vector_node_aux(length,new_token(tag_method_vector));
}

t_node new_if_node()
{
  return new_vector_node_aux(3,new_token(tag_if));
}

t_node new_while_node()
{
  return new_vector_node_aux(2,new_token(tag_while));
}

t_node new_closure_vector_node(int length)
{
  return new_vector_node_aux(length,new_token(tag_closure_vector));
}

t_node new_call_vector_node()
{
  return new_vector_node_aux(3,new_token(tag_call));
}

t_node node_with_name(char* name)
{
  t_node node = new_node(new_token(tag_name),NULL,NULL);

  NODE_NAME(node) = malloc(strlen(name)+1);
  strcpy(NODE_NAME(node),name);

  return node;
}

void extend(t_node node,int extra)
{
  int i;
  t_node* vec = (t_node*)mymalloc(sizeof(t_node) * 
				(extra + NODE_LENGTH(node)));

  for (i=0; i<NODE_LENGTH(node); ++i)
    vec[i] = NODE_ENTRY(node,i);
	 
  free(NODE_VECTOR(node));
  NODE_LENGTH(node) = NODE_LENGTH(node) + extra;
  NODE_VECTOR(node) = vec;
}

void add(t_node liste,t_node ele)
{
  if (NODE_FILL(liste) >= NODE_LENGTH(liste)) {
    extend(liste,8);
  }
  NODE_ENTRY(liste,NODE_FILL(liste)) = ele;
  NODE_FILL(liste) += 1;
}

FILE* in_stream;
t_token current_token;

void match(t_tag tag)
{
  if (current_tag() == tag) {
    current_token = next_token(in_stream);
  }
  else {
    fprintf(stderr,
	    "*** Habe Tag %s erwartet, %s aber gelesen.\n",
	    tag_as_string(tag),
	    tag_as_string(current_tag()));
    exit(10);
  }
}


t_node top_read_expression     (void);
t_node read_dotted_expression  (void);
t_node read_expression         (void);
t_node read_term_expression    (void);
t_node read_factor             (void);
t_node read_function_token     (void);
t_node read_name_list          (void);
static void skip_token         (void);
t_node read_method             (t_tag, t_tag);
t_node read_method_body        (t_tag);
t_node read_expression_list    (t_tag, t_tag, t_tag,t_tag);
t_node read_add_expression     (void);
t_node read_compare_expression (void);


t_node top_read(FILE* stream)
{
  t_node result;
  
  in_stream = stream;
  current_token = next_token(stream);

  if (current_tag() == tag_eof)
    return NULL;

  result = read_method(tag_eof,tag_method_sep);
  /* print_node(NODE_ENTRY(node,i)); */
  top_decorate(result);
  top_compile(result);
  while (current_tag() == tag_method_sep) {
    skip_token();

    if (current_tag() == tag_eof)
      break;

    result = read_method(tag_eof,tag_method_sep);
    top_decorate(result);
    top_compile(result);
  }

  return result;
}

t_node read_method(t_tag ende, t_tag ende2)
{
  int add_pars = 1;
  t_node pars;
  t_node args;
  t_node method = new_method_vector_node(4);
  t_node name;
  t_node classname;

  if (current_tag() != tag_name) {
    fprintf(stderr,"Name als Klassenname erwartet\n");
    match(tag_name);
  }
  classname = node_from_token(current_token);
  skip_token();

  if (current_tag() == tag_dot)
    skip_token();

  if (current_tag() != tag_name) {
    t_tag tag = current_tag();
    if ((tag == tag_plus)    || (tag == tag_minus)      || 
	(tag == tag_mul)     || (tag == tag_div)        || 
	(tag == tag_less)    || (tag == tag_greater)    || 
	(tag == tag_less_eq) || (tag == tag_greater_eq) || 
	(tag == tag_equal)   ||
	(tag == tag_or)      || (tag == tag_and)     ) {
      name = node_with_name(binary_as_string(current_token));
      add_pars = 0;
    } else {
      fprintf(stderr,"Name als Methodenname erwartet\n");
      match(tag_name); 
    }
  } else {
    name = node_from_token(current_token);
  }
  skip_token();
  add(method,name);

  if (current_tag() == tag_open_par) {
    skip_token();
    pars = read_name_list();
    match(tag_close_par);
    if (add_pars) 
      add_string(NODE_TOKEN(name),"()");
  } else {
    pars = new_vector_node(1);
  }
  add(method,pars);
  
  if (current_tag() == tag_pipe) {
    skip_token();
    args = read_name_list();
    match(tag_pipe);
  } else {
    args = new_vector_node(1);
  }
  add(method,args);
  add(method,read_expression_list(tag_semicolon,ende,ende2,tag_errorhandler));
  add(method,classname);
  if ((current_tag() == tag_errorhandler)) {
    char next = next_char_skip_whitespace(in_stream);
    t_node errorhandler;

    if (next != ':') {
      fprintf(stderr,"\nHabe ':' nach Errorhandlerangabe erwartet, aber %c gelesen\n",next);
      exit(1);
    }
    match(tag_errorhandler);
    errorhandler = node_from_token(current_token);
    match(tag_name);
    match(tag_open_par);
    match(tag_close_par);

    add_string(NODE_TOKEN(errorhandler),"()");
    add(method,errorhandler);
  } 
  else {
    add(method,node_from_token(new_token(tag_eof)));
  }

  return method;
}

t_node read_method_body(t_tag ende)
{
  t_node args;
  t_node method = new_closure_vector_node(2);

  if (current_tag() == tag_pipe) {
    skip_token();
    args = read_name_list();
    match(tag_pipe);
  } else {
    args = new_vector_node(1);
  }
  add(method,args);
  add(method,read_expression_list(tag_semicolon,tag_errorhandler,ende,ende));

  return method;
}

t_node read_name_list(void)
{
  t_node liste = new_vector_node(5);

  while(current_tag() == tag_name) {
    add(liste,node_from_token(current_token));
    skip_token();
    if (current_tag() == tag_comma)
      skip_token();
  }
  return liste;
}

t_node read_expression_list(t_tag separator, t_tag ende, t_tag ende2, t_tag ende3)
{
  t_node liste = new_vector_node(5);

  if ((current_tag() == ende)  ||
      (current_tag() == ende2) ||
      (current_tag() == ende3)   )
    return liste;
  do {
    add(liste,top_read_expression());
    if ((current_tag() == ende) ||
	(current_tag() == ende2) ||
	(current_tag() == ende3)  )
      return liste;
    if (current_tag() != separator) {
      printf("Separator-Token ");
      print_tag(separator,NULL);
      printf(" erwartet.\nGelesen: ");
      print_token(current_token);
      printf("\nEnde:  %s\nEnde2: %s\nEnde3: %s\n",
	     tag_as_string(ende),
	     tag_as_string(ende2),
	     tag_as_string(ende3));
      exit(10);
    }
    skip_token();
  } while(1);
}

t_node top_read_expression(void)
{
  return read_expression();
}

t_node read_expression(void)
{
  t_node result;
  t_node result_next;
  t_token token;
  result = read_compare_expression();
  while((current_tag() == tag_or) ||
	(current_tag() == tag_and)  ) {
    token = current_token;
    match(current_tag());
    result_next = read_compare_expression();
    result = new_node(token,result,result_next);
  }

  return result;
}

t_node read_compare_expression(void)
{
  t_node result;
  t_node result_next;
  t_token token;
  result = read_add_expression();
  while((current_tag() == tag_less)    || (current_tag() == tag_less_eq)    ||
	(current_tag() == tag_greater) || (current_tag() == tag_greater_eq) ||
	(current_tag() == tag_equal)  ) {
    token = current_token;
    match(current_tag());
    result_next = read_add_expression();
    result = new_node(token,result,result_next);
  }

  return result;
}

t_node read_add_expression(void)
{
  t_node result;
  t_node result_next;
  t_token token;
  result = read_term_expression();
  while((current_tag() == tag_plus) ||
	(current_tag() == tag_minus)  ) {
    token = current_token;
    match(current_tag());
    result_next = read_term_expression();
    result = new_node(token,result,result_next);
  }

  return result;
}

t_node read_term_expression(void)
{
  t_node result;
  t_node result_next;
  t_token token;
  result = read_dotted_expression();
  while((current_tag() == tag_mul) ||
	(current_tag() == tag_div)  ) {
    token = current_token;
    match(current_tag());
    result_next = read_term_expression();
    result = new_node(token,result,result_next);
  }

  return result;
}


t_node read_dotted_expression(void)
{
  t_node result;
  t_token token;

  result = read_factor();
  while(current_tag() == tag_dot) {
    token = current_token;
    skip_token();
    result = new_node(token,result,read_function_token());    
  }
  return result;  
}

t_node read_function_token(void)
{
  t_node result;

  if (current_tag() != tag_name) {
    fprintf(stderr,"Methodenname erwartet in read_function_token(), gelesen: %s\n",
	    tag_as_string(current_tag()));
    exit(10);
  }
  result = new_call_vector_node();
  CALL_NAME(result) = node_from_token(current_token);
  skip_token();
  if (current_tag() == tag_open_par) {
    skip_token();
    CALL_ARGS(result) = read_expression_list(tag_comma, tag_close_par,tag_close_par,tag_close_par);
    match(tag_close_par);
    add_string(NODE_TOKEN(CALL_NAME(result)),"()");
  } 
  else {
    CALL_ARGS(result) = new_vector_node(0);
  }

  return result;
}

t_node read_factor(void)
{
  t_node result;
  t_token token;
  t_node name,node;

  switch(current_tag()) {
  case tag_number: 
  case tag_string: 
  case tag_symbol: 
    token = current_token;
    match(current_tag());
    return node_from_token(token);

  case tag_name: 
    name = node_from_token(current_token);
    token = current_token;
    skip_token();
    if (current_tag() == tag_assign) {
      token = current_token;
      skip_token();
      return new_node(token,name,top_read_expression());
    }
    return name;

  case tag_minus:
    skip_token();
    if (current_tag() != tag_number) {
      fprintf(stderr,"\nHabe Zahl noch - erwartet");
      exit(10);
    }
    token = current_token;
    TOKEN_NUMBER(token) = - TOKEN_NUMBER(token);
    skip_token();
    return node_from_token(token);

  case tag_return:
    token = current_token;
    skip_token();
    return new_node(token,top_read_expression(),NULL);
    break;

  case tag_blockopen_par:
    skip_token();
    result = read_method_body(tag_blockclose_par);
    match(tag_blockclose_par);
    break;

  case tag_open_par: 
    skip_token();
    result = top_read_expression();
    match(tag_close_par);
    return result;

  case tag_if: 
    skip_token();
    node = new_if_node();
    IF_TEST(node) = top_read_expression();
    match(tag_then);
    IF_THEN(node) = read_expression_list(tag_semicolon,tag_else,tag_endif,tag_endif);
    switch(current_tag()) {
    case tag_else :
      match(tag_else);
      IF_ELSE(node) = read_expression_list(tag_semicolon,tag_endif,tag_endif,tag_endif);
      break;
    case tag_endif :
      { 
	t_node liste = new_vector_node(2);
	add(liste,node_from_token(new_token_with_name("nil")));
	IF_ELSE(node) = liste;
	break;
      }
    default:
      match(tag_endif);
    }
    match(tag_endif);
    return node;


  case tag_while: 
    skip_token();
    node = new_while_node();
    match(tag_open_par);
    WHILE_TEST(node) = top_read_expression();
    match(tag_close_par);
    WHILE_BODY(node) = read_expression_list(tag_semicolon,tag_end,tag_end,tag_end);
    match(tag_end);
    return node;

  default:
    fprintf(stderr,
	    "*** Error in read_factor() :Habe Tag %s zu bearbeiten.\n",
	    tag_as_string(current_tag()));

    exit(10);
  }

  return result;
}



t_node read_file(char* file)
{
  t_node result;
  FILE* fp;

  fp = fopen(file,"r");
  if (fp == NULL) {
    fprintf(stderr,"Kann Datei \"%s\" nicht öffnen\n",file);
    return NULL;
  }
  fprintf(stderr,"Lese Datei %s\n",file);
  result = top_read(fp);
  fclose(fp);

  return result;
}

static void skip_token(void)
{
  match(current_tag());
}

char* binary_as_string(t_token token)
{
  switch(TOKEN_TAG(token)) {
  case tag_plus:       return "+";
  case tag_minus:      return "-";
  case tag_mul:        return "*";
  case tag_div:        return "/";
  case tag_and:        return "&";
  case tag_or:         return "||";
  case tag_equal:      return "==";
  case tag_less:       return "<";
  case tag_less_eq:    return "<=";
  case tag_greater:    return ">";
  case tag_greater_eq: return ">=";
  default: 
  }
  return "";
}

/*eof*/
