Generate a C program (which has a memory error after completion, but still)
authorDavid Kerkeslager <kerkeslager@gmail.com>
Sat, 28 Sep 2019 19:39:05 +0000 (15:39 -0400)
committerDavid Kerkeslager <kerkeslager@gmail.com>
Mon, 30 Sep 2019 21:58:24 +0000 (17:58 -0400)
c_generation.py [new file with mode: 0644]
conversion.py
crossplatform_ir_generation.py
main.py
templates/program2.c [new file with mode: 0644]
templates/stack.c [new file with mode: 0644]

diff --git a/c_generation.py b/c_generation.py
new file mode 100644 (file)
index 0000000..6dd4551
--- /dev/null
@@ -0,0 +1,66 @@
+import os
+
+import jinja2
+
+import crossplatform_ir_generation
+
+TEMPLATE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')
+
+def separate_labels_and_instructions(entry_list):
+    labels_to_instruction_indices = {}
+    instruction_list = []
+
+    for entry in entry_list:
+        if isinstance(entry, crossplatform_ir_generation.CIRInstruction):
+            instruction_list.append(entry)
+        elif isinstance(entry, crossplatform_ir_generation.CIRLabel):
+            labels_to_instruction_indices[entry.label] = len(instruction_list)
+
+    return labels_to_instruction_indices, tuple(instruction_list)
+
+def generate_integer_argument(argument):
+    assert isinstance(argument, int)
+    return '(int32_t){}'.format(argument)
+
+def generate_null_argument(argument):
+    assert argument is None
+    return 'NULL'
+
+def generate_size_t_argument(argument):
+    assert isinstance(argument, int)
+    return '(size_t){}'.format(argument)
+
+def generate_string_argument(argument):
+    return argument
+
+def generate_symbol_argument(argument):
+    assert argument.startswith('sym(') and argument.endswith(')')
+    return '"{}"'.format(argument[4:-1])
+
+def generate_argument(instruction):
+    try:
+        return {
+            'drop': generate_null_argument,
+            'end': generate_null_argument,
+            'call': generate_size_t_argument,
+            'push': generate_symbol_argument,
+            'push_integer': generate_integer_argument,
+            'push_string': generate_string_argument,
+        }[instruction.instruction](instruction.argument)
+
+    except KeyError:
+        import ipdb; ipdb.set_trace()
+
+def generate(ir):
+    environment = jinja2.Environment(loader=jinja2.FileSystemLoader(TEMPLATE_PATH))
+    template = environment.get_template('program2.c')
+
+    labels_to_instruction_indices, instruction_list = separate_labels_and_instructions(
+        ir.entry_list,
+    )
+
+    return template.render(
+        labels_to_instruction_indices=labels_to_instruction_indices,
+        instruction_list=instruction_list,
+        generate_argument=generate_argument,
+    )
index 2b6ccbd..04cbdab 100644 (file)
@@ -101,14 +101,6 @@ CPSIfElseExpression = collections.namedtuple(
     ),
 )
 
