diff --git a/lib/include/pl/core/parser.hpp b/lib/include/pl/core/parser.hpp index 9cd21b72..fd2251f6 100644 --- a/lib/include/pl/core/parser.hpp +++ b/lib/include/pl/core/parser.hpp @@ -71,6 +71,13 @@ namespace pl::core { std::string m_aliasNamespaceString; std::string m_autoNamespace; + enum class AllowKeywords : u8 { + None = 0, + Parent, + This, + Both + }; + Location location() override; // error helpers void errorHere(const std::string &message); @@ -173,6 +180,7 @@ namespace pl::core { hlp::safe_unique_ptr parseFunctionStatement(bool needsSemicolon = true); hlp::safe_unique_ptr parseFunctionVariableAssignment(const std::string &lvalue); hlp::safe_unique_ptr parseFunctionVariableCompoundAssignment(const std::string &lvalue); + hlp::safe_unique_ptr parseFunctionVariableCompoundAssignment(TokenIter curr); hlp::safe_unique_ptr parseFunctionControlFlowStatement(); std::vector> parseStatementBody(const std::function()> &memberParser); hlp::safe_unique_ptr parseFunctionWhileLoop(); @@ -210,7 +218,9 @@ namespace pl::core { std::vector> parseNamespace(); std::vector> parseStatements(); - std::optional parseCompoundAssignment(const Token &token); + std::optional isCompoundAssignmentOperator(const Token &token); + std::optional isCompoundAssignmentOperator(AllowKeywords allowKeywords = AllowKeywords::None); + std::optional isCompoundAssignmentOperator(i32 offset); std::optional parseDocComment(bool global); diff --git a/lib/source/pl/core/parser.cpp b/lib/source/pl/core/parser.cpp index aebe3687..c9489a43 100644 --- a/lib/source/pl/core/parser.cpp +++ b/lib/source/pl/core/parser.cpp @@ -934,9 +934,11 @@ namespace pl::core { statement = parseFunctionVariableAssignment(getValue(-2).get()); else if (sequence(tkn::Operator::Dollar, tkn::Operator::Assign)) statement = parseFunctionVariableAssignment("$"); - else if (const auto identifierOffset = parseCompoundAssignment(tkn::Literal::Identifier); identifierOffset.has_value()) + else if (const auto identifierOffset = isCompoundAssignmentOperator(tkn::Literal::Identifier); identifierOffset.has_value()) statement = parseFunctionVariableCompoundAssignment(getValue(*identifierOffset).get()); - else if (parseCompoundAssignment(tkn::Operator::Dollar).has_value()) + else if (const auto curr = isCompoundAssignmentOperator(AllowKeywords::Parent); curr.has_value()) + statement = parseFunctionVariableCompoundAssignment(curr.value()); + else if (isCompoundAssignmentOperator(tkn::Operator::Dollar).has_value()) statement = parseFunctionVariableCompoundAssignment("$"); else if (oneOf(tkn::Keyword::Return, tkn::Keyword::Break, tkn::Keyword::Continue)) statement = parseFunctionControlFlowStatement(); @@ -955,7 +957,7 @@ namespace pl::core { } else if (sequence(tkn::Keyword::For, tkn::Separator::LeftParenthesis)) { statement = parseFunctionForLoop(); needsSemicolon = false; - } else if (MATCHES(sequence(tkn::Literal::Identifier) && (peek(tkn::Separator::Dot) || (peek(tkn::Separator::LeftBracket, 0) && !peek(tkn::Separator::LeftBracket, 1))))) { + } else if (MATCHES((sequence(tkn::Literal::Identifier) || sequence(tkn::Keyword::Parent)) && (peek(tkn::Separator::Dot) || (peek(tkn::Separator::LeftBracket, 0) && !peek(tkn::Separator::LeftBracket, 1))))) { statement = parseRValueAssignment(); } else if (sequence(tkn::Literal::Identifier)) { const auto originalPos = this->m_curr; @@ -1001,6 +1003,30 @@ namespace pl::core { return create(lvalue, std::move(rvalue)); } + hlp::safe_unique_ptr Parser::parseFunctionVariableCompoundAssignment(TokenIter curr) { + m_curr = curr; + auto lhs = parseRValue(); + Token::Operator op = getValue(0); + next(); + if (op == Token::Operator::BoolLessThan) { + op = Token::Operator::LeftShift; + next(); + } else if (op == Token::Operator::BoolGreaterThan) { + op = Token::Operator::RightShift; + next(); + } + next(); + + auto rhs = parseMathematicalExpression(); + if (rhs == nullptr) + return nullptr; + + auto lhsCopy = lhs->clone(); + rhs = create(std::move(lhsCopy), std::move(rhs), op); + + return create(std::move(lhs), std::move(rhs)); + } + hlp::safe_unique_ptr Parser::parseFunctionVariableCompoundAssignment(const std::string &lvalue) { auto op = getValue(-2); @@ -1884,12 +1910,14 @@ namespace pl::core { if (sequence(tkn::Operator::Dollar, tkn::Operator::Assign)) member = parseFunctionVariableAssignment("$"); - else if (parseCompoundAssignment(tkn::Operator::Dollar).has_value()) + else if (isCompoundAssignmentOperator(tkn::Operator::Dollar).has_value()) member = parseFunctionVariableCompoundAssignment("$"); else if (sequence(tkn::Literal::Identifier, tkn::Operator::Assign)) { member = parseFunctionVariableAssignment(getValue(-2).get()); - } else if (const auto identifierOffset = parseCompoundAssignment(tkn::Literal::Identifier); identifierOffset.has_value()) + } else if (const auto identifierOffset = isCompoundAssignmentOperator(tkn::Literal::Identifier); identifierOffset.has_value()) member = parseFunctionVariableCompoundAssignment(getValue(*identifierOffset).get()); + else if (const auto curr = isCompoundAssignmentOperator(AllowKeywords::Both); curr.has_value()) + member = parseFunctionVariableCompoundAssignment(curr.value()); else if (MATCHES((sequence(tkn::Literal::Identifier) || sequence(tkn::Keyword::Parent) || sequence(tkn::Keyword::This)) && (peek(tkn::Separator::Dot) || (peek(tkn::Separator::LeftBracket, 0) && !peek(tkn::Separator::LeftBracket, 1))))) member = parseRValueAssignment(); else if (peek(tkn::Keyword::Const) || peek(tkn::Keyword::BigEndian) || peek(tkn::Keyword::LittleEndian) || peek(tkn::ValueType::Any) || peek(tkn::Literal::Identifier)) { @@ -2160,8 +2188,12 @@ namespace pl::core { if (identifier != nullptr) identifier->setType(Token::Identifier::IdentifierType::LocalVariable); member = parseFunctionVariableAssignment(variableName); - } else if (const auto identifierOffset = parseCompoundAssignment(tkn::Literal::Identifier); identifierOffset.has_value()) + } else if (const auto identifierOffset = isCompoundAssignmentOperator(tkn::Literal::Identifier); identifierOffset.has_value()) member = parseFunctionVariableCompoundAssignment(getValue(*identifierOffset).get()); + else if (const auto curr = isCompoundAssignmentOperator(AllowKeywords::Parent); curr.has_value()) + member = parseFunctionVariableCompoundAssignment(curr.value()); + else if (MATCHES((sequence(tkn::Literal::Identifier) || sequence(tkn::Keyword::Parent)) && (peek(tkn::Separator::Dot) || (peek(tkn::Separator::LeftBracket, 0) && !peek(tkn::Separator::LeftBracket, 1))))) + member = parseRValueAssignment(); else if (MATCHES(optional(tkn::Keyword::Unsigned) && sequence(tkn::Literal::Identifier, tkn::Operator::Colon))) { auto fieldName = getValue(-2).get(); auto identifier = std::get_if(&((m_curr[-2]).value)); @@ -2643,8 +2675,12 @@ namespace pl::core { statement = parseFunctionVariableAssignment(getValue(-2).get()); else if (sequence(tkn::Operator::Dollar, tkn::Operator::Assign)) statement = parseFunctionVariableAssignment("$"); - else if (const auto identifierOffset = parseCompoundAssignment(tkn::Literal::Identifier); identifierOffset.has_value()) + else if (const auto identifierOffset = isCompoundAssignmentOperator(tkn::Literal::Identifier); identifierOffset.has_value()) statement = parseFunctionVariableCompoundAssignment(getValue(*identifierOffset).get()); + else if (const auto curr = isCompoundAssignmentOperator(); curr.has_value()) + statement = parseFunctionVariableCompoundAssignment(curr.value()); + else if (MATCHES(sequence(tkn::Literal::Identifier) && (peek(tkn::Separator::Dot) || (peek(tkn::Separator::LeftBracket, 0) && !peek(tkn::Separator::LeftBracket, 1))))) + statement = parseRValueAssignment(); else if (MATCHES(sequence(tkn::Keyword::Using, tkn::Literal::Identifier))) statement = parseUsingDeclaration(); else if (sequence(tkn::Keyword::Import)) @@ -2704,23 +2740,53 @@ namespace pl::core { return hlp::moveToVector(std::move(statement)); } - std::optional Parser::parseCompoundAssignment(const Token &token) { + std::optional Parser::isCompoundAssignmentOperator(const Token &token) { + auto save = m_curr; + + if (sequence(token)) { + if (auto offset = isCompoundAssignmentOperator(-1); offset.has_value()) + return offset; + } + + m_curr = save; + return std::nullopt; + } + + std::optional Parser::isCompoundAssignmentOperator(i32 offset) { const static std::array SingleTokens = { tkn::Operator::Plus, tkn::Operator::Minus, tkn::Operator::Star, tkn::Operator::Slash, tkn::Operator::Percent, tkn::Operator::BitOr, tkn::Operator::BitAnd, tkn::Operator::BitXor }; const static std::array DoubleTokens = { tkn::Operator::BoolLessThan, tkn::Operator::BoolGreaterThan }; - for (auto &singleToken : SingleTokens) { - if (sequence(token, singleToken, tkn::Operator::Assign)) - return -3; + for (auto &token : SingleTokens) { + if (sequence(token, tkn::Operator::Assign)) + return -2 + offset; } - for (auto &doubleTokens : DoubleTokens) { - if (sequence(token, doubleTokens, doubleTokens, tkn::Operator::Assign)) - return -4; + for (auto &token : DoubleTokens) { + if (sequence(token, token, tkn::Operator::Assign)) + return -3 + offset; } return std::nullopt; } + std::optional Parser::isCompoundAssignmentOperator(AllowKeywords allowKeywords) { + auto save = this->m_curr; + bool allowParent = ((u32)allowKeywords & (u32)AllowKeywords::Parent) != 0; + bool allowThis = ((u32)allowKeywords & (u32)AllowKeywords::This) != 0; + if (MATCHES((sequence(tkn::Literal::Identifier) || (allowParent && sequence(tkn::Keyword::Parent)) || (allowThis && sequence(tkn::Keyword::This))) && (peek(tkn::Separator::Dot) || (peek(tkn::Separator::LeftBracket, 0) && !peek(tkn::Separator::LeftBracket, 1))))) { + auto result = m_curr; + auto lhs = parseRValue(); + if (lhs == nullptr) { + m_curr = save; + return std::nullopt; + } + if (isCompoundAssignmentOperator(-1).has_value()) + return result; + } + m_curr = save; + return std::nullopt; + } + hlp::safe_shared_ptr Parser::addType(const std::string &name, hlp::safe_unique_ptr &&node) { auto typeName = getNamespacePrefixedNames(name).back();