From a92067702d395d3ff9d0ec60521b310356b9a412 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Sat, 21 Feb 2015 10:39:58 -0800 Subject: [PATCH] Use a more exact test for reusability of error nodes Based on the concept of node fragility from wagner's incremental parsing paper --- spec/runtime/tree_spec.cc | 62 ++++++++++++++++++++++++++++++++------- src/runtime/parser.c | 3 +- src/runtime/tree.c | 13 ++++---- src/runtime/tree.h | 23 ++++++++++----- 4 files changed, 75 insertions(+), 26 deletions(-) diff --git a/spec/runtime/tree_spec.cc b/spec/runtime/tree_spec.cc index a3040844..016c1f6e 100644 --- a/spec/runtime/tree_spec.cc +++ b/spec/runtime/tree_spec.cc @@ -48,19 +48,21 @@ describe("Tree", []() { }); describe("make_leaf(sym, size, padding, is_hidden)", [&]() { - it("does not record that it contains an error", [&]() { - AssertThat(ts_tree_has_error(tree1), IsFalse()); + it("does not record that it is fragile", [&]() { + AssertThat(ts_tree_is_fragile_left(tree1), IsFalse()); + AssertThat(ts_tree_is_fragile_right(tree1), IsFalse()); }); }); describe("make_error(size, padding, lookahead_char)", [&]() { - it("records that it contains an error", [&]() { + it("records that it is fragile", [&]() { TSTree *error_tree = ts_tree_make_error( ts_length_zero(), ts_length_zero(), 'z'); - AssertThat(ts_tree_has_error(error_tree), IsTrue()); + AssertThat(ts_tree_is_fragile_left(error_tree), IsTrue()); + AssertThat(ts_tree_is_fragile_right(error_tree), IsTrue()); }); }); @@ -138,11 +140,12 @@ describe("Tree", []() { }); }); - describe("when one of the child nodes has an error", [&]() { + describe("when the first node is fragile on the left side", [&]() { TSTree *parent; before_each([&]() { - tree2->options = (TSTreeOptions)(TSTreeOptionsHasError|TSTreeOptionsExtra); + ts_tree_set_fragile_left(tree1); + ts_tree_set_extra(tree1); parent = ts_tree_make_node(pig, 2, tree_array({ tree1, tree2, @@ -153,14 +156,51 @@ describe("Tree", []() { ts_tree_release(parent); }); - it("records that it contains an error", [&]() { - AssertThat(ts_tree_has_error(parent), IsTrue()); + it("records that it is fragile on the left side", [&]() { + AssertThat(ts_tree_is_fragile_left(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("when the last node is fragile on the right side", [&]() { + TSTree *parent; + + before_each([&]() { + ts_tree_set_fragile_right(tree2); + ts_tree_set_extra(tree2); + parent = ts_tree_make_node(pig, 2, tree_array({ + tree1, + tree2, + }), 0); + }); + + after_each([&]() { + ts_tree_release(parent); + }); + + it("records that it is fragile on the right side", [&]() { + AssertThat(ts_tree_is_fragile_right(parent), IsTrue()); + }); + }); + + describe("when the outer nodes aren't fragile on their outer side", [&]() { + TSTree *parent; + + before_each([&]() { + ts_tree_set_fragile_right(tree1); + ts_tree_set_fragile_left(tree2); + parent = ts_tree_make_node(pig, 2, tree_array({ + tree1, + tree2, + }), 0); + }); + + after_each([&]() { + ts_tree_release(parent); + }); + + it("records that it is not fragile", [&]() { + AssertThat(ts_tree_is_fragile_left(parent), IsFalse()); + AssertThat(ts_tree_is_fragile_right(parent), IsFalse()); }); }); }); diff --git a/src/runtime/parser.c b/src/runtime/parser.c index 641e7c8b..a434acf7 100644 --- a/src/runtime/parser.c +++ b/src/runtime/parser.c @@ -106,7 +106,8 @@ static TSTree *break_down_right_stack(TSParser *parser) { TSParseAction action = get_action(parser->language, state, node->symbol); bool is_usable = (action.type != TSParseActionTypeError) && - !ts_tree_has_error(node) && !ts_tree_is_extra(node); + !ts_tree_is_extra(node) && + !ts_tree_is_fragile_left(node) && !ts_tree_is_fragile_right(node); if (is_usable && right_subtree_start == current_position.chars) { ts_stack_shrink(&parser->right_stack, parser->right_stack.size - 1); return node; diff --git a/src/runtime/tree.c b/src/runtime/tree.c index 7efd4c54..4a0f1c44 100644 --- a/src/runtime/tree.c +++ b/src/runtime/tree.c @@ -21,7 +21,8 @@ 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); + ts_tree_set_fragile_left(result); + ts_tree_set_fragile_right(result); result->lookahead_char = lookahead_char; return result; } @@ -35,7 +36,6 @@ 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); @@ -51,22 +51,21 @@ 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 && (ts_tree_is_visible(children[0]) || ts_tree_is_wrapper(children[0]))) options |= (TSTreeOptionsWrapper | TSTreeOptionsHidden); + if (child_count > 0 && ts_tree_is_fragile_left(children[0])) + options |= (TSTreeOptionsFragileLeft); + if (child_count > 0 && ts_tree_is_fragile_right(children[child_count - 1])) + options |= (TSTreeOptionsFragileRight); /* * Store the visible child array adjacent to the tree itself. This avoids diff --git a/src/runtime/tree.h b/src/runtime/tree.h index 528a2d01..e9ecf7c2 100644 --- a/src/runtime/tree.h +++ b/src/runtime/tree.h @@ -12,7 +12,8 @@ typedef enum { TSTreeOptionsHidden = 1 << 0, TSTreeOptionsExtra = 1 << 1, TSTreeOptionsWrapper = 1 << 2, - TSTreeOptionsHasError = 1 << 3, + TSTreeOptionsFragileLeft = 1 << 3, + TSTreeOptionsFragileRight = 1 << 4, } TSTreeOptions; struct TSTree { @@ -44,10 +45,6 @@ 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); } @@ -56,8 +53,20 @@ 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); +static inline void ts_tree_set_fragile_left(TSTree *tree) { + ts_tree_set_options(tree, TSTreeOptionsFragileLeft); +} + +static inline void ts_tree_set_fragile_right(TSTree *tree) { + ts_tree_set_options(tree, TSTreeOptionsFragileRight); +} + +static inline bool ts_tree_is_fragile_left(TSTree *tree) { + return tree->options & TSTreeOptionsFragileLeft; +} + +static inline bool ts_tree_is_fragile_right(TSTree *tree) { + return tree->options & TSTreeOptionsFragileRight; } TSTree *ts_tree_make_leaf(TSSymbol, TSLength, TSLength, bool);