about summary refs log tree commit diff
path: root/boot
diff options
context:
space:
mode:
Diffstat (limited to 'boot')
-rw-r--r--boot/common.c19
-rw-r--r--boot/runtime/core.c742
-rw-r--r--boot/transpile.c19
3 files changed, 773 insertions, 7 deletions
diff --git a/boot/common.c b/boot/common.c
index 6a95bb6..4f9c2ad 100644
--- a/boot/common.c
+++ b/boot/common.c
@@ -8,6 +8,9 @@
  * SPDX-License-Identifier: MPL-2.0
  */
 
+// TODO: merge our common library with `boot/runtime/core.c`
+// to avoid implementing a semi-standard library twice!
+
 #pragma once
 
 #include <math.h>
@@ -239,6 +242,22 @@ string_length(struct String s)
     return s.length;
 }
 
+bool
+string_equals(const struct String a, const struct String b)
+{
+    if (string_length(a) != string_length(b)) return false;
+    if (string_length(a) == 0) return true;
+    return memcmp(a.data, b.data, a.length) == 0;
+}
+
+bool
+string_equals_c_str(const struct String a, const ascii* b)
+{
+    uint b_length = strlen(b);
+    if (string_length(a) != b_length) return false;
+    if (string_length(a) == 0) return true;
+    return memcmp(a.data, b, a.length) == 0;
+}
 void
 string_print(struct String s)
 {
diff --git a/boot/runtime/core.c b/boot/runtime/core.c
new file mode 100644
index 0000000..a409604
--- /dev/null
+++ b/boot/runtime/core.c
@@ -0,0 +1,742 @@
+/*
+ * the core catskill c-library for
+ * catskill source files transpiled to c.
+ * used during bootstrapping, while the full
+ * catskill standard library is not yet available.
+ *
+ * copyright (c) 2025, mel g. <mel@rnrd.eu>
+ *
+ * spdx-license-identifier: mpl-2.0
+ */
+
+// TODO: as mentioned in `boot/common.c`, the catskill transpiled
+// source code "core" library should be merged with the catboot
+// "common" library.
+
+#pragma once
+
+// common headers for both your average catskill program.
+// other libc headers can be included with the pragma
+// `| c-header "header.h"`.
+#include <fcntl.h>
+#include <features.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+// types predefined for catskill programs.
+// the transpiler outputs these types directly when seen
+// in the source code.
+#define uint8 uint8_t
+#define uint16 uint16_t
+#define uint32 uint32_t
+#define uint64 uint64_t
+
+#define int8 int8_t
+#define int16 int16_t
+#define int32 int32_t
+#define int64 int64_t
+
+#define float32 float
+#define float64 double
+#define real float64
+
+#define uint uint64
+#define integer int64
+
+#define ascii char
+#define byte char
+
+#define bool _Bool
+#define true 1
+#define false 0
+#define nil NULL
+
+_Noreturn void
+panic(const ascii* message, ...)
+{
+    fprintf(stderr, "panic: ");
+
+    va_list args;
+    va_start(args, message);
+    vfprintf(stderr, message, args);
+    va_end(args);
+
+    fprintf(stderr, "\n");
+    exit(EXIT_FAILURE);
+}
+
+void*
+allocate(size_t size)
+{
+    void* ptr = malloc(size);
+    if (!ptr) panic("failed to allocate %zu bytes. are we out of memory? :(", size);
+    return ptr;
+}
+
+void*
+reallocate(void* ptr, size_t size)
+{
+    void* new_ptr = realloc(ptr, size);
+    if (!new_ptr) panic("failed to reallocate %zu bytes. are we out of memory? :(", size);
+    if (size == 0) return nil;
+    return new_ptr;
+}
+
+struct _Array
+{
+    uint element_size;
+    void* data;
+    uint length;
+    uint capacity;
+};
+
+// a dynamic array type.
+#define Array(type) struct _Array
+
+#define ARRAY_DEFAULT_CAPACITY 16
+
+void array_grow(struct _Array* array);
+void array_grow_until(struct _Array* array, uint min_capacity);
+
+// internal use, try the `array_new` macro.
+struct _Array
+_array_new(uint element_size)
+{
+    struct _Array array = {
+        .element_size = element_size,
+        .data = nil,
+        .length = 0,
+        .capacity = 0,
+    };
+    array_grow(&array);
+    return array;
+}
+
+// creates a new, empty array for the given type.
+#define array_new(type) _array_new(sizeof(type))
+
+// returns the number of elements in the array.
+uint
+array_length(const struct _Array* array)
+{
+    return array->length;
+}
+
+// returns the total number of elements the array can hold before reallocating.
+uint
+array_capacity(const struct _Array* array)
+{
+    return array->capacity;
+}
+
+// true if the array is empty.
+bool
+array_is_empty(const struct _Array* array)
+{
+    return array->length == 0;
+}
+
+// resizes the array backing allocation to the new capacity.
+// if the new capacity is smaller than the current length,
+// the array will be truncated to the new length.
+// if the new capacity is 0, the array's data will be freed.
+void
+array_resize_allocation(struct _Array* array, uint new_capacity)
+{
+    // reallocate can handle data being nil, so we don't need to check for that.
+    array->data = reallocate(array->data, new_capacity * array->element_size);
+    array->capacity = new_capacity;
+    array->length = array->length < new_capacity ? array->length : new_capacity;
+}
+
+// resizes the array to a new length.
+// if the new length is larger than the current capacity,
+// the array will be grown to fit, and the new elements will be zero-initialized.
+// if the new length is smaller than the current length,
+// the array will be truncated, and the discarded elements are zeroed out.
+// this function does not shrink the underlying allocation.
+void
+array_resize(struct _Array* array, uint new_length)
+{
+    if (new_length > array->capacity) {
+        // grow array to fit the new length naturally.
+        array_grow_until(array, new_length);
+        // initialize the new elements to zero.
+        memset((uint8*)array->data + (array->length * array->element_size), 0,
+               (new_length - array->length) * array->element_size);
+    } else if (new_length < array->length) {
+        // truncate the array elements to the new length.
+        memset((uint8*)array->data + (new_length * array->element_size), 0,
+               (array->length - new_length) * array->element_size);
+    }
+    array->length = new_length;
+}
+
+// frees the memory used by the array's data.
+void
+array_free(struct _Array* array)
+{
+    array_resize_allocation(array, 0);
+}
+
+// grows the array's capacity naturally.
+void
+array_grow(struct _Array* array)
+{
+    if (array->capacity == 0)
+        array_resize_allocation(array, ARRAY_DEFAULT_CAPACITY);
+    else
+        array_resize_allocation(array, array->capacity * 2);
+}
+
+// grows the array's capacity naturally to hold at least `min_capacity` elements.
+// if current capacity is already enough, nothing happens.
+void
+array_grow_until(struct _Array* array, uint min_capacity)
+{
+    while (array->capacity < min_capacity) array_grow(array);
+}
+
+// internal use, try the `array_get` macro.
+void
+_array_get(const struct _Array* array, uint index, void* out_element)
+{
+    if (index >= array->length) panic("index out of bounds: %u (length: %u)", index, array->length);
+    memcpy(out_element, (uint8*)array->data + (index * array->element_size), array->element_size);
+}
+
+// gets the element at a given index and copies it to `out_element`.
+// panic if the index is out of bounds.
+#define array_get(array, index, out_element) _array_get(array, index, (void*)(out_element))
+
+// internal use, prefer the `array_set` macro.
+void
+_array_set(struct _Array* array, uint index, const void* element)
+{
+    if (index >= array->length) {
+        panic("index out of bounds: %u (length: %u)", index, array->length);
+    }
+    memcpy((uint8*)array->data + (index * array->element_size), element, array->element_size);
+}
+
+// sets the element at a given index.
+// panic if the index is out of bounds.
+#define array_set(array, index, element) _array_set(array, index, (const void*)(element))
+
+// internal use, prefer the `array_at` macro.
+void*
+_array_at(const struct _Array* array, uint index)
+{
+    if (index >= array->length) {
+        panic("index out of bounds: %u (length: %u)", index, array->length);
+    }
+    return (uint8*)array->data + (index * array->element_size);
+}
+
+// returns a pointer to the element at a given index.
+// panics if the index is out of bounds.
+#define array_at(type, array, index) ((type*)_array_at(array, index))
+
+// internal use, prefer the `array_insert` macro.
+void
+_array_insert(struct _Array* array, uint index, const void* element)
+{
+    if (index > array->length) panic("index out of bounds: %u (length: %u)", index, array->length);
+    array_grow_until(array, array->length + 1);
+
+    // shift elements to the right to make space for the new element
+    // if we are inserting at the end, we don't need to shift anything.
+    // if there aren't any elements, we also don't need to shift anything.
+    if (index < array->length && array->length > 0)
+        memmove(
+            (uint8*)array->data + ((index + 1) * array->element_size),
+            (uint8*)array->data + (index * array->element_size),
+            (array->length - index) * array->element_size);
+    // insert the new element at the specified index
+    memcpy((uint8*)array->data + (index * array->element_size), element, array->element_size);
+
+    ++array->length;
+}
+
+// inserts an element at a given index, shifting subsequent elements.
+// panics if the index is out of bounds (greater than length).
+#define array_insert(array, index, element) _array_insert(array, index, (const void*)(element))
+
+// adds an element to the end of the array.
+// internal use; prefer the `array_push` macro.
+void
+_array_push(struct _Array* array, const void* element)
+{
+    array_insert(array, array->length, element);
+}
+
+// adds an element to the end of the array.
+#define array_push(array, element) _array_push(array, (const void*)(element))
+
+// removes an element at a given index, shifting subsequent elements.
+// if `removed_element` is not null, the removed element is copied to it.
+// panics if the index is out of bounds.
+// internal use; prefer the `array_remove` macro.
+void
+_array_remove(struct _Array* array, uint index, void* removed_element)
+{
+    if (index >= array->length) {
+        panic("index out of bounds: %u (length: %u)", index, array->length);
+    }
+
+    if (removed_element)
+        memcpy(removed_element, (uint8*)array->data + (index * array->element_size),
+               array->element_size);
+
+    // shift elements to the left to fill the gap
+    // if we are removing the last element, we don't need to shift anything.
+    // if there aren't any elements, there is also nothing to shift.
+    if (index < array->length - 1 && array->length > 1)
+        memmove(
+            (uint8*)array->data + (index * array->element_size),
+            (uint8*)array->data + ((index + 1) * array->element_size),
+            (array->length - index - 1) * array->element_size);
+
+    --array->length;
+}
+
+// removes an element at a given index, shifting subsequent elements.
+// if `removed_element` is not null, the removed element is copied to it.
+// panics if the index is out of bounds.
+#define array_remove(array, index, removed_element) \
+    _array_remove(array, index, (void*)(removed_element))
+
+// removes and optionally returns the last element of the array.
+// panics if the array is empty.
+// internal use; prefer the `array_pop` macro.
+void
+_array_pop(struct _Array* array, void* removed_element)
+{
+    if (array->length == 0) panic("cannot pop from an empty array");
+    array_remove(array, array->length - 1, removed_element);
+}
+
+// removes and optionally returns the last element of the array.
+// panics if the array is empty.
+#define array_pop(array, removed_element) _array_pop(array, (void*)(removed_element))
+
+// clears the array, setting its length to 0.
+// this does not free the allocated memory.
+void
+array_clear(struct _Array* array)
+{
+    memset(array->data, 0, array->element_size * array->length);
+    array->length = 0;
+}
+
+// appends all elements from array `b` to the end of array `a`.
+// panics if the element sizes of the arrays are different.
+void
+array_append(struct _Array* a, struct _Array* b)
+{
+    if (a->element_size != b->element_size)
+        panic("cannot append arrays of different type and element size: %u vs %u", a->element_size,
+              b->element_size);
+
+    // grow array `a` to fit the new elements, following the array's growth strategy.
+    array_grow_until(a, a->length + b->length);
+
+    memcpy((uint8*)a->data + (a->length * a->element_size), b->data, b->length * a->element_size);
+
+    a->length += b->length;
+}
+
+// a macro for iterating over each element in an array.
+#define FOR_EACH_ARRAY(type, array, action)          \
+    for (uint i = 0; i < array_length(array); ++i) { \
+        type* element = array_at(type, array, i);    \
+        action;                                      \
+    }
+
+#define ARRAY_FREE_ELEMENTS(type, array, free_action) \
+    FOR_EACH_ARRAY(type, array, { free_action(element); })
+
+// a macro for iterating over a range of elements in an array.
+#define FOR_EACH_IN_RANGE(type, array, start, end, action)          \
+    for (uint i = start; i < end && i < array_length(array); ++i) { \
+        type* element = array_at(type, array, i);                   \
+        action;                                                     \
+    }
+
+// a dynamic string type.
+// the string is always null-terminated to be compatible with c functions.
+// the length of the string does not include the null terminator.
+struct String
+{
+    Array(ascii) data;
+};
+
+void string_ensure_capacity(struct String* str, uint new_len);
+
+// creates a new, empty string.
+struct String
+string_new(void)
+{
+    struct String str;
+    str.data = array_new(ascii);
+    ((ascii*)str.data.data)[0] = '\0';
+    return str;
+}
+
+// creates a new string from a c-style string.
+struct String
+string_from(const ascii* c_str)
+{
+    struct String str = string_new();
+    uint len = strlen(c_str);
+    string_ensure_capacity(&str, len);
+    memcpy(str.data.data, c_str, len);
+    str.data.length = len;
+    ((ascii*)str.data.data)[len] = '\0';
+    return str;
+}
+
+// frees the memory used by the string.
+void
+string_free(struct String* str)
+{
+    array_free(&str->data);
+}
+
+// returns the length of the string.
+uint
+string_length(const struct String* str)
+{
+    return str->data.length;
+}
+
+// returns the current capacity of the string (excluding the null terminator).
+uint
+string_capacity(const struct String* str)
+{
+    return str->data.capacity > 0 ? str->data.capacity - 1 : 0;
+}
+
+Array(ascii) string_backing(const struct String* str)
+{
+    return str->data;
+}
+
+// checks if the string is empty.
+bool
+string_is_empty(const struct String* str)
+{
+    return str->data.length == 0;
+}
+
+// returns a null-terminated c-string representation.
+const ascii*
+string_c_str(const struct String* str)
+{
+    return (const ascii*)str->data.data;
+}
+
+// ensures the array has enough capacity for `new_len` characters plus a null terminator.
+void
+string_ensure_capacity(struct String* str, uint new_len)
+{
+    uint required_capacity = new_len + 1;
+    if (required_capacity > str->data.capacity) {
+        uint new_cap = str->data.capacity;
+        if (new_cap == 0) new_cap = ARRAY_DEFAULT_CAPACITY;
+        while (required_capacity > new_cap) new_cap *= 2;
+        array_resize_allocation(&str->data, new_cap);
+    }
+}
+
+// appends a character to the end of the string.
+void
+string_push(struct String* str, ascii c)
+{
+    uint new_len = str->data.length + 1;
+    string_ensure_capacity(str, new_len);
+    ((ascii*)str->data.data)[str->data.length] = c;
+    str->data.length = new_len;
+    ((ascii*)str->data.data)[new_len] = '\0';
+}
+
+// removes and returns the last character of the string.
+// panics if the string is empty.
+ascii
+string_pop(struct String* str)
+{
+    if (str->data.length == 0) { panic("cannot pop from an empty string"); }
+    str->data.length--;
+    ascii c = ((ascii*)str->data.data)[str->data.length];
+    ((ascii*)str->data.data)[str->data.length] = '\0';
+    return c;
+}
+
+// appends a string to the end of another string.
+void
+string_append(struct String* dest, const struct String* src)
+{
+    uint old_len = dest->data.length;
+    uint new_len = old_len + src->data.length;
+    string_ensure_capacity(dest, new_len);
+    memcpy((ascii*)dest->data.data + old_len, src->data.data, src->data.length);
+    dest->data.length = new_len;
+    ((ascii*)dest->data.data)[new_len] = '\0';
+}
+
+// appends a c-style string to the end of a string.
+void
+string_append_c_str(struct String* dest, const ascii* c_str)
+{
+    uint c_str_len = strlen(c_str);
+    uint old_len = dest->data.length;
+    uint new_len = old_len + c_str_len;
+    string_ensure_capacity(dest, new_len);
+    memcpy((ascii*)dest->data.data + old_len, c_str, c_str_len);
+    dest->data.length = new_len;
+    ((ascii*)dest->data.data)[new_len] = '\0';
+}
+
+// clears the string, making it empty.
+void
+string_clear(struct String* str)
+{
+    str->data.length = 0;
+    ((ascii*)str->data.data)[0] = '\0';
+}
+
+// creates a copy of a string.
+struct String
+string_clone(const struct String* str)
+{
+    struct String new_str = string_new();
+    string_ensure_capacity(&new_str, str->data.length);
+    memcpy(new_str.data.data, str->data.data, str->data.length + 1); // copy with null terminator
+    new_str.data.length = str->data.length;
+    return new_str;
+}
+
+// compares two strings for equality.
+bool
+string_equals(const struct String* a, const struct String* b)
+{
+    if (a->data.length != b->data.length) return false;
+    if (a->data.length == 0) return true;
+    return memcmp(a->data.data, b->data.data, a->data.length) == 0;
+}
+
+// compares a string with a c-style string for equality.
+bool
+string_equals_c_str(const struct String* a, const ascii* b)
+{
+    uint b_len = strlen(b);
+    if (a->data.length != b_len) return false;
+    if (a->data.length == 0) return true;
+    return memcmp(a->data.data, b, a->data.length) == 0;
+}
+
+// returns a new string that is a slice of the original.
+// the slice is from `start` (inclusive) to `end` (exclusive).
+// panics if the range is invalid.
+struct String
+string_slice(const struct String* str, uint start, uint end)
+{
+    if (start > end || end > str->data.length) {
+        panic("invalid slice range [%u, %u) for string of length %u", start, end, str->data.length);
+    }
+    uint slice_len = end - start;
+    struct String new_str = string_new();
+    string_ensure_capacity(&new_str, slice_len);
+    memcpy(new_str.data.data, (const ascii*)str->data.data + start, slice_len);
+    new_str.data.length = slice_len;
+    ((ascii*)new_str.data.data)[slice_len] = '\0';
+    return new_str;
+}
+
+// returns the character at the given index.
+// panics if the index is out of bounds.
+ascii
+string_get(const struct String* str, uint index)
+{
+    if (index >= str->data.length) {
+        panic("index out of bounds: %u (length: %u)", index, str->data.length);
+    }
+    return ((ascii*)str->data.data)[index];
+}
+
+// sets the character at the given index.
+// panics if the index is out of bounds.
+void
+string_set(struct String* str, uint index, ascii c)
+{
+    if (index >= str->data.length) {
+        panic("index out of bounds: %u (length: %u)", index, str->data.length);
+    }
+    ((ascii*)str->data.data)[index] = c;
+}
+
+#define FOR_EACH_IN_STRING(character_variable, str, action)         \
+    for (uint i = 0; i < string_length(str); ++i) {                 \
+        ascii character_variable = ((ascii*)((str)->data.data))[i]; \
+        action;                                                     \
+    }
+
+// a slice is a view into a block of memory.
+// it does not own the data it points to.
+struct _Slice
+{
+    uint element_size;
+    void* data;
+    uint length;
+};
+
+#define Slice(type) struct _Slice
+
+// internal use, try the `slice_new` macro.
+struct _Slice
+slice_new(void* data, uint length, uint element_size)
+{
+    struct _Slice slice = { .data = data, .length = length, .element_size = element_size };
+    return slice;
+}
+
+#define slice_new(data, length) slice_new((void*)(data), (length), sizeof(*(data)))
+
+// a string_view is a slice of characters.
+// it is not null-terminated.
+struct String_View
+{
+    Slice(ascii) data;
+};
+
+uint
+string_view_length(const struct String_View view)
+{
+    return view.data.length;
+}
+
+Slice(ascii) string_view_backing(const struct String_View view)
+{
+    return view.data;
+}
+
+// creates a new string_view from a string.
+struct String_View
+string_view_from_string(const struct String* str)
+{
+    return (struct String_View){ .data = slice_new(str->data.data, str->data.length) };
+}
+
+// creates a new string_view from a c-style string.
+struct String_View
+string_view_from_c_str(const ascii* c_str)
+{
+    return (struct String_View){ .data = slice_new((void*)c_str, strlen(c_str)) };
+}
+
+struct String_View
+string_view_from_slice(Slice(ascii) slice)
+{
+    return (struct String_View){ .data = slice };
+}
+
+// compares two string_views for equality.
+bool
+string_view_equals(struct String_View a, struct String_View b)
+{
+    Slice(ascii) a_backing = string_view_backing(a), b_backing = string_view_backing(b);
+    if (string_view_length(a) != string_view_length(b)) return false;
+    if (a.data.length == 0) return true;
+    return memcmp(a_backing.data, b_backing.data, a_backing.length) == 0;
+}
+
+// a file handle.
+struct File
+{
+    FILE* handle;
+    struct String* path;
+};
+
+// opens a file and returns a handle to it.
+// returns nil if the file cannot be opened.
+struct File*
+file_open(struct String* path, const ascii* mode)
+{
+    FILE* handle = fopen(string_c_str(path), mode);
+    if (!handle) return nil;
+
+    struct File* file = allocate(sizeof(struct File));
+    file->handle = handle;
+    file->path = path;
+    return file;
+}
+
+// closes a file.
+void
+file_close(struct File* file)
+{
+    if (!file) return;
+    fclose(file->handle);
+    free(file);
+}
+
+// reads the entire content of a file into a string.
+struct String
+file_read_all(struct File* file)
+{
+    fseek(file->handle, 0, SEEK_END);
+    long file_size = ftell(file->handle);
+    fseek(file->handle, 0, SEEK_SET);
+
+    struct String content = string_new();
+    string_ensure_capacity(&content, file_size);
+
+    fread(content.data.data, 1, file_size, file->handle);
+    content.data.length = file_size;
+    ((ascii*)content.data.data)[file_size] = '\0';
+
+    return content;
+}
+
+// writes the content of a string to a file.
+void
+file_write(struct File* file, struct String* content)
+{
+    fwrite(string_c_str(content), 1, string_length(content), file->handle);
+}
+
+// checks if a file exists at the given path.
+bool
+file_exists(struct String* path)
+{
+    return access(string_c_str(path), F_OK) != -1;
+}
+
+// memory-maps a file and returns a string_view of its content.
+// panic if the file cannot be mapped.
+struct String_View
+file_map_to_string_view(struct File* file)
+{
+    struct stat st;
+    if (fstat(fileno(file->handle), &st) == -1) { panic("could not get file status for mmap"); }
+
+    void* mapped = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fileno(file->handle), 0);
+    if (mapped == MAP_FAILED) { panic("could not map file to memory"); }
+
+    return string_view_from_slice(slice_new(mapped, st.st_size));
+}
+
+// unmaps a memory-mapped view.
+void
+file_unmap(struct File* file, struct String_View view)
+{
+    (void)file;
+    munmap(string_view_backing(view).data, string_view_length(view));
+}
diff --git a/boot/transpile.c b/boot/transpile.c
index bebcbbc..25a3d79 100644
--- a/boot/transpile.c
+++ b/boot/transpile.c
@@ -65,13 +65,19 @@ transpiler_visit_type_node(struct Visit* visit, struct Type_Node* node)
     case TYPE_NODE_NAME: {
         struct String name = node->value.name.name;
         if (strcmp(name.data, "int") == 0) {
-            fprintf(transpiler->output, "int64_t");
+            fprintf(transpiler->output, "integer");
         } else if (strcmp(name.data, "string") == 0) {
-            fprintf(transpiler->output, "const char*");
+            fprintf(transpiler->output, "struct String");
         } else if (strcmp(name.data, "bool") == 0) {
             fprintf(transpiler->output, "bool");
         } else if (strcmp(name.data, "float") == 0) {
-            fprintf(transpiler->output, "double");
+            fprintf(transpiler->output, "real");
+        } else if (strcmp(name.data, "uint") == 0) {
+            fprintf(transpiler->output, "uint");
+        } else if (strcmp(name.data, "byte") == 0) {
+            fprintf(transpiler->output, "byte");
+        } else if (strcmp(name.data, "ascii") == 0) {
+            fprintf(transpiler->output, "ascii");
         } else {
             fprintf(transpiler->output, "%.*s", (int)name.length, name.data);
         }
@@ -369,10 +375,9 @@ transpiler_visit_tree(struct Visit* visit, struct Tree* tree)
 {
     TRANSPILER_PREAMBLE
 
-    // include some standard c headers
-    // catskill should have it's own standard library,
-    // but until bootstrapping is complete, we will just use a
-    // normal libc (preferably musl).
+    // include the catskill runtime core library
+    // which provides all the necessary types and functions
+    // for transpiled catskill programs.
     // other headers can be included by the user
     // with the pragma `| c-header "header.h"`.
     fprintf(transpiler->output, "#include <stdio.h>\n");