#include "preprocessor.h" #include #include #include #include #include namespace fs = std::filesystem; static std::string ltrim(std::string s) { const size_t first = s.find_first_not_of(" \t"); if (first == std::string::npos) return ""; return s.substr(first); } static std::string loadFileContent(const std::string& path) { std::ifstream in(path, std::ios::binary); if (!in) { std::cerr << "[PREPROCESSOR] Error: Could not open included file: " << path << "\n"; return ""; } return std::string((std::istreambuf_iterator(in)), std::istreambuf_iterator()); } // Próbuje sparsować #include "x" albo #include // Zwraca: true jeśli linia to include i udało się wyciągnąć ścieżkę. static bool parseIncludeLine(const std::string& line, std::string& includePath, bool& isStdLib) { includePath.clear(); isStdLib = false; std::string t = ltrim(line); if (t.rfind("#include", 0) != 0) return false; const size_t openQuote = t.find('"'); const size_t closeQuote = t.rfind('"'); const size_t openAngle = t.find('<'); const size_t closeAngle = t.rfind('>'); if (openQuote != std::string::npos && closeQuote != std::string::npos && closeQuote > openQuote) { includePath = t.substr(openQuote + 1, closeQuote - openQuote - 1); isStdLib = false; return true; } if (openAngle != std::string::npos && closeAngle != std::string::npos && closeAngle > openAngle) { includePath = t.substr(openAngle + 1, closeAngle - openAngle - 1); isStdLib = true; return true; } // To jest #include ale w złym formacie (np. brak cudzysłowu / nawiasów) return true; } // Wewnętrzna funkcja z guardem na rekurencję. static std::string preprocessSourceImpl( const std::string& src, const std::string& projectDir, const std::string& compilerDir, std::unordered_set& includeGuard ) { std::istringstream iss(src); std::string line; std::stringstream output; while (std::getline(iss, line)) { std::string includePath; bool isStdLib = false; const bool isIncludeLine = parseIncludeLine(line, includePath, isStdLib); if (!isIncludeLine) { output << line << "\n"; continue; } if (includePath.empty()) { output << "\n// [PREPROCESSOR] Invalid include (no path): " << line << "\n"; continue; } // Złóż fullPath fs::path fullPath; if (isStdLib) { fullPath = fs::path(compilerDir) / "std" / includePath; std::cout << "[PREPROCESSOR] Including STD lib: " << fullPath.string() << "\n"; } else { if (projectDir.empty()) fullPath = fs::path(includePath); else fullPath = fs::path(projectDir) / includePath; std::cout << "[PREPROCESSOR] Including local file: " << fullPath.string() << "\n"; } // Normalizacja ścieżki (jeśli się da) std::string guardKey; try { guardKey = fs::weakly_canonical(fullPath).string(); } catch (...) { guardKey = fullPath.lexically_normal().string(); } // Guard przeciw include-loop oraz wielokrotnemu includowaniu if (includeGuard.find(guardKey) != includeGuard.end()) { output << "\n// [PREPROCESSOR] Skipped include (already included): " << includePath << "\n"; continue; } includeGuard.insert(guardKey); // Wczytaj + przetwórz rekurencyjnie std::string content = loadFileContent(fullPath.string()); if (content.empty()) { output << "\n// [PREPROCESSOR] FAILED INCLUDE: " << includePath << "\n"; continue; } std::string processedContent = preprocessSourceImpl(content, projectDir, compilerDir, includeGuard); output << "\n// --- BEGIN INCLUDE: " << includePath << " ---\n"; output << processedContent; output << "\n// --- END INCLUDE ---\n"; } return output.str(); } std::string preprocessSource(const std::string& src, const std::string& projectDir, const std::string& compilerDir) { std::unordered_set includeGuard; return preprocessSourceImpl(src, projectDir, compilerDir, includeGuard); }