diff options
Diffstat (limited to 'boot/transpile.c')
| -rw-r--r-- | boot/transpile.c | 191 |
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"); } } |
