Expose lower stack nodes via pop_until() function
This callback-based API allows the parser to easily visit each interior node of the stack when searching for an error repair. It also is a better abstraction over the stack's DAG implementation than having the public functions for accessing entries and their successor entries.
This commit is contained in:
parent
bc8df9f5c5
commit
4348eb89d4
9 changed files with 417 additions and 295 deletions
|
|
@ -40,6 +40,10 @@ bool operator==(const TSNode &left, const TSNode &right) {
|
|||
return ts_node_eq(left, right);
|
||||
}
|
||||
|
||||
bool operator==(const TSLength &left, const TSLength &right) {
|
||||
return ts_length_eq(left, right);
|
||||
}
|
||||
|
||||
bool operator==(const std::vector<TSTree *> &vec, const TreeArray &array) {
|
||||
if (vec.size() != array.size)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ std::ostream &operator<<(std::ostream &stream, const TSTree *tree);
|
|||
std::ostream &operator<<(std::ostream &stream, const TSNode &node);
|
||||
std::ostream &operator<<(std::ostream &stream, const TSLength &length);
|
||||
bool operator==(const TSNode &left, const TSNode &right);
|
||||
bool operator==(const TSLength &left, const TSLength &right);
|
||||
bool operator==(const std::vector<TSTree *> &right, const TreeArray &array);
|
||||
|
||||
#endif // HELPERS_TREE_HELPERS_H_
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@
|
|||
#include "runtime/alloc.h"
|
||||
|
||||
enum {
|
||||
stateA, stateB, stateC, stateD, stateE, stateF, stateG, stateH, stateI, stateJ
|
||||
stateA = 1,
|
||||
stateB,
|
||||
stateC, stateD, stateE, stateF, stateG, stateH, stateI, stateJ
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
@ -59,6 +61,26 @@ void free_slice_array(StackSliceArray *slices) {
|
|||
}
|
||||
}
|
||||
|
||||
struct StackEntry {
|
||||
TSStateId state;
|
||||
size_t depth;
|
||||
};
|
||||
|
||||
vector<StackEntry> get_stack_entries(Stack *stack, int head_index) {
|
||||
vector<StackEntry> result;
|
||||
ts_stack_pop_until(
|
||||
stack,
|
||||
head_index,
|
||||
[](void *payload, TSStateId state, size_t depth, size_t extra_count) {
|
||||
auto entries = static_cast<vector<StackEntry> *>(payload);
|
||||
StackEntry entry = {state, depth};
|
||||
if (find(entries->begin(), entries->end(), entry) == entries->end())
|
||||
entries->push_back(entry);
|
||||
return StackIterateContinue;
|
||||
}, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
START_TEST
|
||||
|
||||
describe("Stack", [&]() {
|
||||
|
|
@ -69,7 +91,7 @@ describe("Stack", [&]() {
|
|||
TSLength tree_len = {2, 3, 0, 3};
|
||||
TSSymbolMetadata metadata = {true, true, true, true};
|
||||
|
||||
before_each([&]() {
|
||||
before_each([&]() {
|
||||
record_alloc::start();
|
||||
|
||||
stack = ts_stack_new();
|
||||
|
|
@ -97,28 +119,30 @@ describe("Stack", [&]() {
|
|||
describe("pushing entries to the stack", [&]() {
|
||||
it("adds entries to the stack", [&]() {
|
||||
AssertThat(ts_stack_head_count(stack), Equals(1));
|
||||
AssertThat(ts_stack_head(stack, 0), Equals<const StackEntry *>(nullptr));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(0));
|
||||
AssertThat(ts_stack_top_position(stack, 0), Equals(ts_length_zero()));
|
||||
|
||||
// . <──0── A*
|
||||
ts_stack_push(stack, 0, trees[0], stateA);
|
||||
const StackEntry *entry1 = ts_stack_head(stack, 0);
|
||||
AssertThat(*entry1, Equals<StackEntry>({stateA, tree_len}));
|
||||
AssertThat(ts_stack_entry_next_count(entry1), Equals(1));
|
||||
AssertThat(ts_stack_entry_next(entry1, 0), Equals<const StackEntry *>(nullptr));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(stateA));
|
||||
AssertThat(ts_stack_top_position(stack, 0), Equals(tree_len));
|
||||
|
||||
// . <──0── A <──1── B*
|
||||
ts_stack_push(stack, 0, trees[1], stateB);
|
||||
const StackEntry *entry2 = ts_stack_head(stack, 0);
|
||||
AssertThat(*entry2, Equals<StackEntry>({stateB, tree_len * 2}));
|
||||
AssertThat(ts_stack_entry_next_count(entry2), Equals(1));
|
||||
AssertThat(ts_stack_entry_next(entry2, 0), Equals(entry1));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(stateB));
|
||||
AssertThat(ts_stack_top_position(stack, 0), Equals(tree_len * 2));
|
||||
|
||||
// . <──0── A <──1── B <──2── C*
|
||||
ts_stack_push(stack, 0, trees[2], stateC);
|
||||
const StackEntry *entry3 = ts_stack_head(stack, 0);
|
||||
AssertThat(*entry3, Equals<StackEntry>({stateC, tree_len * 3}));
|
||||
AssertThat(ts_stack_entry_next_count(entry3), Equals(1));
|
||||
AssertThat(ts_stack_entry_next(entry3, 0), Equals(entry2));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(stateC));
|
||||
AssertThat(ts_stack_top_position(stack, 0), Equals(tree_len * 3));
|
||||
|
||||
AssertThat(get_stack_entries(stack, 0), Equals(vector<StackEntry>({
|
||||
{stateC, 0},
|
||||
{stateB, 1},
|
||||
{stateA, 2},
|
||||
{0, 3},
|
||||
})));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -132,23 +156,23 @@ describe("Stack", [&]() {
|
|||
|
||||
it("removes the given number of nodes from the stack", [&]() {
|
||||
// . <──0── A*
|
||||
StackPopResult pop_result = ts_stack_pop(stack, 0, 2, false);
|
||||
StackPopResult pop_result = ts_stack_pop_count(stack, 0, 2);
|
||||
AssertThat(pop_result.status, Equals(StackPopResult::StackPopSucceeded));
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(1));
|
||||
|
||||
StackSlice slice = pop_result.slices.contents[0];
|
||||
AssertThat(slice.trees, Equals(vector<TSTree *>({ trees[1], trees[2] })));
|
||||
AssertThat(*ts_stack_head(stack, 0), Equals<StackEntry>({stateA, tree_len}));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(stateA));
|
||||
free_slice_array(&pop_result.slices);
|
||||
|
||||
// .*
|
||||
pop_result = ts_stack_pop(stack, 0, 1, false);
|
||||
pop_result = ts_stack_pop_count(stack, 0, 1);
|
||||
AssertThat(pop_result.status, Equals(StackPopResult::StackPopSucceeded));
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(1));
|
||||
|
||||
slice = pop_result.slices.contents[0];
|
||||
AssertThat(slice.trees, Equals(vector<TSTree *>({ trees[0] })));
|
||||
AssertThat(ts_stack_head(stack, 0), Equals<const StackEntry *>(nullptr));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(0));
|
||||
|
||||
free_slice_array(&pop_result.slices);
|
||||
});
|
||||
|
|
@ -157,20 +181,20 @@ describe("Stack", [&]() {
|
|||
trees[1]->extra = true;
|
||||
|
||||
// .*
|
||||
StackPopResult pop_result = ts_stack_pop(stack, 0, 2, false);
|
||||
StackPopResult pop_result = ts_stack_pop_count(stack, 0, 2);
|
||||
AssertThat(pop_result.status, Equals(StackPopResult::StackPopSucceeded));
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(1));
|
||||
|
||||
StackSlice slice = pop_result.slices.contents[0];
|
||||
AssertThat(slice.trees, Equals(vector<TSTree *>({ trees[0], trees[1], trees[2] })));
|
||||
AssertThat(ts_stack_head(stack, 0), Equals<const StackEntry *>(nullptr));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(0));
|
||||
|
||||
free_slice_array(&pop_result.slices);
|
||||
});
|
||||
|
||||
it("pops the entire stack when given a negative count", [&]() {
|
||||
// .*
|
||||
StackPopResult pop_result = ts_stack_pop(stack, 0, -1, false);
|
||||
StackPopResult pop_result = ts_stack_pop_count(stack, 0, -1);
|
||||
AssertThat(pop_result.status, Equals(StackPopResult::StackPopSucceeded));
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(1));
|
||||
|
||||
|
|
@ -180,20 +204,25 @@ describe("Stack", [&]() {
|
|||
free_slice_array(&pop_result.slices);
|
||||
});
|
||||
|
||||
it("stops immediately after popping a node with the error state", [&]() {
|
||||
// . <──0── A <──1── B <──2── C <──3── ? <──4── D*
|
||||
ts_stack_push(stack, 0, trees[3], ts_parse_state_error);
|
||||
ts_stack_push(stack, 0, trees[4], stateD);
|
||||
describe("when an error state exists above the given depth", [&]() {
|
||||
it("stops popping nodes at the error", [&]() {
|
||||
// . <──0── A <──1── B <──2── C <──3── ERROR <──4── D*
|
||||
ts_stack_push(stack, 0, trees[3], ts_parse_state_error);
|
||||
ts_stack_push(stack, 0, trees[4], stateD);
|
||||
|
||||
StackPopResult pop_result = ts_stack_pop(stack, 0, 3, false);
|
||||
AssertThat(pop_result.status, Equals(StackPopResult::StackPopStoppedAtError));
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(1));
|
||||
StackPopResult pop_result = ts_stack_pop_count(stack, 0, 3);
|
||||
AssertThat(pop_result.status, Equals(StackPopResult::StackPopStoppedAtError));
|
||||
|
||||
StackSlice slice = pop_result.slices.contents[0];
|
||||
AssertThat(slice.trees, Equals(vector<TSTree *>({ trees[3], trees[4] })));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(stateC));
|
||||
AssertThat(ts_stack_head_count(stack), Equals(1));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(ts_parse_state_error));
|
||||
|
||||
free_slice_array(&pop_result.slices);
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(1));
|
||||
StackSlice slice = pop_result.slices.contents[0];
|
||||
AssertThat(slice.head_index, Equals(0));
|
||||
AssertThat(slice.trees, Equals(vector<TSTree *>({ trees[4] })));
|
||||
|
||||
free_slice_array(&pop_result.slices);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -216,11 +245,14 @@ describe("Stack", [&]() {
|
|||
// ↑
|
||||
// └─*
|
||||
ts_stack_push(stack, 0, trees[3], stateD);
|
||||
StackPopResult pop_result = ts_stack_pop(stack, 1, 1, false);
|
||||
StackPopResult pop_result = ts_stack_pop_count(stack, 1, 1);
|
||||
|
||||
AssertThat(ts_stack_head_count(stack), Equals(2));
|
||||
AssertThat(*ts_stack_head(stack, 0), Equals<StackEntry>({stateD, tree_len * 4}));
|
||||
AssertThat(*ts_stack_head(stack, 1), Equals<StackEntry>({stateB, tree_len * 2}));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(stateD));
|
||||
AssertThat(ts_stack_top_position(stack, 0), Equals(tree_len * 4));
|
||||
AssertThat(ts_stack_top_state(stack, 1), Equals(stateB));
|
||||
AssertThat(ts_stack_top_position(stack, 1), Equals(tree_len * 2));
|
||||
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(1));
|
||||
StackSlice slice = pop_result.slices.contents[0];
|
||||
AssertThat(slice.trees.size, Equals<size_t>(1));
|
||||
|
|
@ -233,8 +265,10 @@ describe("Stack", [&]() {
|
|||
ts_stack_push(stack, 1, trees[5], stateF);
|
||||
|
||||
AssertThat(ts_stack_head_count(stack), Equals(2));
|
||||
AssertThat(*ts_stack_head(stack, 0), Equals<StackEntry>({stateD, tree_len * 4}));
|
||||
AssertThat(*ts_stack_head(stack, 1), Equals<StackEntry>({stateF, tree_len * 4}));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(stateD));
|
||||
AssertThat(ts_stack_top_position(stack, 0), Equals(tree_len * 4));
|
||||
AssertThat(ts_stack_top_state(stack, 1), Equals(stateF));
|
||||
AssertThat(ts_stack_top_position(stack, 1), Equals(tree_len * 4));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -252,8 +286,20 @@ describe("Stack", [&]() {
|
|||
ts_stack_push(stack, 1, trees[5], stateF);
|
||||
|
||||
AssertThat(ts_stack_head_count(stack), Equals(2));
|
||||
AssertThat(*ts_stack_head(stack, 0), Equals<StackEntry>({stateD, tree_len * 4}));
|
||||
AssertThat(*ts_stack_head(stack, 1), Equals<StackEntry>({stateF, tree_len * 4}));
|
||||
AssertThat(get_stack_entries(stack, 0), Equals(vector<StackEntry>({
|
||||
{stateD, 0},
|
||||
{stateC, 1},
|
||||
{stateB, 2},
|
||||
{stateA, 3},
|
||||
{0, 4},
|
||||
})));
|
||||
AssertThat(get_stack_entries(stack, 1), Equals(vector<StackEntry>({
|
||||
{stateF, 0},
|
||||
{stateE, 1},
|
||||
{stateB, 2},
|
||||
{stateA, 3},
|
||||
{0, 4},
|
||||
})));
|
||||
});
|
||||
|
||||
it("merges the heads", [&]() {
|
||||
|
|
@ -264,11 +310,16 @@ describe("Stack", [&]() {
|
|||
AssertThat(ts_stack_push(stack, 1, trees[7], stateG), Equals(StackPushMerged));
|
||||
|
||||
AssertThat(ts_stack_head_count(stack), Equals(1));
|
||||
const StackEntry *entry1 = ts_stack_head(stack, 0);
|
||||
AssertThat(*entry1, Equals<StackEntry>({stateG, tree_len * 5}));
|
||||
AssertThat(ts_stack_entry_next_count(entry1), Equals(2));
|
||||
AssertThat(*ts_stack_entry_next(entry1, 0), Equals<StackEntry>({stateD, tree_len * 4}));
|
||||
AssertThat(*ts_stack_entry_next(entry1, 1), Equals<StackEntry>({stateF, tree_len * 4}));
|
||||
AssertThat(get_stack_entries(stack, 0), Equals(vector<StackEntry>({
|
||||
{stateG, 0},
|
||||
{stateD, 1},
|
||||
{stateF, 1},
|
||||
{stateC, 2},
|
||||
{stateE, 2},
|
||||
{stateB, 3},
|
||||
{stateA, 4},
|
||||
{0, 5},
|
||||
})));
|
||||
});
|
||||
|
||||
describe("when the merged nodes share a successor", [&]() {
|
||||
|
|
@ -286,13 +337,17 @@ describe("Stack", [&]() {
|
|||
AssertThat(ts_stack_push(stack, 1, trees[7], stateH), Equals(StackPushMerged));
|
||||
|
||||
AssertThat(ts_stack_head_count(stack), Equals(1));
|
||||
StackEntry *head = ts_stack_head(stack, 0);
|
||||
AssertThat(*head, Equals<StackEntry>({stateH, tree_len * 6}))
|
||||
AssertThat(ts_stack_entry_next_count(head), Equals(1));
|
||||
|
||||
StackEntry *next = ts_stack_entry_next(head, 0);
|
||||
AssertThat(*next, Equals<StackEntry>({stateG, tree_len * 5}))
|
||||
AssertThat(ts_stack_entry_next_count(next), Equals(2));
|
||||
AssertThat(get_stack_entries(stack, 0), Equals(vector<StackEntry>({
|
||||
{stateH, 0},
|
||||
{stateG, 1},
|
||||
{stateD, 2},
|
||||
{stateF, 2},
|
||||
{stateC, 3},
|
||||
{stateE, 3},
|
||||
{stateB, 4},
|
||||
{stateA, 5},
|
||||
{0, 6},
|
||||
})));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -302,9 +357,9 @@ describe("Stack", [&]() {
|
|||
ts_tree_retain(trees[3]);
|
||||
TSTree *parent = ts_tree_make_node(5, 2, tree_array({ trees[2], trees[3] }), metadata);
|
||||
|
||||
// . <──2── B <──3── C
|
||||
// . <──────5─────── C*
|
||||
// ↑ |
|
||||
// └────────5────────┘
|
||||
// └───2─── B ───3───┘
|
||||
ts_stack_clear(stack);
|
||||
ts_stack_split(stack, 0);
|
||||
AssertThat(ts_stack_push(stack, 0, parent, stateC), Equals(StackPushContinued));
|
||||
|
|
@ -312,12 +367,13 @@ describe("Stack", [&]() {
|
|||
AssertThat(ts_stack_push(stack, 1, trees[3], stateC), Equals(StackPushMerged));
|
||||
|
||||
AssertThat(ts_stack_head_count(stack), Equals(1));
|
||||
StackEntry *head = ts_stack_head(stack, 0);
|
||||
AssertThat(*head, Equals<StackEntry>({stateC, tree_len * 2}));
|
||||
|
||||
AssertThat(ts_stack_entry_next_count(head), Equals(2));
|
||||
AssertThat(ts_stack_entry_next(head, 0), Equals<StackEntry *>(nullptr));
|
||||
AssertThat(*ts_stack_entry_next(head, 1), Equals<StackEntry>({stateB, tree_len}));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(stateC));
|
||||
AssertThat(get_stack_entries(stack, 0), Equals(vector<StackEntry>({
|
||||
{stateC, 0},
|
||||
{0, 1},
|
||||
{stateB, 1},
|
||||
{0, 2},
|
||||
})));
|
||||
|
||||
ts_tree_release(parent);
|
||||
});
|
||||
|
|
@ -340,8 +396,16 @@ describe("Stack", [&]() {
|
|||
ts_stack_push(stack, 1, trees[7], stateE);
|
||||
|
||||
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));
|
||||
AssertThat(get_stack_entries(stack, 0), Equals(vector<StackEntry>({
|
||||
{stateE, 0},
|
||||
{stateD, 1},
|
||||
{stateG, 1},
|
||||
{stateC, 2},
|
||||
{stateF, 2},
|
||||
{stateB, 3},
|
||||
{stateA, 4},
|
||||
{0, 5},
|
||||
})));
|
||||
});
|
||||
|
||||
describe("when there are two paths that lead to two different heads", [&]() {
|
||||
|
|
@ -349,7 +413,7 @@ describe("Stack", [&]() {
|
|||
// . <──0── A <──1── B <──2── C*
|
||||
// ↑
|
||||
// └───5─── F*
|
||||
StackPopResult pop_result = ts_stack_pop(stack, 0, 2, false);
|
||||
StackPopResult pop_result = ts_stack_pop_count(stack, 0, 2);
|
||||
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(2));
|
||||
StackSlice slice1 = pop_result.slices.contents[0];
|
||||
|
|
@ -363,8 +427,18 @@ describe("Stack", [&]() {
|
|||
AssertThat(slice2.trees, Equals(vector<TSTree *>({ trees[6], trees[7] })));
|
||||
|
||||
AssertThat(ts_stack_head_count(stack), Equals(2));
|
||||
AssertThat(*ts_stack_head(stack, 0), Equals<StackEntry>({stateC, tree_len * 3}));
|
||||
AssertThat(*ts_stack_head(stack, 1), Equals<StackEntry>({stateF, tree_len * 3}));
|
||||
AssertThat(get_stack_entries(stack, 0), Equals(vector<StackEntry>({
|
||||
{stateC, 0},
|
||||
{stateB, 1},
|
||||
{stateA, 2},
|
||||
{0, 3},
|
||||
})));
|
||||
AssertThat(get_stack_entries(stack, 1), Equals(vector<StackEntry>({
|
||||
{stateF, 0},
|
||||
{stateB, 1},
|
||||
{stateA, 2},
|
||||
{0, 3},
|
||||
})));
|
||||
|
||||
free_slice_array(&pop_result.slices);
|
||||
});
|
||||
|
|
@ -382,7 +456,7 @@ describe("Stack", [&]() {
|
|||
// . <──0── A <──1── B <──2── C <──3── D <──4── E*
|
||||
// ↑ |
|
||||
// └───5─── F <──6── G <──7───┘
|
||||
StackPopResult pop_result = ts_stack_pop(stack, 0, 1, false);
|
||||
StackPopResult pop_result = ts_stack_pop_count(stack, 0, 1);
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(1));
|
||||
StackSlice slice1 = pop_result.slices.contents[0];
|
||||
AssertThat(slice1.head_index, Equals(0));
|
||||
|
|
@ -401,9 +475,10 @@ describe("Stack", [&]() {
|
|||
tree_selection_spy.tree_to_return = trees[2];
|
||||
|
||||
// . <──0── A <──1── B*
|
||||
StackPopResult pop_result = ts_stack_pop(stack, 0, 3, false);
|
||||
StackPopResult pop_result = ts_stack_pop_count(stack, 0, 3);
|
||||
AssertThat(ts_stack_head_count(stack), Equals(1));
|
||||
AssertThat(*ts_stack_head(stack, 0), Equals<StackEntry>({stateB, tree_len * 2}));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(stateB));
|
||||
AssertThat(ts_stack_top_position(stack, 0), Equals(tree_len * 2));
|
||||
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(1));
|
||||
StackSlice slice1 = pop_result.slices.contents[0];
|
||||
|
|
@ -419,9 +494,10 @@ describe("Stack", [&]() {
|
|||
tree_selection_spy.tree_to_return = trees[4];
|
||||
|
||||
// . <──0── A <──1── B*
|
||||
StackPopResult pop_result = ts_stack_pop(stack, 0, 3, false);
|
||||
StackPopResult pop_result = ts_stack_pop_count(stack, 0, 3);
|
||||
AssertThat(ts_stack_head_count(stack), Equals(1));
|
||||
AssertThat(*ts_stack_head(stack, 0), Equals<StackEntry>({stateB, tree_len * 2}));
|
||||
AssertThat(ts_stack_top_state(stack, 0), Equals(stateB));
|
||||
AssertThat(ts_stack_top_position(stack, 0), Equals(tree_len * 2));
|
||||
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(1));
|
||||
StackSlice slice1 = pop_result.slices.contents[0];
|
||||
|
|
@ -454,10 +530,21 @@ describe("Stack", [&]() {
|
|||
ts_stack_push(stack, 1, trees[7], stateG);
|
||||
ts_stack_push(stack, 1, trees[8], stateH);
|
||||
ts_stack_push(stack, 1, trees[9], stateD);
|
||||
AssertThat(ts_stack_head_count(stack), Equals(1));
|
||||
AssertThat(ts_stack_entry_next_count(ts_stack_head(stack, 0)), Equals(3));
|
||||
ts_stack_push(stack, 0, trees[10], stateI);
|
||||
AssertThat(ts_stack_entry_next_count(ts_stack_head(stack, 0)), Equals(1));
|
||||
|
||||
AssertThat(ts_stack_head_count(stack), Equals(1));
|
||||
AssertThat(get_stack_entries(stack, 0), Equals(vector<StackEntry>({
|
||||
{stateI, 0},
|
||||
{stateD, 1},
|
||||
{stateC, 2},
|
||||
{stateF, 2},
|
||||
{stateH, 2},
|
||||
{stateB, 3},
|
||||
{stateE, 3},
|
||||
{stateG, 3},
|
||||
{stateA, 4},
|
||||
{0, 5},
|
||||
})));
|
||||
});
|
||||
|
||||
describe("when there are three different paths that lead to three different heads", [&]() {
|
||||
|
|
@ -467,7 +554,7 @@ describe("Stack", [&]() {
|
|||
// ├───4─── E <──5── F*
|
||||
// |
|
||||
// └───7─── G <──8── H*
|
||||
StackPopResult pop_result = ts_stack_pop(stack, 0, 2, false);
|
||||
StackPopResult pop_result = ts_stack_pop_count(stack, 0, 2);
|
||||
AssertThat(ts_stack_head_count(stack), Equals(3));
|
||||
|
||||
AssertThat(pop_result.slices.size, Equals<size_t>(3));
|
||||
|
|
@ -496,11 +583,11 @@ describe("Stack", [&]() {
|
|||
END_TEST
|
||||
|
||||
bool operator==(const StackEntry &left, const StackEntry &right) {
|
||||
return left.state == right.state && ts_length_eq(left.position, right.position);
|
||||
return left.state == right.state && left.depth == right.depth;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, const StackEntry &entry) {
|
||||
return stream << "{" << entry.state << ", " << entry.position << "}";
|
||||
return stream << "{" << entry.state << ", " << entry.depth << "}";
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, const TreeArray &array) {
|
||||
|
|
|
|||
|
|
@ -337,7 +337,3 @@ describe("Tree", []() {
|
|||
});
|
||||
|
||||
END_TEST
|
||||
|
||||
bool operator==(TSLength left, TSLength right) {
|
||||
return ts_length_eq(left, right);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ extern "C" {
|
|||
#define array_init(self) \
|
||||
((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL)
|
||||
|
||||
#define array_new() \
|
||||
{ NULL, 0, 0 }
|
||||
|
||||
#define array_get(self, index) \
|
||||
(assert((size_t)index < (self)->size), &(self)->contents[index])
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@
|
|||
#define STARTING_TREE_CAPACITY 10
|
||||
#define MAX_NODE_POOL_SIZE 50
|
||||
|
||||
#if __has_attribute(always_inline)
|
||||
#define ALWAYS_INLINE __attribute__((always_inline))
|
||||
#else
|
||||
#define ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
typedef struct StackNode StackNode;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -20,19 +26,25 @@ typedef struct {
|
|||
} StackLink;
|
||||
|
||||
struct StackNode {
|
||||
StackEntry entry;
|
||||
TSStateId state;
|
||||
TSLength position;
|
||||
StackLink successors[MAX_SUCCESSOR_COUNT];
|
||||
short unsigned int successor_count;
|
||||
short unsigned int ref_count;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
size_t goal_tree_count;
|
||||
StackNode *node;
|
||||
TreeArray trees;
|
||||
bool is_shared;
|
||||
size_t extra_count;
|
||||
StackNode *node;
|
||||
bool is_done;
|
||||
} PopPath;
|
||||
|
||||
typedef struct {
|
||||
int goal_tree_count;
|
||||
bool found_error;
|
||||
} StackPopSession;
|
||||
|
||||
typedef Array(StackNode *) StackNodeArray;
|
||||
|
||||
struct Stack {
|
||||
|
|
@ -42,8 +54,92 @@ struct Stack {
|
|||
StackNodeArray node_pool;
|
||||
void *tree_selection_payload;
|
||||
TreeSelectionFunction tree_selection_function;
|
||||
StackNode *base_node;
|
||||
};
|
||||
|
||||
static const char *COLORS[] = {
|
||||
"red", "blue", "orange", "green", "purple",
|
||||
};
|
||||
|
||||
/*
|
||||
* Section: Manipulating nodes (Private)
|
||||
*/
|
||||
|
||||
static void stack_node_retain(StackNode *self) {
|
||||
if (!self)
|
||||
return;
|
||||
assert(self->ref_count != 0);
|
||||
self->ref_count++;
|
||||
}
|
||||
|
||||
static void stack_node_release(StackNode *self, StackNodeArray *pool) {
|
||||
if (!self)
|
||||
return;
|
||||
assert(self->ref_count != 0);
|
||||
self->ref_count--;
|
||||
if (self->ref_count == 0) {
|
||||
for (int i = 0; i < self->successor_count; i++) {
|
||||
ts_tree_release(self->successors[i].tree);
|
||||
stack_node_release(self->successors[i].node, pool);
|
||||
}
|
||||
|
||||
if (pool->size >= MAX_NODE_POOL_SIZE)
|
||||
ts_free(self);
|
||||
else
|
||||
array_push(pool, self);
|
||||
}
|
||||
}
|
||||
|
||||
static StackNode *stack_node_new(StackNode *next, TSTree *tree, TSStateId state,
|
||||
TSLength position, StackNodeArray *pool) {
|
||||
StackNode *node;
|
||||
if (pool->size > 0)
|
||||
node = array_pop(pool);
|
||||
else if (!(node = ts_malloc(sizeof(StackNode))))
|
||||
return NULL;
|
||||
|
||||
*node = (StackNode){
|
||||
.ref_count = 1,
|
||||
.successor_count = 0,
|
||||
.successors = {},
|
||||
.state = state,
|
||||
.position = position,
|
||||
};
|
||||
|
||||
if (next) {
|
||||
ts_tree_retain(tree);
|
||||
stack_node_retain(next);
|
||||
node->successor_count = 1, node->successors[0] = (StackLink){ next, tree };
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
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->state == new_node->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_node);
|
||||
ts_tree_retain(new_tree);
|
||||
self->successors[self->successor_count++] = (StackLink){
|
||||
new_node, new_tree,
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Section: Stack lifecycle
|
||||
*/
|
||||
|
|
@ -76,7 +172,13 @@ Stack *ts_stack_new() {
|
|||
if (!array_grow(&self->node_pool, 20))
|
||||
goto error;
|
||||
|
||||
array_push(&self->heads, NULL);
|
||||
self->base_node =
|
||||
stack_node_new(NULL, NULL, 0, ts_length_zero(), &self->node_pool);
|
||||
stack_node_retain(self->base_node);
|
||||
if (!self->base_node)
|
||||
goto error;
|
||||
|
||||
array_push(&self->heads, self->base_node);
|
||||
|
||||
return self;
|
||||
|
||||
|
|
@ -100,92 +202,17 @@ error:
|
|||
*/
|
||||
|
||||
TSStateId ts_stack_top_state(const Stack *self, int head_index) {
|
||||
StackEntry *entry = ts_stack_head((Stack *)self, head_index);
|
||||
return entry ? entry->state : 0;
|
||||
return (*array_get(&self->heads, head_index))->state;
|
||||
}
|
||||
|
||||
TSLength ts_stack_top_position(const Stack *self, int head_index) {
|
||||
StackEntry *entry = ts_stack_head((Stack *)self, head_index);
|
||||
return entry ? entry->position : ts_length_zero();
|
||||
}
|
||||
|
||||
StackEntry *ts_stack_head(Stack *self, int head_index) {
|
||||
StackNode *node = self->heads.contents[head_index];
|
||||
return node ? &node->entry : NULL;
|
||||
return (*array_get(&self->heads, head_index))->position;
|
||||
}
|
||||
|
||||
int ts_stack_head_count(const Stack *self) {
|
||||
return self->heads.size;
|
||||
}
|
||||
|
||||
int ts_stack_entry_next_count(const StackEntry *entry) {
|
||||
return ((const StackNode *)entry)->successor_count;
|
||||
}
|
||||
|
||||
StackEntry *ts_stack_entry_next(const StackEntry *entry, int successor_index) {
|
||||
return &((const StackNode *)entry)->successors[successor_index].node->entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Section: Manipulating nodes (Private)
|
||||
*/
|
||||
|
||||
static void stack_node_retain(StackNode *self) {
|
||||
if (!self)
|
||||
return;
|
||||
assert(self->ref_count != 0);
|
||||
self->ref_count++;
|
||||
}
|
||||
|
||||
static void stack_node_release(StackNode *self, StackNodeArray *pool) {
|
||||
if (!self)
|
||||
return;
|
||||
assert(self->ref_count != 0);
|
||||
self->ref_count--;
|
||||
if (self->ref_count == 0) {
|
||||
for (int i = 0; i < self->successor_count; i++) {
|
||||
stack_node_release(self->successors[i].node, pool);
|
||||
ts_tree_release(self->successors[i].tree);
|
||||
}
|
||||
|
||||
if (pool->size >= MAX_NODE_POOL_SIZE)
|
||||
ts_free(self);
|
||||
else
|
||||
array_push(pool, self);
|
||||
}
|
||||
}
|
||||
|
||||
static StackNode *stack_node_new(StackNode *next, TSTree *tree, TSStateId state,
|
||||
StackNodeArray *pool) {
|
||||
assert(tree->ref_count > 0);
|
||||
StackNode *node;
|
||||
if (pool->size == 0) {
|
||||
node = ts_malloc(sizeof(StackNode));
|
||||
if (!node)
|
||||
return NULL;
|
||||
} else {
|
||||
node = array_pop(pool);
|
||||
}
|
||||
|
||||
ts_tree_retain(tree);
|
||||
stack_node_retain(next);
|
||||
TSLength position = ts_tree_total_size(tree);
|
||||
if (next)
|
||||
position = ts_length_add(next->entry.position, position);
|
||||
*node = (StackNode){
|
||||
.ref_count = 1,
|
||||
.successor_count = 1,
|
||||
.successors = { { next, tree } },
|
||||
.entry = {.state = state, .position = position },
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
static void ts_stack_slice__clear(StackSlice *slice) {
|
||||
ts_tree_array_clear(&slice->trees);
|
||||
array_delete(&slice->trees);
|
||||
}
|
||||
|
||||
static void ts_stack__merge_slice(Stack *self, StackSlice *slice,
|
||||
StackSlice *new_slice) {
|
||||
bool should_update = false;
|
||||
|
|
@ -207,39 +234,14 @@ static void ts_stack__merge_slice(Stack *self, StackSlice *slice,
|
|||
}
|
||||
|
||||
if (should_update) {
|
||||
ts_stack_slice__clear(slice);
|
||||
ts_tree_array_delete(&slice->trees);
|
||||
slice->trees = new_slice->trees;
|
||||
slice->trees.size = new_slice->trees.size;
|
||||
} else {
|
||||
ts_stack_slice__clear(new_slice);
|
||||
ts_tree_array_delete(&new_slice->trees);
|
||||
}
|
||||
}
|
||||
|
||||
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_node);
|
||||
ts_tree_retain(new_tree);
|
||||
self->successors[self->successor_count++] = (StackLink){
|
||||
new_node, new_tree,
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Section: Mutating the stack (Private)
|
||||
*/
|
||||
|
|
@ -253,7 +255,7 @@ static int ts_stack__add_head(Stack *self, StackNode *node) {
|
|||
}
|
||||
}
|
||||
|
||||
static int ts_stack__find_head(Stack *self, StackNode *node) {
|
||||
static int ts_stack__index_of_head(Stack *self, StackNode *node) {
|
||||
for (size_t i = 0; i < self->heads.size; i++) {
|
||||
if (self->heads.contents[i] == node)
|
||||
return i;
|
||||
|
|
@ -273,24 +275,22 @@ void ts_stack_remove_head(Stack *self, int head_index) {
|
|||
|
||||
StackPushResult ts_stack_push(Stack *self, int head_index, TSTree *tree,
|
||||
TSStateId state) {
|
||||
TSLength position = ts_tree_total_size(tree);
|
||||
StackNode *current_head = *array_get(&self->heads, head_index);
|
||||
if (current_head)
|
||||
position = ts_length_add(current_head->entry.position, position);
|
||||
TSLength position =
|
||||
ts_length_add(current_head->position, ts_tree_total_size(tree));
|
||||
|
||||
for (int i = 0; i < head_index; i++) {
|
||||
StackNode *prior_node = self->heads.contents[i];
|
||||
StackEntry prior_entry = prior_node->entry;
|
||||
if (prior_entry.state == state &&
|
||||
ts_length_eq(prior_entry.position, position)) {
|
||||
stack_node__add_successor(prior_node, tree, current_head);
|
||||
if (prior_node->state == state &&
|
||||
prior_node->position.chars == position.chars) {
|
||||
stack_node_add_successor(prior_node, tree, current_head);
|
||||
ts_stack_remove_head(self, head_index);
|
||||
return StackPushMerged;
|
||||
}
|
||||
}
|
||||
|
||||
StackNode *new_head =
|
||||
stack_node_new(current_head, tree, state, &self->node_pool);
|
||||
stack_node_new(current_head, tree, state, position, &self->node_pool);
|
||||
if (!new_head)
|
||||
return StackPushFailed;
|
||||
|
||||
|
|
@ -304,46 +304,51 @@ int ts_stack_split(Stack *self, int head_index) {
|
|||
return ts_stack__add_head(self, head);
|
||||
}
|
||||
|
||||
StackPopResult ts_stack_pop(Stack *self, int head_index, int child_count,
|
||||
bool count_extra) {
|
||||
static inline ALWAYS_INLINE StackSliceArray stack__pop(
|
||||
Stack *self, int head_index, StackIterateCallback callback, void *payload) {
|
||||
array_clear(&self->slices);
|
||||
array_clear(&self->pop_paths);
|
||||
StackNode *initial_head = *array_get(&self->heads, head_index);
|
||||
|
||||
StackNode *previous_head = *array_get(&self->heads, head_index);
|
||||
int capacity = (child_count == -1) ? STARTING_TREE_CAPACITY : child_count;
|
||||
PopPath initial_path = {
|
||||
.goal_tree_count = child_count, .node = previous_head, .is_shared = false,
|
||||
PopPath pop_path = {
|
||||
.node = initial_head, .trees = array_new(), .extra_count = 0, .is_done = false,
|
||||
};
|
||||
array_init(&initial_path.trees);
|
||||
|
||||
if (!array_grow(&initial_path.trees, capacity))
|
||||
if (!array_grow(&pop_path.trees, STARTING_TREE_CAPACITY))
|
||||
goto error;
|
||||
if (!array_push(&self->pop_paths, pop_path))
|
||||
goto error;
|
||||
|
||||
if (!array_push(&self->pop_paths, initial_path))
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* Reduce along every possible path in parallel. Stop when the given number
|
||||
* of child trees have been collected along every path.
|
||||
*/
|
||||
bool all_paths_done = false;
|
||||
int status = StackPopSucceeded;
|
||||
while (!all_paths_done) {
|
||||
for (size_t depth = 0; !all_paths_done; depth++) {
|
||||
all_paths_done = true;
|
||||
|
||||
for (size_t i = 0; i < self->pop_paths.size; i++) {
|
||||
for (size_t i = 0, size = self->pop_paths.size; i < size; i++) {
|
||||
PopPath *path = &self->pop_paths.contents[i];
|
||||
StackNode *node = path->node;
|
||||
|
||||
if (!node || path->trees.size == path->goal_tree_count)
|
||||
if (path->is_done)
|
||||
continue;
|
||||
|
||||
StackNode *node = path->node;
|
||||
size_t successor_count = node->successor_count;
|
||||
switch (callback(payload, node->state, depth, path->extra_count)) {
|
||||
case StackIteratePop:
|
||||
path->is_done = true;
|
||||
continue;
|
||||
case StackIterateAbort:
|
||||
successor_count = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!successor_count) {
|
||||
ts_tree_array_delete(&path->trees);
|
||||
array_erase(&self->pop_paths, i--);
|
||||
size--;
|
||||
continue;
|
||||
}
|
||||
|
||||
all_paths_done = false;
|
||||
|
||||
/*
|
||||
* If a node has more than one successor, create new paths for each of
|
||||
* the additional successors.
|
||||
*/
|
||||
for (int j = 0; j < node->successor_count; j++) {
|
||||
for (size_t j = 0; j < successor_count; j++) {
|
||||
StackLink successor = node->successors[j];
|
||||
|
||||
PopPath *next_path;
|
||||
|
|
@ -353,51 +358,36 @@ StackPopResult ts_stack_pop(Stack *self, int head_index, int child_count,
|
|||
if (!array_push(&self->pop_paths, *path))
|
||||
goto error;
|
||||
next_path = array_back(&self->pop_paths);
|
||||
next_path->is_shared = true;
|
||||
next_path->trees.size--;
|
||||
next_path->trees = ts_tree_array_copy(&next_path->trees);
|
||||
}
|
||||
|
||||
if (next_path->is_shared) {
|
||||
TreeArray trees = path->trees;
|
||||
trees.size--;
|
||||
next_path->trees = ts_tree_array_copy(&trees);
|
||||
next_path->is_shared = false;
|
||||
}
|
||||
|
||||
if (!count_extra && successor.tree->extra)
|
||||
next_path->goal_tree_count++;
|
||||
|
||||
ts_tree_retain(successor.tree);
|
||||
if (!array_push(&next_path->trees, successor.tree))
|
||||
goto error;
|
||||
|
||||
next_path->node = successor.node;
|
||||
if (!count_extra && node->entry.state == ts_parse_state_error) {
|
||||
status = StackPopStoppedAtError;
|
||||
next_path->goal_tree_count = next_path->trees.size;
|
||||
}
|
||||
if (!array_push(&next_path->trees, successor.tree))
|
||||
goto error;
|
||||
if (successor.tree->extra)
|
||||
next_path->extra_count++;
|
||||
ts_tree_retain(successor.tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < self->pop_paths.size; i++) {
|
||||
PopPath *path = &self->pop_paths.contents[i];
|
||||
if (!path->is_done)
|
||||
continue;
|
||||
|
||||
if (!path->is_shared)
|
||||
array_reverse(&path->trees);
|
||||
|
||||
StackSlice slice = {
|
||||
.trees = path->trees, .head_index = -1,
|
||||
};
|
||||
StackSlice slice = {.trees = path->trees, .head_index = -1 };
|
||||
array_reverse(&slice.trees);
|
||||
|
||||
if (i == 0) {
|
||||
stack_node_retain(path->node);
|
||||
self->heads.contents[head_index] = path->node;
|
||||
slice.head_index = head_index;
|
||||
} else {
|
||||
slice.head_index = ts_stack__find_head(self, path->node);
|
||||
slice.head_index = ts_stack__index_of_head(self, path->node);
|
||||
if (slice.head_index == -1) {
|
||||
slice.head_index = ts_stack__add_head(self, path->node);
|
||||
if (slice.head_index == -1)
|
||||
if ((slice.head_index = ts_stack__add_head(self, path->node)) == -1)
|
||||
goto error;
|
||||
} else {
|
||||
bool merged = false;
|
||||
|
|
@ -418,12 +408,62 @@ StackPopResult ts_stack_pop(Stack *self, int head_index, int child_count,
|
|||
goto error;
|
||||
}
|
||||
|
||||
stack_node_release(previous_head, &self->node_pool);
|
||||
return (StackPopResult){ .status = status, .slices = self->slices };
|
||||
if (self->slices.size)
|
||||
stack_node_release(initial_head, &self->node_pool);
|
||||
return self->slices;
|
||||
|
||||
error:
|
||||
array_delete(&initial_path.trees);
|
||||
return (StackPopResult){StackPopFailed, self->slices};
|
||||
for (size_t i = 0; i < self->pop_paths.size; i++)
|
||||
array_delete(&self->pop_paths.contents[i].trees);
|
||||
array_clear(&self->slices);
|
||||
return self->slices;
|
||||
}
|
||||
|
||||
static inline ALWAYS_INLINE StackIterateAction stack__pop_count_callback(
|
||||
void *payload, TSStateId state, size_t depth, size_t extra_count) {
|
||||
StackPopSession *pop_session = (StackPopSession *)payload;
|
||||
if (pop_session->found_error)
|
||||
return StackIterateAbort;
|
||||
|
||||
if (state == ts_parse_state_error && pop_session->goal_tree_count > 0) {
|
||||
pop_session->found_error = true;
|
||||
return StackIteratePop;
|
||||
}
|
||||
|
||||
if ((int)(depth - extra_count) == pop_session->goal_tree_count)
|
||||
return StackIteratePop;
|
||||
|
||||
if (state == 0 && depth > 0)
|
||||
return StackIteratePop;
|
||||
|
||||
return StackIterateContinue;
|
||||
}
|
||||
|
||||
StackPopResult ts_stack_pop_count(Stack *self, int head_index, int count) {
|
||||
StackPopSession session = {
|
||||
.goal_tree_count = count,
|
||||
.found_error = false,
|
||||
};
|
||||
|
||||
StackSliceArray slices =
|
||||
stack__pop(self, head_index, stack__pop_count_callback, &session);
|
||||
int status;
|
||||
if (slices.size) {
|
||||
if (session.found_error)
|
||||
status = StackPopStoppedAtError;
|
||||
else
|
||||
status = StackPopSucceeded;
|
||||
} else {
|
||||
status = StackPopFailed;
|
||||
}
|
||||
|
||||
return (StackPopResult){.status = status, .slices = slices };
|
||||
}
|
||||
|
||||
StackPopResult ts_stack_pop_until(Stack *self, int head_index,
|
||||
StackIterateCallback callback, void *payload) {
|
||||
StackSliceArray slices = stack__pop(self, head_index, callback, payload);
|
||||
return (StackPopResult){.status = StackPopSucceeded, .slices = slices };
|
||||
}
|
||||
|
||||
void ts_stack_shrink(Stack *self, int head_index, int count) {
|
||||
|
|
@ -440,10 +480,11 @@ void ts_stack_shrink(Stack *self, int head_index, int count) {
|
|||
}
|
||||
|
||||
void ts_stack_clear(Stack *self) {
|
||||
stack_node_retain(self->base_node);
|
||||
for (size_t i = 0; i < self->heads.size; i++)
|
||||
stack_node_release(self->heads.contents[i], &self->node_pool);
|
||||
array_clear(&self->heads);
|
||||
array_push(&self->heads, NULL);
|
||||
array_push(&self->heads, self->base_node);
|
||||
}
|
||||
|
||||
void ts_stack_set_tree_selection_callback(Stack *self, void *payload,
|
||||
|
|
@ -457,7 +498,10 @@ void ts_stack_delete(Stack *self) {
|
|||
array_delete(&self->slices);
|
||||
if (self->pop_paths.contents)
|
||||
array_delete(&self->pop_paths);
|
||||
ts_stack_clear(self);
|
||||
stack_node_release(self->base_node, &self->node_pool);
|
||||
for (size_t i = 0; i < self->heads.size; i++)
|
||||
stack_node_release(self->heads.contents[i], &self->node_pool);
|
||||
array_clear(&self->heads);
|
||||
if (self->node_pool.contents) {
|
||||
for (size_t i = 0; i < self->node_pool.size; i++)
|
||||
ts_free(self->node_pool.contents[i]);
|
||||
|
|
@ -467,12 +511,6 @@ void ts_stack_delete(Stack *self) {
|
|||
ts_free(self);
|
||||
}
|
||||
|
||||
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,7 +524,8 @@ size_t ts_stack__write_dot_graph(Stack *self, char *string, size_t n,
|
|||
array_clear(&self->pop_paths);
|
||||
for (size_t i = 0; i < self->heads.size; i++) {
|
||||
StackNode *node = self->heads.contents[i];
|
||||
const char *color = COLORS[i % COLOR_COUNT];
|
||||
size_t color_count = sizeof(COLORS) / sizeof(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 }));
|
||||
}
|
||||
|
|
@ -511,10 +550,10 @@ size_t ts_stack__write_dot_graph(Stack *self, char *string, size_t n,
|
|||
all_paths_done = false;
|
||||
|
||||
cursor += snprintf(*s, n, "node_%p [label=", node);
|
||||
if (node->entry.state == ts_parse_state_error)
|
||||
if (node->state == ts_parse_state_error)
|
||||
cursor += snprintf(*s, n, "\"?\"");
|
||||
else
|
||||
cursor += snprintf(*s, n, "%d", node->entry.state);
|
||||
cursor += snprintf(*s, n, "%d", node->state);
|
||||
cursor += snprintf(*s, n, "];\n");
|
||||
|
||||
for (int j = 0; j < node->successor_count; j++) {
|
||||
|
|
@ -553,7 +592,6 @@ size_t ts_stack__write_dot_graph(Stack *self, char *string, size_t n,
|
|||
}
|
||||
}
|
||||
|
||||
cursor += snprintf(*s, n, "node_%p [label=0];\n", NULL);
|
||||
cursor += snprintf(*s, n, "}\n");
|
||||
|
||||
array_delete(&visited_nodes);
|
||||
|
|
|
|||
|
|
@ -11,11 +11,6 @@ extern "C" {
|
|||
|
||||
typedef struct Stack Stack;
|
||||
|
||||
typedef struct {
|
||||
TSStateId state;
|
||||
TSLength position;
|
||||
} StackEntry;
|
||||
|
||||
typedef struct {
|
||||
TreeArray trees;
|
||||
int head_index;
|
||||
|
|
@ -38,7 +33,17 @@ typedef struct {
|
|||
StackSliceArray slices;
|
||||
} StackPopResult;
|
||||
|
||||
typedef int (*TreeSelectionFunction)(void *, TSTree *, TSTree *);
|
||||
typedef enum {
|
||||
StackIterateContinue,
|
||||
StackIterateAbort,
|
||||
StackIteratePop,
|
||||
} StackIterateAction;
|
||||
|
||||
typedef StackIterateAction (*StackIterateCallback)(void *, TSStateId state,
|
||||
size_t depth,
|
||||
size_t extra_count);
|
||||
|
||||
typedef int (*TreeSelectionFunction)(void *, TSTree *tree1, TSTree *tree2);
|
||||
|
||||
/*
|
||||
* Create a parse stack.
|
||||
|
|
@ -67,21 +72,6 @@ TSStateId ts_stack_top_state(const Stack *, int head_index);
|
|||
*/
|
||||
TSLength ts_stack_top_position(const Stack *, int head_index);
|
||||
|
||||
/*
|
||||
* Get the entry at the given head of the stack.
|
||||
*/
|
||||
StackEntry *ts_stack_head(Stack *, int head_index);
|
||||
|
||||
/*
|
||||
* Get the number of successors for the parse stack entry.
|
||||
*/
|
||||
int ts_stack_entry_next_count(const StackEntry *);
|
||||
|
||||
/*
|
||||
* Get the given successor for the parse stack entry.
|
||||
*/
|
||||
StackEntry *ts_stack_entry_next(const StackEntry *, int head_index);
|
||||
|
||||
/*
|
||||
* Push a (tree, state) pair onto the given head of the stack. This could cause
|
||||
* the head to merge with an existing head.
|
||||
|
|
@ -94,7 +84,10 @@ StackPushResult ts_stack_push(Stack *, int head_index, TSTree *, TSStateId);
|
|||
* which had previously been merged. It returns a struct that indicates the
|
||||
* index of each revealed head and the trees removed from that head.
|
||||
*/
|
||||
StackPopResult ts_stack_pop(Stack *, int head_index, int count, bool count_extra);
|
||||
StackPopResult ts_stack_pop_count(Stack *, int head_index, int count);
|
||||
|
||||
StackPopResult ts_stack_pop_until(Stack *, int head_index, StackIterateCallback,
|
||||
void *);
|
||||
|
||||
/*
|
||||
* Remove the given number of entries from the given head of the stack.
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ TreeArray ts_tree_array_copy(TreeArray *self) {
|
|||
return result;
|
||||
}
|
||||
|
||||
void ts_tree_array_clear(TreeArray *self) {
|
||||
void ts_tree_array_delete(TreeArray *self) {
|
||||
for (size_t i = 0; i < self->size; i++)
|
||||
ts_tree_release(self->contents[i]);
|
||||
array_clear(self);
|
||||
array_delete(self);
|
||||
}
|
||||
|
||||
TSTree *ts_tree_make_error(TSLength size, TSLength padding, char lookahead_char) {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ struct TSTree {
|
|||
|
||||
typedef Array(TSTree *) TreeArray;
|
||||
TreeArray ts_tree_array_copy(TreeArray *);
|
||||
void ts_tree_array_clear(TreeArray *);
|
||||
void ts_tree_array_delete(TreeArray *);
|
||||
|
||||
TSTree *ts_tree_make_leaf(TSSymbol, TSLength, TSLength, TSSymbolMetadata);
|
||||
TSTree *ts_tree_make_node(TSSymbol, size_t, TSTree **, TSSymbolMetadata);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue