/*
        Handles the keywords (and the eval())
        and the directives...
        And the if...tree
*/


/*
if tree's types
 1 = If (remember to check for else on pop)
*/

if_tree *free_if_tree(if_tree *top){
 if_tree *t;
 while(top){
           t=top->prev;
           free(top); malfreecntr--;
           top=t;
                }
return top; }

void push_if_tree(char wot, int num, char ot){
 if_tree *t;
 char k;
 int numr;
 //A: steal all the end of line crap
 if(ot==0)
      while(top_if_line_tree){
         k=top_if_line_tree->type; numr=top_if_line_tree->m_num;
         t=top_if_line_tree->prev; free(top_if_line_tree); malfreecntr--;
         top_if_line_tree=t;
         push_if_tree(k,numr,1);
                }
 t=(if_tree *)malloc(sizeof(if_tree));
 if(t==NULL){ out_of_mem=1; return; }
 malfreecntr++;
 t->prev=top_if_tree; top_if_tree=t;
 t->type=wot; t->m_num=num; t->s_num=0;
 t->linet=ot;
 t->in_case=in_case;
return; }

void push_if_line_tree(char wot, int num){
 if_tree *t;
 t=(if_tree *)malloc(sizeof(if_tree));
 if(t==NULL){ out_of_mem=1; return; }
 malfreecntr++;
 t->prev=top_if_line_tree; top_if_line_tree=t;
 t->type=wot;  t->m_num=num; t->s_num=0;
 our_line_pushed=1;
return ;}

void do_break(k,num){
 if_tree *t; 
 switch(k){
                case 1://if
                case 4://else
                        t=top_if_tree;
                        while(t){
                                  if(t->type==3 || t->type==5){
                                                do_break(t->type,t->m_num);
                                                break;
                                                }
                                  t=t->prev;
                                        }
                        error("break not in a loop",WARNING);
                                break;
                case 2: error("break not in a loop",WARNING); break;//asm
                case 3: //for,while
                        if(prg_out)sprintf(asm_la_str,"J Evf%d",num);
                        else sprintf(asm_la_str,"Jmp Ev_fail_%d",num);
                        go_assemble();
                        break;
                 case 5://switch
                        if(prg_out)sprintf(asm_la_str,"J Esd%d",num);
                        else sprintf(asm_la_str,"Jmp Es_done_%d",num);
                        go_assemble();
                        break;
                        }
return; }

typedef struct else_tree{
                long num;
                char type;
                struct else_tree *prev;
                }else_tree;
else_tree *top_else=NULL;

void free_else(){
 else_tree *t;
 while(top_else){
        t=top_else->prev;
        free(top_else); malfreecntr--;
        top_else=t;
        }
return; }

void store_else(char type, long num){
 else_tree *t;
 t=(else_tree *)malloc(sizeof(else_tree));
 if(t==NULL){ out_of_mem=1; return; }
 malfreecntr++;
 t->prev=top_else;
 top_else=t;
 t->type=type;
 t->num=num;
return; }

void restore_else(){
 else_tree *t;
 while(top_else){
        t=top_else->prev;
        if(top_else->type==1){
                push_if_tree(4,top_else->num,0);
                                }
         else{
                push_if_line_tree(4,top_else->num);
                        }
        free(top_else); malfreecntr--;
        top_else=t;
        }
return; }

