add hoisting pass for preincrement rvalue expressions
This commit is contained in:
parent
abc031683e
commit
3f8377d9fe
17
fixtures/0015-hoist.php
Normal file
17
fixtures/0015-hoist.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function foo() {
|
||||||
|
|
||||||
|
$x = 0;
|
||||||
|
|
||||||
|
if (++$x >= 1) {
|
||||||
|
echo "OK in func";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$y = 0;
|
||||||
|
|
||||||
|
if (++$y >= 1) {
|
||||||
|
echo "OK in root";
|
||||||
|
}
|
135
hoistpass.go
Normal file
135
hoistpass.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/z7zmey/php-parser/node"
|
||||||
|
"github.com/z7zmey/php-parser/node/expr"
|
||||||
|
"github.com/z7zmey/php-parser/node/expr/binary"
|
||||||
|
"github.com/z7zmey/php-parser/node/stmt"
|
||||||
|
|
||||||
|
"php2go/parseutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type hoistPass struct {
|
||||||
|
mw *parseutil.MutatingWalker
|
||||||
|
nextHoist func(stmts ...node.Node) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *hoistPass) Enter(n_ *node.Node) error {
|
||||||
|
n := *n_ // not a copy
|
||||||
|
|
||||||
|
switch n := n.(type) {
|
||||||
|
case *stmt.StmtList:
|
||||||
|
return this.enterStmtList(&n.Stmts) // We can inject extra entries here
|
||||||
|
|
||||||
|
case *stmt.Function:
|
||||||
|
// We can inject extra entries here
|
||||||
|
// Note that we are NOT walking the other parts of this AST node
|
||||||
|
// (e.g. function parameters, function name, ...) since presumably they
|
||||||
|
// can't contain any hoistable statements
|
||||||
|
return this.enterStmtList(&n.Stmts)
|
||||||
|
|
||||||
|
case *node.Root:
|
||||||
|
return this.enterStmtList(&n.Stmts)
|
||||||
|
|
||||||
|
case *binary.BooleanAnd:
|
||||||
|
return this.enterShortCircuitingBinOp(&n.Left, &n.Right)
|
||||||
|
|
||||||
|
case *binary.BooleanOr:
|
||||||
|
return this.enterShortCircuitingBinOp(&n.Left, &n.Right)
|
||||||
|
|
||||||
|
case *binary.LogicalAnd:
|
||||||
|
return this.enterShortCircuitingBinOp(&n.Left, &n.Right)
|
||||||
|
|
||||||
|
case *binary.LogicalOr:
|
||||||
|
return this.enterShortCircuitingBinOp(&n.Left, &n.Right)
|
||||||
|
|
||||||
|
case *expr.PreInc:
|
||||||
|
// Hoist the whole increment expression up to statement context
|
||||||
|
// We also have to convert it to a post-increment
|
||||||
|
err := this.nextHoist(stmt.NewExpression(expr.NewPostInc(n.Variable)))
|
||||||
|
if err != nil {
|
||||||
|
return parseErr{n, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We also need to replace our own node with just the value itself
|
||||||
|
*n_ = n.Variable
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *hoistPass) enterShortCircuitingBinOp(left, right *node.Node) error {
|
||||||
|
// These wreck our ability to hoist, since we can't neccessarily just move
|
||||||
|
// the right-hand side operand out
|
||||||
|
// Although it's still safe to walk the left-hand side
|
||||||
|
err := this.mw.Walk(left)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
lastHoist := this.nextHoist
|
||||||
|
this.nextHoist = func(stmts ...node.Node) error {
|
||||||
|
return parseErr{*right, fmt.Errorf("Can't determine how to hoist expression on right-hand side of && expression")}
|
||||||
|
}
|
||||||
|
err = this.mw.Walk(right)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nextHoist = lastHoist
|
||||||
|
|
||||||
|
return parseutil.ErrDoNotWalkDeeper // since we already did it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *hoistPass) enterStmtList(stmtList *[]node.Node) error {
|
||||||
|
|
||||||
|
// We can inject extra entries here
|
||||||
|
// In order to do so, we need to perform the interior Walk ourselves
|
||||||
|
// so that we know exactly where to put the extra entries
|
||||||
|
|
||||||
|
// Take stmtList by pointer so we can replace the slice outright
|
||||||
|
|
||||||
|
anyMutations := false
|
||||||
|
lastHoist := this.nextHoist
|
||||||
|
|
||||||
|
ret := make([]node.Node, 0, len(*stmtList))
|
||||||
|
|
||||||
|
this.nextHoist = func(stmts ...node.Node) error {
|
||||||
|
anyMutations = true
|
||||||
|
ret = append(ret, stmts...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, _ := range *stmtList {
|
||||||
|
err := this.mw.Walk(&(*stmtList)[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ret = append(ret, (*stmtList)[i]) // *after* the hoisted statements
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nextHoist = lastHoist
|
||||||
|
|
||||||
|
if anyMutations {
|
||||||
|
*stmtList = ret // our modified copy
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseutil.ErrDoNotWalkDeeper // since we already did it
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHoistPass(root *node.Node) error {
|
||||||
|
hp := hoistPass{}
|
||||||
|
|
||||||
|
hp.mw = &parseutil.MutatingWalker{
|
||||||
|
EnterNode: hp.Enter,
|
||||||
|
LeaveNode: parseutil.MutatingWalkerNoop,
|
||||||
|
}
|
||||||
|
hp.nextHoist = func(stmts ...node.Node) error {
|
||||||
|
return fmt.Errorf("nowhere available to hoist %d statements", len(stmts))
|
||||||
|
}
|
||||||
|
|
||||||
|
return hp.mw.Walk(root)
|
||||||
|
}
|
6
main.go
6
main.go
@ -59,6 +59,12 @@ func ConvertFile(filename string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pass 1.5: Hoist some expressions out of rvalue contexts
|
||||||
|
err = runHoistPass(&n)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
// Debug pass: Walk and print JSON...
|
// Debug pass: Walk and print JSON...
|
||||||
if fh, err := os.OpenFile(filename+`.parse2.json`, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644); err == nil {
|
if fh, err := os.OpenFile(filename+`.parse2.json`, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644); err == nil {
|
||||||
v := visitor.NewPrettyJsonDumper(fh, namespaces)
|
v := visitor.NewPrettyJsonDumper(fh, namespaces)
|
||||||
|
Loading…
Reference in New Issue
Block a user