pratt/src/eval.c
Bruno BELANYI ee2d8d1e9d pratt: eval: add exponentiation operator
This showcases right-associative operators
2020-11-08 18:42:53 +01:00

290 lines
8.2 KiB
C

#include <ctype.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#define UNREACHABLE() __builtin_unreachable()
#define ARR_SIZE(Arr) (sizeof(Arr) / sizeof(*Arr))
#define OP_STRING(...) (const char[]){__VA_ARGS__}
#define OP_SIZE(...) (sizeof(OP_STRING(__VA_ARGS__)) - 1)
typedef bool (nul_f)(const char **input, int *res, int until);
typedef bool (left_f)(int lhs, const char **input, int *res, int until);
// Define the different tokens
enum token_kind {
#define OP(Name, ...) Name,
#include "operators.inc"
};
struct token {
enum token_kind kind;
int val; // Used for NUMBER
};
// Forward declare functions
#define NulFunc(Name) \
static nul_f eval_ ## Name ## _nul;
#define LeftFunc(Name) \
static left_f eval_ ## Name ## _left;
#define PREFIX_POSTFIX(Name) \
NulFunc(Name) \
LeftFunc(Name)
#define PREFIX(Name) \
NulFunc(Name)
#define PREFIX_INFIX(Name) \
NulFunc(Name) \
LeftFunc(Name)
#define INFIX(Name) \
LeftFunc(Name)
#define POWER(Name) \
LeftFunc(Name)
#define TERN(Name) \
LeftFunc(Name)
#define PAREN(Name) \
NulFunc(Name)
#define NOT_OP(Name)
# define OP(Name, NulPrio, LeftPrio, Type, ...) \
Type(Name)
#include "operators.inc"
#undef NulFunc
#undef LeftFunc
// Symbol table
static const struct {
const char *op;
const size_t op_len;
const int nul_prio;
const int left_prio;
nul_f *const nul_func;
left_f *const left_func;
} ops[] = {
#define NulFunc(Name) \
eval_ ## Name ## _nul
#define LeftFunc(Name) \
eval_ ## Name ## _left
#define PREFIX_POSTFIX(Name, NulPrio, LeftPrio, ...) \
[Name] = {OP_STRING(__VA_ARGS__), OP_SIZE(__VA_ARGS__), NulPrio, LeftPrio, NulFunc(Name), LeftFunc(Name), },
#define PREFIX(Name, NulPrio, LeftPrio, ...) \
[Name] = {OP_STRING(__VA_ARGS__), OP_SIZE(__VA_ARGS__), NulPrio, LeftPrio, NulFunc(Name), NULL, },
#define PREFIX_INFIX(Name, NulPrio, LeftPrio, ...) \
[Name] = {OP_STRING(__VA_ARGS__), OP_SIZE(__VA_ARGS__), NulPrio, LeftPrio, NulFunc(Name), LeftFunc(Name), },
#define INFIX(Name, NulPrio, LeftPrio, ...) \
[Name] = {OP_STRING(__VA_ARGS__), OP_SIZE(__VA_ARGS__), NulPrio, LeftPrio, NULL, LeftFunc(Name), },
#define POWER(Name, NulPrio, LeftPrio, ...) \
[Name] = {OP_STRING(__VA_ARGS__), OP_SIZE(__VA_ARGS__), NulPrio, LeftPrio, NULL, LeftFunc(Name), },
#define TERN(Name, NulPrio, LeftPrio, ...) \
[Name] = {OP_STRING(__VA_ARGS__), OP_SIZE(__VA_ARGS__), NulPrio, LeftPrio, NULL, LeftFunc(Name), },
#define PAREN(Name, NulPrio, LeftPrio, ...) \
[Name] = {OP_STRING(__VA_ARGS__), OP_SIZE(__VA_ARGS__), NulPrio, LeftPrio, NulFunc(Name), NULL, },
#define NOT_OP(Name, NulPrio, LeftPrio, ...) \
[Name] = {OP_STRING(__VA_ARGS__), OP_SIZE(__VA_ARGS__), NulPrio, LeftPrio, NULL, NULL, },
# define OP(Name, NulPrio, LeftPrio, Type, Operator, /* Operator String */ ...) \
Type(Name, NulPrio, LeftPrio, __VA_ARGS__)
#include "operators.inc"
#undef LeftFunc
#undef NulFunc
};
// Lexing functions
static void skip_whitespace(const char **input) {
while (*input[0] && isspace(*input[0]))
*input += 1; // Skip this character
}
static bool expect(enum token_kind expected, const char **input) {
skip_whitespace(input);
if (strncmp(*input, ops[expected].op, ops[expected].op_len) != 0)
return false;
*input += ops[expected].op_len;
return true;
}
static size_t my_atoi(const char **input, int *val) {
size_t len = 0;
*val = 0; // Initialize its value
while (isdigit((*input)[len]))
{
*val *= 10;
*val += (*input)[len] - '0';
len += 1;
};
return len;
}
static size_t lex_operator(const char **input, enum token_kind *op) {
size_t best_len = 0;
for (size_t i = 0; i < ARR_SIZE(ops); ++i)
{
if (ops[i].op_len <= best_len) // Only look at longer operators
continue;
if (strncmp(*input, ops[i].op, ops[i].op_len) == 0)
{
best_len = ops[i].op_len;
*op = i;
}
}
return best_len;
}
static size_t lex_token(const char **input, struct token *tok) {
skip_whitespace(input);
size_t len;
if ((len = lex_operator(input, &tok->kind)))
return len;
// Assume that it is a number
tok->kind = NUMBER;
return my_atoi(input, &tok->val);
}
static bool parse_until_left(int lhs, const char **input, int *res, int prio) {
struct token tok;
size_t len = 0;
while ((len = lex_token(input, &tok)) &&
prio < ops[tok.kind].left_prio) {
*input += len;
left_f *left_func = ops[tok.kind].left_func;
if (!left_func)
return false; // Error: not a prefix operator
if (!left_func(lhs, input, res, prio))
return false; // Error: could not parse right-hand-side
lhs = *res;
}
return true;
}
static bool parse_until(const char **input, int *res, int prio) {
struct token tok;
size_t len = 0;
// Parse left token
if (!(len = lex_token(input, &tok)))
return false;
// Assume prefix
*input += len;
if (tok.kind == NUMBER) {
*res = tok.val;
} else {
nul_f *nul_func = ops[tok.kind].nul_func;
if (!nul_func)
return false; // Error: not a prefix operator
if (!nul_func(input, res, prio))
return false; // Error: could not parse right-hand-side
}
// Do left-loop
return parse_until_left(*res, input, res, prio);
}
bool eval_string(const char *input, int *res) {
if (!parse_until(&input, res, 0))
return false;
// Verify that only the expression exists
skip_whitespace(&input);
return *input == '\0';
}
#define NulFunc(Name) \
static bool eval_ ## Name ## _nul( \
const char **input, \
int *res, \
__attribute__((unused)) int until)
#define LeftFunc(Name) \
static bool eval_ ## Name ## _left( \
int lhs, \
const char **input, \
int *res, \
__attribute__((unused)) int until)
#define PREFIX_POSTFIX(Name, NulPrio, LeftPrio, Operator) \
LeftFunc(Name) { \
return parse_until_left(lhs Operator, input, res, LeftPrio); \
} \
NulFunc(Name) { \
if (!parse_until(input, res, NulPrio)) \
return false; \
Operator *res; \
return true; \
}
#define PREFIX(Name, NulPrio, LeftPrio, Operator) \
NulFunc(Name) { \
if (!parse_until(input, res, NulPrio)) \
return false; \
*res = Operator *res; \
return true; \
}
#define PREFIX_INFIX(Name, NulPrio, LeftPrio, Operator) \
LeftFunc(Name) { \
if (!parse_until(input, res, LeftPrio)) \
return false; \
*res = lhs Operator *res; \
return true; \
} \
NulFunc(Name) { \
if (!parse_until(input, res, NulPrio)) \
return false; \
*res = Operator *res; \
return true; \
}
#define INFIX(Name, NulPrio, LeftPrio, Operator) \
LeftFunc(Name) { \
if (!parse_until(input, res, LeftPrio)) \
return false; \
*res = lhs Operator *res; \
return true; \
}
static int my_pow(int lhs, int rhs) {
if (!rhs)
return 1;
int rec = my_pow(lhs * lhs, rhs / 2);
if (rhs & 1)
rec *= lhs;
return rec;
}
#define POWER(Name, NulPrio, LeftPrio, Operator) \
LeftFunc(Name) { \
if (!parse_until(input, res, LeftPrio - 1)) \
return false; \
*res = my_pow(lhs, *res); \
return true; \
}
#define TERN(Name, NulPrio, LeftPrio, Operator) \
LeftFunc(Name) { \
int true_val; \
if (!parse_until(input, &true_val, 0) || !expect(COLON, input)) \
return false; \
int false_val; \
if (!parse_until(input, &false_val, until)) \
return false; \
*res = (lhs ? true_val : false_val); \
return true; \
}
#define PAREN(Name, NulPrio, LeftPrio, Operator) \
NulFunc(Name) { \
if (!parse_until(input, res, 0)) \
return false; \
return expect(R_PAREN, input); \
}
#define NOT_OP(Name, NulPrio, LeftPrio, Operator) /* Nothing */
# define OP(Name, NulPrio, LeftPrio, Type, Operator, ...) \
Type(Name, NulPrio, LeftPrio, Operator)
#include "operators.inc"
#undef LeftFunc
#undef NulFunc