evalexpr: parse: parse multi-char prefix operators
This commit is contained in:
parent
10c7a96deb
commit
b12c3562ec
|
@ -4,21 +4,25 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "ast/ast.h"
|
#include "ast/ast.h"
|
||||||
|
|
||||||
#define UNREACHABLE() __builtin_unreachable()
|
#define UNREACHABLE() __builtin_unreachable()
|
||||||
#define ARR_SIZE(Arr) (sizeof(Arr) / sizeof(*Arr))
|
#define ARR_SIZE(Arr) (sizeof(Arr) / sizeof(*Arr))
|
||||||
|
#define OP_STRING(...) (const char[]){__VA_ARGS__}
|
||||||
|
#define OP_SIZE(...) (sizeof(OP_STRING(__VA_ARGS__)) - 1)
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *op;
|
const char *op;
|
||||||
|
const size_t op_len;
|
||||||
const enum op_kind kind;
|
const enum op_kind kind;
|
||||||
const int prio;
|
const int prio;
|
||||||
const enum { ASSOC_LEFT, ASSOC_RIGHT, ASSOC_NONE } assoc;
|
const enum { ASSOC_LEFT, ASSOC_RIGHT, ASSOC_NONE } assoc;
|
||||||
const enum { OP_INFIX, OP_PREFIX, OP_POSTFIX } fix;
|
const enum { OP_INFIX, OP_PREFIX, OP_POSTFIX } fix;
|
||||||
} ops[] = {
|
} ops[] = {
|
||||||
# define OP(Kind, Prio, Assoc, Fix, /* Operator string */ ...) \
|
# define OP(Kind, Prio, Assoc, Fix, /* Operator string */ ...) \
|
||||||
[Kind] = { (const char[]){__VA_ARGS__}, Kind, Prio, Assoc, Fix, },
|
{ OP_STRING(__VA_ARGS__), OP_SIZE(__VA_ARGS__), Kind, Prio, Assoc, Fix, },
|
||||||
#include "operators.inc"
|
#include "operators.inc"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,13 +49,26 @@ static enum op_kind char_to_binop(char c)
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum op_kind char_to_prefix(char c)
|
static size_t parse_prefix(enum op_kind *op, const char **input)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < ARR_SIZE(ops); ++i)
|
skip_whitespace(input);
|
||||||
if (ops[i].fix == OP_PREFIX && c == ops[i].op[0])
|
|
||||||
return ops[i].kind;
|
|
||||||
|
|
||||||
UNREACHABLE();
|
size_t best_len = 0;
|
||||||
|
for (size_t i = 0; i < ARR_SIZE(ops); ++i)
|
||||||
|
{
|
||||||
|
if (ops[i].fix != OP_PREFIX) // Only look at prefix operators
|
||||||
|
continue;
|
||||||
|
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 = ops[i].kind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return how many characters should be skipped
|
||||||
|
return best_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum op_kind char_to_postfix(char c)
|
static enum op_kind char_to_postfix(char c)
|
||||||
|
@ -72,15 +89,6 @@ static bool is_binop(char c)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_prefix(char c)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < ARR_SIZE(ops); ++i)
|
|
||||||
if (ops[i].fix == OP_PREFIX && c == ops[i].op[0])
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_postfix(char c)
|
static bool is_postfix(char c)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < ARR_SIZE(ops); ++i)
|
for (size_t i = 0; i < ARR_SIZE(ops); ++i)
|
||||||
|
@ -159,14 +167,14 @@ static bool update_op(enum op_kind *op, const char **input)
|
||||||
skip_whitespace(input);
|
skip_whitespace(input);
|
||||||
|
|
||||||
char c = *input[0];
|
char c = *input[0];
|
||||||
if (is_binop(c))
|
if (is_binop(c)) // FIXME: Use string
|
||||||
{
|
{
|
||||||
*op = char_to_binop(c);
|
*op = char_to_binop(c); // FIXME: Use string
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (is_postfix(c))
|
if (is_postfix(c)) // FIXME: Use string
|
||||||
{
|
{
|
||||||
*op = char_to_postfix(c);
|
*op = char_to_postfix(c); // FIXME: Use string
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +194,7 @@ static struct ast_node *climbing_parse_internal(const char **input, int prec)
|
||||||
{
|
{
|
||||||
const char c = *input[0];
|
const char c = *input[0];
|
||||||
eat_char(input);
|
eat_char(input);
|
||||||
if (is_binop(c))
|
if (is_binop(c)) // FIXME: Use string
|
||||||
{
|
{
|
||||||
struct ast_node *rhs =
|
struct ast_node *rhs =
|
||||||
climbing_parse_internal(input, right_prec(op));
|
climbing_parse_internal(input, right_prec(op));
|
||||||
|
@ -232,16 +240,14 @@ static bool my_atoi(const char **input, int *val)
|
||||||
|
|
||||||
static struct ast_node *parse_operand(const char **input)
|
static struct ast_node *parse_operand(const char **input)
|
||||||
{
|
{
|
||||||
skip_whitespace(input); // Whitespace is not significant
|
|
||||||
struct ast_node *ast = NULL;
|
struct ast_node *ast = NULL;
|
||||||
|
|
||||||
int val = 0;
|
int val = 0;
|
||||||
if (is_prefix(*input[0]))
|
size_t skip = 0;
|
||||||
|
enum op_kind op;
|
||||||
|
if ((skip = parse_prefix(&op, input))) // Removes whitespace as side-effect
|
||||||
{
|
{
|
||||||
enum op_kind op = char_to_prefix(*input[0]);
|
*input += skip; // Skip the parsed operator
|
||||||
// Remove the operator
|
|
||||||
eat_char(input);
|
|
||||||
|
|
||||||
ast = climbing_parse_internal(input, next_prec(op));
|
ast = climbing_parse_internal(input, next_prec(op));
|
||||||
|
|
||||||
if (!ast)
|
if (!ast)
|
||||||
|
|
Loading…
Reference in a new issue