/*****************************************************
 * PROJECT:       BBB interpreter
 * ORGANIZATION:  Microfluffy Corp.
 * LANGUAGE:      any ANSI C++ complaint
 * FILE:          parser.cpp
 * DESCRIPTION:   This file implements the parser and
 *                the line execution engine
 * VERSION:       1.0
 *****************************************************/

#include <iostream.h>
#include <string.h>
#include <stdlib.h>
#include "bbb.h"


/* there are a bunch of helper procedures here, each of which
    parses a different nonterminal in the BNF.  See the project
    documentation for more information. */

/* a few prototypes */
void Factor(Result &r);
void PrimaryExp(Result &r);
void Term(Result &r);

/*
    <expression> ::= <term>
		     <term> + <expression>
		     <term> - <expression>
  */
void Expression(Result &r)
{
  Result rr;
  TokenType op;

  Term(r);
  while ((Token == T_PLUS) || (Token == T_MINUS)) {
    op = Token;
    NextToken();
    Term(rr);
    if (r.tag != rr.tag)
      PrintError(e_type, CurrentLine);
    switch (op) {
    case T_PLUS:
      if (r.tag == T_NUM)
	r.intResult += rr.intResult;
      else
	strcat(r.strResult, rr.strResult);
      break;
    case T_MINUS:
      if (r.tag == T_NUM)
	r.intResult -= rr.intResult;
      else
	PrintError(e_type, CurrentLine);
      break;
    default:
      break;
    }
  }  
} 

/*
    <term> ::= <factor>
	       <factor> * <term>
	       <factor> / <term>
	       <factor> mod <term>
  */
void Term(Result &r)
{
  Result rr;
  TokenType op;

  Factor(r);
  while ((Token == T_STAR) || (Token == T_SLASH) || (Token == T_MOD)) {
    op = Token;
    NextToken();
    Factor(rr);
    if ((r.tag == T_STRING) || (rr.tag == T_STRING))
      PrintError(e_type, CurrentLine);
    switch(op) {
    case T_STAR: r.intResult *= rr.intResult; break;
    case T_MOD: r.intResult %= rr.intResult; break;
    case T_SLASH:
      if (rr.intResult == 0)
	PrintError (e_divzero, CurrentLine);
      r.intResult /= rr.intResult;
      break;
    default:
      break;
    }
  }
}

/*
    <factor> ::= <primary_exp>
		  - <primary_exp>
  */
void Factor(Result &r)
{
  if (Token == T_MINUS) {
    NextToken();
    PrimaryExp(r);
    if (r.tag == T_STRING)
      PrintError(e_type, CurrentLine);
    r.intResult = - r.intResult;
  }
  else PrimaryExp(r);
}

/*
    <primary_exp> ::= <var>
		      <number>
		      <string>
		      ( <expresion> )
  */
void PrimaryExp(Result &r)
{
  switch (Token) {
  case T_VAR:   /* <var> */
    if (IsStrVar(StrValue)) {
      r.tag = T_STRING;
      GetStrVal(StrValue, r.strResult);
    }
    else {
      r.tag = T_NUM;
      r.intResult = GetIntVal(StrValue);
    }
    NextToken();
    break;

  case T_NUM:    /* <number> */
    r.tag = T_NUM;
    r.intResult = IntValue;
    NextToken();
    break;

  case T_STRING: /* <string> */
    r.tag = T_STRING;
    strcpy(r.strResult, StrValue);
    NextToken();
    break;

  case T_LPAREN:  /* ( <expression> ) */
    NextToken();
    Expression(r);
    if (Token != T_RPAREN)
      PrintError(e_syntax, CurrentLine);
    NextToken();
    break;

  default:
    PrintError(e_syntax, CurrentLine);
    break;
  }
}

/*
    <exp_list> ::=  <expression>
		    <expression> , <exp_list>
    <statement> ::= print <exp_list>
		    print <exp_list> ;
  */
void ParsePrint(void)
{
  Result r;

  do {
    NextToken();
    Expression(r);
    switch(r.tag) {
    case T_NUM:  
      cout << r.intResult; break;
    case T_STRING:
      cout << r.strResult; break;
    }
  } while (Token == T_COMMA);

  if (Token == T_SEMI)
    NextToken();
  else
    cout << endl;

  if (Token != T_EOL)
    PrintError(e_syntax, CurrentLine);
}

/*
    <statement> ::= end
  */
void ParseEnd(void)
{
  NextToken();
  if (Token != T_EOL)
    PrintError(e_syntax, CurrentLine);
  else
    exit(1);
}

/*
   <statement> ::= goto <number>
  */

void ParseGoto(int &linecode)
{
  NextToken();
  if (Token != T_NUM)
    PrintError(e_syntax, CurrentLine);
  else
    linecode = IntValue;
  NextToken();
  if (Token != T_EOL)
    PrintError(e_syntax, CurrentLine);
}

int ParseLine(char* st)
{
  int linecode = NEXT_LINE;	/*by default we will just go to the
				  next line*/

  StartLine(st);		/*start the scanner on this line*/

  switch (Token) {
  case T_END:
    ParseEnd(); break;
  case T_GOTO:
    ParseGoto(linecode); break;
  case T_PRINT:
    ParsePrint(); break;
  case T_EOL:
    break; /* comment handling */

  default:
    PrintError(e_syntax, CurrentLine); break;
  }
  return linecode;
}

