Files
carbon-lang/parser/parse_tree_test.cpp
T
Chandler CarruthandJon Meow 3512c2218f Merge parser library from the toolchain repository. (#214)
Only change is to update the path to the fuzzer build extension.

Original main commit message:

> Add an initial parser library. (#30)
>
> This library builds a parse tree, very similar to a concrete syntax
> tree. There are no semantics here, simply introducing the basic
> syntactic structure.
>
> The current focus has been on the APIs and the data structures used to
> represent the parse tree, and not on the actual code doing the
> parsing. The code doing the parsing tries to be reasonably efficient
> and reasonably easy to understand recursive descent parser. But there
> is likely much that can be done to improve this code path. A notable
> area where very little thought has been given yet are emitting good
> diagnostics and doing good recovery in the event of parse errors.
>
> Also, this code does not try to match the current under-discussion
> grammar closely. It is only partial and reflects discussions from some
> time ago. It should be updated incrementally to reflect the current
> expected grammar.
>
> The data structure used for the parse tree is unusual. The first
> constraint is that there is a precise one-to-one correspondence
> between the tokens produced by the lexer and the nodes in the parse
> tree. Every token results in exactly one node. In that way, the parse
> tree can be thought of as merely shaping the token stream into a tree.
>
> Each node is also represented with a fixed set of data that is densely
> packed. Combined with the exact relationship to tokens, this allows us
> to fully allocate the parse tree's storage, and to use a dense array
> rather than a pointer-based tree structure.
>
> The tree structure itself is implicitly defined by tracking the size
> of each subtree rooted at a particular node. See the code comments for
> more details (and I'm happy to add more comments where necessary). The
> goal is to minimize both the allocations (one), the working set size
> of the tree as a whole, and optimize common iteration patterns. The
> tree is stored in postorder. This allows depth-first postorder
> iteration as well as topological iteration by walking in reverse.
>
> Building the parse tree in postorder is a natural consequence of the
> grammar being LR rather than LL, which is a consequence of supporting
> infix operators.
>
> As with the Lexer, the parser supports an API for operating on the
> parse tree, as well as the ability to print the tree in both
> a human-readable and machine-readable format (YAML-based). It includes
> significant unit tests and a fuzz tester. The fuzzer's corpus will be
> in a follow-up commit.
>
> This is the largest chunk of code already written by several of us
> prior to open sourcing. (There are a few more pieces, but they are
> significantly smaller and less interesting.) If there are major things
> that folks would like to see happen here, it may make sense to move
> them into issues for tracking. I have tried to update the code to
> follow the style guidelines, but apologies if I missed anything, just
> let me know. We also have issues #19 and #29 to track things that
> already came up with the lexer.

Co-authored-by: Jon Meow <46229924+jonmeow@users.noreply.github.com>
2020-12-08 01:52:43 -08:00

535 lines
20 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "parser/parse_tree.h"
#include <forward_list>
#include "diagnostics/diagnostic_emitter.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "lexer/tokenized_buffer.h"
#include "lexer/tokenized_buffer_test_helpers.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/YAMLParser.h"
#include "parser/parse_node_kind.h"
#include "parser/parse_test_helpers.h"
namespace Carbon {
namespace {
using Carbon::Testing::IsKeyValueScalars;
using Carbon::Testing::MatchParseTreeNodes;
using ::testing::Eq;
using ::testing::Ne;
using ::testing::NotNull;
using ::testing::StrEq;
struct ParseTreeTest : ::testing::Test {
std::forward_list<SourceBuffer> source_storage;
std::forward_list<TokenizedBuffer> token_storage;
DiagnosticEmitter emitter = NullDiagnosticEmitter();
auto GetSourceBuffer(llvm::Twine t) -> SourceBuffer& {
source_storage.push_front(SourceBuffer::CreateFromText(t.str()));
return source_storage.front();
}
auto GetTokenizedBuffer(llvm::Twine t) -> TokenizedBuffer& {
token_storage.push_front(TokenizedBuffer::Lex(GetSourceBuffer(t), emitter));
return token_storage.front();
}
};
TEST_F(ParseTreeTest, Empty) {
TokenizedBuffer tokens = GetTokenizedBuffer("");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_FALSE(tree.HasErrors());
EXPECT_THAT(tree.Postorder().begin(), Eq(tree.Postorder().end()));
}
TEST_F(ParseTreeTest, EmptyDeclaration) {
TokenizedBuffer tokens = GetTokenizedBuffer(";");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_FALSE(tree.HasErrors());
auto it = tree.Postorder().begin();
auto end = tree.Postorder().end();
ASSERT_THAT(it, Ne(end));
ParseTree::Node n = *it++;
EXPECT_THAT(it, Eq(end));
// Directly test the main API so that we get easier to understand errors in
// simple cases than what the custom matcher will produce.
EXPECT_FALSE(tree.HasErrorInNode(n));
EXPECT_THAT(tree.GetNodeKind(n), Eq(ParseNodeKind::EmptyDeclaration()));
auto t = tree.GetNodeToken(n);
ASSERT_THAT(tokens.Tokens().begin(), Ne(tokens.Tokens().end()));
EXPECT_THAT(t, Eq(*tokens.Tokens().begin()));
EXPECT_THAT(tokens.GetTokenText(t), Eq(";"));
EXPECT_THAT(tree.Postorder(n).begin(), Eq(tree.Postorder().begin()));
EXPECT_THAT(tree.Postorder(n).end(), Eq(tree.Postorder().end()));
EXPECT_THAT(tree.Children(n).begin(), Eq(tree.Children(n).end()));
}
TEST_F(ParseTreeTest, BasicFunctionDeclaration) {
TokenizedBuffer tokens = GetTokenizedBuffer("fn F();");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_FALSE(tree.HasErrors());
EXPECT_THAT(
tree, MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.text = "fn",
.children = {
{ParseNodeKind::Identifier(), "F"},
{.kind = ParseNodeKind::ParameterList(),
.text = "(",
.children = {{ParseNodeKind::ParameterListEnd(), ")"}}},
{ParseNodeKind::DeclarationEnd(), ";"}}}}));
}
TEST_F(ParseTreeTest, NoDeclarationIntroducerOrSemi) {
TokenizedBuffer tokens = GetTokenizedBuffer("foo bar baz");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(tree.Postorder().begin(), Eq(tree.Postorder().end()));
}
TEST_F(ParseTreeTest, NoDeclarationIntroducerWithSemi) {
TokenizedBuffer tokens = GetTokenizedBuffer("foo;");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(tree,
MatchParseTreeNodes({{.kind = ParseNodeKind::EmptyDeclaration(),
.text = ";",
.has_error = true}}));
}
TEST_F(ParseTreeTest, JustFunctionIntroducerAndSemi) {
TokenizedBuffer tokens = GetTokenizedBuffer("fn;");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(tree, MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.has_error = true,
.children = {{ParseNodeKind::DeclarationEnd()}}}}));
}
TEST_F(ParseTreeTest, RepeatedFunctionIntroducerAndSemi) {
TokenizedBuffer tokens = GetTokenizedBuffer("fn fn;");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(tree, MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.has_error = true,
.children = {{ParseNodeKind::DeclarationEnd()}}}}));
}
TEST_F(ParseTreeTest, FunctionDeclarationWithNoSignatureOrSemi) {
TokenizedBuffer tokens = GetTokenizedBuffer("fn foo");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(tree,
MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.has_error = true,
.children = {{ParseNodeKind::Identifier(), "foo"}}}}));
}
TEST_F(ParseTreeTest,
FunctionDeclarationWithIdentifierInsteadOfSignatureAndSemi) {
TokenizedBuffer tokens = GetTokenizedBuffer("fn foo bar;");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(tree, MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.has_error = true,
.children = {{ParseNodeKind::Identifier(), "foo"},
{ParseNodeKind::DeclarationEnd()}}}}));
}
TEST_F(ParseTreeTest, FunctionDeclarationWithSingleIdentifierParameterList) {
TokenizedBuffer tokens = GetTokenizedBuffer("fn foo(bar);");
ParseTree tree = ParseTree::Parse(tokens, emitter);
// Note: this might become valid depending on the parameter syntax, this test
// shouldn't be taken as a sign it should remain invalid.
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(
tree,
MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.has_error = true,
.children = {{ParseNodeKind::Identifier(), "foo"},
{.kind = ParseNodeKind::ParameterList(),
.has_error = true,
.children = {{ParseNodeKind::ParameterListEnd()}}},
{ParseNodeKind::DeclarationEnd()}}}}));
}
TEST_F(ParseTreeTest, FunctionDeclarationWithoutName) {
TokenizedBuffer tokens = GetTokenizedBuffer("fn ();");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(tree, MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.has_error = true,
.children = {{ParseNodeKind::DeclarationEnd()}}}}));
}
TEST_F(ParseTreeTest,
FunctionDeclarationWithoutNameAndManyTokensToSkipInGroupedSymbols) {
TokenizedBuffer tokens = GetTokenizedBuffer(
"fn (a tokens c d e f g h i j k l m n o p q r s t u v w x y z);");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(tree, MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.has_error = true,
.children = {{ParseNodeKind::DeclarationEnd()}}}}));
}
TEST_F(ParseTreeTest, FunctionDeclarationSkipToNewlineWithoutSemi) {
TokenizedBuffer tokens = GetTokenizedBuffer(
"fn ()\n"
"fn F();");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(
tree,
MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(), .has_error = true},
{.kind = ParseNodeKind::FunctionDeclaration(),
.children = {{ParseNodeKind::Identifier(), "F"},
{.kind = ParseNodeKind::ParameterList(),
.children = {{ParseNodeKind::ParameterListEnd()}}},
{ParseNodeKind::DeclarationEnd()}}}}));
}
TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineWithSemi) {
TokenizedBuffer tokens = GetTokenizedBuffer(
"fn (x,\n"
" y,\n"
" z);\n"
"fn F();");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(
tree,
MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.has_error = true,
.children = {{ParseNodeKind::DeclarationEnd()}}},
{.kind = ParseNodeKind::FunctionDeclaration(),
.children = {{ParseNodeKind::Identifier(), "F"},
{.kind = ParseNodeKind::ParameterList(),
.children = {{ParseNodeKind::ParameterListEnd()}}},
{ParseNodeKind::DeclarationEnd()}}}}));
}
TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineWithoutSemi) {
TokenizedBuffer tokens = GetTokenizedBuffer(
"fn (x,\n"
" y,\n"
" z)\n"
"fn F();");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(
tree,
MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(), .has_error = true},
{.kind = ParseNodeKind::FunctionDeclaration(),
.children = {{ParseNodeKind::Identifier(), "F"},
{.kind = ParseNodeKind::ParameterList(),
.children = {{ParseNodeKind::ParameterListEnd()}}},
{ParseNodeKind::DeclarationEnd()}}}}));
}
TEST_F(ParseTreeTest, FunctionDeclarationSkipIndentedNewlineUntilOutdent) {
TokenizedBuffer tokens = GetTokenizedBuffer(
" fn (x,\n"
" y,\n"
" z)\n"
"fn F();");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(
tree,
MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(), .has_error = true},
{.kind = ParseNodeKind::FunctionDeclaration(),
.children = {{ParseNodeKind::Identifier(), "F"},
{.kind = ParseNodeKind::ParameterList(),
.children = {{ParseNodeKind::ParameterListEnd()}}},
{ParseNodeKind::DeclarationEnd()}}}}));
}
TEST_F(ParseTreeTest, FunctionDeclarationSkipWithoutSemiToCurly) {
// FIXME: We don't have a grammar construct that uses curlies yet so this just
// won't parse at all. Once it does, we should ensure that the close brace
// gets properly parsed for the struct (or whatever other curly-braced syntax
// we have grouping function declarations) despite the invalid function
// declaration missing a semicolon.
TokenizedBuffer tokens = GetTokenizedBuffer(
"struct X { fn () }\n"
"fn F();");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_TRUE(tree.HasErrors());
}
TEST_F(ParseTreeTest, BasicFunctionDefinition) {
TokenizedBuffer tokens = GetTokenizedBuffer(
"fn F() {\n"
"}");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_FALSE(tree.HasErrors());
EXPECT_THAT(
tree, MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.children = {
{ParseNodeKind::Identifier(), "F"},
{.kind = ParseNodeKind::ParameterList(),
.children = {{ParseNodeKind::ParameterListEnd()}}},
{.kind = ParseNodeKind::CodeBlock(),
.text = "{",
.children = {{ParseNodeKind::CodeBlockEnd(), "}"}}}}}}));
}
TEST_F(ParseTreeTest, FunctionDefinitionWithNestedBlocks) {
TokenizedBuffer tokens = GetTokenizedBuffer(
"fn F() {\n"
" {\n"
" {{}}\n"
" }\n"
"}");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_FALSE(tree.HasErrors());
EXPECT_THAT(
tree,
MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.children = {
{ParseNodeKind::Identifier(), "F"},
{.kind = ParseNodeKind::ParameterList(),
.children = {{ParseNodeKind::ParameterListEnd()}}},
{.kind = ParseNodeKind::CodeBlock(),
.children = {
{.kind = ParseNodeKind::CodeBlock(),
.children = {{.kind = ParseNodeKind::CodeBlock(),
.children =
{{.kind = ParseNodeKind::CodeBlock(),
.children = {{ParseNodeKind::
CodeBlockEnd()}}},
{ParseNodeKind::CodeBlockEnd()}}},
{ParseNodeKind::CodeBlockEnd()}}},
{ParseNodeKind::CodeBlockEnd()}}}}}}));
}
TEST_F(ParseTreeTest, FunctionDefinitionWithIdenifierInStatements) {
TokenizedBuffer tokens = GetTokenizedBuffer(
"fn F() {\n"
" bar\n"
"}");
ParseTree tree = ParseTree::Parse(tokens, emitter);
// Note: this might become valid depending on the expression syntax. This test
// shouldn't be taken as a sign it should remain invalid.
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(
tree,
MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.children = {{ParseNodeKind::Identifier(), "F"},
{.kind = ParseNodeKind::ParameterList(),
.children = {{ParseNodeKind::ParameterListEnd()}}},
{.kind = ParseNodeKind::CodeBlock(),
.has_error = true,
.children = {{ParseNodeKind::CodeBlockEnd()}}}}}}));
}
TEST_F(ParseTreeTest, FunctionDefinitionWithIdenifierInNestedBlock) {
TokenizedBuffer tokens = GetTokenizedBuffer(
"fn F() {\n"
" {bar}\n"
"}");
ParseTree tree = ParseTree::Parse(tokens, emitter);
// Note: this might become valid depending on the expression syntax. This test
// shouldn't be taken as a sign it should remain invalid.
EXPECT_TRUE(tree.HasErrors());
EXPECT_THAT(
tree,
MatchParseTreeNodes(
{{.kind = ParseNodeKind::FunctionDeclaration(),
.children = {
{ParseNodeKind::Identifier(), "F"},
{.kind = ParseNodeKind::ParameterList(),
.children = {{ParseNodeKind::ParameterListEnd()}}},
{.kind = ParseNodeKind::CodeBlock(),
.children = {{.kind = ParseNodeKind::CodeBlock(),
.has_error = true,
.children = {{ParseNodeKind::CodeBlockEnd()}}},
{ParseNodeKind::CodeBlockEnd()}}}}}}));
}
auto GetAndDropLine(llvm::StringRef& s) -> std::string {
auto newline_offset = s.find_first_of('\n');
llvm::StringRef line = s.slice(0, newline_offset);
if (newline_offset != llvm::StringRef::npos)
s = s.substr(newline_offset + 1);
else
s = "";
return line.str();
}
TEST_F(ParseTreeTest, Printing) {
TokenizedBuffer tokens = GetTokenizedBuffer("fn F();");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_FALSE(tree.HasErrors());
std::string print_storage;
llvm::raw_string_ostream print_stream(print_storage);
tree.Print(print_stream);
llvm::StringRef print = print_stream.str();
EXPECT_THAT(GetAndDropLine(print), StrEq("["));
EXPECT_THAT(GetAndDropLine(print),
StrEq("{node_index: 4, kind: 'FunctionDeclaration', text: 'fn', "
"subtree_size: 5, children: ["));
EXPECT_THAT(GetAndDropLine(print),
StrEq(" {node_index: 0, kind: 'Identifier', text: 'F'},"));
EXPECT_THAT(GetAndDropLine(print),
StrEq(" {node_index: 2, kind: 'ParameterList', text: '(', "
"subtree_size: 2, children: ["));
EXPECT_THAT(GetAndDropLine(print),
StrEq(" {node_index: 1, kind: 'ParameterListEnd', "
"text: ')'}]},"));
EXPECT_THAT(GetAndDropLine(print),
StrEq(" {node_index: 3, kind: 'DeclarationEnd', text: ';'}]},"));
EXPECT_THAT(GetAndDropLine(print), StrEq("]"));
EXPECT_TRUE(print.empty()) << print;
}
TEST_F(ParseTreeTest, PrintingAsYAML) {
TokenizedBuffer tokens = GetTokenizedBuffer("fn F();");
ParseTree tree = ParseTree::Parse(tokens, emitter);
EXPECT_FALSE(tree.HasErrors());
std::string print_output;
llvm::raw_string_ostream print_stream(print_output);
tree.Print(print_stream);
print_stream.flush();
// Parse the output into a YAML stream. This will print errors to stderr.
llvm::SourceMgr source_manager;
llvm::yaml::Stream yaml_stream(print_output, source_manager);
auto di = yaml_stream.begin();
auto* root_node = llvm::dyn_cast<llvm::yaml::SequenceNode>(di->getRoot());
ASSERT_THAT(root_node, NotNull());
// The root node is just an array of top-level parse nodes.
auto ni = root_node->begin();
auto ne = root_node->end();
auto* node = llvm::dyn_cast<llvm::yaml::MappingNode>(&*ni);
ASSERT_THAT(node, NotNull());
auto nkvi = node->begin();
auto nkve = node->end();
EXPECT_THAT(&*nkvi, IsKeyValueScalars("node_index", "4"));
++nkvi;
EXPECT_THAT(&*nkvi, IsKeyValueScalars("kind", "FunctionDeclaration"));
++nkvi;
EXPECT_THAT(&*nkvi, IsKeyValueScalars("text", "fn"));
++nkvi;
EXPECT_THAT(&*nkvi, IsKeyValueScalars("subtree_size", "5"));
++nkvi;
auto* children_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(&*nkvi);
ASSERT_THAT(children_node, NotNull());
auto* children_key_node =
llvm::dyn_cast<llvm::yaml::ScalarNode>(children_node->getKey());
ASSERT_THAT(children_key_node, NotNull());
EXPECT_THAT(children_key_node->getRawValue(), StrEq("children"));
auto* children_value_node =
llvm::dyn_cast<llvm::yaml::SequenceNode>(children_node->getValue());
ASSERT_THAT(children_value_node, NotNull());
auto ci = children_value_node->begin();
auto ce = children_value_node->end();
ASSERT_THAT(ci, Ne(ce));
node = llvm::dyn_cast<llvm::yaml::MappingNode>(&*ci);
ASSERT_THAT(node, NotNull());
auto ckvi = node->begin();
EXPECT_THAT(&*ckvi, IsKeyValueScalars("node_index", "0"));
++ckvi;
EXPECT_THAT(&*ckvi, IsKeyValueScalars("kind", "Identifier"));
++ckvi;
EXPECT_THAT(&*ckvi, IsKeyValueScalars("text", "F"));
++ckvi;
EXPECT_THAT(ckvi, Eq(node->end()));
++ci;
ASSERT_THAT(ci, Ne(ce));
node = llvm::dyn_cast<llvm::yaml::MappingNode>(&*ci);
ASSERT_THAT(node, NotNull());
ckvi = node->begin();
auto ckve = node->end();
EXPECT_THAT(&*ckvi, IsKeyValueScalars("node_index", "2"));
++ckvi;
EXPECT_THAT(&*ckvi, IsKeyValueScalars("kind", "ParameterList"));
++ckvi;
EXPECT_THAT(&*ckvi, IsKeyValueScalars("text", "("));
++ckvi;
EXPECT_THAT(&*ckvi, IsKeyValueScalars("subtree_size", "2"));
++ckvi;
children_node = llvm::dyn_cast<llvm::yaml::KeyValueNode>(&*ckvi);
ASSERT_THAT(children_node, NotNull());
children_key_node =
llvm::dyn_cast<llvm::yaml::ScalarNode>(children_node->getKey());
ASSERT_THAT(children_key_node, NotNull());
EXPECT_THAT(children_key_node->getRawValue(), StrEq("children"));
children_value_node =
llvm::dyn_cast<llvm::yaml::SequenceNode>(children_node->getValue());
ASSERT_THAT(children_value_node, NotNull());
auto c2_i = children_value_node->begin();
auto c2_e = children_value_node->end();
ASSERT_THAT(c2_i, Ne(c2_e));
node = llvm::dyn_cast<llvm::yaml::MappingNode>(&*c2_i);
ASSERT_THAT(node, NotNull());
auto c2_kvi = node->begin();
EXPECT_THAT(&*c2_kvi, IsKeyValueScalars("node_index", "1"));
++c2_kvi;
EXPECT_THAT(&*c2_kvi, IsKeyValueScalars("kind", "ParameterListEnd"));
++c2_kvi;
EXPECT_THAT(&*c2_kvi, IsKeyValueScalars("text", ")"));
++c2_kvi;
EXPECT_THAT(c2_kvi, Eq(node->end()));
++c2_i;
EXPECT_THAT(c2_i, Eq(c2_e));
++ckvi;
EXPECT_THAT(ckvi, Eq(ckve));
++ci;
ASSERT_THAT(ci, Ne(ce));
node = llvm::dyn_cast<llvm::yaml::MappingNode>(&*ci);
ASSERT_THAT(node, NotNull());
ckvi = node->begin();
EXPECT_THAT(&*ckvi, IsKeyValueScalars("node_index", "3"));
++ckvi;
EXPECT_THAT(&*ckvi, IsKeyValueScalars("kind", "DeclarationEnd"));
++ckvi;
EXPECT_THAT(&*ckvi, IsKeyValueScalars("text", ";"));
++ckvi;
EXPECT_THAT(ckvi, Eq(node->end()));
++ci;
EXPECT_THAT(ci, Eq(ce));
++nkvi;
EXPECT_THAT(nkvi, Eq(nkve));
++ni;
EXPECT_THAT(ni, Eq(ne));
++di;
EXPECT_THAT(di, Eq(yaml_stream.end()));
}
} // namespace
} // namespace Carbon