From 512d2eee1b8ff67ca31a0b0a03c14f852375cb5c Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 16 Jan 2025 05:50:46 +0100 Subject: [PATCH 01/17] cs --- src/Neon/Node/StringNode.php | 4 ++-- tests/Neon/Decoder.errors.phpt | 2 +- tests/Neon/Decoder.phpt | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Neon/Node/StringNode.php b/src/Neon/Node/StringNode.php index f921912..4fcb6a9 100644 --- a/src/Neon/Node/StringNode.php +++ b/src/Neon/Node/StringNode.php @@ -51,7 +51,7 @@ public static function parse(string $s): string } return preg_replace_callback( - '#\\\\(?:ud[89ab][0-9a-f]{2}\\\\ud[c-f][0-9a-f]{2}|u[0-9a-f]{4}|.)#i', + '#\\\(?:ud[89ab][0-9a-f]{2}\\\ud[c-f][0-9a-f]{2}|u[0-9a-f]{4}|.)#i', function (array $m): string { $sq = $m[0]; if (isset(self::EscapeSequences[$sq[1]])) { @@ -77,7 +77,7 @@ public function toString(): string } elseif (preg_match('~[\x00-\x08\x0B-\x1F]|\n[\t ]+\'{3}~', $this->value)) { $s = substr(json_encode($this->value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), 1, -1); $s = preg_replace_callback( - '#[^\\\\]|\\\\(.)#s', + '#[^\\\]|\\\(.)#s', fn($m) => ['n' => "\n", 't' => "\t", '"' => '"'][$m[1] ?? ''] ?? $m[0], $s, ); diff --git a/tests/Neon/Decoder.errors.phpt b/tests/Neon/Decoder.errors.phpt index 8b9d5cb..c183aae 100644 --- a/tests/Neon/Decoder.errors.phpt +++ b/tests/Neon/Decoder.errors.phpt @@ -23,7 +23,7 @@ Assert::exception( Assert::exception( fn() => Neon::decode('"\uD801"'), Nette\Neon\Exception::class, - 'Invalid UTF-8 sequence \\uD801 on line 1, column 1.', + 'Invalid UTF-8 sequence \uD801 on line 1, column 1.', ); diff --git a/tests/Neon/Decoder.phpt b/tests/Neon/Decoder.phpt index 215a630..a1973c6 100644 --- a/tests/Neon/Decoder.phpt +++ b/tests/Neon/Decoder.phpt @@ -53,8 +53,8 @@ $dataSet = [ ["'fo''o'", "fo'o"], ['""', ''], ['"foo"', 'foo'], - ['"f\\no"', "f\no"], - ['"\\b\\f\\n\\r\\t\\"\\/\\\\"', "\x08\f\n\r\t\"/\\"], + ['"f\no"', "f\no"], + ['"\b\f\n\r\t\"\/\\\"', "\x08\f\n\r\t\"/\\"], ['"\u0040"', '@'], ['"\u011B"', "\u{11B}"], ['"\uD801\uDC01"', "\u{10401}"], // U+10401 encoded as surrogate pair @@ -107,7 +107,7 @@ $dataSet = [ ['NO', false], // extended string syntax - ["'single \\n quote'", 'single \\n quote'], + ["'single \\n quote'", 'single \n quote'], // strings without quotes ['a', 'a'], @@ -179,7 +179,7 @@ $dataSet = [ // edge ['"the\'string #literal"', "the'string #literal"], ["'the\"string #literal'", 'the"string #literal'], - ['"the\\"string #literal"', 'the"string #literal'], + ['"the\"string #literal"', 'the"string #literal'], ['a ', 'a'], // backtrack limit ], @@ -189,15 +189,15 @@ $dataSet = [ // inputs with invalid syntax, but still valid UTF-8 'invalid syntax' => [ - ['"\\a invalid escape"'], - ['"\\v invalid escape"'], - ['"\\u202 invalid escape"'], - ['"\\012 invalid escape"'], + ['"\a invalid escape"'], + ['"\v invalid escape"'], + ['"\u202 invalid escape"'], + ['"\012 invalid escape"'], ['"\\\' invalid escape"'], ['"Unterminated string'], - ['"Unterminated string\\"'], - ['"Unterminated string\\\\\\"'], + ['"Unterminated string\"'], + ['"Unterminated string\\\\\"'], ['"42" ""'], ['"" ""'], From 1e990ceb18e9d178147e58041921d33a9ea9933a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 27 Sep 2023 11:04:20 +0200 Subject: [PATCH 02/17] opened 3.5-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d2971d2..746f601 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "3.5-dev" } } } From e4c5cf4f02abdac3c0510bfbb0137bd8937c0e1d Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 27 Sep 2023 13:51:11 +0200 Subject: [PATCH 03/17] tests: matchFile() changed to same() too large files may overflow PREG --- tests/Neon/Encoder.nodes.phpt | 4 ++-- tests/Neon/Parser.nodes.phpt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Neon/Encoder.nodes.phpt b/tests/Neon/Encoder.nodes.phpt index adb1cbc..e2d6d29 100644 --- a/tests/Neon/Encoder.nodes.phpt +++ b/tests/Neon/Encoder.nodes.phpt @@ -28,7 +28,7 @@ $input = [ $encoder = new Neon\Encoder; $node = $encoder->valueToNode($input); -Assert::matchFile( - __DIR__ . '/fixtures/Encoder.nodes.txt', +Assert::same( + strtr(file_get_contents(__DIR__ . '/fixtures/Encoder.nodes.txt'), ["\r\n" => "\n"]), Dumper::toText($node, [Dumper::HASH => false, Dumper::DEPTH => 20]), ); diff --git a/tests/Neon/Parser.nodes.phpt b/tests/Neon/Parser.nodes.phpt index b5a4318..d7533f6 100644 --- a/tests/Neon/Parser.nodes.phpt +++ b/tests/Neon/Parser.nodes.phpt @@ -64,7 +64,7 @@ $traverser->traverse($node, function (Node $node) use ($stream) { unset($node->startTokenPos, $node->endTokenPos); }); -Assert::matchFile( - __DIR__ . '/fixtures/Parser.nodes.txt', +Assert::same( + strtr(file_get_contents(__DIR__ . '/fixtures/Parser.nodes.txt'), ["\r\n" => "\n"]), Dumper::toText($node, [Dumper::HASH => false, Dumper::DEPTH => 20]), ); From df488b8ea02d85b0e9dc604ba12776034ec8b707 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 27 Sep 2023 14:16:53 +0200 Subject: [PATCH 04/17] adjusted exception message --- src/Neon/TokenStream.php | 2 +- tests/Neon/Decoder.errors.phpt | 40 +++++++++++++++++----------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Neon/TokenStream.php b/src/Neon/TokenStream.php index 061edd5..cff4783 100644 --- a/src/Neon/TokenStream.php +++ b/src/Neon/TokenStream.php @@ -90,6 +90,6 @@ public function error(?string $message = null, ?int $pos = null): void $message ??= 'Unexpected ' . ($token === null ? 'end' : "'" . str_replace("\n", '', substr($this->tokens[$pos]->value, 0, 40)) . "'"); - throw new Exception("$message on line $line, column $col."); + throw new Exception("$message on line $line at column $col"); } } diff --git a/tests/Neon/Decoder.errors.phpt b/tests/Neon/Decoder.errors.phpt index c183aae..792bbe8 100644 --- a/tests/Neon/Decoder.errors.phpt +++ b/tests/Neon/Decoder.errors.phpt @@ -16,63 +16,63 @@ require __DIR__ . '/../bootstrap.php'; Assert::exception( fn() => Neon::decode("Hello\nWorld"), Nette\Neon\Exception::class, - "Unexpected 'World' on line 2, column 1.", + "Unexpected 'World' on line 2 at column 1", ); Assert::exception( fn() => Neon::decode('"\uD801"'), Nette\Neon\Exception::class, - 'Invalid UTF-8 sequence \uD801 on line 1, column 1.', + 'Invalid UTF-8 sequence \uD801 on line 1 at column 1', ); Assert::exception( fn() => Neon::decode("- Dave,\n- Rimmer,\n- Kryten,\n"), Nette\Neon\Exception::class, - "Unexpected ',' on line 1, column 7.", + "Unexpected ',' on line 1 at column 7", ); Assert::exception( fn() => Neon::decode('item [a, b]'), Nette\Neon\Exception::class, - "Unexpected ',' on line 1, column 8.", + "Unexpected ',' on line 1 at column 8", ); Assert::exception( fn() => Neon::decode('{,}'), Nette\Neon\Exception::class, - "Unexpected ',' on line 1, column 2.", + "Unexpected ',' on line 1 at column 2", ); Assert::exception( fn() => Neon::decode('{a, ,}'), Nette\Neon\Exception::class, - "Unexpected ',' on line 1, column 5.", + "Unexpected ',' on line 1 at column 5", ); Assert::exception( fn() => Neon::decode('"'), Nette\Neon\Exception::class, - "Unexpected '\"' on line 1, column 1.", + "Unexpected '\"' on line 1 at column 1", ); Assert::exception( fn() => Neon::decode("\ta:\n b:"), Nette\Neon\Exception::class, - 'Invalid combination of tabs and spaces on line 2, column 2.', + 'Invalid combination of tabs and spaces on line 2 at column 2', ); Assert::exception( fn() => Neon::decode("- x: 20\n - a: 10\n\tb: 10"), Nette\Neon\Exception::class, - 'Invalid combination of tabs and spaces on line 3, column 2.', + 'Invalid combination of tabs and spaces on line 3 at column 2', ); @@ -84,7 +84,7 @@ Assert::exception( XX), Nette\Neon\Exception::class, - 'Bad indentation on line 3, column 2.', + 'Bad indentation on line 3 at column 2', ); @@ -94,7 +94,7 @@ Assert::exception( b: XX), Nette\Neon\Exception::class, - 'Bad indentation on line 2, column 3.', + 'Bad indentation on line 2 at column 3', ); @@ -104,7 +104,7 @@ Assert::exception( a: 10 XX), Nette\Neon\Exception::class, - 'Bad indentation on line 2, column 2.', + 'Bad indentation on line 2 at column 2', ); @@ -114,7 +114,7 @@ Assert::exception( a: 10 XX), Nette\Neon\Exception::class, - 'Bad indentation on line 2, column 4.', + 'Bad indentation on line 2 at column 4', ); @@ -124,14 +124,14 @@ Assert::exception( a: 10 XX), Nette\Neon\Exception::class, - 'Bad indentation on line 2, column 2.', + 'Bad indentation on line 2 at column 2', ); Assert::exception( fn() => Neon::decode('- x: y:'), Nette\Neon\Exception::class, - "Unexpected ':' on line 1, column 7.", + "Unexpected ':' on line 1 at column 7", ); @@ -145,7 +145,7 @@ Assert::exception( XX, ), Nette\Neon\Exception::class, - "Unexpected '' on line 3, column 4.", + "Unexpected '' on line 3 at column 4", ); @@ -155,26 +155,26 @@ Assert::exception( a: 2 XX), Nette\Neon\Exception::class, - "Duplicated key 'a' on line 2, column 1.", + "Duplicated key 'a' on line 2 at column 1", ); Assert::exception( fn() => Neon::decode('{ []: foo }'), Nette\Neon\Exception::class, - 'Unacceptable key on line 1, column 3.', + 'Unacceptable key on line 1 at column 3', ); Assert::exception( fn() => Neon::decode('[]: foo'), Nette\Neon\Exception::class, - 'Unacceptable key on line 1, column 1.', + 'Unacceptable key on line 1 at column 1', ); Assert::exception( fn() => Neon::decode('{ - 1}'), Nette\Neon\Exception::class, - "Unexpected '-' on line 1, column 3.", + "Unexpected '-' on line 1 at column 3", ); From 090a6368db8cefe32c327fd2e332fba624c2a790 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 28 Apr 2024 22:42:36 +0200 Subject: [PATCH 05/17] TokenStream: renamed some methods --- src/Neon/Parser.php | 80 ++++++++++++++++++++-------------------- src/Neon/TokenStream.php | 34 ++++++++--------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/Neon/Parser.php b/src/Neon/Parser.php index a2a4a1c..3e1ab98 100644 --- a/src/Neon/Parser.php +++ b/src/Neon/Parser.php @@ -24,11 +24,11 @@ public function parse(TokenStream $tokens): Node $this->tokens = $tokens; $this->initLines(); - while ($this->tokens->consume(Token::Newline)); + while ($this->tokens->tryConsume(Token::Newline)); $node = $this->parseBlock($this->tokens->getIndentation()); - while ($this->tokens->consume(Token::Newline)); - if ($this->tokens->isNext()) { + while ($this->tokens->tryConsume(Token::Newline)); + if ($this->tokens->is()) { $this->tokens->error(); } @@ -45,16 +45,16 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node loop: $item = new Node\ArrayItemNode; $this->injectPos($item); - if ($this->tokens->consume('-')) { + if ($this->tokens->tryConsume('-')) { // continue - } elseif (!$this->tokens->isNext() || $onlyBullets) { + } elseif (!$this->tokens->is() || $onlyBullets) { return $res->items ? $res : $this->injectPos(new Node\LiteralNode(null)); } else { $value = $this->parseValue(); - if ($this->tokens->consume(':', '=')) { + if ($this->tokens->tryConsume(':', '=')) { $this->checkArrayKey($value, $keyCheck); $item->key = $value; } else { @@ -70,8 +70,8 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node $item->value = new Node\LiteralNode(null); $this->injectPos($item->value); - if ($this->tokens->consume(Token::Newline)) { - while ($this->tokens->consume(Token::Newline)); + if ($this->tokens->tryConsume(Token::Newline)) { + while ($this->tokens->tryConsume(Token::Newline)); $nextIndent = $this->tokens->getIndentation(); if (strncmp($nextIndent, $indent, min(strlen($nextIndent), strlen($indent)))) { @@ -83,20 +83,20 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node } elseif (strlen($nextIndent) < strlen($indent)) { // close block return $res; - } elseif ($item->key !== null && $this->tokens->isNext('-')) { // special dash subblock + } elseif ($item->key !== null && $this->tokens->is('-')) { // special dash subblock $item->value = $this->parseBlock($indent, onlyBullets: true); } } elseif ($item->key === null) { // open new block after dash - $save = $this->tokens->getPos(); + $save = $this->tokens->getIndex(); try { $item->value = $this->parseBlock($indent . "\t"); } catch (Exception) { $this->tokens->seek($save); $item->value = $this->parseBlock($indent . ' '); } - } elseif ($this->tokens->isNext()) { + } elseif ($this->tokens->is()) { $item->value = $this->parseValue(); - if ($this->tokens->isNext() && !$this->tokens->isNext(Token::Newline)) { + if ($this->tokens->is() && !$this->tokens->is(Token::Newline)) { $this->tokens->error(); } } @@ -108,8 +108,8 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node $this->injectPos($res, $res->startTokenPos, $item->value->endTokenPos); $this->injectPos($item, $item->startTokenPos, $item->value->endTokenPos); - while ($this->tokens->consume(Token::Newline)); - if (!$this->tokens->isNext()) { + while ($this->tokens->tryConsume(Token::Newline)); + if (!$this->tokens->is()) { return $res; } @@ -130,19 +130,19 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node private function parseValue(): Node { - if ($token = $this->tokens->consume(Token::String)) { + if ($token = $this->tokens->tryConsume(Token::String)) { try { $node = new Node\StringNode(Node\StringNode::parse($token->value)); - $this->injectPos($node, $this->tokens->getPos() - 1); + $this->injectPos($node, $this->tokens->getIndex() - 1); } catch (Exception $e) { - $this->tokens->error($e->getMessage(), $this->tokens->getPos() - 1); + $this->tokens->error($e->getMessage(), $this->tokens->getIndex() - 1); } - } elseif ($token = $this->tokens->consume(Token::Literal)) { - $pos = $this->tokens->getPos() - 1; - $node = new Node\LiteralNode(Node\LiteralNode::parse($token->value, $this->tokens->isNext(':', '='))); + } elseif ($token = $this->tokens->tryConsume(Token::Literal)) { + $pos = $this->tokens->getIndex() - 1; + $node = new Node\LiteralNode(Node\LiteralNode::parse($token->value, $this->tokens->is(':', '='))); $this->injectPos($node, $pos); - } elseif ($this->tokens->isNext('[', '(', '{')) { + } elseif ($this->tokens->is('[', '(', '{')) { $node = $this->parseBraces(); } else { @@ -155,17 +155,17 @@ private function parseValue(): Node private function parseEntity(Node $node): Node { - if (!$this->tokens->isNext('(')) { + if (!$this->tokens->is('(')) { return $node; } $attributes = $this->parseBraces(); $entities[] = $this->injectPos(new Node\EntityNode($node, $attributes->items), $node->startTokenPos, $attributes->endTokenPos); - while ($token = $this->tokens->consume(Token::Literal)) { + while ($token = $this->tokens->tryConsume(Token::Literal)) { $valueNode = new Node\LiteralNode(Node\LiteralNode::parse($token->value)); - $this->injectPos($valueNode, $this->tokens->getPos() - 1); - if ($this->tokens->isNext('(')) { + $this->injectPos($valueNode, $this->tokens->getIndex() - 1); + if ($this->tokens->is('(')) { $attributes = $this->parseBraces(); $entities[] = $this->injectPos(new Node\EntityNode($valueNode, $attributes->items), $valueNode->startTokenPos, $attributes->endTokenPos); } else { @@ -182,28 +182,28 @@ private function parseEntity(Node $node): Node private function parseBraces(): Node\InlineArrayNode { - $token = $this->tokens->consume(); + $token = $this->tokens->tryConsume(); $endBrace = ['[' => ']', '{' => '}', '(' => ')'][$token->value]; $res = new Node\InlineArrayNode($token->value); - $this->injectPos($res, $this->tokens->getPos() - 1); + $this->injectPos($res, $this->tokens->getIndex() - 1); $keyCheck = []; loop: - while ($this->tokens->consume(Token::Newline)); - if ($this->tokens->consume($endBrace)) { - $this->injectPos($res, $res->startTokenPos, $this->tokens->getPos() - 1); + while ($this->tokens->tryConsume(Token::Newline)); + if ($this->tokens->tryConsume($endBrace)) { + $this->injectPos($res, $res->startTokenPos, $this->tokens->getIndex() - 1); return $res; } $res->items[] = $item = new Node\ArrayItemNode; - $this->injectPos($item, $this->tokens->getPos()); + $this->injectPos($item, $this->tokens->getIndex()); $value = $this->parseValue(); - if ($this->tokens->consume(':', '=')) { + if ($this->tokens->tryConsume(':', '=')) { $this->checkArrayKey($value, $keyCheck); $item->key = $value; - $item->value = $this->tokens->isNext(Token::Newline, ',', $endBrace) - ? $this->injectPos(new Node\LiteralNode(null), $this->tokens->getPos()) + $item->value = $this->tokens->is(Token::Newline, ',', $endBrace) + ? $this->injectPos(new Node\LiteralNode(null), $this->tokens->getIndex()) : $this->parseValue(); } else { $item->value = $value; @@ -211,12 +211,12 @@ private function parseBraces(): Node\InlineArrayNode $this->injectPos($item, $item->startTokenPos, $item->value->endTokenPos); - $old = $this->tokens->getPos(); - while ($this->tokens->consume(Token::Newline)); - $this->tokens->consume(','); - if ($old !== $this->tokens->getPos()) { + $old = $this->tokens->getIndex(); + while ($this->tokens->tryConsume(Token::Newline)); + $this->tokens->tryConsume(','); + if ($old !== $this->tokens->getIndex()) { goto loop; - } elseif (!$this->tokens->isNext($endBrace)) { + } elseif (!$this->tokens->is($endBrace)) { $this->tokens->error(); } @@ -242,7 +242,7 @@ private function checkArrayKey(Node $key, array &$arr): void private function injectPos(Node $node, ?int $start = null, ?int $end = null): Node { - $node->startTokenPos = $start ?? $this->tokens->getPos(); + $node->startTokenPos = $start ?? $this->tokens->getIndex(); $node->startLine = $this->posToLine[$node->startTokenPos]; $node->endTokenPos = $end ?? $node->startTokenPos; $node->endLine = $this->posToLine[$node->endTokenPos + 1] ?? end($this->posToLine); diff --git a/src/Neon/TokenStream.php b/src/Neon/TokenStream.php index cff4783..1454a5b 100644 --- a/src/Neon/TokenStream.php +++ b/src/Neon/TokenStream.php @@ -13,7 +13,7 @@ /** @internal */ final class TokenStream { - private int $pos = 0; + private int $index = 0; public function __construct( @@ -23,15 +23,15 @@ public function __construct( } - public function getPos(): int + public function getIndex(): int { - return $this->pos; + return $this->index; } - public function seek(int $position): void + public function seek(int $index): void { - $this->pos = $position; + $this->index = $index; } @@ -42,31 +42,31 @@ public function getTokens(): array } - public function isNext(int|string ...$types): bool + public function is(int|string ...$types): bool { - while (in_array($this->tokens[$this->pos]->type ?? null, [Token::Comment, Token::Whitespace], strict: true)) { - $this->pos++; + while (in_array($this->tokens[$this->index]->type ?? null, [Token::Comment, Token::Whitespace], strict: true)) { + $this->index++; } return $types - ? in_array($this->tokens[$this->pos]->type ?? null, $types, strict: true) - : isset($this->tokens[$this->pos]); + ? in_array($this->tokens[$this->index]->type ?? null, $types, strict: true) + : isset($this->tokens[$this->index]); } - public function consume(int|string ...$types): ?Token + public function tryConsume(int|string ...$types): ?Token { - return $this->isNext(...$types) - ? $this->tokens[$this->pos++] + return $this->is(...$types) + ? $this->tokens[$this->index++] : null; } public function getIndentation(): string { - return in_array($this->tokens[$this->pos - 2]->type ?? null, [Token::Newline, null], strict: true) - && ($this->tokens[$this->pos - 1]->type ?? null) === Token::Whitespace - ? $this->tokens[$this->pos - 1]->value + return in_array($this->tokens[$this->index - 2]->type ?? null, [Token::Newline, null], strict: true) + && ($this->tokens[$this->index - 1]->type ?? null) === Token::Whitespace + ? $this->tokens[$this->index - 1]->value : ''; } @@ -74,7 +74,7 @@ public function getIndentation(): string /** @return never */ public function error(?string $message = null, ?int $pos = null): void { - $pos ??= $this->pos; + $pos ??= $this->index; $input = ''; foreach ($this->tokens as $i => $token) { if ($i >= $pos) { From 92c4f4cfe1b44f2e471eca8b42fee7c3f94ad54d Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 27 Sep 2023 13:39:18 +0200 Subject: [PATCH 06/17] TokenStream: $tokens is readonly --- src/Neon/Parser.php | 2 +- src/Neon/TokenStream.php | 9 +-------- tests/Neon/Parser.nodes.phpt | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Neon/Parser.php b/src/Neon/Parser.php index 3e1ab98..b915026 100644 --- a/src/Neon/Parser.php +++ b/src/Neon/Parser.php @@ -254,7 +254,7 @@ private function initLines(): void { $this->posToLine = []; $line = 1; - foreach ($this->tokens->getTokens() as $token) { + foreach ($this->tokens->tokens as $token) { $this->posToLine[] = $line; $line += substr_count($token->value, "\n"); } diff --git a/src/Neon/TokenStream.php b/src/Neon/TokenStream.php index 1454a5b..5bcaeb6 100644 --- a/src/Neon/TokenStream.php +++ b/src/Neon/TokenStream.php @@ -18,7 +18,7 @@ final class TokenStream public function __construct( /** @var Token[] */ - public array $tokens, + public /*readonly*/ array $tokens, ) { } @@ -35,13 +35,6 @@ public function seek(int $index): void } - /** @return Token[] */ - public function getTokens(): array - { - return $this->tokens; - } - - public function is(int|string ...$types): bool { while (in_array($this->tokens[$this->index]->type ?? null, [Token::Comment, Token::Whitespace], strict: true)) { diff --git a/tests/Neon/Parser.nodes.phpt b/tests/Neon/Parser.nodes.phpt index d7533f6..9eb9464 100644 --- a/tests/Neon/Parser.nodes.phpt +++ b/tests/Neon/Parser.nodes.phpt @@ -57,7 +57,7 @@ Assert::matchFile( $traverser = new Traverser; $traverser->traverse($node, function (Node $node) use ($stream) { @$node->code = ''; // dynamic property is deprecated - foreach (array_slice($stream->getTokens(), $node->startTokenPos, $node->endTokenPos - $node->startTokenPos + 1) as $token) { + foreach (array_slice($stream->tokens, $node->startTokenPos, $node->endTokenPos - $node->startTokenPos + 1) as $token) { $node->code .= $token->value; } From 4665736e2005ca407dce990e593c714da01a6e34 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 26 Sep 2023 16:39:42 +0200 Subject: [PATCH 07/17] Token: renamed props --- src/Neon/Lexer.php | 2 +- src/Neon/Parser.php | 12 ++++++------ src/Neon/Token.php | 2 +- src/Neon/TokenStream.php | 6 +++--- tests/Neon/Parser.nodes.phpt | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Neon/Lexer.php b/src/Neon/Lexer.php index 80bf64a..ad0d0aa 100644 --- a/src/Neon/Lexer.php +++ b/src/Neon/Lexer.php @@ -61,7 +61,7 @@ public function tokenize(string $input): TokenStream $tokens = []; foreach ($matches as $match) { $type = $types[count($match) - 2]; - $tokens[] = new Token($match[0], $type === Token::Char ? $match[0] : $type); + $tokens[] = new Token($type === Token::Char ? $match[0] : $type, $match[0]); $offset += strlen($match[0]); } diff --git a/src/Neon/Parser.php b/src/Neon/Parser.php index b915026..42aff60 100644 --- a/src/Neon/Parser.php +++ b/src/Neon/Parser.php @@ -132,14 +132,14 @@ private function parseValue(): Node { if ($token = $this->tokens->tryConsume(Token::String)) { try { - $node = new Node\StringNode(Node\StringNode::parse($token->value)); + $node = new Node\StringNode(Node\StringNode::parse($token->text)); $this->injectPos($node, $this->tokens->getIndex() - 1); } catch (Exception $e) { $this->tokens->error($e->getMessage(), $this->tokens->getIndex() - 1); } } elseif ($token = $this->tokens->tryConsume(Token::Literal)) { $pos = $this->tokens->getIndex() - 1; - $node = new Node\LiteralNode(Node\LiteralNode::parse($token->value, $this->tokens->is(':', '='))); + $node = new Node\LiteralNode(Node\LiteralNode::parse($token->text, $this->tokens->is(':', '='))); $this->injectPos($node, $pos); } elseif ($this->tokens->is('[', '(', '{')) { @@ -163,7 +163,7 @@ private function parseEntity(Node $node): Node $entities[] = $this->injectPos(new Node\EntityNode($node, $attributes->items), $node->startTokenPos, $attributes->endTokenPos); while ($token = $this->tokens->tryConsume(Token::Literal)) { - $valueNode = new Node\LiteralNode(Node\LiteralNode::parse($token->value)); + $valueNode = new Node\LiteralNode(Node\LiteralNode::parse($token->text)); $this->injectPos($valueNode, $this->tokens->getIndex() - 1); if ($this->tokens->is('(')) { $attributes = $this->parseBraces(); @@ -183,8 +183,8 @@ private function parseEntity(Node $node): Node private function parseBraces(): Node\InlineArrayNode { $token = $this->tokens->tryConsume(); - $endBrace = ['[' => ']', '{' => '}', '(' => ')'][$token->value]; - $res = new Node\InlineArrayNode($token->value); + $endBrace = ['[' => ']', '{' => '}', '(' => ')'][$token->text]; + $res = new Node\InlineArrayNode($token->text); $this->injectPos($res, $this->tokens->getIndex() - 1); $keyCheck = []; @@ -256,7 +256,7 @@ private function initLines(): void $line = 1; foreach ($this->tokens->tokens as $token) { $this->posToLine[] = $line; - $line += substr_count($token->value, "\n"); + $line += substr_count($token->text, "\n"); } $this->posToLine[] = $line; diff --git a/src/Neon/Token.php b/src/Neon/Token.php index 6a1653c..aff8130 100644 --- a/src/Neon/Token.php +++ b/src/Neon/Token.php @@ -22,8 +22,8 @@ final class Token public function __construct( - public string $value, public int|string $type, + public string $text, ) { } } diff --git a/src/Neon/TokenStream.php b/src/Neon/TokenStream.php index 5bcaeb6..74ab0db 100644 --- a/src/Neon/TokenStream.php +++ b/src/Neon/TokenStream.php @@ -59,7 +59,7 @@ public function getIndentation(): string { return in_array($this->tokens[$this->index - 2]->type ?? null, [Token::Newline, null], strict: true) && ($this->tokens[$this->index - 1]->type ?? null) === Token::Whitespace - ? $this->tokens[$this->index - 1]->value + ? $this->tokens[$this->index - 1]->text : ''; } @@ -74,7 +74,7 @@ public function error(?string $message = null, ?int $pos = null): void break; } - $input .= $token->value; + $input .= $token->text; } $line = substr_count($input, "\n") + 1; @@ -82,7 +82,7 @@ public function error(?string $message = null, ?int $pos = null): void $token = $this->tokens[$pos] ?? null; $message ??= 'Unexpected ' . ($token === null ? 'end' - : "'" . str_replace("\n", '', substr($this->tokens[$pos]->value, 0, 40)) . "'"); + : "'" . str_replace("\n", '', substr($this->tokens[$pos]->text, 0, 40)) . "'"); throw new Exception("$message on line $line at column $col"); } } diff --git a/tests/Neon/Parser.nodes.phpt b/tests/Neon/Parser.nodes.phpt index 9eb9464..8c1e3ff 100644 --- a/tests/Neon/Parser.nodes.phpt +++ b/tests/Neon/Parser.nodes.phpt @@ -58,7 +58,7 @@ $traverser = new Traverser; $traverser->traverse($node, function (Node $node) use ($stream) { @$node->code = ''; // dynamic property is deprecated foreach (array_slice($stream->tokens, $node->startTokenPos, $node->endTokenPos - $node->startTokenPos + 1) as $token) { - $node->code .= $token->value; + $node->code .= $token->text; } unset($node->startTokenPos, $node->endTokenPos); From 871948501f69b224d2ad1ee1290dbb5ff91300f6 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 28 Apr 2024 22:43:12 +0200 Subject: [PATCH 08/17] Parser: $tokens renamed to $stream --- src/Neon/Decoder.php | 4 +- src/Neon/Parser.php | 116 +++++++++++++++++++++---------------------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/Neon/Decoder.php b/src/Neon/Decoder.php index 9576067..bff9aa3 100644 --- a/src/Neon/Decoder.php +++ b/src/Neon/Decoder.php @@ -30,7 +30,7 @@ public function parseToNode(string $input): Node { $lexer = new Lexer; $parser = new Parser; - $tokens = $lexer->tokenize($input); - return $parser->parse($tokens); + $stream = $lexer->tokenize($input); + return $parser->parse($stream); } } diff --git a/src/Neon/Parser.php b/src/Neon/Parser.php index 42aff60..8975574 100644 --- a/src/Neon/Parser.php +++ b/src/Neon/Parser.php @@ -13,23 +13,23 @@ /** @internal */ final class Parser { - private TokenStream $tokens; + private TokenStream $stream; /** @var int[] */ private $posToLine = []; - public function parse(TokenStream $tokens): Node + public function parse(TokenStream $stream): Node { - $this->tokens = $tokens; + $this->stream = $stream; $this->initLines(); - while ($this->tokens->tryConsume(Token::Newline)); - $node = $this->parseBlock($this->tokens->getIndentation()); + while ($this->stream->tryConsume(Token::Newline)); + $node = $this->parseBlock($this->stream->getIndentation()); - while ($this->tokens->tryConsume(Token::Newline)); - if ($this->tokens->is()) { - $this->tokens->error(); + while ($this->stream->tryConsume(Token::Newline)); + if ($this->stream->is()) { + $this->stream->error(); } return $node; @@ -45,21 +45,21 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node loop: $item = new Node\ArrayItemNode; $this->injectPos($item); - if ($this->tokens->tryConsume('-')) { + if ($this->stream->tryConsume('-')) { // continue - } elseif (!$this->tokens->is() || $onlyBullets) { + } elseif (!$this->stream->is() || $onlyBullets) { return $res->items ? $res : $this->injectPos(new Node\LiteralNode(null)); } else { $value = $this->parseValue(); - if ($this->tokens->tryConsume(':', '=')) { + if ($this->stream->tryConsume(':', '=')) { $this->checkArrayKey($value, $keyCheck); $item->key = $value; } else { if ($res->items) { - $this->tokens->error(); + $this->stream->error(); } return $value; @@ -70,12 +70,12 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node $item->value = new Node\LiteralNode(null); $this->injectPos($item->value); - if ($this->tokens->tryConsume(Token::Newline)) { - while ($this->tokens->tryConsume(Token::Newline)); - $nextIndent = $this->tokens->getIndentation(); + if ($this->stream->tryConsume(Token::Newline)) { + while ($this->stream->tryConsume(Token::Newline)); + $nextIndent = $this->stream->getIndentation(); if (strncmp($nextIndent, $indent, min(strlen($nextIndent), strlen($indent)))) { - $this->tokens->error('Invalid combination of tabs and spaces'); + $this->stream->error('Invalid combination of tabs and spaces'); } elseif (strlen($nextIndent) > strlen($indent)) { // open new block $item->value = $this->parseBlock($nextIndent); @@ -83,21 +83,21 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node } elseif (strlen($nextIndent) < strlen($indent)) { // close block return $res; - } elseif ($item->key !== null && $this->tokens->is('-')) { // special dash subblock + } elseif ($item->key !== null && $this->stream->is('-')) { // special dash subblock $item->value = $this->parseBlock($indent, onlyBullets: true); } } elseif ($item->key === null) { // open new block after dash - $save = $this->tokens->getIndex(); + $save = $this->stream->getIndex(); try { $item->value = $this->parseBlock($indent . "\t"); } catch (Exception) { - $this->tokens->seek($save); + $this->stream->seek($save); $item->value = $this->parseBlock($indent . ' '); } - } elseif ($this->tokens->is()) { + } elseif ($this->stream->is()) { $item->value = $this->parseValue(); - if ($this->tokens->is() && !$this->tokens->is(Token::Newline)) { - $this->tokens->error(); + if ($this->stream->is() && !$this->stream->is(Token::Newline)) { + $this->stream->error(); } } @@ -108,17 +108,17 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node $this->injectPos($res, $res->startTokenPos, $item->value->endTokenPos); $this->injectPos($item, $item->startTokenPos, $item->value->endTokenPos); - while ($this->tokens->tryConsume(Token::Newline)); - if (!$this->tokens->is()) { + while ($this->stream->tryConsume(Token::Newline)); + if (!$this->stream->is()) { return $res; } - $nextIndent = $this->tokens->getIndentation(); + $nextIndent = $this->stream->getIndentation(); if (strncmp($nextIndent, $indent, min(strlen($nextIndent), strlen($indent)))) { - $this->tokens->error('Invalid combination of tabs and spaces'); + $this->stream->error('Invalid combination of tabs and spaces'); } elseif (strlen($nextIndent) > strlen($indent)) { - $this->tokens->error('Bad indentation'); + $this->stream->error('Bad indentation'); } elseif (strlen($nextIndent) < strlen($indent)) { // close block return $res; @@ -130,23 +130,23 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node private function parseValue(): Node { - if ($token = $this->tokens->tryConsume(Token::String)) { + if ($token = $this->stream->tryConsume(Token::String)) { try { $node = new Node\StringNode(Node\StringNode::parse($token->text)); - $this->injectPos($node, $this->tokens->getIndex() - 1); + $this->injectPos($node, $this->stream->getIndex() - 1); } catch (Exception $e) { - $this->tokens->error($e->getMessage(), $this->tokens->getIndex() - 1); + $this->stream->error($e->getMessage(), $this->stream->getIndex() - 1); } - } elseif ($token = $this->tokens->tryConsume(Token::Literal)) { - $pos = $this->tokens->getIndex() - 1; - $node = new Node\LiteralNode(Node\LiteralNode::parse($token->text, $this->tokens->is(':', '='))); + } elseif ($token = $this->stream->tryConsume(Token::Literal)) { + $pos = $this->stream->getIndex() - 1; + $node = new Node\LiteralNode(Node\LiteralNode::parse($token->text, $this->stream->is(':', '='))); $this->injectPos($node, $pos); - } elseif ($this->tokens->is('[', '(', '{')) { + } elseif ($this->stream->is('[', '(', '{')) { $node = $this->parseBraces(); } else { - $this->tokens->error(); + $this->stream->error(); } return $this->parseEntity($node); @@ -155,17 +155,17 @@ private function parseValue(): Node private function parseEntity(Node $node): Node { - if (!$this->tokens->is('(')) { + if (!$this->stream->is('(')) { return $node; } $attributes = $this->parseBraces(); $entities[] = $this->injectPos(new Node\EntityNode($node, $attributes->items), $node->startTokenPos, $attributes->endTokenPos); - while ($token = $this->tokens->tryConsume(Token::Literal)) { + while ($token = $this->stream->tryConsume(Token::Literal)) { $valueNode = new Node\LiteralNode(Node\LiteralNode::parse($token->text)); - $this->injectPos($valueNode, $this->tokens->getIndex() - 1); - if ($this->tokens->is('(')) { + $this->injectPos($valueNode, $this->stream->getIndex() - 1); + if ($this->stream->is('(')) { $attributes = $this->parseBraces(); $entities[] = $this->injectPos(new Node\EntityNode($valueNode, $attributes->items), $valueNode->startTokenPos, $attributes->endTokenPos); } else { @@ -182,28 +182,28 @@ private function parseEntity(Node $node): Node private function parseBraces(): Node\InlineArrayNode { - $token = $this->tokens->tryConsume(); + $token = $this->stream->tryConsume(); $endBrace = ['[' => ']', '{' => '}', '(' => ')'][$token->text]; $res = new Node\InlineArrayNode($token->text); - $this->injectPos($res, $this->tokens->getIndex() - 1); + $this->injectPos($res, $this->stream->getIndex() - 1); $keyCheck = []; loop: - while ($this->tokens->tryConsume(Token::Newline)); - if ($this->tokens->tryConsume($endBrace)) { - $this->injectPos($res, $res->startTokenPos, $this->tokens->getIndex() - 1); + while ($this->stream->tryConsume(Token::Newline)); + if ($this->stream->tryConsume($endBrace)) { + $this->injectPos($res, $res->startTokenPos, $this->stream->getIndex() - 1); return $res; } $res->items[] = $item = new Node\ArrayItemNode; - $this->injectPos($item, $this->tokens->getIndex()); + $this->injectPos($item, $this->stream->getIndex()); $value = $this->parseValue(); - if ($this->tokens->tryConsume(':', '=')) { + if ($this->stream->tryConsume(':', '=')) { $this->checkArrayKey($value, $keyCheck); $item->key = $value; - $item->value = $this->tokens->is(Token::Newline, ',', $endBrace) - ? $this->injectPos(new Node\LiteralNode(null), $this->tokens->getIndex()) + $item->value = $this->stream->is(Token::Newline, ',', $endBrace) + ? $this->injectPos(new Node\LiteralNode(null), $this->stream->getIndex()) : $this->parseValue(); } else { $item->value = $value; @@ -211,13 +211,13 @@ private function parseBraces(): Node\InlineArrayNode $this->injectPos($item, $item->startTokenPos, $item->value->endTokenPos); - $old = $this->tokens->getIndex(); - while ($this->tokens->tryConsume(Token::Newline)); - $this->tokens->tryConsume(','); - if ($old !== $this->tokens->getIndex()) { + $old = $this->stream->getIndex(); + while ($this->stream->tryConsume(Token::Newline)); + $this->stream->tryConsume(','); + if ($old !== $this->stream->getIndex()) { goto loop; - } elseif (!$this->tokens->is($endBrace)) { - $this->tokens->error(); + } elseif (!$this->stream->is($endBrace)) { + $this->stream->error(); } goto loop; @@ -228,12 +228,12 @@ private function parseBraces(): Node\InlineArrayNode private function checkArrayKey(Node $key, array &$arr): void { if ((!$key instanceof Node\StringNode && !$key instanceof Node\LiteralNode) || !is_scalar($key->value)) { - $this->tokens->error('Unacceptable key', $key->startTokenPos); + $this->stream->error('Unacceptable key', $key->startTokenPos); } $k = (string) $key->value; if (array_key_exists($k, $arr)) { - $this->tokens->error("Duplicated key '$k'", $key->startTokenPos); + $this->stream->error("Duplicated key '$k'", $key->startTokenPos); } $arr[$k] = true; @@ -242,7 +242,7 @@ private function checkArrayKey(Node $key, array &$arr): void private function injectPos(Node $node, ?int $start = null, ?int $end = null): Node { - $node->startTokenPos = $start ?? $this->tokens->getIndex(); + $node->startTokenPos = $start ?? $this->stream->getIndex(); $node->startLine = $this->posToLine[$node->startTokenPos]; $node->endTokenPos = $end ?? $node->startTokenPos; $node->endLine = $this->posToLine[$node->endTokenPos + 1] ?? end($this->posToLine); @@ -254,7 +254,7 @@ private function initLines(): void { $this->posToLine = []; $line = 1; - foreach ($this->tokens->tokens as $token) { + foreach ($this->stream->tokens as $token) { $this->posToLine[] = $line; $line += substr_count($token->text, "\n"); } From b4dffa594618506f636437233583552e05cf0a86 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 27 Sep 2023 12:13:45 +0200 Subject: [PATCH 09/17] added 'end' token --- src/Neon/Lexer.php | 4 +++- src/Neon/Parser.php | 10 +++++----- src/Neon/Token.php | 1 + src/Neon/TokenStream.php | 10 +++++----- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Neon/Lexer.php b/src/Neon/Lexer.php index ad0d0aa..ae2e734 100644 --- a/src/Neon/Lexer.php +++ b/src/Neon/Lexer.php @@ -65,10 +65,12 @@ public function tokenize(string $input): TokenStream $offset += strlen($match[0]); } + $tokens[] = new Token(Token::End, ''); + $stream = new TokenStream($tokens); if ($offset !== strlen($input)) { $s = str_replace("\n", '\n', substr($input, $offset, 40)); - $stream->error("Unexpected '$s'", count($tokens)); + $stream->error("Unexpected '$s'", count($tokens) - 1); } return $stream; diff --git a/src/Neon/Parser.php b/src/Neon/Parser.php index 8975574..67a487b 100644 --- a/src/Neon/Parser.php +++ b/src/Neon/Parser.php @@ -28,7 +28,7 @@ public function parse(TokenStream $stream): Node $node = $this->parseBlock($this->stream->getIndentation()); while ($this->stream->tryConsume(Token::Newline)); - if ($this->stream->is()) { + if (!$this->stream->is(Token::End)) { $this->stream->error(); } @@ -47,7 +47,7 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node $this->injectPos($item); if ($this->stream->tryConsume('-')) { // continue - } elseif (!$this->stream->is() || $onlyBullets) { + } elseif ($this->stream->is(Token::End) || $onlyBullets) { return $res->items ? $res : $this->injectPos(new Node\LiteralNode(null)); @@ -94,9 +94,9 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node $this->stream->seek($save); $item->value = $this->parseBlock($indent . ' '); } - } elseif ($this->stream->is()) { + } elseif (!$this->stream->is(Token::End)) { $item->value = $this->parseValue(); - if ($this->stream->is() && !$this->stream->is(Token::Newline)) { + if (!$this->stream->is(Token::End, Token::Newline)) { $this->stream->error(); } } @@ -109,7 +109,7 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node $this->injectPos($item, $item->startTokenPos, $item->value->endTokenPos); while ($this->stream->tryConsume(Token::Newline)); - if (!$this->stream->is()) { + if ($this->stream->is(Token::End)) { return $res; } diff --git a/src/Neon/Token.php b/src/Neon/Token.php index aff8130..0671aff 100644 --- a/src/Neon/Token.php +++ b/src/Neon/Token.php @@ -19,6 +19,7 @@ final class Token public const Comment = 3; public const Newline = 4; public const Whitespace = 5; + public const End = -1; public function __construct( diff --git a/src/Neon/TokenStream.php b/src/Neon/TokenStream.php index 74ab0db..3f44e42 100644 --- a/src/Neon/TokenStream.php +++ b/src/Neon/TokenStream.php @@ -37,13 +37,13 @@ public function seek(int $index): void public function is(int|string ...$types): bool { - while (in_array($this->tokens[$this->index]->type ?? null, [Token::Comment, Token::Whitespace], strict: true)) { + while (in_array($this->tokens[$this->index]->type, [Token::Comment, Token::Whitespace], strict: true)) { $this->index++; } return $types - ? in_array($this->tokens[$this->index]->type ?? null, $types, strict: true) - : isset($this->tokens[$this->index]); + ? in_array($this->tokens[$this->index]->type, $types, strict: true) + : $this->tokens[$this->index]->type !== Token::End; } @@ -79,8 +79,8 @@ public function error(?string $message = null, ?int $pos = null): void $line = substr_count($input, "\n") + 1; $col = strlen($input) - strrpos("\n" . $input, "\n") + 1; - $token = $this->tokens[$pos] ?? null; - $message ??= 'Unexpected ' . ($token === null + $token = $this->tokens[$pos]; + $message ??= 'Unexpected ' . ($token->type === Token::End ? 'end' : "'" . str_replace("\n", '', substr($this->tokens[$pos]->text, 0, 40)) . "'"); throw new Exception("$message on line $line at column $col"); From 8ca4bdd3da31b16ec04d63d40fe415f2c36eae6d Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 27 Sep 2023 14:56:37 +0200 Subject: [PATCH 10/17] TokenStream: refactoring --- src/Neon/Token.php | 6 ++++++ src/Neon/TokenStream.php | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Neon/Token.php b/src/Neon/Token.php index 0671aff..9985185 100644 --- a/src/Neon/Token.php +++ b/src/Neon/Token.php @@ -27,4 +27,10 @@ public function __construct( public string $text, ) { } + + + public function is(int|string ...$kind): bool + { + return in_array($this->type, $kind, strict: true); + } } diff --git a/src/Neon/TokenStream.php b/src/Neon/TokenStream.php index 3f44e42..e756ba2 100644 --- a/src/Neon/TokenStream.php +++ b/src/Neon/TokenStream.php @@ -35,21 +35,27 @@ public function seek(int $index): void } - public function is(int|string ...$types): bool + /** + * Tells whether the token at current position is of given kind. + */ + public function is(int|string ...$kind): bool { - while (in_array($this->tokens[$this->index]->type, [Token::Comment, Token::Whitespace], strict: true)) { + while ($this->tokens[$this->index]->is(Token::Comment, Token::Whitespace)) { $this->index++; } - return $types - ? in_array($this->tokens[$this->index]->type, $types, strict: true) + return $kind + ? $this->tokens[$this->index]->is(...$kind) : $this->tokens[$this->index]->type !== Token::End; } - public function tryConsume(int|string ...$types): ?Token + /** + * Consumes the current token of given kind or returns null. + */ + public function tryConsume(int|string ...$kind): ?Token { - return $this->is(...$types) + return $this->is(...$kind) ? $this->tokens[$this->index++] : null; } From 2cb0dba20d4bb6933a1f21e2a23f4deed8f82aa4 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 27 Sep 2023 11:58:05 +0200 Subject: [PATCH 11/17] added Position to Token --- src/Neon/Lexer.php | 30 ++++++++++++++++++++++++------ src/Neon/Position.php | 27 +++++++++++++++++++++++++++ src/Neon/Token.php | 1 + src/Neon/TokenStream.php | 15 ++------------- 4 files changed, 54 insertions(+), 19 deletions(-) create mode 100644 src/Neon/Position.php diff --git a/src/Neon/Lexer.php b/src/Neon/Lexer.php index ae2e734..4791f9f 100644 --- a/src/Neon/Lexer.php +++ b/src/Neon/Lexer.php @@ -56,20 +56,20 @@ public function tokenize(string $input): TokenStream } $types = array_keys(self::Patterns); - $offset = 0; + $position = new Position; $tokens = []; foreach ($matches as $match) { $type = $types[count($match) - 2]; - $tokens[] = new Token($type === Token::Char ? $match[0] : $type, $match[0]); - $offset += strlen($match[0]); + $tokens[] = new Token($type === Token::Char ? $match[0] : $type, $match[0], $position); + $position = $this->advance($position, $match[0]); } - $tokens[] = new Token(Token::End, ''); + $tokens[] = new Token(Token::End, '', $position); $stream = new TokenStream($tokens); - if ($offset !== strlen($input)) { - $s = str_replace("\n", '\n', substr($input, $offset, 40)); + if ($position->offset !== strlen($input)) { + $s = str_replace("\n", '\n', substr($input, $position->offset, 40)); $stream->error("Unexpected '$s'", count($tokens) - 1); } @@ -77,6 +77,24 @@ public function tokenize(string $input): TokenStream } + private function advance(Position $position, string $str): Position + { + if ($lines = substr_count($str, "\n")) { + return new Position( + $position->line + $lines, + strlen($str) - strrpos($str, "\n"), + $position->offset + strlen($str), + ); + } else { + return new Position( + $position->line, + $position->column + strlen($str), + $position->offset + strlen($str), + ); + } + } + + public static function requiresDelimiters(string $s): bool { return preg_match('~[\x00-\x1F]|^[+-.]?\d|^(true|false|yes|no|on|off|null)$~Di', $s) diff --git a/src/Neon/Position.php b/src/Neon/Position.php new file mode 100644 index 0000000..a172bd1 --- /dev/null +++ b/src/Neon/Position.php @@ -0,0 +1,27 @@ +line" . ($this->column ? " at column $this->column" : ''); + } +} diff --git a/src/Neon/Token.php b/src/Neon/Token.php index 9985185..d92eee4 100644 --- a/src/Neon/Token.php +++ b/src/Neon/Token.php @@ -25,6 +25,7 @@ final class Token public function __construct( public int|string $type, public string $text, + public Position $position, ) { } diff --git a/src/Neon/TokenStream.php b/src/Neon/TokenStream.php index e756ba2..2b5c460 100644 --- a/src/Neon/TokenStream.php +++ b/src/Neon/TokenStream.php @@ -74,21 +74,10 @@ public function getIndentation(): string public function error(?string $message = null, ?int $pos = null): void { $pos ??= $this->index; - $input = ''; - foreach ($this->tokens as $i => $token) { - if ($i >= $pos) { - break; - } - - $input .= $token->text; - } - - $line = substr_count($input, "\n") + 1; - $col = strlen($input) - strrpos("\n" . $input, "\n") + 1; $token = $this->tokens[$pos]; $message ??= 'Unexpected ' . ($token->type === Token::End ? 'end' - : "'" . str_replace("\n", '', substr($this->tokens[$pos]->text, 0, 40)) . "'"); - throw new Exception("$message on line $line at column $col"); + : "'" . str_replace("\n", '', substr($token->text, 0, 40)) . "'"); + throw new Exception("$message $token->position"); } } From 5687b7845e2abc4bbf042fa3df5043aea167c4fe Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 27 Sep 2023 14:05:28 +0200 Subject: [PATCH 12/17] added Position to Exception --- src/Neon/Exception.php | 9 +++++++++ src/Neon/Lexer.php | 2 +- src/Neon/Node/StringNode.php | 8 ++++---- src/Neon/Parser.php | 9 +++------ src/Neon/TokenStream.php | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/Neon/Exception.php b/src/Neon/Exception.php index 0a37ad9..09d94e1 100644 --- a/src/Neon/Exception.php +++ b/src/Neon/Exception.php @@ -15,4 +15,13 @@ */ class Exception extends \Exception { + public ?Position /*readonly*/ $position = null; + + + public function __construct(string $message, ?Position $position = null, ?\Throwable $previous = null) + { + $message .= $position ? ' ' . $position : ''; + $this->position = $position; + parent::__construct($message, 0, $previous); + } } diff --git a/src/Neon/Lexer.php b/src/Neon/Lexer.php index 4791f9f..8fa7e17 100644 --- a/src/Neon/Lexer.php +++ b/src/Neon/Lexer.php @@ -70,7 +70,7 @@ public function tokenize(string $input): TokenStream $stream = new TokenStream($tokens); if ($position->offset !== strlen($input)) { $s = str_replace("\n", '\n', substr($input, $position->offset, 40)); - $stream->error("Unexpected '$s'", count($tokens) - 1); + throw new Exception("Unexpected '$s'", $position); } return $stream; diff --git a/src/Neon/Node/StringNode.php b/src/Neon/Node/StringNode.php index 4fcb6a9..eaa9d7e 100644 --- a/src/Neon/Node/StringNode.php +++ b/src/Neon/Node/StringNode.php @@ -33,7 +33,7 @@ public function toValue(): string } - public static function parse(string $s): string + public static function parse(string $s, Nette\Neon\Position $position): string { if (preg_match('#^...\n++([\t ]*+)#', $s, $m)) { // multiline $res = substr($s, 3, -3); @@ -52,14 +52,14 @@ public static function parse(string $s): string return preg_replace_callback( '#\\\(?:ud[89ab][0-9a-f]{2}\\\ud[c-f][0-9a-f]{2}|u[0-9a-f]{4}|.)#i', - function (array $m): string { + function (array $m) use ($position): string { $sq = $m[0]; if (isset(self::EscapeSequences[$sq[1]])) { return self::EscapeSequences[$sq[1]]; } elseif ($sq[1] === 'u' && strlen($sq) >= 6) { - return json_decode('"' . $sq . '"') ?? throw new Nette\Neon\Exception("Invalid UTF-8 sequence $sq"); + return json_decode('"' . $sq . '"') ?? throw new Nette\Neon\Exception("Invalid UTF-8 sequence $sq", $position); } else { - throw new Nette\Neon\Exception("Invalid escaping sequence $sq"); + throw new Nette\Neon\Exception("Invalid escaping sequence $sq", $position); } }, $res, diff --git a/src/Neon/Parser.php b/src/Neon/Parser.php index 67a487b..8545c78 100644 --- a/src/Neon/Parser.php +++ b/src/Neon/Parser.php @@ -131,12 +131,9 @@ private function parseBlock(string $indent, bool $onlyBullets = false): Node private function parseValue(): Node { if ($token = $this->stream->tryConsume(Token::String)) { - try { - $node = new Node\StringNode(Node\StringNode::parse($token->text)); - $this->injectPos($node, $this->stream->getIndex() - 1); - } catch (Exception $e) { - $this->stream->error($e->getMessage(), $this->stream->getIndex() - 1); - } + $node = new Node\StringNode(Node\StringNode::parse($token->text, $token->position)); + $this->injectPos($node, $this->stream->getIndex() - 1); + } elseif ($token = $this->stream->tryConsume(Token::Literal)) { $pos = $this->stream->getIndex() - 1; $node = new Node\LiteralNode(Node\LiteralNode::parse($token->text, $this->stream->is(':', '='))); diff --git a/src/Neon/TokenStream.php b/src/Neon/TokenStream.php index 2b5c460..56a5bd6 100644 --- a/src/Neon/TokenStream.php +++ b/src/Neon/TokenStream.php @@ -78,6 +78,6 @@ public function error(?string $message = null, ?int $pos = null): void $message ??= 'Unexpected ' . ($token->type === Token::End ? 'end' : "'" . str_replace("\n", '', substr($token->text, 0, 40)) . "'"); - throw new Exception("$message $token->position"); + throw new Exception($message, $token->position); } } From b59b81acde662d3e138fc2b86f665be6a57d560a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 27 Sep 2023 12:12:22 +0200 Subject: [PATCH 13/17] Node: added $start & $end positions (replaces $startLine & $endLine) --- src/Neon/Node.php | 4 +- src/Neon/Parser.php | 22 +- tests/Neon/fixtures/Encoder.nodes.txt | 228 ++++---- tests/Neon/fixtures/Parser.nodes.txt | 740 ++++++++++++++++++++------ 4 files changed, 711 insertions(+), 283 deletions(-) diff --git a/src/Neon/Node.php b/src/Neon/Node.php index 4c5c8f8..259963a 100644 --- a/src/Neon/Node.php +++ b/src/Neon/Node.php @@ -17,8 +17,8 @@ abstract class Node implements \IteratorAggregate { public ?int $startTokenPos = null; public ?int $endTokenPos = null; - public ?int $startLine = null; - public ?int $endLine = null; + public ?Position $start = null; + public ?Position $end = null; abstract public function toValue(): mixed; diff --git a/src/Neon/Parser.php b/src/Neon/Parser.php index 8545c78..827d74c 100644 --- a/src/Neon/Parser.php +++ b/src/Neon/Parser.php @@ -15,14 +15,10 @@ final class Parser { private TokenStream $stream; - /** @var int[] */ - private $posToLine = []; - public function parse(TokenStream $stream): Node { $this->stream = $stream; - $this->initLines(); while ($this->stream->tryConsume(Token::Newline)); $node = $this->parseBlock($this->stream->getIndentation()); @@ -240,22 +236,10 @@ private function checkArrayKey(Node $key, array &$arr): void private function injectPos(Node $node, ?int $start = null, ?int $end = null): Node { $node->startTokenPos = $start ?? $this->stream->getIndex(); - $node->startLine = $this->posToLine[$node->startTokenPos]; + $node->start = $this->stream->tokens[$node->startTokenPos]->position; $node->endTokenPos = $end ?? $node->startTokenPos; - $node->endLine = $this->posToLine[$node->endTokenPos + 1] ?? end($this->posToLine); + $token = $this->stream->tokens[$node->startTokenPos + 1] ?? $this->stream->tokens[$node->startTokenPos]; + $node->end = $token->position; return $node; } - - - private function initLines(): void - { - $this->posToLine = []; - $line = 1; - foreach ($this->stream->tokens as $token) { - $this->posToLine[] = $line; - $line += substr_count($token->text, "\n"); - } - - $this->posToLine[] = $line; - } } diff --git a/tests/Neon/fixtures/Encoder.nodes.txt b/tests/Neon/fixtures/Encoder.nodes.txt index d505e10..8366dc6 100644 --- a/tests/Neon/fixtures/Encoder.nodes.txt +++ b/tests/Neon/fixtures/Encoder.nodes.txt @@ -6,8 +6,8 @@ Nette\Neon\Node\InlineArrayNode | | | value: 'map' | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | value: Nette\Neon\Node\InlineArrayNode | | | bracket: '{' | | | items: array (2) @@ -16,50 +16,50 @@ Nette\Neon\Node\InlineArrayNode | | | | | | value: 'a' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | value: 'b' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | | 1 => Nette\Neon\Node\ArrayItemNode | | | | | key: Nette\Neon\Node\LiteralNode | | | | | | value: 'c' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | value: 'd' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | startTokenPos: null | | endTokenPos: null - | | startLine: null - | | endLine: null + | | start: null + | | end: null | 1 => Nette\Neon\Node\ArrayItemNode | | key: Nette\Neon\Node\LiteralNode | | | value: 'index' | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | value: Nette\Neon\Node\InlineArrayNode | | | bracket: '[' | | | items: array (3) @@ -69,51 +69,51 @@ Nette\Neon\Node\InlineArrayNode | | | | | | value: 'a' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | | 1 => Nette\Neon\Node\ArrayItemNode | | | | | key: null | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | value: 'b' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | | 2 => Nette\Neon\Node\ArrayItemNode | | | | | key: null | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | value: 'c' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | startTokenPos: null | | endTokenPos: null - | | startLine: null - | | endLine: null + | | start: null + | | end: null | 2 => Nette\Neon\Node\ArrayItemNode | | key: Nette\Neon\Node\LiteralNode | | | value: 'mixed' | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | value: Nette\Neon\Node\InlineArrayNode | | | bracket: '{' | | | items: array (4) @@ -123,80 +123,80 @@ Nette\Neon\Node\InlineArrayNode | | | | | | value: 'a' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | | 1 => Nette\Neon\Node\ArrayItemNode | | | | | key: null | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | value: 'b' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | | 2 => Nette\Neon\Node\ArrayItemNode | | | | | key: Nette\Neon\Node\LiteralNode | | | | | | value: 4 | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | value: 'c' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | | 3 => Nette\Neon\Node\ArrayItemNode | | | | | key: Nette\Neon\Node\LiteralNode | | | | | | value: 5 | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | value: 'd' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | startTokenPos: null | | endTokenPos: null - | | startLine: null - | | endLine: null + | | start: null + | | end: null | 3 => Nette\Neon\Node\ArrayItemNode | | key: Nette\Neon\Node\LiteralNode | | | value: 'entity' | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | value: Nette\Neon\Node\EntityNode | | | value: Nette\Neon\Node\LiteralNode | | | | value: 'ent' | | | | startTokenPos: null | | | | endTokenPos: null - | | | | startLine: null - | | | | endLine: null + | | | | start: null + | | | | end: null | | | attributes: array (2) | | | | 0 => Nette\Neon\Node\ArrayItemNode | | | | | key: null @@ -204,39 +204,39 @@ Nette\Neon\Node\InlineArrayNode | | | | | | value: 'a' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | | 1 => Nette\Neon\Node\ArrayItemNode | | | | | key: null | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | value: 'b' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | startTokenPos: null | | endTokenPos: null - | | startLine: null - | | endLine: null + | | start: null + | | end: null | 4 => Nette\Neon\Node\ArrayItemNode | | key: Nette\Neon\Node\LiteralNode | | | value: 'chain' | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | value: Nette\Neon\Node\EntityChainNode | | | chain: array (2) | | | | 0 => Nette\Neon\Node\EntityNode @@ -244,8 +244,8 @@ Nette\Neon\Node\InlineArrayNode | | | | | | value: 'first' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | attributes: array (2) | | | | | | 0 => Nette\Neon\Node\ArrayItemNode | | | | | | | key: null @@ -253,74 +253,74 @@ Nette\Neon\Node\InlineArrayNode | | | | | | | | value: 'a' | | | | | | | | startTokenPos: null | | | | | | | | endTokenPos: null - | | | | | | | | startLine: null - | | | | | | | | endLine: null + | | | | | | | | start: null + | | | | | | | | end: null | | | | | | | startTokenPos: null | | | | | | | endTokenPos: null - | | | | | | | startLine: null - | | | | | | | endLine: null + | | | | | | | start: null + | | | | | | | end: null | | | | | | 1 => Nette\Neon\Node\ArrayItemNode | | | | | | | key: null | | | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | | | value: 'b' | | | | | | | | startTokenPos: null | | | | | | | | endTokenPos: null - | | | | | | | | startLine: null - | | | | | | | | endLine: null + | | | | | | | | start: null + | | | | | | | | end: null | | | | | | | startTokenPos: null | | | | | | | endTokenPos: null - | | | | | | | startLine: null - | | | | | | | endLine: null + | | | | | | | start: null + | | | | | | | end: null | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | | 1 => Nette\Neon\Node\EntityNode | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | value: 'second' | | | | | | startTokenPos: null | | | | | | endTokenPos: null - | | | | | | startLine: null - | | | | | | endLine: null + | | | | | | start: null + | | | | | | end: null | | | | | attributes: array (0) | | | | | startTokenPos: null | | | | | endTokenPos: null - | | | | | startLine: null - | | | | | endLine: null + | | | | | start: null + | | | | | end: null | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | startTokenPos: null | | endTokenPos: null - | | startLine: null - | | endLine: null + | | start: null + | | end: null | 5 => Nette\Neon\Node\ArrayItemNode | | key: Nette\Neon\Node\LiteralNode | | | value: 'multiline' | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | value: Nette\Neon\Node\StringNode | | | value: string | | | | 'hello\n | | | | world' | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | startTokenPos: null | | endTokenPos: null - | | startLine: null - | | endLine: null + | | start: null + | | end: null | 6 => Nette\Neon\Node\ArrayItemNode | | key: Nette\Neon\Node\LiteralNode | | | value: 'date' | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | value: Nette\Neon\Node\LiteralNode | | | value: DateTime | | | | date: '2016-06-03 19:00:00.000000' @@ -328,13 +328,13 @@ Nette\Neon\Node\InlineArrayNode | | | | timezone: '+02:00' | | | startTokenPos: null | | | endTokenPos: null - | | | startLine: null - | | | endLine: null + | | | start: null + | | | end: null | | startTokenPos: null | | endTokenPos: null - | | startLine: null - | | endLine: null + | | start: null + | | end: null startTokenPos: null endTokenPos: null - startLine: null - endLine: null + start: null + end: null diff --git a/tests/Neon/fixtures/Parser.nodes.txt b/tests/Neon/fixtures/Parser.nodes.txt index 9bafce4..20bf7b5 100644 --- a/tests/Neon/fixtures/Parser.nodes.txt +++ b/tests/Neon/fixtures/Parser.nodes.txt @@ -27,8 +27,14 @@ Nette\Neon\Node\BlockArrayNode | | | value: 'first' | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 3 - | | | endLine: 3 + | | | start: Nette\Neon\Position + | | | | line: 3 + | | | | column: 1 + | | | | offset: 9 + | | | end: Nette\Neon\Position + | | | | line: 3 + | | | | column: 6 + | | | | offset: 14 | | value: Nette\Neon\Node\BlockArrayNode | | | code: '- a' | | | indentation: '\t ' @@ -41,20 +47,44 @@ Nette\Neon\Node\BlockArrayNode | | | | | | value: 'a' | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 5 - | | | | | | endLine: 5 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 5 + | | | | | | | column: 4 + | | | | | | | offset: 54 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 5 + | | | | | | | column: 5 + | | | | | | | offset: 55 | | | | | startTokenPos: unset | | | | | endTokenPos: unset - | | | | | startLine: 5 - | | | | | endLine: 5 + | | | | | start: Nette\Neon\Position + | | | | | | line: 5 + | | | | | | column: 2 + | | | | | | offset: 52 + | | | | | end: Nette\Neon\Position + | | | | | | line: 5 + | | | | | | column: 3 + | | | | | | offset: 53 | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 5 - | | | endLine: 5 + | | | start: Nette\Neon\Position + | | | | line: 5 + | | | | column: 2 + | | | | offset: 52 + | | | end: Nette\Neon\Position + | | | | line: 5 + | | | | column: 3 + | | | | offset: 53 | | startTokenPos: unset | | endTokenPos: unset - | | startLine: 3 - | | endLine: 5 + | | start: Nette\Neon\Position + | | | line: 3 + | | | column: 1 + | | | offset: 9 + | | end: Nette\Neon\Position + | | | line: 3 + | | | column: 6 + | | | offset: 14 | 1 => Nette\Neon\Node\ArrayItemNode | | code: string | | | 'next:\n @@ -66,8 +96,14 @@ Nette\Neon\Node\BlockArrayNode | | | value: 'next' | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 6 - | | | endLine: 6 + | | | start: Nette\Neon\Position + | | | | line: 6 + | | | | column: 1 + | | | | offset: 69 + | | | end: Nette\Neon\Position + | | | | line: 6 + | | | | column: 5 + | | | | offset: 73 | | value: Nette\Neon\Node\BlockArrayNode | | | code: string | | | | '- [k,\n @@ -96,12 +132,24 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | | | value: 'k' | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 7 - | | | | | | | | | endLine: 7 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 7 + | | | | | | | | | | column: 5 + | | | | | | | | | | offset: 79 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 7 + | | | | | | | | | | column: 6 + | | | | | | | | | | offset: 80 | | | | | | | | startTokenPos: unset | | | | | | | | endTokenPos: unset - | | | | | | | | startLine: 7 - | | | | | | | | endLine: 7 + | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | line: 7 + | | | | | | | | | column: 5 + | | | | | | | | | offset: 79 + | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | line: 7 + | | | | | | | | | column: 6 + | | | | | | | | | offset: 80 | | | | | | | 1 => Nette\Neon\Node\ArrayItemNode | | | | | | | | code: 'l' | | | | | | | | key: null @@ -110,12 +158,24 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | | | value: 'l' | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 8 - | | | | | | | | | endLine: 8 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 8 + | | | | | | | | | | column: 3 + | | | | | | | | | | offset: 84 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 8 + | | | | | | | | | | column: 4 + | | | | | | | | | | offset: 85 | | | | | | | | startTokenPos: unset | | | | | | | | endTokenPos: unset - | | | | | | | | startLine: 8 - | | | | | | | | endLine: 8 + | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | line: 8 + | | | | | | | | | column: 3 + | | | | | | | | | offset: 84 + | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | line: 8 + | | | | | | | | | column: 4 + | | | | | | | | | offset: 85 | | | | | | | 2 => Nette\Neon\Node\ArrayItemNode | | | | | | | | code: 'm:\n' | | | | | | | | key: Nette\Neon\Node\LiteralNode @@ -123,19 +183,37 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | | | value: 'm' | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 8 - | | | | | | | | | endLine: 8 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 8 + | | | | | | | | | | column: 6 + | | | | | | | | | | offset: 87 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 8 + | | | | | | | | | | column: 7 + | | | | | | | | | | offset: 88 | | | | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | | | | code: '\n' | | | | | | | | | value: null | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 8 - | | | | | | | | | endLine: 9 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 8 + | | | | | | | | | | column: 8 + | | | | | | | | | | offset: 89 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 9 + | | | | | | | | | | column: 1 + | | | | | | | | | | offset: 90 | | | | | | | | startTokenPos: unset | | | | | | | | endTokenPos: unset - | | | | | | | | startLine: 8 - | | | | | | | | endLine: 9 + | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | line: 8 + | | | | | | | | | column: 6 + | | | | | | | | | offset: 87 + | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | line: 8 + | | | | | | | | | column: 7 + | | | | | | | | | offset: 88 | | | | | | | 3 => Nette\Neon\Node\ArrayItemNode | | | | | | | | code: 'n' | | | | | | | | key: null @@ -144,28 +222,64 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | | | value: 'n' | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 9 - | | | | | | | | | endLine: 9 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 9 + | | | | | | | | | | column: 2 + | | | | | | | | | | offset: 91 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 9 + | | | | | | | | | | column: 3 + | | | | | | | | | | offset: 92 | | | | | | | | startTokenPos: unset | | | | | | | | endTokenPos: unset - | | | | | | | | startLine: 9 - | | | | | | | | endLine: 9 + | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | line: 9 + | | | | | | | | | column: 2 + | | | | | | | | | offset: 91 + | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | line: 9 + | | | | | | | | | column: 3 + | | | | | | | | | offset: 92 | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 7 - | | | | | | endLine: 9 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 7 + | | | | | | | column: 4 + | | | | | | | offset: 78 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 7 + | | | | | | | column: 5 + | | | | | | | offset: 79 | | | | | startTokenPos: unset | | | | | endTokenPos: unset - | | | | | startLine: 7 - | | | | | endLine: 9 + | | | | | start: Nette\Neon\Position + | | | | | | line: 7 + | | | | | | column: 2 + | | | | | | offset: 76 + | | | | | end: Nette\Neon\Position + | | | | | | line: 7 + | | | | | | column: 3 + | | | | | | offset: 77 | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 7 - | | | endLine: 9 + | | | start: Nette\Neon\Position + | | | | line: 7 + | | | | column: 2 + | | | | offset: 76 + | | | end: Nette\Neon\Position + | | | | line: 7 + | | | | column: 3 + | | | | offset: 77 | | startTokenPos: unset | | endTokenPos: unset - | | startLine: 6 - | | endLine: 9 + | | start: Nette\Neon\Position + | | | line: 6 + | | | column: 1 + | | | offset: 69 + | | end: Nette\Neon\Position + | | | line: 6 + | | | column: 5 + | | | offset: 73 | 2 => Nette\Neon\Node\ArrayItemNode | | code: string | | | 'second:\n @@ -177,8 +291,14 @@ Nette\Neon\Node\BlockArrayNode | | | value: 'second' | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 10 - | | | endLine: 10 + | | | start: Nette\Neon\Position + | | | | line: 10 + | | | | column: 1 + | | | | offset: 94 + | | | end: Nette\Neon\Position + | | | | line: 10 + | | | | column: 7 + | | | | offset: 100 | | value: Nette\Neon\Node\BlockArrayNode | | | code: string | | | | 'sub:\n @@ -196,8 +316,14 @@ Nette\Neon\Node\BlockArrayNode | | | | | | value: 'sub' | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 11 - | | | | | | endLine: 11 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 11 + | | | | | | | column: 2 + | | | | | | | offset: 103 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 11 + | | | | | | | column: 5 + | | | | | | | offset: 106 | | | | | value: Nette\Neon\Node\BlockArrayNode | | | | | | code: string | | | | | | | 'a: 1\n @@ -211,19 +337,37 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | | | value: 'a' | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 12 - | | | | | | | | | endLine: 12 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 12 + | | | | | | | | | | column: 3 + | | | | | | | | | | offset: 110 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 12 + | | | | | | | | | | column: 4 + | | | | | | | | | | offset: 111 | | | | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | | | | code: '1' | | | | | | | | | value: 1 | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 12 - | | | | | | | | | endLine: 12 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 12 + | | | | | | | | | | column: 6 + | | | | | | | | | | offset: 113 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 12 + | | | | | | | | | | column: 7 + | | | | | | | | | | offset: 114 | | | | | | | | startTokenPos: unset | | | | | | | | endTokenPos: unset - | | | | | | | | startLine: 12 - | | | | | | | | endLine: 12 + | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | line: 12 + | | | | | | | | | column: 3 + | | | | | | | | | offset: 110 + | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | line: 12 + | | | | | | | | | column: 4 + | | | | | | | | | offset: 111 | | | | | | | 1 => Nette\Neon\Node\ArrayItemNode | | | | | | | | code: 'b: 2' | | | | | | | | key: Nette\Neon\Node\LiteralNode @@ -231,35 +375,77 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | | | value: 'b' | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 13 - | | | | | | | | | endLine: 13 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 13 + | | | | | | | | | | column: 3 + | | | | | | | | | | offset: 117 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 13 + | | | | | | | | | | column: 4 + | | | | | | | | | | offset: 118 | | | | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | | | | code: '2' | | | | | | | | | value: 2 | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 13 - | | | | | | | | | endLine: 13 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 13 + | | | | | | | | | | column: 6 + | | | | | | | | | | offset: 120 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 13 + | | | | | | | | | | column: 7 + | | | | | | | | | | offset: 121 | | | | | | | | startTokenPos: unset | | | | | | | | endTokenPos: unset - | | | | | | | | startLine: 13 - | | | | | | | | endLine: 13 + | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | line: 13 + | | | | | | | | | column: 3 + | | | | | | | | | offset: 117 + | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | line: 13 + | | | | | | | | | column: 4 + | | | | | | | | | offset: 118 | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 12 - | | | | | | endLine: 13 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 12 + | | | | | | | column: 3 + | | | | | | | offset: 110 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 12 + | | | | | | | column: 4 + | | | | | | | offset: 111 | | | | | startTokenPos: unset | | | | | endTokenPos: unset - | | | | | startLine: 11 - | | | | | endLine: 13 + | | | | | start: Nette\Neon\Position + | | | | | | line: 11 + | | | | | | column: 2 + | | | | | | offset: 103 + | | | | | end: Nette\Neon\Position + | | | | | | line: 11 + | | | | | | column: 5 + | | | | | | offset: 106 | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 11 - | | | endLine: 13 + | | | start: Nette\Neon\Position + | | | | line: 11 + | | | | column: 2 + | | | | offset: 103 + | | | end: Nette\Neon\Position + | | | | line: 11 + | | | | column: 5 + | | | | offset: 106 | | startTokenPos: unset | | endTokenPos: unset - | | startLine: 10 - | | endLine: 13 + | | start: Nette\Neon\Position + | | | line: 10 + | | | column: 1 + | | | offset: 94 + | | end: Nette\Neon\Position + | | | line: 10 + | | | column: 7 + | | | offset: 100 | 3 => Nette\Neon\Node\ArrayItemNode | | code: string | | | 'third:\n @@ -270,8 +456,14 @@ Nette\Neon\Node\BlockArrayNode | | | value: 'third' | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 14 - | | | endLine: 14 + | | | start: Nette\Neon\Position + | | | | line: 14 + | | | | column: 1 + | | | | offset: 122 + | | | end: Nette\Neon\Position + | | | | line: 14 + | | | | column: 6 + | | | | offset: 127 | | value: Nette\Neon\Node\BlockArrayNode | | | code: string | | | | '- entity(a: 1)\n @@ -288,8 +480,14 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | value: 'entity' | | | | | | | startTokenPos: unset | | | | | | | endTokenPos: unset - | | | | | | | startLine: 15 - | | | | | | | endLine: 15 + | | | | | | | start: Nette\Neon\Position + | | | | | | | | line: 15 + | | | | | | | | column: 4 + | | | | | | | | offset: 132 + | | | | | | | end: Nette\Neon\Position + | | | | | | | | line: 15 + | | | | | | | | column: 10 + | | | | | | | | offset: 138 | | | | | | attributes: array (1) | | | | | | | 0 => Nette\Neon\Node\ArrayItemNode | | | | | | | | code: 'a: 1' @@ -298,27 +496,57 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | | | value: 'a' | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 15 - | | | | | | | | | endLine: 15 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 15 + | | | | | | | | | | column: 11 + | | | | | | | | | | offset: 139 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 15 + | | | | | | | | | | column: 12 + | | | | | | | | | | offset: 140 | | | | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | | | | code: '1' | | | | | | | | | value: 1 | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 15 - | | | | | | | | | endLine: 15 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 15 + | | | | | | | | | | column: 14 + | | | | | | | | | | offset: 142 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 15 + | | | | | | | | | | column: 15 + | | | | | | | | | | offset: 143 | | | | | | | | startTokenPos: unset | | | | | | | | endTokenPos: unset - | | | | | | | | startLine: 15 - | | | | | | | | endLine: 15 + | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | line: 15 + | | | | | | | | | column: 11 + | | | | | | | | | offset: 139 + | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | line: 15 + | | | | | | | | | column: 12 + | | | | | | | | | offset: 140 | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 15 - | | | | | | endLine: 15 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 15 + | | | | | | | column: 4 + | | | | | | | offset: 132 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 15 + | | | | | | | column: 10 + | | | | | | | offset: 138 | | | | | startTokenPos: unset | | | | | endTokenPos: unset - | | | | | startLine: 15 - | | | | | endLine: 15 + | | | | | start: Nette\Neon\Position + | | | | | | line: 15 + | | | | | | column: 2 + | | | | | | offset: 130 + | | | | | end: Nette\Neon\Position + | | | | | | line: 15 + | | | | | | column: 3 + | | | | | | offset: 131 | | | | 1 => Nette\Neon\Node\ArrayItemNode | | | | | code: '- entity(a: 1)foo()bar' | | | | | key: null @@ -332,8 +560,14 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | | | value: 'entity' | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 16 - | | | | | | | | | endLine: 16 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 16 + | | | | | | | | | | column: 4 + | | | | | | | | | | offset: 148 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 16 + | | | | | | | | | | column: 10 + | | | | | | | | | | offset: 154 | | | | | | | | attributes: array (1) | | | | | | | | | 0 => Nette\Neon\Node\ArrayItemNode | | | | | | | | | | code: 'a: 1' @@ -342,23 +576,47 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | | | | | value: 'a' | | | | | | | | | | | startTokenPos: unset | | | | | | | | | | | endTokenPos: unset - | | | | | | | | | | | startLine: 16 - | | | | | | | | | | | endLine: 16 + | | | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | | | line: 16 + | | | | | | | | | | | | column: 11 + | | | | | | | | | | | | offset: 155 + | | | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | | | line: 16 + | | | | | | | | | | | | column: 12 + | | | | | | | | | | | | offset: 156 | | | | | | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | | | | | | code: '1' | | | | | | | | | | | value: 1 | | | | | | | | | | | startTokenPos: unset | | | | | | | | | | | endTokenPos: unset - | | | | | | | | | | | startLine: 16 - | | | | | | | | | | | endLine: 16 + | | | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | | | line: 16 + | | | | | | | | | | | | column: 14 + | | | | | | | | | | | | offset: 158 + | | | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | | | line: 16 + | | | | | | | | | | | | column: 15 + | | | | | | | | | | | | offset: 159 | | | | | | | | | | startTokenPos: unset | | | | | | | | | | endTokenPos: unset - | | | | | | | | | | startLine: 16 - | | | | | | | | | | endLine: 16 + | | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | | line: 16 + | | | | | | | | | | | column: 11 + | | | | | | | | | | | offset: 155 + | | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | | line: 16 + | | | | | | | | | | | column: 12 + | | | | | | | | | | | offset: 156 | | | | | | | | startTokenPos: unset | | | | | | | | endTokenPos: unset - | | | | | | | | startLine: 16 - | | | | | | | | endLine: 16 + | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | line: 16 + | | | | | | | | | column: 4 + | | | | | | | | | offset: 148 + | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | line: 16 + | | | | | | | | | column: 10 + | | | | | | | | | offset: 154 | | | | | | | 1 => Nette\Neon\Node\EntityNode | | | | | | | | code: 'foo()' | | | | | | | | value: Nette\Neon\Node\LiteralNode @@ -366,13 +624,25 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | | | value: 'foo' | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 16 - | | | | | | | | | endLine: 16 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 16 + | | | | | | | | | | column: 16 + | | | | | | | | | | offset: 160 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 16 + | | | | | | | | | | column: 19 + | | | | | | | | | | offset: 163 | | | | | | | | attributes: array (0) | | | | | | | | startTokenPos: unset | | | | | | | | endTokenPos: unset - | | | | | | | | startLine: 16 - | | | | | | | | endLine: 16 + | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | line: 16 + | | | | | | | | | column: 16 + | | | | | | | | | offset: 160 + | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | line: 16 + | | | | | | | | | column: 19 + | | | | | | | | | offset: 163 | | | | | | | 2 => Nette\Neon\Node\EntityNode | | | | | | | | code: 'bar' | | | | | | | | value: Nette\Neon\Node\LiteralNode @@ -380,29 +650,65 @@ Nette\Neon\Node\BlockArrayNode | | | | | | | | | value: 'bar' | | | | | | | | | startTokenPos: unset | | | | | | | | | endTokenPos: unset - | | | | | | | | | startLine: 16 - | | | | | | | | | endLine: 16 + | | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | | line: 16 + | | | | | | | | | | column: 21 + | | | | | | | | | | offset: 165 + | | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | | line: 16 + | | | | | | | | | | column: 24 + | | | | | | | | | | offset: 168 | | | | | | | | attributes: array (0) | | | | | | | | startTokenPos: unset | | | | | | | | endTokenPos: unset - | | | | | | | | startLine: 16 - | | | | | | | | endLine: 16 + | | | | | | | | start: Nette\Neon\Position + | | | | | | | | | line: 16 + | | | | | | | | | column: 21 + | | | | | | | | | offset: 165 + | | | | | | | | end: Nette\Neon\Position + | | | | | | | | | line: 16 + | | | | | | | | | column: 24 + | | | | | | | | | offset: 168 | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 16 - | | | | | | endLine: 16 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 16 + | | | | | | | column: 4 + | | | | | | | offset: 148 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 16 + | | | | | | | column: 10 + | | | | | | | offset: 154 | | | | | startTokenPos: unset | | | | | endTokenPos: unset - | | | | | startLine: 16 - | | | | | endLine: 16 + | | | | | start: Nette\Neon\Position + | | | | | | line: 16 + | | | | | | column: 2 + | | | | | | offset: 146 + | | | | | end: Nette\Neon\Position + | | | | | | line: 16 + | | | | | | column: 3 + | | | | | | offset: 147 | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 15 - | | | endLine: 16 + | | | start: Nette\Neon\Position + | | | | line: 15 + | | | | column: 2 + | | | | offset: 130 + | | | end: Nette\Neon\Position + | | | | line: 15 + | | | | column: 3 + | | | | offset: 131 | | startTokenPos: unset | | endTokenPos: unset - | | startLine: 14 - | | endLine: 16 + | | start: Nette\Neon\Position + | | | line: 14 + | | | column: 1 + | | | offset: 122 + | | end: Nette\Neon\Position + | | | line: 14 + | | | column: 6 + | | | offset: 127 | 4 => Nette\Neon\Node\ArrayItemNode | | code: string | | | '- a: 1\n @@ -421,19 +727,37 @@ Nette\Neon\Node\BlockArrayNode | | | | | | value: 'a' | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 17 - | | | | | | endLine: 17 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 17 + | | | | | | | column: 3 + | | | | | | | offset: 171 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 17 + | | | | | | | column: 4 + | | | | | | | offset: 172 | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | code: '1' | | | | | | value: 1 | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 17 - | | | | | | endLine: 17 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 17 + | | | | | | | column: 6 + | | | | | | | offset: 174 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 17 + | | | | | | | column: 7 + | | | | | | | offset: 175 | | | | | startTokenPos: unset | | | | | endTokenPos: unset - | | | | | startLine: 17 - | | | | | endLine: 17 + | | | | | start: Nette\Neon\Position + | | | | | | line: 17 + | | | | | | column: 3 + | | | | | | offset: 171 + | | | | | end: Nette\Neon\Position + | | | | | | line: 17 + | | | | | | column: 4 + | | | | | | offset: 172 | | | | 1 => Nette\Neon\Node\ArrayItemNode | | | | | code: 'b: 2' | | | | | key: Nette\Neon\Node\LiteralNode @@ -441,27 +765,57 @@ Nette\Neon\Node\BlockArrayNode | | | | | | value: 'b' | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 18 - | | | | | | endLine: 18 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 18 + | | | | | | | column: 3 + | | | | | | | offset: 178 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 18 + | | | | | | | column: 4 + | | | | | | | offset: 179 | | | | | value: Nette\Neon\Node\LiteralNode | | | | | | code: '2' | | | | | | value: 2 | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 18 - | | | | | | endLine: 18 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 18 + | | | | | | | column: 6 + | | | | | | | offset: 181 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 18 + | | | | | | | column: 7 + | | | | | | | offset: 182 | | | | | startTokenPos: unset | | | | | endTokenPos: unset - | | | | | startLine: 18 - | | | | | endLine: 18 + | | | | | start: Nette\Neon\Position + | | | | | | line: 18 + | | | | | | column: 3 + | | | | | | offset: 178 + | | | | | end: Nette\Neon\Position + | | | | | | line: 18 + | | | | | | column: 4 + | | | | | | offset: 179 | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 17 - | | | endLine: 18 + | | | start: Nette\Neon\Position + | | | | line: 17 + | | | | column: 3 + | | | | offset: 171 + | | | end: Nette\Neon\Position + | | | | line: 17 + | | | | column: 4 + | | | | offset: 172 | | startTokenPos: unset | | endTokenPos: unset - | | startLine: 17 - | | endLine: 18 + | | start: Nette\Neon\Position + | | | line: 17 + | | | column: 1 + | | | offset: 169 + | | end: Nette\Neon\Position + | | | line: 17 + | | | column: 2 + | | | offset: 170 | 5 => Nette\Neon\Node\ArrayItemNode | | code: '- - c' | | key: null @@ -477,20 +831,44 @@ Nette\Neon\Node\BlockArrayNode | | | | | | value: 'c' | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 19 - | | | | | | endLine: 19 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 19 + | | | | | | | column: 5 + | | | | | | | offset: 187 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 19 + | | | | | | | column: 6 + | | | | | | | offset: 188 | | | | | startTokenPos: unset | | | | | endTokenPos: unset - | | | | | startLine: 19 - | | | | | endLine: 19 + | | | | | start: Nette\Neon\Position + | | | | | | line: 19 + | | | | | | column: 3 + | | | | | | offset: 185 + | | | | | end: Nette\Neon\Position + | | | | | | line: 19 + | | | | | | column: 4 + | | | | | | offset: 186 | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 19 - | | | endLine: 19 + | | | start: Nette\Neon\Position + | | | | line: 19 + | | | | column: 3 + | | | | offset: 185 + | | | end: Nette\Neon\Position + | | | | line: 19 + | | | | column: 4 + | | | | offset: 186 | | startTokenPos: unset | | endTokenPos: unset - | | startLine: 19 - | | endLine: 19 + | | start: Nette\Neon\Position + | | | line: 19 + | | | column: 1 + | | | offset: 183 + | | end: Nette\Neon\Position + | | | line: 19 + | | | column: 2 + | | | offset: 184 | 6 => Nette\Neon\Node\ArrayItemNode | | code: string | | | 'dash subblock:\n @@ -501,8 +879,14 @@ Nette\Neon\Node\BlockArrayNode | | | value: 'dash subblock' | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 20 - | | | endLine: 20 + | | | start: Nette\Neon\Position + | | | | line: 20 + | | | | column: 1 + | | | | offset: 189 + | | | end: Nette\Neon\Position + | | | | line: 20 + | | | | column: 14 + | | | | offset: 202 | | value: Nette\Neon\Node\BlockArrayNode | | | code: string | | | | '- a\n @@ -517,12 +901,24 @@ Nette\Neon\Node\BlockArrayNode | | | | | | value: 'a' | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 21 - | | | | | | endLine: 21 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 21 + | | | | | | | column: 3 + | | | | | | | offset: 206 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 21 + | | | | | | | column: 4 + | | | | | | | offset: 207 | | | | | startTokenPos: unset | | | | | endTokenPos: unset - | | | | | startLine: 21 - | | | | | endLine: 21 + | | | | | start: Nette\Neon\Position + | | | | | | line: 21 + | | | | | | column: 1 + | | | | | | offset: 204 + | | | | | end: Nette\Neon\Position + | | | | | | line: 21 + | | | | | | column: 2 + | | | | | | offset: 205 | | | | 1 => Nette\Neon\Node\ArrayItemNode | | | | | code: '- b' | | | | | key: null @@ -531,20 +927,44 @@ Nette\Neon\Node\BlockArrayNode | | | | | | value: 'b' | | | | | | startTokenPos: unset | | | | | | endTokenPos: unset - | | | | | | startLine: 22 - | | | | | | endLine: 22 + | | | | | | start: Nette\Neon\Position + | | | | | | | line: 22 + | | | | | | | column: 3 + | | | | | | | offset: 210 + | | | | | | end: Nette\Neon\Position + | | | | | | | line: 22 + | | | | | | | column: 4 + | | | | | | | offset: 211 | | | | | startTokenPos: unset | | | | | endTokenPos: unset - | | | | | startLine: 22 - | | | | | endLine: 22 + | | | | | start: Nette\Neon\Position + | | | | | | line: 22 + | | | | | | column: 1 + | | | | | | offset: 208 + | | | | | end: Nette\Neon\Position + | | | | | | line: 22 + | | | | | | column: 2 + | | | | | | offset: 209 | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 21 - | | | endLine: 22 + | | | start: Nette\Neon\Position + | | | | line: 21 + | | | | column: 1 + | | | | offset: 204 + | | | end: Nette\Neon\Position + | | | | line: 21 + | | | | column: 2 + | | | | offset: 205 | | startTokenPos: unset | | endTokenPos: unset - | | startLine: 20 - | | endLine: 22 + | | start: Nette\Neon\Position + | | | line: 20 + | | | column: 1 + | | | offset: 189 + | | end: Nette\Neon\Position + | | | line: 20 + | | | column: 14 + | | | offset: 202 | 7 => Nette\Neon\Node\ArrayItemNode | | code: string | | | 'text: """\n @@ -556,8 +976,14 @@ Nette\Neon\Node\BlockArrayNode | | | value: 'text' | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 23 - | | | endLine: 23 + | | | start: Nette\Neon\Position + | | | | line: 23 + | | | | column: 1 + | | | | offset: 212 + | | | end: Nette\Neon\Position + | | | | line: 23 + | | | | column: 5 + | | | | offset: 216 | | value: Nette\Neon\Node\StringNode | | | code: string | | | | '"""\n @@ -569,13 +995,31 @@ Nette\Neon\Node\BlockArrayNode | | | | two' | | | startTokenPos: unset | | | endTokenPos: unset - | | | startLine: 23 - | | | endLine: 26 + | | | start: Nette\Neon\Position + | | | | line: 23 + | | | | column: 7 + | | | | offset: 218 + | | | end: Nette\Neon\Position + | | | | line: 26 + | | | | column: 4 + | | | | offset: 243 | | startTokenPos: unset | | endTokenPos: unset - | | startLine: 23 - | | endLine: 26 + | | start: Nette\Neon\Position + | | | line: 23 + | | | column: 1 + | | | offset: 212 + | | end: Nette\Neon\Position + | | | line: 23 + | | | column: 5 + | | | offset: 216 startTokenPos: unset endTokenPos: unset - startLine: 3 - endLine: 26 + start: Nette\Neon\Position + | line: 3 + | column: 1 + | offset: 9 + end: Nette\Neon\Position + | line: 3 + | column: 6 + | offset: 14 From cced370a418fd92a31a4d5dd3ed1e430b7b060b9 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 23 Nov 2024 17:44:10 +0100 Subject: [PATCH 14/17] used attribute Deprecated --- src/Neon/Encoder.php | 2 +- src/Neon/Neon.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Neon/Encoder.php b/src/Neon/Encoder.php index 4ee6732..292f880 100644 --- a/src/Neon/Encoder.php +++ b/src/Neon/Encoder.php @@ -16,7 +16,7 @@ */ final class Encoder { - /** @deprecated */ + #[\Deprecated] public const BLOCK = true; public bool $blockMode = false; diff --git a/src/Neon/Neon.php b/src/Neon/Neon.php index 750186e..cb327da 100644 --- a/src/Neon/Neon.php +++ b/src/Neon/Neon.php @@ -18,10 +18,10 @@ final class Neon { public const Chain = '!!chain'; - /** @deprecated use Neon::Chain */ + #[\Deprecated('use Neon::Chain')] public const CHAIN = self::Chain; - /** @deprecated use parameter $blockMode */ + #[\Deprecated('use parameter $blockMode')] public const BLOCK = Encoder::BLOCK; From 76a42e955963d0834d49a92f4448df82685d873a Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 3 Feb 2025 03:31:47 +0100 Subject: [PATCH 15/17] github actions updated --- .github/workflows/coding-style.yml | 4 ++-- .github/workflows/tests.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/coding-style.yml b/.github/workflows/coding-style.yml index 97e6c16..9e412ba 100644 --- a/.github/workflows/coding-style.yml +++ b/.github/workflows/coding-style.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: 8.3 coverage: none - run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: 8.3 coverage: none - run: composer create-project nette/coding-standard temp/coding-standard ^3 --no-progress diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 296194d..21444ad 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,9 +23,9 @@ jobs: - run: composer install --no-progress --prefer-dist - run: vendor/bin/tester tests -s -C - if: failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: output + name: output-${{ matrix.php }} path: tests/**/output From 03a40354e0d85296b54bd8e4f3475936b49a5fb4 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Tue, 15 Apr 2025 12:29:13 +0200 Subject: [PATCH 16/17] Mixed indents: add failing test --- tests/Neon/Encoder.phpt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Neon/Encoder.phpt b/tests/Neon/Encoder.phpt index 490fccc..969ac22 100644 --- a/tests/Neon/Encoder.phpt +++ b/tests/Neon/Encoder.phpt @@ -180,3 +180,8 @@ Assert::same( "\"\"\"\n\tspecial\\r\n\tchars\n\"\"\"", Neon::encode("special\r\nchars", true), ); + +Assert::same( + "inner:\n msg: '''\n string\n with newline\n '''\n\n", + Neon::encode(['inner' => ['msg' => "string\nwith newline"]], true, ' '), +); From 5ce9ea5134f246302717244508522d5c6cd55def Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Tue, 15 Apr 2025 13:19:31 +0200 Subject: [PATCH 17/17] Fix mixed indents in block strings --- src/Neon/Encoder.php | 2 +- src/Neon/Node.php | 2 +- src/Neon/Node/ArrayItemNode.php | 14 +++++++------- src/Neon/Node/BlockArrayNode.php | 4 ++-- src/Neon/Node/EntityChainNode.php | 4 ++-- src/Neon/Node/EntityNode.php | 6 +++--- src/Neon/Node/InlineArrayNode.php | 4 ++-- src/Neon/Node/LiteralNode.php | 2 +- src/Neon/Node/StringNode.php | 6 ++++-- tests/Neon/Parser.nodes.phpt | 2 +- 10 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/Neon/Encoder.php b/src/Neon/Encoder.php index 292f880..f436102 100644 --- a/src/Neon/Encoder.php +++ b/src/Neon/Encoder.php @@ -29,7 +29,7 @@ final class Encoder public function encode(mixed $val): string { $node = $this->valueToNode($val, $this->blockMode); - return $node->toString(); + return $node->toString($this->indentation); } diff --git a/src/Neon/Node.php b/src/Neon/Node.php index 259963a..dac38a5 100644 --- a/src/Neon/Node.php +++ b/src/Neon/Node.php @@ -24,7 +24,7 @@ abstract class Node implements \IteratorAggregate abstract public function toValue(): mixed; - abstract public function toString(): string; + abstract public function toString(string $indentation): string; public function &getIterator(): \Generator diff --git a/src/Neon/Node/ArrayItemNode.php b/src/Neon/Node/ArrayItemNode.php index 6acc66b..c9a3c71 100644 --- a/src/Neon/Node/ArrayItemNode.php +++ b/src/Neon/Node/ArrayItemNode.php @@ -39,13 +39,13 @@ public static function itemsToArray(array $items): array /** @param self[] $items */ - public static function itemsToInlineString(array $items): string + public static function itemsToInlineString(array $items, string $indentation): string { $res = ''; foreach ($items as $item) { $res .= ($res === '' ? '' : ', ') - . ($item->key ? $item->key->toString() . ': ' : '') - . $item->value->toString(); + . ($item->key ? $item->key->toString($indentation) . ': ' : '') + . $item->value->toString($indentation); } return $res; @@ -53,12 +53,12 @@ public static function itemsToInlineString(array $items): string /** @param self[] $items */ - public static function itemsToBlockString(array $items): string + public static function itemsToBlockString(array $items, string $indentation): string { $res = ''; foreach ($items as $item) { - $v = $item->value->toString(); - $res .= ($item->key ? $item->key->toString() . ':' : '-') + $v = $item->value->toString($indentation); + $res .= ($item->key ? $item->key->toString($indentation) . ':' : '-') . ($item->value instanceof BlockArrayNode && $item->value->items ? "\n" . $v . (substr($v, -2, 1) === "\n" ? '' : "\n") : ' ' . $v . "\n"); @@ -74,7 +74,7 @@ public function toValue(): mixed } - public function toString(): string + public function toString(string $indentation): string { throw new \LogicException; } diff --git a/src/Neon/Node/BlockArrayNode.php b/src/Neon/Node/BlockArrayNode.php index 4a96e80..3cae80d 100644 --- a/src/Neon/Node/BlockArrayNode.php +++ b/src/Neon/Node/BlockArrayNode.php @@ -19,13 +19,13 @@ public function __construct( } - public function toString(): string + public function toString(string $indentation): string { if (count($this->items) === 0) { return '[]'; } - $res = ArrayItemNode::itemsToBlockString($this->items); + $res = ArrayItemNode::itemsToBlockString($this->items, $indentation); return preg_replace('#^(?=.)#m', $this->indentation, $res); } } diff --git a/src/Neon/Node/EntityChainNode.php b/src/Neon/Node/EntityChainNode.php index ee0d850..09c3e59 100644 --- a/src/Neon/Node/EntityChainNode.php +++ b/src/Neon/Node/EntityChainNode.php @@ -34,9 +34,9 @@ public function toValue(): Neon\Entity } - public function toString(): string + public function toString(string $indentation): string { - return implode('', array_map(fn($entity) => $entity->toString(), $this->chain)); + return implode('', array_map(fn($entity) => $entity->toString($indentation), $this->chain)); } diff --git a/src/Neon/Node/EntityNode.php b/src/Neon/Node/EntityNode.php index 30b93db..12bfd9b 100644 --- a/src/Neon/Node/EntityNode.php +++ b/src/Neon/Node/EntityNode.php @@ -33,11 +33,11 @@ public function toValue(): Entity } - public function toString(): string + public function toString(string $indentation): string { - return $this->value->toString() + return $this->value->toString($indentation) . '(' - . ($this->attributes ? ArrayItemNode::itemsToInlineString($this->attributes) : '') + . ($this->attributes ? ArrayItemNode::itemsToInlineString($this->attributes, $indentation) : '') . ')'; } diff --git a/src/Neon/Node/InlineArrayNode.php b/src/Neon/Node/InlineArrayNode.php index de586e5..7add566 100644 --- a/src/Neon/Node/InlineArrayNode.php +++ b/src/Neon/Node/InlineArrayNode.php @@ -19,10 +19,10 @@ public function __construct( } - public function toString(): string + public function toString(string $indentation): string { return $this->bracket - . ArrayItemNode::itemsToInlineString($this->items) + . ArrayItemNode::itemsToInlineString($this->items, $indentation) . ['[' => ']', '{' => '}', '(' => ')'][$this->bracket]; } } diff --git a/src/Neon/Node/LiteralNode.php b/src/Neon/Node/LiteralNode.php index 23b4b32..695bdef 100644 --- a/src/Neon/Node/LiteralNode.php +++ b/src/Neon/Node/LiteralNode.php @@ -90,7 +90,7 @@ public static function baseConvert(string $number, int $base): string|int } - public function toString(): string + public function toString(string $indentation): string { if ($this->value instanceof \DateTimeInterface) { return $this->value->format('Y-m-d H:i:s O'); diff --git a/src/Neon/Node/StringNode.php b/src/Neon/Node/StringNode.php index eaa9d7e..7389dda 100644 --- a/src/Neon/Node/StringNode.php +++ b/src/Neon/Node/StringNode.php @@ -64,10 +64,12 @@ function (array $m) use ($position): string { }, $res, ); + + } - public function toString(): string + public function toString(string $indentation): string { if (!str_contains($this->value, "\n")) { return preg_match('~[\x00-\x08\x0B-\x1F]~', $this->value) @@ -89,7 +91,7 @@ public function toString(): string $delim = "'''"; } - $s = preg_replace('#^(?=.)#m', "\t", $s); + $s = preg_replace('#^(?=.)#m', $indentation, $s); return $delim . "\n" . $s . "\n" . $delim; } } diff --git a/tests/Neon/Parser.nodes.phpt b/tests/Neon/Parser.nodes.phpt index 8c1e3ff..6cfaa9d 100644 --- a/tests/Neon/Parser.nodes.phpt +++ b/tests/Neon/Parser.nodes.phpt @@ -51,7 +51,7 @@ $node = $parser->parse($stream); Assert::matchFile( __DIR__ . '/fixtures/Parser.nodes.neon', - $node->toString(), + $node->toString("\t"), ); $traverser = new Traverser;