hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-19 16:20:14 +0200
committerhachem <im@hachem.wtf>2026-09-19 16:20:14 +0200
commitcd2a86435e5e413630e0ab0caa48d900e49ea275 (patch)
tree559a1bfc52a853b61fe07e0b7aa17eca977f5fd2
parent0a9605b6b13f2e066a69662f5802b397f8b89424 (diff)
feat: tuples baby
-rw-r--r--ast.h1
-rw-r--r--eval.c65
-rw-r--r--eval.h6
-rw-r--r--examples/hello.sfinae23
-rw-r--r--sfinae.l1
-rw-r--r--sfinae.y14
6 files changed, 94 insertions, 16 deletions
diff --git a/ast.h b/ast.h
index da409be..f45c84a 100644
--- a/ast.h
+++ b/ast.h
@@ -19,6 +19,7 @@ enum NodeKind
TYPE_DECLTYPE,
TYPE_CHAR,
TYPE_CHAR_CONSTANT,
+ TYPE_TUPLE,
VALUE_DIFFERENCE,
VALUE_SUM,
VALUE_PRODUCT,
diff --git a/eval.c b/eval.c
index 170b122..d19d029 100644
--- a/eval.c
+++ b/eval.c
@@ -118,10 +118,53 @@ 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;
+ 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;
+}
+
+// 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;
+ 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 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)
@@ -131,6 +174,7 @@ static int evaluate_type(struct Machine* machine, const struct Node* node, struc
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_CHAR_CONSTANT:
if (evaluate_value(machine, node->first, &out->number))
return 1;
@@ -162,7 +206,8 @@ static int evaluate_type(struct Machine* machine, const struct Node* node, struc
static bool same_type(const struct Type* left, const struct Type* right)
{
- return left->kind == right->kind && left->number == right->number;
+ 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)
@@ -248,18 +293,18 @@ static enum RunStatus emit(struct Machine* machine, const struct Node* node)
if (evaluate_type(machine, node->first, &type))
return RUN_FAILED;
- switch (type.kind)
+ // void says nothing, not even a newline. everything else says its piece and ends the line
+ if (type.kind == KIND_VOID)
+ return RUN_DONE;
+
+ char text[TEXT_LENGTH_MAX + 1];
+ if (render(&type, text, sizeof text))
{
- case KIND_VOID: break;
- case KIND_INT32: (void)printf("int32_t\n"); break;
- case KIND_BYTE: (void)printf("byte\n"); break;
- case KIND_SIZE: (void)printf("size_t\n"); break;
- case KIND_ARRAY: (void)printf("array[%" PRId64 "]\n", type.number); break;
- case KIND_CHAR: (void)printf("char\n"); break;
- case KIND_CHAR_CONSTANT: (void)printf("%c\n", (char)type.number); break;
- case KIND_VALUE: (void)printf("%" PRId64 "\n", type.number); break;
+ (void)complain(node, "that is too much to say in one breath");
+ return RUN_FAILED;
}
+ (void)printf("%s\n", text);
return RUN_DONE;
}
diff --git a/eval.h b/eval.h
index eb3fe1d..c2365cc 100644
--- a/eval.h
+++ b/eval.h
@@ -7,6 +7,9 @@
#define VARIABLES_MAX 64
+// the longest thing a single type can say when emitted
+#define TEXT_LENGTH_MAX 255
+
enum TypeKind
{
KIND_VOID,
@@ -16,13 +19,16 @@ enum TypeKind
KIND_ARRAY,
KIND_CHAR,
KIND_CHAR_CONSTANT,
+ KIND_TUPLE,
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;
int64_t number;
+ char text[TEXT_LENGTH_MAX + 1];
};
struct Variable
diff --git a/examples/hello.sfinae b/examples/hello.sfinae
index 39f3a62..0154af8 100644
--- a/examples/hello.sfinae
+++ b/examples/hello.sfinae
@@ -1,8 +1,21 @@
-// prints 5
+// hello, world
+// there are no strings. a ::tuple says what its elements say, glued together,
+// and each element is a character type, its own distance from '\0'
+
::emit<
- ::conditional_t<
- ::true_type::value,
- ::decltype(::difference<'F', 'A'>),
- ::void
+ ::tuple<
+ ::integral_constant<::char, ::difference<'H', '\0'>>,
+ ::integral_constant<::char, ::difference<'e', '\0'>>,
+ ::integral_constant<::char, ::difference<'l', '\0'>>,
+ ::integral_constant<::char, ::difference<'l', '\0'>>,
+ ::integral_constant<::char, ::difference<'o', '\0'>>,
+ ::integral_constant<::char, ::difference<',', '\0'>>,
+ ::integral_constant<::char, ::difference<' ', '\0'>>,
+ ::integral_constant<::char, ::difference<'w', '\0'>>,
+ ::integral_constant<::char, ::difference<'o', '\0'>>,
+ ::integral_constant<::char, ::difference<'r', '\0'>>,
+ ::integral_constant<::char, ::difference<'l', '\0'>>,
+ ::integral_constant<::char, ::difference<'d', '\0'>>,
+ ::integral_constant<::char, ::difference<'!', '\0'>>
>
>::commit;
diff --git a/sfinae.l b/sfinae.l
index f5b9cf1..f2bf473 100644
--- a/sfinae.l
+++ b/sfinae.l
@@ -47,6 +47,7 @@ static int fail(const char* message)
"byte" return TOK_BYTE;
"size_t" return TOK_SIZE_T;
"char" return TOK_CHAR_T;
+"tuple" return TOK_TUPLE;
"remove_cv_t" return TOK_REMOVE_CV;
"decltype" return TOK_DECLTYPE;
"difference" return TOK_DIFFERENCE;
diff --git a/sfinae.y b/sfinae.y
index aa7650b..9fe994c 100644
--- a/sfinae.y
+++ b/sfinae.y
@@ -33,6 +33,7 @@ void yyerror(const char* message);
%token TOK_BYTE
%token TOK_SIZE_T
%token TOK_CHAR_T
+%token TOK_TUPLE
%token TOK_REMOVE_CV
%token TOK_DECLTYPE
%token TOK_DIFFERENCE
@@ -54,7 +55,7 @@ void yyerror(const char* message);
%token <character> TOK_CHAR
%type <name> name char_list
-%type <node> statements statement sequence sequence_items type value condition
+%type <node> statements statement sequence sequence_items type type_list value condition
%%
@@ -143,6 +144,17 @@ type
{ $$ = node_leaf(TYPE_CHAR); }
| TOK_COLONCOLON TOK_INTEGRAL_CONSTANT '<' TOK_COLONCOLON TOK_CHAR_T ',' value '>'
{ $$ = node_unary(TYPE_CHAR_CONSTANT, $7); }
+ | TOK_COLONCOLON TOK_TUPLE '<' type_list '>'
+ { $$ = node_unary(TYPE_TUPLE, $4); }
+ ;
+
+type_list
+ : /* nothing */
+ { $$ = NULL; }
+ | type
+ { $$ = $1; }
+ | type_list ',' type
+ { $$ = node_append($1, $3); }
;
value