import { Token , TokenType } from './tokenizer' ; import { isNumeric , NbtFormat } from '../utils' ; import { NbtValue , NbtCompound , NbtList , NbtByteArray , NbtIntArray , NbtLongArray , NbtByte , NbtShort , NbtInt , NbtLong , NbtFloat , NbtDouble , NbtString } from './nbt' ; author SPGoding
  export class Parser { public parseCompounds ( tokens , version = 'after 1.12' ) { let result ; if ( tokens [ 0 ] . type === 'BeginCompound' ) { result = this . parseCompound ( tokens , 0 , version ) ; } else { throw `Unexpected start token: '${ tokens [ 0 ] . type }'` ; } if ( tokens [ result . pos + 1 ] . type === 'EndOfDocument' ) { return < NbtCompound > result . value ; } else { throw 'Unsymmetrical squares.' ; } } public parseLists ( tokens , version = 'after 1.12' ) { let result ; if ( tokens [ 0 ] . type === 'BeginList' ) { result = this . parseList ( tokens , 0 , version ) ; } else { throw `Unexpected start token: '${ tokens [ 0 ] . type }'` ; } if ( tokens [ result . pos + 1 ] . type === 'EndOfDocument' ) { return < NbtList > result . value ; } else { throw 'Unsymmetrical squares.' ; } } returns { pos private parseCompound ( tokens , pos , version ) { let expectedTypes ; let state = 'key' ; let key = '' ; let val ; const result = new NbtCompound ( ) ; expectedTypes = [ 'BeginCompound' ] ; for ( ; pos < tokens . length ; pos ++ ) { const token = tokens [ pos ] ; if ( expectedTypes . indexOf ( token . type ) !== - 1 ) { switch ( token . type ) { case 'BeginCompound' : if ( state === 'key' ) { expectedTypes = [ 'EndCompound' , 'Thing' , 'String' ] ; } else if ( state === 'val' ) { expectedTypes = [ 'Comma' , 'EndCompound' ] ; const parseResult = this . parseCompound ( tokens , pos , version ) ; val = parseResult . value ; pos = parseResult . pos ; result . set ( key , val ) ; state = 'key' ; } break ; case 'EndCompound' : return { value : result , pos : pos } ; case 'Thing' : case 'String' : if ( token . type === 'Thing' ) { val = this . parseThing ( token ) ; } else { val = new NbtString ( ) ; val . set ( token . value . toString ( ) ) ; } if ( state === 'key' ) { expectedTypes = [ 'Colon' ] ; key = token . value ; state = 'val' ; } else if ( state === 'val' ) { expectedTypes = [ 'Comma' , 'EndCompound' ] ; if ( val ) { result . set ( key , val ) ; } state = 'key' ; } break ; case 'BeginByteArray' : case 'BeginIntArray' : case 'BeginList' : case 'BeginLongArray' : expectedTypes = [ 'Comma' , 'EndCompound' ] ; const parseResult = this . parseValue ( tokens , pos , version ) ; val = parseResult . value ; pos = parseResult . pos ; result . set ( key , val ) ; state = 'key' ; break ; case 'Colon' : expectedTypes = [ 'Thing' , 'String' , 'BeginByteArray' , 'BeginCompound' , 'BeginIntArray' , 'BeginList' , 'BeginLongArray' ] ; break ; case 'Comma' : expectedTypes = [ 'EndCompound' , 'Thing' , 'String' ] ; break ; default : break ; } } else { throw `Expect '${ expectedTypes }' but got '${ token . type }' at pos '${ pos }'.` ; } } throw 'Parsing compound error!' ; } private parseList ( tokens , pos , version ) { let expectedTypes ; const resultValue = new NbtList ( ) ; let state = 'begin' ; let val ; expectedTypes = [ 'BeginList' ] ; for ( ; pos < tokens . length ; pos ++ ) { const token = tokens [ pos ] ; if ( expectedTypes . indexOf ( token . type ) !== - 1 ) { switch ( token . type ) { case 'BeginList' : if ( state === 'begin' ) { expectedTypes = [ 'EndListOrArray' , 'Thing' , 'String' , 'BeginByteArray' , 'BeginCompound' , 'BeginIntArray' , 'BeginList' , 'BeginLongArray' ] ; state = 'item' ; } else if ( state === 'item' ) { expectedTypes = [ 'Comma' , 'EndListOrArray' ] ; const parseResult = this . parseList ( tokens , pos , version ) ; val = parseResult . value ; pos = parseResult . pos ; resultValue . add ( val ) ; } break ; case 'EndListOrArray' : return { value : resultValue , pos : pos } ; case 'Thing' : { if ( this . parseThing ( token ) instanceof NbtInt ) { if ( tokens [ pos + 1 ] . type === 'Colon' ) { if ( version === 'before 1.12' ) { pos += 1 ; continue ; } else { throw `Unexpected token in ${ tokens }.` ; } } } expectedTypes = [ 'Comma' , 'EndListOrArray' ] ; const parseResult = this . parseValue ( tokens , pos , version ) ; val = parseResult . value ; pos = parseResult . pos ; resultValue . add ( val ) ; break ; } case 'String' : case 'BeginByteArray' : case 'BeginIntArray' : case 'BeginList' : case 'BeginLongArray' : case 'BeginCompound' : { expectedTypes = [ 'Comma' , 'EndListOrArray' ] ; const parseResult = this . parseValue ( tokens , pos , version ) ; val = parseResult . value ; pos = parseResult . pos ; resultValue . add ( val ) ; break ; } case 'Comma' : expectedTypes = [ 'EndListOrArray' , 'Thing' , 'String' , 'BeginByteArray' , 'BeginCompound' , 'BeginIntArray' , 'BeginList' , 'BeginLongArray' ] ; break ; default : break ; } } else { throw `Expect '${ expectedTypes }' but got '${ token . type }' at pos '${ pos }'.` ; } } throw 'Parsing list error!' ; } private parseByteArray ( tokens , pos ) { let expectedTypes ; const resultValue = new NbtByteArray ( ) ; let val ; expectedTypes = [ 'BeginByteArray' ] ; for ( ; pos < tokens . length ; pos ++ ) { const token = tokens [ pos ] ; if ( expectedTypes . indexOf ( token . type ) !== - 1 ) { switch ( token . type ) { case 'BeginByteArray' : expectedTypes = [ 'EndListOrArray' , 'Thing' ] ; break ; case 'EndListOrArray' : return { value : resultValue , pos : pos } ; case 'Thing' : expectedTypes = [ 'Comma' , 'EndListOrArray' ] ; if ( ( val = this . parseByte ( token ) ) !== null ) { resultValue . add ( val ) ; } else { throw `Get a token at '${ pos }' whoose type isn't 'Byte' when parsing byte array!` ; } break ; case 'Comma' : expectedTypes = [ 'EndListOrArray' , 'Thing' ] ; break ; default : break ; } } else { throw `Expect '${ expectedTypes }' but got '${ token . type }' at pos '${ pos }'.` ; } } throw 'Parsing byte array error!' ; } private parseIntArray ( tokens , pos ) { let expectedTypes ; const resultValue = new NbtIntArray ( ) ; let val ; expectedTypes = [ 'BeginIntArray' ] ; for ( ; pos < tokens . length ; pos ++ ) { const token = tokens [ pos ] ; if ( expectedTypes . indexOf ( token . type ) !== - 1 ) { switch ( token . type ) { case 'BeginIntArray' : expectedTypes = [ 'EndListOrArray' , 'Thing' ] ; break ; case 'EndListOrArray' : return { value : resultValue , pos : pos } ; case 'Thing' : expectedTypes = [ 'Comma' , 'EndListOrArray' ] ; if ( ( val = this . parseInt ( token ) ) !== null ) { resultValue . add ( val ) ; } else { throw `Get a token at '${ pos }' whoose type isn't 'Int' when parsing int array!` ; } break ; case 'Comma' : expectedTypes = [ 'EndListOrArray' , 'Thing' ] ; break ; default : break ; } } else { throw `Expect '${ expectedTypes }' but got '${ token . type }' at pos '${ pos }'.` ; } } throw 'Parsing int array error!' ; } private parseLongArray ( tokens , pos ) { let expectedTypes ; const resultValue = new NbtLongArray ( ) ; let val ; expectedTypes = [ 'BeginLongArray' ] ; for ( ; pos < tokens . length ; pos ++ ) { const token = tokens [ pos ] ; if ( expectedTypes . indexOf ( token . type ) !== - 1 ) { switch ( token . type ) { case 'BeginLongArray' : expectedTypes = [ 'EndListOrArray' , 'Thing' ] ; break ; case 'EndListOrArray' : return { value : resultValue , pos : pos } ; case 'Thing' : expectedTypes = [ 'Comma' , 'EndListOrArray' ] ; if ( ( val = this . parseLong ( token ) ) !== null ) { resultValue . add ( val ) ; } else { throw `Get a token at '${ pos }' whoose type isn't 'Long' when parsing long array!` ; } break ; case 'Comma' : expectedTypes = [ 'EndListOrArray' , 'Thing' ] ; break ; default : break ; } } else { throw `Expect '${ expectedTypes }' but got '${ token . type }' at pos '${ pos }'.` ; } } throw 'Parsing long array error!' ; } param tokens param pos private parseValue ( tokens , pos , version ) { const token = tokens [ pos ] ; let val ; let parseResult ; switch ( token . type ) { case 'Thing' : case 'String' : if ( token . type === 'Thing' ) { val = this . parseThing ( token ) ; } else { val = new NbtString ( ) ; val . set ( token . value . toString ( ) ) ; } return { value : val , pos : pos } ; case 'BeginCompound' : parseResult = this . parseCompound ( tokens , pos , version ) ; break ; case 'BeginByteArray' : parseResult = this . parseByteArray ( tokens , pos ) ; break ; case 'BeginIntArray' : parseResult = this . parseIntArray ( tokens , pos ) ; break ; case 'BeginList' : parseResult = this . parseList ( tokens , pos , version ) ; break ; case 'BeginLongArray' : parseResult = this . parseLongArray ( tokens , pos ) ; break ; default : throw `Token '${ token . type }' is not a value!` ; } val = parseResult . value ; pos = parseResult . pos ; return { value : val , pos : pos } ; } private parseThing ( token ) { let result ; if ( ( result = this . parseByte ( token ) ) !== null ) { return result ; } else if ( ( result = this . parseShort ( token ) ) !== null ) { return result ; } else if ( ( result = this . parseInt ( token ) ) !== null ) { return result ; } else if ( ( result = this . parseLong ( token ) ) !== null ) { return result ; } else if ( ( result = this . parseFloat ( token ) ) !== null ) { return result ; } else if ( ( result = this . parseDouble ( token ) ) !== null ) { return result ; } else { return this . parseString ( token ) ; } } private parseString ( token ) { const result = new NbtString ( ) ; result . set ( token . value ) ; return result ; } private parseByte ( token ) { let num ; const result = new NbtByte ( ) ; if ( token . value === 'true' ) { num = 1 ; } else if ( token . value === 'false' ) { num = 0 ; } else if ( isNumeric ( token . value . slice ( 0 , - 1 ) ) && token . value . slice ( - 1 ) . toLowerCase ( ) === 'b' ) { num = parseInt ( token . value ) ; } else { return null ; } result . set ( num ) ; return result ; } private parseShort ( token ) { let num ; const result = new NbtShort ( ) ; if ( isNumeric ( token . value . slice ( 0 , - 1 ) ) && token . value . slice ( - 1 ) . toLowerCase ( ) === 's' ) { num = parseInt ( token . value ) ; } else { return null ; } result . set ( num ) ; return result ; } private parseInt ( token ) { let num ; const result = new NbtInt ( ) ; if ( isNumeric ( token . value ) ) { if ( token . value . indexOf ( '.' ) === - 1 ) { num = parseFloat ( token . value ) ; } else { return null ; } } else { return null ; } result . set ( num ) ; return result ; } private parseLong ( token ) { let num ; const result = new NbtLong ( ) ; if ( isNumeric ( token . value . slice ( 0 , - 1 ) ) && token . value . slice ( - 1 ) . toLowerCase ( ) === 'l' ) { num = parseInt ( token . value ) ; } else { return null ; } result . set ( num ) ; return result ; } private parseFloat ( token ) { let num ; const result = new NbtFloat ( ) ; if ( isNumeric ( token . value . slice ( 0 , - 1 ) ) && token . value . slice ( - 1 ) . toLowerCase ( ) === 'f' ) { num = parseFloat ( token . value ) ; } else { return null ; } result . set ( num ) ; return result ; } private parseDouble ( token ) { let num ; const result = new NbtDouble ( ) ; if ( isNumeric ( token . value . slice ( 0 , - 1 ) ) && token . value . slice ( - 1 ) . toLowerCase ( ) === 'd' ) { num = parseFloat ( token . value ) ; } else if ( isNumeric ( token . value ) ) { if ( token . value . indexOf ( '.' ) !== - 1 ) { num = parseFloat ( token . value ) ; } else { return null ; } } else { return null ; } result . set ( num ) ; return result ; } } interface ParseResult { value : NbtValue ; pos : number ; }