void do_tree_thing(char k,int num,int jj,char who){
 switch(k){
        case 1://if
                if(las_was_if==0)if(readline()==0){
                        do_defines(); lptr=0; lineptr=line;
                        get_nextword();
                        las_was_if=1;
                        if(!strcmp(word,"else")){
                                        skipspaces();
                                        if(lineptr[lptr]=='{'){ lptr++;
                                                                las_was_if=0;
                                                                //push_if_tree(4,num,0);//cur_if_num++
                                                                store_else(1,num);
                                                                }
                                        else {//push_if_line_tree(4,num);//cur_if_num++
                                                   store_else(2,num);
                                                        }
                                        if(jj==0){
                                                if(prg_out)sprintf(asm_la_str,"J Evd%d",num);
                                                else sprintf(asm_la_str,"Jmp Ev_done_%d",num);//cur_if_num-1
                                                go_assemble();
                                                        }
                                                }
                        else lptr=0;//not for us
                         }
                if(jj==0){
                        if(prg_out)sprintf(asm_la_str,"P Evf%d",num);
                        else sprintf(asm_la_str,"Ev_fail_%d:",num);
                        go_assemble();
                        }
                break;
          case 2: in_multi_asm=0;
                  if(debug_type && outfile)fprintf(outfile,";         }\n");
                  break; //asm
          case 3://for
                if(prg_out)sprintf(asm_la_str,"J Evl%d",num);
                else sprintf(asm_la_str,"Jmp Ev_loop_%d",num);
                go_assemble();
                if(prg_out)sprintf(asm_la_str,"P Evf%d",num);
                else sprintf(asm_la_str,"Ev_fail_%d:",num);
                go_assemble();
                break;
          case 4://else
                if(our_line_pushed) store_else(who,num);
                else{   if(prg_out)sprintf(asm_la_str,"P Evd%d",num);
                        else sprintf(asm_la_str,"Ev_done_%d:",num);
                        go_assemble(); }
                break;
          case 5://switch
                if(prg_out)sprintf(asm_la_str,"P Esd%d",num);
                else sprintf(asm_la_str,"Es_done_%d:",num);
                go_assemble();
                in_switch--;
                break;
                }
return; }

void pop_if_tree2(){
 if_tree *t;
 char k;
 int num;
 if(top_if_tree==NULL)return;
 k=top_if_tree->type; num=top_if_tree->m_num;
 if(k==5)in_case=top_if_tree->in_case;
 t=top_if_tree->prev; free(top_if_tree); malfreecntr--; top_if_tree=t;
 do_tree_thing(k,num,0,1);
 for(;;){
        if(top_if_tree==NULL)break;
        if(top_if_tree->linet==1) pop_if_tree2();
        else break;
        }
return; }

void pop_if_tree(){
 pop_if_tree2();
 restore_else();
return; }

int pop_if_line_tree(){
 if_tree *t;
 char k;
 int num;
 if(our_line_pushed){
                t=top_if_line_tree;
                while(t){
                        if(t->type==1){
                              do_tree_thing(1,t->m_num,0,2);
                             // return 0;
                              break;
                                      }
                        t=t->prev;
                        }
               //return 0;
                }

 if(top_if_line_tree==NULL)return 0;
 while(top_if_line_tree){
         k=top_if_line_tree->type; num=top_if_line_tree->m_num;
         t=top_if_line_tree->prev; free(top_if_line_tree); malfreecntr--; top_if_line_tree=t;
         do_tree_thing(k,num,1,2);
         }
 restore_else();
return 1; }


int eval(char need_b){
  int a;
    skipspaces();
    if(need_b){
            if(lineptr[lptr]!='('){ error("Missing (",ERROR); return 1; }
            lptr++;
            }
    skipspaces();
    if(lineptr[lptr]=='('){ error("Unexpected (",ERROR); return 1; }
    for(;;){
         a=getexpr(1,1);
         if(a==1)return 1;
         if(a=='|'){
                    cmp_mathreg_0();
                    if(prg_out)sprintf(asm_la_str,"N Evp%d",cur_if_num);
                    else sprintf(asm_la_str,"Jne Ev_pass_%d",cur_if_num);
                    go_assemble();
                       }
          else if(a=='&'){
                    cmp_mathreg_0();
                    if(prg_out)sprintf(asm_la_str,"E Evf%d",cur_if_num);
                    else sprintf(asm_la_str,"Je Ev_fail_%d",cur_if_num);
                    go_assemble();
                           }
          else if(lineptr[lptr-1]==')' || need_b==0){ 
                     skipspaces();
                     if(lineptr[lptr]==1){ error("Unexpected )",ERROR);
                                            return 1; }
                     cmp_mathreg_0();
                     if(prg_out)sprintf(asm_la_str,"E Evf%d",cur_if_num);
                     else sprintf(asm_la_str,"Je Ev_fail_%d",cur_if_num);
                     go_assemble();
                     //if(nowritepos==0){
                             if(prg_out)sprintf(asm_la_str,"P Evp%d",cur_if_num);
                             else sprintf(asm_la_str,"Ev_pass_%d:",cur_if_num);
                             go_assemble();
                     //        }
                     break;
                        }
          else { error("Missing )",ERROR); return 1; }
               }
return 0; }

