11import * as either from '@matt.kantor/either'
22import type {
3+ InvalidInputError ,
34 Note ,
45 Parser ,
56 ParserResult ,
67 ParserWhichAlwaysSucceeds ,
78 Success ,
89} from './parser.js'
10+ import {
11+ deduplicateNotes ,
12+ furthest ,
13+ furthestOrUndefined ,
14+ messageForExpectations ,
15+ } from './internal.js'
916
1017/**
1118 * Substitute the output of a successful parse.
@@ -17,6 +24,7 @@ export const as = <NewOutput>(
1724 const replaceOutput = ( success : Success < unknown > ) => ( {
1825 output : newOutput ,
1926 offset : success . offset ,
27+ furthestFailure : success . furthestFailure ,
2028 } )
2129 return ( input , offset = 0n ) =>
2230 either . map ( parser ( input , offset ) , replaceOutput )
@@ -42,13 +50,18 @@ export const butNot = <Output>(
4250 either . flatMap ( parser ( input , offset ) , success => {
4351 const notResult = not ( input , offset )
4452 if ( ! either . isLeft ( notResult ) ) {
45- return either . makeLeft ( {
53+ const newError = {
4654 source : input ,
4755 offset,
4856 message : errorMessage ,
4957 expected,
5058 notes : [ ] ,
51- } )
59+ }
60+ return either . makeLeft (
61+ success . furthestFailure === undefined
62+ ? newError
63+ : furthest ( success . furthestFailure , newError ) ,
64+ )
5265 } else {
5366 return either . makeRight ( success )
5467 }
@@ -66,7 +79,9 @@ export const flatMap =
6679 ) : Parser < NewOutput > =>
6780 ( input , offset = 0n ) =>
6881 either . flatMap ( parser ( input , offset ) , success =>
69- f ( success . output ) ( input , success . offset ) ,
82+ withPotentiallyFurtherFailure ( success . furthestFailure ) (
83+ f ( success . output ) ( input , success . offset ) ,
84+ ) ,
7085 )
7186
7287/**
@@ -97,14 +112,20 @@ export const lookaheadNot = <Output>(
97112 either . flatMap ( parser ( input , offset ) , success =>
98113 either . match ( notFollowedBy ( input , success . offset ) , {
99114 left : _ => either . makeRight ( success ) ,
100- right : _ =>
101- either . makeLeft ( {
115+ right : _ => {
116+ const newError = {
102117 source : input ,
103118 offset : success . offset ,
104119 message : errorMessage ,
105120 expected,
106121 notes : [ ] ,
107- } ) ,
122+ }
123+ return either . makeLeft (
124+ success . furthestFailure === undefined
125+ ? newError
126+ : furthest ( success . furthestFailure , newError ) ,
127+ )
128+ } ,
108129 } ) ,
109130 )
110131}
@@ -119,6 +140,7 @@ export const map = <Output, NewOutput>(
119140 const applyF = ( success : Success < Output > ) => ( {
120141 output : f ( success . output ) ,
121142 offset : success . offset ,
143+ furthestFailure : success . furthestFailure ,
122144 } )
123145 return ( input , offset = 0n ) => either . map ( parser ( input , offset ) , applyF )
124146}
@@ -160,38 +182,32 @@ export const oneOf =
160182 }
161183 } else {
162184 // Success!
163- return result
185+ return mutableFurthestOffset < 0n
186+ ? result
187+ : either . makeRight ( {
188+ ...result . value ,
189+ furthestFailure : furthestOrUndefined (
190+ result . value . furthestFailure ,
191+ failureAt (
192+ input ,
193+ mutableFurthestOffset ,
194+ mutableFurthestExpectations ,
195+ mutableFurthestNotes ,
196+ ) ,
197+ ) ,
198+ } )
164199 }
165200 }
166201
167- const expected = new Set ( mutableFurthestExpectations )
168-
169- const [ onlyExpectation , ...otherExpectations ] = expected
170- const message =
171- onlyExpectation === undefined
172- ? 'unexpected input'
173- : otherExpectations . length === 0
174- ? `expected ${ onlyExpectation } `
175- : `expected one of: ${ [ ...expected ] . join ( ', ' ) } `
176-
177- // Deduplicate notes.
178- const notes = mutableFurthestNotes . filter (
179- ( note , index ) =>
180- mutableFurthestNotes . findIndex (
181- otherNote =>
182- otherNote . offset === note . offset &&
183- otherNote . message === note . message ,
184- ) === index ,
185- )
186-
187202 // If we haven't already returned then parsing failed.
188- return either . makeLeft ( {
189- source : input ,
190- offset : mutableFurthestOffset ,
191- message,
192- expected,
193- notes,
194- } )
203+ return either . makeLeft (
204+ failureAt (
205+ input ,
206+ mutableFurthestOffset ,
207+ mutableFurthestExpectations ,
208+ mutableFurthestNotes ,
209+ ) ,
210+ )
195211 }
196212type OneOfOutput < Parsers extends readonly Parser < unknown > [ ] > = {
197213 [ Index in keyof Parsers ] : OutputOf < Parsers [ Index ] >
@@ -226,12 +242,18 @@ export const sequence =
226242 parser ,
227243 ) =>
228244 either . isRight ( results )
229- ? either . map ( parser ( input , results . value . offset ) , newSuccess => ( {
230- offset : newSuccess . offset ,
231- output : [ ...results . value . output , newSuccess . output ] ,
232- } ) )
245+ ? either . map (
246+ withPotentiallyFurtherFailure ( results . value . furthestFailure ) (
247+ parser ( input , results . value . offset ) ,
248+ ) ,
249+ newSuccess => ( {
250+ offset : newSuccess . offset ,
251+ output : [ ...results . value . output , newSuccess . output ] ,
252+ furthestFailure : newSuccess . furthestFailure ,
253+ } ) ,
254+ )
233255 : results ,
234- either . makeRight ( { offset, output : [ ] } ) , // `parsers` is non-empty so this is never returned
256+ either . makeRight ( { offset, output : [ ] , furthestFailure : undefined } ) , // `parsers` is non-empty so this is never returned
235257 )
236258 // The above `reduce` callback constructs `output` such that its
237259 // elements align with `Parsers`, but TypeScript doesn't know that.
@@ -250,17 +272,64 @@ export const zeroOrMore =
250272 // Uses a loop rather than recursion to avoid stack overflow.
251273 ( input , offset = 0n ) => {
252274 const output : Output [ ] = [ ]
253- const mutableState = { output, offset }
275+ const mutableState : {
276+ output : Output [ ]
277+ offset : bigint
278+ furthestFailure : InvalidInputError | undefined
279+ } = { output, offset, furthestFailure : undefined }
254280
255281 let result = parser ( input , mutableState . offset )
256282 while ( either . isRight ( result ) ) {
257283 mutableState . output . push ( result . value . output )
258284 mutableState . offset = result . value . offset
285+ mutableState . furthestFailure = furthestOrUndefined (
286+ mutableState . furthestFailure ,
287+ result . value . furthestFailure ,
288+ )
259289 result = parser ( input , mutableState . offset )
260290 }
261291
262- return either . makeRight ( mutableState )
292+ return either . makeRight ( {
293+ ...mutableState ,
294+ furthestFailure : furthestOrUndefined (
295+ mutableState . furthestFailure ,
296+ result . value ,
297+ ) ,
298+ } )
299+ }
300+
301+ const failureAt = (
302+ input : string ,
303+ offset : bigint ,
304+ expectations : readonly string [ ] ,
305+ notes : readonly Note [ ] ,
306+ ) : InvalidInputError => {
307+ const expected = new Set ( expectations )
308+ return {
309+ source : input ,
310+ offset,
311+ message : messageForExpectations ( expected ) ,
312+ expected,
313+ notes : deduplicateNotes ( notes ) ,
263314 }
315+ }
316+
317+ const withPotentiallyFurtherFailure =
318+ ( carried : InvalidInputError | undefined ) =>
319+ < Output > ( result : ParserResult < Output > ) : ParserResult < Output > =>
320+ carried === undefined
321+ ? result
322+ : either . match ( result , {
323+ left : error => either . makeLeft ( furthest ( carried , error ) ) ,
324+ right : success =>
325+ either . makeRight ( {
326+ ...success ,
327+ furthestFailure : furthestOrUndefined (
328+ carried ,
329+ success . furthestFailure ,
330+ ) ,
331+ } ) ,
332+ } )
264333
265334type OutputOf < SpecificParser extends Parser < unknown > > = Extract <
266335 ReturnType < SpecificParser > [ 'value' ] ,
0 commit comments