From 9e2e36724d87a639be967d212a3c60d0ea5fed2b Mon Sep 17 00:00:00 2001 From: Itamar Date: Tue, 6 Apr 2021 19:43:43 +0300 Subject: [PATCH] LibCpp: Add TemplatizedName This type represents templatized names, and replaces our previous use of 'TemplatizedType' and 'TemplatizedFunctionCall'. Also, we now parse function calls as secondary expressions. --- .../Cpp/ParserAutoComplete.cpp | 10 +- Userland/Libraries/LibCpp/AST.cpp | 64 ++++---- Userland/Libraries/LibCpp/AST.h | 54 +++---- Userland/Libraries/LibCpp/Parser.cpp | 137 ++++++------------ Userland/Libraries/LibCpp/Parser.h | 15 +- 5 files changed, 96 insertions(+), 184 deletions(-) diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp index 977b09f8bd..7a4726159f 100644 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp +++ b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp @@ -398,22 +398,16 @@ struct TargetDeclaration { static Optional get_target_declaration(const ASTNode& node) { - dbgln("get_target_declaration"); if (!node.is_identifier()) { dbgln_if(CPP_LANGUAGE_SERVER_DEBUG, "node is not an identifier"); return {}; } - const ASTNode* n = &node; - while (n) { - dbgln("{}", n->class_name()); - n = n->parent(); - } - String name = static_cast(node).m_name; - if ((node.parent() && node.parent()->is_function_call()) || (node.parent()->is_name() && node.parent()->parent() && node.parent()->parent()->is_function_call())) + if ((node.parent() && node.parent()->is_function_call()) || (node.parent()->is_name() && node.parent()->parent() && node.parent()->parent()->is_function_call())) { return TargetDeclaration { TargetDeclaration::Type::Function, name }; + } if ((node.parent() && node.parent()->is_type()) || (node.parent()->is_name() && node.parent()->parent() && node.parent()->parent()->is_type())) return TargetDeclaration { TargetDeclaration::Type::Type, name }; diff --git a/Userland/Libraries/LibCpp/AST.cpp b/Userland/Libraries/LibCpp/AST.cpp index b3754bd7b1..0b77c21828 100644 --- a/Userland/Libraries/LibCpp/AST.cpp +++ b/Userland/Libraries/LibCpp/AST.cpp @@ -88,10 +88,25 @@ void Type::dump(size_t indent) const { ASTNode::dump(indent); print_indent(indent + 1); + outln("{}", to_string()); +} + +String Type::to_string() const +{ String qualifiers_string; if (!m_qualifiers.is_empty()) qualifiers_string = String::formatted("[{}] ", String::join(" ", m_qualifiers)); - outln("{}{}", qualifiers_string, m_name.is_null() ? "" : m_name->full_name()); + return String::formatted("{}{}", qualifiers_string, m_name.is_null() ? "" : m_name->full_name()); +} + +String Pointer::to_string() const +{ + if (!m_pointee) + return {}; + StringBuilder builder; + builder.append(m_pointee->to_string()); + builder.append("*"); + return builder.to_string(); } void Parameter::dump(size_t indent) const @@ -254,8 +269,7 @@ void AssignmentExpression::dump(size_t indent) const void FunctionCall::dump(size_t indent) const { ASTNode::dump(indent); - print_indent(indent); - outln("{}", m_name->full_name()); + m_callee->dump(indent + 1); for (const auto& arg : m_arguments) { arg.dump(indent + 1); } @@ -460,26 +474,6 @@ void NullPointerLiteral::dump(size_t indent) const ASTNode::dump(indent); } -void TemplatizedType::dump(size_t indent) const -{ - ASTNode::dump(indent); - - String qualifiers_string; - if (!m_qualifiers.is_empty()) - qualifiers_string = String::formatted("[{}] ", String::join(" ", m_qualifiers)); - - print_indent(indent + 1); - outln("{}{}", qualifiers_string, m_name); - - print_indent(indent + 1); - outln("<"); - for (auto& arg : m_template_arguments) { - arg.dump(indent + 1); - } - print_indent(indent + 1); - outln(">"); -} - void Name::dump(size_t indent) const { ASTNode::dump(indent); @@ -498,24 +492,16 @@ String Name::full_name() const return String::formatted("{}{}", builder.to_string(), m_name.is_null() ? "" : m_name->m_name); } -void TemplatizedFunctionCall::dump(size_t indent) const +String TemplatizedName::full_name() const { - ASTNode::dump(indent); - - print_indent(indent); - outln("{}", m_name->full_name()); - - print_indent(indent + 1); - outln("<"); - for (auto& arg : m_template_arguments) { - arg.dump(indent + 1); - } - print_indent(indent + 1); - outln(">"); - - for (const auto& arg : m_arguments) { - arg.dump(indent + 1); + StringBuilder name; + name.append(Name::full_name()); + name.append('<'); + for (auto& type : m_template_arguments) { + name.append(type.to_string()); } + name.append('>'); + return name.to_string(); } void CppCastExpression::dump(size_t indent) const diff --git a/Userland/Libraries/LibCpp/AST.h b/Userland/Libraries/LibCpp/AST.h index 402057c19a..5551751fd1 100644 --- a/Userland/Libraries/LibCpp/AST.h +++ b/Userland/Libraries/LibCpp/AST.h @@ -218,6 +218,7 @@ public: virtual void dump(size_t indent) const override; virtual bool is_type() const override { return true; } virtual bool is_templatized() const { return false; } + virtual String to_string() const; Type(ASTNode* parent, Optional start, Optional end, const String& filename) : ASTNode(parent, start, end, filename) @@ -228,26 +229,12 @@ public: Vector m_qualifiers; }; -class TemplatizedType : public Type { -public: - virtual ~TemplatizedType() override = default; - virtual const char* class_name() const override { return "TemplatizedType"; } - virtual void dump(size_t indent) const override; - virtual bool is_templatized() const override { return true; } - - TemplatizedType(ASTNode* parent, Optional start, Optional end, const String& filename) - : Type(parent, start, end, filename) - { - } - - NonnullRefPtrVector m_template_arguments; -}; - class Pointer : public Type { public: virtual ~Pointer() override = default; virtual const char* class_name() const override { return "Pointer"; } virtual void dump(size_t indent) const override; + virtual String to_string() const override; Pointer(ASTNode* parent, Optional start, Optional end, const String& filename) : Type(parent, start, end, filename) @@ -349,17 +336,33 @@ public: virtual const char* class_name() const override { return "Name"; } virtual void dump(size_t indent) const override; virtual bool is_name() const override { return true; } + virtual bool is_templatized() const { return false; } Name(ASTNode* parent, Optional start, Optional end, const String& filename) : Expression(parent, start, end, filename) { } - String full_name() const; + virtual String full_name() const; RefPtr m_name; NonnullRefPtrVector m_scope; }; +class TemplatizedName : public Name { +public: + virtual ~TemplatizedName() override = default; + virtual const char* class_name() const override { return "TemplatizedName"; } + virtual bool is_templatized() const override { return true; } + virtual String full_name() const override; + + TemplatizedName(ASTNode* parent, Optional start, Optional end, const String& filename) + : Name(parent, start, end, filename) + { + } + + NonnullRefPtrVector m_template_arguments; +}; + class NumericLiteral : public Expression { public: virtual ~NumericLiteral() override = default; @@ -475,25 +478,10 @@ public: virtual bool is_function_call() const override { return true; } virtual bool is_templatized() const { return false; } - RefPtr m_name; + RefPtr m_callee; NonnullRefPtrVector m_arguments; }; -class TemplatizedFunctionCall final : public FunctionCall { -public: - TemplatizedFunctionCall(ASTNode* parent, Optional start, Optional end, const String& filename) - : FunctionCall(parent, start, end, filename) - { - } - - ~TemplatizedFunctionCall() override = default; - virtual const char* class_name() const override { return "TemplatizedFunctionCall"; } - virtual void dump(size_t indent) const override; - virtual bool is_templatized() const override { return true; } - - NonnullRefPtrVector m_template_arguments; -}; - class StringLiteral final : public Expression { public: StringLiteral(ASTNode* parent, Optional start, Optional end, const String& filename) @@ -761,7 +749,7 @@ public: class DummyAstNode : public ASTNode { public: DummyAstNode(ASTNode* parent, Optional start, Optional end, const String& filename) - : ASTNode(parent, start, end, filename) + : ASTNode(parent, start, end, filename) { } virtual bool is_dummy_node() const override { return true; } diff --git a/Userland/Libraries/LibCpp/Parser.cpp b/Userland/Libraries/LibCpp/Parser.cpp index 6dec241c3b..a78baac06b 100644 --- a/Userland/Libraries/LibCpp/Parser.cpp +++ b/Userland/Libraries/LibCpp/Parser.cpp @@ -251,7 +251,7 @@ NonnullRefPtr Parser::parse_block_statement(ASTNode& parent) return block_statement; } -Parser::TemplatizedMatchResult Parser::match_type() +bool Parser::match_type() { save_state(); ScopeGuard state_guard = [this] { load_state(); }; @@ -262,17 +262,9 @@ Parser::TemplatizedMatchResult Parser::match_type() } if (!match_name()) - return TemplatizedMatchResult::NoMatch; - parse_name(get_dummy_node()); + return false; - if (peek(Token::Type::Less).has_value()) { - if (match_template_arguments()) { - return TemplatizedMatchResult::Templatized; - } - return TemplatizedMatchResult::NoMatch; - } - - return TemplatizedMatchResult::Regular; + return true; } bool Parser::match_template_arguments() @@ -285,7 +277,7 @@ bool Parser::match_template_arguments() consume(); while (!eof() && peek().type() != Token::Type::Greater) { - if (match_type() == TemplatizedMatchResult::NoMatch) + if (!match_type()) return false; parse_type(get_dummy_node()); } @@ -315,7 +307,7 @@ bool Parser::match_variable_declaration() save_state(); ScopeGuard state_guard = [this] { load_state(); }; - if (match_type() == TemplatizedMatchResult::NoMatch) { + if (!match_type()) { return false; } @@ -426,7 +418,8 @@ bool Parser::match_secondary_expression() || type == Token::Type::PipePipe || type == Token::Type::ExclamationMarkEquals || type == Token::Type::PipePipe - || type == Token::Type::Arrow; + || type == Token::Type::Arrow + || type == Token::Type::LeftParen; } NonnullRefPtr Parser::parse_primary_expression(ASTNode& parent) @@ -458,8 +451,6 @@ NonnullRefPtr Parser::parse_primary_expression(ASTNode& parent) return parse_braced_init_list(parent); if (match_name()) { - if (match_function_call() != TemplatizedMatchResult::NoMatch) - return parse_function_call(parent); return parse_name(parent); } @@ -595,6 +586,20 @@ NonnullRefPtr Parser::parse_secondary_expression(ASTNode& parent, No exp->set_end(position()); return exp; } + case Token::Type::LeftParen: { + consume(); + auto func = create_ast_node(parent, lhs->start(), {}); + lhs->set_parent(*func); + func->m_callee = lhs; + while (peek().type() != Token::Type::RightParen && !eof()) { + func->m_arguments.append(parse_expression(*func)); + if (peek().type() == Token::Type::Comma) + consume(Token::Type::Comma); + } + consume(Token::Type::RightParen); + func->set_end(position()); + return func; + } default: { error(String::formatted("unexpected operator for expression. operator: {}", peek().to_string())); auto token = consume(); @@ -666,8 +671,9 @@ bool Parser::match_function_declaration() parse_function_qualifiers(); - if (match_type() == TemplatizedMatchResult::NoMatch) + if (!match_type()) return false; + VERIFY(m_root_node); parse_type(get_dummy_node()); @@ -940,64 +946,6 @@ void Parser::print_tokens() const } } -Parser::TemplatizedMatchResult Parser::match_function_call() -{ - save_state(); - ScopeGuard state_guard = [this] { load_state(); }; - if (!match_name()) - return TemplatizedMatchResult::NoMatch; - parse_name(get_dummy_node()); - - bool is_templatized = false; - if (match_template_arguments()) { - is_templatized = true; - parse_template_arguments(get_dummy_node()); - } - - if (!match(Token::Type::LeftParen)) - return TemplatizedMatchResult::NoMatch; - - return is_templatized ? TemplatizedMatchResult::Templatized : TemplatizedMatchResult::Regular; -} - -NonnullRefPtr Parser::parse_function_call(ASTNode& parent) -{ - SCOPE_LOGGER(); - - auto match_result = match_type(); - if (match_result == TemplatizedMatchResult::NoMatch) { - error("expected type"); - return create_ast_node(parent, position(), position()); - } - - bool is_templatized = match_result == TemplatizedMatchResult::Templatized; - - RefPtr call; - if (is_templatized) { - call = create_ast_node(parent, position(), {}); - } else { - call = create_ast_node(parent, position(), {}); - } - - call->m_name = parse_name(*call); - if (is_templatized) { - static_cast(*call).m_template_arguments = parse_template_arguments(*call); - } - - NonnullRefPtrVector args; - consume(Token::Type::LeftParen); - while (peek().type() != Token::Type::RightParen && !eof()) { - args.append(parse_expression(*call)); - if (peek().type() == Token::Type::Comma) - consume(Token::Type::Comma); - } - consume(Token::Type::RightParen); - call->m_arguments = move(args); - call->set_end(position()); - - return call.release_nonnull(); -} - NonnullRefPtr Parser::parse_string_literal(ASTNode& parent) { SCOPE_LOGGER(); @@ -1161,19 +1109,12 @@ NonnullRefPtr Parser::parse_type(ASTNode& parent) { SCOPE_LOGGER(); - auto match_result = match_type(); - if (match_result == TemplatizedMatchResult::NoMatch) { + if (!match_type()) { auto token = consume(); return create_ast_node(parent, token.start(), token.end()); } - bool is_templatized = match_result == TemplatizedMatchResult::Templatized; - RefPtr type; - if (is_templatized) { - type = create_ast_node(parent, position(), {}); - } else { - type = create_ast_node(parent, position(), {}); - } + auto type = create_ast_node(parent, position(), {}); auto qualifiers = parse_type_qualifiers(); type->m_qualifiers = move(qualifiers); @@ -1185,14 +1126,10 @@ NonnullRefPtr Parser::parse_type(ASTNode& parent) if (!match_name()) { type->set_end(position()); error(String::formatted("expected name instead of: {}", peek().text())); - return type.release_nonnull(); + return type; } type->m_name = parse_name(*type); - if (is_templatized) { - static_cast(*type).m_template_arguments = parse_template_arguments(*type); - } - while (!eof() && peek().type() == Token::Type::Asterisk) { type->set_end(position()); auto asterisk = consume(); @@ -1203,7 +1140,7 @@ NonnullRefPtr Parser::parse_type(ASTNode& parent) } type->set_end(position()); - return type.release_nonnull(); + return type; } NonnullRefPtr Parser::parse_for_statement(ASTNode& parent) @@ -1368,7 +1305,7 @@ bool Parser::match_name() NonnullRefPtr Parser::parse_name(ASTNode& parent) { - auto name_node = create_ast_node(parent, position(), {}); + NonnullRefPtr name_node = create_ast_node(parent, position(), {}); while (!eof() && (peek().type() == Token::Type::Identifier || peek().type() == Token::Type::KnownType)) { auto token = consume(); name_node->m_scope.append(create_ast_node(*name_node, token.start(), token.end(), token.text())); @@ -1380,6 +1317,22 @@ NonnullRefPtr Parser::parse_name(ASTNode& parent) VERIFY(!name_node->m_scope.is_empty()); name_node->m_name = name_node->m_scope.take_last(); + + if (match_template_arguments()) { + consume(Token::Type::Less); + NonnullRefPtr templatized_name = create_ast_node(parent, name_node->start(), {}); + templatized_name->m_name = move(name_node->m_name); + templatized_name->m_scope = move(name_node->m_scope); + name_node->set_end(position()); + name_node = templatized_name; + while (peek().type() != Token::Type::Greater && !eof()) { + templatized_name->m_template_arguments.append(parse_type(*templatized_name)); + if (peek().type() == Token::Type::Comma) + consume(Token::Type::Comma); + } + consume(Token::Type::Greater); + } + name_node->set_end(position()); return name_node; } @@ -1407,7 +1360,7 @@ bool Parser::match_c_style_cast_expression() if (consume().type() != Token::Type::LeftParen) return false; - if (match_type() == TemplatizedMatchResult::NoMatch) + if (!match_type()) return false; parse_type(get_dummy_node()); diff --git a/Userland/Libraries/LibCpp/Parser.h b/Userland/Libraries/LibCpp/Parser.h index 94961e6968..aad21bd9d0 100644 --- a/Userland/Libraries/LibCpp/Parser.h +++ b/Userland/Libraries/LibCpp/Parser.h @@ -93,14 +93,7 @@ private: bool match_c_style_cast_expression(); bool match_sizeof_expression(); bool match_braced_init_list(); - - enum class TemplatizedMatchResult { - NoMatch, - Regular, - Templatized, - }; - TemplatizedMatchResult match_type(); - TemplatizedMatchResult match_function_call(); + bool match_type(); Optional> parse_parameter_list(ASTNode& parent); Optional consume_whitespace(); @@ -165,7 +158,7 @@ private: create_ast_node(ASTNode& parent, const Position& start, Optional end, Args&&... args) { auto node = adopt(*new T(&parent, start, end, m_filename, forward(args)...)); - if(!parent.is_dummy_node()) { + if (!parent.is_dummy_node()) { m_state.nodes.append(node); } return node; @@ -180,14 +173,12 @@ private: return node; } - DummyAstNode& get_dummy_node() { - static NonnullRefPtr dummy = adopt(*new DummyAstNode(nullptr, {}, {}, {})); + static NonnullRefPtr dummy = adopt(*new DummyAstNode(nullptr, {}, {}, {})); return dummy; } - bool match_attribute_specification(); void consume_attribute_specification(); bool match_ellipsis();