diff --git a/spec/runtime/parse_stack_spec.cc b/spec/runtime/parse_stack_spec.cc index ef348e76..2f1a39dd 100644 --- a/spec/runtime/parse_stack_spec.cc +++ b/spec/runtime/parse_stack_spec.cc @@ -32,7 +32,7 @@ describe("ParseStack", [&]() { ts_tree_release(trees[i]); }); - describe("push(head_index, state, tree)", [&]() { + describe("pushing entries to the stack", [&]() { it("adds entries to the stack", [&]() { AssertThat(ts_parse_stack_head_count(stack), Equals(1)); AssertThat(ts_parse_stack_head(stack, 0), Equals(nullptr)); @@ -66,7 +66,7 @@ describe("ParseStack", [&]() { }); }); - describe("pop(head_index, count, should_count_extra)", [&]() { + describe("popping nodes from the stack", [&]() { ParseStackPopResultList pop; before_each([&]() { @@ -122,7 +122,7 @@ describe("ParseStack", [&]() { }); }); - describe("split(head_index)", [&]() { + describe("splitting the stack", [&]() { it("creates a new independent head with the same entries", [&]() { /* * A0__B1__C2. @@ -157,142 +157,164 @@ describe("ParseStack", [&]() { AssertThat(*ts_parse_stack_head(stack, 0), Equals({trees[3], stateD})); AssertThat(*ts_parse_stack_head(stack, 1), Equals({trees[3], stateF})); }); + }); - describe("when same state is pushed onto two heads", [&]() { - bool merged; + describe("pushing the same state onto two different heads of the stack", [&]() { + before_each([&]() { + /* + * A0__B1__C2__D3. + * \__E4__F5. + */ + ts_parse_stack_push(stack, 0, stateA, trees[0]); + ts_parse_stack_push(stack, 0, stateB, trees[1]); + ts_parse_stack_split(stack, 0); + ts_parse_stack_push(stack, 0, stateC, trees[2]); + ts_parse_stack_push(stack, 0, stateD, trees[3]); + ts_parse_stack_push(stack, 1, stateE, trees[4]); + ts_parse_stack_push(stack, 1, stateF, trees[5]); - before_each([&]() { + AssertThat(ts_parse_stack_head_count(stack), Equals(2)); + AssertThat(*ts_parse_stack_head(stack, 0), Equals({trees[3], stateD})); + AssertThat(*ts_parse_stack_head(stack, 1), Equals({trees[5], stateF})); + }); + + describe("when the trees are identical", [&]() { + it("merges the heads", [&]() { + /* + * A0__B1__C2__D3__G6. + * \__E4__F5__/ + */ + bool merged = ts_parse_stack_push(stack, 0, stateG, trees[6]); + AssertThat(merged, IsFalse()); + merged = ts_parse_stack_push(stack, 1, stateG, trees[6]); + AssertThat(merged, IsTrue()); + + AssertThat(ts_parse_stack_head_count(stack), Equals(1)); + const ParseStackEntry *entry1 = ts_parse_stack_head(stack, 0); + AssertThat(*entry1, Equals({trees[6], stateG})); + AssertThat(ts_parse_stack_entry_next_count(entry1), Equals(2)); + AssertThat(*ts_parse_stack_entry_next(entry1, 0), Equals({trees[3], stateD})); + AssertThat(*ts_parse_stack_entry_next(entry1, 1), Equals({trees[5], stateF})); + }); + }); + + describe("when the trees are different", [&]() { + it("merges the heads by creating an ambiguity node", [&]() { + /* + * A0__B1__C2__D3__G(6|7) + * \__E4__F5____/ + */ + bool merged = ts_parse_stack_push(stack, 0, stateG, trees[6]); + AssertThat(merged, IsFalse()); + merged = ts_parse_stack_push(stack, 1, stateG, trees[7]); + AssertThat(merged, IsTrue()); + + AssertThat(ts_parse_stack_head_count(stack), Equals(1)); + AssertThat(*ts_parse_stack_head(stack, 0), Equals({ + ts_tree_make_ambiguity(trees[6], trees[7]), + stateG + })); + }); + }); + }); + + describe("popping from a stack head that has been merged", [&]() { + before_each([&]() { + /* + * A0__B1__C2__D3__G6. + * \__E4__F5__/ + */ + ts_parse_stack_push(stack, 0, stateA, trees[0]); + ts_parse_stack_push(stack, 0, stateB, trees[1]); + ts_parse_stack_split(stack, 0); + ts_parse_stack_push(stack, 0, stateC, trees[2]); + ts_parse_stack_push(stack, 0, stateD, trees[3]); + ts_parse_stack_push(stack, 0, stateG, trees[6]); + ts_parse_stack_push(stack, 1, stateE, trees[4]); + ts_parse_stack_push(stack, 1, stateF, trees[5]); + ts_parse_stack_push(stack, 1, stateG, trees[6]); + }); + + describe("when there are two paths that lead to two different heads", [&]() { + it("returns an entry for each revealed head", [&]() { /* * A0__B1__C2. + * \__E4. */ - ts_parse_stack_push(stack, 0, stateA, trees[0]); - ts_parse_stack_push(stack, 0, stateB, trees[1]); - ts_parse_stack_push(stack, 0, stateC, trees[2]); + ParseStackPopResultList pop = ts_parse_stack_pop(stack, 0, 2, false); + AssertThat(pop.size, Equals(2)); + ParseStackPopResult pop1 = pop.contents[0]; + AssertThat(pop1.index, Equals(0)); + AssertThat(pop1.tree_count, Equals(2)); + AssertThat(pop1.trees[0], Equals(trees[3])); + AssertThat(pop1.trees[1], Equals(trees[6])); + + ParseStackPopResult pop2 = pop.contents[1]; + AssertThat(pop2.index, Equals(1)); + AssertThat(pop2.tree_count, Equals(2)); + AssertThat(pop2.trees[0], Equals(trees[5])); + AssertThat(pop2.trees[1], Equals(trees[6])); + + AssertThat(ts_parse_stack_head_count(stack), Equals(2)); + AssertThat(*ts_parse_stack_head(stack, 0), Equals({trees[2], stateC})); + AssertThat(*ts_parse_stack_head(stack, 1), Equals({trees[4], stateE})); + }); + }); + + describe("when there is one path, leading to one head", [&]() { + it("returns a single entry", [&]() { + /* + * A0__B1__C2__D3__G6__H7. + * \__E4__F5__/ + */ + bool merged = ts_parse_stack_push(stack, 0, stateH, trees[7]); + AssertThat(merged, IsFalse()); + AssertThat(ts_parse_stack_head_count(stack), Equals(1)); + + /* + * A0__B1__C2__D3__G6. + * \__E4__F5__/ + */ + ParseStackPopResultList pop = ts_parse_stack_pop(stack, 0, 1, false); + + AssertThat(pop.size, Equals(1)); + AssertThat(ts_parse_stack_head_count(stack), Equals(1)); + }); + }); + + describe("when there is one path that leads to two different heads", [&]() { + it("returns two entries with the same array of trees", [&]() { /* * A0__B1__C2__D3. * \__E4__F5. */ - ts_parse_stack_split(stack, 0); - ts_parse_stack_push(stack, 0, stateD, trees[3]); - ts_parse_stack_pop(stack, 1, 1, false); - ts_parse_stack_push(stack, 1, stateE, trees[4]); - ts_parse_stack_push(stack, 1, stateF, trees[5]); - + ParseStackPopResultList pop = ts_parse_stack_pop(stack, 0, 1, false); AssertThat(ts_parse_stack_head_count(stack), Equals(2)); - AssertThat(*ts_parse_stack_head(stack, 0), Equals({trees[3], stateD})); - AssertThat(*ts_parse_stack_head(stack, 1), Equals({trees[5], stateF})); + + AssertThat(pop.size, Equals(2)); + AssertThat(pop.contents[0].index, Equals(0)); + AssertThat(pop.contents[1].index, Equals(1)); + AssertThat(pop.contents[0].trees, Equals(pop.contents[1].trees)); }); + }); - describe("when the trees are identical", [&]() { - before_each([&]() { - /* - * A0__B1__C2__D3__G6. - * \__E4__F5__/ - */ - merged = ts_parse_stack_push(stack, 0, stateG, trees[6]); - AssertThat(merged, IsFalse()); - merged = ts_parse_stack_push(stack, 1, stateG, trees[6]); - AssertThat(merged, IsTrue()); - }); + describe("when there are two paths that converge at the same head", [&]() { + it("returns two entries for that head", [&]() { + /* + * A0__B1. + */ + ParseStackPopResultList pop = ts_parse_stack_pop(stack, 0, 3, false); + AssertThat(ts_parse_stack_head_count(stack), Equals(1)); + AssertThat(*ts_parse_stack_head(stack, 0), Equals({trees[1], stateB})); - it("merges the heads", [&]() { - AssertThat(ts_parse_stack_head_count(stack), Equals(1)); - const ParseStackEntry *entry1 = ts_parse_stack_head(stack, 0); - AssertThat(*entry1, Equals({trees[6], stateG})); - AssertThat(ts_parse_stack_entry_next_count(entry1), Equals(2)); - AssertThat(*ts_parse_stack_entry_next(entry1, 0), Equals({trees[3], stateD})); - AssertThat(*ts_parse_stack_entry_next(entry1, 1), Equals({trees[5], stateF})); - }); - - it("removes nodes along both heads on subsequent pop operations", [&]() { - /* - * A0__B1__C2. - * \__E4. - */ - ParseStackPopResultList pop = ts_parse_stack_pop(stack, 0, 2, false); - - AssertThat(pop.size, Equals(2)); - AssertThat(pop.contents[0].tree_count, Equals(2)); - AssertThat(pop.contents[0].trees[0], Equals(trees[3])); - AssertThat(pop.contents[0].trees[1], Equals(trees[6])); - AssertThat(pop.contents[1].tree_count, Equals(2)); - AssertThat(pop.contents[1].trees[0], Equals(trees[5])); - AssertThat(pop.contents[1].trees[1], Equals(trees[6])); - - AssertThat(ts_parse_stack_head_count(stack), Equals(2)); - AssertThat(*ts_parse_stack_head(stack, 0), Equals({trees[2], stateC})); - AssertThat(*ts_parse_stack_head(stack, 1), Equals({trees[4], stateE})); - }); - - it("pops only one path if the split is hidden under sufficiently many nodes", [&]() { - /* - * A0__B1__C2__D3__G6__H7. - * \__E4__F5__/ - */ - merged = ts_parse_stack_push(stack, 0, stateH, trees[7]); - AssertThat(merged, IsFalse()); - AssertThat(ts_parse_stack_head_count(stack), Equals(1)); - - /* - * A0__B1__C2__D3__G6. - * \__E4__F5__/ - */ - ParseStackPopResultList pop = ts_parse_stack_pop(stack, 0, 1, false); - - AssertThat(pop.size, Equals(1)); - AssertThat(ts_parse_stack_head_count(stack), Equals(1)); - }); - - it("can pop one branch that reveals two head", [&]() { - /* - * A0__B1__C2__D3. - * \__E4__F5. - */ - ParseStackPopResultList pop = ts_parse_stack_pop(stack, 0, 1, false); - AssertThat(ts_parse_stack_head_count(stack), Equals(2)); - - AssertThat(pop.size, Equals(2)); - AssertThat(pop.contents[0].index, Equals(0)); - AssertThat(pop.contents[1].index, Equals(1)); - AssertThat(pop.contents[0].trees, Equals(pop.contents[1].trees)); - }); - - it("can pop two branches that converge at the same head", [&]() { - /* - * A0__B1. - */ - ParseStackPopResultList pop = ts_parse_stack_pop(stack, 0, 3, false); - AssertThat(ts_parse_stack_head_count(stack), Equals(1)); - AssertThat(*ts_parse_stack_head(stack, 0), Equals({trees[1], stateB})); - - AssertThat(pop.size, Equals(2)); - AssertThat(pop.contents[0].tree_count, Equals(3)); - AssertThat(pop.contents[0].index, Equals(0)); - AssertThat(pop.contents[0].trees[0], Equals(trees[2])); - AssertThat(pop.contents[1].tree_count, Equals(3)); - AssertThat(pop.contents[1].index, Equals(0)); - AssertThat(pop.contents[1].trees[0], Equals(trees[4])); - }); - }); - - describe("when the trees are different", [&]() { - it("merges the heads by creating an ambiguity node", [&]() { - /* - * A0__B1__C2__D3__G(6|7) - * \__E4__F5____/ - */ - merged = ts_parse_stack_push(stack, 0, stateG, trees[6]); - AssertThat(merged, IsFalse()); - merged = ts_parse_stack_push(stack, 1, stateG, trees[7]); - AssertThat(merged, IsTrue()); - - AssertThat(ts_parse_stack_head_count(stack), Equals(1)); - AssertThat(*ts_parse_stack_head(stack, 0), Equals({ - ts_tree_make_ambiguity(trees[6], trees[7]), - stateG - })); - }); + AssertThat(pop.size, Equals(2)); + AssertThat(pop.contents[0].tree_count, Equals(3)); + AssertThat(pop.contents[0].index, Equals(0)); + AssertThat(pop.contents[0].trees[0], Equals(trees[2])); + AssertThat(pop.contents[1].tree_count, Equals(3)); + AssertThat(pop.contents[1].index, Equals(0)); + AssertThat(pop.contents[1].trees[0], Equals(trees[4])); }); }); });