/****************************************************/
/* File: parse.c                                    */
/* The parser implementation for the TINY compiler  */
/* Compiler Construction: Principles and Practice   */
/* Kenneth C. Louden                                */
/****************************************************/

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

static TokenType token; /* holds current token */

/* function prototypes for recursive calls */
static TreeNode * stmt_sequence(void);
static TreeNode * statement(void);
static TreeNode * while_stmt(void);
static TreeNode * if_stmt(void);
static TreeNode * repeat_stmt(void);
static TreeNode * assign_stmt(void);
static TreeNode * read_stmt(void);
static TreeNode * write_stmt(void);
static TreeNode * exp(void);
static TreeNode * simple_exp(void);
static TreeNode * term(void);
static TreeNode * factor(void);
static TreeNode * varlist();

static void syntaxError(const char * message) {
    fprintf(listing,"\n>>> ");
    fprintf(listing,"Syntax error at line %d: %s",lineno,message);
    Error = TRUE;
}

static void match(TokenType expected) {
    if (token == expected) {
        token = getToken();
    } else {
        syntaxError("unexpected token -> ");
        printToken(token,tokenString);
        fprintf(listing,"      ");
    }
}

static TreeNode * delc(void) {
    TreeNode * t = NULL;
    switch (token) {
        case INT:
            t = newDelcNode(IntK);
            t->child[0] = varlist();
            break;
        case BOOL:
            t = newDelcNode(BoolK);
            t->child[0] = varlist();
            break;
        case STRING:
            t = newDelcNode(StringK);
            t->child[0] = varlist();
            break;
        case FLOAT:
            t = newDelcNode(FloatK);
            t->child[0] = varlist();
            break;
        case DOUBLE:
            t = newDelcNode(DoubleK);
            t = varlist();
            break;
        default:
            syntaxError("unexpected token -> ");
            printToken(token,tokenString);
            fprintf(listing,"      ");
            break;
    }
    return t;
}

static TreeNode * varlist() {
    token = getToken();
    TreeNode * t = newExpNode(IdK);
    t->attr.name = copyString(tokenString);
    match(ID);
    if(token==COMMA) {
        t->child[0] = varlist();
    }
    return t;
}

static TreeNode * stmt_sequence(void) {
    TreeNode * t = statement();
    TreeNode * p = t;
    while ((token!=ENDFILE) && (token!=END) &&
           (token!=ELSE) && (token!=UNTIL) &&
           (token!=WHILE)) {
        TreeNode * q;
        match(SEMI);
        q = statement();
        if (q!=NULL) {
            if (t==NULL) {
                t = p = q;
            } else { /* now p cannot be NULL either */
                p->sibling = q;
                p = q;
            }
        }
    }
    return t;
}

static TreeNode * statement(void) {
    TreeNode * t = NULL;
    switch (token) {
        case DO : t = while_stmt(); break;
        case IF : t = if_stmt(); break;
        case REPEAT : t = repeat_stmt(); break;
        case ID : t = assign_stmt(); break;
        case READ : t = read_stmt(); break;
        case WRITE : t = write_stmt(); break;
        default : syntaxError("unexpected token -> ");
                  printToken(token,tokenString);
                  token = getToken();
                  break;
    } /* end case */
    return t;
}

static TreeNode * while_stmt(void) {
    TreeNode * t = newStmtNode(WhileK);
    match(DO);
    if (t != NULL) {
        t->child[0] = stmt_sequence();
    }
    match(WHILE);
    if (t != NULL) {
        t->child[1] = exp();
    }
    return t;
}

static TreeNode * if_stmt(void) {
    TreeNode * t = newStmtNode(IfK);
    match(IF);
    if (t!=NULL) {
        t->child[0] = exp();
    }
    match(THEN);
    if (t!=NULL) {
        t->child[1] = stmt_sequence();
    }
    if (token==ELSE) {
        match(ELSE);
        if (t!=NULL) {
            t->child[2] = stmt_sequence();
        }
    }
    match(END);
    return t;
}

static TreeNode * repeat_stmt(void) {
    TreeNode * t = newStmtNode(RepeatK);
    match(REPEAT);
    if (t!=NULL) {
        t->child[0] = stmt_sequence();
    }
    match(UNTIL);
    if (t!=NULL) {
        t->child[1] = exp();
    }
    return t;
}

