From da2ef7ad357ec99c238a032fd9dd6f109412ade5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 23 Feb 2016 17:35:50 -0800 Subject: [PATCH] Store trees in the links between stack nodes, not in the nodes themselves --- spec/integration/corpus_specs.cc | 1 + spec/runtime/stack_spec.cc | 364 ++++++++++++++----------------- src/runtime/array.h | 2 +- src/runtime/parser.c | 76 +++---- src/runtime/stack.c | 196 +++++++++-------- src/runtime/stack.h | 7 - 6 files changed, 295 insertions(+), 351 deletions(-) diff --git a/spec/integration/corpus_specs.cc b/spec/integration/corpus_specs.cc index 8f264e8f..8f8d9944 100644 --- a/spec/integration/corpus_specs.cc +++ b/spec/integration/corpus_specs.cc @@ -112,6 +112,7 @@ describe("The Corpus", []() { document = ts_document_make(); ts_document_set_language(document, get_test_language(language_name)); // ts_document_set_debugger(document, log_debugger_make(true)); + // ts_document_print_debugging_graphs(document, true); }); after_each([&]() { diff --git a/spec/runtime/stack_spec.cc b/spec/runtime/stack_spec.cc index eda3442f..32640561 100644 --- a/spec/runtime/stack_spec.cc +++ b/spec/runtime/stack_spec.cc @@ -12,7 +12,8 @@ enum { }; enum { - symbol0, symbol1, symbol2, symbol3, symbol4, symbol5, symbol6, symbol7, symbol8 + symbol0, symbol1, symbol2, symbol3, symbol4, symbol5, symbol6, symbol7, symbol8, + symbol9, symbol10 }; struct TreeSelectionSpy { @@ -62,7 +63,7 @@ START_TEST describe("Stack", [&]() { Stack *stack; - const size_t tree_count = 10; + const size_t tree_count = 11; TSTree *trees[tree_count]; TreeSelectionSpy tree_selection_spy{0, NULL, {NULL, NULL}}; TSLength tree_len = {2, 3, 0, 3}; @@ -99,29 +100,29 @@ describe("Stack", [&]() { AssertThat(ts_stack_head(stack, 0), Equals(nullptr)); /* - * A0. + * . <--0-- A* */ ts_stack_push(stack, 0, stateA, trees[0]); const StackEntry *entry1 = ts_stack_head(stack, 0); - AssertThat(*entry1, Equals({trees[0], stateA, tree_len})); + AssertThat(*entry1, Equals({stateA, tree_len})); AssertThat(ts_stack_entry_next_count(entry1), Equals(1)); AssertThat(ts_stack_entry_next(entry1, 0), Equals(nullptr)); /* - * A0__B1. + * . <--0-- A <--1-- B* */ ts_stack_push(stack, 0, stateB, trees[1]); const StackEntry *entry2 = ts_stack_head(stack, 0); - AssertThat(*entry2, Equals({trees[1], stateB, tree_len * 2})); + AssertThat(*entry2, Equals({stateB, tree_len * 2})); AssertThat(ts_stack_entry_next_count(entry2), Equals(1)); AssertThat(ts_stack_entry_next(entry2, 0), Equals(entry1)); /* - * A0__B1__C2. + * . <--0-- A <--1-- B <--2-- C* */ ts_stack_push(stack, 0, stateC, trees[2]); const StackEntry *entry3 = ts_stack_head(stack, 0); - AssertThat(*entry3, Equals({trees[2], stateC, tree_len * 3})); + AssertThat(*entry3, Equals({stateC, tree_len * 3})); AssertThat(ts_stack_entry_next_count(entry3), Equals(1)); AssertThat(ts_stack_entry_next(entry3, 0), Equals(entry2)); }); @@ -130,7 +131,7 @@ describe("Stack", [&]() { describe("popping nodes from the stack", [&]() { before_each([&]() { /* - * A0__B1__C2. + * . <--0-- A <--1-- B <--2-- C* */ ts_stack_push(stack, 0, stateA, trees[0]); ts_stack_push(stack, 0, stateB, trees[1]); @@ -139,14 +140,14 @@ describe("Stack", [&]() { it("removes the given number of nodes from the stack", [&]() { /* - * A0. + * . <--0-- A* */ StackPopResultArray results = ts_stack_pop(stack, 0, 2, false); AssertThat(results.size, Equals(1)); StackPopResult result = results.contents[0]; AssertThat(result.trees, Equals(vector({ trees[1], trees[2] }))); - AssertThat(*ts_stack_head(stack, 0), Equals({trees[0], stateA, tree_len})); + AssertThat(*ts_stack_head(stack, 0), Equals({stateA, tree_len})); free_pop_results(&results); /* @@ -165,6 +166,9 @@ describe("Stack", [&]() { it("does not count 'extra' trees toward the count", [&]() { trees[1]->extra = true; + /* + * . + */ StackPopResultArray results = ts_stack_pop(stack, 0, 2, false); AssertThat(results.size, Equals(1)); @@ -176,6 +180,9 @@ describe("Stack", [&]() { }); it("pops the entire stack when given a negative count", [&]() { + /* + * . + */ StackPopResultArray results = ts_stack_pop(stack, 0, -1, false); AssertThat(results.size, Equals(1)); @@ -189,49 +196,58 @@ describe("Stack", [&]() { describe("splitting the stack", [&]() { it("creates a new independent head with the same entries", [&]() { /* - * A0__B1__C2. + * . <--0-- A <--1-- B <--2-- C* */ ts_stack_push(stack, 0, stateA, trees[0]); ts_stack_push(stack, 0, stateB, trees[1]); ts_stack_push(stack, 0, stateC, trees[2]); + /* + * . <--0-- A <--1-- B <--2-- C* + * ↑ + * `-* + */ int new_index = ts_stack_split(stack, 0); AssertThat(ts_stack_head_count(stack), Equals(2)); AssertThat(new_index, Equals(1)); + AssertThat(ts_stack_top_state(stack, 1), Equals(stateC)); /* - * A0__B1__C2__D3. - * \. + * . <--0-- A <--1-- B <--2-- C <--3-- D* + * ↑ + * `-* */ ts_stack_push(stack, 0, stateD, trees[3]); StackPopResultArray pop_results = ts_stack_pop(stack, 1, 1, false); AssertThat(ts_stack_head_count(stack), Equals(2)); - AssertThat(*ts_stack_head(stack, 0), Equals({trees[3], stateD, tree_len * 4})); - AssertThat(*ts_stack_head(stack, 1), Equals({trees[1], stateB, tree_len * 2})); + AssertThat(*ts_stack_head(stack, 0), Equals({stateD, tree_len * 4})); + AssertThat(*ts_stack_head(stack, 1), Equals({stateB, tree_len * 2})); AssertThat(pop_results.size, Equals(1)); StackPopResult pop_result = pop_results.contents[0]; AssertThat(pop_result.trees.size, Equals(1)); free_pop_results(&pop_results); /* - * A0__B1__C2__D3. - * \__E4__F3. + * . <--0-- A <--1-- B <--2-- C <--3-- D* + * ↑ + * `---4--- E <--5-- F* */ ts_stack_push(stack, 1, stateE, trees[4]); - ts_stack_push(stack, 1, stateF, trees[3]); + ts_stack_push(stack, 1, stateF, trees[5]); AssertThat(ts_stack_head_count(stack), Equals(2)); - AssertThat(*ts_stack_head(stack, 0), Equals({trees[3], stateD, tree_len * 4})); - AssertThat(*ts_stack_head(stack, 1), Equals({trees[3], stateF, tree_len * 4})); + AssertThat(*ts_stack_head(stack, 0), Equals({stateD, tree_len * 4})); + AssertThat(*ts_stack_head(stack, 1), Equals({stateF, tree_len * 4})); }); }); describe("pushing the same state onto two different heads of the stack", [&]() { before_each([&]() { /* - * A0__B1__C2__D3. - * \__E4__F5. + * . <--0-- A <--1-- B <--2-- C <--3-- D* + * ↑ + * `---4--- E <--5-- F* */ ts_stack_push(stack, 0, stateA, trees[0]); ts_stack_push(stack, 0, stateB, trees[1]); @@ -242,73 +258,52 @@ describe("Stack", [&]() { ts_stack_push(stack, 1, stateF, trees[5]); AssertThat(ts_stack_head_count(stack), Equals(2)); - AssertThat(*ts_stack_head(stack, 0), Equals({trees[3], stateD, tree_len * 4})); - AssertThat(*ts_stack_head(stack, 1), Equals({trees[5], stateF, tree_len * 4})); + AssertThat(*ts_stack_head(stack, 0), Equals({stateD, tree_len * 4})); + AssertThat(*ts_stack_head(stack, 1), Equals({stateF, tree_len * 4})); }); - describe("when the trees are identical", [&]() { - it("merges the heads", [&]() { - /* - * A0__B1__C2__D3__G6. - * \__E4__F5__/ - */ - AssertThat(ts_stack_push(stack, 0, stateG, trees[6]), Equals(StackPushResultContinued)); - AssertThat(ts_stack_push(stack, 1, stateG, trees[6]), Equals(StackPushResultMerged)); + it("merges the heads", [&]() { + /* + * . <--0-- A <--1-- B <--2-- C <--3-- D <--6-- G* + * ^ | + * `---4--- E <--5-- F <--7---' + */ + AssertThat(ts_stack_push(stack, 0, stateG, trees[6]), Equals(StackPushResultContinued)); + AssertThat(ts_stack_push(stack, 1, stateG, trees[7]), Equals(StackPushResultMerged)); - AssertThat(ts_stack_head_count(stack), Equals(1)); - const StackEntry *entry1 = ts_stack_head(stack, 0); - AssertThat(*entry1, Equals({trees[6], stateG, tree_len * 5})); - AssertThat(ts_stack_entry_next_count(entry1), Equals(2)); - AssertThat(*ts_stack_entry_next(entry1, 0), Equals({trees[3], stateD, tree_len * 4})); - AssertThat(*ts_stack_entry_next(entry1, 1), Equals({trees[5], stateF, tree_len * 4})); - }); + AssertThat(ts_stack_head_count(stack), Equals(1)); + const StackEntry *entry1 = ts_stack_head(stack, 0); + AssertThat(*entry1, Equals({stateG, tree_len * 5})); + AssertThat(ts_stack_entry_next_count(entry1), Equals(2)); + AssertThat(*ts_stack_entry_next(entry1, 0), Equals({stateD, tree_len * 4})); + AssertThat(*ts_stack_entry_next(entry1, 1), Equals({stateF, tree_len * 4})); }); - describe("when the trees are different", [&]() { - before_each([&]() { - tree_selection_spy.tree_to_return = trees[7]; - AssertThat(tree_selection_spy.call_count, Equals(0)); - }); - - it("merges the heads, selecting the tree with the tree selection callback", [&]() { + describe("when the merged nodes share a successor", [&]() { + it("recursively merges the successor nodes", [&]() { /* - * A0__B1__C2__D3__G(6|7) - * \__E4__F5____/ - */ - AssertThat(ts_stack_push(stack, 0, stateG, trees[6]), Equals(StackPushResultContinued)); - AssertThat(ts_stack_push(stack, 1, stateG, trees[7]), Equals(StackPushResultMerged)); - - AssertThat(ts_stack_head_count(stack), Equals(1)); - AssertThat(tree_selection_spy.call_count, Equals(1)); - AssertThat(tree_selection_spy.arguments[0], Equals(trees[6])); - AssertThat(tree_selection_spy.arguments[1], Equals(trees[7])); - AssertThat(*ts_stack_head(stack, 0), Equals({trees[7], stateG, tree_len * 5})); - }); - }); - - describe("when successor nodes of the merged nodes have the same state", [&]() { - it("recursively merges those successor nodes", [&]() { - /* - * A0__B1__C2__D3__G6__H7. - * \__E4__F5__G6. + * . <--0-- A <--1-- B <--2-- C <--3-- D <--6-- G <--7--H* + * ↑ + * `---4--- E <--5-- F <--8-- G* */ AssertThat(ts_stack_push(stack, 0, stateG, trees[6]), Equals(StackPushResultContinued)); AssertThat(ts_stack_push(stack, 0, stateH, trees[7]), Equals(StackPushResultContinued)); AssertThat(ts_stack_push(stack, 1, stateG, trees[6]), Equals(StackPushResultContinued)); /* - * A0__B1__C2__D3__G6__H7. - * \__E4__F5_/ + * . <--0-- A <--1-- B <--2-- C <--3-- D <--6-- G <--7--H* + * ↑ | + * `---4--- E <--5-- F <--8---' */ AssertThat(ts_stack_push(stack, 1, stateH, trees[7]), Equals(StackPushResultMerged)); AssertThat(ts_stack_head_count(stack), Equals(1)); StackEntry *head = ts_stack_head(stack, 0); - AssertThat(*head, Equals({trees[7], stateH, tree_len * 6})) + AssertThat(*head, Equals({stateH, tree_len * 6})) AssertThat(ts_stack_entry_next_count(head), Equals(1)); StackEntry *next = ts_stack_entry_next(head, 0); - AssertThat(*next, Equals({trees[6], stateG, tree_len * 5})) + AssertThat(*next, Equals({stateG, tree_len * 5})) AssertThat(ts_stack_entry_next_count(next), Equals(2)); }); }); @@ -319,27 +314,24 @@ describe("Stack", [&]() { ts_tree_retain(trees[3]); TSTree *parent = ts_tree_make_node(5, 2, tree_array({ trees[2], trees[3] }), metadata); - tree_selection_spy.tree_to_return = parent; - tree_selection_spy.call_count = 0; - /* - * .__C5. - * B2.__/ + * . <--2-- B <--3-- C + * ^ | + * `--------5--------' */ ts_stack_clear(stack); ts_stack_split(stack, 0); AssertThat(ts_stack_push(stack, 0, stateC, parent), Equals(StackPushResultContinued)); AssertThat(ts_stack_push(stack, 1, stateB, trees[2]), Equals(StackPushResultContinued)); AssertThat(ts_stack_push(stack, 1, stateC, trees[3]), Equals(StackPushResultMerged)); - AssertThat(tree_selection_spy.call_count, Equals(1)); AssertThat(ts_stack_head_count(stack), Equals(1)); StackEntry *head = ts_stack_head(stack, 0); - AssertThat(*head, Equals({parent, stateC, tree_len * 2})); + AssertThat(*head, Equals({stateC, tree_len * 2})); AssertThat(ts_stack_entry_next_count(head), Equals(2)); AssertThat(ts_stack_entry_next(head, 0), Equals(nullptr)); - AssertThat(*ts_stack_entry_next(head, 1), Equals({trees[2], stateB, tree_len})); + AssertThat(*ts_stack_entry_next(head, 1), Equals({stateB, tree_len})); ts_tree_release(parent); }); @@ -349,43 +341,48 @@ describe("Stack", [&]() { describe("popping from a stack head that has been merged", [&]() { before_each([&]() { /* - * A0__B1__C2__D3__G6. - * \__E4__F5__/ + * . <--0-- A <--1-- B <--2-- C <--3-- D <--4-- E* + * ^ | + * `---5--- F <--6-- G <--7---' */ ts_stack_push(stack, 0, stateA, trees[0]); ts_stack_push(stack, 0, stateB, trees[1]); ts_stack_split(stack, 0); ts_stack_push(stack, 0, stateC, trees[2]); ts_stack_push(stack, 0, stateD, trees[3]); - ts_stack_push(stack, 0, stateG, trees[6]); - ts_stack_push(stack, 1, stateE, trees[4]); + ts_stack_push(stack, 0, stateE, trees[4]); ts_stack_push(stack, 1, stateF, trees[5]); ts_stack_push(stack, 1, stateG, trees[6]); + ts_stack_push(stack, 1, stateE, trees[7]); AssertThat(ts_stack_head_count(stack), Equals(1)); + AssertThat(ts_stack_top_state(stack, 0), Equals(stateE)); AssertThat(ts_stack_entry_next_count(ts_stack_head(stack, 0)), Equals(2)); }); describe("when there are two paths that lead to two different heads", [&]() { it("returns an entry for each revealed head", [&]() { /* - * A0__B1__C2. - * \__E4. + * . <--0-- A <--1-- B <--2-- C* + * ^ + * `---5--- F* */ StackPopResultArray results = ts_stack_pop(stack, 0, 2, false); AssertThat(results.size, Equals(2)); - StackPopResult pop1 = results.contents[0]; - AssertThat(pop1.head_index, Equals(0)); - AssertThat(pop1.trees, Equals(vector({ trees[3], trees[6] }))); + StackPopResult result1 = results.contents[0]; + AssertThat(result1.head_index, Equals(0)); + AssertThat(ts_stack_top_state(stack, 0), Equals(stateC)); + AssertThat(result1.trees, Equals(vector({ trees[3], trees[4] }))); - StackPopResult pop2 = results.contents[1]; - AssertThat(pop2.head_index, Equals(1)); - AssertThat(pop2.trees, Equals(vector({ trees[5], trees[6] }))); + StackPopResult result2 = results.contents[1]; + AssertThat(result2.head_index, Equals(1)); + AssertThat(ts_stack_top_state(stack, 1), Equals(stateF)); + AssertThat(result2.trees, Equals(vector({ trees[6], trees[7] }))); AssertThat(ts_stack_head_count(stack), Equals(2)); - AssertThat(*ts_stack_head(stack, 0), Equals({trees[2], stateC, tree_len * 3})); - AssertThat(*ts_stack_head(stack, 1), Equals({trees[4], stateE, tree_len * 3})); + AssertThat(*ts_stack_head(stack, 0), Equals({stateC, tree_len * 3})); + AssertThat(*ts_stack_head(stack, 1), Equals({stateF, tree_len * 3})); free_pop_results(&results); }); @@ -394,50 +391,27 @@ describe("Stack", [&]() { describe("when there is one path, leading to one head", [&]() { it("returns a single entry", [&]() { /* - * A0__B1__C2__D3__G6__H7. - * \__E4__F5__/ + * . <--0-- A <--1-- B <--2-- C <--3-- D <--4-- E <--8--H* + * ^ | + * `---5--- F <--6-- G <--7---' */ - AssertThat(ts_stack_push(stack, 0, stateH, trees[7]), Equals(StackPushResultContinued)); + AssertThat(ts_stack_push(stack, 0, stateH, trees[8]), Equals(StackPushResultContinued)); AssertThat(ts_stack_head_count(stack), Equals(1)); + AssertThat(ts_stack_top_state(stack, 0), Equals(stateH)); /* - * A0__B1__C2__D3__G6. - * \__E4__F5__/ + * . <--0-- A <--1-- B <--2-- C <--3-- D <--4-- E* + * ^ | + * `---5--- F <--6-- G <--7---' */ StackPopResultArray results = ts_stack_pop(stack, 0, 1, false); - AssertThat(results.size, Equals(1)); + StackPopResult result1 = results.contents[0]; + AssertThat(result1.head_index, Equals(0)); + AssertThat(result1.trees, Equals(vector({ trees[8] }))); + AssertThat(ts_stack_head_count(stack), Equals(1)); - - free_pop_results(&results); - }); - }); - - 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__G6__H7. - * \__E4__F5__/ - */ - ts_stack_push(stack, 0, stateH, trees[7]); - - - /* - * A0__B1__C2__D3. - * \__E4__F5. - */ - StackPopResultArray results = ts_stack_pop(stack, 0, 2, false); - AssertThat(ts_stack_head_count(stack), Equals(2)); - - AssertThat(results.size, Equals(2)); - StackPopResult pop1 = results.contents[0]; - AssertThat(pop1.head_index, Equals(0)); - AssertThat(pop1.trees, Equals(vector({ trees[6], trees[7] }))); - - StackPopResult pop2 = results.contents[1]; - AssertThat(pop2.head_index, Equals(1)); - AssertThat(pop2.trees, Equals(vector({ trees[6], trees[7] }))); - AssertThat(pop2.trees.contents, Equals(pop1.trees.contents)); + AssertThat(ts_stack_top_state(stack, 0), Equals(stateE)); free_pop_results(&results); }); @@ -449,16 +423,16 @@ describe("Stack", [&]() { tree_selection_spy.tree_to_return = trees[2]; /* - * A0__B1. + * . <--0-- A <--1-- B* */ StackPopResultArray results = ts_stack_pop(stack, 0, 3, false); AssertThat(ts_stack_head_count(stack), Equals(1)); - AssertThat(*ts_stack_head(stack, 0), Equals({trees[1], stateB, tree_len * 2})); + AssertThat(*ts_stack_head(stack, 0), Equals({stateB, tree_len * 2})); AssertThat(results.size, Equals(1)); - StackPopResult pop1 = results.contents[0]; - AssertThat(pop1.head_index, Equals(0)); - AssertThat(pop1.trees, Equals(vector({ trees[2], trees[3], trees[ 6] }))); + StackPopResult result1 = results.contents[0]; + AssertThat(result1.head_index, Equals(0)); + AssertThat(result1.trees, Equals(vector({ trees[2], trees[3], trees[4] }))); free_pop_results(&results); }); @@ -469,16 +443,16 @@ describe("Stack", [&]() { tree_selection_spy.tree_to_return = trees[4]; /* - * A0__B1. + * . <--0-- A <--1-- B* */ StackPopResultArray results = ts_stack_pop(stack, 0, 3, false); AssertThat(ts_stack_head_count(stack), Equals(1)); - AssertThat(*ts_stack_head(stack, 0), Equals({trees[1], stateB, tree_len * 2})); + AssertThat(*ts_stack_head(stack, 0), Equals({stateB, tree_len * 2})); AssertThat(results.size, Equals(1)); - StackPopResult pop1 = results.contents[0]; - AssertThat(pop1.head_index, Equals(0)); - AssertThat(pop1.trees, Equals(vector({ trees[4], trees[5], trees[6] }))) + StackPopResult result1 = results.contents[0]; + AssertThat(result1.head_index, Equals(0)); + AssertThat(result1.trees, Equals(vector({ trees[5], trees[6], trees[7] }))) free_pop_results(&results); }); @@ -489,91 +463,59 @@ describe("Stack", [&]() { describe("popping from a stack head that has been 3-way merged", [&]() { before_each([&]() { /* - * A0__B1__C2__D3__I8__J9. - * \__E4__F5__/ - * \__G6__H7__/ + * . <--0-- A <--1-- B <--2-- C <--3-- D <--10-- I + * ^ | + * `---4--- E <--5-- F <--6---' + * | | + * `---7--- G <--8-- H <--9---' */ ts_stack_clear(stack); ts_stack_push(stack, 0, stateA, trees[0]); - ts_stack_push(stack, 0, stateB, trees[1]); ts_stack_split(stack, 0); ts_stack_split(stack, 1); + ts_stack_push(stack, 0, stateB, trees[1]); ts_stack_push(stack, 0, stateC, trees[2]); - ts_stack_push(stack, 1, stateE, trees[4]); - ts_stack_push(stack, 2, stateG, trees[6]); ts_stack_push(stack, 0, stateD, trees[3]); + ts_stack_push(stack, 1, stateE, trees[4]); ts_stack_push(stack, 1, stateF, trees[5]); - ts_stack_push(stack, 2, stateH, trees[7]); - ts_stack_push(stack, 0, stateI, trees[8]); - ts_stack_push(stack, 1, stateI, trees[8]); - ts_stack_push(stack, 1, stateI, trees[8]); - ts_stack_push(stack, 0, stateJ, trees[9]); - + ts_stack_push(stack, 1, stateD, trees[6]); + ts_stack_push(stack, 1, stateG, trees[7]); + ts_stack_push(stack, 1, stateH, trees[8]); + ts_stack_push(stack, 1, stateD, trees[9]); AssertThat(ts_stack_head_count(stack), Equals(1)); - StackEntry *head = ts_stack_head(stack, 0); - AssertThat(ts_stack_entry_next_count(head), Equals(1)); - AssertThat(ts_stack_entry_next_count(ts_stack_entry_next(head, 0)), Equals(3)); + AssertThat(ts_stack_entry_next_count(ts_stack_head(stack, 0)), Equals(3)); + ts_stack_push(stack, 0, stateI, trees[10]); + AssertThat(ts_stack_entry_next_count(ts_stack_head(stack, 0)), Equals(1)); }); - describe("when there is one path that leads to three different heads", [&]() { - it("returns three entries with the same array of trees", [&]() { + describe("when there are three different paths that lead to three different heads", [&]() { + it("returns three entries with different arrays of trees", [&]() { /* - * A0__B1__C2__D3. - * \__E4__F5. - * \__G6__H7. + * . <--0-- A <--1-- B <--2-- C* + * ^ + * `---4--- E <--5-- F* + * | + * `---7--- G <--8-- H* */ StackPopResultArray results = ts_stack_pop(stack, 0, 2, false); AssertThat(ts_stack_head_count(stack), Equals(3)); AssertThat(results.size, Equals(3)); - StackPopResult pop1 = results.contents[0]; - AssertThat(ts_stack_top_tree(stack, 0), Equals(trees[3])); - AssertThat(pop1.head_index, Equals(0)); - AssertThat(pop1.trees, Equals(vector({ trees[8], trees[9] }))) + StackPopResult result1 = results.contents[0]; + AssertThat(ts_stack_top_state(stack, 0), Equals(stateC)); + AssertThat(result1.head_index, Equals(0)); + AssertThat(result1.trees, Equals(vector({ trees[3], trees[10] }))) - StackPopResult pop2 = results.contents[1]; - AssertThat(ts_stack_top_tree(stack, 1), Equals(trees[5])); - AssertThat(pop2.head_index, Equals(1)); - AssertThat(pop2.trees.size, Equals(2)); - AssertThat(pop2.trees.contents, Equals(pop1.trees.contents)); + StackPopResult result2 = results.contents[1]; + AssertThat(ts_stack_top_state(stack, 1), Equals(stateF)); + AssertThat(result2.head_index, Equals(1)); + AssertThat(result2.trees, Equals(vector({ trees[6], trees[10] }))) - StackPopResult pop3 = results.contents[2]; - AssertThat(ts_stack_top_tree(stack, 2), Equals(trees[7])); - AssertThat(pop3.head_index, Equals(2)); - AssertThat(pop3.trees.size, Equals(2)); - AssertThat(pop3.trees.contents, Equals(pop1.trees.contents)); - - free_pop_results(&results); - }); - }); - - describe("when there are three different paths that lead to three different heads", [&]() { - it("returns three entries with different arrays of trees", [&]() { - /* - * A0__B1__C2. - * \__E4. - * \__G6. - */ - StackPopResultArray results = ts_stack_pop(stack, 0, 3, false); - AssertThat(ts_stack_head_count(stack), Equals(3)); - - AssertThat(results.size, Equals(3)); - - StackPopResult pop1 = results.contents[0]; - AssertThat(ts_stack_top_tree(stack, 0), Equals(trees[2])); - AssertThat(pop1.head_index, Equals(0)); - AssertThat(pop1.trees, Equals(vector({ trees[3], trees[8], trees[9] }))) - - StackPopResult pop2 = results.contents[1]; - AssertThat(ts_stack_top_tree(stack, 1), Equals(trees[4])); - AssertThat(pop2.head_index, Equals(1)); - AssertThat(pop2.trees, Equals(vector({ trees[5], trees[8], trees[9] }))) - - StackPopResult pop3 = results.contents[2]; - AssertThat(ts_stack_top_tree(stack, 2), Equals(trees[6])); - AssertThat(pop3.head_index, Equals(2)); - AssertThat(pop3.trees, Equals(vector({ trees[7], trees[8], trees[9] }))) + StackPopResult result3 = results.contents[2]; + AssertThat(ts_stack_top_state(stack, 2), Equals(stateH)); + AssertThat(result3.head_index, Equals(2)); + AssertThat(result3.trees, Equals(vector({ trees[9], trees[10] }))) free_pop_results(&results); }); @@ -584,9 +526,21 @@ describe("Stack", [&]() { END_TEST bool operator==(const StackEntry &left, const StackEntry &right) { - return left.state == right.state && ts_tree_eq(left.tree, right.tree) && ts_length_eq(left.position, right.position); + return left.state == right.state && ts_length_eq(left.position, right.position); } std::ostream &operator<<(std::ostream &stream, const StackEntry &entry) { - return stream << "{" << entry.state << ", " << entry.tree << ", " << entry.position << "}"; + return stream << "{" << entry.state << ", " << entry.position << "}"; +} + +std::ostream &operator<<(std::ostream &stream, const TreeArray &array) { + stream << "["; + bool first = true; + for (size_t i = 0; i < array.size; i++) { + if (!first) + stream << ", "; + first = false; + stream << array.contents[i]; + } + return stream << "]"; } diff --git a/src/runtime/array.h b/src/runtime/array.h index 10a5c1a7..abe0bd64 100644 --- a/src/runtime/array.h +++ b/src/runtime/array.h @@ -40,7 +40,7 @@ extern "C" { #define array_push(self, element) \ (((self)->size < (self)->capacity || \ - array_grow((self), (self)->capacity * 2)) && \ + array_grow((self), (self)->capacity ? (self)->capacity * 2 : 4)) && \ ((self)->contents[(self)->size++] = (element), true)) #define array_splice(self, index, old_count, new_count, new_elements) \ diff --git a/src/runtime/parser.c b/src/runtime/parser.c index 0bb3cb9e..a0cbb842 100644 --- a/src/runtime/parser.c +++ b/src/runtime/parser.c @@ -71,16 +71,14 @@ static ParseActionResult ts_parser__breakdown_top_of_stack(TSParser *self, * Since only one entry (not counting extra trees) is being popped from the * stack, there should only be one possible array of removed trees. */ - StackPopResult first_result = pop_results.contents[0]; - TreeArray removed_trees = first_result.trees; - TSTree *parent = *array_front(&removed_trees); - LOG("breakdown_pop sym:%s, size:%lu", SYM_NAME(parent->symbol), - ts_tree_total_size(parent).chars); for (size_t i = 0; i < pop_results.size; i++) { StackPopResult pop_result = pop_results.contents[i]; - assert(pop_result.trees.contents == removed_trees.contents); + TreeArray removed_trees = pop_result.trees; + TSTree *parent = *array_front(&removed_trees); int head_index = pop_result.head_index; + LOG("breakdown_pop sym:%s, size:%lu", SYM_NAME(parent->symbol), + ts_tree_total_size(parent).chars); StackPushResult last_push = StackPushResultContinued; TSStateId state = ts_stack_top_state(self->stack, head_index); @@ -112,12 +110,11 @@ static ParseActionResult ts_parser__breakdown_top_of_stack(TSParser *self, assert(last_push != StackPushResultMerged); else assert(last_push == StackPushResultMerged); + + for (size_t j = 0, count = removed_trees.size; j < count; j++) + ts_tree_release(removed_trees.contents[j]); + array_delete(&removed_trees); } - - for (size_t j = 0, count = first_result.trees.size; j < count; j++) - ts_tree_release(first_result.trees.contents[j]); - array_delete(&removed_trees); - } while (last_child && last_child->child_count > 0); return UpdatedStackHead; @@ -268,13 +265,17 @@ static int ts_parser__select_tree(void *data, TSTree *left, TSTree *right) { TSParser *self = data; int comparison = ts_tree_compare(left, right); - if (comparison <= 0) { - LOG("select tree:%s, over_tree:%s", SYM_NAME(left->symbol), - SYM_NAME(right->symbol)); - } else { - LOG("select tree:%s, over_tree:%s", SYM_NAME(right->symbol), - SYM_NAME(left->symbol)); + switch (comparison) { + case -1: + LOG_ACTION("select tree:%s, over_tree:%s", SYM_NAME(left->symbol), + SYM_NAME(right->symbol)); + break; + case 1: + LOG_ACTION("select tree:%s, over_tree:%s", SYM_NAME(right->symbol), + SYM_NAME(left->symbol)); + break; } + return comparison; } @@ -339,35 +340,21 @@ static ParseActionResult ts_parser__reduce(TSParser *self, int head, */ TSTree *parent = NULL; size_t trailing_extra_count = 0; - for (size_t j = 0; j < i; j++) { - StackPopResult prior_result = pop_results.contents[j]; - if (pop_result.trees.contents == prior_result.trees.contents) { - parent = self->reduce_parents.contents[j]; - trailing_extra_count = pop_result.trees.size - parent->child_count; - ts_tree_retain(parent); - for (size_t k = parent->child_count; k < pop_result.trees.size; k++) - ts_tree_retain(pop_result.trees.contents[k]); + for (size_t j = pop_result.trees.size - 1; j + 1 > 0; j--) { + if (pop_result.trees.contents[j]->extra) + trailing_extra_count++; + else break; - } } + size_t popped_child_count = pop_result.trees.size - trailing_extra_count; + parent = ts_tree_make_node(symbol, popped_child_count, pop_result.trees.contents, + metadata); if (!parent) { - for (size_t j = pop_result.trees.size - 1; j + 1 > 0; j--) { - if (pop_result.trees.contents[j]->extra) { - trailing_extra_count++; - } else - break; - } - - size_t child_count = pop_result.trees.size - trailing_extra_count; - parent = ts_tree_make_node(symbol, child_count, pop_result.trees.contents, - metadata); - if (!parent) { - for (size_t i = 0; i < pop_result.trees.size; i++) - ts_tree_release(pop_result.trees.contents[i]); - array_delete(&pop_result.trees); - goto error; - } + for (size_t i = 0; i < pop_result.trees.size; i++) + ts_tree_release(pop_result.trees.contents[i]); + array_delete(&pop_result.trees); + goto error; } if (!array_push(&self->reduce_parents, parent)) @@ -487,7 +474,8 @@ static ParseActionResult ts_parser__reduce_error(TSParser *self, int head, default: { StackEntry *entry = ts_stack_head(self->stack, head); entry->position = ts_length_add(entry->position, lookahead->padding); - entry->tree->size = ts_length_add(entry->tree->size, lookahead->padding); + TSTree *tree = *array_front(&self->reduce_parents); + tree->size = ts_length_add(tree->size, lookahead->padding); lookahead->padding = ts_length_zero(); return UpdatedStackHead; } @@ -824,7 +812,7 @@ TSTree *ts_parser_parse(TSParser *self, TSInput input, TSTree *previous_tree) { return NULL; } - LOG("lookahead sym:%s, size:%lu", SYM_NAME(lookahead->symbol), + LOG("lookahead sym:(%s,%d), size:%lu", SYM_NAME(lookahead->symbol), lookahead->symbol, ts_tree_total_chars(lookahead)); switch (ts_parser__consume_lookahead(self, head, lookahead)) { diff --git a/src/runtime/stack.c b/src/runtime/stack.c index c98eab34..d93d3303 100644 --- a/src/runtime/stack.c +++ b/src/runtime/stack.c @@ -12,12 +12,19 @@ #define STARTING_TREE_CAPACITY 10 #define MAX_NODE_POOL_SIZE 50 -typedef struct StackNode { +typedef struct StackNode StackNode; + +typedef struct { + StackNode *node; + TSTree *tree; +} StackLink; + +struct StackNode { StackEntry entry; - struct StackNode *successors[MAX_SUCCESSOR_COUNT]; + StackLink successors[MAX_SUCCESSOR_COUNT]; short unsigned int successor_count; short unsigned int ref_count; -} StackNode; +}; typedef struct { size_t goal_tree_count; @@ -100,11 +107,6 @@ TSLength ts_stack_top_position(const Stack *self, int head) { return entry ? entry->position : ts_length_zero(); } -TSTree *ts_stack_top_tree(const Stack *self, int head) { - StackEntry *entry = ts_stack_head((Stack *)self, head); - return entry ? entry->tree : NULL; -} - StackEntry *ts_stack_head(Stack *self, int head) { StackNode *node = self->heads.contents[head]; return node ? &node->entry : NULL; @@ -119,7 +121,7 @@ int ts_stack_entry_next_count(const StackEntry *entry) { } StackEntry *ts_stack_entry_next(const StackEntry *entry, int i) { - return &((const StackNode *)entry)->successors[i]->entry; + return &((const StackNode *)entry)->successors[i].node->entry; } /* @@ -139,9 +141,10 @@ static bool stack_node_release(Stack *self, StackNode *node) { assert(node->ref_count != 0); node->ref_count--; if (node->ref_count == 0) { - for (int i = 0; i < node->successor_count; i++) - stack_node_release(self, node->successors[i]); - ts_tree_release(node->entry.tree); + for (int i = 0; i < node->successor_count; i++) { + stack_node_release(self, node->successors[i].node); + ts_tree_release(node->successors[i].tree); + } if (self->node_pool.size >= MAX_NODE_POOL_SIZE) ts_free(node); @@ -174,26 +177,12 @@ static StackNode *stack_node_new(Stack *self, StackNode *next, TSStateId state, *node = (StackNode){ .ref_count = 1, .successor_count = 1, - .successors = { next, NULL, NULL }, - .entry = {.state = state, .tree = tree, .position = position }, + .successors = { {next, tree} }, + .entry = {.state = state, .position = position }, }; return node; } -static void ts_stack__add_alternative_tree(Stack *self, StackNode *node, - TSTree *tree) { - if (tree != node->entry.tree) { - int comparison = self->tree_selection_function(self->tree_selection_payload, - node->entry.tree, tree); - - if (comparison > 0) { - ts_tree_retain(tree); - ts_tree_release(node->entry.tree); - node->entry.tree = tree; - } - } -} - static void ts_stack__clear_pop_result(Stack *self, StackPopResult *result) { for (size_t i = 0; i < result->trees.size; i++) ts_tree_release(result->trees.contents[i]); @@ -230,27 +219,31 @@ static void ts_stack__add_alternative_pop_result(Stack *self, } } -static void ts_stack__add_node_successor(Stack *self, StackNode *node, - StackNode *new_successor) { - for (int i = 0; i < node->successor_count; i++) { - StackNode *successor = node->successors[i]; - if (successor == new_successor) - return; - if (!successor) - continue; - - if (successor->entry.state == new_successor->entry.state) { - ts_stack__add_alternative_tree(self, successor, new_successor->entry.tree); - for (int j = 0; j < new_successor->successor_count; j++) - ts_stack__add_node_successor(self, successor, - new_successor->successors[j]); - return; +static void stack_node__add_successor(StackNode *self, + TSTree *new_tree, + StackNode *new_node) { + for (int i = 0; i < self->successor_count; i++) { + StackLink successor = self->successors[i]; + if (successor.tree == new_tree) { + if (successor.node == new_node) + return; + if (successor.node && new_node && + successor.node->entry.state == new_node->entry.state) { + for (int j = 0; j < new_node->successor_count; j++) { + stack_node__add_successor(successor.node, + new_node->successors[j].tree, new_node->successors[j].node); + } + return; + } } } - stack_node_retain(new_successor); - node->successors[node->successor_count] = new_successor; - node->successor_count++; + stack_node_retain(new_node); + ts_tree_retain(new_tree); + self->successors[self->successor_count++] = (StackLink){ + new_node, + new_tree, + }; } /* @@ -296,8 +289,7 @@ StackPushResult ts_stack_push(Stack *self, int head_index, TSStateId state, StackEntry prior_entry = prior_node->entry; if (prior_entry.state == state && ts_length_eq(prior_entry.position, position)) { - ts_stack__add_alternative_tree(self, prior_node, tree); - ts_stack__add_node_successor(self, prior_node, current_head); + stack_node__add_successor(prior_node, tree, current_head); ts_stack_remove_head(self, head_index); return StackPushResultMerged; } @@ -349,38 +341,43 @@ StackPopResultArray ts_stack_pop(Stack *self, int head_index, int child_count, if (!node || path->trees.size == path->goal_tree_count) continue; - all_paths_done = false; - /* - * Children that are 'extra' do not count towards the total child count. - */ - if (node->entry.tree->extra && !count_extra) - path->goal_tree_count++; - /* * If a node has more than one successor, create new paths for each of * the additional successors. */ - if (path->is_shared) { - path->trees = (TreeArray)array_copy(&path->trees); - for (size_t j = 0; j < path->trees.size; j++) - ts_tree_retain(path->trees.contents[j]); - path->is_shared = false; - } + for (int j = 0; j < node->successor_count; j++) { + StackLink successor = node->successors[j]; - ts_tree_retain(node->entry.tree); - if (!array_push(&path->trees, node->entry.tree)) - goto error; + PopPath *next_path; + if (j == 0) { + next_path = path; + } else { + if (!array_push(&self->pop_paths, *path)) + goto error; + next_path = array_back(&self->pop_paths); + next_path->is_shared = true; + } - path->node = path->node->successors[0]; - for (int j = 1; j < node->successor_count; j++) { - if (!array_push(&self->pop_paths, *path)) + if (next_path->is_shared) { + next_path->trees = (TreeArray)array_copy(&path->trees); + next_path->trees.size--; + for (size_t j = 0; j < next_path->trees.size; j++) + ts_tree_retain(next_path->trees.contents[j]); + next_path->is_shared = false; + } + + next_path->node = successor.node; + ts_tree_retain(successor.tree); + if (!array_push(&next_path->trees, successor.tree)) goto error; - PopPath *next_path = array_back(&self->pop_paths); - next_path->node = node->successors[j]; - next_path->is_shared = true; + /* + * Children that are 'extra' do not count towards the total child count. + */ + if (successor.tree->extra && !count_extra) + next_path->goal_tree_count++; } } } @@ -440,7 +437,7 @@ void ts_stack_shrink(Stack *self, int head_index, int count) { for (int i = 0; i < count; i++) { if (new_head->successor_count == 0) break; - new_head = new_head->successors[0]; + new_head = new_head->successors[0].node; } stack_node_retain(new_head); stack_node_release(self, head); @@ -475,10 +472,12 @@ void ts_stack_delete(Stack *self) { ts_free(self); } -static const char *graph_colors[] = { +static const char *COLORS[] = { "red", "blue", "orange", "green", "purple", }; +static size_t COLOR_COUNT = sizeof(COLORS) / sizeof(COLORS[0]); + size_t ts_stack__write_dot_graph(Stack *self, char *string, size_t n, const char **symbol_names) { char *cursor = string; @@ -486,15 +485,15 @@ size_t ts_stack__write_dot_graph(Stack *self, char *string, size_t n, cursor += snprintf(*s, n, "digraph stack {\n"); cursor += snprintf(*s, n, "rankdir=\"RL\";\n"); + Array(StackNode *) visited_nodes; + array_init(&visited_nodes); + array_clear(&self->pop_paths); for (size_t i = 0; i < self->heads.size; i++) { StackNode *node = self->heads.contents[i]; - const char *color = - graph_colors[i % (sizeof(graph_colors) / sizeof(graph_colors[0]))]; + const char *color = COLORS[i % COLOR_COUNT]; cursor += snprintf(*s, n, "node_%p [color=%s];\n", node, color); - array_push(&self->pop_paths, ((PopPath){ - .node = node, - })); + array_push(&self->pop_paths, ((PopPath){ .node = node })); } bool all_paths_done = false; @@ -505,38 +504,47 @@ size_t ts_stack__write_dot_graph(Stack *self, char *string, size_t n, PopPath *path = &self->pop_paths.contents[i]; StackNode *node = path->node; + for (size_t j = 0; j < visited_nodes.size; j++) { + if (visited_nodes.contents[j] == node) { + node = NULL; + break; + } + } + if (!node) continue; - all_paths_done = false; - cursor += - snprintf(*s, n, "node_%p [label=\"%s\\n%d\"];\n", node, - symbol_names[node->entry.tree->symbol], node->entry.state); + cursor += snprintf(*s, n, "node_%p [label=%d];\n", node, node->entry.state); - path->node = node->successors[0]; - cursor += - snprintf(*s, n, "node_%p -> node_%p;\n", node, node->successors[0]); + for (int j = 0; j < node->successor_count; j++) { + StackLink successor = node->successors[j]; + cursor += snprintf(*s, n, "node_%p -> node_%p [label=\"%s\"];\n", node, + successor.node, symbol_names[successor.tree->symbol]); - for (int j = 1; j < node->successor_count; j++) { - if (!array_push(&self->pop_paths, *path)) - goto error; - cursor += - snprintf(*s, n, "node_%p -> node_%p;\n", node, node->successors[j]); - - PopPath *next_path = array_back(&self->pop_paths); - next_path->node = node->successors[j]; - next_path->is_shared = true; + if (j == 0) { + path->node = successor.node; + } else { + if (!array_push(&self->pop_paths, *path)) + goto error; + PopPath *next_path = array_back(&self->pop_paths); + next_path->node = successor.node; + } } + + if (!array_push(&visited_nodes, node)) + goto error; } } - cursor += snprintf(*s, n, "node_%p [label=\"-\\n0\"];\n", NULL); + cursor += snprintf(*s, n, "node_%p [label=0];\n", NULL); cursor += snprintf(*s, n, "}\n"); + array_delete(&visited_nodes); return cursor - string; error: + array_delete(&visited_nodes); return (size_t)-1; } diff --git a/src/runtime/stack.h b/src/runtime/stack.h index ae76c2e6..55803d57 100644 --- a/src/runtime/stack.h +++ b/src/runtime/stack.h @@ -12,7 +12,6 @@ extern "C" { typedef struct Stack Stack; typedef struct { - TSTree *tree; TSStateId state; TSLength position; } StackEntry; @@ -53,12 +52,6 @@ int ts_stack_head_count(const Stack *); */ TSStateId ts_stack_top_state(const Stack *, int head); -/* - * Get the tree at given head of the stack. If the stack is empty, this - * returns NULL. - */ -TSTree *ts_stack_top_tree(const Stack *, int head); - /* * Get the position of the given head of the stack. If the stack is empty, this * returns {0, 0}.