Skip to content

Commit 150145e

Browse files
committed
Simplify native function binding
1 parent aca59c0 commit 150145e

File tree

4 files changed

+50
-65
lines changed

4 files changed

+50
-65
lines changed

README.md

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -190,15 +190,15 @@ functionality, I/O operations, and performance-critical code written in C.
190190
Native functions in C must follow this signature:
191191

192192
```c
193-
tea_val_t your_function_name(tea_ctx_t* context, const tea_fn_args_t* args)
193+
tea_val_t your_function_name(tea_fn_args_t* args)
194194
```
195195
196196
The function receives a context and a list of arguments, and must return a `tea_value_t`. Arguments are accessed by
197197
calling `tea_function_args_pop()` in a loop. Here's an example:
198198
199199
```c
200200
// Native function to print values (similar to built-in print)
201-
static tea_val_t tea_print_values(tea_ctx_t* context, const tea_fn_args_t* args) {
201+
static tea_val_t tea_print_values(tea_fn_args_t* args) {
202202
for (;;) {
203203
tea_var_t* arg = tea_fn_args_pop(args);
204204
if (!arg) {
@@ -217,38 +217,28 @@ static tea_val_t tea_print_values(tea_ctx_t* context, const tea_fn_args_t* args)
217217
case TEA_V_UNDEF:
218218
break;
219219
}
220-
221-
tea_free_var(context, arg);
222220
}
223-
printf("\n");
224221
225222
return tea_val_undef();
226223
}
227224
228225
// Native function to add two numbers
229-
static tea_val_t tea_add_numbers(tea_ctx_t* context, const tea_fn_args_t* args) {
226+
static tea_val_t tea_add_numbers(tea_fn_args_t* args) {
230227
tea_var_t* arg1 = tea_fn_args_pop(args);
231228
tea_var_t* arg2 = tea_fn_args_pop(args);
232229
233230
if (!arg1 || !arg2) {
234231
// Handle error case - not enough arguments
235-
if (arg1) tea_free_var(context, arg1);
236-
if (arg2) tea_free_var(context, arg2);
237232
return tea_val_undef();
238233
}
239234
240235
if (arg1->val.type == TEA_V_I32 && arg2->val.type == TEA_V_I32) {
241236
tea_val_t result = {0};
242237
result.type = TEA_V_I32;
243238
result.i32 = arg1->val.i32 + arg2->val.i32;
244-
245-
tea_free_var(context, arg1);
246-
tea_free_var(context, arg2);
247239
return result;
248240
}
249241
250-
tea_free_var(context, arg1);
251-
tea_free_var(context, arg2);
252242
return tea_val_undef();
253243
}
254244
```
@@ -305,7 +295,6 @@ Native functions work with the `tea_val_t` type system:
305295
1. **Always validate arguments**: Use `tea_fn_args_pop()` to safely access arguments and check for NULL
306296
2. **Handle errors gracefully**: Return `tea_val_undef()` for error conditions
307297
3. **Memory management**:
308-
- Always call `tea_free_var(context, arg)` for each argument you pop
309298
- Strings returned from native functions should be allocated with `tea_malloc`
310299
4. **Performance**: Use native functions for computationally intensive operations
311300
5. **Argument handling**: Process arguments in the order they were passed by calling `tea_fn_args_pop()` sequentially

include/tea_fn.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ typedef struct {
1313
} tea_fn_t;
1414

1515
typedef struct {
16-
tea_list_entry_t head;
16+
tea_list_entry_t args;
17+
tea_list_entry_t popped_args;
1718
} tea_fn_args_t;
1819

19-
typedef tea_val_t (*tea_native_fn_cb_t)(tea_ctx_t *ctx,
20-
const tea_fn_args_t *args);
20+
typedef tea_val_t (*tea_native_fn_cb_t)(tea_fn_args_t *args);
2121

