/** Warning:  Unless you are familliar with both parse theory and LL(1)
 ** top-down parsing algorithms, it's probably a very BAD idea to change
 ** anything here ... or even to READ it.  :-) 
 ** Just send a String* to parse_string() and get the value it returns.
 ** if it returns a NULL pointer, check the parse_fail_msg to see why it
 ** failed.  Both are available externally through parser.h
 ** if you want to know WHY i did parsing by hand instead of YACC .. read
 ** the WHY file.
 **/

#include "parser.h"
#pragma implementation

#define GET_TOKEN   {yylval->release();                                     \
                     yylval=NULL;                                           \
                     inc = yylex();}
#define VERIFY1(a) if (!(a)) return NULL;
#define VERIFY2(a, b) if (!(a)) {delete b; return NULL;}
#define VERIFY3(a,b,c) if (!(a)) {delete b; delete c; return NULL;}

String parse_fail_msg;
String* parsestring;
int parse_pointer;
static int inc;

/** local functions **/
Value* parse_val_expr(void);
Value* parse_fexpr(void);
Value* parse_frest(void);
Value* parse_listexpr(Value_Type l_or_ex);
Val_List* parse_listrest(void);
Value* parse_const(void);
Value* parse_user_def(void);
Val_List* parse_exprlist(void);
Val_List* parse_symlist(void);
Val_List* parse_symrest(void);
Value* parse_coloned(void);


Value* raise_parse_error(const char* msg){
  char numstr[10];
  parse_fail_msg = msg;
  parse_fail_msg += ", line ";
  sprintf (numstr, "%d", lex_line);
  parse_fail_msg += numstr;
  while (inc)
     GET_TOKEN;
  return (Value*)NULL;
}
#define PERROR(a) {raise_parse_error(a); return NULL;}


Value* parse_string (String* instring){
  Value* retval;
  static int lex_init_yet = 0;
  start_lex ();
  parsestring = instring;
  parse_pointer = 0;
  parse_fail_msg = "";
  if (!lex_init_yet)
    {lex_init_yet = 1;
     lexinit();}
  yylval = NULL;
  GET_TOKEN;
  retval = parse_val_expr();
  if (inc)
    {delete retval;
     PERROR ("GARBAGE FOUND AFTER END OF VALUE");}
  parsestring = NULL;
  return retval;
}

Value* parse_val_expr(){
  switch (inc)
    {case L_PARENS: case RESERVED:
      return parse_fexpr();
    case NUM: case REAL: case STR: case OBJ: case APOSTROPHE: case SYM:
    case L_BRACKET: case ERR:
      return parse_const();
    case MTHD:
      PERROR ("ILLEGAL USE OF RESERVED FORM: 'method'");
    case OBJ_COLON: case SYM_COLON:
      PERROR ("INVALID MESSAGE CALL");
    case R_PARENS: case 0:
      PERROR ("UNEXPECTED END OF EXPRESSION");
    case R_BRACKET:
      PERROR ("CLOSING BRACKET } FOUND WITH NO CORRESPONDING OPEN");
    default:
      PERROR ("UNEXPECTED TOKEN: parse_val_expr()");}
}

Value* parse_fexpr(){
  Value *temp1;
  switch (inc)
    {case L_PARENS:
      GET_TOKEN;
      return parse_frest();
    case OBJ_COLON: case SYM_COLON:
      return parse_coloned();
    case RESERVED: case SYM:
      temp1 = yylval->grab();
      GET_TOKEN;
      return temp1;
    default:
      PERROR("EXPRESSION MUST BEGIN WITH FUNCTION OR METHOD CALL");}
}

Value* parse_frest(){
  Value *temp1;
  Val_List *temp2;
 switch(inc)
   {case MTHD:
      GET_TOKEN;
      temp2 = parse_symlist();
      if (parse_fail_msg.length())
	{delete (temp2);
	 return NULL;}
      temp1 = parse_val_expr();
      VERIFY2(temp1, temp2);
      if (inc==R_PARENS)
	{GET_TOKEN;
	 return new Value (new Method (temp2, temp1));}
      else
	{delete temp1;  delete temp2;
	 PERROR ("ONLY ONE EXPRESSION ALLOWED IN METHOD DECLARATION.");}
    case OBJ_COLON: case SYM_COLON:
      temp1 = parse_fexpr();
      VERIFY1 (temp1);
      temp2 = parse_exprlist();
      if (parse_fail_msg.length())
	{delete temp1; return NULL;}
      temp1->concat (temp2);
      return temp1;
    case L_PARENS: case SYM: case RESERVED:
      temp1 = parse_fexpr();
      VERIFY1 (temp1);
      temp2 = parse_exprlist();
      if (!temp2 && parse_fail_msg.length())
	{delete temp1;
	 return NULL;}
      temp1->toexpr(temp2);
      return temp1;
    default:
      PERROR("INVALID EXPRESSION");}
}

Value* parse_listexpr(Value_Type l_or_ex){
  Val_List* temp;
  Value* temp2;
  switch(inc)
    {case L_PARENS:
      GET_TOKEN;
      if (inc==MTHD)
	{temp2 = parse_frest();
	 return temp2;}
      temp = parse_listrest();
      if (!temp && parse_fail_msg.length())
	return NULL;
      return new Value (temp, l_or_ex);
    case APOSTROPHE: case OBJ: case NUM: case REAL: case STR: case ERR:
    case SYM: case RESERVED: case L_BRACKET:
      return parse_const();
    case MTHD:
      PERROR("EXPECTING A LIST OR A LITERAL AFTER APOSTROPHE (')");
    case 0:
      PERROR("UNEXPECTED END OF EXPRESSION");
    case R_BRACKET:
      PERROR ("CLOSING BRACKET } FOUND WITH NO CORRESPONDING OPEN");
    default:
      PERROR("UNABLE TO PARSE LIST");}
}

