module JS { type TokenSingle = string | number | boolean | null | { } ; type Token = TokenSingle | Array < TokenSingle > ; export class SyntaxError extends Error { constructor ( ) { super ( 'JSON Syntax Error' ) ; } } export class JSONParse { private Unescapes = { 92 : '\\' , 34 : '"' , 47 : '/' , 98 : '\b' , 116 : '\t' , 110 : '\n' , 102 : '\f' , 114 : '\r' } ; private Index : number ; private Source : string ; public abort ( ) { this . Index = this . Source = null ; throw SyntaxError ( ) ; } public lex ( ) { const source = this . Source , length = source . length ; let value , begin , position , isSigned , charCode ; while ( this . Index < length ) { charCode = source . charCodeAt ( this . Index ) ; switch ( charCode ) { case 9 : case 10 : case 13 : case 32 : this . Index ++ ; break ; case 123 : case 125 : case 91 : case 93 : case 58 : case 44 : value = source [ this . Index ] ; this . Index ++ ; return value ; case 34 : for ( value = '@' , this . Index ++ ; this . Index < length ; ) { charCode = source . charCodeAt ( this . Index ) ; if ( charCode < 32 ) { this . abort ( ) ; } else if ( charCode === 92 ) { charCode = source . charCodeAt ( ++ this . Index ) ; switch ( charCode ) { case 92 : case 34 : case 47 : case 98 : case 116 : case 110 : case 102 : case 114 : value += this . Unescapes [ charCode ] ; this . Index ++ ; break ; case 117 : begin = ++ this . Index ; for ( position = this . Index + 4 ; this . Index < position ; this . Index ++ ) { charCode = source . charCodeAt ( this . Index ) ; if ( ! ( charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70 ) ) { this . abort ( ) ; } } value += StringHelper . fromCharCode ( '0x' + source . slice ( begin , this . Index ) ) ; break ; default : this . abort ( ) ; } } else { if ( charCode === 34 ) { break ; } charCode = source . charCodeAt ( this . Index ) ; begin = this . Index ; while ( charCode >= 32 && charCode !== 92 && charCode !== 34 ) { charCode = source . charCodeAt ( ++ this . Index ) ; } value += source . slice ( begin , this . Index ) ; } } if ( source . charCodeAt ( this . Index ) === 34 ) { this . Index ++ ; return value ; } this . abort ( ) ; break ; default : begin = this . Index ; if ( charCode === 45 ) { isSigned = true ; charCode = source . charCodeAt ( ++ this . Index ) ; } if ( charCode >= 48 && charCode <= 57 ) { if ( charCode === 48 && ( ( charCode = source . charCodeAt ( this . Index + 1 ) ) , charCode >= 48 && charCode <= 57 ) ) { this . abort ( ) ; } isSigned = false ; for ( ; this . Index < length && ( ( charCode = source . charCodeAt ( this . Index ) ) , charCode >= 48 && charCode <= 57 ) ; this . Index ++ ) { } if ( source . charCodeAt ( this . Index ) === 46 ) { position = ++ this . Index ; for ( ; position < length && ( ( charCode = source . charCodeAt ( position ) ) , charCode >= 48 && charCode <= 57 ) ; position ++ ) { } if ( position === this . Index ) { this . abort ( ) ; } this . Index = position ; } charCode = source . charCodeAt ( this . Index ) ; if ( charCode === 101 || charCode === 69 ) { charCode = source . charCodeAt ( ++ this . Index ) ; if ( charCode === 43 || charCode === 45 ) { this . Index ++ ; } for ( position = this . Index ; position < length && ( ( charCode = source . charCodeAt ( position ) ) , charCode >= 48 && charCode <= 57 ) ; position ++ ) { } if ( position === this . Index ) { this . abort ( ) ; } this . Index = position ; } const val = source . slice ( begin , this . Index ) ; return tonumber ( val ) ; } if ( isSigned ) { this . abort ( ) ; } if ( source . slice ( this . Index , this . Index + 4 ) === 'true' ) { this . Index += 4 ; return true ; } else if ( source . slice ( this . Index , this . Index + 5 ) === 'false' ) { this . Index += 5 ; return false ; } else if ( source . slice ( this . Index , this . Index + 4 ) === 'null' ) { this . Index += 4 ; return null ; } this . abort ( ) ; } } return '$' ; } public get ( value ) { let results ; let hasMembers = false ; if ( value === '$' ) { this . abort ( ) ; } if ( typeof ( value ) === 'string' ) { if ( value [ 0 ] === '@' ) { return value . slice ( 1 ) ; } if ( value === '[' ) { results = new Array < Token > ( ) ; for ( ; ; hasMembers || ( hasMembers = true ) ) { value = this . lex ( ) ; if ( value === ']' ) { break ; } if ( hasMembers ) { if ( value === ',' ) { value = this . lex ( ) ; if ( value === ']' ) { this . abort ( ) ; } } else { this . abort ( ) ; } } if ( value === ',' ) { this . abort ( ) ; } ( < Array < Token > > results ) . push ( this . get ( value ) ) ; } return results ; } else if ( value === '{' ) { results = { } ; for ( ; ; hasMembers || ( hasMembers = true ) ) { value = this . lex ( ) ; if ( value === '}' ) { break ; } if ( hasMembers ) { if ( value === ',' ) { value = this . lex ( ) ; if ( value === '}' ) { this . abort ( ) ; } } else { this . abort ( ) ; } } if ( value === ',' || typeof value !== 'string' || value [ 0 ] !== '@' || this . lex ( ) !== ':' ) { this . abort ( ) ; } const nameProp = ( < string > value ) . slice ( 1 ) ; ( < any > results ) [ nameProp ] = this . get ( this . lex ( ) ) ; } return results ; } this . abort ( ) ; } return value ; } public parse ( source ) { this . Index = 0 ; this . Source = '' + source ; const result = this . get ( this . lex ( ) ) ; if ( this . lex ( ) !== '$' ) { this . abort ( ) ; } this . Index = this . Source = null ; return result ; } } export class JSON { public static parse ( source ) { return new JSONParse ( ) . parse ( source ) ; } } }