@ 2023-09-19T16:00:27Z <?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();
Enable javascript to submit You have javascript disabled. You will not be able to edit any code.
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).
Version System time (s) User time (s) Memory (MiB) 8.4.12 0.014 0.007 21.07 8.4.11 0.005 0.005 22.96 8.4.10 0.013 0.009 18.66 8.4.9 0.006 0.003 19.04 8.4.8 0.011 0.009 20.32 8.4.7 0.009 0.011 20.11 8.4.6 0.015 0.005 19.51 8.4.5 0.013 0.009 19.00 8.4.4 0.010 0.010 18.51 8.4.3 0.007 0.013 20.96 8.4.2 0.010 0.010 20.11 8.4.1 0.003 0.006 19.96 8.3.25 0.004 0.004 19.41 8.3.24 0.011 0.008 17.71 8.3.23 0.010 0.010 17.43 8.3.22 0.004 0.004 17.75 8.3.21 0.003 0.006 17.38 8.3.20 0.007 0.014 17.44 8.3.19 0.008 0.012 19.40 8.3.18 0.009 0.011 18.97 8.3.17 0.003 0.016 17.57 8.3.16 0.006 0.013 19.07 8.3.15 0.010 0.010 20.96 8.3.14 0.013 0.007 17.07 8.3.13 0.006 0.003 17.43 8.3.12 0.009 0.000 19.32 8.3.11 0.004 0.004 17.48 8.3.10 0.010 0.013 17.24 8.3.5 0.007 0.014 17.23 8.2.29 0.008 0.002 20.85 8.2.28 0.013 0.007 19.10 8.2.27 0.007 0.014 17.49 8.2.26 0.005 0.003 19.49 8.2.25 0.006 0.003 18.90 8.2.24 0.020 0.000 19.57 8.2.23 0.011 0.011 16.87 8.2.22 0.003 0.006 17.37 8.2.10 0.018 0.007 19.28 8.1.33 0.010 0.009 22.11 8.1.32 0.009 0.010 16.12 8.1.31 0.004 0.004 16.97 8.1.30 0.005 0.005 17.86
preferences:dark mode live preview ace vim emacs key bindings
34.73 ms | 403 KiB | 5 Q