1cfca06d7SDimitry Andric //===-- CPlusPlusNameParser.cpp -------------------------------------------===//
274a628f7SDimitry Andric //
35f29bb8aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f29bb8aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f29bb8aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
674a628f7SDimitry Andric //
774a628f7SDimitry Andric //===----------------------------------------------------------------------===//
874a628f7SDimitry Andric
974a628f7SDimitry Andric #include "CPlusPlusNameParser.h"
1074a628f7SDimitry Andric
1174a628f7SDimitry Andric #include "clang/Basic/IdentifierTable.h"
12e3b55780SDimitry Andric #include "clang/Basic/TokenKinds.h"
1374a628f7SDimitry Andric #include "llvm/ADT/StringMap.h"
1474a628f7SDimitry Andric #include "llvm/Support/Threading.h"
15e3b55780SDimitry Andric #include <optional>
1674a628f7SDimitry Andric
1774a628f7SDimitry Andric using namespace lldb;
1874a628f7SDimitry Andric using namespace lldb_private;
1974a628f7SDimitry Andric using ParsedFunction = lldb_private::CPlusPlusNameParser::ParsedFunction;
2074a628f7SDimitry Andric using ParsedName = lldb_private::CPlusPlusNameParser::ParsedName;
2174a628f7SDimitry Andric namespace tok = clang::tok;
2274a628f7SDimitry Andric
ParseAsFunctionDefinition()23e3b55780SDimitry Andric std::optional<ParsedFunction> CPlusPlusNameParser::ParseAsFunctionDefinition() {
2474a628f7SDimitry Andric m_next_token_index = 0;
25e3b55780SDimitry Andric std::optional<ParsedFunction> result(std::nullopt);
2674a628f7SDimitry Andric
27f73363f1SDimitry Andric // Try to parse the name as function without a return type specified e.g.
28f73363f1SDimitry Andric // main(int, char*[])
2974a628f7SDimitry Andric {
3074a628f7SDimitry Andric Bookmark start_position = SetBookmark();
3174a628f7SDimitry Andric result = ParseFunctionImpl(false);
3274a628f7SDimitry Andric if (result && !HasMoreTokens())
3374a628f7SDimitry Andric return result;
3474a628f7SDimitry Andric }
3574a628f7SDimitry Andric
36f73363f1SDimitry Andric // Try to parse the name as function with function pointer return type e.g.
37f73363f1SDimitry Andric // void (*get_func(const char*))()
3874a628f7SDimitry Andric result = ParseFuncPtr(true);
3974a628f7SDimitry Andric if (result)
4074a628f7SDimitry Andric return result;
4174a628f7SDimitry Andric
4274a628f7SDimitry Andric // Finally try to parse the name as a function with non-function return type
4374a628f7SDimitry Andric // e.g. int main(int, char*[])
4474a628f7SDimitry Andric result = ParseFunctionImpl(true);
4574a628f7SDimitry Andric if (HasMoreTokens())
46e3b55780SDimitry Andric return std::nullopt;
4774a628f7SDimitry Andric return result;
4874a628f7SDimitry Andric }
4974a628f7SDimitry Andric
ParseAsFullName()50e3b55780SDimitry Andric std::optional<ParsedName> CPlusPlusNameParser::ParseAsFullName() {
5174a628f7SDimitry Andric m_next_token_index = 0;
52e3b55780SDimitry Andric std::optional<ParsedNameRanges> name_ranges = ParseFullNameImpl();
5374a628f7SDimitry Andric if (!name_ranges)
54e3b55780SDimitry Andric return std::nullopt;
5574a628f7SDimitry Andric if (HasMoreTokens())
56e3b55780SDimitry Andric return std::nullopt;
5774a628f7SDimitry Andric ParsedName result;
58e3b55780SDimitry Andric result.basename = GetTextForRange(name_ranges->basename_range);
59e3b55780SDimitry Andric result.context = GetTextForRange(name_ranges->context_range);
6074a628f7SDimitry Andric return result;
6174a628f7SDimitry Andric }
6274a628f7SDimitry Andric
HasMoreTokens()6374a628f7SDimitry Andric bool CPlusPlusNameParser::HasMoreTokens() {
6474a628f7SDimitry Andric return m_next_token_index < m_tokens.size();
6574a628f7SDimitry Andric }
6674a628f7SDimitry Andric
Advance()6774a628f7SDimitry Andric void CPlusPlusNameParser::Advance() { ++m_next_token_index; }
6874a628f7SDimitry Andric
TakeBack()6974a628f7SDimitry Andric void CPlusPlusNameParser::TakeBack() { --m_next_token_index; }
7074a628f7SDimitry Andric
ConsumeToken(tok::TokenKind kind)7174a628f7SDimitry Andric bool CPlusPlusNameParser::ConsumeToken(tok::TokenKind kind) {
7274a628f7SDimitry Andric if (!HasMoreTokens())
7374a628f7SDimitry Andric return false;
7474a628f7SDimitry Andric
7574a628f7SDimitry Andric if (!Peek().is(kind))
7674a628f7SDimitry Andric return false;
7774a628f7SDimitry Andric
7874a628f7SDimitry Andric Advance();
7974a628f7SDimitry Andric return true;
8074a628f7SDimitry Andric }
8174a628f7SDimitry Andric
ConsumeToken(Ts...kinds)8274a628f7SDimitry Andric template <typename... Ts> bool CPlusPlusNameParser::ConsumeToken(Ts... kinds) {
8374a628f7SDimitry Andric if (!HasMoreTokens())
8474a628f7SDimitry Andric return false;
8574a628f7SDimitry Andric
8674a628f7SDimitry Andric if (!Peek().isOneOf(kinds...))
8774a628f7SDimitry Andric return false;
8874a628f7SDimitry Andric
8974a628f7SDimitry Andric Advance();
9074a628f7SDimitry Andric return true;
9174a628f7SDimitry Andric }
9274a628f7SDimitry Andric
SetBookmark()9374a628f7SDimitry Andric CPlusPlusNameParser::Bookmark CPlusPlusNameParser::SetBookmark() {
9474a628f7SDimitry Andric return Bookmark(m_next_token_index);
9574a628f7SDimitry Andric }
9674a628f7SDimitry Andric
GetCurrentPosition()9774a628f7SDimitry Andric size_t CPlusPlusNameParser::GetCurrentPosition() { return m_next_token_index; }
9874a628f7SDimitry Andric
Peek()9974a628f7SDimitry Andric clang::Token &CPlusPlusNameParser::Peek() {
10074a628f7SDimitry Andric assert(HasMoreTokens());
10174a628f7SDimitry Andric return m_tokens[m_next_token_index];
10274a628f7SDimitry Andric }
10374a628f7SDimitry Andric
104e3b55780SDimitry Andric std::optional<ParsedFunction>
ParseFunctionImpl(bool expect_return_type)10574a628f7SDimitry Andric CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) {
10674a628f7SDimitry Andric Bookmark start_position = SetBookmark();
107e3b55780SDimitry Andric
108e3b55780SDimitry Andric ParsedFunction result;
10974a628f7SDimitry Andric if (expect_return_type) {
110e3b55780SDimitry Andric size_t return_start = GetCurrentPosition();
11174a628f7SDimitry Andric // Consume return type if it's expected.
112e3b55780SDimitry Andric if (!ConsumeToken(tok::kw_auto) && !ConsumeTypename())
113e3b55780SDimitry Andric return std::nullopt;
114e3b55780SDimitry Andric
115e3b55780SDimitry Andric size_t return_end = GetCurrentPosition();
116e3b55780SDimitry Andric result.return_type = GetTextForRange(Range(return_start, return_end));
11774a628f7SDimitry Andric }
11874a628f7SDimitry Andric
11974a628f7SDimitry Andric auto maybe_name = ParseFullNameImpl();
12074a628f7SDimitry Andric if (!maybe_name) {
121e3b55780SDimitry Andric return std::nullopt;
12274a628f7SDimitry Andric }
12374a628f7SDimitry Andric
12474a628f7SDimitry Andric size_t argument_start = GetCurrentPosition();
12574a628f7SDimitry Andric if (!ConsumeArguments()) {
126e3b55780SDimitry Andric return std::nullopt;
12774a628f7SDimitry Andric }
12874a628f7SDimitry Andric
12974a628f7SDimitry Andric size_t qualifiers_start = GetCurrentPosition();
13074a628f7SDimitry Andric SkipFunctionQualifiers();
13174a628f7SDimitry Andric size_t end_position = GetCurrentPosition();
13274a628f7SDimitry Andric
133e3b55780SDimitry Andric result.name.basename = GetTextForRange(maybe_name->basename_range);
134e3b55780SDimitry Andric result.name.context = GetTextForRange(maybe_name->context_range);
13574a628f7SDimitry Andric result.arguments = GetTextForRange(Range(argument_start, qualifiers_start));
13674a628f7SDimitry Andric result.qualifiers = GetTextForRange(Range(qualifiers_start, end_position));
13774a628f7SDimitry Andric start_position.Remove();
13874a628f7SDimitry Andric return result;
13974a628f7SDimitry Andric }
14074a628f7SDimitry Andric
141e3b55780SDimitry Andric std::optional<ParsedFunction>
ParseFuncPtr(bool expect_return_type)14274a628f7SDimitry Andric CPlusPlusNameParser::ParseFuncPtr(bool expect_return_type) {
143e3b55780SDimitry Andric // This function parses a function definition
144e3b55780SDimitry Andric // that returns a pointer type.
145e3b55780SDimitry Andric // E.g., double (*(*func(long))(int))(float)
146e3b55780SDimitry Andric
147e3b55780SDimitry Andric // Step 1:
148e3b55780SDimitry Andric // Remove the return type of the innermost
149e3b55780SDimitry Andric // function pointer type.
150e3b55780SDimitry Andric //
151e3b55780SDimitry Andric // Leaves us with:
152e3b55780SDimitry Andric // (*(*func(long))(int))(float)
15374a628f7SDimitry Andric Bookmark start_position = SetBookmark();
15474a628f7SDimitry Andric if (expect_return_type) {
15574a628f7SDimitry Andric // Consume return type.
15674a628f7SDimitry Andric if (!ConsumeTypename())
157e3b55780SDimitry Andric return std::nullopt;
15874a628f7SDimitry Andric }
15974a628f7SDimitry Andric
160e3b55780SDimitry Andric // Step 2:
161e3b55780SDimitry Andric //
162e3b55780SDimitry Andric // Skip a pointer and parenthesis pair.
163e3b55780SDimitry Andric //
164e3b55780SDimitry Andric // Leaves us with:
165e3b55780SDimitry Andric // (*func(long))(int))(float)
16674a628f7SDimitry Andric if (!ConsumeToken(tok::l_paren))
167e3b55780SDimitry Andric return std::nullopt;
16874a628f7SDimitry Andric if (!ConsumePtrsAndRefs())
169e3b55780SDimitry Andric return std::nullopt;
17074a628f7SDimitry Andric
171e3b55780SDimitry Andric // Step 3:
172e3b55780SDimitry Andric //
173e3b55780SDimitry Andric // Consume inner function name. This will fail unless
174e3b55780SDimitry Andric // we stripped all the pointers on the left hand side
1757fa27ce4SDimitry Andric // of the function name.
17674a628f7SDimitry Andric {
17774a628f7SDimitry Andric Bookmark before_inner_function_pos = SetBookmark();
17874a628f7SDimitry Andric auto maybe_inner_function_name = ParseFunctionImpl(false);
17974a628f7SDimitry Andric if (maybe_inner_function_name)
18074a628f7SDimitry Andric if (ConsumeToken(tok::r_paren))
18174a628f7SDimitry Andric if (ConsumeArguments()) {
18274a628f7SDimitry Andric SkipFunctionQualifiers();
18374a628f7SDimitry Andric start_position.Remove();
18474a628f7SDimitry Andric before_inner_function_pos.Remove();
18574a628f7SDimitry Andric return maybe_inner_function_name;
18674a628f7SDimitry Andric }
18774a628f7SDimitry Andric }
18874a628f7SDimitry Andric
189e3b55780SDimitry Andric // Step 4:
190e3b55780SDimitry Andric //
191e3b55780SDimitry Andric // Parse the remaining string as a function pointer again.
192e3b55780SDimitry Andric // This time don't consume the inner-most typename since
193e3b55780SDimitry Andric // we're left with pointers only. This will strip another
194e3b55780SDimitry Andric // layer of pointers until we're left with the innermost
195e3b55780SDimitry Andric // function name/argument. I.e., func(long))(int))(float)
196e3b55780SDimitry Andric //
197e3b55780SDimitry Andric // Once we successfully stripped all pointers and gotten
198e3b55780SDimitry Andric // the innermost function name from ParseFunctionImpl above,
199e3b55780SDimitry Andric // we consume a single ')' and the arguments '(...)' that follows.
200e3b55780SDimitry Andric //
201e3b55780SDimitry Andric // Leaves us with:
202e3b55780SDimitry Andric // )(float)
203e3b55780SDimitry Andric //
204e3b55780SDimitry Andric // This is the remnant of the outer function pointers' arguments.
205e3b55780SDimitry Andric // Unwinding the recursive calls will remove the remaining
206e3b55780SDimitry Andric // arguments.
20774a628f7SDimitry Andric auto maybe_inner_function_ptr_name = ParseFuncPtr(false);
20874a628f7SDimitry Andric if (maybe_inner_function_ptr_name)
20974a628f7SDimitry Andric if (ConsumeToken(tok::r_paren))
21074a628f7SDimitry Andric if (ConsumeArguments()) {
21174a628f7SDimitry Andric SkipFunctionQualifiers();
21274a628f7SDimitry Andric start_position.Remove();
21374a628f7SDimitry Andric return maybe_inner_function_ptr_name;
21474a628f7SDimitry Andric }
215e3b55780SDimitry Andric
216e3b55780SDimitry Andric return std::nullopt;
21774a628f7SDimitry Andric }
21874a628f7SDimitry Andric
ConsumeArguments()21974a628f7SDimitry Andric bool CPlusPlusNameParser::ConsumeArguments() {
22074a628f7SDimitry Andric return ConsumeBrackets(tok::l_paren, tok::r_paren);
22174a628f7SDimitry Andric }
22274a628f7SDimitry Andric
ConsumeTemplateArgs()22374a628f7SDimitry Andric bool CPlusPlusNameParser::ConsumeTemplateArgs() {
22474a628f7SDimitry Andric Bookmark start_position = SetBookmark();
22574a628f7SDimitry Andric if (!HasMoreTokens() || Peek().getKind() != tok::less)
22674a628f7SDimitry Andric return false;
22774a628f7SDimitry Andric Advance();
22874a628f7SDimitry Andric
22974a628f7SDimitry Andric // Consuming template arguments is a bit trickier than consuming function
230f73363f1SDimitry Andric // arguments, because '<' '>' brackets are not always trivially balanced. In
231f73363f1SDimitry Andric // some rare cases tokens '<' and '>' can appear inside template arguments as
232f73363f1SDimitry Andric // arithmetic or shift operators not as template brackets. Examples:
233f73363f1SDimitry Andric // std::enable_if<(10u)<(64), bool>
23474a628f7SDimitry Andric // f<A<operator<(X,Y)::Subclass>>
235f73363f1SDimitry Andric // Good thing that compiler makes sure that really ambiguous cases of '>'
236f73363f1SDimitry Andric // usage should be enclosed within '()' brackets.
23774a628f7SDimitry Andric int template_counter = 1;
23874a628f7SDimitry Andric bool can_open_template = false;
23974a628f7SDimitry Andric while (HasMoreTokens() && template_counter > 0) {
24074a628f7SDimitry Andric tok::TokenKind kind = Peek().getKind();
24174a628f7SDimitry Andric switch (kind) {
24274a628f7SDimitry Andric case tok::greatergreater:
24374a628f7SDimitry Andric template_counter -= 2;
24474a628f7SDimitry Andric can_open_template = false;
24574a628f7SDimitry Andric Advance();
24674a628f7SDimitry Andric break;
24774a628f7SDimitry Andric case tok::greater:
24874a628f7SDimitry Andric --template_counter;
24974a628f7SDimitry Andric can_open_template = false;
25074a628f7SDimitry Andric Advance();
25174a628f7SDimitry Andric break;
25274a628f7SDimitry Andric case tok::less:
25374a628f7SDimitry Andric // '<' is an attempt to open a subteamplte
25474a628f7SDimitry Andric // check if parser is at the point where it's actually possible,
255f73363f1SDimitry Andric // otherwise it's just a part of an expression like 'sizeof(T)<(10)'. No
256f73363f1SDimitry Andric // need to do the same for '>' because compiler actually makes sure that
257f73363f1SDimitry Andric // '>' always surrounded by brackets to avoid ambiguity.
25874a628f7SDimitry Andric if (can_open_template)
25974a628f7SDimitry Andric ++template_counter;
26074a628f7SDimitry Andric can_open_template = false;
26174a628f7SDimitry Andric Advance();
26274a628f7SDimitry Andric break;
26374a628f7SDimitry Andric case tok::kw_operator: // C++ operator overloading.
26474a628f7SDimitry Andric if (!ConsumeOperator())
26574a628f7SDimitry Andric return false;
26674a628f7SDimitry Andric can_open_template = true;
26774a628f7SDimitry Andric break;
26874a628f7SDimitry Andric case tok::raw_identifier:
26974a628f7SDimitry Andric can_open_template = true;
27074a628f7SDimitry Andric Advance();
27174a628f7SDimitry Andric break;
27274a628f7SDimitry Andric case tok::l_square:
273e3b55780SDimitry Andric // Handle templates tagged with an ABI tag.
274e3b55780SDimitry Andric // An example demangled/prettified version is:
275e3b55780SDimitry Andric // func[abi:tag1][abi:tag2]<type[abi:tag3]>(int)
276e3b55780SDimitry Andric if (ConsumeAbiTag())
277e3b55780SDimitry Andric can_open_template = true;
278e3b55780SDimitry Andric else if (ConsumeBrackets(tok::l_square, tok::r_square))
27974a628f7SDimitry Andric can_open_template = false;
280e3b55780SDimitry Andric else
281e3b55780SDimitry Andric return false;
28274a628f7SDimitry Andric break;
28374a628f7SDimitry Andric case tok::l_paren:
28474a628f7SDimitry Andric if (!ConsumeArguments())
28574a628f7SDimitry Andric return false;
28674a628f7SDimitry Andric can_open_template = false;
28774a628f7SDimitry Andric break;
28874a628f7SDimitry Andric default:
28974a628f7SDimitry Andric can_open_template = false;
29074a628f7SDimitry Andric Advance();
29174a628f7SDimitry Andric break;
29274a628f7SDimitry Andric }
29374a628f7SDimitry Andric }
29474a628f7SDimitry Andric
295f73363f1SDimitry Andric if (template_counter != 0) {
29674a628f7SDimitry Andric return false;
29774a628f7SDimitry Andric }
29874a628f7SDimitry Andric start_position.Remove();
29974a628f7SDimitry Andric return true;
30074a628f7SDimitry Andric }
30174a628f7SDimitry Andric
ConsumeAbiTag()302e3b55780SDimitry Andric bool CPlusPlusNameParser::ConsumeAbiTag() {
303e3b55780SDimitry Andric Bookmark start_position = SetBookmark();
304e3b55780SDimitry Andric if (!ConsumeToken(tok::l_square))
305e3b55780SDimitry Andric return false;
306e3b55780SDimitry Andric
307e3b55780SDimitry Andric if (HasMoreTokens() && Peek().is(tok::raw_identifier) &&
308e3b55780SDimitry Andric Peek().getRawIdentifier() == "abi")
309e3b55780SDimitry Andric Advance();
310e3b55780SDimitry Andric else
311e3b55780SDimitry Andric return false;
312e3b55780SDimitry Andric
313e3b55780SDimitry Andric if (!ConsumeToken(tok::colon))
314e3b55780SDimitry Andric return false;
315e3b55780SDimitry Andric
316e3b55780SDimitry Andric // Consume the actual tag string (and allow some special characters)
317e3b55780SDimitry Andric while (ConsumeToken(tok::raw_identifier, tok::comma, tok::period,
318e3b55780SDimitry Andric tok::numeric_constant))
319e3b55780SDimitry Andric ;
320e3b55780SDimitry Andric
321e3b55780SDimitry Andric if (!ConsumeToken(tok::r_square))
322e3b55780SDimitry Andric return false;
323e3b55780SDimitry Andric
324e3b55780SDimitry Andric start_position.Remove();
325e3b55780SDimitry Andric return true;
326e3b55780SDimitry Andric }
327e3b55780SDimitry Andric
ConsumeAnonymousNamespace()32874a628f7SDimitry Andric bool CPlusPlusNameParser::ConsumeAnonymousNamespace() {
32974a628f7SDimitry Andric Bookmark start_position = SetBookmark();
33074a628f7SDimitry Andric if (!ConsumeToken(tok::l_paren)) {
33174a628f7SDimitry Andric return false;
33274a628f7SDimitry Andric }
33374a628f7SDimitry Andric constexpr llvm::StringLiteral g_anonymous("anonymous");
33474a628f7SDimitry Andric if (HasMoreTokens() && Peek().is(tok::raw_identifier) &&
33574a628f7SDimitry Andric Peek().getRawIdentifier() == g_anonymous) {
33674a628f7SDimitry Andric Advance();
33774a628f7SDimitry Andric } else {
33874a628f7SDimitry Andric return false;
33974a628f7SDimitry Andric }
34074a628f7SDimitry Andric
34174a628f7SDimitry Andric if (!ConsumeToken(tok::kw_namespace)) {
34274a628f7SDimitry Andric return false;
34374a628f7SDimitry Andric }
34474a628f7SDimitry Andric
34574a628f7SDimitry Andric if (!ConsumeToken(tok::r_paren)) {
34674a628f7SDimitry Andric return false;
34774a628f7SDimitry Andric }
34874a628f7SDimitry Andric start_position.Remove();
34974a628f7SDimitry Andric return true;
35074a628f7SDimitry Andric }
35174a628f7SDimitry Andric
ConsumeLambda()352a884e649SDimitry Andric bool CPlusPlusNameParser::ConsumeLambda() {
353a884e649SDimitry Andric Bookmark start_position = SetBookmark();
354a884e649SDimitry Andric if (!ConsumeToken(tok::l_brace)) {
355a884e649SDimitry Andric return false;
356a884e649SDimitry Andric }
357a884e649SDimitry Andric constexpr llvm::StringLiteral g_lambda("lambda");
358a884e649SDimitry Andric if (HasMoreTokens() && Peek().is(tok::raw_identifier) &&
359a884e649SDimitry Andric Peek().getRawIdentifier() == g_lambda) {
360a884e649SDimitry Andric // Put the matched brace back so we can use ConsumeBrackets
361a884e649SDimitry Andric TakeBack();
362a884e649SDimitry Andric } else {
363a884e649SDimitry Andric return false;
364a884e649SDimitry Andric }
365a884e649SDimitry Andric
366a884e649SDimitry Andric if (!ConsumeBrackets(tok::l_brace, tok::r_brace)) {
367a884e649SDimitry Andric return false;
368a884e649SDimitry Andric }
369a884e649SDimitry Andric
370a884e649SDimitry Andric start_position.Remove();
371a884e649SDimitry Andric return true;
372a884e649SDimitry Andric }
373a884e649SDimitry Andric
ConsumeBrackets(tok::TokenKind left,tok::TokenKind right)37474a628f7SDimitry Andric bool CPlusPlusNameParser::ConsumeBrackets(tok::TokenKind left,
37574a628f7SDimitry Andric tok::TokenKind right) {
37674a628f7SDimitry Andric Bookmark start_position = SetBookmark();
37774a628f7SDimitry Andric if (!HasMoreTokens() || Peek().getKind() != left)
37874a628f7SDimitry Andric return false;
37974a628f7SDimitry Andric Advance();
38074a628f7SDimitry Andric
38174a628f7SDimitry Andric int counter = 1;
38274a628f7SDimitry Andric while (HasMoreTokens() && counter > 0) {
38374a628f7SDimitry Andric tok::TokenKind kind = Peek().getKind();
38474a628f7SDimitry Andric if (kind == right)
38574a628f7SDimitry Andric --counter;
38674a628f7SDimitry Andric else if (kind == left)
38774a628f7SDimitry Andric ++counter;
38874a628f7SDimitry Andric Advance();
38974a628f7SDimitry Andric }
39074a628f7SDimitry Andric
39174a628f7SDimitry Andric assert(counter >= 0);
39274a628f7SDimitry Andric if (counter > 0) {
39374a628f7SDimitry Andric return false;
39474a628f7SDimitry Andric }
39574a628f7SDimitry Andric start_position.Remove();
39674a628f7SDimitry Andric return true;
39774a628f7SDimitry Andric }
39874a628f7SDimitry Andric
ConsumeOperator()39974a628f7SDimitry Andric bool CPlusPlusNameParser::ConsumeOperator() {
40074a628f7SDimitry Andric Bookmark start_position = SetBookmark();
40174a628f7SDimitry Andric if (!ConsumeToken(tok::kw_operator))
40274a628f7SDimitry Andric return false;
40374a628f7SDimitry Andric
40474a628f7SDimitry Andric if (!HasMoreTokens()) {
40574a628f7SDimitry Andric return false;
40674a628f7SDimitry Andric }
40774a628f7SDimitry Andric
40874a628f7SDimitry Andric const auto &token = Peek();
409cfca06d7SDimitry Andric
410cfca06d7SDimitry Andric // When clang generates debug info it adds template parameters to names.
411cfca06d7SDimitry Andric // Since clang doesn't add a space between the name and the template parameter
412cfca06d7SDimitry Andric // in some cases we are not generating valid C++ names e.g.:
413cfca06d7SDimitry Andric //
414cfca06d7SDimitry Andric // operator<<A::B>
415cfca06d7SDimitry Andric //
416cfca06d7SDimitry Andric // In some of these cases we will not parse them correctly. This fixes the
417cfca06d7SDimitry Andric // issue by detecting this case and inserting tok::less in place of
418cfca06d7SDimitry Andric // tok::lessless and returning successfully that we consumed the operator.
419cfca06d7SDimitry Andric if (token.getKind() == tok::lessless) {
420cfca06d7SDimitry Andric // Make sure we have more tokens before attempting to look ahead one more.
421cfca06d7SDimitry Andric if (m_next_token_index + 1 < m_tokens.size()) {
422cfca06d7SDimitry Andric // Look ahead two tokens.
423cfca06d7SDimitry Andric clang::Token n_token = m_tokens[m_next_token_index + 1];
424cfca06d7SDimitry Andric // If we find ( or < then this is indeed operator<< no need for fix.
425cfca06d7SDimitry Andric if (n_token.getKind() != tok::l_paren && n_token.getKind() != tok::less) {
426cfca06d7SDimitry Andric clang::Token tmp_tok;
427cfca06d7SDimitry Andric tmp_tok.startToken();
428cfca06d7SDimitry Andric tmp_tok.setLength(1);
429cfca06d7SDimitry Andric tmp_tok.setLocation(token.getLocation().getLocWithOffset(1));
430cfca06d7SDimitry Andric tmp_tok.setKind(tok::less);
431cfca06d7SDimitry Andric
432cfca06d7SDimitry Andric m_tokens[m_next_token_index] = tmp_tok;
433cfca06d7SDimitry Andric
434cfca06d7SDimitry Andric start_position.Remove();
435cfca06d7SDimitry Andric return true;
436cfca06d7SDimitry Andric }
437cfca06d7SDimitry Andric }
438cfca06d7SDimitry Andric }
439cfca06d7SDimitry Andric
44074a628f7SDimitry Andric switch (token.getKind()) {
44174a628f7SDimitry Andric case tok::kw_new:
44274a628f7SDimitry Andric case tok::kw_delete:
44374a628f7SDimitry Andric // This is 'new' or 'delete' operators.
44474a628f7SDimitry Andric Advance();
44574a628f7SDimitry Andric // Check for array new/delete.
44674a628f7SDimitry Andric if (HasMoreTokens() && Peek().is(tok::l_square)) {
44774a628f7SDimitry Andric // Consume the '[' and ']'.
44874a628f7SDimitry Andric if (!ConsumeBrackets(tok::l_square, tok::r_square))
44974a628f7SDimitry Andric return false;
45074a628f7SDimitry Andric }
45174a628f7SDimitry Andric break;
45274a628f7SDimitry Andric
45374a628f7SDimitry Andric #define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
45474a628f7SDimitry Andric case tok::Token: \
45574a628f7SDimitry Andric Advance(); \
45674a628f7SDimitry Andric break;
45774a628f7SDimitry Andric #define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemberOnly)
45874a628f7SDimitry Andric #include "clang/Basic/OperatorKinds.def"
45974a628f7SDimitry Andric #undef OVERLOADED_OPERATOR
46074a628f7SDimitry Andric #undef OVERLOADED_OPERATOR_MULTI
46174a628f7SDimitry Andric
46274a628f7SDimitry Andric case tok::l_paren:
46374a628f7SDimitry Andric // Call operator consume '(' ... ')'.
46474a628f7SDimitry Andric if (ConsumeBrackets(tok::l_paren, tok::r_paren))
46574a628f7SDimitry Andric break;
46674a628f7SDimitry Andric return false;
46774a628f7SDimitry Andric
46874a628f7SDimitry Andric case tok::l_square:
46974a628f7SDimitry Andric // This is a [] operator.
47074a628f7SDimitry Andric // Consume the '[' and ']'.
47174a628f7SDimitry Andric if (ConsumeBrackets(tok::l_square, tok::r_square))
47274a628f7SDimitry Andric break;
47374a628f7SDimitry Andric return false;
47474a628f7SDimitry Andric
47574a628f7SDimitry Andric default:
47674a628f7SDimitry Andric // This might be a cast operator.
47774a628f7SDimitry Andric if (ConsumeTypename())
47874a628f7SDimitry Andric break;
47974a628f7SDimitry Andric return false;
48074a628f7SDimitry Andric }
48174a628f7SDimitry Andric start_position.Remove();
48274a628f7SDimitry Andric return true;
48374a628f7SDimitry Andric }
48474a628f7SDimitry Andric
SkipTypeQualifiers()48574a628f7SDimitry Andric void CPlusPlusNameParser::SkipTypeQualifiers() {
48674a628f7SDimitry Andric while (ConsumeToken(tok::kw_const, tok::kw_volatile))
48774a628f7SDimitry Andric ;
48874a628f7SDimitry Andric }
48974a628f7SDimitry Andric
SkipFunctionQualifiers()49074a628f7SDimitry Andric void CPlusPlusNameParser::SkipFunctionQualifiers() {
49174a628f7SDimitry Andric while (ConsumeToken(tok::kw_const, tok::kw_volatile, tok::amp, tok::ampamp))
49274a628f7SDimitry Andric ;
49374a628f7SDimitry Andric }
49474a628f7SDimitry Andric
ConsumeBuiltinType()49574a628f7SDimitry Andric bool CPlusPlusNameParser::ConsumeBuiltinType() {
49674a628f7SDimitry Andric bool result = false;
49774a628f7SDimitry Andric bool continue_parsing = true;
498f73363f1SDimitry Andric // Built-in types can be made of a few keywords like 'unsigned long long
499f73363f1SDimitry Andric // int'. This function consumes all built-in type keywords without checking
500f73363f1SDimitry Andric // if they make sense like 'unsigned char void'.
50174a628f7SDimitry Andric while (continue_parsing && HasMoreTokens()) {
50274a628f7SDimitry Andric switch (Peek().getKind()) {
50374a628f7SDimitry Andric case tok::kw_short:
50474a628f7SDimitry Andric case tok::kw_long:
50574a628f7SDimitry Andric case tok::kw___int64:
50674a628f7SDimitry Andric case tok::kw___int128:
50774a628f7SDimitry Andric case tok::kw_signed:
50874a628f7SDimitry Andric case tok::kw_unsigned:
50974a628f7SDimitry Andric case tok::kw_void:
51074a628f7SDimitry Andric case tok::kw_char:
51174a628f7SDimitry Andric case tok::kw_int:
51274a628f7SDimitry Andric case tok::kw_half:
51374a628f7SDimitry Andric case tok::kw_float:
51474a628f7SDimitry Andric case tok::kw_double:
51574a628f7SDimitry Andric case tok::kw___float128:
51674a628f7SDimitry Andric case tok::kw_wchar_t:
51774a628f7SDimitry Andric case tok::kw_bool:
51874a628f7SDimitry Andric case tok::kw_char16_t:
51974a628f7SDimitry Andric case tok::kw_char32_t:
52074a628f7SDimitry Andric result = true;
52174a628f7SDimitry Andric Advance();
52274a628f7SDimitry Andric break;
52374a628f7SDimitry Andric default:
52474a628f7SDimitry Andric continue_parsing = false;
52574a628f7SDimitry Andric break;
52674a628f7SDimitry Andric }
52774a628f7SDimitry Andric }
52874a628f7SDimitry Andric return result;
52974a628f7SDimitry Andric }
53074a628f7SDimitry Andric
SkipPtrsAndRefs()53174a628f7SDimitry Andric void CPlusPlusNameParser::SkipPtrsAndRefs() {
53274a628f7SDimitry Andric // Ignoring result.
53374a628f7SDimitry Andric ConsumePtrsAndRefs();
53474a628f7SDimitry Andric }
53574a628f7SDimitry Andric
ConsumePtrsAndRefs()53674a628f7SDimitry Andric bool CPlusPlusNameParser::ConsumePtrsAndRefs() {
53774a628f7SDimitry Andric bool found = false;
53874a628f7SDimitry Andric SkipTypeQualifiers();
53974a628f7SDimitry Andric while (ConsumeToken(tok::star, tok::amp, tok::ampamp, tok::kw_const,
54074a628f7SDimitry Andric tok::kw_volatile)) {
54174a628f7SDimitry Andric found = true;
54274a628f7SDimitry Andric SkipTypeQualifiers();
54374a628f7SDimitry Andric }
54474a628f7SDimitry Andric return found;
54574a628f7SDimitry Andric }
54674a628f7SDimitry Andric
ConsumeDecltype()54774a628f7SDimitry Andric bool CPlusPlusNameParser::ConsumeDecltype() {
54874a628f7SDimitry Andric Bookmark start_position = SetBookmark();
54974a628f7SDimitry Andric if (!ConsumeToken(tok::kw_decltype))
55074a628f7SDimitry Andric return false;
55174a628f7SDimitry Andric
55274a628f7SDimitry Andric if (!ConsumeArguments())
55374a628f7SDimitry Andric return false;
55474a628f7SDimitry Andric
55574a628f7SDimitry Andric start_position.Remove();
55674a628f7SDimitry Andric return true;
55774a628f7SDimitry Andric }
55874a628f7SDimitry Andric
ConsumeTypename()55974a628f7SDimitry Andric bool CPlusPlusNameParser::ConsumeTypename() {
56074a628f7SDimitry Andric Bookmark start_position = SetBookmark();
56174a628f7SDimitry Andric SkipTypeQualifiers();
56274a628f7SDimitry Andric if (!ConsumeBuiltinType() && !ConsumeDecltype()) {
56374a628f7SDimitry Andric if (!ParseFullNameImpl())
56474a628f7SDimitry Andric return false;
56574a628f7SDimitry Andric }
56674a628f7SDimitry Andric SkipPtrsAndRefs();
56774a628f7SDimitry Andric start_position.Remove();
56874a628f7SDimitry Andric return true;
56974a628f7SDimitry Andric }
57074a628f7SDimitry Andric
571e3b55780SDimitry Andric std::optional<CPlusPlusNameParser::ParsedNameRanges>
ParseFullNameImpl()57274a628f7SDimitry Andric CPlusPlusNameParser::ParseFullNameImpl() {
57374a628f7SDimitry Andric // Name parsing state machine.
57474a628f7SDimitry Andric enum class State {
57574a628f7SDimitry Andric Beginning, // start of the name
57674a628f7SDimitry Andric AfterTwoColons, // right after ::
57774a628f7SDimitry Andric AfterIdentifier, // right after alphanumerical identifier ([a-z0-9_]+)
57874a628f7SDimitry Andric AfterTemplate, // right after template brackets (<something>)
57974a628f7SDimitry Andric AfterOperator, // right after name of C++ operator
58074a628f7SDimitry Andric };
58174a628f7SDimitry Andric
58274a628f7SDimitry Andric Bookmark start_position = SetBookmark();
58374a628f7SDimitry Andric State state = State::Beginning;
58474a628f7SDimitry Andric bool continue_parsing = true;
585e3b55780SDimitry Andric std::optional<size_t> last_coloncolon_position;
58674a628f7SDimitry Andric
58774a628f7SDimitry Andric while (continue_parsing && HasMoreTokens()) {
58874a628f7SDimitry Andric const auto &token = Peek();
58974a628f7SDimitry Andric switch (token.getKind()) {
59074a628f7SDimitry Andric case tok::raw_identifier: // Just a name.
59174a628f7SDimitry Andric if (state != State::Beginning && state != State::AfterTwoColons) {
59274a628f7SDimitry Andric continue_parsing = false;
59374a628f7SDimitry Andric break;
59474a628f7SDimitry Andric }
59574a628f7SDimitry Andric Advance();
59674a628f7SDimitry Andric state = State::AfterIdentifier;
59774a628f7SDimitry Andric break;
598e3b55780SDimitry Andric case tok::l_square: {
599e3b55780SDimitry Andric // Handles types or functions that were tagged
600e3b55780SDimitry Andric // with, e.g.,
601e3b55780SDimitry Andric // [[gnu::abi_tag("tag1","tag2")]] func()
602e3b55780SDimitry Andric // and demangled/prettified into:
603e3b55780SDimitry Andric // func[abi:tag1][abi:tag2]()
604e3b55780SDimitry Andric
605e3b55780SDimitry Andric // ABI tags only appear after a method or type name
606e3b55780SDimitry Andric const bool valid_state =
607e3b55780SDimitry Andric state == State::AfterIdentifier || state == State::AfterOperator;
608e3b55780SDimitry Andric if (!valid_state || !ConsumeAbiTag()) {
609e3b55780SDimitry Andric continue_parsing = false;
610e3b55780SDimitry Andric }
611e3b55780SDimitry Andric
612e3b55780SDimitry Andric break;
613e3b55780SDimitry Andric }
61474a628f7SDimitry Andric case tok::l_paren: {
61574a628f7SDimitry Andric if (state == State::Beginning || state == State::AfterTwoColons) {
61674a628f7SDimitry Andric // (anonymous namespace)
61774a628f7SDimitry Andric if (ConsumeAnonymousNamespace()) {
61874a628f7SDimitry Andric state = State::AfterIdentifier;
61974a628f7SDimitry Andric break;
62074a628f7SDimitry Andric }
62174a628f7SDimitry Andric }
62274a628f7SDimitry Andric
62374a628f7SDimitry Andric // Type declared inside a function 'func()::Type'
62474a628f7SDimitry Andric if (state != State::AfterIdentifier && state != State::AfterTemplate &&
62574a628f7SDimitry Andric state != State::AfterOperator) {
62674a628f7SDimitry Andric continue_parsing = false;
62774a628f7SDimitry Andric break;
62874a628f7SDimitry Andric }
62974a628f7SDimitry Andric Bookmark l_paren_position = SetBookmark();
63074a628f7SDimitry Andric // Consume the '(' ... ') [const]'.
63174a628f7SDimitry Andric if (!ConsumeArguments()) {
63274a628f7SDimitry Andric continue_parsing = false;
63374a628f7SDimitry Andric break;
63474a628f7SDimitry Andric }
63574a628f7SDimitry Andric SkipFunctionQualifiers();
63674a628f7SDimitry Andric
63774a628f7SDimitry Andric // Consume '::'
63874a628f7SDimitry Andric size_t coloncolon_position = GetCurrentPosition();
63974a628f7SDimitry Andric if (!ConsumeToken(tok::coloncolon)) {
64074a628f7SDimitry Andric continue_parsing = false;
64174a628f7SDimitry Andric break;
64274a628f7SDimitry Andric }
64374a628f7SDimitry Andric l_paren_position.Remove();
64474a628f7SDimitry Andric last_coloncolon_position = coloncolon_position;
64574a628f7SDimitry Andric state = State::AfterTwoColons;
64674a628f7SDimitry Andric break;
64774a628f7SDimitry Andric }
648a884e649SDimitry Andric case tok::l_brace:
649a884e649SDimitry Andric if (state == State::Beginning || state == State::AfterTwoColons) {
650a884e649SDimitry Andric if (ConsumeLambda()) {
651a884e649SDimitry Andric state = State::AfterIdentifier;
652a884e649SDimitry Andric break;
653a884e649SDimitry Andric }
654a884e649SDimitry Andric }
655a884e649SDimitry Andric continue_parsing = false;
656a884e649SDimitry Andric break;
65774a628f7SDimitry Andric case tok::coloncolon: // Type nesting delimiter.
65874a628f7SDimitry Andric if (state != State::Beginning && state != State::AfterIdentifier &&
65974a628f7SDimitry Andric state != State::AfterTemplate) {
66074a628f7SDimitry Andric continue_parsing = false;
66174a628f7SDimitry Andric break;
66274a628f7SDimitry Andric }
66374a628f7SDimitry Andric last_coloncolon_position = GetCurrentPosition();
66474a628f7SDimitry Andric Advance();
66574a628f7SDimitry Andric state = State::AfterTwoColons;
66674a628f7SDimitry Andric break;
66774a628f7SDimitry Andric case tok::less: // Template brackets.
66874a628f7SDimitry Andric if (state != State::AfterIdentifier && state != State::AfterOperator) {
66974a628f7SDimitry Andric continue_parsing = false;
67074a628f7SDimitry Andric break;
67174a628f7SDimitry Andric }
67274a628f7SDimitry Andric if (!ConsumeTemplateArgs()) {
67374a628f7SDimitry Andric continue_parsing = false;
67474a628f7SDimitry Andric break;
67574a628f7SDimitry Andric }
67674a628f7SDimitry Andric state = State::AfterTemplate;
67774a628f7SDimitry Andric break;
67874a628f7SDimitry Andric case tok::kw_operator: // C++ operator overloading.
67974a628f7SDimitry Andric if (state != State::Beginning && state != State::AfterTwoColons) {
68074a628f7SDimitry Andric continue_parsing = false;
68174a628f7SDimitry Andric break;
68274a628f7SDimitry Andric }
68374a628f7SDimitry Andric if (!ConsumeOperator()) {
68474a628f7SDimitry Andric continue_parsing = false;
68574a628f7SDimitry Andric break;
68674a628f7SDimitry Andric }
68774a628f7SDimitry Andric state = State::AfterOperator;
68874a628f7SDimitry Andric break;
68974a628f7SDimitry Andric case tok::tilde: // Destructor.
69074a628f7SDimitry Andric if (state != State::Beginning && state != State::AfterTwoColons) {
69174a628f7SDimitry Andric continue_parsing = false;
69274a628f7SDimitry Andric break;
69374a628f7SDimitry Andric }
69474a628f7SDimitry Andric Advance();
69574a628f7SDimitry Andric if (ConsumeToken(tok::raw_identifier)) {
69674a628f7SDimitry Andric state = State::AfterIdentifier;
69774a628f7SDimitry Andric } else {
69874a628f7SDimitry Andric TakeBack();
69974a628f7SDimitry Andric continue_parsing = false;
70074a628f7SDimitry Andric }
70174a628f7SDimitry Andric break;
70274a628f7SDimitry Andric default:
70374a628f7SDimitry Andric continue_parsing = false;
70474a628f7SDimitry Andric break;
70574a628f7SDimitry Andric }
70674a628f7SDimitry Andric }
70774a628f7SDimitry Andric
70874a628f7SDimitry Andric if (state == State::AfterIdentifier || state == State::AfterOperator ||
70974a628f7SDimitry Andric state == State::AfterTemplate) {
71074a628f7SDimitry Andric ParsedNameRanges result;
71174a628f7SDimitry Andric if (last_coloncolon_position) {
712e3b55780SDimitry Andric result.context_range =
713e3b55780SDimitry Andric Range(start_position.GetSavedPosition(), *last_coloncolon_position);
71474a628f7SDimitry Andric result.basename_range =
715e3b55780SDimitry Andric Range(*last_coloncolon_position + 1, GetCurrentPosition());
71674a628f7SDimitry Andric } else {
71774a628f7SDimitry Andric result.basename_range =
71874a628f7SDimitry Andric Range(start_position.GetSavedPosition(), GetCurrentPosition());
71974a628f7SDimitry Andric }
72074a628f7SDimitry Andric start_position.Remove();
72174a628f7SDimitry Andric return result;
72274a628f7SDimitry Andric } else {
723e3b55780SDimitry Andric return std::nullopt;
72474a628f7SDimitry Andric }
72574a628f7SDimitry Andric }
72674a628f7SDimitry Andric
GetTextForRange(const Range & range)72774a628f7SDimitry Andric llvm::StringRef CPlusPlusNameParser::GetTextForRange(const Range &range) {
72874a628f7SDimitry Andric if (range.empty())
72974a628f7SDimitry Andric return llvm::StringRef();
73074a628f7SDimitry Andric assert(range.begin_index < range.end_index);
73174a628f7SDimitry Andric assert(range.begin_index < m_tokens.size());
73274a628f7SDimitry Andric assert(range.end_index <= m_tokens.size());
73374a628f7SDimitry Andric clang::Token &first_token = m_tokens[range.begin_index];
73474a628f7SDimitry Andric clang::Token &last_token = m_tokens[range.end_index - 1];
73574a628f7SDimitry Andric clang::SourceLocation start_loc = first_token.getLocation();
73674a628f7SDimitry Andric clang::SourceLocation end_loc = last_token.getLocation();
73774a628f7SDimitry Andric unsigned start_pos = start_loc.getRawEncoding();
73874a628f7SDimitry Andric unsigned end_pos = end_loc.getRawEncoding() + last_token.getLength();
73974a628f7SDimitry Andric return m_text.take_front(end_pos).drop_front(start_pos);
74074a628f7SDimitry Andric }
74174a628f7SDimitry Andric
GetLangOptions()74274a628f7SDimitry Andric static const clang::LangOptions &GetLangOptions() {
74374a628f7SDimitry Andric static clang::LangOptions g_options;
74474a628f7SDimitry Andric static llvm::once_flag g_once_flag;
74574a628f7SDimitry Andric llvm::call_once(g_once_flag, []() {
74674a628f7SDimitry Andric g_options.LineComment = true;
74774a628f7SDimitry Andric g_options.C99 = true;
74874a628f7SDimitry Andric g_options.C11 = true;
74974a628f7SDimitry Andric g_options.CPlusPlus = true;
75074a628f7SDimitry Andric g_options.CPlusPlus11 = true;
75174a628f7SDimitry Andric g_options.CPlusPlus14 = true;
752ef5d0b5eSDimitry Andric g_options.CPlusPlus17 = true;
7537fa27ce4SDimitry Andric g_options.CPlusPlus20 = true;
75474a628f7SDimitry Andric });
75574a628f7SDimitry Andric return g_options;
75674a628f7SDimitry Andric }
75774a628f7SDimitry Andric
GetKeywordsMap()75874a628f7SDimitry Andric static const llvm::StringMap<tok::TokenKind> &GetKeywordsMap() {
75974a628f7SDimitry Andric static llvm::StringMap<tok::TokenKind> g_map{
76074a628f7SDimitry Andric #define KEYWORD(Name, Flags) {llvm::StringRef(#Name), tok::kw_##Name},
76174a628f7SDimitry Andric #include "clang/Basic/TokenKinds.def"
76274a628f7SDimitry Andric #undef KEYWORD
76374a628f7SDimitry Andric };
76474a628f7SDimitry Andric return g_map;
76574a628f7SDimitry Andric }
76674a628f7SDimitry Andric
ExtractTokens()76774a628f7SDimitry Andric void CPlusPlusNameParser::ExtractTokens() {
7685f29bb8aSDimitry Andric if (m_text.empty())
7695f29bb8aSDimitry Andric return;
77074a628f7SDimitry Andric clang::Lexer lexer(clang::SourceLocation(), GetLangOptions(), m_text.data(),
77174a628f7SDimitry Andric m_text.data(), m_text.data() + m_text.size());
77274a628f7SDimitry Andric const auto &kw_map = GetKeywordsMap();
77374a628f7SDimitry Andric clang::Token token;
77474a628f7SDimitry Andric for (lexer.LexFromRawLexer(token); !token.is(clang::tok::eof);
77574a628f7SDimitry Andric lexer.LexFromRawLexer(token)) {
77674a628f7SDimitry Andric if (token.is(clang::tok::raw_identifier)) {
77774a628f7SDimitry Andric auto it = kw_map.find(token.getRawIdentifier());
77874a628f7SDimitry Andric if (it != kw_map.end()) {
77974a628f7SDimitry Andric token.setKind(it->getValue());
78074a628f7SDimitry Andric }
78174a628f7SDimitry Andric }
78274a628f7SDimitry Andric
78374a628f7SDimitry Andric m_tokens.push_back(token);
78474a628f7SDimitry Andric }
78574a628f7SDimitry Andric }
786