mod std;

type_alias Tok: struct {
    type: string,
    value: string,
};

fn lex(text: string) -> [Tok] {
    let i = 0;
    let len = std::string_length(text);
    
    let toks = std::array_new::<Tok>();

    while i < len {
        if std::string_contains(" \t\n", text[i]) {
            i += 1;
            if i >= len {
                break;
            }
        }
        if text[i] >= '1' and text[i] <= '9' {
            let value = std::ctos(text[i]);
            i += 1;
            while i < len and text[i] >= '0' and text[i] <= '9' {
                value = std::string_push_char(value, text[i]);
                i += 1;
            }
            let tok = struct { type: "int", value: value };
            std::array_push(toks, tok);
        } else if text[i] == '0' {
            i += 1;
            let tok = struct { type: "int", value: "0" };
            std::array_push(toks, tok);
        } else if text[i] == '+' {
            i += 1;
            let tok = struct { type: "+", value: "+" };
            std::array_push(toks, tok);
        } else if text[i] == '-' {
            i += 1;
            let tok = struct { type: "-", value: "-" };
            std::array_push(toks, tok);
        } else if text[i] == '*' {
            i += 1;
            let tok = struct { type: "*", value: "*" };
            std::array_push(toks, tok);
        } else if text[i] == '/' {
            i += 1;
            let tok = struct { type: "/", value: "/" };
            std::array_push(toks, tok);
        } else if text[i] == '(' {
            i += 1;
            let tok = struct { type: "(", value: "(" };
            std::array_push(toks, tok);
        } else if text[i] == ')' {
            i += 1;
            let tok = struct { type: ")", value: ")" };
            std::array_push(toks, tok);
        } else {
            std::println("error: illegal character '" + std::ctos(text[i]) + "'");
            i += 1;
        }
    }
    
    toks
}

type_alias Calc: struct {
    toks: [Tok],
    toks_len: int,
    i: int,
};

fn calc_new(text: string) -> Calc {
    let toks = lex(text);
    let toks_len = std::array_length(toks);
    struct { toks: toks, toks_len: toks_len, i: 0 }
}

fn calc_expr(self: Calc) -> int {
    calc_add_sub(self)
}

fn calc_add_sub(self: Calc) -> int {
    let left = calc_mul_div(self);
    loop {
        if self.toks[self.i].type == "+" {
            self.i += 1;
            let right = calc_mul_div(self);
            left = left + right;
        } else if self.toks[self.i].type == "-" {
            self.i += 1;
            let right = calc_mul_div(self);
            left = left - right;
        } else {
            break;
        }
    }
    left
}

fn calc_mul_div(self: Calc) -> int {
    let left = calc_unary(self);
    loop {
        if self.toks[self.i].type == "*" {
            self.i += 1;
            let right = calc_unary(self);
            left = left * right;
        } else if self.toks[self.i].type == "/" {
            self.i += 1;
            let right = calc_unary(self);
            left = left / right;
        } else {
            break;
        }
    }
    left
    
}

fn calc_unary(self: Calc) -> int {
    if self.toks[self.i].type == "-" {
        self.i += 1;
        let subject = calc_unary(self);
        -subject
    } else {
        calc_operand(self)
    }
}

fn calc_operand(self: Calc) -> int {
    if self.i >= self.toks_len {
        std::println("error: expected expr");
        0
    } else if self.toks[self.i].type == "int" {
        let val = std::stoi(self.toks[self.i].value);
        self.i += 1;
        val
    } else if self.toks[self.i].type == "(" {
        self.i += 1;
        let val = calc_expr(self);
        if self.i >= self.toks_len or self.toks[self.i].type != ")" {
            std::println("error: missing ')'");
            return 0;
        }
        self.i += 1;
        val
    } else {
        std::println("error: expected expr");
        self.i += 1;
        0
    }
}

fn main() {
    loop {
        let line = std::input("> ");
        if line == "exit" {
            break;
        }
        let calc = calc_new(line);
        let val = calc_expr(calc);
        std::println(line);
    }
}
