diff options
Diffstat (limited to 'boot/transpile.c')
| -rw-r--r-- | boot/transpile.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/boot/transpile.c b/boot/transpile.c new file mode 100644 index 0000000..bebcbbc --- /dev/null +++ b/boot/transpile.c @@ -0,0 +1,417 @@ +/* + * transpiler from catskill to the c programming language. + * takes a direct catskill syntax tree and produces a c source file, + * without an intermediate representation. + * + * Copyright (c) 2025, Mel G. <mel@rnrd.eu> + * + * SPDX-License-Identifier: MPL-2.0 + */ + +#pragma once + +#include "catboot.h" +#include "visit.c" +#include <stdio.h> + +struct Transpile_Context +{ + bool in_function; + struct String function_name; + struct String function_return_type; +}; + +#define CONTEXT_START(name) \ + context = transpiler->context; \ + transpiler->context.name = true; + +#define CONTEXT_END(name) transpiler->context.name = context.name; + +struct Transpiler +{ + FILE* output; + struct Transpile_Context context; +}; + +#define TRANSPILER_PREAMBLE \ + DATA_FOR_VISIT(struct Transpiler, transpiler) \ + struct Transpile_Context context = transpiler->context; \ + (void)context; + +void +transpiler_new(struct Transpiler* transpiler, FILE* output) +{ + *transpiler = (struct Transpiler){ + .output = output, + .context = { + .in_function = false, + .function_name = string_empty(), + .function_return_type = string_empty(), + }, + }; +} + +void +transpiler_visit_type_node(struct Visit* visit, struct Type_Node* node) +{ + TRANSPILER_PREAMBLE + + if (!node || node->type == TYPE_NODE_NONE) { + fprintf(transpiler->output, "void"); + return; + } + + switch (node->type) { + case TYPE_NODE_NAME: { + struct String name = node->value.name.name; + if (strcmp(name.data, "int") == 0) { + fprintf(transpiler->output, "int64_t"); + } else if (strcmp(name.data, "string") == 0) { + fprintf(transpiler->output, "const char*"); + } else if (strcmp(name.data, "bool") == 0) { + fprintf(transpiler->output, "bool"); + } else if (strcmp(name.data, "float") == 0) { + fprintf(transpiler->output, "double"); + } else { + fprintf(transpiler->output, "%.*s", (int)name.length, name.data); + } + break; + } + default: + fprintf(transpiler->output, "/* unknown type %d */", node->type); + break; + } +} + +void +transpiler_visit_function_header_node(struct Visit* visit, struct Function_Header_Node* header) +{ + TRANSPILER_PREAMBLE + + fprintf(transpiler->output, "("); + struct Type_Node* param = header->parameters_type_and_name; + if (!param) { + fprintf(transpiler->output, "void"); + } else { + bool first = true; + while (param) { + if (!first) { fprintf(transpiler->output, ", "); } + VISIT(visit_type_node, param); + fprintf( + transpiler->output, " %.*s", (int)param->value_name.length, param->value_name.data); + first = false; + param = param->next; + } + } + fprintf(transpiler->output, ")"); +} + +void +transpiler_visit_block_node(struct Visit* visit, struct Block_Node* node) +{ + TRANSPILER_PREAMBLE + + fprintf(transpiler->output, "{\n"); + FOR_EACH (struct Statement*, statement, node->statements) { VISIT(visit_statement, statement); } + fprintf(transpiler->output, "}\n"); +} + +void +transpiler_visit_argument_group_node(struct Visit* visit, struct Argument_Group_Node* node) +{ + TRANSPILER_PREAMBLE + + struct Expression* arg = node->arguments; + bool first = true; + while (arg) { + if (!first) { fprintf(transpiler->output, ", "); } + VISIT(visit_expression, arg); + first = false; + arg = arg->next; + } +} + +void +transpiler_visit_statement_declaration(struct Visit* visit, struct Statement* stmt) +{ + TRANSPILER_PREAMBLE + + struct Statement_Value_Declaration* declaration = &stmt->value.declaration; + struct Expression* initializer = declaration->inner.initializer; + + if (initializer && initializer->kind == EXPRESSION_FUNCTION) { + struct Expression_Function* fun = &initializer->value.function; + struct Function_Header_Node* header = &fun->header; + + VISIT(visit_type_node, header->return_type); + fprintf(transpiler->output, " %.*s", (int)declaration->inner.names.strings[0].length, + declaration->inner.names.strings[0].data); + VISIT(visit_function_header_node, header); + fprintf(transpiler->output, " "); + VISIT(visit_block_node, &fun->body); + } else { + if (declaration->kind == STATEMENT_DECLARATION_CONSTANT) { + fprintf(transpiler->output, "const "); + } + VISIT(visit_type_node, declaration->inner.type); + fprintf(transpiler->output, " %.*s", (int)declaration->inner.names.strings[0].length, + declaration->inner.names.strings[0].data); + if (initializer) { + fprintf(transpiler->output, " = "); + VISIT(visit_expression, initializer); + } + } +} + +void +transpiler_visit_statement_return(struct Visit* visit, struct Statement* stmt) +{ + TRANSPILER_PREAMBLE + + fprintf(transpiler->output, "return"); + if (stmt->value.return_value.value) { + fprintf(transpiler->output, " "); + VISIT(visit_expression, stmt->value.return_value.value); + } +} + +void +transpiler_visit_statement_conditional(struct Visit* visit, struct Statement* stmt) +{ + TRANSPILER_PREAMBLE + + 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 (branch->when) { + fprintf(transpiler->output, "if ("); + VISIT(visit_expression, branch->when); + fprintf(transpiler->output, ") "); + } + VISIT(visit_block_node, &branch->then); + } +} + +void +transpiler_visit_statement(struct Visit* visit, struct Statement* statement) +{ + TRANSPILER_PREAMBLE + + switch (statement->kind) { + case STATEMENT_EXPRESSION: + VISIT(visit_expression, statement->value.expression.inner); + if (transpiler->context.in_function) fprintf(transpiler->output, ";\n"); + break; + case STATEMENT_DECLARATION: + VISIT(visit_statement_declaration, statement); + if (transpiler->context.in_function) fprintf(transpiler->output, ";\n"); + break; + case STATEMENT_RETURN: + VISIT(visit_statement_return, statement); + if (transpiler->context.in_function) fprintf(transpiler->output, ";\n"); + break; + case STATEMENT_BLOCK: + VISIT(visit_block_node, &statement->value.block.inner); + break; + default: + walk_statement(visit, statement); + break; + } +} + +void +transpiler_visit_expression_integer_literal(struct Visit* visit, struct Expression* expr) +{ + TRANSPILER_PREAMBLE + + fprintf(transpiler->output, "%ld", expr->value.integer_literal.value); +} + +void +transpiler_visit_expression_float_literal(struct Visit* visit, struct Expression* expr) +{ + TRANSPILER_PREAMBLE + + fprintf(transpiler->output, "%f", expr->value.float_literal.value); +} + +void +transpiler_visit_expression_string_literal(struct Visit* visit, struct Expression* expr) +{ + TRANSPILER_PREAMBLE + + fprintf(transpiler->output, "\"%.*s\"", (int)expr->value.string_literal.value.length, + expr->value.string_literal.value.data); +} + +void +transpiler_visit_expression_boolean_literal(struct Visit* visit, struct Expression* expr) +{ + TRANSPILER_PREAMBLE + + fprintf(transpiler->output, "%s", expr->value.bool_literal.value ? "true" : "false"); +} + +void +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); +} + +void +transpiler_visit_expression_unary_operation(struct Visit* visit, struct Expression* expr) +{ + TRANSPILER_PREAMBLE + + fprintf( + transpiler->output, "(%s", unary_operation_to_string(expr->value.unary_operator.operation)); + VISIT(visit_expression, expr->value.unary_operator.operand); + fprintf(transpiler->output, ")"); +} + +void +transpiler_visit_expression_binary_operation(struct Visit* visit, struct Expression* expr) +{ + TRANSPILER_PREAMBLE + struct Expression_Binary_Operator* bin_op = &expr->value.binary_operator; + + if (bin_op->operation == BINARY_ASSIGN && bin_op->right_operand->kind == EXPRESSION_FUNCTION) { + struct Expression* fun_expr = bin_op->right_operand; + struct Expression_Function* fun = &fun_expr->value.function; + struct Function_Header_Node* header = &fun->header; + struct Expression* name_expr = bin_op->left_operand; + + VISIT(visit_type_node, header->return_type); + fprintf(transpiler->output, " "); + VISIT(visit_expression, name_expr); + VISIT(visit_function_header_node, header); + fprintf(transpiler->output, " "); + + CONTEXT_START(in_function); + VISIT(visit_block_node, &fun->body); + CONTEXT_END(in_function); + } else { + enum Binary_Operation op = expr->value.binary_operator.operation; + if (op == BINARY_ASSIGN_AND || op == BINARY_ASSIGN_OR) { + fprintf(transpiler->output, "("); + VISIT(visit_expression, expr->value.binary_operator.left_operand); + fprintf(transpiler->output, " = "); + VISIT(visit_expression, expr->value.binary_operator.left_operand); + if (op == BINARY_ASSIGN_AND) + fprintf(transpiler->output, " && "); + else + fprintf(transpiler->output, " || "); + VISIT(visit_expression, expr->value.binary_operator.right_operand); + fprintf(transpiler->output, ")"); + } else { + fprintf(transpiler->output, "("); + VISIT(visit_expression, expr->value.binary_operator.left_operand); + fprintf(transpiler->output, " %s ", binary_operation_to_string(op)); + VISIT(visit_expression, expr->value.binary_operator.right_operand); + fprintf(transpiler->output, ")"); + } + } +} + +void +transpiler_visit_expression_call(struct Visit* visit, struct Expression* expr) +{ + TRANSPILER_PREAMBLE + + VISIT(visit_expression, expr->value.call.subject); + fprintf(transpiler->output, "("); + VISIT(visit_argument_group_node, &expr->value.call.argument_group); + fprintf(transpiler->output, ")"); +} + +void +transpiler_visit_expression(struct Visit* visit, struct Expression* expression) +{ + TRANSPILER_PREAMBLE + + switch (expression->kind) { + case EXPRESSION_INTEGER_LITERAL: + VISIT(visit_expression_integer_literal, expression); + break; + case EXPRESSION_FLOAT_LITERAL: + VISIT(visit_expression_float_literal, expression); + break; + case EXPRESSION_STRING_LITERAL: + VISIT(visit_expression_string_literal, expression); + break; + case EXPRESSION_BOOLEAN_LITERAL: + VISIT(visit_expression_boolean_literal, expression); + break; + case EXPRESSION_NAME: + VISIT(visit_expression_name, expression); + break; + case EXPRESSION_UNARY_OPERATION: + VISIT(visit_expression_unary_operation, expression); + break; + case EXPRESSION_BINARY_OPERATION: + VISIT(visit_expression_binary_operation, expression); + break; + case EXPRESSION_CALL: + VISIT(visit_expression_call, expression); + break; + default: + walk_expression(visit, expression); + break; + } +} + +void +transpiler_visit_tree(struct Visit* visit, struct Tree* tree) +{ + TRANSPILER_PREAMBLE + + // include some standard c headers + // catskill should have it's own standard library, + // but until bootstrapping is complete, we will just use a + // normal libc (preferably musl). + // other headers can be included by the user + // with the pragma `| c-header "header.h"`. + fprintf(transpiler->output, "#include <stdio.h>\n"); + fprintf(transpiler->output, "#include <stdint.h>\n"); + fprintf(transpiler->output, "#include <stdbool.h>\n\n"); + + FOR_EACH (struct Statement*, statement, tree->top_level_statements) { + VISIT(visit_statement, statement); + } +} + +struct Visit_Table transpiler_visit_functions = { + .visit_tree = transpiler_visit_tree, + .visit_statement = transpiler_visit_statement, + .visit_statement_declaration = transpiler_visit_statement_declaration, + .visit_statement_conditional = transpiler_visit_statement_conditional, + .visit_statement_return = transpiler_visit_statement_return, + .visit_expression = transpiler_visit_expression, + .visit_expression_integer_literal = transpiler_visit_expression_integer_literal, + .visit_expression_float_literal = transpiler_visit_expression_float_literal, + .visit_expression_string_literal = transpiler_visit_expression_string_literal, + .visit_expression_boolean_literal = transpiler_visit_expression_boolean_literal, + .visit_expression_name = transpiler_visit_expression_name, + .visit_expression_unary_operation = transpiler_visit_expression_unary_operation, + .visit_expression_binary_operation = transpiler_visit_expression_binary_operation, + .visit_expression_call = transpiler_visit_expression_call, + .visit_type_node = transpiler_visit_type_node, + .visit_function_header_node = transpiler_visit_function_header_node, + .visit_block_node = transpiler_visit_block_node, + .visit_argument_group_node = transpiler_visit_argument_group_node, +}; + +int +transpiler_catskill_to_c(struct Transpiler* transpiler, struct Tree* tree) +{ + struct Visit visit = { .table = &transpiler_visit_functions, .user_data = transpiler }; + visit_table_fill_defaults(visit.table); + + walk(&visit, tree); + + return 0; +} |
