diff options
| -rw-r--r-- | lab03/expression.h | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/lab03/expression.h b/lab03/expression.h new file mode 100644 index 0000000..89d3a36 --- /dev/null +++ b/lab03/expression.h @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef EXPRESSION_H +#define EXPRESSION_H + +#include <ctype.h> +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include "vec.h" + +#define EXPRESSION_NAME_MAX 64 + +enum expression_error_code { + EXPRESSION_OK, + EXPRESSION_SYNTAX_ERROR, + EXPRESSION_PAREN_ERROR, + EXPRESSION_UNKNOWN_VARIABLE, + EXPRESSION_UNSET_VARIABLE, + EXPRESSION_DIVISION_BY_ZERO, +}; + +struct expression_error { + enum expression_error_code code; + size_t position; +}; + +struct expression_variable { + char name[EXPRESSION_NAME_MAX]; + double value; + bool assigned; +}; + +struct expression_context { + struct vec variables; +}; + +static inline void expression_init(struct expression_context *ctx) +{ + vec_init(&ctx->variables, struct expression_variable); +} + +static inline void expression_destroy(struct expression_context *ctx) +{ + vec_destroy(&ctx->variables); +} + +static inline void expression_error(struct expression_error *error, + enum expression_error_code code, + size_t position) +{ + if (error) { + error->code = code; + error->position = position; + } +} + +static inline bool expression_name_start(unsigned char c) +{ + return isalpha(c) || c == '_'; +} + +static inline bool expression_name_char(unsigned char c) +{ + return isalnum(c) || c == '_'; +} + +static inline struct expression_variable * +expression_variable(struct expression_context *ctx, const char *name) +{ + for (size_t i = 0; i < ctx->variables.size; ++i) { + struct expression_variable *v = vec_at(&ctx->variables, i); + if (strcmp(v->name, name) == 0) { + return v; + } + } + return NULL; +} + +static inline bool expression_set_variable(struct expression_context *ctx, + const char *name, + double value) +{ + if (!expression_name_start((unsigned char)name[0]) || + strlen(name) >= EXPRESSION_NAME_MAX) return false; + for (const unsigned char *p = (const unsigned char *)name + 1; *p; ++p) { + if (!expression_name_char(*p)) { + return false; + } + } + struct expression_variable *v = expression_variable(ctx, name); + if (!v) { + struct expression_variable item = { .value = value, .assigned = true }; + strcpy(item.name, name); + if (!vec_push_back(&ctx->variables, &item)) { + return false; + } + } else { + v->value = value; + v->assigned = true; + } + return true; +} + +static inline bool expression_collect_variables(struct expression_context *ctx, + const char *text, + struct expression_error *error) +{ + vec_clear(&ctx->variables); + for (size_t i = 0; text[i];) { + unsigned char c = (unsigned char)text[i]; + if (isspace(c)) { + ++i; + } else if (expression_name_start(c)) { + size_t start = i++; + while (expression_name_char((unsigned char)text[i])) { + ++i; + } + size_t length = i - start; + if (length >= EXPRESSION_NAME_MAX) { + expression_error(error, EXPRESSION_SYNTAX_ERROR, start); + return false; + } + char name[EXPRESSION_NAME_MAX]; + memcpy(name, text + start, length); + name[length] = '\0'; + if (!expression_variable(ctx, name)) { + struct expression_variable item = { .assigned = false }; + strcpy(item.name, name); + if (!vec_push_back(&ctx->variables, &item)) { + expression_error(error, EXPRESSION_SYNTAX_ERROR, start); + return false; + } + } + } else if (isdigit(c) || c == '.' || c == '+' || c == '-' || + c == '*' || c == '/' || c == '(' || c == ')') { + ++i; + } else { + expression_error(error, EXPRESSION_SYNTAX_ERROR, i); + return false; + } + } + expression_error(error, EXPRESSION_OK, 0); + return true; +} + +struct expression_parser { + const char *text; + size_t position; + const struct expression_context *ctx; + struct expression_error *error; +}; + +static inline void expression_skip(struct expression_parser *p) +{ + while (isspace((unsigned char)p->text[p->position])) { + ++p->position; + } +} + +static inline bool expression_parse_sum(struct expression_parser *, double *); + +static inline bool expression_parse_primary(struct expression_parser *p, + double *out) +{ + expression_skip(p); + size_t start = p->position; + char c = p->text[start]; + if (c == '(') { + ++p->position; + if (!expression_parse_sum(p, out)) return false; + expression_skip(p); + if (p->text[p->position] != ')') { + expression_error(p->error, EXPRESSION_PAREN_ERROR, p->position); + return false; + } + ++p->position; + return true; + } + if (isdigit((unsigned char)c) || c == '.') { + char *end; + errno = 0; + *out = strtod(p->text + start, &end); + if (end == p->text + start || errno == ERANGE) { + expression_error(p->error, EXPRESSION_SYNTAX_ERROR, start); + return false; + } + p->position = (size_t)(end - p->text); + return true; + } + if (expression_name_start((unsigned char)c)) { + char name[EXPRESSION_NAME_MAX]; + size_t length; + ++p->position; + while (expression_name_char((unsigned char)p->text[p->position])) ++p->position; + length = p->position - start; + if (length >= sizeof(name)) { + expression_error(p->error, EXPRESSION_SYNTAX_ERROR, start); + return false; + } + memcpy(name, p->text + start, length); + name[length] = '\0'; + const struct expression_variable *v = + expression_variable((struct expression_context *)p->ctx, name); + if (!v) expression_error(p->error, EXPRESSION_UNKNOWN_VARIABLE, start); + else if (!v->assigned) expression_error(p->error, EXPRESSION_UNSET_VARIABLE, start); + else *out = v->value; + return v && v->assigned; + } + expression_error(p->error, EXPRESSION_SYNTAX_ERROR, start); + return false; +} + +static inline bool expression_parse_unary(struct expression_parser *p, double *out) +{ + expression_skip(p); + char c = p->text[p->position]; + if (c == '+' || c == '-') { + ++p->position; + if (!expression_parse_unary(p, out)) return false; + if (c == '-') *out = -*out; + return true; + } + return expression_parse_primary(p, out); +} + +static inline bool expression_parse_product(struct expression_parser *p, double *out) +{ + if (!expression_parse_unary(p, out)) return false; + for (;;) { + expression_skip(p); + char op = p->text[p->position]; + if (op != '*' && op != '/') return true; + ++p->position; + double rhs; + if (!expression_parse_unary(p, &rhs)) return false; + if (op == '/') { + if (rhs == 0.0) { + expression_error(p->error, EXPRESSION_DIVISION_BY_ZERO, p->position); + return false; + } + *out /= rhs; + } else *out *= rhs; + } +} + +static inline bool expression_parse_sum(struct expression_parser *p, double *out) +{ + if (!expression_parse_product(p, out)) return false; + for (;;) { + expression_skip(p); + char op = p->text[p->position]; + if (op != '+' && op != '-') return true; + ++p->position; + double rhs; + if (!expression_parse_product(p, &rhs)) return false; + if (op == '+') *out += rhs; + else *out -= rhs; + } +} + +static inline bool expression_eval(const struct expression_context *ctx, + const char *text, + double *result, + struct expression_error *error) +{ + struct expression_parser p = { text, 0, ctx, error }; + if (!expression_parse_sum(&p, result)) return false; + expression_skip(&p); + if (p.text[p.position]) { + expression_error(error, + p.text[p.position] == ')' ? EXPRESSION_PAREN_ERROR + : EXPRESSION_SYNTAX_ERROR, + p.position); + return false; + } + expression_error(error, EXPRESSION_OK, 0); + return true; +} + +#endif /* EXPRESSION_H */ |
