#ifndef PARSER_HPP #define PARSER_HPP #include #include #include // Hibaüzenetekhez #include #include #include // Szükséges header-ök a Lexer és Symbol komponensekből #include "Lexer/Token.hpp" #include "Lexer/TokenType.hpp" // Enum és TypeToString #include "Symbols/SymbolContainer.hpp" #include "Symbols/SymbolFactory.hpp" #include "Symbols/Value.hpp" // Symbols::Value miatt namespace Parser { class SyntaxError : public std::runtime_error { public: SyntaxError(const std::string & message, const int line, const int col) : std::runtime_error(message + " at line " + std::to_string(line) + ", column " + std::to_string(col)) {} SyntaxError(const std::string & message, const Lexer::Tokens::Token & token) : SyntaxError( message + " (found token: '" + token.value + "' type: " + Lexer::Tokens::TypeToString(token.type) + ")", token.line_number, token.column_number) {} }; class Parser { public: Parser() {} void parseProgram(const std::vector & tokens, std::string_view input_string) { tokens_ = tokens; input_str_view_ = input_string; current_token_index_ = 0; symbol_container_ = std::make_unique(); try { while (!isAtEnd() && currentToken().type != Lexer::Tokens::Type::END_OF_FILE) { parseStatement(); } if (!isAtEnd() && currentToken().type != Lexer::Tokens::Type::END_OF_FILE) { reportError("Unexpected tokens after program end"); } } catch (const SyntaxError & e) { std::cerr << "Syntax Error: " << e.what() << '\n'; } catch (const std::exception & e) { std::cerr << "Error during parsing: " << e.what() << '\n'; throw; } } const std::shared_ptr & getSymbolContainer() const { if (!symbol_container_) { throw std::runtime_error("Symbol container is not initialized."); } return symbol_container_; } static const std::unordered_map keywords; static const std::unordered_map variable_types; private: std::vector tokens_; std::string_view input_str_view_; size_t current_token_index_; std::shared_ptr symbol_container_; // Token Stream Kezelő és Hibakezelő segédfüggvények (változatlanok) const Lexer::Tokens::Token & currentToken() const { if (isAtEnd()) { // Technikailag itt már nem kellene lennünk, ha a parseProgram ciklus jól van megírva // De biztonsági ellenőrzésként jó lehet if (!tokens_.empty() && tokens_.back().type == Lexer::Tokens::Type::END_OF_FILE) { return tokens_.back(); // Visszaadjuk az EOF tokent } throw std::runtime_error("Unexpected end of token stream reached."); } return tokens_[current_token_index_]; } // Előre néz a token stream-ben const Lexer::Tokens::Token & peekToken(size_t offset = 1) const { if (current_token_index_ + offset >= tokens_.size()) { // EOF vagy azon túl vagyunk, adjuk vissza az utolsó tokent (ami EOF kell legyen) if (!tokens_.empty()) { return tokens_.back(); } throw std::runtime_error("Cannot peek beyond end of token stream."); } return tokens_[current_token_index_ + offset]; } // Elfogyasztja (lépteti az indexet) az aktuális tokent és visszaadja azt Lexer::Tokens::Token consumeToken() { if (isAtEnd()) { throw std::runtime_error("Cannot consume token at end of stream."); } return tokens_[current_token_index_++]; } // Ellenőrzi, hogy az aktuális token típusa megegyezik-e a várttal. // Ha igen, elfogyasztja és true-t ad vissza. Ha nem, false-t ad vissza. bool match(Lexer::Tokens::Type expected_type) { if (isAtEnd()) { return false; } if (currentToken().type == expected_type) { consumeToken(); return true; } return false; } // Ellenőrzi, hogy az aktuális token típusa és értéke megegyezik-e a várttal. // Csak OPERATOR és PUNCTUATION esetén érdemes használni az érték ellenőrzést. bool match(Lexer::Tokens::Type expected_type, const std::string & expected_value) { if (isAtEnd()) { return false; } const auto & token = currentToken(); if (token.type == expected_type && token.value == expected_value) { consumeToken(); return true; } return false; } Lexer::Tokens::Token expect(Lexer::Tokens::Type expected_type) { if (isAtEnd()) { reportError("Unexpected end of file, expected token type: " + Lexer::Tokens::TypeToString(expected_type)); } const auto & token = currentToken(); if (token.type == expected_type) { return consumeToken(); } reportError("Expected token type " + Lexer::Tokens::TypeToString(expected_type)); // A reportError dob, ez a return sosem fut le, de a fordító kedvéért kellhet: return token; // Vagy dobjon a reportError } // Mint az expect, de az értékét is ellenőrzi. Lexer::Tokens::Token expect(Lexer::Tokens::Type expected_type, const std::string & expected_value) { if (isAtEnd()) { reportError("Unexpected end of file, expected token: " + Lexer::Tokens::TypeToString(expected_type) + " with value '" + expected_value + "'"); } const auto & token = currentToken(); if (token.type == expected_type && token.value == expected_value) { return consumeToken(); } reportError("Expected token " + Lexer::Tokens::TypeToString(expected_type) + " with value '" + expected_value + "'"); return token; // reportError dob } // Ellenőrzi, hogy a releváns tokenek végére értünk-e (az EOF előtti utolsó tokenen vagyunk-e) bool isAtEnd() const { // Akkor vagyunk a végén, ha az index a tokenek méretével egyenlő, // vagy ha már csak az EOF token van hátra (ha az a lista utolsó eleme). return current_token_index_ >= tokens_.size() || (current_token_index_ == tokens_.size() - 1 && tokens_.back().type == Lexer::Tokens::Type::END_OF_FILE); } // --- Hibakezelés --- // Hiba jelentése és kivétel dobása [[noreturn]] void reportError(const std::string & message) { // Használjuk az aktuális token pozícióját, ha még nem értünk a végére if (current_token_index_ < tokens_.size()) { throw SyntaxError(message, tokens_[current_token_index_]); } // Ha már a végén vagyunk, az utolsó ismert pozíciót használjuk int line = tokens_.empty() ? 0 : tokens_.back().line_number; int col = tokens_.empty() ? 0 : tokens_.back().column_number; throw SyntaxError(message, line, col); } // --- Elemzési Módszerek (Moduláris részek) --- // parseStatement (változatlan) void parseStatement() { const auto & token_type = currentToken().type; if (token_type == Lexer::Tokens::Type::KEYWORD_FUNCTION) { parseFunctionDefinition(); return; } for (const auto & _type : Parser::Parser::variable_types) { if (token_type == _type.first) { parseVariableDefinition(); return; } } reportError("Unexpected token at beginning of statement"); } // parseVariableDefinition (SymbolFactory használata már korrekt volt) void parseVariableDefinition() { Symbols::Variables::Type var_type_enum = parseType(); // A típus stringjének tárolása csak a debug kiíráshoz kell //std::string var_type_str = tokens_[current_token_index_ - 1].value; std::cout << "var_name: " << currentToken().lexeme << std::endl; Lexer::Tokens::Token id_token = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER); std::string var_name = id_token.value; // Levágjuk a '$' jelet, ha a lexer mégis benne hagyta if (!var_name.empty() && var_name[0] == '$') { var_name = var_name.substr(1); } expect(Lexer::Tokens::Type::OPERATOR_ASSIGNMENT, "="); Symbols::Value initial_value = parseValue(var_type_enum); expect(Lexer::Tokens::Type::PUNCTUATION, ";"); std::string context = "global"; // Globális változó // SymbolFactory használata a létrehozáshoz auto variable_symbol = Symbols::SymbolFactory::createVariable(var_name, initial_value, context, var_type_enum); // Ellenőrzés és definíció az *aktuális* scope-ban (ami itt a globális) if (symbol_container_->exists("variables", var_name)) { reportError("Variable '" + var_name + "' already defined in this scope"); } symbol_container_->define("variables", variable_symbol); // Debugging kiírás (változatlan) // std::cout << "Parsed variable: " << var_type_str << " " << id_token.value << " = ... ;\n"; } // *** MÓDOSÍTOTT parseFunctionDefinition *** void parseFunctionDefinition() { expect(Lexer::Tokens::Type::KEYWORD_FUNCTION); Lexer::Tokens::Token id_token = expect(Lexer::Tokens::Type::IDENTIFIER); std::string func_name = id_token.value; Symbols::Variables::Type func_return_type = Symbols::Variables::Type::NULL_TYPE; expect(Lexer::Tokens::Type::OPERATOR_ASSIGNMENT, "="); expect(Lexer::Tokens::Type::PUNCTUATION, "("); Symbols::FunctionParameterInfo param_infos; if (currentToken().type != Lexer::Tokens::Type::PUNCTUATION || currentToken().value != ")") { while (true) { // Paraméter típusa //size_t type_token_index = current_token_index_; // Elmentjük a típus token indexét Symbols::Variables::Type param_type = parseType(); // Ez elfogyasztja a type tokent // Paraméter név ($variable) Lexer::Tokens::Token param_id_token = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER); std::string param_name = param_id_token.value; if (!param_name.empty() && param_name[0] == '$') { // '$' eltávolítása param_name = param_name.substr(1); } param_infos.push_back({ param_name, param_type }); // Vessző vagy zárójel következik? if (match(Lexer::Tokens::Type::PUNCTUATION, ",")) { continue; } if (currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == ")") { break; // Lista vége } reportError("Expected ',' or ')' in parameter list"); } } // Most a ')' következik expect(Lexer::Tokens::Type::PUNCTUATION, ")"); // check if we have a option return type: function name() type { ... } for (const auto & _type : Parser::variable_types) { if (match(_type.first)) { func_return_type = _type.second; break; } } auto function_symbol = Symbols::SymbolFactory::createFunction(func_name, "global", param_infos, "", func_return_type); if (symbol_container_->exists("functions", func_name)) { reportError("Function '" + func_name + "' already defined in this scope"); } Lexer::Tokens::Token opening_brace = expect(Lexer::Tokens::Type::PUNCTUATION, "{"); symbol_container_->define("functions", function_symbol); // only parse the body if we checked out if not exists the function and created the symbol Symbols::SymbolContainer func_symbols = parseFunctionBody(opening_brace, func_return_type != Symbols::Variables::Type::NULL_TYPE); // create new container for the function std::cout << "Defined function symbol: " << func_name << " in global scope.\n"; // 3. Új scope nyitása a függvény paraméterei (és később lokális változói) számára symbol_container_->enterScope(); //std::cout << "Entered scope for function: " << func_name << "\n"; // 4. Paraméterek definiálása mint változók az *új* (függvény) scope-ban const std::string & param_context = func_name; // Paraméter kontextusa a függvény neve for (const auto & p_info : param_infos) { auto param_symbol = Symbols::SymbolFactory::createVariable(p_info.name, // Név '$' nélkül Symbols::Value(), // Alapértelmezett/üres érték param_context, // Kontextus p_info.type // Típus ); if (symbol_container_->exists("variables", p_info.name)) { reportError("Parameter name '" + p_info.name + "' conflicts with another symbol in function '" + func_name + "'"); } symbol_container_->define("variables", param_symbol); } // 5. Függvény scope elhagyása symbol_container_->leaveScope(); //std::cout << "Left scope for function: " << func_name << "\n"; // Debugging kiírás (változatlan) // std::cout << "Parsed function: " << func_name << " (...) { ... } scope handled.\n"; } // --- Elemzési Segédfüggvények --- // type : KEYWORD_STRING | KEYWORD_INT | KEYWORD_DOUBLE // Visszaadja a megfelelő Symbols::Variables::Type enum értéket és elfogyasztja a tokent. Symbols::Variables::Type parseType() { const auto & token = currentToken(); for (const auto & _type : Parser::variable_types) { if (token.type == _type.first) { consumeToken(); return _type.second; } } reportError("Expected type keyword (string, int, double, float)"); } // value : STRING_LITERAL | NUMBER // Feldolgozza az értéket a várt típus alapján. Symbols::Value parseValue(Symbols::Variables::Type expected_var_type) { Lexer::Tokens::Token token = currentToken(); /// find if this is a function call if (token.type == Lexer::Tokens::Type::IDENTIFIER && peekToken().lexeme == "(") { for (const auto & symbol_ptr : this->symbol_container_->listNamespace("functions")) { if (auto func_symbol = std::dynamic_pointer_cast(symbol_ptr)) { // TODO: A függvény hívását kellene feldolgozni, a func_symbol-ból kellene lekérni a paramétereket, func_symbol->plainBody() tartalmazza a függvény törzsét // A függvény hívásának feldolgozása std::cout << "Function call: " << token.value << "\n"; while (consumeToken().lexeme != ")") { // Feltételezzük, hogy a függvény hívását a lexer már feldolgozta } return Symbols::Value(""); // TODO: Implementálni a függvény hívását } } } bool is_negative = false; std::cout << "Peek: " << peekToken().lexeme << "\n"; if (token.type == Lexer::Tokens::Type::OPERATOR_ARITHMETIC && peekToken().type == Lexer::Tokens::Type::NUMBER) { is_negative = true; token = peekToken(); consumeToken(); } if (expected_var_type == Symbols::Variables::Type::STRING) { if (token.type == Lexer::Tokens::Type::STRING_LITERAL) { consumeToken(); return Symbols::Value(token.value); // A lexer value-ja már a feldolgozott string } reportError("Expected string literal value"); } else if (expected_var_type == Symbols::Variables::Type::INTEGER) { if (token.type == Lexer::Tokens::Type::NUMBER) { // Konvertálás int-re, hibakezeléssel try { // TODO: Ellenőrizni, hogy a szám valóban egész-e (nincs benne '.') // Most egyszerűen std::stoi-t használunk. int int_value = std::stoi(token.value); if (is_negative) { int_value = -int_value; } consumeToken(); return Symbols::Value(int_value); } catch (const std::invalid_argument & e) { reportError("Invalid integer literal: " + token.value); } catch (const std::out_of_range & e) { reportError("Integer literal out of range: " + token.value); } } reportError("Expected integer literal value"); } else if (expected_var_type == Symbols::Variables::Type::DOUBLE) { if (token.type == Lexer::Tokens::Type::NUMBER) { // Konvertálás double-re, hibakezeléssel try { double double_value = std::stod(token.value); if (is_negative) { double_value = -double_value; } consumeToken(); return Symbols::Value(double_value); } catch (const std::invalid_argument & e) { reportError("Invalid double literal: " + token.value); } catch (const std::out_of_range & e) { reportError("Double literal out of range: " + token.value); } } reportError("Expected numeric literal value for double"); } else if (expected_var_type == Symbols::Variables::Type::FLOAT) { if (token.type == Lexer::Tokens::Type::NUMBER) { // Konvertálás double-re, hibakezeléssel try { float float_value = std::atof(token.value.data()); if (is_negative) { float_value = -float_value; } consumeToken(); return Symbols::Value(float_value); } catch (const std::invalid_argument & e) { reportError("Invalid float literal: " + token.value); } catch (const std::out_of_range & e) { reportError("Float literal out of range: " + token.value); } } reportError("Expected numeric literal value for double"); } else if (expected_var_type == Symbols::Variables::Type::BOOLEAN) { if (token.type == Lexer::Tokens::Type::KEYWORD) { consumeToken(); return Symbols::Value(token.value == "true"); // A lexer value-ja már a feldolgozott string } reportError("Expected boolean literal value"); } else { // Más típusok (pl. boolean) itt kezelendők, ha lennének reportError("Unsupported variable type encountered during value parsing"); } // Should not reach here due to reportError throwing return Symbols::Value(); // Default return to satisfy compiler } Symbols::SymbolContainer parseFunctionBody(const Lexer::Tokens::Token & opening_brace, bool return_required = false) { size_t braceDepth = 0; int tokenIndex = current_token_index_; Lexer::Tokens::Token currentToken_; Lexer::Tokens::Token closing_brace; while (tokenIndex < tokens_.size()) { currentToken_ = peekToken(); if (currentToken_.type == Lexer::Tokens::Type::PUNCTUATION) { if (currentToken_.value == "{") { ++braceDepth; } else if (currentToken_.value == "}") { if (braceDepth == 0) { closing_brace = currentToken_; break; } --braceDepth; } } } if (braceDepth != 0) { reportError("Unmatched braces in function body"); } std::vector filtered_tokens; auto startIt = std::find(tokens_.begin(), tokens_.end(), opening_brace); auto endIt = std::find(tokens_.begin(), tokens_.end(), closing_brace); // Ellenőrzés: mindkét token megtalálva és start_token megelőzi az end_token-t if (startIt != tokens_.end() && endIt != tokens_.end() && startIt < endIt) { filtered_tokens = std::vector(startIt + 1, endIt); } std::string_view input_string = input_str_view_.substr(opening_brace.end_pos, closing_brace.end_pos); auto parser = Parser(); parser.parseProgram(filtered_tokens, input_string); return *parser.getSymbolContainer(); } std::string parseFunctionBodyOld(size_t body_start_pos, bool return_required = false) { std::stringstream body_ss; int brace_level = 1; size_t last_token_end_pos = body_start_pos; bool has_return = false; while (true) { if (isAtEnd()) { reportError("Unexpected end of file inside function body (missing '}')"); } const auto & token = currentToken(); // Whitespace visszaállítása (ha volt) az 'input_str_view_' alapján if (token.start_pos > last_token_end_pos) { if (last_token_end_pos < input_str_view_.length() && token.start_pos <= input_str_view_.length()) { body_ss << input_str_view_.substr(last_token_end_pos, token.start_pos - last_token_end_pos); } else { reportError("Invalid position range in body reconstruction"); } } if (token.type == Lexer::Tokens::Type::KEYWORD_RETURN) { has_return = true; } if (token.type == Lexer::Tokens::Type::PUNCTUATION && token.value == "{") { brace_level++; body_ss << token.lexeme; } else if (token.type == Lexer::Tokens::Type::PUNCTUATION && token.value == "}") { brace_level--; if (brace_level == 0) { consumeToken(); // Záró '}' elfogyasztása break; } body_ss << token.lexeme; } else { body_ss << token.lexeme; } last_token_end_pos = token.end_pos; consumeToken(); } if (return_required && !has_return) { return ""; } return body_ss.str(); } }; // class Parser } // namespace Parser #endif // PARSER_HPP