+ body = indent(generate_function_body(function_declaration.body)),
+ )
+
+PROGRAM_TEMPLATE = string.Template(
+'''
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct Object;
+typedef struct Object Object;
+
+enum Type
+{
+ CELL,
+ STRING,
+ SYMBOL
+};
+typedef enum Type Type;
+
+#define MAX_TYPE_STRING_LENGTH 7
+
+void typeToString(Type type, char* target)
+{
+ switch(type)
+ {
+ case CELL:
+ snprintf(target, MAX_TYPE_STRING_LENGTH, "CELL");
+ return;
+
+ case STRING:
+ snprintf(target, MAX_TYPE_STRING_LENGTH, "STRING");
+ return;
+
+ case SYMBOL:
+ snprintf(target, MAX_TYPE_STRING_LENGTH, "%s", "SYMBOL");
+ return;
+
+ default:
+ fprintf(stderr, "ERROR: Unknown type");
+ exit(1);
+ }
+}
+
+struct Cell;
+typedef struct Cell Cell;
+struct Cell
+{
+ Object* left;
+ Object* right;
+};
+
+struct Environment;
+typedef struct Environment Environment;
+struct Environment
+{
+ char* key;
+ Object* value;
+ Environment* next;
+};
+
+Environment makeEnvironment(char* key, Object* value, Environment* next)
+{
+ Environment result;
+ result.key = key;
+ result.value = value;
+ result.next = next;
+ return result;
+}
+
+Environment* makeEnvironmentPointerFromEnvironment(Environment env)
+{
+ Environment* result = malloc(sizeof(Environment));
+ *result = env;
+ return result;
+}
+
+Environment* makeEnvironmentPointer(char* key, Object* value, Environment* next)
+{
+ return makeEnvironmentPointerFromEnvironment(makeEnvironment(key, value, next));
+}
+
+union Instance
+{
+ Cell cell;
+ char* string;
+ char* symbol;
+};
+typedef union Instance Instance;
+
+Instance makeInstanceFromCell(Cell cell)
+{
+ Instance result;
+ result.cell = cell;
+ return result;
+}
+
+Instance makeInstanceFromString(char* string)
+{
+ Instance result;
+ result.string = string;
+ return result;
+}
+
+Instance makeInstanceFromSymbol(char* symbol)
+{
+ Instance result;
+ result.symbol = symbol;
+ return result;
+}
+
+struct Object
+{
+ Type type;
+ Instance instance;
+};
+
+Object makeObject(Type t, Instance i)
+{
+ Object result;
+ result.type = t;
+ result.instance = i;
+ return result;
+}
+
+Object makeObjectFromCell(Cell cell)
+{
+ return makeObject(CELL, makeInstanceFromCell(cell));
+}
+
+Object makeObjectFromString(char* string)
+{
+ return makeObject(STRING, makeInstanceFromString(string));
+}
+
+Object makeObjectFromSymbol(char* symbol)
+{
+ return makeObject(SYMBOL, makeInstanceFromSymbol(symbol));
+}
+
+Object* makeObjectPointerFromObject(Object o)
+{
+ Object* result = malloc(sizeof(Object));
+ *result = o;
+ return result;
+}
+
+Object* makeObjectPointerFromCell(Cell cell)
+{
+ return makeObjectPointerFromObject(makeObjectFromCell(cell));
+}
+
+Object* makeObjectPointerFromString(char* string)
+{
+ return makeObjectPointerFromObject(makeObjectFromString(string));
+}
+
+Object* makeObjectPointerFromSymbol(char* symbol)
+{
+ return makeObjectPointerFromObject(makeObjectFromSymbol(symbol));
+}
+
+Object* getSymbol(char* symbol)
+{
+ // This will not always be how this is implemented
+ return makeObjectPointerFromSymbol(symbol);
+}
+
+Cell makeCell(Object* left, Object* right)
+{
+ Cell result;
+ result.left = left;
+ result.right = right;
+ return result;
+}
+
+Object* c_cons(Object* left, Object* right)
+{
+ Cell cell = makeCell(left, right);
+ return makeObjectPointerFromCell(cell);
+}
+
+void c_print(Object* stutter_string)
+{
+ if(stutter_string->type != STRING)
+ {
+ char typeName[MAX_TYPE_STRING_LENGTH];
+ typeToString(stutter_string->type, typeName);
+ fprintf(stderr, "ERROR: Expected type STRING, got type %s.", typeName);
+ exit(1);
+ }
+
+ char* c_string = stutter_string->instance.string;
+ printf("%s", c_string);
+}
+
+bool c_symbol_equal(char* left, char* right)
+{
+ return strcmp(left, right) == 0;
+}
+
+Object* c_evaluate_symbol(Environment* env, Object* s)
+{
+ if(env == NULL)
+ {
+ fprintf(stderr, "ERROR: symbol %s not found.", s->instance.symbol);
+ exit(1);
+ }
+
+ if(c_symbol_equal(env->key, s->instance.symbol))
+ {
+ return env->value;
+ }
+
+ return c_evaluate_symbol(env->next, s);
+}
+
+Object* c_evaluate(Environment** env, Object* o)
+{
+ switch(o->type)
+ {
+ case STRING:
+ return o;
+
+ case SYMBOL:
+ return c_evaluate_symbol(*env, o);
+
+ default:
+ break;
+ }
+
+ char typeName[MAX_TYPE_STRING_LENGTH];
+ typeToString(o->type, typeName);
+ fprintf(stderr, "ERROR: Could not evaluate type %s.", typeName);
+ exit(1);
+}
+
+int countArgs(Object* args)
+{
+ if(args == NULL) return 0;
+
+ assert(args->type == CELL);
+ return 1 + countArgs(args->instance.cell.right);
+}
+
+Object* getArg(int index, Object* args)
+{
+ if(index == 0) return args->instance.cell.left;
+
+ return getArg(index - 1, args->instance.cell.right);
+}
+
+void print(Environment** parent, Object* args)
+{
+ assert(countArgs(args) == 1);
+ Object* stutter_string = c_evaluate(parent, getArg(0, args));
+ c_print(stutter_string);
+}
+
+void define(Environment** parent, Object* args)
+{
+ assert(countArgs(args) == 2);
+ Object* name = getArg(0, args);
+ Object* value = c_evaluate(parent, getArg(1, args));
+
+ assert(name->type == SYMBOL);
+ *parent = makeEnvironmentPointer(name->instance.symbol, value, *parent);
+}
+
+int main(int argc, char** argv)
+{
+$body
+}
+'''.strip())
+
+def generate_program(body):
+ return PROGRAM_TEMPLATE.substitute(
+ body = body,