|  | // Copyright 2018 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "src/developer/debug/zxdb/expr/expr_parser.h" | 
|  |  | 
|  | #include <sstream> | 
|  |  | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include "src/developer/debug/zxdb/common/string_util.h" | 
|  | #include "src/developer/debug/zxdb/expr/expr_tokenizer.h" | 
|  | #include "src/developer/debug/zxdb/symbols/collection.h" | 
|  |  | 
|  | namespace zxdb { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // This name looker-upper declares anything beginning with "Namespace" is a namespace, anything | 
|  | // beginning with "Template" is a template, and anything beginning with "Type" is a type. | 
|  | FoundName TestLookupName(const ParsedIdentifier& ident, const FindNameOptions& opts) { | 
|  | const ParsedIdentifierComponent& comp = ident.components().back(); | 
|  | const std::string& name = comp.name(); | 
|  |  | 
|  | if (opts.find_namespaces && StringBeginsWith(name, "Namespace")) | 
|  | return FoundName(FoundName::kNamespace, ident.GetFullName()); | 
|  | if (opts.find_types && StringBeginsWith(name, "Type")) { | 
|  | // Make up a random class to go with the type. | 
|  | auto type = fxl::MakeRefCounted<Collection>(DwarfTag::kClassType); | 
|  | type->set_assigned_name("Type"); | 
|  |  | 
|  | // NOTE: This doesn't qualify the type with namespaces or classes present in the identifier so | 
|  | // qualified names ("Namespace::Type") won't convert to strings properly. This could be added if | 
|  | // necessary. | 
|  | return FoundName(std::move(type)); | 
|  | } | 
|  | if (opts.find_templates && StringBeginsWith(name, "Template")) { | 
|  | if (comp.has_template()) { | 
|  | // Assume templates with arguments are types. | 
|  | auto collection = fxl::MakeRefCounted<Collection>(DwarfTag::kClassType); | 
|  | // This name won't always be correct if the input has a namespace or other qualifier, but is | 
|  | // good enough for this test. | 
|  | collection->set_assigned_name(ident.GetFullName()); | 
|  | return FoundName(std::move(collection)); | 
|  | } | 
|  | return FoundName(FoundName::kTemplate, ident.GetFullName()); | 
|  | } | 
|  | return FoundName(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class ExprParserTest : public testing::Test { | 
|  | public: | 
|  | ExprParserTest() = default; | 
|  |  | 
|  | // Valid after Parse() is called. | 
|  | ExprParser& parser() { return *parser_; } | 
|  |  | 
|  | fxl::RefPtr<ExprNode> Parse(const char* input, NameLookupCallback name_lookup) { | 
|  | return Parse(input, ExprLanguage::kC, std::move(name_lookup)); | 
|  | } | 
|  |  | 
|  | fxl::RefPtr<ExprNode> Parse(const char* input, ExprLanguage lang = ExprLanguage::kC, | 
|  | NameLookupCallback name_lookup = NameLookupCallback()) { | 
|  | parser_.reset(); | 
|  |  | 
|  | tokenizer_ = std::make_unique<ExprTokenizer>(input, lang); | 
|  | if (!tokenizer_->Tokenize()) { | 
|  | ADD_FAILURE() << "Tokenization failure: " << input; | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | parser_ = std::make_unique<ExprParser>(tokenizer_->TakeTokens(), tokenizer_->language(), | 
|  | std::move(name_lookup)); | 
|  | return parser_->ParseExpression(); | 
|  | } | 
|  |  | 
|  | // Does the parse and returns the string dump of the structure. | 
|  | std::string GetParseString(const char* input, | 
|  | NameLookupCallback name_lookup = NameLookupCallback()) { | 
|  | return GetParseString(input, ExprLanguage::kC, std::move(name_lookup)); | 
|  | } | 
|  |  | 
|  | std::string GetParseString(const char* input, ExprLanguage lang, | 
|  | NameLookupCallback name_lookup = NameLookupCallback()) { | 
|  | auto root = Parse(input, lang, std::move(name_lookup)); | 
|  | if (!root) { | 
|  | // Expect calls to this to parse successfully. | 
|  | if (parser_.get()) | 
|  | ADD_FAILURE() << "Parse failure: " << parser_->err().msg(); | 
|  | return std::string(); | 
|  | } | 
|  |  | 
|  | std::ostringstream out; | 
|  | root->Print(out, 0); | 
|  | return out.str(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<ExprTokenizer> tokenizer_; | 
|  | std::unique_ptr<ExprParser> parser_; | 
|  | }; | 
|  |  | 
|  | TEST_F(ExprParserTest, Empty) { | 
|  | auto result = Parse(""); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected expression instead of end of input.", parser().err().msg()); | 
|  |  | 
|  | result = Parse(" "); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected expression instead of end of input.", parser().err().msg()); | 
|  |  | 
|  | result = Parse("; "); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Empty expression not permitted here.", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, Block) { | 
|  | EXPECT_EQ("BLOCK\n", GetParseString("{}")); | 
|  | EXPECT_EQ( | 
|  | "BLOCK\n" | 
|  | " LITERAL(1)\n", | 
|  | GetParseString("{ 1; }")); | 
|  | EXPECT_EQ( | 
|  | "BLOCK\n" | 
|  | " BINARY_OP(+)\n" | 
|  | "  LITERAL(1)\n" | 
|  | "  IDENTIFIER(\"n\")\n" | 
|  | " LITERAL(2)\n" | 
|  | " LITERAL(3)\n", | 
|  | GetParseString("{ 1+n;2;    \n3 ;}")); | 
|  |  | 
|  | // Last Rust expression doesn't require a semicolon. | 
|  | EXPECT_EQ( | 
|  | "BLOCK\n" | 
|  | " LITERAL(1)\n", | 
|  | GetParseString("{1}", ExprLanguage::kRust)); | 
|  | EXPECT_EQ( | 
|  | "BLOCK\n" | 
|  | " LITERAL(1)\n" | 
|  | " LITERAL(2)\n", | 
|  | GetParseString("{1;2}", ExprLanguage::kRust)); | 
|  |  | 
|  | // Duplicate statement separators. | 
|  | EXPECT_EQ( | 
|  | "BLOCK\n" | 
|  | " LITERAL(1)\n" | 
|  | " LITERAL(2)\n", | 
|  | GetParseString("{;1;;2;}", ExprLanguage::kRust)); | 
|  |  | 
|  | // Nested blocks. | 
|  | EXPECT_EQ( | 
|  | "BLOCK\n" | 
|  | " BLOCK\n" | 
|  | "  BLOCK\n" | 
|  | " LITERAL(1)\n" | 
|  | " LITERAL(2)\n" | 
|  | " BLOCK\n", | 
|  | GetParseString("{{{}};1;;2;{;}}", ExprLanguage::kRust)); | 
|  |  | 
|  | // C requires a semicolon as the last element. | 
|  | auto result = Parse("{1}"); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected ';'.", parser().err().msg()); | 
|  | result = Parse("{1;2}"); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected ';'.", parser().err().msg()); | 
|  |  | 
|  | // Rust blocks as expressions. Our parser will currenly accept this in C as well but it will get | 
|  | // rejected during execution since blocks don't return anything. | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(=)\n" | 
|  | " IDENTIFIER(\"a\")\n" | 
|  | " BLOCK\n" | 
|  | "  LITERAL(1)\n" | 
|  | "  LITERAL(2)\n", | 
|  | GetParseString("a = {1;2}", ExprLanguage::kRust)); | 
|  |  | 
|  | // Missing terminating "}". | 
|  | result = Parse("{1;"); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected '}'. Hit the end of input instead.", parser().err().msg()); | 
|  |  | 
|  | // No separator between elements. | 
|  | result = Parse("{1 {}}"); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected ';'.", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, Identifier) { | 
|  | auto result = Parse("name"); | 
|  | ASSERT_TRUE(result); | 
|  |  | 
|  | const IdentifierExprNode* ident = result->AsIdentifier(); | 
|  | ASSERT_TRUE(ident); | 
|  | EXPECT_EQ("name", ident->ident().GetFullName()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, Dot) { | 
|  | auto result = Parse("base.member"); | 
|  | ASSERT_TRUE(result); | 
|  |  | 
|  | const MemberAccessExprNode* access = result->AsMemberAccess(); | 
|  | ASSERT_TRUE(access); | 
|  | EXPECT_EQ(ExprTokenType::kDot, access->accessor().type()); | 
|  | EXPECT_EQ(".", access->accessor().value()); | 
|  |  | 
|  | // Left side is the "base" identifier. | 
|  | const IdentifierExprNode* base = access->left()->AsIdentifier(); | 
|  | ASSERT_TRUE(base); | 
|  | EXPECT_EQ("base", base->ident().GetFullName()); | 
|  |  | 
|  | // Member name. | 
|  | EXPECT_EQ("member", access->member().GetFullName()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, DotNumber) { | 
|  | auto result = Parse("base.0", ExprLanguage::kRust); | 
|  | ASSERT_TRUE(result); | 
|  |  | 
|  | const MemberAccessExprNode* access = result->AsMemberAccess(); | 
|  | ASSERT_TRUE(access); | 
|  | EXPECT_EQ(ExprTokenType::kDot, access->accessor().type()); | 
|  | EXPECT_EQ(".", access->accessor().value()); | 
|  |  | 
|  | // Left side is the "base" identifier. | 
|  | const IdentifierExprNode* base = access->left()->AsIdentifier(); | 
|  | ASSERT_TRUE(base); | 
|  | EXPECT_EQ("base", base->ident().GetFullName()); | 
|  |  | 
|  | // Member name. | 
|  | EXPECT_EQ("0", access->member().GetFullName()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, DotNumberNoHex) { | 
|  | auto result = Parse("base.0xA", ExprLanguage::kRust); | 
|  | ASSERT_FALSE(result); | 
|  |  | 
|  | EXPECT_EQ("Expected identifier for right-hand-side of \".\".", parser().err().msg()); | 
|  | EXPECT_EQ(4u, parser().error_token().byte_offset()); | 
|  | EXPECT_EQ(".", parser().error_token().value()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, DotNumberNoRust) { | 
|  | auto result = Parse("base.0"); | 
|  | ASSERT_FALSE(result); | 
|  |  | 
|  | // This is parsed as an identifier followed by a floating-point number. | 
|  | EXPECT_EQ("Unexpected input, did you forget an operator?", parser().err().msg()); | 
|  | EXPECT_EQ(4u, parser().error_token().byte_offset()); | 
|  | EXPECT_EQ(".0", parser().error_token().value()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, AccessorAtEnd) { | 
|  | auto result = Parse("base. "); | 
|  | ASSERT_FALSE(result); | 
|  |  | 
|  | EXPECT_EQ("Expected identifier for right-hand-side of \".\".", parser().err().msg()); | 
|  |  | 
|  | EXPECT_EQ(4u, parser().error_token().byte_offset()); | 
|  | EXPECT_EQ(".", parser().error_token().value()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, BadAccessorMemberName) { | 
|  | auto result = Parse("base->23"); | 
|  | ASSERT_FALSE(result); | 
|  |  | 
|  | EXPECT_EQ("Expected identifier for right-hand-side of \"->\".", parser().err().msg()); | 
|  |  | 
|  | // This error reports the "->" as the location, one could also imagine reporting the right-side | 
|  | // token (if any) instead. | 
|  | EXPECT_EQ(4u, parser().error_token().byte_offset()); | 
|  | EXPECT_EQ("->", parser().error_token().value()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, Arrow) { | 
|  | auto result = Parse("base->member"); | 
|  | ASSERT_TRUE(result); | 
|  |  | 
|  | const MemberAccessExprNode* access = result->AsMemberAccess(); | 
|  | ASSERT_TRUE(access); | 
|  | EXPECT_EQ(ExprTokenType::kArrow, access->accessor().type()); | 
|  | EXPECT_EQ("->", access->accessor().value()); | 
|  |  | 
|  | // Left side is the "base" identifier. | 
|  | const IdentifierExprNode* base = access->left()->AsIdentifier(); | 
|  | ASSERT_TRUE(base); | 
|  | EXPECT_EQ("base", base->ident().GetFullName()); | 
|  |  | 
|  | // Member name. | 
|  | EXPECT_EQ("member", access->member().GetFullName()); | 
|  |  | 
|  | // Arrow with no name. | 
|  | result = Parse("base->"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected identifier for right-hand-side of \"->\".", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, NestedDotArrow) { | 
|  | // These should be left-associative so do the "." first, then the "->". When evaluating the tree, | 
|  | // one first evaluates the left side of the accessor, then the right, which is why it looks | 
|  | // backwards in the dump. | 
|  | EXPECT_EQ( | 
|  | "ACCESSOR(->)\n" | 
|  | " ACCESSOR(.)\n" | 
|  | "  IDENTIFIER(\"foo\")\n" | 
|  | "  bar\n" | 
|  | " baz\n", | 
|  | GetParseString("foo.bar->baz")); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, UnexpectedInput) { | 
|  | auto result = Parse("foo 5"); | 
|  | ASSERT_FALSE(result); | 
|  |  | 
|  | EXPECT_EQ("Unexpected input, did you forget an operator?", parser().err().msg()); | 
|  | EXPECT_EQ(4u, parser().error_token().byte_offset()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, ArrayAccess) { | 
|  | EXPECT_EQ( | 
|  | "ARRAY_ACCESS\n" | 
|  | " ARRAY_ACCESS\n" | 
|  | "  ACCESSOR(->)\n" | 
|  | "   ACCESSOR(.)\n" | 
|  | "    IDENTIFIER(\"foo\")\n" | 
|  | "    bar\n" | 
|  | "   baz\n" | 
|  | "  LITERAL(34)\n" | 
|  | " IDENTIFIER(\"bar\")\n", | 
|  | GetParseString("foo.bar->baz[34][bar]")); | 
|  |  | 
|  | // Empty array access is an error. | 
|  | auto result = Parse("foo[]"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Unexpected token ']'.", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, DereferenceAndAddress) { | 
|  | EXPECT_EQ( | 
|  | "DEREFERENCE\n" | 
|  | " IDENTIFIER(\"foo\")\n", | 
|  | GetParseString("*foo")); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "ADDRESS_OF\n" | 
|  | " IDENTIFIER(\"foo\")\n", | 
|  | GetParseString("&foo")); | 
|  |  | 
|  | // "*" and "&" should be right-associative with respect to each other but lower precedence than -> | 
|  | // and []. | 
|  | EXPECT_EQ( | 
|  | "ADDRESS_OF\n" | 
|  | " DEREFERENCE\n" | 
|  | "  ADDRESS_OF\n" | 
|  | "   ARRAY_ACCESS\n" | 
|  | "    ACCESSOR(->)\n" | 
|  | "     IDENTIFIER(\"foo\")\n" | 
|  | "     bar\n" | 
|  | "    LITERAL(1)\n", | 
|  | GetParseString("&*&foo->bar[1]")); | 
|  |  | 
|  | // "*" by itself is an error. | 
|  | auto result = Parse("*"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected expression instead of end of input.", parser().err().msg()); | 
|  | EXPECT_EQ(0u, parser().error_token().byte_offset()); | 
|  | } | 
|  |  | 
|  | // () should override the default precedence of other operators. | 
|  | TEST_F(ExprParserTest, Parens) { | 
|  | EXPECT_EQ( | 
|  | "ADDRESS_OF\n" | 
|  | " ARRAY_ACCESS\n" | 
|  | "  DEREFERENCE\n" | 
|  | "   ACCESSOR(->)\n" | 
|  | "    ADDRESS_OF\n" | 
|  | "     IDENTIFIER(\"foo\")\n" | 
|  | "    bar\n" | 
|  | "  LITERAL(1)\n", | 
|  | GetParseString("(&(*(&foo)->bar)[1])")); | 
|  | } | 
|  |  | 
|  | // This test covers that the extent of number tokens are identified properly. Actually converting | 
|  | // the strings to integers should be covered by the number parser tests. | 
|  | TEST_F(ExprParserTest, IntegerLiterals) { | 
|  | EXPECT_EQ("LITERAL(5)\n", GetParseString("5")); | 
|  | EXPECT_EQ("LITERAL(5ull)\n", GetParseString(" 5ull")); | 
|  | EXPECT_EQ("LITERAL(0xAbC)\n", GetParseString("0xAbC")); | 
|  | EXPECT_EQ("LITERAL(0o551)\n", GetParseString("  0o551 ")); | 
|  | EXPECT_EQ("LITERAL(0123)\n", GetParseString("0123")); | 
|  | } | 
|  |  | 
|  | // Similar to IntegerLiterals tests, this just covers basic integration. | 
|  | TEST_F(ExprParserTest, StringLiterals) { | 
|  | EXPECT_EQ("LITERAL(foo)\n", GetParseString("\"foo\"")); | 
|  | EXPECT_EQ("LITERAL(foo\"bar)\n", GetParseString("R\"(foo\"bar)\"")); | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(+)\n" | 
|  | " LITERAL(hello)\n" | 
|  | " LITERAL(world)\n", | 
|  | GetParseString("  \"hello\" + \"world\"")); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, UnaryMath) { | 
|  | EXPECT_EQ( | 
|  | "UNARY(-)\n" | 
|  | " LITERAL(5)\n", | 
|  | GetParseString("-5")); | 
|  | EXPECT_EQ( | 
|  | "UNARY(-)\n" | 
|  | " LITERAL(5)\n", | 
|  | GetParseString(" - 5 ")); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "UNARY(!)\n" | 
|  | " IDENTIFIER(\"foo\")\n", | 
|  | GetParseString("!foo ")); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "UNARY(-)\n" | 
|  | " DEREFERENCE\n" | 
|  | "  IDENTIFIER(\"foo\")\n", | 
|  | GetParseString("-*foo")); | 
|  |  | 
|  | // "-" by itself is an error. | 
|  | auto result = Parse("-"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected expression for '-'.", parser().err().msg()); | 
|  | EXPECT_EQ(0u, parser().error_token().byte_offset()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, AndOr) { | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(&&)\n" | 
|  | " BINARY_OP(&)\n" | 
|  | "  ADDRESS_OF\n" | 
|  | "   IDENTIFIER(\"a\")\n" | 
|  | "  ADDRESS_OF\n" | 
|  | "   IDENTIFIER(\"b\")\n" | 
|  | " BINARY_OP(&)\n" | 
|  | "  IDENTIFIER(\"c\")\n" | 
|  | "  IDENTIFIER(\"d\")\n", | 
|  | GetParseString("& a & & b && c & d")); | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(||)\n" | 
|  | " BINARY_OP(|)\n" | 
|  | "  IDENTIFIER(\"a\")\n" | 
|  | "  IDENTIFIER(\"b\")\n" | 
|  | " BINARY_OP(|)\n" | 
|  | "  IDENTIFIER(\"c\")\n" | 
|  | "  IDENTIFIER(\"d\")\n", | 
|  | GetParseString("a | b || c | d")); | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(||)\n" | 
|  | " BINARY_OP(&&)\n" | 
|  | "  IDENTIFIER(\"a\")\n" | 
|  | "  IDENTIFIER(\"b\")\n" | 
|  | " BINARY_OP(&&)\n" | 
|  | "  IDENTIFIER(\"c\")\n" | 
|  | "  IDENTIFIER(\"d\")\n", | 
|  | GetParseString("a && b || c && d")); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, Identifiers) { | 
|  | EXPECT_EQ("IDENTIFIER(\"foo\")\n", GetParseString("foo")); | 
|  | EXPECT_EQ("IDENTIFIER(::\"foo\")\n", GetParseString("::foo")); | 
|  | EXPECT_EQ("IDENTIFIER(::\"foo\"; ::\"bar\")\n", GetParseString("::foo :: bar")); | 
|  |  | 
|  | auto result = Parse("::"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected name after '::'.", parser().err().msg()); | 
|  |  | 
|  | result = Parse(":: :: name"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Could not identify thing to the left of '::' as a type or namespace.", | 
|  | parser().err().msg()); | 
|  |  | 
|  | result = Parse("foo bar"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Unexpected identifier, did you forget an operator?", parser().err().msg()); | 
|  |  | 
|  | // It's valid to have identifiers with colons in them to access class members (this is how you | 
|  | // provide an explicit base class). | 
|  | /* TODO(brettw) convert an accessor to use an Identifier for the name so this | 
|  | test works. | 
|  | EXPECT_EQ( | 
|  | "ACCESSOR(->)\n" | 
|  | "  IDENTIFIER(\"foo\")\n", | 
|  | GetParseString("foo->Baz::bar")); | 
|  | */ | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, SpecialIdentifiers) { | 
|  | // Most special identifier parsing is tested in the dedicated special identifier parser tests. | 
|  | // This checks that the integration is correct. | 
|  | EXPECT_EQ("IDENTIFIER(\"ns\"; ::\"$anon\"; ::\"Foo\")\n", GetParseString("ns::$anon::Foo")); | 
|  | EXPECT_EQ("IDENTIFIER(\"$main\")\n", GetParseString("$main")); | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(+)\n" | 
|  | " BINARY_OP(+)\n" | 
|  | "  LITERAL(2)\n" | 
|  | "  IDENTIFIER(\"$plt(foo_bar)\")\n" | 
|  | " LITERAL(2)\n", | 
|  | GetParseString("2+$plt(foo_bar)+2")); | 
|  |  | 
|  | // For escaping, the identifier won't contain the escaping characters, these are for parsing and | 
|  | // output only. | 
|  | EXPECT_EQ("IDENTIFIER(\"{{impl}}\"; ::\"some(crazyness)$here)\")\n", | 
|  | GetParseString("$({{impl}})::$(some(crazyness)$here\\))")); | 
|  | } | 
|  |  | 
|  | // Tests << and >> operators. They are challenging because this can also appear in template | 
|  | // expressions and are parsed as something else. | 
|  | TEST_F(ExprParserTest, Shift) { | 
|  | // This is parsed as (2 << 2) < (static_cast<Template<int>>(2) >> 2) > 2" with the < and > then | 
|  | // parsed left-to-right. | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(>)\n" | 
|  | " BINARY_OP(<)\n" | 
|  | "  BINARY_OP(<<)\n" | 
|  | "   LITERAL(2)\n" | 
|  | "   LITERAL(2)\n" | 
|  | "  BINARY_OP(>>)\n" | 
|  | "   CAST(static_cast)\n" | 
|  | "    TYPE(Template<int>)\n" | 
|  | "    LITERAL(2)\n" | 
|  | "   LITERAL(2)\n" | 
|  | " LITERAL(2)\n", | 
|  | GetParseString("2<<2<static_cast<Template<int>>(2)>>2>2", &TestLookupName)); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, FunctionCall) { | 
|  | // Simple call with no args. | 
|  | EXPECT_EQ( | 
|  | "FUNCTIONCALL\n" | 
|  | " IDENTIFIER(\"Call\")\n", | 
|  | GetParseString("Call()")); | 
|  |  | 
|  | // Scoped call with namespaces and templates. | 
|  | EXPECT_EQ( | 
|  | "FUNCTIONCALL\n" | 
|  | " IDENTIFIER(\"ns\"; ::\"Foo\",<\"int\">; ::\"GetCurrent\")\n", | 
|  | GetParseString("ns::Foo<int>::GetCurrent()")); | 
|  |  | 
|  | // One arg. | 
|  | EXPECT_EQ( | 
|  | "FUNCTIONCALL\n" | 
|  | " IDENTIFIER(\"Call\")\n" | 
|  | " LITERAL(42)\n", | 
|  | GetParseString("Call(42)")); | 
|  |  | 
|  | // Several complex args and a nested call. | 
|  | EXPECT_EQ( | 
|  | "FUNCTIONCALL\n" | 
|  | " IDENTIFIER(\"Call\")\n" | 
|  | " IDENTIFIER(\"a\")\n" | 
|  | " BINARY_OP(=)\n" | 
|  | "  IDENTIFIER(\"b\")\n" | 
|  | "  LITERAL(5)\n" | 
|  | " FUNCTIONCALL\n" | 
|  | "  IDENTIFIER(\"OtherCall\")\n", | 
|  | GetParseString("Call(a, b = 5, OtherCall())")); | 
|  |  | 
|  | // Function call on an object with ".". | 
|  | EXPECT_EQ( | 
|  | "FUNCTIONCALL\n" | 
|  | " ACCESSOR(.)\n" | 
|  | "  ACCESSOR(.)\n" | 
|  | "   IDENTIFIER(\"foo\")\n" | 
|  | "   bar\n" | 
|  | "  Baz\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("foo.bar.Baz(a)")); | 
|  |  | 
|  | // Function call on nested stuff with "->". | 
|  | EXPECT_EQ( | 
|  | "FUNCTIONCALL\n" | 
|  | " ACCESSOR(->)\n" | 
|  | "  BINARY_OP(+)\n" | 
|  | "   IDENTIFIER(\"a\")\n" | 
|  | "   IDENTIFIER(\"b\")\n" | 
|  | "  Call\n", | 
|  | GetParseString("(a + b)-> Call()")); | 
|  |  | 
|  | // Unmatched "(" error. | 
|  | auto result = Parse("Call(a, "); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected expression instead of end of input.", parser().err().msg()); | 
|  |  | 
|  | // Arguments not separated by commas. | 
|  | result = Parse("Call(a b)"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Unexpected identifier, did you forget an operator?", parser().err().msg()); | 
|  |  | 
|  | // Empty parameter | 
|  | result = Parse("Call(a, , b)"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Unexpected token ','.", parser().err().msg()); | 
|  |  | 
|  | // Trailing comma. | 
|  | result = Parse("Call(a,)"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Unexpected token ')'.", parser().err().msg()); | 
|  |  | 
|  | // Thing on the left is not an identifier. | 
|  | result = Parse("5()"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Unexpected '('.", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, Templates) { | 
|  | EXPECT_EQ(R"(IDENTIFIER("foo",<>))" | 
|  | "\n", | 
|  | GetParseString("foo<>")); | 
|  | EXPECT_EQ(R"(IDENTIFIER("foo",<"Foo">))" | 
|  | "\n", | 
|  | GetParseString("foo<Foo>")); | 
|  | EXPECT_EQ(R"(IDENTIFIER("foo",<"Foo", "5">))" | 
|  | "\n", | 
|  | GetParseString("foo< Foo,5 >")); | 
|  | EXPECT_EQ( | 
|  | R"(IDENTIFIER("std"; ::"map",<"Key", "Value", "std::less<Key>", "std::allocator<std::pair<Key const, Value>>">; ::"insert"))" | 
|  | "\n", | 
|  | GetParseString("std::map<Key, Value, std::less<Key>, " | 
|  | "std::allocator<std::pair<Key const, Value>>>::insert")); | 
|  |  | 
|  | // Unmatched "<" error. This is generated by the parser because it's expecting the match for the | 
|  | // outer level. | 
|  | auto result = Parse("std::map<Key, Value"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected '>' to match. Hit the end of input instead.", parser().err().msg()); | 
|  |  | 
|  | // This unmatched token is generated by the template type skipper which is why the error message | 
|  | // is slightly different (both are OK). | 
|  | result = Parse("std::map<key[, value"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Unmatched '['.", parser().err().msg()); | 
|  |  | 
|  | // Duplicate template spec. This will actually be parsed as "less than" and "greater than" and it | 
|  | // will look like "greater than" is missing the right-hand-side. | 
|  | result = Parse("Foo<Bar><Baz>"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected expression after '>'.", parser().err().msg()); | 
|  |  | 
|  | // Empty value. | 
|  | result = Parse("Foo<1,,2>"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected template parameter.", parser().err().msg()); | 
|  |  | 
|  | // Trailing comma. | 
|  | result = Parse("Foo<Bar,>"); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected template parameter.", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, BinaryOp) { | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(=)\n" | 
|  | " IDENTIFIER(\"a\")\n" | 
|  | " BINARY_OP(+)\n" | 
|  | "  LITERAL(23)\n" | 
|  | "  BINARY_OP(*)\n" | 
|  | "   IDENTIFIER(\"j\")\n" | 
|  | "   BINARY_OP(+)\n" | 
|  | "    IDENTIFIER(\"a\")\n" | 
|  | "    LITERAL(1)\n", | 
|  | GetParseString("a = 23 + j * (a + 1)")); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, Comparison) { | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(!=)\n" | 
|  | " IDENTIFIER(\"a\",<\"int\">; ::\"foo\")\n" | 
|  | " LITERAL(0)\n", | 
|  | GetParseString("a<int>::foo != 0")); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(&&)\n" | 
|  | " BINARY_OP(<)\n" | 
|  | "  LITERAL(1)\n" | 
|  | "  LITERAL(2)\n" | 
|  | " BINARY_OP(>)\n" | 
|  | "  IDENTIFIER(\"a\")\n" | 
|  | "  LITERAL(4)\n", | 
|  | GetParseString("1 < 2 && a > 4")); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(||)\n" | 
|  | " BINARY_OP(<=)\n" | 
|  | "  LITERAL(1)\n" | 
|  | "  LITERAL(2.3)\n" | 
|  | " BINARY_OP(>=)\n" | 
|  | "  IDENTIFIER(\"a\")\n" | 
|  | "  LITERAL(4e9f)\n", | 
|  | GetParseString("1 <= 2.3 || a >= 4e9f")); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(<=>)\n" | 
|  | " IDENTIFIER(\"a\")\n" | 
|  | " LITERAL(4)\n", | 
|  | GetParseString("a <=> 4")); | 
|  | } | 
|  |  | 
|  | // Tests parsing identifier names that require lookups from the symbol system. See TestLookupName | 
|  | // above for the mocked symbol rules. | 
|  | TEST_F(ExprParserTest, NamesWithSymbolLookup) { | 
|  | // Bare namespace is an error. | 
|  | auto result = Parse("Namespace", &TestLookupName); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected expression after namespace name.", parser().err().msg()); | 
|  |  | 
|  | // Bare template is an error. | 
|  | result = Parse("Template", &TestLookupName); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected template args after template name.", parser().err().msg()); | 
|  |  | 
|  | // Nothing after "::" | 
|  | result = Parse("Namespace::", &TestLookupName); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected name after '::'.", parser().err().msg()); | 
|  |  | 
|  | // Can't put a template on a type that's not a template. | 
|  | result = Parse("Type<int>", &TestLookupName); | 
|  | ASSERT_FALSE(result); | 
|  | // This error message might change with future type support because it might look like a | 
|  | // comparison between a type and an int. | 
|  | EXPECT_EQ("Template parameters not valid on this object type.", parser().err().msg()); | 
|  |  | 
|  | // Can't put a template on a namespace. | 
|  | result = Parse("Namespace<int>", &TestLookupName); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Template parameters not valid on this object type.", parser().err().msg()); | 
|  |  | 
|  | // Good type name. | 
|  | EXPECT_EQ( | 
|  | "FUNCTIONCALL\n" | 
|  | " IDENTIFIER(\"Namespace\"; ::\"Template\",<\"int\">; ::\"fn\")\n", | 
|  | GetParseString("Namespace::Template<int>::fn()", &TestLookupName)); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, TrueFalse) { | 
|  | EXPECT_EQ("LITERAL(true)\n", GetParseString("true")); | 
|  | EXPECT_EQ("LITERAL(false)\n", GetParseString("false")); | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(&&)\n" | 
|  | " LITERAL(false)\n" | 
|  | " LITERAL(true)\n", | 
|  | GetParseString("false&&true")); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, Types) { | 
|  | EXPECT_EQ("TYPE(Type)\n", GetParseString("Type", &TestLookupName)); | 
|  | EXPECT_EQ("IDENTIFIER(\"NotType\")\n", GetParseString("NotType", &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ("TYPE(const Type)\n", GetParseString("const Type", &TestLookupName)); | 
|  | EXPECT_EQ("TYPE(const Type)\n", GetParseString("Type const", &TestLookupName)); | 
|  |  | 
|  | // It would be better it this printed as "const volatile Type" but our heuristic for moving | 
|  | // modifiers to the beginning isn't good enough. | 
|  | EXPECT_EQ("TYPE(volatile Type const)\n", GetParseString("const volatile Type", &TestLookupName)); | 
|  |  | 
|  | // Duplicate const qualifications. | 
|  | auto result = Parse("const Type const", &TestLookupName); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Duplicate 'const' type qualification.", parser().err().msg()); | 
|  | result = Parse("const const Type", &TestLookupName); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Duplicate 'const' type qualification.", parser().err().msg()); | 
|  |  | 
|  | EXPECT_EQ("TYPE(Type*)\n", GetParseString("Type*", &TestLookupName)); | 
|  | EXPECT_EQ("TYPE(Type**)\n", GetParseString("Type * *", &TestLookupName)); | 
|  | EXPECT_EQ("TYPE(Type&&)\n", GetParseString("Type &&", &TestLookupName)); | 
|  | EXPECT_EQ("TYPE(Type&**)\n", GetParseString("Type&**", &TestLookupName)); | 
|  | EXPECT_EQ("TYPE(volatile Type* restrict* const)\n", | 
|  | GetParseString("Type volatile *restrict* const", &TestLookupName)); | 
|  |  | 
|  | // "const" should force us into type mode. | 
|  | result = Parse("const NonType", &TestLookupName); | 
|  | ASSERT_FALSE(result); | 
|  | EXPECT_EQ("Expected a type name but could not find a type named 'NonType'.", | 
|  | parser().err().msg()); | 
|  |  | 
|  | // Try sizeof() with both a type and a non-type. | 
|  | EXPECT_EQ( | 
|  | "SIZEOF\n" | 
|  | " TYPE(Type*)\n", | 
|  | GetParseString("sizeof(Type*)", &TestLookupName)); | 
|  | EXPECT_EQ( | 
|  | "SIZEOF\n" | 
|  | " IDENTIFIER(\"foo\")\n", | 
|  | GetParseString("sizeof(foo)", &TestLookupName)); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, C_Cast) { | 
|  | EXPECT_EQ( | 
|  | "CAST(C)\n" | 
|  | " TYPE(Type)\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("(Type)(a)", &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(&&)\n" | 
|  | " CAST(C)\n" | 
|  | "  TYPE(Type*)\n" | 
|  | "  IDENTIFIER(\"a\")\n" | 
|  | " IDENTIFIER(\"b\")\n", | 
|  | GetParseString("(Type*)a && b", &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CAST(C)\n" | 
|  | " TYPE(Type)\n" | 
|  | " ACCESSOR(->)\n" | 
|  | "  ARRAY_ACCESS\n" | 
|  | "   IDENTIFIER(\"a\")\n" | 
|  | "   LITERAL(0)\n" | 
|  | "  b\n", | 
|  | GetParseString("(Type)a[0]->b", &TestLookupName)); | 
|  |  | 
|  | // Looks like a cast but it's not a type. | 
|  | auto result = Parse("(NotType)a", &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Unexpected input, did you forget an operator?", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, RustCast) { | 
|  | EXPECT_EQ( | 
|  | "CAST(Rust)\n" | 
|  | " TYPE(Type)\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("a as Type", ExprLanguage::kRust, &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(&&)\n" | 
|  | " CAST(Rust)\n" | 
|  | "  TYPE(Type*)\n" | 
|  | "  IDENTIFIER(\"a\")\n" | 
|  | " IDENTIFIER(\"b\")\n", | 
|  | GetParseString("a as *Type && b", ExprLanguage::kRust, &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "BINARY_OP(&&)\n" | 
|  | " CAST(Rust)\n" | 
|  | "  TYPE(Type*******)\n" | 
|  | "  IDENTIFIER(\"a\")\n" | 
|  | " IDENTIFIER(\"b\")\n", | 
|  | GetParseString("a as &mut &mut && mut &*&Type && b", ExprLanguage::kRust, &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CAST(Rust)\n" | 
|  | " TYPE(Type)\n" | 
|  | " ACCESSOR(->)\n" | 
|  | "  ARRAY_ACCESS\n" | 
|  | "   IDENTIFIER(\"a\")\n" | 
|  | "   LITERAL(0)\n" | 
|  | "  b\n", | 
|  | GetParseString("a[0]->b as Type", ExprLanguage::kRust, &TestLookupName)); | 
|  |  | 
|  | // We can't actually cast to tuple, so these wouldn't evaluate, but we want to test the type | 
|  | // parsing anyway. | 
|  | EXPECT_EQ( | 
|  | "CAST(Rust)\n" | 
|  | " TYPE((Type, Type, Type))\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("a as (Type, Type, Type)", ExprLanguage::kRust, &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CAST(Rust)\n" | 
|  | " TYPE(())\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("a as ()", ExprLanguage::kRust, &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CAST(Rust)\n" | 
|  | " TYPE((Type,))\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("a as (Type,)", ExprLanguage::kRust, &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CAST(Rust)\n" | 
|  | " TYPE(Type)\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("a as (Type)", ExprLanguage::kRust, &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CAST(Rust)\n" | 
|  | " TYPE(Type[23])\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("a as [Type; 23]", ExprLanguage::kRust, &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CAST(Rust)\n" | 
|  | " TYPE(Type[])\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("a as [Type]", ExprLanguage::kRust, &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CAST(Rust)\n" | 
|  | " TYPE(Type[23]*)\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("a as &[Type; 23]", ExprLanguage::kRust, &TestLookupName)); | 
|  |  | 
|  | // Looks like a cast but it's not a type. | 
|  | auto result = Parse("a as NotType", ExprLanguage::kRust, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected a type name but could not find a type named 'NotType'.", | 
|  | parser().err().msg()); | 
|  |  | 
|  | // Rust cast but we're not in rust | 
|  | result = Parse("a as Type", ExprLanguage::kC, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Unexpected identifier, did you forget an operator?", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, BadRustArrays) { | 
|  | auto result = Parse("a as [NotType]", ExprLanguage::kRust, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected a type name but could not find a type named 'NotType'.", | 
|  | parser().err().msg()); | 
|  |  | 
|  | result = Parse("a as [NotType; 23]", ExprLanguage::kRust, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected a type name but could not find a type named 'NotType'.", | 
|  | parser().err().msg()); | 
|  |  | 
|  | result = Parse("a as [Type; 23", ExprLanguage::kRust, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected ']' before end of input.", parser().err().msg()); | 
|  |  | 
|  | result = Parse("a as [Type;", ExprLanguage::kRust, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected element count before end of input.", parser().err().msg()); | 
|  |  | 
|  | result = Parse("a as [Type", ExprLanguage::kRust, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected ']' before end of input.", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, BadRustTuples) { | 
|  | auto result = Parse("a as (Type, NotType)", ExprLanguage::kRust, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected a type name but could not find a type named 'NotType'.", | 
|  | parser().err().msg()); | 
|  |  | 
|  | // Missing comma | 
|  | result = Parse("a as (Type Type)", ExprLanguage::kRust, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("This looks like a declaration which is not supported.", parser().err().msg()); | 
|  |  | 
|  | // Missing end | 
|  | result = Parse("a as (Type, Type", ExprLanguage::kRust, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected ')' or ',' before end of input.", parser().err().msg()); | 
|  |  | 
|  | // Missing end with comma | 
|  | result = Parse("a as (Type,", ExprLanguage::kRust, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected ')' or ',' before end of input.", parser().err().msg()); | 
|  |  | 
|  | // Missing end, no groupings. | 
|  | result = Parse("a as (Type", ExprLanguage::kRust, &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected ')' or ',' before end of input.", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, CppCast) { | 
|  | EXPECT_EQ( | 
|  | "CAST(reinterpret_cast)\n" | 
|  | " TYPE(Type*)\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("reinterpret_cast<Type*>(a)", &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CAST(static_cast)\n" | 
|  | " TYPE(Type*)\n" | 
|  | " IDENTIFIER(\"a\")\n", | 
|  | GetParseString("static_cast<Type*>(a)", &TestLookupName)); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CAST(reinterpret_cast)\n" | 
|  | " TYPE(const Type&&)\n" | 
|  | " BINARY_OP(&&)\n" | 
|  | "  IDENTIFIER(\"x\")\n" | 
|  | "  IDENTIFIER(\"y\")\n", | 
|  | GetParseString("reinterpret_cast<  const Type&& >( x && y)", &TestLookupName)); | 
|  |  | 
|  | auto result = Parse("reinterpret_cast<", &TestLookupName); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected type name before end of input.", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, ParseIdentifier) { | 
|  | Err err; | 
|  |  | 
|  | // Empty input. | 
|  | ParsedIdentifier empty_ident; | 
|  | err = ExprParser::ParseIdentifier("", &empty_ident); | 
|  | EXPECT_TRUE(err.has_error()); | 
|  | EXPECT_EQ("Expected expression instead of end of input.", err.msg()); | 
|  | EXPECT_EQ("", empty_ident.GetDebugName()); | 
|  |  | 
|  | // Normal word. | 
|  | ParsedIdentifier word_ident; | 
|  | err = ExprParser::ParseIdentifier("foo", &word_ident); | 
|  | EXPECT_FALSE(err.has_error()) << err.msg(); | 
|  | EXPECT_EQ("\"foo\"", word_ident.GetDebugName()); | 
|  |  | 
|  | // Destructor. | 
|  | ParsedIdentifier destr_ident; | 
|  | err = ExprParser::ParseIdentifier("Foo::~Foo", &destr_ident); | 
|  | EXPECT_FALSE(err.has_error()) << err.msg(); | 
|  | EXPECT_EQ(R"("Foo"; ::"~Foo")", destr_ident.GetDebugName()); | 
|  |  | 
|  | // Complicated identifier (copied from STL). | 
|  | ParsedIdentifier complex_ident; | 
|  | err = ExprParser::ParseIdentifier( | 
|  | "std::unordered_map<" | 
|  | "std::__2::basic_string<char>, " | 
|  | "unsigned long, " | 
|  | "std::__2::hash<std::__2::basic_string<char> >, " | 
|  | "std::__2::equal_to<std::__2::basic_string<char> >, " | 
|  | "std::__2::allocator<std::__2::pair<const std::__2::basic_string<char>, " | 
|  | "unsigned long> >>", | 
|  | &complex_ident); | 
|  | EXPECT_FALSE(err.has_error()); | 
|  | EXPECT_EQ( | 
|  | "\"std\"; " | 
|  | "::" | 
|  | "\"unordered_map\"," | 
|  | "<\"std::__2::basic_string<char>\", " | 
|  | "\"unsigned long\", " | 
|  | "\"std::__2::hash<std::__2::basic_string<char>>\", " | 
|  | "\"std::__2::equal_to<std::__2::basic_string<char>>\", " | 
|  | "\"std::__2::allocator<std::__2::pair<" | 
|  | "const std::__2::basic_string<char>, unsigned long>>\">", | 
|  | complex_ident.GetDebugName()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, FromStringError) { | 
|  | // Error from input. | 
|  | Identifier bad_ident; | 
|  | Err err = ExprParser::ParseIdentifier("Foo<Bar", &bad_ident); | 
|  | EXPECT_TRUE(err.has_error()); | 
|  | EXPECT_EQ("Expected '>' to match. Hit the end of input instead.", err.msg()); | 
|  | EXPECT_EQ("", bad_ident.GetDebugName()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, If) { | 
|  | EXPECT_EQ( | 
|  | "CONDITION\n" | 
|  | " IF\n" | 
|  | "  BINARY_OP(==)\n" | 
|  | "   IDENTIFIER(\"i\")\n" | 
|  | "   LITERAL(1)\n" | 
|  | " THEN\n" | 
|  | "  BINARY_OP(=)\n" | 
|  | "   IDENTIFIER(\"b\")\n" | 
|  | "   LITERAL(1)\n", | 
|  | GetParseString("if (i == 1) b = 1;")); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CONDITION\n" | 
|  | " IF\n" | 
|  | "  IDENTIFIER(\"i\")\n", | 
|  | GetParseString("if (i) ;")); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CONDITION\n" | 
|  | " IF\n" | 
|  | "  IDENTIFIER(\"i\")\n" | 
|  | " THEN\n" | 
|  | "  BLOCK\n", | 
|  | GetParseString("if (i) {} else ;")); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CONDITION\n" | 
|  | " IF\n" | 
|  | "  LITERAL(1)\n" | 
|  | " THEN\n" | 
|  | "  IDENTIFIER(\"foo\")\n" | 
|  | " ELSEIF\n" | 
|  | "  LITERAL(0)\n" | 
|  | " THEN\n" | 
|  | "  IDENTIFIER(\"bar\")\n", | 
|  | GetParseString("if (1) foo; else if (0) bar;")); | 
|  |  | 
|  | // Rust with no parens is OK. | 
|  | EXPECT_EQ( | 
|  | "CONDITION\n" | 
|  | " IF\n" | 
|  | "  LITERAL(1)\n" | 
|  | " THEN\n" | 
|  | "  BLOCK\n" | 
|  | "   IDENTIFIER(\"foo\")\n" | 
|  | " ELSEIF\n" | 
|  | "  LITERAL(0)\n" | 
|  | " THEN\n" | 
|  | "  BLOCK\n" | 
|  | "   IDENTIFIER(\"bar\")\n", | 
|  | GetParseString("if 1 {foo} else if 0 {bar}", ExprLanguage::kRust)); | 
|  |  | 
|  | // C with no parens is a failure. | 
|  | auto result = Parse("if 1 foo; else if 0 bar;", ExprLanguage::kC); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected '(' for if.", parser().err().msg()); | 
|  |  | 
|  | // Rust requires {} for the blocks. | 
|  | result = Parse("if 1 foo;", ExprLanguage::kRust); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected '{'.", parser().err().msg()); | 
|  |  | 
|  | // Missing semicolons. | 
|  | result = Parse("if (1) foo else bar;", ExprLanguage::kC); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Unexpected token 'else'.", parser().err().msg()); | 
|  | result = Parse("if (1) foo; else bar", ExprLanguage::kC); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected ';'. Hit the end of input instead.", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExprParserTest, CTernaryIf) { | 
|  | EXPECT_EQ( | 
|  | "CONDITION\n" | 
|  | " IF\n" | 
|  | "  LITERAL(1)\n" | 
|  | " THEN\n" | 
|  | "  LITERAL(2)\n" | 
|  | " ELSE\n" | 
|  | "  LITERAL(3)\n", | 
|  | GetParseString("1 ? 2 : 3")); | 
|  |  | 
|  | EXPECT_EQ( | 
|  | "CONDITION\n" | 
|  | " IF\n" | 
|  | "  BINARY_OP(==)\n" | 
|  | "   LITERAL(2)\n" | 
|  | "   BINARY_OP(-)\n" | 
|  | "    LITERAL(3)\n" | 
|  | "    LITERAL(1)\n" | 
|  | " THEN\n" | 
|  | "  CONDITION\n" | 
|  | "   IF\n" | 
|  | "    LITERAL(2)\n" | 
|  | "   THEN\n" | 
|  | "    LITERAL(1)\n" | 
|  | "   ELSE\n" | 
|  | "    IDENTIFIER(\"a\")\n" | 
|  | " ELSE\n" | 
|  | "  BINARY_OP(+)\n" | 
|  | "   LITERAL(3)\n" | 
|  | "   LITERAL(9)\n", | 
|  | GetParseString("2==3-1 ? 2?1:a : 3+9")); | 
|  |  | 
|  | // Missing colon. | 
|  | auto result = Parse(" 3 ? 1"); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Expected ':' for previous '?'. Hit the end of input instead.", parser().err().msg()); | 
|  |  | 
|  | // Missing condition | 
|  | result = Parse("? a : b"); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Unexpected token '?'.", parser().err().msg()); | 
|  |  | 
|  | // Not supported in Rust. | 
|  | result = Parse("a ? b : c", ExprLanguage::kRust); | 
|  | EXPECT_FALSE(result); | 
|  | EXPECT_EQ("Rust '?' operators are not supported.", parser().err().msg()); | 
|  | } | 
|  |  | 
|  | }  // namespace zxdb |