hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-19 18:53:06 +0200
committerhachem <im@hachem.wtf>2026-09-19 18:53:06 +0200
commitc6aab10e74d58fbcad723c5f074a0bb51e9f2283 (patch)
tree89d66cc61c99c4cc4b7824e3a3a2fabf3d5a674e
parentcd2a86435e5e413630e0ab0caa48d900e49ea275 (diff)
feat: add functions with defines params and return
-rw-r--r--ast.h10
-rw-r--r--eval.c121
-rw-r--r--eval.h15
-rw-r--r--examples/functions.sfinae55
-rw-r--r--sfinae.l5
-rw-r--r--sfinae.y33
6 files changed, 230 insertions, 9 deletions
diff --git a/ast.h b/ast.h
index f45c84a..e8d4c45 100644
--- a/ast.h
+++ b/ast.h
@@ -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
diff --git a/eval.c b/eval.c
index d19d029..c872c90 100644
--- a/eval.c
+++ b/eval.c
@@ -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;
}
diff --git a/eval.h b/eval.h
index c2365cc..924ad92 100644
--- a/eval.h
+++ b/eval.h
@@ -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;
diff --git a/sfinae.l b/sfinae.l
index f2bf473..63314c9 100644
--- a/sfinae.l
+++ b/sfinae.l
@@ -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;
diff --git a/sfinae.y b/sfinae.y
index 9fe994c..4e366a8 100644
--- a/sfinae.y
+++ b/sfinae.y
@@ -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