Skip to content

Rewind index to improve error recovery #292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 16, 2018
Merged

Conversation

stasm
Copy link
Contributor

@stasm stasm commented Oct 15, 2018

The error recovery logic in the reference parser and the optimistic runtime parser is able to resume parsing at an offset which precedes the exact index of the error. Consider the following example:

foo = {
bar = Bar
     ^----- Expected token: "}"

The error occurs well into the bar… line. At the same time, I would expect bar to be a functional message and to parse correctly. Messages preceding any given message should not be allowed to break it.

This wasn't easy to achieve with the ParserStream architecture based on an iterator over the source string. I re-wrote it to use a simpler cursor-based approach. As it turned out, this simplified stream.js quite much and it also improved the performance :)

jsshell 63 (ms, compared to zeroseven):
  mean:   28.86 (-28%)
  stdev:  1.35
  sample: 30

node.js 9.11 (ms, compared to zeroseven):
  mean:   8.51 (-32%)
  stdev:  1.05
  sample: 30

Copy link
Contributor

@Pike Pike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As much as I understand the parser, this looks good to me. Simpler code is good, and also more consistency between stream API usage in parser and ftlstream.

I found one issue, where our error position is outside of our junk entry, and I don't think that's right. Good news, AFAICT, you have all the right information in getEntryOrJunk already.

And one stylistic nit.

The behavior tests are always a bit of black magic to me. They look better now than before (wth # key-05), but that's not a strong statement.

I'd love to cross-check the fixtures after the fix, so doing a request-changes instead of approve-with-comments.

"span": {
"type": "Span",
"start": 16,
"end": 16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should rewind the error position, too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we rewind the error position, pretty printing the annotations (or highlighting them in IDEs) would always point to the beginning of the line. Which I'll admit is sometimes good, and sometimes bad :) It's the choice between:

A) Error position unchanged.

# This makes sense to me.
! E0003 on line 2:
  | foo = {
  | invalid placeable }
  …         ^----- Expected token: "}"

# Meh, this is kind of misleading.
! E0003 on line 2:
  | foo = {
  | bar = Bar
  …     ^----- Expected token: "}"

And:

B) Error position rewound.

# Meh, we could do better.
! E0003 on line 2:
  | foo = {
  | invalid placeable }
  … ^----- Expected token: "}"

# This makes sense to me.
! E0003 on line 2:
  | foo = {
  | bar = Bar
  … ^----- Expected token: "}"

I take it that you vote for B, as it's likely more common?


return ret === ch;
this.peekOffset++;
return this.currentPeek;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just sugar, I'd love for next() and peek() to be symmetric in their implementation. I can see reasons for both ways, maybe perf can throw a dice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean. There's no difference in the perf benchmarks, but I like the idea of making the implementation symmetric. Also, I think I'll ditch the pre-increment operator in next(). It's easy to miss when reading the code.

Copy link
Contributor

@Pike Pike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r=me, not feeling strongly about the const nit, I'm not zibi ;-)

ps.skipToNextEntryStart(entryStartPos);
const nextEntryStart = ps.index;
let nextEntryStart = ps.index;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, this can still be a const, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes. I stopped using const in most of the code I write these days and only use them for actual constants. I typed let out of habit. I'll revert to const for consistency with the rest of the file.

@stasm stasm merged commit bcf6799 into projectfluent:zeroseven Oct 16, 2018
@stasm stasm deleted the rewind branch October 16, 2018 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants