11// Antlr
2- import { CharStream , CommonTokenStream , Token , TokenStream } from 'antlr4ng' ;
2+ import { CharStream , CommonTokenStream , InputMismatchException , IntervalSet , Token , TokenStream } from 'antlr4ng' ;
33import { DefaultErrorStrategy , Parser , RecognitionException } from 'antlr4ng' ;
44import { vbaLexer } from '../../antlr/out/vbaLexer' ;
55import { vbaParser } from '../../antlr/out/vbaParser' ;
@@ -89,12 +89,80 @@ export class VbaFmtParser extends vbafmtParser {
8989
9090
9191export class VbaErrorHandler extends DefaultErrorStrategy {
92- recover ( recognizer : Parser , e : RecognitionException ) : void {
92+ override recover ( recognizer : Parser , e : RecognitionException ) : void {
9393 // Consume the error token if look-ahead is not EOF.
9494 const inputStream = recognizer . inputStream ;
95- if ( inputStream . LA ( 1 ) = == Token . EOF ) {
95+ if ( inputStream . LA ( 1 ) ! == Token . EOF ) {
9696 inputStream . consume ( ) ;
9797 }
9898 this . endErrorCondition ( recognizer ) ;
9999 }
100+
101+ override recoverInline ( recognizer : Parser ) : Token {
102+ const stream = recognizer . inputStream ;
103+ const thisToken = recognizer . getCurrentToken ( ) ;
104+
105+ // Recover using deletion strategy.
106+ const nextToken = stream . LT ( 2 ) ;
107+ const expectedTokens = recognizer . getExpectedTokens ( ) ;
108+ if ( nextToken && expectedTokens . contains ( nextToken . type ) ) {
109+ recognizer . consume ( ) ;
110+ this . reportMatch ( recognizer ) ;
111+ return thisToken ;
112+ }
113+
114+ // Failsafe to prevent circular insertions.
115+ const MAXRECURSION = - 20
116+ for ( let i = - 1 ; i >= MAXRECURSION ; i -- ) {
117+ if ( i <= - 20 ) {
118+ throw new InputMismatchException ( recognizer ) ;
119+ }
120+ const wasInsertedToken = this . isTokenPositionMatch ( thisToken , recognizer . inputStream . LT ( i ) ) ;
121+ if ( ! wasInsertedToken ) {
122+ break ;
123+ }
124+ }
125+
126+ // Recover using insertion strategy.
127+ const missingToken = this . createErrorToken ( recognizer , expectedTokens ) ;
128+ this . reportMatch ( recognizer ) ;
129+ return missingToken ;
130+ }
131+
132+ private createErrorToken ( recognizer : Parser , expectedTokens : IntervalSet ) : Token {
133+ // Set up the token attributes.
134+ const type = expectedTokens . length === 0
135+ ? Token . INVALID_TYPE
136+ : expectedTokens . minElement ;
137+
138+ const expectedIdentifiers = expectedTokens . toArray ( ) . map (
139+ t => recognizer . vocabulary . getLiteralName ( t )
140+ ?? recognizer . vocabulary . getDisplayName ( t )
141+ ) ;
142+ const plural = expectedIdentifiers . length > 1 ? 's' : '' ;
143+ const expectedText = expectedIdentifiers . join ( ', ' ) ;
144+ const text = `<missing token${ plural } ${ expectedText } >` ;
145+ const currentToken = recognizer . getCurrentToken ( ) ;
146+
147+ // Create the token.
148+ return recognizer . getTokenFactory ( ) . create (
149+ [
150+ recognizer . tokenStream . tokenSource ,
151+ recognizer . tokenStream . tokenSource . inputStream
152+ ] ,
153+ type ,
154+ text ,
155+ Token . DEFAULT_CHANNEL ,
156+ currentToken . start ,
157+ currentToken . stop ,
158+ currentToken . line ,
159+ currentToken . column
160+ )
161+ }
162+
163+ private isTokenPositionMatch ( a : Token | null , b : Token | null ) : boolean {
164+ return ! ! a && ! ! b
165+ && a . line === b . line
166+ && a . column === b . column ;
167+ }
100168}
0 commit comments