From 570553aadb56557255f06ff04040588b7aec5044 Mon Sep 17 00:00:00 2001 From: Bram van den Heuvel Date: Fri, 21 Feb 2025 15:39:49 +0100 Subject: [PATCH] Initial proof-of-concept --- README.md | 5 + low.c | 476 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 481 insertions(+) create mode 100644 README.md create mode 100644 low.c diff --git a/README.md b/README.md new file mode 100644 index 0000000..3777fcf --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# New programming language + +This custom programming language uses a custom design containing various technologies and optimizations. + +It is a proof-of-concept. diff --git a/low.c b/low.c new file mode 100644 index 0000000..1c25ac5 --- /dev/null +++ b/low.c @@ -0,0 +1,476 @@ +#include +#include +#include +#include + +// Structures defined in this file +typedef struct BlockHeader BlockHeader; +typedef struct Closure Closure; +typedef struct Frame Frame; +typedef struct Text Text; +typedef struct UnionType UnionType; +typedef struct Var Var; + +#define MEM_SIZE 200//1024 * 1024 // 1 MB + +#define EXIT_GRACEFULLY 0 +#define EXIT_MEMORY_LIMIT_TOO_LOW 11 +#define EXIT_COULD_NOT_INIT_MEMORY 12 +#define EXIT_OUT_OF_MEMORY_DURING_INITIALIZATION 13 + +#define EXIT_IMPOSSIBLE_FUNCTION_PHASE 77 +#define EXIT_IMPOSSIBLE_FUNCTION_ID 78 +#define EXIT_IMPOSSIBLE_FUNCTION_NO_CONTINUATION 79 + +#define EXIT_IF_NULL(ptr) ({ \ + if (!ptr) { \ + free(memory); \ + printf("Failed to initialize program: out of memory\n"); \ + exit(EXIT_OUT_OF_MEMORY_DURING_INITIALIZATION); \ + } \ +}) + +/* ============================================================================ + * MEMORY + * ============================================================================ + */ + +// Memory block structure +// This block functions as a header for memory allocations on the heap +typedef struct BlockHeader { + size_t size; + int free; + struct BlockHeader *next; +} BlockHeader; + +// Global memory pool +static uint8_t *memory = NULL; +static uintptr_t heap_base; +static uintptr_t heap_top; +static uintptr_t stack_base; +static uintptr_t stack_top; + +// Initialize the custom memory manager +void init_memory() { + if (MEM_SIZE < sizeof(BlockHeader) + 1) { + printf("Memory limit is too low!\n"); + exit(EXIT_MEMORY_LIMIT_TOO_LOW); + } + + memory = (uint8_t *)malloc(MEM_SIZE); + if (!memory) { + printf("Memory allocation failed!\n"); + exit(EXIT_COULD_NOT_INIT_MEMORY); + } + heap_base = (uintptr_t)memory; + heap_top = heap_base; + stack_base = (uintptr_t)(memory + MEM_SIZE); + stack_top = stack_base; + + BlockHeader *curr = (BlockHeader *)heap_base; + curr->free = 1; + curr->size = MEM_SIZE - sizeof(BlockHeader); + curr->next = NULL; +} + +// Macro for allocating a number of bytes on the heap +#define HEAP_ALLOC(nbytes) ({ \ + void *ptr = NULL; \ + BlockHeader *curr = (BlockHeader *)heap_base; \ + while(curr) { \ + if (curr->free && curr->size >= (nbytes)) { \ + /* If there's enough memory left over, split off into two */ \ + if (curr->size >= (nbytes) + sizeof(BlockHeader) + 1) { \ + BlockHeader *new_block = (BlockHeader *)( \ + (uint8_t *)curr + sizeof(BlockHeader) + (nbytes) \ + ); \ + new_block->size = curr->size - (nbytes) - sizeof(BlockHeader); \ + new_block->free = 1; \ + new_block->next = curr->next; \ + curr->size = (nbytes); \ + curr->next = new_block; \ + } \ + /* Assign block to new memory */ \ + curr->free = 0; \ + ptr = (uint8_t *)curr + sizeof(BlockHeader); \ + break; \ + } \ + /* Otherwise, go to the next block */ \ + curr = curr->next; \ + } \ + ptr; \ +}) + +// Macro for freeing bytes at a given address on the heap +#define HEAP_FREE(ptr) ({ \ + if (ptr) { \ + BlockHeader *block = (BlockHeader *)( \ + (uint8_t *)(ptr) - sizeof(BlockHeader) \ + ); \ + block->free = 1; \ + /* Merge adjacent free blocks */ \ + BlockHeader *curr = (BlockHeader *)heap_base; \ + while (curr) { \ + if (curr->free && curr->next && curr->next->free) { \ + curr->size += sizeof(BlockHeader) + curr->next->size; \ + curr->next = curr->next->next; \ + } else { \ + curr = curr->next; \ + } \ + } \ + } \ +}) + +// Pointer for debugging: print the heap to stdout +#define HEAP_PRINT_WIDTH 60 +#define HEAP_PRINT() ({ \ + BlockHeader *curr = (BlockHeader *)heap_base; \ + while (curr) { \ + printf("BlockHeader at address %ld (physical %p): %lu byte", \ + (uint8_t *)curr - memory, curr, curr->size \ + ); \ + if (curr->size == 1) { printf(" of "); } else { printf("s of "); } \ + if (curr->free) { \ + printf("free memory\n "); \ + } else { \ + printf("reserved memory\n "); \ + } \ + unsigned char *content = (uint8_t *)curr; \ + for (int i = 0; i < curr->size + sizeof(BlockHeader); i++) { \ + if (i == sizeof(BlockHeader)) { printf("| "); } \ + printf("%02X ", content[i]); \ + if (i > HEAP_PRINT_WIDTH) { printf("..."); break; } \ + } \ + printf("\n "); \ + for (int i = 0; i < curr->size + sizeof(BlockHeader); i++) { \ + if (i == sizeof(BlockHeader)) { printf("| "); } \ + if (content[i] == 0) { printf(" "); \ + } else if (content[i] >= 32 && content[i] < 127) { \ + printf("%c ", content[i]); \ + } else { printf("__ "); } \ + if (i > HEAP_PRINT_WIDTH) { printf("..."); break; } \ + } \ + printf("\n"); \ + curr = curr->next; \ + } \ +}) + +#define SPACE_FILL(n) for (int i=0; i= heap_top + (nbytes)) { \ + stack_top -= (nbytes); \ + ptr = (void *)stack_top; \ + } \ + ptr; \ +}) + +#define STACK_PRINT_VARIABLE_MAX 5 +#define STACK_PRINT(topPtr) ({ \ + Frame *cursor = topPtr; \ + while (cursor) { \ + Frame *next = cursor->cont; \ + size_t size; \ + if (next) { \ + size = (uint8_t *)(next) - (uint8_t *)(cursor); \ + } else { \ + size = (uint8_t *)(stack_base) - (uint8_t *)(cursor); \ + } \ + printf("Stack layer at offset -%ld (phsyical %p): %ld byte", \ + (uint8_t *)(stack_base) - (uint8_t *)(cursor), cursor, size \ + ); \ + if (size == 1) { printf("\n "); } else { printf("s\n "); } \ + unsigned char *content = (uint8_t *)cursor; \ + printf("| Code ID"); SPACE_FILL(sizeof(CodeID)*3 - 7 - 1); \ + printf("| Phase"); SPACE_FILL(sizeof(int)*3 - 5 - 2); \ + printf("| Prev out"); SPACE_FILL(sizeof(Var *)*3-8 - 2); \ + printf("| Continuation"); SPACE_FILL(sizeof(Frame *)*3-12 - 1); \ + for (int i = 0; i < size - sizeof(Frame); i++) { \ + printf("| Variable %d", i + 2); SPACE_FILL(2); \ + if (i + 2 == STACK_PRINT_VARIABLE_MAX) { break; } \ + } \ + printf("|\n | "); \ + for (int i = 0; i < sizeof(Frame); i++) { \ + printf("%02X ", content[i]); \ + } \ + for (int i = sizeof(Frame); i < size; i++) { \ + if ((i - sizeof(Frame)) % sizeof(Var) == 0) { printf("|"); } \ + printf("%02X ", content[i]); \ + if (i - sizeof(Frame) == STACK_PRINT_VARIABLE_MAX * sizeof(Var)) { \ + break; \ + } \ + } \ + printf("|\n"); \ + cursor = cursor->cont; \ + } \ +}) +// #define STACK_PRINT ({ \ +// printf("Stack from base %p to top %p:\n", (void *)stack_base, (void *)stack_top); \ +// uint8_t *curr = (uint8_t *)stack_top; \ +// while ((uintptr_t)curr < stack_base) { \ +// printf("Address %ld (physical %p): \n ", curr - memory, curr); \ +// for (int i = 0; i < STACK_PRINT_WIDTH && (uintptr_t)(curr + i) < stack_base; i++) { \ +// printf("%02X ", curr[i]); \ +// } \ +// printf("\n "); \ +// for (int i = 0; i < STACK_PRINT_WIDTH && (uintptr_t)(curr + i) < stack_base; i++) { \ +// if (curr[i] == 0) { \ +// printf(" "); \ +// } else if (curr[i] >= 32 && curr[i] < 127) { \ +// printf("%c ", curr[i]); \ +// } else { \ +// printf("__ "); \ +// } \ +// } \ +// printf("\n"); \ +// curr += STACK_PRINT_WIDTH; \ +// } \ +// }) + + +// Macro for freeing a number of bytes on the stack +#define STACK_FREE(nbytes) ({ \ + stack_top += (nbytes); \ +}) + + +/* ============================================================================ + * VARIABLES & VALUES + * ============================================================================ + */ + +// Types of values that can be stored in the heap +typedef union { + int i; + char c; + Closure *f; + Text *t; + UnionType *u; +} Value; + +#define HEAP_ALLOC_VALUE() HEAP_ALLOC(sizeof(Value)) + +// Types of variables stored in a frame +typedef enum { + ORIGINAL_VARIABLE, + MUTABLE_REFERENCE, + IMMUTABLE_REFERENCE +} VarType; + +// Variable +typedef struct Var { + VarType var_type; + Value *value; +} Var; + +#define HEAP_ALLOC_VARIABLE() HEAP_ALLOC(sizeof(Var)) + +// Names of functions +typedef enum { + CODE_F, + CODE_G, + CODE_H, + CODE_EXIT // Indicates termination +} CodeID; + +// A closure can represent a function that has yet to gather all inputs +typedef struct Closure { + CodeID code_id; + Var variables[]; +} Closure; + +// A Text struct represents a string +typedef struct Text { + size_t len; + char s[]; +} Text; + +// A Union type represents a type that can be one of several values. +// For example: +// type Maybe a = Just a | Nothing +typedef struct UnionType { + int name; + Value values[]; +} UnionType; + +#define HEAP_ALLOC_CLOSURE(code_id, arity) ({ \ + Closure *c = HEAP_ALLOC(sizeof(Closure) + arity * sizeof(Var)); \ + if (c) { \ + c->code_id = code_id; \ + } \ + c; \ +}) + +#define HEAP_ALLOC_TEXT(length) ({ \ + Text *t = HEAP_ALLOC(sizeof(Text) + length * sizeof(char)); \ + if (t) { \ + t->len = length; \ + for (int i = 0; i < length; i++) { \ + t->s[i] = '\0'; \ + } \ + } \ + t; \ +}) + +#define HEAP_ALLOC_UNION_TYPE(name, arity) ({ \ + UnionType *u = HEAP_ALLOC(sizeof(UnionType) + arity * sizeof(Value)); \ + if (u) { \ + u->name = name; \ + } \ + u; \ +}) + +// Function arity +#define ARITY_F 1 +#define ARITY_G 1 +#define ARITY_H 1 +#define ARITY_EXIT 1 + +typedef struct Frame { + // Function to execute + CodeID code_id; + + // Phase of the function + int phase; + + // Return variable from previous frame + Var *prevOut; + + // CPS-style next frame to execute + struct Frame *cont; + + // Previously captured variables + Var variables[]; +} Frame; + +#define FRAME_SIZE(arity) sizeof(Frame) + (arity - 1) * sizeof(Var) + +#define STACK_ALLOC_FRAME(code_name, arity) ({ \ + Frame *f = STACK_ALLOC(FRAME_SIZE(arity)); \ + if(f) { \ + f->code_id = code_name; \ + f->phase = 0; \ + f->prevOut = NULL; \ + f->cont = NULL; \ + } \ + f; \ +}) + +#define STACK_FREE_FRAME(arity) STACK_FREE(FRAME_SIZE(arity)) + +int main(void) { + init_memory(); + + Text *t = HEAP_ALLOC_TEXT(50); EXIT_IF_NULL(t); + Value *v = HEAP_ALLOC_VALUE(); EXIT_IF_NULL(v); + Var *var = HEAP_ALLOC_VARIABLE(); EXIT_IF_NULL(var); + Frame *fnsh = STACK_ALLOC_FRAME(CODE_EXIT, ARITY_EXIT); EXIT_IF_NULL(fnsh); + Frame *curr = STACK_ALLOC_FRAME(CODE_H, ARITY_H); EXIT_IF_NULL(curr); + + sprintf(t->s, "Kaokkokos shall prevail by the hands of Alkbaard!"); + + v->t = t; + var->var_type = ORIGINAL_VARIABLE; + var->value = v; + curr->prevOut = var; + curr->cont = fnsh; + + // STACK_PRINT(curr); + + while (curr) { + switch (curr->code_id) { + case CODE_F: { + // f(x) = print(x) and return x + Var *x = curr->prevOut; + Frame *next = curr->cont; + STACK_FREE_FRAME(ARITY_F); + + if (x) { + printf("%s\n", x->value->t->s); + } else { + printf("NULL: Out of memory!\n"); + } + + curr = next; + break; + } + case CODE_G: { + // g(x) = String.upper(x) + Var *x = curr->prevOut; + Frame *next = curr->cont; + STACK_FREE_FRAME(ARITY_G); + + if (x) { + for (int i = 0; i < x->value->t->len; i++) { + char c = x->value->t->s[i]; + if (c >= 'a' && c <= 'z') { + x->value->t->s[i] = c - ('a' - 'A'); + } + } + + next->prevOut = x; + } + + curr = next; + break; + } + case CODE_H : { + // h(x) = f(g(x)) + Var *x = curr->prevOut; + Frame *next = curr->cont; + STACK_FREE_FRAME(ARITY_H); + + if (x) { + Frame *f = STACK_ALLOC_FRAME(CODE_F, ARITY_F); + + if (f) { + Frame *g = STACK_ALLOC_FRAME(CODE_G, ARITY_G); + + if (g) { + f->cont = next; + + g->prevOut = x; + g->cont = f; + curr = g; + } else { + // No memory available! Free f & exit this function + STACK_FREE_FRAME(ARITY_F); + next->prevOut = NULL; + curr = next; + } + } else { + // No memory available! Exit this function + next->prevOut = NULL; + curr = next; + } + } else { + // prevOut is NULL - we were out of memory! + curr = next; + } + + break; + } + case CODE_EXIT: { + // HEAP_PRINT(); + free(memory); + exit(EXIT_GRACEFULLY); + break; + } + default: { + free(memory); + printf("ERROR: Reached impossible function id!\n"); + exit(EXIT_IMPOSSIBLE_FUNCTION_ID); + break; + } + } + } + + free(memory); + + printf("ERROR: Reached function with no continuation!\n"); + + return EXIT_IMPOSSIBLE_FUNCTION_NO_CONTINUATION; +} +