summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/arena.c121
-rw-r--r--src/array.c67
-rw-r--r--src/ir.c42
-rw-r--r--src/lexer.c222
-rw-r--r--src/obe.c142
-rw-r--r--src/parser.c224
-rw-r--r--src/string.c34
-rw-r--r--src/utils.c74
-rw-r--r--src/x86_64/codegen.c152
9 files changed, 1078 insertions, 0 deletions
diff --git a/src/arena.c b/src/arena.c
new file mode 100644
index 0000000..8822fdd
--- /dev/null
+++ b/src/arena.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2025 Johnny Richard <johnny@johnnyrichard.com>
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ *
+ * This file is part of obe.
+ *
+ * obe is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * obe is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with obe. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include <assert.h>
+#include <obe/arena.h>
+#include <stdlib.h>
+#include <string.h>
+
+void*
+obe_arena_alloc(obe_arena_t* arena, size_t size)
+{
+ if (arena->end == NULL) {
+ assert(arena->begin == NULL);
+ size_t capacity = OBE_ARENA_REGION_DEFAULT_CAPACITY;
+ if (capacity < OBE_ARENA_PADDING(size))
+ capacity = size + OBE_ARENA_PADDING(size);
+ arena->end = obe_arena_region_new(capacity);
+ arena->begin = arena->end;
+ }
+
+ while (arena->end->offset + size > arena->end->capacity &&
+ arena->end->next != NULL) {
+ arena->end = arena->end->next;
+ }
+
+ if (arena->end->offset + size > arena->end->capacity) {
+ assert(arena->end->next == NULL);
+ size_t capacity = OBE_ARENA_REGION_DEFAULT_CAPACITY;
+ if (capacity < size)
+ capacity = size + OBE_ARENA_PADDING(size);
+ arena->end->next = obe_arena_region_new(capacity);
+ arena->end = arena->end->next;
+ }
+
+ void* ptr = arena->end->data + arena->end->offset;
+ arena->end->offset += size + OBE_ARENA_PADDING(size);
+ return ptr;
+}
+
+void*
+obe_arena_realloc(obe_arena_t* arena,
+ void* old_ptr,
+ size_t old_size,
+ size_t new_size)
+{
+ if (new_size <= old_size)
+ return old_ptr;
+ void* new_ptr = obe_arena_alloc(arena, new_size);
+ return memcpy(new_ptr, old_ptr, old_size);
+}
+
+void
+obe_arena_release(obe_arena_t* arena)
+{
+ for (obe_arena_region_t* r = arena->begin; r != NULL; r = r->next) {
+ r->offset = 0;
+ }
+ arena->end = arena->begin;
+}
+
+void
+obe_arena_free(obe_arena_t* arena)
+{
+ obe_arena_region_t* r = arena->begin;
+ while (r) {
+ obe_arena_region_t* r_tmp = r;
+ r = r->next;
+ obe_arena_region_free(r_tmp);
+ }
+ arena->begin = NULL;
+ arena->end = NULL;
+}
+
+obe_arena_region_t*
+obe_arena_region_new(size_t capacity)
+{
+ size_t size = sizeof(obe_arena_region_t) + sizeof(uint8_t) * capacity;
+ obe_arena_region_t* r = (obe_arena_region_t*)malloc(size);
+ assert(r);
+ r->next = NULL;
+ r->offset = 0;
+ r->capacity = capacity;
+ r->data = (uint8_t*)(r + 1);
+ return r;
+}
+
+void
+obe_arena_region_free(obe_arena_region_t* r)
+{
+ free(r);
+}
+
+char*
+obe_arena_strdup(obe_arena_t* arena, char* s)
+{
+ size_t slen = strlen(s);
+ char* d = obe_arena_alloc(arena, sizeof(char) * (slen + 1));
+ assert(d);
+
+ for (size_t i = 0; i <= slen; ++i)
+ d[i] = s[i];
+
+ return d;
+}
diff --git a/src/array.c b/src/array.c
new file mode 100644
index 0000000..dde3eba
--- /dev/null
+++ b/src/array.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2025 Johnny Richard <johnny@johnnyrichard.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <obe/arena.h>
+#include <obe/array.h>
+
+void*
+obe_array_new(obe_arena_t* arena, size_t item_size)
+{
+ obe_array_header_t* h = obe_arena_alloc(
+ arena,
+ (item_size * OBE_ARRAY_INITIAL_CAPACITY) + sizeof(obe_array_header_t));
+ if (h == NULL) {
+ return NULL;
+ }
+ h->arena = arena;
+ h->length = 0;
+ h->item_size = item_size;
+ h->capacity = OBE_ARRAY_INITIAL_CAPACITY;
+
+ return ((uint8_t *)h) + sizeof(obe_array_header_t);
+}
+
+obe_array_header_t*
+obe_array_get_header(void* arr)
+{
+ return (obe_array_header_t*)(((uint8_t *)arr) - sizeof(obe_array_header_t));
+}
+
+void*
+obe_array_grow(void* arr)
+{
+ obe_array_header_t* h = obe_array_get_header(arr);
+
+ size_t old_size = sizeof(obe_array_header_t) + (h->capacity * h->item_size);
+ h->capacity *= 2;
+ size_t new_size = sizeof(obe_array_header_t) + (h->capacity * h->item_size);
+
+ h = obe_arena_realloc(h->arena, h, old_size, new_size);
+
+ return ((uint8_t *)h) + sizeof(obe_array_header_t);
+}
+
+size_t
+obe_array_length(void* arr)
+{
+ assert(arr);
+ obe_array_header_t* header = obe_array_get_header(arr);
+ return header->length;
+}
diff --git a/src/ir.c b/src/ir.c
new file mode 100644
index 0000000..2c0b0c9
--- /dev/null
+++ b/src/ir.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2025 Johnny Richard <johnny@johnnyrichard.com>
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ *
+ * This file is part of obe.
+ *
+ * obe is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * obe is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with obe. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include <assert.h>
+#include <obe/arena.h>
+#include <obe/ir.h>
+#include <obe/array.h>
+
+obe_ir_translation_unit_t*
+obe_ir_translation_unit_new(obe_arena_t* arena)
+{
+ obe_ir_translation_unit_t* tu = obe_arena_alloc(arena, sizeof(obe_ir_translation_unit_t));
+ assert(tu);
+ tu->funcs = obe_array(arena, obe_ir_function_t);
+ return tu;
+}
+
+obe_ir_function_t*
+obe_ir_function_new(obe_arena_t* arena, obe_string_t name)
+{
+ obe_ir_function_t* func = obe_arena_alloc(arena, sizeof(obe_ir_function_t));
+ assert(func);
+ func->name = name;
+ return func;
+}
diff --git a/src/lexer.c b/src/lexer.c
new file mode 100644
index 0000000..3ad8751
--- /dev/null
+++ b/src/lexer.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2025 Johnny Richard <johnny@johnnyrichard.com>
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ *
+ * This file is part of obe.
+ *
+ * obe is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * obe is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with obe. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include <assert.h>
+#include <ctype.h>
+#include <obe/lexer.h>
+#include <obe/string.h>
+#include <obe/utils.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void
+obe_lexer_init(obe_lexer_t* lexer, char* filename)
+{
+ assert(lexer);
+
+ char* program = read_file_contents(filename);
+ if (program == NULL) {
+ fprintf(stderr, "Unable to read file contents <%s>\n", filename);
+ exit(EXIT_FAILURE);
+ }
+
+ lexer->filename = filename;
+ lexer->loc = (obe_lexer_loc_t){ 0 };
+ lexer->source = obe_string_from_cstr(program);
+}
+
+bool
+obe_lexer_is_eof(obe_lexer_t* lexer)
+{
+ return !(lexer->loc.offset < lexer->source.length);
+}
+
+char
+obe_lexer_current_char(obe_lexer_t* lexer)
+{
+ return lexer->source.chars[lexer->loc.offset];
+}
+
+char
+obe_lexer_next_char(obe_lexer_t* lexer)
+{
+ assert(lexer->loc.offset < lexer->source.length);
+
+ char previous_char = obe_lexer_current_char(lexer);
+ if (previous_char == '\n') {
+ lexer->loc.lineno++;
+ lexer->loc.lineoffset = ++lexer->loc.offset;
+ } else {
+ lexer->loc.offset++;
+ }
+ return obe_lexer_current_char(lexer);
+}
+
+void
+obe_lexer_next_token(obe_lexer_t* lexer, obe_token_t* token)
+{
+ if (obe_lexer_is_eof(lexer)) {
+ *token = (obe_token_t){ .kind = TOKEN_EOF };
+ return;
+ }
+
+ char c = obe_lexer_current_char(lexer);
+ if (isspace(c) && !obe_lexer_is_eof(lexer)) {
+ while (isspace(c) && !obe_lexer_is_eof(lexer)) {
+ c = obe_lexer_next_char(lexer);
+ }
+ }
+
+ if (obe_lexer_is_eof(lexer)) {
+ *token = (obe_token_t){ .kind = TOKEN_EOF };
+ return;
+ }
+
+ if (isalpha(c) || c == '_') {
+ obe_lexer_loc_t start_loc = lexer->loc;
+ while ((isalnum(c) || c == '_') && !obe_lexer_is_eof(lexer)) {
+ c = obe_lexer_next_char(lexer);
+ }
+ obe_string_t token_value = {
+ .chars = lexer->source.chars + start_loc.offset,
+ .length = lexer->loc.offset - start_loc.offset
+ };
+ token->value = token_value;
+ token->loc = start_loc;
+ if (obe_string_eq(token_value, obe_string_from_cstr("fn"))) {
+ token->kind = TOKEN_KW_FN;
+ return;
+ }
+ if (obe_string_eq(token_value, obe_string_from_cstr("br"))) {
+ token->kind = TOKEN_KW_BR;
+ return;
+ }
+ if (obe_string_eq(token_value,
+ obe_string_from_cstr("return"))) {
+ token->kind = TOKEN_KW_RETURN;
+ return;
+ }
+ if (obe_string_eq(token_value, obe_string_from_cstr("int"))) {
+ token->kind = TOKEN_INT;
+ return;
+ }
+ token->kind = TOKEN_IDENT;
+ return;
+ }
+
+ if (c == '.') {
+ obe_lexer_loc_t start_loc = lexer->loc;
+ do {
+ c = obe_lexer_next_char(lexer);
+ } while ((isalnum(c) || c == '_') && !obe_lexer_is_eof(lexer));
+
+ obe_string_t token_value = {
+ .chars = lexer->source.chars + start_loc.offset,
+ .length = lexer->loc.offset - start_loc.offset
+ };
+
+ token->value = token_value;
+ token->loc = start_loc;
+ token->kind = TOKEN_LABEL;
+ return;
+ }
+
+ if (isdigit(c)) {
+ obe_lexer_loc_t start_loc = lexer->loc;
+ while (isdigit(c) && !obe_lexer_is_eof(lexer)) {
+ c = obe_lexer_next_char(lexer);
+ }
+ obe_string_t token_value = {
+ .chars = lexer->source.chars + start_loc.offset,
+ .length = lexer->loc.offset - start_loc.offset
+ };
+ token->kind = TOKEN_NUMBER;
+ token->value = token_value;
+ token->loc = start_loc;
+ return;
+ }
+
+ if (c == ';') {
+ token->kind = TOKEN_SEMICOLON;
+ token->loc = lexer->loc;
+ token->value = (obe_string_t){ .chars = lexer->source.chars + lexer->loc.offset, .length = 1 };
+ obe_lexer_next_char(lexer);
+ return;
+ }
+
+ if (c == ':') {
+ token->kind = TOKEN_COLON;
+ token->value = (obe_string_t){ .chars = lexer->source.chars + lexer->loc.offset , .length = 1} ;
+ token->loc = lexer->loc;
+ obe_lexer_next_char(lexer);
+ return;
+ }
+
+ if (c == '=') {
+ token->kind = TOKEN_EQ;
+ token->value = (obe_string_t){ .chars = lexer->source.chars + lexer->loc.offset, .length = 1 };
+ token->loc = lexer->loc;
+ obe_lexer_next_char(lexer);
+ return;
+ }
+
+ if (c == '{') {
+ token->kind = TOKEN_LBRACE;
+ token->value = (obe_string_t){ .chars = lexer->source.chars + lexer->loc.offset, .length = 1 };
+ token->loc = lexer->loc;
+ obe_lexer_next_char(lexer);
+ return;
+ }
+
+ if (c == '}') {
+ token->kind = TOKEN_RBRACE;
+ token->value = (obe_string_t){ .chars = lexer->source.chars + lexer->loc.offset, .length = 1 };
+ token->loc = lexer->loc;
+ obe_lexer_next_char(lexer);
+ return;
+ }
+
+ token->kind = TOKEN_UNKOWN;
+ token->value = (obe_string_t){ .chars = lexer->source.chars + lexer->loc.offset, .length = 1 };
+ token->loc = lexer->loc;
+ obe_lexer_next_char(lexer);
+ return;
+}
+
+static char* token_to_cstr_table[] = { [TOKEN_KW_RETURN] = "return",
+ [TOKEN_KW_FN] = "fn",
+ [TOKEN_KW_BR] = "br",
+ [TOKEN_IDENT] = "<ident>",
+ [TOKEN_LABEL] = "<label>",
+ [TOKEN_NUMBER] = "<number>",
+ [TOKEN_INT] = "int",
+ [TOKEN_EQ] = "=",
+ [TOKEN_COLON] = ":",
+ [TOKEN_SEMICOLON] = ";",
+ [TOKEN_LBRACE] = "{",
+ [TOKEN_RBRACE] = "}",
+ [TOKEN_EOF] = "<eof>",
+ [TOKEN_UNKOWN] = "<?unkown?>" };
+
+char*
+obe_token_to_cstr(obe_token_kind_t kind)
+{
+ return token_to_cstr_table[kind];
+}
diff --git a/src/obe.c b/src/obe.c
new file mode 100644
index 0000000..38af876
--- /dev/null
+++ b/src/obe.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 Johnny Richard <johnny@johnnyrichard.com>
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ *
+ * This file is part of obe.
+ *
+ * obe is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * obe is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with obe. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include <config.h>
+#include <obe/parser.h>
+#include <obe/x86_64/codegen.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct obe_cli_args
+{
+ int argc;
+ char** argv;
+} obe_cli_args_t;
+
+typedef struct obe_cli_opts
+{
+ uint32_t options;
+ char* ir_file;
+ char* program_path;
+} obe_cli_opts_t;
+
+typedef enum
+{
+ OBE_CLI_OPT_HELP = 1,
+ OBE_CLI_OPT_VERSION = 1 << 1,
+ OBE_CLI_OPT_OUTPUT = 1 << 2,
+} cli_opt_t;
+
+obe_cli_opts_t
+obe_cli_parse_args(int argc, char** argv);
+
+static char*
+obe_cli_args_shift(obe_cli_args_t* args)
+{
+ if (args->argc == 0)
+ return NULL;
+ --(args->argc);
+ return *(args->argv)++;
+}
+
+void
+obe_cli_print_usage(FILE* stream)
+{
+ fprintf(stream,
+ "Usage: " PACKAGE_NAME " [OPTIONS] file...\n"
+ "\n"
+ "Options:\n"
+ " -h, --help display the help and exit\n"
+ " -v, --version output version information and exit\n"
+ " -o, --output output gas assembly\n"
+ "\n"
+ "Report bugs to " PACKAGE_BUGREPORT "\n");
+}
+
+void
+obe_cli_print_version(void)
+{
+ printf(PACKAGE_STRING
+ "\n"
+ "\n"
+ "Copyright (C) 2025 Olang project.\n"
+ "This is free software; see the source for copying conditions. "
+ "There is NO\n"
+ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR "
+ "PURPOSE.\n"
+ "\n"
+ "Written by Johnny Richard <johnny@johnnyrichard.com>\n");
+}
+
+int
+main(int argc, char** argv)
+{
+ obe_cli_opts_t opts = { 0 };
+ obe_cli_args_t args = { .argc = argc, .argv = argv };
+
+ opts.program_path = obe_cli_args_shift(&args);
+
+ char* arg = obe_cli_args_shift(&args);
+ while (arg != NULL) {
+ if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
+ opts.options |= OBE_CLI_OPT_HELP;
+ } else if (strcmp(arg, "-v") == 0 || strcmp(arg, "--version") == 0) {
+ opts.options |= OBE_CLI_OPT_VERSION;
+ } else if (strcmp(arg, "-o") == 0 || strcmp(arg, "--output") == 0) {
+ opts.options |= OBE_CLI_OPT_OUTPUT;
+ } else {
+ opts.ir_file = arg;
+ }
+ arg = obe_cli_args_shift(&args);
+ }
+
+ if (opts.ir_file != NULL) {
+ obe_arena_t arena = { 0 };
+
+ obe_lexer_t lexer = { 0 };
+ obe_lexer_init(&lexer, opts.ir_file);
+
+ obe_parser_t parser = { 0 };
+ obe_parser_init(&parser, &lexer, &arena);
+ obe_ir_translation_unit_t* tu = obe_parser_parse(&parser);
+
+ obe_x86_64_codegen_emit(tu, stdout);
+ return EXIT_SUCCESS;
+ }
+
+ if (opts.options & OBE_CLI_OPT_HELP) {
+ obe_cli_print_usage(stdout);
+ return EXIT_SUCCESS;
+ }
+
+ if (opts.options & OBE_CLI_OPT_VERSION) {
+ obe_cli_print_version();
+ return EXIT_SUCCESS;
+ }
+
+ if (opts.options & OBE_CLI_OPT_OUTPUT) {
+ return EXIT_SUCCESS;
+ }
+
+ obe_cli_print_usage(stderr);
+ return EXIT_FAILURE;
+}
diff --git a/src/parser.c b/src/parser.c
new file mode 100644
index 0000000..d95e6f9
--- /dev/null
+++ b/src/parser.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2025 Johnny Richard <johnny@johnnyrichard.com>
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ *
+ * This file is part of obe.
+ *
+ * obe is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * obe is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with obe. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include <assert.h>
+#include <obe/array.h>
+#include <obe/lexer.h>
+#include <obe/parser.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static obe_ir_function_t obe_parser_parse_function(obe_parser_t* parser);
+
+inline static bool expected_next_token(obe_lexer_t *lexer, obe_token_t *token,
+ obe_token_kind_t expected_kind);
+
+static bool expected_token(obe_lexer_t *lexer, obe_token_t *token,
+ obe_token_kind_t expected_kind);
+
+void
+obe_parser_init(obe_parser_t* parser, obe_lexer_t* lexer, obe_arena_t* arena)
+{
+ assert(parser && lexer && arena);
+ parser->lexer = lexer;
+ parser->arena = arena;
+}
+
+void
+obe_parser_next_expected_token(obe_parser_t* parser,
+ obe_token_t* token,
+ obe_token_kind_t kind)
+{
+ obe_lexer_next_token(parser->lexer, token);
+ if (token->kind != kind) {
+ fprintf(stderr,
+ "%s:%zu:%zu: syntax error: expected token <%s> but got <%s>.\n",
+ parser->lexer->filename,
+ token->loc.lineno + 1,
+ token->loc.offset - token->loc.lineoffset + 1,
+ obe_token_to_cstr(kind),
+ obe_token_to_cstr(token->kind));
+ exit(EXIT_FAILURE);
+ }
+}
+
+obe_ir_translation_unit_t*
+obe_parser_parse(obe_parser_t* parser)
+{
+ obe_ir_translation_unit_t* tu = obe_ir_translation_unit_new(parser->arena);
+
+ // FIXME: Add support to parse multiple functions
+ obe_array_append(tu->funcs, obe_parser_parse_function(parser));
+
+ return tu;
+}
+
+static obe_ir_function_t
+obe_parser_parse_function(obe_parser_t* parser)
+{
+ obe_token_t token;
+
+ expected_next_token(parser->lexer, &token, TOKEN_KW_FN);
+ expected_next_token(parser->lexer, &token, TOKEN_IDENT);
+
+ obe_ir_function_t func = { .name = token.value, .instrs = obe_array(parser->arena, obe_ir_inst_t) };
+
+ expected_next_token(parser->lexer, &token, TOKEN_LBRACE);
+
+ obe_lexer_next_token(parser->lexer, &token);
+ while (token.kind != TOKEN_EOF && token.kind != TOKEN_RBRACE) {
+ switch (token.kind) {
+ case TOKEN_KW_RETURN:
+ {
+ expected_next_token(parser->lexer, &token, TOKEN_IDENT);
+
+ char value[token.value.length + 1];
+ memcpy(value, token.value.chars, token.value.length);
+ value[token.value.length] = 0;
+
+ obe_ir_inst_t inst = {
+ .kind = OBE_IR_INST_RETURN,
+ .operand1 = (obe_ir_operand_t) {
+ .kind = OBE_IR_OPERAND_IDENT,
+ .value = obe_arena_strdup(parser->arena, value)
+ }
+ };
+
+ obe_array_append(func.instrs, inst);
+
+ expected_next_token(parser->lexer, &token, TOKEN_SEMICOLON);
+ } break;
+ case TOKEN_KW_BR:
+ {
+ expected_next_token(parser->lexer, &token, TOKEN_IDENT);
+
+ char op1[token.value.length + 1];
+ memcpy(op1, token.value.chars, token.value.length);
+ op1[token.value.length] = 0;
+
+ expected_next_token(parser->lexer, &token, TOKEN_LABEL);
+
+ char dest[token.value.length + 1];
+ memcpy(dest, token.value.chars, token.value.length);
+ dest[token.value.length] = 0;
+
+ expected_next_token(parser->lexer, &token, TOKEN_SEMICOLON);
+
+ obe_ir_inst_t inst = {
+ .kind = OBE_IR_INST_BR,
+ .dest = obe_arena_strdup(parser->arena, dest),
+ .operand1 = (obe_ir_operand_t) {
+ .kind = OBE_IR_OPERAND_IDENT,
+ .value = obe_arena_strdup(parser->arena, op1)
+ }
+ };
+
+ obe_array_append(func.instrs, inst);
+ } break;
+ case TOKEN_LABEL:
+ {
+ char label[token.value.length + 1];
+ memcpy(label, token.value.chars, token.value.length);
+ label[token.value.length] = 0;
+
+ expected_next_token(parser->lexer, &token, TOKEN_COLON);
+
+ obe_ir_inst_t inst = {
+ .kind = OBE_IR_INST_LABEL,
+ .dest = obe_arena_strdup(parser->arena, label)
+ };
+
+ obe_array_append(func.instrs, inst);
+ } break;
+ case TOKEN_IDENT:
+ {
+ obe_string_t val = token.value;
+
+ char dest[token.value.length + 1];
+ memcpy(dest, token.value.chars, token.value.length);
+ dest[token.value.length] = 0;
+
+ expected_next_token(parser->lexer, &token, TOKEN_COLON);
+ expected_next_token(parser->lexer, &token, TOKEN_INT);
+ expected_next_token(parser->lexer, &token, TOKEN_EQ);
+ expected_next_token(parser->lexer, &token, TOKEN_NUMBER);
+
+
+ char number[token.value.length + 1];
+ memcpy(number, token.value.chars, token.value.length);
+ number[token.value.length] = 0;
+
+ obe_ir_inst_t inst = {
+ .kind = OBE_IR_INST_CONST,
+ .type = OBE_IR_TYPE_INT,
+ .dest = obe_arena_strdup(parser->arena, dest),
+ .operand1 = (obe_ir_operand_t) {
+ .kind = OBE_IR_OPERAND_LITERAL,
+ .value = obe_arena_strdup(parser->arena, number)
+ }
+ };
+
+ obe_array_append(func.instrs, inst);
+
+ expected_next_token(parser->lexer, &token, TOKEN_SEMICOLON);
+ } break;
+ default:
+ {
+ printf("%s:%ld:%ld: token <" PRIs ">\n",
+ parser->lexer->filename,
+ token.loc.lineno + 1,
+ token.loc.offset - token.loc.lineoffset + 1,
+ PRIsARG(token.value));
+ }
+ }
+ obe_lexer_next_token(parser->lexer, &token);
+ }
+
+ expected_token(parser->lexer, &token, TOKEN_RBRACE);
+
+ return func;
+}
+
+static bool
+expected_token(obe_lexer_t *lexer, obe_token_t *token, obe_token_kind_t expected_kind)
+{
+ if (token->kind != expected_kind) {
+ fprintf(stderr,
+ "%s:%lu:%lu: syntax error: got '" PRIs "' token but expect '%s'\n",
+ lexer->filename,
+ token->loc.lineno + 1,
+ token->loc.offset - lexer->loc.lineoffset + 1,
+ PRIsARG(token->value),
+ obe_token_to_cstr(expected_kind));
+ exit(EXIT_FAILURE);
+ }
+ return true;
+}
+
+inline static bool
+expected_next_token(obe_lexer_t *lexer,
+ obe_token_t *token,
+ obe_token_kind_t expected_kind)
+{
+ obe_lexer_next_token(lexer, token);
+ return expected_token(lexer, token, expected_kind);
+}
diff --git a/src/string.c b/src/string.c
new file mode 100644
index 0000000..b6638bd
--- /dev/null
+++ b/src/string.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 Johnny Richard <johnny@johnnyrichard.com>
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ *
+ * This file is part of obe.
+ *
+ * obe is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * obe is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with obe. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include <obe/string.h>
+#include <string.h>
+
+obe_string_t
+obe_string_from_cstr(char* cstr)
+{
+ return (obe_string_t){ .chars = cstr, .length = strlen(cstr) };
+}
+
+bool
+obe_string_eq(obe_string_t s1, obe_string_t s2)
+{
+ return s1.length == s2.length && memcmp(s1.chars, s2.chars, s1.length) == 0;
+}
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..345d5a4
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2025 Johnny Richard <johnny@johnnyrichard.com>
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ *
+ * This file is part of obe.
+ *
+ * obe is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * obe is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with obe. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include <obe/utils.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+char*
+read_file_contents(char* file_path)
+{
+ FILE* file;
+ int file_size;
+
+ file = fopen(file_path, "r");
+
+ if (file == NULL) {
+ perror("fopen");
+ return NULL;
+ }
+
+ if (fseek(file, 0, SEEK_END) == -1) {
+ perror("fseek SEEK_END");
+ fclose(file);
+ return NULL;
+ }
+
+ if ((file_size = ftell(file)) == -1) {
+ perror("ftell");
+ fclose(file);
+ return NULL;
+ }
+
+ if (fseek(file, 0, SEEK_SET) == -1) {
+ perror("fseek 0 SEEK_SET");
+ fclose(file);
+ return NULL;
+ }
+
+ char* file_content = (char*)calloc(file_size + 1, sizeof(char));
+ if (file_content == NULL) {
+ fprintf(stderr, "Could not allocate mem for file_content\n");
+ fclose(file);
+ return NULL;
+ }
+
+ size_t ret = fread(file_content, sizeof(char), file_size, file);
+ if (ret == 0 && ferror(file) != 0) {
+ perror("fread");
+ free(file_content);
+ fclose(file);
+ return NULL;
+ }
+
+ fclose(file);
+
+ return file_content;
+}
diff --git a/src/x86_64/codegen.c b/src/x86_64/codegen.c
new file mode 100644
index 0000000..6cafda0
--- /dev/null
+++ b/src/x86_64/codegen.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2025 Johnny Richard <johnny@johnnyrichard.com>
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ *
+ * This file is part of obe.
+ *
+ * obe is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * obe is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with obe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <obe/arena.h>
+#include <obe/array.h>
+#include <obe/ir.h>
+#include <obe/x86_64/codegen.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void
+obe_x86_64_codegen_emit_function(obe_ir_function_t *fn, FILE *out)
+{
+ fprintf(out, PRIs ":\n", PRIsARG(fn->name));
+ fprintf(out, "\tpush %%rbp\n");
+ fprintf(out, "\tmov %%rsp, %%rbp\n");
+
+ size_t insts_length = obe_array_length(fn->instrs);
+ for (size_t i = 0; i < insts_length; ++i) {
+ obe_ir_inst_t inst = fn->instrs[i];
+ switch (inst.kind) {
+ case OBE_IR_INST_RETURN:
+ {
+ if (inst.operand1.kind == OBE_IR_OPERAND_LITERAL) {
+ fprintf(out, "\tmov $%s, %%rax\n", inst.operand1.value);
+ } else {
+ fprintf(stderr, "Operation kind not supported\n");
+ exit(EXIT_FAILURE);
+ }
+ } break;
+ default:
+ {
+ fprintf(stderr, "Instruction not supported\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ fprintf(out, "\tleave\n");
+ fprintf(out, "\tret\n");
+}
+
+void
+_obe_x86_64_codegen_emit(obe_ir_translation_unit_t* tu, FILE *out)
+{
+ // FIXME: Add syntax to configure globl
+ fprintf(out, ".globl main\n");
+
+ fprintf(out, ".text\n");
+ size_t funcs_length = obe_array_length(tu->funcs);
+ for (size_t i = 0; i < funcs_length; ++i) {
+ obe_ir_function_t fn = tu->funcs[i];
+ obe_x86_64_codegen_emit_function(&fn, out);
+ }
+}
+
+typedef struct obe_basic_block obe_basic_block_t;
+typedef struct obe_cfg obe_cfg_t;
+typedef struct obe_int_graph obe_int_graph_t;
+
+struct obe_basic_block {
+ obe_string_t id;
+ obe_ir_inst_t *instrs;
+ struct obe_basic_block *pred;
+ struct obe_basic_block *succ;
+};
+
+struct obe_cfg {
+ obe_basic_block_t *init_block;
+ obe_basic_block_t *curr_block;
+};
+
+
+typedef struct obe_int_edge {
+ size_t v1;
+ size_t v2;
+} obe_int_edge_t;
+
+struct obe_int_graph {
+ obe_int_edge_t *edges;
+};
+
+obe_int_graph_t
+create_interference_graph(obe_cfg_t *cfg, obe_arena_t *arena)
+{
+ obe_int_graph_t graph = {0};
+ graph.edges = obe_array(arena, obe_int_edge_t);
+
+ return graph;
+}
+
+void
+obe_x86_64_codegen_emit(obe_ir_translation_unit_t* tu, FILE *out)
+{
+ // FIXME: Create a valid CFG
+ obe_arena_t arena = {0};
+ obe_cfg_t cfg = {0};
+if (cfg.init_block == NULL) {
+ cfg.init_block = obe_arena_alloc(&arena, sizeof(obe_basic_block_t));
+ cfg.curr_block = cfg.init_block;
+ cfg.curr_block->instrs = obe_array(&arena, obe_ir_inst_t);
+ }
+
+ size_t funcs_length = obe_array_length(tu->funcs);
+ for (size_t i = 0; i < funcs_length; ++i) {
+ obe_ir_function_t fn = tu->funcs[i];
+
+ size_t instrs_length = obe_array_length(fn.instrs);
+ for (size_t j = 0; j < instrs_length; ++j) {
+ obe_ir_inst_t inst = fn.instrs[j];
+ switch (inst.kind) {
+ default:
+ {
+ obe_array_append(cfg.curr_block->instrs, inst);
+ }
+ }
+ }
+ }
+
+ // TODO: Register Allocation
+ // liveness analysis (create interference graph)
+ size_t instrs_length = obe_array_length(cfg.init_block->instrs);
+ for (size_t i = 0; i < instrs_length; ++i) {
+ obe_ir_inst_t inst = cfg.init_block->instrs[i];
+ // asm("int3");
+ printf("inst = %d\n", inst.kind);
+ }
+ // TODO: create_interference_graph(&cfg, &arena);
+
+
+ // TODO: Emit assembly code
+
+ obe_arena_free(&arena);
+}