From 1080303768125b33e1f7fc041a774b80c755a3a5 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 29 Aug 2025 16:30:28 +0200 Subject: [PATCH] Using ... to expand elements. --- lib/std/core/array.c3 | 38 +++++++------- src/compiler/compiler_internal.h | 1 + src/compiler/sema_expr.c | 81 ++++++++++++++++++++++++++++- src/compiler/sema_name_resolution.c | 2 +- 4 files changed, 102 insertions(+), 20 deletions(-) diff --git a/lib/std/core/array.c3 b/lib/std/core/array.c3 index 323f69d18..fbd6cba0b 100644 --- a/lib/std/core/array.c3 +++ b/lib/std/core/array.c3 @@ -385,17 +385,17 @@ macro bool @all(array, #predicate) @param fill_with : "The value used to fill or pad the shorter iterable to the length of the longer one while zipping." @require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable" - @require @is_valid_operation(#operation, left, right) : "The operator must take two parameters matching the elements of the left and right side" - @require @is_valid_fill(left, right, fill_with) : "The specified fill value does not match either the left or the right array's underlying type." + @require @is_valid_operation(left, right, ...#operation) : "The operator must take two parameters matching the elements of the left and right side" + @require @is_valid_fill(left, right, ...fill_with) : "The specified fill value does not match either the left or the right array's underlying type." *> -macro @zip(Allocator allocator, left, right, #operation = EMPTY_MACRO_SLOT, fill_with = EMPTY_MACRO_SLOT) @nodiscard +macro @zip(Allocator allocator, left, right, #operation = ..., fill_with = ...) @nodiscard { var $LeftType = $typeof(left[0]); var $RightType = $typeof(right[0]); var $Type = Pair { $LeftType, $RightType }; - bool $is_op = @is_valid_macro_slot(#operation); + bool $is_op = $defined(#operation); $if $is_op: $Type = $typeof(#operation).returns; $endif @@ -406,8 +406,7 @@ macro @zip(Allocator allocator, left, right, #operation = EMPTY_MACRO_SLOT, fill $LeftType left_fill; $RightType right_fill; usz result_len = min(left_len, right_len); - bool $has_fill = @is_valid_macro_slot(fill_with); - $if $has_fill: + $if $defined(fill_with): switch { case left_len > right_len: @@ -459,13 +458,13 @@ macro @zip(Allocator allocator, left, right, #operation = EMPTY_MACRO_SLOT, fill @param fill_with : "The value used to fill or pad the shorter iterable to the length of the longer one while zipping." @require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable" - @require @is_valid_operation(#operation, left, right) : "The operator must take two parameters matching the elements of the left and right side" - @require @is_valid_fill(left, right, fill_with) : "The specified fill value does not match either the left or the right array's underlying type." + @require @is_valid_operation(left, right, ...#operation) : "The operator must take two parameters matching the elements of the left and right side" + @require @is_valid_fill(left, right, ...fill_with) : "The specified fill value does not match either the left or the right array's underlying type." *> -macro @tzip(left, right, #operation = EMPTY_MACRO_SLOT, fill_with = EMPTY_MACRO_SLOT) @nodiscard +macro @tzip(left, right, #operation = ..., fill_with = ...) @nodiscard { - return @zip(tmem, left, right, #operation, fill_with); + return @zip(tmem, left, right, #operation: ...#operation, fill_with: ...fill_with); } @@ -526,10 +525,10 @@ macro typeid @zip_into_fn(#left, #right) @const return @typeid(fn $typeof(#left[0]) ($typeof(#left[0]) l, $typeof(#right[0]) r) => l); } -macro bool @is_valid_operation(#operation, #left, #right) @const +macro bool @is_valid_operation(#left, #right, #operation = ...) @const { $switch: - $case @is_empty_macro_slot(#operation): + $case !$defined(#operation): return true; $case $kindof(#operation) != FUNC: return false; @@ -543,13 +542,16 @@ macro bool @is_valid_list(#expr) @const return $defined(#expr[0]) &&& ($defined(#expr.len) ||| $defined(#expr.len())); } -macro bool @is_valid_fill(left, right, fill_with) +macro bool @is_valid_fill(left, right, fill_with = ...) { - if (@is_empty_macro_slot(fill_with)) return true; - usz left_len = $defined(left.len()) ??? left.len() : left.len; - usz right_len = $defined(right.len()) ??? right.len() : right.len; - if (left_len == right_len) return true; - return left_len > right_len ? $defined(($typeof(right[0]))fill_with) : $defined(($typeof(left[0]))fill_with); + $if !$defined(fill_with): + return true; + $else + usz left_len = $defined(left.len()) ??? left.len() : left.len; + usz right_len = $defined(right.len()) ??? right.len() : right.len; + if (left_len == right_len) return true; + return left_len > right_len ? $defined(($typeof(right[0]))fill_with) : $defined(($typeof(left[0]))fill_with); + $endif } macro usz find_len(list) => $defined(list.len()) ??? list.len() : list.len; \ No newline at end of file diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 2442f93fc..68c96eed4 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2449,6 +2449,7 @@ Decl *sema_find_symbol(SemaContext *context, const char *symbol); Decl *sema_find_path_symbol(SemaContext *context, const char *symbol, Path *path); Decl *sema_find_label_symbol(SemaContext *context, const char *symbol); Decl *sema_find_label_symbol_anywhere(SemaContext *context, const char *symbol); +Decl *sema_find_local(SemaContext *context, const char *symbol); Decl *sema_resolve_symbol(SemaContext *context, const char *symbol, Path *path, SourceSpan span); BoolErr sema_symbol_is_defined_in_scope(SemaContext *c, const char *symbol); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 4fa0f30eb..d80cbbf56 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1759,6 +1759,51 @@ static inline ArrayIndex sema_len_from_expr(Expr *expr) return range_const_len(&expr->slice_expr.range); } +static Decl *sema_find_splat_arg(Decl *macro, const char *name) +{ + FOREACH(Decl *, decl, macro->func_decl.signature.params) + { + if (decl->name == name) return decl; + } + return NULL; +} + +typedef enum +{ + SPLAT_ZERO, + SPLAT_ONE, + SPLAT_NONE, +} SplatResult; + +static SplatResult sema_splat_optional_argument(SemaContext *context, Expr *expr) +{ + Decl *macro = context->current_macro; + if (!macro) return SPLAT_NONE; + Decl *candidate = NULL; + switch (expr->expr_kind) + { + case EXPR_UNRESOLVED_IDENTIFIER: + if (expr->unresolved_ident_expr.path) break; + candidate = sema_find_splat_arg(macro, expr->unresolved_ident_expr.ident); + break; + case EXPR_HASH_IDENT: + candidate = sema_find_splat_arg(macro, expr->hash_ident_expr.identifier); + break; + case EXPR_CT_IDENT: + candidate = sema_find_splat_arg(macro, expr->ct_ident_expr.identifier); + break; + default: + return false; + } + if (!candidate) return SPLAT_NONE; + if (!candidate->var.no_init) return SPLAT_NONE; + // We found it, it's a valid variable. + Decl *local = sema_find_local(context, candidate->name); + if (local && local->var.kind == candidate->var.kind) return SPLAT_ONE; + // It's missing! Let's splat-zero + return SPLAT_ZERO; +} + INLINE Type *sema_get_va_type(SemaContext *context, Expr *expr, Variadic variadic) { if (variadic == VARIADIC_RAW) @@ -1842,7 +1887,20 @@ INLINE bool sema_call_evaluate_arguments(SemaContext *context, CalledDecl *calle if (arg->expr_kind == EXPR_SPLAT) { Expr *inner = arg->inner_expr; - + switch (sema_splat_optional_argument(context, inner)) + { + case SPLAT_ZERO: + vec_erase_at(args, i); + i--; + num_args--; + continue; + case SPLAT_ONE: + expr_replace(arg, inner); + i--; + continue; + case SPLAT_NONE: + break; + } if (!sema_analyse_expr(context, inner)) return false; // Let's try fit up a slice to the in the vaslot @@ -1942,6 +2000,27 @@ SPLAT_NORMAL:; last_named_arg = arg; actual_args[index] = arg->named_argument_expr.value; + if (actual_args[index]->expr_kind == EXPR_SPLAT) + { + Expr *inner = actual_args[index]->inner_expr; + switch (sema_splat_optional_argument(context, inner)) + { + case SPLAT_ZERO: + if (!sema_set_default_argument(context, callee, call, + params[index], no_match_ref, + &actual_args[index], + optional)) + { + return false; + } + continue; + case SPLAT_ONE: + expr_replace(actual_args[index], inner); + break; + case SPLAT_NONE: + break; + } + } if (!sema_analyse_parameter(context, actual_args[index], param, callee->definition, optional, no_match_ref, callee->macro, false)) return false; continue; diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 2ef38781d..d9b812e8c 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -490,7 +490,7 @@ static inline Decl *sema_find_ct_local(SemaContext *context, const char *symbol) return NULL; } -static inline Decl *sema_find_local(SemaContext *context, const char *symbol) +Decl *sema_find_local(SemaContext *context, const char *symbol) { if (symbol[0] == '$') return sema_find_ct_local(context, symbol); Decl **locals = context->locals;