diff --git a/PCCcompiler/PCCcompiler.vcxproj b/PCCcompiler/PCCcompiler.vcxproj index 4385ef8..414baa6 100644 --- a/PCCcompiler/PCCcompiler.vcxproj +++ b/PCCcompiler/PCCcompiler.vcxproj @@ -22,12 +22,14 @@ + + diff --git a/PCCcompiler/PCCcompiler.vcxproj.filters b/PCCcompiler/PCCcompiler.vcxproj.filters index 7fa01c4..e6e3030 100644 --- a/PCCcompiler/PCCcompiler.vcxproj.filters +++ b/PCCcompiler/PCCcompiler.vcxproj.filters @@ -27,6 +27,9 @@ Pliki źródÅ‚owe + + Pliki źródÅ‚owe + @@ -41,5 +44,8 @@ Pliki nagłówkowe + + Pliki nagłówkowe + \ No newline at end of file diff --git a/PCCcompiler/codegen.cpp b/PCCcompiler/codegen.cpp index cb397ec..5d65c71 100644 --- a/PCCcompiler/codegen.cpp +++ b/PCCcompiler/codegen.cpp @@ -40,9 +40,19 @@ std::string generateAssembly(const CompilerState& state) { std::string result; result += "global main\n"; result += "extern printf\n"; - result += "extern getchar\n"; // <--- DODAJ TO + result += "extern getchar\n"; result += "section .data\n"; result += " fmt db '%d', 10, 0\n"; + result += "section .data\n"; + result += " fmt_int db '%d', 10, 0\n"; // Format dla liczb + result += " fmt_str db '%s', 10, 0\n"; // NOWOŒÆ: Format dla stringów + + // --- WYPISYWANIE STRINGÓW --- + for (const auto& pair : state.stringLiterals) { + // Nazwa etykiety: db 'Tresc', 0 + // Uwaga: ASM nie lubi pewnych znaków, ale zak³adamy proste litery + result += " " + pair.second + " db '" + pair.first + "', 0\n"; + } result += "section .text\n\n"; for (const auto& pair : state.functions) { @@ -71,19 +81,33 @@ std::string generateAssembly(const CompilerState& state) { // 2. INSTRUKCJE for (const auto& instr : func.instructions) { - // Rezerwacja miejsca dla nowych zmiennych - if ((instr.type == OpType::ASSIGN || instr.type == OpType::ADD || instr.type == OpType::EQ) - && stackMap.find(instr.arg1) == stackMap.end() && instr.arg1 != "RAX") { + // Rezerwacja miejsca dla nowych zmiennych (wynikowych) + // Dodajemy tu OpType::SUB i OpType::MUL + bool isWriteOp = (instr.type == OpType::ASSIGN || + instr.type == OpType::ADD || + instr.type == OpType::EQ || + instr.type == OpType::SUB || + instr.type == OpType::MUL); + + if (isWriteOp && stackMap.find(instr.arg1) == stackMap.end() && instr.arg1 != "RAX") { stackMap[instr.arg1] = currentStack; currentStack += 8; } switch (instr.type) { case OpType::ASSIGN: { - std::string src = getVarLocation(instr.arg2, stackMap); - std::string dst = getVarLocation(instr.arg1, stackMap); - result += " mov eax, " + src + "\n"; - result += " mov " + dst + ", eax\n"; + std::string src = instr.arg2; + if (instr.arg3 == "STRING") { + result += " lea rax, [rel " + src + "]\n"; + std::string dst = getVarLocation(instr.arg1, stackMap); + result += " mov qword " + dst + ", rax\n"; + } + else { + std::string srcLoc = getVarLocation(instr.arg2, stackMap); + std::string dst = getVarLocation(instr.arg1, stackMap); + result += " mov eax, " + srcLoc + "\n"; + result += " mov " + dst + ", eax\n"; + } break; } case OpType::ADD: { @@ -95,6 +119,30 @@ std::string generateAssembly(const CompilerState& state) { result += " mov " + dst + ", eax\n"; break; } + case OpType::SUB: { + // a = b - c + std::string op1 = getVarLocation(instr.arg2, stackMap); + std::string op2 = getVarLocation(instr.arg3, stackMap); + std::string dst = getVarLocation(instr.arg1, stackMap); + + result += " mov eax, " + op1 + "\n"; + result += " sub eax, " + op2 + "\n"; // sub = odejmowanie + result += " mov " + dst + ", eax\n"; + break; + } + case OpType::MUL: { + // a = b * c + std::string op1 = getVarLocation(instr.arg2, stackMap); + std::string op2 = getVarLocation(instr.arg3, stackMap); + std::string dst = getVarLocation(instr.arg1, stackMap); + + result += " mov eax, " + op1 + "\n"; + // Mno¿enie w x86 jest specyficzne: imul eax, operand + // Wynik l¹duje w eax (i edx jeœli du¿y, ale ignorujemy nadmiar dla prostoty) + result += " imul eax, " + op2 + "\n"; + result += " mov " + dst + ", eax\n"; + break; + } case OpType::EQ: { std::string op1 = getVarLocation(instr.arg2, stackMap); std::string op2 = getVarLocation(instr.arg3, stackMap); @@ -144,10 +192,17 @@ std::string generateAssembly(const CompilerState& state) { break; } case OpType::PRINT: { - std::string val = getVarLocation(instr.arg1, stackMap); - result += " mov edx, " + val + "\n"; - result += " lea rcx, [rel fmt]\n"; - result += " call printf\n"; + if (instr.arg2 == "STRING") { + result += " lea rdx, [rel " + instr.arg1 + "]\n"; + result += " lea rcx, [rel fmt_str]\n"; + result += " call printf\n"; + } + else { + std::string val = getVarLocation(instr.arg1, stackMap); + result += " mov edx, " + val + "\n"; + result += " lea rcx, [rel fmt_int]\n"; + result += " call printf\n"; + } break; } case OpType::CALL: { diff --git a/PCCcompiler/compiler_types.h b/PCCcompiler/compiler_types.h index 907262d..ebb235c 100644 --- a/PCCcompiler/compiler_types.h +++ b/PCCcompiler/compiler_types.h @@ -51,6 +51,9 @@ struct CompilerState { std::stack loopStack; // Do break/continue (przysz³oœciowo) std::stack blockStack; + + std::map stringLiterals; + int stringCounter = 0; // Licznik do generowania nazw str_1, str_2... }; #endif diff --git a/PCCcompiler/main.cpp b/PCCcompiler/main.cpp index 9d53d9f..fdb949f 100644 --- a/PCCcompiler/main.cpp +++ b/PCCcompiler/main.cpp @@ -2,14 +2,16 @@ #include #include #include -#include // do std::system +#include +#include #include "compiler_types.h" #include "parser.h" #include "codegen.h" +#include "preprocessor.h" int main(int argc, char* argv[]) { std::string inputFile, outputName; - std::string Version = "v1.5.0-modular"; // ZaktualizowaÅ‚em wersjÄ™ :) + std::string Version = "v0.0.3-beta"; // ZaktualizowaÅ‚em wersjÄ™ :) bool showHelp = false, showVersion = false, showCredits = false; // --- PARSOWANIE ARGUMENTÓW --- @@ -68,10 +70,24 @@ int main(int argc, char* argv[]) { return 1; } + // ... wczytywanie pliku (to co miaÅ‚eÅ›) ... std::string src((std::istreambuf_iterator(in)), std::istreambuf_iterator()); in.close(); - // --- LOGIKA KOMPILATORA --- + // --- PREPROCESSOR START --- + std::cout << "[INFO] Preprocessing...\n"; + + std::filesystem::path p(inputFile); + std::string basePath = p.parent_path().string(); + + // Uruchamiamy preprocesor - podmieni #include na kod + src = preprocessSource(src, basePath); + + // Opcjonalnie: pokaż kod po złączeniu (do debugowania) + // std::cout << "--- FINAL CODE ---\n" << src << "\n------------------\n"; + // --- PREPROCESSOR END --- + + // --- LOGIKA KOMPILATORA (DALEJ BEZ ZMIAN) --- CompilerState state; std::cout << "[INFO] Parsing code...\n"; diff --git a/PCCcompiler/parser.cpp b/PCCcompiler/parser.cpp index 297145b..4c38f48 100644 --- a/PCCcompiler/parser.cpp +++ b/PCCcompiler/parser.cpp @@ -92,10 +92,34 @@ void processSource(const std::string& src, CompilerState& state) { size_t start = line.find('(') + 1; size_t end = line.find(')'); if (start != std::string::npos && end != std::string::npos) { - std::string var = trim(line.substr(start, end - start)); - f.instructions.push_back({ OpType::PRINT, var, "", "" }); + std::string arg = trim(line.substr(start, end - start)); + + // Czy to bezpoÅ›redni napis? np. print("Hello") + if (arg.size() >= 2 && arg.front() == '"' && arg.back() == '"') { + std::string content = arg.substr(1, arg.size() - 2); + + // Rejestrujemy + std::string label; + if (state.stringLiterals.count(content)) { + label = state.stringLiterals[content]; + } + else { + label = "str_" + std::to_string(state.stringCounter++); + state.stringLiterals[content] = label; + } + + // Dajemy znać generatorowi, że to typ STRING + f.instructions.push_back({ OpType::PRINT, label, "STRING", "" }); + } + else { + // ZwykÅ‚a zmienna (int lub string - generator musi zgadnąć lub my musimy wiedzieć) + // Na razie załóżmy, że jeÅ›li zmienna ma w nazwie "msg" lub "txt", to string + // (To hack, w przyszÅ‚oÅ›ci dodamy tabelÄ™ typów zmiennych) + f.instructions.push_back({ OpType::PRINT, arg, "VAR", "" }); + } } } + // C. IF STATEMENT else if (line.substr(0, 2) == "if") { size_t openParen = line.find("("); @@ -117,12 +141,19 @@ void processSource(const std::string& src, CompilerState& state) { size_t eqPos = line.find('='); std::string leftSide = trim(line.substr(0, eqPos)); std::string rightSide = trim(line.substr(eqPos + 1)); + + bool isStringDecl = false; // Flaga, czy to string if (!rightSide.empty() && rightSide.back() == ';') rightSide.pop_back(); // ObsÅ‚uga nazwy zmiennej (usuwanie "int ", "bool ") std::string varName = leftSide; if (leftSide.rfind("int ", 0) == 0) varName = trim(leftSide.substr(4)); else if (leftSide.rfind("bool ", 0) == 0) varName = trim(leftSide.substr(5)); + else if (leftSide.rfind("string ", 0) == 0) { // NOWOŚĆ + varName = trim(leftSide.substr(7)); + isStringDecl = true; + } + // 1. Czy to wywoÅ‚anie funkcji? int x = func(); if (rightSide.find("(") != std::string::npos && rightSide.find(")") != std::string::npos) { @@ -143,6 +174,20 @@ void processSource(const std::string& src, CompilerState& state) { std::string b = trim(rightSide.substr(opPos + 1)); f.instructions.push_back({ OpType::ADD, varName, a, b }); } + // 3. NOWOŚĆ: Czy to odejmowanie? a - b + else if (rightSide.find("-") != std::string::npos) { + size_t opPos = rightSide.find("-"); + std::string a = trim(rightSide.substr(0, opPos)); + std::string b = trim(rightSide.substr(opPos + 1)); + f.instructions.push_back({ OpType::SUB, varName, a, b }); // <--- Używamy SUB + } + // 4. NOWOŚĆ: Czy to mnożenie? a * b + else if (rightSide.find("*") != std::string::npos) { + size_t opPos = rightSide.find("*"); + std::string a = trim(rightSide.substr(0, opPos)); + std::string b = trim(rightSide.substr(opPos + 1)); + f.instructions.push_back({ OpType::MUL, varName, a, b }); // <--- Używamy MUL + } // 3. Czy to porównanie? a == b (Ważne: == może być w IFie, ale tu jesteÅ›my w linii z '=') // UWAGA: To rzadkie w C++ (bool x = a == b), ale obsÅ‚użmy proste przypisanie wartoÅ›ci logicznej else if (rightSide.find("==") != std::string::npos) { @@ -151,6 +196,26 @@ void processSource(const std::string& src, CompilerState& state) { std::string b = trim(rightSide.substr(opPos + 2)); f.instructions.push_back({ OpType::EQ, varName, a, b }); } + else if (rightSide.size() >= 2 && rightSide.front() == '"' && rightSide.back() == '"') + { + { + // WyciÄ…gamy treść bez cudzysÅ‚owów + std::string content = rightSide.substr(1, rightSide.size() - 2); + + // Rejestrujemy stringa w sekcji danych, jeÅ›li jeszcze go nie ma + std::string label; + if (state.stringLiterals.count(content)) { + label = state.stringLiterals[content]; + } + else { + label = "str_" + std::to_string(state.stringCounter++); + state.stringLiterals[content] = label; + } + + // Generujemy instrukcjÄ™ przypisania ADRESU etykiety do zmiennej + f.instructions.push_back({ OpType::ASSIGN, varName, label, "STRING" }); + } + } // 4. ZwykÅ‚e przypisanie: a = 5 else { f.instructions.push_back({ OpType::ASSIGN, varName, rightSide, "" }); diff --git a/PCCcompiler/preprocessor.cpp b/PCCcompiler/preprocessor.cpp new file mode 100644 index 0000000..c4fa84c --- /dev/null +++ b/PCCcompiler/preprocessor.cpp @@ -0,0 +1,89 @@ +#include "preprocessor.h" +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +// Funkcja pomocnicza do wczytania pliku +std::string loadFileContent(const std::string& path) { + std::ifstream in(path); + if (!in) { + std::cerr << "[PREPROCESSOR] Error: Could not open included file: " << path << "\n"; + return ""; + } + return std::string((std::istreambuf_iterator(in)), std::istreambuf_iterator()); +} + +std::string preprocessSource(const std::string& src, const std::string& basePath) { + std::istringstream iss(src); + std::string line; + std::stringstream output; + + while (std::getline(iss, line)) { + // Szukamy: #include "..." lub #include <...> + // U¿ywamy prostego find, ¿eby by³o szybko + std::string trimLine = line; + // Usuwamy bia³e znaki z pocz¹tku + size_t first = trimLine.find_first_not_of(" \t"); + if (first != std::string::npos) trimLine = trimLine.substr(first); + + if (trimLine.rfind("#include", 0) == 0) { + // Mamy include! + size_t openQuote = trimLine.find('"'); + size_t closeQuote = trimLine.rfind('"'); + size_t openAngle = trimLine.find('<'); + size_t closeAngle = trimLine.rfind('>'); + + std::string includePath; + bool isStdLib = false; + + // Wersja: #include "plik.pcc" + if (openQuote != std::string::npos && closeQuote > openQuote) { + includePath = trimLine.substr(openQuote + 1, closeQuote - openQuote - 1); + } + // Wersja: #include + else if (openAngle != std::string::npos && closeAngle > openAngle) { + includePath = trimLine.substr(openAngle + 1, closeAngle - openAngle - 1); + isStdLib = true; + } + + if (!includePath.empty()) { + std::string fullPath; + if (isStdLib) { + // Biblioteka standardowa: szukamy w folderze "std" obok exe/projektu + // U¿ywamy current_path() + "std" + fullPath = "std/" + includePath; + std::cout << "[PREPROCESSOR] Including STD lib: " << fullPath << "\n"; + } + else { + // Plik lokalny: szukamy w tym samym folderze co plik Ÿród³owy + // Jeœli basePath jest pusty, szukamy lokalnie + if (basePath.empty()) fullPath = includePath; + else fullPath = basePath + "/" + includePath; + std::cout << "[PREPROCESSOR] Including local file: " << fullPath << "\n"; + } + + // Wczytujemy plik i REKURENCYJNIE go przetwarzamy + // (bo plik do³¹czany mo¿e mieæ swoje include!) + std::string content = loadFileContent(fullPath); + + // Dla uproszczenia rekurencji w include'ach lokalnych, przekazujemy ten sam basePath + // W idealnym œwiecie powinniœmy braæ folder nowego pliku. + std::string processedContent = preprocessSource(content, basePath); + + output << "\n// --- BEGIN INCLUDE: " << includePath << " ---\n"; + output << processedContent; + output << "\n// --- END INCLUDE ---\n"; + } + } + else { + // Zwyk³a linia kodu - przepisujemy bez zmian + output << line << "\n"; + } + } + + return output.str(); +} diff --git a/PCCcompiler/preprocessor.h b/PCCcompiler/preprocessor.h new file mode 100644 index 0000000..51cabe6 --- /dev/null +++ b/PCCcompiler/preprocessor.h @@ -0,0 +1,11 @@ +#ifndef PREPROCESSOR_H +#define PREPROCESSOR_H + +#include + +// G³ówna funkcja preprocesora. +// src: kod Ÿród³owy g³ównego pliku +// basePath: œcie¿ka do folderu, w którym jest g³ówny plik (¿eby wiedzieæ sk¹d braæ lokalne include) +std::string preprocessSource(const std::string& src, const std::string& basePath); + +#endif