static TreeNode * assign_stmt(void) {
    TreeNode * t = newStmtNode(AssignK);
    if ((t!=NULL) && (token==ID))
        t->attr.name = copyString(tokenString);
    match(ID);
    match(ASSIGN);
    if (t!=NULL) t->child[0] = exp();
    return t;
}

static TreeNode * read_stmt(void) {
    TreeNode * t = newStmtNode(ReadK);
    match(READ);
    if ((t!=NULL) && (token==ID))
    t->attr.name = copyString(tokenString);
    match(ID);
    return t;
}

static TreeNode * write_stmt(void){
    TreeNode * t = newStmtNode(WriteK);
    match(WRITE);
    if (t!=NULL) t->child[0] = exp();
    return t;
}

static TreeNode * exp(void) {
    TreeNode * t = simple_exp();
    if ((token==LT)||(token==EQ)||(token==LTE)
        || (token==GT) || (token == GTE)
        ) {
        TreeNode * p = newExpNode(OpK);
        if (p!=NULL) {
            p->child[0] = t;
            p->attr.op = token;
            t = p;
        }
        match(token);
        if (t!=NULL) t->child[1] = simple_exp();
    }
    return t;
}

static TreeNode * simple_exp(void) {
    TreeNode * t = term();
    while ((token==PLUS)||(token==MINUS)) {
        TreeNode * p = newExpNode(OpK);
        if (p!=NULL) {
            p->child[0] = t;
            p->attr.op = token;
            t = p;
            match(token);
            t->child[1] = term();
        }
    }
    return t;
}

static TreeNode * term(void) {
    TreeNode * t = factor();
     while ((token==TIMES)||(token==OVER)) {
         TreeNode * p = newExpNode(OpK);
         if (p!=NULL) {
             p->child[0] = t;
             p->attr.op = token;
             t = p;
             match(token);
             p->child[1] = factor();
         }
     }
     return t;
}

static TreeNode * factor(void) {
    TreeNode * t = NULL;
    switch (token) {
        case NUM :
            t = newExpNode(ConstK);
            if ((t!=NULL) && (token==NUM))
                t->attr.val = atoi(tokenString);
            match(NUM);
            break;
        case ID :
            t = newExpNode(IdK);
            if ((t!=NULL) && (token==ID))
                t->attr.name = copyString(tokenString);
            match(ID);
            break;
        case STR:
            t = newExpNode(ConstStringK);
            if ((t!=NULL) && (token==STR))
                t->attr.name = copyString(tokenString);
            match(STR);
            break;
        case FLOATNUM:
            t = newExpNode(ConstFloatK);
            if ((t!=NULL) && (token==FLOATNUM))
                t->attr.name = copyString(tokenString);
            match(FLOATNUM);
            break;
        case T_TRUE:
        case T_FALSE:
            t = newExpNode(ConstBoolK);
            if ((t!=NULL) && (token==T_TRUE || token==T_FALSE))
                t->attr.name = copyString(tokenString);
            token = getToken();
            break;
        case LPAREN :
            match(LPAREN);
            t = exp();
            match(RPAREN);
            break;
        default:
            syntaxError("unexpected token -> ");
            printToken(token,tokenString);
            token = getToken();
            break;
      }
    return t;
}

/****************************************/
/* the primary function of the parser   */
/****************************************/
/* Function parse returns the newly 
 * constructed syntax tree
 */
TreeNode * parse(void) {
    TreeNode * t = NULL, * r = NULL, * p = NULL;
    token = getToken();
    while(token == INT || token == BOOL || token == STRING
       || token == FLOAT || token == DOUBLE) {
        p = delc();
        match(SEMI);
        if (t == NULL) {
            t = r = p;
        } else {
            r->sibling = p;
            r = p;
        }
    }
    if (t == NULL) {
        t = stmt_sequence();
    } else{
        r->sibling = stmt_sequence();
    }
    if (token != ENDFILE) {
        syntaxError("Code ends before file\n");
    }
    p = newProgNode();
    p->child[0] = t;
    return p;
}