int do_keyword(char *w){
  int wh,a,tmp_num;
  char str[100];
        for(wh=0;;wh++){
                   if(keywords[wh]==NULL){ wh=-1; break; }
                   if(!strcmp(keywords[wh],w))break;
                        }
       switch(wh){
            case 0://"goto"
                  get_nextword();
                  if(word[0]==0){error("Missing label",ERROR); return 1; }
                  fadd_gotopos(word,0);
                  if(prg_out)sprintf(asm_la_str,"J _%s",word);
                  else sprintf(asm_la_str,"Jmp _%s",word);
                  go_assemble();
                        break;
            case 1://"asm"
                  if(prg_out){
                             error("PRG files cannot have inline assembler",ERROR);
                             return 1;
                                }
                   skipspaces();
                   if(lineptr[lptr]=='{'){lptr++;
                                         in_multi_asm=1;
                                         push_if_tree(2,0,0);
                                                }
                   else{
                             sprintf(asm_la_str,"%s",lineptr+lptr);
                             go_assemble();
                                }
                        break;
            case 2://"for"
                skipspaces();
                if(lineptr[lptr]!='('){ error("For statement missing (",ERROR);
                                        return 1; }
                lptr++;
                skipspaces();
                if(lineptr[lptr]!=0 && lineptr[lptr]!=';')if(getexpr(0,0)==1)return 1;
                if(readline()!=0 || endofit){  error("Syntax error",ERROR);
                                                return 1;}
                do_defines(); lptr=0; lineptr=line;
                if(debug_type){sprintf(asm_la_str,"; %s",line); go_assemble(); }
                tmp_num=cur_if_num++;
                if(prg_out)sprintf(asm_la_str,"J Fs%d",tmp_num);
                else sprintf(asm_la_str,"Jmp For_start_%d",tmp_num);
                go_assemble();
                if(prg_out)sprintf(asm_la_str,"P Evl%d",cur_if_num);
                else sprintf(asm_la_str,"Ev_loop_%d:",cur_if_num);
                go_assemble();
                skipspaces();
                if(lineptr[lptr]!=0 && lineptr[lptr]!=';') if(eval(0)==1)return 1;
                if(readline()!=0 || endofit){  error("Syntax error",ERROR);
                                                return 1;}
                do_defines(); lptr=0; lineptr=line;
                if(debug_type){sprintf(asm_la_str,"; %s",line); go_assemble(); }

                skipspaces();
                if(lineptr[lptr]!=0 && lineptr[lptr]!=')'){
                                                if(getexpr(0,2)==1)return 1;
                                                }
                else if(lineptr[lptr]==')')lptr++;
                if(lineptr[lptr-1]!=')'){ error("for statement missing )",ERROR);
                                                return 1; }
                 skipspaces();
                 if(lineptr[lptr]=='{'){ lptr++;
                                        push_if_tree(3,cur_if_num++,0);
                                        }
                 else { push_if_line_tree(3,cur_if_num++);
                        return 2;}                
                if(prg_out)sprintf(asm_la_str,"P Fs%d",tmp_num);
                else sprintf(asm_la_str,"For_start_%d:",tmp_num);
                go_assemble();
                        break;
            case 3://"while"
                if(prg_out)sprintf(asm_la_str,"P Evl%d",cur_if_num);
                else sprintf(asm_la_str,"Ev_loop_%d:",cur_if_num);
                go_assemble();
                if(eval(1)==1)return 1;
                 skipspaces();
                 if(lineptr[lptr]=='{'){ lptr++;
                                        push_if_tree(3,cur_if_num++,0);
                                        }
                 else { push_if_line_tree(3,cur_if_num++);
                        return 2;}
                        break;
            case 4://"return"
                skipspaces();
                if(lineptr[lptr]!=0 && lineptr[lptr]!=';'){
                        if(thecur_proc->type==5 && thecur_proc->type!=10) error("procedure doesn't return a value",WARNING);
                        if(getexpr(0,0)==1)return 1;
                        mov_mathreg_to_bracketreg();
                                                }
                else{
                    if(thecur_proc->type!=5 && thecur_proc->type!=10) error("procedure should return a value",WARNING);
                                }
                asm_ret();
                last_was_ret=1;
                        break;
            case 5://"if"
                if(eval(1)==1)return 1;
                skipspaces();
                if(lineptr[lptr]=='{'){ lptr++;
                                        push_if_tree(1,cur_if_num++,0);
                                        }
                else{  push_if_line_tree(1,cur_if_num++);
                       return 2;
                        }
                break;
            case 6://"else"
                  error("else without if",ERROR); return 1;
                        break;
            case 7://"switch"
                  skipspaces();
                  if(lineptr[lptr]!='('){ error("switch statement missing (",ERROR);
                                         return 1; }
                  lptr++;
                  if(getexpr(0,2)==1)return 1;
                  if(lineptr[lptr-1]!=')'){ error("switch statement missing )",ERROR);
                                                return 1; }
                  skipspaces();
                  if(lineptr[lptr]!='{'){ error("switch statement missing {",ERROR);
                                         return 1; }
                   lptr++;
                   push_if_tree(5,cur_if_num++,0);
                   in_switch++;
                   in_case=0;
                        break;
            case 8://"case"
                 if(top_if_tree==NULL || top_if_tree->type!=5){
                                        error("case out of switch",ERROR);
                                        return 1; }
                 skipspaces(); a=0;
                 if(lineptr[lptr]=='-'){a=1; lptr++; }
                 if(get_nextnumber(1,a)==1)return 1;
                 //future update will check previous numbers to see if dublicate case error
                 skipspaces(); if(lineptr[lptr]!=':'){ error("case missing :",ERROR);
                                                                return 1; }
                 lptr++;
                 if(prg_out)sprintf(asm_la_str,"P Ecf%d_%d",top_if_tree->m_num,top_if_tree->s_num++);
                 else sprintf(asm_la_str,"Ec_fail_%d_%d:",top_if_tree->m_num,top_if_tree->s_num++);
                 go_assemble();

                 if(prg_out)sprintf(asm_la_str,"Cm %ld",pass_num);// (eax will hold switch value)
                 else sprintf(asm_la_str,"Cmp %s,%ld",MATHREG,pass_num);// (eax will hold switch value)
                 go_assemble();

                 if(prg_out)sprintf(asm_la_str,"N Ecf%d_%d",top_if_tree->m_num,top_if_tree->s_num);
                 else sprintf(asm_la_str,"Jne Ec_fail_%d_%d",top_if_tree->m_num,top_if_tree->s_num);
                 go_assemble();
                in_case=1;
                return 2;
                        break;
            case 9://"default"
                 if(top_if_tree==NULL || top_if_tree->type!=5){
                                        error("default out of switch",ERROR);
                                        return 1; }
                skipspaces();
                if(lineptr[lptr]!=':'){error("default missing :",ERROR);
                                        return 1; }
                if(prg_out)sprintf(asm_la_str,"P Ecf%d_%d",top_if_tree->m_num,top_if_tree->s_num++);
                else sprintf(asm_la_str,"Ec_fail_%d_%d:",top_if_tree->m_num,top_if_tree->s_num++);
                go_assemble();
                lptr++;
                in_case=1;
                return 2;
                        break;
            case 10: case 11: case 12: case 13: case 14: case 15: case 17: 
               error("Declaration Error",ERROR); return 1;//these should have already been caught by main
            case 16: break; //"sizeof" done in getexpr
            case 18://break;
                    if(top_if_tree && top_if_tree->type==5 && in_case==1){
                                             //if(prg_out)sprintf(asm_la_str,"J Esd%d",top_if_tree->m_num);
                                             //else sprintf(asm_la_str,"Jmp Es_done_%d",top_if_tree->m_num);
                                             //go_assemble();
                                             in_case=0;
                                                }
                    if(top_if_tree==NULL) { error("break not in a loop",WARNING);
                                        return 1; }
                    do_break(top_if_tree->type,top_if_tree->m_num);
                    break;
            default:
               sprintf(str,"Error with keyword %s",w);
               error(str,ERROR); return 1;
                }

return 0; }

