Add expr
This commit is contained in:
savalet
2025-04-29 22:29:18 +02:00
parent 779fb2d3ff
commit 868785cf02
7 changed files with 302 additions and 0 deletions

View File

@@ -13,6 +13,7 @@ LIB_NAME := libu.a
SRC := $(wildcard src/*.c)
SRC += $(wildcard src/builtins/*.c)
SRC += $(wildcard src/builtins/expr/*.c)
SRC += $(wildcard src/ast/*.c)
SRC += $(wildcard src/utils/*.c)
SRC += $(wildcard src/local/*.c)

View File

@@ -37,4 +37,5 @@ int builtins_unset(ef_t *ef, char **args);
int builtins_where(ef_t *ef, char **args);
int builtins_which(ef_t *ef, char **args);
int builtins_break(ef_t *ef, char **args);
int builtins_expr(ef_t *ef, char **args);
#endif /* BUILTIND_H */

51
src/builtins/expr/expr.h Normal file
View File

@@ -0,0 +1,51 @@
/*
** EPITECH PROJECT, 2025
** __
** File description:
** _
*/
#ifndef EXPR_H
#define EXPR_H
#include <stdbool.h>
#include <stddef.h>
#define FIRST_TOKEN_BATCH_SIZE 64
#define IS_CHAR(s, c) ((s)[0] == (c) && (s)[1] == '\0')
#define COUNT_OF(arr) (sizeof (arr) / (sizeof *(arr)))
typedef unsigned char uchar_t;
typedef enum {
E_VAL_INT = 'd',
E_VAL_STR = 's',
E_VAL_ERR = 'e',
} expr_val_type_t;
typedef struct {
char *p;
union {
long val;
char const *str;
};
char type;
} expr_val_t;
typedef struct {
char name[4];
unsigned int prec;
expr_val_t (*apply)(expr_val_t *, expr_val_t *);
} expr_op_precedence_t;
typedef struct {
char **args;
} expr_state_t;
expr_val_t expr_run(expr_state_t *state, uchar_t depth, int prec);
extern const expr_op_precedence_t OPERATOR_PRECEDENCE[];
extern const size_t OPERATOR_PRECEDENCE_COUNT;
#endif

View File

@@ -0,0 +1,111 @@
/*
** EPITECH PROJECT, 2025
** __
** File description:
** _
*/
#include <stdio.h>
#include <string.h>
#include "expr.h"
static
expr_val_t apply_add(expr_val_t *left, expr_val_t *right)
{
expr_val_t res;
if (left->type != E_VAL_INT || right->type != E_VAL_INT) {
res.str = "non-integer argument";
res.type = E_VAL_ERR;
} else {
res.val = left->val + right->val;
res.type = E_VAL_INT;
}
return res;
}
static
expr_val_t apply_sub(expr_val_t *left, expr_val_t *right)
{
expr_val_t res;
if (left->type != E_VAL_INT || right->type != E_VAL_INT) {
res.str = "non-integer argument";
res.type = E_VAL_ERR;
} else {
res.val = left->val - right->val;
res.type = E_VAL_INT;
}
return res;
}
static
expr_val_t apply_mul(expr_val_t *left, expr_val_t *right)
{
expr_val_t res;
if (left->type != E_VAL_INT || right->type != E_VAL_INT) {
res.str = "non-integer argument";
res.type = E_VAL_ERR;
} else {
res.val = left->val * right->val;
res.type = E_VAL_INT;
}
return res;
}
static
expr_val_t apply_div(expr_val_t *left, expr_val_t *right)
{
expr_val_t res = { .type = E_VAL_ERR };
if (left->type != E_VAL_INT || right->type != E_VAL_INT) {
res.str = "non-integer argument";
res.type = E_VAL_ERR;
return res;
}
if (right->val == 0) {
res.str = "division by zero";
res.type = E_VAL_ERR;
return res;
}
res.val = left->val / right->val;
res.type = E_VAL_INT;
return res;
}
static
expr_val_t apply_lt(expr_val_t *left, expr_val_t *right)
{
expr_val_t res = { .type = E_VAL_INT };
if (left->type == E_VAL_INT && right->type == E_VAL_INT)
res.val = left->val < right->val;
else
res.val = strcmp(left->p, right->p) < 0;
return res;
}
static
expr_val_t apply_gt(expr_val_t *left, expr_val_t *right)
{
expr_val_t res = { .type = E_VAL_INT };
if (left->type == E_VAL_INT && right->type == E_VAL_INT)
res.val = left->val > right->val;
else
res.val = strcmp(left->p, right->p) > 0;
return res;
}
const expr_op_precedence_t OPERATOR_PRECEDENCE[] = {
{ .name = "+", .prec = 2, apply_add },
{ .name = "-", .prec = 2, apply_sub },
{ .name = "*", .prec = 3, apply_mul },
{ .name = "/", .prec = 3, apply_div },
{ .name = "<", .prec = 1, apply_lt },
{ .name = ">", .prec = 1, apply_gt },
};
const size_t OPERATOR_PRECEDENCE_COUNT = COUNT_OF(OPERATOR_PRECEDENCE);

View File

@@ -0,0 +1,100 @@
/*
** EPITECH PROJECT, 2025
** __
** File description:
** _
*/
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "expr.h"
static
const expr_op_precedence_t *op_precedence(char *op)
{
for (size_t i = 0; i < OPERATOR_PRECEDENCE_COUNT; i++)
if (!strcmp(op, OPERATOR_PRECEDENCE[i].name))
return OPERATOR_PRECEDENCE + i;
return NULL;
}
static
expr_val_t expr_parse_paren(expr_state_t *state, uchar_t depth)
{
expr_val_t out;
state->args++;
out = expr_run(state, depth, 0);
if (out.type == E_VAL_ERR)
return out;
if (*state->args == NULL || !IS_CHAR(*state->args, ')')) {
out.str = "syntax error: missing ')'";
out.type = E_VAL_ERR;
return out;
}
state->args++;
return out;
}
static
void expr_solve_precedence(
expr_val_t *out,
expr_state_t *state, uchar_t depth, unsigned int prec)
{
const expr_op_precedence_t *op_prec;
expr_val_t tmp;
while (*state->args != NULL && !IS_CHAR(*state->args, ')')) {
op_prec = op_precedence(*state->args);
if (op_prec == NULL) {
out->type = E_VAL_ERR;
out->str = "syntax error: unexpected argument";
break;
}
if (op_prec->prec == 0 || op_prec->prec <= prec)
return;
state->args++;
tmp = expr_run(state, depth, op_prec->prec);
if (tmp.type == E_VAL_ERR) {
*out = tmp;
break;
}
*out = op_prec->apply(out, &tmp);
}
}
expr_val_t expr_parse_val(expr_state_t *state)
{
expr_val_t out;
char *chk;
out.val = strtol(*state->args, &chk, 10);
out.p = *state->args;
if (chk != *state->args && *chk == '\0')
out.type = E_VAL_INT;
else
out.type = E_VAL_STR;
state->args++;
return out;
}
expr_val_t expr_run(expr_state_t *state, uchar_t depth, int prec)
{
expr_val_t out = {
.type = E_VAL_ERR,
.str = "syntax error: missing argument"
};
if (*state->args == NULL)
return out;
if (IS_CHAR(*state->args, '('))
out = expr_parse_paren(state, depth + 1);
else
out = expr_parse_val(state);
expr_solve_precedence(&out, state, depth, prec);
return out;
}

View File

@@ -0,0 +1,37 @@
/*
** EPITECH PROJECT, 2025
** __
** File description:
** _
*/
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "exec.h"
#include "expr.h"
int builtins_expr(ef_t *ef, char **args)
{
int argc = 0;
expr_state_t state;
expr_val_t ret;
for (; args[argc] != NULL; argc++);
if (argc < 2) {
fprintf(stderr, "expr: missing operand\n");
return RETURN_FAILURE;
}
state = (expr_state_t){ .args = &args[1] };
ret = expr_run(&state, 0, 0);
if (ret.type == E_VAL_ERR) {
printf("expr: %s\n", ret.str);
return RETURN_FAILURE;
}
if (ret.type == E_VAL_INT)
printf("%ld\n", ret.val);
if (ret.type == E_VAL_STR)
printf("%s\n", ret.p);
return RETURN_SUCCESS;
}

View File

@@ -44,6 +44,7 @@ const builtins_funcs_t BUILTINS[] = {
{ "unset", &builtins_unset },
{ "where", &builtins_where },
{ "which", &builtins_which },
{ "expr", &builtins_expr },
{ "break", &builtins_break }
};