diff options
| -rw-r--r-- | boot/catboot.c | 59 | ||||
| -rw-r--r-- | boot/catboot.h | 1 | ||||
| -rw-r--r-- | boot/parse.c | 2 | ||||
| -rw-r--r-- | boot/transpile.c | 417 |
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; +} |