void free_def(){
 while(top_def){
        cur_def=top_def->prev;
        if(top_def->name){free(top_def->name); malfreecntr--; }
        if(top_def->line){free(top_def->name); malfreecntr--; }
        free(top_def);malfreecntr--;
        top_def=cur_def;
        }
return;}

void makedefine(char *label,char *rest){
 cur_def=(def_stack *)malloc(sizeof(def_stack));
 if(cur_def==NULL){ out_of_mem=1; return; }
 malfreecntr++;
 cur_def->prev=top_def; top_def=cur_def;
 cur_def->removedit=0;
 cur_def->name=NULL; cur_def->line=NULL;
 cur_def->name=(char *)malloc(strlen(label)+1);
 if(cur_def->name==NULL){ out_of_mem=1; return; }
 malfreecntr++;
 strcpy(cur_def->name,label);
 cur_def->line=(char *)malloc(strlen(rest)+1);
 if(cur_def->line==NULL){ out_of_mem=1; return; }
 malfreecntr++;
 strcpy(cur_def->line,rest);
return; }

int find_define(char *label){//ret==-1 not found
 cur_def=top_def;
 while(cur_def){
        if(cur_def->removedit==0 && cur_def->name && !strcmp(cur_def->name,label))
                                                           return 1;
        cur_def=cur_def->prev;
                }
return -1; }


