From 97aa61e7c4f3b05a1147ae87748a47dc67b9f0d4 Mon Sep 17 00:00:00 2001 From: Aymeric Wibo Date: Sat, 31 Aug 2024 19:17:47 +0200 Subject: [PATCH] call: Pass arguments to functions --- build.sh | 2 +- flamingo/flamingo.h | 3 +- flamingo/grammar/assignment.h | 6 +-- flamingo/grammar/call.h | 65 +++++++++++++++++++++++-- flamingo/grammar/class_declaration.h | 5 +- flamingo/grammar/function_declaration.h | 30 ++++++++++-- hello_world.fl | 8 +-- 7 files changed, 98 insertions(+), 21 deletions(-) diff --git a/build.sh b/build.sh index 77e8a3e..ecef616 100644 --- a/build.sh +++ b/build.sh @@ -14,6 +14,6 @@ cc $cc_flags -c main.c -o bin/main.o cc $(find bin -name "*.o") $cc_flags -o bin/flamingo -bin/flamingo std.fl # hello_world.fl +bin/flamingo hello_world.fl # TODO Run more "serious" tests too. diff --git a/flamingo/flamingo.h b/flamingo/flamingo.h index aedf608..a43a10d 100644 --- a/flamingo/flamingo.h +++ b/flamingo/flamingo.h @@ -34,7 +34,7 @@ typedef struct { struct { void* body; - size_t body_size; + void* params; // Functions can be defined in other files entirely. // While the Tree-sitter state is held within the nodes themselves, the source they point to is not, which is why we need to keep track of it here. @@ -45,7 +45,6 @@ typedef struct { struct { void* body; - size_t body_size; // Ditto as for functions. diff --git a/flamingo/grammar/assignment.h b/flamingo/grammar/assignment.h index e5bd2ad..86de5bf 100644 --- a/flamingo/grammar/assignment.h +++ b/flamingo/grammar/assignment.h @@ -3,8 +3,6 @@ #pragma once -#include "expr.h" - #include #include #include @@ -45,8 +43,8 @@ static int parse_assignment(flamingo_t* flamingo, TSNode node) { var = scope_add_var(cur_scope(flamingo), identifier, size); } - else if (var->val->kind == FLAMINGO_VAL_KIND_FN) { - return error(flamingo, "cannot assign to function '%.*s'", (int) size, identifier); + else if (var->val->kind == FLAMINGO_VAL_KIND_FN || var->val->kind == FLAMINGO_VAL_KIND_CLASS) { + return error(flamingo, "cannot assign to %s '%.*s'", val_kind_str(var->val), (int) size, identifier); } // If variable is already in current or previous scope, since we're assigning a new value to it, we must decrement the reference counter of the previous value which was in the variable. diff --git a/flamingo/grammar/call.h b/flamingo/grammar/call.h index 9709520..18d46f6 100644 --- a/flamingo/grammar/call.h +++ b/flamingo/grammar/call.h @@ -11,7 +11,7 @@ static int parse_call(flamingo_t* flamingo, TSNode node, flamingo_val_t** val) { assert(strcmp(ts_node_type(node), "call") == 0); - assert(ts_node_child_count(node) == 3); + assert(ts_node_child_count(node) == 3 || ts_node_child_count(node) == 4); // Get callable expression. // TODO Evaluate this motherfucker. @@ -24,9 +24,8 @@ static int parse_call(flamingo_t* flamingo, TSNode node, flamingo_val_t** val) { } // Get arguments. - // TODO Do something with these arguments. - TSNode const args = ts_node_child_by_field_name(node, "args", 6); + TSNode const args = ts_node_child_by_field_name(node, "args", 4); bool const has_args = !ts_node_is_null(args); if (has_args) { @@ -71,15 +70,75 @@ static int parse_call(flamingo_t* flamingo, TSNode node, flamingo_val_t** val) { } */ + // Switch context's source if the function was created in another. + char* const prev_src = flamingo->src; size_t const prev_src_size = flamingo->src_size; flamingo->src = callable->fn.src; flamingo->src_size = callable->fn.src_size; + // Create a new scope for the function for the argument assignments. + + scope_stack_push(flamingo); + + // Evaluate argument expressions. + // Add our arguments as variables, with the function parameters as names. + + TSNode* const params = callable->fn.params; + size_t const param_count = params == NULL ? 0 : ts_node_child_count(*params); + + if (has_args) { + size_t const n = ts_node_named_child_count(args); + + if (n != param_count) { + return error(flamingo, "callable expected %zu arguments, got %zu instead", param_count, n); + } + + for (size_t i = 0; i < n; i++) { + // Get argument. + + TSNode const arg = ts_node_named_child(args, i); + char const* const arg_type = ts_node_type(arg); + + if (strcmp(arg_type, "expression") != 0) { + return error(flamingo, "expected expression in argument list, got %s", arg_type); + } + + // Get parameter in same position. + // Type should already have been checked when declaring the function. + + TSNode const param = ts_node_named_child(*params, i); + char const* const param_type = ts_node_type(param); + assert(strcmp(param_type, "param") == 0); + + size_t const start = ts_node_start_byte(param); + size_t const end = ts_node_end_byte(param); + + char const* const identifier = flamingo->src + start; + size_t const size = end - start; + + // Create parameter variable. + + flamingo_var_t* const var = scope_add_var(cur_scope(flamingo), identifier, size); + + // Parse argument expression into that parameter variable. + + if (parse_expr(flamingo, arg, &var->val) < 0) { + return -1; + } + } + } + + // Actually parse the function's body. + TSNode* const body = callable->fn.body; int const rv = parse_statement(flamingo, *body); + // Unwind the scope stack and switch back to previous source context. + + scope_pop(flamingo); + flamingo->src = prev_src; flamingo->src_size = prev_src_size; diff --git a/flamingo/grammar/class_declaration.h b/flamingo/grammar/class_declaration.h index be9cde8..0c3a194 100644 --- a/flamingo/grammar/class_declaration.h +++ b/flamingo/grammar/class_declaration.h @@ -67,9 +67,8 @@ static int parse_class_declaration(flamingo_t* flamingo, TSNode node) { // Assign body node. // See comment in function declaration. - var->val->class.body_size = sizeof body; - var->val->class.body = malloc(var->val->class.body_size); - memcpy(var->val->class.body, &body, var->val->class.body_size); + var->val->class.body = malloc(sizeof body); + memcpy(var->val->class.body, &body, sizeof body); var->val->class.src = flamingo->src; var->val->class.src_size = flamingo->src_size; diff --git a/flamingo/grammar/function_declaration.h b/flamingo/grammar/function_declaration.h index 06a8f74..1fae3d0 100644 --- a/flamingo/grammar/function_declaration.h +++ b/flamingo/grammar/function_declaration.h @@ -8,7 +8,7 @@ #include static int parse_function_declaration(flamingo_t* flamingo, TSNode node) { - assert(ts_node_child_count(node) == 5); + assert(ts_node_child_count(node) == 5 || ts_node_child_count(node) == 6); // Get qualifier list. @@ -39,7 +39,6 @@ static int parse_function_declaration(flamingo_t* flamingo, TSNode node) { size_t const size = end - start; // Get function parameters. - // TODO Do something with these parameters. TSNode const params = ts_node_child_by_field_name(node, "params", 6); bool const has_params = !ts_node_is_null(params); @@ -61,6 +60,21 @@ static int parse_function_declaration(flamingo_t* flamingo, TSNode node) { return error(flamingo, "expected statement for body, got %s", body_type); } + // Check parameter types. + + if (has_params) { + size_t const n = ts_node_child_count(params); + + for (size_t i = 0; i < n; i++) { + TSNode const child = ts_node_named_child(params, i); + char const* const child_type = ts_node_type(child); + + if (strcmp(child_type, "param") != 0) { + return error(flamingo, "expected param in parameter list, got %s", child_type); + } + } + } + // Check if identifier is already in scope (or a previous one) and error if it is. // Right now, redeclaring functions is not allowed. // Although this will probably work a bit differently once function prototypes are added. @@ -82,9 +96,15 @@ static int parse_function_declaration(flamingo_t* flamingo, TSNode node) { // Since I want 'flamingo.h' to be usable without importing all of Tree-sitter, 'var->val->fn.body' can't just be a 'TSNode'. // Thus, since only this file knows about the size of 'TSNode', we must dynamically allocate this on the heap. - var->val->fn.body_size = sizeof body; - var->val->fn.body = malloc(var->val->fn.body_size); - memcpy(var->val->fn.body, &body, var->val->fn.body_size); + var->val->fn.body = malloc(sizeof body); + memcpy(var->val->fn.body, &body, sizeof body); + + var->val->fn.params = NULL; + + if (has_params) { + var->val->fn.params = malloc(sizeof params); + memcpy(var->val->fn.params, ¶ms, sizeof params); + } var->val->fn.src = flamingo->src; var->val->fn.src_size = flamingo->src_size; diff --git a/hello_world.fl b/hello_world.fl index 81b4cac..5792a31 100644 --- a/hello_world.fl +++ b/hello_world.fl @@ -1,5 +1,7 @@ -import .std +# import .std -print a +fn do_nothing(a) { + print a +} -function_in_other_file() +do_nothing("test")