/****************************************************/
/* File: util.c                                     */
/* Utility function implementation                  */
/* for the TINY compiler                            */
/* Compiler Construction: Principles and Practice   */
/* Kenneth C. Louden                                */
/****************************************************/

#include "GLOBALS.H"
#include "UTIL.H"
#include "SCAN.H"

/* Procedure printToken prints a token 
 * and its lexeme to the listing file
 */
void printToken( TokenType token, const char* tokenString ) {
    switch (token) {
        case T_TRUE:
        case T_FALSE:
            fprintf(listing,"BOOL, name= %s\n",tokenString); break;
        case IF:
        case THEN:
        case ELSE:
        case END:
        case REPEAT:
        case UNTIL:
        case READ:
        case WRITE:
        case OR:
        case AND:
        case NOT:
        case INT:
        case BOOL:
        case STRING:
        case FLOAT:
        case DOUBLE:
        case DO:
        case WHILE:
            fprintf(listing, "reserved word: %s\n",tokenString);
            break;
        case ASSIGN: fprintf(listing,":=\n"); break;
        case LT: fprintf(listing,"<\n"); break;
        case EQ: fprintf(listing,"=\n"); break;
        case GT: fprintf(listing,">\n"); break;
        case LTE: fprintf(listing,"<=\n"); break;
        case GTE: fprintf(listing,">=\n"); break;
        case LPAREN: fprintf(listing,"(\n"); break;
        case RPAREN: fprintf(listing,")\n"); break;
        case SEMI: fprintf(listing,";\n"); break;
        case COMMA: fprintf(listing,",\n"); break;
        case SQM: fprintf(listing,"\'\n"); break;
        case PLUS: fprintf(listing,"+\n"); break;
        case MINUS: fprintf(listing,"-\n"); break;
        case TIMES: fprintf(listing,"*\n"); break;
        case OVER: fprintf(listing,"/\n"); break;
        case ENDFILE: fprintf(listing,"EOF\n"); break;
        case NUM: fprintf(listing,"INT, val= %s\n",tokenString); break;
        case ID: fprintf(listing,"ID, name= %s\n",tokenString); break;
        case FLOATNUM: fprintf(listing,"FLOAT, name= %s\n",tokenString); break;
        case STR: fprintf(listing,"STR,name= %s\n",tokenString); break;
        case ERROR: fprintf(listing, "ERROR %s :%s\n",
                            errorMsg[errorCode],tokenString); break;
        default: /* should never happen */
          fprintf(listing,"Unknown token: %d\n",token);
    }
}

TreeNode * newProgNode() {
    TreeNode * t = (TreeNode *) malloc(sizeof(TreeNode));
    int i;
    if (t == NULL) {
        fprintf(listing,"Out of memory error at line %d\n",lineno);
    } else {
        for (i=0;i<MAXCHILDREN;i++)
            t->child[i] = NULL;
        t->sibling = NULL;
        t->nodekind = ProgK;
        t->lineno = lineno;
    }
    return t;
}

TreeNode * newDelcNode(TypeKind kind) {
    TreeNode * t = (TreeNode *) malloc(sizeof(TreeNode));
    int i;
    if (t == NULL) {
        fprintf(listing,"Out of memory error at line %d\n",lineno);
    } else {
        for (i=0;i<MAXCHILDREN;i++) t->child[i] = NULL;
        t->sibling = NULL;
        t->nodekind = TypeK;
        t->kind.type = kind;
        t->lineno = lineno;
    }
    return t;
}

/* Function newStmtNode creates a new statement
 * node for syntax tree construction
 */
TreeNode * newStmtNode(StmtKind kind) {
    TreeNode * t = (TreeNode *) malloc(sizeof(TreeNode));
    int i;
    if (t == NULL) {
        fprintf(listing,"Out of memory error at line %d\n",lineno);
    } else {
        for (i=0;i<MAXCHILDREN;i++)
            t->child[i] = NULL;
        t->sibling = NULL;
        t->nodekind = StmtK;
        t->kind.stmt = kind;
        t->lineno = lineno;
    }
    return t;
}

/* Function newExpNode creates a new expression 
 * node for syntax tree construction
 */
