├── .gitignore ├── LICENSE ├── README.md ├── ds.h ├── example.txt ├── linux.inc ├── main.c └── utils.inc /.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Alexandru Jercan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Compiler Tutorial in C 2 | 3 | A really simple programming language meant to be used as a tutorial for compilers (really easy ones). 4 | 5 | ```text 6 | term = | variable | literal 7 | expression = term | term + term | ... 8 | rel = term < term | ... 9 | instr = variable = expression | rel instr | :label | term | :label 10 | ``` 11 | 12 | Example of a program in this language 13 | 14 | ```text 15 | n = input 16 | i = 0 17 | :label 18 | output i 19 | i = i + 1 20 | if i < n then goto :label 21 | if i < 10 then goto :label2 22 | output 69 23 | :label2 24 | ``` 25 | 26 | ## Quickstart 27 | 28 | ```console 29 | gcc -g main.c -o main 30 | cat example.txt | ./main > example.asm 31 | fasm example.asm 32 | ./example 33 | ``` 34 | -------------------------------------------------------------------------------- /ds.h: -------------------------------------------------------------------------------- 1 | // DOCUMENTATION 2 | // 3 | // DS.H 4 | // 5 | // This is a single-header library that provides a set of data structures and 6 | // utilities for C programs. The library is designed to be simple and easy to 7 | // use, and it is intended to be used in small to medium-sized projects. 8 | // 9 | // Options: 10 | // - DS_IMPLEMENTATION: Define this macro in one source file to include the 11 | // implementation of all the data structures and utilities 12 | // - DS_PQ_IMPLEMENTATION: Define this macro in one source file to include the 13 | // implementation of the priority queue data structure 14 | // - DS_SB_IMPLEMENTATION: Define this macro in one source file to include the 15 | // implementation of the string builder utility 16 | // - DS_SS_IMPLEMENTATION: Define this macro in one source file to include the 17 | // implementation of the string slice utility 18 | // - DS_DA_IMPLEMENTATION: Define this macro in one source file to include the 19 | // implementation of the dynamic array data structure 20 | // - DS_LL_IMPLEMENTATION: Define this macro in one source file to include the 21 | // implementation of the linked list data structure 22 | // - DS_HT_IMPLEMENTATION: Define this macro in one source file to include the 23 | // implementation of the hash table data structure 24 | // - DS_AL_IMPLEMENTATION: Define this macro in one source file to include the 25 | // implementation of the allocator utility and set the allocator to use 26 | // - DS_AP_IMPLEMENTATION: Define this macro in one source file to include the 27 | // implementation of the ds_argument parser utility 28 | // - DS_IO_IMPLEMENTATION: Define this macro for some io utils 29 | // 30 | // MEMORY MANAGEMENT 31 | // 32 | // The memory management macros are used to allocate, reallocate and free 33 | // memory, and to exit the program. The DS_MALLOC macro is used to allocate 34 | // memory, the DS_REALLOC macro is used to reallocate memory, the DS_FREE macro 35 | // is used to free memory, and the DS_EXIT macro is used to exit the program. 36 | // 37 | // Options: 38 | // - DS_NO_STDLIB: Disables the use of the standard library 39 | // 40 | // LOGGING 41 | // 42 | // The logging macros are used to print messages to the standard output and 43 | // standard error streams. The DS_LOG_ERROR macro is used to print error 44 | // messages, and the DS_LOG_INFO macro is used to print informational messages. 45 | // The DS_PANIC macro is used to print an error message and exit the program. 46 | // 47 | // Options: 48 | // - DS_NO_STDIO: Disables the use of the standard input/output streams 49 | // - DS_LOG_LEVEL: Sets the logging level to one of the following values: 50 | // - DS_LOG_LEVEL_DEBUG: Print debug, info, warn and error messages 51 | // - DS_LOG_LEVEL_INFO: Print info, warn and error messages 52 | // - DS_LOG_LEVEL_WARN: Print warn and error messages 53 | // - DS_LOG_LEVEL_ERROR: Print error messages 54 | // - DS_LOG_LEVEL_NONE: Print no messages 55 | // The default logging level is DS_LOG_LEVEL_DEBUG 56 | // - DS_NO_TERMINAL_COLORS: Disables the use of terminal colors in the log 57 | // messages 58 | 59 | #ifndef DS_H 60 | #define DS_H 61 | 62 | #include 63 | #include 64 | 65 | // TODO: rework the hash table to actually work 66 | 67 | #ifndef DSHDEF 68 | #ifdef DSH_STATIC 69 | #define DSHDEF static 70 | #else 71 | #define DSHDEF extern 72 | #endif 73 | #endif 74 | 75 | // ALLOCATOR 76 | // 77 | // The allocator is a simple utility to allocate and free memory. You can define 78 | // the allocator to use when allocating and freeing memory. This can be used in 79 | // all the other data structures and utilities to use a custom allocator. 80 | typedef struct ds_allocator { 81 | uint8_t *start; 82 | uint8_t *prev; 83 | uint8_t *top; 84 | uint64_t size; 85 | } ds_allocator; 86 | 87 | DSHDEF void ds_allocator_init(ds_allocator *allocator, uint8_t *start, 88 | uint64_t size); 89 | DSHDEF void ds_allocator_dump(ds_allocator *allocator); 90 | DSHDEF void *ds_allocator_alloc(ds_allocator *allocator, uint64_t size); 91 | DSHDEF void ds_allocator_free(ds_allocator *allocator, void *ptr); 92 | 93 | // DYNAMIC ARRAY 94 | // 95 | // The dynamic array is a simple array that grows as needed. This is the real 96 | // implementation of dynamic arrays. The macros from the header file are just 97 | // quick inline versions of these functions. This implementation is generic and 98 | // can be used with any type of item, unlike the macros which require you to 99 | // define the array structure with the items, count and capacity fields. 100 | typedef struct ds_dynamic_array { 101 | struct ds_allocator *allocator; 102 | void *items; 103 | unsigned int item_size; 104 | unsigned int count; 105 | unsigned int capacity; 106 | } ds_dynamic_array; 107 | 108 | DSHDEF void ds_dynamic_array_init_allocator(ds_dynamic_array *da, 109 | unsigned int item_size, 110 | struct ds_allocator *allocator); 111 | DSHDEF void ds_dynamic_array_init(ds_dynamic_array *da, unsigned int item_size); 112 | DSHDEF int ds_dynamic_array_append(ds_dynamic_array *da, const void *item); 113 | DSHDEF int ds_dynamic_array_pop(ds_dynamic_array *da, const void **item); 114 | DSHDEF int ds_dynamic_array_append_many(ds_dynamic_array *da, void **new_items, 115 | unsigned int new_items_count); 116 | DSHDEF int ds_dynamic_array_get(ds_dynamic_array *da, unsigned int index, 117 | void *item); 118 | DSHDEF void ds_dynamic_array_get_ref(ds_dynamic_array *da, unsigned int index, 119 | void **item); 120 | DSHDEF int ds_dynamic_array_copy(ds_dynamic_array *da, ds_dynamic_array *copy); 121 | DSHDEF void ds_dynamic_array_sort(ds_dynamic_array *da, 122 | int (*compare)(const void *, const void *)); 123 | DSHDEF int ds_dynamic_array_reverse(ds_dynamic_array *da); 124 | DSHDEF int ds_dynamic_array_swap(ds_dynamic_array *da, unsigned int index1, 125 | unsigned int index2); 126 | DSHDEF void ds_dynamic_array_free(ds_dynamic_array *da); 127 | 128 | // PRIORITY QUEUE 129 | // 130 | // The priority queue is implemented as a heap, where you can define the 131 | // comparison function to use when inserting items. The comparison function 132 | // should return a positive value if the first item has higher priority than 133 | // the second item, a negative value if the second item has higher priority than 134 | // the first item, and 0 if the items have the same priority. 135 | typedef struct ds_priority_queue { 136 | ds_dynamic_array items; 137 | 138 | int (*compare)(const void *, const void *); 139 | } ds_priority_queue; 140 | 141 | DSHDEF void ds_priority_queue_init_allocator( 142 | ds_priority_queue *pq, int (*compare)(const void *, const void *), 143 | unsigned int item_size, struct ds_allocator *allocator); 144 | DSHDEF void ds_priority_queue_init(ds_priority_queue *pq, 145 | int (*compare)(const void *, const void *), 146 | unsigned int item_size); 147 | DSHDEF int ds_priority_queue_insert(ds_priority_queue *pq, void *item); 148 | DSHDEF int ds_priority_queue_pull(ds_priority_queue *pq, void *item); 149 | DSHDEF int ds_priority_queue_peek(ds_priority_queue *pq, void *item); 150 | DSHDEF int ds_priority_queue_empty(ds_priority_queue *pq); 151 | DSHDEF void ds_priority_queue_free(ds_priority_queue *pq); 152 | 153 | // STRING BUILDER 154 | // 155 | // The string builder is a simple utility to build strings. You can append 156 | // formatted strings to the string builder, and then build the final string. 157 | // The string builder will automatically grow as needed. 158 | typedef struct ds_string_builder { 159 | ds_dynamic_array items; 160 | } ds_string_builder; 161 | 162 | DSHDEF void ds_string_builder_init_allocator(ds_string_builder *sb, 163 | struct ds_allocator *allocator); 164 | DSHDEF void ds_string_builder_init(ds_string_builder *sb); 165 | DSHDEF int ds_string_builder_append(ds_string_builder *sb, const char *format, 166 | ...); 167 | DSHDEF int ds_string_builder_appendn(ds_string_builder *sb, const char *str, 168 | unsigned int len); 169 | DSHDEF int ds_string_builder_appendc(ds_string_builder *sb, char chr); 170 | DSHDEF int ds_string_builder_build(ds_string_builder *sb, char **str); 171 | DSHDEF void ds_string_builder_free(ds_string_builder *sb); 172 | 173 | // STRING SLICE 174 | // 175 | // The string slice is a simple utility to work with substrings. You can use the 176 | // string slice to tokenize a string, and to convert a string slice to an owned 177 | // string. 178 | typedef struct ds_string_slice { 179 | struct ds_allocator *allocator; 180 | char *str; 181 | unsigned int len; 182 | } ds_string_slice; 183 | 184 | DSHDEF void ds_string_slice_init_allocator(ds_string_slice *ss, char *str, 185 | unsigned int len, 186 | struct ds_allocator *allocator); 187 | DSHDEF void ds_string_slice_init(ds_string_slice *ss, char *str, 188 | unsigned int len); 189 | DSHDEF int ds_string_slice_tokenize(ds_string_slice *ss, char delimiter, 190 | ds_string_slice *token); 191 | DSHDEF int ds_string_slice_trim_left(ds_string_slice *ss, char chr); 192 | DSHDEF int ds_string_slice_trim_right(ds_string_slice *ss, char chr); 193 | DSHDEF int ds_string_slice_trim(ds_string_slice *ss, char chr); 194 | DSHDEF int ds_string_slice_to_owned(ds_string_slice *ss, char **str); 195 | DSHDEF void ds_string_slice_free(ds_string_slice *ss); 196 | 197 | // (DOUBLY) LINKED LIST 198 | // 199 | // The linked list is a simple list that can be used to push and pop items from 200 | // the front and back of the list. 201 | typedef struct ds_linked_list_node { 202 | void *item; 203 | struct ds_linked_list_node *prev; 204 | struct ds_linked_list_node *next; 205 | } ds_linked_list_node; 206 | 207 | typedef struct ds_linked_list { 208 | struct ds_allocator *allocator; 209 | unsigned int item_size; 210 | ds_linked_list_node *head; 211 | ds_linked_list_node *tail; 212 | } ds_linked_list; 213 | 214 | DSHDEF void ds_linked_list_init_allocator(ds_linked_list *ll, 215 | unsigned int item_size, 216 | struct ds_allocator *allocator); 217 | DSHDEF void ds_linked_list_init(ds_linked_list *ll, unsigned int item_size); 218 | DSHDEF int ds_linked_list_push_back(ds_linked_list *ll, void *item); 219 | DSHDEF int ds_linked_list_push_front(ds_linked_list *ll, void *item); 220 | DSHDEF int ds_linked_list_pop_back(ds_linked_list *ll, void *item); 221 | DSHDEF int ds_linked_list_pop_front(ds_linked_list *ll, void *item); 222 | DSHDEF int ds_linked_list_empty(ds_linked_list *ll); 223 | DSHDEF void ds_linked_list_free(ds_linked_list *ll); 224 | 225 | // HASH TABLE 226 | // 227 | // The hash table is a simple table that uses a hash function to store and 228 | // retrieve items. The hash table uses separate chaining to handle collisions. 229 | // You can define the hash and compare functions to use when inserting and 230 | // retrieving items. 231 | typedef struct ds_hash_table { 232 | struct ds_allocator *allocator; 233 | ds_dynamic_array *keys; 234 | ds_dynamic_array *values; 235 | unsigned int key_size; 236 | unsigned int value_size; 237 | unsigned int capacity; 238 | unsigned int (*hash)(const void *); 239 | int (*compare)(const void *, const void *); 240 | } ds_hash_table; 241 | 242 | DSHDEF int ds_hash_table_init_allocator( 243 | ds_hash_table *ht, unsigned int key_size, unsigned int value_size, 244 | unsigned int capacity, unsigned int (*hash)(const void *), 245 | int (*compare)(const void *, const void *), struct ds_allocator *allocator); 246 | DSHDEF int ds_hash_table_init(ds_hash_table *ht, unsigned int key_size, 247 | unsigned int value_size, unsigned int capacity, 248 | unsigned int (*hash)(const void *), 249 | int (*compare)(const void *, const void *)); 250 | DSHDEF int ds_hash_table_insert(ds_hash_table *ht, const void *key, 251 | void *value); 252 | DSHDEF int ds_hash_table_has(ds_hash_table *ht, const void *key); 253 | DSHDEF int ds_hash_table_get(ds_hash_table *ht, const void *key, void *value); 254 | DSHDEF int ds_hash_table_get_ref(ds_hash_table *ht, const void *key, 255 | void **value); 256 | DSHDEF unsigned int ds_hash_table_count(ds_hash_table *ht); 257 | DSHDEF int ds_hash_table_remove(ds_hash_table *ht, const void *key); 258 | DSHDEF void ds_hash_table_free(ds_hash_table *ht); 259 | 260 | // ARGUMENT PARSER 261 | // 262 | // The ds_argument parser is a simple utility to parse command line arguments. 263 | // You can define the options and arguments to parse, and then parse the command 264 | // line arguments. 265 | // Argument types 266 | enum ds_argument_type { 267 | ARGUMENT_TYPE_VALUE, // Argument with a value 268 | ARGUMENT_TYPE_FLAG, // Flag ds_argument 269 | ARGUMENT_TYPE_POSITIONAL, // Positional ds_argument 270 | ARGUMENT_TYPE_POSITIONAL_REST, // Positional ds_argument that consumes the 271 | // rest 272 | ARGUMENT_TYPE_VALUE_ARRAY, // Argument with an array of values 273 | }; 274 | 275 | // Argument options 276 | typedef struct ds_argparse_options { 277 | char short_name; // Short name of the ds_argument 278 | char *long_name; // Long name of the ds_argument 279 | char *description; // Description of the ds_argument 280 | enum ds_argument_type type; // Type of the ds_argument 281 | unsigned int required; // Whether the ds_argument is required 282 | } ds_argparse_options; 283 | 284 | // Argument 285 | typedef struct ds_argument { 286 | struct ds_argparse_options options; 287 | union { 288 | char *value; 289 | unsigned int flag; 290 | ds_dynamic_array values; 291 | }; 292 | } ds_argument; 293 | 294 | typedef struct ds_argparse_parser { 295 | struct ds_allocator *allocator; 296 | char *name; 297 | char *description; 298 | char *version; 299 | ds_dynamic_array arguments; // ds_argument 300 | } ds_argparse_parser; 301 | 302 | DSHDEF void ds_argparse_parser_init_allocator(struct ds_argparse_parser *parser, 303 | char *name, char *description, 304 | char *version, 305 | struct ds_allocator *allocator); 306 | DSHDEF void ds_argparse_parser_init(struct ds_argparse_parser *parser, 307 | char *name, char *description, 308 | char *version); 309 | DSHDEF int ds_argparse_add_argument(struct ds_argparse_parser *parser, 310 | struct ds_argparse_options options); 311 | DSHDEF int ds_argparse_parse(struct ds_argparse_parser *parser, int argc, 312 | char **argv); 313 | DSHDEF char *ds_argparse_get_value(struct ds_argparse_parser *parser, 314 | char *name); 315 | DSHDEF unsigned int ds_argparse_get_flag(struct ds_argparse_parser *parser, 316 | char *name); 317 | DSHDEF int ds_argparse_get_values(struct ds_argparse_parser *parser, char *name, 318 | ds_dynamic_array *values); 319 | DSHDEF void ds_argparse_print_help(struct ds_argparse_parser *parser); 320 | DSHDEF void ds_argparse_print_version(struct ds_argparse_parser *parser); 321 | DSHDEF void ds_argparse_parser_free(struct ds_argparse_parser *parser); 322 | 323 | // IO 324 | // 325 | // The io utils are a simple set of utilities to read and write files. 326 | #ifndef LINE_MAX 327 | #define LINE_MAX 4096 328 | #endif 329 | 330 | DSHDEF int ds_io_read_file(const char *path, char **buffer); 331 | DSHDEF int ds_io_write_file(const char *path, const char *buffer, const char *mode); 332 | 333 | // RETURN DEFER 334 | // 335 | // The return_defer macro is a simple way to return a value and jump to a label 336 | // to execute cleanup code. It is similar to the defer statement in Go. 337 | 338 | #ifndef return_defer 339 | #define return_defer(code) \ 340 | do { \ 341 | result = code; \ 342 | goto defer; \ 343 | } while (0) 344 | #endif // return_defer 345 | 346 | #ifndef DS_NO_STDLIB 347 | #include 348 | #include 349 | #endif 350 | 351 | #ifndef NULL 352 | #define NULL 0 353 | #endif 354 | 355 | #if defined(DS_MEMCPY) 356 | // ok 357 | #elif !defined(DS_MEMCPY) && !defined(DS_NO_STDLIB) 358 | #define DS_MEMCPY(dst, src, sz) memcpy(dst, src, sz) 359 | #elif defined(DS_NO_STDLIB) 360 | #define DS_MEMCPY(dst, src, sz) \ 361 | do { \ 362 | for (unsigned int i = 0; i < sz; i++) { \ 363 | ((char *)dst)[i] = ((char *)src)[i]; \ 364 | } \ 365 | } while (0) 366 | #endif 367 | 368 | #if defined(DS_MEMCMP) 369 | // ok 370 | #elif !defined(DS_MEMCMP) && !defined(DS_NO_STDLIB) 371 | #define DS_MEMCMP(ptr1, ptr2, sz) memcmp(ptr1, ptr2, sz) 372 | #elif defined(DS_NO_STDLIB) 373 | #define DS_MEMCMP(ptr1, ptr2, sz) \ 374 | ({ \ 375 | int result = 0; \ 376 | for (unsigned int i = 0; i < sz; i++) { \ 377 | if (((char *)ptr1)[i] != ((char *)ptr2)[i]) { \ 378 | result = ((char *)ptr1)[i] - ((char *)ptr2)[i]; \ 379 | break; \ 380 | } \ 381 | } \ 382 | result; \ 383 | }) 384 | #endif 385 | 386 | #if defined(DS_MALLOC) && defined(DS_FREE) 387 | // ok 388 | #elif !defined(DS_MALLOC) && !defined(DS_FREE) && !defined(DS_REALLOC) && \ 389 | !defined(DS_NO_STDLIB) && !defined(DS_AL_IMPLEMENTATION) 390 | #define DS_MALLOC(a, sz) malloc(sz) 391 | #define DS_REALLOC(a, ptr, old_sz, new_sz) realloc(ptr, new_sz) 392 | #define DS_FREE(a, ptr) free(ptr) 393 | #elif !defined(DS_MALLOC) && !defined(DS_FREE) && !defined(DS_REALLOC) && \ 394 | defined(DS_AL_IMPLEMENTATION) 395 | #define DS_MALLOC(a, sz) ds_allocator_alloc(a, sz) 396 | #define DS_FREE(a, ptr) ds_allocator_free(a, ptr) 397 | #elif defined(DS_NO_STDLIB) 398 | #error "Must define DS_MALLOC and DS_FREE when DS_NO_STDLIB is defined" 399 | #elif defined(DS_REALLOC) && !defined(DS_MALLOC) && !defined(DS_FREE) 400 | #error "Must define DS_MALLOC and DS_FREE when DS_REALLOC is defined" 401 | #else 402 | #error "Must define both DS_MALLOC and DS_FREE, or neither" 403 | #endif 404 | 405 | #ifndef DS_REALLOC 406 | static inline void *ds_realloc(void *a, void *ptr, unsigned int old_sz, 407 | unsigned int new_sz) { 408 | void *new_ptr = DS_MALLOC(a, new_sz); 409 | if (new_ptr == NULL) { 410 | DS_FREE(a, ptr); 411 | return NULL; 412 | } 413 | DS_MEMCPY(new_ptr, ptr, old_sz < new_sz ? old_sz : new_sz); 414 | DS_FREE(a, ptr); 415 | return new_ptr; 416 | } 417 | #define DS_REALLOC(a, ptr, old_sz, new_sz) ds_realloc(a, ptr, old_sz, new_sz) 418 | #endif 419 | 420 | #if defined(DS_EXIT) 421 | // ok 422 | #elif !defined(DS_EXIT) && !defined(DS_NO_STDLIB) 423 | #define DS_EXIT(code) exit(code) 424 | #elif defined(DS_NO_STDLIB) 425 | #error "Must define DS_EXIT when DS_NO_STDLIB is defined" 426 | #endif 427 | 428 | #ifndef DS_NO_STDIO 429 | #include 430 | #endif 431 | 432 | // TODO: actually do something for fprintf 433 | #if defined(DS_NO_STDIO) && !defined(fprintf) 434 | #define fprintf(stream, format, ...) 0 435 | #endif 436 | 437 | // TODO: actually do something for vsnprintf 438 | #if defined(DS_NO_STDIO) && !defined(vsnprintf) 439 | #define vsnprintf(buffer, size, format, args) 0 440 | #endif 441 | 442 | #if defined(DS_NO_STDIO) && !defined(stderr) 443 | #define stderr NULL 444 | #endif 445 | 446 | #if defined(DS_NO_STDIO) && !defined(stdout) 447 | #define stdout NULL 448 | #endif 449 | 450 | #define DS_LOG_LEVEL_DEBUG 1 451 | #define DS_LOG_LEVEL_INFO 10 452 | #define DS_LOG_LEVEL_WARN 100 453 | #define DS_LOG_LEVEL_ERROR 1000 454 | #define DS_LOG_LEVEL_NONE 10000 455 | 456 | #ifndef DS_LOG_LEVEL 457 | #define DS_LOG_LEVEL DS_LOG_LEVEL_DEBUG 458 | #endif 459 | 460 | #ifdef DS_NO_TERMINAL_COLORS 461 | #define DS_TERMINAL_RED "" 462 | #define DS_TERMINAL_YELLOW "" 463 | #define DS_TERMINAL_BLUE "" 464 | #define DS_TERMINAL_RESET "" 465 | #else 466 | #define DS_TERMINAL_RED "\033[1;31m" 467 | #define DS_TERMINAL_YELLOW "\033[1;33m" 468 | #define DS_TERMINAL_BLUE "\033[1;34m" 469 | #define DS_TERMINAL_RESET "\033[0m" 470 | #endif 471 | 472 | #if DS_LOG_LEVEL > DS_LOG_LEVEL_ERROR 473 | #define DS_LOG_ERROR(format, ...) 474 | #else 475 | #define DS_LOG_ERROR(format, ...) \ 476 | fprintf(stderr, \ 477 | DS_TERMINAL_RED "ERROR" DS_TERMINAL_RESET ": %s:%d: " format "\n", \ 478 | __FILE__, __LINE__, ##__VA_ARGS__) 479 | #endif 480 | 481 | #if DS_LOG_LEVEL > DS_LOG_LEVEL_WARN 482 | #define DS_LOG_WARN(format, ...) 483 | #else 484 | #define DS_LOG_WARN(format, ...) \ 485 | fprintf(stdout, \ 486 | DS_TERMINAL_YELLOW "WARN" DS_TERMINAL_RESET ": %s:%d: " format \ 487 | "\n", \ 488 | __FILE__, __LINE__, ##__VA_ARGS__) 489 | #endif 490 | 491 | #if DS_LOG_LEVEL > DS_LOG_LEVEL_INFO 492 | #define DS_LOG_INFO(format, ...) 493 | #else 494 | #define DS_LOG_INFO(format, ...) \ 495 | fprintf(stderr, \ 496 | DS_TERMINAL_BLUE "INFO" DS_TERMINAL_RESET ": %s:%d: " format "\n", \ 497 | __FILE__, __LINE__, ##__VA_ARGS__) 498 | #endif 499 | 500 | #if DS_LOG_LEVEL > DS_LOG_LEVEL_DEBUG 501 | #define DS_LOG_DEBUG(format, ...) 502 | #else 503 | #define DS_LOG_DEBUG(format, ...) \ 504 | fprintf(stdout, "DEBUG: %s:%d: " format "\n", __FILE__, __LINE__, \ 505 | ##__VA_ARGS__) 506 | #endif 507 | 508 | #ifndef DS_PANIC 509 | #define DS_PANIC(format, ...) \ 510 | do { \ 511 | DS_LOG_ERROR(format, ##__VA_ARGS__); \ 512 | DS_EXIT(1); \ 513 | } while (0) 514 | #endif 515 | 516 | // DYNAMIC ARRAY 517 | // 518 | // The dynamic array is a simple array that grows as needed. To use the dynamic 519 | // array append macro, you need to define a struct with the following fields: 520 | // - items: a pointer to the array of items 521 | // - count: the number of items in the array 522 | // - capacity: the number of items that can be stored in the array 523 | 524 | #define DS_DA_INIT_CAPACITY 8192 525 | #define ds_da_append(da, item) \ 526 | do { \ 527 | if ((da)->count >= (da)->capacity) { \ 528 | unsigned int new_capacity = (da)->capacity * 2; \ 529 | if (new_capacity == 0) { \ 530 | new_capacity = DS_DA_INIT_CAPACITY; \ 531 | } \ 532 | \ 533 | (da)->items = DS_REALLOC(NULL, (da)->items, \ 534 | (da)->capacity * sizeof(*(da)->items), \ 535 | new_capacity * sizeof(*(da)->items)); \ 536 | if ((da)->items == NULL) { \ 537 | DS_PANIC("Failed to reallocate dynamic array"); \ 538 | } \ 539 | \ 540 | (da)->capacity = new_capacity; \ 541 | } \ 542 | \ 543 | (da)->items[(da)->count++] = (item); \ 544 | } while (0) 545 | 546 | #define ds_da_append_many(da, new_items, new_items_count) \ 547 | do { \ 548 | if ((da)->count + new_items_count > (da)->capacity) { \ 549 | if ((da)->capacity == 0) { \ 550 | (da)->capacity = DS_DA_INIT_CAPACITY; \ 551 | } \ 552 | while ((da)->count + new_items_count > (da)->capacity) { \ 553 | (da)->capacity *= 2; \ 554 | } \ 555 | \ 556 | (da)->items = DS_REALLOC(NULL, (da)->items, \ 557 | (da)->capacity * sizeof(*(da)->items), \ 558 | (da)->capacity * sizeof(*(da)->items)); \ 559 | if ((da)->items == NULL) { \ 560 | DS_PANIC("Failed to reallocate dynamic array"); \ 561 | } \ 562 | } \ 563 | \ 564 | DS_MEMCPY((da)->items + (da)->count, new_items, \ 565 | new_items_count * sizeof(*(da)->items)); \ 566 | (da)->count += new_items_count; \ 567 | } while (0) 568 | 569 | #endif // DS_H 570 | 571 | #ifdef DS_IMPLEMENTATION 572 | #define DS_PQ_IMPLEMENTATION 573 | #define DS_SB_IMPLEMENTATION 574 | #define DS_SS_IMPLEMENTATION 575 | #define DS_DA_IMPLEMENTATION 576 | #define DS_LL_IMPLEMENTATION 577 | #define DS_HT_IMPLEMENTATION 578 | #define DS_AL_IMPLEMENTATION 579 | #define DS_AP_IMPLEMENTATION 580 | #define DS_IO_IMPLEMENTATION 581 | #endif // DS_IMPLEMENTATION 582 | 583 | #ifdef DS_IO_IMPLEMENTATION 584 | #define DS_SB_IMPLEMENTATION 585 | #endif // DS_IO_IMPLEMENTATION 586 | 587 | #ifdef DS_PQ_IMPLEMENTATION 588 | #define DS_DA_IMPLEMENTATION 589 | #endif // DS_PQ_IMPLEMENTATION 590 | 591 | #ifdef DS_SB_IMPLEMENTATION 592 | #define DS_DA_IMPLEMENTATION 593 | #endif // DS_SB_IMPLEMENTATION 594 | 595 | #ifdef DS_HT_IMPLEMENTATION 596 | #define DS_DA_IMPLEMENTATION 597 | #endif // DS_HT_IMPLEMENTATION 598 | 599 | #ifdef DS_AP_IMPLEMENTATION 600 | #define DS_DA_IMPLEMENTATION 601 | #endif // DS_AP_IMPLEMENTATION 602 | 603 | #ifdef DS_PQ_IMPLEMENTATION 604 | 605 | // Initialize the priority queue with a custom allocator 606 | DSHDEF void ds_priority_queue_init_allocator( 607 | ds_priority_queue *pq, int (*compare)(const void *, const void *), 608 | unsigned int item_size, struct ds_allocator *allocator) { 609 | ds_dynamic_array_init_allocator(&pq->items, item_size, allocator); 610 | 611 | pq->compare = compare; 612 | } 613 | 614 | // Initialize the priority queue 615 | DSHDEF void ds_priority_queue_init(ds_priority_queue *pq, 616 | int (*compare)(const void *, const void *), 617 | unsigned int item_size) { 618 | ds_priority_queue_init_allocator(pq, compare, item_size, NULL); 619 | } 620 | 621 | // Insert an item into the priority queue 622 | // 623 | // Returns 0 if the item was inserted successfully. 624 | DSHDEF int ds_priority_queue_insert(ds_priority_queue *pq, void *item) { 625 | ds_dynamic_array_append(&pq->items, item); 626 | 627 | int index = pq->items.count - 1; 628 | int parent = (index - 1) / 2; 629 | 630 | void *index_item = NULL; 631 | ds_dynamic_array_get_ref(&pq->items, index, &index_item); 632 | 633 | void *parent_item = NULL; 634 | ds_dynamic_array_get_ref(&pq->items, parent, &parent_item); 635 | 636 | while (index != 0 && pq->compare(index_item, parent_item) > 0) { 637 | ds_dynamic_array_swap(&pq->items, index, parent); 638 | 639 | index = parent; 640 | parent = (index - 1) / 2; 641 | 642 | ds_dynamic_array_get_ref(&pq->items, index, &index_item); 643 | ds_dynamic_array_get_ref(&pq->items, parent, &parent_item); 644 | } 645 | 646 | return 0; 647 | } 648 | 649 | // Pull the item with the highest priority from the priority queue 650 | // 651 | // Returns 0 if an item was pulled successfully, 1 if the priority queue is 652 | // empty. 653 | DSHDEF int ds_priority_queue_pull(ds_priority_queue *pq, void *item) { 654 | int result = 0; 655 | 656 | if (pq->items.count == 0) { 657 | DS_LOG_ERROR("Priority queue is empty"); 658 | return_defer(1); 659 | } 660 | 661 | ds_dynamic_array_get(&pq->items, 0, item); 662 | ds_dynamic_array_swap(&pq->items, 0, pq->items.count - 1); 663 | 664 | unsigned int index = 0; 665 | unsigned int swap = index; 666 | void *swap_item = NULL; 667 | do { 668 | index = swap; 669 | 670 | unsigned int left = 2 * index + 1; 671 | void *left_item = NULL; 672 | ds_dynamic_array_get_ref(&pq->items, swap, &swap_item); 673 | ds_dynamic_array_get_ref(&pq->items, left, &left_item); 674 | if (left < pq->items.count - 1 && 675 | pq->compare(left_item, swap_item) > 0) { 676 | swap = left; 677 | } 678 | 679 | unsigned int right = 2 * index + 2; 680 | void *right_item = NULL; 681 | ds_dynamic_array_get_ref(&pq->items, swap, &swap_item); 682 | ds_dynamic_array_get_ref(&pq->items, right, &right_item); 683 | if (right < pq->items.count - 1 && 684 | pq->compare(right_item, swap_item) > 0) { 685 | swap = right; 686 | } 687 | 688 | ds_dynamic_array_swap(&pq->items, index, swap); 689 | } while (swap != index); 690 | 691 | pq->items.count--; 692 | defer: 693 | return result; 694 | } 695 | 696 | // Peek at the item with the highest priority in the priority queue 697 | // 698 | // Returns 0 if an item was peeked successfully, 1 if the priority queue is 699 | // empty. 700 | DSHDEF int ds_priority_queue_peek(ds_priority_queue *pq, void *item) { 701 | int result = 0; 702 | 703 | if (pq->items.count == 0) { 704 | DS_LOG_ERROR("Priority queue is empty"); 705 | return_defer(1); 706 | } 707 | 708 | ds_dynamic_array_get(&pq->items, 0, item); 709 | 710 | defer: 711 | return result; 712 | } 713 | 714 | // Check if the priority queue is empty 715 | DSHDEF int ds_priority_queue_empty(ds_priority_queue *pq) { 716 | return pq->items.count == 0; 717 | } 718 | 719 | // Free the priority queue 720 | DSHDEF void ds_priority_queue_free(ds_priority_queue *pq) { 721 | ds_dynamic_array_free(&pq->items); 722 | 723 | pq->compare = NULL; 724 | } 725 | 726 | #endif // DS_PQ_IMPLEMENTATION 727 | 728 | #ifdef DS_SB_IMPLEMENTATION 729 | 730 | DSHDEF void ds_string_builder_init_allocator(ds_string_builder *sb, 731 | struct ds_allocator *allocator) { 732 | ds_dynamic_array_init_allocator(&sb->items, sizeof(char), allocator); 733 | } 734 | 735 | // Initialize the string builder 736 | DSHDEF void ds_string_builder_init(ds_string_builder *sb) { 737 | ds_string_builder_init_allocator(sb, NULL); 738 | } 739 | 740 | // Append a formatted string to the string builder 741 | // 742 | // Returns 0 if the string was appended successfully. 743 | DSHDEF int ds_string_builder_appendn(ds_string_builder *sb, const char *str, 744 | unsigned int len) { 745 | return ds_dynamic_array_append_many(&sb->items, (void **)str, len); 746 | } 747 | 748 | // Append a formatted string to the string builder 749 | // 750 | // Returns 0 if the string was appended successfully. 751 | DSHDEF int ds_string_builder_append(ds_string_builder *sb, const char *format, 752 | ...) { 753 | int result = 0; 754 | 755 | va_list args; 756 | va_start(args, format); 757 | int needed = vsnprintf(NULL, 0, format, args); 758 | va_end(args); 759 | 760 | char *buffer = DS_MALLOC(sb->items.allocator, needed + 1); 761 | if (buffer == NULL) { 762 | DS_LOG_ERROR("Failed to allocate string"); 763 | return_defer(1); 764 | } 765 | 766 | va_start(args, format); 767 | vsnprintf(buffer, needed + 1, format, args); 768 | va_end(args); 769 | 770 | if (ds_dynamic_array_append_many(&sb->items, (void **)buffer, needed) != 771 | 0) { 772 | return_defer(1); 773 | } 774 | 775 | defer: 776 | if (buffer != NULL) { 777 | DS_FREE(sb->items.allocator, buffer); 778 | } 779 | return result; 780 | } 781 | 782 | // Append a character to the string builder 783 | // 784 | // Returns 0 if the character was appended successfully. 785 | DSHDEF int ds_string_builder_appendc(ds_string_builder *sb, char chr) { 786 | return ds_dynamic_array_append(&sb->items, &chr); 787 | } 788 | 789 | // Build the final string from the string builder 790 | // 791 | // Returns 0 if the string was built successfully, 1 if the string could not be 792 | // allocated. 793 | DSHDEF int ds_string_builder_build(ds_string_builder *sb, char **str) { 794 | int result = 0; 795 | 796 | *str = DS_MALLOC(NULL, sb->items.count + 1); 797 | if (*str == NULL) { 798 | DS_LOG_ERROR("Failed to allocate string"); 799 | return_defer(1); 800 | } 801 | 802 | DS_MEMCPY(*str, (char *)sb->items.items, sb->items.count); 803 | (*str)[sb->items.count] = '\0'; 804 | 805 | defer: 806 | return result; 807 | } 808 | 809 | // Free the string builder 810 | DSHDEF void ds_string_builder_free(ds_string_builder *sb) { 811 | ds_dynamic_array_free(&sb->items); 812 | } 813 | 814 | #endif // DS_SB_IMPLEMENTATION 815 | 816 | #ifdef DS_SS_IMPLEMENTATION 817 | 818 | DSHDEF void ds_string_slice_init_allocator(ds_string_slice *ss, char *str, 819 | unsigned int len, 820 | struct ds_allocator *allocator) { 821 | ss->allocator = allocator; 822 | ss->str = str; 823 | ss->len = len; 824 | } 825 | 826 | // Initialize the string slice 827 | DSHDEF void ds_string_slice_init(ds_string_slice *ss, char *str, 828 | unsigned int len) { 829 | ds_string_slice_init_allocator(ss, str, len, NULL); 830 | } 831 | 832 | // Tokenize the string slice by a delimiter 833 | // 834 | // Returns 0 if a token was found, 1 if the string slice is empty. 835 | DSHDEF int ds_string_slice_tokenize(ds_string_slice *ss, char delimiter, 836 | ds_string_slice *token) { 837 | int result = 0; 838 | 839 | if (ss->len == 0 || ss->str == NULL) { 840 | return_defer(1); 841 | } 842 | 843 | token->str = ss->str; 844 | token->len = 0; 845 | 846 | for (unsigned int i = 0; i < ss->len; i++) { 847 | if (ss->str[i] == delimiter) { 848 | token->len = i; 849 | ss->str += i + 1; 850 | ss->len -= i + 1; 851 | return_defer(0); 852 | } 853 | } 854 | 855 | token->len = ss->len; 856 | ss->str += ss->len; 857 | ss->len = 0; 858 | 859 | defer: 860 | return result; 861 | } 862 | 863 | // Trim the left side of the string slice by a character 864 | // 865 | // Returns 0 if the string was trimmed successfully, 1 if the string slice is 866 | DSHDEF int ds_string_slice_trim_left(ds_string_slice *ss, char chr) { 867 | int result = 0; 868 | 869 | while (ss->len > 0 && ss->str[0] == chr) { 870 | ss->str++; 871 | ss->len--; 872 | } 873 | 874 | return result; 875 | } 876 | 877 | // Trim the right side of the string slice by a character 878 | // 879 | // Returns 0 if the string was trimmed successfully, 1 if the string slice is 880 | DSHDEF int ds_string_slice_trim_right(ds_string_slice *ss, char chr) { 881 | int result = 0; 882 | 883 | while (ss->len > 0 && ss->str[ss->len - 1] == chr) { 884 | ss->len--; 885 | } 886 | 887 | return result; 888 | } 889 | 890 | // Trim the string slice by a character 891 | // 892 | // Returns 0 if the string was trimmed successfully, 1 if the string slice is 893 | DSHDEF int ds_string_slice_trim(ds_string_slice *ss, char chr) { 894 | int result = 0; 895 | 896 | if (ds_string_slice_trim_left(ss, chr) != 0) { 897 | return_defer(1); 898 | } 899 | 900 | if (ds_string_slice_trim_right(ss, chr) != 0) { 901 | return_defer(1); 902 | } 903 | 904 | defer: 905 | return result; 906 | } 907 | 908 | // Convert the string slice to an owned string 909 | // 910 | // Returns 0 if the string was converted successfully, 1 if the string could not 911 | // be allocated. 912 | DSHDEF int ds_string_slice_to_owned(ds_string_slice *ss, char **str) { 913 | int result = 0; 914 | 915 | *str = DS_MALLOC(ss->allocator, ss->len + 1); 916 | if (*str == NULL) { 917 | DS_LOG_ERROR("Failed to allocate string"); 918 | return_defer(1); 919 | } 920 | 921 | DS_MEMCPY(*str, ss->str, ss->len); 922 | (*str)[ss->len] = '\0'; 923 | 924 | defer: 925 | return result; 926 | } 927 | 928 | // Free the string slice 929 | DSHDEF void ds_string_slice_free(ds_string_slice *ss) { 930 | ss->str = NULL; 931 | ss->len = 0; 932 | } 933 | 934 | #endif // DS_SS_IMPLEMENTATION 935 | 936 | #ifdef DS_DA_IMPLEMENTATION 937 | 938 | // Initialize the dynamic array with a custom allocator 939 | // 940 | // The item_size parameter is the size of each item in the array. 941 | DSHDEF void ds_dynamic_array_init_allocator(ds_dynamic_array *da, 942 | unsigned int item_size, 943 | struct ds_allocator *allocator) { 944 | da->allocator = allocator; 945 | da->items = NULL; 946 | da->item_size = item_size; 947 | da->count = 0; 948 | da->capacity = 0; 949 | } 950 | 951 | // Initialize the dynamic array 952 | // 953 | // The item_size parameter is the size of each item in the array. 954 | DSHDEF void ds_dynamic_array_init(ds_dynamic_array *da, 955 | unsigned int item_size) { 956 | ds_dynamic_array_init_allocator(da, item_size, NULL); 957 | } 958 | 959 | // Append an item to the dynamic array 960 | // 961 | // Returns 0 if the item was appended successfully, 1 if the array could not be 962 | // reallocated. 963 | DSHDEF int ds_dynamic_array_append(ds_dynamic_array *da, const void *item) { 964 | int result = 0; 965 | 966 | if (da->count >= da->capacity) { 967 | unsigned int new_capacity = da->capacity * 2; 968 | if (new_capacity == 0) { 969 | new_capacity = DS_DA_INIT_CAPACITY; 970 | } 971 | 972 | da->items = 973 | DS_REALLOC(da->allocator, da->items, da->capacity * da->item_size, 974 | new_capacity * da->item_size); 975 | 976 | if (da->items == NULL) { 977 | DS_LOG_ERROR("Failed to reallocate dynamic array"); 978 | return_defer(1); 979 | } 980 | 981 | da->capacity = new_capacity; 982 | } 983 | 984 | DS_MEMCPY((char *)da->items + da->count * da->item_size, item, 985 | da->item_size); 986 | da->count++; 987 | 988 | defer: 989 | return result; 990 | } 991 | 992 | // Pop an item from the dynamic array 993 | // 994 | // Returns 0 if the item was popped successfully, 1 if the array is empty. 995 | // If the item is NULL, then we just pop the item without returning it. 996 | DSHDEF int ds_dynamic_array_pop(ds_dynamic_array *da, const void **item) { 997 | int result = 0; 998 | 999 | if (da->count == 0) { 1000 | DS_LOG_ERROR("Dynamic array is empty"); 1001 | *item = NULL; 1002 | return_defer(1); 1003 | } 1004 | 1005 | if (item != NULL) { 1006 | *item = (char *)da->items + (da->count - 1) * da->item_size; 1007 | } 1008 | da->count--; 1009 | 1010 | defer: 1011 | return result; 1012 | } 1013 | 1014 | // Append multiple items to the dynamic array 1015 | // 1016 | // Returns 0 if the items were appended successfully, 1 if the array could not 1017 | // be reallocated. 1018 | DSHDEF int ds_dynamic_array_append_many(ds_dynamic_array *da, void **new_items, 1019 | unsigned int new_items_count) { 1020 | int result = 0; 1021 | 1022 | if (da->count + new_items_count > da->capacity) { 1023 | if (da->capacity == 0) { 1024 | da->capacity = DS_DA_INIT_CAPACITY; 1025 | } 1026 | while (da->count + new_items_count > da->capacity) { 1027 | da->capacity *= 2; 1028 | } 1029 | 1030 | da->items = 1031 | DS_REALLOC(da->allocator, da->items, da->capacity * da->item_size, 1032 | da->capacity * da->item_size); 1033 | if (da->items == NULL) { 1034 | DS_LOG_ERROR("Failed to reallocate dynamic array"); 1035 | return_defer(1); 1036 | } 1037 | } 1038 | 1039 | DS_MEMCPY((char *)da->items + da->count * da->item_size, new_items, 1040 | new_items_count * da->item_size); 1041 | da->count += new_items_count; 1042 | 1043 | defer: 1044 | return result; 1045 | } 1046 | 1047 | // Get an item from the dynamic array 1048 | // 1049 | // Returns 0 if the item was retrieved successfully, 1 if the index is out of 1050 | // bounds. 1051 | DSHDEF int ds_dynamic_array_get(ds_dynamic_array *da, unsigned int index, 1052 | void *item) { 1053 | int result = 0; 1054 | 1055 | if (index >= da->count) { 1056 | DS_LOG_ERROR("Index out of bounds"); 1057 | return_defer(1); 1058 | } 1059 | 1060 | DS_MEMCPY(item, (char *)da->items + index * da->item_size, da->item_size); 1061 | 1062 | defer: 1063 | return result; 1064 | } 1065 | 1066 | // Get a reference to an item from the dynamic array 1067 | DSHDEF void ds_dynamic_array_get_ref(ds_dynamic_array *da, unsigned int index, 1068 | void **item) { 1069 | *item = (char *)da->items + index * da->item_size; 1070 | } 1071 | 1072 | // Copy the dynamic array to another dynamic array 1073 | // 1074 | // Returns 0 if the array was copied successfully, 1 if the array could not be 1075 | // allocated. 1076 | DSHDEF int ds_dynamic_array_copy(ds_dynamic_array *da, ds_dynamic_array *copy) { 1077 | int result = 0; 1078 | 1079 | copy->items = DS_MALLOC(da->allocator, da->capacity * da->item_size); 1080 | if (copy->items == NULL) { 1081 | DS_LOG_ERROR("Failed to allocate dynamic array items"); 1082 | return_defer(1); 1083 | } 1084 | 1085 | copy->item_size = da->item_size; 1086 | copy->count = da->count; 1087 | copy->capacity = da->capacity; 1088 | 1089 | DS_MEMCPY(copy->items, da->items, da->count * da->item_size); 1090 | 1091 | defer: 1092 | return result; 1093 | } 1094 | 1095 | DSHDEF void ds_dynamic_array_sort(ds_dynamic_array *da, 1096 | int (*compare)(const void *, const void *)) { 1097 | qsort(da->items, da->count, da->item_size, compare); 1098 | } 1099 | 1100 | // Reverse the dynamic array 1101 | // 1102 | // Returns 0 if the array was reversed successfully, 1 if the array could not be 1103 | // allocated. 1104 | DSHDEF int ds_dynamic_array_reverse(ds_dynamic_array *da) { 1105 | int result = 0; 1106 | 1107 | for (unsigned int i = 0; i < da->count / 2; i++) { 1108 | unsigned int j = da->count - i - 1; 1109 | 1110 | void *temp = DS_MALLOC(da->allocator, da->item_size); 1111 | if (temp == NULL) { 1112 | DS_LOG_ERROR("Failed to allocate temporary item"); 1113 | return_defer(1); 1114 | } 1115 | 1116 | DS_MEMCPY(temp, (char *)da->items + i * da->item_size, da->item_size); 1117 | DS_MEMCPY((char *)da->items + i * da->item_size, 1118 | (char *)da->items + j * da->item_size, da->item_size); 1119 | DS_MEMCPY((char *)da->items + j * da->item_size, temp, da->item_size); 1120 | DS_FREE(da->allocator, temp); 1121 | } 1122 | 1123 | defer: 1124 | return result; 1125 | } 1126 | 1127 | // Swap two items in the dynamic array 1128 | // 1129 | // Returns 0 if the items were swapped successfully, 1 if the index is out of 1130 | // bounds or if the temporary item could not be allocated. 1131 | DSHDEF int ds_dynamic_array_swap(ds_dynamic_array *da, unsigned int index1, 1132 | unsigned int index2) { 1133 | int result = 0; 1134 | 1135 | if (index1 >= da->count || index2 >= da->count) { 1136 | DS_LOG_ERROR("Index out of bounds"); 1137 | return_defer(1); 1138 | } 1139 | 1140 | void *temp = DS_MALLOC(da->allocator, da->item_size); 1141 | if (temp == NULL) { 1142 | DS_LOG_ERROR("Failed to allocate temporary item"); 1143 | return_defer(1); 1144 | } 1145 | 1146 | void *index1_item = NULL; 1147 | ds_dynamic_array_get_ref(da, index1, &index1_item); 1148 | 1149 | void *index2_item = NULL; 1150 | ds_dynamic_array_get_ref(da, index2, &index2_item); 1151 | 1152 | DS_MEMCPY(temp, index1_item, da->item_size); 1153 | DS_MEMCPY(index1_item, index2_item, da->item_size); 1154 | DS_MEMCPY(index2_item, temp, da->item_size); 1155 | 1156 | DS_FREE(da->allocator, temp); 1157 | 1158 | defer: 1159 | return result; 1160 | } 1161 | 1162 | // Free the dynamic array 1163 | DSHDEF void ds_dynamic_array_free(ds_dynamic_array *da) { 1164 | if (da->items != NULL) { 1165 | DS_FREE(da->allocator, da->items); 1166 | } 1167 | da->items = NULL; 1168 | da->count = 0; 1169 | da->capacity = 0; 1170 | } 1171 | 1172 | #endif // DS_DA_IMPLEMENTATION 1173 | 1174 | #ifdef DS_LL_IMPLEMENTATION 1175 | 1176 | // Initialize the linked list with a custom allocator 1177 | // 1178 | // The item_size parameter is the size of each item in the list. 1179 | DSHDEF void ds_linked_list_init_allocator(ds_linked_list *ll, 1180 | unsigned int item_size, 1181 | struct ds_allocator *allocator) { 1182 | ll->allocator = allocator; 1183 | ll->item_size = item_size; 1184 | ll->head = NULL; 1185 | ll->tail = NULL; 1186 | } 1187 | 1188 | // Initialize the linked list 1189 | // 1190 | // The item_size parameter is the size of each item in the list. 1191 | DSHDEF void ds_linked_list_init(ds_linked_list *ll, unsigned int item_size) { 1192 | ds_linked_list_init_allocator(ll, item_size, NULL); 1193 | } 1194 | 1195 | // Push an item to the back of the linked list 1196 | // 1197 | // Returns 0 if the item was pushed successfully, 1 if the list could not be 1198 | // allocated. 1199 | DSHDEF int ds_linked_list_push_back(ds_linked_list *ll, void *item) { 1200 | int result = 0; 1201 | 1202 | ds_linked_list_node *node = 1203 | DS_MALLOC(ll->allocator, sizeof(ds_linked_list_node)); 1204 | if (node == NULL) { 1205 | DS_LOG_ERROR("Failed to allocate linked list node"); 1206 | return_defer(1); 1207 | } 1208 | 1209 | node->item = DS_MALLOC(ll->allocator, ll->item_size); 1210 | if (node->item == NULL) { 1211 | DS_LOG_ERROR("Failed to allocate linked list item"); 1212 | return_defer(1); 1213 | } 1214 | 1215 | DS_MEMCPY(node->item, item, ll->item_size); 1216 | node->prev = ll->tail; 1217 | node->next = NULL; 1218 | 1219 | if (ll->tail != NULL) { 1220 | ll->tail->next = node; 1221 | } 1222 | ll->tail = node; 1223 | 1224 | if (ll->head == NULL) { 1225 | ll->head = node; 1226 | } 1227 | 1228 | defer: 1229 | if (result != 0 && node != NULL) { 1230 | if (node->item != NULL) { 1231 | DS_FREE(ll->allocator, node->item); 1232 | } 1233 | DS_FREE(ll->allocator, node); 1234 | } 1235 | return result; 1236 | } 1237 | 1238 | // Push an item to the front of the linked list 1239 | // 1240 | // Returns 0 if the item was pushed successfully, 1 if the list could not be 1241 | // allocated. 1242 | DSHDEF int ds_linked_list_push_front(ds_linked_list *ll, void *item) { 1243 | int result = 0; 1244 | 1245 | ds_linked_list_node *node = 1246 | DS_MALLOC(ll->allocator, sizeof(ds_linked_list_node)); 1247 | if (node == NULL) { 1248 | DS_LOG_ERROR("Failed to allocate linked list node"); 1249 | return_defer(1); 1250 | } 1251 | 1252 | node->item = DS_MALLOC(ll->allocator, ll->item_size); 1253 | if (node->item == NULL) { 1254 | DS_LOG_ERROR("Failed to allocate linked list item"); 1255 | return_defer(1); 1256 | } 1257 | 1258 | DS_MEMCPY(node->item, item, ll->item_size); 1259 | node->prev = NULL; 1260 | node->next = ll->head; 1261 | 1262 | if (ll->head != NULL) { 1263 | ll->head->prev = node; 1264 | } 1265 | ll->head = node; 1266 | 1267 | if (ll->tail == NULL) { 1268 | ll->tail = node; 1269 | } 1270 | 1271 | defer: 1272 | if (result != 0 && node != NULL) { 1273 | if (node->item != NULL) { 1274 | DS_FREE(ll->allocator, node->item); 1275 | } 1276 | DS_FREE(ll->allocator, node); 1277 | } 1278 | return result; 1279 | } 1280 | 1281 | // Pop an item from the back of the linked list 1282 | // 1283 | // Returns 0 if the item was popped successfully, 1 if the list is empty. 1284 | // The item is stored in the item parameter. 1285 | DSHDEF int ds_linked_list_pop_back(ds_linked_list *ll, void *item) { 1286 | ds_linked_list_node *node = NULL; 1287 | int result = 0; 1288 | 1289 | if (ll->tail == NULL) { 1290 | DS_LOG_ERROR("Linked list is empty"); 1291 | return_defer(1); 1292 | } 1293 | 1294 | node = ll->tail; 1295 | DS_MEMCPY(item, node->item, ll->item_size); 1296 | 1297 | ll->tail = node->prev; 1298 | if (ll->tail != NULL) { 1299 | ll->tail->next = NULL; 1300 | } 1301 | 1302 | if (node == ll->head) { 1303 | ll->head = NULL; 1304 | } 1305 | 1306 | defer: 1307 | if (node != NULL) { 1308 | if (node->item != NULL) { 1309 | DS_FREE(ll->allocator, node->item); 1310 | } 1311 | DS_FREE(ll->allocator, node); 1312 | } 1313 | return result; 1314 | } 1315 | 1316 | // Pop an item from the front of the linked list 1317 | // 1318 | // Returns 0 if the item was popped successfully, 1 if the list is empty. 1319 | // The item is stored in the item parameter. 1320 | DSHDEF int ds_linked_list_pop_front(ds_linked_list *ll, void *item) { 1321 | ds_linked_list_node *node = NULL; 1322 | int result = 0; 1323 | 1324 | if (ll->head == NULL) { 1325 | DS_LOG_ERROR("Linked list is empty"); 1326 | return_defer(1); 1327 | } 1328 | 1329 | node = ll->head; 1330 | DS_MEMCPY(item, node->item, ll->item_size); 1331 | 1332 | ll->head = node->next; 1333 | if (ll->head != NULL) { 1334 | ll->head->prev = NULL; 1335 | } 1336 | 1337 | if (node == ll->tail) { 1338 | ll->tail = NULL; 1339 | } 1340 | 1341 | defer: 1342 | if (node != NULL) { 1343 | if (node->item != NULL) { 1344 | DS_FREE(ll->allocator, node->item); 1345 | } 1346 | DS_FREE(ll->allocator, node); 1347 | } 1348 | return result; 1349 | } 1350 | 1351 | // Check if the linked list is empty 1352 | // 1353 | // Returns 1 if the list is empty, 0 if the list is not empty. 1354 | DSHDEF int ds_linked_list_empty(ds_linked_list *ll) { return ll->head == NULL; } 1355 | 1356 | DSHDEF void ds_linked_list_free(ds_linked_list *ll) { 1357 | ds_linked_list_node *node = ll->head; 1358 | while (node != NULL) { 1359 | ds_linked_list_node *next = node->next; 1360 | if (node->item != NULL) { 1361 | DS_FREE(ll->allocator, node->item); 1362 | } 1363 | DS_FREE(ll->allocator, node); 1364 | node = next; 1365 | } 1366 | ll->head = NULL; 1367 | ll->tail = NULL; 1368 | } 1369 | 1370 | #endif // DS_ST_IMPLEMENTATION 1371 | 1372 | #ifdef DS_HT_IMPLEMENTATION 1373 | 1374 | // Initialize the hash table with a custom allocator 1375 | DSHDEF int 1376 | ds_hash_table_init_allocator(ds_hash_table *ht, unsigned int key_size, 1377 | unsigned int value_size, unsigned int capacity, 1378 | unsigned int (*hash)(const void *), 1379 | int (*compare)(const void *, const void *), 1380 | struct ds_allocator *allocator) { 1381 | int result = 0; 1382 | 1383 | ht->allocator = allocator; 1384 | 1385 | ht->keys = DS_MALLOC(ht->allocator, capacity * sizeof(ds_dynamic_array)); 1386 | if (ht->keys == NULL) { 1387 | DS_LOG_ERROR("Failed to allocate hash table keys"); 1388 | return_defer(1); 1389 | } 1390 | 1391 | ht->values = DS_MALLOC(ht->allocator, capacity * sizeof(ds_dynamic_array)); 1392 | if (ht->values == NULL) { 1393 | DS_LOG_ERROR("Failed to allocate hash table values"); 1394 | return_defer(1); 1395 | } 1396 | 1397 | for (unsigned int i = 0; i < capacity; i++) { 1398 | ds_dynamic_array_init(ht->keys + i, key_size); 1399 | ds_dynamic_array_init(ht->values + i, value_size); 1400 | } 1401 | 1402 | ht->key_size = key_size; 1403 | ht->value_size = value_size; 1404 | ht->capacity = capacity; 1405 | 1406 | ht->hash = hash; 1407 | ht->compare = compare; 1408 | 1409 | defer: 1410 | if (result != 0) { 1411 | if (ht->keys != NULL) { 1412 | DS_FREE(NULL, ht->keys); 1413 | } 1414 | if (ht->values != NULL) { 1415 | DS_FREE(NULL, ht->values); 1416 | } 1417 | } 1418 | return result; 1419 | } 1420 | 1421 | // Initialize the hash table 1422 | // 1423 | // The key_size and value_size parameters are the size of each key and value in 1424 | // the table. The capacity parameter is the initial capacity of the table. The 1425 | // hash and compare parameters are the hash and compare functions to use when 1426 | // inserting and retrieving items. 1427 | DSHDEF int ds_hash_table_init(ds_hash_table *ht, unsigned int key_size, 1428 | unsigned int value_size, unsigned int capacity, 1429 | unsigned int (*hash)(const void *), 1430 | int (*compare)(const void *, const void *)) { 1431 | return ds_hash_table_init_allocator(ht, key_size, value_size, capacity, 1432 | hash, compare, NULL); 1433 | } 1434 | 1435 | // Insert an item into the hash table 1436 | // 1437 | // Returns 0 if the item was inserted successfully, 1 if the item could not be 1438 | // inserted. 1439 | DSHDEF int ds_hash_table_insert(ds_hash_table *ht, const void *key, 1440 | void *value) { 1441 | int result = 0; 1442 | 1443 | unsigned int index = ht->hash(key) % ht->capacity; 1444 | 1445 | ds_dynamic_array *keys = ht->keys + index; 1446 | ds_dynamic_array *values = ht->values + index; 1447 | 1448 | for (unsigned int i = 0; i < keys->count; i++) { 1449 | void *k = NULL; 1450 | ds_dynamic_array_get_ref(keys, i, &k); 1451 | 1452 | if (ht->compare(k, key) == 0) { 1453 | void *v = NULL; 1454 | ds_dynamic_array_get_ref(values, i, &v); 1455 | 1456 | DS_MEMCPY(v, value, ht->value_size); 1457 | return_defer(0); 1458 | } 1459 | } 1460 | 1461 | if (ds_dynamic_array_append(keys, key) != 0) { 1462 | DS_LOG_ERROR("Failed to append key to hash table"); 1463 | return_defer(1); 1464 | } 1465 | 1466 | if (ds_dynamic_array_append(values, value) != 0) { 1467 | DS_LOG_ERROR("Failed to append value to hash table"); 1468 | return_defer(1); 1469 | } 1470 | 1471 | defer: 1472 | return result; 1473 | } 1474 | 1475 | // Check if the hash table has an item with the given key 1476 | // 1477 | // Returns 1 if the item was found, 0 if the item was not found. 1478 | DSHDEF int ds_hash_table_has(ds_hash_table *ht, const void *key) { 1479 | unsigned int index = ht->hash(key) % ht->capacity; 1480 | 1481 | ds_dynamic_array *keys = ht->keys + index; 1482 | 1483 | for (unsigned int i = 0; i < keys->count; i++) { 1484 | void *k = NULL; 1485 | ds_dynamic_array_get_ref(keys, i, &k); 1486 | 1487 | if (ht->compare(k, key) == 0) { 1488 | return 1; 1489 | } 1490 | } 1491 | 1492 | return 0; 1493 | } 1494 | 1495 | // Get an item from the hash table 1496 | // 1497 | // Returns 0 if the item was retrieved successfully, 1 if the item was not 1498 | // found. 1499 | DSHDEF int ds_hash_table_get(ds_hash_table *ht, const void *key, void *value) { 1500 | int result = 0; 1501 | 1502 | unsigned int index = ht->hash(key) % ht->capacity; 1503 | 1504 | ds_dynamic_array *keys = ht->keys + index; 1505 | ds_dynamic_array *values = ht->values + index; 1506 | 1507 | for (unsigned int i = 0; i < keys->count; i++) { 1508 | void *k = NULL; 1509 | ds_dynamic_array_get_ref(keys, i, &k); 1510 | 1511 | if (ht->compare(k, key) == 0) { 1512 | ds_dynamic_array_get(values, i, value); 1513 | return_defer(0); 1514 | } 1515 | } 1516 | 1517 | return_defer(1); 1518 | 1519 | defer: 1520 | return result; 1521 | } 1522 | 1523 | // Get a reference to an item from the hash table 1524 | // 1525 | // Returns 0 if the item was retrieved successfully, 1 if the item was not 1526 | // found. 1527 | DSHDEF int ds_hash_table_get_ref(ds_hash_table *ht, const void *key, 1528 | void **value) { 1529 | int result = 0; 1530 | 1531 | unsigned int index = ht->hash(key) % ht->capacity; 1532 | 1533 | ds_dynamic_array *keys = ht->keys + index; 1534 | ds_dynamic_array *values = ht->values + index; 1535 | 1536 | for (unsigned int i = 0; i < keys->count; i++) { 1537 | void *k = NULL; 1538 | ds_dynamic_array_get_ref(keys, i, &k); 1539 | 1540 | if (ht->compare(k, key) == 0) { 1541 | ds_dynamic_array_get_ref(values, i, value); 1542 | return_defer(0); 1543 | } 1544 | } 1545 | 1546 | return_defer(1); 1547 | 1548 | defer: 1549 | return result; 1550 | } 1551 | 1552 | // Get the number of items in the hash table 1553 | // 1554 | // Returns the number of items in the hash table. 1555 | DSHDEF unsigned int ds_hash_table_count(ds_hash_table *ht) { 1556 | unsigned int count = 0; 1557 | for (unsigned int i = 0; i < ht->capacity; i++) { 1558 | count += (ht->keys + i)->count; 1559 | } 1560 | return count; 1561 | } 1562 | 1563 | // Remove an item from the hash table 1564 | // 1565 | // Returns 0 if the item was removed successfully, 1 if the item was not found. 1566 | DSHDEF int ds_hash_table_remove(ds_hash_table *ht, const void *key) { 1567 | (void)ht; 1568 | (void)key; 1569 | DS_LOG_ERROR("Not implemented"); 1570 | return 1; 1571 | } 1572 | 1573 | // Free the hash table 1574 | // 1575 | // This function frees all the memory used by the hash table. 1576 | DSHDEF void ds_hash_table_free(ds_hash_table *ht) { 1577 | for (unsigned int i = 0; i < ht->capacity; i++) { 1578 | ds_dynamic_array_free(ht->keys + i); 1579 | ds_dynamic_array_free(ht->values + i); 1580 | } 1581 | DS_FREE(NULL, ht->keys); 1582 | DS_FREE(NULL, ht->values); 1583 | } 1584 | 1585 | #endif // DS_HT_IMPLEMENTATION 1586 | 1587 | #ifdef DS_AL_IMPLEMENTATION 1588 | 1589 | static void uint64_read_le(uint8_t *data, uint64_t *value) { 1590 | *value = ((uint64_t)data[0] << 0) | ((uint64_t)data[1] << 8) | 1591 | ((uint64_t)data[2] << 16) | ((uint64_t)data[3] << 24) | 1592 | ((uint64_t)data[4] << 32) | ((uint64_t)data[5] << 40) | 1593 | ((uint64_t)data[6] << 48) | ((uint64_t)data[7] << 56); 1594 | } 1595 | 1596 | static void uint64_write_le(uint8_t *data, uint64_t value) { 1597 | data[0] = (value >> 0) & 0xff; 1598 | data[1] = (value >> 8) & 0xff; 1599 | data[2] = (value >> 16) & 0xff; 1600 | data[3] = (value >> 24) & 0xff; 1601 | data[4] = (value >> 32) & 0xff; 1602 | data[5] = (value >> 40) & 0xff; 1603 | data[6] = (value >> 48) & 0xff; 1604 | data[7] = (value >> 56) & 0xff; 1605 | } 1606 | 1607 | static void uint32_read_le(uint8_t *data, uint32_t *value) { 1608 | *value = ((uint32_t)data[0] << 0) | ((uint32_t)data[1] << 8) | 1609 | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24); 1610 | } 1611 | 1612 | static void uint32_write_le(uint8_t *data, uint32_t value) { 1613 | data[0] = (value >> 0) & 0xff; 1614 | data[1] = (value >> 8) & 0xff; 1615 | data[2] = (value >> 16) & 0xff; 1616 | data[3] = (value >> 24) & 0xff; 1617 | } 1618 | 1619 | #define BLOCK_METADATA_SIZE 28 1620 | #define BLOCK_INDEX_UNDEFINED -1 1621 | 1622 | /* 1623 | * | prev | next | size | free | ... size bytes of data ... | 1624 | */ 1625 | typedef struct block { 1626 | int64_t prev; // 8 bytes 1627 | int64_t next; // 8 bytes 1628 | uint64_t size; // 8 bytes 1629 | uint32_t free; // 4 bytes 1630 | uint8_t *data; // 8 bytes 1631 | } block_t; 1632 | 1633 | static void block_read(uint8_t *data, block_t *block) { 1634 | uint64_read_le(data + 0, (uint64_t *)&block->prev); 1635 | uint64_read_le(data + 8, (uint64_t *)&block->next); 1636 | uint64_read_le(data + 16, &block->size); 1637 | uint32_read_le(data + 24, &block->free); 1638 | block->data = data + BLOCK_METADATA_SIZE; 1639 | } 1640 | 1641 | static void block_write(uint8_t *data, block_t *block) { 1642 | uint64_write_le(data + 0, block->prev); 1643 | uint64_write_le(data + 8, block->next); 1644 | uint64_write_le(data + 16, block->size); 1645 | uint32_write_le(data + 24, block->free); 1646 | } 1647 | 1648 | // Initialize the allocator 1649 | // 1650 | // The start parameter is the start of the memory block to allocate from, and 1651 | // the size parameter is the maximum size of the memory allocator. 1652 | DSHDEF void ds_allocator_init(ds_allocator *allocator, uint8_t *start, 1653 | uint64_t size) { 1654 | allocator->start = start; 1655 | allocator->prev = NULL; 1656 | allocator->top = start; 1657 | allocator->size = size; 1658 | } 1659 | 1660 | // Dump the allocator to stdout 1661 | // 1662 | // This function prints the contents of the allocator to stdout. 1663 | DSHDEF void ds_allocator_dump(ds_allocator *allocator) { 1664 | block_t block = {0}; 1665 | uint8_t *ptr = allocator->start; 1666 | 1667 | fprintf(stdout, "%*s %*s %*s %*s %*s\n", 14, "", 14, "prev", 14, "next", 14, 1668 | "size", 14, "free"); 1669 | 1670 | while (ptr < allocator->top) { 1671 | block_read(ptr, &block); 1672 | 1673 | uint8_t *prev = (block.prev == BLOCK_INDEX_UNDEFINED) 1674 | ? NULL 1675 | : allocator->start + block.prev; 1676 | uint8_t *next = (block.next == BLOCK_INDEX_UNDEFINED) 1677 | ? NULL 1678 | : allocator->start + block.next; 1679 | 1680 | fprintf(stdout, "%*p %*p %*p %*lu %*u\n", 14, ptr, 14, prev, 14, next, 1681 | 14, block.size, 14, block.free); 1682 | 1683 | ptr += (block.size + BLOCK_METADATA_SIZE); 1684 | } 1685 | } 1686 | 1687 | static int allocator_find_block(ds_allocator *allocator, uint64_t size, 1688 | block_t *block) { 1689 | if (allocator->prev == NULL) { 1690 | return 0; 1691 | } 1692 | 1693 | block_t current = {0}; 1694 | uint8_t *ptr = allocator->start; 1695 | 1696 | while (ptr < allocator->top) { 1697 | block_read(ptr, ¤t); 1698 | 1699 | if (current.free && current.size >= size + BLOCK_METADATA_SIZE * 2) { 1700 | uint64_t old_size = current.size; 1701 | int64_t old_next = current.next; 1702 | 1703 | block_t split = {0}; 1704 | split.prev = (uint64_t)(ptr - allocator->start); 1705 | split.next = old_next; 1706 | split.size = old_size - size - BLOCK_METADATA_SIZE; 1707 | split.free = 1; 1708 | split.data = ptr + BLOCK_METADATA_SIZE + size + BLOCK_METADATA_SIZE; 1709 | 1710 | block_write(ptr + BLOCK_METADATA_SIZE + size, &split); 1711 | 1712 | *block = current; 1713 | block->next = 1714 | (uint64_t)(ptr - allocator->start) + BLOCK_METADATA_SIZE + size; 1715 | block->size = size; 1716 | block->free = 0; 1717 | block->data = ptr + BLOCK_METADATA_SIZE; 1718 | 1719 | block_write(ptr, block); 1720 | 1721 | block_t next = {0}; 1722 | block_read(allocator->start + old_next, &next); 1723 | 1724 | next.prev = 1725 | (uint64_t)(ptr - allocator->start) + BLOCK_METADATA_SIZE + size; 1726 | 1727 | block_write(allocator->start + old_next, &next); 1728 | 1729 | return 1; 1730 | } 1731 | 1732 | if (current.free && current.size >= size) { 1733 | *block = current; 1734 | block->free = 0; 1735 | 1736 | block_write(ptr, block); 1737 | 1738 | return 1; 1739 | } 1740 | 1741 | ptr += (current.size + BLOCK_METADATA_SIZE); 1742 | } 1743 | 1744 | return 0; 1745 | } 1746 | 1747 | // Allocate memory from the allocator 1748 | // 1749 | // This function allocates memory from the allocator. If the allocator is unable 1750 | // to allocate the memory, it returns NULL. 1751 | DSHDEF void *ds_allocator_alloc(ds_allocator *allocator, uint64_t size) { 1752 | block_t block = {0}; 1753 | if (allocator_find_block(allocator, size, &block) != 0) { 1754 | return block.data; 1755 | } 1756 | 1757 | if (allocator->top + size + BLOCK_METADATA_SIZE > 1758 | allocator->start + allocator->size) { 1759 | return NULL; 1760 | } 1761 | 1762 | block.next = BLOCK_INDEX_UNDEFINED; 1763 | block.size = size; 1764 | block.free = 0; 1765 | block.data = allocator->top + BLOCK_METADATA_SIZE; 1766 | 1767 | if (allocator->prev == NULL) { 1768 | block.prev = BLOCK_INDEX_UNDEFINED; 1769 | } else { 1770 | block.prev = (uint64_t)(allocator->prev - allocator->start); 1771 | 1772 | block_t prev = {0}; 1773 | block_read(allocator->prev, &prev); 1774 | prev.next = (uint64_t)(allocator->top - allocator->start); 1775 | 1776 | block_write(allocator->prev, &prev); 1777 | } 1778 | 1779 | block_write(allocator->top, &block); 1780 | 1781 | allocator->prev = allocator->top; 1782 | allocator->top += size + BLOCK_METADATA_SIZE; 1783 | 1784 | return block.data; 1785 | } 1786 | 1787 | // Free memory from the allocator 1788 | // 1789 | // This function frees memory from the allocator. If the pointer is not within 1790 | // the bounds of the allocator, it does nothing. 1791 | DSHDEF void ds_allocator_free(ds_allocator *allocator, void *ptr) { 1792 | if ((uint8_t *)ptr > allocator->top || (uint8_t *)ptr < allocator->start) { 1793 | return; 1794 | } 1795 | 1796 | block_t block = {0}; 1797 | block_read(ptr - BLOCK_METADATA_SIZE, &block); 1798 | block.free = 1; 1799 | 1800 | if (block.prev != BLOCK_INDEX_UNDEFINED) { 1801 | block_t prev = {0}; 1802 | block_read(allocator->start + block.prev, &prev); 1803 | 1804 | if (prev.free) { 1805 | prev.next = block.next; 1806 | prev.size += block.size + BLOCK_METADATA_SIZE; 1807 | 1808 | uint8_t *mptr = allocator->start + block.prev; 1809 | 1810 | block_t next = {0}; 1811 | block_read(allocator->start + block.next, &next); 1812 | 1813 | next.prev = (uint64_t)((uint8_t *)mptr - allocator->start); 1814 | 1815 | block_write(allocator->start + block.next, &next); 1816 | block_write(allocator->start + block.prev, &prev); 1817 | 1818 | block = prev; 1819 | ptr = mptr + BLOCK_METADATA_SIZE; 1820 | } 1821 | } 1822 | 1823 | if (block.next != BLOCK_INDEX_UNDEFINED) { 1824 | block_t next = {0}; 1825 | block_read(allocator->start + block.next, &next); 1826 | 1827 | if (next.free) { 1828 | block_t next_next = {0}; 1829 | block_read(allocator->start + next.next, &next_next); 1830 | 1831 | uint8_t *mptr = ptr - BLOCK_METADATA_SIZE; 1832 | 1833 | next_next.prev = (uint64_t)((uint8_t *)mptr - allocator->start); 1834 | 1835 | block_write(allocator->start + next.next, &next_next); 1836 | 1837 | block.next = next.next; 1838 | block.size += next.size + BLOCK_METADATA_SIZE; 1839 | } 1840 | } 1841 | 1842 | block_write(ptr - BLOCK_METADATA_SIZE, &block); 1843 | } 1844 | 1845 | #endif // DS_AL_IMPLEMENTATION 1846 | 1847 | #ifdef DS_AP_IMPLEMENTATION 1848 | 1849 | // Initialize the argparser with a custom allocator 1850 | DSHDEF void ds_argparse_parser_init_allocator(ds_argparse_parser *parser, 1851 | char *name, char *description, 1852 | char *version, 1853 | ds_allocator *allocator) { 1854 | parser->allocator = allocator; 1855 | parser->name = name; 1856 | parser->description = description; 1857 | parser->version = version; 1858 | ds_dynamic_array_init_allocator(&parser->arguments, sizeof(ds_argument), 1859 | allocator); 1860 | 1861 | ds_argparse_add_argument( 1862 | parser, 1863 | (ds_argparse_options){.short_name = 'v', 1864 | .long_name = "version", 1865 | .description = "print the program version", 1866 | .type = ARGUMENT_TYPE_FLAG, 1867 | .required = 0}); 1868 | ds_argparse_add_argument( 1869 | parser, (ds_argparse_options){.short_name = 'h', 1870 | .long_name = "help", 1871 | .description = "print this help message", 1872 | .type = ARGUMENT_TYPE_FLAG, 1873 | .required = 0}); 1874 | } 1875 | 1876 | // Initialize with the default malloc allocator 1877 | DSHDEF void ds_argparse_parser_init(ds_argparse_parser *parser, char *name, 1878 | char *description, char *version) { 1879 | ds_argparse_parser_init_allocator(parser, name, description, version, NULL); 1880 | } 1881 | 1882 | // Add an argument to the parser 1883 | // 1884 | // Adds a new argument to the parser with the given options. 1885 | // 1886 | // Arguments: 1887 | // - parser: argument parser 1888 | // - options: argument options 1889 | DSHDEF int ds_argparse_add_argument(ds_argparse_parser *parser, 1890 | ds_argparse_options options) { 1891 | ds_argument arg = { 1892 | .options = options, 1893 | }; 1894 | 1895 | switch (options.type) { 1896 | case ARGUMENT_TYPE_VALUE: 1897 | arg.value = NULL; 1898 | break; 1899 | case ARGUMENT_TYPE_FLAG: 1900 | arg.flag = 0; 1901 | break; 1902 | case ARGUMENT_TYPE_POSITIONAL: 1903 | arg.value = NULL; 1904 | break; 1905 | case ARGUMENT_TYPE_POSITIONAL_REST: 1906 | ds_dynamic_array_init(&arg.values, sizeof(char *)); 1907 | break; 1908 | case ARGUMENT_TYPE_VALUE_ARRAY: 1909 | ds_dynamic_array_init(&arg.values, sizeof(char *)); 1910 | break; 1911 | } 1912 | 1913 | return ds_dynamic_array_append(&parser->arguments, &arg); 1914 | } 1915 | 1916 | static int argparse_validate_parser(ds_argparse_parser *parser) { 1917 | int result = 0; 1918 | int found_optional_positional = 0; 1919 | int found_positional_rest = 0; 1920 | 1921 | for (size_t i = 0; i < parser->arguments.count; i++) { 1922 | ds_argument *item = NULL; 1923 | ds_dynamic_array_get_ref(&parser->arguments, i, (void **)&item); 1924 | 1925 | ds_argparse_options options = item->options; 1926 | 1927 | if (options.type == ARGUMENT_TYPE_POSITIONAL && found_positional_rest) { 1928 | DS_LOG_ERROR("positional argument after positional rest: %s", 1929 | options.long_name); 1930 | result = 1; 1931 | } 1932 | 1933 | if (options.type == ARGUMENT_TYPE_POSITIONAL_REST && 1934 | found_positional_rest) { 1935 | DS_LOG_ERROR("multiple positional rest arguments"); 1936 | result = 1; 1937 | } 1938 | 1939 | if (options.type == ARGUMENT_TYPE_POSITIONAL_REST && 1940 | found_positional_rest == 0) { 1941 | found_positional_rest = 1; 1942 | } 1943 | 1944 | if (options.type == ARGUMENT_TYPE_POSITIONAL && options.required == 0) { 1945 | found_optional_positional = 1; 1946 | } 1947 | 1948 | if (options.short_name == '\0' && options.long_name == NULL) { 1949 | DS_LOG_ERROR("no short_name and long_name for argument %zu", i); 1950 | result = 1; 1951 | } 1952 | if (options.type == ARGUMENT_TYPE_FLAG && options.required == 1) { 1953 | DS_LOG_ERROR("flag argument cannot be required: %s", 1954 | options.long_name); 1955 | result = 1; 1956 | } 1957 | if (options.type == ARGUMENT_TYPE_POSITIONAL && options.required == 1 && 1958 | found_optional_positional == 1) { 1959 | DS_LOG_ERROR("required positional argument after optional: %s", 1960 | options.long_name); 1961 | result = 1; 1962 | } 1963 | } 1964 | 1965 | return result; 1966 | } 1967 | 1968 | static int argparse_post_validate_parser(ds_argparse_parser *parser) { 1969 | int result = 0; 1970 | 1971 | for (size_t i = 0; i < parser->arguments.count; i++) { 1972 | ds_argument *item = NULL; 1973 | ds_dynamic_array_get_ref(&parser->arguments, i, (void **)&item); 1974 | 1975 | ds_argparse_options options = item->options; 1976 | 1977 | if (options.type == ARGUMENT_TYPE_POSITIONAL && options.required == 1) { 1978 | if (item->value == NULL) { 1979 | DS_LOG_ERROR("missing required positional argument: %s", 1980 | options.long_name); 1981 | result = 1; 1982 | } 1983 | } 1984 | 1985 | if (options.type == ARGUMENT_TYPE_VALUE && options.required == 1) { 1986 | if (item->value == NULL) { 1987 | DS_LOG_ERROR("missing required argument: --%s", 1988 | options.long_name); 1989 | result = 1; 1990 | } 1991 | } 1992 | 1993 | if (options.type == ARGUMENT_TYPE_VALUE_ARRAY && 1994 | options.required == 1) { 1995 | if (item->values.count == 0) { 1996 | DS_LOG_ERROR("missing required argument: --%s", 1997 | options.long_name); 1998 | result = 1; 1999 | } 2000 | } 2001 | 2002 | if (options.type == ARGUMENT_TYPE_POSITIONAL_REST && 2003 | options.required == 1) { 2004 | if (item->values.count == 0) { 2005 | DS_LOG_ERROR("missing required positional rest argument: %s", 2006 | options.long_name); 2007 | result = 1; 2008 | } 2009 | } 2010 | } 2011 | 2012 | return result; 2013 | } 2014 | 2015 | static ds_argument *argparse_get_option_arg(ds_argparse_parser *parser, 2016 | const char *name) { 2017 | if (name[0] != '-') { 2018 | DS_LOG_WARN("provided name is not an option: %s", name); 2019 | return NULL; 2020 | } 2021 | 2022 | ds_argument *arg = NULL; 2023 | 2024 | for (size_t j = 0; j < parser->arguments.count; j++) { 2025 | ds_argument *item = NULL; 2026 | ds_dynamic_array_get_ref(&parser->arguments, j, (void **)&item); 2027 | 2028 | if ((name[1] == '-' && item->options.long_name != NULL && 2029 | strcmp(name + 2, item->options.long_name) == 0) || 2030 | (name[1] != '-' && item->options.short_name != '\0' && 2031 | name[1] == item->options.short_name)) { 2032 | arg = item; 2033 | break; 2034 | } 2035 | } 2036 | 2037 | if (arg == NULL) { 2038 | DS_LOG_ERROR("invalid argument: %s", name); 2039 | return NULL; 2040 | } 2041 | 2042 | return arg; 2043 | } 2044 | 2045 | static ds_argument *argparse_get_positional_arg(ds_argparse_parser *parser, 2046 | const char *name) { 2047 | if (name[0] == '-') { 2048 | DS_LOG_WARN("provided name is not a positional argument: %s", name); 2049 | return NULL; 2050 | } 2051 | 2052 | ds_argument *arg = NULL; 2053 | for (size_t j = 0; j < parser->arguments.count; j++) { 2054 | ds_argument *item = NULL; 2055 | ds_dynamic_array_get_ref(&parser->arguments, j, (void **)&item); 2056 | 2057 | if (item->options.type == ARGUMENT_TYPE_POSITIONAL && 2058 | item->value == NULL) { 2059 | arg = item; 2060 | break; 2061 | } 2062 | 2063 | if (item->options.type == ARGUMENT_TYPE_POSITIONAL_REST) { 2064 | arg = item; 2065 | break; 2066 | } 2067 | } 2068 | 2069 | return arg; 2070 | } 2071 | 2072 | // Parse the command line arguments 2073 | // 2074 | // Parses the command line arguments and sets the values of the arguments in the 2075 | // parser. 2076 | // 2077 | // Arguments: 2078 | // - parser: argument parser 2079 | // - argc: number of command line arguments 2080 | // - argv: command line arguments 2081 | // 2082 | // Returns 0 if the parsing was successful, 1 otherwise. 2083 | int ds_argparse_parse(ds_argparse_parser *parser, int argc, char *argv[]) { 2084 | int result = 0; 2085 | 2086 | if (argparse_validate_parser(parser) != 0) { 2087 | return_defer(1); 2088 | } 2089 | 2090 | for (int i = 1; i < argc; i++) { 2091 | char *name = argv[i]; 2092 | 2093 | if (strcmp(name, "-h") == 0 || strcmp(name, "--help") == 0) { 2094 | ds_argparse_print_help(parser); 2095 | exit(0); 2096 | } 2097 | 2098 | if (strcmp(name, "-v") == 0 || strcmp(name, "--version") == 0) { 2099 | ds_argparse_print_version(parser); 2100 | exit(0); 2101 | } 2102 | 2103 | if (name[0] == '-') { 2104 | ds_argument *arg = argparse_get_option_arg(parser, name); 2105 | 2106 | if (arg == NULL) { 2107 | return_defer(1); 2108 | } 2109 | 2110 | switch (arg->options.type) { 2111 | case ARGUMENT_TYPE_FLAG: { 2112 | arg->flag = 1; 2113 | break; 2114 | } 2115 | case ARGUMENT_TYPE_VALUE: { 2116 | if (i + 1 >= argc) { 2117 | DS_LOG_ERROR("missing value for argument: %s", name); 2118 | ds_argparse_print_help(parser); 2119 | return_defer(1); 2120 | } 2121 | 2122 | arg->value = argv[++i]; 2123 | break; 2124 | } 2125 | case ARGUMENT_TYPE_VALUE_ARRAY: { 2126 | if (i + 1 >= argc) { 2127 | DS_LOG_ERROR("missing value for argument: %s", name); 2128 | ds_argparse_print_help(parser); 2129 | return_defer(1); 2130 | } 2131 | 2132 | if (ds_dynamic_array_append(&arg->values, &argv[++i]) != 0) { 2133 | DS_LOG_ERROR("failed to append value to argument: %s", 2134 | name); 2135 | return_defer(1); 2136 | } 2137 | break; 2138 | } 2139 | default: { 2140 | DS_LOG_ERROR("type not supported for argument: %s", name); 2141 | ds_argparse_print_help(parser); 2142 | return_defer(1); 2143 | } 2144 | } 2145 | } else { 2146 | ds_argument *arg = argparse_get_positional_arg(parser, name); 2147 | 2148 | if (arg == NULL) { 2149 | DS_LOG_ERROR("unexpected positional argument: %s", name); 2150 | ds_argparse_print_help(parser); 2151 | return_defer(1); 2152 | } 2153 | 2154 | switch (arg->options.type) { 2155 | case ARGUMENT_TYPE_POSITIONAL: { 2156 | arg->value = name; 2157 | break; 2158 | } 2159 | case ARGUMENT_TYPE_POSITIONAL_REST: { 2160 | if (ds_dynamic_array_append(&arg->values, &name) != 0) { 2161 | DS_LOG_ERROR("failed to append value to positional rest"); 2162 | return_defer(1); 2163 | } 2164 | break; 2165 | } 2166 | default: { 2167 | DS_LOG_ERROR("type not supported for argument: %s", name); 2168 | ds_argparse_print_help(parser); 2169 | return_defer(1); 2170 | } 2171 | } 2172 | 2173 | arg->value = name; 2174 | } 2175 | } 2176 | 2177 | if (argparse_post_validate_parser(parser) != 0) { 2178 | ds_argparse_print_help(parser); 2179 | return_defer(1); 2180 | } 2181 | 2182 | defer: 2183 | return result; 2184 | } 2185 | 2186 | // Get the value of an argument 2187 | // 2188 | // Returns the value of the argument with the given long name. 2189 | // 2190 | // Arguments: 2191 | // - parser: argument parser 2192 | // - long_name: long name of the argument 2193 | // 2194 | // Returns: 2195 | // - value of the argument 2196 | char *ds_argparse_get_value(ds_argparse_parser *parser, char *long_name) { 2197 | for (size_t i = 0; i < parser->arguments.count; i++) { 2198 | ds_argument *item = NULL; 2199 | ds_dynamic_array_get_ref(&parser->arguments, i, (void **)&item); 2200 | 2201 | if (item->options.long_name != NULL && 2202 | strcmp(long_name, item->options.long_name) == 0) { 2203 | if (item->options.type != ARGUMENT_TYPE_VALUE && 2204 | item->options.type != ARGUMENT_TYPE_POSITIONAL) { 2205 | DS_LOG_WARN("argument is not a value: %s", long_name); 2206 | } 2207 | return item->value; 2208 | } 2209 | } 2210 | 2211 | return NULL; 2212 | } 2213 | 2214 | // Get the value of a positional argument 2215 | // 2216 | // Returns the value of the positional argument with the given long name. 2217 | // 2218 | // Arguments: 2219 | // - parser: argument parser 2220 | // - long_name: long name of the argument 2221 | // 2222 | // Returns: 2223 | // - value of the flag argument 2224 | unsigned int ds_argparse_get_flag(ds_argparse_parser *parser, char *long_name) { 2225 | for (size_t i = 0; i < parser->arguments.count; i++) { 2226 | ds_argument *item = NULL; 2227 | ds_dynamic_array_get_ref(&parser->arguments, i, (void **)&item); 2228 | 2229 | if (item->options.long_name != NULL && 2230 | strcmp(long_name, item->options.long_name) == 0) { 2231 | if (item->options.type != ARGUMENT_TYPE_FLAG) { 2232 | DS_LOG_WARN("argument is not a flag: %s", long_name); 2233 | } 2234 | return item->flag; 2235 | } 2236 | } 2237 | 2238 | return 0; 2239 | } 2240 | 2241 | DSHDEF int ds_argparse_get_values(struct ds_argparse_parser *parser, 2242 | char *long_name, ds_dynamic_array *values) { 2243 | for (size_t i = 0; i < parser->arguments.count; i++) { 2244 | ds_argument *item = NULL; 2245 | ds_dynamic_array_get_ref(&parser->arguments, i, (void **)&item); 2246 | 2247 | if (item->options.long_name != NULL && 2248 | strcmp(long_name, item->options.long_name) == 0) { 2249 | if (item->options.type != ARGUMENT_TYPE_POSITIONAL_REST && 2250 | item->options.type != ARGUMENT_TYPE_VALUE_ARRAY) { 2251 | DS_LOG_WARN("argument is not a array: %s", long_name); 2252 | } 2253 | *values = item->values; 2254 | return item->values.count; 2255 | } 2256 | } 2257 | 2258 | return 0; 2259 | } 2260 | 2261 | // Show the help message 2262 | // 2263 | // Prints the help message for the argument parser. 2264 | // 2265 | // Arguments: 2266 | // - parser: argument parser 2267 | void ds_argparse_print_help(ds_argparse_parser *parser) { 2268 | fprintf(stdout, "usage: %s [options]", parser->name); 2269 | 2270 | for (size_t i = 0; i < parser->arguments.count; i++) { 2271 | ds_argument *item = NULL; 2272 | ds_dynamic_array_get_ref(&parser->arguments, i, (void **)&item); 2273 | 2274 | ds_argparse_options options = item->options; 2275 | 2276 | if (options.type == ARGUMENT_TYPE_VALUE && options.required == 1) { 2277 | fprintf(stdout, " -%c <%s>", options.short_name, options.long_name); 2278 | } 2279 | } 2280 | 2281 | for (size_t i = 0; i < parser->arguments.count; i++) { 2282 | ds_argument *item = NULL; 2283 | ds_dynamic_array_get_ref(&parser->arguments, i, (void **)&item); 2284 | 2285 | ds_argparse_options options = item->options; 2286 | 2287 | if (options.type == ARGUMENT_TYPE_POSITIONAL) { 2288 | if (options.required == 1) { 2289 | fprintf(stdout, " <%s>", options.long_name); 2290 | } else { 2291 | fprintf(stdout, " [%s]", options.long_name); 2292 | } 2293 | } 2294 | } 2295 | 2296 | for (size_t i = 0; i < parser->arguments.count; i++) { 2297 | ds_argument *item = NULL; 2298 | ds_dynamic_array_get_ref(&parser->arguments, i, (void **)&item); 2299 | 2300 | ds_argparse_options options = item->options; 2301 | 2302 | if (options.type == ARGUMENT_TYPE_VALUE_ARRAY) { 2303 | if (options.required == 1) { 2304 | fprintf(stdout, " -%c <%s>...", options.short_name, 2305 | options.long_name); 2306 | } else { 2307 | fprintf(stdout, " -%c [%s]...", options.short_name, 2308 | options.long_name); 2309 | } 2310 | } 2311 | } 2312 | 2313 | for (size_t i = 0; i < parser->arguments.count; i++) { 2314 | ds_argument *item = NULL; 2315 | ds_dynamic_array_get_ref(&parser->arguments, i, (void **)&item); 2316 | 2317 | ds_argparse_options options = item->options; 2318 | 2319 | if (options.type == ARGUMENT_TYPE_POSITIONAL_REST) { 2320 | if (options.required == 1) { 2321 | fprintf(stdout, " <%s>...", options.long_name); 2322 | } else { 2323 | fprintf(stdout, " [%s]...", options.long_name); 2324 | } 2325 | } 2326 | } 2327 | 2328 | fprintf(stdout, "\n"); 2329 | fprintf(stdout, "%s\n", parser->description); 2330 | fprintf(stdout, "\n"); 2331 | fprintf(stdout, "options:\n"); 2332 | 2333 | for (size_t i = 0; i < parser->arguments.count; i++) { 2334 | ds_argument *item = NULL; 2335 | ds_dynamic_array_get_ref(&parser->arguments, i, (void **)&item); 2336 | 2337 | switch (item->options.type) { 2338 | case ARGUMENT_TYPE_POSITIONAL: { 2339 | fprintf(stdout, " %c, %s\n", item->options.short_name, 2340 | item->options.long_name); 2341 | fprintf(stdout, " %s\n", item->options.description); 2342 | fprintf(stdout, "\n"); 2343 | break; 2344 | } 2345 | case ARGUMENT_TYPE_POSITIONAL_REST: { 2346 | fprintf(stdout, " %c, %s\n", item->options.short_name, 2347 | item->options.long_name); 2348 | fprintf(stdout, " %s\n", item->options.description); 2349 | fprintf(stdout, "\n"); 2350 | break; 2351 | } 2352 | case ARGUMENT_TYPE_FLAG: { 2353 | fprintf(stdout, " -%c, --%s\n", item->options.short_name, 2354 | item->options.long_name); 2355 | fprintf(stdout, " %s\n", item->options.description); 2356 | fprintf(stdout, "\n"); 2357 | break; 2358 | } 2359 | case ARGUMENT_TYPE_VALUE: { 2360 | fprintf(stdout, " -%c, --%s \n", item->options.short_name, 2361 | item->options.long_name); 2362 | fprintf(stdout, " %s\n", item->options.description); 2363 | fprintf(stdout, "\n"); 2364 | break; 2365 | } 2366 | case ARGUMENT_TYPE_VALUE_ARRAY: { 2367 | fprintf(stdout, " -%c, --%s ...\n", 2368 | item->options.short_name, item->options.long_name); 2369 | fprintf(stdout, " %s\n", item->options.description); 2370 | fprintf(stdout, "\n"); 2371 | break; 2372 | } 2373 | default: { 2374 | DS_PANIC("invalid argument type"); 2375 | } 2376 | } 2377 | } 2378 | } 2379 | 2380 | // Show the version 2381 | // 2382 | // Prints the version of the program. 2383 | // 2384 | // Arguments: 2385 | // - parser: argument parser 2386 | void ds_argparse_print_version(ds_argparse_parser *parser) { 2387 | fprintf(stdout, "%s %s\n", parser->name, parser->version); 2388 | } 2389 | 2390 | // Free the argument parser 2391 | // 2392 | // Frees the memory allocated for the argument parser. 2393 | // 2394 | // Arguments: 2395 | // - parser: argument parser 2396 | void ds_argparse_free(ds_argparse_parser *parser) { 2397 | ds_dynamic_array_free(&parser->arguments); 2398 | } 2399 | 2400 | #endif // DS_AP_IMPLEMENTATION 2401 | 2402 | #ifdef DS_IO_IMPLEMENTATION 2403 | 2404 | // Read a file 2405 | // 2406 | // Reads the contents of a file into a buffer. 2407 | // 2408 | // Arguments: 2409 | // - filename: name of the file to read 2410 | // - buffer: pointer to the buffer to store the contents of the file 2411 | // 2412 | // Returns: 2413 | // - the number of bytes read 2414 | DSHDEF int ds_io_read_file(const char *filename, char **buffer) { 2415 | int result = 0; 2416 | FILE *file = NULL; 2417 | ds_string_builder sb; 2418 | ds_string_builder_init(&sb); 2419 | 2420 | if (filename != NULL) { 2421 | file = fopen(filename, "r"); 2422 | if (file == NULL) { 2423 | DS_LOG_ERROR("Failed to open file: %s", filename); 2424 | return_defer(-1); 2425 | } 2426 | } else { 2427 | file = stdin; 2428 | } 2429 | 2430 | char line[LINE_MAX] = {0}; 2431 | while (fgets(line, sizeof(line), file) != NULL) { 2432 | unsigned int len = 0; 2433 | for (len = 0; len < LINE_MAX; len++) { 2434 | if (line[len] == '\n' || line[len] == EOF) { 2435 | break; 2436 | } 2437 | } 2438 | 2439 | if (len == LINE_MAX) { 2440 | len = strlen(line); 2441 | } else { 2442 | len += 1; 2443 | line[len] = '\0'; 2444 | } 2445 | 2446 | if (len == LINE_MAX) { 2447 | DS_LOG_ERROR("Line too long"); 2448 | return_defer(-1); 2449 | } 2450 | 2451 | if (ds_string_builder_appendn(&sb, line, len) != 0) { 2452 | DS_LOG_ERROR("Failed to append line to string builder"); 2453 | return_defer(-1); 2454 | } 2455 | 2456 | memset(line, 0, sizeof(line)); 2457 | } 2458 | 2459 | if (ds_string_builder_build(&sb, buffer) != 0) { 2460 | DS_LOG_ERROR("Failed to build string from string builder"); 2461 | return_defer(-1); 2462 | } 2463 | 2464 | result = sb.items.count; 2465 | 2466 | defer: 2467 | if (filename != NULL && file != NULL) 2468 | fclose(file); 2469 | ds_string_builder_free(&sb); 2470 | return result; 2471 | } 2472 | 2473 | // Write a file 2474 | // 2475 | // Writes the contents of a buffer to a file. 2476 | // 2477 | // Arguments: 2478 | // - filename: name of the file to write 2479 | // - buffer: pointer to the buffer to write to the file 2480 | // - mode: mode to open the file in 2481 | // 2482 | // Returns: 2483 | // - 0 if the write was successful, -1 otherwise 2484 | DSHDEF int ds_io_write_file(const char *filename, const char *buffer, const char *mode) { 2485 | int result = 0; 2486 | FILE *file = NULL; 2487 | 2488 | if (filename != NULL) { 2489 | file = fopen(filename, mode); 2490 | if (file == NULL) { 2491 | DS_LOG_ERROR("Failed to open file: %s", filename); 2492 | return_defer(-1); 2493 | } 2494 | } else { 2495 | file = stdout; 2496 | } 2497 | 2498 | if (fputs(buffer, file) == EOF) { 2499 | DS_LOG_ERROR("Failed to write to file"); 2500 | return_defer(-1); 2501 | } 2502 | 2503 | result = 0; 2504 | 2505 | defer: 2506 | if (filename != NULL && file != NULL) 2507 | fclose(file); 2508 | return result; 2509 | } 2510 | 2511 | #endif // DS_IO_IMPLEMENTATION 2512 | -------------------------------------------------------------------------------- /example.txt: -------------------------------------------------------------------------------- 1 | n = input 2 | i = 0 3 | :label 4 | output i 5 | i = i + 1 6 | if i < n then goto :label 7 | if i < 10 then goto :label2 8 | output 69 9 | :label2 10 | -------------------------------------------------------------------------------- /linux.inc: -------------------------------------------------------------------------------- 1 | ;; Linux related constants and macros 2 | 3 | SYS_read equ 0 4 | SYS_open equ 2 5 | SYS_write equ 1 6 | SYS_exit equ 60 7 | SYS_socket equ 41 8 | SYS_accept equ 43 9 | SYS_bind equ 49 10 | SYS_listen equ 50 11 | SYS_close equ 3 12 | SYS_setsockopt equ 54 13 | SYS_fstat64 equ 5 14 | 15 | O_RDONLY = 0 16 | O_WRONLY = 1 17 | O_CREAT = 64 18 | O_TRUNC = 512 19 | 20 | AF_INET equ 2 21 | SOCK_STREAM equ 1 22 | INADDR_ANY equ 0 23 | 24 | SOL_SOCKET = 1 25 | SO_REUSEADDR = 2 26 | SO_REUSEPORT = 15 27 | 28 | SOL_TCP = 6 29 | TCP_NODELAY = 1 30 | 31 | STDOUT equ 1 32 | STDERR equ 2 33 | 34 | EXIT_SUCCESS equ 0 35 | EXIT_FAILURE equ 1 36 | 37 | ; struct stat64 { 38 | ; unsigned long long st_dev; 39 | ; unsigned char __pad0[4]; 40 | 41 | ; unsigned long __st_ino; 42 | 43 | ; unsigned int st_mode; 44 | ; unsigned int st_nlink; 45 | 46 | ; unsigned long st_uid; 47 | ; unsigned long st_gid; 48 | 49 | ; unsigned long long st_rdev; 50 | ; unsigned char __pad3[4]; 51 | 52 | ; long long st_size; 53 | ; unsigned long st_blksize; 54 | 55 | ; /* Number 512-byte blocks allocated. */ 56 | ; unsigned long long st_blocks; 57 | 58 | ; unsigned long st_atime; 59 | ; unsigned long st_atime_nsec; 60 | 61 | ; unsigned long st_mtime; 62 | ; unsigned int st_mtime_nsec; 63 | 64 | ; unsigned long st_ctime; 65 | ; unsigned long st_ctime_nsec; 66 | 67 | ; unsigned long long st_ino; 68 | ; }; 69 | 70 | sizeof_stat64 = 144 71 | stat64.st_size = 48 72 | 73 | macro funcall2 func, a, b 74 | { 75 | mov rdi, a 76 | mov rsi, b 77 | call func 78 | } 79 | 80 | macro funcall3 func, a, b, c 81 | { 82 | mov rdi, a 83 | mov rsi, b 84 | mov rdx, c 85 | call func 86 | } 87 | 88 | macro funcall4 func, a, b, c, d 89 | { 90 | mov rdi, a 91 | mov rsi, b 92 | mov rdx, c 93 | mov r10, d 94 | call func 95 | } 96 | 97 | macro syscall1 number, a 98 | { 99 | mov rax, number 100 | mov rdi, a 101 | syscall 102 | } 103 | 104 | macro syscall2 number, a, b 105 | { 106 | mov rax, number 107 | mov rdi, a 108 | mov rsi, b 109 | syscall 110 | } 111 | 112 | macro syscall3 number, a, b, c 113 | { 114 | mov rax, number 115 | mov rdi, a 116 | mov rsi, b 117 | mov rdx, c 118 | syscall 119 | } 120 | 121 | macro syscall5 number, a, b, c, d, e 122 | { 123 | mov rax, number 124 | mov rdi, a 125 | mov rsi, b 126 | mov rdx, c 127 | mov r10, d 128 | mov r8, e 129 | syscall 130 | } 131 | 132 | macro write fd, buf, count 133 | { 134 | syscall3 SYS_write, fd, buf, count 135 | } 136 | 137 | macro read fd, buf, count 138 | { 139 | syscall3 SYS_read, fd, buf, count 140 | } 141 | 142 | macro close fd 143 | { 144 | syscall1 SYS_close, fd 145 | } 146 | 147 | ;; int socket(int domain, int type, int protocol); 148 | macro socket domain, type, protocol 149 | { 150 | mov rax, SYS_socket 151 | mov rdi, domain 152 | mov rsi, type 153 | mov rdx, protocol 154 | syscall 155 | } 156 | 157 | ;; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 158 | macro bind sockfd, addr, addrlen 159 | { 160 | syscall3 SYS_bind, sockfd, addr, addrlen 161 | } 162 | 163 | ;; int listen(int sockfd, int backlog); 164 | macro listen sockfd, backlog 165 | { 166 | syscall2 SYS_listen, sockfd, backlog 167 | } 168 | 169 | ;; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 170 | macro accept sockfd, addr, addrlen 171 | { 172 | syscall3 SYS_accept, sockfd, addr, addrlen 173 | } 174 | 175 | macro exit code 176 | { 177 | mov rax, SYS_exit 178 | mov rdi, code 179 | syscall 180 | } 181 | 182 | macro open filename, flags, mode 183 | { 184 | syscall3 SYS_open, filename, flags, mode 185 | } 186 | 187 | struc servaddr_in 188 | { 189 | .sin_family dw 0 190 | .sin_port dw 0 191 | .sin_addr dd 0 192 | .sin_zero dq 0 193 | } 194 | 195 | ; int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); 196 | macro setsockopt sockfd, level, optname, optval, optlen 197 | { 198 | syscall5 SYS_setsockopt, sockfd, level, optname, optval, optlen 199 | } 200 | 201 | macro fstat64 fd, statbuf 202 | { 203 | syscall2 SYS_fstat64, fd, statbuf 204 | } 205 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define DS_IMPLEMENTATION 4 | #include "ds.h" 5 | #include 6 | 7 | enum token_kind { 8 | IDENT, 9 | LABEL, 10 | INT, 11 | INPUT, 12 | OUTPUT, 13 | GOTO, 14 | IF, 15 | THEN, 16 | EQUAL, 17 | PLUS, 18 | LESS_THAN, 19 | INVALID, 20 | END 21 | }; 22 | 23 | struct token { 24 | enum token_kind kind; 25 | char *value; 26 | }; 27 | 28 | const char *show_token_kind(enum token_kind kind) { 29 | switch (kind) { 30 | case IDENT: 31 | return "ident"; 32 | case LABEL: 33 | return "label"; 34 | case INT: 35 | return "int"; 36 | case INPUT: 37 | return "input"; 38 | case OUTPUT: 39 | return "output"; 40 | case GOTO: 41 | return "goto"; 42 | case IF: 43 | return "if"; 44 | case THEN: 45 | return "then"; 46 | case EQUAL: 47 | return "equal"; 48 | case PLUS: 49 | return "plus"; 50 | case LESS_THAN: 51 | return "less_than"; 52 | case INVALID: 53 | return "invalid"; 54 | case END: 55 | return "end"; 56 | } 57 | } 58 | 59 | void print_token(struct token tok) { 60 | const char *kind = show_token_kind(tok.kind); 61 | printf("%s", kind); 62 | if (tok.value != NULL) { 63 | printf("(%s)", tok.value); 64 | } 65 | printf("\n"); 66 | } 67 | 68 | struct lexer { 69 | char *buffer; 70 | unsigned int buffer_len; 71 | unsigned int pos; 72 | unsigned int read_pos; 73 | char ch; 74 | }; 75 | 76 | static char lexer_peek_char(struct lexer *l) { 77 | if (l->read_pos >= l->buffer_len) { 78 | return EOF; 79 | } 80 | 81 | return l->buffer[l->read_pos]; 82 | } 83 | 84 | static char lexer_read_char(struct lexer *l) { 85 | l->ch = lexer_peek_char(l); 86 | 87 | l->pos = l->read_pos; 88 | l->read_pos += 1; 89 | 90 | return l->ch; 91 | } 92 | 93 | static void skip_whitespaces(struct lexer *l) { 94 | while (isspace(l->ch)) { 95 | lexer_read_char(l); 96 | } 97 | } 98 | 99 | static void lexer_init(struct lexer *l, char *buffer, unsigned int buffer_len) { 100 | l->buffer = buffer; 101 | l->buffer_len = buffer_len; 102 | l->pos = 0; 103 | l->read_pos = 0; 104 | l->ch = 0; 105 | 106 | lexer_read_char(l); 107 | } 108 | 109 | static struct token lexer_next_token(struct lexer *l) { 110 | skip_whitespaces(l); 111 | 112 | if (l->ch == EOF) { 113 | lexer_read_char(l); 114 | return (struct token){.kind = END, .value = NULL}; 115 | } else if (l->ch == '=') { 116 | lexer_read_char(l); 117 | return (struct token){.kind = EQUAL, .value = NULL}; 118 | } else if (l->ch == '+') { 119 | lexer_read_char(l); 120 | return (struct token){.kind = PLUS, .value = NULL}; 121 | } else if (l->ch == '<') { 122 | lexer_read_char(l); 123 | return (struct token){.kind = LESS_THAN, .value = NULL}; 124 | } else if (l->ch == ':') { 125 | lexer_read_char(l); 126 | ds_string_slice slice = {.str = l->buffer + l->pos, .len = 0}; 127 | while (isalnum(l->ch) || l->ch == '_') { 128 | slice.len += 1; 129 | lexer_read_char(l); 130 | } 131 | char *value = NULL; 132 | ds_string_slice_to_owned(&slice, &value); 133 | return (struct token){.kind = LABEL, .value = value}; 134 | } else if (isdigit(l->ch)) { 135 | ds_string_slice slice = {.str = l->buffer + l->pos, .len = 0}; 136 | while (isdigit(l->ch)) { 137 | slice.len += 1; 138 | lexer_read_char(l); 139 | } 140 | char *value = NULL; 141 | ds_string_slice_to_owned(&slice, &value); 142 | return (struct token){.kind = INT, .value = value}; 143 | } else if (isalnum(l->ch) || l->ch == '_') { 144 | ds_string_slice slice = {.str = l->buffer + l->pos, .len = 0}; 145 | while (isalnum(l->ch) || l->ch == '_') { 146 | slice.len += 1; 147 | lexer_read_char(l); 148 | } 149 | char *value = NULL; 150 | ds_string_slice_to_owned(&slice, &value); 151 | if (strcmp(value, "input") == 0) { 152 | return (struct token){.kind = INPUT, .value = NULL}; 153 | } else if (strcmp(value, "output") == 0) { 154 | return (struct token){.kind = OUTPUT, .value = NULL}; 155 | } else if (strcmp(value, "goto") == 0) { 156 | return (struct token){.kind = GOTO, .value = NULL}; 157 | } else if (strcmp(value, "if") == 0) { 158 | return (struct token){.kind = IF, .value = NULL}; 159 | } else if (strcmp(value, "then") == 0) { 160 | return (struct token){.kind = THEN, .value = NULL}; 161 | } else { 162 | return (struct token){.kind = IDENT, .value = value}; 163 | } 164 | } else { 165 | ds_string_slice slice = {.str = l->buffer + l->pos, .len = 1}; 166 | char *value = NULL; 167 | ds_string_slice_to_owned(&slice, &value); 168 | lexer_read_char(l); 169 | return (struct token){.kind = INVALID, .value = value}; 170 | } 171 | } 172 | 173 | int lexer_tokenize(char *buffer, unsigned int length, 174 | ds_dynamic_array *tokens) { 175 | struct lexer lexer; 176 | lexer_init(&lexer, (char *)buffer, length); 177 | 178 | struct token tok; 179 | do { 180 | tok = lexer_next_token(&lexer); 181 | if (ds_dynamic_array_append(tokens, &tok) != 0) { 182 | DS_PANIC("Failed to append token to array"); 183 | } 184 | } while (tok.kind != END); 185 | 186 | return 0; 187 | } 188 | 189 | enum term_kind { TERM_INPUT, TERM_INT, TERM_IDENT }; 190 | 191 | struct term_node { 192 | enum term_kind kind; 193 | union { 194 | char *value; 195 | }; 196 | }; 197 | 198 | enum expr_kind { 199 | EXPR_TERM, 200 | EXPR_PLUS, 201 | }; 202 | 203 | struct term_binary_node { 204 | struct term_node lhs; 205 | struct term_node rhs; 206 | }; 207 | 208 | struct expr_node { 209 | enum expr_kind kind; 210 | union { 211 | struct term_node term; 212 | struct term_binary_node add; 213 | }; 214 | }; 215 | 216 | enum rel_kind { 217 | REL_LESS_THAN, 218 | }; 219 | 220 | struct rel_node { 221 | enum rel_kind kind; 222 | union { 223 | struct term_binary_node less_than; 224 | }; 225 | }; 226 | 227 | enum instr_kind { 228 | INSTR_ASSIGN, 229 | INSTR_IF, 230 | INSTR_GOTO, 231 | INSTR_OUTPUT, 232 | INSTR_LABEL 233 | }; 234 | 235 | struct assign_node { 236 | char *ident; 237 | struct expr_node expr; 238 | }; 239 | 240 | struct if_node { 241 | struct rel_node rel; 242 | struct instr_node *instr; 243 | }; 244 | 245 | struct goto_node { 246 | char *label; 247 | }; 248 | 249 | struct output_node { 250 | struct term_node term; 251 | }; 252 | 253 | struct label_node { 254 | char *label; 255 | }; 256 | 257 | struct instr_node { 258 | enum instr_kind kind; 259 | union { 260 | struct assign_node assign; 261 | struct if_node if_; 262 | struct goto_node goto_; 263 | struct output_node output; 264 | struct label_node label; 265 | }; 266 | }; 267 | 268 | struct program_node { 269 | ds_dynamic_array instrs; 270 | }; 271 | 272 | struct parser { 273 | ds_dynamic_array tokens; 274 | unsigned int index; 275 | }; 276 | 277 | void parser_init(ds_dynamic_array tokens, struct parser *p) { 278 | p->tokens = tokens; 279 | p->index = 0; 280 | } 281 | 282 | void parser_current(struct parser *p, struct token *token) { 283 | ds_dynamic_array_get(&p->tokens, p->index, token); 284 | } 285 | 286 | void parser_advance(struct parser *p) { p->index++; } 287 | 288 | void parse_term(struct parser *p, struct term_node *term) { 289 | struct token token; 290 | 291 | parser_current(p, &token); 292 | if (token.kind == INPUT) { 293 | term->kind = TERM_INPUT; 294 | } else if (token.kind == INT) { 295 | term->kind = TERM_INT; 296 | term->value = token.value; 297 | } else if (token.kind == IDENT) { 298 | term->kind = TERM_IDENT; 299 | term->value = token.value; 300 | } else { 301 | DS_PANIC("Expected a term (input, int or ident) but found %s", 302 | show_token_kind(token.kind)); 303 | } 304 | 305 | parser_advance(p); 306 | } 307 | 308 | void parse_expr(struct parser *p, struct expr_node *expr) { 309 | struct token token; 310 | struct term_node lhs, rhs; 311 | 312 | parse_term(p, &lhs); 313 | 314 | parser_current(p, &token); 315 | if (token.kind == PLUS) { 316 | parser_advance(p); 317 | parse_term(p, &rhs); 318 | 319 | expr->kind = EXPR_PLUS; 320 | expr->add.lhs = lhs; 321 | expr->add.rhs = rhs; 322 | } else { 323 | expr->kind = EXPR_TERM; 324 | expr->term = lhs; 325 | } 326 | } 327 | 328 | void parse_rel(struct parser *p, struct rel_node *rel) { 329 | struct token token; 330 | struct term_node lhs, rhs; 331 | 332 | parse_term(p, &lhs); 333 | 334 | parser_current(p, &token); 335 | if (token.kind == LESS_THAN) { 336 | parser_advance(p); 337 | parse_term(p, &rhs); 338 | 339 | rel->kind = REL_LESS_THAN; 340 | rel->less_than.lhs = lhs; 341 | rel->less_than.rhs = rhs; 342 | } else { 343 | DS_PANIC("Expected rel (<) found %s", show_token_kind(token.kind)); 344 | } 345 | } 346 | 347 | void parse_assign(struct parser *p, struct instr_node *instr) { 348 | struct token token; 349 | 350 | instr->kind = INSTR_ASSIGN; 351 | 352 | parser_current(p, &token); 353 | instr->assign.ident = token.value; 354 | parser_advance(p); 355 | 356 | parser_current(p, &token); 357 | if (token.kind != EQUAL) { 358 | DS_PANIC("Expected equal found %s", show_token_kind(token.kind)); 359 | } 360 | parser_advance(p); 361 | 362 | parse_expr(p, &instr->assign.expr); 363 | } 364 | 365 | void parse_instr(struct parser *p, struct instr_node *instr); 366 | 367 | void parse_if(struct parser *p, struct instr_node *instr) { 368 | struct token token; 369 | 370 | instr->kind = INSTR_IF; 371 | parser_advance(p); 372 | 373 | parse_rel(p, &instr->if_.rel); 374 | 375 | parser_current(p, &token); 376 | if (token.kind != THEN) { 377 | DS_PANIC("Expected then found %s", show_token_kind(token.kind)); 378 | } 379 | parser_advance(p); 380 | 381 | instr->if_.instr = malloc(sizeof(struct instr_node)); 382 | parse_instr(p, instr->if_.instr); 383 | } 384 | 385 | void parse_goto(struct parser *p, struct instr_node *instr) { 386 | struct token token; 387 | 388 | instr->kind = INSTR_GOTO; 389 | parser_advance(p); 390 | 391 | parser_current(p, &token); 392 | if (token.kind != LABEL) { 393 | DS_PANIC("Expected label found %s", show_token_kind(token.kind)); 394 | } 395 | parser_advance(p); 396 | 397 | instr->goto_.label = token.value; 398 | } 399 | 400 | void parse_output(struct parser *p, struct instr_node *instr) { 401 | struct token token; 402 | struct term_node lhs; 403 | 404 | instr->kind = INSTR_OUTPUT; 405 | parser_advance(p); 406 | 407 | parse_term(p, &lhs); 408 | 409 | instr->output.term = lhs; 410 | } 411 | 412 | void parse_label(struct parser *p, struct instr_node *instr) { 413 | struct token token; 414 | 415 | instr->kind = INSTR_LABEL; 416 | 417 | parser_current(p, &token); 418 | instr->label.label = token.value; 419 | 420 | parser_advance(p); 421 | } 422 | 423 | void parse_instr(struct parser *p, struct instr_node *instr) { 424 | struct token token; 425 | 426 | parser_current(p, &token); 427 | if (token.kind == IDENT) { 428 | parse_assign(p, instr); 429 | } else if (token.kind == IF) { 430 | parse_if(p, instr); 431 | } else if (token.kind == GOTO) { 432 | parse_goto(p, instr); 433 | } else if (token.kind == OUTPUT) { 434 | parse_output(p, instr); 435 | } else if (token.kind == LABEL) { 436 | parse_label(p, instr); 437 | } else { 438 | DS_PANIC("unexpected token %s", show_token_kind(token.kind)); 439 | } 440 | } 441 | 442 | int find_variable(ds_dynamic_array *variables, char *ident) { 443 | for (unsigned int i = 0; i < variables->count; i++) { 444 | char *variable = NULL; 445 | ds_dynamic_array_get(variables, i, &variable); 446 | 447 | if (strcmp(ident, variable) == 0) { 448 | return i; 449 | } 450 | } 451 | 452 | return -1; 453 | } 454 | 455 | void parse_program(struct parser *p, struct program_node *program) { 456 | ds_dynamic_array_init(&program->instrs, sizeof(struct instr_node)); 457 | 458 | struct token token; 459 | do { 460 | struct instr_node instr; 461 | 462 | parse_instr(p, &instr); 463 | 464 | ds_dynamic_array_append(&program->instrs, &instr); 465 | 466 | parser_current(p, &token); 467 | } while (token.kind != END); 468 | } 469 | 470 | void term_asm(struct term_node *term, ds_dynamic_array *variables) { 471 | switch (term->kind) { 472 | case TERM_INPUT: { 473 | printf(" read 0, line, LINE_MAX\n"); 474 | printf(" mov rdi, line\n"); 475 | printf(" call strlen\n"); 476 | printf(" mov rdi, line\n"); 477 | printf(" mov rsi, rax\n"); 478 | printf(" call parse_uint\n"); 479 | break; 480 | } 481 | case TERM_INT: 482 | printf(" mov rax, %s\n", term->value); 483 | break; 484 | case TERM_IDENT: { 485 | int index = find_variable(variables, term->value); 486 | printf(" mov rax, qword [rbp - %d]\n", index * 8 + 8); 487 | break; 488 | } 489 | } 490 | } 491 | 492 | void expr_asm(struct expr_node *expr, ds_dynamic_array *variables) { 493 | switch (expr->kind) { 494 | case EXPR_TERM: { 495 | term_asm(&expr->term, variables); 496 | break; 497 | } 498 | case EXPR_PLUS: 499 | term_asm(&expr->add.lhs, variables); 500 | printf(" mov rdx, rax\n"); 501 | term_asm(&expr->add.rhs, variables); 502 | printf(" add rax, rdx\n"); 503 | break; 504 | } 505 | } 506 | 507 | void rel_asm(struct rel_node *rel, ds_dynamic_array *variables) { 508 | switch (rel->kind) { 509 | case REL_LESS_THAN: 510 | term_asm(&rel->less_than.lhs, variables); 511 | printf(" mov rdx, rax\n"); 512 | term_asm(&rel->less_than.rhs, variables); 513 | printf(" cmp rdx, rax\n"); 514 | printf(" setl al\n"); 515 | printf(" and al, 1\n"); 516 | printf(" movzx rax, al\n"); 517 | break; 518 | } 519 | } 520 | 521 | void instr_asm(struct instr_node *instr, ds_dynamic_array *variables, 522 | int *if_count) { 523 | switch (instr->kind) { 524 | case INSTR_ASSIGN: { 525 | expr_asm(&instr->assign.expr, variables); // the result is in rax 526 | int index = find_variable(variables, instr->assign.ident); 527 | printf(" mov qword [rbp - %d], rax\n", index * 8 + 8); 528 | break; 529 | } 530 | case INSTR_IF: { 531 | rel_asm(&instr->if_.rel, variables); // the result is in rax 532 | int label = (*if_count)++; 533 | printf(" test rax, rax\n"); 534 | printf(" jz .endif%d\n", label); 535 | instr_asm(instr->if_.instr, variables, if_count); 536 | printf(".endif%d:\n", label); 537 | break; 538 | } 539 | case INSTR_GOTO: { 540 | printf(" jmp .%s\n", instr->goto_.label); 541 | break; 542 | } 543 | case INSTR_OUTPUT: { 544 | term_asm(&instr->output.term, variables); 545 | printf(" mov rdi, 1\n"); 546 | printf(" mov rsi, rax\n"); 547 | printf(" call write_uint\n"); 548 | break; 549 | } 550 | case INSTR_LABEL: 551 | printf(".%s:\n", instr->label.label); 552 | break; 553 | } 554 | } 555 | 556 | void term_declare_variables(struct term_node *term, 557 | ds_dynamic_array *variables) { 558 | switch (term->kind) { 559 | case TERM_INPUT: 560 | break; 561 | case TERM_INT: 562 | break; 563 | case TERM_IDENT: 564 | for (unsigned int i = 0; i < variables->count; i++) { 565 | char *variable = NULL; 566 | ds_dynamic_array_get(variables, i, &variable); 567 | 568 | if (strcmp(term->value, variable) == 0) { 569 | return; 570 | } 571 | } 572 | DS_PANIC("Identifier is not defined %s", term->value); 573 | break; 574 | } 575 | } 576 | 577 | void expr_declare_variables(struct expr_node *expr, 578 | ds_dynamic_array *variables) { 579 | switch (expr->kind) { 580 | case EXPR_TERM: { 581 | term_declare_variables(&expr->term, variables); 582 | break; 583 | } 584 | case EXPR_PLUS: 585 | term_declare_variables(&expr->add.lhs, variables); 586 | term_declare_variables(&expr->add.rhs, variables); 587 | break; 588 | } 589 | } 590 | 591 | void rel_declare_variables(struct rel_node *rel, ds_dynamic_array *variables) { 592 | switch (rel->kind) { 593 | case REL_LESS_THAN: 594 | term_declare_variables(&rel->less_than.lhs, variables); 595 | term_declare_variables(&rel->less_than.rhs, variables); 596 | break; 597 | } 598 | } 599 | 600 | void instr_declare_variables(struct instr_node *instr, 601 | ds_dynamic_array *variables) { 602 | switch (instr->kind) { 603 | case INSTR_ASSIGN: { 604 | expr_declare_variables(&instr->assign.expr, variables); 605 | for (unsigned int i = 0; i < variables->count; i++) { 606 | char *variable = NULL; 607 | ds_dynamic_array_get(variables, i, &variable); 608 | 609 | if (strcmp(instr->assign.ident, variable) == 0) { 610 | return; 611 | } 612 | } 613 | ds_dynamic_array_append(variables, &instr->assign.ident); 614 | break; 615 | } 616 | case INSTR_IF: { 617 | rel_declare_variables(&instr->if_.rel, variables); 618 | instr_declare_variables(instr->if_.instr, variables); 619 | break; 620 | } 621 | case INSTR_GOTO: { 622 | break; 623 | } 624 | case INSTR_OUTPUT: { 625 | term_declare_variables(&instr->output.term, variables); 626 | break; 627 | } 628 | case INSTR_LABEL: 629 | break; 630 | } 631 | } 632 | 633 | void program_asm(struct program_node *program) { 634 | int if_count = 0; 635 | ds_dynamic_array variables; 636 | ds_dynamic_array_init(&variables, sizeof(char *)); 637 | 638 | for (unsigned int i = 0; i < program->instrs.count; i++) { 639 | struct instr_node instr; 640 | ds_dynamic_array_get(&program->instrs, i, &instr); 641 | 642 | instr_declare_variables(&instr, &variables); 643 | } 644 | 645 | printf("format ELF64 executable\n"); 646 | printf("LINE_MAX equ 1024\n"); 647 | printf("segment readable executable\n"); 648 | printf("include \"linux.inc\"\n"); 649 | printf("include \"utils.inc\"\n"); 650 | printf("entry _start\n"); 651 | printf("_start:\n"); 652 | 653 | printf(" mov rbp, rsp\n"); 654 | printf(" sub rsp, %d\n", variables.count * 8); 655 | 656 | for (unsigned int i = 0; i < program->instrs.count; i++) { 657 | struct instr_node instr; 658 | ds_dynamic_array_get(&program->instrs, i, &instr); 659 | 660 | instr_asm(&instr, &variables, &if_count); 661 | } 662 | 663 | printf(" add rsp, %d\n", variables.count * 8); 664 | 665 | printf(" mov rax, 60\n"); 666 | printf(" xor rdi, rdi\n"); 667 | printf(" syscall\n"); 668 | 669 | printf("segment readable writeable\n"); 670 | printf("line rb LINE_MAX\n"); 671 | } 672 | 673 | int main() { 674 | char *buffer = NULL; 675 | int length = ds_io_read_file(NULL, &buffer); 676 | 677 | ds_dynamic_array tokens; 678 | ds_dynamic_array_init(&tokens, sizeof(struct token)); 679 | 680 | lexer_tokenize(buffer, length, &tokens); 681 | 682 | struct parser p; 683 | struct program_node program; 684 | 685 | parser_init(tokens, &p); 686 | parse_program(&p, &program); 687 | 688 | program_asm(&program); 689 | } 690 | -------------------------------------------------------------------------------- /utils.inc: -------------------------------------------------------------------------------- 1 | ;; Generally useful utility functions 2 | 3 | ;; Write an integer to a file 4 | ;; rdi - int fd 5 | ;; rsi - uint64_t int x 6 | write_uint: 7 | test rsi, rsi 8 | jz .base_zero 9 | 10 | mov rcx, 10 ;; 10 literal for division 11 | mov rax, rsi ;; keeping track of rsi in rax cause it's easier to div it like that 12 | mov r10, 0 ;; counter of how many digits we already converted 13 | .next_digit: 14 | test rax, rax 15 | jz .done 16 | mov rdx, 0 17 | div rcx 18 | add rdx, '0' 19 | dec rsp 20 | mov byte [rsp], dl 21 | inc r10 22 | jmp .next_digit 23 | .done: 24 | write rdi, rsp, r10 25 | add rsp, r10 26 | ret 27 | .base_zero: 28 | dec rsp 29 | mov byte [rsp], '0' 30 | write rdi, rsp, 1 31 | inc rsp 32 | ret 33 | 34 | ;; Write a NULL-terminated string to a file 35 | ;; rdi - int fd 36 | ;; rsi - const char *s 37 | write_cstr: 38 | push rsi 39 | push rdi 40 | mov rdi, rsi 41 | call strlen 42 | 43 | mov rdx, rax 44 | mov rax, SYS_write 45 | pop rdi 46 | pop rsi 47 | syscall 48 | ret 49 | 50 | ;; Compute the length of a NULL-terminated string 51 | ;; rdi - const char *s 52 | strlen: 53 | push rdi 54 | xor rax, rax 55 | .next_char: 56 | mov al, byte [rdi] 57 | cmp rax, 0 58 | je .done 59 | 60 | inc rdi 61 | jmp .next_char 62 | .done: 63 | pop rsi 64 | sub rdi, rsi 65 | mov rax, rdi 66 | ret 67 | 68 | ;; Parse unsigned integer from a sized string 69 | ;; rdi - void *buf 70 | ;; rsi - size_t n 71 | parse_uint: 72 | xor rax, rax 73 | xor rbx, rbx 74 | mov rcx, 10 75 | .next_digit: 76 | cmp rsi, 0 77 | jle .done 78 | 79 | mov bl, byte [rdi] 80 | cmp rbx, '0' 81 | jl .done 82 | cmp rbx, '9' 83 | jg .done 84 | sub rbx, '0' 85 | 86 | mul rcx 87 | add rax, rbx 88 | 89 | inc rdi 90 | dec rsi 91 | jmp .next_digit 92 | .done: 93 | ret 94 | 95 | ;; Copy a chunk of memory 96 | ;; rdi - void *dst 97 | ;; rsi - void *src 98 | ;; rdx - size_t n 99 | memcpy: 100 | .next_byte: 101 | cmp rdx, 0 102 | jle .done 103 | 104 | mov al, byte [rsi] 105 | mov byte [rdi], al 106 | 107 | inc rdi 108 | inc rsi 109 | dec rdx 110 | 111 | jmp .next_byte 112 | .done: 113 | ret 114 | 115 | 116 | ;; Find a character with a sized string 117 | ;; rdi - void *buf 118 | ;; rsi - size_t n 119 | ;; rdx - char c 120 | find_char: 121 | cmp rsi, 0 122 | jle .not_found 123 | 124 | mov al, byte [rdi] 125 | cmp dl, al 126 | je .found 127 | 128 | inc rdi 129 | dec rsi 130 | jmp find_char 131 | 132 | .not_found: 133 | xor rax, rax 134 | ret 135 | .found: 136 | mov rax, rdi 137 | ret 138 | 139 | ;; Check if text starts with the prefix (both strings are sized) 140 | ;; rdi - void *text 141 | ;; rsi - size_t text_len 142 | ;; rdx - void *prefix 143 | ;; r10 - size_t prefix_len 144 | starts_with: 145 | xor rax, rax 146 | xor rbx, rbx 147 | .next_char: 148 | cmp rsi, 0 149 | jle .done 150 | cmp r10,0 151 | jle .done 152 | 153 | mov al, byte [rdi] 154 | mov bl, byte [rdx] 155 | cmp rax, rbx 156 | jne .done 157 | 158 | dec rsi 159 | inc rdi 160 | dec r10 161 | inc rdx 162 | jmp .next_char 163 | 164 | .done: 165 | cmp r10, 0 166 | je .yes 167 | .no: 168 | mov rax, 0 169 | ret 170 | .yes: 171 | mov rax, 1 172 | ret 173 | --------------------------------------------------------------------------------