first commit

This commit is contained in:
2024-04-07 17:55:16 +02:00
commit a15ca3add7
12 changed files with 905 additions and 0 deletions
+442
View File
@@ -0,0 +1,442 @@
#include <new>
#include <cassert>
#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);
}