compiler / tree.cc
tree.cc
Raw
#include "tree.h"

AnalysisError::AnalysisError(string type): type(type) {}

const string AnalysisError::what() const {
    return type;
}

Node::Node(string rule, Tree *t): rule(rule), isTerminal(false), t(t) {
    std::istringstream iss(rule);
    string sym;

    // Left Hand Side
    iss >> sym;
    symbol = sym;
    if ('A' <= symbol[0] && symbol[0] <= 'Z') {
        isTerminal = true;
    }

    // Right Hand Side
    if (isTerminal) {
        iss >> sym;
        value = sym;
    } else {
        while (iss >> sym) {
            string line = t->getNextToken();
            Node *child = new Node(line, t);
            children.push_back(child);
        }
    }
}

Node::~Node() {
    for (auto &child : children) {
        delete child;
    }
}

void Node::print() {
    if (isTerminal) {
        std::cout << symbol << ' ' << value << std::endl;
    } else {
        std::cout << symbol << std::endl;
    }
    for (auto &child : children) {
        child->print();
    }
}

void Node::traverse() {
    if (symbol == "procedure") {
        if (t->tables.find(children[1]->value) != t->tables.end()) {
            throw AnalysisError("AnalysisError: Multiple Procedure Declaration");
        }
        t->currentProcedure = children[1]->value; // ID

        // Check Variable Declaration
        children[3]->checkParam(); // params
        children[6]->checkVar(); // dcls

        // add table to tables
        t->tables[t->currentProcedure] = std::make_pair(t->params, t->table);
        t->params.clear();
        t->table.clear();
        t->varOffsets[t->currentProcedure] = std::make_pair(t->varOffset, t->varCount);
        t->varOffset.clear();
        t->varCount = 0;

        // Traverse statements, expr
        children[7]->traverse(); // statements
        children[9]->traverse(); // expr

    } else if (symbol == "main") {
        if (t->tables.find(children[1]->value) != t->tables.end()) {
            throw AnalysisError("AnalysisError: Multiple Procedure Declaration");
        }
        t->currentProcedure = children[1]->value; // WAIN
        t->varOffset.clear();
        t->varCount = 0;

        // Check dcl1
        Node *dcl = children[3];
        string dclType = dcl->children[0]->getType();
        string dclId = dcl->children[1]->value;
        t->table[dclId] = dclType;
        t->params.push_back(dclType);
        t->varOffset[dclId] = t->varCount;
        t->varCount++;
        
        // Check dcl2
        dcl = children[5];
        dclType = dcl->children[0]->getType();
        dclId = dcl->children[1]->value;
        if (dclType == "int*") {
            throw AnalysisError("AnalysisError: Invalid Type: int*");
        } else if (t->table.find(dclId) != t->table.end()) {
            throw AnalysisError("AnalysisError: Multiple Variable Declaration");
        }
        t->table[dclId] = dclType;
        t->params.push_back(dclType);
        t->varOffset[dclId] = t->varCount;
        t->varCount++;

        // Check dcls
        children[8]->checkVar(); // dcls

        // add table to tables
        t->tables[t->currentProcedure] = std::make_pair(t->params, t->table);
        t->params.clear();
        t->table.clear();
        t->varOffsets[t->currentProcedure] = std::make_pair(t->varOffset, t->varCount);
        t->varOffset.clear();
        t->varCount = 0;

        // Traverse statements, expr
        children[9]->traverse(); // statements
        children[11]->traverse(); // expr

    } else if (symbol == "statements") {
        if (children.size() == 0) {
            // statements
            return;
        } else if (children[0]->symbol == "statements") {
            // statements statements statement
            children[0]->traverse(); // statements
            children[1]->traverse(); // statement
        }

    } else if (symbol == "statement") {
        if (children[0]->symbol == "lvalue") {
            // statement lvalue BECOMES expr SEMI
            string type1 = children[0]->getType(); // lvalue
            string type2 = children[2]->getType(); // expr
            if (type1 != type2) {
                throw AnalysisError("AnalysisError: Incompatible type in BECOMES");
            }
        } else if (children[0]->symbol == "IF" || children[0]->symbol == "WHILE") {
            // statement IF/WHILE
            for (auto child : children) {
                if (!child->isTerminal) {
                    child->traverse(); // test, statements
                }
            }
        } else if (children[0]->symbol == "PRINTLN") {
            // statement PRINTLN
            string type1 = children[2]->getType(); // expr
            if (type1 != "int") {
                throw AnalysisError("AnalysisError: Incorrect type in PRINTLN");
            }
        } else if (children[0]->symbol == "DELETE") {
            // statement DELETE
            string type1 = children[3]->getType(); // expr
            if (type1 != "int*") {
                throw AnalysisError("AnalysisError: Incorrect type in DELETE");
            }
        }

    } else if (symbol == "test") {
        // test expr TERM expr
        string type1 = children[0]->getType(); // left expr
        string type2 = children[2]->getType(); // right expr
        if (type1 != type2) {
            throw AnalysisError("AnalysisError: Incompatible type in test");
        }
        
    } else if (symbol == "expr") {
        // expr @ RETURN
        string type1 = this->getType();
        if (type1 != "int") {
            throw AnalysisError("AnalysisError: Incorrect RETURN type");
        }

    } else {
        for (auto child : children) {
            if (!child->isTerminal) {
                child->traverse();
            }
        }
    }
}

