#include "token.hpp"


Token::Token(type_t t, uint64_t v): type(t), val_i(v), val_s(NULL), offset(NULL) {
    assert(t != TOK_IDENTIFIER); // && t != TOK_COMMENT);
}


Token::Token(type_t t, const char* c, Token* off): type(t), val_i(0), val_s(strdup(c)), offset(off) {
    assert(t == TOK_IDENTIFIER); // || t == TOK_COMMENT);
    if (offset) {
        assert(t == TOK_IDENTIFIER);
        assert(offset->type == TOK_IDENTIFIER || offset->type == TOK_IMMEDIATE);
        assert(!offset->offset);
    }
}


Token::~Token() {
    if (val_s) free((void*)val_s);
    if (offset) delete (Token*)offset;
}


bool Token::is(type_t t) const {
    return type == t;
}


Token::var_t Token::vartype() const {
    switch (type) {
        case TOK_IMMEDIATE:
            return TOK_VAR_IMM;
        case TOK_IDENTIFIER:
            return offset? TOK_VAR_PTR: TOK_VAR_ID;
        default:
            return TOK_VAR_NONE;
    }
}


Token* Token::parseType(const char* line) {
    int n;
    char c;
    unsigned u;
    if (sscanf(line, " %c%u %n", &c, &u, &n) == 2 && n == (int)strlen(line)) {
        width_t w;
        switch (u) {
            case 8:  w = WIDTH_8;  break;
            case 16: w = WIDTH_16; break;
            case 32: w = WIDTH_32; break;
            case 64: w = WIDTH_64; break;
            default: return NULL;
        }
        switch (c) {
            case 'u':
                return new Token(TOK_TYPE_UINT, w);
            case 'p':
            case 's':
                return new Token(TOK_TYPE_PTR, w);
        }
    }
    return NULL;
}


Token* Token::parseVal(const char* line, bool imm_ok, bool id_ok, bool mem_ok) {
    int n;
    uint64_t i;
    unsigned u;
    char a[64], b[64];
    if (sscanf(line, " %[a-z][%u] %n", a, &u, &n) == 2 && n == (int)strlen(line)) {
        return (id_ok && mem_ok)? new Token(TOK_IDENTIFIER, a, new Token(TOK_IMMEDIATE, u)): NULL;
    } else if (sscanf(line, " %[a-z][%[a-z]] %n", a, b, &n) == 2 && n == (int)strlen(line)) {
        return (id_ok && mem_ok)? new Token(TOK_IDENTIFIER, a, new Token(TOK_IDENTIFIER, b)): NULL;
    } else if (sscanf(line, " %[a-z] %n", a, &n) == 1 && n == (int)strlen(line)) {
        return (id_ok)? new Token(TOK_IDENTIFIER, a): NULL;
    } else if (sscanf(line, " %" SCNu64 " %n", &i, &n) == 1 && n == (int)strlen(line)) {
        return (imm_ok)? new Token(TOK_IMMEDIATE, i): NULL;
    }
    return NULL;
}


Token* Token::parseCmp(const char* line) {
    int n;
    char a[64];
    if (sscanf(line, " %[<>=] %n", a, &n) == 1 && n == (int)strlen(line)) {
        if (!strcmp(a, "==")) {
            return new Token(TOK_CMP, TOK_CMP_EQ);
        } else if (!strcmp(a, "<>")) {
            return new Token(TOK_CMP, TOK_CMP_NE);
        } else if (!strcmp(a, "<=")) {
            return new Token(TOK_CMP, TOK_CMP_LE);
        } else if (!strcmp(a, ">=")) {
            return new Token(TOK_CMP, TOK_CMP_GE);
        } else if (!strcmp(a, "<")) {
            return new Token(TOK_CMP, TOK_CMP_LT);
        } else if (!strcmp(a, ">")) {
            return new Token(TOK_CMP, TOK_CMP_GT);
        }
    }
    return NULL;
}


Token* Token::parseOp(char c) {
    switch (c) {
        case ':':
            return new Token(TOK_OP, TOK_OP_ASSIGN);
        case '+':
            return new Token(TOK_OP, TOK_OP_ADD);
        case '-':
            return new Token(TOK_OP, TOK_OP_SUB);
        case '<':
            return new Token(TOK_OP, TOK_OP_SHL);
        case '>':
            return new Token(TOK_OP, TOK_OP_SHR);
        case '*':
            return new Token(TOK_OP, TOK_OP_MUL);
        case '/':
            return new Token(TOK_OP, TOK_OP_DIV);
        case '%':
            return new Token(TOK_OP, TOK_OP_MOD);
        default:
            return NULL;
    }
}