22declare(strict_types=1);
38use
function is_numeric;
40use
function strtolower;
55 if(($b = $stream->get(1)) !==
"{"){
56 throw new NbtDataException(
"Syntax error: expected compound start but got '$b'");
58 $ret = self::parseCompound($stream);
60 throw new NbtDataException($e->getMessage() .
" at offset " . $stream->getOffset());
61 }
catch(BinaryDataException $e){
62 throw new NbtDataException(
"Syntax error: " . $e->getMessage() .
" at offset " . $stream->getOffset());
65 throw new NbtDataException(
"Syntax error: unexpected trailing characters after end of tag: " . $stream->getRemaining());
75 private static function parseList(BinaryStream $stream) : ListTag{
76 $retval = new ListTag();
78 if(self::skipWhitespace($stream,
"]")){
79 while(!$stream->feof()){
81 $value = self::readValue($stream);
82 }
catch(InvalidTagValueException $e){
83 throw new NbtDataException(
"Data error: " . $e->getMessage());
85 $expectedType = $retval->getTagType();
86 if($expectedType !== NBT::TAG_End && $expectedType !== $value->getType()){
87 throw new NbtDataException(
"Data error: lists can only contain one type of value");
89 $retval->push($value);
90 if(self::readBreak($stream,
"]")){
95 throw new NbtDataException(
"Syntax error: unexpected end of stream");
105 private static function parseCompound(BinaryStream $stream) : CompoundTag{
106 $retval = new CompoundTag();
108 if(self::skipWhitespace($stream,
"}")){
109 while(!$stream->feof()){
110 $k = self::readKey($stream);
111 if($retval->getTag($k) !==
null){
112 throw new NbtDataException(
"Syntax error: duplicate compound leaf node '$k'");
115 $retval->setTag($k, self::readValue($stream));
116 }
catch(InvalidTagValueException $e){
117 throw new NbtDataException(
"Data error: " . $e->getMessage());
120 if(self::readBreak($stream,
"}")){
125 throw new NbtDataException(
"Syntax error: unexpected end of stream");
135 private static function skipWhitespace(BinaryStream $stream,
string $terminator) : bool{
136 while(!$stream->feof()){
137 $b = $stream->get(1);
138 if($b === $terminator){
141 if($b ===
" " or $b ===
"\n" or $b ===
"\t" or $b ===
"\r"){
145 $stream->setOffset($stream->getOffset() - 1);
149 throw new NbtDataException(
"Syntax error: unexpected end of stream, expected start of key");
157 private static function readBreak(BinaryStream $stream,
string $terminator) : bool{
159 throw new NbtDataException(
"Syntax error: unexpected end of stream, expected '$terminator'");
161 $offset = $stream->getOffset();
162 $c = $stream->get(1);
166 if($c === $terminator){
170 throw new NbtDataException(
"Syntax error: unexpected '$c' end at offset $offset");
178 private static function readValue(BinaryStream $stream) : Tag{
182 $offset = $stream->getOffset();
189 while(!$stream->feof()){
190 $offset = $stream->getOffset();
191 $c = $stream->get(1);
196 $retval =
new StringTag($value);
198 }elseif($c ===
"\\"){
199 $value .= $stream->get(1);
204 if($c ===
"," or $c ===
"}" or $c ===
"]"){
205 $stream->setOffset($stream->getOffset() - 1);
210 if($value ===
"" or $foundEnd){
211 if($c ===
"\r" or $c ===
"\n" or $c ===
"\t" or $c ===
" "){
216 throw new NbtDataException(
"Syntax error: unexpected '$c' after end of value at offset $offset");
222 throw new NbtDataException(
"Syntax error: unexpected quote at offset $offset");
228 throw new NbtDataException(
"Syntax error: unexpected compound start at offset $offset (enclose in double quotes for literal)");
231 $retval = self::parseCompound($stream);
236 throw new NbtDataException(
"Syntax error: unexpected list start at offset $offset (enclose in double quotes for literal)");
239 $retval = self::parseList($stream);
248 if($retval !==
null){
253 throw new NbtDataException(
"Syntax error: empty value at offset $offset");
256 throw new NbtDataException(
"Syntax error: unexpected end of stream at offset $offset");
259 $last = strtolower(substr($value, -1));
260 $part = substr($value, 0, -1);
262 if($last !==
"b" and $last !==
"s" and $last !==
"l" and $last !==
"f" and $last !==
"d"){
267 if(is_numeric($part)){
268 if($last ===
"f" or $last ===
"d" or strpos($part,
".") !==
false or strpos($part,
"e") !==
false){
269 $value = (float) $part;
272 return new DoubleTag($value);
275 return new FloatTag($value);
278 $value = (int) $part;
281 return new ByteTag($value);
283 return new ShortTag($value);
285 return new LongTag($value);
287 return new IntTag($value);
291 return new StringTag($value);
299 private static function readKey(BinaryStream $stream) : string{
301 $offset = $stream->getOffset();
306 while(!$stream->feof()){
307 $c = $stream->get(1);
313 }elseif($c ===
"\\"){
314 $key .= $stream->get(1);
324 if($key ===
"" or $foundEnd){
325 if($c ===
"\r" or $c ===
"\n" or $c ===
"\t" or $c ===
" "){
330 throw new NbtDataException(
"Syntax error: unexpected '$c' after end of value at offset $offset");
336 throw new NbtDataException(
"Syntax error: unexpected quote at offset $offset");
340 }elseif($c ===
"{" or $c ===
"}" or $c ===
"[" or $c ===
"]" or $c ===
","){
341 throw new NbtDataException(
"Syntax error: unexpected '$c' at offset $offset (enclose in double quotes for literal)");
349 throw new NbtDataException(
"Syntax error: invalid empty key at offset $offset");
352 throw new NbtDataException(
"Syntax error: unexpected end of stream at offset $offset");
static parseJson(string $data)