summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohnny Richard <johnny@johnnyrichard.com>2023-04-30 03:11:00 -0300
committerJohnny Richard <johnny@johnnyrichard.com>2023-04-30 17:40:01 +0200
commitb1f7b0cccc6986ca107d9eeb1c7d5e5e5a32dc49 (patch)
treefb1eb8192c3f6da14796d59831030dd23935edcd
parent090da456910429708ef7ee202b627e3dbce17819 (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.pipa7
-rw-r--r--src/ast.c7
-rw-r--r--src/gas_assembly_generator.c81
-rw-r--r--src/gas_assembly_generator.h3
-rw-r--r--src/parser.c9
-rw-r--r--test/integration_test.c1
-rw-r--r--test/parser_test.c4
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;
+}
diff --git a/src/ast.c b/src/ast.c
index 1131bbf..55b65fa 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -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);