@@ -4,7 +4,14 @@ import {
4
4
ANTLRErrorListener ,
5
5
RecognitionException ,
6
6
ATNSimulator ,
7
+ LexerNoViableAltException ,
8
+ Lexer ,
9
+ Parser ,
10
+ InputMismatchException ,
11
+ NoViableAltException ,
7
12
} from 'antlr4ng' ;
13
+ import { LOCALE_TYPE } from './types' ;
14
+ import { transform } from './transform' ;
8
15
9
16
/**
10
17
* Converted from {@link SyntaxError}.
@@ -39,10 +46,12 @@ export interface SyntaxError {
39
46
*/
40
47
export type ErrorListener = ( parseError : ParseError , originalError : SyntaxError ) => void ;
41
48
42
- export class ParseErrorListener implements ANTLRErrorListener {
49
+ export abstract class ParseErrorListener implements ANTLRErrorListener {
43
50
private _errorListener : ErrorListener ;
51
+ private locale : LOCALE_TYPE ;
44
52
45
- constructor ( errorListener : ErrorListener ) {
53
+ constructor ( errorListener : ErrorListener , locale : LOCALE_TYPE = 'en_US' ) {
54
+ this . locale = locale ;
46
55
this . _errorListener = errorListener ;
47
56
}
48
57
@@ -52,6 +61,8 @@ export class ParseErrorListener implements ANTLRErrorListener {
52
61
53
62
reportContextSensitivity ( ) { }
54
63
64
+ protected abstract getExpectedText ( parser : Parser , token : Token ) : string ;
65
+
55
66
syntaxError (
56
67
recognizer : Recognizer < ATNSimulator > ,
57
68
offendingSymbol : Token | null ,
@@ -60,6 +71,87 @@ export class ParseErrorListener implements ANTLRErrorListener {
60
71
msg : string ,
61
72
e : RecognitionException
62
73
) {
74
+ let message = '' ;
75
+ // If not undefined then offendingSymbol is of type Token.
76
+ if ( offendingSymbol ) {
77
+ let token = offendingSymbol as Token ;
78
+ const parser = recognizer as Parser ;
79
+
80
+ // judge token is EOF
81
+ const isEof = token . type === Token . EOF ;
82
+ if ( isEof ) {
83
+ token = parser . tokenStream . get ( token . tokenIndex - 1 ) ;
84
+ }
85
+ const wrongText = token . text ?? '' ;
86
+
87
+ const isInComplete = isEof && wrongText !== ' ' ;
88
+
89
+ const expectedText = isInComplete ? '' : this . getExpectedText ( parser , token ) ;
90
+
91
+ if ( ! e ) {
92
+ // handle missing or unwanted tokens.
93
+ message = msg ;
94
+ if ( msg . includes ( 'extraneous' ) ) {
95
+ message = `'${ wrongText } ' {noValidPosition}${
96
+ expectedText . length ? `{expecting}${ expectedText } ` : ''
97
+ } `;
98
+ }
99
+ if ( msg . includes ( 'missing' ) ) {
100
+ const regex = / m i s s i n g \s + ' ( [ ^ ' ] + ) ' / ;
101
+ const match = msg . match ( regex ) ;
102
+ message = `{missing}` ;
103
+ if ( match ) {
104
+ const missKeyword = match [ 1 ] ;
105
+ message += `'${ missKeyword } '` ;
106
+ } else {
107
+ message += `{keyword}` ;
108
+ }
109
+ message += `{at}'${ wrongText } '` ;
110
+ }
111
+ } else {
112
+ // handle mismatch exception or no viable alt exception
113
+ if ( e instanceof InputMismatchException || e instanceof NoViableAltException ) {
114
+ if ( isEof ) {
115
+ message = `{stmtInComplete}` ;
116
+ } else {
117
+ message = `'${ wrongText } ' {noValidPosition}` ;
118
+ }
119
+ if ( expectedText . length > 0 ) {
120
+ message += `{expecting}${ expectedText } ` ;
121
+ }
122
+ } else {
123
+ message = msg ;
124
+ }
125
+ }
126
+ } else {
127
+ // No offending symbol, which indicates this is a lexer error.
128
+ if ( e instanceof LexerNoViableAltException ) {
129
+ const lexer = recognizer as Lexer ;
130
+ const input = lexer . inputStream ;
131
+ let text = lexer . getErrorDisplay (
132
+ input . getText ( lexer . _tokenStartCharIndex , input . index )
133
+ ) ;
134
+ switch ( text [ 0 ] ) {
135
+ case '/' :
136
+ message = '{unfinishedMultilineComment}' ;
137
+ break ;
138
+ case '"' :
139
+ message = '{unfinishedDoubleQuoted}' ;
140
+ break ;
141
+ case "'" :
142
+ message = '{unfinishedSingleQuoted}' ;
143
+ break ;
144
+ case '`' :
145
+ message = '{unfinishedTickQuoted}' ;
146
+ break ;
147
+
148
+ default :
149
+ message = '"' + text + '" {noValidInput}' ;
150
+ break ;
151
+ }
152
+ }
153
+ }
154
+ message = transform ( message , this . locale ) ;
63
155
let endCol = charPositionInLine + 1 ;
64
156
if ( offendingSymbol && offendingSymbol . text !== null ) {
65
157
endCol = charPositionInLine + offendingSymbol . text . length ;
@@ -71,7 +163,7 @@ export class ParseErrorListener implements ANTLRErrorListener {
71
163
endLine : line ,
72
164
startColumn : charPositionInLine + 1 ,
73
165
endColumn : endCol + 1 ,
74
- message : msg ,
166
+ message,
75
167
} ,
76
168
{
77
169
e,
0 commit comments