char/fjson
friendly JSON implementation in TypeScript
git clone https://git.t4t.associates/char/fjson
bb03820
main
1/** UTF-16 code-unit offsets into the parsed source. */ 2export interface Span { 3readonly start :number ; 4readonly end :number ; 5} 6 7/** malformed input | bad Unicode escape | nesting exceeds limit */ 8export type FjsonErrorKind = "syntax" | "unicode" | "nesting" ; 9 10function location ( name :string , source :string , offset :number ) :string { 11const prefix = source . slice ( 0 , Math . max ( 0 , Math . min ( offset , source . length ))); 12let line = 1 ; 13let column = 1 ; 14let previousCr = false ; 15for ( const ch of prefix ) { 16if ( ch === "\r" ) { 17line += 1 ; 18column = 1 ; 19} else if ( ch === "\n" ) { 20if ( ! previousCr ) line += 1 ; 21column = 1 ; 22} else { 23column += 1 ; 24} 25previousCr = ch === "\r" ; 26} 27return ` ${ name } : ${ line } : ${ column } ` ; 28} 29 30/** Represents a failure to parse an fJSON document */ 31export class FjsonError extends SyntaxError { 32readonly kind :FjsonErrorKind ; 33readonly span :Span ; 34readonly previous ?:Span ; 35 36constructor ( 37kind :FjsonErrorKind , 38message :string , 39span :Span , 40previous ?:Span , 41) { 42super ( message ); 43this . kind = kind ; 44this . span = span ; 45if ( previous !== undefined ) this . previous = previous ; 46} 47 48/** Render against the same source that was parsed. */ 49render ( name :string , source :string ) :string { 50let message = ` ${ location ( name , source , this . span . start )} : ${ this . message } ` ; 51if ( this . previous !== undefined ) { 52message += `\n ${ 53location ( name , source , this . previous . start ) 54} : First defined here` ; 55} 56return message ; 57} 58}