stmt/for: hoist multiple initial conditions from 3-clause for loops

This commit is contained in:
mappu 2020-04-05 16:43:27 +12:00
parent 7dbb523763
commit 90ce6af6dd
2 changed files with 27 additions and 7 deletions

View File

@ -19,3 +19,7 @@ for($i = 0; $i < 3; ++$i) {
// Loop with no separate body statement
while (true) echo "hello";
// Loop with multiple initialiser conditions
for ($i = 0, $e = 3; $i < $e; ++$i) {
}

30
node.go
View File

@ -233,16 +233,32 @@ func convert(n_ node.Node) (string, error) {
return "return nil, " + child + "\n", nil
case *stmt.For:
if len(n.Init) != 1 {
var preinit, finit string
var err error = nil
if len(n.Init) == 0 {
// No initialiser in loop
} else if len(n.Init) == 1 {
finit, err = convert(n.Init[0])
if err != nil {
return "", parseErr{n, err}
}
} else {
// We can handle the case of multiple init statements by hoisting them
// above the loop. There is no negative impact on PHP scoping rules, but
// it may cause an extra local variable after the loop that may result
// in type mismatch (can be fixed by using an extra scope).
return "", parseErr{n, fmt.Errorf("for loop can only have 1 init clause, found %d", len(n.Init))}
}
finit, err := convert(n.Init[0])
if err != nil {
return "", parseErr{n, err}
for _, initStmt := range n.Init {
singleInitStmt, err := convert(initStmt)
if err != nil {
return "", parseErr{initStmt, err}
}
preinit += singleInitStmt + "\n"
}
}
if len(n.Cond) != 1 {
@ -275,7 +291,7 @@ func convert(n_ node.Node) (string, error) {
return "", parseErr{n, err}
}
return "for " + finit + "; " + fcond + "; " + floop + " " + body + "\n", nil
return preinit + "for " + finit + "; " + fcond + "; " + floop + " " + body + "\n", nil
case *stmt.Foreach:
iterand, err := convert(n.Expr)