From: David Kerkeslager Date: Sat, 28 Sep 2019 19:39:05 +0000 (-0400) Subject: Generate a C program (which has a memory error after completion, but still) X-Git-Url: https://code.kerkeslager.com/?p=fur;a=commitdiff_plain;h=c45c61444aae3df328e4dc84acf7e31e58a1f64e Generate a C program (which has a memory error after completion, but still) --- diff --git a/c_generation.py b/c_generation.py new file mode 100644 index 0000000..6dd4551 --- /dev/null +++ b/c_generation.py @@ -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, + ) diff --git a/conversion.py b/conversion.py index 2b6ccbd..04cbdab 100644 --- a/conversion.py +++ b/conversion.py @@ -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) diff --git a/crossplatform_ir_generation.py b/crossplatform_ir_generation.py index 7f8a6ec..09079cd 100644 --- a/crossplatform_ir_generation.py +++ b/crossplatform_ir_generation.py @@ -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 --- 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 index 0000000..6e21ce0 --- /dev/null +++ b/templates/program2.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include +#include + +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 index 0000000..bf9c7a5 --- /dev/null +++ b/templates/stack.c @@ -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; +}