/* * C code generator. Does not actually call the compiler, * that's done by the Makefile. The other `out*.c` files handle * specific parts of C code generation. * * Copyright © 2020-2025 Samuel Lidén Borell * * SPDX-License-Identifier: EUPL-1.2+ OR LGPL-2.1-or-later */ #include #include #include "compiler.h" #include "out.h" #include "token.h" static void emit_header(void) { outf( "/* Generated code from bootstrap compiler */\n" "#include \"rtl.h\"\n" "\n"); } static void emit_footer(void) { } void emit_ident(struct Ident *ident) { outf("%.*s", ident->node.length, ident->node.name); } /* FIXME arrays, optional types, generics... */ void emit_typeref_prefix(struct TypeRef *tr) { /* TODO output parentheses for nested types e.g. array of pointer etc. */ switch (tr->kind) { case TR_UNKNOWN: /* TODO at the moment, this happens for unimplemented exprs: E_ARRAY */ outf("int /*unimplemented stuff*/ "); /*ast_error("TR_UNKNOWN encountered at codegen time");*/ break; case TR_VOID: unreachable(); case TR_BOOL: outf("bool "); break; case TR_INT: outf("SlulInt "); break; case TR_CLASS: if ((tr->quals & Q_VAR) == 0) { outf("const "); } emit_type(tr->u.class_); outc(' '); outc('*'); } } void emit_typeref_suffix(struct TypeRef *tr) { /* TODO arrays? */ (void)tr; } void emit_func_ident(struct Func *func) { struct Type *class_ = func->class_; if (class_) { emit_type_name(class_); outc('_'); } emit_ident(&func->ident); } void emit_type_name(struct Type *type) { if (type->outer) { assert(type->outer->outer == NULL); emit_ident(&type->outer->ident); outc('_'); } emit_ident(&type->ident); } void emit_type(struct Type *type) { outf("struct "); emit_type_name(type); } static void emit_module(struct Module *mod) { struct Type *type, *inner; commandmain_type = NULL; main_func = NULL; emit_string_constants(); if (mod->types_list) { outc('\n'); /* Pre-declare types */ for (type = mod->types_list; type; type = type->next) { predeclare_type(type); for (inner = type->inner_types_list; inner; inner = inner->next) { assert(inner->inner_types_list == NULL); predeclare_type(inner); } } /* Emit types */ /* XXX this is tricky if structs are allowed to be embedded without a pointer. Then it has to be done in the correct order */ for (type = mod->types_list; type; type = type->next) { define_type(type); for (inner = type->inner_types_list; inner; inner = inner->next) { define_type(inner); } } } outc('\n'); /* Pre-declare functions */ predeclare_funcs(mod->funcs_list); for (type = mod->types_list; type; type = type->next) { current_type = type; predeclare_funcs(type->funcs_list); predeclare_funcs(type->ctors_list); for (inner = type->inner_types_list; inner; inner = inner->next) { current_type = inner; predeclare_funcs(inner->funcs_list); predeclare_funcs(inner->ctors_list); } } current_type = NULL; /* Emit function bodies */ define_funcs(mod->funcs_list); for (type = mod->types_list; type; type = type->next) { current_type = type; define_funcs(type->funcs_list); define_funcs(type->ctors_list); for (inner = type->inner_types_list; inner; inner = inner->next) { current_type = inner; define_funcs(inner->funcs_list); define_funcs(inner->ctors_list); } } current_type = NULL; } static void emit_code(void) { struct Module *mod; assert(current_type == NULL); assert(current_func == NULL); /* Emit "user" code */ for (mod = modules; mod; mod = mod->next) { emit_module(mod); } /* Emit main that calls the class that is a `CommandMain` */ if (!commandmain_type) { warning("No class is a `CommandMain`, no entry point will be found."); } if (!main_func) { error("No `main` entry point found."); } outf("\n"); beginf("int main(int argc, char **argv) {\n"); indent(); emit_type(commandmain_type); outf(" *cmdmain;\n\n"); /* Allocate cmdmain and initialize `giveme`s, including command-line argument parsing */ emit_cmdmain_initialization(commandmain_type, "cmdmain"); /* Call `main` on cmdmain */ indent(); emit_func_ident(main_func); outf("(cmdmain);\n"); /* Return */ indentf("return EXIT_SUCCESS;\n"); /* TODO exit code */ endf("}\n"); } void emit_c_code(const char *filename) { outfile_path = filename; outfile = fopen(filename, "wb"); if (!outfile) { io_error(); } emit_header(); emit_code(); emit_footer(); if (fclose(outfile) < 0) { io_error(); } }