aboutsummaryrefslogtreecommitdiff
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;
}