diff options
| author | hachem <im@hachem.wtf> | 2026-09-19 15:47:03 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-09-19 15:47:03 +0200 |
| commit | 6d8b0392bb0c0df82285eec1b0f692113e66eb1a (patch) | |
| tree | c08fde36b934b759b1255bac33dc69ae1adf7169 | |
| parent | e5cc80fb6a608aa1fbd0d6fabd1cf614a0fc004a (diff) | |
feat: enable_if
| -rw-r--r-- | ast.c | 9 | ||||
| -rw-r--r-- | ast.h | 3 | ||||
| -rw-r--r-- | eval.c | 13 | ||||
| -rw-r--r-- | examples/enable_if.sfinae | 22 | ||||
| -rw-r--r-- | sfinae.l | 2 | ||||
| -rw-r--r-- | sfinae.y | 21 |
6 files changed, 69 insertions, 1 deletions
@@ -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); } @@ -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); @@ -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; @@ -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; @@ -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 |