-CPSListAppendStatement = collections.namedtuple(
-    'CPSListAppendStatement',
-    (
-        'list_expression',
-        'item_expression',
-    ),
-)
-
 CPSPushStatement = collections.namedtuple(
     'CPSPushStatement',
     (
@@ -211,13 +203,6 @@ def convert_if_else_expression(statement):
         else_statement_list=else_statement_list,
     )
 
-def convert_list_append_statement(statement):
-    return CPSListAppendStatement(
-        list_expression=convert_expression(statement.list_expression),
-        item_expression=convert_expression(statement.item_expression),
-    )
-
-
 def convert_push_statement(statement):
     return CPSPushStatement(
         expression=convert_expression(statement.expression),
@@ -233,7 +218,6 @@ def convert_statement(statement):
     return {
         normalization.NormalAssignmentStatement: convert_assignment_statement,
         normalization.NormalExpressionStatement: convert_expression_statement,
-        normalization.NormalListAppendStatement: convert_list_append_statement,
         normalization.NormalPushStatement: convert_push_statement,
         normalization.NormalVariableInitializationStatement: convert_variable_initialization_statement,
     }[type(statement)](statement)
index 7f8a6ec..09079cd 100644 (file)
@@ -54,7 +54,7 @@ def generate_function_call_expression(counters, expression):
 def generate_integer_literal_expression(counters, expression):
     referenced_entry_list = ()
     instruction_list = (CIRInstruction(
-        instruction='push_value',
+        instruction='push_integer',
         argument=generate_integer_literal(expression.integer),
     ),)
 
@@ -113,7 +113,7 @@ def generate_list_construct_expression(counters, expression):
 def generate_string_literal_expression(counters, expression):
     referenced_entry_list = ()
     instruction_list = (CIRInstruction(
-        instruction='push_value',
+        instruction='push_string',
         argument=generate_string_literal(expression.string),
     ),)
 
@@ -140,7 +140,7 @@ def generate_symbol_expression(counters, expression):
 def generate_symbol_literal_expression(counters, expression):
     referenced_entry_list = ()
     instruction_list = (CIRInstruction(
-        instruction='push_value',
+        instruction='push_symbol',
         argument=generate_symbol_literal(expression.symbol),
     ),)
 
@@ -301,7 +301,9 @@ def generate(converted):
     return CIRProgram(
         entry_list=flatten(referenced_entry_list_list) + (
             CIRLabel(label='__main__'),
-        ) + flatten(instruction_list_list),
+        ) + flatten(instruction_list_list) + (
+            CIRInstruction(instruction='end', argument=None),
+        )
     )
 
 NO_ARGUMENT_INSTRUCTIONS = set([
diff --git a/main.py b/main.py
index 7deafb8..9ec852a 100644 (file)
--- a/main.py
+++ b/main.py
@@ -3,6 +3,7 @@ import sys
 import conversion
 import crossplatform_ir_generation
 import desugaring
+import c_generation
 import normalization
 import optimization
 import parsing
@@ -19,15 +20,15 @@ desugared = desugaring.desugar(parsed)
 normalized = normalization.normalize(desugared)
 converted = conversion.convert(normalized)
 
-assert source_path.endswith('.fur')
-destination_path = source_path + '.c'
-
-with open(destination_path, 'w') as f:
-    pass
-    #f.write(generated)
-
-# This is the crossplatform IR generation path
 crossplatform_ir = crossplatform_ir_generation.generate(converted)
 optimized = optimization.optimize(crossplatform_ir)
 outputted = crossplatform_ir_generation.output(optimized)
 print(outputted)
+
+generated = c_generation.generate(optimized)
+
+assert source_path.endswith('.fur')
+destination_path = source_path + '.c'
+
+with open(destination_path, 'w') as f:
+    f.write(generated)
diff --git a/templates/program2.c b/templates/program2.c
new file mode 100644 (file)
index 0000000..6e21ce0
--- /dev/null
@@ -0,0 +1,181 @@
+#include<assert.h>
+#include<stdbool.h>
+#include<stdint.h>
+#include<stdio.h>
+#include<stdlib.h>
+#include<string.h>
+
+enum Type;
+typedef enum Type Type;
+enum Type {
+  BUILTIN,
+  INTEGER,
+  STRING
+};
+
+enum Builtin;
+typedef enum Builtin Builtin;
+enum Builtin {
+  NIL,
+  PRINT
+};
+
+union Value;
+typedef union Value Value;
+union Value {
+  Builtin builtin;
+  char* string;
+  int32_t integer;
+};
+
+struct Object;
+typedef struct Object Object;
+struct Object {
+  Type type;
+  Value value;
+};
+
+#define BUILTIN_NIL (Object) { BUILTIN, (Value)(Builtin)NIL }
+
+void Object_deinitialize(Object* self) {
+}
+
+{% include "stack.c" %}
+
+struct Thread;
+typedef struct Thread Thread;
+struct Thread {
+  Stack stack;
+  size_t program_counter;
+};
+
+void Thread_initialize(Thread* self, size_t program_counter) {
+  Stack_initialize(&(self->stack));
+  self->program_counter = program_counter;
+}
+
+void Thread_deinitialize(Thread* self) {
+  Stack_deinitialize(&(self->stack));
+}
+
+union Argument;
+typedef const union Argument Argument;
+union Argument {
+  size_t label;
+  void* pointer;
+  char* string;
+  int32_t integer;
+};
+
+void call(struct Thread* thread, const union Argument argument) {
+  assert(!Stack_isEmpty(&(thread->stack)));
+  Object f = Stack_pop(&(thread->stack));
+
+  switch(f.type) {
+    case BUILTIN:
+      switch(f.value.builtin) {
+        case PRINT:
+          {
+            // TODO Handle multiple arguments
+            assert(!Stack_isEmpty(&(thread->stack)));
+            Object arg = Stack_pop(&(thread->stack));
+
+            switch(arg.type) {
+              case INTEGER:
+                printf("%i", arg.value.integer);
+                break;
+
+              case STRING:
+                printf("%s", arg.value.string);
+                break;
+
+              default:
+                assert(0);
+            }
+
+            Stack_push(&(thread->stack), BUILTIN_NIL);
+          }
+          break;
+
+        default:
+          assert(false);
+      }
+      break;
+
+    default:
+      assert(false);
+  }
+}
+
+void drop(struct Thread* thread, const union Argument argument) {
+  assert(!Stack_isEmpty(&(thread->stack)));
+  Object result = Stack_pop(&(thread->stack));
+  Object_deinitialize(&result);
+}
+
+void end(struct Thread* thread, const union Argument argument) {
+}
+
+void push(struct Thread* thread, const union Argument argument) {
+  char* argumentString = argument.string;
+
+  Object result;
+
+  if(strcmp(argumentString, "print") == 0) {
+    result.type = BUILTIN;
+    result.value.builtin = PRINT;
+  } else {
+    assert(false);
+  }
+
+  Stack_push(&(thread->stack), result);
+}
+
+void push_integer(struct Thread* thread, const union Argument argument) {
+  Object result;
+  result.type = INTEGER;
+  result.value.integer = argument.integer;
+
+  Stack_push(&(thread->stack), result);
+}
+
+void push_string(struct Thread* thread, const union Argument argument) {
+  Object result;
+  result.type = STRING;
+  result.value.string = argument.string;
+
+  Stack_push(&(thread->stack), result);
+}
+
+struct Instruction;
+typedef const struct Instruction Instruction;
+struct Instruction {
+  void (*instruction)(struct Thread*,const union Argument);
+  Argument argument;
+};
+
+{% for label in labels_to_instruction_indices.keys() %}
+#define LABEL_{{ label }} {{ labels_to_instruction_indices[label] }}
+{% endfor %}
+
+const Instruction program[] = {
+{% for instruction in instruction_list %}
+  (Instruction){ {{ instruction.instruction }}, (Argument){{ generate_argument(instruction) }} },
+{% endfor %}
+};
+
+int main() {
+  Thread thread;
+  Thread_initialize(&thread, 0);
+
+  for(; program[thread.program_counter].instruction != end; thread.program_counter++) {
+    program[thread.program_counter].instruction(
+      &thread,
+      program[thread.program_counter].argument
+    );
+  }
+
+  Thread_deinitialize(&thread);
+
+  return 0;
+}
diff --git a/templates/stack.c b/templates/stack.c
new file mode 100644 (file)
index 0000000..bf9c7a5
--- /dev/null
@@ -0,0 +1,47 @@
+struct StackNode;
+typedef struct StackNode StackNode;
+struct StackNode {
+  Object value;
+  StackNode* next;
+};
+
+struct Stack;
+typedef struct Stack Stack;
+struct Stack {
+  StackNode* top;
+};
+
+void Stack_initialize(Stack* self) {
+  self->top = NULL;
+}
+
+bool Stack_isEmpty(Stack* self) {
+  return self->top == NULL;
+}
+
+Object Stack_pop(Stack*);
+
+void Stack_deinitialize(Stack* self) {
+  while(self->top != NULL) {
+    Object o = Stack_pop(self);
+    Object_deinitialize(&o);
+  }
+}
+
+void Stack_push(Stack* self, Object value) {
+  StackNode* node = malloc(sizeof(StackNode));
+  node->value = value;
+  node->next = self->top;
+  self->top = node;
+}
+
+Object Stack_pop(Stack* self) {
+  assert(self->top != NULL);
+
+  StackNode* node = self->top;
+  self->top = node->next;
+
+  Object result = node->value;
+  free(node);
+  return result;
+}