about summary refs log tree commit diff
path: root/boot/transpile.c
diff options
context:
space:
mode:
Diffstat (limited to 'boot/transpile.c')
-rw-r--r--boot/transpile.c191
1 files changed, 132 insertions, 59 deletions
diff --git a/boot/transpile.c b/boot/transpile.c
index b1171d7..05aa5c4 100644
--- a/boot/transpile.c
+++ b/boot/transpile.c
@@ -12,6 +12,82 @@
 
 #include "catboot.h"
 
+// the output of the transpilation pass.
+// used for both file output and string buffer output.
+struct Transpile_Output
+{
+    FILE* file;
+    struct String_Buffer string;
+};
+
+struct Transpile_Output
+transpile_output_from_file(FILE* file)
+{
+    check(file != nil, "transpile output file is nil");
+    return (struct Transpile_Output){
+        .file = file,
+        .string = string_buffer_empty(),
+    };
+}
+
+#define TRANSPILE_OUTPUT_MAX_LENGTH 262144 // 256 KiB
+
+struct Transpile_Output
+transpile_output_from_string(void)
+{
+    return (struct Transpile_Output){
+        .file = nil,
+        .string = string_buffer_new(TRANSPILE_OUTPUT_MAX_LENGTH),
+    };
+}
+
+#define TRANSPILE_OUTPUT_MAX_WRITE_LENGTH 4096
+
+void
+transpile_output_write(struct Transpile_Output* output, const ascii* format, ...)
+{
+    va_list args;
+    va_start(args, format);
+
+    if (output->file) {
+        vfprintf(output->file, format, args);
+    } else {
+        ascii buffer[TRANSPILE_OUTPUT_MAX_WRITE_LENGTH];
+        int written = vsnprintf(buffer, TRANSPILE_OUTPUT_MAX_WRITE_LENGTH, format, args);
+        check(written >= 0 && (uint)written < TRANSPILE_OUTPUT_MAX_LENGTH, "transpile output string buffer overflow");
+
+        string_buffer_append_c_str(&output->string, buffer);
+    }
+
+    va_end(args);
+}
+
+#define TRANSPILE_WRITE(...) \
+    transpile_output_write(&transpiler->output, __VA_ARGS__)
+
+struct String
+transpile_output_string(struct Transpile_Output* output)
+{
+    check(output->file == nil, "transpile output is not a string");
+    return string_buffer_to_string(&output->string);
+}
+
+FILE*
+transpile_output_file(struct Transpile_Output* output)
+{
+    check(output->file != nil, "transpile output is not a file");
+    return output->file;
+}
+
+struct Transpile_Output
+transpile_output_to_string(void)
+{
+    return (struct Transpile_Output){
+        .file = nil,
+        .string = string_buffer_empty(),
+    };
+}
+
 struct Transpile_Context
 {
     bool in_function;
@@ -29,7 +105,7 @@ struct Transpile_Context
 
 struct Transpiler
 {
-    FILE* output;
+    struct Transpile_Output output;
     struct Transpile_Context context;
 };
 
@@ -39,7 +115,7 @@ struct Transpiler
     (void)context;
 
 void
-transpiler_new(struct Transpiler* transpiler, FILE* output)
+transpiler_new(struct Transpiler* transpiler, struct Transpile_Output output)
 {
     *transpiler = (struct Transpiler){
         .output = output,
@@ -59,7 +135,7 @@ transpiler_visit_type_node(struct Visit* visit, struct Type_Node* node)
     TRANSPILER_PREAMBLE
 
     if (!node || node->type == TYPE_NODE_NONE) {
-        fprintf(transpiler->output, "void");
+        TRANSPILE_WRITE("void");
         return;
     }
 
@@ -67,26 +143,26 @@ transpiler_visit_type_node(struct Visit* visit, struct Type_Node* node)
     case TYPE_NODE_NAME: {
         struct String name = node->value.name.name;
         if (strcmp(name.data, "int") == 0) {
-            fprintf(transpiler->output, "integer");
+            TRANSPILE_WRITE("integer");
         } else if (strcmp(name.data, "string") == 0) {
-            fprintf(transpiler->output, "struct String");
+            TRANSPILE_WRITE("struct String");
         } else if (strcmp(name.data, "bool") == 0) {
-            fprintf(transpiler->output, "bool");
+            TRANSPILE_WRITE("bool");
         } else if (strcmp(name.data, "float") == 0) {
-            fprintf(transpiler->output, "real");
+            TRANSPILE_WRITE("real");
         } else if (strcmp(name.data, "uint") == 0) {
-            fprintf(transpiler->output, "uint");
+            TRANSPILE_WRITE("uint");
         } else if (strcmp(name.data, "byte") == 0) {
-            fprintf(transpiler->output, "byte");
+            TRANSPILE_WRITE("byte");
         } else if (strcmp(name.data, "ascii") == 0) {
-            fprintf(transpiler->output, "ascii");
+            TRANSPILE_WRITE("ascii");
         } else {
-            fprintf(transpiler->output, "%.*s", (int)name.length, name.data);
+            TRANSPILE_WRITE("%.*s", (int)name.length, name.data);
         }
         break;
     }
     default:
-        fprintf(transpiler->output, "/* unknown type %d */", node->type);
+        TRANSPILE_WRITE("/* unknown type %d */", node->type);
         break;
     }
 }
@@ -96,22 +172,21 @@ transpiler_visit_function_header_node(struct Visit* visit, struct Function_Heade
 {
     TRANSPILER_PREAMBLE
 
-    fprintf(transpiler->output, "(");
+    TRANSPILE_WRITE("(");
     struct Type_Node* param = header->parameters_type_and_name;
     if (!param) {
-        fprintf(transpiler->output, "void");
+        TRANSPILE_WRITE("void");
     } else {
         bool first = true;
         while (param) {
-            if (!first) { fprintf(transpiler->output, ", "); }
+            if (!first) { TRANSPILE_WRITE(", "); }
             VISIT(visit_type_node, param);
-            fprintf(
-                transpiler->output, " %.*s", (int)param->value_name.length, param->value_name.data);
+            TRANSPILE_WRITE(" %.*s", (int)param->value_name.length, param->value_name.data);
             first = false;
             param = param->next;
         }
     }
-    fprintf(transpiler->output, ")");
+    TRANSPILE_WRITE(")");
 }
 
 void
@@ -119,9 +194,9 @@ transpiler_visit_block_node(struct Visit* visit, struct Block_Node* node)
 {
     TRANSPILER_PREAMBLE
 
-    fprintf(transpiler->output, "{\n");
+    TRANSPILE_WRITE("{\n");
     FOR_EACH (struct Statement*, statement, node->statements) { VISIT(visit_statement, statement); }
-    fprintf(transpiler->output, "}\n");
+    TRANSPILE_WRITE("}\n");
 }
 
 void
@@ -132,7 +207,7 @@ transpiler_visit_argument_group_node(struct Visit* visit, struct Argument_Group_
     struct Expression* arg = node->arguments;
     bool first = true;
     while (arg) {
-        if (!first) { fprintf(transpiler->output, ", "); }
+        if (!first) { TRANSPILE_WRITE(", "); }
         VISIT(visit_expression, arg);
         first = false;
         arg = arg->next;
@@ -153,19 +228,19 @@ transpiler_visit_statement_declaration(struct Visit* visit, struct Statement* st
 
         VISIT(visit_type_node, header->return_type);
         struct String name = *array_at(struct String, &declaration->inner.names, 0);
-        fprintf(transpiler->output, " %.*s", (int)name.length, name.data);
+        TRANSPILE_WRITE(" %.*s", (int)name.length, name.data);
         VISIT(visit_function_header_node, header);
-        fprintf(transpiler->output, " ");
+        TRANSPILE_WRITE(" ");
         VISIT(visit_block_node, &fun->body);
     } else {
         if (declaration->kind == STATEMENT_DECLARATION_CONSTANT) {
-            fprintf(transpiler->output, "const ");
+            TRANSPILE_WRITE("const ");
         }
         VISIT(visit_type_node, declaration->inner.type);
         struct String name = *array_at(struct String, &declaration->inner.names, 0);
-        fprintf(transpiler->output, " %.*s", (int)name.length, name.data);
+        TRANSPILE_WRITE(" %.*s", (int)name.length, name.data);
         if (initializer) {
-            fprintf(transpiler->output, " = ");
+            TRANSPILE_WRITE(" = ");
             VISIT(visit_expression, initializer);
         }
     }
@@ -176,9 +251,9 @@ transpiler_visit_statement_return(struct Visit* visit, struct Statement* stmt)
 {
     TRANSPILER_PREAMBLE
 
-    fprintf(transpiler->output, "return");
+    TRANSPILE_WRITE("return");
     if (stmt->value.return_value.value) {
-        fprintf(transpiler->output, " ");
+        TRANSPILE_WRITE(" ");
         VISIT(visit_expression, stmt->value.return_value.value);
     }
 }
@@ -191,11 +266,11 @@ transpiler_visit_statement_conditional(struct Visit* visit, struct Statement* st
     struct Statement_Value_Conditional* conditional = &stmt->value.conditional;
     for (uint i = 0; i < conditional->condition_count; ++i) {
         struct Statement_Conditional_Branch* branch = &conditional->conditions[i];
-        if (i > 0) { fprintf(transpiler->output, "else "); }
+        if (i > 0) { TRANSPILE_WRITE("else "); }
         if (branch->when) {
-            fprintf(transpiler->output, "if (");
+            TRANSPILE_WRITE("if (");
             VISIT(visit_expression, branch->when);
-            fprintf(transpiler->output, ") ");
+            TRANSPILE_WRITE(") ");
         }
         VISIT(visit_block_node, &branch->then);
     }
@@ -209,15 +284,15 @@ transpiler_visit_statement(struct Visit* visit, struct Statement* statement)
     switch (statement->kind) {
     case STATEMENT_EXPRESSION:
         VISIT(visit_expression, statement->value.expression.inner);
-        if (transpiler->context.in_function) fprintf(transpiler->output, ";\n");
+        if (transpiler->context.in_function) TRANSPILE_WRITE(";\n");
         break;
     case STATEMENT_DECLARATION:
         VISIT(visit_statement_declaration, statement);
-        if (transpiler->context.in_function) fprintf(transpiler->output, ";\n");
+        if (transpiler->context.in_function) TRANSPILE_WRITE(";\n");
         break;
     case STATEMENT_RETURN:
         VISIT(visit_statement_return, statement);
-        if (transpiler->context.in_function) fprintf(transpiler->output, ";\n");
+        if (transpiler->context.in_function) TRANSPILE_WRITE(";\n");
         break;
     case STATEMENT_BLOCK:
         VISIT(visit_block_node, &statement->value.block.inner);
@@ -233,7 +308,7 @@ transpiler_visit_expression_integer_literal(struct Visit* visit, struct Expressi
 {
     TRANSPILER_PREAMBLE
 
-    fprintf(transpiler->output, "%ld", expr->value.integer_literal.value);
+    TRANSPILE_WRITE("%ld", expr->value.integer_literal.value);
 }
 
 void
@@ -241,7 +316,7 @@ transpiler_visit_expression_float_literal(struct Visit* visit, struct Expression
 {
     TRANSPILER_PREAMBLE
 
-    fprintf(transpiler->output, "%f", expr->value.float_literal.value);
+    TRANSPILE_WRITE("%f", expr->value.float_literal.value);
 }
 
 void
@@ -249,7 +324,7 @@ transpiler_visit_expression_string_literal(struct Visit* visit, struct Expressio
 {
     TRANSPILER_PREAMBLE
 
-    fprintf(transpiler->output, "\"%.*s\"", (int)expr->value.string_literal.value.length,
+    TRANSPILE_WRITE("\"%.*s\"", (int)expr->value.string_literal.value.length,
             expr->value.string_literal.value.data);
 }
 
@@ -258,7 +333,7 @@ transpiler_visit_expression_boolean_literal(struct Visit* visit, struct Expressi
 {
     TRANSPILER_PREAMBLE
 
-    fprintf(transpiler->output, "%s", expr->value.bool_literal.value ? "true" : "false");
+    TRANSPILE_WRITE("%s", expr->value.bool_literal.value ? "true" : "false");
 }
 
 void
@@ -266,8 +341,7 @@ transpiler_visit_expression_name(struct Visit* visit, struct Expression* expr)
 {
     TRANSPILER_PREAMBLE
 
-    fprintf(
-        transpiler->output, "%.*s", (int)expr->value.name.name.length, expr->value.name.name.data);
+    TRANSPILE_WRITE("%.*s", (int)expr->value.name.name.length, expr->value.name.name.data);
 }
 
 void
@@ -275,10 +349,9 @@ transpiler_visit_expression_unary_operation(struct Visit* visit, struct Expressi
 {
     TRANSPILER_PREAMBLE
 
-    fprintf(
-        transpiler->output, "(%s", unary_operation_to_string(expr->value.unary_operator.operation));
+    TRANSPILE_WRITE("(%s", unary_operation_to_string(expr->value.unary_operator.operation));
     VISIT(visit_expression, expr->value.unary_operator.operand);
-    fprintf(transpiler->output, ")");
+    TRANSPILE_WRITE(")");
 }
 
 void
@@ -294,7 +367,7 @@ transpiler_visit_expression_binary_operation(struct Visit* visit, struct Express
         struct Expression* name_expr = bin_op->left_operand;
 
         VISIT(visit_type_node, header->return_type);
-        fprintf(transpiler->output, " ");
+        TRANSPILE_WRITE(" ");
 
         // check if this is a main function assignment
         if (name_expr && name_expr->kind == EXPRESSION_NAME) {
@@ -303,7 +376,7 @@ transpiler_visit_expression_binary_operation(struct Visit* visit, struct Express
                 transpiler->context.main_function_found = true;
                 transpiler->context.main_function_takes_args =
                     header->parameters_type_and_name != nil;
-                fprintf(transpiler->output, "catskill_main");
+                TRANSPILE_WRITE("catskill_main");
             } else {
                 VISIT(visit_expression, name_expr);
             }
@@ -311,7 +384,7 @@ transpiler_visit_expression_binary_operation(struct Visit* visit, struct Express
             VISIT(visit_expression, name_expr);
         }
         VISIT(visit_function_header_node, header);
-        fprintf(transpiler->output, " ");
+        TRANSPILE_WRITE(" ");
 
         CONTEXT_START(in_function);
         VISIT(visit_block_node, &fun->body);
@@ -319,22 +392,22 @@ transpiler_visit_expression_binary_operation(struct Visit* visit, struct Express
     } else {
         enum Binary_Operation op = expr->value.binary_operator.operation;
         if (op == BINARY_ASSIGN_AND || op == BINARY_ASSIGN_OR) {
-            fprintf(transpiler->output, "(");
+            TRANSPILE_WRITE("(");
             VISIT(visit_expression, expr->value.binary_operator.left_operand);
-            fprintf(transpiler->output, " = ");
+            TRANSPILE_WRITE(" = ");
             VISIT(visit_expression, expr->value.binary_operator.left_operand);
             if (op == BINARY_ASSIGN_AND)
-                fprintf(transpiler->output, " && ");
+                TRANSPILE_WRITE(" && ");
             else
-                fprintf(transpiler->output, " || ");
+                TRANSPILE_WRITE(" || ");
             VISIT(visit_expression, expr->value.binary_operator.right_operand);
-            fprintf(transpiler->output, ")");
+            TRANSPILE_WRITE(")");
         } else {
-            fprintf(transpiler->output, "(");
+            TRANSPILE_WRITE("(");
             VISIT(visit_expression, expr->value.binary_operator.left_operand);
-            fprintf(transpiler->output, " %s ", binary_operation_to_string(op));
+            TRANSPILE_WRITE(" %s ", binary_operation_to_string(op));
             VISIT(visit_expression, expr->value.binary_operator.right_operand);
-            fprintf(transpiler->output, ")");
+            TRANSPILE_WRITE(")");
         }
     }
 }
@@ -345,9 +418,9 @@ transpiler_visit_expression_call(struct Visit* visit, struct Expression* expr)
     TRANSPILER_PREAMBLE
 
     VISIT(visit_expression, expr->value.call.subject);
-    fprintf(transpiler->output, "(");
+    TRANSPILE_WRITE("(");
     VISIT(visit_argument_group_node, &expr->value.call.argument_group);
-    fprintf(transpiler->output, ")");
+    TRANSPILE_WRITE(")");
 }
 
 void
@@ -400,7 +473,7 @@ transpiler_visit_tree(struct Visit* visit, struct Tree* tree)
     // exactly and tell the backend in `./build.c` to look for includes there,
     // but in the real implementation we should embed all runtime files into
     // this executable and then write them out into the temporary build directory.
-    fprintf(transpiler->output, "#include \"core.c\"\n");
+    TRANSPILE_WRITE("#include \"core.c\"\n");
 
     FOR_EACH (struct Statement*, statement, tree->top_level_statements) {
         VISIT(visit_statement, statement);
@@ -411,9 +484,9 @@ transpiler_visit_tree(struct Visit* visit, struct Tree* tree)
     // the catskill source.
     if (transpiler->context.main_function_found) {
         if (transpiler->context.main_function_takes_args) {
-            fprintf(transpiler->output, "\n#define CATSKILL_MAIN_TAKES_ARGS\n");
+            TRANSPILE_WRITE("\n#define CATSKILL_MAIN_TAKES_ARGS\n");
         }
-        fprintf(transpiler->output, "#include \"runtime.c\"\n");
+        TRANSPILE_WRITE("#include \"runtime.c\"\n");
     }
 }