diff options
| author | hachem <im@hachem.wtf> | 2026-09-19 18:53:06 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-09-19 18:53:06 +0200 |
| commit | c6aab10e74d58fbcad723c5f074a0bb51e9f2283 (patch) | |
| tree | 89d66cc61c99c4cc4b7824e3a3a2fabf3d5a674e | |
| parent | cd2a86435e5e413630e0ab0caa48d900e49ea275 (diff) | |
feat: add functions with defines params and return
| -rw-r--r-- | ast.h | 10 | ||||
| -rw-r--r-- | eval.c | 121 | ||||
| -rw-r--r-- | eval.h | 15 | ||||
| -rw-r--r-- | examples/functions.sfinae | 55 | ||||
| -rw-r--r-- | sfinae.l | 5 | ||||
| -rw-r--r-- | sfinae.y | 33 |
6 files changed, 230 insertions, 9 deletions
@@ -9,6 +9,8 @@ enum NodeKind { NODE_CHAR, + NODE_PARAMETER, + TYPE_VOID, TYPE_INT32, TYPE_BYTE, @@ -20,6 +22,8 @@ enum NodeKind TYPE_CHAR, TYPE_CHAR_CONSTANT, TYPE_TUPLE, + TYPE_INVOKE, + VALUE_DIFFERENCE, VALUE_SUM, VALUE_PRODUCT, @@ -29,17 +33,21 @@ enum NodeKind VALUE_INTEGRAL_CONSTANT, VALUE_CONDITIONAL, VALUE_VARIABLE, + COND_TRUE, COND_FALSE, COND_IS_SAME, COND_EQUAL, COND_LESS, + STMT_DECLARE, STMT_UNDECLARE, STMT_ENABLE_IF, STMT_REPEAT, STMT_EMIT, - STMT_STATIC_ASSERT + STMT_STATIC_ASSERT, + STMT_DEFINE, + STMT_RETURN }; struct Name @@ -6,6 +6,9 @@ #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); @@ -44,6 +47,18 @@ static bool bind_variable(struct Machine* machine, const char* name, int64_t val 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); @@ -118,7 +133,6 @@ static int evaluate_value(struct Machine* machine, const struct Node* node, int6 return 1; } -// writes what a type looks like when emitted. true when it does not fit static bool render(const struct Type* type, char* buffer, size_t size) { int written = 0; @@ -140,7 +154,6 @@ static bool render(const struct Type* type, char* buffer, size_t size) return written < 0 || (size_t)written >= size; } -// a tuple says everything its elements say, in order static int evaluate_tuple(struct Machine* machine, const struct Node* node, struct Type* out) { out->kind = KIND_TUPLE; @@ -160,6 +173,67 @@ static int evaluate_tuple(struct Machine* machine, const struct Node* node, stru 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; @@ -175,6 +249,7 @@ static int evaluate_type(struct Machine* machine, const struct Node* node, struc 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; @@ -293,7 +368,6 @@ static enum RunStatus emit(struct Machine* machine, const struct Node* node) if (evaluate_type(machine, node->first, &type)) return RUN_FAILED; - // void says nothing, not even a newline. everything else says its piece and ends the line if (type.kind == KIND_VOID) return RUN_DONE; @@ -320,7 +394,6 @@ static enum RunStatus enable_if(struct Machine* machine, const struct Node* node return execute(machine, node->body); } -// runs the body count times. no index, no condition, no break. you wanted a loop static enum RunStatus repeat(struct Machine* machine, const struct Node* node) { int64_t count = 0; @@ -352,6 +425,44 @@ static enum RunStatus static_assert_statement(struct Machine* machine, const str 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) @@ -362,6 +473,8 @@ static enum RunStatus execute_one(struct Machine* machine, const struct Node* no 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; } @@ -6,6 +6,7 @@ #include <stdint.h> #define VARIABLES_MAX 64 +#define FUNCTIONS_MAX 32 // the longest thing a single type can say when emitted #define TEXT_LENGTH_MAX 255 @@ -23,7 +24,6 @@ enum TypeKind KIND_VALUE }; -// a tuple keeps what it will say, already said, so nothing inside it can go out of scope struct Type { enum TypeKind kind; @@ -38,15 +38,26 @@ struct Variable bool declared; }; +struct Function +{ + char name[NAME_LENGTH_MAX + 1]; + const struct Node* node; + bool defined; +}; + struct Machine { struct Variable variables[VARIABLES_MAX]; + struct Function functions[FUNCTIONS_MAX]; + struct Type returned; + int depth; }; enum RunStatus { RUN_DONE, - RUN_FAILED + RUN_FAILED, + RUN_RETURNED }; void machine_init(struct Machine* machine); diff --git a/examples/functions.sfinae b/examples/functions.sfinae new file mode 100644 index 0000000..d73261f --- /dev/null +++ b/examples/functions.sfinae @@ -0,0 +1,55 @@ +// a function takes types and gives back a type. calling one is itself a type +// it sees nothing but its parameters, and every argument must be a ::decltype so it carries a value + +::define< + ::name<'s', 'q', 'u', 'a', 'r', 'e'>, + ::parameters<::name<'x'>>, + ::sequence< + ::return<::decltype(::product<::value_of<::name<'x'>>, ::value_of<::name<'x'>>>)>::commit + > +>::commit; + +::emit< + ::invoke_result_t< + ::name<'s', 'q', 'u', 'a', 'r', 'e'>, + ::arguments<::decltype(::difference<'F', 'A'>)> + > +>::commit; + +// a function can return anything a type can be, which includes a tuple of characters +::define< + ::name<'g', 'r', 'e', 'e', 't'>, + ::parameters<>, + ::sequence< + ::return< + ::tuple< + ::integral_constant<::char, ::difference<'h', '\0'>>, + ::integral_constant<::char, ::difference<'e', '\0'>>, + ::integral_constant<::char, ::difference<'y', '\0'>> + > + >::commit + > +>::commit; + +::emit<::invoke_result_t<::name<'g', 'r', 'e', 'e', 't'>, ::arguments<>>>::commit; + +// a function that never returns gives back ::void, and emitting ::void prints nothing +::define<::name<'n', 'o', 'p'>, ::parameters<>, ::sequence<>>::commit; +::emit<::invoke_result_t<::name<'n', 'o', 'p'>, ::arguments<>>>::commit; + +// a return inside a branch still leaves the function +::define< + ::name<'s', 'i', 'g', 'n'>, + ::parameters<::name<'n'>>, + ::sequence< + ::enable_if_t< + ::less<::value_of<::name<'n'>>, ::difference<'A', 'A'>>::value, + ::sequence<::return<::decltype(::difference<'A', 'B'>)>::commit> + >::commit, + ::return<::decltype(::difference<'B', 'A'>)>::commit + > +>::commit; + +::emit< + ::invoke_result_t<::name<'s', 'i', 'g', 'n'>, ::arguments<::decltype(::difference<'A', 'Z'>)>> +>::commit; @@ -39,6 +39,11 @@ static int fail(const char* message) "undeclare" return TOK_UNDECLARE; "enable_if_t" return TOK_ENABLE_IF; "repeat" return TOK_REPEAT; +"define" return TOK_DEFINE; +"parameters" return TOK_PARAMETERS; +"return" return TOK_RETURN; +"invoke_result_t" return TOK_INVOKE_RESULT; +"arguments" return TOK_ARGUMENTS; "sequence" return TOK_SEQUENCE; "name" return TOK_NAME; "value_of" return TOK_VALUE_OF; @@ -25,6 +25,11 @@ void yyerror(const char* message); %token TOK_UNDECLARE %token TOK_ENABLE_IF %token TOK_REPEAT +%token TOK_DEFINE +%token TOK_PARAMETERS +%token TOK_RETURN +%token TOK_INVOKE_RESULT +%token TOK_ARGUMENTS %token TOK_SEQUENCE %token TOK_NAME %token TOK_VALUE_OF @@ -55,7 +60,8 @@ void yyerror(const char* message); %token <character> TOK_CHAR %type <name> name char_list -%type <node> statements statement sequence sequence_items type type_list value condition +%type <node> statements statement sequence sequence_items parameters name_list +%type <node> type type_list value condition %% @@ -84,9 +90,15 @@ statement { $$ = node_block(STMT_ENABLE_IF, $4, $6); } | TOK_COLONCOLON TOK_REPEAT '<' value ',' sequence '>' TOK_COLONCOLON TOK_COMMIT { $$ = node_block(STMT_REPEAT, $4, $6); } + | TOK_COLONCOLON TOK_DEFINE '<' name ',' parameters ',' sequence '>' TOK_COLONCOLON TOK_COMMIT + { + $$ = node_named(STMT_DEFINE, &$4, $6); + $$->body = $8; + } + | TOK_COLONCOLON TOK_RETURN '<' type '>' TOK_COLONCOLON TOK_COMMIT + { $$ = node_unary(STMT_RETURN, $4); } ; -// the statements inside are committed one by one, without the semicolons sequence : TOK_COLONCOLON TOK_SEQUENCE '<' sequence_items '>' { $$ = $4; } @@ -101,6 +113,20 @@ sequence_items { $$ = node_append($1, $3); } ; +parameters + : TOK_COLONCOLON TOK_PARAMETERS '<' name_list '>' + { $$ = $4; } + ; + +name_list + : /* nothing */ + { $$ = NULL; } + | name + { $$ = node_named(NODE_PARAMETER, &$1, NULL); } + | name_list ',' name + { $$ = node_append($1, node_named(NODE_PARAMETER, &$3, NULL)); } + ; + name : TOK_COLONCOLON TOK_NAME '<' char_list '>' { $$ = $4; } @@ -146,6 +172,9 @@ type { $$ = node_unary(TYPE_CHAR_CONSTANT, $7); } | TOK_COLONCOLON TOK_TUPLE '<' type_list '>' { $$ = node_unary(TYPE_TUPLE, $4); } + | TOK_COLONCOLON TOK_INVOKE_RESULT '<' name ',' + TOK_COLONCOLON TOK_ARGUMENTS '<' type_list '>' '>' + { $$ = node_named(TYPE_INVOKE, &$4, $9); } ; type_list |
