diff --git a/internal/php5/php5.go b/internal/php5/php5.go index 9cc409a..a2bd33c 100644 Binary files a/internal/php5/php5.go and b/internal/php5/php5.go differ diff --git a/internal/php5/php5.y b/internal/php5/php5.y index ac247f2..ba55690 100644 --- a/internal/php5/php5.y +++ b/internal/php5/php5.y @@ -4709,7 +4709,9 @@ variable: ) $3 = append($3, $4[1:len($4)]...) case *ast.ExprPropertyFetch: + $4[0].(*ast.ExprMethodCall).OpenCurlyBracketTkn = l.OpenCurlyBracketTkn $4[0].(*ast.ExprMethodCall).Method = l.Property + $4[0].(*ast.ExprMethodCall).CloseCurlyBracketTkn = l.CloseCurlyBracketTkn $4[0].(*ast.ExprMethodCall).ObjectOperatorTkn = l.ObjectOperatorTkn $3 = append($3[:len($3)-1], $4...) } @@ -4801,7 +4803,9 @@ variable_property: ) $2 = append($2, $3[1:len($3)]...) case *ast.ExprPropertyFetch: + $3[0].(*ast.ExprMethodCall).OpenCurlyBracketTkn = l.OpenCurlyBracketTkn $3[0].(*ast.ExprMethodCall).Method = l.Property + $3[0].(*ast.ExprMethodCall).CloseCurlyBracketTkn = l.CloseCurlyBracketTkn $3[0].(*ast.ExprMethodCall).ObjectOperatorTkn = l.ObjectOperatorTkn $2 = append($2[:len($2)-1], $3...) } @@ -5078,12 +5082,18 @@ object_dim_list: } | variable_name { - $$ = []ast.Vertex{ - &ast.ExprPropertyFetch{ - Position: yylex.(*Parser).builder.NewNodePosition($1), - Property: $1, - }, + property := &ast.ExprPropertyFetch{ + Position: yylex.(*Parser).builder.NewNodePosition($1), + Property: $1, } + + if brackets, ok := $1.(*ast.ParserBrackets); ok { + property.OpenCurlyBracketTkn = brackets.OpenBracketTkn + property.Property = brackets.Child + property.CloseCurlyBracketTkn = brackets.CloseBracketTkn + } + + $$ = []ast.Vertex{ property } } ; diff --git a/internal/php7/php7.go b/internal/php7/php7.go index da378e5..bdd9dc6 100644 Binary files a/internal/php7/php7.go and b/internal/php7/php7.go differ diff --git a/internal/php7/php7.y b/internal/php7/php7.y index ae5f176..078c6eb 100644 --- a/internal/php7/php7.y +++ b/internal/php7/php7.y @@ -3738,7 +3738,7 @@ callable_variable: } | dereferencable T_OBJECT_OPERATOR property_name argument_list { - $$ = &ast.ExprMethodCall{ + methodCall := &ast.ExprMethodCall{ Position: yylex.(*Parser).builder.NewNodesPosition($1, $4), Var: $1, ObjectOperatorTkn: $2, @@ -3748,6 +3748,14 @@ callable_variable: SeparatorTkns: $4.(*ast.ArgumentList).SeparatorTkns, CloseParenthesisTkn: $4.(*ast.ArgumentList).CloseParenthesisTkn, } + + if brackets, ok := $3.(*ast.ParserBrackets); ok { + methodCall.OpenCurlyBracketTkn = brackets.OpenBracketTkn + methodCall.Method = brackets.Child + methodCall.CloseCurlyBracketTkn = brackets.CloseBracketTkn + } + + $$ = methodCall } | function_call { @@ -3766,12 +3774,20 @@ variable: } | dereferencable T_OBJECT_OPERATOR property_name { - $$ = &ast.ExprPropertyFetch{ + propertyFetch := &ast.ExprPropertyFetch{ Position: yylex.(*Parser).builder.NewNodesPosition($1, $3), Var: $1, ObjectOperatorTkn: $2, Property: $3, } + + if brackets, ok := $3.(*ast.ParserBrackets); ok { + propertyFetch.OpenCurlyBracketTkn = brackets.OpenBracketTkn + propertyFetch.Property = brackets.Child + propertyFetch.CloseCurlyBracketTkn = brackets.CloseBracketTkn + } + + $$ = propertyFetch } ; @@ -3855,12 +3871,20 @@ new_variable: } | new_variable T_OBJECT_OPERATOR property_name { - $$ = &ast.ExprPropertyFetch{ + propertyFetch := &ast.ExprPropertyFetch{ Position: yylex.(*Parser).builder.NewNodesPosition($1, $3), Var: $1, ObjectOperatorTkn: $2, Property: $3, } + + if brackets, ok := $3.(*ast.ParserBrackets); ok { + propertyFetch.OpenCurlyBracketTkn = brackets.OpenBracketTkn + propertyFetch.Property = brackets.Child + propertyFetch.CloseCurlyBracketTkn = brackets.CloseBracketTkn + } + + $$ = propertyFetch } | class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable { diff --git a/pkg/ast/node.go b/pkg/ast/node.go index 400537f..7a89a90 100644 --- a/pkg/ast/node.go +++ b/pkg/ast/node.go @@ -1501,14 +1501,16 @@ func (n *ExprList) GetPosition() *position.Position { // ExprMethodCall node type ExprMethodCall struct { - Position *position.Position - Var Vertex - ObjectOperatorTkn *token.Token - Method Vertex - OpenParenthesisTkn *token.Token - Arguments []Vertex - SeparatorTkns []*token.Token - CloseParenthesisTkn *token.Token + Position *position.Position + Var Vertex + ObjectOperatorTkn *token.Token + OpenCurlyBracketTkn *token.Token + Method Vertex + CloseCurlyBracketTkn *token.Token + OpenParenthesisTkn *token.Token + Arguments []Vertex + SeparatorTkns []*token.Token + CloseParenthesisTkn *token.Token } func (n *ExprMethodCall) Accept(v NodeVisitor) { @@ -1615,10 +1617,12 @@ func (n *ExprPrint) GetPosition() *position.Position { // ExprPropertyFetch node type ExprPropertyFetch struct { - Position *position.Position - Var Vertex - ObjectOperatorTkn *token.Token - Property Vertex + Position *position.Position + Var Vertex + ObjectOperatorTkn *token.Token + OpenCurlyBracketTkn *token.Token + Property Vertex + CloseCurlyBracketTkn *token.Token } func (n *ExprPropertyFetch) Accept(v NodeVisitor) { diff --git a/pkg/ast/visitor/dumper.go b/pkg/ast/visitor/dumper.go index 9778215..11f3159 100644 --- a/pkg/ast/visitor/dumper.go +++ b/pkg/ast/visitor/dumper.go @@ -1326,7 +1326,9 @@ func (v *Dumper) ExprMethodCall(n *ast.ExprMethodCall) { v.dumpPosition(n.Position) v.dumpVertex("Var", n.Var) v.dumpToken("ObjectOperatorTkn", n.ObjectOperatorTkn) + v.dumpToken("OpenCurlyBracketTkn", n.OpenCurlyBracketTkn) v.dumpVertex("Method", n.Method) + v.dumpToken("CloseCurlyBracketTkn", n.CloseCurlyBracketTkn) v.dumpToken("OpenParenthesisTkn", n.OpenParenthesisTkn) v.dumpVertexList("Arguments", n.Arguments) v.dumpTokenList("SeparatorTkns", n.SeparatorTkns) @@ -1419,7 +1421,9 @@ func (v *Dumper) ExprPropertyFetch(n *ast.ExprPropertyFetch) { v.dumpPosition(n.Position) v.dumpVertex("Var", n.Var) v.dumpToken("ObjectOperatorTkn", n.ObjectOperatorTkn) + v.dumpToken("OpenCurlyBracketTkn", n.OpenCurlyBracketTkn) v.dumpVertex("Property", n.Property) + v.dumpToken("CloseCurlyBracketTkn", n.CloseCurlyBracketTkn) v.indent-- v.print(v.indent, "},\n") diff --git a/pkg/ast/visitor/formatter.go b/pkg/ast/visitor/formatter.go index 7fde3cb..9dd5d00 100644 --- a/pkg/ast/visitor/formatter.go +++ b/pkg/ast/visitor/formatter.go @@ -1290,6 +1290,17 @@ func (f *formatter) ExprList(n *ast.ExprList) { func (f *formatter) ExprMethodCall(n *ast.ExprMethodCall) { n.Var.Accept(f) n.ObjectOperatorTkn = f.newToken(token.T_OBJECT_OPERATOR, []byte("->")) + + n.OpenCurlyBracketTkn = nil + n.CloseCurlyBracketTkn = nil + switch n.Method.(type) { + case *ast.Identifier: + case *ast.ExprVariable: + default: + n.OpenCurlyBracketTkn = f.newToken('{', []byte("{")) + n.CloseCurlyBracketTkn = f.newToken('}', []byte("}")) + } + n.Method.Accept(f) n.OpenParenthesisTkn = f.newToken('(', []byte("(")) @@ -1346,6 +1357,17 @@ func (f *formatter) ExprPrint(n *ast.ExprPrint) { func (f *formatter) ExprPropertyFetch(n *ast.ExprPropertyFetch) { n.Var.Accept(f) n.ObjectOperatorTkn = f.newToken(token.T_OBJECT_OPERATOR, []byte("->")) + + n.OpenCurlyBracketTkn = nil + n.CloseCurlyBracketTkn = nil + switch n.Property.(type) { + case *ast.Identifier: + case *ast.ExprVariable: + default: + n.OpenCurlyBracketTkn = f.newToken('{', []byte("{")) + n.CloseCurlyBracketTkn = f.newToken('}', []byte("}")) + } + n.Property.Accept(f) } diff --git a/pkg/ast/visitor/formatter_test.go b/pkg/ast/visitor/formatter_test.go index 62580e7..bc08e81 100644 --- a/pkg/ast/visitor/formatter_test.go +++ b/pkg/ast/visitor/formatter_test.go @@ -4216,6 +4216,34 @@ func TestFormatter_ExprMethodCall(t *testing.T) { } } +func TestFormatter_ExprMethodCall_Expr(t *testing.T) { + o := bytes.NewBufferString("") + + n := &ast.ExprMethodCall{ + Var: &ast.ExprVariable{ + VarName: &ast.Identifier{ + Value: []byte("$foo"), + }, + }, + Method: &ast.ScalarString{ + Value: []byte("'bar'"), + }, + } + + f := visitor.NewFormatter().WithState(visitor.FormatterStatePHP).WithIndent(1) + n.Accept(f) + + p := visitor.NewPrinter(o).WithState(visitor.PrinterStatePHP) + n.Accept(p) + + expected := `$foo->{'bar'}()` + actual := o.String() + + if expected != actual { + t.Errorf("\nexpected: %s\ngot: %s\n", expected, actual) + } +} + func TestFormatter_ExprMethodCall_Arguments(t *testing.T) { o := bytes.NewBufferString("") @@ -4483,6 +4511,34 @@ func TestFormatter_ExprPropertyFetch(t *testing.T) { } } +func TestFormatter_ExprPropertyFetch_Expr(t *testing.T) { + o := bytes.NewBufferString("") + + n := &ast.ExprPropertyFetch{ + Var: &ast.ExprVariable{ + VarName: &ast.Identifier{ + Value: []byte("$foo"), + }, + }, + Property: &ast.ScalarString{ + Value: []byte("'bar'"), + }, + } + + f := visitor.NewFormatter().WithState(visitor.FormatterStatePHP).WithIndent(1) + n.Accept(f) + + p := visitor.NewPrinter(o).WithState(visitor.PrinterStatePHP) + n.Accept(p) + + expected := `$foo->{'bar'}` + actual := o.String() + + if expected != actual { + t.Errorf("\nexpected: %s\ngot: %s\n", expected, actual) + } +} + func TestFormatter_ExprRequire(t *testing.T) { o := bytes.NewBufferString("") diff --git a/pkg/ast/visitor/printer.go b/pkg/ast/visitor/printer.go index 99db93a..d4ac78a 100644 --- a/pkg/ast/visitor/printer.go +++ b/pkg/ast/visitor/printer.go @@ -772,7 +772,9 @@ func (p *printer) ExprList(n *ast.ExprList) { func (p *printer) ExprMethodCall(n *ast.ExprMethodCall) { p.printNode(n.Var) p.printToken(n.ObjectOperatorTkn, []byte("->")) + p.printToken(n.OpenCurlyBracketTkn, nil) p.printNode(n.Method) + p.printToken(n.CloseCurlyBracketTkn, nil) p.printToken(n.OpenParenthesisTkn, []byte("(")) p.printSeparatedList(n.Arguments, n.SeparatorTkns, []byte(",")) p.printToken(n.CloseParenthesisTkn, []byte(")")) @@ -814,7 +816,9 @@ func (p *printer) ExprPrint(n *ast.ExprPrint) { func (p *printer) ExprPropertyFetch(n *ast.ExprPropertyFetch) { p.printNode(n.Var) p.printToken(n.ObjectOperatorTkn, []byte("->")) + p.printToken(n.OpenCurlyBracketTkn, nil) p.printNode(n.Property) + p.printToken(n.CloseCurlyBracketTkn, nil) } func (p *printer) ExprRequire(n *ast.ExprRequire) {