@@ -37,11 +37,20 @@ export class Parser<T> {
37
37
. map ( ( { value, length } ) => ( { value : mapper ( value ) , length } ) )
38
38
) ;
39
39
}
40
+ mapWithPositionedError < U > ( mapper : ( value : T ) => U ) : Parser < U > {
41
+ return withPosition ( this )
42
+ . map ( ( value ) => withPositionedError ( ( ) => mapper ( value . value ) , value ) ) ;
43
+ }
40
44
filter ( mapper : ( value : T ) => boolean ) : Parser < T > {
41
45
return new Parser ( ( input ) =>
42
46
this . rawParser ( input ) . filter ( ( { value } ) => mapper ( value ) )
43
47
) ;
44
48
}
49
+ filterWithPositionedError ( mapper : ( value : T ) => boolean ) : Parser < T > {
50
+ return withPosition ( this )
51
+ . filter ( ( value ) => withPositionedError ( ( ) => mapper ( value . value ) , value ) )
52
+ . map ( ( { value } ) => value ) ;
53
+ }
45
54
then < U > ( mapper : ( value : T ) => Parser < U > ) : Parser < U > {
46
55
return new Parser ( ( position ) =>
47
56
this . rawParser ( position )
@@ -71,13 +80,36 @@ export class Parser<T> {
71
80
return sequence ( this , parser ) . map ( ( [ arrayResult ] ) => arrayResult ) ;
72
81
}
73
82
}
74
- export class UnexpectedError extends ArrayResultError {
75
- constructor ( unexpected : string , expected : string ) {
76
- super ( `unexpected ${ unexpected } . ${ expected } were expected instead` ) ;
83
+ export type Position = Readonly < { position : number ; length : number } > ;
84
+ export class PositionedError extends ArrayResultError {
85
+ public position : null | Position ;
86
+ constructor ( message : string , position ?: Position ) {
87
+ super ( message ) ;
88
+ this . position = position ?? null ;
89
+ this . name = "PositionedError" ;
90
+ }
91
+ }
92
+ function withPositionedError < T > ( fn : ( ) => T , position : Position ) : T {
93
+ try {
94
+ return fn ( ) ;
95
+ } catch ( error ) {
96
+ if ( typeof error === "string" ) {
97
+ throw new PositionedError ( error , position ) ;
98
+ } else {
99
+ throw error ;
100
+ }
101
+ }
102
+ }
103
+ export class UnexpectedError extends PositionedError {
104
+ constructor ( unexpected : string , expected : string , position ?: Position ) {
105
+ super (
106
+ `unexpected ${ unexpected } . ${ expected } were expected instead` ,
107
+ position ,
108
+ ) ;
77
109
this . name = "UnexpectedError" ;
78
110
}
79
111
}
80
- export class UnrecognizedError extends ArrayResultError {
112
+ export class UnrecognizedError extends PositionedError {
81
113
constructor ( element : string ) {
82
114
super ( `${ element } is unrecognized` ) ;
83
115
this . name = "UnrecognizedError" ;
@@ -183,36 +215,47 @@ export function count(
183
215
) : Parser < number > {
184
216
return parser . map ( ( { length } ) => length ) ;
185
217
}
186
- function describeSource ( source : string ) : string {
187
- if ( source === "" ) {
188
- return "end of text" ;
218
+ function generateError (
219
+ position : number ,
220
+ expected : string ,
221
+ ) : ArrayResult < never > {
222
+ let source : string ;
223
+ let length : number ;
224
+ if ( position === currentSource . length ) {
225
+ source = "end of text" ;
226
+ length = 0 ;
189
227
} else {
190
- const [ token ] = source . match ( / \S * / ) ! ;
228
+ const sourceString = currentSource . slice ( position ) ;
229
+ const [ token ] = sourceString . match ( / \S * / ) ! ;
191
230
if ( token === "" ) {
192
- if ( / ^ \r ? \n / . test ( source ) ) {
193
- return "newline" ;
231
+ if ( / ^ \r ? \n / . test ( sourceString ) ) {
232
+ source = "newline" ;
233
+ length = 0 ;
194
234
} else {
195
- return "space" ;
235
+ const [ token ] = sourceString . match ( / \s * ?(? = \r ? \n ) / ) ! ;
236
+ source = "space" ;
237
+ length = token . length ;
196
238
}
197
239
} else {
198
- return `"${ token } "` ;
240
+ source = `"${ token } "` ;
241
+ length = sourceString . length ;
199
242
}
200
243
}
244
+ return new ArrayResult (
245
+ new UnexpectedError ( source , expected , { position, length } ) ,
246
+ ) ;
201
247
}
202
248
export function matchCapture (
203
249
regex : RegExp ,
204
250
description : string ,
205
251
) : Parser < RegExpMatchArray > {
206
252
const newRegex = new RegExp ( `^${ regex . source } ` , regex . flags ) ;
207
253
return new Parser ( ( position ) => {
208
- const sourceString = currentSource . slice ( position ) ;
209
- const match = sourceString . match ( newRegex ) ;
254
+ const match = currentSource . slice ( position ) . match ( newRegex ) ;
210
255
if ( match != null ) {
211
256
return new ArrayResult ( [ { value : match , length : match [ 0 ] . length } ] ) ;
212
257
} else {
213
- return new ArrayResult (
214
- new UnexpectedError ( describeSource ( sourceString ) , description ) ,
215
- ) ;
258
+ return generateError ( position , description ) ;
216
259
}
217
260
} ) ;
218
261
}
@@ -233,12 +276,7 @@ export function matchString(
233
276
length : match . length ,
234
277
} ] ) ;
235
278
} else {
236
- return new ArrayResult (
237
- new UnexpectedError (
238
- describeSource ( currentSource . slice ( position ) ) ,
239
- description ,
240
- ) ,
241
- ) ;
279
+ return generateError ( position , description ) ;
242
280
}
243
281
} ) ;
244
282
}
@@ -250,19 +288,20 @@ export const allRest = new Parser((position) =>
250
288
) ;
251
289
export const end = new Parser ( ( position ) =>
252
290
position === currentSource . length
291
+ ? new ArrayResult ( [ { value : null , length : 0 } ] )
292
+ : generateError ( position , "end of text" )
293
+ ) ;
294
+ export const notEnd = new Parser ( ( position ) =>
295
+ position < currentSource . length
253
296
? new ArrayResult ( [ { value : null , length : 0 } ] )
254
297
: new ArrayResult (
255
298
new UnexpectedError (
256
- describeSource ( currentSource . slice ( position ) ) ,
257
299
"end of text" ,
300
+ "not end of text" ,
301
+ { position, length : currentSource . length - position } ,
258
302
) ,
259
303
)
260
304
) ;
261
- export const notEnd = new Parser ( ( position ) =>
262
- position < currentSource . length
263
- ? new ArrayResult ( [ { value : null , length : 0 } ] )
264
- : new ArrayResult ( new UnexpectedError ( "end of text" , "not end of text" ) )
265
- ) ;
266
305
export function withSource < T > (
267
306
parser : Parser < T > ,
268
307
) : Parser < readonly [ value : T , source : string ] > {
@@ -276,14 +315,36 @@ export function withSource<T>(
276
315
} ) )
277
316
) ;
278
317
}
318
+ export function withPosition < T > (
319
+ parser : Parser < T > ,
320
+ ) : Parser < Readonly < { value : T } > & Position > {
321
+ return new Parser ( ( position ) =>
322
+ parser . rawParser ( position ) . map ( ( { value, length } ) => ( {
323
+ value : { value, position, length } ,
324
+ length,
325
+ } ) )
326
+ ) ;
327
+ }
279
328
export class CheckedParser < T > {
280
329
constructor ( public check : Parser < unknown > , public parser : Parser < T > ) { }
281
330
map < U > ( mapper : ( value : T ) => U ) : CheckedParser < U > {
282
331
return new CheckedParser ( this . check , this . parser . map ( mapper ) ) ;
283
332
}
333
+ mapWithPositionedError < U > ( mapper : ( value : T ) => U ) : CheckedParser < U > {
334
+ return new CheckedParser (
335
+ this . check ,
336
+ this . parser . mapWithPositionedError ( mapper ) ,
337
+ ) ;
338
+ }
284
339
filter ( check : ( value : T ) => boolean ) : CheckedParser < T > {
285
340
return new CheckedParser ( this . check , this . parser . filter ( check ) ) ;
286
341
}
342
+ filterWithPositionedError ( check : ( value : T ) => boolean ) : CheckedParser < T > {
343
+ return new CheckedParser (
344
+ this . check ,
345
+ this . parser . filterWithPositionedError ( check ) ,
346
+ ) ;
347
+ }
287
348
}
288
349
export function checkedSequence < T , U > (
289
350
check : Parser < T > ,
0 commit comments