TreeNode * newExpNode(ExpKind kind) {
    TreeNode * t = (TreeNode *)malloc(sizeof(TreeNode));
    int i;
    if (t==NULL) {
        fprintf(listing,"Out of memory error at line %d\n",lineno);
    } else {
        for (i=0;i<MAXCHILDREN;i++)
            t->child[i] = NULL;
        t->sibling = NULL;
        t->nodekind = ExpK;
        t->kind.exp = kind;
        t->lineno = lineno;
        t->type = Void;
    }
    return t;
}

/* Function copyString allocates and makes a new
 * copy of an existing string
 */
char * copyString(char * s) {
    int n;
    char * t;
    if (s==NULL) {
        return NULL;
    }
    n = (int)strlen(s)+1;
    t = (char *)malloc(n);
    if (t==NULL) {
        fprintf(listing,"Out of memory error at line %d\n",lineno);
    } else {
        strcpy(t,s);
    }
    return t;
}

/* Variable indentno is used by printTree to
 * store current number of spaces to indent
 */
static int indentno = 0;

/* macros to increase/decrease indentation */
#define INDENT indentno+=2
#define UNINDENT indentno-=2

/* printSpaces indents by printing spaces */
static void printSpaces(void) {
    int i;
    for (i=0;i<indentno;i++) {
        fprintf(listing," ");
    }
}

/*
 * procedure printTree prints a syntax tree to the
 * listing file using indentation to indicate subtrees
 */
void printTree(TreeNode * tree) {
    int i;
    INDENT;
    while (tree != NULL) {
        printSpaces();
        if(tree->nodekind==ProgK) {
            fprintf(listing,"Program\n");
        } else if (tree->nodekind==TypeK) {
            switch (tree->kind.type) {
                case IntK:
                    fprintf(listing,"Type: int\n");
                    break;
                case BoolK:
                    fprintf(listing,"Type: bool\n");
                    break;
                case StringK:
                    fprintf(listing,"Type: string\n");
                    break;
                case FloatK:
                    fprintf(listing,"Type: float\n");
                    break;
                case DoubleK:
                    fprintf(listing,"Type: double\n");
                    break;
                default:
                    fprintf(listing,"Unknown TypeNode kind\n");
                    break;
            }
        } else if (tree->nodekind==StmtK) {
            switch (tree->kind.stmt) {
                case WhileK:
                    fprintf(listing,"While\n");
                    break;
                case IfK:
                    fprintf(listing,"If\n");
                    break;
                case RepeatK:
                    fprintf(listing,"Repeat\n");
                    break;
                case AssignK:
                    fprintf(listing,"Assign to: %s\n",tree->attr.name);
                    break;
                case ReadK:
                    fprintf(listing,"Read: %s\n",tree->attr.name);
                    break;
                case WriteK:
                    fprintf(listing,"Write\n");
                    break;
                default:
                    fprintf(listing,"Unknown ExpNode kind\n");
                    break;
            }
        } else if (tree->nodekind==ExpK) {
            switch (tree->kind.exp) {
                case OpK:
                    fprintf(listing,"Op: ");
                    printToken(tree->attr.op,"\0");
                    break;
                case ConstK:
                    fprintf(listing,"Const: Integer: %d\n",tree->attr.val);
                    break;
                case IdK:
                    fprintf(listing,"Id: %s\n",tree->attr.name);
                    break;
                case ConstStringK:
                    fprintf(listing,"Const: String: %s\n",tree->attr.name);
                    break;
                case ConstFloatK:
                    fprintf(listing,"Const: Float: %s\n",tree->attr.name);
                    break;
                case ConstBoolK:
                    fprintf(listing,"Const: Bool: %s\n",tree->attr.name);
                    break;
                default:
                    fprintf(listing,"Unknown ExpNode kind\n");
                    break;
            }
        }
        else {
            fprintf(listing,"Unknown node kind\n");
        }
        for (i=0;i<MAXCHILDREN;i++)
            printTree(tree->child[i]);
        {
            tree = tree->sibling;
        }
    }
    UNINDENT;
}

