98
98
K_ALL_NODE,
99
99
K_BY_NODE,
100
100
K_AUTO_DEREF_NODE,
101
+ AMP_NODE, // & symbol for varargs
101
102
START_USER_NODES,
102
103
103
104
// node types
@@ -147,6 +148,7 @@ enum {
147
148
NODE_FLAG_FOREVER = 1 <<7 , // never release this node
148
149
NODE_FLAG_GARBAGE = 1 <<8 , // this node is garbage
149
150
NODE_FLAG_CHAR = 1 <<9 , // char
151
+ NODE_FLAG_VARARGS = 1 <<10 , // function has varargs
150
152
};
151
153
152
154
struct node_t ;
@@ -554,6 +556,7 @@ struct node_t {
554
556
struct {
555
557
vector_ptr_t args;
556
558
list_ptr_t body;
559
+ size_t fixed_args_count; // For varargs functions, number of arguments before &
557
560
} t_func;
558
561
// var, delay, lazy_fn, reduced
559
562
node_idx_t t_extra;
@@ -1754,6 +1757,7 @@ static node_idx_t parse_next(env_ptr_t env, parse_state_t *state, int stop_on_se
1754
1757
if (tok.str == " %6" ) return PCT6_NODE;
1755
1758
if (tok.str == " %7" ) return PCT7_NODE;
1756
1759
if (tok.str == " %8" ) return PCT8_NODE;
1760
+ if (tok.str == " &" ) return AMP_NODE;
1757
1761
node_idx_t ret = new_node_symbol (tok.str .c_str (), NODE_FLAG_FOREVER);
1758
1762
/* // NOTE: This is broken... don't re-enable.
1759
1763
node_idx_t node = env->get(ret);
@@ -2067,12 +2071,31 @@ static node_idx_t eval_list(env_ptr_t env, list_ptr_t list, int list_flags) {
2067
2071
int is_macro = (sym_flags|list_flags) & NODE_FLAG_MACRO;
2068
2072
vector_t ::iterator i = proto_args->begin ();
2069
2073
list_t ::iterator i2 (args1);
2070
- for (; i && i2; i++, i2++) {
2074
+ bool has_varargs = (sym_node->flags & NODE_FLAG_VARARGS) != 0 ;
2075
+ size_t fixed_args_count = sym_node->t_func .fixed_args_count ;
2076
+
2077
+ // Bind fixed parameters
2078
+ size_t param_index = 0 ;
2079
+ for (; i && i2 && param_index < fixed_args_count; i++, i2++, param_index++) {
2071
2080
node_let (fn_env, *i, is_macro ? *i2 : eval_node (env, *i2));
2072
2081
}
2073
2082
2074
- // Check for extra arguments - Clojure ArityException behavior
2075
- if (i2) {
2083
+ // Handle varargs if present
2084
+ if (has_varargs) {
2085
+ // Skip the & symbol in parameter list
2086
+ i++;
2087
+ if (i) {
2088
+ // Collect all remaining arguments into a list
2089
+ list_ptr_t rest_args = new_list ();
2090
+ for (; i2; i2++) {
2091
+ rest_args->push_back_inplace (is_macro ? *i2 : eval_node (env, *i2));
2092
+ }
2093
+ // Bind the rest parameter
2094
+ node_let (fn_env, *i, new_node_list (rest_args));
2095
+ }
2096
+ }
2097
+ // Check for arity errors
2098
+ else if (i2) {
2076
2099
warnf (" ArityException: Wrong number of args (%zu) passed to: %s\n " ,
2077
2100
args1->size (), sym_node->t_string .c_str ());
2078
2101
return NIL_NODE;
@@ -2089,9 +2112,30 @@ static node_idx_t eval_list(env_ptr_t env, list_ptr_t list, int list_flags) {
2089
2112
while (last_node->type == NODE_RECUR) {
2090
2113
auto proto_it = proto_args->begin ();
2091
2114
list_t ::iterator recur_it (last_node->t_list );
2092
- for (; proto_it && recur_it; ) {
2093
- node_let (fn_env, *proto_it++, *recur_it++);
2115
+ bool has_varargs = (sym_node->flags & NODE_FLAG_VARARGS) != 0 ;
2116
+ size_t fixed_args_count = sym_node->t_func .fixed_args_count ;
2117
+
2118
+ // Bind fixed parameters for recur
2119
+ size_t param_index = 0 ;
2120
+ for (; proto_it && recur_it && param_index < fixed_args_count; proto_it++, recur_it++, param_index++) {
2121
+ node_let (fn_env, *proto_it, *recur_it);
2094
2122
}
2123
+
2124
+ // Handle varargs if present
2125
+ if (has_varargs) {
2126
+ // Skip the & symbol
2127
+ proto_it++;
2128
+ if (proto_it) {
2129
+ // Collect remaining recur arguments into a list
2130
+ list_ptr_t rest_args = new_list ();
2131
+ for (; recur_it; recur_it++) {
2132
+ rest_args->push_front_inplace (*recur_it);
2133
+ }
2134
+ // Bind rest parameter
2135
+ node_let (fn_env, *proto_it, new_node_list (rest_args->reverse ()));
2136
+ }
2137
+ }
2138
+
2095
2139
for (list_t ::iterator i (proto_body); i; i++) {
2096
2140
last = eval_node (fn_env, *i);
2097
2141
}
@@ -3313,6 +3357,21 @@ static node_idx_t native_fn_internal(env_ptr_t env, list_ptr_t args, node_idx_t
3313
3357
ret->flags |= flags;
3314
3358
ret->t_func .args = get_node (*i)->as_vector ();
3315
3359
ret->t_func .body = args->rest ();
3360
+
3361
+ // Check for varargs and compute fixed arguments count
3362
+ vector_ptr_t func_args = ret->t_func .args ;
3363
+ ret->t_func .fixed_args_count = func_args->size ();
3364
+
3365
+ // Look for & symbol in the arguments list
3366
+ for (size_t j = 0 ; j < func_args->size (); j++) {
3367
+ if (func_args->nth (j) == AMP_NODE) {
3368
+ // Set the varargs flag and update fixed_args_count
3369
+ ret->flags |= NODE_FLAG_VARARGS;
3370
+ ret->t_func .fixed_args_count = j;
3371
+ break ;
3372
+ }
3373
+ }
3374
+
3316
3375
if (private_fn_name == NIL_NODE) {
3317
3376
ret->t_string = " <anonymous>" ;
3318
3377
ret->t_env = env;
@@ -3357,12 +3416,20 @@ static node_idx_t native_fn_macro(env_ptr_t env, list_ptr_t args, bool macro) {
3357
3416
if (fn->type != NODE_FUNC) {
3358
3417
continue ;
3359
3418
}
3360
- if (fn->t_func .args ->size () == num_args) {
3419
+ // Check for exact match or matching varargs pattern
3420
+ vector_ptr_t fn_args = fn->t_func .args ;
3421
+ size_t fixed_args_count = fn->t_func .fixed_args_count ;
3422
+ bool has_varargs = (fn->flags & NODE_FLAG_VARARGS) != 0 ;
3423
+
3424
+ if ((has_varargs && num_args >= fixed_args_count) ||
3425
+ (!has_varargs && fn_args->size () == num_args)) {
3361
3426
return eval_list (env, args->push_front (*i));
3362
3427
}
3363
3428
}
3364
3429
// No matching arity found, report an error
3365
- warnf (" ArityException: Wrong number of args (%lld) passed\n " , num_args);
3430
+ const char * fn_name = private_fn_name != NIL_NODE ?
3431
+ get_node (private_fn_name)->t_string .c_str () : " <anonymous>" ;
3432
+ warnf (" ArityException: Wrong number of args (%lld) passed to: %s\n " , num_args, fn_name);
3366
3433
return NIL_NODE;
3367
3434
}, macro);
3368
3435
}
@@ -4915,11 +4982,11 @@ static node_idx_t native_cond_thread(env_ptr_t env, list_ptr_t args) {
4915
4982
form_args->push_front_inplace (form_idx, value_idx);
4916
4983
value_idx = eval_list (env, form_args);
4917
4984
} else if (form_type == NODE_LIST) {
4918
- list_ptr_t form_args = get_node (form_idx)->t_list ;
4919
- node_idx_t sym = form_args ->first_value ();
4920
- form_args = form_args ->pop_front ();
4921
- form_args ->push_front_inplace (sym, value_idx);
4922
- value_idx = eval_list (env, form_args );
4985
+ list_ptr_t form_list = get_node (form_idx)->t_list ;
4986
+ node_idx_t sym = form_list ->first_value ();
4987
+ form_list = form_list ->pop_front ();
4988
+ form_list ->push_front_inplace (sym, value_idx);
4989
+ value_idx = eval_list (env, form_list );
4923
4990
} else {
4924
4991
warnf (" (cond->) requires a symbol or list" );
4925
4992
return NIL_NODE;
@@ -5042,7 +5109,6 @@ static node_idx_t native_is_contains(env_ptr_t env, list_ptr_t args) {
5042
5109
warnf (" (contains?) requires a collection\n " );
5043
5110
return NIL_NODE;
5044
5111
}
5045
-
5046
5112
// (counted? coll)
5047
5113
// Returns true if coll implements count in constant time
5048
5114
static node_idx_t native_is_counted (env_ptr_t env, list_ptr_t args) {
@@ -5167,7 +5233,6 @@ static node_idx_t native_fnil(env_ptr_t env, list_ptr_t args) {
5167
5233
return eval_list (env, new_args);
5168
5234
}, false );
5169
5235
}
5170
-
5171
5236
// (split-at n coll)
5172
5237
// Returns a vector of [(take n coll) (drop n coll)]
5173
5238
static node_idx_t native_split_at (env_ptr_t env, list_ptr_t args) {
@@ -6455,6 +6520,7 @@ int main(int argc, char **argv) {
6455
6520
new_node_keyword (" __ALL__" , NODE_FLAG_PRERESOLVE);
6456
6521
new_node_keyword (" __BY__" , NODE_FLAG_PRERESOLVE);
6457
6522
new_node_keyword (" __AUTO_DEREF__" , NODE_FLAG_PRERESOLVE);
6523
+ new_node_keyword (" &" , NODE_FLAG_PRERESOLVE); // AMP_NODE
6458
6524
}
6459
6525
6460
6526
env->set (" nil" , NIL_NODE);
@@ -6746,3 +6812,5 @@ int main(int argc, char **argv) {
6746
6812
return 0 ;
6747
6813
}
6748
6814
6815
+
6816
+
0 commit comments