void Node::checkParam() {
    if (symbol == "dcl") {
        string type = children[0]->getType();
        string id = children[1]->value;
        if (t->table.find(id) != t->table.end()) {
            throw AnalysisError("AnalysisError: Multiple Declaration");
        }
        t->table[id] = type;
        t->params.push_back(type);
        // t->varOffset[id] = t->varCount; // add paramlist var to varOffset

    } else {
        for (auto child : children) {
            if (!child->isTerminal) {
                child->checkParam();
            }
        }
    }
}

void Node::checkVar() {
    if (symbol == "dcls" && children.size() == 0) {
        return;
    } else if (symbol == "dcls" && children[3]->symbol == "NUM") {
        children[0]->checkVar();
        string type = children[1]->children[0]->getType(); // dcl->type->getType
        if (type != "int") {
            throw AnalysisError("AnalysisError: Incorrect Variable Type Declaration");
        }
        string id = children[1]->children[1]->value; // dcl->id
        if (t->table.find(id) != t->table.end()) {
            throw AnalysisError("AnalysisError: Multiple Declaration");
        }
        t->table[id] = type;
        t->varOffset[id] = t->varCount;
        t->varCount++;

    } else if (symbol == "dcls" && children[3]->symbol == "NULL") {
        children[0]->checkVar();
        string type = children[1]->children[0]->getType(); // dcl->type->getType
        if (type != "int*") {
            throw AnalysisError("AnalysisError: Incorrect Variable Type Declaration");
        }
        string id = children[1]->children[1]->value; // dcl->id
        if (t->table.find(id) != t->table.end()) {
            throw AnalysisError("AnalysisError: Multiple Declaration");
        }
        t->table[id] = type;
        t->varOffset[id] = t->varCount;
        t->varCount++;
    } else {
        for (auto child : children) {
            if (!child->isTerminal) {
                child->checkVar();
            }
        }
    }
}

void checkArgs(Node *n, vector<string> &v) {
    if (n->symbol == "expr") {
        string t = n->getType();
        v.push_back(t);

    } else {
        for (auto child : n->children) {
            if (!child->isTerminal) {
                checkArgs(child, v);
            }
        }
    }
}

