#include #include #include "parse.hpp" struct Arena { char *base; uint32_t size; uint32_t used; Arena() { base = nullptr; size = 0; used = 0; } void init(void *base_, uint32_t size_) { base = (char*) base_; size = size_; used = 0; } static bool is_pow2(uint32_t n) { return (n & (n-1)) == 0; } void *alloc(uint32_t num, uint32_t align) { assert(is_pow2(align)); uint32_t pad = -(uintptr_t) (base + num) & (align-1); if (used + pad + num > size) return nullptr; void *ptr = base + used + pad; used += pad + num; return ptr; } }; struct Scanner { const char *src; uint32_t cur; uint32_t len; }; struct Token { enum Type { ADD, SUB, MUL, DIV, INT, FLOAT, OTHER, END, }; Type type; union { double _float; int64_t _int; } data; Token() { type = OTHER; } Token(Type type_) { type = type_; if (type == INT) data._int = 0; else data._float = 0; } Token(int64_t val) { type = INT; data._int = val; } Token(double val) { type = FLOAT; data._float = val; } }; static bool is_space(char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } static bool is_digit(char c) { return c >= '0' && c <= '9'; } static Token tokenize(Scanner &s) { while (s.cur < s.len && is_space(s.src[s.cur])) s.cur++; if (s.cur == s.len) return Token::END; if (is_digit(s.src[s.cur])) { /* * May be either an integer or a float. Peek after the first * digit sequence to see if there's a dot */ uint32_t peek = s.cur; while (peek < s.len && is_digit(s.src[peek])) peek++; bool no_dot = (peek == s.len || s.src[peek] != '.'); if (no_dot) { /* * Parse integer token */ int64_t buf = 0; do { int n = s.src[s.cur] - '0'; if (buf > (INT64_MAX - n) / 10) { /* * Overflow. Consume all remaining digits and return * the maximum representable value. */ while (s.cur < s.len && is_digit(s.src[s.cur])) s.cur++; return INT64_MAX; } buf = buf * 10 + n; s.cur++; } while (s.cur < s.len && is_digit(s.src[s.cur])); return buf; } else { /* * Parse float token */ double buf = 0; do { int n = s.src[s.cur] - '0'; buf = buf * 10 + n; s.cur++; } while (s.src[s.cur] != '.'); s.cur++; // Consume the dot /* * Consume the decimal part, if present */ if (s.cur < s.len && is_digit(s.src[s.cur])) { double q = 1; do { q /= 10; int n = s.src[s.cur] - '0'; buf += q * n; s.cur++; } while (s.cur < s.len && is_digit(s.src[s.cur])); } return buf; } } if (s.src[s.cur] == '+') { s.cur++; return Token::ADD; } if (s.src[s.cur] == '-') { s.cur++; return Token::SUB; } if (s.src[s.cur] == '*') { s.cur++; return Token::MUL; } if (s.src[s.cur] == '/') { s.cur++; return Token::DIV; } /* * Unexpected character */ return Token::OTHER; } static int precedence_of(Token token) { switch (token.type) { case Token::ADD: case Token::SUB: return 0; case Token::MUL: case Token::DIV: return 1; default:break; } return -1; } static bool is_right_associative(Token op) { (void) op; return false; } /* * Returns true if the next token is an operator * with a greater or equal precedence than the one * specified. The following operator is stored in * "op", but is only consimed when true is returned. */ static bool follows_oper_of_higher_precedence(Scanner &s, Token &op, int min) { uint32_t save = s.cur; op = tokenize(s); if (precedence_of(op) >= min) return true; s.cur = save; return false; } static bool should_associate_right(Scanner &s, Token op, Token &peek) { uint32_t save = s.cur; peek = tokenize(s); s.cur = save; int p1 = precedence_of(op); int p2 = precedence_of(peek); if (p1 < p2) return true; if (p1 == p2 && is_right_associative(peek)) return true; return false; } static Expr *make_int(Arena &a, int64_t raw) { void *p = a.alloc(sizeof(IntValue), alignof(IntValue)); if (p == nullptr) return nullptr; return new (p) IntValue(raw); } static Expr *make_float(Arena &a, double raw) { FloatValue *n; n = (FloatValue*) a.alloc(sizeof(FloatValue), alignof(FloatValue)); if (n == nullptr) return nullptr; new (n) FloatValue(raw); return n; } static Expr *make_neg(Arena &a, Expr *operand) { NegOperation *n; n = (NegOperation*) a.alloc(sizeof(NegOperation), alignof(NegOperation)); if (n == nullptr) return nullptr; new (n) NegOperation(operand); return n; } static Expr *parse_primary(Scanner &s, Arena &a) { Token t; /* * Handle preceding unary operators */ bool neg = false; for (;;) { t = tokenize(s); if (t.type == Token::SUB) neg = !neg; else { if (t.type != Token::ADD) break; } } Expr *e; switch (t.type) { case Token::INT : e = make_int (a, t.data._int); break; case Token::FLOAT : e = make_float(a, t.data._float); break; default: case Token::END: case Token::OTHER: return nullptr; } if (e == nullptr) return nullptr; if (neg) return make_neg(a, e); // May return NULL else return e; } static Expr *make_add(Arena &a, Expr *lhs, Expr *rhs) { AddOperation *n; n = (AddOperation*) a.alloc(sizeof(AddOperation), alignof(AddOperation)); if (n == nullptr) return nullptr; new (n) AddOperation(lhs, rhs); return n; } static Expr *make_sub(Arena &a, Expr *lhs, Expr *rhs) { SubOperation *n; n = (SubOperation*) a.alloc(sizeof(SubOperation), alignof(SubOperation)); if (n == nullptr) return nullptr; new (n) SubOperation(lhs, rhs); return n; } static Expr *make_mul(Arena &a, Expr *lhs, Expr *rhs) { MulOperation *n; n = (MulOperation*) a.alloc(sizeof(MulOperation), alignof(MulOperation)); if (n == nullptr) return nullptr; new (n) MulOperation(lhs, rhs); return n; } static Expr *make_div(Arena &a, Expr *lhs, Expr *rhs) { DivOperation *n; n = (DivOperation*) a.alloc(sizeof(DivOperation), alignof(DivOperation)); if (n == nullptr) return nullptr; new (n) DivOperation(lhs, rhs); return n; } /* * Got this algorithm from: * https://en.wikipedia.org/wiki/Operator-precedence_parser */ static Expr *parse_expression_2(Scanner &s, Arena &a, Expr *lhs, int min_precedence) { for (Token op; follows_oper_of_higher_precedence(s, op, min_precedence); ) { Expr *rhs = parse_primary(s, a); if (rhs == nullptr) return nullptr; for (Token peek; should_associate_right(s, op, peek); ) { int p1 = precedence_of(op); int p2 = precedence_of(peek); rhs = parse_expression_2(s, a, rhs, p1 + (p2 > p1)); if (rhs == nullptr) return nullptr; } switch (op.type) { case Token::ADD: lhs = make_add(a, lhs, rhs); break; case Token::SUB: lhs = make_sub(a, lhs, rhs); break; case Token::MUL: lhs = make_mul(a, lhs, rhs); break; case Token::DIV: lhs = make_div(a, lhs, rhs); break; default:break; } if (lhs == nullptr) return nullptr; } return lhs; } Expr *parse(const char *src, uint32_t len, void *mem, uint32_t memlen) { Arena a; a.init(mem, memlen); Scanner s; s.src = src; s.len = len; s.cur = 0; Expr *e; e = parse_primary(s, a); if (e == nullptr) return nullptr; return parse_expression_2(s, a, e, 0); }