about summary refs log tree commit diff
path: root/boot/transpile.c
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2025-07-23 03:52:50 +0200
committerMel <mel@rnrd.eu>2025-07-23 03:56:24 +0200
commit3a4e9a21361ac930f17fe18687f4a1bce6a5863b (patch)
tree69c5f7319197334daa5e18482a555c4e5d592b4c /boot/transpile.c
parentb0acce3b858715a04fba154a23c6ea5fac083a74 (diff)
downloadcatskill-3a4e9a21361ac930f17fe18687f4a1bce6a5863b.tar.zst
catskill-3a4e9a21361ac930f17fe18687f4a1bce6a5863b.zip
Add rudimentary catskill-to-C transpiler (yay!)
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot/transpile.c')
-rw-r--r--boot/transpile.c417
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;
+}