/
vit1251
/
cmm
Обзор
Документация
Войти
/
vit1251
/
cmm
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/main.cpp
531 строка
21 KB
Vitold S
update
09 авг 2026, 02:25
09 авг 2026, 02:25
a36ae1e
Код
Авторство
О чём код?
#include "lexer.h" #include "parser.h" #include "codegen.h" #include "header_gen.h" #include "types_ser.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/Binary.h" #include "llvm/Support/Error.h" #include <cstdio> #include <cstring> #include <string> #include <unordered_set> #include <filesystem> #include <fstream> #include <iostream> #include <sstream> namespace fs = std::filesystem; // Read .cmm_types section from an ELF .o file static std::string read_cmm_types_section(const std::string& path) { auto buf = llvm::object::createBinary(path); if (!buf) { return ""; } auto* obj = llvm::dyn_cast<llvm::object::ObjectFile>(buf->getBinary()); if (!obj) return ""; for (auto& sec : obj->sections()) { auto name = sec.getName(); if (name && *name == ".cmm_types") { auto content = sec.getContents(); if (content) return content->str(); } } return ""; } struct CompileSession { Program program; std::unordered_set<std::string> loaded_files; std::unordered_set<std::string> loaded_obj_modules; // module names from .o files std::unordered_set<std::string> auto_obj_files; // .o/.a discovered alongside .cmm std::string main_source_dir; bool has_errors = false; std::vector<fs::path> search_paths; std::string resolve_path(const std::string& path, const std::string& referencing_file) { fs::path ref_dir = fs::path(referencing_file).parent_path(); auto try_path = [&](const fs::path& base) -> std::string { fs::path p = base / path; if (fs::exists(p)) return fs::absolute(p).string(); p = base / (path + ".cmm"); if (fs::exists(p)) return fs::absolute(p).string(); return ""; }; for (auto& sp : search_paths) { std::string r = try_path(sp); if (!r.empty()) return r; } std::string r = try_path(ref_dir); if (!r.empty()) return r; if (!main_source_dir.empty()) { r = try_path(fs::path(main_source_dir)); if (!r.empty()) return r; } r = try_path(fs::current_path() / "libcmm"); if (!r.empty()) return r; r = try_path(fs::current_path() / "std" / "mini"); if (!r.empty()) return r; r = try_path(fs::current_path() / "std" / "libc"); if (!r.empty()) return r; r = try_path(fs::current_path() / "std" / "sdl2"); if (!r.empty()) return r; r = try_path(fs::current_path() / "std" / "libm"); if (!r.empty()) return r; return ""; } void load_file(const std::string& path, const std::string& referencing_file) { std::string resolved = resolve_path(path, referencing_file); if (resolved.empty()) { fprintf(stderr, "error: module '%s' not found (referenced from %s)\n", path.c_str(), referencing_file.c_str()); has_errors = true; return; } if (loaded_files.count(resolved)) return; loaded_files.insert(resolved); // Auto-discover sibling .o / .a files for linking fs::path cmm_path(resolved); fs::path mod_dir = cmm_path.parent_path(); fs::path mod_stem = cmm_path.stem(); for (const char* ext : {".o", ".a"}) { fs::path obj = mod_dir / (mod_stem.string() + ext); if (fs::exists(obj)) { std::string abs = fs::absolute(obj).string(); if (auto_obj_files.insert(abs).second) { // Load types from .cmm_types section if present std::string data = read_cmm_types_section(abs); if (!data.empty()) { CmmModuleInfo info; if (deserialize_cmm_types(data, info)) { std::string mname = mod_stem.string(); loaded_obj_modules.insert(mname); loaded_obj_modules.insert(info.module_name); for (auto& sd : info.structs) { bool dup = false; for (auto& existing : program.structs) if (existing.name == sd.name) { dup = true; break; } if (!dup) program.structs.push_back(std::move(sd)); } for (auto& fn : info.pub_fns) { bool dup = false; for (auto& existing : program.functions) if (existing->name == fn.name) { dup = true; break; } if (!dup) { auto ff = std::make_unique<FnDecl>(); ff->name = fn.name; ff->is_extern = true; ff->is_pub = true; ff->ret_type_name = fn.ret_type; ff->source_module = abs; for (auto& pt : fn.param_types) { Param pp; pp.type_name = pt; pp.name = ""; ff->params.push_back(std::move(pp)); } program.functions.push_back(std::move(ff)); } } } } } } } std::ifstream ifs(resolved); if (!ifs) { fprintf(stderr, "error: cannot open '%s'\n", resolved.c_str()); has_errors = true; return; } std::stringstream buf; buf << ifs.rdbuf(); std::string source = buf.str(); Lexer lexer(source, resolved); Parser parser(lexer, [this](const std::string& p, const SourceLoc& loc, Program& prog) { if (loaded_obj_modules.count(p)) return; load_file(p, loc.file); }); auto sub_prog = parser.parse_program(); if (lexer.current().type == TokenType::ERROR) { fprintf(stderr, "%s: lexer error\n", resolved.c_str()); has_errors = true; return; } for (auto& fn : sub_prog.functions) { bool dup = false; for (auto& existing : program.functions) if (existing->name == fn->name) { dup = true; break; } if (!dup) { fn->source_module = resolved; program.functions.push_back(std::move(fn)); } } for (auto& st : sub_prog.structs) program.structs.push_back(std::move(st)); for (auto& ib : sub_prog.impls) program.impls.push_back(std::move(ib)); for (auto& cd : sub_prog.constants) program.constants.push_back(std::move(cd)); for (auto& ed : sub_prog.enums) program.enums.push_back(std::move(ed)); for (auto& imp : sub_prog.imports) program.imports.push_back(std::move(imp)); } void load_main(const std::string& path) { fs::path abs_path = fs::absolute(path); main_source_dir = abs_path.parent_path().string(); std::string resolved = abs_path.string(); if (!fs::exists(resolved)) { fprintf(stderr, "error: file '%s' not found\n", path.c_str()); has_errors = true; return; } if (loaded_files.count(resolved)) return; loaded_files.insert(resolved); std::ifstream ifs(resolved); if (!ifs) { fprintf(stderr, "error: cannot open '%s'\n", resolved.c_str()); has_errors = true; return; } std::stringstream buf; buf << ifs.rdbuf(); std::string source = buf.str(); Lexer lexer(source, resolved); Parser parser(lexer, [this](const std::string& p, const SourceLoc& loc, Program& caller_prog) { // Check if this module was already loaded from .o file if (loaded_obj_modules.count(p)) return; size_t before = program.constants.size(); load_file(p, loc.file); // Copy newly imported constants into caller's program for resolution for (size_t i = before; i < program.constants.size(); i++) caller_prog.constants.push_back(program.constants[i]); }); auto main_prog = parser.parse_program(); if (lexer.current().type == TokenType::ERROR) { fprintf(stderr, "%s: lexer error\n", resolved.c_str()); has_errors = true; return; } for (auto& fn : main_prog.functions) { fn->source_module = resolved; program.functions.push_back(std::move(fn)); } for (auto& st : main_prog.structs) program.structs.push_back(std::move(st)); for (auto& ib : main_prog.impls) program.impls.push_back(std::move(ib)); for (auto& cd : main_prog.constants) program.constants.push_back(std::move(cd)); for (auto& ed : main_prog.enums) program.enums.push_back(std::move(ed)); for (auto& imp : main_prog.imports) program.imports.push_back(std::move(imp)); } }; // Architecture → asm file mapping struct ArchInfo { std::string triple; std::string asm_file; // relative to lib/mini/ }; static const ArchInfo archs[] = { {"x86_64-linux-gnu", "linux-x86_64.s"}, {"x86_64-linux", "linux-x86_64.s"}, {"aarch64-linux-gnu", "linux-aarch64.s"}, {"aarch64-linux", "linux-aarch64.s"}, }; static std::string find_asm_file(const std::string& target) { for (auto& a : archs) { if (target.find(a.triple) != std::string::npos) return a.asm_file; } // Default to x86_64 return "linux-x86_64.s"; } int main(int argc, char** argv) { const char* input_file = nullptr; const char* output_file = nullptr; bool emit_ll = false; bool emit_obj = false; bool emit_header = false; bool dump_types = false; bool no_std = false; bool do_link = false; std::string stdlib = "mini"; std::string target_triple; std::string output_file_auto; std::vector<fs::path> include_paths; std::vector<std::string> obj_files; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) { output_file = argv[++i]; } else if (strcmp(argv[i], "-S") == 0) { emit_ll = true; } else if (strcmp(argv[i], "-c") == 0) { emit_obj = true; } else if (strcmp(argv[i], "--header") == 0) { emit_header = true; } else if (strcmp(argv[i], "--dump-types") == 0) { dump_types = true; } else if (strcmp(argv[i], "--no-std") == 0) { no_std = true; } else if (strcmp(argv[i], "--stdlib") == 0 && i + 1 < argc) { stdlib = argv[++i]; } else if (strcmp(argv[i], "--target") == 0 && i + 1 < argc) { target_triple = argv[++i]; } else if (strcmp(argv[i], "-I") == 0 && i + 1 < argc) { include_paths.push_back(fs::path(argv[++i])); } else if (argv[i][0] != '-') { std::string arg = argv[i]; // Check if it's a .o file if (arg.size() > 2 && arg.substr(arg.size() - 2) == ".o") obj_files.push_back(arg); else input_file = argv[i]; } } // Dump .cmm_types from .o files (no .cmm file needed) if (dump_types) { for (auto& opath : obj_files) { std::string data = read_cmm_types_section(opath); if (data.empty()) { printf("%s: no .cmm_types section\n", opath.c_str()); continue; } CmmModuleInfo info; if (!deserialize_cmm_types(data, info)) { printf("%s: failed to parse .cmm_types\n", opath.c_str()); continue; } printf("Module: %s\n", info.module_name.c_str()); printf(" Structs:\n"); for (auto& sd : info.structs) { printf(" %s%s {", sd.is_pub ? "pub " : "", sd.name.c_str()); for (size_t i = 0; i < sd.fields.size(); i++) { if (i > 0) printf(","); printf(" %s: %s", sd.fields[i].name.c_str(), sd.fields[i].type_name.c_str()); } printf(" }\n"); } printf(" Functions:\n"); for (auto& sig : info.pub_fns) { printf(" %s (", sig.name.c_str()); for (size_t i = 0; i < sig.param_types.size(); i++) { if (i > 0) printf(", "); printf("%s", sig.param_types[i].c_str()); } printf(") -> %s\n", sig.ret_type.c_str()); } printf(" Constants:\n"); for (auto& cn : info.pub_const_names) printf(" %s\n", cn.c_str()); } return 0; } if (!input_file) { fprintf(stderr, "usage: cmmc <file.cmm> [options]\n"); fprintf(stderr, " -o <file> output filename\n"); fprintf(stderr, " -S emit LLVM IR to stdout\n"); fprintf(stderr, " -c emit object file only\n"); fprintf(stderr, " --header generate C header file\n"); fprintf(stderr, " --dump-types dump .cmm_types section of .o files\n"); fprintf(stderr, " --stdlib standard library (mini|libc)\n"); fprintf(stderr, " --no-std no standard library at all\n"); fprintf(stderr, " --target target triple (e.g. aarch64-linux-gnu)\n"); fprintf(stderr, " -I <dir> add directory to module search path\n"); return 1; } if (!emit_ll && !emit_obj && !emit_header) { do_link = true; emit_obj = true; if (!output_file) { std::string in(input_file); size_t dot = in.rfind('.'); if (dot != std::string::npos) output_file_auto = in.substr(0, dot); else output_file_auto = in; output_file = output_file_auto.c_str(); } } std::string obj_path; std::string exe_path; if (emit_obj) { if (do_link) { obj_path = std::string(output_file) + ".o"; exe_path = output_file; } else if (output_file) { obj_path = output_file; } else { std::string in(input_file); size_t dot = in.rfind('.'); obj_path = (dot != std::string::npos) ? in.substr(0, dot) + ".o" : in + ".o"; } } // Compile CompileSession session; session.search_paths = std::move(include_paths); // Load types from .o files FIRST (before .cmm imports) for (auto& opath : obj_files) { std::string data = read_cmm_types_section(opath); if (data.empty()) { fprintf(stderr, "warning: '%s' has no .cmm_types section\n", opath.c_str()); continue; } CmmModuleInfo info; if (!deserialize_cmm_types(data, info)) { fprintf(stderr, "warning: failed to parse .cmm_types from '%s'\n", opath.c_str()); continue; } // Derive module name from .o filename (strip .o, strip path) std::string mod_name = fs::path(opath).stem().string(); session.loaded_obj_modules.insert(mod_name); session.loaded_obj_modules.insert(info.module_name); // also store the embedded name // Add structs to program (for type resolution during parsing) for (auto& sd : info.structs) { // Check for duplicates bool dup = false; for (auto& existing : session.program.structs) if (existing.name == sd.name) { dup = true; break; } if (!dup) session.program.structs.push_back(sd); } } session.load_main(input_file); if (session.has_errors) return 1; // Generate C header if (emit_header) { std::string hdr = generate_c_header(session.program); if (output_file) { std::ofstream ofs(output_file); ofs << hdr; } else { printf("%s\n", hdr.c_str()); } return 0; } Codegen cg; // Register imported structs and functions from .o files for (auto& opath : obj_files) { std::string data = read_cmm_types_section(opath); if (data.empty()) continue; CmmModuleInfo info; if (!deserialize_cmm_types(data, info)) continue; for (auto& sd : info.structs) { cg.import_struct(sd); } for (auto& sig : info.pub_fns) { auto* func = cg.module()->getFunction(sig.name); if (!func) { // Build LLVM function type from signature auto* ret_ty = cg.resolve_type(sig.ret_type); std::vector<llvm::Type*> param_tys; for (auto& pt : sig.param_types) { // Self: *WavWriter → pointer. The type name is like "*WavWriter" if (pt.size() > 0 && pt[0] == '*') { param_tys.push_back(llvm::PointerType::get(cg.context(), 0)); } else { param_tys.push_back(cg.resolve_type(pt)); } } auto* ft = llvm::FunctionType::get(ret_ty, param_tys, false); llvm::Function::Create(ft, llvm::Function::ExternalLinkage, sig.name, cg.module()); } } } { std::string mod_id = fs::path(input_file).stem().string(); std::string main_abs = fs::absolute(input_file).string(); cg.resolve_imports(session.program, main_abs); cg.generate(session.program, mod_id); } if (emit_ll) cg.printIR(output_file ? output_file : "-"); if (emit_obj) cg.emitObject(obj_path); // Link if (do_link) { // Don't try to link if there's no main function (library file) bool has_main = false; for (auto& fn : session.program.functions) if (!fn->is_extern && fn->name == "main") { has_main = true; break; } if (!has_main) { fprintf(stderr, "warning: no 'main' function, skipping link step\n"); // Still keep the .o if it was generated return 0; } // Check if any extern functions use a library (like "c") bool needs_libc = false; std::unordered_set<std::string> libs; for (auto& fn : session.program.functions) { if (fn->is_extern && !fn->lib_name.empty()) { libs.insert(fn->lib_name); if (fn->lib_name == "c") needs_libc = true; } } if (no_std) { // User provides everything, just link raw std::string cmd = "ld.lld --gc-sections -nostdlib " + obj_path; for (auto& o : obj_files) cmd += " " + o; cmd += " -o " + exe_path; int ret = system(cmd.c_str()); if (ret) { fprintf(stderr, "error: linker failed (exit %d)\n", ret); return 1; } remove(obj_path.c_str()); return 0; } if (needs_libc || stdlib == "libc" || !libs.empty()) { std::string cmd = "clang -no-pie -Wl,--gc-sections " + obj_path; for (auto& o : obj_files) cmd += " " + o; for (auto& l : libs) cmd += " -l" + l; cmd += " -o " + exe_path; int ret = system(cmd.c_str()); if (ret) { fprintf(stderr, "error: linker failed (exit %d)\n", ret); return 1; } remove(obj_path.c_str()); return 0; } // stdlib = mini: assemble .s, link with ld.lld -nostdlib std::string arch = target_triple.empty() ? "x86_64" : target_triple; std::string asm_file = find_asm_file(arch); fs::path std_path = fs::current_path() / "std" / "mini" / asm_file; if (!fs::exists(std_path)) { fprintf(stderr, "error: asm file not found: %s\n", std_path.c_str()); fprintf(stderr, " try: --target x86_64-linux-gnu or aarch64-linux-gnu\n"); return 1; } // Assemble the .s file std::string asm_obj = obj_path + ".asm.o"; std::string asm_cmd = "clang -c " + std_path.string() + " -o " + asm_obj; if (!target_triple.empty()) asm_cmd += " --target=" + target_triple; int asm_ret = system(asm_cmd.c_str()); if (asm_ret) { fprintf(stderr, "error: assembler failed (exit %d)\n", asm_ret); return 1; } // Link std::string link_cmd = "ld.lld --gc-sections -nostdlib " + obj_path; for (auto& o : obj_files) link_cmd += " " + o; link_cmd += " " + asm_obj + " -o " + exe_path; int link_ret = system(link_cmd.c_str()); if (link_ret) { fprintf(stderr, "error: linker failed (exit %d)\n", link_ret); return 1; } remove(obj_path.c_str()); remove(asm_obj.c_str()); } return 0; }