7 {% for standard_library in standard_libraries %}
8 #include <{{standard_library}}>
12 typedef struct String String;
14 typedef enum Type Type;
16 typedef union Instance Instance;
18 typedef struct Object Object;
20 typedef struct Runtime Runtime;
28 #define MAX_SYMBOL_LENGTH {{ MAX_SYMBOL_LENGTH }}
30 typedef struct Symbol Symbol;
34 char name[MAX_SYMBOL_LENGTH];
55 struct EnvironmentNode;
56 typedef struct EnvironmentNode EnvironmentNode;
57 struct EnvironmentNode
61 EnvironmentNode* next;
65 typedef struct Environment Environment;
68 EnvironmentNode* root;
71 Environment* Environment_construct()
73 // TODO Handle malloc returning NULL
74 Environment* result = malloc(sizeof(Environment));
79 void Environment_destruct(Environment* self)
81 EnvironmentNode* next;
82 for(EnvironmentNode* node = self->root; node != NULL; node = next)
84 // We don't need to destruct the keys, because those will be destructed at the end when the Runtime is destructed
85 // We don't need to destruct the permanent strings, because those will be destructed at the end when the Runtime is destructed
86 // The above two comments represent all heap-allocated objects currently, so we don't need to destruct Objects (yet)
92 // This need not be thread safe because environments exist on one thread only
93 void Environment_set(Environment* self, Symbol* key, Object value)
95 EnvironmentNode* node = malloc(sizeof(EnvironmentNode));
98 node->next = self->root;
102 Object Environment_get(Environment* self, Symbol* symbol)
104 for(EnvironmentNode* node = self->root; node != NULL; node = node->next)
106 // We can compare pointers because pointers are unique within Runtime->symbols
107 if(node->key == symbol)
113 // TODO Handle symbol errors
118 // TODO Allocate all symbols and strings as static constants so we can remove the level of indirection
121 size_t permanentStringsLength;
122 size_t permanentStringsAllocated;
123 String** permanentStrings;
124 size_t symbolsLength;
125 size_t symbolsAllocated;
129 Runtime* Runtime_construct()
131 Runtime* result = malloc(sizeof(Runtime));
132 result->permanentStringsLength = 0;
133 result->permanentStringsAllocated = 0;
134 result->permanentStrings = NULL;
135 result->symbolsLength = 0;
136 result->symbolsAllocated =0;
137 result->symbols = NULL;
141 void Runtime_destruct(Runtime* self)
143 for(size_t i = 0; i < self->permanentStringsLength; i++)
145 free(self->permanentStrings[i]);
148 for(size_t i = 0; i < self->symbolsLength; i++)
150 free(self->symbols[i]);
153 free(self->permanentStrings);
158 void Runtime_addPermanentString(Runtime* self, String* string)
160 // TODO Make this function thread-safe
161 if(self->permanentStringsLength == self->permanentStringsAllocated)
163 if(self->permanentStringsAllocated == 0)
165 self->permanentStringsAllocated = 8;
169 self->permanentStringsAllocated = self->permanentStringsAllocated * 2;
172 self->permanentStrings = realloc(
173 self->permanentStrings,
174 sizeof(String*) * self->permanentStringsAllocated
177 // TODO Handle realloc returning NULL
180 self->permanentStrings[self->permanentStringsLength] = string;
181 self->permanentStringsLength++;
184 // TODO Optimize this by sorting the symbols
185 // TODO Make this function thread safe
186 Symbol* Runtime_symbol(Runtime* self, const char* name)
188 assert(strlen(name) <= MAX_SYMBOL_LENGTH);
190 for(size_t i = 0; i < self->symbolsLength; i++)
192 if(strcmp(self->symbols[i]->name, name) == 0)
194 return self->symbols[i];
198 if(self->symbolsLength == self->symbolsAllocated)
200 if(self->symbolsAllocated == 0)
202 self->symbolsAllocated = 8;
206 self->symbolsAllocated = self->symbolsAllocated * 2;
209 self->symbols = realloc(
211 sizeof(Symbol*) * self->symbolsAllocated
214 // TODO Handle realloc returning NULL
217 Symbol* result = malloc(sizeof(Symbol));
218 result->length = strlen(name);
219 strcpy(result->name, name);
221 self->symbols[self->symbolsLength] = result;
222 self->symbolsLength++;
227 Object integerLiteral(int32_t literal)
230 result.type = INTEGER;
231 result.instance.integer = literal;
235 Object stringLiteral(Runtime* runtime, const char* literal)
237 String* resultString = malloc(sizeof(String));
238 resultString->length = strlen(literal);
239 resultString->characters = malloc(resultString->length);
240 memcpy(resultString->characters, literal, resultString->length);
241 Runtime_addPermanentString(runtime, resultString);
244 result.type = STRING;
245 result.instance.string = resultString;
249 // TODO Make this conditionally added
250 Object builtin$negate(Object input)
252 assert(input.type == INTEGER);
255 result.type = INTEGER;
256 result.instance.integer = -input.instance.integer;
260 Object builtin$add(Object left, Object right)
262 assert(left.type == INTEGER);
263 assert(right.type == INTEGER);
266 result.type = INTEGER;
267 result.instance.integer = left.instance.integer + right.instance.integer;
271 Object builtin$subtract(Object left, Object right)
273 assert(left.type == INTEGER);
274 assert(right.type == INTEGER);
277 result.type = INTEGER;
278 result.instance.integer = left.instance.integer - right.instance.integer;
282 Object builtin$multiply(Object left, Object right)
284 assert(left.type == INTEGER);
285 assert(right.type == INTEGER);
288 result.type = INTEGER;
289 result.instance.integer = left.instance.integer * right.instance.integer;
293 Object builtin$integerDivide(Object left, Object right)
295 assert(left.type == INTEGER);
296 assert(right.type == INTEGER);
299 result.type = INTEGER;
300 result.instance.integer = left.instance.integer / right.instance.integer;
304 Object builtin$modularDivide(Object left, Object right)
306 assert(left.type == INTEGER);
307 assert(right.type == INTEGER);
310 result.type = INTEGER;
311 result.instance.integer = left.instance.integer % right.instance.integer;
315 {% if 'pow' in builtins %}
316 Object builtin$pow(Object base, Object exponent)
318 assert(base.type == INTEGER);
319 assert(exponent.type == INTEGER);
322 result.type = INTEGER;
323 result.instance.integer = pow(base.instance.integer, exponent.instance.integer);
328 {% if 'print' in builtins %}
329 void builtin$print(Object output)
334 printf("%" PRId32, output.instance.integer);
338 // Using fwrite instead of printf to handle size_t length
339 fwrite(output.instance.string->characters, 1, output.instance.string->length, stdout);
348 int main(int argc, char** argv)
350 Runtime* runtime = Runtime_construct();
351 Environment* environment = Environment_construct();
353 {% for statement in statements %}
357 Environment_destruct(environment);
358 Runtime_destruct(runtime);