Val_List* parse_listrest(){
  Value *temp1;
  Val_List* temp2;
  switch (inc)
   {case R_PARENS:
     GET_TOKEN;
     return (Val_List*)NULL;
   case L_PARENS:
     temp1 = parse_listexpr(EXPR);
     break;
   case APOSTROPHE: case OBJ: case NUM: case REAL: case STR: case RESERVED:
   case SYM: case L_BRACKET: case ERR:
     temp1 = parse_const();
     break;
   case MTHD:
     GET_TOKEN;
     temp2 = parse_symlist();
     if (parse_fail_msg.length())
       {delete (temp2);
	return NULL;}
     temp1 = parse_val_expr();
     VERIFY2(temp1, temp2);
      if (inc==R_PARENS)
	temp1 = new Value (new Method (temp2, temp1));
      else
	{delete temp1;  delete temp2;
	 PERROR ("ONLY ONE EXPRESSION ALLOWED IN METHOD DECLARATION.");}
     break;
   case R_BRACKET:
     PERROR ("CLOSING BRACKET } FOUND WITH NO CORRESPONDING OPEN");
   case 0:
      PERROR("UNEXPECTED END OF EXPRESSION");
    default:
     PERROR("UNABLE TO PARSE LIST");}
  VERIFY1 (temp1);
  temp2 = parse_listrest();
  if (!temp2 && parse_fail_msg.length())
    {delete temp1;
     return temp2;}
  else
    return new Val_List (temp1, temp2);
}

Value* parse_const(){
  Value* temp;
  switch(inc)
    {case APOSTROPHE:
      GET_TOKEN;
      temp = parse_listexpr(LIST);
      if (temp)
	{if (temp->type == LIST)
	   return temp;
	 else
	   return new Value (new Val_List (new Value (QUOTE , RESERVED),
					   new Val_List (temp)), EXPR);}
      else
	return temp;
    case OBJ: case NUM: case REAL: case STR:
    case RESERVED: case SYM: case ERR:
      temp = yylval->grab();
      GET_TOKEN;
      return temp;
    case R_PARENS:
      PERROR("EXPECTED A CONSTANT, GOT A )");
    case OBJ_COLON: case  SYM_COLON:
      PERROR("INVALID METHOD CALL");
    case R_BRACKET:
      PERROR ("CLOSING BRACKET } FOUND WITH NO CORRESPONDING OPEN");
    case L_BRACKET:
      return parse_user_def();
    default:
      PERROR("INVALID TOKEN -- EXPECTING 'CONST'");}
}

Val_List* parse_exprlist(){
  Value *temp1;
  Val_List *temp2;
  switch(inc)
    {case R_PARENS:
      GET_TOKEN;
      return (Val_List*)NULL;
    case L_PARENS: case APOSTROPHE: case OBJ: case NUM: case REAL: case STR:
    case RESERVED: case SYM: case L_BRACKET: case ERR:
      temp1 = parse_val_expr();
      VERIFY1 (temp1);
      temp2 = parse_exprlist();
      if (!temp2 && parse_fail_msg.length())
	{delete temp1;
	 return temp2;}
      else
	return new Val_List (temp1, temp2);
    case R_BRACKET:
      PERROR ("CLOSING BRACKET } FOUND WITH NO CORRESPONDING OPEN");
    default:
      PERROR("INVALID METHOD CALLS IN VALUE EXPRESSION");}
}

Val_List* parse_symlist(){
  switch (inc)
    {case L_PARENS:
      GET_TOKEN;
      return parse_symrest();
    default:
      PERROR("METHOD DECLARATIONS REQUIRE A LIST OF VARIABLE NAMES");}
}

Val_List* parse_symrest(){
  Value* temp;
  switch (inc)
    {case R_PARENS:
      GET_TOKEN;
      return NULL;
    case SYM:
      temp = yylval->grab();
      GET_TOKEN;
      return new Val_List (temp, parse_symrest());
    default:
      PERROR("NON-SYMBOL FOUND IN PARAMETER LIST");}
}

Value* parse_coloned(){
  Value* temp1, *temp2, *temp3;
  switch (inc)
   {case OBJ_COLON: case SYM_COLON:
     temp1 = yylval->grab();
     GET_TOKEN;
     if (inc != SYM)
       {delete temp1;
	PERROR("OBJECT CALL REQUIRES VALID METHOD NAME");}
     temp2 = yylval->grab();
     GET_TOKEN;
     temp3 = (new Value (CALL, RESERVED));
     temp3->toexpr (new Val_List (temp1, new Val_List (temp2)));
     return temp3;
   default:
     PERROR ("PARSE ERROR IN parse_coloned()");}
}

Value* parse_user_def(){
  Value* ob_type;
  Value* val;
  Value* retval;
  GET_TOKEN;
  ob_type = parse_val_expr();
  if (parse_fail_msg.length())
    {delete ob_type;
     return NULL;}
  if (ob_type -> get_type() != OBJ)
    {delete ob_type;
     PERROR ("USER DEFINED DATA TYPE MUST BE AN OBJECT");}
  val = parse_val_expr();
  if (parse_fail_msg.length())
    {delete ob_type;
     delete val;
     return NULL;}
  if (val->get_type() == EXPR)
    {delete ob_type;
     delete val;
     PERROR ("USER-DEFINED DATA VALUE MUST BE A CONSTANT, NOT EXPRESSION");}
  if (inc != R_BRACKET)
    {delete ob_type;
     delete val;
     PERROR ("EXPECTING '}' AFTER USER-DEFINED DATA");}
  GET_TOKEN;
  retval = new Value (ob_type, val);
  val->release();
  ob_type->release();
  return retval;
}