├── .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 |
--------------------------------------------------------------------------------