string Node::getType() {
    if (symbol == "type") {
        // type INT, type INT STAR
        string type = "";
        for (auto child : children) {
            type += child->value;
        }
        return type;

    } else if (symbol == "expr") {
        if (children.size() == 1) {
            // expr term
            return children[0]->getType(); // term
        } else if (children[1]->symbol == "PLUS") {
            // expr expr PLUS term
            string type1 = children[0]->getType(); // expr
            string type2 = children[2]->getType(); // term
            if (type1 == "int" && type2 == "int") {
                return "int";
            } else if (type1 == "int" && type2 == "int*") {
                return "int*";
            } else if (type1 == "int*" && type2 == "int") {
                return "int*";
            } else {
                throw AnalysisError("AnalysisError: Incorrect Type: int* PLUS int*");
            }
        } else if (children[1]->symbol == "MINUS") {
            // expr expr MINUS term
            string type1 = children[0]->getType(); // expr
            string type2 = children[2]->getType(); // term
            if (type1 == "int" && type2 == "int") {
                return "int";
            } else if (type1 == "int*" && type2 == "int") {
                return "int*";
            } else if (type1 == "int*" && type2 == "int*") {
                return "int";
            } else {
                throw AnalysisError("AnalysisError: Incorrect Type: int* MINUS int*");
            }
        }

    } else if (symbol == "term") {
        if (children.size() == 1) {
            // term factor
            return children[0]->getType(); // factor
        } else {
            // term term STAR/SLASH/PCT factor
            string type1 = children[0]->getType(); // term
            string type2 = children[2]->getType(); // factor
            if (type1 == "int" && type2 == "int") {
                return "int";
            } else {
                throw AnalysisError("AnalysisError: Incorrect Type: int* at term");
            }
        }

    } else if (symbol == "factor") {
        if (children[0]->symbol == "ID") {
            if (children.size() == 1) {
                // factor ID
                map<string, string> variables = t->tables[t->currentProcedure].second;
                if (variables.find(children[0]->value) == variables.end()) {
                    throw AnalysisError("AnalysisError: Procedure used without Declaration");
                }
                return variables[children[0]->value];

            } else if (children.size() == 3) {
                // factor ID LPAREN RPAREN
                if (t->tables.find(children[0]->value) == t->tables.end()) {
                    throw AnalysisError("AnalysisError: Procedure used without Declaration");
                } else if (children[0]->value == "wain") {
                    throw AnalysisError("AnalysisError: Invalid call of WAIN");
                }

                vector<string> paramlist = t->tables[children[0]->value].first;

                if (paramlist.size() == 0) {
                    return "int";
                } else {
                    throw AnalysisError("AnalysisError: Incorrect Procedure Arguments");
                }
            } else if (children.size() == 4) {
                // factor ID LPAREN arglist RPAREN
                if (t->tables.find(children[0]->value) == t->tables.end()) {
                    throw AnalysisError("AnalysisError: Procedure used without Declaration");
                } else if (children[0]->value == "wain") {
                    throw AnalysisError("AnalysisError: Invalid call of WAIN");
                }

                vector<string> paramlist = t->tables[children[0]->value].first;
                vector<string> argslist;
                checkArgs(children[2], argslist);

                if (paramlist.size() == argslist.size() &&
                    std::equal(paramlist.begin(), paramlist.end(), argslist.begin())) {
                    return "int";
                } else {
                    throw AnalysisError("AnalysisError: Incorrect Procedure Arguments");
                }
            } 
        } else if (children[0]->symbol == "NUM") {
            // factor NUM
            return "int";
        } else if (children[0]->symbol == "NULL") {
            // factor NULL
            return "int*";
        } else if (children[0]->symbol == "LPAREN") {
            // factor LPAREN expr RPAREN
            return children[1]->getType(); // expr
        } else if (children[0]->symbol == "AMP") {
            // factor AMP lvalue
            string type1 = children[1]->getType(); // lvalue
            if (type1 == "int") {
                return "int*";
            } else {
                throw AnalysisError("AnalysisError: Incorrect Type: AMP int*");
            }
        } else if (children[0]->symbol == "STAR") {
            // factor STAR factor
            string type1 = children[1]->getType(); // factor
            if (type1 == "int*") {
                return "int";
            } else {
                throw AnalysisError("AnalysisError: Incorrect Type: STAR int");
            }
        } else if (children[0]->symbol == "NEW") {
            // factor NEW INT LBRACK expr RBRACK
            string type1 = children[3]->getType(); // expr
            if (type1 == "int") {
                return "int*";
            } else {
                throw AnalysisError("AnalysisError: Incorrect Type: NEW INT LBRACK int* RBRACK");
            }
        }

    } else if (symbol == "lvalue") {
        if (children[0]->symbol == "ID") {
            // lvalue ID
            map<string, string> variables = t->tables[t->currentProcedure].second;
            if (variables.find(children[0]->value) == variables.end()) {
                throw AnalysisError("Procedure used without Declaration");
            }
            return variables[children[0]->value];  
        } else if (children[0]->symbol == "STAR") {
            // lvalue STAR factor
            string type1 = children[1]->getType(); // factor
            if (type1 == "int*") {
                return "int";
            } else {
                throw AnalysisError("Incorrect Type: STAR int");
            }
        } else if (children[0]->symbol == "LPAREN") {
            // lvalue LPAREN lvalue RPAREN
            return children[1]->getType(); // lvalue
        }
    }

    return "";
}

