2424use Twig \Node \Expression \ConditionalExpression ;
2525use Twig \Node \Expression \ConstantExpression ;
2626use Twig \Node \Expression \GetAttrExpression ;
27- use Twig \Node \Expression \MethodCallExpression ;
27+ use Twig \Node \Expression \MacroReferenceExpression ;
2828use Twig \Node \Expression \NameExpression ;
2929use Twig \Node \Expression \TempNameExpression ;
3030use Twig \Node \Expression \TestExpression ;
3333use Twig \Node \Expression \Unary \NotUnary ;
3434use Twig \Node \Expression \Unary \PosUnary ;
3535use Twig \Node \Expression \Unary \SpreadUnary ;
36+ use Twig \Node \Expression \Variable \TemplateVariable ;
3637use Twig \Node \Node ;
3738use Twig \Node \Nodes ;
3839
@@ -531,11 +532,7 @@ public function parsePostfixExpression($node)
531532 public function getFunctionNode ($ name , $ line )
532533 {
533534 if (null !== $ alias = $ this ->parser ->getImportedSymbol ('function ' , $ name )) {
534- $ arguments = $ this ->createArguments ($ line );
535- $ node = new MethodCallExpression ($ alias ['node ' ], $ alias ['name ' ], $ arguments , $ line );
536- $ node ->setAttribute ('safe ' , true );
537-
538- return $ node ;
535+ return new MacroReferenceExpression (new TemplateVariable ($ alias ['node ' ], $ line ), $ alias ['name ' ], $ this ->createArguments ($ line ), $ line );
539536 }
540537
541538 $ args = $ this ->parseOnlyArguments ();
@@ -561,84 +558,11 @@ public function getFunctionNode($name, $line)
561558
562559 public function parseSubscriptExpression ($ node )
563560 {
564- $ stream = $ this ->parser ->getStream ();
565- $ token = $ stream ->next ();
566- $ lineno = $ token ->getLine ();
567- $ arguments = new ArrayExpression ([], $ lineno );
568- $ type = Template::ANY_CALL ;
569- if ('. ' == $ token ->getValue ()) {
570- if ($ stream ->nextIf (Token::PUNCTUATION_TYPE , '( ' )) {
571- $ arg = $ this ->parseExpression ();
572- $ stream ->expect (Token::PUNCTUATION_TYPE , ') ' );
573- if ($ stream ->test (Token::PUNCTUATION_TYPE , '( ' )) {
574- $ type = Template::METHOD_CALL ;
575- $ arguments = $ this ->createArguments ($ lineno );
576- }
577-
578- return new GetAttrExpression ($ node , $ arg , $ arguments , $ type , $ lineno );
579- }
580- $ token = $ stream ->next ();
581- if (
582- Token::NAME_TYPE == $ token ->getType ()
583- ||
584- Token::NUMBER_TYPE == $ token ->getType ()
585- ||
586- (Token::OPERATOR_TYPE == $ token ->getType () && preg_match (Lexer::REGEX_NAME , $ token ->getValue ()))
587- ) {
588- $ arg = new ConstantExpression ($ token ->getValue (), $ lineno );
589-
590- if ($ stream ->test (Token::PUNCTUATION_TYPE , '( ' )) {
591- $ type = Template::METHOD_CALL ;
592- $ arguments = $ this ->createArguments ($ lineno );
593- }
594- } else {
595- throw new SyntaxError (\sprintf ('Expected name or number, got value "%s" of type %s. ' , $ token ->getValue (), Token::typeToEnglish ($ token ->getType ())), $ lineno , $ stream ->getSourceContext ());
596- }
597-
598- if ($ node instanceof NameExpression && null !== $ this ->parser ->getImportedSymbol ('template ' , $ node ->getAttribute ('name ' ))) {
599- $ name = $ arg ->getAttribute ('value ' );
600-
601- $ node = new MethodCallExpression ($ node , 'macro_ ' .$ name , $ arguments , $ lineno );
602- $ node ->setAttribute ('safe ' , true );
603-
604- return $ node ;
605- }
606- } else {
607- $ type = Template::ARRAY_CALL ;
608-
609- // slice?
610- $ slice = false ;
611- if ($ stream ->test (Token::PUNCTUATION_TYPE , ': ' )) {
612- $ slice = true ;
613- $ arg = new ConstantExpression (0 , $ token ->getLine ());
614- } else {
615- $ arg = $ this ->parseExpression ();
616- }
617-
618- if ($ stream ->nextIf (Token::PUNCTUATION_TYPE , ': ' )) {
619- $ slice = true ;
620- }
621-
622- if ($ slice ) {
623- if ($ stream ->test (Token::PUNCTUATION_TYPE , '] ' )) {
624- $ length = new ConstantExpression (null , $ token ->getLine ());
625- } else {
626- $ length = $ this ->parseExpression ();
627- }
628-
629- $ filter = $ this ->getFilter ('slice ' , $ token ->getLine ());
630- $ arguments = new Nodes ([$ arg , $ length ]);
631- $ filter = new ($ filter ->getNodeClass ())($ node , $ filter , $ arguments , $ token ->getLine ());
632-
633- $ stream ->expect (Token::PUNCTUATION_TYPE , '] ' );
634-
635- return $ filter ;
636- }
637-
638- $ stream ->expect (Token::PUNCTUATION_TYPE , '] ' );
561+ if ('. ' === $ this ->parser ->getStream ()->next ()->getValue ()) {
562+ return $ this ->parseSubscriptExpressionDot ($ node );
639563 }
640564
641- return new GetAttrExpression ($ node, $ arg , $ arguments , $ type , $ lineno );
565+ return $ this -> parseSubscriptExpressionArray ($ node );
642566 }
643567
644568 public function parseFilterExpression ($ node )
@@ -825,8 +749,7 @@ private function parseTestExpression(Node $node): TestExpression
825749 }
826750
827751 if ('defined ' === $ test ->getName () && $ node instanceof NameExpression && null !== $ alias = $ this ->parser ->getImportedSymbol ('function ' , $ node ->getAttribute ('name ' ))) {
828- $ node = new MethodCallExpression ($ alias ['node ' ], $ alias ['name ' ], new ArrayExpression ([], $ node ->getTemplateLine ()), $ node ->getTemplateLine ());
829- $ node ->setAttribute ('safe ' , true );
752+ $ node = new MacroReferenceExpression (new TemplateVariable ($ alias ['node ' ], $ node ->getTemplateLine ()), $ alias ['name ' ], new ArrayExpression ([], $ node ->getTemplateLine ()), $ node ->getTemplateLine ());
830753 }
831754
832755 $ ready = $ test instanceof TwigTest;
@@ -997,4 +920,91 @@ public function parseOnlyArguments()
997920
998921 return new Nodes ($ args );
999922 }
923+
924+ private function parseSubscriptExpressionDot (Node $ node ): AbstractExpression
925+ {
926+ $ stream = $ this ->parser ->getStream ();
927+ $ token = $ stream ->getCurrent ();
928+ $ lineno = $ token ->getLine ();
929+ $ arguments = new ArrayExpression ([], $ lineno );
930+ $ type = Template::ANY_CALL ;
931+
932+ if ($ stream ->nextIf (Token::PUNCTUATION_TYPE , '( ' )) {
933+ $ attribute = $ this ->parseExpression ();
934+ $ stream ->expect (Token::PUNCTUATION_TYPE , ') ' );
935+ } else {
936+ $ token = $ stream ->next ();
937+ if (
938+ Token::NAME_TYPE == $ token ->getType ()
939+ ||
940+ Token::NUMBER_TYPE == $ token ->getType ()
941+ ||
942+ (Token::OPERATOR_TYPE == $ token ->getType () && preg_match (Lexer::REGEX_NAME , $ token ->getValue ()))
943+ ) {
944+ $ attribute = new ConstantExpression ($ token ->getValue (), $ token ->getLine ());
945+ } else {
946+ throw new SyntaxError (\sprintf ('Expected name or number, got value "%s" of type %s. ' , $ token ->getValue (), Token::typeToEnglish ($ token ->getType ())), $ token ->getLine (), $ stream ->getSourceContext ());
947+ }
948+ }
949+
950+ if ($ stream ->test (Token::PUNCTUATION_TYPE , '( ' )) {
951+ $ type = Template::METHOD_CALL ;
952+ $ arguments = $ this ->createArguments ($ token ->getLine ());
953+ }
954+
955+ if (
956+ $ node instanceof NameExpression
957+ &&
958+ (
959+ null !== $ this ->parser ->getImportedSymbol ('template ' , $ node ->getAttribute ('name ' ))
960+ ||
961+ '_self ' === $ node ->getAttribute ('name ' ) && $ attribute instanceof ConstantExpression
962+ )
963+ ) {
964+ return new MacroReferenceExpression (new TemplateVariable ($ node ->getAttribute ('name ' ), $ node ->getTemplateLine ()), 'macro_ ' .$ attribute ->getAttribute ('value ' ), $ arguments , $ node ->getTemplateLine ());
965+ }
966+
967+ return new GetAttrExpression ($ node , $ attribute , $ arguments , $ type , $ lineno );
968+ }
969+
970+ private function parseSubscriptExpressionArray (Node $ node ): AbstractExpression
971+ {
972+ $ stream = $ this ->parser ->getStream ();
973+ $ token = $ stream ->getCurrent ();
974+ $ lineno = $ token ->getLine ();
975+ $ arguments = new ArrayExpression ([], $ lineno );
976+
977+ // slice?
978+ $ slice = false ;
979+ if ($ stream ->test (Token::PUNCTUATION_TYPE , ': ' )) {
980+ $ slice = true ;
981+ $ attribute = new ConstantExpression (0 , $ token ->getLine ());
982+ } else {
983+ $ attribute = $ this ->parseExpression ();
984+ }
985+
986+ if ($ stream ->nextIf (Token::PUNCTUATION_TYPE , ': ' )) {
987+ $ slice = true ;
988+ }
989+
990+ if ($ slice ) {
991+ if ($ stream ->test (Token::PUNCTUATION_TYPE , '] ' )) {
992+ $ length = new ConstantExpression (null , $ token ->getLine ());
993+ } else {
994+ $ length = $ this ->parseExpression ();
995+ }
996+
997+ $ filter = $ this ->getFilter ('slice ' , $ token ->getLine ());
998+ $ arguments = new Nodes ([$ attribute , $ length ]);
999+ $ filter = new ($ filter ->getNodeClass ())($ node , $ filter , $ arguments , $ token ->getLine ());
1000+
1001+ $ stream ->expect (Token::PUNCTUATION_TYPE , '] ' );
1002+
1003+ return $ filter ;
1004+ }
1005+
1006+ $ stream ->expect (Token::PUNCTUATION_TYPE , '] ' );
1007+
1008+ return new GetAttrExpression ($ node , $ attribute , $ arguments , Template::ARRAY_CALL , $ lineno );
1009+ }
10001010}
0 commit comments