diff --git a/README.md b/README.md index b88acf6..b7b7efc 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ The goal is to produce idiomatic, maintainable Go code as part of a one-off conv [ ] Assignment expressions - Go doesn't support assignment in rvalues, only as a statement - When walking below stmt level, need to first fully walk and check for any function calls + assignments that may need hoisting (and we can probably only do that correctly if there is no short-circuiting) + - [X] Add subtree walk + catch error for this case + - [ ] Add hoisting pass [ ] Closures - [ ] Handle value/reference captures [ ] Sort callbacks diff --git a/miniwalker.go b/miniwalker.go new file mode 100644 index 0000000..5aaf623 --- /dev/null +++ b/miniwalker.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + + "github.com/z7zmey/php-parser/node" + "github.com/z7zmey/php-parser/walker" +) + +type miniWalker struct { + cb func(node.Node) error + lastErr error +} + +func (mw *miniWalker) EnterNode(w walker.Walkable) bool { + n, ok := w.(node.Node) + if !ok { + mw.lastErr = fmt.Errorf("Tried to walk non-node '%t'", w) + return false + } + + err := mw.cb(n) + if err != nil { + mw.lastErr = err + return false + } + + return true +} + +func (mw *miniWalker) LeaveNode(w walker.Walkable) {} +func (mw *miniWalker) EnterChildNode(key string, w walker.Walkable) {} +func (mw *miniWalker) LeaveChildNode(key string, w walker.Walkable) {} +func (mw *miniWalker) EnterChildList(key string, w walker.Walkable) {} +func (mw *miniWalker) LeaveChildList(key string, w walker.Walkable) {} + +func walk(n node.Node, cb func(node.Node) error) error { + mw := miniWalker{cb: cb} + n.Walk(&mw) + + return mw.lastErr +} diff --git a/node.go b/node.go index 6db7e2a..dc67dd1 100644 --- a/node.go +++ b/node.go @@ -511,6 +511,15 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error) return "fmt.Print(" + quoted + ")\n", nil // newline - standalone statement case *stmt.If: + + hasCondAssign, err := hasInteriorAssignment(n.Cond) + if err != nil { + return "", parseErr{n, err} + } + if hasCondAssign { + return "", parseErr{n.Cond, fmt.Errorf("please remove assignment from if-expression")} + } + cond, err := this.convert(n.Cond) if err != nil { return "", parseErr{n, err} @@ -1158,3 +1167,17 @@ func (this *conversionState) convertFunctionCommon(params []node.Node, returnTyp // No extra trailing newline in case this is part of a large expression return ret, nil } + +// hasInteriorAssignment recursively walks a node, to determine if it contains +// any assignment expressions +func hasInteriorAssignment(n node.Node) (hasAnyAssign bool, err error) { + + err = walk(n, func(n node.Node) error { + if _, ok := n.(*assign.Assign); ok { + hasAnyAssign = true + } + return nil + }) + + return // named return +}