void pushReg(int n) {
    std::cout << "sw $" << n << ", -4($30) \t ; push $" << n << std::endl;
    std::cout << "sub $30, $30, $4" << std::endl;
}

void popReg(int n) {
    std::cout << "add $30, $30, $4" << std::endl;
    std::cout << "lw $" << n << ", -4($30) \t ; pop $" << n << std::endl;
}

void Node::generate() {
    if (symbol == "procedures") {
        if (children.size() == 1) {
            // procedures: main
            children[0]->generate();

        } else {
            // procedures: procedure procedures
            children[1]->generate();
            children[0]->generate();
        }

    } else if (symbol == "procedure") {
        t->currentProcedure = children[1]->value; // ID
        t->argCount = 0;

        std::cout << std::endl << "F" << children[1]->value << ":" << std::endl;
        std::cout << "sub $29, $30, $4" << std::endl; // caller-saves old frame pointer
        children[3]->generate(); // params
        children[6]->generate(); // dcls
        children[7]->generate(); // statements
        children[9]->generate(); // expr
        int count = t->varOffsets[t->currentProcedure].second - t->argCount;
        for (int i = 0; i < count; i++) {
            std::cout << "add $30, $30, $4" << std::endl; // pop saved registers
        }
        std::cout << "add $30, $29, $4" << std::endl;
        std::cout << "jr $31" << std::endl;

    } else if (symbol == "main") {
        t->currentProcedure = children[1]->value; // WAIN
        t->argCount = 0;

        std::cout << "; begin prologue" << std::endl;
        std::cout << ".import print" << std::endl;
        std::cout << ".import init" << std::endl;
        std::cout << ".import new" << std::endl;
        std::cout << ".import delete" << std::endl;
        std::cout << "lis $4" << std::endl;
        std::cout << ".word 4" << std::endl;
        std::cout << "lis $10" << std::endl;
        std::cout << ".word print" << std::endl;
        std::cout << "lis $11" << std::endl;
        std::cout << ".word 1" << std::endl;
        std::cout << "sub $29, $30, $4 \t ; setup frame pointer" << std::endl;

        // dcl1
        Node *dcl = children[3];
        string dclId = dcl->children[1]->value;
        std::cout << "sw $1, 0($29) \t ; push variable " << dclId << std::endl;
        std::cout << "sub $30, $30, $4 \t ; update stack pointer" << std::endl;
        
        // dcl2
        dcl = children[5];
        dclId = dcl->children[1]->value;
        std::cout << "sw $2, -4($29) \t ; push variable " << dclId << std::endl;
        std::cout << "sub $30, $30, $4 \t ; update stack pointer" << std::endl;
        if (children[3]->children[0]->getType() == "int") {
            std::cout << "add $2, $0, $0 \t ; set $2 to 0" << std::endl;
        }

        // dcls
        children[8]->generate(); // dcls

        // init
        std::cout << "lis $5" << std::endl;
        std::cout << ".word init" << std::endl;
        pushReg(31);
        std::cout << "jalr $5" << std::endl;
        popReg(31);
        std::cout << "; end prologue" << std::endl << std::endl;

        // statements, expr
        children[9]->generate(); // statements
        children[11]->generate(); // expr

        std::cout << std::endl << "; begin epilogue" << std::endl;
        int count = t->varOffsets[t->currentProcedure].second;
        for (int i = 0; i < count; i++) {
            std::cout << "add $30, $30, $4" << std::endl;
        }
        std::cout << "jr $31" << std::endl;

    } else if (symbol == "params") {
        if (children.size() == 0) {
            // params:
        } else {
            // params: paramlist
            children[0]->generate();
        }

    } else if (symbol == "paramlist") {
        // paramlist: dcl
        int argsCount = t->tables[t->currentProcedure].first.size();
        int offset = argsCount - t->argCount;
        string id = children[0]->children[1]->value; // dcl -> id
        t->varOffsets[t->currentProcedure].first[id] = offset * (-1); // Update param offset
        t->argCount++;

        if (children.size() == 3) {
            // paramlist: dcl COMMA paramlist
            children[2]->generate();
        }
    } else if (symbol == "arglist") {
        if (children.size() == 1) {
            // arglist: expr
            children[0]->generate(); // expr
            pushReg(3);

        } else {
            // arglist: expr COMMA arglist
            children[0]->generate(); // expr
            pushReg(3);
            children[2]->generate(); // arglist
        }
    } else if (symbol == "statements") {
        if (children.size() == 0) {
            // statements:
        } else if (children[0]->symbol == "statements") {
            // statements: statements statement
            children[0]->generate();
            children[1]->generate();
        }

    } else if (symbol == "statement") {
        if (children[0]->symbol == "lvalue") {
            // statement: lvalue BECOMES expr SEMI
            children[2]->generate();
            children[0]->generate();

        } else if (children[0]->symbol == "IF") {
            // statement: IF LPAREN test RPAREN LBRACE statements RBRACE ELSE LBRACE statements RBRACE 
            int n = t->loopCount;
            t->loopCount++;
            children[2]->generate(); // test
            std::cout << "beq $3, $0, else" << n << std::endl;
            children[5]->generate(); // statements1
            std::cout << "beq $0, $0, endif" << n << std::endl;
            std::cout << "else" << n << ":" << std::endl;
            children[9]->generate(); // statements2
            std::cout << "endif" << n << ":" << std::endl;

        } else if (children[0]->symbol == "WHILE") {
            // statement: WHILE LPAREN test RPAREN LBRACE statements RBRACE
            int n = t->loopCount;
            t->loopCount++;
            std::cout << "startWhile" << n << ":" << std::endl;
            children[2]->generate(); // test
            std::cout << "beq $3, $0, endWhile" << n << std::endl;
            children[5]->generate(); // statements
            std::cout << "beq $0, $0, startWhile" << n << std::endl;
            std::cout << "endWhile" << n << ":" << std::endl;

        } else if (children[0]->symbol == "PRINTLN") {
            // statement: PRINTLN LPAREN expr RPAREN SEMI
            pushReg(1);
            children[2]->generate();
            std::cout << "add $1, $3, $0" << std::endl;
            pushReg(31);
            std::cout << "jalr $10" << std::endl;
            popReg(31);
            popReg(1);

        } else if (children[0]->symbol == "DELETE") {
            // statement: DELETE LBRACK RBRACK expr SEMI
            children[3]->generate();
            int n = t->loopCount;
            t->loopCount++;
            std::cout << "beq $3, $11, skipDelete" << n << std::endl;
            std::cout << "add $1, $3, $0 \t ; move $3 to $1" << std::endl;
            pushReg(31);
            std::cout << "lis $5" << std::endl;
            std::cout << ".word delete" << std::endl;
            std::cout << "jalr $5" << std::endl;
            popReg(31);
            std::cout << "skipDelete" << n << ":" << std::endl;

        }

    } else if (symbol == "test") {
        bool pointerCheck = (children[0]->getType() == "int*");
        if (children[1]->symbol == "LT") {
            // test: expr LT expr
            children[0]->generate();
            pushReg(3);
            children[2]->generate();
            popReg(5);
            if (pointerCheck) {
                std::cout << "sltu $3, $5, $3" << std::endl;
            } else {
                std::cout << "slt $3, $5, $3" << std::endl;
            }
            
        } else if (children[1]->symbol == "GE") {
            // test: expr GE expr
            children[0]->generate();
            pushReg(3);
            children[2]->generate();
            popReg(5);
            if (pointerCheck) {
                std::cout << "sltu $3, $5, $3" << std::endl;
            } else {
                std::cout << "slt $3, $5, $3" << std::endl;
            }
            std::cout << "sub $3, $11, $3" << std::endl;

        } else if (children[1]->symbol == "GT") {
            // test: expr GT expr
            children[0]->generate();
            pushReg(3);
            children[2]->generate();
            popReg(5);
            if (pointerCheck) {
                std::cout << "sltu $3, $3, $5" << std::endl;
            } else {
                std::cout << "slt $3, $3, $5" << std::endl;
            }

        } else if (children[1]->symbol == "LE") {
            // test: expr GT expr
            children[0]->generate();
            pushReg(3);
            children[2]->generate();
            popReg(5);
            if (pointerCheck) {
                std::cout << "sltu $3, $3, $5" << std::endl;
            } else {
                std::cout << "slt $3, $3, $5" << std::endl;
            }
            std::cout << "sub $3, $11, $3" << std::endl;

        } else if (children[1]->symbol == "NE") {
            // test: expr NE expr
            children[0]->generate();
            pushReg(3);
            children[2]->generate();
            popReg(5);
            if (pointerCheck) {
                std::cout << "sltu $6, $3, $5" << std::endl;
                std::cout << "sltu $7, $5, $3" << std::endl;
            } else {
                std::cout << "slt $6, $3, $5" << std::endl;
                std::cout << "slt $7, $5, $3" << std::endl;
            }
            std::cout << "add $3, $6, $7" << std::endl;

        } else if (children[1]->symbol == "EQ") {
            // test: expr EQ expr
            children[0]->generate();
            pushReg(3);
            children[2]->generate();
            popReg(5);
            if (pointerCheck) {
                std::cout << "sltu $6, $3, $5" << std::endl;
                std::cout << "sltu $7, $5, $3" << std::endl;
            } else {
                std::cout << "slt $6, $3, $5" << std::endl;
                std::cout << "slt $7, $5, $3" << std::endl;
            }
            std::cout << "add $3, $6, $7" << std::endl;
            std::cout << "sub $3, $11, $3" << std::endl;
            
        }
        
    } else if (symbol == "expr") {
        if (children.size() == 1) {
            // expr: term
            children[0]->generate(); // term
        } else if (children[1]->symbol == "PLUS") {
            // expr: expr PLUS term
            children[0]->generate(); // expr
            if (children[2]->getType() == "int*") {
                std::cout << "mult $3, $4" << std::endl;
                std::cout << "mflo $3" << std::endl;
            }
            pushReg(3);
            children[2]->generate(); // term
            if (children[0]->getType() == "int*") {
                std::cout << "mult $3, $4" << std::endl;
                std::cout << "mflo $3" << std::endl;
            }
            popReg(5);
            std::cout << "add $3, $5, $3 \t ; expr PLUS term" << std::endl;

        } else if (children[1]->symbol == "MINUS") {
            // expr: expr MINUS term
            children[0]->generate(); // expr
            pushReg(3);
            children[2]->generate(); // term
            if (children[0]->getType() == "int*" && children[2]->getType() == "int") {
                std::cout << "mult $3, $4" << std::endl;
                std::cout << "mflo $3" << std::endl;
            }
            popReg(5);
            std::cout << "sub $3, $5, $3 \t ; expr MINUS term" << std::endl;
            if (children[0]->getType() == "int*" && children[2]->getType() == "int*") {
                std::cout << "div $3, $4" << std::endl;
                std::cout << "mflo $3" << std::endl;
            }

        }

    } else if (symbol == "term") {
        if (children.size() == 1) {
            // term: factor
            return children[0]->generate(); // factor

        } else {
            children[0]->generate(); // term
            pushReg(3);
            children[2]->generate(); // factor
            popReg(5);

            if (children[1]->symbol == "STAR") {
                // term: term STAR factor
                std::cout << "mult $5, $3" << std::endl;
                std::cout << "mflo $3" << std::endl;
            } else if (children[1]->symbol == "SLASH") {
                // term: term SLASH factor
                std::cout << "div $5, $3" << std::endl;
                std::cout << "mflo $3" << std::endl;
            } else if (children[1]->symbol == "PCT") {
                // term: term PCT factor
                std::cout << "div $5, $3" << std::endl;
                std::cout << "mfhi $3" << std::endl;
            }
        }

    } else if (symbol == "dcls") {
        if (children.size() == 0) {
            // dcls:
            return;

        } else if (children[3]->symbol == "NUM") {
            // dcls: dcls dcl BECOMES NUM SEMI
            children[0]->generate();
            string id = children[1]->children[1]->value; // dcl->id
            int offset = (t->varOffsets[t->currentProcedure].first)[id];

            std::cout << "lis $3" << std::endl;
            std::cout << ".word " << children[3]->value << std::endl;
            std::cout << "sw $3, " << offset * (-4) << "($29) \t ; push variable " << id << std::endl;
            std::cout << "sub $30, $30, $4 \t ; update stack pointer" << std::endl;

        } else if (children[3]->symbol == "NULL") {
            // dcls: dcls dcl BECOMES NULL SEMI
            children[0]->generate();
            string id = children[1]->children[1]->value; // dcl->id
            int offset = (t->varOffsets[t->currentProcedure].first)[id];

            std::cout << "sw $11, " << offset * (-4) << "($29) \t ; push variable " << id << std::endl;
            std::cout << "sub $30, $30, $4 \t ; update stack pointer" << std::endl;

        }

    } else if (symbol == "factor") {
        if (children[0]->symbol == "ID") {
            if (children.size() == 1) {
                // factor: ID
                int offset = (t->varOffsets[t->currentProcedure].first)[children[0]->value];
                std::cout << "lw $3, " << offset * (-4) << "($29) \t ; variable " << children[0]->value << std::endl;

            } else if (children.size() == 3) {
                // factor: ID LPAREN RPAREN
                pushReg(29);
                pushReg(31);
                std::cout << "lis $5" << std::endl;
                std::cout << ".word F" << children[0]->value << std::endl;
                std::cout << "jalr $5" << std::endl;
                popReg(31);
                popReg(29);

            } else if (children.size() == 4) {
                // factor: ID LPAREN arglist RPAREN
                pushReg(29);
                pushReg(31);
                children[2]->generate();
                std::cout << "lis $5" << std::endl;
                std::cout << ".word F" << children[0]->value << std::endl;
                std::cout << "jalr $5" << std::endl;
                int count = t->tables[children[0]->value].first.size();
                for (int i = 0; i < count; i++) {
                    std::cout << "add $30, $30, $4" << std::endl;
                }
                popReg(31);
                popReg(29);
            } 
        } else if (children[0]->symbol == "NUM") {
            // factor: NUM
            std::cout << "lis $3" << std::endl;
            std::cout << ".word " << children[0]->value << std::endl;

        } else if (children[0]->symbol == "NULL") {
            // factor: NULL
            std::cout << "add $3, $0, $11" << std::endl;

        } else if (children[0]->symbol == "LPAREN") {
            // factor: LPAREN expr RPAREN
            children[1]->generate(); // expr

        } else if (children[0]->symbol == "AMP") {
            // factor: AMP lvalue
            Node *n = children[1]; // n = lvalue
            while (n->children.size() == 3) {
                n = n->children[1];
            }

            if (n->children.size() == 1) {
                int offset = (t->varOffsets[t->currentProcedure].first)[n->children[0]->value];
                std::cout << "lis $3" << std::endl;
                std::cout << ".word " << offset * (-4) << std::endl;
                std::cout << "add $3, $3, $29" << std::endl;

            } else if (n->children.size() == 2) {
                n->children[1]->generate();

            }

        } else if (children[0]->symbol == "STAR") {
            // factor: STAR factor
            children[1]->generate(); // lvalue
            std::cout << "lw $3, 0($3)" << std::endl;
            
        } else if (children[0]->symbol == "NEW") {
            // factor: NEW INT LBRACK expr RBRACK
            children[3]->generate(); // expr
            std::cout << "add $1, $3, $0" << std::endl;
            pushReg(31);
            std::cout << "lis $5" << std::endl;
            std::cout << ".word new" << std::endl;
            std::cout << "jalr $5 \t ; call new" << std::endl;
            popReg(31);
            std::cout << "bne $3, $0, 1 \t ; check if call succeeded" << std::endl;
            std::cout << "add $3, $11, $0" << std::endl;

        }

    } else if (symbol == "lvalue") {
        if (children[0]->symbol == "ID") {
            // lvalue: ID
            int offset = (t->varOffsets[t->currentProcedure].first)[children[0]->value];
            std::cout << "sw $3, " << offset * (-4) << "($29) \t ; variable" << children[0]->value << std::endl;

        } else if (children[0]->symbol == "STAR") {
            // lvalue: STAR factor
            pushReg(3);
            children[1]->generate();
            popReg(5);
            std::cout << "sw $5, 0($3)" << std::endl;

        } else if (children[0]->symbol == "LPAREN") {
            // lvalue: LPAREN lvalue RPAREN
            children[1]->generate();

        }

    } else {
        for (auto child : children) {
            if (!child->isTerminal) {
                child->generate();
            }
        }
    }
}

Tree::Tree(vector<string> &parsedTokens): parsedTokens(parsedTokens) {
    varCount = 0;
    argCount = 0;
    loopCount = 0;
    string currentProcedure;
    string line = getNextToken();
    root = new Node(line, this);
}

Tree::~Tree() {
    delete root;
}

void Tree::print() {
    root->print();
}

void Tree::traverse() {
    root->traverse();
}

string Tree::getNextToken() {
    string token = parsedTokens.back();
    parsedTokens.pop_back();
    return token;
}

void Tree::generate() {
    root->generate();
}