Start work on removing parent pointers
Co-Authored-By: Rick Winfrey <rewinfrey@github.com>
This commit is contained in:
parent
8300f24fec
commit
973e4a44f0
14 changed files with 410 additions and 324 deletions
|
|
@ -58,6 +58,9 @@ extern "C" {
|
|||
|
||||
#define array_pop(self) ((self)->contents[--(self)->size])
|
||||
|
||||
#define array_assign(self, other) \
|
||||
array__assign((VoidArray *)(self), (const VoidArray *)(other), array__elem_size(self))
|
||||
|
||||
// Private
|
||||
|
||||
typedef Array(void) VoidArray;
|
||||
|
|
@ -91,6 +94,12 @@ static inline void array__reserve(VoidArray *self, size_t element_size, uint32_t
|
|||
}
|
||||
}
|
||||
|
||||
static inline void array__assign(VoidArray *self, const VoidArray *other, size_t element_size) {
|
||||
array__reserve(self, element_size, other->size);
|
||||
self->size = other->size;
|
||||
memcpy(self->contents, other->contents, self->size * element_size);
|
||||
}
|
||||
|
||||
static inline void array__grow(VoidArray *self, size_t element_size) {
|
||||
if (self->size == self->capacity) {
|
||||
size_t new_capacity = self->capacity * 2;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "runtime/alloc.h"
|
||||
#include "runtime/node.h"
|
||||
#include "runtime/tree.h"
|
||||
#include "runtime/parser.h"
|
||||
#include "runtime/string_input.h"
|
||||
|
|
@ -171,7 +170,12 @@ void ts_document_invalidate(TSDocument *self) {
|
|||
}
|
||||
|
||||
TSNode ts_document_root_node(const TSDocument *self) {
|
||||
return ts_node_make(self->tree, 0, 0);
|
||||
return (TSNode) {
|
||||
.subtree = self->tree,
|
||||
.document = self,
|
||||
.position = {0, 0},
|
||||
.byte = 0,
|
||||
};
|
||||
}
|
||||
|
||||
uint32_t ts_document_parse_count(const TSDocument *self) {
|
||||
|
|
|
|||
|
|
@ -1,43 +1,91 @@
|
|||
#include <stdbool.h>
|
||||
#include "runtime/node.h"
|
||||
#include "runtime/tree.h"
|
||||
#include "runtime/document.h"
|
||||
#include "runtime/language.h"
|
||||
|
||||
TSNode ts_node_make(const Tree *tree, uint32_t byte, uint32_t row) {
|
||||
return (TSNode){.data = tree, .offset = { byte, row } };
|
||||
}
|
||||
// NodeChildIterator
|
||||
|
||||
/*
|
||||
* Private
|
||||
*/
|
||||
typedef struct {
|
||||
const Tree *parent;
|
||||
const TSDocument *document;
|
||||
Length position;
|
||||
uint32_t child_index;
|
||||
uint32_t structural_child_index;
|
||||
const TSSymbol *alias_sequence;
|
||||
} NodeChildIterator;
|
||||
|
||||
// TSNode - Private
|
||||
|
||||
static inline TSNode ts_node__null() {
|
||||
return ts_node_make(NULL, 0, 0);
|
||||
return (TSNode) {
|
||||
.subtree = NULL,
|
||||
.document = NULL,
|
||||
.position = {0, 0},
|
||||
.byte = 0,
|
||||
};
|
||||
}
|
||||
|
||||
static inline const Tree *ts_node__tree(TSNode self) {
|
||||
return self.data;
|
||||
return self.subtree;
|
||||
}
|
||||
|
||||
static inline uint32_t ts_node__offset_byte(TSNode self) {
|
||||
return self.offset[0];
|
||||
static inline NodeChildIterator ts_node_child_iterator_begin(const TSNode *node) {
|
||||
const Tree *tree = ts_node__tree(*node);
|
||||
const TSSymbol *alias_sequence = ts_language_alias_sequence(
|
||||
node->document->parser.language,
|
||||
tree->alias_sequence_id
|
||||
);
|
||||
return (NodeChildIterator) {
|
||||
.parent = tree,
|
||||
.document = node->document,
|
||||
.position = {node->byte, node->position},
|
||||
.child_index = 0,
|
||||
.structural_child_index = 0,
|
||||
.alias_sequence = alias_sequence,
|
||||
};
|
||||
}
|
||||
|
||||
static inline uint32_t ts_node__offset_row(TSNode self) {
|
||||
return self.offset[1];
|
||||
static inline bool ts_node_child_iterator_next(NodeChildIterator *self, TSNode *result) {
|
||||
if (self->child_index == self->parent->children.size) return false;
|
||||
Tree *child = self->parent->children.contents[self->child_index];
|
||||
TSSymbol alias_symbol = 0;
|
||||
if (!child->extra) {
|
||||
if (self->alias_sequence) {
|
||||
alias_symbol = self->alias_sequence[self->structural_child_index];
|
||||
}
|
||||
self->structural_child_index++;
|
||||
}
|
||||
*result = (TSNode) {
|
||||
.subtree = child,
|
||||
.document = self->document,
|
||||
.position = self->position.extent,
|
||||
.byte = self->position.bytes,
|
||||
.alias_symbol = alias_symbol,
|
||||
};
|
||||
self->position = length_add(self->position, ts_tree_total_size(child));
|
||||
self->child_index++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool ts_node__is_relevant(TSNode self, bool include_anonymous) {
|
||||
const Tree *tree = ts_node__tree(self);
|
||||
if (include_anonymous) {
|
||||
return tree->context.alias_symbol || tree->visible;
|
||||
return tree->visible || self.alias_symbol;
|
||||
} else {
|
||||
return tree->context.alias_is_named || (tree->visible && tree->named);
|
||||
return (
|
||||
(tree->visible && tree->named) ||
|
||||
(
|
||||
self.alias_symbol &&
|
||||
ts_language_symbol_metadata(
|
||||
self.document->parser.language,
|
||||
self.alias_symbol
|
||||
).named
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t ts_node__relevant_child_count(TSNode self,
|
||||
bool include_anonymous) {
|
||||
static inline uint32_t ts_node__relevant_child_count(TSNode self, bool include_anonymous) {
|
||||
const Tree *tree = ts_node__tree(self);
|
||||
if (tree->children.size > 0) {
|
||||
if (include_anonymous) {
|
||||
|
|
@ -50,44 +98,23 @@ static inline uint32_t ts_node__relevant_child_count(TSNode self,
|
|||
}
|
||||
}
|
||||
|
||||
static inline TSNode ts_node__direct_parent(TSNode self, uint32_t *index) {
|
||||
const Tree *tree = ts_node__tree(self);
|
||||
*index = tree->context.index;
|
||||
return ts_node_make(
|
||||
tree->context.parent,
|
||||
ts_node__offset_byte(self) - tree->context.offset.bytes,
|
||||
ts_node__offset_row(self) - tree->context.offset.extent.row
|
||||
);
|
||||
}
|
||||
|
||||
static inline TSNode ts_node__direct_child(TSNode self, uint32_t i) {
|
||||
const Tree *child_tree = ts_node__tree(self)->children.contents[i];
|
||||
return ts_node_make(
|
||||
child_tree,
|
||||
ts_node__offset_byte(self) + child_tree->context.offset.bytes,
|
||||
ts_node__offset_row(self) + child_tree->context.offset.extent.row
|
||||
);
|
||||
}
|
||||
|
||||
static inline TSNode ts_node__child(TSNode self, uint32_t child_index,
|
||||
bool include_anonymous) {
|
||||
static inline TSNode ts_node__child(TSNode self, uint32_t child_index, bool include_anonymous) {
|
||||
TSNode result = self;
|
||||
bool did_descend = true;
|
||||
|
||||
while (did_descend) {
|
||||
did_descend = false;
|
||||
|
||||
TSNode child;
|
||||
uint32_t index = 0;
|
||||
for (uint32_t i = 0; i < ts_node__tree(result)->children.size; i++) {
|
||||
TSNode child = ts_node__direct_child(result, i);
|
||||
NodeChildIterator iterator = ts_node_child_iterator_begin(&result);
|
||||
while (ts_node_child_iterator_next(&iterator, &child)) {
|
||||
if (ts_node__is_relevant(child, include_anonymous)) {
|
||||
if (index == child_index)
|
||||
return child;
|
||||
if (index == child_index) return child;
|
||||
index++;
|
||||
} else {
|
||||
uint32_t grandchild_index = child_index - index;
|
||||
uint32_t grandchild_count =
|
||||
ts_node__relevant_child_count(child, include_anonymous);
|
||||
uint32_t grandchild_count = ts_node__relevant_child_count(child, include_anonymous);
|
||||
if (grandchild_index < grandchild_count) {
|
||||
did_descend = true;
|
||||
result = child;
|
||||
|
|
@ -102,48 +129,80 @@ static inline TSNode ts_node__child(TSNode self, uint32_t child_index,
|
|||
return ts_node__null();
|
||||
}
|
||||
|
||||
static inline TSNode ts_node__prev_sibling(TSNode self, bool include_anonymous) {
|
||||
TSNode result = self;
|
||||
|
||||
do {
|
||||
uint32_t index;
|
||||
result = ts_node__direct_parent(result, &index);
|
||||
if (!result.data)
|
||||
static inline bool ts_node__last_child_before(TSNode self, TSNode target,
|
||||
bool include_anonymous, TSNode *result) {
|
||||
TSNode child;
|
||||
TSNode earlier_child = ts_node__null();
|
||||
bool earlier_child_is_relevant = false;
|
||||
bool found_child_containing_target = false;
|
||||
NodeChildIterator iterator = ts_node_child_iterator_begin(&self);
|
||||
uint32_t target_end_byte = ts_node_end_byte(target);
|
||||
while (ts_node_child_iterator_next(&iterator, &child)) {
|
||||
if (iterator.position.bytes >= target_end_byte) {
|
||||
found_child_containing_target = true;
|
||||
break;
|
||||
|
||||
for (uint32_t i = index - 1; i + 1 > 0; i--) {
|
||||
TSNode child = ts_node__direct_child(result, i);
|
||||
if (ts_node__is_relevant(child, include_anonymous))
|
||||
return child;
|
||||
uint32_t grandchild_count =
|
||||
ts_node__relevant_child_count(child, include_anonymous);
|
||||
if (grandchild_count > 0)
|
||||
return ts_node__child(child, grandchild_count - 1, include_anonymous);
|
||||
}
|
||||
} while (!ts_node__tree(result)->visible);
|
||||
|
||||
return ts_node__null();
|
||||
if (ts_node__is_relevant(child, include_anonymous)) {
|
||||
earlier_child = child;
|
||||
earlier_child_is_relevant = true;
|
||||
} else if (ts_node__relevant_child_count(child, include_anonymous) > 0) {
|
||||
earlier_child = child;
|
||||
earlier_child_is_relevant = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_child_containing_target && child.subtree != target.subtree) {
|
||||
if (ts_node__last_child_before(child, target, include_anonymous, result)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (earlier_child_is_relevant) {
|
||||
*result = earlier_child;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (earlier_child.subtree) {
|
||||
return ts_node__last_child_before(earlier_child, target, include_anonymous, result);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline TSNode ts_node__prev_sibling(TSNode self, bool include_anonymous) {
|
||||
TSNode result = ts_node__null();
|
||||
TSNode parent = ts_node_parent(self);
|
||||
if (parent.subtree) {
|
||||
ts_node__last_child_before(parent, self, include_anonymous, &result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline TSNode ts_node__next_sibling(TSNode self, bool include_anonymous) {
|
||||
TSNode result = self;
|
||||
TSNode node = ts_node_parent(self);
|
||||
if (!node.subtree) return ts_node__null();
|
||||
uint32_t end_byte = ts_node_end_byte(self);
|
||||
|
||||
do {
|
||||
uint32_t index;
|
||||
result = ts_node__direct_parent(result, &index);
|
||||
if (!result.data)
|
||||
break;
|
||||
bool did_descend = true;
|
||||
while (did_descend) {
|
||||
did_descend = false;
|
||||
|
||||
for (uint32_t i = index + 1; i < ts_node__tree(result)->children.size; i++) {
|
||||
TSNode child = ts_node__direct_child(result, i);
|
||||
if (ts_node__is_relevant(child, include_anonymous))
|
||||
return child;
|
||||
uint32_t grandchild_count =
|
||||
ts_node__relevant_child_count(child, include_anonymous);
|
||||
if (grandchild_count > 0)
|
||||
return ts_node__child(child, 0, include_anonymous);
|
||||
TSNode child;
|
||||
NodeChildIterator iterator = ts_node_child_iterator_begin(&node);
|
||||
while (ts_node_child_iterator_next(&iterator, &child)) {
|
||||
if (iterator.position.bytes > end_byte && child.subtree != self.subtree) {
|
||||
if (ts_node__is_relevant(child, include_anonymous)) {
|
||||
return child;
|
||||
}
|
||||
if (ts_node__relevant_child_count(child, include_anonymous) > 0) {
|
||||
node = child;
|
||||
did_descend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!ts_node__tree(result)->visible);
|
||||
}
|
||||
|
||||
return ts_node__null();
|
||||
}
|
||||
|
|
@ -160,8 +219,9 @@ static inline TSNode ts_node__first_child_for_byte(TSNode self, uint32_t goal,
|
|||
while (did_descend) {
|
||||
did_descend = false;
|
||||
|
||||
for (uint32_t i = 0; i < ts_node__tree(node)->children.size; i++) {
|
||||
TSNode child = ts_node__direct_child(node, i);
|
||||
TSNode child;
|
||||
NodeChildIterator iterator = ts_node_child_iterator_begin(&node);
|
||||
while (ts_node_child_iterator_next(&iterator, &child)) {
|
||||
if (ts_node_end_byte(child) > goal) {
|
||||
if (ts_node__is_relevant(child, include_anonymous)) {
|
||||
return child;
|
||||
|
|
@ -187,10 +247,11 @@ static inline TSNode ts_node__descendant_for_byte_range(TSNode self, uint32_t mi
|
|||
while (did_descend) {
|
||||
did_descend = false;
|
||||
|
||||
for (uint32_t i = 0, n = ts_node__tree(node)->children.size; i < n; i++) {
|
||||
TSNode child = ts_node__direct_child(node, i);
|
||||
if (ts_node_end_byte(child) > max) {
|
||||
if (ts_node_start_byte(child) > min) break;
|
||||
TSNode child;
|
||||
NodeChildIterator iterator = ts_node_child_iterator_begin(&node);
|
||||
while (ts_node_child_iterator_next(&iterator, &child)) {
|
||||
if (iterator.position.bytes > max) {
|
||||
if (child.byte > min) break;
|
||||
node = child;
|
||||
if (ts_node__is_relevant(node, include_anonymous)) last_visible_node = node;
|
||||
did_descend = true;
|
||||
|
|
@ -214,10 +275,13 @@ static inline TSNode ts_node__descendant_for_point_range(TSNode self, TSPoint mi
|
|||
while (did_descend) {
|
||||
did_descend = false;
|
||||
|
||||
for (uint32_t i = 0, n = ts_node__tree(node)->children.size; i < n; i++) {
|
||||
TSNode child = ts_node__direct_child(node, i);
|
||||
TSNode child;
|
||||
NodeChildIterator iterator = ts_node_child_iterator_begin(&node);
|
||||
while (ts_node_child_iterator_next(&iterator, &child)) {
|
||||
const Tree *child_tree = ts_node__tree(child);
|
||||
if (i > 0) start_position = point_add(start_position, child_tree->padding.extent);
|
||||
if (iterator.child_index != 1) {
|
||||
start_position = point_add(start_position, child_tree->padding.extent);
|
||||
}
|
||||
end_position = point_add(start_position, child_tree->size.extent);
|
||||
if (point_gt(end_position, max)) {
|
||||
if (point_gt(start_position, min)) break;
|
||||
|
|
@ -233,12 +297,10 @@ static inline TSNode ts_node__descendant_for_point_range(TSNode self, TSPoint mi
|
|||
return last_visible_node;
|
||||
}
|
||||
|
||||
/*
|
||||
* Public
|
||||
*/
|
||||
// TSNode - Public
|
||||
|
||||
uint32_t ts_node_start_byte(TSNode self) {
|
||||
return ts_node__offset_byte(self) + ts_node__tree(self)->padding.bytes;
|
||||
return self.byte + ts_node__tree(self)->padding.bytes;
|
||||
}
|
||||
|
||||
uint32_t ts_node_end_byte(TSNode self) {
|
||||
|
|
@ -246,21 +308,16 @@ uint32_t ts_node_end_byte(TSNode self) {
|
|||
}
|
||||
|
||||
TSPoint ts_node_start_point(TSNode self) {
|
||||
const Tree *tree = ts_node__tree(self);
|
||||
return (TSPoint){ ts_node__offset_row(self) + tree->padding.extent.row,
|
||||
ts_tree_start_column(tree) };
|
||||
return point_add(self.position, ts_node__tree(self)->padding.extent);
|
||||
}
|
||||
|
||||
TSPoint ts_node_end_point(TSNode self) {
|
||||
const Tree *tree = ts_node__tree(self);
|
||||
return (TSPoint){ ts_node__offset_row(self) + tree->padding.extent.row +
|
||||
tree->size.extent.row,
|
||||
ts_tree_end_column(tree) };
|
||||
return point_add(ts_node_start_point(self), ts_node__tree(self)->size.extent);
|
||||
}
|
||||
|
||||
TSSymbol ts_node_symbol(TSNode self) {
|
||||
const Tree *tree = ts_node__tree(self);
|
||||
return tree->context.alias_symbol ? tree->context.alias_symbol : tree->symbol;
|
||||
return self.alias_symbol ? self.alias_symbol : tree->symbol;
|
||||
}
|
||||
|
||||
const char *ts_node_type(TSNode self, const TSDocument *document) {
|
||||
|
|
@ -272,15 +329,17 @@ char *ts_node_string(TSNode self, const TSDocument *document) {
|
|||
}
|
||||
|
||||
bool ts_node_eq(TSNode self, TSNode other) {
|
||||
return
|
||||
return (
|
||||
ts_tree_eq(ts_node__tree(self), ts_node__tree(other)) &&
|
||||
self.offset[0] == other.offset[0] &&
|
||||
self.offset[1] == other.offset[1];
|
||||
self.byte == other.byte
|
||||
);
|
||||
}
|
||||
|
||||
bool ts_node_is_named(TSNode self) {
|
||||
const Tree *tree = ts_node__tree(self);
|
||||
return tree->context.alias_symbol ? tree->context.alias_is_named : tree->named;
|
||||
return self.alias_symbol
|
||||
? ts_language_symbol_metadata(self.document->parser.language, self.alias_symbol).named
|
||||
: tree->named;
|
||||
}
|
||||
|
||||
bool ts_node_is_missing(TSNode self) {
|
||||
|
|
@ -297,35 +356,31 @@ bool ts_node_has_error(TSNode self) {
|
|||
}
|
||||
|
||||
TSNode ts_node_parent(TSNode self) {
|
||||
TSNode result = self;
|
||||
uint32_t index;
|
||||
TSNode node = ts_document_root_node(self.document);
|
||||
uint32_t end_byte = ts_node_end_byte(self);
|
||||
if (node.subtree == self.subtree) return ts_node__null();
|
||||
|
||||
do {
|
||||
result = ts_node__direct_parent(result, &index);
|
||||
if (!result.data)
|
||||
return ts_node__null();
|
||||
} while (!ts_node__tree(result)->visible);
|
||||
TSNode last_visible_node = node;
|
||||
bool did_descend = true;
|
||||
while (did_descend) {
|
||||
did_descend = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t ts_node_child_index(TSNode self) {
|
||||
const Tree *tree = ts_node__tree(self);
|
||||
uint32_t result = 0;
|
||||
|
||||
for (;;) {
|
||||
const Tree *parent = tree->context.parent;
|
||||
uint32_t index = tree->context.index;
|
||||
if (!parent) return UINT32_MAX;
|
||||
for (uint32_t i = 0; i < index; i++) {
|
||||
Tree *child = parent->children.contents[i];
|
||||
result += child->visible ? 1 : child->visible_child_count;
|
||||
TSNode child;
|
||||
NodeChildIterator iterator = ts_node_child_iterator_begin(&node);
|
||||
while (ts_node_child_iterator_next(&iterator, &child)) {
|
||||
if (child.byte > self.byte || child.subtree == self.subtree) break;
|
||||
if (iterator.position.bytes >= end_byte) {
|
||||
node = child;
|
||||
if (ts_node__is_relevant(child, true)) {
|
||||
last_visible_node = node;
|
||||
}
|
||||
did_descend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parent->visible) break;
|
||||
tree = parent;
|
||||
}
|
||||
|
||||
return result;
|
||||
return last_visible_node;
|
||||
}
|
||||
|
||||
TSNode ts_node_child(TSNode self, uint32_t child_index) {
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef RUNTIME_NODE_H_
|
||||
#define RUNTIME_NODE_H_
|
||||
|
||||
#include "runtime/tree.h"
|
||||
|
||||
TSNode ts_node_make(const Tree *, uint32_t byte, uint32_t row);
|
||||
|
||||
#endif
|
||||
|
|
@ -119,16 +119,19 @@ static bool parser__breakdown_top_of_stack(Parser *self, StackVersion version) {
|
|||
static void parser__breakdown_lookahead(Parser *self, Tree **lookahead,
|
||||
TSStateId state,
|
||||
ReusableNode *reusable_node) {
|
||||
bool did_break_down = false;
|
||||
while (reusable_node->tree->children.size > 0 && reusable_node->tree->parse_state != state) {
|
||||
LOG("state_mismatch sym:%s", SYM_NAME(reusable_node->tree->symbol));
|
||||
reusable_node_breakdown(reusable_node);
|
||||
did_break_down = true;
|
||||
bool did_descend = false;
|
||||
Tree *tree = reusable_node_tree(reusable_node);
|
||||
while (tree->children.size > 0 && tree->parse_state != state) {
|
||||
LOG("state_mismatch sym:%s", SYM_NAME(tree->symbol));
|
||||
reusable_node_descend(reusable_node);
|
||||
tree = reusable_node_tree(reusable_node);
|
||||
did_descend = true;
|
||||
}
|
||||
|
||||
if (did_break_down) {
|
||||
if (did_descend) {
|
||||
ts_tree_release(&self->tree_pool, *lookahead);
|
||||
ts_tree_retain(*lookahead = reusable_node->tree);
|
||||
*lookahead = tree;
|
||||
ts_tree_retain(*lookahead);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -419,21 +422,22 @@ static Tree *parser__get_lookahead(Parser *self, StackVersion version, TSStateId
|
|||
Tree *last_external_token = ts_stack_last_external_token(self->stack, version);
|
||||
|
||||
Tree *result;
|
||||
while ((result = reusable_node->tree)) {
|
||||
if (reusable_node->byte_index > position.bytes) {
|
||||
while ((result = reusable_node_tree(reusable_node))) {
|
||||
uint32_t byte_offset = reusable_node_byte_offset(reusable_node);
|
||||
if (byte_offset > position.bytes) {
|
||||
LOG("before_reusable_node symbol:%s", SYM_NAME(result->symbol));
|
||||
break;
|
||||
}
|
||||
|
||||
if (reusable_node->byte_index < position.bytes) {
|
||||
if (byte_offset < position.bytes) {
|
||||
LOG("past_reusable_node symbol:%s", SYM_NAME(result->symbol));
|
||||
reusable_node_pop(reusable_node);
|
||||
reusable_node_advance(reusable_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ts_tree_external_token_state_eq(reusable_node->last_external_token, last_external_token)) {
|
||||
LOG("reusable_node_has_different_external_scanner_state symbol:%s", SYM_NAME(result->symbol));
|
||||
reusable_node_pop(reusable_node);
|
||||
reusable_node_advance(reusable_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -452,8 +456,8 @@ static Tree *parser__get_lookahead(Parser *self, StackVersion version, TSStateId
|
|||
|
||||
if (reason) {
|
||||
LOG("cant_reuse_node_%s tree:%s", reason, SYM_NAME(result->symbol));
|
||||
if (!reusable_node_breakdown(reusable_node)) {
|
||||
reusable_node_pop(reusable_node);
|
||||
if (!reusable_node_descend(reusable_node)) {
|
||||
reusable_node_advance(reusable_node);
|
||||
parser__breakdown_top_of_stack(self, version);
|
||||
*state = ts_stack_state(self->stack, version);
|
||||
}
|
||||
|
|
@ -467,7 +471,7 @@ static Tree *parser__get_lookahead(Parser *self, StackVersion version, TSStateId
|
|||
SYM_NAME(result->symbol),
|
||||
SYM_NAME(result->first_leaf.symbol)
|
||||
);
|
||||
reusable_node_pop_leaf(reusable_node);
|
||||
reusable_node_advance_past_leaf(reusable_node);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -678,7 +682,7 @@ static void parser__start(Parser *self, TSInput input, Tree *previous_tree) {
|
|||
|
||||
ts_lexer_set_input(&self->lexer, input);
|
||||
ts_stack_clear(self->stack);
|
||||
self->reusable_node = reusable_node_new(previous_tree);
|
||||
reusable_node_reset(&self->reusable_node, previous_tree);
|
||||
self->finished_tree = NULL;
|
||||
self->accept_count = 0;
|
||||
self->in_ambiguity = false;
|
||||
|
|
@ -1106,7 +1110,9 @@ static void parser__advance(Parser *self, StackVersion version, ReusableNode *re
|
|||
}
|
||||
|
||||
parser__shift(self, version, next_state, lookahead, action.params.extra);
|
||||
if (lookahead == reusable_node->tree) reusable_node_pop(reusable_node);
|
||||
if (lookahead == reusable_node_tree(reusable_node)) {
|
||||
reusable_node_advance(reusable_node);
|
||||
}
|
||||
ts_tree_release(&self->tree_pool, lookahead);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1136,7 +1142,9 @@ static void parser__advance(Parser *self, StackVersion version, ReusableNode *re
|
|||
parser__breakdown_lookahead(self, &lookahead, state, reusable_node);
|
||||
}
|
||||
parser__recover(self, version, lookahead);
|
||||
if (lookahead == reusable_node->tree) reusable_node_pop(reusable_node);
|
||||
if (lookahead == reusable_node_tree(reusable_node)) {
|
||||
reusable_node_advance(reusable_node);
|
||||
}
|
||||
ts_tree_release(&self->tree_pool, lookahead);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1255,6 +1263,7 @@ bool parser_init(Parser *self) {
|
|||
ts_tree_pool_init(&self->tree_pool);
|
||||
self->stack = ts_stack_new(&self->tree_pool);
|
||||
self->finished_tree = NULL;
|
||||
self->reusable_node = reusable_node_new();
|
||||
parser__set_cached_token(self, 0, NULL, NULL);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1277,6 +1286,7 @@ void parser_destroy(Parser *self) {
|
|||
if (self->reduce_actions.contents)
|
||||
array_delete(&self->reduce_actions);
|
||||
ts_tree_pool_delete(&self->tree_pool);
|
||||
reusable_node_delete(&self->reusable_node);
|
||||
parser_set_language(self, NULL);
|
||||
}
|
||||
|
||||
|
|
@ -1285,11 +1295,12 @@ Tree *parser_parse(Parser *self, TSInput input, Tree *old_tree, bool halt_on_err
|
|||
|
||||
StackVersion version = STACK_VERSION_NONE;
|
||||
uint32_t position = 0, last_position = 0;
|
||||
ReusableNode reusable_node;
|
||||
ReusableNode reusable_node = reusable_node_new();
|
||||
reusable_node_assign(&reusable_node, &self->reusable_node);
|
||||
|
||||
do {
|
||||
for (version = 0; version < ts_stack_version_count(self->stack); version++) {
|
||||
reusable_node = self->reusable_node;
|
||||
reusable_node_assign(&reusable_node, &self->reusable_node);
|
||||
|
||||
while (ts_stack_is_active(self->stack, version)) {
|
||||
LOG("process version:%d, version_count:%u, state:%d, row:%u, col:%u",
|
||||
|
|
@ -1309,7 +1320,7 @@ Tree *parser_parse(Parser *self, TSInput input, Tree *old_tree, bool halt_on_err
|
|||
}
|
||||
}
|
||||
|
||||
self->reusable_node = reusable_node;
|
||||
reusable_node_assign(&self->reusable_node, &reusable_node);
|
||||
|
||||
unsigned min_error_cost = parser__condense_stack(self);
|
||||
if (self->finished_tree && self->finished_tree->error_cost < min_error_cost) {
|
||||
|
|
@ -1322,6 +1333,7 @@ Tree *parser_parse(Parser *self, TSInput input, Tree *old_tree, bool halt_on_err
|
|||
self->in_ambiguity = version > 1;
|
||||
} while (version != 0);
|
||||
|
||||
reusable_node_delete(&reusable_node);
|
||||
ts_stack_clear(self->stack);
|
||||
parser__set_cached_token(self, 0, NULL, NULL);
|
||||
ts_tree_assign_parents(self->finished_tree, &self->tree_pool, self->language);
|
||||
|
|
|
|||
|
|
@ -2,44 +2,81 @@
|
|||
|
||||
typedef struct {
|
||||
Tree *tree;
|
||||
uint32_t byte_index;
|
||||
uint32_t child_index;
|
||||
uint32_t byte_offset;
|
||||
} StackEntry;
|
||||
|
||||
typedef struct {
|
||||
Array(StackEntry) stack;
|
||||
Tree *last_external_token;
|
||||
} ReusableNode;
|
||||
|
||||
static inline ReusableNode reusable_node_new(Tree *tree) {
|
||||
ReusableNode result = {tree, 0, NULL};
|
||||
return result;
|
||||
static inline ReusableNode reusable_node_new() {
|
||||
return (ReusableNode) {array_new(), NULL};
|
||||
}
|
||||
|
||||
static inline void reusable_node_pop(ReusableNode *self) {
|
||||
self->byte_index += ts_tree_total_bytes(self->tree);
|
||||
if (self->tree->has_external_tokens) {
|
||||
self->last_external_token = ts_tree_last_external_token(self->tree);
|
||||
}
|
||||
|
||||
while (self->tree) {
|
||||
Tree *parent = self->tree->context.parent;
|
||||
uint32_t next_index = self->tree->context.index + 1;
|
||||
if (parent && parent->children.size > next_index) {
|
||||
self->tree = parent->children.contents[next_index];
|
||||
return;
|
||||
}
|
||||
self->tree = parent;
|
||||
}
|
||||
static inline void reusable_node_reset(ReusableNode *self, Tree *tree) {
|
||||
array_clear(&self->stack);
|
||||
array_push(&self->stack, ((StackEntry) {
|
||||
.tree = tree,
|
||||
.child_index = 0,
|
||||
.byte_offset = 0,
|
||||
}));
|
||||
}
|
||||
|
||||
static inline void reusable_node_pop_leaf(ReusableNode *self) {
|
||||
while (self->tree->children.size > 0) {
|
||||
self->tree = self->tree->children.contents[0];
|
||||
}
|
||||
reusable_node_pop(self);
|
||||
static inline Tree *reusable_node_tree(ReusableNode *self) {
|
||||
return array_back(&self->stack)->tree;
|
||||
}
|
||||
|
||||
static inline bool reusable_node_breakdown(ReusableNode *self) {
|
||||
if (self->tree->children.size == 0) {
|
||||
return false;
|
||||
} else {
|
||||
self->tree = self->tree->children.contents[0];
|
||||
static inline uint32_t reusable_node_byte_offset(ReusableNode *self) {
|
||||
return array_back(&self->stack)->byte_offset;
|
||||
}
|
||||
|
||||
static inline void reusable_node_delete(ReusableNode *self) {
|
||||
array_delete(&self->stack);
|
||||
}
|
||||
|
||||
static inline void reusable_node_assign(ReusableNode *self, const ReusableNode *other) {
|
||||
array_assign(&self->stack, &other->stack);
|
||||
}
|
||||
|
||||
static inline void reusable_node_advance(ReusableNode *self) {
|
||||
StackEntry last_entry = *array_back(&self->stack);
|
||||
uint32_t byte_offset = last_entry.byte_offset + ts_tree_total_bytes(last_entry.tree);
|
||||
if (last_entry.tree->has_external_tokens) {
|
||||
self->last_external_token = ts_tree_last_external_token(last_entry.tree);
|
||||
}
|
||||
|
||||
Tree *tree;
|
||||
uint32_t next_index;
|
||||
do {
|
||||
StackEntry popped_entry = array_pop(&self->stack);
|
||||
next_index = popped_entry.child_index + 1;
|
||||
tree = array_back(&self->stack)->tree;
|
||||
} while (tree->children.size <= next_index);
|
||||
|
||||
array_push(&self->stack, ((StackEntry) {
|
||||
.tree = tree->children.contents[next_index],
|
||||
.child_index = next_index,
|
||||
.byte_offset = byte_offset,
|
||||
}));
|
||||
}
|
||||
|
||||
static inline bool reusable_node_descend(ReusableNode *self) {
|
||||
StackEntry last_entry = *array_back(&self->stack);
|
||||
if (last_entry.tree->children.size > 0) {
|
||||
array_push(&self->stack, ((StackEntry) {
|
||||
.tree = last_entry.tree->children.contents[0],
|
||||
.child_index = 0,
|
||||
.byte_offset = last_entry.byte_offset,
|
||||
}));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void reusable_node_advance_past_leaf(ReusableNode *self) {
|
||||
while (reusable_node_descend(self)) {}
|
||||
reusable_node_advance(self);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,7 +169,9 @@ Tree *ts_tree_make_copy(TreePool *pool, Tree *self) {
|
|||
return result;
|
||||
}
|
||||
|
||||
static void ts_tree__compress(Tree *self, unsigned count, const TSLanguage *language) {
|
||||
static void ts_tree__compress(Tree *self, unsigned count, const TSLanguage *language, TreeArray *stack) {
|
||||
unsigned initial_stack_size = stack->size;
|
||||
|
||||
Tree *tree = self;
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (tree->ref_count > 1 || tree->children.size != 2) break;
|
||||
|
|
@ -189,22 +191,14 @@ static void ts_tree__compress(Tree *self, unsigned count, const TSLanguage *lang
|
|||
) break;
|
||||
|
||||
tree->children.contents[0] = grandchild;
|
||||
grandchild->context.parent = tree;
|
||||
grandchild->context.index = -1;
|
||||
|
||||
child->children.contents[0] = grandchild->children.contents[1];
|
||||
child->children.contents[0]->context.parent = child;
|
||||
child->children.contents[0]->context.index = -1;
|
||||
|
||||
grandchild->children.contents[1] = child;
|
||||
grandchild->children.contents[1]->context.parent = grandchild;
|
||||
grandchild->children.contents[1]->context.index = -1;
|
||||
|
||||
array_push(stack, tree);
|
||||
tree = grandchild;
|
||||
}
|
||||
|
||||
while (tree != self) {
|
||||
tree = tree->context.parent;
|
||||
while (stack->size > initial_stack_size) {
|
||||
tree = array_pop(stack);
|
||||
Tree *child = tree->children.contents[0];
|
||||
Tree *grandchild = child->children.contents[1];
|
||||
ts_tree_set_children(grandchild, &grandchild->children, language);
|
||||
|
|
@ -213,50 +207,30 @@ static void ts_tree__compress(Tree *self, unsigned count, const TSLanguage *lang
|
|||
}
|
||||
}
|
||||
|
||||
void ts_tree__balance(Tree *self, const TSLanguage *language) {
|
||||
if (self->children.contents[0]->repeat_depth > self->children.contents[1]->repeat_depth) {
|
||||
unsigned n = self->children.contents[0]->repeat_depth - self->children.contents[1]->repeat_depth;
|
||||
for (unsigned i = n / 2; i > 0; i /= 2) {
|
||||
ts_tree__compress(self, i, language);
|
||||
n -= i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ts_tree_assign_parents(Tree *self, TreePool *pool, const TSLanguage *language) {
|
||||
self->context.parent = NULL;
|
||||
array_clear(&pool->tree_stack);
|
||||
array_push(&pool->tree_stack, self);
|
||||
while (pool->tree_stack.size > 0) {
|
||||
Tree *tree = array_pop(&pool->tree_stack);
|
||||
|
||||
if (tree->repeat_depth > 0) {
|
||||
ts_tree__balance(tree, language);
|
||||
if (tree->children.contents[0]->repeat_depth > tree->children.contents[1]->repeat_depth) {
|
||||
unsigned n = (
|
||||
tree->children.contents[0]->repeat_depth -
|
||||
tree->children.contents[1]->repeat_depth
|
||||
);
|
||||
for (unsigned i = n / 2; i > 0; i /= 2) {
|
||||
ts_tree__compress(tree, i, language, &pool->tree_stack);
|
||||
n -= i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Length offset = length_zero();
|
||||
const TSSymbol *alias_sequence = ts_language_alias_sequence(language, tree->alias_sequence_id);
|
||||
uint32_t non_extra_index = 0;
|
||||
bool earlier_child_was_changed = false;
|
||||
for (uint32_t i = 0; i < tree->children.size; i++) {
|
||||
Tree *child = tree->children.contents[i];
|
||||
if (earlier_child_was_changed || child->context.parent != tree || child->context.index != i) {
|
||||
earlier_child_was_changed = true;
|
||||
child->context.parent = tree;
|
||||
child->context.index = i;
|
||||
child->context.offset = offset;
|
||||
if (!child->extra && alias_sequence && alias_sequence[non_extra_index] != 0) {
|
||||
TSSymbolMetadata metadata = ts_language_symbol_metadata(language, alias_sequence[non_extra_index]);
|
||||
child->context.alias_symbol = alias_sequence[non_extra_index];
|
||||
child->context.alias_is_named = metadata.named;
|
||||
} else {
|
||||
child->context.alias_symbol = 0;
|
||||
child->context.alias_is_named = false;
|
||||
}
|
||||
if (child->ref_count == 1) {
|
||||
array_push(&pool->tree_stack, child);
|
||||
}
|
||||
offset = length_add(offset, ts_tree_total_size(child));
|
||||
if (!child->extra) non_extra_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -407,25 +381,6 @@ void ts_tree_release(TreePool *pool, Tree *self) {
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t ts_tree_start_column(const Tree *self) {
|
||||
uint32_t column = self->padding.extent.column;
|
||||
if (self->padding.extent.row > 0)
|
||||
return column;
|
||||
for (const Tree *tree = self; tree != NULL; tree = tree->context.parent) {
|
||||
column += tree->context.offset.extent.column;
|
||||
if (tree->context.offset.extent.row > 0)
|
||||
break;
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
uint32_t ts_tree_end_column(const Tree *self) {
|
||||
uint32_t result = self->size.extent.column;
|
||||
if (self->size.extent.row == 0)
|
||||
result += ts_tree_start_column(self);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ts_tree_eq(const Tree *self, const Tree *other) {
|
||||
if (self) {
|
||||
if (!other) return false;
|
||||
|
|
@ -577,7 +532,6 @@ void ts_tree_edit(Tree *self, const TSInputEdit *edit) {
|
|||
}
|
||||
|
||||
child_right = length_add(child_left, ts_tree_total_size(child));
|
||||
child->context.offset = child_left;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -612,9 +566,10 @@ static size_t ts_tree__write_char_to_string(char *s, size_t n, int32_t c) {
|
|||
return snprintf(s, n, "%d", c);
|
||||
}
|
||||
|
||||
static size_t ts_tree__write_to_string(const Tree *self, const TSLanguage *language,
|
||||
char *string, size_t limit, bool is_root,
|
||||
bool include_all) {
|
||||
static size_t ts_tree__write_to_string(const Tree *self, char *string, size_t limit,
|
||||
const TSLanguage *language, bool is_root,
|
||||
bool include_all, TSSymbol alias_symbol,
|
||||
bool alias_is_named) {
|
||||
if (!self) return snprintf(string, limit, "(NULL)");
|
||||
|
||||
char *cursor = string;
|
||||
|
|
@ -624,7 +579,7 @@ static size_t ts_tree__write_to_string(const Tree *self, const TSLanguage *langu
|
|||
is_root ||
|
||||
self->is_missing ||
|
||||
(self->visible && self->named) ||
|
||||
self->context.alias_is_named;
|
||||
alias_is_named;
|
||||
|
||||
if (visible && !is_root) {
|
||||
cursor += snprintf(*writer, limit, " ");
|
||||
|
|
@ -637,15 +592,35 @@ static size_t ts_tree__write_to_string(const Tree *self, const TSLanguage *langu
|
|||
} else if (self->is_missing) {
|
||||
cursor += snprintf(*writer, limit, "(MISSING");
|
||||
} else {
|
||||
TSSymbol symbol = self->context.alias_symbol ? self->context.alias_symbol : self->symbol;
|
||||
TSSymbol symbol = alias_symbol ? alias_symbol : self->symbol;
|
||||
const char *symbol_name = ts_language_symbol_name(language, symbol);
|
||||
cursor += snprintf(*writer, limit, "(%s", symbol_name);
|
||||
}
|
||||
}
|
||||
|
||||
const TSSymbol *alias_sequence = ts_language_alias_sequence(language, self->alias_sequence_id);
|
||||
uint32_t structural_child_index = 0;
|
||||
for (uint32_t i = 0; i < self->children.size; i++) {
|
||||
Tree *child = self->children.contents[i];
|
||||
cursor += ts_tree__write_to_string(child, language, *writer, limit, false, include_all);
|
||||
if (child->extra) {
|
||||
cursor += ts_tree__write_to_string(
|
||||
child, *writer, limit,
|
||||
language, false, include_all,
|
||||
0, false
|
||||
);
|
||||
} else {
|
||||
cursor += ts_tree__write_to_string(
|
||||
child, *writer, limit,
|
||||
language, false, include_all,
|
||||
alias_sequence
|
||||
? alias_sequence[structural_child_index]
|
||||
: 0,
|
||||
alias_sequence
|
||||
? ts_language_symbol_metadata(language, alias_sequence[structural_child_index]).named
|
||||
: false
|
||||
);
|
||||
structural_child_index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (visible) cursor += snprintf(*writer, limit, ")");
|
||||
|
|
@ -655,15 +630,19 @@ static size_t ts_tree__write_to_string(const Tree *self, const TSLanguage *langu
|
|||
|
||||
char *ts_tree_string(const Tree *self, const TSLanguage *language, bool include_all) {
|
||||
char scratch_string[1];
|
||||
size_t size = ts_tree__write_to_string(self, language, scratch_string, 0, true, include_all) + 1;
|
||||
size_t size = ts_tree__write_to_string(
|
||||
self, scratch_string, 0,
|
||||
language, true,
|
||||
include_all, 0, false
|
||||
) + 1;
|
||||
char *result = ts_malloc(size * sizeof(char));
|
||||
ts_tree__write_to_string(self, language, result, size, true, include_all);
|
||||
ts_tree__write_to_string(self, result, size, language, true, include_all, 0, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ts_tree__print_dot_graph(const Tree *self, uint32_t byte_offset,
|
||||
const TSLanguage *language, FILE *f) {
|
||||
TSSymbol symbol = self->context.alias_symbol ? self->context.alias_symbol : self->symbol;
|
||||
const TSLanguage *language, TSSymbol alias_symbol, FILE *f) {
|
||||
TSSymbol symbol = alias_symbol ? alias_symbol : self->symbol;
|
||||
fprintf(f, "tree_%p [label=\"%s\"", self, ts_language_symbol_name(language, symbol));
|
||||
|
||||
if (self->children.size == 0)
|
||||
|
|
@ -674,9 +653,18 @@ void ts_tree__print_dot_graph(const Tree *self, uint32_t byte_offset,
|
|||
fprintf(f, ", tooltip=\"address:%p\nrange:%u - %u\nstate:%d\nerror-cost:%u\nrepeat-depth:%u\"]\n",
|
||||
self, byte_offset, byte_offset + ts_tree_total_bytes(self), self->parse_state,
|
||||
self->error_cost, self->repeat_depth);
|
||||
|
||||
const TSSymbol *alias_sequence = ts_language_alias_sequence(language, self->alias_sequence_id);
|
||||
uint32_t structural_child_index = 0;
|
||||
for (uint32_t i = 0; i < self->children.size; i++) {
|
||||
const Tree *child = self->children.contents[i];
|
||||
ts_tree__print_dot_graph(child, byte_offset, language, f);
|
||||
if (child->extra) {
|
||||
ts_tree__print_dot_graph(child, byte_offset, language, 0, f);
|
||||
} else {
|
||||
TSSymbol alias_symbol = alias_sequence ? alias_sequence[structural_child_index] : 0;
|
||||
ts_tree__print_dot_graph(child, byte_offset, language, alias_symbol, f);
|
||||
structural_child_index++;
|
||||
}
|
||||
fprintf(f, "tree_%p -> tree_%p [tooltip=%u]\n", self, child, i);
|
||||
byte_offset += ts_tree_total_bytes(child);
|
||||
}
|
||||
|
|
@ -685,7 +673,7 @@ void ts_tree__print_dot_graph(const Tree *self, uint32_t byte_offset,
|
|||
void ts_tree_print_dot_graph(const Tree *self, const TSLanguage *language, FILE *f) {
|
||||
fprintf(f, "digraph tree {\n");
|
||||
fprintf(f, "edge [arrowhead=none]\n");
|
||||
ts_tree__print_dot_graph(self, 0, language, f);
|
||||
ts_tree__print_dot_graph(self, 0, language, 0, f);
|
||||
fprintf(f, "}\n");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,14 +27,6 @@ typedef struct Tree Tree;
|
|||
typedef Array(Tree *) TreeArray;
|
||||
|
||||
struct Tree {
|
||||
struct {
|
||||
struct Tree *parent;
|
||||
uint32_t index;
|
||||
Length offset;
|
||||
TSSymbol alias_symbol : 15;
|
||||
bool alias_is_named : 1;
|
||||
} context;
|
||||
|
||||
Length padding;
|
||||
Length size;
|
||||
uint32_t ref_count;
|
||||
|
|
@ -106,8 +98,6 @@ void ts_tree_retain(Tree *tree);
|
|||
void ts_tree_release(TreePool *, Tree *tree);
|
||||
bool ts_tree_eq(const Tree *tree1, const Tree *tree2);
|
||||
int ts_tree_compare(const Tree *tree1, const Tree *tree2);
|
||||
uint32_t ts_tree_start_column(const Tree *self);
|
||||
uint32_t ts_tree_end_column(const Tree *self);
|
||||
void ts_tree_set_children(Tree *, TreeArray *, const TSLanguage *);
|
||||
void ts_tree_assign_parents(Tree *, TreePool *, const TSLanguage *);
|
||||
void ts_tree_edit(Tree *, const TSInputEdit *edit);
|
||||
|
|
@ -120,6 +110,10 @@ static inline uint32_t ts_tree_total_bytes(const Tree *self) {
|
|||
return self->padding.bytes + self->size.bytes;
|
||||
}
|
||||
|
||||
static inline uint32_t ts_tree_total_rows(const Tree *self) {
|
||||
return self->padding.extent.row + self->size.extent.row;
|
||||
}
|
||||
|
||||
static inline Length ts_tree_total_size(const Tree *self) {
|
||||
return length_add(self->padding, self->size);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue