#ifndef PARSER_HPP #define PARSER_HPP #include #include #include #include "BaseException.hpp" #include "Lexer/Token.hpp" #include "Lexer/TokenType.hpp" #include "Parser/ParsedExpression.hpp" #include "Symbols/ParameterContainer.hpp" #include "Symbols/Value.hpp" namespace Parser { class Parser { public: Parser() {} class Exception : public BaseException { public: using BaseException::BaseException; Exception(const std::string & msg, const std::string & expected, const Lexer::Tokens::Token & token) { rawMessage_ = msg + ": " + token.dump(); context_ = " at line: " + std::to_string(token.line_number) + ", column: " + std::to_string(token.column_number); if (expected.empty() == false) { rawMessage_ += " (expected: " + expected + ")"; } formattedMessage_ = formatMessage(); } Exception(const std::string & msg, std::string & expected, int line, int col) { rawMessage_ = msg; if (expected.empty() == false) { rawMessage_ += " (expected: " + expected + ")"; } context_ = " at line: " + std::to_string(line) + ", column: " + std::to_string(col); formattedMessage_ = formatMessage(); } std::string formatMessage() const override { return "[Syntax ERROR] >>" + context_ + " << : " + rawMessage_; } }; void parseScript(const std::vector & tokens, std::string_view input_string, const std::string & filename); 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::string current_filename_; // 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); } [[noreturn]] void reportError(const std::string & message, const std::string expected = "") { if (current_token_index_ < tokens_.size()) { throw Exception(message, expected, tokens_[current_token_index_]); } int line = tokens_.empty() ? 0 : tokens_.back().line_number; int col = tokens_.empty() ? 0 : tokens_.back().column_number; throw Exception(message, expected, line, col); } // parseStatement (változatlan) void parseStatement() { const auto & token_type = currentToken().type; if (token_type == Lexer::Tokens::Type::KEYWORD_FUNCTION_DECLARATION) { parseFunctionDefinition(); return; } for (const auto & _type : Parser::Parser::variable_types) { if (token_type == _type.first) { parseVariableDefinition(); return; } } reportError("Unexpected token at beginning of statement"); } void parseVariableDefinition(); void parseFunctionDefinition(); // --- 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)"); } Symbols::Value parseValue(Symbols::Variables::Type expected_var_type) { Lexer::Tokens::Token token = currentToken(); bool is_negative = false; // Előjel kezelése if (token.type == Lexer::Tokens::Type::OPERATOR_ARITHMETIC && (token.lexeme == "-" || token.lexeme == "+") && peekToken().type == Lexer::Tokens::Type::NUMBER) { is_negative = (token.lexeme == "-"); token = peekToken(); consumeToken(); // előjelet elfogyasztottuk } // STRING típus if (expected_var_type == Symbols::Variables::Type::STRING) { if (token.type == Lexer::Tokens::Type::STRING_LITERAL) { consumeToken(); return Symbols::Value(token.value); } reportError("Expected string literal value"); } // BOOLEAN típus if (expected_var_type == Symbols::Variables::Type::BOOLEAN) { if (token.type == Lexer::Tokens::Type::KEYWORD && (token.value == "true" || token.value == "false")) { consumeToken(); return Symbols::Value(token.value == "true"); } reportError("Expected boolean literal value (true or false)"); } // NUMERIC típusok if (expected_var_type == Symbols::Variables::Type::INTEGER || expected_var_type == Symbols::Variables::Type::DOUBLE || expected_var_type == Symbols::Variables::Type::FLOAT) { if (token.type == Lexer::Tokens::Type::NUMBER) { Symbols::Value val = parseNumericLiteral(token.value, is_negative, expected_var_type); consumeToken(); return val; } reportError("Expected numeric literal value"); } reportError("Unsupported variable type encountered during value parsing"); return Symbols::Value(); // compiler happy } Symbols::Value parseNumericLiteral(const std::string & value, bool is_negative, Symbols::Variables::Type type); void parseFunctionBody(const Lexer::Tokens::Token & opening_brace, const std::string & function_name, Symbols::Variables::Type return_type, const Symbols::FunctionParameterInfo & params); ParsedExpressionPtr parseParsedExpression(const Symbols::Variables::Type & expected_var_type); }; // class Parser } // namespace Parser #endif // PARSER_HPP