diff options
author | Johnny Richard <johnny@johnnyrichard.com> | 2023-04-30 03:11:00 -0300 |
---|---|---|
committer | Johnny Richard <johnny@johnnyrichard.com> | 2023-04-30 17:40:01 +0200 |
commit | b1f7b0cccc6986ca107d9eeb1c7d5e5e5a32dc49 (patch) | |
tree | fb1eb8192c3f6da14796d59831030dd23935edcd | |
parent | 090da456910429708ef7ee202b627e3dbce17819 (diff) |
gas: Compile variable expression with scope support
This patch adds the variable compilation and uses a scope (a stack of
map) to lookup for identities.
Today we use a vector + ref_entry structs in order to achieve the scope
implementation. The ref_entry lacks memory management, we are still no
sure who will be the owner of the pointer.
We also want to replace the scope a hashtable_t type as soon as we get
one.
Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
Co-authored-by: Carlos Maniero <carlosmaniero@gmail.com>
-rw-r--r-- | examples/variables.pipa | 7 | ||||
-rw-r--r-- | src/ast.c | 7 | ||||
-rw-r--r-- | src/gas_assembly_generator.c | 81 | ||||
-rw-r--r-- | src/gas_assembly_generator.h | 3 | ||||
-rw-r--r-- | src/parser.c | 9 | ||||
-rw-r--r-- | test/integration_test.c | 1 | ||||
-rw-r--r-- | test/parser_test.c | 4 |
7 files changed, 95 insertions, 17 deletions
diff --git a/examples/variables.pipa b/examples/variables.pipa new file mode 100644 index 0000000..9c395ce --- /dev/null +++ b/examples/variables.pipa @@ -0,0 +1,7 @@ +main(): i32 { + a: i32 = 12; + b: i32 = 32; + c: i32 = 2 * (b + a); + d: i32 = (c - 33) + 1; + return d / 2; +} @@ -54,17 +54,14 @@ ast_node_destroy(ast_node_t *node) case AST_VARIABLE_DECLARATION: ast_node_destroy(node->data.variable_declaration.value); break; - case AST_LITERAL: - break; case AST_RETURN_STMT: ast_node_destroy(node->data.return_stmt.argument); break; + case AST_LITERAL: case AST_IDENTIFIER: - break; case AST_UNKOWN_NODE: + case AST_VARIABLE: break; - default: - assert(false && "unmapped free strategy"); } free(node); } diff --git a/src/gas_assembly_generator.c b/src/gas_assembly_generator.c index f09b60d..5091120 100644 --- a/src/gas_assembly_generator.c +++ b/src/gas_assembly_generator.c @@ -17,8 +17,10 @@ #include "gas_assembly_generator.h" #include <assert.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> static void gas_assembly_generator_binary_operation(gas_assembly_generator_t *gen, ast_binary_operation_t *binary_operation); @@ -32,11 +34,41 @@ gas_assembly_generator_compile_literal(gas_assembly_generator_t *gen, ast_litera static void gas_assembly_generator_compile_return_stmt(gas_assembly_generator_t *gen, ast_return_stmt_t *return_stmt); +static void +gas_assembly_generator_compile_variable_declaration(gas_assembly_generator_t *gen, + ast_variable_declaration_t *variable_declaration); + +static void +gas_assembly_generator_compile_variable(gas_assembly_generator_t *gen, ast_variable_t *variable); + +typedef struct ref_entry_t +{ + ast_identifier_t *id; + int stack_offset; +} ref_entry_t; + +// TODO: We need a destroy function for ref_entry. +static ref_entry_t * +ref_entry_new(void) +{ + ref_entry_t *entry = (ref_entry_t *)malloc(sizeof(ref_entry_t)); + + if (entry == NULL) { + fprintf(stderr, "ref_entry_new: Out of memory: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + return entry; +} + + void gas_assembly_generator_init(gas_assembly_generator_t *gen, FILE *stream) { assert(gen && stream); gen->stream = stream; + gen->refs = vector_new(); + gen->stack_offset = 0; } void @@ -59,10 +91,10 @@ gas_assembly_generator_compile(gas_assembly_generator_t *gen, ast_node_t *ast) assert(false && "TODO: ast identifier not implemented yet"); break; case AST_VARIABLE_DECLARATION: - assert(false && "TODO: ast variable declaration not implemented yet"); + gas_assembly_generator_compile_variable_declaration(gen, &ast->data.variable_declaration); break; case AST_VARIABLE: - assert(false && "TODO: ast variable not implemented yet"); + gas_assembly_generator_compile_variable(gen, &ast->data.variable); break; case AST_UNKOWN_NODE: assert(false && "unreachable"); @@ -82,10 +114,14 @@ gas_assembly_generator_compile_function(gas_assembly_generator_t *gen, ast_funct fprintf(gen->stream, ".global _start\n"); fprintf(gen->stream, ".text\n"); fprintf(gen->stream, "_start:\n"); + fprintf(gen->stream, " push %%rbp\n"); + fprintf(gen->stream, " mov %%rsp, %%rbp\n"); for (size_t i = 0; i < func->body->size; i++) { gas_assembly_generator_compile(gen, vector_at(func->body, i)); } + + fprintf(gen->stream, " pop %%rbp\n"); } static void @@ -95,9 +131,38 @@ gas_assembly_generator_compile_return_stmt(gas_assembly_generator_t *gen, ast_re gas_assembly_generator_compile(gen, return_stmt->argument); - fprintf(gen->stream, " mov %%rax, %%rbx\n"); - fprintf(gen->stream, " mov $1, %%al\n"); - fprintf(gen->stream, " int $0x80\n"); + fprintf(gen->stream, " mov %%rax, %%rdi\n"); + fprintf(gen->stream, " mov $60, %%rax\n"); + fprintf(gen->stream, " syscall\n"); +} + +static void +gas_assembly_generator_compile_variable_declaration(gas_assembly_generator_t *gen, + ast_variable_declaration_t *variable_declaration) +{ + gen->stack_offset -= 8; + gas_assembly_generator_compile(gen, variable_declaration->value); + + ref_entry_t *entry = ref_entry_new(); + *entry = (ref_entry_t){ .id = &variable_declaration->identifier, .stack_offset = gen->stack_offset }; + vector_push_back(gen->refs, entry); + + fprintf(gen->stream, " mov %%rax, %d(%%rbp)\n", entry->stack_offset); +} + +static void +gas_assembly_generator_compile_variable(gas_assembly_generator_t *gen, ast_variable_t *variable) +{ + ref_entry_t *entry = NULL; + for (int i = gen->refs->size - 1; i >= 0; --i) { + ref_entry_t *entry_tmp = (ref_entry_t *)vector_at(gen->refs, i); + if (entry_tmp->id == variable->identifier) { + entry = entry_tmp; + break; + } + } + assert(entry && "reference not found"); + fprintf(gen->stream, " mov %d(%%rbp), %%rax\n", entry->stack_offset); } static void @@ -117,11 +182,13 @@ gas_assembly_generator_binary_operation(gas_assembly_generator_t *gen, ast_binar { gas_assembly_generator_compile(gen, binary_operation->right); - fprintf(gen->stream, " push %%rax\n"); + gen->stack_offset -= 8; + fprintf(gen->stream, " mov %%rax, %d(%%rbp)\n", gen->stack_offset); gas_assembly_generator_compile(gen, binary_operation->left); - fprintf(gen->stream, " pop %%rcx\n"); + fprintf(gen->stream, " mov %d(%%rbp), %%rcx\n", gen->stack_offset); + gen->stack_offset += 8; if (binary_operation->kind == AST_BINOP_ADITION) { fprintf(gen->stream, " add %%rcx, %%rax\n"); diff --git a/src/gas_assembly_generator.h b/src/gas_assembly_generator.h index 4d4b28e..044e8ed 100644 --- a/src/gas_assembly_generator.h +++ b/src/gas_assembly_generator.h @@ -18,11 +18,14 @@ #define GAS_ASSEMBLY_GENERATOR_H #include "ast.h" +#include "vector.h" #include <stdio.h> typedef struct gas_assembly_generator_t { FILE *stream; + vector_t *refs; + int stack_offset; } gas_assembly_generator_t; void diff --git a/src/parser.c b/src/parser.c index 48a9125..f4829ee 100644 --- a/src/parser.c +++ b/src/parser.c @@ -135,10 +135,10 @@ parser_parse_factor(parser_t *parser, ast_node_t *node) if (!drop_expected_token(parser, TOKEN_CPAREN)) return false; return true; - case TOKEN_NAME: - ast_node_init_identifier(node, token.value); + case TOKEN_NAME: { // TODO: Check node kind, today accepts only variables - if (scope_get(parser->scope, token.value) == NULL) { + ast_node_t *var_node = scope_get(parser->scope, token.value); + if (var_node == NULL) { parser_error_t error; error.token = token; sprintf(error.message, "identifier '" SVFMT "' not defined", SVARG(&token.value)); @@ -146,7 +146,10 @@ parser_parse_factor(parser_t *parser, ast_node_t *node) return false; } + ast_node_init_variable(node, &var_node->data.variable_declaration.identifier); + return true; + } default: { parser_error_t error; error.token = token; diff --git a/test/integration_test.c b/test/integration_test.c index 739a938..4178b42 100644 --- a/test/integration_test.c +++ b/test/integration_test.c @@ -42,6 +42,7 @@ test_examples(const MunitParameter params[], void *user_data_or_fixture) { assert_exit_status("../examples/main.pipa", 69); assert_exit_status("../examples/arithmetics.pipa", 13); + assert_exit_status("../examples/variables.pipa", 28); return MUNIT_OK; } diff --git a/test/parser_test.c b/test/parser_test.c index d534509..5d615ec 100644 --- a/test/parser_test.c +++ b/test/parser_test.c @@ -126,8 +126,8 @@ test_parse_variable_definition(const MunitParameter params[], void *user_data_or ast_node_t *ast_literal = ast_return->data.return_stmt.argument; - assert_int(AST_IDENTIFIER, ==, ast_literal->kind); - assert_string_view_equal("variable", ast_literal->data.identifier.name); + assert_int(AST_VARIABLE, ==, ast_literal->kind); + assert_string_view_equal("variable", ast_literal->data.variable.identifier->name); ast_node_destroy(ast_function); scope_destroy(scope); |