From 3295609aea3cbd1d0de4d347634b12ce0762185b Mon Sep 17 00:00:00 2001 From: Itamar Date: Mon, 29 Mar 2021 16:52:35 +0300 Subject: LibCpp: Add AST::Name A Name node is basically an identifier with an optional scope, e.g Core::File. --- .../LanguageServers/Cpp/ParserAutoComplete.cpp | 33 ++++++----- Userland/Libraries/LibCpp/AST.cpp | 22 +++++++- Userland/Libraries/LibCpp/AST.h | 26 +++++++-- Userland/Libraries/LibCpp/Parser.cpp | 66 ++++++++++++++-------- Userland/Libraries/LibCpp/Parser.h | 2 + 5 files changed, 105 insertions(+), 44 deletions(-) diff --git a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp index a5c232318f..7115b98e3c 100644 --- a/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp +++ b/Userland/DevTools/HackStudio/LanguageServers/Cpp/ParserAutoComplete.cpp @@ -109,8 +109,8 @@ Vector ParserAutoComplete::get_suggestions(con } if (is_empty_property(document, *node, position)) { - VERIFY(node->is_member_expression()); - return autocomplete_property(document, (MemberExpression&)(*node), ""); + VERIFY(node->parent()->is_member_expression()); + return autocomplete_property(document, (MemberExpression&)(*node->parent()), ""); } String partial_text = String::empty(); @@ -203,7 +203,7 @@ bool ParserAutoComplete::is_property(const ASTNode& node) const bool ParserAutoComplete::is_empty_property(const DocumentData& document, const ASTNode& node, const Position& autocomplete_position) const { - if (!node.is_member_expression()) + if (!node.parent()->is_member_expression()) return false; auto previous_token = document.parser().token_at(autocomplete_position); if (!previous_token.has_value()) @@ -217,7 +217,7 @@ String ParserAutoComplete::type_of_property(const DocumentData& document, const auto properties = properties_of_type(document, type_of(document, *parent.m_object)); for (auto& prop : properties) { if (prop.name == identifier.m_name) - return prop.type->m_name; + return prop.type->m_name->full_name(); } return {}; } @@ -230,7 +230,7 @@ String ParserAutoComplete::type_of_variable(const Identifier& identifier) const if (decl.is_variable_or_parameter_declaration()) { auto& var_or_param = (VariableOrParameterDeclaration&)decl; if (var_or_param.m_name == identifier.m_name) { - return var_or_param.m_type->m_name; + return var_or_param.m_type->m_name->full_name(); } } } @@ -247,16 +247,21 @@ String ParserAutoComplete::type_of(const DocumentData& document, const Expressio return type_of_property(document, static_cast(*member_expression.m_property)); return {}; } - if (!expression.is_identifier()) { + + const Identifier* identifier { nullptr }; + if (expression.is_name()) { + identifier = static_cast(expression).m_name.ptr(); + } else if (expression.is_identifier()) { + identifier = &static_cast(expression); + } else { + dbgln("expected identifier or name, got: {}", expression.class_name()); VERIFY_NOT_REACHED(); // TODO } + VERIFY(identifier); + if (is_property(*identifier)) + return type_of_property(document, *identifier); - auto& identifier = (const Identifier&)expression; - - if (is_property(identifier)) - return type_of_property(document, identifier); - - return type_of_variable(identifier); + return type_of_variable(*identifier); } Vector ParserAutoComplete::properties_of_type(const DocumentData& document, const String& type) const @@ -391,11 +396,11 @@ RefPtr ParserAutoComplete::find_declaration_of(const DocumentData& return decl; } if (node.is_type() && decl.is_struct_or_class()) { - if (((Cpp::StructOrClassDeclaration&)decl).m_name == static_cast(node).m_name) + if (((Cpp::StructOrClassDeclaration&)decl).m_name == static_cast(node).m_name->full_name()) return decl; } if (node.is_function_call() && decl.is_function()) { - if (((Cpp::FunctionDeclaration&)decl).m_name == static_cast(node).m_name) + if (((Cpp::FunctionDeclaration&)decl).m_name == static_cast(node).m_name->full_name()) return decl; } if (is_property(node) && decl.is_struct_or_class()) { diff --git a/Userland/Libraries/LibCpp/AST.cpp b/Userland/Libraries/LibCpp/AST.cpp index 7b93ba692d..713a785780 100644 --- a/Userland/Libraries/LibCpp/AST.cpp +++ b/Userland/Libraries/LibCpp/AST.cpp @@ -91,7 +91,7 @@ void Type::dump(size_t indent) const String qualifiers_string; if (!m_qualifiers.is_empty()) qualifiers_string = String::formatted("[{}] ", String::join(" ", m_qualifiers)); - outln("{}{}", qualifiers_string, m_name); + outln("{}{}", qualifiers_string, m_name.is_null() ? "" : m_name->full_name()); } void Parameter::dump(size_t indent) const @@ -243,7 +243,7 @@ void FunctionCall::dump(size_t indent) const { ASTNode::dump(indent); print_indent(indent); - outln("{}", m_name); + outln("{}", m_name->full_name()); for (const auto& arg : m_arguments) { arg.dump(indent + 1); } @@ -462,4 +462,22 @@ void TemplatizedType::dump(size_t indent) const outln(">"); } +void Name::dump(size_t indent) const +{ + ASTNode::dump(indent); + print_indent(indent); + outln("{}", full_name()); +} + +String Name::full_name() const +{ + StringBuilder builder; + if (!m_scope.is_empty()) { + for (auto& scope : m_scope) { + builder.appendff("{}::", scope.m_name); + } + } + return String::formatted("{}{}", builder.to_string(), m_name.is_null() ? "" : m_name->m_name); +} + } diff --git a/Userland/Libraries/LibCpp/AST.h b/Userland/Libraries/LibCpp/AST.h index 487c2c094a..9634d40ebc 100644 --- a/Userland/Libraries/LibCpp/AST.h +++ b/Userland/Libraries/LibCpp/AST.h @@ -43,6 +43,7 @@ class FunctionDefinition; class Type; class Parameter; class Statement; +class Name; class ASTNode : public RefCounted { public: @@ -76,6 +77,7 @@ public: virtual bool is_function_call() const { return false; } virtual bool is_type() const { return false; } virtual bool is_declaration() const { return false; } + virtual bool is_name() const {return false;} protected: ASTNode(ASTNode* parent, Optional start, Optional end, const String& filename) @@ -212,7 +214,6 @@ class Type : public ASTNode { public: virtual ~Type() override = default; virtual const char* class_name() const override { return "Type"; } - const StringView& name() const { return m_name; } virtual void dump(size_t indent) const override; virtual bool is_type() const override { return true; } virtual bool is_templatized() const { return false; } @@ -222,7 +223,7 @@ public: { } - StringView m_name; + RefPtr m_name; Vector m_qualifiers; }; @@ -341,6 +342,23 @@ public: StringView m_name; }; +class Name : public Expression { +public: + virtual ~Name() override = default; + 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;} + + Name(ASTNode* parent, Optional start, Optional end, const String& filename) + : Expression(parent, start, end, filename) + { + } + String full_name() const; + + RefPtr m_name; + NonnullRefPtrVector m_scope; +}; + class NumericLiteral : public Expression { public: virtual ~NumericLiteral() override = default; @@ -453,7 +471,7 @@ public: virtual void dump(size_t indent) const override; virtual bool is_function_call() const override { return true; } - StringView m_name; + RefPtr m_name; NonnullRefPtrVector m_arguments; }; @@ -577,7 +595,7 @@ public: virtual bool is_member_expression() const override { return true; } RefPtr m_object; - RefPtr m_property; + RefPtr m_property; }; class ForStatement : public Statement { diff --git a/Userland/Libraries/LibCpp/Parser.cpp b/Userland/Libraries/LibCpp/Parser.cpp index e2a7f86099..eaedac2b73 100644 --- a/Userland/Libraries/LibCpp/Parser.cpp +++ b/Userland/Libraries/LibCpp/Parser.cpp @@ -258,10 +258,9 @@ Parser::MatchTypeResult Parser::match_type() parse_type_qualifiers(); - if (!peek(Token::Type::KnownType).has_value() && !peek(Token::Type::Identifier).has_value()) + if (!match_name()) return MatchTypeResult::NoMatch; - - consume(); + parse_name(*m_root_node); if (peek(Token::Type::Less).has_value()) { if (match_template_arguments()) { @@ -439,19 +438,16 @@ NonnullRefPtr Parser::parse_primary_expression(ASTNode& parent) if (match_literal()) { return parse_literal(parent); } - switch (peek().type()) { - case Token::Type::Identifier: { + + if (match_name()) { if (match_function_call()) return parse_function_call(parent); - auto token = consume(); - return create_ast_node(parent, token.start(), token.end(), text_of_token(token)); - } - default: { - error("could not parse primary expression"); - auto token = consume(); - return create_ast_node(parent, token.start(), token.end()); - } + return parse_name(parent); } + + error("could not parse primary expression"); + auto token = consume(); + return create_ast_node(parent, token.start(), token.end()); } bool Parser::match_literal() @@ -557,9 +553,8 @@ NonnullRefPtr Parser::parse_secondary_expression(ASTNode& parent, No auto exp = create_ast_node(parent, lhs->start(), {}); lhs->set_parent(*exp); exp->m_object = move(lhs); - auto property_token = consume(Token::Type::Identifier); - exp->m_property = create_ast_node(*exp, property_token.start(), property_token.end(), text_of_token(property_token)); - exp->set_end(property_token.end()); + exp->m_property = parse_expression(*exp); + exp->set_end(position()); return exp; } default: { @@ -907,9 +902,10 @@ bool Parser::match_function_call() { save_state(); ScopeGuard state_guard = [this] { load_state(); }; - if (!match(Token::Type::Identifier)) + if (!match_name()) return false; - consume(); + parse_name(*m_root_node); + return match(Token::Type::LeftParen); } @@ -917,8 +913,7 @@ NonnullRefPtr Parser::parse_function_call(ASTNode& parent) { SCOPE_LOGGER(); auto call = create_ast_node(parent, position(), {}); - auto name_identifier = consume(Token::Type::Identifier); - call->m_name = text_of_token(name_identifier); + call->m_name = parse_name(*call); NonnullRefPtrVector args; consume(Token::Type::LeftParen); @@ -1115,15 +1110,14 @@ NonnullRefPtr Parser::parse_type(ASTNode& parent) } auto qualifiers = parse_type_qualifiers(); - auto type_name_token = consume(); type->m_qualifiers = move(qualifiers); - type->m_name = text_of_token(type_name_token); - if (type_name_token.type() != Token::Type::KnownType && type_name_token.type() != Token::Type::Identifier) { + if (!match_name()) { type->set_end(position()); - error(String::formatted("unexpected type_name_token for type: {}", type_name_token.to_string())); + error(String::formatted("expected name instead of: {}", peek().text())); return type.release_nonnull(); } + type->m_name = parse_name(*type); if (is_templatized) { static_cast(*type).m_template_arguments = parse_template_arguments(*type); @@ -1289,4 +1283,28 @@ NonnullRefPtr Parser::parse_namespace_declaration(ASTNode& return namespace_decl; } +bool Parser::match_name() +{ + auto type = peek().type(); + return type == Token::Type::Identifier || type == Token::Type::KnownType; +} + +NonnullRefPtr Parser::parse_name(ASTNode& parent) +{ + auto 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())); + if (peek().type() == Token::Type::ColonColon) + consume(); + else + break; + } + + VERIFY(!name_node->m_scope.is_empty()); + name_node->m_name = name_node->m_scope.take_last(); + name_node->set_end(position()); + return name_node; +} + } diff --git a/Userland/Libraries/LibCpp/Parser.h b/Userland/Libraries/LibCpp/Parser.h index d4a70be0db..e496cc94eb 100644 --- a/Userland/Libraries/LibCpp/Parser.h +++ b/Userland/Libraries/LibCpp/Parser.h @@ -89,6 +89,7 @@ private: bool match_block_statement(); bool match_namespace_declaration(); bool match_template_arguments(); + bool match_name(); enum class MatchTypeResult { NoMatch, @@ -129,6 +130,7 @@ private: NonnullRefPtrVector parse_declarations_in_translation_unit(ASTNode& parent); RefPtr parse_single_declaration_in_translation_unit(ASTNode& parent); NonnullRefPtrVector parse_template_arguments(ASTNode& parent); + NonnullRefPtr parse_name(ASTNode& parent); bool match(Token::Type); Token consume(Token::Type); -- cgit v1.2.3