#include"mbasic.h"

static int LoopLevel=0;
int InFunction=0;
int FuncExpression();
int __EOF=0;
char current_function[128];
static int IfLevel=0;

extern int GetCurNrParams();

char Look;
char current_token[64];

void Expression(void);

void GetChar(void)
{
 Look=GetProgramChar();
 if(Look=='\0')
 {
  __EOF=1;
  return;
 }
}

int IsAlpha(char c)
{
 if(c>='a' && c<='z') return 1;
 if(c>='A' && c<='Z') return 1;
 if(c=='_') return 1;
 return 0;
}

int IsDigit(char c)
{
 if(isdigit(c)) return 1;
 return 0;
}

int IsAlnum(char c)
{
 if(IsAlpha(c) || IsDigit(c)) return 1;
 return 0;
}

int IsWhite(char c)
{
 if(c==' ' || c=='\t') return 1;
 return 0;
}

int IsMulop(char c)
{
 if(c=='*' || c=='/') return 1;
 return 0;
}

int IsAddop(char c)
{
 if(c=='+' || c=='-') return 1;
 return 0;
}

void SkipWhite(void)
{
 if(__EOF) return;
 while(IsWhite(Look) || Look=='\r' || Look=='\n') GetChar();
}

void MathSkipWhite(void)
{
 while(IsWhite(Look)) GetChar();
}

void SkipCRLF(void)
{
}

void GetName(void)
{
 char * p;
 p=current_token+0;
 SkipWhite();
 memset(p,0,sizeof(current_token));
 if(__EOF) return;
 if(!IsAlpha(Look)) Expected("Name");
 while(IsAlnum(Look))
 {
  *p++=toupper(Look);
  GetChar();
 }
/* printf("|%s|\n",current_token); */
}

static int ishex(char c)
{
 if(c>='0' && c<='9') return 1;
 if(c>='a' && c<='f') return 1;
 if(c>='A' && c<='F') return 1;
 return 0;
}

static int h2d(char c)
{ 
 if(c>='0' && c<='9') return c-'0';
 if(c>='a' && c<='f') return (c-'a')+10;
 if(c>='A' && c<='F') return (c-'A')+10;
 Error("Bad numeric format, nor hex neither decimal");
}

void GetNum(void)
{
 char * p;
 int p1;
 p=current_token+0;
 if(!strncmp(InputFilePtr-1,"0x",2)) goto MAKE_HEX;
 memset(p,0,sizeof(current_token));
 if(!IsDigit(Look)) Expected("Digit");
 while(IsDigit(Look))
 {
  *p++=Look;
  GetChar();
 }
 return;
MAKE_HEX:
 p1=0;
 GetChar();
 GetChar();
 while(ishex(Look))
 {
  p1=(p1<<4)|h2d(Look);
  GetChar();
 }
 sprintf(current_token,"%u",p1);
}

void Match(char c)
{
 if(Look!=c) Expected("'%c'",c);
 GetChar();
 SkipWhite();
}

static char __ident_str[256];

void Ident(void)
{
 char tmp_name[128];
 char * q;
 int k;
 if(Look=='"')
 {
  char * q;
  memset(__ident_str,0,256);
  q=__ident_str;
  GetChar();
  while(Look!='"')
  {
   *q=Look;
   q++;
   GetChar();
  }
  GetChar();
  k=RegisterString(__ident_str);
  emit("mov eax,_LC%u\n",k);
  return;
 }
 if(Look=='$')
 {
  int n;
  if(!InFunction) Error("$n variables can be used only in functions");
  GetChar();
  GetNum();
  k=atoi(current_token);
  if(!k) Error("You cannot read $0 because it is function return value");
  n=GetCurNrParams();
  emit("mov eax,dword [ebp+%u]\n",(n<<2)-((k-1)<<2));
  return;
 }
 GetName();
 STORE_TMP(tmp_name);
 if(Look=='[')
 {
  gen_array_code(1);
  return;
 }
 if(Look=='(')
 {
  Match('(');
  k=FuncExpression();
  Match(')');
  emit("call %s\n",tmp_name);
  emit("mov esp,ebp\n");
  emit("pop ebp\n");
  if(k) emit("add esp,%u\n",k);
 } else {
  emit("mov eax,dword [%s]\n",tmp_name);
  RegisterVariable(tmp_name);
 }
}

void Factor(void)
{
 if(Look=='(')
 {
  Match('(');
  Expression();
  Match(')');
 } else if(IsAlpha(Look) || (Look=='$' && InFunction) || Look=='"')
 {
  Ident();
 } else {
  GetNum();
  emit("mov eax,%s\n",current_token);
 }
}

void SignedFactor(void)
{
 if(Look=='+') GetChar();
 if(Look=='-')
 {
  GetChar();
  if(IsDigit(Look))
  {
   GetNum();
   emit("mov eax,-%s\n",current_token);
  } else {
   Factor();
   emit("not eax\n");
  }
 } else Factor();
}

void Multiple(void)
{
 Match('*');
 Factor();
 emit("mul dword [esp]\n");
 emit("add esp,4\n");
}

void Divide(void)
{
 Match('/');
 Factor();
 emit("xchg dword [esp],eax\n");
 emit("div dword [esp]\n");
 emit("add esp,4\n");
}

void Term(void)
{
 SignedFactor();
 while(IsMulop(Look))
 {
  emit("push eax\n");
  switch(Look)
  {
   case '*':
    Multiple();
    break;
   case '/':
    Divide();
    break;
  }
 }
}

void Add(void)
{
 Match('+');
 Term();
 emit("add eax,dword [esp]\n");
 emit("add esp,4\n");
}

