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