Skip to content

Commit

Permalink
call: Pass arguments to functions
Browse files Browse the repository at this point in the history
  • Loading branch information
obiwac committed Aug 31, 2024
1 parent d0d96e0 commit 97aa61e
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 21 deletions.
2 changes: 1 addition & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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.
3 changes: 1 addition & 2 deletions flamingo/flamingo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -45,7 +45,6 @@ typedef struct {

struct {
void* body;
size_t body_size;

// Ditto as for functions.

Expand Down
6 changes: 2 additions & 4 deletions flamingo/grammar/assignment.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

#pragma once

#include "expr.h"

#include <common.h>
#include <scope.c>
#include <val.c>
Expand Down Expand Up @@ -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.
Expand Down
65 changes: 62 additions & 3 deletions flamingo/grammar/call.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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) {
Expand Down Expand Up @@ -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;

Expand Down
5 changes: 2 additions & 3 deletions flamingo/grammar/class_declaration.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
30 changes: 25 additions & 5 deletions flamingo/grammar/function_declaration.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <val.c>

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.

Expand Down Expand Up @@ -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);
Expand All @@ -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.
Expand All @@ -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, &params, sizeof params);
}

var->val->fn.src = flamingo->src;
var->val->fn.src_size = flamingo->src_size;
Expand Down
8 changes: 5 additions & 3 deletions hello_world.fl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import .std
# import .std

print a
fn do_nothing(a) {
print a
}

function_in_other_file()
do_nothing("test")

0 comments on commit 97aa61e

Please sign in to comment.