void Substract(void)
{
 Match('-');
 Term();
 emit("sub eax,dword [esp]\n");
 emit("not eax\n");
 emit("add esp,4\n");
}

void Expression(void)
{
 Term();
 while(IsAddop(Look))
 {
  emit("push eax\n");
  switch(Look)
  {
   case '+':
    Add();
    break;
   case '-':
    Substract();
    break;
  }
 }
}

void Assignment(char * p)
{
 int k;
 if(Look=='(')
 {
  Ident();
 } else {
  Match('=');
  Expression();
  if(!strncmp(p,"eax",3) && strlen(p)==3)
  {
   /* Do nothing */
  } else if(p[0]=='$')
  {
   int n;
   n=GetCurNrParams();
   k=atoi(p+1);
   emit("mov dword [ebp+%u],eax\n",(n<<2)-((k-1)<<2));
  } else {
   emit("mov dword [%s],eax\n",p);
   RegisterVariable(p);
  }
 }
}

void InitParser(void)
{
 GetChar();
 SkipWhite();
}

static char * res_token[]={
 "IF",
 "THEN",
 "ELSE",
 "ENDIF",
 "FOR",
 "TO",
 "DOWNTO",
 "NEXT",
 "WHILE",
 "WEND",
 "END",
 "LABEL",
 "EXIT",
 "ASMBEGIN",
 "ASMEND",
 "GOTO",
 "GOSUB",
 "RETURN",
 "FUNCTION",
 "ENDS",
 "HALT",
 "INCLUDE",
 "REM",
 "DIM",
};

#define NR_RES_TOK		(sizeof(res_token)/sizeof(char *))

void SkipToken(unsigned char c)
{
 int i;
 for(i=0;i<strlen(res_token[c]);i++) GetChar();
}

static int is_resv(char * n)
{
 int i;
 for(i=0;i<NR_RES_TOK;i++)
 {
  if(!strncmp(res_token[i],n,strlen(n)) && strlen(n)==strlen(res_token[i]))
   return i;
 }
 return -1;
}


static int gosub_level=0;

void DoLabel(void)
{
 SkipWhite();
 GetName();
 register_label(current_token);
 _emit("____%s:\n",current_token);
}

void DoGoto(void)
{
 SkipWhite();
 GetName();
 emit("jmp ____%s\n",current_token);
}

void DoGosub(void)
{
 SkipWhite();
 GetName();
 gosub_level++;
 emit("call ____%s\n",current_token);
}

void DoReturn(void)
{
 SkipWhite();
 gosub_level--;
 emit("ret\n");
}

void ParseComment(void)
{
 while(Look!='\n') GetChar();
 SkipWhite();
}

void Block(void)
{
 char tmp[64];
 int t;
 for(;;)
 {
  SkipCRLF();
  SkipWhite();
  if(Look=='$')
  {
   int k;
   if(!InFunction) Error("$n variables can be used only in functions");
   GetChar();
   GetNum();
   k=atoi(current_token);
   Match('=');
   Expression();
   t=GetCurNrParams();
   if(k)
    emit("mov dword [ebp+%u],eax\n",(t<<2)-((k-1)<<2));
   continue;
  }
  GetName();
  if(__EOF) return;
  STORE_TMP(tmp);
  if(Look=='[')
  {
   gen_array_code(0);
   continue;
  }
  if(Look=='(')
  {
   Match('(');
   t=FuncExpression();
   Match(')');
   emit("call %s\n",tmp);
   emit("mov esp,ebp\n");
   emit("pop ebp\n");
   if(t) emit("add esp,%u\n",t);
   continue;
  } 
  t=is_resv(tmp);
  if(t==-1)
  {
   Assignment(tmp);
  } else {
   switch(t)
   {
    case TOK_END:
     if(InFunction)
     {
      return;
     }
     emit("jmp INTERNAL_app_exit\n");
     continue;
    case TOK_EXIT:
     if(!InFunction) Error("Cannot do EXIT outside function\n");
     emit("ret\n");
     return;
    case TOK_LABEL:
     DoLabel();
     continue;
    case TOK_GOTO:
     DoGoto();
     continue;
    case TOK_GOSUB:
     DoGosub();
     continue;
    case TOK_RETURN:
     DoReturn();
     continue;       
    case TOK_ASMBEGIN:
     ParseAssemblyBlock();
     continue;
    case TOK_ASMEND:
     Error("ASMEND without ASMBEGIN");       
    case TOK_FOR:
     LoopLevel++;
     ParseForLoop();
     continue; 
    case TOK_NEXT:
     if(!LoopLevel) Error("NEXT without FOR");
     LoopLevel--;
     return;
    case TOK_FUNCTION:
     ProcessFunctionDecl();
     continue;
    case TOK_ENDS:
     if(InFunction || LoopLevel) Error("ENDS must be declared at the end of file");
     return; 
    case TOK_IF:
     IfLevel++;
     ParseIf();
     continue; 
    case TOK_THEN:
     if(!IfLevel) printf("THEN without IF");
     continue;
    case TOK_ENDIF:
     if(!IfLevel) Error("ENDIF without IF");
     IfLevel--;
     return;
    case TOK_ELSE:
     if(!IfLevel) Error("ELSE without IF");
     return; 
    case TOK_HALT:
     emit("jmp INTERNAL_app_exit\n");
     continue;
    case TOK_INCLUDE:
     ParseInclude();
     continue; 
    case TOK_REM:
     ParseComment();
     continue;
    case TOK_DIM:
     ParseArrayDecl();
     continue; 
    default:
     Error("Token '%s' is not yet supported\n",current_token);
   }
  }
 }
}
