aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/catboot.c59
-rw-r--r--boot/catboot.h1
-rw-r--r--boot/parse.c2
-rw-r--r--boot/transpile.c417
4 files changed, 467 insertions, 12 deletions
diff --git a/boot/catboot.c b/boot/catboot.c
index 155c3c5..9afb710 100644
--- a/boot/catboot.c
+++ b/boot/catboot.c
@@ -105,9 +105,7 @@ debug_parse_pass(struct Source_File source_file)
struct Tree tree;
int parser_result = parser_do_your_thing(&parser, &tree);
- if (parser_result != 0) {
- fprintf(stderr, "parser finished with errors\n");
- }
+ if (parser_result != 0) fprintf(stderr, "parser finished with errors\n");
tree_printer(&tree);
@@ -123,6 +121,7 @@ enum Command_Result
struct Command_Arguments
{
const ascii* input;
+ const ascii* output;
};
enum Command_Result
@@ -167,18 +166,50 @@ test_parse_command(struct Command_Arguments* arguments)
enum Command_Result
default_command(struct Command_Arguments* arguments)
{
+ enum Command_Result result = COMMAND_OK;
+
struct String source = read_file(arguments->input);
struct Source_File source_file = {
.source = source,
.path = string_from_static_c_string(arguments->input),
};
- debug_lex_pass(source);
- printf("\n");
- if (debug_parse_pass(source_file)) return COMMAND_FAIL;
- printf("\n");
+ struct Lexer lexer;
+ lexer_new(&lexer, source);
- return COMMAND_OK;
+ struct Parser parser;
+ parser_new(&parser, &lexer, source_file);
+
+ struct Tree tree;
+ if (parser_do_your_thing(&parser, &tree) != 0) {
+ fprintf(stderr, "parser finished with errors\n");
+ goto end;
+ }
+
+ FILE* output_file = nil;
+ if (arguments->output) {
+ output_file = fopen(arguments->output, "w");
+ if (!output_file) {
+ fprintf(stderr, "could not open output file: %s\n", arguments->output);
+ result = COMMAND_FAIL;
+ goto end;
+ }
+ } else {
+ output_file = stdout;
+ }
+
+ struct Transpiler transpiler;
+ transpiler_new(&transpiler, output_file);
+
+ if (transpiler_catskill_to_c(&transpiler, &tree) != 0) {
+ fprintf(stderr, "transpiler finished with errors\n");
+ result = COMMAND_FAIL;
+ }
+
+ if (output_file != stdout) fclose(output_file);
+
+end:
+ return result;
}
typedef enum Command_Result (*Command_Function)(struct Command_Arguments*);
@@ -209,7 +240,7 @@ main(const int32 argc, ascii* argv[])
}
bool have_command = false;
- ascii *command_name = nil, short_command_name = '\0', *input = nil;
+ ascii *command_name = nil, short_command_name = '\0', *input = nil, *output = nil;
for (uint a = 1; a < argc; ++a) {
ascii* arg = argv[a];
@@ -224,12 +255,16 @@ main(const int32 argc, ascii* argv[])
}
have_command = true;
} else {
- check(!input, "multiple inputs given");
- input = arg;
+ if (!input)
+ input = arg;
+ else if (!output)
+ output = arg;
+ else
+ failure("too many arguments, expected at most 2: input and output");
}
}
- struct Command_Arguments arguments = { .input = input };
+ struct Command_Arguments arguments = { .input = input, .output = output };
for (uint d = 0; d < ARRAY_SIZE(command_definitions); ++d) {
struct Command_Definition* definition = &command_definitions[d];
diff --git a/boot/catboot.h b/boot/catboot.h
index 4c40676..621ed11 100644
--- a/boot/catboot.h
+++ b/boot/catboot.h
@@ -18,3 +18,4 @@
#include "tree.c"
#include "parse.c"
#include "visit.c"
+#include "transpile.c"
diff --git a/boot/parse.c b/boot/parse.c
index 091a6f7..481fa90 100644
--- a/boot/parse.c
+++ b/boot/parse.c
@@ -1646,3 +1646,5 @@ parser_do_your_thing(struct Parser* p, struct Tree* tree)
#undef CHECK
#undef CHECK_RETURN
+#undef CONTEXT_START
+#undef CONTEXT_END
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;
+}