3v4l.org

run code in 300+ PHP versions simultaneously
<?php namespace LLSDN\Token; enum Type: string { // structural tokens case LeftMapDelimiter = '{'; case RightMapDelimiter = '}'; case LeftArrayDelimiter = '['; case RightArrayDelimiter = ']'; case ItemSeparator = ','; case PairSeparator = ':'; // key case Name = '#'; // <-- symbole bidon // type tokens case Undef = '!'; case BooleanTrue = 'true'; case BooleanFalse = 'false'; case Integer = 'i'; case Real = 'r'; case UUID = 'u'; case String = 's'; case RawBinary = 'b'; case Binary16 = 'b16'; case Binary64 = 'b64'; case URI = 'l'; case Date = 'd'; public function endsValue(): bool { return match($this) { Type::RightMapDelimiter, Type::RightArrayDelimiter, Type::ItemSeparator => true, default => false }; } public function isStructural(): bool { return match($this) { Type::LeftMapDelimiter, Type::RightMapDelimiter, Type::LeftArrayDelimiter, Type::RightArrayDelimiter, Type::PairSeparator, Type::ItemSeparator => true, default => false }; } }; abstract class AbstractToken { public ?Type $type; public ?string $value; } class Token extends AbstractToken { } class Tokenizer { protected \Iterator $chunk; protected AbstractToken $token; protected ?string $onHold = null; public function __construct(\Iterator | Array $chunks, AbstractToken $token = new Token()) { $this->chunk = is_array($chunks) ? new \ArrayIterator($chunks) : $chunks; $this->token = $token; } public function tokenize() { while($this->chunk->valid()) { $chunk = $this->chunk->current(); if (in_array($chunk, ["'", '"', 's'], true)) { $this->onHold = $this->stringContent(); } else { yield from match($chunk) { '{', '}', '[', ']', ',', ':', '!' => $this->noValue(), '0', 'f', 'false', 'F', 'FALSE' => $this->boolean(false), '1', 't', 'true', 'T', 'TRUE' => $this->boolean(true), 'b16', 'b64', 'd', 'l' => $this->quoted(), 'b' => $this->rawBinary(), 'i', 'r', 'u' => $this->simple(), }; } $this->skipWS(); } } protected function noValue() { $type = Type::from($this->chunk->current()); if ($this->onHold !== null) { if ($type->endsValue()) { yield $this->buildToken(Type::String, $this->onHold); } elseif ($type === Type::PairSeparator) { yield $this->buildToken(Type::Name, $this->onHold); } $this->onHold = null; } yield $this->buildToken($type); } protected function simple() { $type = Type::from($this->chunk->current()); $this->chunk->next(); yield $this->buildToken($type, $this->chunk->current()); } protected function quoted(): \Generator { $type = Type::from($this->chunk->current()); $this->chunk->next(); $value = $this->escapedContent(); yield $this->buildToken($type, $value); } protected function boolean($value): \Generator { yield $value ? $this->buildToken(Type::BooleanTrue, 'true') : $this->buildToken(Type::BooleanFalse, 'false'); } protected function RawBinary(): \Generator { $this->chunk->next(); yield $this->buildToken(Type::RawBinary, $this->sizedContent()); } protected function stringContent(): string { if (in_array($this->chunk->current(), ["'", '"'], true)) { return $this->escapedContent(); } else { $this->chunk->next(); // parenthèse ouvrante return $this->sizedContent(); } } protected function escapedContent(): string { $delimiter = $this->chunk->current(); $content = ''; do { $escaped = false; $this->chunk->next(); if ($this->chunk->current() === '\\') { $escaped = true; $this->chunk->next(); if ($this->chunk->current() !== $delimiter) { $content .= '\\'; } $content .= $this->chunk->current(); } elseif ($this->chunk->current() !== $delimiter) { $content .= $this->chunk->current(); } } while($this->chunk->current() !== $delimiter || $escaped === true); return $content; } protected function sizedContent(): string { $this->chunk->next(); // taille $size = (int)$this->chunk->current(); $this->chunk->next(); // parenthèse fermante $this->chunk->next(); // délimiteur de début $content = ''; while(strlen($content) < $size) { $this->chunk->next(); $content .= $this->chunk->current(); } $this->chunk->next(); // délimiteur de fin return $content; } protected function skipWS() { $this->chunk->next(); if ($this->chunk->valid() && preg_match('~\A \s+ \z~ux', $this->chunk->current())) { $this->chunk->next(); } } protected function buildToken(Type $tokenType, ?string $value = null): AbstractToken { $token = clone $this->token; $token->type = $tokenType; $token->value = $value; return $token; } } function toXML(\Iterator $tokens): string { $result = '<?xml version="1.0" encoding="UTF-8"?>' . "\n"; $nameAttribute = null; foreach($tokens as $token) { $tagName = null; if ($token->type === Type::Name) { $nameAttribute = $token->value; } elseif (in_array($token->type, [Type::ItemSeparator, Type::PairSeparator], true)) { continue; } else { $tagName = match($token->type) { Type::LeftMapDelimiter, Type::RightMapDelimiter => 'map', Type::LeftArrayDelimiter, Type::RightArrayDelimiter => 'array', Type::BooleanFalse, Type::BooleanTrue => 'boolean', Type::UUID => 'UUID', Type::URI => 'URI', default => strtolower($token->type->name) }; $result .= '<'; if (str_starts_with(needle: 'Right', haystack: $token->type->name)) { $result .= '/' . $tagName . '>'; continue; } $result .= $tagName; if ($nameAttribute) { $result .= ' name="' . $nameAttribute . '"'; $nameAttribute = null; } if ($token->type === Type::Undef) { $result .= '/>'; continue; } $result .= '>'; if (str_starts_with(needle: 'Left', haystack: $token->type->name)) continue; $result .= $token->type === Type::RawBinary ? '<![CDATA[' . strtr($token->value, [']]>' => ']]]]><![CDATA[>']) . ']]>' : htmlspecialchars($token->value, ENT_XML1, 'UTF-8'); $result .= '</' . $tagName . '>'; } } return $result; } $test = <<<'LLSDN' [ { 'creation-date':d"2007-03-15T18:30:18Z", 'creator-id':u3c115e51-04f4-523c-9fa6-98aff1034730 }, s(10)"0123456789", "Where are the beef & the <pig>?", 'Over here.', b(158)"default { state_entry() { llSay(0, "Hello, Avatar!"); } touch_start(integer total_number) { llSay(0, "Touched."); } }", b64"AABAAAAAAAAAAAIAAAA//wAAP/8AAADgAAAA5wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AABkAAAAZAAAAAAAAAAAAAAAZAAAAAAAAAABAAAAAAAAAAAAAAAAAAAABQAAAAEAAAAQAAAAAAAA AAUAAAAFAAAAABAAAAAAAAAAPgAAAAQAAAAFAGNbXgAAAABgSGVsbG8sIEF2YXRhciEAZgAAAABc XgAAAAhwEQjRABeVAAAABQBjW14AAAAAYFRvdWNoZWQuAGYAAAAAXF4AAAAIcBEI0QAXAZUAAEAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", !, 0, f, t, T, TRUE, FALSE, F ] LLSDN; $pattern = <<<'REGEX' ~(?xx) (?<chunk> [ ] [ ) ( } { : , ! ' " \\ ] | [[:<:]] ( ( # boolean (?<! [ - + . ] ) [ 0 1 ] (?= [ ] \s , } ]) | f (alse)? | t (rue)? | F (ALSE)? | T (RUE)? | # binary b ( 16 | 64 )? | # string, date, URI [ s d l ] ) [[:>:]] | # integer, real, UUID [ i r u ] ) | \s+ ) ~un REGEX; $chunks = preg_split( pattern: $pattern, subject: $test, flags: PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); $myTKZ = new Tokenizer($chunks); $tokenGen = $myTKZ->tokenize(); $dom = new \DOMDocument; $dom->loadXML(toXML($tokenGen)); $dom->preserveWhiteSpace = false; $dom->formatOutput = true; echo $dom->saveXML();

Here you find the average performance (time & memory) of each version. A grayed out version indicates it didn't complete successfully (based on exit-code).

VersionSystem time (s)User time (s)Memory (MiB)
8.4.120.0140.00721.07
8.4.110.0050.00522.96
8.4.100.0130.00918.66
8.4.90.0060.00319.04
8.4.80.0110.00920.32
8.4.70.0090.01120.11
8.4.60.0150.00519.51
8.4.50.0130.00919.00
8.4.40.0100.01018.51
8.4.30.0070.01320.96
8.4.20.0100.01020.11
8.4.10.0030.00619.96
8.3.250.0040.00419.41
8.3.240.0110.00817.71
8.3.230.0100.01017.43
8.3.220.0040.00417.75
8.3.210.0030.00617.38
8.3.200.0070.01417.44
8.3.190.0080.01219.40
8.3.180.0090.01118.97
8.3.170.0030.01617.57
8.3.160.0060.01319.07
8.3.150.0100.01020.96
8.3.140.0130.00717.07
8.3.130.0060.00317.43
8.3.120.0090.00019.32
8.3.110.0040.00417.48
8.3.100.0100.01317.24
8.3.50.0070.01417.23
8.2.290.0080.00220.85
8.2.280.0130.00719.10
8.2.270.0070.01417.49
8.2.260.0050.00319.49
8.2.250.0060.00318.90
8.2.240.0200.00019.57
8.2.230.0110.01116.87
8.2.220.0030.00617.37
8.2.100.0180.00719.28
8.1.330.0100.00922.11
8.1.320.0090.01016.12
8.1.310.0040.00416.97
8.1.300.0050.00517.86

preferences:
34.73 ms | 403 KiB | 5 Q