From 245daffbc428d2d52e44c43bb9db102f7e97580b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 8 Sep 2015 21:43:37 -0700 Subject: [PATCH] Add {next,prev}_concrete_sibling Node methods --- include/tree_sitter/runtime.h | 4 + spec/runtime/helpers/tree_helpers.cc | 6 +- spec/runtime/helpers/tree_helpers.h | 2 +- spec/runtime/node_spec.cc | 68 ++++++++++- src/runtime/node.c | 167 ++++++++++++++++----------- src/runtime/tree.c | 102 ++++++---------- src/runtime/tree.h | 1 + 7 files changed, 208 insertions(+), 142 deletions(-) diff --git a/include/tree_sitter/runtime.h b/include/tree_sitter/runtime.h index 91773703..20cdd515 100644 --- a/include/tree_sitter/runtime.h +++ b/include/tree_sitter/runtime.h @@ -55,9 +55,13 @@ TSNode ts_node_concrete_child(TSNode, size_t); size_t ts_node_concrete_child_count(TSNode); TSNode ts_node_find_for_pos(TSNode, size_t); TSNode ts_node_find_for_range(TSNode, size_t, size_t); +TSNode ts_node_find_concrete_for_pos(TSNode, size_t); +TSNode ts_node_find_concrete_for_range(TSNode, size_t, size_t); TSNode ts_node_parent(TSNode); TSNode ts_node_next_sibling(TSNode); TSNode ts_node_prev_sibling(TSNode); +TSNode ts_node_next_concrete_sibling(TSNode); +TSNode ts_node_prev_concrete_sibling(TSNode); const char *ts_node_name(TSNode, const TSDocument *); const char *ts_node_string(TSNode, const TSDocument *); bool ts_node_eq(TSNode, TSNode); diff --git a/spec/runtime/helpers/tree_helpers.cc b/spec/runtime/helpers/tree_helpers.cc index 92705179..6b2aa53c 100644 --- a/spec/runtime/helpers/tree_helpers.cc +++ b/spec/runtime/helpers/tree_helpers.cc @@ -1,8 +1,10 @@ #include "runtime/helpers/tree_helpers.h" -const char *symbol_names[12] = { +const char *symbol_names[24] = { "ERROR", "END", "two", "three", "four", "five", "six", "seven", "eight", - "nine", "ten", "eleven", + "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", + "sixteen", "seventeen", "eighteen", "nineteen", "twenty", "twenty-one", + "twenty-two", "twenty-three" }; TSTree ** tree_array(std::vector trees) { diff --git a/spec/runtime/helpers/tree_helpers.h b/spec/runtime/helpers/tree_helpers.h index 433a64ca..2e81fc8b 100644 --- a/spec/runtime/helpers/tree_helpers.h +++ b/spec/runtime/helpers/tree_helpers.h @@ -5,7 +5,7 @@ #include #include -extern const char *symbol_names[12]; +extern const char *symbol_names[24]; TSTree ** tree_array(std::vector trees); diff --git a/spec/runtime/node_spec.cc b/spec/runtime/node_spec.cc index be079df2..aed37058 100644 --- a/spec/runtime/node_spec.cc +++ b/spec/runtime/node_spec.cc @@ -130,7 +130,59 @@ describe("Node", []() { }); }); - describe("next_sibling() and prev_sibling()", [&]() { + describe("next_concrete_sibling(), prev_concrete_sibling()", [&]() { + it("returns the node's next and previous sibling, including anonymous nodes", [&]() { + TSNode bracket_node1 = ts_node_concrete_child(array_node, 0); + TSNode number_node = ts_node_concrete_child(array_node, 1); + TSNode array_comma_node1 = ts_node_concrete_child(array_node, 2); + TSNode false_node = ts_node_concrete_child(array_node, 3); + TSNode array_comma_node2 = ts_node_concrete_child(array_node, 4); + TSNode object_node = ts_node_concrete_child(array_node, 5); + TSNode brace_node1 = ts_node_concrete_child(object_node, 0); + TSNode string_node = ts_node_concrete_child(object_node, 1); + TSNode colon_node = ts_node_concrete_child(object_node, 2); + TSNode null_node = ts_node_concrete_child(object_node, 3); + TSNode brace_node2 = ts_node_concrete_child(object_node, 4); + TSNode bracket_node2 = ts_node_concrete_child(array_node, 6); + + AssertThat(ts_node_next_concrete_sibling(bracket_node1), Equals(number_node)); + AssertThat(ts_node_next_concrete_sibling(number_node), Equals(array_comma_node1)); + AssertThat(ts_node_next_concrete_sibling(array_comma_node1), Equals(false_node)); + AssertThat(ts_node_next_concrete_sibling(false_node), Equals(array_comma_node2)); + AssertThat(ts_node_next_concrete_sibling(array_comma_node2), Equals(object_node)); + AssertThat(ts_node_next_concrete_sibling(object_node), Equals(bracket_node2)); + AssertThat(ts_node_next_concrete_sibling(bracket_node2).data, Equals(nullptr)); + + AssertThat(ts_node_prev_concrete_sibling(bracket_node1).data, Equals(nullptr)); + AssertThat(ts_node_prev_concrete_sibling(number_node), Equals(bracket_node1)); + AssertThat(ts_node_prev_concrete_sibling(array_comma_node1), Equals(number_node)); + AssertThat(ts_node_prev_concrete_sibling(false_node), Equals(array_comma_node1)); + AssertThat(ts_node_prev_concrete_sibling(array_comma_node2), Equals(false_node)); + AssertThat(ts_node_prev_concrete_sibling(object_node), Equals(array_comma_node2)); + AssertThat(ts_node_prev_concrete_sibling(bracket_node2), Equals(object_node)); + + AssertThat(ts_node_next_concrete_sibling(brace_node1), Equals(string_node)); + AssertThat(ts_node_next_concrete_sibling(string_node), Equals(colon_node)); + AssertThat(ts_node_next_concrete_sibling(colon_node), Equals(null_node)); + AssertThat(ts_node_next_concrete_sibling(null_node), Equals(brace_node2)); + AssertThat(ts_node_next_concrete_sibling(brace_node2).data, Equals(nullptr)); + + AssertThat(ts_node_prev_concrete_sibling(brace_node1).data, Equals(nullptr)); + AssertThat(ts_node_prev_concrete_sibling(string_node), Equals(brace_node1)); + AssertThat(ts_node_prev_concrete_sibling(colon_node), Equals(string_node)); + AssertThat(ts_node_prev_concrete_sibling(null_node), Equals(colon_node)); + AssertThat(ts_node_prev_concrete_sibling(brace_node2), Equals(null_node)); + }); + + it("returns null when the node has no parent", [&]() { + AssertThat(ts_node_next_sibling(array_node).data, Equals(nullptr)); + AssertThat(ts_node_prev_sibling(array_node).data, Equals(nullptr)); + AssertThat(ts_node_next_sibling(array_node).data, Equals(nullptr)); + AssertThat(ts_node_prev_sibling(array_node).data, Equals(nullptr)); + }); + }); + + describe("next_concrete_sibling(), prev_concrete_sibling()", [&]() { it("returns the node's next and previous siblings", [&]() { TSNode number_node = ts_node_child(array_node, 0); TSNode false_node = ts_node_child(array_node, 1); @@ -200,6 +252,20 @@ describe("Node", []() { }); }); + describe("find_concrete_for_range(start, end)", [&]() { + it("returns the smallest concrete node that spans the given range", [&]() { + TSNode node1 = ts_node_find_concrete_for_range(array_node, 19, 19); + AssertThat(ts_node_name(node1, document), Equals(":")); + AssertThat(ts_node_pos(node1).bytes, Equals(19)); + AssertThat(ts_node_size(node1).bytes, Equals(1)); + + TSNode node2 = ts_node_find_concrete_for_range(array_node, 18, 20); + AssertThat(ts_node_name(node2, document), Equals("object")); + AssertThat(ts_node_pos(node2).bytes, Equals(15)); + AssertThat(ts_node_size(node2).bytes, Equals(11)); + }); + }); + describe("find_for_pos(position)", [&]() { it("finds the smallest node that spans the given position", [&]() { TSNode node = ts_node_find_for_pos(array_node, 10); diff --git a/src/runtime/node.c b/src/runtime/node.c index 57bde1ce..fd81fc70 100644 --- a/src/runtime/node.c +++ b/src/runtime/node.c @@ -62,69 +62,6 @@ TSNode ts_node_parent(TSNode this) { return ts_node_make(tree, position); } -TSNode ts_node_prev_sibling(TSNode this) { - const TSTree *tree = ts_node__tree(this); - TSLength position = ts_node__offset(this); - - do { - TSTree *parent = tree->context.parent; - if (!parent) - break; - - for (size_t i = tree->context.index - 1; i + 1 > 0; i--) { - const TSTree *child = parent->children[i]; - position = ts_length_sub(position, ts_tree_total_size(child)); - if (child->options.type == TSNodeTypeNormal) - return ts_node_make(child, position); - if (child->named_child_count > 0) - return ts_node_child(ts_node_make(child, position), - child->named_child_count - 1); - } - - tree = parent; - } while (!ts_tree_is_visible(tree)); - - return ts_node__null(); -} - -TSNode ts_node_next_sibling(TSNode this) { - const TSTree *tree = ts_node__tree(this); - TSLength position = ts_node__offset(this); - - do { - TSTree *parent = tree->context.parent; - if (!parent) - break; - - const TSTree *prev_child = tree; - for (size_t i = tree->context.index + 1; i < parent->child_count; i++) { - const TSTree *child = parent->children[i]; - position = ts_length_add(position, ts_tree_total_size(prev_child)); - if (child->options.type == TSNodeTypeNormal) - return ts_node_make(child, position); - if (child->named_child_count > 0) - return ts_node_child(ts_node_make(child, position), 0); - prev_child = child; - } - - tree = parent; - } while (!ts_tree_is_visible(tree)); - - return ts_node__null(); -} - -size_t ts_node_concrete_child_count(TSNode this) { - return ts_node__tree(this)->visible_child_count; -} - -bool ts_node_is_concrete(TSNode this) { - return ts_node__tree(this)->options.type == TSNodeTypeConcrete; -} - -size_t ts_node_child_count(TSNode this) { - return ts_node__tree(this)->named_child_count; -} - static TSNode ts_node__child_with_type(TSNode this, size_t child_index, TSNodeType type) { const TSTree *tree = ts_node__tree(this); @@ -161,15 +98,63 @@ static TSNode ts_node__child_with_type(TSNode this, size_t child_index, return ts_node__null(); } -TSNode ts_node_child(TSNode this, size_t child_index) { - return ts_node__child_with_type(this, child_index, TSNodeTypeNormal); +static TSNode ts_node__prev_sibling_with_type(TSNode this, TSNodeType type) { + const TSTree *tree = ts_node__tree(this); + TSLength position = ts_node__offset(this); + + do { + size_t index = tree->context.index; + position = ts_length_sub(position, tree->context.offset); + tree = tree->context.parent; + if (!tree) + break; + + for (size_t i = index - 1; i + 1 > 0; i--) { + const TSTree *child = tree->children[i]; + TSLength child_position = ts_length_add(position, child->context.offset); + if (child->options.type >= type) + return ts_node_make(child, child_position); + size_t grandchild_count = (type == TSNodeTypeNormal) + ? child->named_child_count + : child->visible_child_count; + if (grandchild_count > 0) + return ts_node__child_with_type(ts_node_make(child, child_position), + grandchild_count - 1, type); + } + } while (!ts_tree_is_visible(tree)); + + return ts_node__null(); } -TSNode ts_node_concrete_child(TSNode this, size_t child_index) { - return ts_node__child_with_type(this, child_index, TSNodeTypeConcrete); +static TSNode ts_node__next_sibling_with_type(TSNode this, TSNodeType type) { + const TSTree *tree = ts_node__tree(this); + TSLength position = ts_node__offset(this); + + do { + size_t index = tree->context.index; + position = ts_length_sub(position, tree->context.offset); + tree = tree->context.parent; + if (!tree) + break; + + for (size_t i = index + 1; i < tree->child_count; i++) { + const TSTree *child = tree->children[i]; + TSLength child_position = ts_length_add(position, child->context.offset); + if (child->options.type >= type) + return ts_node_make(child, child_position); + size_t grandchild_count = (type == TSNodeTypeNormal) + ? child->named_child_count + : child->visible_child_count; + if (grandchild_count > 0) + return ts_node__child_with_type(ts_node_make(child, child_position), 0, + type); + } + } while (!ts_tree_is_visible(tree)); + + return ts_node__null(); } -TSNode ts_node_find_for_range(TSNode this, size_t min, size_t max) { +static TSNode ts_node__find_for_range_with_type(TSNode this, size_t min, size_t max, TSNodeType type) { const TSTree *tree = ts_node__tree(this), *last_visible_tree = tree; TSLength position = ts_node__offset(this), last_visible_position = position; @@ -183,7 +168,7 @@ TSNode ts_node_find_for_range(TSNode this, size_t min, size_t max) { break; if (position.chars + child->padding.chars + child->size.chars > max) { tree = child; - if (ts_tree_is_visible(child)) { + if (child->options.type >= type) { last_visible_tree = tree; last_visible_position = position; } @@ -197,6 +182,50 @@ TSNode ts_node_find_for_range(TSNode this, size_t min, size_t max) { return ts_node_make(last_visible_tree, last_visible_position); } +TSNode ts_node_next_sibling(TSNode this) { + return ts_node__next_sibling_with_type(this, TSNodeTypeNormal); +} + +TSNode ts_node_prev_sibling(TSNode this) { + return ts_node__prev_sibling_with_type(this, TSNodeTypeNormal); +} + +TSNode ts_node_next_concrete_sibling(TSNode this) { + return ts_node__next_sibling_with_type(this, TSNodeTypeConcrete); +} + +TSNode ts_node_prev_concrete_sibling(TSNode this) { + return ts_node__prev_sibling_with_type(this, TSNodeTypeConcrete); +} + +size_t ts_node_concrete_child_count(TSNode this) { + return ts_node__tree(this)->visible_child_count; +} + +bool ts_node_is_concrete(TSNode this) { + return ts_node__tree(this)->options.type == TSNodeTypeConcrete; +} + +size_t ts_node_child_count(TSNode this) { + return ts_node__tree(this)->named_child_count; +} + +TSNode ts_node_child(TSNode this, size_t child_index) { + return ts_node__child_with_type(this, child_index, TSNodeTypeNormal); +} + +TSNode ts_node_concrete_child(TSNode this, size_t child_index) { + return ts_node__child_with_type(this, child_index, TSNodeTypeConcrete); +} + +TSNode ts_node_find_for_range(TSNode this, size_t min, size_t max) { + return ts_node__find_for_range_with_type(this, min, max, TSNodeTypeNormal); +} + +TSNode ts_node_find_concrete_for_range(TSNode this, size_t min, size_t max) { + return ts_node__find_for_range_with_type(this, min, max, TSNodeTypeConcrete); +} + TSNode ts_node_find_for_pos(TSNode this, size_t position) { return ts_node_find_for_range(this, position, position); } diff --git a/src/runtime/tree.c b/src/runtime/tree.c index fe97ed0d..62e51d89 100644 --- a/src/runtime/tree.c +++ b/src/runtime/tree.c @@ -20,80 +20,69 @@ TSTree *ts_tree_make_leaf(TSSymbol sym, TSLength size, TSLength padding, .padding = padding, .options = {.type = node_type }, }; + + if (sym == ts_builtin_sym_error) { + result->options.fragile_left = true; + result->options.fragile_right = true; + } + return result; } TSTree *ts_tree_make_error(TSLength size, TSLength padding, char lookahead_char) { TSTree *result = ts_tree_make_leaf(ts_builtin_sym_error, size, padding, TSNodeTypeNormal); - ts_tree_set_fragile_left(result); - ts_tree_set_fragile_right(result); result->lookahead_char = lookahead_char; return result; } -TSTree *ts_tree_make_node(TSSymbol symbol, size_t child_count, - TSTree **children, TSNodeType node_type) { - TSTree *result = malloc(sizeof(TSTree)); - - /* - * Determine the new node's size, padding and visible child count based on - * the given child nodes. - */ - TSLength size = ts_length_zero(), padding = ts_length_zero(); - size_t visible_child_count = 0, named_child_count = 0; +static void ts_tree__set_children(TSTree *this, TSTree **children, + size_t child_count) { + this->children = children; + this->child_count = child_count; for (size_t i = 0; i < child_count; i++) { TSTree *child = children[i]; ts_tree_retain(child); - child->context.parent = result; + child->context.parent = this; child->context.index = i; + child->context.offset = ts_tree_total_size(this); if (i == 0) { - padding = child->padding; - size = child->size; + this->padding = child->padding; + this->size = child->size; } else { - size = ts_length_add(ts_length_add(size, child->padding), child->size); + this->size = + ts_length_add(ts_length_add(this->size, child->padding), child->size); } switch (child->options.type) { case TSNodeTypeNormal: - visible_child_count++; - named_child_count++; + this->visible_child_count++; + this->named_child_count++; break; case TSNodeTypeConcrete: - visible_child_count++; + this->visible_child_count++; break; case TSNodeTypeHidden: - visible_child_count += child->visible_child_count; - named_child_count += child->named_child_count; + this->visible_child_count += child->visible_child_count; + this->named_child_count += child->named_child_count; break; } } - TSTreeOptions options = (TSTreeOptions){ - .type = node_type, - }; - - if (symbol == ts_builtin_sym_error) { - options.fragile_left = true; - options.fragile_left = true; - } else if (child_count > 0) { - options.fragile_left = children[0]->options.fragile_left; - options.fragile_right = children[child_count - 1]->options.fragile_right; + if (child_count > 0) { + if (children[0]->options.fragile_left) + this->options.fragile_left = true; + if (children[child_count - 1]->options.fragile_right) + this->options.fragile_right = true; } +} - *result = (TSTree){ - .ref_count = 1, - .context = {.parent = NULL, .index = 0 }, - .symbol = symbol, - .children = children, - .child_count = child_count, - .visible_child_count = visible_child_count, - .named_child_count = named_child_count, - .size = size, - .padding = padding, - .options = options, - }; +TSTree *ts_tree_make_node(TSSymbol symbol, size_t child_count, + TSTree **children, TSNodeType node_type) { + TSTree *result = + ts_tree_make_leaf(symbol, ts_length_zero(), ts_length_zero(), node_type); + ts_tree__set_children(result, children, child_count); return result; } @@ -197,36 +186,11 @@ void ts_tree_prepend_children(TSTree *tree, size_t count, TSTree **children) { if (count == 0) return; - tree->size = ts_length_add(tree->size, tree->padding); - - size_t visible_count = 0, named_count = 0; - for (size_t i = 0; i < count; i++) { - if (i == 0) - tree->padding = children[i]->padding; - else - tree->size = ts_length_add(tree->size, children[i]->padding); - tree->size = ts_length_add(tree->size, children[i]->size); - switch (children[i]->options.type) { - case TSNodeTypeNormal: - visible_count++; - named_count++; - break; - case TSNodeTypeConcrete: - visible_count++; - break; - case TSNodeTypeHidden: - break; - } - } - size_t new_child_count = count + tree->child_count; TSTree **new_children = realloc(children, new_child_count * sizeof(TSTree *)); memcpy(new_children + count, tree->children, tree->child_count * sizeof(TSTree *)); free(tree->children); - tree->children = new_children; - tree->visible_child_count += visible_count; - tree->named_child_count += named_count; - tree->child_count += count; + ts_tree__set_children(tree, new_children, new_child_count); } diff --git a/src/runtime/tree.h b/src/runtime/tree.h index c75b858b..8e304592 100644 --- a/src/runtime/tree.h +++ b/src/runtime/tree.h @@ -19,6 +19,7 @@ struct TSTree { struct { struct TSTree *parent; size_t index; + TSLength offset; } context; size_t child_count; size_t visible_child_count;