8000 fix lexing nested sequences/mappings · symfony/symfony@4c15f80 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4c15f80

Browse files
committed
fix lexing nested sequences/mappings
1 parent 1f46250 commit 4c15f80

File tree

2 files changed

+303
-68
lines changed

2 files changed

+303
-68
lines changed

src/Symfony/Component/Yaml/Parser.php

Lines changed: 119 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ private function doParse(string $value, int $flags)
355355
}
356356

357357
try {
358-
return Inline::parse($this->parseQuotedString($this->currentLine), $flags, $this->refs);
358+
return Inline::parse($this->lexInlineQuotedString(), $flags, $this->refs);
359359
} catch (ParseException $e) {
360360
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
361361
$e->setSnippet($this->currentLine);
@@ -368,7 +368,7 @@ private function doParse(string $value, int $flags)
368368
}
369369

370370
try {
371-
$parsedMapping = Inline::parse($this->lexInlineMapping($this->currentLine), $flags, $this->refs);
371+
$parsedMapping = Inline::parse($this->lexInlineMapping(), $flags, $this->refs);
372372

373373
while ($this->moveToNextLine()) {
374374
if (!$this->isCurrentLineEmpty()) {
@@ -389,7 +389,7 @@ private function doParse(string $value, int $flags)
389389
}
390390

391391
try {
392-
$parsedSequence = Inline::parse($this->lexInlineSequence($this->currentLine), $flags, $this->refs);
392+
$parsedSequence = Inline::parse($this->lexInlineSequence(), $flags, $this->refs);
393393

394394
while ($this->moveToNextLine()) {
395395
if (!$this->isCurrentLineEmpty()) {
@@ -659,6 +659,11 @@ private function getNextEmbedBlock(int $indentation = null, bool $inSequence = f
659659
return implode("\n", $data);
660660
}
661661

662+
private function hasMoreLines(): bool
663+
{
664+
return (\count($this->lines) - 1) > $this->currentLineNb;
665+
}
666+
662667
/**
663668
* Moves the parser to the next line.
664669
*/
@@ -736,9 +741,13 @@ private function parseValue(string $value, int $flags, string $context)
736741

737742
try {
738743
if ('' !== $value && '{' === $value[0]) {
739-
return Inline::parse($this->lexInlineMapping($value), $flags, $this->refs);
744+
$cursor = \strlen($this->currentLine) - \strlen($value);
745+
746+
return Inline::parse($this->lexInlineMapping($cursor), $flags, $this->refs);
740747
} elseif ('' !== $value && '[' === $value[0]) {
741-
return Inline::parse($this->lexInlineSequence($value), $flags, $this->refs);
748+
$cursor = \strlen($this->currentLine) - \strlen($value);
749+
750+
return Inline::parse($this->lexInlineSequence($cursor), $flags, $this->refs);
742751
}
743752

744753
$quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null;
@@ -1137,106 +1146,148 @@ private function getLineTag(string $value, int $flags, bool $nextLineCheck = tru
11371146
throw new ParseException(sprintf('Tags support is not enabled. You must use the flag "Yaml::PARSE_CUSTOM_TAGS" to use "%s".', $matches['tag']), $this->getRealCurrentLineNb() + 1, $value, $this->filename);
11381147
}
11391148

1140-
private function parseQuotedString(string $yaml): ?string
1149+
private function lexInlineQuotedString(int &$cursor = 0): string
11411150
{
1142-
if ('' === $yaml || ('"' !== $yaml[0] && "'" !== $yaml[0])) {
1143-
throw new \InvalidArgumentException(sprintf('"%s" is not a quoted string.', $yaml));
1144-
}
1151+
$quotation = $this->currentLine[$cursor];
1152+
$value = $quotation;
1153+
++$cursor;
11451154

1146-
$lines = [$yaml];
1147-
1148-
while ($this->moveToNextLine()) {
1149-
$lines[] = $this->currentLine;
1155+
$previousLineWasNewline = true;
1156+
$previousLineWasTerminatedWithBackslash = false;
11501157

1151-
if (!$this->isCurrentLineEmpty() && $yaml[0] === $this->currentLine[-1]) {
1152-
break;
1153-
}
1154-
}
1155-
1156-
$value = '';
1157-
1158-
for ($i = 0, $linesCount = \count($lines), $previousLineWasNewline = false, $previousLineWasTerminatedWithBackslash = false; $i < $linesCount; ++$i) {
1159-
if ('' === trim($lines[$i])) {
1158+
do {
1159+
if ($this->isCurrentLineBlank()) {
11601160
$value .= "\n";
11611161
} elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
11621162
$value .= ' ';
11631163
}
11641164

1165-
if ('' !== trim($lines[$i]) && '\\' === substr($lines[$i], -1)) {
1166-
$value .= ltrim(substr($lines[$i], 0, -1));
1167-
} elseif ('' !== trim($lines[$i])) {
1168-
$value .= trim($lines[$i]);
1165+
for (; \strlen($this->currentLine) > $cursor; ++$cursor) {
1166+
switch ($this->currentLine[$cursor]) {
1167+
case '\\':
1168+
if (isset($this->currentLine[++$cursor])) {
1169+
$value .= '\\'.$this->currentLine[$cursor];
1170+
}
1171+
1172+
break;
1173+
case $quotation:
1174+
++$cursor;
1175+
1176+
if ("'" === $quotation && isset($this->currentLine[$cursor]) && "'" === $this->currentLine[$cursor]) {
1177+
$value .= "''";
1178+
break;
1179+
}
1180+
1181+
return $value.$quotation;
1182+
default:
1183+
$value .= $this->currentLine[$cursor];
1184+
}
11691185
}
11701186

1171-
if ('' === trim($lines[$i])) {
1187+
if ($this->isCurrentLineBlank()) {
11721188
$previousLineWasNewline = true;
11731189
$previousLineWasTerminatedWithBackslash = false;
1174-
} elseif ('\\' === substr($lines[$i], -1)) {
1190+
} elseif ('\\' === $this->currentLine[-1]) {
11751191
$previousLineWasNewline = false;
11761192
$previousLineWasTerminatedWithBackslash = true;
11771193
} else {
11781194
$previousLineWasNewline = false;
11791195
$previousLineWasTerminatedWithBackslash = false;
11801196
}
1181-
}
11821197

1183-
return $value;
1198+
if ($this->hasMoreLines()) {
1199+
$cursor = 0;
1200+
}
1201+
} while ($this->moveToNextLine());
1202+
1203+
throw new ParseException('Malformed inline YAML string');
11841204
}
11851205

1186-
private function lexInlineMapping(string $yaml): string
1206+
private function lexUnquotedString(int &$cursor): string
11871207
{
1188-
if ('' === $yaml || '{' !== $yaml[0]) {
1189-
throw new \InvalidArgumentException(sprintf('"%s" is not a sequence.', $yaml));
1190-
}
1191-
1192-
for ($i = 1; isset($yaml[$i]) && '}' !== $yaml[$i]; ++$i) {
1193-
}
1208+
$offset = $cursor;
1209+
$cursor += strcspn($this->currentLine, '[]{},: ', $cursor);
11941210

1195-
if (isset($yaml[$i]) && '}' === $yaml[$i]) {
1196-
return $yaml;
1197-
}
1198-
1199-
$lines = [$yaml];
1200-
1201-
while ($this->moveToNextLine()) {
1202-
$lines[] = $this->currentLine;
1203-
}
1211+
return substr($this->currentLine, $offset, $cursor - $offset);
1212+
}
12041213

1205-
return implode("\n", $lines);
1214+
private function lexInlineMapping(int &$cursor = 0): string
1215+
{
1216+
return $this->lexInlineStructure($cursor, '}');
12061217
}
12071218

1208-
private function lexInlineSequence(string $yaml): string
1219+
private function lexInlineSequence(int &$cursor = 0): string
12091220
{
1210-
if ('' === $yaml || '[' !== $yaml[0]) {
1211-
throw new \InvalidArgumentException(sprintf('"%s" is not a sequence.', $yaml));
1212-
}
1221+< 1C6A div class="diff-text-inner"> return $this->lexInlineStructure($cursor, ']');
1222+
}
12131223

1214-
for ($i = 1; isset($yaml[$i]) && ']' !== $yaml[$i]; ++$i) {
1215-
}
1224+
private function lexInlineStructure(int &$cursor, string $closingTag): string
1225+
{
1226+
$value = $this->currentLine[$cursor];
1227+
++$cursor;
12161228

1217-
if (isset($yaml[$i]) && ']' === $yaml[$i]) {
1218-
return $yaml;
1219-
}
1229+
do {
1230+
$this->consumeWhitespaces($cursor);
1231+
1232+
while (isset($this->currentLine[$cursor])) {
1233+
switch ($this->currentLine[$cursor]) {
1234+
case '"':
1235+
case "'":
1236+
$value .= $this->lexInlineQuotedString($cursor);
1237+
break;
1238+
case ':':
1239+
case ',':
1240+
$value .= $this->currentLine[$cursor];
1241+
++$cursor;
1242+
break;
1243+
case '{':
1244+
$value .= $this->lexInlineMapping($cursor);
1245+
break;
1246+
case '[':
1247+
$value .= $this->lexInlineSequence($cursor);
1248+
break;
1249+
case $closingTag:
1250+
$value .= $this->currentLine[$cursor];
1251+
++$cursor;
1252+
1253+
return $value;
1254+
case '#':
1255+
break 2;
1256+
default:
1257+
$value .= $this->lexUnquotedString($cursor);
1258+
}
12201259

1221-
$value = $yaml;
1260+
if ($this->consumeWhitespaces($cursor)) {
1261+
$value .= ' ';
1262+
}
1263+
}
12221264

1223-
while ($this->moveToNextLine()) {
1224-
for ($i = 1; isset($this->currentLine[$i]) && ']' !== $this->currentLine[$i]; ++$i) {
1265+
if ($this->hasMoreLines()) {
1266+
$cursor = 0;
12251267
}
1268+
} while ($this->moveToNextLine());
12261269

1227-
$trimmedValue = trim($this->currentLine);
1270+
throw new ParseException('Malformed inline YAML string');
1271+
}
12281272

1229-
if ('' !== $trimmedValue && '#' === $trimmedValue[0]) {
1230-
continue;
1231-
}
1273+
private function consumeWhitespaces(int &$cursor): bool
1274+
{
1275+
$whitespacesConsumed = 0;
12321276

1233-
$value .= $trimmedValue;
1277+
do {
1278+
$whitespaceOnlyTokenLength = strspn($this->currentLine, ' ', $cursor);
1279+
$whitespacesConsumed += $whitespaceOnlyTokenLength;
1280+
$cursor += $whitespaceOnlyTokenLength;
12341281

1235-
if (isset($this->currentLine[$i]) && ']' === $this->currentLine[$i]) {
1236-
break;
1282+
if (isset($this->currentLine[$cursor])) {
1283+
return 0 < $whitespacesConsumed;
12371284
}
1238-
}
12391285

1240-
return $value;
1286+
if ($this->hasMoreLines()) {
1287+
$cursor = 0;
1288+
}
1289+
} while ($this->moveToNextLine());
1290+
1291+
return 0 < $whitespacesConsumed;
12411292
}
12421293
}

0 commit comments

Comments
 (0)
0