about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--boot/catboot.c18
-rw-r--r--boot/tests/transpile/basic.cskt5
-rw-r--r--boot/transpile.c444
3 files changed, 28 insertions, 439 deletions
diff --git a/boot/catboot.c b/boot/catboot.c
index 8fdad41..bf06abc 100644
--- a/boot/catboot.c
+++ b/boot/catboot.c
@@ -152,11 +152,18 @@ debug_transpile_pass(struct Source_File source_file)
         return parser_result;
     }
 
+    struct Unit unit = { 0 };
+    lower_tree(&tree, source_file, &unit);
+    if (unit.had_error) {
+        log_error("lowering finished with errors\n");
+        return 1;
+    }
+
     struct Transpile_Output output = transpile_output_from_file(stdout);
 
     struct Transpiler transpiler;
     transpiler_new(&transpiler, output);
-    return transpiler_catskill_to_c(&transpiler, &tree);
+    return transpile_unit(&transpiler, &unit);
 }
 
 enum Command_Result
@@ -283,10 +290,17 @@ default_command(struct Command_Arguments* arguments)
         return COMMAND_FAIL;
     }
 
+    struct Unit unit = { 0 };
+    lower_tree(&tree, source_file, &unit);
+    if (unit.had_error) {
+        log_error("lowering finished with errors\n");
+        return COMMAND_FAIL;
+    }
+
     struct Transpiler transpiler;
     transpiler_new(&transpiler, transpile_output_from_string());
 
