diff --git a/spec/fixtures/error_corpus/javascript_errors.txt b/spec/fixtures/error_corpus/javascript_errors.txt index 03a80189..973db0a8 100644 --- a/spec/fixtures/error_corpus/javascript_errors.txt +++ b/spec/fixtures/error_corpus/javascript_errors.txt @@ -65,3 +65,17 @@ if ({a: 'b'} {c: 'd'}) { (ERROR (function (formal_parameters (identifier)) (statement_block (expression_statement (identifier))))))))) + +=================================================== +one invalid token at the end of the file +=================================================== + +// skip the equals sign +a.b = +--- + +(program + (comment) + (trailing_expression_statement + (member_access (identifier) (identifier))) + (ERROR)) diff --git a/spec/runtime/parser_spec.cc b/spec/runtime/parser_spec.cc index 655a2354..085a5d7c 100644 --- a/spec/runtime/parser_spec.cc +++ b/spec/runtime/parser_spec.cc @@ -272,7 +272,8 @@ describe("Parser", [&]() { insert_text(strlen("var x = y"), " *"); assert_root_node( - "(program (var_declaration (identifier) (ERROR (identifier))))"); + "(program (var_declaration (var_assignment " + "(identifier) (identifier)) (ERROR)))"); insert_text(strlen("var x = y *"), " z"); diff --git a/src/runtime/parser.c b/src/runtime/parser.c index 86dfc417..282e3c36 100644 --- a/src/runtime/parser.c +++ b/src/runtime/parser.c @@ -903,7 +903,57 @@ error: return PotentialReductionsFailed; } -static bool parser__handle_error(Parser *self, StackVersion version) { +typedef struct { + Parser *parser; + TSSymbol lookahead_symbol; +} SkipPrecedingTokensSession; + +static StackIterateAction parser__repair_consumed_error_callback( + void *payload, TSStateId state, TreeArray *trees, size_t tree_count, + bool is_done, bool is_pending) { + if (tree_count > 0 && state != TS_STATE_ERROR) { + SkipPrecedingTokensSession *session = payload; + Parser *self = session->parser; + TSSymbol lookahead_symbol = session->lookahead_symbol; + const TSParseAction *action = + ts_language_last_action(self->language, state, lookahead_symbol); + if (action && action->type == TSParseActionTypeReduce) { + return StackIteratePop | StackIterateStop; + } + } + return StackIterateNone; +} + +static bool parser__repair_consumed_error(Parser *self, StackVersion version, + TSSymbol lookahead_symbol) { + SkipPrecedingTokensSession session = { self, lookahead_symbol }; + StackPopResult pop = ts_stack_iterate( + self->stack, version, parser__repair_consumed_error_callback, &session); + CHECK(pop.status); + + StackVersion last_slice_version = STACK_VERSION_NONE; + for (size_t i = 0; i < pop.slices.size; i++) { + StackSlice slice = pop.slices.contents[i]; + if (slice.version == last_slice_version) { + ts_tree_array_delete(&slice.trees); + continue; + } + + last_slice_version = slice.version; + TSTree *error = ts_tree_make_error_node(&slice.trees); + CHECK(error); + error->extra = true; + TSStateId state = ts_stack_top_state(self->stack, slice.version); + parser__push(self, slice.version, error, state); + } + + return true; +error: + return false; +} + +static bool parser__handle_error(Parser *self, StackVersion version, + TSSymbol lookahead_symbol) { unsigned error_cost = ts_stack_error_cost(self->stack, version); unsigned error_depth = ts_stack_error_depth(self->stack, version) + 1; if (parser__better_version_exists(self, version, error_depth, error_cost)) { @@ -913,6 +963,7 @@ static bool parser__handle_error(Parser *self, StackVersion version) { } LOG("handle_error"); + CHECK(parser__repair_consumed_error(self, version, lookahead_symbol)); size_t previous_version_count = ts_stack_version_count(self->stack); for (StackVersion v = version; v < ts_stack_version_count(self->stack);) { @@ -1141,7 +1192,7 @@ static bool parser__advance(Parser *self, StackVersion version, return parser__push(self, version, lookahead, TS_STATE_ERROR); } - CHECK(parser__handle_error(self, version)); + CHECK(parser__handle_error(self, version, lookahead->symbol)); if (ts_stack_is_halted(self->stack, version)) { ts_tree_release(lookahead);