import { Position } from 'acorn' ; import { Node } from './ast' ; export class ENDSyntaxError extends SyntaxError { readonly fileName : string | null ; readonly lineNumber : number ; readonly columnNumber : number ; readonly rawMessage : string ; readonly snippet ? : string ; constructor ( message , fileName ? , pos ? , source ? ) { const rawMessage = message ; if ( pos ) { message += ` at line ${ pos . line }, column ${ pos . column }` ; } if ( fileName ) { message += ` in ${ fileName }` ; } let snippet ; if ( pos && source ) { snippet = getSnippet ( source , pos . line , pos . column ) ; message += `\n\n${ snippet }` ; } super ( message ) ; this . fileName = fileName ; this . lineNumber = pos && pos . line ; this . columnNumber = pos && pos . column ; this . snippet = snippet ; this . rawMessage = rawMessage ; } } export class ENDCompileError extends Error { constructor ( message , readonly node ) { super ( message ) ; } } /**
 * Returns code fragment with pointer to given `line` and `column`
 */ function getSnippet ( code , line , column ) { const lines = splitByLines ( code ) ; const start = Math . max ( line - 3 , 0 ) ; const targetLine = line - start - 1 ; const indent = '  ' ; const chunk = lines . slice ( start , start + 5 ) . map ( ( l , i ) => { if ( i === targetLine ) { const result = replaceTabs ( l , indent , column ) ; column += result . offset ; return result . text ; } return l . replace ( /\t/g , indent ) ; } ) ; chunk . splice ( targetLine + 1 , 0 , '-' . repeat ( column ) + '^' ) ; return chunk . join ( '\n' ) ; } function splitByLines ( text ) { return text . replace ( /\r\n/g , '\n' ) . split ( '\n' ) ; } function replaceTabs ( text , replacement , column = 0 ) { let offset = 0 ; let output = '' ; for ( let i = 0 ; i < text . length ; i ++ ) { const ch = text [ i ] ; if ( ch === '\t' ) { output += replacement ; if ( i < column ) { offset += replacement . length - 1 ; } } else { output += ch ; } } return { text : output , offset } ; }