hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-19 13:47:08 +0200
committerhachem <im@hachem.wtf>2026-09-19 13:47:08 +0200
commitd0ca03c84bcad3dc42fd173b0baf3a15e9b52f9b (patch)
tree89fa494129849e23b1d760466e87ef6046cbfcb4
parenta79c97e04562b171780a24cba2cb1a71acfa4bbb (diff)
feat: add conditionals
-rw-r--r--ast.c11
-rw-r--r--ast.h10
-rw-r--r--eval.c58
-rw-r--r--examples/conditions.sfinae35
-rw-r--r--examples/hello.sfinae8
-rw-r--r--examples/xs.sfinae2
-rw-r--r--sfinae.l7
-rw-r--r--sfinae.y26
8 files changed, 149 insertions, 8 deletions
diff --git a/ast.c b/ast.c
index 25cecb7..e420ba3 100644
--- a/ast.c
+++ b/ast.c
@@ -45,6 +45,16 @@ struct Node* node_binary(enum NodeKind kind, struct Node* first, struct Node* se
return node;
}
+struct Node* node_ternary(enum NodeKind kind, struct Node* first, struct Node* second,
+ struct Node* third)
+{
+ struct Node* node = node_new(kind);
+ node->first = first;
+ node->second = second;
+ node->third = third;
+ return node;
+}
+
struct Node* node_append(struct Node* list, struct Node* node)
{
struct Node* last = list;
@@ -63,6 +73,7 @@ void node_free(struct Node* node)
return;
node_free(node->first);
node_free(node->second);
+ node_free(node->third);
node_free(node->next);
free(node);
}
diff --git a/ast.h b/ast.h
index c25e118..d993c2e 100644
--- a/ast.h
+++ b/ast.h
@@ -9,6 +9,7 @@ enum NodeKind
TYPE_SIZE,
TYPE_REMOVE_CV,
TYPE_ARRAY,
+ TYPE_CONDITIONAL,
TYPE_DECLTYPE,
VALUE_DIFFERENCE,
VALUE_SUM,
@@ -17,6 +18,12 @@ enum NodeKind
VALUE_EXTENT,
VALUE_TUPLE_SIZE,
VALUE_INTEGRAL_CONSTANT,
+ VALUE_CONDITIONAL,
+ COND_TRUE,
+ COND_FALSE,
+ COND_IS_SAME,
+ COND_EQUAL,
+ COND_LESS,
STMT_EMIT
};
@@ -27,6 +34,7 @@ struct Node
char character;
struct Node* first;
struct Node* second;
+ struct Node* third;
struct Node* next;
};
@@ -34,6 +42,8 @@ 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_append(struct Node* list, struct Node* node);
void node_free(struct Node* node);
diff --git a/eval.c b/eval.c
index 8c92cd8..eddeb6b 100644
--- a/eval.c
+++ b/eval.c
@@ -1,11 +1,13 @@
#include <assert.h>
#include <inttypes.h>
+#include <stdbool.h>
#include <stdio.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 complain(const struct Node* node, const char* message)
{
@@ -26,6 +28,7 @@ static int evaluate_value(const struct Node* node, int64_t* out)
int64_t left = 0;
int64_t right = 0;
struct Type type;
+ bool truth = false;
switch (node->kind)
{
case VALUE_DIFFERENCE:
@@ -56,11 +59,14 @@ static int evaluate_value(const struct Node* node, int64_t* out)
case VALUE_TUPLE_SIZE:
if (evaluate_type(node->first, &type))
return 1;
-
- // a non-array has no extent, so it gets zero, exactly like the real thing
*out = type.kind == KIND_ARRAY ? type.number : 0;
return 0;
case VALUE_INTEGRAL_CONSTANT: return evaluate_value(node->first, out);
+ case VALUE_CONDITIONAL:
+ if (evaluate_condition(node->first, &truth))
+ return 1;
+
+ return evaluate_value(truth ? node->second : node->third, out);
default: break;
}
@@ -73,6 +79,7 @@ static int evaluate_type(const struct Node* node, struct Type* out)
out->kind = KIND_VOID;
out->number = 0;
struct Type element;
+ bool truth = false;
switch (node->kind)
{
case TYPE_VOID: return 0;
@@ -81,12 +88,14 @@ static int evaluate_type(const struct Node* node, struct Type* out)
case TYPE_SIZE: out->kind = KIND_SIZE; return 0;
case TYPE_REMOVE_CV: return evaluate_type(node->first, out);
case TYPE_ARRAY:
- // the element type is evaluated for its side of the bargain, then forgotten
if (evaluate_type(node->first, &element) || evaluate_value(node->second, &out->number))
return 1;
-
out->kind = KIND_ARRAY;
return 0;
+ case TYPE_CONDITIONAL:
+ if (evaluate_condition(node->first, &truth))
+ return 1;
+ return evaluate_type(truth ? node->second : node->third, out);
case TYPE_DECLTYPE:
if (evaluate_value(node->first, &out->number))
return 1;
@@ -99,6 +108,47 @@ static int evaluate_type(const struct Node* node, struct Type* out)
return 1;
}
+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)
+{
+ 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(node->first, &left_type) || evaluate_type(node->second, &right_type))
+ return 1;
+
+ *out = same_type(&left_type, &right_type);
+ return 0;
+ case COND_EQUAL:
+ if (evaluate_operands(node, &left, &right))
+ return 1;
+
+ *out = left == right;
+ return 0;
+ case COND_LESS:
+ if (evaluate_operands(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 emit(const struct Node* node)
{
struct Type type;
diff --git a/examples/conditions.sfinae b/examples/conditions.sfinae
new file mode 100644
index 0000000..e930480
--- /dev/null
+++ b/examples/conditions.sfinae
@@ -0,0 +1,35 @@
+// a condition is not a condition until you say ::value
+
+// ::conditional_t picks a type. picking ::void and emitting it prints nothing at all
+::emit<
+ ::conditional_t<
+ ::less<::difference<'B', 'A'>, ::difference<'A', 'A'>>::value,
+ ::decltype(::difference<'Z', 'A'>),
+ ::void
+ >
+>::commit;
+
+// ::conditional does the same for values. this prints 9
+::emit<
+ ::decltype(
+ ::conditional<
+ ::is_same<::remove_cv_t<::int32_t>, ::int32_t>::value,
+ ::difference<'J', 'A'>,
+ ::difference<'A', 'A'>
+ >
+ )
+>::commit;
+
+// two arrays are the same type when their extents agree. this prints 1
+::emit<
+ ::decltype(
+ ::conditional<
+ ::is_same<
+ ::array<::byte, ::difference<'C', 'A'>>,
+ ::array<::int32_t, ::sum<::difference<'B', 'A'>, ::difference<'B', 'A'>>>
+ >::value,
+ ::difference<'B', 'A'>,
+ ::difference<'A', 'A'>
+ >
+ )
+>::commit;
diff --git a/examples/hello.sfinae b/examples/hello.sfinae
index 0be95fa..39f3a62 100644
--- a/examples/hello.sfinae
+++ b/examples/hello.sfinae
@@ -1,2 +1,8 @@
// prints 5
-::emit<::decltype(::difference<'F', 'A'>)>::commit;
+::emit<
+ ::conditional_t<
+ ::true_type::value,
+ ::decltype(::difference<'F', 'A'>),
+ ::void
+ >
+>::commit;
diff --git a/examples/xs.sfinae b/examples/xs.sfinae
index faae464..7cb7dd9 100644
--- a/examples/xs.sfinae
+++ b/examples/xs.sfinae
@@ -1,5 +1,3 @@
-// the original sin: how wide is int xs[20][20]
-
::emit<
::decltype(
::extent<
diff --git a/sfinae.l b/sfinae.l
index 98da1cf..4832ed0 100644
--- a/sfinae.l
+++ b/sfinae.l
@@ -49,6 +49,13 @@ static int fail(const char* message)
"tuple_size" return TOK_TUPLE_SIZE;
"integral_constant" return TOK_INTEGRAL_CONSTANT;
"value" return TOK_VALUE;
+"conditional_t" return TOK_CONDITIONAL_T;
+"conditional" return TOK_CONDITIONAL;
+"true_type" return TOK_TRUE_TYPE;
+"false_type" return TOK_FALSE_TYPE;
+"is_same" return TOK_IS_SAME;
+"equal" return TOK_EQUAL;
+"less" return TOK_LESS;
[<>(),;] return yytext[0];
'\\.' yylval.character = unescape(yytext[2]); return TOK_CHAR;
'.' yylval.character = yytext[1]; return TOK_CHAR;
diff --git a/sfinae.y b/sfinae.y
index 5b5d1be..ca3121d 100644
--- a/sfinae.y
+++ b/sfinae.y
@@ -33,9 +33,16 @@ void yyerror(const char* message);
%token TOK_TUPLE_SIZE
%token TOK_INTEGRAL_CONSTANT
%token TOK_VALUE
+%token TOK_CONDITIONAL_T
+%token TOK_CONDITIONAL
+%token TOK_TRUE_TYPE
+%token TOK_FALSE_TYPE
+%token TOK_IS_SAME
+%token TOK_EQUAL
+%token TOK_LESS
%token <character> TOK_CHAR
-%type <node> statements statement type value
+%type <node> statements statement type value condition
%%
@@ -69,6 +76,8 @@ type
{ $$ = node_unary(TYPE_REMOVE_CV, $4); }
| TOK_COLONCOLON TOK_ARRAY '<' type ',' value '>'
{ $$ = node_binary(TYPE_ARRAY, $4, $6); }
+ | TOK_COLONCOLON TOK_CONDITIONAL_T '<' condition ',' type ',' type '>'
+ { $$ = node_ternary(TYPE_CONDITIONAL, $4, $6, $8); }
| TOK_COLONCOLON TOK_DECLTYPE '(' value ')'
{ $$ = node_unary(TYPE_DECLTYPE, $4); }
;
@@ -89,6 +98,21 @@ value
| TOK_COLONCOLON TOK_INTEGRAL_CONSTANT '<' TOK_COLONCOLON TOK_SIZE_T ',' value '>'
TOK_COLONCOLON TOK_VALUE
{ $$ = node_unary(VALUE_INTEGRAL_CONSTANT, $7); }
+ | TOK_COLONCOLON TOK_CONDITIONAL '<' condition ',' value ',' value '>'
+ { $$ = node_ternary(VALUE_CONDITIONAL, $4, $6, $8); }
+ ;
+
+condition
+ : TOK_COLONCOLON TOK_TRUE_TYPE TOK_COLONCOLON TOK_VALUE
+ { $$ = node_leaf(COND_TRUE); }
+ | TOK_COLONCOLON TOK_FALSE_TYPE TOK_COLONCOLON TOK_VALUE
+ { $$ = node_leaf(COND_FALSE); }
+ | TOK_COLONCOLON TOK_IS_SAME '<' type ',' type '>' TOK_COLONCOLON TOK_VALUE
+ { $$ = node_binary(COND_IS_SAME, $4, $6); }
+ | TOK_COLONCOLON TOK_EQUAL '<' value ',' value '>' TOK_COLONCOLON TOK_VALUE
+ { $$ = node_binary(COND_EQUAL, $4, $6); }
+ | TOK_COLONCOLON TOK_LESS '<' value ',' value '>' TOK_COLONCOLON TOK_VALUE
+ { $$ = node_binary(COND_LESS, $4, $6); }
;
%%