Skip to content

Commit

Permalink
Close Bug #71 ParseRuleContext in empty input has undefined stop token
Browse files Browse the repository at this point in the history
Signed-off-by: Mike Lischke <[email protected]>
  • Loading branch information
mike-lischke committed Sep 14, 2024
1 parent 51aec40 commit 4eec0b9
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 7 deletions.
24 changes: 19 additions & 5 deletions src/BufferedTokenStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,13 +303,27 @@ export class BufferedTokenStream implements TokenStream {
}

/**
* Given a starting index, return the index of the previous token on channel.
* Return i if tokens[i] is on channel. Return -1 if there are no tokens
* on channel between i and 0.
* Given a starting index, return the index of the previous token on
* channel. Return `i` if `tokens[i]` is on channel. Return -1
* if there are no tokens on channel between `i` and 0.
*
* If `i` specifies an index at or after the EOF token, the EOF token
* index is returned. This is due to the fact that the EOF token is treated
* as though it were on every channel.
*/
public previousTokenOnChannel(i: number, channel: number): number {
while (i >= 0 && this.tokens[i].channel !== channel) {
i -= 1;
if (i >= this.tokens.length) {
// The EOF token is on every channel.
return this.tokens.length - 1;
}

while (i >= 0) {
const token = this.tokens[i];
if (token.type === Token.EOF || token.channel === channel) {
return i;
}

--i;
}

return i;
Expand Down
15 changes: 13 additions & 2 deletions src/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export abstract class Parser extends Recognizer<ParserATNSimulator> {
this.errorHandler.reset(this);
this.context = null;
this.syntaxErrors = 0;
this.matchedEOF = false;
this.setTrace(false);
this.precedenceStack = [];
this.precedenceStack.push(0);
Expand Down Expand Up @@ -150,6 +151,10 @@ export abstract class Parser extends Recognizer<ParserATNSimulator> {
public match(ttype: number): Token {
let t = this.getCurrentToken();
if (t.type === ttype) {
if (ttype === Token.EOF) {
this.matchedEOF = true;
}

this.errorHandler.reportMatch(this);
this.consume();
} else {
Expand Down Expand Up @@ -473,8 +478,14 @@ export abstract class Parser extends Recognizer<ParserATNSimulator> {
}

public exitRule(): void {
this.context!.stop = this.inputStream.LT(-1);
// trigger event on _ctx, before it reverts to parent
if (this.matchedEOF) {
// If we have matched EOF, it cannot consume past EOF so we use LT(1) here.
this.context!.stop = this.inputStream.LT(1); // LT(1) will be end of file
} else {
this.context!.stop = this.inputStream.LT(-1); // stop node is what we just matched
}

// Trigger event on context, before it reverts to parent.
this.triggerExitRuleEvent();
this.state = this.context!.invokingState;
this.context = this.context!.parent;
Expand Down

0 comments on commit 4eec0b9

Please sign in to comment.