diff options
| author | Johnny Richard <johnny@johnnyrichard.com> | 2024-10-30 22:58:03 +0100 |
|---|---|---|
| committer | Johnny Richard <johnny@johnnyrichard.com> | 2025-12-14 09:53:52 +0100 |
| commit | 10bb8a05088f1d3bb24f7167f609b5f6fb0ba026 (patch) | |
| tree | 7a4b3f69a461301c45204ed856b61f92a7d42233 /src | |
Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/arena.c | 121 | ||||
| -rw-r--r-- | src/array.c | 67 | ||||
| -rw-r--r-- | src/ir.c | 42 | ||||
| -rw-r--r-- | src/lexer.c | 222 | ||||
| -rw-r--r-- | src/obe.c | 142 | ||||
| -rw-r--r-- | src/parser.c | 224 | ||||
| -rw-r--r-- | src/string.c | 34 | ||||
| -rw-r--r-- | src/utils.c | 74 | ||||
| -rw-r--r-- | src/x86_64/codegen.c | 152 |
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); +} |
