refactoring: update ast structure of "Foreach" node

This commit is contained in:
Vadym Slizov 2020-12-26 19:38:45 +02:00
parent b85bae2ec1
commit 8bf1fa822d
No known key found for this signature in database
GPG Key ID: AEA2A9388EF42A4A
12 changed files with 310 additions and 820 deletions

View File

@ -15045,13 +15045,6 @@ func TestStmtForeach_WithRef(t *testing.T) {
},
},
},
Var: &ast.ExprReference{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 24,
EndPos: 27,
},
AmpersandTkn: &token.Token{
ID: token.ID(38),
Value: []byte("&"),
@ -15101,7 +15094,6 @@ func TestStmtForeach_WithRef(t *testing.T) {
Value: []byte("$v"),
},
},
},
CloseParenthesisTkn: &token.Token{
ID: token.ID(41),
Value: []byte(")"),
@ -32123,8 +32115,7 @@ func TestExprClosure_Use(t *testing.T) {
},
},
},
EndTkn: &token.Token{
},
EndTkn: &token.Token{},
}
lexer := scanner.NewLexer([]byte(src), "5.6", nil)
@ -32513,8 +32504,7 @@ func TestExprClosure_Use2(t *testing.T) {
},
},
},
EndTkn: &token.Token{
},
EndTkn: &token.Token{},
}
lexer := scanner.NewLexer([]byte(src), "5.6", nil)
@ -38567,309 +38557,6 @@ func TestExprPropertyFetch(t *testing.T) {
assert.DeepEqual(t, expected, actual)
}
func TestExprReference_ForeachWithRef(t *testing.T) {
t.Helper()
src := `<? foreach ($a as $k => &$v) {}`
expected := &ast.Root{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 3,
EndPos: 31,
},
Stmts: []ast.Vertex{
&ast.StmtForeach{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 3,
EndPos: 31,
},
ForeachTkn: &token.Token{
ID: token.T_FOREACH,
Value: []byte("foreach"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 3,
EndPos: 10,
},
FreeFloating: []*token.Token{
{
ID: token.T_OPEN_TAG,
Value: []byte("<?"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 0,
EndPos: 2,
},
},
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 2,
EndPos: 3,
},
},
},
},
OpenParenthesisTkn: &token.Token{
ID: token.ID(40),
Value: []byte("("),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 11,
EndPos: 12,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 10,
EndPos: 11,
},
},
},
},
Expr: &ast.ExprVariable{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 12,
EndPos: 14,
},
VarName: &ast.Identifier{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 12,
EndPos: 14,
},
IdentifierTkn: &token.Token{
ID: token.T_VARIABLE,
Value: []byte("$a"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 12,
EndPos: 14,
},
},
Value: []byte("$a"),
},
},
AsTkn: &token.Token{
ID: token.T_AS,
Value: []byte("as"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 15,
EndPos: 17,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 14,
EndPos: 15,
},
},
},
},
Key: &ast.ExprVariable{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 18,
EndPos: 20,
},
VarName: &ast.Identifier{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 18,
EndPos: 20,
},
IdentifierTkn: &token.Token{
ID: token.T_VARIABLE,
Value: []byte("$k"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 18,
EndPos: 20,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 17,
EndPos: 18,
},
},
},
},
Value: []byte("$k"),
},
},
DoubleArrowTkn: &token.Token{
ID: token.T_DOUBLE_ARROW,
Value: []byte("=>"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 21,
EndPos: 23,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 20,
EndPos: 21,
},
},
},
},
Var: &ast.ExprReference{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 24,
EndPos: 27,
},
AmpersandTkn: &token.Token{
ID: token.ID(38),
Value: []byte("&"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 24,
EndPos: 25,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 23,
EndPos: 24,
},
},
},
},
Var: &ast.ExprVariable{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 25,
EndPos: 27,
},
VarName: &ast.Identifier{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 25,
EndPos: 27,
},
IdentifierTkn: &token.Token{
ID: token.T_VARIABLE,
Value: []byte("$v"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 25,
EndPos: 27,
},
},
Value: []byte("$v"),
},
},
},
CloseParenthesisTkn: &token.Token{
ID: token.ID(41),
Value: []byte(")"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 27,
EndPos: 28,
},
},
Stmt: &ast.StmtStmtList{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 29,
EndPos: 31,
},
OpenCurlyBracketTkn: &token.Token{
ID: token.ID(123),
Value: []byte("{"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 29,
EndPos: 30,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 28,
EndPos: 29,
},
},
},
},
Stmts: []ast.Vertex{},
CloseCurlyBracketTkn: &token.Token{
ID: token.ID(125),
Value: []byte("}"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 30,
EndPos: 31,
},
},
},
},
},
EndTkn: &token.Token{},
}
lexer := scanner.NewLexer([]byte(src), "5.6", nil)
php5parser := php5.NewParser(lexer, nil)
php5parser.Parse()
actual := php5parser.GetRootNode()
assert.DeepEqual(t, expected, actual)
}
func TestExprShellExec(t *testing.T) {
src := "<? `cmd $a`;"

BIN
internal/php5/php5.go generated

Binary file not shown.

View File

@ -5,6 +5,7 @@ import (
"strconv"
"github.com/z7zmey/php-parser/pkg/ast"
"github.com/z7zmey/php-parser/pkg/errors"
"github.com/z7zmey/php-parser/pkg/token"
)
@ -983,39 +984,63 @@ unticked_statement:
}
| T_FOREACH '(' variable T_AS foreach_variable foreach_optional_arg ')' foreach_statement
{
$8.(*ast.StmtForeach).ForeachTkn = $1
$8.(*ast.StmtForeach).OpenParenthesisTkn = $2
$8.(*ast.StmtForeach).Expr = $3
$8.(*ast.StmtForeach).AsTkn = $4
if $6 == nil {
$8.(*ast.StmtForeach).Var = $5
} else {
$8.(*ast.StmtForeach).Key = $5
$8.(*ast.StmtForeach).DoubleArrowTkn = $6.(*ast.StmtForeach).DoubleArrowTkn
$8.(*ast.StmtForeach).Var = $6.(*ast.StmtForeach).Var
}
$8.(*ast.StmtForeach).CloseParenthesisTkn = $7
$8.(*ast.StmtForeach).Position = yylex.(*Parser).builder.NewTokenNodePosition($1, $8)
foreach := $8.(*ast.StmtForeach)
$$ = $8
foreach.Position = yylex.(*Parser).builder.NewTokenNodePosition($1, $8)
foreach.ForeachTkn = $1
foreach.OpenParenthesisTkn = $2
foreach.Expr = $3
foreach.AsTkn = $4
foreach.Var = $5
foreach.CloseParenthesisTkn = $7
if $6 != nil {
foreach.Key = foreach.Var
foreach.DoubleArrowTkn = $6.(*ast.StmtForeach).DoubleArrowTkn
foreach.Var = $6.(*ast.StmtForeach).Var
}
if val, ok := foreach.Key.(*ast.ExprReference); ok {
yylex.(*Parser).errHandlerFunc(errors.NewError("Key element cannot be a reference", val.AmpersandTkn.Position))
foreach.Key = val.Var
}
if val, ok := foreach.Var.(*ast.ExprReference); ok {
foreach.AmpersandTkn = val.AmpersandTkn
foreach.Var = val.Var
}
$$ = foreach
}
| T_FOREACH '(' expr_without_variable T_AS foreach_variable foreach_optional_arg ')' foreach_statement
{
$8.(*ast.StmtForeach).ForeachTkn = $1
$8.(*ast.StmtForeach).OpenParenthesisTkn = $2
$8.(*ast.StmtForeach).Expr = $3
$8.(*ast.StmtForeach).AsTkn = $4
if $6 == nil {
$8.(*ast.StmtForeach).Var = $5
} else {
$8.(*ast.StmtForeach).Key = $5
$8.(*ast.StmtForeach).DoubleArrowTkn = $6.(*ast.StmtForeach).DoubleArrowTkn
$8.(*ast.StmtForeach).Var = $6.(*ast.StmtForeach).Var
}
$8.(*ast.StmtForeach).CloseParenthesisTkn = $7
$8.(*ast.StmtForeach).Position = yylex.(*Parser).builder.NewTokenNodePosition($1, $8)
foreach := $8.(*ast.StmtForeach)
$$ = $8
foreach.Position = yylex.(*Parser).builder.NewTokenNodePosition($1, $8)
foreach.ForeachTkn = $1
foreach.OpenParenthesisTkn = $2
foreach.Expr = $3
foreach.AsTkn = $4
foreach.Var = $5
foreach.CloseParenthesisTkn = $7
if $6 != nil {
foreach.Key = foreach.Var
foreach.DoubleArrowTkn = $6.(*ast.StmtForeach).DoubleArrowTkn
foreach.Var = $6.(*ast.StmtForeach).Var
}
if val, ok := foreach.Key.(*ast.ExprReference); ok {
yylex.(*Parser).errHandlerFunc(errors.NewError("Key element cannot be a reference", val.AmpersandTkn.Position))
foreach.Key = val.Var
}
if val, ok := foreach.Var.(*ast.ExprReference); ok {
foreach.AmpersandTkn = val.AmpersandTkn
foreach.Var = val.Var
}
$$ = foreach
}
| T_DECLARE '(' declare_list ')' declare_statement
{

View File

@ -16524,13 +16524,6 @@ func TestStmtForeach_WithRef(t *testing.T) {
},
},
},
Var: &ast.ExprReference{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 24,
EndPos: 27,
},
AmpersandTkn: &token.Token{
ID: token.ID(38),
Value: []byte("&"),
@ -16580,7 +16573,6 @@ func TestStmtForeach_WithRef(t *testing.T) {
Value: []byte("$v"),
},
},
},
CloseParenthesisTkn: &token.Token{
ID: token.ID(41),
Value: []byte(")"),
@ -36621,8 +36613,7 @@ func TestExprClosure_Use(t *testing.T) {
},
},
},
EndTkn: &token.Token{
},
EndTkn: &token.Token{},
}
lexer := scanner.NewLexer([]byte(src), "7.4", nil)
@ -37011,8 +37002,7 @@ func TestExprClosure_Use2(t *testing.T) {
},
},
},
EndTkn: &token.Token{
},
EndTkn: &token.Token{},
}
lexer := scanner.NewLexer([]byte(src), "7.4", nil)
@ -43525,309 +43515,6 @@ func TestExprPropertyFetch(t *testing.T) {
assert.DeepEqual(t, expected, actual)
}
func TestExprReference_ForeachWithRef(t *testing.T) {
t.Helper()
src := `<? foreach ($a as $k => &$v) {}`
expected := &ast.Root{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 3,
EndPos: 31,
},
Stmts: []ast.Vertex{
&ast.StmtForeach{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 3,
EndPos: 31,
},
ForeachTkn: &token.Token{
ID: token.T_FOREACH,
Value: []byte("foreach"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 3,
EndPos: 10,
},
FreeFloating: []*token.Token{
{
ID: token.T_OPEN_TAG,
Value: []byte("<?"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 0,
EndPos: 2,
},
},
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 2,
EndPos: 3,
},
},
},
},
OpenParenthesisTkn: &token.Token{
ID: token.ID(40),
Value: []byte("("),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 11,
EndPos: 12,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 10,
EndPos: 11,
},
},
},
},
Expr: &ast.ExprVariable{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 12,
EndPos: 14,
},
VarName: &ast.Identifier{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 12,
EndPos: 14,
},
IdentifierTkn: &token.Token{
ID: token.T_VARIABLE,
Value: []byte("$a"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 12,
EndPos: 14,
},
},
Value: []byte("$a"),
},
},
AsTkn: &token.Token{
ID: token.T_AS,
Value: []byte("as"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 15,
EndPos: 17,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 14,
EndPos: 15,
},
},
},
},
Key: &ast.ExprVariable{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 18,
EndPos: 20,
},
VarName: &ast.Identifier{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 18,
EndPos: 20,
},
IdentifierTkn: &token.Token{
ID: token.T_VARIABLE,
Value: []byte("$k"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 18,
EndPos: 20,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 17,
EndPos: 18,
},
},
},
},
Value: []byte("$k"),
},
},
DoubleArrowTkn: &token.Token{
ID: token.T_DOUBLE_ARROW,
Value: []byte("=>"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 21,
EndPos: 23,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 20,
EndPos: 21,
},
},
},
},
Var: &ast.ExprReference{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 24,
EndPos: 27,
},
AmpersandTkn: &token.Token{
ID: token.ID(38),
Value: []byte("&"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 24,
EndPos: 25,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 23,
EndPos: 24,
},
},
},
},
Var: &ast.ExprVariable{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 25,
EndPos: 27,
},
VarName: &ast.Identifier{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 25,
EndPos: 27,
},
IdentifierTkn: &token.Token{
ID: token.T_VARIABLE,
Value: []byte("$v"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 25,
EndPos: 27,
},
},
Value: []byte("$v"),
},
},
},
CloseParenthesisTkn: &token.Token{
ID: token.ID(41),
Value: []byte(")"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 27,
EndPos: 28,
},
},
Stmt: &ast.StmtStmtList{
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 29,
EndPos: 31,
},
OpenCurlyBracketTkn: &token.Token{
ID: token.ID(123),
Value: []byte("{"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 29,
EndPos: 30,
},
FreeFloating: []*token.Token{
{
ID: token.T_WHITESPACE,
Value: []byte(" "),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 28,
EndPos: 29,
},
},
},
},
Stmts: []ast.Vertex{},
CloseCurlyBracketTkn: &token.Token{
ID: token.ID(125),
Value: []byte("}"),
Position: &position.Position{
StartLine: 1,
EndLine: 1,
StartPos: 30,
EndPos: 31,
},
},
},
},
},
EndTkn: &token.Token{},
}
lexer := scanner.NewLexer([]byte(src), "7.4", nil)
php7parser := php7.NewParser(lexer, nil)
php7parser.Parse()
actual := php7parser.GetRootNode()
assert.DeepEqual(t, expected, actual)
}
func TestExprShellExec(t *testing.T) {
src := "<? `cmd $a`;"

BIN
internal/php7/php7.go generated

Binary file not shown.

View File

@ -962,29 +962,43 @@ statement:
}
| T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement
{
$7.(*ast.StmtForeach).ForeachTkn = $1
$7.(*ast.StmtForeach).OpenParenthesisTkn = $2
$7.(*ast.StmtForeach).Expr = $3
$7.(*ast.StmtForeach).AsTkn = $4
$7.(*ast.StmtForeach).Var = $5
$7.(*ast.StmtForeach).CloseParenthesisTkn = $6
$7.(*ast.StmtForeach).Position = yylex.(*Parser).builder.NewTokenNodePosition($1, $7)
foreach := $7.(*ast.StmtForeach)
$$ = $7
foreach.Position = yylex.(*Parser).builder.NewTokenNodePosition($1, $7)
foreach.ForeachTkn = $1
foreach.OpenParenthesisTkn = $2
foreach.Expr = $3
foreach.AsTkn = $4
foreach.Var = $5
foreach.CloseParenthesisTkn = $6
if val, ok := $5.(*ast.ExprReference); ok {
foreach.AmpersandTkn = val.AmpersandTkn
foreach.Var = val.Var
}
$$ = foreach
}
| T_FOREACH '(' expr T_AS variable T_DOUBLE_ARROW foreach_variable ')' foreach_statement
{
$9.(*ast.StmtForeach).ForeachTkn = $1
$9.(*ast.StmtForeach).OpenParenthesisTkn = $2
$9.(*ast.StmtForeach).Expr = $3
$9.(*ast.StmtForeach).AsTkn = $4
$9.(*ast.StmtForeach).Key = $5
$9.(*ast.StmtForeach).DoubleArrowTkn = $6
$9.(*ast.StmtForeach).Var = $7
$9.(*ast.StmtForeach).CloseParenthesisTkn = $8
$9.(*ast.StmtForeach).Position = yylex.(*Parser).builder.NewTokenNodePosition($1, $9)
foreach := $9.(*ast.StmtForeach)
$$ = $9
foreach.Position = yylex.(*Parser).builder.NewTokenNodePosition($1, $9)
foreach.ForeachTkn = $1
foreach.OpenParenthesisTkn = $2
foreach.Expr = $3
foreach.AsTkn = $4
foreach.Key = $5
foreach.DoubleArrowTkn = $6
foreach.Var = $7
foreach.CloseParenthesisTkn = $8
if val, ok := $7.(*ast.ExprReference); ok {
foreach.AmpersandTkn = val.AmpersandTkn
foreach.Var = val.Var
}
$$ = foreach
}
| T_DECLARE '(' const_list ')' declare_statement
{

View File

@ -574,6 +574,7 @@ type StmtForeach struct {
AsTkn *token.Token
Key Vertex
DoubleArrowTkn *token.Token
AmpersandTkn *token.Token
Var Vertex
CloseParenthesisTkn *token.Token
ColonTkn *token.Token

View File

@ -552,6 +552,7 @@ func (v *Dumper) StmtForeach(n *ast.StmtForeach) {
v.dumpToken("AsTkn", n.AsTkn)
v.dumpVertex("Key", n.Key)
v.dumpToken("DoubleArrowTkn", n.DoubleArrowTkn)
v.dumpToken("AmpersandTkn", n.AmpersandTkn)
v.dumpVertex("Var", n.Var)
v.dumpToken("CloseParenthesisTkn", n.CloseParenthesisTkn)
v.dumpToken("ColonTkn", n.ColonTkn)

View File

@ -557,6 +557,9 @@ func (f *formatter) StmtForeach(n *ast.StmtForeach) {
}
f.addFreeFloating(token.T_WHITESPACE, []byte(" "))
if n.AmpersandTkn != nil {
n.AmpersandTkn = f.newToken('&', []byte("&"))
}
n.Var.Accept(f)
n.CloseParenthesisTkn = f.newToken(')', []byte(")"))

View File

@ -1372,6 +1372,44 @@ func TestFormatter_StmtForeach(t *testing.T) {
}
}
func TestFormatter_StmtForeach_Reference(t *testing.T) {
o := bytes.NewBufferString("")
n := &ast.StmtForeach{
Expr: &ast.ExprVariable{
VarName: &ast.Identifier{
Value: []byte("$foo"),
},
},
AmpersandTkn: &token.Token{},
Var: &ast.ExprVariable{
VarName: &ast.Identifier{
Value: []byte("$val"),
},
},
Stmt: &ast.StmtStmtList{
Stmts: []ast.Vertex{
&ast.StmtNop{},
},
},
}
f := visitor.NewFormatter().WithState(visitor.FormatterStatePHP).WithIndent(1)
n.Accept(f)
p := visitor.NewPrinter(o).WithState(visitor.PrinterStatePHP)
n.Accept(p)
expected := `foreach($foo as &$val) {
;
}`
actual := o.String()
if expected != actual {
t.Errorf("\nexpected: %s\ngot: %s\n", expected, actual)
}
}
func TestFormatter_StmtForeach_Key(t *testing.T) {
o := bytes.NewBufferString("")

View File

@ -356,6 +356,7 @@ func (p *printer) StmtForeach(n *ast.StmtForeach) {
p.printToken(n.AsTkn, []byte("as"))
p.printNode(n.Key)
p.printToken(n.DoubleArrowTkn, p.ifNode(n.Key, []byte("=>")))
p.printToken(n.AmpersandTkn, nil)
p.printNode(n.Var)
p.printToken(n.CloseParenthesisTkn, []byte(")"))
p.printToken(n.ColonTkn, nil)

View File

@ -3799,6 +3799,39 @@ func TestPrinterPrintStmtForeach(t *testing.T) {
}
}
func TestPrinterPrintStmtForeach_Reference(t *testing.T) {
o := bytes.NewBufferString("")
p := visitor.NewPrinter(o).WithState(visitor.PrinterStatePHP)
n := &ast.StmtForeach{
Expr: &ast.ExprVariable{
VarName: &ast.Identifier{Value: []byte("$a")},
},
Key: &ast.ExprVariable{
VarName: &ast.Identifier{Value: []byte("$k")},
},
AmpersandTkn: &token.Token{
Value: []byte("&"),
},
Var: &ast.ExprVariable{
VarName: &ast.Identifier{Value: []byte("$v")},
},
Stmt: &ast.StmtStmtList{
Stmts: []ast.Vertex{
&ast.StmtNop{},
},
},
}
n.Accept(p)
expected := `foreach($a as$k=>&$v){;}`
actual := o.String()
if expected != actual {
t.Errorf("\nexpected: %s\ngot: %s\n", expected, actual)
}
}
func TestPrinterPrintStmtFunction(t *testing.T) {
o := bytes.NewBufferString("")