2222
typedef struct {
2323
tea_list_entry_t link;
@@ -30,19 +30,20 @@ typedef struct {
3030
bool is_set;
3131
} tea_ret_ctx_t;
3232

33-
tea_var_t *tea_fn_args_pop(const tea_fn_args_t *args);
33+
tea_var_t *tea_fn_args_pop(tea_fn_args_t *args);
3434

35-
const tea_native_fn_t *tea_ctx_find_native_fn(const tea_list_entry_t *fns,
35+
const tea_native_fn_t *tea_ctx_find_native_fn(const tea_list_entry_t *functions,
3636
const char *name);
37-
const tea_fn_t *tea_ctx_find_fn(const tea_list_entry_t *fns, const char *name);
37+
const tea_fn_t *tea_ctx_find_fn(const tea_list_entry_t *functions,
38+
const char *name);
3839

39-
bool tea_decl_fn(const tea_node_t *node, tea_list_entry_t *fns);
40+
bool tea_decl_fn(const tea_node_t *node, tea_list_entry_t *functions);
4041
bool tea_interp_fn_decl(tea_ctx_t *ctx, const tea_node_t *node);
4142

4243
tea_val_t tea_eval_fn_call(tea_ctx_t *ctx, tea_scope_t *scp,
4344
const tea_node_t *node);
4445
tea_val_t tea_eval_native_fn_call(tea_ctx_t *ctx, tea_scope_t *scp,
45-
const tea_native_fn_t *nfn,
46+
const tea_native_fn_t *nat_fn,
4647
const tea_node_t *args);
4748

4849
void tea_bind_native_fn(tea_ctx_t *ctx, const char *name,

main.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ void print_usage(const char *program_name)
1919
tea_log_inf(" %s example.tea", program_name);
2020
}
2121

22-
static tea_val_t tea_print(tea_ctx_t *context, const tea_fn_args_t *args)
22+
static tea_val_t tea_print(tea_fn_args_t *args)
2323
{
2424
for (;;) {
2525
tea_var_t *arg = tea_fn_args_pop(args);
@@ -45,17 +45,18 @@ static tea_val_t tea_print(tea_ctx_t *context, const tea_fn_args_t *args)
4545
case TEA_V_UNDEF:
4646
break;
4747
}
48-
49-
tea_free_var(context, arg);
5048
}
5149

5250
return tea_val_undef();
5351
}
5452

55-
static tea_val_t tea_println(tea_ctx_t *context, const tea_fn_args_t *args)
53+
static tea_val_t tea_println(tea_fn_args_t *args)
5654
{
57-
const tea_val_t value = tea_print(context, args);
58-
printf("\n");
55+
const tea_val_t value = tea_print(args);
56+
57+
static const char newline = '\n';
58+
fwrite(&newline, 1, 1, stdout);
59+
5960
return value;
6061
}
6162

src/tea_fn.c

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,13 @@
1010
#include "tea_log.h"
1111
#include "tea_memory.h"
1212

13-
#include "tea_grammar.h"
14-
15-
tea_var_t *tea_fn_args_pop(const tea_fn_args_t *args)
13+
tea_var_t *tea_fn_args_pop(tea_fn_args_t *args)
1614
{
17-
tea_list_entry_t *first = tea_list_first(&args->head);
15+
tea_list_entry_t *first = tea_list_first(&args->args);
1816
if (first) {
1917
tea_var_t *arg = tea_list_record(first, tea_var_t, link);
2018
tea_list_remove(first);
19+
tea_list_add_tail(&args->popped_args, &arg->link);
2120
return arg;
2221
}
2322

@@ -124,12 +123,33 @@ bool tea_interp_fn_decl(tea_ctx_t *ctx, const tea_node_t *node)
124123
return tea_decl_fn(node, &ctx->funcs);
125124
}
126125

126+
static void tea_cleanup_fn_args(tea_ctx_t *ctx, const tea_fn_args_t *fn_args)
127+
{
128+
tea_list_entry_t *cleanup_entry;
129+
tea_list_entry_t *cleanup_safe;
130+
131+
tea_list_for_each_safe(cleanup_entry, cleanup_safe, &fn_args->popped_args)
132+
{
133+
tea_var_t *cleanup_arg = tea_list_record(cleanup_entry, tea_var_t, link);
134+
tea_list_remove(cleanup_entry);
135+
tea_free_var(ctx, cleanup_arg);
136+
}
137+
138+
tea_list_for_each_safe(cleanup_entry, cleanup_safe, &fn_args->args)
139+
{
140+
tea_var_t *cleanup_arg = tea_list_record(cleanup_entry, tea_var_t, link);
141+
tea_list_remove(cleanup_entry);
142+
tea_free_var(ctx, cleanup_arg);
143+
}
144+
}
145+
127146
tea_val_t tea_eval_native_fn_call(tea_ctx_t *ctx, tea_scope_t *scp,
128147
const tea_native_fn_t *nat_fn,
129148
const tea_node_t *args)
130149
{
131150
tea_fn_args_t fn_args;
132-
tea_list_init(&fn_args.head);
151+
tea_list_init(&fn_args.args);
152+
tea_list_init(&fn_args.popped_args);
133153

134154
tea_list_entry_t *arg_entry;
135155
tea_list_for_each(arg_entry, &args->children)
@@ -139,52 +159,26 @@ tea_val_t tea_eval_native_fn_call(tea_ctx_t *ctx, tea_scope_t *scp,
139159
if (!arg) {
140160
tea_log_err(
141161
"Memory error: Failed to allocate variable for function argument");
142-
// Clean up any previously allocated args
143-
tea_list_entry_t *cleanup_entry;
144-
tea_list_entry_t *cleanup_safe;
145-
tea_list_for_each_safe(cleanup_entry, cleanup_safe, &fn_args.head)
146-
{
147-
tea_var_t *cleanup_arg =
148-
tea_list_record(cleanup_entry, tea_var_t, link);
149-
tea_list_remove(cleanup_entry);
150-
tea_free_var(ctx, cleanup_arg);
151-
}
162+
tea_cleanup_fn_args(ctx, &fn_args);
152163
return tea_val_undef();
153164
}
154165
arg->val = tea_eval_expr(ctx, scp, arg_expr);
155166
if (arg->val.type == TEA_V_UNDEF) {
156167
tea_free_var(ctx, arg);
157-
// Clean up any previously allocated args
158-
tea_list_entry_t *cleanup_entry;
159-
tea_list_entry_t *cleanup_safe;
160-
tea_list_for_each_safe(cleanup_entry, cleanup_safe, &fn_args.head)
161-
{
162-
tea_var_t *cleanup_arg =
163-
tea_list_record(cleanup_entry, tea_var_t, link);
164-
tea_list_remove(cleanup_entry);
165-
tea_free_var(ctx, cleanup_arg);
166-
}
168+
tea_cleanup_fn_args(ctx, &fn_args);
167169
return tea_val_undef();
168170
}
171+
169172
// TODO: Check mutability and optionality
170173
arg->flags = 0;
171174
// TODO: Set the proper arg name
172175
arg->name = "unknown";
173176

174-
tea_list_add_tail(&fn_args.head, &arg->link);
177+
tea_list_add_tail(&fn_args.args, &arg->link);
175178
}
176179

177-
const tea_val_t result = nat_fn->cb(ctx, &fn_args);
178-
179-
// Deallocate all the args
180-
tea_list_entry_t *cleanup_entry;
181-
tea_list_entry_t *safe;
182-
tea_list_for_each_safe(cleanup_entry, safe, &fn_args.head)
183-
{
184-
tea_var_t *arg = tea_list_record(cleanup_entry, tea_var_t, link);
185-
tea_list_remove(cleanup_entry);
186-
tea_free_var(ctx, arg);
187-
}
180+
const tea_val_t result = nat_fn->cb(&fn_args);
181+
tea_cleanup_fn_args(ctx, &fn_args);
188182

189183
return result;
190184
}

0 commit comments

Comments
 (0)