summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/function_call.pipa11
-rw-r--r--examples/main.pipa6
-rw-r--r--src/ast.c54
-rw-r--r--src/ast.h33
-rw-r--r--src/ast_pretty_printer.c6
-rw-r--r--src/gas_assembly_generator.c50
-rw-r--r--src/lexer.c8
-rw-r--r--src/lexer.h1
-rw-r--r--src/parser.c95
-rw-r--r--test/integration_test.c1
-rw-r--r--test/parser_test.c6
11 files changed, 241 insertions, 30 deletions
diff --git a/examples/function_call.pipa b/examples/function_call.pipa
new file mode 100644
index 0000000..833c195
--- /dev/null
+++ b/examples/function_call.pipa
@@ -0,0 +1,11 @@
+fn add(n: i32, n2: i32): i32 {
+ return n + n2;
+}
+
+fn id(n: i32): i32 {
+ return n;
+}
+
+fn main(): i32 {
+ return id(add(40, 44)) / 2;
+}
diff --git a/examples/main.pipa b/examples/main.pipa
index 5ea0077..2da2231 100644
--- a/examples/main.pipa
+++ b/examples/main.pipa
@@ -1,7 +1,3 @@
-fn give_me_the_number(): i32 {
- return 69;
-}
-
fn main(): i32 {
- return give_me_the_number();
+ return 69;
}
diff --git a/src/ast.c b/src/ast.c
index e9aa677..f6f8f08 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -49,6 +49,7 @@ ast_node_destroy(ast_node_t *node)
break;
case AST_FUNCTION_DECLARATION:
ast_node_destroy(node->data.function.body);
+ ast_node_destroy_vector(node->data.function.prototype.parameters);
break;
case AST_IF_STMT:
ast_node_destroy(node->data.if_stmt.condition);
@@ -69,6 +70,7 @@ ast_node_destroy(ast_node_t *node)
break;
case AST_VARIABLE_ASSIGNMENT:
ast_node_destroy(node->data.variable_assignment.expression);
+ case AST_FUNCTION_PARAMETER:
case AST_LITERAL:
case AST_UNKOWN_NODE:
case AST_VARIABLE:
@@ -93,7 +95,30 @@ ast_node_new_return_stmt(ast_node_t *argument)
}
ast_node_t *
-ast_node_new_function_declaration(string_view_t function_name, type_t return_type, ast_node_t *body)
+ast_node_new_function_parameter(string_view_t name, type_t type)
+{
+ ast_node_t *node = ast_node_new();
+
+ *node = (ast_node_t){
+ .kind = AST_FUNCTION_PARAMETER,
+ .data = {
+ .function_parameter = {
+ .identifier = {
+ .name = name,
+ },
+ .type = type,
+ },
+ },
+ };
+
+ return node;
+}
+
+ast_node_t *
+ast_node_new_function_declaration(string_view_t function_name,
+ type_t return_type,
+ vector_t *parameters,
+ ast_node_t *body)
{
ast_node_t *node = ast_node_new();
@@ -101,8 +126,11 @@ ast_node_new_function_declaration(string_view_t function_name, type_t return_typ
.kind = AST_FUNCTION_DECLARATION,
.data = {
.function = {
- .identifier = { .name = function_name },
- .return_type = return_type,
+ .prototype = {
+ .identifier = { .name = function_name },
+ .return_type = return_type,
+ .parameters = parameters,
+ },
.body = body,
}
},
@@ -277,14 +305,14 @@ ast_node_new_variable(ast_identifier_t *identifier, type_t result_type)
}
ast_node_t *
-ast_node_new_function_call(ast_identifier_t *identifier, type_t result_type)
+ast_node_new_function_call(ast_identifier_t *identifier, type_t result_type, vector_t *arguments)
{
ast_node_t *node = ast_node_new();
*node = (ast_node_t){
.kind = AST_FUNCTION_CALL,
.result_type = result_type,
- .data = { .function_call = { .identifier = identifier } },
+ .data = { .function_call = { .identifier = identifier, .arguments = arguments } },
};
return node;
@@ -298,7 +326,7 @@ ast_node_ns_get_function_node_by_sv(ast_node_t *ns, string_view_t name)
for (size_t i = 0; i < ns->data.ns.nodes->size; i++) {
ast_node_t *node = vector_at(ns->data.ns.nodes, i);
- if (node->kind == AST_FUNCTION_DECLARATION && string_view_eq(node->data.function.identifier.name, name)) {
+ if (node->kind == AST_FUNCTION_DECLARATION && string_view_eq(node->data.function.prototype.identifier.name, name)) {
return node;
}
}
@@ -323,6 +351,20 @@ ast_node_is_function_declaration(ast_node_t *node)
return node->kind == AST_FUNCTION_DECLARATION;
}
+ast_identifier_t *
+ast_node_function_declaration_identifier(ast_node_t *node)
+{
+ assert(node->kind == AST_FUNCTION_DECLARATION);
+
+ return &node->data.function.prototype.identifier;
+}
+
+string_view_t
+ast_node_function_declaration_name(ast_node_t *node)
+{
+ return ast_node_function_declaration_identifier(node)->name;
+}
+
char *
ast_type_to_str(type_t type)
{
diff --git a/src/ast.h b/src/ast.h
index 315de1e..9e0e514 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -57,12 +57,25 @@ typedef struct ast_variable_t
typedef struct ast_function_call_t
{
ast_identifier_t *identifier;
+ vector_t *arguments;
} ast_function_call_t;
-typedef struct ast_function_declaration_t
+typedef struct ast_function_parameter_t
+{
+ ast_identifier_t identifier;
+ type_t type;
+} ast_function_parameter_t;
+
+typedef struct ast_function_prototype_t
{
ast_identifier_t identifier;
type_t return_type;
+ vector_t *parameters;
+} ast_function_prototype_t;
+
+typedef struct ast_function_declaration_t
+{
+ ast_function_prototype_t prototype;
ast_node_t *body;
} ast_function_declaration_t;
@@ -133,6 +146,7 @@ typedef enum
AST_BINARY_OPERATION,
AST_BLOCK,
AST_FUNCTION_DECLARATION,
+ AST_FUNCTION_PARAMETER,
AST_FUNCTION_CALL,
AST_LITERAL,
AST_RETURN_STMT,
@@ -148,6 +162,7 @@ typedef union
ast_namespace_t ns;
ast_binary_operation_t binary_operation;
ast_function_declaration_t function;
+ ast_function_parameter_t function_parameter;
ast_function_call_t function_call;
ast_literal_t literal;
ast_block_t block;
@@ -187,10 +202,16 @@ ast_node_new_binary_operation(ast_binary_operation_kind_t kind,
type_t result_type);
ast_node_t *
-ast_node_new_function_declaration(string_view_t function_name, type_t return_type, ast_node_t *body);
+ast_node_new_function_declaration(string_view_t function_name,
+ type_t return_type,
+ vector_t *parameters,
+ ast_node_t *body);
+
+ast_node_t *
+ast_node_new_function_parameter(string_view_t name, type_t type);
ast_node_t *
-ast_node_new_function_call(ast_identifier_t *identifier, type_t result_type);
+ast_node_new_function_call(ast_identifier_t *identifier, type_t result_type, vector_t *arguments);
ast_node_t *
ast_node_new_return_stmt(ast_node_t *argument);
@@ -228,4 +249,10 @@ ast_node_is_variable_declaration(ast_node_t *node);
bool
ast_node_is_function_declaration(ast_node_t *node);
+ast_identifier_t *
+ast_node_function_declaration_identifier(ast_node_t *node);
+
+string_view_t
+ast_node_function_declaration_name(ast_node_t *node);
+
#endif /* AST_H */
diff --git a/src/ast_pretty_printer.c b/src/ast_pretty_printer.c
index 0d8b66c..a211fa4 100644
--- a/src/ast_pretty_printer.c
+++ b/src/ast_pretty_printer.c
@@ -65,6 +65,8 @@ ast_pretty_printer_print_ast(ast_pretty_printer_t *printer, ast_node_t *ast)
assert(ast);
switch (ast->kind) {
+ case AST_FUNCTION_PARAMETER:
+ break;
case AST_NAMESPACE:
ast_pretty_printer_printf(printer, "Namespace\n");
ast_pretty_printer_add_indentation(printer);
@@ -136,8 +138,8 @@ ast_pretty_printer_print_ast(ast_pretty_printer_t *printer, ast_node_t *ast)
break;
}
case AST_FUNCTION_DECLARATION: {
- ast_function_declaration_t function = ast->data.function;
- ast_pretty_printer_printf(printer, "FunctionDecl name='" SVFMT "'\n", SVARG(&function.identifier.name));
+ string_view_t function_name = ast_node_function_declaration_name(ast);
+ ast_pretty_printer_printf(printer, "FunctionDecl name='" SVFMT "'\n", SVARG(&function_name));
ast_pretty_printer_add_indentation(printer);
{
diff --git a/src/gas_assembly_generator.c b/src/gas_assembly_generator.c
index 2d6b993..26bf3f9 100644
--- a/src/gas_assembly_generator.c
+++ b/src/gas_assembly_generator.c
@@ -22,6 +22,9 @@
#include <stdlib.h>
#include <string.h>
+size_t available_calling_registers = 6;
+char *calling_registers[] = { "rdi", "rsi", "rdx", "rcx", "r8", "r9" };
+
static void
gas_assembly_generator_binary_operation(gas_assembly_generator_t *gen, ast_binary_operation_t *binary_operation);
@@ -183,6 +186,7 @@ gas_assembly_generator_compile(gas_assembly_generator_t *gen, ast_node_t *ast)
case AST_IF_STMT:
gas_assembly_generator_compile_if_stmt(gen, &ast->data.if_stmt);
break;
+ case AST_FUNCTION_PARAMETER:
case AST_UNKOWN_NODE:
assert(false && "unreachable");
}
@@ -204,10 +208,27 @@ gas_assembly_generator_compile_function(gas_assembly_generator_t *gen, ast_funct
uint64_t previous_index = gen->return_label_index;
uint64_t return_label_index = gen->return_label_index = gen->counter_label_index++;
- fprintf(gen->stream, SVFMT ":\n", SVARG(&func->identifier.name));
+ assert((func->prototype.parameters->size <= available_calling_registers) &&
+ "Not enough registers to process this function. Stack parameters will come soon.");
+
+ fprintf(gen->stream, SVFMT ":\n", SVARG(&func->prototype.identifier.name));
fprintf(gen->stream, " push %%rbp\n");
fprintf(gen->stream, " mov %%rsp, %%rbp\n");
+ for (size_t i = 0; i < func->prototype.parameters->size; i++) {
+ char *reg = calling_registers[i];
+ ast_node_t *parameter = vector_at(func->prototype.parameters, i);
+
+ assert(parameter->kind == AST_FUNCTION_PARAMETER);
+
+ gen->stack_offset -= 8;
+
+ fprintf(gen->stream, " mov %%%s, %d(%%rbp)\n", reg, gen->stack_offset);
+
+ ref_set_variable_reference_stack_offset(
+ gen->refs, &parameter->data.function_parameter.identifier, gen->stack_offset);
+ }
+
gas_assembly_generator_compile(gen, func->body);
fprintf(gen->stream, ".L%ld:\n", return_label_index);
@@ -215,11 +236,38 @@ gas_assembly_generator_compile_function(gas_assembly_generator_t *gen, ast_funct
fprintf(gen->stream, " ret\n");
gen->return_label_index = previous_index;
+ gen->stack_offset = 0;
}
static void
gas_assembly_generator_compile_function_call(gas_assembly_generator_t *gen, ast_function_call_t *func_call)
{
+ assert((func_call->arguments->size <= available_calling_registers) &&
+ "Not enough registers to process this function. Stack parameters will come soon.");
+
+ for (size_t i = 0; i < func_call->arguments->size; i++) {
+ char *reg = calling_registers[i];
+ ast_node_t *argument = vector_at(func_call->arguments, i);
+
+ gas_assembly_generator_compile(gen, argument);
+
+ switch (gen->latest_evaluation.kind) {
+ case EVALUATION_RESULT_AS_LITERAL_INTEGER:
+ fprintf(gen->stream, " mov $%ld, %%%s\n", gen->latest_evaluation.data.literal_int, reg);
+ break;
+ case EVALUATION_RESULT_ON_RAX:
+ fprintf(gen->stream, " mov %%rax, %%%s\n", reg);
+ break;
+ case EVALUATION_RESULT_ON_STACK:
+ fprintf(gen->stream, " mov %d(%%rbp), %%%s\n", gen->latest_evaluation.data.stack_offset, reg);
+ break;
+ case EVALUATION_RESULT_AS_LITERAL_BOOL:
+ fprintf(gen->stream, " mov %d(%%rbp), %%%s\n", gen->latest_evaluation.data.literal_bool, reg);
+ break;
+ case EVALUATION_RESULT_VOID:
+ assert(false && "Unexpected");
+ }
+ }
fprintf(gen->stream, " call " SVFMT "\n", SVARG(&func_call->identifier->name));
gas_assembly_generator_set_latest_evaluation_to_rax(gen);
}
diff --git a/src/lexer.c b/src/lexer.c
index a6c8ae2..7c668d3 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -165,6 +165,12 @@ lexer_next_token(lexer_t *lexer, token_t *token)
return;
}
+ if (lexer_current_char(lexer) == ',') {
+ lexer_define_literal_token_props(lexer, token, TOKEN_COMMA);
+ lexer_drop_char(lexer);
+ return;
+ }
+
if (lexer_current_char(lexer) == ';') {
lexer_define_literal_token_props(lexer, token, TOKEN_SEMICOLON);
lexer_drop_char(lexer);
@@ -412,6 +418,8 @@ token_kind_to_str(token_kind_t kind)
return ")";
case TOKEN_COLON:
return ":";
+ case TOKEN_COMMA:
+ return ",";
case TOKEN_SEMICOLON:
return ";";
case TOKEN_OCURLY:
diff --git a/src/lexer.h b/src/lexer.h
index 0e36ada..46912dc 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -32,6 +32,7 @@ typedef enum
// Literal Tokens
TOKEN_OPAREN,
TOKEN_CPAREN,
+ TOKEN_COMMA,
TOKEN_COLON,
TOKEN_SEMICOLON,
TOKEN_OCURLY,
diff --git a/src/parser.c b/src/parser.c
index 377ff73..fe2a2f0 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -33,6 +33,9 @@ parser_expression_matches_the_expected_type(parser_t *parser, ast_node_t *expres
static ast_node_t *
parser_parse_block_declarations(parser_t *parser, type_t result_type);
+static bool
+is_next_token_cparen(parser_t *parser);
+
void
parser_init(parser_t *parser, lexer_t *lexer, scope_t *scope)
{
@@ -354,16 +357,40 @@ parser_parse_factor(parser_t *parser)
referenced_node->data.variable_declaration.type);
}
+ // Function parameters are also variables.
+ if (referenced_node->kind == AST_FUNCTION_PARAMETER) {
+ return ast_node_new_variable(&referenced_node->data.function_parameter.identifier,
+ referenced_node->data.function_parameter.type);
+ }
+
// Parse function parameters
if (!drop_expected_token(parser, TOKEN_OPAREN)) {
return NULL;
}
- if (!drop_expected_token(parser, TOKEN_CPAREN)) {
- return NULL;
+
+ vector_t *arguments = vector_new();
+
+ while (!is_next_token_cparen(parser)) {
+ ast_node_t *argument = parser_parse_expression(parser);
+
+ if (argument == NULL) {
+ ast_node_destroy_vector(arguments);
+ return NULL;
+ }
+
+ vector_push_back(arguments, argument);
+
+ if (!is_next_token_cparen(parser) && !drop_expected_token(parser, TOKEN_COMMA)) {
+ ast_node_destroy_vector(arguments);
+ return NULL;
+ }
}
- return ast_node_new_function_call(&referenced_node->data.function.identifier,
- referenced_node->data.function.return_type);
+ drop_expected_token(parser, TOKEN_CPAREN);
+
+ return ast_node_new_function_call(ast_node_function_declaration_identifier(referenced_node),
+ referenced_node->data.function.prototype.return_type,
+ arguments);
}
default: {
parser_error_t error;
@@ -591,6 +618,14 @@ is_next_function_declaration(parser_t *parser)
}
static bool
+is_next_token_cparen(parser_t *parser)
+{
+ token_t token;
+ lexer_peek_next_token(parser->lexer, &token);
+ return token.kind == TOKEN_CPAREN;
+}
+
+static bool
is_next_token_eof(parser_t *parser)
{
token_t token;
@@ -722,10 +757,48 @@ parser_parse_block_declarations(parser_t *parser, type_t result_type)
return ast_node_new_block(body);
}
-static bool
-parser_parse_function_arguments(parser_t *parser)
+static vector_t *
+parser_parse_function_parameters(parser_t *parser)
{
- return drop_expected_token(parser, TOKEN_OPAREN) && drop_expected_token(parser, TOKEN_CPAREN);
+ if (!drop_expected_token(parser, TOKEN_OPAREN)) {
+ return NULL;
+ }
+
+ vector_t *vector = vector_new();
+
+ while (!is_next_token_cparen(parser)) {
+ token_t token_name;
+ type_t type;
+
+ if (!expected_token(&token_name, parser, TOKEN_NAME)) {
+ ast_node_destroy_vector(vector);
+ return NULL;
+ }
+ if (!drop_expected_token(parser, TOKEN_COLON)) {
+ ast_node_destroy_vector(vector);
+ return NULL;
+ }
+
+ if (!parser_parse_type(parser, &type)) {
+ ast_node_destroy_vector(vector);
+ return NULL;
+ }
+
+ ast_node_t *parameter = ast_node_new_function_parameter(token_name.value, type);
+
+ scope_push(parser->scope, &parameter->data.function_parameter.identifier, parameter);
+
+ vector_push_back(vector, parameter);
+
+ if (!is_next_token_cparen(parser) && !drop_expected_token(parser, TOKEN_COMMA)) {
+ ast_node_destroy_vector(vector);
+ return NULL;
+ }
+ }
+
+ drop_expected_token(parser, TOKEN_CPAREN);
+
+ return vector;
}
ast_node_t *
@@ -741,7 +814,9 @@ parser_parse_function_declaration(parser_t *parser)
return NULL;
}
- if (!parser_parse_function_arguments(parser)) {
+ vector_t *parameters = parser_parse_function_parameters(parser);
+
+ if (parameters == NULL) {
return NULL;
}
@@ -766,11 +841,11 @@ parser_parse_function_declaration(parser_t *parser)
return NULL;
}
- ast_node_t *node = ast_node_new_function_declaration(func_name_token.value, return_type, body);
+ ast_node_t *node = ast_node_new_function_declaration(func_name_token.value, return_type, parameters, body);
// TODO: When implementing function calls the scope must be pushed before the
// body to be parsed, otherwise recursion not gonna work.
- scope_push(parser->scope, &node->data.function.identifier, node);
+ scope_push(parser->scope, &node->data.function.prototype.identifier, node);
return node;
}
diff --git a/test/integration_test.c b/test/integration_test.c
index 985e574..608764a 100644
--- a/test/integration_test.c
+++ b/test/integration_test.c
@@ -44,6 +44,7 @@ test_examples(const MunitParameter params[], void *user_data_or_fixture)
assert_exit_status("../examples/arithmetics.pipa", 13);
assert_exit_status("../examples/variables.pipa", 28);
assert_exit_status("../examples/if.pipa", 42);
+ assert_exit_status("../examples/function_call.pipa", 42);
return MUNIT_OK;
}
diff --git a/test/parser_test.c b/test/parser_test.c
index ebb917c..44ffd57 100644
--- a/test/parser_test.c
+++ b/test/parser_test.c
@@ -104,7 +104,7 @@ test_parse_variable_definition(const MunitParameter params[], void *user_data_or
char actual[5];
- string_view_to_str(&ast_function->data.function.identifier.name, actual);
+ string_view_to_str(&ast_function->data.function.prototype.identifier.name, actual);
assert_string_equal("main", actual);
assert_int(AST_FUNCTION_DECLARATION, ==, ast_function->kind);
@@ -141,7 +141,7 @@ test_parse_boolean(const MunitParameter params[], void *user_data_or_fixture)
assert_true(ast_function != NULL);
- assert_string_view_equal("my_bool_fn", ast_function->data.function.identifier.name);
+ assert_string_view_equal("my_bool_fn", ast_node_function_declaration_name(ast_function));
assert_int(AST_FUNCTION_DECLARATION, ==, ast_function->kind);
ast_node_t *ast_variable = vector_at(ast_function->data.function.body->data.block.body, 0);
@@ -397,7 +397,7 @@ test_parse_basic_syntax_errors(const MunitParameter params[], void *user_data_or
assert_parser_error("main(): i32 { return 42; }", "Unexpected token 'main'");
assert_parser_error("fn (): i32 { return 42; }", "expected 'TOKEN_NAME' but got '('");
assert_parser_error("fn main): i32 { return 42; }", "expected '(' but got ')'");
- assert_parser_error("fn main(: i32 { return 42; }", "expected ')' but got ':'");
+ assert_parser_error("fn main(: i32 { return 42; }", "expected 'TOKEN_NAME' but got ':'");
assert_parser_error("fn main() i32 { return 42; }", "expected ':' but got 'TOKEN_NAME'");
assert_parser_error("fn main(): { return 42; }", "expected 'TOKEN_NAME' but got '{'");
assert_parser_error("fn main(): i32 return 42; }", "expected '{' but got 'return'");