diff --git a/spec/runtime/languages/language_specs.cc b/spec/runtime/languages/language_specs.cc index 9a8fe1fd..2cd6812e 100644 --- a/spec/runtime/languages/language_specs.cc +++ b/spec/runtime/languages/language_specs.cc @@ -40,11 +40,7 @@ describe("Languages", [&]() { expect_the_correct_tree(); }); - // TODO - make these tests pass - set skipped({ - "while loops", - "function expressions", - }); + set skipped({}); if (skipped.find(entry.description) != skipped.end()) continue; diff --git a/spec/runtime/tree_spec.cc b/spec/runtime/tree_spec.cc index 1832e0c9..a3040844 100644 --- a/spec/runtime/tree_spec.cc +++ b/spec/runtime/tree_spec.cc @@ -10,7 +10,13 @@ enum { pig = 4, }; -static const char *names[] = { "error", "end", "cat", "dog", "pig" }; +static const char *names[] = { + "error", + "end", + "cat", + "dog", + "pig", +}; describe("Tree", []() { TSTree *tree1, *tree2, *parent1; @@ -20,19 +26,19 @@ describe("Tree", []() { cat, ts_length_make(5, 4), ts_length_make(2, 1), - 0); + false); tree2 = ts_tree_make_leaf( cat, ts_length_make(3, 3), ts_length_make(1, 1), - 0); + false); parent1 = ts_tree_make_node( dog, 2, tree_array({ tree1, tree2, }), - 0); + false); }); after_each([&]() { @@ -41,7 +47,24 @@ describe("Tree", []() { ts_tree_release(parent1); }); - describe("building a parent node", [&]() { + describe("make_leaf(sym, size, padding, is_hidden)", [&]() { + it("does not record that it contains an error", [&]() { + AssertThat(ts_tree_has_error(tree1), IsFalse()); + }); + }); + + describe("make_error(size, padding, lookahead_char)", [&]() { + it("records that it contains an error", [&]() { + TSTree *error_tree = ts_tree_make_error( + ts_length_zero(), + ts_length_zero(), + 'z'); + + AssertThat(ts_tree_has_error(error_tree), IsTrue()); + }); + }); + + describe("make_node(symbol, child_count, children, is_hidden)", [&]() { it("computes its size based on its child nodes", [&]() { AssertThat(parent1->size.bytes, Equals( tree1->size.bytes + + tree2->padding.bytes + tree2->size.bytes)); @@ -114,6 +137,32 @@ describe("Tree", []() { tree1->size.chars + tree2->padding.chars + tree2->size.chars + tree3->padding.chars)); }); }); + + describe("when one of the child nodes has an error", [&]() { + TSTree *parent; + + before_each([&]() { + tree2->options = (TSTreeOptions)(TSTreeOptionsHasError|TSTreeOptionsExtra); + parent = ts_tree_make_node(pig, 2, tree_array({ + tree1, + tree2, + }), 0); + }); + + after_each([&]() { + ts_tree_release(parent); + }); + + it("records that it contains an error", [&]() { + AssertThat(ts_tree_has_error(parent), IsTrue()); + }); + }); + + describe("when none of the child nodes have an error", [&]() { + it("records that it does not contain an error", [&]() { + AssertThat(ts_tree_has_error(parent1), IsFalse()); + }); + }); }); describe("equality", [&]() { diff --git a/src/runtime/parser.c b/src/runtime/parser.c index 404b2c28..f3929e1f 100644 --- a/src/runtime/parser.c +++ b/src/runtime/parser.c @@ -106,7 +106,7 @@ static TSTree *break_down_right_stack(TSParser *parser) { TSParseAction action = get_action(parser->language, state, node->symbol); bool is_usable = (action.type != TSParseActionTypeError) && - (node->symbol != ts_builtin_sym_error) && + !ts_tree_has_error(node) && !ts_tree_is_extra(node); if (is_usable && right_subtree_start == current_position.chars) { ts_stack_shrink(&parser->right_stack, parser->right_stack.size - 1); diff --git a/src/runtime/tree.c b/src/runtime/tree.c index c72ee16a..7efd4c54 100644 --- a/src/runtime/tree.c +++ b/src/runtime/tree.c @@ -21,6 +21,7 @@ TSTree *ts_tree_make_leaf(TSSymbol sym, TSLength size, TSLength padding, TSTree *ts_tree_make_error(TSLength size, TSLength padding, char lookahead_char) { TSTree *result = ts_tree_make_leaf(ts_builtin_sym_error, size, padding, false); + ts_tree_set_has_error(result); result->lookahead_char = lookahead_char; return result; } @@ -34,6 +35,7 @@ TSTree *ts_tree_make_node(TSSymbol symbol, size_t child_count, */ TSLength size = ts_length_zero(), padding = ts_length_zero(); size_t visible_child_count = 0; + bool has_error = false; for (size_t i = 0; i < child_count; i++) { TSTree *child = children[i]; ts_tree_retain(child); @@ -49,12 +51,17 @@ TSTree *ts_tree_make_node(TSSymbol symbol, size_t child_count, visible_child_count++; else visible_child_count += child->visible_child_count; + + if (ts_tree_has_error(child)) + has_error = true; } /* * Mark the tree as hidden if it wraps a single child node. */ TSTreeOptions options = 0; + if (has_error) + options |= TSTreeOptionsHasError; if (is_hidden) options |= TSTreeOptionsHidden; if (child_count == 1 && @@ -197,15 +204,9 @@ static size_t tree_write_to_string(const TSTree *tree, const char **symbol_names } char *ts_tree_string(const TSTree *tree, const char **symbol_names) { - - /* - * Determine the length of the string first, so that the right amount of - * memory can be allocated. - */ static char SCRATCH[1]; size_t size = tree_write_to_string(tree, symbol_names, SCRATCH, 0, 1) + 1; char *result = malloc(size * sizeof(char)); - tree_write_to_string(tree, symbol_names, result, size, 1); return result; } diff --git a/src/runtime/tree.h b/src/runtime/tree.h index 4a16fb62..528a2d01 100644 --- a/src/runtime/tree.h +++ b/src/runtime/tree.h @@ -9,9 +9,10 @@ extern "C" { #include "tree_sitter/parser.h" typedef enum { - TSTreeOptionsHidden = 1, - TSTreeOptionsExtra = 2, - TSTreeOptionsWrapper = 4, + TSTreeOptionsHidden = 1 << 0, + TSTreeOptionsExtra = 1 << 1, + TSTreeOptionsWrapper = 1 << 2, + TSTreeOptionsHasError = 1 << 3, } TSTreeOptions; struct TSTree { @@ -39,14 +40,26 @@ static inline bool ts_tree_is_visible(const TSTree *tree) { return !(tree->options & TSTreeOptionsHidden); } -static inline void ts_tree_set_extra(TSTree *tree) { - tree->options = (TSTreeOptions)(tree->options | TSTreeOptionsExtra); -} - static inline bool ts_tree_is_wrapper(const TSTree *tree) { return !!(tree->options & TSTreeOptionsWrapper); } +static inline bool ts_tree_has_error(const TSTree *tree) { + return !!(tree->options & TSTreeOptionsHasError); +} + +static inline void ts_tree_set_options(TSTree *tree, TSTreeOptions options) { + tree->options = (TSTreeOptions)(tree->options | options); +} + +static inline void ts_tree_set_extra(TSTree *tree) { + ts_tree_set_options(tree, TSTreeOptionsExtra); +} + +static inline void ts_tree_set_has_error(TSTree *tree) { + ts_tree_set_options(tree, TSTreeOptionsHasError); +} + TSTree *ts_tree_make_leaf(TSSymbol, TSLength, TSLength, bool); TSTree *ts_tree_make_node(TSSymbol, size_t, TSTree **, bool); TSTree *ts_tree_make_error(TSLength size, TSLength padding, char lookahead_char);