X-Git-Url: https://code.kerkeslager.com/?a=blobdiff_plain;f=templates%2Fprogram.c;h=25bb41348abf3a3454cad00da4c08e6cf51aca44;hb=151f60b119247efb1bcf05a664f4324b71fac782;hp=0cab0c8f45f99389608ee7b3d20c9746aad6a7b7;hpb=d8fdecee02795cb0372627208c4f0a52ae7814f9;p=fur diff --git a/templates/program.c b/templates/program.c index 0cab0c8..25bb413 100644 --- a/templates/program.c +++ b/templates/program.c @@ -4,6 +4,20 @@ #include #include +/* Some terminology used in function names: + * - initialize: These functions take a pointer and potentially some other arguments, and use those + * to initialize the value pointed to by self. Initialize functions DO NOT allocate the function, + * so they can be used to initialize stack-allocated variables. + * - construct: This allocates a value for a pointer, initializes it, and returns it. This is for + * heap-allocated values. It may be as simple as allocating the memory, calling an initialize, and + * returning it. + * - deinitialize: These functions dereference or free any objects pointed to by the self pointer's + * value, but they don't actually free the self pointer. This is useful for stack-allocated objects + * which point to heap-allocated objects. + * - destruct: This dereferences or frees memory pointed to by the self argument, and all the + * pointers on the self argument. + */ + {% for standard_library in standard_libraries %} #include <{{standard_library}}> {% endfor %} @@ -18,6 +32,8 @@ struct EnvironmentNode; typedef struct EnvironmentNode EnvironmentNode; struct Environment; typedef struct Environment Environment; +struct EnvironmentPool; +typedef struct EnvironmentPool EnvironmentPool; const char* const STRING_LITERAL_LIST[] = { {% for string_literal in string_literal_list %} @@ -36,15 +52,40 @@ enum Type BOOLEAN, CLOSURE, INTEGER, - STRING + LIST, + STRING_CONCATENATION, + STRING_LITERAL, + VOID +}; + +struct Closure; +typedef struct Closure Closure; +struct Closure +{ + Environment* closed; + Object (*call)(EnvironmentPool*, Environment*, size_t, Object*); +}; + +struct List; +typedef struct List List; +struct List +{ + size_t allocated; + size_t length; + Object* items; }; +struct StringConcatenation; +typedef struct StringConcatenation StringConcatenation; + union Instance { bool boolean; - Object (*closure)(Environment*, size_t, Object*); + Closure closure; int32_t integer; - const char* string; + List list; + StringConcatenation* string_concatenation; + const char* string_literal; }; struct Object @@ -53,16 +94,49 @@ struct Object Instance instance; }; -const Object TRUE = { - BOOLEAN, - true -}; +const Object builtin$true = { BOOLEAN, (Instance)(bool){ true } }; +const Object builtin$false = { BOOLEAN, (Instance)(bool){ false } }; +const Object builtin$nil = { VOID, { 0 } }; -const Object FALSE = { - BOOLEAN, - false +struct StringConcatenation +{ + size_t referenceCount; + Object left; + Object right; }; +Object List_construct(size_t allocate) +{ + Object* items = malloc(sizeof(Object) * allocate); + Object result = { LIST, (Instance)(List){ allocate, 0, items } }; + return result; +} + +void List_append(Object* list, Object item) +{ + assert(list->type == LIST); + + if(list->instance.list.allocated == list->instance.list.length) + { + list->instance.list.allocated *= 2; + list->instance.list.items = realloc( + list->instance.list.items, + sizeof(Object) * list->instance.list.allocated + ); + } + + list->instance.list.items[list->instance.list.length] = item; + list->instance.list.length++; +} + +Object List_get(Object* list, Object index) +{ + assert(list->type == LIST); + assert(index.type == INTEGER); + + return list->instance.list.items[index.instance.integer]; +} + struct EnvironmentNode { const char* key; @@ -72,6 +146,9 @@ struct EnvironmentNode struct Environment { + bool mark; + bool live; + Environment* parent; EnvironmentNode* root; }; @@ -80,6 +157,49 @@ void Environment_initialize(Environment* self, Environment* parent) { self->parent = parent; self->root = NULL; + + // We are currently only ever initializing environments at the beginning of running functions, so + // for now at least we can assume that we want it to be live immediately. + self->live = true; +} + +void Object_deinitialize(Object* self) +{ + switch(self->type) + { + case BOOLEAN: + break; + case CLOSURE: + break; + case INTEGER: + break; + case STRING_LITERAL: + break; + case VOID: + break; + + case LIST: + for(size_t i = 0; i < self->instance.list.length; i++) { + Object_deinitialize(&(self->instance.list.items[i])); + } + + free(self->instance.list.items); + break; + + case STRING_CONCATENATION: + self->instance.string_concatenation->referenceCount--; + + if(self->instance.string_concatenation->referenceCount == 0) + { + Object_deinitialize(&(self->instance.string_concatenation->left)); + Object_deinitialize(&(self->instance.string_concatenation->right)); + free(self->instance.string_concatenation); + } + break; + + default: + assert(false); + } } void Environment_deinitialize(Environment* self) @@ -87,12 +207,46 @@ void Environment_deinitialize(Environment* self) EnvironmentNode* next; for(EnvironmentNode* node = self->root; node != NULL; node = next) { - // No objects are allocated on the heap (yet!) so we don't need to free anything else next = node->next; + Object_deinitialize(&(node->value)); free(node); } } +void Environment_setLive(Environment* self, bool live) +{ + self->live = live; +} + +void Environment_mark(Environment* self) +{ + if(self == NULL) return; + if(self->mark) return; // Prevents infinite recursion in the case of cycles + + self->mark = true; + + Environment_mark(self->parent); + + for(EnvironmentNode* node = self->root; node != NULL; node = node->next) + { + switch(node->value.type) + { + case BOOLEAN: + case INTEGER: + case STRING_LITERAL: + case VOID: + break; + + case CLOSURE: + Environment_mark(node->value.instance.closure.closed); + break; + + default: + assert(false); + } + } +} + // This need not be thread safe because environments exist on one thread only void Environment_set(Environment* self, const char* const key, Object value) { @@ -123,162 +277,210 @@ Object Environment_get(Environment* self, const char* const symbol) assert(false); } -Object integerLiteral(int32_t literal) +# define POOL_SIZE 64 +struct EnvironmentPool { - Object result; - result.type = INTEGER; - result.instance.integer = literal; - return result; -} + int8_t freeIndex; + bool allocatedFlags[POOL_SIZE]; + Environment environments[POOL_SIZE]; + EnvironmentPool* overflow; +}; -Object stringLiteral(const char* literal) +EnvironmentPool* EnvironmentPool_construct(); +void EnvironmentPool_initialize(EnvironmentPool*); +void EnvironmentPool_deinitialize(EnvironmentPool*); +void EnvironmentPool_destruct(EnvironmentPool*); + +EnvironmentPool* EnvironmentPool_construct() { - Object result; - result.type = STRING; - result.instance.string = literal; + EnvironmentPool* result = malloc(sizeof(EnvironmentPool)); + EnvironmentPool_initialize(result); return result; } -// TODO Make this conditionally added -Object operator$negate(Object input) +void EnvironmentPool_initialize(EnvironmentPool* self) { - assert(input.type == INTEGER); + self->overflow = NULL; + self->freeIndex = 0; - Object result; - result.type = INTEGER; - result.instance.integer = -input.instance.integer; - return result; + for(size_t i = 0; i < POOL_SIZE; i++) + { + self->allocatedFlags[i] = false; + self->environments[i].live = false; + } } -Object operator$add(Object left, Object right) +void EnvironmentPool_deinitialize(EnvironmentPool* self) { - assert(left.type == INTEGER); - assert(right.type == INTEGER); + // We can assume if this is being called, none of the Environments are live + for(int8_t i = 0; i < POOL_SIZE; i++) + { + if(self->allocatedFlags[i]) Environment_deinitialize(&(self->environments[i])); + } - Object result; - result.type = INTEGER; - result.instance.integer = left.instance.integer + right.instance.integer; - return result; + EnvironmentPool_destruct(self->overflow); } -Object operator$subtract(Object left, Object right) +void EnvironmentPool_destruct(EnvironmentPool* self) { - assert(left.type == INTEGER); - assert(right.type == INTEGER); - - Object result; - result.type = INTEGER; - result.instance.integer = left.instance.integer - right.instance.integer; - return result; + if(self == NULL) return; + EnvironmentPool_deinitialize(self); + free(self); } -Object operator$multiply(Object left, Object right) +void EnvironmentPool_GC(EnvironmentPool* self) { - assert(left.type == INTEGER); - assert(right.type == INTEGER); + // Unmark all the environments + for(EnvironmentPool* current = self; current != NULL; current = current->overflow) + { + for(int8_t i = 0; i < POOL_SIZE; i++) + { + current->environments[i].mark = false; + } + } - Object result; - result.type = INTEGER; - result.instance.integer = left.instance.integer * right.instance.integer; - return result; + // Mark live enviroments and environments referenced by live environments + for(EnvironmentPool* current = self; current != NULL; current = current->overflow) + { + for(int8_t i = 0; i < POOL_SIZE; i++) + { + if(current->environments[i].live) + { + Environment_mark(&(current->environments[i])); + } + } + } + + // TODO We never free pools until the very end--we could free a pool if two pools are empty + for(EnvironmentPool* current = self; current != NULL; current = current->overflow) + { + for(int8_t i = POOL_SIZE - 1; i >= 0; i--) + { + if(!current->environments[i].mark && current->allocatedFlags[i]) + { + Environment_deinitialize(&(current->environments[i])); + current->allocatedFlags[i] = false; + current->freeIndex = i; + } + } + } } -Object operator$integerDivide(Object left, Object right) +Environment* EnvironmentPool_allocate(EnvironmentPool* self) { - assert(left.type == INTEGER); - assert(right.type == INTEGER); + for(EnvironmentPool* current = self; current != NULL; current = current->overflow) + { + for(; current->freeIndex < POOL_SIZE; current->freeIndex++) + { + if(!current->allocatedFlags[current->freeIndex]) + { + current->allocatedFlags[current->freeIndex] = true; + return &(current->environments[current->freeIndex]); + } + } + } - Object result; - result.type = INTEGER; - result.instance.integer = left.instance.integer / right.instance.integer; - return result; -} + EnvironmentPool_GC(self); -Object operator$modularDivide(Object left, Object right) -{ - assert(left.type == INTEGER); - assert(right.type == INTEGER); + EnvironmentPool* previous; + for(EnvironmentPool* current = self; current != NULL; current = current->overflow) + { + for(; current->freeIndex < POOL_SIZE; current->freeIndex++) + { + if(!current->allocatedFlags[current->freeIndex]) + { + current->allocatedFlags[current->freeIndex] = true; + return &(current->environments[current->freeIndex]); + } + else + { + previous = current; + } + } + } - Object result; - result.type = INTEGER; - result.instance.integer = left.instance.integer % right.instance.integer; - return result; + previous->overflow = EnvironmentPool_construct(); + return EnvironmentPool_allocate(previous->overflow); } -Object operator$equals(Object left, Object right) +Object integerLiteral(int32_t literal) { - assert(left.type == INTEGER); - assert(right.type == INTEGER); - - Object result = { BOOLEAN, left.instance.integer == right.instance.integer }; + Object result; + result.type = INTEGER; + result.instance.integer = literal; return result; } -Object operator$notEquals(Object left, Object right) +Object stringLiteral(const char* literal) { - assert(left.type == INTEGER); - assert(right.type == INTEGER); - - Object result = { BOOLEAN, left.instance.integer != right.instance.integer }; + Object result; + result.type = STRING_LITERAL; + result.instance.string_literal = literal; return result; } -Object operator$greaterThan(Object left, Object right) +// TODO Make this conditionally added +Object operator$negate(Object input) { - assert(left.type == INTEGER); - assert(right.type == INTEGER); + assert(input.type == INTEGER); - Object result = { BOOLEAN, left.instance.integer > right.instance.integer }; + Object result; + result.type = INTEGER; + result.instance.integer = -input.instance.integer; return result; } -Object operator$lessThan(Object left, Object right) +// TODO Make this conditionally added +Object operator$concatenate(Object left, Object right) { - assert(left.type == INTEGER); - assert(right.type == INTEGER); + switch(left.type) { + case STRING_CONCATENATION: + left.instance.string_concatenation->referenceCount++; + break; - Object result = { BOOLEAN, left.instance.integer < right.instance.integer }; - return result; -} + case STRING_LITERAL: + break; -Object operator$greaterThanOrEqual(Object left, Object right) -{ - assert(left.type == INTEGER); - assert(right.type == INTEGER); + default: + assert(false); + } - Object result = { BOOLEAN, left.instance.integer >= right.instance.integer }; - return result; -} + switch(right.type) { + case STRING_CONCATENATION: + right.instance.string_concatenation->referenceCount++; + break; -Object operator$lessThanOrEqual(Object left, Object right) -{ - assert(left.type == INTEGER); - assert(right.type == INTEGER); + case STRING_LITERAL: + break; - Object result = { BOOLEAN, left.instance.integer <= right.instance.integer }; - return result; -} + default: + assert(false); + } -Object operator$and(Object left, Object right) -{ - assert(left.type == BOOLEAN); - assert(right.type == BOOLEAN); + StringConcatenation* concatenation = malloc(sizeof(StringConcatenation)); + concatenation->referenceCount = 1; + concatenation->left = left; + concatenation->right = right; - Object result = { BOOLEAN, left.instance.boolean && right.instance.boolean }; + Object result = { STRING_CONCATENATION, (Instance)concatenation }; return result; } -Object operator$or(Object left, Object right) +{% for id in infix_declarations %} +Object operator${{ id.name }}(Object left, Object right) { - assert(left.type == BOOLEAN); - assert(right.type == BOOLEAN); + assert(left.type == {{ id.in_type.upper() }}); + assert(right.type == {{ id.in_type.upper() }}); - Object result = { BOOLEAN, left.instance.boolean || right.instance.boolean }; + Object result; + result.type = {{ id.out_type.upper() }}; + result.instance.{{ id.out_type.lower() }} = left.instance.{{ id.in_type.lower() }} {{ id.operator }} right.instance.{{ id.in_type.lower() }}; return result; } +{% endfor %} {% if 'pow' in builtins %} -Object builtin$pow$implementation(Environment* parent, size_t argc, Object* args) +Object builtin$pow$implementation(EnvironmentPool* environmentPool, Environment* parent, size_t argc, Object* args) { assert(argc == 2); @@ -294,11 +496,11 @@ Object builtin$pow$implementation(Environment* parent, size_t argc, Object* args return result; } -Object builtin$pow = { CLOSURE, (Instance)builtin$pow$implementation }; +Object builtin$pow = { CLOSURE, (Instance)(Closure){ NULL, builtin$pow$implementation } }; {% endif %} {% if 'print' in builtins %} -Object builtin$print$implementation(Environment* parent, size_t argc, Object* args) +Object builtin$print$implementation(EnvironmentPool* environmentPool, Environment* parent, size_t argc, Object* args) { for(size_t i = 0; i < argc; i++) { @@ -309,60 +511,61 @@ Object builtin$print$implementation(Environment* parent, size_t argc, Object* ar fputs(output.instance.boolean ? "true" : "false", stdout); break; + case CLOSURE: + // TODO Print something better + printf(""); + break; + case INTEGER: printf("%" PRId32, output.instance.integer); break; - case STRING: + case STRING_CONCATENATION: + builtin$print$implementation(NULL, NULL, 1, &(output.instance.string_concatenation->left)); + builtin$print$implementation(NULL, NULL, 1, &(output.instance.string_concatenation->right)); + break; + + case STRING_LITERAL: // Using fwrite instead of printf to handle size_t length - printf("%s", output.instance.string); + printf("%s", output.instance.string_literal); + break; + + case VOID: + printf("nil"); break; default: assert(false); } + Object_deinitialize(&output); } // TODO Return something better - return FALSE; + return builtin$false; } -Object builtin$print = { CLOSURE, (Instance)builtin$print$implementation }; +Object builtin$print = { CLOSURE, (Instance)(Closure){ NULL, builtin$print$implementation } }; {% endif %} - {% for function_definition in function_definition_list %} -Object user${{function_definition.name}}$implementation(Environment* parent, size_t argc, Object* args) -{ - Environment environment; - Environment_initialize(&environment, parent); - - {% for statement in function_definition.statement_list[:-1] %} - {{ generate_statement(statement) }} - {% endfor %} - - Object result = {{ generate_statement(function_definition.statement_list[-1]) }} - Environment_deinitialize(&environment); - return result; -} - -Object user${{function_definition.name}} = { CLOSURE, (Instance)user${{function_definition.name}}$implementation }; +{{ function_definition }} {% endfor %} int main(int argc, char** argv) { - Environment environment; - Environment_initialize(&environment, NULL); + EnvironmentPool* environmentPool = EnvironmentPool_construct(); + Environment* environment = EnvironmentPool_allocate(environmentPool); + Environment_initialize(environment, NULL); // TODO Use the symbol from SYMBOL_LIST {% for builtin in builtins %} - Environment_set(&environment, "{{ builtin }}", builtin${{ builtin }}); + Environment_set(environment, "{{ builtin }}", builtin${{ builtin }}); {% endfor %} {% for statement in statements %} - {{ generate_statement(statement) }} + {{ statement }} {% endfor %} - Environment_deinitialize(&environment); - + Environment_setLive(environment, false); + EnvironmentPool_destruct(environmentPool); return 0; }