@@ -37,11 +37,20 @@ export class Parser<T> {
3737 . map ( ( { value, length } ) => ( { value : mapper ( value ) , length } ) )
3838 ) ;
3939 }
40+ mapWithPositionedError < U > ( mapper : ( value : T ) => U ) : Parser < U > {
41+ return withPosition ( this )
42+ . map ( ( value ) => withPositionedError ( ( ) => mapper ( value . value ) , value ) ) ;
43+ }
4044 filter ( mapper : ( value : T ) => boolean ) : Parser < T > {
4145 return new Parser ( ( input ) =>
4246 this . rawParser ( input ) . filter ( ( { value } ) => mapper ( value ) )
4347 ) ;
4448 }
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+ }
4554 then < U > ( mapper : ( value : T ) => Parser < U > ) : Parser < U > {
4655 return new Parser ( ( position ) =>
4756 this . rawParser ( position )
@@ -71,13 +80,36 @@ export class Parser<T> {
7180 return sequence ( this , parser ) . map ( ( [ arrayResult ] ) => arrayResult ) ;
7281 }
7382}
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+ ) ;
77109 this . name = "UnexpectedError" ;
78110 }
79111}
80- export class UnrecognizedError extends ArrayResultError {
112+ export class UnrecognizedError extends PositionedError {
81113 constructor ( element : string ) {
82114 super ( `${ element } is unrecognized` ) ;
83115 this . name = "UnrecognizedError" ;
@@ -183,36 +215,47 @@ export function count(
183215) : Parser < number > {
184216 return parser . map ( ( { length } ) => length ) ;
185217}
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 ;
189227 } else {
190- const [ token ] = source . match ( / \S * / ) ! ;
228+ const sourceString = currentSource . slice ( position ) ;
229+ const [ token ] = sourceString . match ( / \S * / ) ! ;
191230 if ( token === "" ) {
192- if ( / ^ \r ? \n / . test ( source ) ) {
193- return "newline" ;
231+ if ( / ^ \r ? \n / . test ( sourceString ) ) {
232+ source = "newline" ;
233+ length = 0 ;
194234 } else {
195- return "space" ;
235+ const [ token ] = sourceString . match ( / \s * ?(? = \r ? \n ) / ) ! ;
236+ source = "space" ;
237+ length = token . length ;
196238 }
197239 } else {
198- return `"${ token } "` ;
240+ source = `"${ token } "` ;
241+ length = sourceString . length ;
199242 }
200243 }
244+ return new ArrayResult (
245+ new UnexpectedError ( source , expected , { position, length } ) ,
246+ ) ;
201247}
202248export function matchCapture (
203249 regex : RegExp ,
204250 description : string ,
205251) : Parser < RegExpMatchArray > {
206252 const newRegex = new RegExp ( `^${ regex . source } ` , regex . flags ) ;
207253 return new Parser ( ( position ) => {
208- const sourceString = currentSource . slice ( position ) ;
209- const match = sourceString . match ( newRegex ) ;
254+ const match = currentSource . slice ( position ) . match ( newRegex ) ;
210255 if ( match != null ) {
211256 return new ArrayResult ( [ { value : match , length : match [ 0 ] . length } ] ) ;
212257 } else {
213- return new ArrayResult (
214- new UnexpectedError ( describeSource ( sourceString ) , description ) ,
215- ) ;
258+ return generateError ( position , description ) ;
216259 }
217260 } ) ;
218261}
@@ -233,12 +276,7 @@ export function matchString(
233276 length : match . length ,
234277 } ] ) ;
235278 } else {
236- return new ArrayResult (
237- new UnexpectedError (
238- describeSource ( currentSource . slice ( position ) ) ,
239- description ,
240- ) ,
241- ) ;
279+ return generateError ( position , description ) ;
242280 }
243281 } ) ;
244282}
@@ -250,19 +288,20 @@ export const allRest = new Parser((position) =>
250288) ;
251289export const end = new Parser ( ( position ) =>
252290 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
253296 ? new ArrayResult ( [ { value : null , length : 0 } ] )
254297 : new ArrayResult (
255298 new UnexpectedError (
256- describeSource ( currentSource . slice ( position ) ) ,
257299 "end of text" ,
300+ "not end of text" ,
301+ { position, length : currentSource . length - position } ,
258302 ) ,
259303 )
260304) ;
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- ) ;
266305export function withSource < T > (
267306 parser : Parser < T > ,
268307) : Parser < readonly [ value : T , source : string ] > {
@@ -276,14 +315,36 @@ export function withSource<T>(
276315 } ) )
277316 ) ;
278317}
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+ }
279328export class CheckedParser < T > {
280329 constructor ( public check : Parser < unknown > , public parser : Parser < T > ) { }
281330 map < U > ( mapper : ( value : T ) => U ) : CheckedParser < U > {
282331 return new CheckedParser ( this . check , this . parser . map ( mapper ) ) ;
283332 }
333+ mapWithPositionedError < U > ( mapper : ( value : T ) => U ) : CheckedParser < U > {
334+ return new CheckedParser (
335+ this . check ,
336+ this . parser . mapWithPositionedError ( mapper ) ,
337+ ) ;
338+ }
284339 filter ( check : ( value : T ) => boolean ) : CheckedParser < T > {
285340 return new CheckedParser ( this . check , this . parser . filter ( check ) ) ;
286341 }
342+ filterWithPositionedError ( check : ( value : T ) => boolean ) : CheckedParser < T > {
343+ return new CheckedParser (
344+ this . check ,
345+ this . parser . filterWithPositionedError ( check ) ,
346+ ) ;
347+ }
287348}
288349export function checkedSequence < T , U > (
289350 check : Parser < T > ,
0 commit comments