diff options
-rw-r--r-- | examples/function_call.pipa | 11 | ||||
-rw-r--r-- | examples/main.pipa | 6 | ||||
-rw-r--r-- | src/ast.c | 54 | ||||
-rw-r--r-- | src/ast.h | 33 | ||||
-rw-r--r-- | src/ast_pretty_printer.c | 6 | ||||
-rw-r--r-- | src/gas_assembly_generator.c | 50 | ||||
-rw-r--r-- | src/lexer.c | 8 | ||||
-rw-r--r-- | src/lexer.h | 1 | ||||
-rw-r--r-- | src/parser.c | 95 | ||||
-rw-r--r-- | test/integration_test.c | 1 | ||||
-rw-r--r-- | test/parser_test.c | 6 |
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; } @@ -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) { @@ -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, ¶meter->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, ¶meter->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'"); |