@@ -103,11 +103,13 @@ public function __construct(Environment $env, array $options = [])
103
103
104
104
$ tokenizerHelper = new TokenizerHelper ($ env , $ this ->options );
105
105
$ this ->regexes = [
106
- 'lex_block ' => $ tokenizerHelper ->getBlockRegex (),
107
- 'lex_comment ' => $ tokenizerHelper ->getCommentRegex (),
108
- 'lex_variable ' => $ tokenizerHelper ->getVariableRegex (),
109
- 'operator ' => $ tokenizerHelper ->getOperatorRegex (),
110
- 'lex_tokens_start ' => $ tokenizerHelper ->getTokensStartRegex (),
106
+ 'lex_block ' => $ tokenizerHelper ->getBlockRegex (),
107
+ 'lex_comment ' => $ tokenizerHelper ->getCommentRegex (),
108
+ 'lex_variable ' => $ tokenizerHelper ->getVariableRegex (),
109
+ 'operator ' => $ tokenizerHelper ->getOperatorRegex (),
110
+ 'lex_tokens_start ' => $ tokenizerHelper ->getTokensStartRegex (),
111
+ 'interpolation_start ' => $ tokenizerHelper ->getInterpolationStartRegex (),
112
+ 'interpolation_end ' => $ tokenizerHelper ->getInterpolationEndRegex (),
111
113
];
112
114
}
113
115
@@ -150,6 +152,12 @@ public function tokenize(Source $source): array
150
152
$ this ->lexData ();
151
153
}
152
154
break ;
155
+ case self ::STATE_STRING :
156
+ $ this ->lexString ();
157
+ break ;
158
+ case self ::STATE_INTERPOLATION :
159
+ $ this ->lexInterpolation ();
160
+ break ;
153
161
default :
154
162
throw new Exception ('Unhandled state in tokenize ' , 1 );
155
163
}
@@ -284,6 +292,10 @@ protected function lexExpression(): void
284
292
$ this ->lexWhitespace ();
285
293
} elseif (PHP_EOL === $ currentToken ) {
286
294
$ this ->lexEOL ();
295
+ } elseif ('= ' === $ this ->code [$ this ->cursor ] && '> ' === $ this ->code [$ this ->cursor + 1 ]) {
296
+ // arrow functions
297
+ $ this ->pushToken (Token::ARROW_TYPE , '=> ' );
298
+ $ this ->moveCursor ('=> ' );
287
299
} elseif (preg_match ($ this ->regexes ['operator ' ], $ this ->code , $ match , 0 , $ this ->cursor )) {
288
300
$ this ->lexOperator ($ match [0 ]);
289
301
} elseif (preg_match (self ::REGEX_NAME , $ this ->code , $ match , 0 , $ this ->cursor )) {
@@ -304,6 +316,10 @@ protected function lexExpression(): void
304
316
// strings
305
317
$ this ->pushToken (Token::STRING_TYPE , addcslashes (stripcslashes ($ match [0 ]), '\\' ));
306
318
$ this ->moveCursor ($ match [0 ]);
319
+ } elseif (preg_match (self ::REGEX_DQ_STRING_DELIM , $ this ->code , $ match , 0 , $ this ->cursor )) {
320
+ $ this ->bracketsAndTernary [] = ['" ' , $ this ->line ];
321
+ $ this ->pushState (self ::STATE_STRING );
322
+ $ this ->moveCursor ($ match [0 ]);
307
323
} else {
308
324
// unlexable
309
325
throw new Exception (sprintf ('Unexpected character "%s" ' , $ currentToken ));
@@ -368,6 +384,50 @@ protected function lexComment(): void
368
384
}
369
385
}
370
386
387
+ /**
388
+ * @throws Exception
389
+ */
390
+ protected function lexString (): void
391
+ {
392
+ if (preg_match ($ this ->regexes ['interpolation_start ' ], $ this ->code , $ match , 0 , $ this ->cursor )) {
393
+ $ this ->bracketsAndTernary [] = [$ this ->options ['interpolation ' ][0 ], $ this ->line ];
394
+ $ this ->pushToken (Token::INTERPOLATION_START_TYPE );
395
+ $ this ->moveCursor ($ match [0 ]);
396
+ $ this ->pushState (self ::STATE_INTERPOLATION );
397
+ } elseif (preg_match (self ::REGEX_DQ_STRING_PART , $ this ->code , $ match , 0 , $ this ->cursor ) && strlen ($ match [0 ]) > 0 ) {
398
+ $ this ->pushToken (Token::STRING_TYPE , stripcslashes ($ match [0 ]));
399
+ $ this ->moveCursor ($ match [0 ]);
400
+ } elseif (preg_match (self ::REGEX_DQ_STRING_DELIM , $ this ->code , $ match , 0 , $ this ->cursor )) {
401
+ $ bracket = array_pop ($ this ->bracketsAndTernary );
402
+
403
+ if ('" ' !== $ this ->code [$ this ->cursor ]) {
404
+ throw new Exception (sprintf ('Unclosed "%s" ' , $ bracket [0 ]));
405
+ }
406
+
407
+ $ this ->popState ();
408
+ $ this ->moveCursor ('" ' );
409
+ } else {
410
+ throw new Exception (sprintf ('Unexpected character "%s" ' , $ this ->code [$ this ->cursor ]));
411
+ }
412
+ }
413
+
414
+ /**
415
+ * @throws Exception
416
+ */
417
+ protected function lexInterpolation (): void
418
+ {
419
+ $ bracket = end ($ this ->bracketsAndTernary );
420
+ if ($ this ->options ['interpolation ' ][0 ] === $ bracket [0 ]
421
+ && preg_match ($ this ->regexes ['interpolation_end ' ], $ this ->code , $ match , 0 , $ this ->cursor )) {
422
+ array_pop ($ this ->bracketsAndTernary );
423
+ $ this ->pushToken (Token::INTERPOLATION_END_TYPE );
424
+ $ this ->moveCursor ($ match [0 ]);
425
+ $ this ->popState ();
426
+ } else {
427
+ $ this ->lexExpression ();
428
+ }
429
+ }
430
+
371
431
/**
372
432
* @param int $limit
373
433
*/
@@ -515,12 +575,13 @@ protected function lexPunctuation(): void
515
575
throw new Exception (sprintf ('Unexpected "%s" ' , $ currentToken ));
516
576
}
517
577
518
- $ expect = array_pop ($ this ->bracketsAndTernary )[0 ];
519
- if ('? ' === $ expect ) {
520
- throw new Exception ('Unclosed ternary ' );
578
+ $ bracket = array_pop ($ this ->bracketsAndTernary );
579
+ if ('? ' === $ bracket [0 ]) {
580
+ // Because {{ foo ? 'yes' }} is the same as {{ foo ? 'yes' : '' }}
581
+ $ bracket = array_pop ($ this ->bracketsAndTernary );
521
582
}
522
- if (strtr ($ expect , '([{ ' , ')]} ' ) !== $ currentToken ) {
523
- throw new Exception (sprintf ('Unclosed "%s" ' , $ expect ));
583
+ if (strtr ($ bracket [ 0 ] , '([{ ' , ')]} ' ) !== $ currentToken ) {
584
+ throw new Exception (sprintf ('Unclosed "%s" ' , $ bracket [ 0 ] ));
524
585
}
525
586
}
526
587
0 commit comments