void skiptillendif(){
  for(;;){
       if(readline()==0){
               do_defines(); lptr=0; lineptr=line;
               skipspaces();
               if(lineptr[lptr]=='#'){ lptr++;
                                       get_nextword();
                                       if(!strcmp(word,"endif"))break;
                                       if(!strcmp(word,"else")){
                                                        in_ifdef++;
                                                        break; }
                                        }
                         }
          if(endofit)break;
                }
return; }

int do_directive(){
 int wh,tp,pr;
 char str[100],k;
 get_nextword();
 if(word[0]==0){ error("Missing directive",ERROR); return 1; }
 for(wh=0; ;wh++){
        if(directives[wh]==NULL){ wh=-1; break; }
        if(!strcmp(directives[wh],word))break;
        }
 switch(wh){
        case 0: //include
                skipspaces();
                if(lineptr[lptr]=='\"')tp=1;
                else if(lineptr[lptr]=='<')tp=2;
                else { error("include filename syntax error",ERROR);
                                return 1; }
                lptr++; pr=0; skipspaces();
                for(;;){
                   k=lineptr[lptr];
                   if(k=='>' || k==0 || k=='\"')break;
                   word[pr++]=k;
                   lptr++;
                   }
                word[pr]=0;
                if(word[0]==0){ error("Missing filename",ERROR);
                                                        return 1;}
                skipspaces();
                if(lineptr[lptr]=='\"'){
                               if(tp!=1) error("filename type mismatch",WARNING);
                                        }
                else if(lineptr[lptr]=='>'){
                                if(tp!=2) error("filename type mismatch",WARNING);
                                           }
                else error("include filename missing end marker",WARNING);
                do_include(word,tp);
                 break;
        case 1://define
                get_nextword(); if(word[0]==0){ error("Missing label",ERROR);
                                                        return 1;}
                skipspaces();
                if(find_define(word)!=-1){ sprintf(str,"\"%s\" already defined",word);
                                           error(str,ERROR); return 1; }
                makedefine(word,lineptr+lptr);
                        break;
        case 2://ifdef
                get_nextword(); if(word[0]==0){ error("Missing label",ERROR);
                                 return 1;}
                if(find_define(word)==-1)skiptillendif();
                else in_ifdef++;
                        break;
        case 3://ifndef
                get_nextword(); if(word[0]==0){ error("Missing label",ERROR);
                                 return 1;}
                if(find_define(word)!=-1)skiptillendif();
                else in_ifdef++;
                        break;
        case 4://undef
                get_nextword(); if(word[0]==0){ error("Missing label",ERROR);
                                 return 1;}
                if(find_define(word)==-1){
                         sprintf(str,"define \"%s\" not found",word);
                        error(str,WARNING);
                                }
                else cur_def->removedit=1;
                        break;
         case 5://endif
                if(in_ifdef==0)error("#endif directive without #if directive",WARNING);
                else in_ifdef--;
                break;
         case 6://else
                if(in_ifdef==0)error("#else directive without #if directive",WARNING);
                else { skiptillendif(); in_ifdef--; }
                break;
         default:
                sprintf(str,"Unknown directive %s",word);
                error(str,ERROR); return 1;
                 }
return 0; }
