Skip to content

Commit 3cd3c01

Browse files
author
jon
committed
implemented varargs
1 parent 6bd05bc commit 3cd3c01

File tree

3 files changed

+92
-16
lines changed

3 files changed

+92
-16
lines changed

c.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ if [ "$(uname)" == "Darwin" ]; then
1818
-framework Cocoa -framework QuartzCore -framework OpenGL -framework Metal -framework MetalKit \
1919
-L/opt/homebrew/opt/mysql-client/lib \
2020
-lmysqlclient \
21-
-o jclj
21+
-o jo_clojure
2222
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
2323
c++ -std=c++17 \
2424
-DNO_SOKOL \
@@ -28,5 +28,5 @@ elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
2828
imgui/imgui.cpp imgui/imgui_widgets.cpp imgui/imgui_draw.cpp imgui/imgui_tables.cpp imgui/imgui_demo.cpp \
2929
$opt -fexceptions -lpthread \
3030
-lmysqlclient -lgtk-3 -lgdk-3 -lpangocairo-1.0 -lpango-1.0 -latk-1.0 -lcairo-gobject -lcairo -lgio-2.0 -lgobject-2.0 -lglib-2.0 \
31-
-o jclj
31+
-o jo_clojure
3232
fi

jo_clojure.cpp

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ enum {
9898
K_ALL_NODE,
9999
K_BY_NODE,
100100
K_AUTO_DEREF_NODE,
101+
AMP_NODE, // & symbol for varargs
101102
START_USER_NODES,
102103

103104
// node types
@@ -147,6 +148,7 @@ enum {
147148
NODE_FLAG_FOREVER = 1<<7, // never release this node
148149
NODE_FLAG_GARBAGE = 1<<8, // this node is garbage
149150
NODE_FLAG_CHAR = 1<<9, // char
151+
NODE_FLAG_VARARGS = 1<<10, // function has varargs
150152
};
151153

152154
struct node_t;
@@ -554,6 +556,7 @@ struct node_t {
554556
struct {
555557
vector_ptr_t args;
556558
list_ptr_t body;
559+
size_t fixed_args_count; // For varargs functions, number of arguments before &
557560
} t_func;
558561
// var, delay, lazy_fn, reduced
559562
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
17541757
if(tok.str == "%6") return PCT6_NODE;
17551758
if(tok.str == "%7") return PCT7_NODE;
17561759
if(tok.str == "%8") return PCT8_NODE;
1760+
if(tok.str == "&") return AMP_NODE;
17571761
node_idx_t ret = new_node_symbol(tok.str.c_str(), NODE_FLAG_FOREVER);
17581762
/* // NOTE: This is broken... don't re-enable.
17591763
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) {
20672071
int is_macro = (sym_flags|list_flags) & NODE_FLAG_MACRO;
20682072
vector_t::iterator i = proto_args->begin();
20692073
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++) {
20712080
node_let(fn_env, *i, is_macro ? *i2 : eval_node(env, *i2));
20722081
}
20732082

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) {
20762099
warnf("ArityException: Wrong number of args (%zu) passed to: %s\n",
20772100
args1->size(), sym_node->t_string.c_str());
20782101
return NIL_NODE;
@@ -2089,9 +2112,30 @@ static node_idx_t eval_list(env_ptr_t env, list_ptr_t list, int list_flags) {
20892112
while(last_node->type == NODE_RECUR) {
20902113
auto proto_it = proto_args->begin();
20912114
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);
20942122
}
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+
20952139
for(list_t::iterator i(proto_body); i; i++) {
20962140
last = eval_node(fn_env, *i);
20972141
}
@@ -3313,6 +3357,21 @@ static node_idx_t native_fn_internal(env_ptr_t env, list_ptr_t args, node_idx_t
33133357
ret->flags |= flags;
33143358
ret->t_func.args = get_node(*i)->as_vector();
33153359
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+
33163375
if(private_fn_name == NIL_NODE) {
33173376
ret->t_string = "<anonymous>";
33183377
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) {
33573416
if(fn->type != NODE_FUNC) {
33583417
continue;
33593418
}
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)) {
33613426
return eval_list(env, args->push_front(*i));
33623427
}
33633428
}
33643429
// 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);
33663433
return NIL_NODE;
33673434
}, macro);
33683435
}
@@ -4915,11 +4982,11 @@ static node_idx_t native_cond_thread(env_ptr_t env, list_ptr_t args) {
49154982
form_args->push_front_inplace(form_idx, value_idx);
49164983
value_idx = eval_list(env, form_args);
49174984
} 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);
49234990
} else {
49244991
warnf("(cond->) requires a symbol or list");
49254992
return NIL_NODE;
@@ -5042,7 +5109,6 @@ static node_idx_t native_is_contains(env_ptr_t env, list_ptr_t args) {
50425109
warnf("(contains?) requires a collection\n");
50435110
return NIL_NODE;
50445111
}
5045-
50465112
// (counted? coll)
50475113
// Returns true if coll implements count in constant time
50485114
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) {
51675233
return eval_list(env, new_args);
51685234
}, false);
51695235
}
5170-
51715236
// (split-at n coll)
51725237
// Returns a vector of [(take n coll) (drop n coll)]
51735238
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) {
64556520
new_node_keyword("__ALL__", NODE_FLAG_PRERESOLVE);
64566521
new_node_keyword("__BY__", NODE_FLAG_PRERESOLVE);
64576522
new_node_keyword("__AUTO_DEREF__", NODE_FLAG_PRERESOLVE);
6523+
new_node_keyword("&", NODE_FLAG_PRERESOLVE); // AMP_NODE
64586524
}
64596525

64606526
env->set("nil", NIL_NODE);
@@ -6746,3 +6812,5 @@ int main(int argc, char **argv) {
67466812
return 0;
67476813
}
67486814

6815+
6816+

test2.clj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,11 @@
3939
(def data-str (pr-str data))
4040
(println "Data as string:" data-str)
4141
(println "Data can be read back:" (= data (read-string data-str)))
42+
43+
(defn my-function [a b & rest]
44+
(println "Fixed args:" a b)
45+
(println "Rest args:" rest))
46+
47+
;; Can be called with 2 or more arguments:
48+
(my-function 1 2)
49+
(my-function 1 2 3 4 5)

0 commit comments

Comments
 (0)