diff options
| -rw-r--r-- | boot/catboot.c | 56 | ||||
| -rw-r--r-- | boot/common.c | 146 |
2 files changed, 179 insertions, 23 deletions
diff --git a/boot/catboot.c b/boot/catboot.c index ae0fc6c..b36bb97 100644 --- a/boot/catboot.c +++ b/boot/catboot.c @@ -1,43 +1,53 @@ +/* + * # catboot + * + * the bootstrap compiler for catskill, + * implemented as simply as possible, + * depending only on the C standard library, + * in this case musl, and built statically + * in a single union build. + * + * should only be used to compile `catskill` itself, + * as it is not a full-featured compiler, and never will be. + * once `catskill` can compile itself using a C backend, + * this compiler version will be permanently retired. + * (although that's still very far away!! have fun! :3) + */ + #include <fcntl.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include <sys/mman.h> #include <sys/stat.h> -void -error(const char* msg) { - fprintf(stderr, ":( error: %s\n", msg); - exit(EXIT_FAILURE); -} +#include "common.c" -const char* -read_file(const char* path) { +const ascii* +read_file(const ascii* path) +{ struct stat stat_info; - if (stat(path, &stat_info) == -1) - error("i couldn't open that file, sorry :("); + if (stat(path, &stat_info) == -1) failure("i couldn't open that file, sorry :("); - const int file = open(path, O_RDONLY); - if (file == -1) - error("i couldn't open that file, sorry :("); + const int32 file_descriptor = open(path, O_RDONLY); + check(file_descriptor != -1, "i couldn't open that file, sorry :("); - const int mmap_prot = PROT_READ; - const int mmap_flags = MAP_PRIVATE; - const void* file_data = mmap( - NULL, stat_info.st_size, mmap_prot, mmap_flags, file, 0 - ); + const flags mmap_prot = PROT_READ; + const flags mmap_flags = MAP_PRIVATE; + const unknown* file_data = + mmap(nil, stat_info.st_size, mmap_prot, mmap_flags, file_descriptor, 0); - return (const char*)file_data; + return file_data; } -int -main(const int argc, const char* argv[]) { +integer +main(const integer argc, const ascii* argv[]) +{ if (argc != 2) { printf("usage: catboot <filename>\n"); return EXIT_FAILURE; } - const char* file_path = argv[1]; - const char* source = read_file(file_path); + const ascii* file_path = argv[1]; + const ascii* source = read_file(file_path); printf("%s", source); diff --git a/boot/common.c b/boot/common.c new file mode 100644 index 0000000..67e13eb --- /dev/null +++ b/boot/common.c @@ -0,0 +1,146 @@ +/* + * a small library of types, functions and macros that + * are used throughout the bootstrap compiler. + * allocation done purely statically. + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define uint8 uint8_t +#define uint16 uint16_t +#define uint32 uint32_t +#define uint64 uint64_t + +#define int8 int8_t +#define int16 int16_t +#define int32 int32_t +#define int64 int64_t + +#define float32 float +#define float64 double +#define real float64 + +#define uint uint64 +#define integer int64 +#define flags int32 + +#define ascii char +#define byte char + +#define bool _Bool +#define true 1 +#define false 0 +#define nil NULL +#define unknown void + +// call on irrecoverable failure. +// prints a very sad, apologetic message for +// the user and aborts program with failure status. +void +failure(const ascii* message) +{ + const ascii* format = + "\\e[0;31m" + ";( sorry, a failure has occurred...\n" + "-> %s!\n" + "\\e[0m"; + fprintf(stderr, format, message); + + exit(EXIT_FAILURE); +} + +// check a condition, triggering a failure if it's false. +void +check(bool condition, const ascii* message) +{ + if (!condition) failure(message); +} + +// the common size of region memory blocks. +#define REGION_SIZE 65536 + +// statically allocates a region of memory of a given size +// for a single type. +#define REGION_OF_SIZE(type, of, size) \ + type region_##of[size]; \ + uint region_##of##_cursor = 0; + +// statically allocates a region of memory for a type. +#define REGION(type, of) REGION_OF_SIZE(type, of, REGION_SIZE) + +// the global string region. +REGION(ascii, string) + +// a string. +struct String +{ + ascii* data; + uint length; +}; + +#define STRING_ITERATE(index, c, str) \ + ascii c = string_at(str, 0); \ + for (uint index = 0; index < str.length; c = string_at(str, ++index)) + +// allocates a new string in the global string region. +struct String +string_new(const ascii* data, uint length) +{ + // for compatibility, we include an additional null byte at the end. + uint allocation_length = length + 1; + check(region_string_cursor + allocation_length < REGION_SIZE, "out of string memory"); + + ascii* at = region_string + region_string_cursor; + region_string_cursor += allocation_length; + + for (uint i = 0; i < length; ++i) at[i] = data[i]; + at[length] = '\0'; + + return { + .data = at, + .length = length, + }; +} + +// allocates a new string in the global string region, +// taking the data from a null-terminated C string. +struct String +string_from_c_string(const char* c_string) +{ + uint length = strlen(c_string); + return string_new(c_string, length); +} + +// allocates a new string in the global string region, +// taking the data from a static null-terminated C string. +// +// NOTE: The string is not copied, so it MUST have a lifetime +// spanning the entire program. +struct String +string_from_static_c_string(const char* c_string) +{ + uint length = strlen(c_string); + return { + .data = (ascii*)c_string, + .length = length, + }; +} + +// returns the character at a given index. +// does bounds-checking. +ascii +string_at(struct String s, uint index) +{ + check(index < s.length, "index out of bounds"); + return s.data[index]; +} + +uint +string_length(struct String s) +{ + return s.length; +} |
