first commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
main
|
||||||
|
main.exe
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
This repository implements a simple arithmetic expression parser and evaluator using the visitor pattern.
|
||||||
|
|
||||||
|
The main inspiration was this article:
|
||||||
|
https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions/
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <http://unlicense.org/>
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
#include "expr.hpp"
|
||||||
|
|
||||||
|
struct EvalVisitor : Visitor {
|
||||||
|
|
||||||
|
static const uint32_t max_depth = 32;
|
||||||
|
|
||||||
|
bool fail;
|
||||||
|
uint32_t depth;
|
||||||
|
double stack[max_depth];
|
||||||
|
|
||||||
|
EvalVisitor()
|
||||||
|
{
|
||||||
|
depth = 0;
|
||||||
|
fail = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_int(IntValue *node)
|
||||||
|
{
|
||||||
|
if (fail) return;
|
||||||
|
|
||||||
|
if (depth == max_depth) {
|
||||||
|
fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack[depth++] = node->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_float(FloatValue *node)
|
||||||
|
{
|
||||||
|
if (fail) return;
|
||||||
|
|
||||||
|
if (depth == max_depth) {
|
||||||
|
fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack[depth++] = node->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_neg(NegOperation *node)
|
||||||
|
{
|
||||||
|
node->op->visit(*this);
|
||||||
|
|
||||||
|
if (fail) return;
|
||||||
|
|
||||||
|
if (depth < 1) {
|
||||||
|
fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack[depth-1] = -stack[depth-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_add(AddOperation *node)
|
||||||
|
{
|
||||||
|
node->lhs->visit(*this);
|
||||||
|
node->rhs->visit(*this);
|
||||||
|
|
||||||
|
if (fail) return;
|
||||||
|
|
||||||
|
if (depth < 2) {
|
||||||
|
fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double rhs = stack[--depth];
|
||||||
|
double lhs = stack[--depth];
|
||||||
|
stack[depth++] = lhs + rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_sub(SubOperation *node)
|
||||||
|
{
|
||||||
|
node->lhs->visit(*this);
|
||||||
|
node->rhs->visit(*this);
|
||||||
|
|
||||||
|
if (fail) return;
|
||||||
|
|
||||||
|
if (depth < 2) {
|
||||||
|
fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double rhs = stack[--depth];
|
||||||
|
double lhs = stack[--depth];
|
||||||
|
stack[depth++] = lhs - rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_mul(MulOperation *node)
|
||||||
|
{
|
||||||
|
node->lhs->visit(*this);
|
||||||
|
node->rhs->visit(*this);
|
||||||
|
|
||||||
|
if (fail) return;
|
||||||
|
|
||||||
|
if (depth < 2) {
|
||||||
|
fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double rhs = stack[--depth];
|
||||||
|
double lhs = stack[--depth];
|
||||||
|
stack[depth++] = lhs * rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_div(DivOperation *node)
|
||||||
|
{
|
||||||
|
node->lhs->visit(*this);
|
||||||
|
node->rhs->visit(*this);
|
||||||
|
|
||||||
|
if (fail) return;
|
||||||
|
|
||||||
|
if (depth < 2) {
|
||||||
|
fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double rhs = stack[--depth];
|
||||||
|
double lhs = stack[--depth];
|
||||||
|
stack[depth++] = lhs / rhs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
double eval(Expr *e)
|
||||||
|
{
|
||||||
|
EvalVisitor visitor;
|
||||||
|
e->visit(visitor);
|
||||||
|
return visitor.stack[visitor.depth-1];
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
struct IntValue;
|
||||||
|
struct FloatValue;
|
||||||
|
struct NegOperation;
|
||||||
|
struct AddOperation;
|
||||||
|
struct SubOperation;
|
||||||
|
struct MulOperation;
|
||||||
|
struct DivOperation;
|
||||||
|
|
||||||
|
struct Visitor {
|
||||||
|
virtual void visit_int(IntValue *node) = 0;
|
||||||
|
virtual void visit_float(FloatValue *node) = 0;
|
||||||
|
virtual void visit_neg(NegOperation *node) = 0;
|
||||||
|
virtual void visit_add(AddOperation *node) = 0;
|
||||||
|
virtual void visit_sub(SubOperation *node) = 0;
|
||||||
|
virtual void visit_mul(MulOperation *node) = 0;
|
||||||
|
virtual void visit_div(DivOperation *node) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Expr {
|
||||||
|
virtual void visit(Visitor &visitor) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IntValue : Expr {
|
||||||
|
|
||||||
|
int64_t val;
|
||||||
|
|
||||||
|
IntValue(int64_t val_)
|
||||||
|
{
|
||||||
|
val = val_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void visit(Visitor &visitor)
|
||||||
|
{
|
||||||
|
visitor.visit_int(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FloatValue : Expr {
|
||||||
|
|
||||||
|
double val;
|
||||||
|
|
||||||
|
FloatValue(double val_)
|
||||||
|
{
|
||||||
|
val = val_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void visit(Visitor &visitor)
|
||||||
|
{
|
||||||
|
visitor.visit_float(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NegOperation : Expr {
|
||||||
|
|
||||||
|
Expr *op;
|
||||||
|
|
||||||
|
NegOperation(Expr *op_)
|
||||||
|
{
|
||||||
|
op = op_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void visit(Visitor &visitor)
|
||||||
|
{
|
||||||
|
visitor.visit_neg(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AddOperation : Expr {
|
||||||
|
|
||||||
|
Expr *lhs;
|
||||||
|
Expr *rhs;
|
||||||
|
|
||||||
|
AddOperation(Expr *lhs_, Expr *rhs_)
|
||||||
|
{
|
||||||
|
lhs = lhs_;
|
||||||
|
rhs = rhs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void visit(Visitor &visitor)
|
||||||
|
{
|
||||||
|
visitor.visit_add(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SubOperation : Expr {
|
||||||
|
|
||||||
|
Expr *lhs;
|
||||||
|
Expr *rhs;
|
||||||
|
|
||||||
|
SubOperation(Expr *lhs_, Expr *rhs_)
|
||||||
|
{
|
||||||
|
lhs = lhs_;
|
||||||
|
rhs = rhs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void visit(Visitor &visitor)
|
||||||
|
{
|
||||||
|
visitor.visit_sub(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MulOperation : Expr {
|
||||||
|
|
||||||
|
Expr *lhs;
|
||||||
|
Expr *rhs;
|
||||||
|
|
||||||
|
MulOperation(Expr *lhs_, Expr *rhs_)
|
||||||
|
{
|
||||||
|
lhs = lhs_;
|
||||||
|
rhs = rhs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void visit(Visitor &visitor)
|
||||||
|
{
|
||||||
|
visitor.visit_mul(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DivOperation : Expr {
|
||||||
|
|
||||||
|
Expr *lhs;
|
||||||
|
Expr *rhs;
|
||||||
|
|
||||||
|
DivOperation(Expr *lhs_, Expr *rhs_)
|
||||||
|
{
|
||||||
|
lhs = lhs_;
|
||||||
|
rhs = rhs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void visit(Visitor &visitor)
|
||||||
|
{
|
||||||
|
visitor.visit_div(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "expr.hpp"
|
||||||
|
#include "eval.hpp"
|
||||||
|
#include "parse.hpp"
|
||||||
|
#include "stringify.hpp"
|
||||||
|
|
||||||
|
uint32_t read_line(FILE *stream, char *dst, uint32_t max)
|
||||||
|
{
|
||||||
|
uint32_t num = 0;
|
||||||
|
for (char c; (c = getc(stream)) != '\n'; )
|
||||||
|
if (num < max) dst[num++] = c;
|
||||||
|
|
||||||
|
if (max > 0) {
|
||||||
|
if (num == max)
|
||||||
|
num--;
|
||||||
|
dst[num] = '\0';
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
uint32_t len;
|
||||||
|
char text[1<<9];
|
||||||
|
char pool[1<<12];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read an expression from stdin
|
||||||
|
*/
|
||||||
|
fprintf(stdout, "> ");
|
||||||
|
len = read_line(stdin, text, sizeof(text));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse it
|
||||||
|
*/
|
||||||
|
Expr *e = parse(text, len, pool, sizeof(pool));
|
||||||
|
if (e == nullptr) {
|
||||||
|
fprintf(stderr, "Error\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print the expression and its result
|
||||||
|
*/
|
||||||
|
stringify(e, text, sizeof(text));
|
||||||
|
fprintf(stdout, "%s = %g\n", text, eval(e));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
all:
|
||||||
|
g++ main.cpp parse.cpp eval.cpp stringify.cpp -o main -Wall -Wextra -ggdb
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#include "expr.hpp"
|
||||||
|
|
||||||
|
Expr *parse(const char *src, uint32_t len,
|
||||||
|
void *mem, uint32_t memlen);
|
||||||
+101
@@ -0,0 +1,101 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "expr.hpp"
|
||||||
|
|
||||||
|
struct StringifyVisitor : Visitor {
|
||||||
|
|
||||||
|
char *dst;
|
||||||
|
uint32_t num;
|
||||||
|
uint32_t max;
|
||||||
|
|
||||||
|
void append(const char *str, uint32_t len)
|
||||||
|
{
|
||||||
|
if (num < max) {
|
||||||
|
uint32_t cpy = std::min(len, max - num);
|
||||||
|
memcpy(dst + num, str, cpy);
|
||||||
|
}
|
||||||
|
num += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringifyVisitor(char *dst_, uint32_t max_)
|
||||||
|
{
|
||||||
|
dst = dst_;
|
||||||
|
max = max_;
|
||||||
|
num = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_int(IntValue *node)
|
||||||
|
{
|
||||||
|
std::ostringstream sstr;
|
||||||
|
sstr << node->val;
|
||||||
|
std::string str = sstr.str();
|
||||||
|
append(str.c_str(), str.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_float(FloatValue *node)
|
||||||
|
{
|
||||||
|
std::ostringstream sstr;
|
||||||
|
sstr << node->val;
|
||||||
|
std::string str = sstr.str();
|
||||||
|
append(str.c_str(), str.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_neg(NegOperation *node)
|
||||||
|
{
|
||||||
|
append("-", 1);
|
||||||
|
node->op->visit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_add(AddOperation *node)
|
||||||
|
{
|
||||||
|
append("(", 1);
|
||||||
|
node->lhs->visit(*this);
|
||||||
|
append(" + ", 3);
|
||||||
|
node->rhs->visit(*this);
|
||||||
|
append(")", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_sub(SubOperation *node)
|
||||||
|
{
|
||||||
|
append("(", 1);
|
||||||
|
node->lhs->visit(*this);
|
||||||
|
append(" - ", 3);
|
||||||
|
node->rhs->visit(*this);
|
||||||
|
append(")", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_mul(MulOperation *node)
|
||||||
|
{
|
||||||
|
append("(", 1);
|
||||||
|
node->lhs->visit(*this);
|
||||||
|
append(" * ", 3);
|
||||||
|
node->rhs->visit(*this);
|
||||||
|
append(")", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void visit_div(DivOperation *node)
|
||||||
|
{
|
||||||
|
append("(", 1);
|
||||||
|
node->lhs->visit(*this);
|
||||||
|
append(" / ", 3);
|
||||||
|
node->rhs->visit(*this);
|
||||||
|
append(")", 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t stringify(Expr *e, char *dst, uint32_t max)
|
||||||
|
{
|
||||||
|
StringifyVisitor visitor(dst, max);
|
||||||
|
e->visit(visitor);
|
||||||
|
uint32_t num = visitor.num;
|
||||||
|
|
||||||
|
if (max > 0) {
|
||||||
|
if (num == max)
|
||||||
|
num--;
|
||||||
|
dst[num] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#include "expr.hpp"
|
||||||
|
|
||||||
|
uint32_t stringify(Expr *e, char *dst, uint32_t max);
|
||||||
Reference in New Issue
Block a user