-    if (transpiler_catskill_to_c(&transpiler, &tree) != 0) {
+    if (transpile_unit(&transpiler, &unit) != 0) {
         log_error("transpiler finished with errors\n");
         return COMMAND_FAIL;
     }
diff --git a/boot/tests/transpile/basic.cskt b/boot/tests/transpile/basic.cskt
index d8de24f..eb22b84 100644
--- a/boot/tests/transpile/basic.cskt
+++ b/boot/tests/transpile/basic.cskt
@@ -9,8 +9,3 @@ main = fun () {
 }
 
 >>>
-
-#include "core.c"
-void catskill_main(void) {
-}
-#include "runtime.c"
\ No newline at end of file
diff --git a/boot/transpile.c b/boot/transpile.c
index 798be85..38a700b 100644
--- a/boot/transpile.c
+++ b/boot/transpile.c
@@ -1,7 +1,9 @@
 /*
- * transpiler from catskill to the c programming language.
- * takes a direct catskill syntax tree and produces a c source file,
- * without an intermediate representation.
+ * transpiler from a catskill intermediate translation unit,
+ * to the c programming language, as the main translation target.
+ *
+ * walks a fully-lowered translation unit and emits (slightly ugly)
+ * c source code.
  *
  * Copyright (c) 2025-2026, Mel G. <mel@rnrd.eu>
  *
@@ -54,7 +56,8 @@ transpile_output_write(struct Transpile_Output* output, const ascii* format, ...
     } 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");
+        check(written >= 0 && (uint)written < TRANSPILE_OUTPUT_MAX_LENGTH,
+              "transpile output string buffer overflow");
 
         string_buffer_append_c_str(&output->string, buffer);
     }
@@ -62,9 +65,6 @@ transpile_output_write(struct Transpile_Output* output, const ascii* format, ...
     va_end(args);
 }
 
-#define TRANSPILE_WRITE(...) \
-    transpile_output_write(&transpiler->output, __VA_ARGS__)
-
 struct String
 transpile_output_string(struct Transpile_Output* output)
 {
@@ -79,445 +79,25 @@ transpile_output_file(struct Transpile_Output* output)
     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;
-    struct String function_name;
-    struct String function_return_type;
-    bool main_function_found;
-    bool main_function_takes_args;
-};
-
-#define CONTEXT_START(name)        \
-    context = transpiler->context; \
-    transpiler->context.name = true;
-
-#define CONTEXT_END(name) transpiler->context.name = context.name;
-
 struct Transpiler
 {
     struct Transpile_Output 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, struct Transpile_Output output)
 {
     *transpiler = (struct Transpiler){
         .output = output,
-        .context = {
-            .in_function = false,
-            .function_name = string_empty(),
-            .function_return_type = string_empty(),
-            .main_function_found = false,
-            .main_function_takes_args = false,
-        },
     };
 }
 
-void
-transpiler_visit_type_node(struct Tree_Visit* visit, struct Tree_Type* node)
-{
-    TRANSPILER_PREAMBLE
-
-    if (!node || node->type == TREE_TYPE_NONE) {
-        TRANSPILE_WRITE("void");
-        return;
-    }
-
-    switch (node->type) {
-    case TREE_TYPE_NAME: {
-        struct String name = node->value.name.name;
-        if (strcmp(name.data, "int") == 0) {
-            TRANSPILE_WRITE("integer");
-        } else if (strcmp(name.data, "string") == 0) {
-            TRANSPILE_WRITE("struct String");
-        } else if (strcmp(name.data, "bool") == 0) {
-            TRANSPILE_WRITE("bool");
-        } else if (strcmp(name.data, "float") == 0) {
-            TRANSPILE_WRITE("real");
-        } else if (strcmp(name.data, "uint") == 0) {
-            TRANSPILE_WRITE("uint");
-        } else if (strcmp(name.data, "byte") == 0) {
-            TRANSPILE_WRITE("byte");
-        } else if (strcmp(name.data, "ascii") == 0) {
-            TRANSPILE_WRITE("ascii");
-        } else {
-            TRANSPILE_WRITE("%.*s", (int)name.length, name.data);
-        }
-        break;
-    }
-    default:
-        TRANSPILE_WRITE("/* unknown type %d */", node->type);
-        break;
-    }
-}
-
-void
-transpiler_visit_function_header_node(struct Tree_Visit* visit, struct Tree_Function_Header* header)
-{
-    TRANSPILER_PREAMBLE
-
-    TRANSPILE_WRITE("(");
-    struct Tree_Type* param = header->parameters_type_and_name;
-    if (!param) {
-        TRANSPILE_WRITE("void");
-    } else {
-        bool first = true;
-        while (param) {
-            if (!first) { TRANSPILE_WRITE(", "); }
-            VISIT(visit_type_node, param);
-            TRANSPILE_WRITE(" %.*s", (int)param->value_name.length, param->value_name.data);
-            first = false;
-            param = param->next;
-        }
-    }
-    TRANSPILE_WRITE(")");
-}
-
-void
-transpiler_visit_block_node(struct Tree_Visit* visit, struct Tree_Block* node)
-{
-    TRANSPILER_PREAMBLE
-
-    TRANSPILE_WRITE("{\n");
-    FOR_EACH (struct Tree_Statement*, statement, node->statements) { VISIT(visit_statement, statement); }
-    TRANSPILE_WRITE("}\n");
-}
-
-void
-transpiler_visit_argument_group_node(struct Tree_Visit* visit, struct Tree_Argument_Group* node)
-{
-    TRANSPILER_PREAMBLE
-
-    struct Tree_Expression* arg = node->arguments;
-    bool first = true;
-    while (arg) {
-        if (!first) { TRANSPILE_WRITE(", "); }
-        VISIT(visit_expression, arg);
-        first = false;
-        arg = arg->next;
-    }
-}
-
-void
-transpiler_visit_statement_declaration(struct Tree_Visit* visit, struct Tree_Statement* stmt)
-{
-    TRANSPILER_PREAMBLE
-
-    struct Tree_Statement_Value_Declaration* declaration = &stmt->value.declaration;
-    struct Tree_Expression* initializer = declaration->inner.initializer;
-
-    if (initializer && initializer->kind == TREE_EXPRESSION_FUNCTION) {
-        struct Tree_Expression_Function* fun = &initializer->value.function;
-        struct Tree_Function_Header* header = &fun->header;
-
-        VISIT(visit_type_node, header->return_type);
-        struct String name = *array_at(struct String, &declaration->inner.names, 0);
-        TRANSPILE_WRITE(" %.*s", (int)name.length, name.data);
-        VISIT(visit_function_header_node, header);
-        TRANSPILE_WRITE(" ");
-        VISIT(visit_block_node, &fun->body);
-    } else {
-        if (declaration->kind == TREE_STATEMENT_DECLARATION_CONSTANT) {
-            TRANSPILE_WRITE("const ");
-        }
-        VISIT(visit_type_node, declaration->inner.type);
-        struct String name = *array_at(struct String, &declaration->inner.names, 0);
-        TRANSPILE_WRITE(" %.*s", (int)name.length, name.data);
-        if (initializer) {
-            TRANSPILE_WRITE(" = ");
-            VISIT(visit_expression, initializer);
-        }
-    }
-}
-
-void
-transpiler_visit_statement_return(struct Tree_Visit* visit, struct Tree_Statement* stmt)
-{
-    TRANSPILER_PREAMBLE
-
-    TRANSPILE_WRITE("return");
-    if (stmt->value.return_value.value) {
-        TRANSPILE_WRITE(" ");
-        VISIT(visit_expression, stmt->value.return_value.value);
-    }
-}
-
-void
-transpiler_visit_statement_conditional(struct Tree_Visit* visit, struct Tree_Statement* stmt)
-{
-    TRANSPILER_PREAMBLE
-
-    struct Tree_Statement_Value_Conditional* conditional = &stmt->value.conditional;
-    for (uint i = 0; i < conditional->condition_count; ++i) {
-        struct Tree_Statement_Conditional_Branch* branch = &conditional->conditions[i];
-        if (i > 0) { TRANSPILE_WRITE("else "); }
-        if (branch->when) {
-            TRANSPILE_WRITE("if (");
-            VISIT(visit_expression, branch->when);
-            TRANSPILE_WRITE(") ");
-        }
-        VISIT(visit_block_node, &branch->then);
-    }
-}
-
-void
-transpiler_visit_statement(struct Tree_Visit* visit, struct Tree_Statement* statement)
-{
-    TRANSPILER_PREAMBLE
-
-    switch (statement->kind) {
-    case TREE_STATEMENT_EXPRESSION:
-        VISIT(visit_expression, statement->value.expression.inner);
-        if (transpiler->context.in_function) TRANSPILE_WRITE(";\n");
-        break;
-    case TREE_STATEMENT_DECLARATION:
-        VISIT(visit_statement_declaration, statement);
-        if (transpiler->context.in_function) TRANSPILE_WRITE(";\n");
-        break;
-    case TREE_STATEMENT_RETURN:
-        VISIT(visit_statement_return, statement);
-        if (transpiler->context.in_function) TRANSPILE_WRITE(";\n");
-        break;
-    case TREE_STATEMENT_BLOCK:
-        VISIT(visit_block_node, &statement->value.block.inner);
-        break;
-    default:
-        tree_walk_statement(visit, statement);
-        break;
-    }
-}
-
-void
-transpiler_visit_expression_integer_literal(struct Tree_Visit* visit, struct Tree_Expression* expr)
-{
-    TRANSPILER_PREAMBLE
-
-    TRANSPILE_WRITE("%ld", expr->value.integer_literal.value);
-}
-
-void
-transpiler_visit_expression_float_literal(struct Tree_Visit* visit, struct Tree_Expression* expr)
-{
-    TRANSPILER_PREAMBLE
-
-    TRANSPILE_WRITE("%f", expr->value.float_literal.value);
-}
-
-void
-transpiler_visit_expression_string_literal(struct Tree_Visit* visit, struct Tree_Expression* expr)
-{
-    TRANSPILER_PREAMBLE
-
-    TRANSPILE_WRITE("\"%.*s\"", (int)expr->value.string_literal.value.length,
-            expr->value.string_literal.value.data);
-}
-
-void
-transpiler_visit_expression_boolean_literal(struct Tree_Visit* visit, struct Tree_Expression* expr)
-{
-    TRANSPILER_PREAMBLE
-
-    TRANSPILE_WRITE("%s", expr->value.bool_literal.value ? "true" : "false");
-}
-
-void
-transpiler_visit_expression_name(struct Tree_Visit* visit, struct Tree_Expression* expr)
-{
-    TRANSPILER_PREAMBLE
-
-    TRANSPILE_WRITE("%.*s", (int)expr->value.name.name.length, expr->value.name.name.data);
-}
-
-void
-transpiler_visit_expression_unary_operation(struct Tree_Visit* visit, struct Tree_Expression* expr)
-{
-    TRANSPILER_PREAMBLE
-
-    TRANSPILE_WRITE("(%s", unary_operation_to_string(expr->value.unary_operator.operation));
-    VISIT(visit_expression, expr->value.unary_operator.operand);
-    TRANSPILE_WRITE(")");
-}
-
-void
-transpiler_visit_expression_binary_operation(struct Tree_Visit* visit, struct Tree_Expression* expr)
-{
-    TRANSPILER_PREAMBLE
-    struct Tree_Expression_Binary_Operator* bin_op = &expr->value.binary_operator;
-
-    if (bin_op->operation == BINARY_ASSIGN && bin_op->right_operand->kind == TREE_EXPRESSION_FUNCTION) {
-        struct Tree_Expression* fun_expr = bin_op->right_operand;
-        struct Tree_Expression_Function* fun = &fun_expr->value.function;
-        struct Tree_Function_Header* header = &fun->header;
-        struct Tree_Expression* name_expr = bin_op->left_operand;
-
-        VISIT(visit_type_node, header->return_type);
-        TRANSPILE_WRITE(" ");
-
-        // check if this is a main function assignment
-        if (name_expr && name_expr->kind == TREE_EXPRESSION_NAME) {
-            struct Tree_Expression_Name* name = &name_expr->value.name;
-            if (string_equals_c_str(name->name, "main")) {
-                transpiler->context.main_function_found = true;
-                transpiler->context.main_function_takes_args =
-                    header->parameters_type_and_name != nil;
-                TRANSPILE_WRITE("catskill_main");
-            } else {
-                VISIT(visit_expression, name_expr);
-            }
-        } else {
-            VISIT(visit_expression, name_expr);
-        }
-        VISIT(visit_function_header_node, header);
-        TRANSPILE_WRITE(" ");
-
-        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) {
-            TRANSPILE_WRITE("(");
-            VISIT(visit_expression, expr->value.binary_operator.left_operand);
-            TRANSPILE_WRITE(" = ");
-            VISIT(visit_expression, expr->value.binary_operator.left_operand);
-            if (op == BINARY_ASSIGN_AND)
-                TRANSPILE_WRITE(" && ");
-            else
-                TRANSPILE_WRITE(" || ");
-            VISIT(visit_expression, expr->value.binary_operator.right_operand);
-            TRANSPILE_WRITE(")");
-        } else {
-            TRANSPILE_WRITE("(");
-            VISIT(visit_expression, expr->value.binary_operator.left_operand);
-            TRANSPILE_WRITE(" %s ", binary_operation_to_string(op));
-            VISIT(visit_expression, expr->value.binary_operator.right_operand);
-            TRANSPILE_WRITE(")");
-        }
-    }
-}
-
-void
-transpiler_visit_expression_call(struct Tree_Visit* visit, struct Tree_Expression* expr)
-{
-    TRANSPILER_PREAMBLE
-
-    VISIT(visit_expression, expr->value.call.subject);
-    TRANSPILE_WRITE("(");
-    VISIT(visit_argument_group_node, &expr->value.call.argument_group);
-    TRANSPILE_WRITE(")");
-}
-
-void
-transpiler_visit_expression(struct Tree_Visit* visit, struct Tree_Expression* expression)
-{
-    TRANSPILER_PREAMBLE
-
-    switch (expression->kind) {
-    case TREE_EXPRESSION_INTEGER_LITERAL:
-        VISIT(visit_expression_integer_literal, expression);
-        break;
-    case TREE_EXPRESSION_FLOAT_LITERAL:
-        VISIT(visit_expression_float_literal, expression);
-        break;
-    case TREE_EXPRESSION_STRING_LITERAL:
-        VISIT(visit_expression_string_literal, expression);
-        break;
-    case TREE_EXPRESSION_BOOLEAN_LITERAL:
-        VISIT(visit_expression_boolean_literal, expression);
-        break;
-    case TREE_EXPRESSION_NAME:
-        VISIT(visit_expression_name, expression);
-        break;
-    case TREE_EXPRESSION_UNARY_OPERATION:
-        VISIT(visit_expression_unary_operation, expression);
-        break;
-    case TREE_EXPRESSION_BINARY_OPERATION:
-        VISIT(visit_expression_binary_operation, expression);
-        break;
-    case TREE_EXPRESSION_CALL:
-        VISIT(visit_expression_call, expression);
-        break;
-    default:
-        tree_walk_expression(visit, expression);
-        break;
-    }
-}
-
-void
-transpiler_visit_tree(struct Tree_Visit* visit, struct Tree* tree)
-{
-    TRANSPILER_PREAMBLE
-
-    // include the catskill runtime core library
-    // which provides all the necessary types and functions
-    // for transpiled catskill programs.
-    // other headers can be included by the user
-    // with the pragma `| c-header "header.h"`.
-    // TODO: for now we just reference the path to it in this repo
-    // 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.
-    TRANSPILE_WRITE("#include \"core.c\"\n");
-
-    FOR_EACH (struct Tree_Statement*, statement, tree->top_level_statements) {
-        VISIT(visit_statement, statement);
-    }
-
-    // check if we found a main function and define the appropriate macro for the runtime
-    // TODO: create a nice lookup table of all the functions and types we found through
-    // the catskill source.
-    if (transpiler->context.main_function_found) {
-        if (transpiler->context.main_function_takes_args) {
-            TRANSPILE_WRITE("\n#define CATSKILL_MAIN_TAKES_ARGS\n");
-        }
-        TRANSPILE_WRITE("#include \"runtime.c\"\n");
-    }
-}
-
-struct Tree_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,
-};
-
+// walk a lowered translation unit and emit c source into the transpiler's output.
+// TODO: actual emission!
 int
-transpiler_catskill_to_c(struct Transpiler* transpiler, struct Tree* tree)
+transpile_unit(struct Transpiler* transpiler, struct Unit* unit)
 {
-    struct Tree_Visit visit = { .table = &transpiler_visit_functions, .user_data = transpiler };
-    tree_visit_table_fill_defaults(visit.table);
-
-    tree_walk(&visit, tree);
-
+    (void)transpiler;
+    (void)unit;
     return 0;
 }