diff options
author | Bad Diode <bd@badd10de.dev> | 2024-06-14 17:55:51 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-06-14 17:55:51 +0200 |
commit | 3aae2f36047e9adc6a59b886492254eb4370777d (patch) | |
tree | 7d8f8fc069393dd38e9688d8d7cf77117ba5235f /src | |
parent | deaf192df939c9ec9a62ef4febaf76ade0dcbb01 (diff) | |
download | bdl-3aae2f36047e9adc6a59b886492254eb4370777d.tar.gz bdl-3aae2f36047e9adc6a59b886492254eb4370777d.zip |
Starting fresh with the `badlang`
Diffstat (limited to 'src')
-rw-r--r-- | src/badlib.h | 876 | ||||
-rw-r--r-- | src/main.c | 138 |
2 files changed, 908 insertions, 106 deletions
diff --git a/src/badlib.h b/src/badlib.h new file mode 100644 index 0000000..f7a7974 --- /dev/null +++ b/src/badlib.h | |||
@@ -0,0 +1,876 @@ | |||
1 | #ifndef BADLIB_H | ||
2 | #define BADLIB_H | ||
3 | |||
4 | // TODO: | ||
5 | // - Add string operations. | ||
6 | // - sub | ||
7 | // - Add math operations for vectors and matrices. | ||
8 | // - Can we remove the PlatformAPI function pointers and just use extern | ||
9 | // references? | ||
10 | // - Breakdown this file in the different library parts. | ||
11 | // - Sort arrays / linked lists with custom functions? | ||
12 | // - Implement binary search for searching into an array: | ||
13 | // SearchResult find_array(Array haystack, Array needle). | ||
14 | // | ||
15 | |||
16 | #include <stdbool.h> | ||
17 | #include <stddef.h> | ||
18 | #include <stdint.h> | ||
19 | #include <stdio.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <strings.h> | ||
22 | #define printstr(s) \ | ||
23 | if (s) printf("%.*s", (int)(s)->size, (s)->mem); | ||
24 | #define printstrln(s) \ | ||
25 | if (s) printf("%.*s\n", (int)(s)->size, (s)->mem); | ||
26 | |||
27 | // | ||
28 | // This simple header just typedefs the basic C define types to a shorter name, | ||
29 | // loads the quality of life bool macro for _Bool and defines shorthand macros | ||
30 | // for byte sizes. We need that the targeted architecture uses the floating | ||
31 | // point representation as described on the IEEE-754 standard. | ||
32 | // | ||
33 | |||
34 | _Static_assert(sizeof(double) == 8, "no support for IEEE-754"); | ||
35 | _Static_assert(sizeof(float) == 4, "no support for IEEE-754"); | ||
36 | |||
37 | typedef uint8_t u8; | ||
38 | typedef uint16_t u16; | ||
39 | typedef uint32_t u32; | ||
40 | typedef uint64_t u64; | ||
41 | typedef int8_t s8; | ||
42 | typedef int16_t s16; | ||
43 | typedef int32_t s32; | ||
44 | typedef int64_t s64; | ||
45 | typedef float f32; | ||
46 | typedef double f64; | ||
47 | typedef ptrdiff_t sz; | ||
48 | typedef uintptr_t ptrsize; | ||
49 | typedef size_t usize; | ||
50 | |||
51 | #define KB(N) ((sz)(N)*1024) | ||
52 | #define MB(N) ((sz)KB(N) * 1024) | ||
53 | #define GB(N) ((sz)MB(N) * 1024) | ||
54 | #define TB(N) ((sz)GB(N) * 1024) | ||
55 | |||
56 | // Custom assert macro, better for debugging. | ||
57 | #ifdef DEBUG | ||
58 | #define assert(c) \ | ||
59 | while (!(c)) \ | ||
60 | __builtin_unreachable() | ||
61 | #else | ||
62 | #define assert(c) ; | ||
63 | #endif | ||
64 | |||
65 | // Abort macro without stdlib. | ||
66 | #define abort() __builtin_trap() | ||
67 | |||
68 | // Utility macros for numeric comparisons. | ||
69 | #define MIN(A, B) ((A) <= (B) ? (A) : (B)) | ||
70 | #define MAX(A, B) ((A) >= (B) ? (A) : (B)) | ||
71 | #define ABS(A) \ | ||
72 | (((A) ^ ((A) >> (sizeof(A) * 8 - 1))) - ((A) >> (sizeof(A) * 8 - 1))) | ||
73 | #define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX) : (X)) | ||
74 | #define LEN(a) (sz)(sizeof(a) / sizeof(*(a))) | ||
75 | |||
76 | // Handy defer bacro for scoped operations. | ||
77 | #define MACRO_VAR_CONCAT_HELPER(X, Y) X##Y | ||
78 | #define MACRO_VAR_CONCAT(X, Y) MACRO_VAR_CONCAT_HELPER(X, Y) | ||
79 | #define MACRO_VAR(name) MACRO_VAR_CONCAT(name, __LINE__) | ||
80 | #define defer(begin, end) \ | ||
81 | for (sz MACRO_VAR(_i_) = (begin, 0); !MACRO_VAR(_i_); \ | ||
82 | (MACRO_VAR(_i_) += 1), end) | ||
83 | |||
84 | // | ||
85 | // Allocators and Arenas. | ||
86 | // | ||
87 | |||
88 | typedef struct Allocator { | ||
89 | void *(*malloc)(sz, void *ctx); | ||
90 | void *(*calloc)(sz, void *ctx); | ||
91 | void (*free)(void *, void *ctx); | ||
92 | void *(*realloc)(void *ptr, sz old_size, sz new_size, void *ctx); | ||
93 | void *ctx; | ||
94 | } Allocator; | ||
95 | |||
96 | typedef struct Arena { | ||
97 | u8 *beg; | ||
98 | sz size; | ||
99 | sz cap; | ||
100 | } Arena; | ||
101 | |||
102 | #define ARENA_ALIGNMENT 8 | ||
103 | |||
104 | void * | ||
105 | arena_malloc(sz size, void *ctx) { | ||
106 | assert(size > 0); | ||
107 | Arena *a = (Arena *)ctx; | ||
108 | sz padding = -size & (ARENA_ALIGNMENT - 1); | ||
109 | sz available = a->cap - a->size; | ||
110 | if (available < 0 || available < size) { | ||
111 | abort(); | ||
112 | } | ||
113 | void *p = a->beg + a->size; | ||
114 | a->size += padding + size; | ||
115 | return p; | ||
116 | } | ||
117 | |||
118 | void * | ||
119 | arena_calloc(sz size, void *ctx) { | ||
120 | void *mem = arena_malloc(size, ctx); | ||
121 | return memset(mem, 0, size); | ||
122 | } | ||
123 | |||
124 | void | ||
125 | arena_free(void *ptr, sz size, void *ctx) { | ||
126 | // Undo the latest allocation if possible. | ||
127 | Arena *a = (Arena *)ctx; | ||
128 | sz padding = -size & (ARENA_ALIGNMENT - 1); | ||
129 | size += padding; | ||
130 | if (ptr == a->beg + a->size - size) { | ||
131 | a->size -= size; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | void * | ||
136 | arena_realloc(void *ptr, sz old_size, sz new_size, void *ctx) { | ||
137 | // This function can avoid copying memory around if we could just extend the | ||
138 | // latest allocation, otherwise a new malloc will be performed (keeping the | ||
139 | // previous data alive!). | ||
140 | Arena *a = (Arena *)ctx; | ||
141 | sz old_padding = -old_size & (ARENA_ALIGNMENT - 1); | ||
142 | old_size += old_padding; | ||
143 | if (ptr == a->beg + a->size - old_size) { | ||
144 | sz new_padding = -new_size & (ARENA_ALIGNMENT - 1); | ||
145 | new_size += new_padding; | ||
146 | a->size += new_size - old_size; | ||
147 | return ptr; | ||
148 | } | ||
149 | u8 *p = (u8 *)arena_malloc(new_size, ctx); | ||
150 | memcpy(p, ptr, old_size); | ||
151 | return p; | ||
152 | } | ||
153 | |||
154 | Arena | ||
155 | arena_create(sz cap, Allocator allocator) { | ||
156 | Arena arena = {0}; | ||
157 | arena.beg = (u8 *)allocator.malloc(cap, allocator.ctx); | ||
158 | arena.cap = arena.beg ? cap : 0; | ||
159 | return arena; | ||
160 | } | ||
161 | |||
162 | void | ||
163 | arena_destroy(Arena *arena, Allocator allocator) { | ||
164 | allocator.free(arena->beg, allocator.ctx); | ||
165 | } | ||
166 | |||
167 | void | ||
168 | arena_reset(Arena *a) { | ||
169 | a->size = 0; | ||
170 | } | ||
171 | |||
172 | // | ||
173 | // Arrays, Buffers and Strings. | ||
174 | // | ||
175 | |||
176 | // A fixed buffer or buffer view, represented as a fat pointer. | ||
177 | typedef struct Array { | ||
178 | u8 *mem; | ||
179 | sz size; | ||
180 | } Array; | ||
181 | |||
182 | typedef struct SearchResult { | ||
183 | // Position on the buffer where a result was found. | ||
184 | sz pos; | ||
185 | // Size of the matched query. | ||
186 | sz matched; | ||
187 | // Wether the search was successful or not. | ||
188 | bool found; | ||
189 | } SearchResult; | ||
190 | |||
191 | bool | ||
192 | array_eq(Array a, Array b) { | ||
193 | return a.size == b.size && !memcmp(a.mem, b.mem, a.size); | ||
194 | } | ||
195 | |||
196 | SearchResult | ||
197 | array_find_next(Array haystack, Array needle) { | ||
198 | sz pos = 0; | ||
199 | while (haystack.size >= needle.size) { | ||
200 | if (*haystack.mem == *needle.mem) { | ||
201 | Array candidate = (Array){ | ||
202 | .mem = haystack.mem, | ||
203 | .size = needle.size, | ||
204 | }; | ||
205 | if (array_eq(candidate, needle)) { | ||
206 | return (SearchResult){ | ||
207 | .pos = pos, | ||
208 | .matched = needle.size, | ||
209 | .found = true, | ||
210 | }; | ||
211 | } | ||
212 | } | ||
213 | haystack.mem++; | ||
214 | haystack.size--; | ||
215 | pos++; | ||
216 | } | ||
217 | return (SearchResult){.found = false}; | ||
218 | } | ||
219 | |||
220 | // A growable arena backed buffer. | ||
221 | typedef struct Buf { | ||
222 | u8 *mem; | ||
223 | sz size; | ||
224 | sz cap; | ||
225 | } Buf; | ||
226 | |||
227 | // Reserve a given amount of bytes for a growable buffer. | ||
228 | void | ||
229 | buf_reserve(Buf *buf, sz size, Arena *a) { | ||
230 | assert(buf); | ||
231 | buf->mem = arena_realloc(buf->mem, buf->size, size, a); | ||
232 | buf->size = 0; | ||
233 | buf->cap = size; | ||
234 | } | ||
235 | |||
236 | // Zero initialize a growable buffer. | ||
237 | void | ||
238 | buf_zero(Buf *buf, sz size, Arena *a) { | ||
239 | buf_reserve(buf, size, a); | ||
240 | memset(buf->mem, 0, buf->cap); | ||
241 | } | ||
242 | |||
243 | // Insert a number of bytes into a growable buffer. | ||
244 | void | ||
245 | buf_insert(Buf *buf, void *value, sz size, Arena *a) { | ||
246 | assert(buf); | ||
247 | if (size == 0) { | ||
248 | return; | ||
249 | } | ||
250 | if (!buf->mem) { | ||
251 | buf->mem = arena_malloc(size, a); | ||
252 | buf->size = size; | ||
253 | buf->cap = size; | ||
254 | memcpy(buf->mem, value, size); | ||
255 | return; | ||
256 | } | ||
257 | while (buf->cap < buf->size + size) { | ||
258 | arena_realloc(buf->mem, buf->cap, buf->cap * 2, a); | ||
259 | buf->cap *= 2; | ||
260 | } | ||
261 | memcpy(buf->mem + buf->size, value, size); | ||
262 | buf->size += size; | ||
263 | } | ||
264 | |||
265 | // Gets a reference to a given position of the buffer. It doesn't copy any | ||
266 | // information so bear in mind it could become invalidated if the buffer is | ||
267 | // modified. | ||
268 | void * | ||
269 | buf_ref(Buf *buf, sz pos, sz size) { | ||
270 | return buf->mem + pos * size; | ||
271 | } | ||
272 | |||
273 | // Copies a number of bytes from an indexed position from growable buffer into | ||
274 | // the given destination. | ||
275 | void | ||
276 | buf_get(Buf *buf, void *dst, sz pos, sz size) { | ||
277 | memcpy(dst, buf->mem + pos * size, size); | ||
278 | } | ||
279 | |||
280 | // Pops a number of bytes from a growable buffer into the given destination, | ||
281 | // copying the result and reducing the size of the buffer. | ||
282 | void | ||
283 | buf_pop(Buf *buf, void *dst, sz size) { | ||
284 | assert(buf->mem + buf->size - size >= buf->mem); | ||
285 | memcpy(dst, buf->mem + buf->size - size, size); | ||
286 | buf->size -= size; | ||
287 | } | ||
288 | |||
289 | // A string or string view. | ||
290 | typedef Array Str; | ||
291 | |||
292 | // Create a string view from a C literal with these. | ||
293 | #define cstr(s) \ | ||
294 | (Str) { \ | ||
295 | (u8 *)s, LEN(s) - 1 \ | ||
296 | } | ||
297 | |||
298 | bool | ||
299 | str_eq(Str a, Str b) { | ||
300 | return array_eq(a, b); | ||
301 | } | ||
302 | |||
303 | Str | ||
304 | str_split(Str *a, Str split) { | ||
305 | assert(a != NULL); | ||
306 | Str ret = *a; | ||
307 | SearchResult res = array_find_next(*a, split); | ||
308 | if (res.found) { | ||
309 | ret.size = res.pos; | ||
310 | a->mem += res.pos + res.matched; | ||
311 | a->size -= res.pos + res.matched; | ||
312 | return ret; | ||
313 | } | ||
314 | *a = (Str){0}; | ||
315 | return ret; | ||
316 | } | ||
317 | |||
318 | // A customizable splitting function. | ||
319 | typedef sz(StrSplitFn)(Str s); | ||
320 | |||
321 | Str | ||
322 | str_split_fn(Str *a, StrSplitFn split_fn) { | ||
323 | assert(a != NULL); | ||
324 | Str ret = *a; | ||
325 | while (a->size > 0) { | ||
326 | sz advance = split_fn(*a); | ||
327 | if (advance) { | ||
328 | ret.size -= a->size; | ||
329 | a->size -= advance; | ||
330 | a->mem += advance; | ||
331 | return ret; | ||
332 | } | ||
333 | a->size--; | ||
334 | a->mem++; | ||
335 | } | ||
336 | return ret; | ||
337 | } | ||
338 | |||
339 | // Replaces a single pattern on a string. `from` and `to` have to be of the same | ||
340 | // size. | ||
341 | void | ||
342 | str_replace(Str *a, Str from, Str to) { | ||
343 | assert(a != NULL); | ||
344 | assert(from.size == to.size); | ||
345 | SearchResult res = array_find_next(*a, from); | ||
346 | if (res.found) { | ||
347 | memcpy(a->mem + res.pos, to.mem, res.matched); | ||
348 | return; | ||
349 | } | ||
350 | } | ||
351 | |||
352 | // Same as `str_replace` but applied to all occurences of `from`. | ||
353 | void | ||
354 | str_replace_all(Str *a, Str from, Str to) { | ||
355 | assert(a != NULL); | ||
356 | assert(from.size == to.size); | ||
357 | Str data = *a; | ||
358 | SearchResult res = array_find_next(data, from); | ||
359 | while (res.found) { | ||
360 | memcpy(data.mem + res.pos, to.mem, res.matched); | ||
361 | data.mem += res.pos; | ||
362 | data.size -= res.pos; | ||
363 | res = array_find_next(data, from); | ||
364 | } | ||
365 | } | ||
366 | |||
367 | bool | ||
368 | str_has_prefix(Str str, Str prefix) { | ||
369 | if (str.size < prefix.size) { | ||
370 | return false; | ||
371 | } | ||
372 | Str candidate = (Str){ | ||
373 | .mem = str.mem, | ||
374 | .size = prefix.size, | ||
375 | }; | ||
376 | return str_eq(candidate, prefix); | ||
377 | } | ||
378 | |||
379 | bool | ||
380 | str_has_suffix(Str str, Str suffix) { | ||
381 | if (str.size < suffix.size) { | ||
382 | return false; | ||
383 | } | ||
384 | Str candidate = (Str){ | ||
385 | .mem = str.mem + str.size - suffix.size, | ||
386 | .size = suffix.size, | ||
387 | }; | ||
388 | return str_eq(candidate, suffix); | ||
389 | } | ||
390 | |||
391 | Str | ||
392 | str_remove_prefix(Str str, Str prefix) { | ||
393 | if (!str_has_prefix(str, prefix)) { | ||
394 | return str; | ||
395 | } | ||
396 | str.mem += prefix.size; | ||
397 | str.size -= prefix.size; | ||
398 | return str; | ||
399 | } | ||
400 | |||
401 | Str | ||
402 | str_remove_suffix(Str str, Str suffix) { | ||
403 | if (!str_has_suffix(str, suffix)) { | ||
404 | return str; | ||
405 | } | ||
406 | str.size -= suffix.size; | ||
407 | return str; | ||
408 | } | ||
409 | |||
410 | // Concat. | ||
411 | Str | ||
412 | str_concat(Str x, Str y, Arena *a) { | ||
413 | Buf buf = {0}; | ||
414 | buf_insert(&buf, x.mem, x.size, a); | ||
415 | buf_insert(&buf, y.mem, y.size, a); | ||
416 | return (Str){ | ||
417 | .mem = buf.mem, | ||
418 | .size = buf.size, | ||
419 | }; | ||
420 | } | ||
421 | |||
422 | Str | ||
423 | str_insert(Str orig, Str value, sz position, Arena *a) { | ||
424 | Buf buf = {0}; | ||
425 | buf_insert(&buf, orig.mem, position, a); | ||
426 | buf_insert(&buf, value.mem, value.size, a); | ||
427 | buf_insert(&buf, orig.mem + position, orig.size - position, a); | ||
428 | return (Str){ | ||
429 | .mem = buf.mem, | ||
430 | .size = buf.size, | ||
431 | }; | ||
432 | } | ||
433 | |||
434 | // Str | ||
435 | // str_delete(Str orig, Str value, Arena *a) { | ||
436 | // Buf buf = {0}; | ||
437 | // // TODO: find first location | ||
438 | // // TODO: insert orig until that location | ||
439 | // // TODO: insert orig after location + value.size | ||
440 | // // buf_insert(&buf, orig.mem, position, a); | ||
441 | // // buf_insert(&buf, value.mem, value.size, a); | ||
442 | // // buf_insert(&buf, orig.mem + position, orig.size - position, a); | ||
443 | // return (Str){ | ||
444 | // .mem = buf.mem, | ||
445 | // .size = buf.size, | ||
446 | // }; | ||
447 | // } | ||
448 | |||
449 | // | ||
450 | // Queue. | ||
451 | // | ||
452 | |||
453 | typedef struct QueueVal { | ||
454 | struct QueueVal *next; | ||
455 | void *val; | ||
456 | } QueueVal; | ||
457 | |||
458 | typedef struct Queue { | ||
459 | QueueVal *head; | ||
460 | QueueVal *tail; | ||
461 | sz size; | ||
462 | } Queue; | ||
463 | |||
464 | void | ||
465 | queue_push(Queue *l, void *val, Arena *a) { | ||
466 | assert(l); | ||
467 | QueueVal *next = (QueueVal *)arena_calloc(sizeof(QueueVal), a); | ||
468 | next->val = val; | ||
469 | if (l->size == 0) { | ||
470 | l->head = next; | ||
471 | l->tail = next; | ||
472 | l->size = 1; | ||
473 | return; | ||
474 | } | ||
475 | QueueVal *cur = l->tail; | ||
476 | cur->next = next; | ||
477 | l->tail = next; | ||
478 | l->size++; | ||
479 | } | ||
480 | |||
481 | void * | ||
482 | queue_peek(Queue *l) { | ||
483 | assert(l); | ||
484 | if (l->size == 0) { | ||
485 | return NULL; | ||
486 | } | ||
487 | QueueVal *cur = l->head; | ||
488 | return cur->val; | ||
489 | } | ||
490 | |||
491 | void * | ||
492 | queue_pop(Queue *l) { | ||
493 | assert(l); | ||
494 | if (l->size == 0) { | ||
495 | return NULL; | ||
496 | } | ||
497 | QueueVal *cur = l->head; | ||
498 | l->head = cur->next; | ||
499 | l->size--; | ||
500 | return cur->val; | ||
501 | } | ||
502 | |||
503 | // | ||
504 | // Map. | ||
505 | // | ||
506 | |||
507 | typedef u64(HashFunc)(void *in); | ||
508 | typedef bool(EqFunc)(void *a, void *b); | ||
509 | |||
510 | typedef struct MapNode { | ||
511 | struct MapNode *child[4]; | ||
512 | void *key; | ||
513 | void *val; | ||
514 | } MapNode; | ||
515 | |||
516 | typedef struct Map { | ||
517 | HashFunc *hash_func; | ||
518 | EqFunc *eq_func; | ||
519 | MapNode *root; | ||
520 | Arena *storage; | ||
521 | } Map; | ||
522 | |||
523 | MapNode * | ||
524 | map_upsert(Map *m, void *key, Arena *a) { | ||
525 | u64 h = m->hash_func(key); | ||
526 | MapNode **item = &m->root; | ||
527 | while (*item) { | ||
528 | if (m->eq_func(key, (*item)->key)) { | ||
529 | return *item; | ||
530 | } | ||
531 | h = (h << 2) | (h >> 62); | ||
532 | item = &(*item)->child[h & 0x3]; | ||
533 | } | ||
534 | *item = (MapNode *)arena_calloc(sizeof(MapNode), a); | ||
535 | (*item)->key = key; | ||
536 | return *item; | ||
537 | } | ||
538 | |||
539 | void | ||
540 | map_insert(Map *m, void *key, void *val, Arena *a) { | ||
541 | u64 h = m->hash_func(key); | ||
542 | MapNode **item = &m->root; | ||
543 | while (*item) { | ||
544 | if (m->eq_func(key, (*item)->key)) { | ||
545 | (*item)->val = val; | ||
546 | return; | ||
547 | } | ||
548 | h = (h << 2) | (h >> 62); | ||
549 | item = &(*item)->child[h & 0x3]; | ||
550 | } | ||
551 | *item = (MapNode *)arena_calloc(sizeof(MapNode), a); | ||
552 | (*item)->key = key; | ||
553 | (*item)->val = val; | ||
554 | } | ||
555 | |||
556 | MapNode * | ||
557 | map_lookup(Map *m, void *key) { | ||
558 | u64 h = m->hash_func(key); | ||
559 | MapNode **item = &m->root; | ||
560 | while (*item) { | ||
561 | if (m->eq_func(key, (*item)->key)) { | ||
562 | return *item; | ||
563 | } | ||
564 | h = (h << 2) | (h >> 62); | ||
565 | item = &(*item)->child[h & 0x3]; | ||
566 | } | ||
567 | return NULL; | ||
568 | } | ||
569 | |||
570 | typedef struct MapIter { | ||
571 | Queue queue; | ||
572 | } MapIter; | ||
573 | |||
574 | MapIter | ||
575 | map_iterator(Map map, Arena *a) { | ||
576 | MapIter it = {0}; | ||
577 | queue_push(&it.queue, map.root, a); | ||
578 | return it; | ||
579 | } | ||
580 | |||
581 | MapNode * | ||
582 | map_next(MapIter *it, Arena *a) { | ||
583 | assert(it); | ||
584 | assert(a); | ||
585 | while (it->queue.head) { | ||
586 | MapNode *item = (MapNode *)queue_pop(&it->queue); | ||
587 | for (sz i = 0; i < 4; i++) { | ||
588 | MapNode *child = item->child[i]; | ||
589 | if (child) { | ||
590 | queue_push(&it->queue, child, a); | ||
591 | } | ||
592 | } | ||
593 | return item; | ||
594 | } | ||
595 | return NULL; | ||
596 | } | ||
597 | |||
598 | // | ||
599 | // Dynamic arrays. | ||
600 | // | ||
601 | |||
602 | typedef struct ArrayHeader { | ||
603 | sz size; | ||
604 | sz cap; | ||
605 | } ArrayHeader; | ||
606 | |||
607 | // Header/Size/capacity accessors. | ||
608 | #define array_head(ARR) ((ArrayHeader *)((char *)(ARR) - sizeof(ArrayHeader))) | ||
609 | #define array_size(ARR) ((ARR) ? array_head(ARR)->size : 0) | ||
610 | #define array_cap(ARR) ((ARR) ? array_head(ARR)->cap : 0) | ||
611 | |||
612 | // Initialize a dynamic array ARR with N elements. The initialization doesn't | ||
613 | // zero out the data, so thread carefully.. | ||
614 | #define array_init(ARR, N, ARENA) \ | ||
615 | ((ARR) = _array_reserve(N, sizeof(*(ARR)), (ARENA))) | ||
616 | |||
617 | // Push a given element T to the dynamic array ARR. | ||
618 | #define array_push(ARR, T, ARENA) \ | ||
619 | ((ARR) = _array_maybe_grow((ARR), sizeof(T), (ARENA)), \ | ||
620 | (ARR)[array_head(ARR)->size++] = (T)) | ||
621 | |||
622 | // Return the last element of the array. Can be used to build stacks. | ||
623 | #define array_pop(ARR) (ARR)[--array_head(ARR)->size] | ||
624 | |||
625 | // Return the value stored at the OFFSET position from the tail of the array. | ||
626 | #define array_peek(ARR, OFFSET) (ARR)[array_head(ARR)->size - 1 - (OFFSET)] | ||
627 | |||
628 | // Insert N bytes from the SRC array into the ARR dynamic array. | ||
629 | #define array_insert(ARR, SRC, N, ARENA) \ | ||
630 | ((ARR) = _array_insert((ARR), (SRC), (N), sizeof(*(ARR)), (ARENA))) | ||
631 | |||
632 | // Free the memory from the original allocated position. | ||
633 | #define array_free(ARR) ((ARR) ? free(array_head(ARR)), (ARR) = NULL : 0) | ||
634 | |||
635 | static inline void * | ||
636 | _array_reserve(sz num_elem, sz type_size, Arena *a) { | ||
637 | u8 *p = (u8 *)arena_malloc(num_elem * type_size + sizeof(ArrayHeader), a); | ||
638 | p += sizeof(ArrayHeader); | ||
639 | array_head(p)->size = 0; | ||
640 | array_head(p)->cap = num_elem; | ||
641 | return p; | ||
642 | } | ||
643 | |||
644 | static inline void * | ||
645 | _array_maybe_grow(void *arr, sz type_size, Arena *a) { | ||
646 | ArrayHeader *head = array_head(arr); | ||
647 | if (head->cap == head->size) { | ||
648 | sz prev_size = head->cap * type_size + sizeof(ArrayHeader); | ||
649 | if (head->cap == 0) { | ||
650 | head->cap++; | ||
651 | } else { | ||
652 | head->cap *= 2; | ||
653 | } | ||
654 | sz new_size = head->cap * type_size + sizeof(ArrayHeader); | ||
655 | head = (ArrayHeader *)arena_realloc(head, prev_size, new_size, a); | ||
656 | } | ||
657 | arr = (char *)head + sizeof(ArrayHeader); | ||
658 | return arr; | ||
659 | } | ||
660 | |||
661 | static inline char * | ||
662 | _array_insert(char *arr, const char *src, sz n_bytes, sz type_size, Arena *a) { | ||
663 | ArrayHeader *head = array_head(arr); | ||
664 | sz new_size = n_bytes + head->size; | ||
665 | if (new_size > head->cap * type_size) { | ||
666 | sz prev_size = head->cap * type_size + sizeof(ArrayHeader); | ||
667 | if (head->cap == 0) { | ||
668 | head->cap = 1; | ||
669 | } | ||
670 | while (new_size >= head->cap * type_size) { | ||
671 | head->cap *= 2; | ||
672 | } | ||
673 | sz new_size = head->cap * type_size + sizeof(ArrayHeader); | ||
674 | head = (ArrayHeader *)arena_realloc(head, prev_size, new_size, a); | ||
675 | } | ||
676 | arr = (char *)head + sizeof(ArrayHeader); | ||
677 | memcpy((arr + head->size), src, n_bytes); | ||
678 | head->size = new_size; | ||
679 | return arr; | ||
680 | } | ||
681 | |||
682 | // | ||
683 | // Math. | ||
684 | // | ||
685 | |||
686 | typedef union Vec2f { | ||
687 | struct { | ||
688 | f32 x, y; | ||
689 | }; | ||
690 | struct { | ||
691 | f32 u, v; | ||
692 | }; | ||
693 | struct { | ||
694 | f32 left, right; | ||
695 | }; | ||
696 | struct { | ||
697 | f32 width, height; | ||
698 | }; | ||
699 | f32 data[2]; | ||
700 | } Vec2f; | ||
701 | |||
702 | typedef union Vec3f { | ||
703 | struct { | ||
704 | f32 x, y, z; | ||
705 | }; | ||
706 | struct { | ||
707 | f32 u, v, w; | ||
708 | }; | ||
709 | struct { | ||
710 | f32 r, g, b; | ||
711 | }; | ||
712 | struct { | ||
713 | Vec2f xy; | ||
714 | f32 _z; | ||
715 | }; | ||
716 | struct { | ||
717 | f32 _x; | ||
718 | Vec2f yz; | ||
719 | }; | ||
720 | struct { | ||
721 | Vec2f uv; | ||
722 | f32 _w; | ||
723 | }; | ||
724 | struct { | ||
725 | f32 _u; | ||
726 | Vec2f vw; | ||
727 | }; | ||
728 | f32 data[2]; | ||
729 | } Vec3f; | ||
730 | |||
731 | typedef union Vec4f { | ||
732 | struct { | ||
733 | f32 x, y, z, w; | ||
734 | }; | ||
735 | struct { | ||
736 | Vec3f xyz; | ||
737 | f32 _w; | ||
738 | }; | ||
739 | struct { | ||
740 | union { | ||
741 | Vec3f rgb; | ||
742 | struct { | ||
743 | f32 r, g, b; | ||
744 | }; | ||
745 | }; | ||
746 | f32 a; | ||
747 | }; | ||
748 | struct { | ||
749 | Vec2f xy; | ||
750 | f32 _y0; | ||
751 | f32 _z0; | ||
752 | }; | ||
753 | struct { | ||
754 | f32 _x0; | ||
755 | Vec2f yz; | ||
756 | f32 _z1; | ||
757 | }; | ||
758 | struct { | ||
759 | f32 _x1; | ||
760 | f32 _y1; | ||
761 | Vec2f zw; | ||
762 | }; | ||
763 | f32 data[4]; | ||
764 | } Vec4f; | ||
765 | |||
766 | // | ||
767 | // OS/Platform stuff. | ||
768 | // | ||
769 | |||
770 | typedef enum { | ||
771 | FILE_ERR_OK = 0, | ||
772 | FILE_ERR_CANT_OPEN, | ||
773 | FILE_ERR_READ_ERR, | ||
774 | FILE_ERR_PATH_TOO_BIG, | ||
775 | FILE_ERR_EMPTY, | ||
776 | FILE_ERR_NUM, | ||
777 | } FileErr; | ||
778 | |||
779 | Str file_err_str[FILE_ERR_NUM] = { | ||
780 | cstr(""), | ||
781 | cstr("couldn't open file"), | ||
782 | cstr("couldn't read file"), | ||
783 | cstr("file path too big"), | ||
784 | cstr("file is empty"), | ||
785 | }; | ||
786 | |||
787 | typedef struct FileContents { | ||
788 | Str path; | ||
789 | Array data; | ||
790 | FileErr err; | ||
791 | } FileContents; | ||
792 | |||
793 | FileContents | ||
794 | platform_read_file(Str path, Arena *a) { | ||
795 | // Transform Str to cstr. | ||
796 | char path_str[KB(1)]; | ||
797 | if (path.size >= KB(1) - 1) { | ||
798 | return (FileContents){ | ||
799 | .path = path, | ||
800 | .err = FILE_ERR_PATH_TOO_BIG, | ||
801 | }; | ||
802 | } | ||
803 | memcpy(path_str, path.mem, path.size); | ||
804 | path_str[path.size] = 0; | ||
805 | |||
806 | // Read the entire file into memory. | ||
807 | sz file_size = 0; | ||
808 | FILE *fp = fopen(path_str, "rb+"); | ||
809 | if (!fp) { | ||
810 | return (FileContents){ | ||
811 | .path = path, | ||
812 | .err = FILE_ERR_CANT_OPEN, | ||
813 | }; | ||
814 | } | ||
815 | fseek(fp, 0, SEEK_END); | ||
816 | file_size = ftell(fp); | ||
817 | rewind(fp); | ||
818 | u8 *memory = arena_malloc(file_size, a); | ||
819 | sz read = fread(memory, 1, file_size, fp); | ||
820 | fclose(fp); | ||
821 | if (read == 0) { | ||
822 | return (FileContents){ | ||
823 | .path = path, | ||
824 | .err = FILE_ERR_EMPTY, | ||
825 | }; | ||
826 | } | ||
827 | |||
828 | return (FileContents){ | ||
829 | .path = path, | ||
830 | .data = (Array){.mem = (u8 *)memory, .size = file_size}, | ||
831 | .err = FILE_ERR_OK, | ||
832 | }; | ||
833 | } | ||
834 | |||
835 | void * | ||
836 | platform_calloc(sz size, void *ctx) { | ||
837 | (void)ctx; | ||
838 | return calloc(1, size); | ||
839 | } | ||
840 | |||
841 | void * | ||
842 | platform_malloc(sz size, void *ctx) { | ||
843 | (void)ctx; | ||
844 | return malloc(size); | ||
845 | } | ||
846 | |||
847 | void * | ||
848 | platform_realloc(void *ptr, sz old_size, sz new_size, void *ctx) { | ||
849 | (void)ctx; | ||
850 | (void)old_size; | ||
851 | (void)new_size; | ||
852 | return realloc(ptr, new_size); | ||
853 | } | ||
854 | |||
855 | void | ||
856 | platform_free(void *ptr, void *ctx) { | ||
857 | (void)ctx; | ||
858 | free(ptr); | ||
859 | } | ||
860 | |||
861 | #include <time.h> | ||
862 | #include <unistd.h> | ||
863 | |||
864 | void | ||
865 | platform_sleep(size_t microseconds) { | ||
866 | usleep(microseconds); | ||
867 | } | ||
868 | |||
869 | sz | ||
870 | platform_time(void) { | ||
871 | struct timespec ts; | ||
872 | timespec_get(&ts, TIME_UTC); | ||
873 | return ts.tv_sec * 1000000000 + ts.tv_nsec; | ||
874 | } | ||
875 | |||
876 | #endif // BADLIB_H | ||
@@ -2,16 +2,7 @@ | |||
2 | #include <stdio.h> | 2 | #include <stdio.h> |
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | 4 | ||
5 | #include "common.h" | 5 | #include "badlib.h" |
6 | #include "darray.h" | ||
7 | #include "string_view.c" | ||
8 | #include "errors.c" | ||
9 | #include "lexer.c" | ||
10 | #include "nodes.c" | ||
11 | #include "parser.c" | ||
12 | #include "semantic.c" | ||
13 | #include "ir.c" | ||
14 | #include "viz.c" | ||
15 | 6 | ||
16 | typedef enum ExecMode { | 7 | typedef enum ExecMode { |
17 | RUN_NORMAL, | 8 | RUN_NORMAL, |
@@ -23,101 +14,32 @@ typedef enum ExecMode { | |||
23 | 14 | ||
24 | static ExecMode mode = RUN_NORMAL; | 15 | static ExecMode mode = RUN_NORMAL; |
25 | 16 | ||
26 | void | 17 | #define LEXER_MEM GB(2) |
27 | init(void) { | ||
28 | // STUB | ||
29 | } | ||
30 | |||
31 | void | ||
32 | halt(void) { | ||
33 | // STUB | ||
34 | } | ||
35 | |||
36 | void | ||
37 | process_source(const StringView *source, const char *file_name) { | ||
38 | // Read tokens. | ||
39 | Token *tokens = tokenize(source); | ||
40 | check_errors(file_name); | ||
41 | if (mode == PRINT_LEX) { | ||
42 | print_tokens(tokens); | ||
43 | return; | ||
44 | } | ||
45 | |||
46 | // Parser. | ||
47 | Root *roots = parse(tokens); | ||
48 | check_errors(file_name); | ||
49 | if (mode == PRINT_PARSE) { | ||
50 | viz_ast(roots); | ||
51 | return; | ||
52 | } | ||
53 | |||
54 | // Symbol table generation and type checking. | ||
55 | ParseTree *parse_tree = semantic_analysis(roots); | ||
56 | check_errors(file_name); | ||
57 | if (mode == PRINT_SEMANTIC) { | ||
58 | viz_ast(parse_tree->roots); | ||
59 | return; | ||
60 | } | ||
61 | if (mode == PRINT_SYMTABLES) { | ||
62 | viz_symtables(parse_tree->scopes); | ||
63 | return; | ||
64 | } | ||
65 | |||
66 | ProgramBASM *program = generate_basm(parse_tree); | ||
67 | check_errors(file_name); | ||
68 | viz_basm(program); | ||
69 | } | ||
70 | 18 | ||
71 | void | 19 | void |
72 | run_file(char *file_name) { | 20 | process_file(Str path) { |
73 | FILE *file = fopen(file_name, "r"); | 21 | printf("processing: "); |
74 | if (!file) { | 22 | printstrln(&path); |
75 | fprintf(stderr, "error: couldn't open input file: %s\n", file_name); | 23 | Allocator os_allocator = { |
76 | exit(EXIT_FAILURE); | 24 | .malloc = platform_malloc, |
77 | } | 25 | .calloc = platform_calloc, |
78 | 26 | .realloc = platform_realloc, | |
79 | // Read entire file into memory. | 27 | .free = platform_free, |
80 | fseek(file, 0, SEEK_END); | ||
81 | size_t file_size = ftell(file); | ||
82 | fseek(file, 0, SEEK_SET); | ||
83 | |||
84 | char *source = malloc(file_size + 1); | ||
85 | fread(source, 1, file_size, file); | ||
86 | source[file_size] = 0; | ||
87 | |||
88 | StringView sv = (StringView){ | ||
89 | .start = source, | ||
90 | .n = file_size, | ||
91 | }; | 28 | }; |
92 | 29 | printf("initialing memory...\n"); | |
93 | process_source(&sv, file_name); | 30 | Arena lexer_arena = arena_create(LEXER_MEM, os_allocator); |
94 | 31 | printf("reading file...\n"); | |
95 | free(source); | 32 | FileContents file = platform_read_file(path, &lexer_arena); |
96 | fclose(file); | 33 | |
97 | } | 34 | // NOTE: Testing file read line by line. |
98 | 35 | Str scanner = file.data; | |
99 | #define STDIN_BUF_CAP 16 | 36 | for (sz i = 0; scanner.size != 0; i++) { |
100 | 37 | Str line = str_split(&scanner, cstr("\n")); | |
101 | void | 38 | printf("%04ld ", i); |
102 | run_stdin(void) { | 39 | printstrln(&line); |
103 | size_t buf_size = 0; | ||
104 | char *source = NULL; | ||
105 | array_init(source, STDIN_BUF_CAP); | ||
106 | |||
107 | char c; | ||
108 | while ((c = getchar()) != EOF) { | ||
109 | array_push(source, c); | ||
110 | buf_size++; | ||
111 | } | 40 | } |
112 | 41 | ||
113 | StringView sv = (StringView){ | 42 | // TODO: run lexer. |
114 | .start = source, | ||
115 | .n = buf_size, | ||
116 | }; | ||
117 | |||
118 | process_source(&sv, "stdin"); | ||
119 | |||
120 | array_free(source); | ||
121 | } | 43 | } |
122 | 44 | ||
123 | #ifndef BIN_NAME | 45 | #ifndef BIN_NAME |
@@ -129,14 +51,14 @@ print_usage(void) { | |||
129 | printf("Usage: %s [options] <filename filename ...>\n", BIN_NAME); | 51 | printf("Usage: %s [options] <filename filename ...>\n", BIN_NAME); |
130 | printf("\n"); | 52 | printf("\n"); |
131 | printf("\t-h \t\tShow usage.\n"); | 53 | printf("\t-h \t\tShow usage.\n"); |
132 | printf("\t-p [l|p|s|t]\tPrint mode for [l]exing, [p]arsing, [s]emantic analysis, symbol [t]ables\n"); | 54 | printf( |
55 | "\t-p [l|p|s|t]\tPrint mode for [l]exing, [p]arsing, [s]emantic " | ||
56 | "analysis, symbol [t]ables\n"); | ||
133 | printf("\n"); | 57 | printf("\n"); |
134 | } | 58 | } |
135 | 59 | ||
136 | int | 60 | int |
137 | main(int argc, char *argv[]) { | 61 | main(int argc, char *argv[]) { |
138 | init(); | ||
139 | |||
140 | int option; | 62 | int option; |
141 | while ((option = getopt(argc, argv, "hp:")) != -1) { | 63 | while ((option = getopt(argc, argv, "hp:")) != -1) { |
142 | switch (option) { | 64 | switch (option) { |
@@ -167,18 +89,22 @@ main(int argc, char *argv[]) { | |||
167 | 89 | ||
168 | // Run from stdin. | 90 | // Run from stdin. |
169 | if (optind == argc) { | 91 | if (optind == argc) { |
170 | run_stdin(); | 92 | // TODO: REPL |
93 | // repl(); | ||
171 | goto exit_success; | 94 | goto exit_success; |
172 | } | 95 | } |
173 | 96 | ||
174 | // Run from file. | 97 | // Run from file. |
175 | while (optind < argc) { | 98 | while (optind < argc) { |
176 | char *file_name = argv[optind]; | 99 | char *file_name = argv[optind]; |
177 | run_file(file_name); | 100 | Str file_path = { |
101 | .mem = (u8 *)file_name, | ||
102 | .size = strlen(file_name), | ||
103 | }; | ||
104 | process_file(file_path); | ||
178 | optind++; | 105 | optind++; |
179 | } | 106 | } |
180 | 107 | ||
181 | exit_success: | 108 | exit_success: |
182 | halt(); | ||
183 | return EXIT_SUCCESS; | 109 | return EXIT_SUCCESS; |
184 | } | 110 | } |