hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-19 15:47:03 +0200
committerhachem <im@hachem.wtf>2026-09-19 15:47:03 +0200
commit6d8b0392bb0c0df82285eec1b0f692113e66eb1a (patch)
treec08fde36b934b759b1255bac33dc69ae1adf7169
parente5cc80fb6a608aa1fbd0d6fabd1cf614a0fc004a (diff)
feat: enable_if
-rw-r--r--ast.c9
-rw-r--r--ast.h3
-rw-r--r--eval.c13
-rw-r--r--examples/enable_if.sfinae22
-rw-r--r--sfinae.l2
-rw-r--r--sfinae.y21
6 files changed, 69 insertions, 1 deletions
diff --git a/ast.c b/ast.c
index ed694f9..58cbaf5 100644
--- a/ast.c
+++ b/ast.c
@@ -79,6 +79,14 @@ struct Node* node_named(enum NodeKind kind, const struct Name* name, struct Node
return node;
}
+struct Node* node_block(enum NodeKind kind, struct Node* first, struct Node* body)
+{
+ struct Node* node = node_new(kind);
+ node->first = first;
+ node->body = body;
+ return node;
+}
+
struct Node* node_append(struct Node* list, struct Node* node)
{
struct Node* last = list;
@@ -98,6 +106,7 @@ void node_free(struct Node* node)
node_free(node->first);
node_free(node->second);
node_free(node->third);
+ node_free(node->body);
node_free(node->next);
free(node);
}
diff --git a/ast.h b/ast.h
index aaa5f7a..4a5db15 100644
--- a/ast.h
+++ b/ast.h
@@ -33,6 +33,7 @@ enum NodeKind
COND_LESS,
STMT_DECLARE,
STMT_UNDECLARE,
+ STMT_ENABLE_IF,
STMT_EMIT,
STMT_STATIC_ASSERT
};
@@ -52,6 +53,7 @@ struct Node
struct Node* first;
struct Node* second;
struct Node* third;
+ struct Node* body;
struct Node* next;
};
@@ -65,6 +67,7 @@ struct Node* node_binary(enum NodeKind kind, struct Node* first, struct Node* se
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_block(enum NodeKind kind, struct Node* first, struct Node* body);
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 cb967c2..3f27fa2 100644
--- a/eval.c
+++ b/eval.c
@@ -254,6 +254,18 @@ static enum RunStatus emit(struct Machine* machine, const struct Node* node)
return RUN_DONE;
}
+static enum RunStatus enable_if(struct Machine* machine, const struct Node* node)
+{
+ bool truth = false;
+ if (evaluate_condition(machine, node->first, &truth))
+ return RUN_FAILED;
+
+ if (!truth)
+ return RUN_DONE;
+
+ return execute(machine, node->body);
+}
+
static enum RunStatus static_assert_statement(struct Machine* machine, const struct Node* node)
{
bool truth = false;
@@ -275,6 +287,7 @@ static enum RunStatus execute_one(struct Machine* machine, const struct Node* no
{
case STMT_DECLARE: return declare(machine, node);
case STMT_UNDECLARE: return undeclare(machine, node);
+ case STMT_ENABLE_IF: return enable_if(machine, node);
case STMT_EMIT: return emit(machine, node);
case STMT_STATIC_ASSERT: return static_assert_statement(machine, node);
default: break;
diff --git a/examples/enable_if.sfinae b/examples/enable_if.sfinae
new file mode 100644
index 0000000..ff7dacc
--- /dev/null
+++ b/examples/enable_if.sfinae
@@ -0,0 +1,22 @@
+// ::enable_if_t runs a ::sequence of statements when its condition holds
+// the statements inside are separated by commas and each one is still committed
+
+::enable_if_t<
+ ::is_same<::remove_cv_t<::int32_t>, ::int32_t>::value,
+ ::sequence<
+ ::declare<::name<'x'>, ::decltype(::difference<'C', 'A'>)>::commit,
+ ::emit<::decltype(::value_of<::name<'x'>>)>::commit
+ >
+>::commit;
+
+// this one never runs, so nothing is printed and nothing is asserted
+::enable_if_t<
+ ::false_type::value,
+ ::sequence<
+ ::static_assert<::false_type::value>::commit,
+ ::emit<::decltype(::difference<'Z', 'A'>)>::commit
+ >
+>::commit;
+
+// an empty sequence is allowed, for when you have nothing to say
+::enable_if_t<::true_type::value, ::sequence<>>::commit;
diff --git a/sfinae.l b/sfinae.l
index 17bf28e..0ab1eeb 100644
--- a/sfinae.l
+++ b/sfinae.l
@@ -37,6 +37,8 @@ static int fail(const char* message)
"commit" return TOK_COMMIT;
"declare" return TOK_DECLARE;
"undeclare" return TOK_UNDECLARE;
+"enable_if_t" return TOK_ENABLE_IF;
+"sequence" return TOK_SEQUENCE;
"name" return TOK_NAME;
"value_of" return TOK_VALUE_OF;
"void" return TOK_VOID;
diff --git a/sfinae.y b/sfinae.y
index 1acb4d0..f97147f 100644
--- a/sfinae.y
+++ b/sfinae.y
@@ -23,6 +23,8 @@ void yyerror(const char* message);
%token TOK_COMMIT
%token TOK_DECLARE
%token TOK_UNDECLARE
+%token TOK_ENABLE_IF
+%token TOK_SEQUENCE
%token TOK_NAME
%token TOK_VALUE_OF
%token TOK_VOID
@@ -50,7 +52,7 @@ void yyerror(const char* message);
%token <character> TOK_CHAR
%type <name> name char_list
-%type <node> statements statement type value condition
+%type <node> statements statement sequence sequence_items type value condition
%%
@@ -75,6 +77,23 @@ statement
{ $$ = node_named(STMT_DECLARE, &$4, $6); }
| TOK_COLONCOLON TOK_UNDECLARE '<' name '>' TOK_COLONCOLON TOK_COMMIT
{ $$ = node_named(STMT_UNDECLARE, &$4, NULL); }
+ | TOK_COLONCOLON TOK_ENABLE_IF '<' condition ',' sequence '>' TOK_COLONCOLON TOK_COMMIT
+ { $$ = node_block(STMT_ENABLE_IF, $4, $6); }
+ ;
+
+// the statements inside are committed one by one, without the semicolons
+sequence
+ : TOK_COLONCOLON TOK_SEQUENCE '<' sequence_items '>'
+ { $$ = $4; }
+ ;
+
+sequence_items
+ : /* nothing */
+ { $$ = NULL; }
+ | statement
+ { $$ = $1; }
+ | sequence_items ',' statement
+ { $$ = node_append($1, $3); }
;
name