diff options
Diffstat (limited to 'lab04/expression.h')
| -rw-r--r-- | lab04/expression.h | 438 |
1 files changed, 365 insertions, 73 deletions
diff --git a/lab04/expression.h b/lab04/expression.h index f0e2847..234ecb3 100644 --- a/lab04/expression.h +++ b/lab04/expression.h @@ -4,6 +4,7 @@ #include <ctype.h> #include <errno.h> +#include <math.h> #include <stdbool.h> #include <stddef.h> #include <stdlib.h> @@ -12,6 +13,7 @@ #include "stack.h" #define EXPRESSION_NAME_MAX 64 +#define EXPRESSION_TEXT_MAX 256 enum expression_error_code { EXPRESSION_OK, @@ -20,6 +22,7 @@ enum expression_error_code { EXPRESSION_UNKNOWN_VARIABLE, EXPRESSION_UNSET_VARIABLE, EXPRESSION_DIVISION_BY_ZERO, + EXPRESSION_UNKNOWN_FUNCTION, }; struct expression_error { @@ -33,18 +36,27 @@ struct expression_variable { bool assigned; }; +struct expression_function { + char name[EXPRESSION_NAME_MAX]; + char parameter[EXPRESSION_NAME_MAX]; + char body[EXPRESSION_TEXT_MAX]; +}; + struct expression_context { struct vec variables; + struct vec functions; }; static inline void expression_init(struct expression_context *ctx) { vec_init(&ctx->variables, struct expression_variable); + vec_init(&ctx->functions, struct expression_function); } static inline void expression_destroy(struct expression_context *ctx) { vec_destroy(&ctx->variables); + vec_destroy(&ctx->functions); } static inline void expression_error(struct expression_error *error, @@ -67,6 +79,27 @@ static inline bool expression_name_char(unsigned char c) return isalnum(c) || c == '_'; } +static inline size_t expression_skip_space(const char *text, size_t position) +{ + while (isspace((unsigned char)text[position])) ++position; + return position; +} + +static inline bool expression_read_name(const char *text, size_t *position, + char name[EXPRESSION_NAME_MAX]) +{ + size_t start = *position; + if (!expression_name_start((unsigned char)text[start])) return false; + size_t end = start + 1; + while (expression_name_char((unsigned char)text[end])) ++end; + size_t length = end - start; + if (length >= EXPRESSION_NAME_MAX) return false; + memcpy(name, text + start, length); + name[length] = '\0'; + *position = end; + return true; +} + static inline struct expression_variable * expression_variable(struct expression_context *ctx, const char *name) { @@ -108,57 +141,159 @@ static inline bool expression_collect_variables(struct expression_context *ctx, const char *text, struct expression_error *error) { - vec_clear(&ctx->variables); + VEC(variables, struct expression_variable); for (size_t i = 0; text[i];) { unsigned char c = (unsigned char)text[i]; if (isspace(c)) { ++i; + } else if (isdigit(c) || c == '.') { + char *end; + errno = 0; + strtod(text + i, &end); + if (end == text + i || errno == ERANGE) goto syntax_error; + i = (size_t)(end - text); } 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)) { + if (!expression_read_name(text, &i, name)) goto syntax_error; + if (text[expression_skip_space(text, i)] == '(') continue; + size_t index = 0; + while (index < variables.size && + strcmp(((struct expression_variable *)vec_at(&variables, index))->name, + name) != 0) ++index; + if (index == variables.size) { 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; - } + if (!vec_push_back(&variables, &item)) goto syntax_error; } - } else if (isdigit(c) || c == '.' || c == '+' || c == '-' || - c == '*' || c == '/' || c == '(' || c == ')') { + } else if (c == '<' || c == '>') { + i += text[i + 1] == '=' ? 2 : 1; + } else if ((c == '=' || c == '!') && text[i + 1] == '=') { + i += 2; + } else if (strchr("+-*/^()?:", c)) { ++i; } else { - expression_error(error, EXPRESSION_SYNTAX_ERROR, i); + goto syntax_error; + } + continue; +syntax_error: + expression_error(error, EXPRESSION_SYNTAX_ERROR, i); + vec_destroy(&variables); + return false; + } + vec_swap(&ctx->variables, &variables); + vec_destroy(&variables); + expression_error(error, EXPRESSION_OK, 0); + return true; +} + +static inline bool expression_define(struct expression_context *ctx, + const char *text, + struct expression_error *error) +{ + struct expression_function function; + size_t position = expression_skip_space(text, 0); + if (strncmp(text + position, "DEF", 3) != 0 || + expression_name_char((unsigned char)text[position + 3])) goto syntax_error; + position = expression_skip_space(text, position + 3); + if (!expression_read_name(text, &position, function.name)) goto syntax_error; + position = expression_skip_space(text, position); + if (text[position] != '(') goto syntax_error; + position = expression_skip_space(text, position + 1); + if (!expression_read_name(text, &position, function.parameter)) goto syntax_error; + position = expression_skip_space(text, position); + if (text[position] != ')') goto syntax_error; + position = expression_skip_space(text, position + 1); + if (text[position] != '=') goto syntax_error; + position = expression_skip_space(text, position + 1); + + size_t end = strlen(text); + while (end > position && isspace((unsigned char)text[end - 1])) --end; + if (end > position && text[end - 1] == ';') --end; + while (end > position && isspace((unsigned char)text[end - 1])) --end; + size_t length = end - position; + if (length == 0 || length >= sizeof(function.body)) goto syntax_error; + memcpy(function.body, text + position, length); + function.body[length] = '\0'; + + struct expression_context body; + expression_init(&body); + if (!expression_collect_variables(&body, function.body, error)) { + if (error) error->position += position; + expression_destroy(&body); + return false; + } + for (size_t i = 0; i < body.variables.size; ++i) { + const struct expression_variable *variable = vec_at_const(&body.variables, i); + if (strcmp(variable->name, function.parameter) != 0) { + expression_error(error, EXPRESSION_UNKNOWN_VARIABLE, position); + expression_destroy(&body); return false; } } + expression_destroy(&body); + if (!vec_push_back(&ctx->functions, &function)) goto syntax_error; expression_error(error, EXPRESSION_OK, 0); return true; + +syntax_error: + expression_error(error, EXPRESSION_SYNTAX_ERROR, position); + return false; +} + +static inline size_t expression_find_function(const struct expression_context *ctx, + const char *name, + size_t current_function) +{ + if (current_function != SIZE_MAX) { + const struct expression_function *function = + vec_at_const(&ctx->functions, current_function); + if (strcmp(function->name, name) == 0) return current_function; + } + for (size_t i = ctx->functions.size; i-- > 0;) { + const struct expression_function *function = vec_at_const(&ctx->functions, i); + if (strcmp(function->name, name) == 0) return i; + } + return SIZE_MAX; } +enum expression_operator_kind { + EXPRESSION_OPEN, + EXPRESSION_POSITIVE, + EXPRESSION_NEGATIVE, + EXPRESSION_ADD, + EXPRESSION_SUBTRACT, + EXPRESSION_MULTIPLY, + EXPRESSION_DIVIDE, + EXPRESSION_POWER, + EXPRESSION_LESS, + EXPRESSION_LESS_EQUAL, + EXPRESSION_GREATER, + EXPRESSION_GREATER_EQUAL, + EXPRESSION_EQUAL, + EXPRESSION_NOT_EQUAL, + EXPRESSION_CONDITIONAL, +}; + /** @brief An operator and its byte offset in the expression. */ struct expression_operator { - char symbol; + enum expression_operator_kind kind; size_t position; + size_t function_index; + size_t branch_end; + size_t resume; + size_t previous_branch; }; -/** @brief Returns precedence; `p` and `n` denote unary plus and minus. */ -static inline int expression_precedence(char symbol) +static inline int expression_precedence(enum expression_operator_kind kind) { - switch (symbol) { - case 'p': case 'n': return 3; - case '*': case '/': return 2; - case '+': case '-': return 1; + switch (kind) { + case EXPRESSION_POWER: return 7; + case EXPRESSION_POSITIVE: case EXPRESSION_NEGATIVE: return 6; + case EXPRESSION_MULTIPLY: case EXPRESSION_DIVIDE: return 5; + case EXPRESSION_ADD: case EXPRESSION_SUBTRACT: return 4; + case EXPRESSION_LESS: case EXPRESSION_LESS_EQUAL: + case EXPRESSION_GREATER: case EXPRESSION_GREATER_EQUAL: return 3; + case EXPRESSION_EQUAL: case EXPRESSION_NOT_EQUAL: return 2; default: return 0; } } @@ -169,29 +304,75 @@ static inline bool expression_apply(stack *values, stack *operators, { struct expression_operator op = *stack_top_as(operators, struct expression_operator); stack_pop(operators); - bool unary = op.symbol == 'p' || op.symbol == 'n'; + bool unary = op.kind == EXPRESSION_POSITIVE || op.kind == EXPRESSION_NEGATIVE; if (stack_size(values) < (unary ? 1u : 2u)) { expression_error(error, EXPRESSION_SYNTAX_ERROR, op.position); return false; } if (unary) { - if (op.symbol == 'n') *stack_top_as(values, double) *= -1; + if (op.kind == EXPRESSION_NEGATIVE) *stack_top_as(values, double) *= -1; return true; } double rhs = *stack_top_as(values, double); stack_pop(values); double *lhs = stack_top_as(values, double); - switch (op.symbol) { - case '+': *lhs += rhs; break; - case '-': *lhs -= rhs; break; - case '*': *lhs *= rhs; break; - case '/': + switch (op.kind) { + case EXPRESSION_ADD: *lhs += rhs; break; + case EXPRESSION_SUBTRACT: *lhs -= rhs; break; + case EXPRESSION_MULTIPLY: *lhs *= rhs; break; + case EXPRESSION_DIVIDE: if (rhs == 0.0) { expression_error(error, EXPRESSION_DIVISION_BY_ZERO, op.position); return false; } *lhs /= rhs; break; + case EXPRESSION_POWER: *lhs = pow(*lhs, rhs); break; + case EXPRESSION_LESS: *lhs = *lhs < rhs; break; + case EXPRESSION_LESS_EQUAL: *lhs = *lhs <= rhs; break; + case EXPRESSION_GREATER: *lhs = *lhs > rhs; break; + case EXPRESSION_GREATER_EQUAL: *lhs = *lhs >= rhs; break; + case EXPRESSION_EQUAL: *lhs = *lhs == rhs; break; + case EXPRESSION_NOT_EQUAL: *lhs = *lhs != rhs; break; + default: break; + } + return true; +} + +/* Find the matching colon and the end of the false branch without executing it. */ +static inline bool expression_branch_bounds(const char *text, size_t question, + size_t *colon, size_t *end, + struct expression_error *error) +{ + size_t parentheses = 0; + size_t questions = 0; + size_t position = question + 1; + *colon = SIZE_MAX; + for (; text[position]; ++position) { + char c = text[position]; + if (c == '(') { + ++parentheses; + } else if (c == ')') { + if (parentheses == 0) break; + --parentheses; + } else if (parentheses == 0 && c == '?') { + ++questions; + } else if (parentheses == 0 && c == ':') { + if (questions != 0) --questions; + else if (*colon == SIZE_MAX) *colon = position; + else break; + } + } + *end = position; + if (parentheses != 0) { + expression_error(error, EXPRESSION_PAREN_ERROR, position); + return false; + } + if (*colon == SIZE_MAX || questions != 0 || + expression_skip_space(text, question + 1) == *colon || + expression_skip_space(text, *colon + 1) == *end) { + expression_error(error, EXPRESSION_SYNTAX_ERROR, question); + return false; } return true; } @@ -205,49 +386,85 @@ static inline bool expression_apply(stack *values, stack *operators, * @return `true` on success; otherwise `false`. * @pre `ctx` is initialized; `text` and `result` are valid pointers. */ -static inline bool expression_eval(const struct expression_context *ctx, - const char *text, - double *result, - struct expression_error *error) +static inline bool __expression_eval(const struct expression_context *ctx, + const char *text, + size_t current_function, + double argument, + double *result, + struct expression_error *error) { STACK(values, double); STACK(operators, struct expression_operator); bool expect_operand = true; bool success = false; size_t position = 0; + size_t branch = SIZE_MAX; - while (text[position]) { - unsigned char c = (unsigned char)text[position]; - if (isspace(c)) { - ++position; - continue; + for (;;) { + position = expression_skip_space(text, position); + if (branch != SIZE_MAX) { + struct expression_operator boundary = + vec_at_as(&operators.container, struct expression_operator, branch); + if (position == boundary.branch_end) { + if (expect_operand) goto syntax_error; + while (stack_size(&operators) > branch + 1) { + if (!expression_apply(&values, &operators, error)) goto done; + } + stack_pop(&operators); + branch = boundary.previous_branch; + position = boundary.resume; + continue; + } } + unsigned char c = (unsigned char)text[position]; + if (c == '\0') break; if (isdigit(c) || c == '.' || expression_name_start(c)) { if (!expect_operand) goto syntax_error; double value; if (expression_name_start(c)) { - size_t start = position++; - while (expression_name_char((unsigned char)text[position])) ++position; - size_t length = position - start; - if (length >= EXPRESSION_NAME_MAX) { - position = start; - goto syntax_error; - } - const struct expression_variable *variable = NULL; - for (size_t i = 0; i < ctx->variables.size; ++i) { - const struct expression_variable *v = vec_at_const(&ctx->variables, i); - if (strlen(v->name) == length && - strncmp(v->name, text + start, length) == 0) { - variable = v; - break; + size_t start = position; + char name[EXPRESSION_NAME_MAX]; + if (!expression_read_name(text, &position, name)) goto syntax_error; + size_t next = expression_skip_space(text, position); + if (text[next] == '(') { + size_t index = expression_find_function(ctx, name, current_function); + if (index == SIZE_MAX) { + expression_error(error, EXPRESSION_UNKNOWN_FUNCTION, start); + goto done; } + struct expression_operator call = { + .kind = EXPRESSION_OPEN, + .position = next, + .function_index = index, + }; + if (!stack_push(&operators, &call)) goto syntax_error; + position = next + 1; + continue; } - if (!variable || !variable->assigned) { - expression_error(error, variable ? EXPRESSION_UNSET_VARIABLE - : EXPRESSION_UNKNOWN_VARIABLE, start); - goto done; + if (current_function != SIZE_MAX) { + const struct expression_function *function = + vec_at_const(&ctx->functions, current_function); + if (strcmp(function->parameter, name) != 0) { + expression_error(error, EXPRESSION_UNKNOWN_VARIABLE, start); + goto done; + } + value = argument; + } else { + const struct expression_variable *variable = NULL; + for (size_t i = 0; i < ctx->variables.size; ++i) { + const struct expression_variable *v = vec_at_const(&ctx->variables, i); + if (strcmp(v->name, name) == 0) { + variable = v; + break; + } + } + if (!variable || !variable->assigned) { + expression_error(error, variable ? EXPRESSION_UNSET_VARIABLE + : EXPRESSION_UNKNOWN_VARIABLE, start); + goto done; + } + value = variable->value; } - value = variable->value; } else { char *end; errno = 0; @@ -259,41 +476,98 @@ static inline bool expression_eval(const struct expression_context *ctx, expect_operand = false; continue; } - struct expression_operator op = { .symbol = (char)c, .position = position }; + struct expression_operator op = { + .position = position, + .function_index = SIZE_MAX, + }; + size_t length = 1; if (c == '(') { if (!expect_operand) goto syntax_error; + op.kind = EXPRESSION_OPEN; } else if (c == ')') { if (expect_operand) goto syntax_error; while (!stack_empty(&operators) && - stack_top_as(&operators, struct expression_operator)->symbol != '(') { + stack_top_as(&operators, struct expression_operator)->kind != EXPRESSION_OPEN) { if (!expression_apply(&values, &operators, error)) goto done; } if (stack_empty(&operators)) goto paren_error; + struct expression_operator open = + *stack_top_as(&operators, struct expression_operator); stack_pop(&operators); + if (open.function_index != SIZE_MAX) { + const struct expression_function *function = + vec_at_const(&ctx->functions, open.function_index); + double value; + if (!__expression_eval(ctx, function->body, open.function_index, + *stack_top_as(&values, double), &value, error)) goto done; + *stack_top_as(&values, double) = value; + } ++position; continue; - } else if (c == '+' || c == '-' || c == '*' || c == '/') { + } else if (c == '?') { + if (expect_operand) goto syntax_error; + while (!stack_empty(&operators) && + expression_precedence(stack_top_as(&operators, + struct expression_operator)->kind) != 0) { + if (!expression_apply(&values, &operators, error)) goto done; + } + size_t colon, end; + if (!expression_branch_bounds(text, position, &colon, &end, error)) goto done; + bool condition = *stack_top_as(&values, double) != 0.0; + stack_pop(&values); + op.kind = EXPRESSION_CONDITIONAL; + op.branch_end = condition ? colon : end; + op.resume = end; + op.previous_branch = branch; + size_t index = stack_size(&operators); + if (!stack_push(&operators, &op)) goto syntax_error; + branch = index; + position = condition ? position + 1 : colon + 1; + expect_operand = true; + continue; + } else { + switch (c) { + case '+': op.kind = EXPRESSION_ADD; break; + case '-': op.kind = EXPRESSION_SUBTRACT; break; + case '*': op.kind = EXPRESSION_MULTIPLY; break; + case '/': op.kind = EXPRESSION_DIVIDE; break; + case '^': op.kind = EXPRESSION_POWER; break; + case '<': + op.kind = text[position + 1] == '=' ? EXPRESSION_LESS_EQUAL : EXPRESSION_LESS; + length = text[position + 1] == '=' ? 2 : 1; + break; + case '>': + op.kind = text[position + 1] == '=' ? EXPRESSION_GREATER_EQUAL : EXPRESSION_GREATER; + length = text[position + 1] == '=' ? 2 : 1; + break; + case '=': case '!': + if (text[position + 1] != '=') goto syntax_error; + op.kind = c == '=' ? EXPRESSION_EQUAL : EXPRESSION_NOT_EQUAL; + length = 2; + break; + default: goto syntax_error; + } if (expect_operand) { if (c != '+' && c != '-') goto syntax_error; - op.symbol = c == '+' ? 'p' : 'n'; + op.kind = c == '+' ? EXPRESSION_POSITIVE : EXPRESSION_NEGATIVE; } else { - while (!stack_empty(&operators) && - expression_precedence(stack_top_as(&operators, - struct expression_operator)->symbol) >= expression_precedence(op.symbol)) { + while (!stack_empty(&operators)) { + int top = expression_precedence(stack_top_as(&operators, + struct expression_operator)->kind); + int current = expression_precedence(op.kind); + if (top < current || (top == current && op.kind == EXPRESSION_POWER)) break; if (!expression_apply(&values, &operators, error)) goto done; } } - } else { - goto syntax_error; } if (!stack_push(&operators, &op)) goto syntax_error; expect_operand = true; - ++position; + position += length; } if (expect_operand) goto syntax_error; while (!stack_empty(&operators)) { struct expression_operator op = *stack_top_as(&operators, struct expression_operator); - if (op.symbol == '(') { + if (op.kind == EXPRESSION_OPEN) { position = op.position; goto paren_error; } @@ -316,4 +590,22 @@ done: return success; } +static inline bool expression_eval(const struct expression_context *ctx, + const char *text, + double *result, + struct expression_error *error) +{ + return __expression_eval(ctx, text, SIZE_MAX, 0.0, result, error); +} + +static inline bool expression_call(const struct expression_context *ctx, + size_t function_index, double argument, + double *result, + struct expression_error *error) +{ + const struct expression_function *function = + vec_at_const(&ctx->functions, function_index); + return __expression_eval(ctx, function->body, function_index, argument, result, error); +} + #endif /* EXPRESSION_H */ |
