From c0763c69c4278277937048aa45f9c7094fd7f86c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 22 May 2018 13:30:10 -0700 Subject: [PATCH] Fix handling of aliases in TreeCursor --- src/runtime/tree_cursor.c | 64 ++++++++++++++++++++++++++++----------- test/runtime/node_test.cc | 62 ++++++++++++++++++++++++++++++++----- 2 files changed, 101 insertions(+), 25 deletions(-) diff --git a/src/runtime/tree_cursor.c b/src/runtime/tree_cursor.c index 4f7e0078..c7c106b3 100644 --- a/src/runtime/tree_cursor.c +++ b/src/runtime/tree_cursor.c @@ -29,17 +29,27 @@ void ts_tree_cursor_delete(TSTreeCursor *_self) { bool ts_tree_cursor_goto_first_child(TSTreeCursor *_self) { TreeCursor *self = (TreeCursor *)_self; TreeCursorEntry *last_entry = array_back(&self->stack); - const Subtree *tree = last_entry->subtree; + const Subtree *subtree = last_entry->subtree; + if (subtree->children.size == 0 || subtree->visible_child_count == 0) return false; Length position = last_entry->position; bool did_descend; do { did_descend = false; + const TSSymbol *alias_sequence = ts_language_alias_sequence( + self->tree->language, + subtree->alias_sequence_id + ); uint32_t structural_child_index = 0; - for (uint32_t i = 0; i < tree->children.size; i++) { - const Subtree *child = tree->children.contents[i]; - if (child->visible || child->visible_child_count > 0) { + for (uint32_t i = 0; i < subtree->children.size; i++) { + const Subtree *child = subtree->children.contents[i]; + bool visible = child->visible; + if (!child->extra) { + visible |= alias_sequence && alias_sequence[structural_child_index]; + } + + if (visible || (child->children.size > 0 && child->visible_child_count > 0)) { array_push(&self->stack, ((TreeCursorEntry) { .subtree = child, .child_index = i, @@ -47,10 +57,10 @@ bool ts_tree_cursor_goto_first_child(TSTreeCursor *_self) { .position = position, })); - if (child->visible) { + if (visible) { return true; } else { - tree = child; + subtree = child; did_descend = true; break; } @@ -67,7 +77,7 @@ int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *_self, uint32_t g TreeCursor *self = (TreeCursor *)_self; uint32_t initial_size = self->stack.size; TreeCursorEntry *last_entry = array_back(&self->stack); - const Subtree *tree = last_entry->subtree; + const Subtree *subtree = last_entry->subtree; Length position = last_entry->position; uint32_t visible_child_index = 0; @@ -75,14 +85,25 @@ int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *_self, uint32_t g do { did_descend = false; + const TSSymbol *alias_sequence = ts_language_alias_sequence( + self->tree->language, + subtree->alias_sequence_id + ); uint32_t structural_child_index = 0; - for (uint32_t i = 0; i < tree->children.size; i++) { - const Subtree *child = tree->children.contents[i]; + for (uint32_t i = 0; i < subtree->children.size; i++) { + const Subtree *child = subtree->children.contents[i]; Length next_position = length_add(position, ts_subtree_total_size(child)); bool at_goal = next_position.bytes > goal_byte; + bool visible = child->visible; + if (!child->extra) { + visible |= alias_sequence && alias_sequence[structural_child_index]; + } + + uint32_t visible_child_count = child->children.size > 0 ? child->visible_child_count : 0; + if (at_goal) { - if (child->visible || child->visible_child_count > 0) { + if (visible || visible_child_count > 0) { array_push(&self->stack, ((TreeCursorEntry) { .subtree = child, .child_index = i, @@ -90,19 +111,19 @@ int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *_self, uint32_t g .position = position, })); - if (child->visible) { + if (visible) { return visible_child_index; } else { - tree = child; + subtree = child; did_descend = true; break; } } } else { - if (child->visible) { + if (visible) { visible_child_index++; } else { - visible_child_index += child->visible_child_count; + visible_child_index += visible_child_count; } } @@ -127,13 +148,22 @@ bool ts_tree_cursor_goto_next_sibling(TSTreeCursor *_self) { uint32_t structural_child_index = child_entry->structural_child_index; Length position = child_entry->position; const Subtree *child = parent->children.contents[child_index]; + const TSSymbol *alias_sequence = ts_language_alias_sequence( + self->tree->language, + parent->alias_sequence_id + ); while (++child_index < parent->children.size) { if (!child->extra) structural_child_index++; position = length_add(position, ts_subtree_total_size(child)); child = parent->children.contents[child_index]; - if (child->visible || child->visible_child_count > 0) { + bool visible = child->visible; + if (!child->extra) { + visible |= alias_sequence && alias_sequence[structural_child_index]; + } + + if (visible || (child->children.size > 0 && child->visible_child_count > 0)) { self->stack.contents[i + 1] = (TreeCursorEntry) { .subtree = child, .child_index = child_index, @@ -142,7 +172,7 @@ bool ts_tree_cursor_goto_next_sibling(TSTreeCursor *_self) { }; self->stack.size = i + 2; - if (child->visible) { + if (visible) { return true; } else { ts_tree_cursor_goto_first_child(_self); @@ -180,7 +210,7 @@ TSNode ts_tree_cursor_current_node(const TSTreeCursor *_self) { self->tree->language, parent_entry->subtree->alias_sequence_id ); - if (alias_sequence) { + if (alias_sequence && !last_entry->subtree->extra) { alias_symbol = alias_sequence[last_entry->structural_child_index]; } } diff --git a/test/runtime/node_test.cc b/test/runtime/node_test.cc index 3cbd48c8..313bec40 100644 --- a/test/runtime/node_test.cc +++ b/test/runtime/node_test.cc @@ -52,15 +52,28 @@ string grammar_with_aliases_and_extras = R"JSON({ "named": true, "content": {"type": "SYMBOL", "name": "b"} }, - {"type": "SYMBOL", "name": "b"} + { + "type": "ALIAS", + "value": "C", + "named": true, + "content": {"type": "SYMBOL", "name": "_c"} + } ] }, "b": {"type": "STRING", "value": "b"}, + + "_c": {"type": "STRING", "value": "c"}, + "comment": {"type": "STRING", "value": "..."} } })JSON"; +const TSLanguage *language_with_aliases_and_extras = load_test_language( + "aliases_and_extras", + ts_compile_grammar(grammar_with_aliases_and_extras.c_str()) +); + describe("Node", [&]() { TSParser *parser; TSTree *tree; @@ -163,16 +176,13 @@ describe("Node", [&]() { }); it("works correctly when the node contains aliased children and extras", [&]() { - TSCompileResult compile_result = ts_compile_grammar(grammar_with_aliases_and_extras.c_str()); - const TSLanguage *language = load_test_language("aliases_and_extras", compile_result); - ts_parser_set_language(parser, language); - + ts_parser_set_language(parser, language_with_aliases_and_extras); ts_tree_delete(tree); - tree = ts_parser_parse_string(parser, nullptr, "b ... b ... b", 13); + tree = ts_parser_parse_string(parser, nullptr, "b ... b ... c", 13); root_node = ts_tree_root_node(tree); char *node_string = ts_node_string(root_node); - AssertThat(node_string, Equals("(a (b) (comment) (B) (comment) (b))")); + AssertThat(node_string, Equals("(a (b) (comment) (B) (comment) (C))")); ts_free(node_string); AssertThat(ts_node_named_child_count(root_node), Equals(5u)); @@ -180,7 +190,7 @@ describe("Node", [&]() { AssertThat(ts_node_type(ts_node_named_child(root_node, 1)), Equals("comment")); AssertThat(ts_node_type(ts_node_named_child(root_node, 2)), Equals("B")); AssertThat(ts_node_type(ts_node_named_child(root_node, 3)), Equals("comment")); - AssertThat(ts_node_type(ts_node_named_child(root_node, 4)), Equals("b")); + AssertThat(ts_node_type(ts_node_named_child(root_node, 4)), Equals("C")); AssertThat( ts_node_symbol(ts_node_named_child(root_node, 0)), @@ -700,6 +710,42 @@ describe("TreeCursor", [&]() { AssertThat(ts_node_start_byte(node), Equals(array_index)); AssertThat(child_index, Equals(-1)); }); + + it("walks the tree correctly when the node contains aliased children and extras", [&]() { + ts_parser_set_language(parser, language_with_aliases_and_extras); + ts_tree_cursor_delete(&cursor); + ts_tree_delete(tree); + + tree = ts_parser_parse_string(parser, nullptr, "b ... b ... c", 13); + cursor = ts_tree_cursor_new(tree); + + TSNode node = ts_tree_cursor_current_node(&cursor); + AssertThat(ts_node_type(node), Equals("a")); + + AssertThat(ts_tree_cursor_goto_first_child(&cursor), IsTrue()); + node = ts_tree_cursor_current_node(&cursor); + AssertThat(ts_node_type(node), Equals("b")); + + AssertThat(ts_tree_cursor_goto_next_sibling(&cursor), IsTrue()); + node = ts_tree_cursor_current_node(&cursor); + AssertThat(ts_node_type(node), Equals("comment")); + + AssertThat(ts_tree_cursor_goto_next_sibling(&cursor), IsTrue()); + node = ts_tree_cursor_current_node(&cursor); + AssertThat(ts_node_type(node), Equals("B")); + + AssertThat(ts_tree_cursor_goto_next_sibling(&cursor), IsTrue()); + node = ts_tree_cursor_current_node(&cursor); + AssertThat(ts_node_type(node), Equals("comment")); + + AssertThat(ts_tree_cursor_goto_next_sibling(&cursor), IsTrue()); + node = ts_tree_cursor_current_node(&cursor); + AssertThat(ts_node_type(node), Equals("C")); + + AssertThat(ts_tree_cursor_goto_next_sibling(&cursor), IsFalse()); + AssertThat(ts_tree_cursor_goto_parent(&cursor), IsTrue()); + AssertThat(ts_tree_cursor_goto_first_child_for_byte(&cursor, 0), Equals(0)); + }); }); END_TEST