#include #include #include #include #include #include "eval.h" #define DEPTH_MAX 32 #define PARAMETERS_MAX 8 static int evaluate_type(struct Machine* machine, const struct Node* node, struct Type* out); static int evaluate_value(struct Machine* machine, const struct Node* node, int64_t* out); static int evaluate_condition(struct Machine* machine, const struct Node* node, bool* out); void machine_init(struct Machine* machine) { memset(machine, 0, sizeof *machine); } static struct Variable* find_variable(struct Machine* machine, const char* name) { for (size_t index = 0; index < VARIABLES_MAX; index++) { struct Variable* variable = &machine->variables[index]; if (variable->declared && strcmp(variable->name, name) == 0) return variable; } return NULL; } static bool bind_variable(struct Machine* machine, const char* name, int64_t value) { for (size_t index = 0; index < VARIABLES_MAX; index++) { struct Variable* variable = &machine->variables[index]; if (variable->declared) continue; (void)snprintf(variable->name, sizeof variable->name, "%s", name); variable->value = value; variable->declared = true; return true; } return false; } static struct Function* find_function(struct Machine* machine, const char* name) { for (size_t index = 0; index < FUNCTIONS_MAX; index++) { struct Function* function = &machine->functions[index]; if (function->defined && strcmp(function->name, name) == 0) return function; } return NULL; } static int complain(const struct Node* node, const char* message) { (void)fprintf(stderr, "line %d: %s\n", node->line, message); return 1; } static int evaluate_operands(struct Machine* machine, const struct Node* node, int64_t* left, int64_t* right) { if (evaluate_value(machine, node->first, left)) return 1; return evaluate_value(machine, node->second, right); } static int evaluate_value(struct Machine* machine, const struct Node* node, int64_t* out) { int64_t left = 0; int64_t right = 0; struct Type type; struct Variable* variable = NULL; bool truth = false; switch (node->kind) { case VALUE_DIFFERENCE: *out = (int64_t)node->first->character - (int64_t)node->second->character; return 0; case VALUE_SUM: if (evaluate_operands(machine, node, &left, &right)) return 1; *out = left + right; return 0; case VALUE_PRODUCT: if (evaluate_operands(machine, node, &left, &right)) return 1; *out = left * right; return 0; case VALUE_QUOTIENT: if (evaluate_operands(machine, node, &left, &right)) return 1; if (right == 0) return complain(node, "division by zero"); *out = left / right; return 0; case VALUE_EXTENT: case VALUE_TUPLE_SIZE: if (evaluate_type(machine, node->first, &type)) return 1; *out = type.kind == KIND_ARRAY ? type.number : 0; return 0; case VALUE_INTEGRAL_CONSTANT: return evaluate_value(machine, node->first, out); case VALUE_VARIABLE: variable = find_variable(machine, node->name); if (variable == NULL) return complain(node, "that name was never declared"); *out = variable->value; return 0; case VALUE_DECLVAL: if (evaluate_type(machine, node->first, &type)) return 1; if (type.kind != KIND_VALUE) return complain(node, "declval of a type with no value in it, what did you expect"); *out = type.number; return 0; case VALUE_CONDITIONAL: if (evaluate_condition(machine, node->first, &truth)) return 1; return evaluate_value(machine, truth ? node->second : node->third, out); default: break; } assert(!"the parser handed a non-value where a value goes"); return 1; } static bool render(const struct Type* type, char* buffer, size_t size) { int written = 0; switch (type->kind) { case KIND_VOID: written = snprintf(buffer, size, "%s", ""); break; case KIND_INT32: written = snprintf(buffer, size, "int32_t"); break; case KIND_BYTE: written = snprintf(buffer, size, "byte"); break; case KIND_SIZE: written = snprintf(buffer, size, "size_t"); break; case KIND_ARRAY: written = snprintf(buffer, size, "array[%" PRId64 "]", type->number); break; case KIND_CHAR: written = snprintf(buffer, size, "char"); break; case KIND_CHAR_CONSTANT: written = snprintf(buffer, size, "%c", (char)type->number); break; case KIND_VALUE: written = snprintf(buffer, size, "%" PRId64, type->number); break; case KIND_TUPLE: written = snprintf(buffer, size, "%s", type->text); break; } return written < 0 || (size_t)written >= size; } static int evaluate_tuple(struct Machine* machine, const struct Node* node, struct Type* out) { out->kind = KIND_TUPLE; size_t used = 0; for (const struct Node* element = node->first; element != NULL; element = element->next) { struct Type inner; if (evaluate_type(machine, element, &inner)) return 1; if (render(&inner, out->text + used, sizeof out->text - used)) return complain(node, "that tuple is too much to say in one breath"); used = strlen(out->text); } return 0; } static int invoke(struct Machine* machine, const struct Node* node, struct Type* out) { struct Function* function = find_function(machine, node->name); if (function == NULL) return complain(node, "no such function, define it first, like everything else"); if (machine->depth >= DEPTH_MAX) return complain(node, "too deep, this was never meant to scale"); struct Type arguments[PARAMETERS_MAX]; size_t count = 0; const struct Node* parameter = function->node->first; const struct Node* argument = node->first; while (parameter != NULL && argument != NULL) { if (count == PARAMETERS_MAX) return complain(node, "too many arguments, nobody needs that many"); if (evaluate_type(machine, argument, &arguments[count])) return 1; if (arguments[count].kind != KIND_VALUE) return complain(argument, "an argument carries a value, decltype it"); count++; parameter = parameter->next; argument = argument->next; } if (parameter != NULL || argument != NULL) return complain(node, "wrong number of arguments, count them again"); struct Variable saved[VARIABLES_MAX]; memcpy(saved, machine->variables, sizeof saved); memset(machine->variables, 0, sizeof machine->variables); parameter = function->node->first; for (size_t index = 0; index < count; index++) { (void)bind_variable(machine, parameter->name, arguments[index].number); parameter = parameter->next; } machine->depth++; enum RunStatus status = execute(machine, function->node->body); machine->depth--; memcpy(machine->variables, saved, sizeof saved); if (status == RUN_FAILED) return 1; if (status == RUN_RETURNED) *out = machine->returned; else { out->kind = KIND_VOID; out->number = 0; out->text[0] = '\0'; } return 0; } static int evaluate_type(struct Machine* machine, const struct Node* node, struct Type* out) { out->kind = KIND_VOID; out->number = 0; out->text[0] = '\0'; struct Type element; bool truth = false; switch (node->kind) { case TYPE_VOID: return 0; case TYPE_INT32: out->kind = KIND_INT32; return 0; case TYPE_BYTE: out->kind = KIND_BYTE; return 0; case TYPE_SIZE: out->kind = KIND_SIZE; return 0; case TYPE_CHAR: out->kind = KIND_CHAR; return 0; case TYPE_TUPLE: return evaluate_tuple(machine, node, out); case TYPE_INVOKE: return invoke(machine, node, out); case TYPE_CHAR_CONSTANT: if (evaluate_value(machine, node->first, &out->number)) return 1; out->kind = KIND_CHAR_CONSTANT; return 0; case TYPE_REMOVE_CV: return evaluate_type(machine, node->first, out); case TYPE_ARRAY: if (evaluate_type(machine, node->first, &element) || evaluate_value(machine, node->second, &out->number)) return 1; out->kind = KIND_ARRAY; return 0; case TYPE_CONDITIONAL: if (evaluate_condition(machine, node->first, &truth)) return 1; return evaluate_type(machine, truth ? node->second : node->third, out); case TYPE_DECLTYPE: if (evaluate_value(machine, node->first, &out->number)) return 1; out->kind = KIND_VALUE; return 0; default: break; } assert(!"the parser handed a non-type where a type goes"); return 1; } static bool same_type(const struct Type* left, const struct Type* right) { return left->kind == right->kind && left->number == right->number && strcmp(left->text, right->text) == 0; } static int evaluate_condition(struct Machine* machine, const struct Node* node, bool* out) { int64_t left = 0; int64_t right = 0; struct Type left_type; struct Type right_type; *out = false; switch (node->kind) { case COND_TRUE: *out = true; return 0; case COND_FALSE: return 0; case COND_IS_SAME: if (evaluate_type(machine, node->first, &left_type) || evaluate_type(machine, node->second, &right_type)) return 1; *out = same_type(&left_type, &right_type); return 0; case COND_EQUAL: if (evaluate_operands(machine, node, &left, &right)) return 1; *out = left == right; return 0; case COND_LESS: if (evaluate_operands(machine, node, &left, &right)) return 1; *out = left < right; return 0; default: break; } assert(!"the parser handed a non-condition where a condition goes"); return 1; } static enum RunStatus declare(struct Machine* machine, const struct Node* node) { if (find_variable(machine, node->name) != NULL) { (void)complain(node, "already declared, and there is no changing that"); return RUN_FAILED; } struct Type type; if (evaluate_type(machine, node->first, &type)) return RUN_FAILED; if (type.kind != KIND_VALUE) { (void)complain(node, "that type carries no value, decltype something"); return RUN_FAILED; } if (!bind_variable(machine, node->name, type.number)) { (void)complain(node, "too many variables, this was never meant to scale"); return RUN_FAILED; } return RUN_DONE; } static enum RunStatus undeclare(struct Machine* machine, const struct Node* node) { struct Variable* variable = find_variable(machine, node->name); if (variable == NULL) { (void)complain(node, "cannot undeclare what was never declared"); return RUN_FAILED; } variable->declared = false; return RUN_DONE; } static enum RunStatus emit(struct Machine* machine, const struct Node* node) { struct Type type; if (evaluate_type(machine, node->first, &type)) return RUN_FAILED; if (type.kind == KIND_VOID) return RUN_DONE; char text[TEXT_LENGTH_MAX + 1]; if (render(&type, text, sizeof text)) { (void)complain(node, "that is too much to say in one breath"); return RUN_FAILED; } (void)printf("%s\n", text); return RUN_DONE; } static enum RunStatus enable_if(struct Machine* machine, const struct Node* node) { bool truth = false; if (evaluate_condition(machine, node->first, &truth)) return RUN_FAILED; if (!truth) return RUN_DONE; return execute(machine, node->body); } static enum RunStatus repeat(struct Machine* machine, const struct Node* node) { int64_t count = 0; if (evaluate_value(machine, node->first, &count)) return RUN_FAILED; for (int64_t index = 0; index < count; index++) { enum RunStatus status = execute(machine, node->body); if (status != RUN_DONE) return status; } return RUN_DONE; } static enum RunStatus static_assert_statement(struct Machine* machine, const struct Node* node) { bool truth = false; if (evaluate_condition(machine, node->first, &truth)) return RUN_FAILED; if (!truth) { (void)complain(node, "static assertion failed, at runtime, which is the best time"); return RUN_FAILED; } return RUN_DONE; } static enum RunStatus define(struct Machine* machine, const struct Node* node) { if (find_function(machine, node->name) != NULL) { (void)complain(node, "already defined, functions are forever"); return RUN_FAILED; } for (size_t index = 0; index < FUNCTIONS_MAX; index++) { struct Function* function = &machine->functions[index]; if (function->defined) continue; (void)snprintf(function->name, sizeof function->name, "%s", node->name); function->node = node; function->defined = true; return RUN_DONE; } (void)complain(node, "too many functions, this was never meant to scale"); return RUN_FAILED; } static enum RunStatus return_statement(struct Machine* machine, const struct Node* node) { if (machine->depth == 0) { (void)complain(node, "return from what, exactly"); return RUN_FAILED; } if (evaluate_type(machine, node->first, &machine->returned)) return RUN_FAILED; return RUN_RETURNED; } static enum RunStatus execute_one(struct Machine* machine, const struct Node* node) { switch (node->kind) { case STMT_DECLARE: return declare(machine, node); case STMT_UNDECLARE: return undeclare(machine, node); case STMT_ENABLE_IF: return enable_if(machine, node); case STMT_REPEAT: return repeat(machine, node); case STMT_EMIT: return emit(machine, node); case STMT_STATIC_ASSERT: return static_assert_statement(machine, node); case STMT_DEFINE: return define(machine, node); case STMT_RETURN: return return_statement(machine, node); default: break; } assert(!"the parser handed a non-statement where a statement goes"); return RUN_FAILED; } enum RunStatus execute(struct Machine* machine, const struct Node* statement) { while (statement != NULL) { enum RunStatus status = execute_one(machine, statement); if (status != RUN_DONE) return status; statement = statement->next; } return RUN_DONE; }