Skip to content

Commit

Permalink
merged branch ezzatron/yaml-folded-scalar (PR #7455)
Browse files Browse the repository at this point in the history
This PR was merged into the 2.1 branch.

Discussion
----------

[Yaml] fixed bugs with folded scalar parsing (2.1 branch)

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | n/a

This is a re-do of #7445, which was submitted against 2.0, which is no longer supported. Additionally, the previous PR broke some tests because literal tabs were accidentally changed to spaces, and GitHub's diff engine seemingly ignored the change, although the differences showed up when using git diff directly. This PR leaves the literals untouched, and hence the tests should now pass.

**From the original PR:**

I found some more bugs with the parsing of folded scalars. I'd also made some mistakes in the tests introduced by me in #6785. This PR fixes the incorrect tests, and introduces more related tests.

I had to completely rewrite the `Parser::parseFoldedScalar()` method. I think it came out simpler in the end - less 'special cases'. I've done some basic profiling by running the parser tests on repeat and haven't noticed any difference in parsing performance.

Commits
-------

bbcdfe2 [Yaml] fixed bugs with folded scalar parsing
  • Loading branch information
fabpot committed Mar 23, 2013
2 parents aac8f8f + bbcdfe2 commit 9969121
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 54 deletions.
85 changes: 41 additions & 44 deletions src/Symfony/Component/Yaml/Parser.php
Expand Up @@ -414,64 +414,61 @@ private function parseValue($value, $exceptionOnInvalidType, $objectSupport)
*/
private function parseFoldedScalar($separator, $indicator = '', $indentation = 0)
{
$separator = '|' == $separator ? "\n" : ' ';
$text = '';

$notEOF = $this->moveToNextLine();

while ($notEOF && $this->isCurrentLineBlank()) {
$text .= "\n";

$notEOF = $this->moveToNextLine();
}

if (!$notEOF) {
return '';
}

if (!preg_match('#^(?P<indent>'.($indentation ? str_repeat(' ', $indentation) : ' +').')(?P<text>.*)$#u', $this->currentLine, $matches)) {
$this->moveToPreviousLine();

return '';
// determine indentation if not specified
if (0 === $indentation) {
if (preg_match('/^ +/', $this->currentLine, $matches)) {
$indentation = strlen($matches[0]);
}
}

$textIndent = $matches['indent'];
$previousIndent = 0;

$text .= $matches['text'].$separator;
while ($this->currentLineNb + 1 < count($this->lines)) {
$this->moveToNextLine();

if (preg_match('#^(?P<indent> {'.strlen($textIndent).',})(?P<text>.+)$#u', $this->currentLine, $matches)) {
if (' ' == $separator && $previousIndent != $matches['indent']) {
$text = substr($text, 0, -1)."\n";
$text = '';
if ($indentation > 0) {
$pattern = sprintf('/^ {%d}(.*)$/', $indentation);

$isCurrentLineBlank = $this->isCurrentLineBlank();
while (
$notEOF && (
$isCurrentLineBlank ||
preg_match($pattern, $this->currentLine, $matches)
)
) {
if ($isCurrentLineBlank) {
$text .= substr($this->currentLine, $indentation);
} else {
$text .= $matches[1];
}
$previousIndent = $matches['indent'];

$text .= str_repeat(' ', $diff = strlen($matches['indent']) - strlen($textIndent)).$matches['text'].($diff ? "\n" : $separator);
} elseif (preg_match('#^(?P<text> *)$#', $this->currentLine, $matches)) {
$text .= preg_replace('#^ {1,'.strlen($textIndent).'}#', '', $matches['text'])."\n";
} else {
$this->moveToPreviousLine();

break;
// newline only if not EOF
if ($notEOF = $this->moveToNextLine()) {
$text .= "\n";
$isCurrentLineBlank = $this->isCurrentLineBlank();
}
}
} elseif ($notEOF) {
$text .= "\n";
}

if (' ' == $separator) {
// replace last separator by a newline
$text = preg_replace('/ (\n*)$/', "\n$1", $text);
if ($notEOF) {
$this->moveToPreviousLine();
}

switch ($indicator) {
case '':
$text = preg_replace('#\n+$#s', "\n", $text);
break;
case '+':
break;
case '-':
$text = preg_replace('#\n+$#s', '', $text);
break;
// replace all non-trailing single newlines with spaces in folded blocks
if ('>' === $separator) {
preg_match('/(\n*)$/', $text, $matches);
$text = preg_replace('/(?<!\n)\n(?!\n)/', ' ', rtrim($text, "\n"));
$text .= $matches[1];
}

// deal with trailing newlines as indicated
if ('' === $indicator) {
$text = preg_replace('/\n+$/s', "\n", $text);
} elseif ('-' === $indicator) {
$text = preg_replace('/\n+$/s', '', $text);
}

return $text;
Expand Down
116 changes: 106 additions & 10 deletions src/Symfony/Component/Yaml/Tests/ParserTest.php
Expand Up @@ -113,17 +113,33 @@ public function getBlockChompingTests()
foo: |-
one
two
bar: |-
one
two
EOF;
$expected = array(
'foo' => "one\ntwo",
'bar' => "one\ntwo",
);
$tests['Literal block chomping strip with single trailing newline'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: |-
one
two
bar: |-
one
two
EOF;
$expected = array(
'foo' => "one\ntwo",
'bar' => "one\ntwo",
);
$tests['Literal block chomping strip with trailing newline'] = array($expected, $yaml);
$tests['Literal block chomping strip with multiple trailing newlines'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: |-
Expand All @@ -143,7 +159,6 @@ public function getBlockChompingTests()
foo: |
one
two
bar: |
one
two
Expand All @@ -153,37 +168,70 @@ public function getBlockChompingTests()
'foo' => "one\ntwo\n",
'bar' => "one\ntwo\n",
);
$tests['Literal block chomping clip with trailing newline'] = array($expected, $yaml);
$tests['Literal block chomping clip with single trailing newline'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: |
one
two
bar: |
one
two
EOF;
$expected = array(
'foo' => "one\ntwo\n",
'bar' => "one\ntwo\n",
);
$tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: |
one
two
bar: |
one
two
EOF;
$expected = array(
'foo' => "one\ntwo\n",
'bar' => "one\ntwo",
);
$tests['Literal block chomping clip without trailing newline'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: |+
one
two
bar: |+
one
two
EOF;
$expected = array(
'foo' => "one\ntwo\n",
'bar' => "one\ntwo\n",
);
$tests['Literal block chomping keep with single trailing newline'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: |+
one
two
bar: |+
one
two
EOF;
$expected = array(
'foo' => "one\ntwo\n\n",
'bar' => "one\ntwo\n\n",
);
$tests['Literal block chomping keep with trailing newline'] = array($expected, $yaml);
$tests['Literal block chomping keep with multiple trailing newlines'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: |+
Expand All @@ -195,25 +243,41 @@ public function getBlockChompingTests()
EOF;
$expected = array(
'foo' => "one\ntwo\n",
'bar' => "one\ntwo\n",
'bar' => "one\ntwo",
);
$tests['Literal block chomping keep without trailing newline'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: >-
one
two
bar: >-
one
two
EOF;
$expected = array(
'foo' => "one two",
'bar' => "one two",
);
$tests['Folded block chomping strip with single trailing newline'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: >-
one
two
bar: >-
one
two
EOF;
$expected = array(
'foo' => "one two",
'bar' => "one two",
);
$tests['Folded block chomping strip with trailing newline'] = array($expected, $yaml);
$tests['Folded block chomping strip with multiple trailing newlines'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: >-
Expand All @@ -233,7 +297,6 @@ public function getBlockChompingTests()
foo: >
one
two
bar: >
one
two
Expand All @@ -243,37 +306,70 @@ public function getBlockChompingTests()
'foo' => "one two\n",
'bar' => "one two\n",
);
$tests['Folded block chomping clip with trailing newline'] = array($expected, $yaml);
$tests['Folded block chomping clip with single trailing newline'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: >
one
two
bar: >
one
two
EOF;
$expected = array(
'foo' => "one two\n",
'bar' => "one two\n",
);
$tests['Folded block chomping clip with multiple trailing newlines'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: >
one
two
bar: >
one
two
EOF;
$expected = array(
'foo' => "one two\n",
'bar' => "one two",
);
$tests['Folded block chomping clip without trailing newline'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: >+
one
two
bar: >+
one
two
EOF;
$expected = array(
'foo' => "one two\n",
'bar' => "one two\n",
);
$tests['Folded block chomping keep with single trailing newline'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: >+
one
two
bar: >+
one
two
EOF;
$expected = array(
'foo' => "one two\n\n",
'bar' => "one two\n\n",
);
$tests['Folded block chomping keep with trailing newline'] = array($expected, $yaml);
$tests['Folded block chomping keep with multiple trailing newlines'] = array($expected, $yaml);

$yaml = <<<'EOF'
foo: >+
Expand All @@ -285,7 +381,7 @@ public function getBlockChompingTests()
EOF;
$expected = array(
'foo' => "one two\n",
'bar' => "one two\n",
'bar' => "one two",
);
$tests['Folded block chomping keep without trailing newline'] = array($expected, $yaml);

Expand Down

0 comments on commit 9969121

Please sign in to comment.