export function assertUnreachable ( value ) { console . error ( "shouldnt have gotten (" , value , ")" ) ; throw new Error ( `unreachable` ) ; } export function union < T > ( left , right ) { return new Set ( [ ... left , ... right ] ) ; } export function intersection < T > ( left , right ) { const out = new Set < T > ( ) ; for ( const item of left ) { if ( right . has ( item ) ) out . add ( item ) ; } return out ; } export function partition < T > ( xs , fn ) { const trues = [ ] ; const falses = [ ] ; for ( const x of xs ) { if ( fn ( x ) ) { trues . push ( x ) ; } else { falses . push ( x ) ; } } return [ trues , falses ] ; } export type TokenPosition = { index : number ; outerIndex : number ; length : number ; } ; const MAX_OFFSET = 100 ; function scan ( strs , pos , direction , fn ) { const state = { length : 0 , index : pos . index , outerIndex : pos . outerIndex } ; while ( state . outerIndex >= 0 && state . outerIndex < strs . length ) { while ( state . index >= 0 && state . index < strs [ state . outerIndex ] . length ) { const ch = strs [ state . outerIndex ] [ state . index ] ; if ( fn ( ch , state ) ) return state ; state . length ++ ; state . index += direction ; } state . outerIndex += direction ; if ( direction < 0 ) { state . index = ( strs [ state . outerIndex ] || "" ) . length - 1 ; } else { state . index = 0 ; } } return state ; } function findOffset ( strs , pos , direction ) { return scan ( strs , pos , direction , ( ch , newPos ) => ch === "\n" || newPos . length >= MAX_OFFSET ) ; } function toEnd ( strs , pos ) { return scan ( strs , pos , 1 , ( _ , nextPos ) => nextPos . length === pos . length ) ; } export function showInContext ( strs , pos ) { const lines = [ ] ; let strInContext = "" ; let underline = "" ; const startPos = findOffset ( strs , pos , - 1 ) ; const endPos = findOffset ( strs , toEnd ( strs , pos ) , 1 ) ; let offset = - startPos . length ; scan ( strs , startPos , 1 , ( ch , { index , outerIndex } ) => { if ( outerIndex > 0 && index === 0 ) { strInContext += "${...}" ; if ( offset > 0 && offset <= pos . length ) { underline += "^^^^^^" ; } else { underline += "      " ; } } offset ++ ; if ( ch === "\n" ) { lines . push ( strInContext , underline ) ; strInContext = "" ; underline = "" ; } else { strInContext += ch ; if ( offset > 0 && offset <= pos . length ) { underline += "^" ; } else { underline += " " ; } } return ! ( outerIndex < endPos . outerIndex || index <= endPos . index ) ; } ) ; lines . push ( strInContext , underline ) ; return lines . filter ( ( line ) => line . trimEnd ( ) . length > 0 ) . join ( "\n" ) ; } export class ParseError extends Error { constructor ( message , strs , pos ) { super ( `${ message }\n${ showInContext ( strs , pos ) }` ) ; } } export class CompileError extends Error { }