diff options
| author | hachem <im@hachem.wtf> | 2026-09-19 14:38:55 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-09-19 14:38:55 +0200 |
| commit | b79902cd36b2c5b54bbf0f07078fc528754977b2 (patch) | |
| tree | 0f3baa55b58831c46c7631619f76eef5cb371f71 | |
| parent | 7160bdb390ef1c9bdcfb0c075b33be39d62860f3 (diff) | |
feat: variables and shi
| -rw-r--r-- | ast.c | 24 | ||||
| -rw-r--r-- | ast.h | 19 | ||||
| -rw-r--r-- | eval.c | 140 | ||||
| -rw-r--r-- | eval.h | 18 | ||||
| -rw-r--r-- | examples/xs.sfinae | 54 | ||||
| -rw-r--r-- | main.c | 6 | ||||
| -rw-r--r-- | sfinae.l | 3 | ||||
| -rw-r--r-- | sfinae.y | 35 |
8 files changed, 237 insertions, 62 deletions
@@ -5,6 +5,22 @@ #include <stdlib.h> #include <string.h> +void name_init(struct Name* name) +{ + name->length = 0; + name->text[0] = '\0'; +} + +bool name_push(struct Name* name, char character) +{ + if (name->length == NAME_LENGTH_MAX) + return false; + + name->text[name->length++] = character; + name->text[name->length] = '\0'; + return true; +} + static struct Node* node_new(enum NodeKind kind) { struct Node* node = calloc(1, sizeof *node); @@ -55,6 +71,14 @@ struct Node* node_ternary(enum NodeKind kind, struct Node* first, struct Node* s return node; } +struct Node* node_named(enum NodeKind kind, const struct Name* name, struct Node* first) +{ + struct Node* node = node_new(kind); + (void)snprintf(node->name, sizeof node->name, "%s", name->text); + node->first = first; + return node; +} + struct Node* node_append(struct Node* list, struct Node* node) { struct Node* last = list; @@ -1,5 +1,11 @@ #pragma once +#include <stdbool.h> +#include <stddef.h> + +// names longer than this are refused, nobody needs a longer name +#define NAME_LENGTH_MAX 15 + enum NodeKind { NODE_CHAR, @@ -19,32 +25,45 @@ enum NodeKind VALUE_TUPLE_SIZE, VALUE_INTEGRAL_CONSTANT, VALUE_CONDITIONAL, + VALUE_VARIABLE, COND_TRUE, COND_FALSE, COND_IS_SAME, COND_EQUAL, COND_LESS, + STMT_DECLARE, STMT_EMIT, STMT_STATIC_ASSERT }; +struct Name +{ + char text[NAME_LENGTH_MAX + 1]; + size_t length; +}; + struct Node { enum NodeKind kind; int line; char character; + char name[NAME_LENGTH_MAX + 1]; struct Node* first; struct Node* second; struct Node* third; struct Node* next; }; +void name_init(struct Name* name); +bool name_push(struct Name* name, char character); + struct Node* node_leaf(enum NodeKind kind); struct Node* node_char(char character); struct Node* node_unary(enum NodeKind kind, struct Node* first); struct Node* node_binary(enum NodeKind kind, struct Node* first, struct Node* second); struct Node* node_ternary(enum NodeKind kind, struct Node* first, struct Node* second, struct Node* third); +struct Node* node_named(enum NodeKind kind, const struct Name* name, struct Node* first); struct Node* node_append(struct Node* list, struct Node* node); void node_free(struct Node* node); @@ -2,12 +2,47 @@ #include <inttypes.h> #include <stdbool.h> #include <stdio.h> +#include <string.h> #include "eval.h" -static int evaluate_type(const struct Node* node, struct Type* out); -static int evaluate_value(const struct Node* node, int64_t* out); -static int evaluate_condition(const struct Node* node, bool* out); +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 int complain(const struct Node* node, const char* message) { @@ -15,19 +50,21 @@ static int complain(const struct Node* node, const char* message) return 1; } -static int evaluate_operands(const struct Node* node, int64_t* left, int64_t* right) +static int evaluate_operands(struct Machine* machine, const struct Node* node, int64_t* left, + int64_t* right) { - if (evaluate_value(node->first, left)) + if (evaluate_value(machine, node->first, left)) return 1; - return evaluate_value(node->second, right); + return evaluate_value(machine, node->second, right); } -static int evaluate_value(const struct Node* node, int64_t* out) +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) { @@ -35,19 +72,19 @@ static int evaluate_value(const struct Node* node, int64_t* out) *out = (int64_t)node->first->character - (int64_t)node->second->character; return 0; case VALUE_SUM: - if (evaluate_operands(node, &left, &right)) + if (evaluate_operands(machine, node, &left, &right)) return 1; *out = left + right; return 0; case VALUE_PRODUCT: - if (evaluate_operands(node, &left, &right)) + if (evaluate_operands(machine, node, &left, &right)) return 1; *out = left * right; return 0; case VALUE_QUOTIENT: - if (evaluate_operands(node, &left, &right)) + if (evaluate_operands(machine, node, &left, &right)) return 1; if (right == 0) @@ -57,16 +94,23 @@ static int evaluate_value(const struct Node* node, int64_t* out) return 0; case VALUE_EXTENT: case VALUE_TUPLE_SIZE: - if (evaluate_type(node->first, &type)) + 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(node->first, out); + 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_CONDITIONAL: - if (evaluate_condition(node->first, &truth)) + if (evaluate_condition(machine, node->first, &truth)) return 1; - return evaluate_value(truth ? node->second : node->third, out); + return evaluate_value(machine, truth ? node->second : node->third, out); default: break; } @@ -74,7 +118,7 @@ static int evaluate_value(const struct Node* node, int64_t* out) return 1; } -static int evaluate_type(const struct Node* node, struct Type* out) +static int evaluate_type(struct Machine* machine, const struct Node* node, struct Type* out) { out->kind = KIND_VOID; out->number = 0; @@ -86,18 +130,19 @@ static int evaluate_type(const struct Node* node, struct Type* out) 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_REMOVE_CV: return evaluate_type(node->first, out); + case TYPE_REMOVE_CV: return evaluate_type(machine, node->first, out); case TYPE_ARRAY: - if (evaluate_type(node->first, &element) || evaluate_value(node->second, &out->number)) + 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(node->first, &truth)) + if (evaluate_condition(machine, node->first, &truth)) return 1; - return evaluate_type(truth ? node->second : node->third, out); + return evaluate_type(machine, truth ? node->second : node->third, out); case TYPE_DECLTYPE: - if (evaluate_value(node->first, &out->number)) + if (evaluate_value(machine, node->first, &out->number)) return 1; out->kind = KIND_VALUE; return 0; @@ -113,7 +158,7 @@ static bool same_type(const struct Type* left, const struct Type* right) return left->kind == right->kind && left->number == right->number; } -static int evaluate_condition(const struct Node* node, bool* out) +static int evaluate_condition(struct Machine* machine, const struct Node* node, bool* out) { int64_t left = 0; int64_t right = 0; @@ -125,19 +170,20 @@ static int evaluate_condition(const struct Node* node, bool* out) case COND_TRUE: *out = true; return 0; case COND_FALSE: return 0; case COND_IS_SAME: - if (evaluate_type(node->first, &left_type) || evaluate_type(node->second, &right_type)) + 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(node, &left, &right)) + if (evaluate_operands(machine, node, &left, &right)) return 1; *out = left == right; return 0; case COND_LESS: - if (evaluate_operands(node, &left, &right)) + if (evaluate_operands(machine, node, &left, &right)) return 1; *out = left < right; @@ -149,10 +195,37 @@ static int evaluate_condition(const struct Node* node, bool* out) return 1; } -static enum RunStatus emit(const struct Node* node) +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 emit(struct Machine* machine, const struct Node* node) { struct Type type; - if (evaluate_type(node->first, &type)) + if (evaluate_type(machine, node->first, &type)) return RUN_FAILED; switch (type.kind) @@ -168,10 +241,10 @@ static enum RunStatus emit(const struct Node* node) return RUN_DONE; } -static enum RunStatus static_assert_statement(const struct Node* node) +static enum RunStatus static_assert_statement(struct Machine* machine, const struct Node* node) { bool truth = false; - if (evaluate_condition(node->first, &truth)) + if (evaluate_condition(machine, node->first, &truth)) return RUN_FAILED; if (!truth) @@ -183,12 +256,13 @@ static enum RunStatus static_assert_statement(const struct Node* node) return RUN_DONE; } -static enum RunStatus execute_one(const struct Node* node) +static enum RunStatus execute_one(struct Machine* machine, const struct Node* node) { switch (node->kind) { - case STMT_EMIT: return emit(node); - case STMT_STATIC_ASSERT: return static_assert_statement(node); + case STMT_DECLARE: return declare(machine, node); + case STMT_EMIT: return emit(machine, node); + case STMT_STATIC_ASSERT: return static_assert_statement(machine, node); default: break; } @@ -196,11 +270,11 @@ static enum RunStatus execute_one(const struct Node* node) return RUN_FAILED; } -enum RunStatus execute(const struct Node* statement) +enum RunStatus execute(struct Machine* machine, const struct Node* statement) { while (statement != NULL) { - enum RunStatus status = execute_one(statement); + enum RunStatus status = execute_one(machine, statement); if (status != RUN_DONE) return status; @@ -2,8 +2,11 @@ #include "ast.h" +#include <stdbool.h> #include <stdint.h> +#define VARIABLES_MAX 64 + enum TypeKind { KIND_VOID, @@ -20,10 +23,23 @@ struct Type int64_t number; }; +struct Variable +{ + char name[NAME_LENGTH_MAX + 1]; + int64_t value; + bool declared; +}; + +struct Machine +{ + struct Variable variables[VARIABLES_MAX]; +}; + enum RunStatus { RUN_DONE, RUN_FAILED }; -enum RunStatus execute(const struct Node* statement); +void machine_init(struct Machine* machine); +enum RunStatus execute(struct Machine* machine, const struct Node* statement); diff --git a/examples/xs.sfinae b/examples/xs.sfinae index 7cb7dd9..e140252 100644 --- a/examples/xs.sfinae +++ b/examples/xs.sfinae @@ -1,34 +1,36 @@ -::emit< - ::decltype( - ::extent< - ::array< +::declare< + ::name<'x', 's'>, + ::conditional_t< + ::true_type::value, + ::decltype( + ::extent< ::array< - ::remove_cv_t< - ::int32_t + ::array< + ::remove_cv_t< + ::int32_t + >, + ::integral_constant< + ::size_t, + ::tuple_size< + ::array< + ::byte, + ::difference<'Z', 'F'> + > + >::value + >::value >, ::integral_constant< ::size_t, - ::tuple_size< - ::array< - ::byte, - ::difference<'Z', 'F'> - > - >::value + ::difference<'Z', 'F'> >::value - >, - ::integral_constant< - ::size_t, - ::difference<'Z', 'F'> - >::value - > - >::value - ) + > + >::value + ), + ::void + > >::commit; +::emit<::decltype(::value_of<::name<'x', 's'>>)>::commit; + // an array type says its extent, a bare type says its name -::emit< - ::array< - ::byte, - ::difference<'Z', 'F'> - > ->::commit; +::emit<::array<::byte, ::value_of<::name<'x', 's'>>>>::commit; @@ -13,7 +13,11 @@ static int run_program(void) if (status != 0 || lexer_failed()) status = 1; else - status = execute(program_root) == RUN_FAILED; + { + struct Machine machine; + machine_init(&machine); + status = execute(&machine, program_root) == RUN_FAILED; + } node_free(program_root); program_root = NULL; @@ -35,6 +35,9 @@ static int fail(const char* message) "emit" return TOK_EMIT; "static_assert" return TOK_STATIC_ASSERT; "commit" return TOK_COMMIT; +"declare" return TOK_DECLARE; +"name" return TOK_NAME; +"value_of" return TOK_VALUE_OF; "void" return TOK_VOID; "int32_t" return TOK_INT32; "byte" return TOK_BYTE; @@ -3,6 +3,7 @@ #include "lexer.h" #include <stdio.h> +#include <string.h> struct Node* program_root = NULL; @@ -12,6 +13,7 @@ void yyerror(const char* message); %union { char character; + struct Name name; struct Node* node; } @@ -19,6 +21,9 @@ void yyerror(const char* message); %token TOK_EMIT %token TOK_STATIC_ASSERT %token TOK_COMMIT +%token TOK_DECLARE +%token TOK_NAME +%token TOK_VALUE_OF %token TOK_VOID %token TOK_INT32 %token TOK_BYTE @@ -43,6 +48,7 @@ void yyerror(const char* message); %token TOK_LESS %token <character> TOK_CHAR +%type <name> name char_list %type <node> statements statement type value condition %% @@ -64,6 +70,30 @@ statement { $$ = node_unary(STMT_EMIT, $4); } | TOK_COLONCOLON TOK_STATIC_ASSERT '<' condition '>' TOK_COLONCOLON TOK_COMMIT { $$ = node_unary(STMT_STATIC_ASSERT, $4); } + | TOK_COLONCOLON TOK_DECLARE '<' name ',' type '>' TOK_COLONCOLON TOK_COMMIT + { $$ = node_named(STMT_DECLARE, &$4, $6); } + ; + +name + : TOK_COLONCOLON TOK_NAME '<' char_list '>' + { $$ = $4; } + ; + +char_list + : TOK_CHAR + { + name_init(&$$); + (void)name_push(&$$, $1); + } + | char_list ',' TOK_CHAR + { + $$ = $1; + if (!name_push(&$$, $3)) + { + yyerror("that name is too long, who are you"); + YYERROR; + } + } ; type @@ -94,6 +124,8 @@ value { $$ = node_binary(VALUE_PRODUCT, $4, $6); } | TOK_COLONCOLON TOK_QUOTIENT '<' value ',' value '>' { $$ = node_binary(VALUE_QUOTIENT, $4, $6); } + | TOK_COLONCOLON TOK_VALUE_OF '<' name '>' + { $$ = node_named(VALUE_VARIABLE, &$4, NULL); } | TOK_COLONCOLON TOK_EXTENT '<' type '>' TOK_COLONCOLON TOK_VALUE { $$ = node_unary(VALUE_EXTENT, $4); } | TOK_COLONCOLON TOK_TUPLE_SIZE '<' type '>' TOK_COLONCOLON TOK_VALUE @@ -124,6 +156,7 @@ void yyerror(const char* message) { if (!lexer_failed()) { - (void)fprintf(stderr, "line %d: %s, did you forget to commit\n", yylineno, message); + (void)fprintf(stderr, "line %d: %s%s\n", yylineno, message, + strcmp(message, "syntax error") == 0 ? ", did you forget to commit" : ""); } } |