void generateCode(TreeNode * tree) {
    static int t = 0;
    static char buf[BUFLEN];
    if (tree->nodekind == StmtK && tree->kind.stmt == AssignK) {
        generateCode(tree->child[0]);
        if(tree->child[0]->kind.exp == ConstK) {
            fprintf(listing, "%2s := %d\n",
                    tree->attr.name,
                    tree->child[0]->attr.val);
        } else {
            fprintf(listing, "%2s := %s\n",
                    tree->attr.name,
                    tree->child[0]->attr.name);
        }
        tree->nodekind = ExpK;
        tree->kind.exp = IdK;
    } else if (tree->nodekind == ExpK && tree->kind.exp == OpK) {
        generateCode((TreeNode *)tree->child[0]);
        generateCode((TreeNode *)tree->child[1]);
        if(   tree->child[0] != NULL && tree->child[0]->nodekind==ExpK
           && tree->child[0]->kind.exp != OpK && tree->child[1] != NULL
           && tree->child[1]->nodekind==ExpK && tree->child[1]->kind.exp != OpK) {
            fprintf(listing, "t%d := ", t);
            if(tree->child[0]->kind.exp == ConstK) {
                fprintf(listing, "%d ", tree->child[0]->attr.val);
            } else {
                fprintf(listing, "%2s ", tree->child[0]->attr.name);
            }
            switch (tree->attr.op) {
                case PLUS:  fprintf(listing, "%s ", "+"); break;
                case MINUS: fprintf(listing, "%s ", "-"); break;
                case TIMES: fprintf(listing, "%s ", "*"); break;
                case OVER:  fprintf(listing, "%s ", "/"); break;
                default: break;
            }
            if(tree->child[1]->kind.exp == ConstK) {
                fprintf(listing, "%d\n", tree->child[1]->attr.val);
            } else {
                fprintf(listing, "%s\n", tree->child[1]->attr.name);
            }
            tree->kind.exp = IdK;
            sprintf(buf, "t%d", t++);
            tree->attr.name = copyString(buf);
        }
    }
}

void generateMidCode(TreeNode * tree) {
    while (tree != NULL) {
        if (tree->nodekind == StmtK
            && tree->kind.stmt == AssignK) {
            generateCode(tree);
        } else if(tree->child[0]) {
            generateMidCode(tree->child[0]);
        }
        tree = tree->sibling;
    }
}

int isLegalChar(char c) {
    return (isalnum(c)||
            isspace(c)||
            c == '>'||
            c == '<' ||
            c == '=' ||
            c == ',' ||
            c ==';' ||
            c =='\'' ||
            c =='{'    ||
            c =='}' ||
            c =='+' ||
            c =='-' ||
            c =='*' ||
            c =='/' ||
            c =='(' ||
            c ==')');
}

void lexicalAnalysisHeader() {
    int i;
    fprintf(listing, "|");
    for (i = 1; i < WIDTH - 1; i++)
        fprintf(listing, "-");
    fprintf(listing, "|\n|");
    for (i = 0; i < (WIDTH - 18)/2; i++)
        fprintf(listing, " ");
    fprintf(listing, "Lexical Analysis");
    for (i = 0; i < (WIDTH - 18)/2; i++)
        fprintf(listing, " ");
    fprintf(listing, "|\n|");
    for (i = 1; i < WIDTH - 1; i++)
        fprintf(listing, "-");
    fprintf(listing, "|\n");
}

void syntacticAnalysisHeader() {
    int i = 0;
    fprintf(listing, "|");
    for (i = 1; i < WIDTH - 1; i++)
        fprintf(listing, "-");
    fprintf(listing, "|\n|");
    for (i = 0; i < (WIDTH - 19)/2; i++)
        fprintf(listing, " ");
    fprintf(listing, "Syntactic Analysis");
    for (i = 0; i < (WIDTH - 19)/2; i++)
        fprintf(listing, " ");
    fprintf(listing, "|\n|");
    for (i = 1; i < WIDTH - 1; i++)
        fprintf(listing, "-");
    fprintf(listing, "|\n");
}

void semanticAnalysisHeader() {
    int i = 0;
    fprintf(listing, "|");
    for (i = 1; i < WIDTH - 1; i++)
        fprintf(listing, "-");
    fprintf(listing, "|\n|");
    for (i = 0; i < (WIDTH - 19)/2; i++)
        fprintf(listing, " ");
    fprintf(listing, "Semantic Analysis");
    for (i = 0; i < (WIDTH - 18)/2; i++)
        fprintf(listing, " ");
    fprintf(listing, "|\n|");
    for (i = 1; i < WIDTH - 1; i++)
        fprintf(listing, "-");
    fprintf(listing, "|\n");
}

void footer() {
    int i = 0;
    fprintf(listing, "|");
    for (i = 1; i < WIDTH - 1; i++)
        fprintf(listing, "-");
    fprintf(listing, "|\n");
}
