diff options
| author | hachem <im@hachem.wtf> | 2026-09-19 13:47:08 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-09-19 13:47:08 +0200 |
| commit | d0ca03c84bcad3dc42fd173b0baf3a15e9b52f9b (patch) | |
| tree | 89fa494129849e23b1d760466e87ef6046cbfcb4 | |
| parent | a79c97e04562b171780a24cba2cb1a71acfa4bbb (diff) | |
feat: add conditionals
| -rw-r--r-- | ast.c | 11 | ||||
| -rw-r--r-- | ast.h | 10 | ||||
| -rw-r--r-- | eval.c | 58 | ||||
| -rw-r--r-- | examples/conditions.sfinae | 35 | ||||
| -rw-r--r-- | examples/hello.sfinae | 8 | ||||
| -rw-r--r-- | examples/xs.sfinae | 2 | ||||
| -rw-r--r-- | sfinae.l | 7 | ||||
| -rw-r--r-- | sfinae.y | 26 |
8 files changed, 149 insertions, 8 deletions
@@ -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); } @@ -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); @@ -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< @@ -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; @@ -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); } ; %% |
