@@ -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+ 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