diff --git a/main.go b/main.go index 3d65166..2b4450a 100644 --- a/main.go +++ b/main.go @@ -33,25 +33,38 @@ func ConvertFile(filename string) (string, error) { // Enable comments extraction p.WithFreeFloating() + // Parse PHP content p.Parse() for _, err := range p.GetErrors() { return "", errors.New(err.String()) } + n := p.GetRootNode() - // Walk and print JSON... + // Debug pass: Walk and print JSON... if fh, err := os.OpenFile(filename+`.parse.json`, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644); err == nil { v := visitor.NewPrettyJsonDumper(fh, namespaces) - p.GetRootNode().Walk(v) + n.Walk(v) fh.Close() } - // Walk and print (converted) - ret, err := state.convert(p.GetRootNode()) + // Pass 1: Normalise Alt** Stmt types + normPass := normaliseAltsPass{} + n.Walk(&normPass) // can't modify root node, but that's fine + + // 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 { + v := visitor.NewPrettyJsonDumper(fh, namespaces) + n.Walk(v) + fh.Close() + } + + // Pass 2: Walk and print (converted) + ret, err := state.convert(n) if err != nil { return "", err } - // Gofmt output + // Pass 3: Gofmt output // TODO pass flags to get -s/-r equivalent for more agressive simplification formatted, err := format.Source([]byte(ret)) if err != nil { @@ -59,6 +72,7 @@ func ConvertFile(filename string) (string, error) { return ret, nil } + // Done return string(formatted), nil } diff --git a/normalisealts.go b/normalisealts.go new file mode 100644 index 0000000..db719c2 --- /dev/null +++ b/normalisealts.go @@ -0,0 +1,101 @@ +package main + +import ( + "fmt" + //"io/ioutil" + + "github.com/z7zmey/php-parser/node" + "github.com/z7zmey/php-parser/node/stmt" + "github.com/z7zmey/php-parser/walker" +) + +type normaliseAltsPass struct { +} + +func (np *normaliseAltsPass) transformNonRecursive(n node.Node) (node.Node, bool) { + + switch n := n.(type) { + case *stmt.AltIf: + // Build replacement node + ifStmt := stmt.NewIf(n.Cond, n.Stmt, n.ElseIf, n.Else) + ifStmt.FreeFloating = n.FreeFloating + ifStmt.Position = n.Position + // ifStmt has no .Attributes prop + return ifStmt, true + + case *stmt.AltElse: + elseStmt := stmt.NewElse(n.Stmt) + elseStmt.FreeFloating = n.FreeFloating + elseStmt.Position = n.Position + return elseStmt, true + + case *stmt.AltElseIf: + elifStmt := stmt.NewElseIf(n.Cond, n.Stmt) + elifStmt.FreeFloating = n.FreeFloating + elifStmt.Position = n.Position + return elifStmt, true + + default: + return n, false // no change + } +} + +func (np *normaliseAltsPass) walkMutateArray(stmts []node.Node) { + for i, _ := range stmts { + np.walkMutate(&stmts[i]) + } +} + +func (np *normaliseAltsPass) walkMutate(child *node.Node) { + + v2, didChange := np.transformNonRecursive(*child) + if didChange { + v2.Walk(np) + *child = v2 + } else { + (*child).Walk(np) + } +} + +func (np *normaliseAltsPass) EnterNode(w walker.Walkable) bool { + n, ok := w.(node.Node) + if !ok { + panic(fmt.Errorf("Tried to walk non-node '%t'", w)) + } + + // We need to override the .Walk() behaviour for all possible *parents* + // of affected nodes. This is because the .Walk() interface does not give + // us any opportunity to override single elements by the time we get there + // Our custom Walk() implementation is not firing all EnterChildList/LeaveNode + // visitors - but - that's fine since they're all noops anyway + + switch n := n.(type) { + case *stmt.StmtList: + np.walkMutateArray(n.Stmts) // mutate slice in place + return false // Signal not to use upstream .Walk() implementation + + case *node.Root: + np.walkMutateArray(n.Stmts) // mutate slice in place + return false // Signal not to use upstream .Walk() implementation + + case *stmt.AltIf: + panic("altif") + np.walkMutate(&n.Cond) + np.walkMutateArray(n.ElseIf) + np.walkMutate(&n.Else) + return false + + default: + // No normalisation needed for this node type + // Recurse deeper + return true + } +} + +func (np *normaliseAltsPass) LeaveNode(w walker.Walkable) {} +func (np *normaliseAltsPass) EnterChildNode(key string, w walker.Walkable) {} +func (np *normaliseAltsPass) LeaveChildNode(key string, w walker.Walkable) {} +func (np *normaliseAltsPass) EnterChildList(key string, w walker.Walkable) {} +func (np *normaliseAltsPass) LeaveChildList(key string, w walker.Walkable) {} + +var _ walker.Visitor = &normaliseAltsPass{} // interface assertion