diff --git a/main.go b/main.go index a8aec8e..61c5d6a 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ func ConvertFile(filename string) (string, error) { namespaces := visitor.NewNamespaceResolver() // scope := NewScope() + state := conversionState{} p, err := parser.NewParser([]byte(inputFile), "7.4") if err != nil { @@ -40,7 +41,7 @@ func ConvertFile(filename string) (string, error) { } // Walk and print (converted) - ret, err := convert(p.GetRootNode()) + ret, err := state.convert(p.GetRootNode()) if err != nil { return "", err } diff --git a/node.go b/node.go index 1e43391..6c4ab85 100644 --- a/node.go +++ b/node.go @@ -36,7 +36,10 @@ func (pe parseErr) Unwrap() error { // -func convert(n_ node.Node) (string, error) { +type conversionState struct { +} + +func (this *conversionState) convert(n_ node.Node) (string, error) { switch n := n_.(type) { // @@ -50,7 +53,7 @@ func convert(n_ node.Node) (string, error) { statements := []string{} for _, s := range n.Stmts { - sm, err := convert(s) + sm, err := this.convert(s) if err != nil { return "", parseErr{s, err} } @@ -91,7 +94,7 @@ func convert(n_ node.Node) (string, error) { ret := "{\n" // new variable scope for _, s := range n.Stmts { - line, err := convert(s) + line, err := this.convert(s) if err != nil { return "", parseErr{s, err} } @@ -167,7 +170,7 @@ func convert(n_ node.Node) (string, error) { allStmts = append(allStmts, Literal{`return this, nil`}) // Method body - funcStmt, err := convertFunctionCommon(s.Params, returnType, true /* always use ptr return */, allStmts) + funcStmt, err := this.convertFunctionCommon(s.Params, returnType, true /* always use ptr return */, allStmts) if err != nil { return "", parseErr{s, err} } @@ -183,7 +186,7 @@ func convert(n_ node.Node) (string, error) { } // Method body - funcStmt, err := convertFunctionCommon(s.Params, s.ReturnType, s.ReturnsRef, s.Stmt.(*stmt.StmtList).Stmts) + funcStmt, err := this.convertFunctionCommon(s.Params, s.ReturnType, s.ReturnsRef, s.Stmt.(*stmt.StmtList).Stmts) if err != nil { return "", parseErr{s, err} } @@ -223,7 +226,7 @@ func convert(n_ node.Node) (string, error) { funcName = toPublic(funcName) // Convert body - funcStmt, err := convertFunctionCommon(n.Params, n.ReturnType, n.ReturnsRef, n.Stmts) + funcStmt, err := this.convertFunctionCommon(n.Params, n.ReturnType, n.ReturnsRef, n.Stmts) if err != nil { return "", parseErr{n, err} } @@ -232,7 +235,7 @@ func convert(n_ node.Node) (string, error) { return ret, nil case *stmt.Return: - child, err := convert(n.Expr) + child, err := this.convert(n.Expr) if err != nil { return "", parseErr{n, err} } @@ -252,7 +255,7 @@ func convert(n_ node.Node) (string, error) { return "return nil, errors.New(" + str.Value + ")\n", nil } - child, err := convert(n.Expr) + child, err := this.convert(n.Expr) if err != nil { return "", parseErr{n, err} } @@ -268,7 +271,7 @@ func convert(n_ node.Node) (string, error) { // No initialiser in loop } else if len(n.Init) == 1 { - finit, err = convert(n.Init[0]) + finit, err = this.convert(n.Init[0]) if err != nil { return "", parseErr{n, err} } @@ -279,7 +282,7 @@ func convert(n_ node.Node) (string, error) { // it may cause an extra local variable after the loop that may result // in type mismatch (can be fixed by using an extra scope). for _, initStmt := range n.Init { - singleInitStmt, err := convert(initStmt) + singleInitStmt, err := this.convert(initStmt) if err != nil { return "", parseErr{initStmt, err} } @@ -291,7 +294,7 @@ func convert(n_ node.Node) (string, error) { if len(n.Cond) != 1 { return "", parseErr{n, fmt.Errorf("for loop can only have 1 cond clause, found %d", len(n.Cond))} } - fcond, err := convert(n.Cond[0]) + fcond, err := this.convert(n.Cond[0]) if err != nil { return "", parseErr{n, err} } @@ -308,12 +311,12 @@ func convert(n_ node.Node) (string, error) { loopStmt = expr.NewPostDec(predec.Variable) } - floop, err := convert(loopStmt) + floop, err := this.convert(loopStmt) if err != nil { return "", parseErr{n, err} } - body, err := convert(convertToStmtList(n.Stmt)) + body, err := this.convert(convertToStmtList(n.Stmt)) if err != nil { return "", parseErr{n, err} } @@ -321,25 +324,25 @@ func convert(n_ node.Node) (string, error) { return preinit + "for " + finit + "; " + fcond + "; " + floop + " " + body + "\n", nil case *stmt.Foreach: - iterand, err := convert(n.Expr) + iterand, err := this.convert(n.Expr) if err != nil { return "", parseErr{n, err} } - valueReceiver, err := convert(n.Variable) + valueReceiver, err := this.convert(n.Variable) if err != nil { return "", parseErr{n, err} } keyReceiver := `_` if n.Key != nil { - keyReceiver, err = convert(n.Key) + keyReceiver, err = this.convert(n.Key) if err != nil { return "", parseErr{n, err} } } - body, err := convert(convertToStmtList(n.Stmt)) + body, err := this.convert(convertToStmtList(n.Stmt)) if err != nil { return "", parseErr{n, err} } @@ -347,12 +350,12 @@ func convert(n_ node.Node) (string, error) { return "for " + keyReceiver + ", " + valueReceiver + " := range " + iterand + " " + body + "\n", nil case *stmt.While: - cond, err := convert(n.Cond) + cond, err := this.convert(n.Cond) if err != nil { return "", parseErr{n, err} } - body, err := convert(convertToStmtList(n.Stmt)) + body, err := this.convert(convertToStmtList(n.Stmt)) if err != nil { return "", parseErr{n, err} } @@ -360,7 +363,7 @@ func convert(n_ node.Node) (string, error) { return "for " + cond + " " + body + "\n", nil case *stmt.Do: - cond, err := convert(n.Cond) + cond, err := this.convert(n.Cond) if err != nil { return "", parseErr{n, err} } @@ -368,7 +371,7 @@ func convert(n_ node.Node) (string, error) { bodyStmts := convertToStmtList(n.Stmt) bodyStmts.Stmts = append(bodyStmts.Stmts, Literal{"if " + cond + "{\nbreak\n}"}) - body, err := convert(bodyStmts) + body, err := this.convert(bodyStmts) if err != nil { return "", parseErr{n, err} } @@ -376,7 +379,7 @@ func convert(n_ node.Node) (string, error) { return "for " + cond + " " + body + "\n", nil case *stmt.Expression: - child, err := convert(n.Expr) + child, err := this.convert(n.Expr) if err != nil { return "", parseErr{n, err} } @@ -388,7 +391,7 @@ func convert(n_ node.Node) (string, error) { // Convert into fmt.Print args := make([]string, 0, len(n.Exprs)) for _, expr := range n.Exprs { - exprGo, err := convert(expr) + exprGo, err := this.convert(expr) if err != nil { return "", parseErr{n, err} } @@ -420,7 +423,7 @@ func convert(n_ node.Node) (string, error) { case *assign.Assign: - rvalue, err := convert(n.Expression) + rvalue, err := this.convert(n.Expression) if err != nil { return "", parseErr{n, err} } @@ -428,7 +431,7 @@ func convert(n_ node.Node) (string, error) { if dimf, ok := n.Variable.(*expr.ArrayDimFetch); ok && dimf.Dim == nil { // Special handling for the case of foo[] = bar // Transform into append() - arrayVar, err := convert(dimf.Variable) + arrayVar, err := this.convert(dimf.Variable) if err != nil { return "", parseErr{dimf, err} } @@ -439,7 +442,7 @@ func convert(n_ node.Node) (string, error) { // Normal assignment - lvalue, err := convert(n.Variable) // might be a more complicated lvalue + lvalue, err := this.convert(n.Variable) // might be a more complicated lvalue if err != nil { return "", parseErr{n, err} } @@ -461,7 +464,7 @@ func convert(n_ node.Node) (string, error) { return "", parseErr{n, err} } - callParams, err := convertFuncCallArgsCommon(n.ArgumentList) + callParams, err := this.convertFuncCallArgsCommon(n.ArgumentList) if err != nil { return "", parseErr{n, err} } @@ -474,12 +477,12 @@ func convert(n_ node.Node) (string, error) { return "", parseErr{n, err} } - funcName, err := convert(n.Call) + funcName, err := this.convert(n.Call) if err != nil { return "", parseErr{n, err} } - callParams, err := convertFuncCallArgsCommon(n.ArgumentList) + callParams, err := this.convertFuncCallArgsCommon(n.ArgumentList) if err != nil { return "", parseErr{n, err} } @@ -500,11 +503,11 @@ func convert(n_ node.Node) (string, error) { // Convert resolved back to node.Name transparentNameNode := name.NewName([]node.Node{name.NewNamePart(nn)}) - return convert(expr.NewFunctionCall(transparentNameNode, n.ArgumentList)) + return this.convert(expr.NewFunctionCall(transparentNameNode, n.ArgumentList)) case *expr.PreInc: // """In Go, i++ is a statement, not an expression. So you can't use its value in another expression such as a function call.""" - v, err := convert(n.Variable) + v, err := this.convert(n.Variable) if err != nil { return "", parseErr{n, err} } @@ -513,7 +516,7 @@ func convert(n_ node.Node) (string, error) { case *expr.PostInc: // """In Go, i++ is a statement, not an expression. So you can't use its value in another expression such as a function call.""" - v, err := convert(n.Variable) + v, err := this.convert(n.Variable) if err != nil { return "", parseErr{n, err} } @@ -522,17 +525,17 @@ func convert(n_ node.Node) (string, error) { case *expr.MethodCall: // Foo->Bar(Baz) - parent, err := convert(n.Variable) + parent, err := this.convert(n.Variable) if err != nil { return "", parseErr{n, err} } - child, err := convert(n.Method) + child, err := this.convert(n.Method) if err != nil { return "", parseErr{n, err} } - args, err := convertFuncCallArgsCommon(n.ArgumentList) + args, err := this.convertFuncCallArgsCommon(n.ArgumentList) if err != nil { return "", parseErr{n, err} } @@ -541,12 +544,12 @@ func convert(n_ node.Node) (string, error) { case *expr.PropertyFetch: // Foo->Bar - parent, err := convert(n.Variable) + parent, err := this.convert(n.Variable) if err != nil { return "", parseErr{n, err} } - child, err := convert(n.Property) + child, err := this.convert(n.Property) if err != nil { return "", parseErr{n, err} } @@ -560,16 +563,16 @@ func convert(n_ node.Node) (string, error) { return resolveName(n.Constant) case *expr.Array: - return convertArrayLiteralCommon(n.Items) + return this.convertArrayLiteralCommon(n.Items) case *expr.ShortArray: - return convertArrayLiteralCommon(n.Items) + return this.convertArrayLiteralCommon(n.Items) case *expr.ArrayDimFetch: // Might be x[foo], might be x[] (i.e. append() call) // In order to make the append() transformation, we need to lookahead // for ArrayDimFetch in the `*assign.Assign` case - vv, err := convert(n.Variable) + vv, err := this.convert(n.Variable) if err != nil { return "", parseErr{n, err} } @@ -578,7 +581,7 @@ func convert(n_ node.Node) (string, error) { return "", parseErr{n, fmt.Errorf("found '%s[]' outside of lvalue assignment context", vv)} } - idx, err := convert(n.Dim) + idx, err := this.convert(n.Dim) if err != nil { return "", parseErr{n, err} } @@ -590,40 +593,40 @@ func convert(n_ node.Node) (string, error) { // case *binary.BitwiseAnd: - return convertBinaryCommon(n.Left, n.Right, `&`) + return this.convertBinaryCommon(n.Left, n.Right, `&`) case *binary.BitwiseOr: - return convertBinaryCommon(n.Left, n.Right, `|`) + return this.convertBinaryCommon(n.Left, n.Right, `|`) case *binary.BitwiseXor: - return convertBinaryCommon(n.Left, n.Right, `^`) // n.b. Go only supports this for integers; PHP also supports it for bools + return this.convertBinaryCommon(n.Left, n.Right, `^`) // n.b. Go only supports this for integers; PHP also supports it for bools case *binary.BooleanAnd: - return convertBinaryCommon(n.Left, n.Right, `&&`) + return this.convertBinaryCommon(n.Left, n.Right, `&&`) case *binary.BooleanOr: - return convertBinaryCommon(n.Left, n.Right, `||`) + return this.convertBinaryCommon(n.Left, n.Right, `||`) //case *binary.Coalesce: // TODO this can't be expressed in an rvalue context in Go (unless we create a typed closure..?) case *binary.Concat: - return convertBinaryCommon(n.Left, n.Right, `+`) // PHP uses + for numbers, `.` for strings; Go uses `+` in both cases + return this.convertBinaryCommon(n.Left, n.Right, `+`) // PHP uses + for numbers, `.` for strings; Go uses `+` in both cases case *binary.Div: - return convertBinaryCommon(n.Left, n.Right, `/`) // PHP will upgrade ints to floats, Go won't + return this.convertBinaryCommon(n.Left, n.Right, `/`) // PHP will upgrade ints to floats, Go won't case *binary.Equal: - return convertBinaryCommon(n.Left, n.Right, `==`) // Type-lax equality comparator + return this.convertBinaryCommon(n.Left, n.Right, `==`) // Type-lax equality comparator case *binary.GreaterOrEqual: - return convertBinaryCommon(n.Left, n.Right, `>=`) + return this.convertBinaryCommon(n.Left, n.Right, `>=`) case *binary.Greater: - return convertBinaryCommon(n.Left, n.Right, `>`) + return this.convertBinaryCommon(n.Left, n.Right, `>`) case *binary.Identical: - return convertBinaryCommon(n.Left, n.Right, `==`) // PHP uses `===`, Go is already type-safe + return this.convertBinaryCommon(n.Left, n.Right, `==`) // PHP uses `===`, Go is already type-safe case *binary.LogicalAnd: // This is the lexer token when using `and` in PHP. It's equivalent to @@ -632,27 +635,27 @@ func convert(n_ node.Node) (string, error) { // $a = $b and $c ==> ($a = $b) and $c // So far, we are relying on the PHP parser having already having handled // the precedence difference - transform to `&&` unconditionally - return convertBinaryCommon(n.Left, n.Right, `&&`) + return this.convertBinaryCommon(n.Left, n.Right, `&&`) case *binary.LogicalOr: // As above - return convertBinaryCommon(n.Left, n.Right, `||`) + return this.convertBinaryCommon(n.Left, n.Right, `||`) case *binary.LogicalXor: // As above - return convertBinaryCommon(n.Left, n.Right, `^`) // n.b. Go only supports this for integers; PHP also supports it for bools + return this.convertBinaryCommon(n.Left, n.Right, `^`) // n.b. Go only supports this for integers; PHP also supports it for bools case *binary.Minus: - return convertBinaryCommon(n.Left, n.Right, `-`) + return this.convertBinaryCommon(n.Left, n.Right, `-`) case *binary.Mod: // Go doesn't have a built-in operator for mod - convert to a call to math.Mod() - rval, err := convert(n.Left) + rval, err := this.convert(n.Left) if err != nil { return "", parseErr{n, err} } - modulo, err := convert(n.Right) + modulo, err := this.convert(n.Right) if err != nil { return "", parseErr{n, err} } @@ -660,26 +663,26 @@ func convert(n_ node.Node) (string, error) { return `math.Mod(` + rval + `, ` + modulo + `)`, nil case *binary.Mul: - return convertBinaryCommon(n.Left, n.Right, `*`) + return this.convertBinaryCommon(n.Left, n.Right, `*`) case *binary.NotEqual: - return convertBinaryCommon(n.Left, n.Right, `!=`) // Type-lax equality comparator + return this.convertBinaryCommon(n.Left, n.Right, `!=`) // Type-lax equality comparator case *binary.NotIdentical: - return convertBinaryCommon(n.Left, n.Right, `!=`) // PHP uses `!==`, Go is already type-safe + return this.convertBinaryCommon(n.Left, n.Right, `!=`) // PHP uses `!==`, Go is already type-safe case *binary.Plus: - return convertBinaryCommon(n.Left, n.Right, `+`) // PHP uses + for numbers, `.` for strings; Go uses `+` in both cases + return this.convertBinaryCommon(n.Left, n.Right, `+`) // PHP uses + for numbers, `.` for strings; Go uses `+` in both cases case *binary.Pow: // Go doesn't have a built-in operator for mod - convert to a call to math.Pow() - base, err := convert(n.Left) + base, err := this.convert(n.Left) if err != nil { return "", parseErr{n, err} } - exponent, err := convert(n.Right) + exponent, err := this.convert(n.Right) if err != nil { return "", parseErr{n, err} } @@ -687,16 +690,16 @@ func convert(n_ node.Node) (string, error) { return `math.Pow(` + base + `, ` + exponent + `)`, nil case *binary.ShiftLeft: - return convertBinaryCommon(n.Left, n.Right, `<<`) + return this.convertBinaryCommon(n.Left, n.Right, `<<`) case *binary.ShiftRight: - return convertBinaryCommon(n.Left, n.Right, `>>`) + return this.convertBinaryCommon(n.Left, n.Right, `>>`) case *binary.SmallerOrEqual: - return convertBinaryCommon(n.Left, n.Right, `<=`) + return this.convertBinaryCommon(n.Left, n.Right, `<=`) case *binary.Smaller: - return convertBinaryCommon(n.Left, n.Right, `<`) + return this.convertBinaryCommon(n.Left, n.Right, `<`) case *binary.Spaceship: // The spaceship operator returns -1 / 0 / 1 based on a gteq/leq comparison @@ -704,7 +707,7 @@ func convert(n_ node.Node) (string, error) { // The primary use case is in user-definded sort comparators, where Go // uses bools instead ints anyway. // Subtraction is a reasonable substitute - return convertBinaryCommon(n.Left, n.Right, `-`) + return this.convertBinaryCommon(n.Left, n.Right, `-`) // // scalar @@ -820,15 +823,15 @@ func convertToStmtList(n node.Node) *stmt.StmtList { return stmt.NewStmtList([]node.Node{n}) } -func convertBinaryCommon(left, right node.Node, goBinaryOperator string) (string, error) { +func (this *conversionState) convertBinaryCommon(left, right node.Node, goBinaryOperator string) (string, error) { // PHP uses + for numbers, `.` for strings; Go uses `+` in both cases // Assume PHP/Go have the same associativity here - lhs, err := convert(left) + lhs, err := this.convert(left) if err != nil { return "", parseErr{left, err} } - rhs, err := convert(right) + rhs, err := this.convert(right) if err != nil { return "", parseErr{right, err} } @@ -836,7 +839,7 @@ func convertBinaryCommon(left, right node.Node, goBinaryOperator string) (string return "(" + lhs + " " + goBinaryOperator + " " + rhs + ")", nil } -func convertFuncCallArgsCommon(args *node.ArgumentList) (string, error) { +func (this *conversionState) convertFuncCallArgsCommon(args *node.ArgumentList) (string, error) { callParams := make([]string, 0, len(args.Arguments)) for _, arg_ := range args.Arguments { @@ -845,7 +848,7 @@ func convertFuncCallArgsCommon(args *node.ArgumentList) (string, error) { return "", parseErr{arg_, fmt.Errorf("expected node.Argument")} } - rvalue, err := convert(arg.Expr) + rvalue, err := this.convert(arg.Expr) if err != nil { return "", parseErr{arg, err} } @@ -862,7 +865,7 @@ func convertFuncCallArgsCommon(args *node.ArgumentList) (string, error) { return "(" + strings.Join(callParams, `, `) + ")", nil // expr only, no semicolon/newline } -func convertArrayLiteralCommon(items []node.Node) (string, error) { +func (this *conversionState) convertArrayLiteralCommon(items []node.Node) (string, error) { // Array literal // We need to know the type. See if we can guess it from the first child element // At least, we may be able to determine if this is a map or an array @@ -886,13 +889,13 @@ func convertArrayLiteralCommon(items []node.Node) (string, error) { } } - vv, err := convert(itm.Val) + vv, err := this.convert(itm.Val) if err != nil { return "", parseErr{itm, err} } if itm.Key != nil { - kv, err := convert(itm.Key) + kv, err := this.convert(itm.Key) if err != nil { return "", parseErr{itm, err} } @@ -910,7 +913,7 @@ func convertArrayLiteralCommon(items []node.Node) (string, error) { } } -func convertFunctionCommon(params []node.Node, returnType node.Node, returnsRef bool, bodyStmts []node.Node) (string, error) { +func (this *conversionState) convertFunctionCommon(params []node.Node, returnType node.Node, returnsRef bool, bodyStmts []node.Node) (string, error) { // TODO scan function and see if it contains any return statements at all // If not, then we only need an err return parameter, not anything else @@ -954,7 +957,7 @@ func convertFunctionCommon(params []node.Node, returnType node.Node, returnsRef // Recurse through body statements - fullBody, err := convert(stmt.NewStmtList(bodyStmts)) + fullBody, err := this.convert(stmt.NewStmtList(bodyStmts)) if err != nil { return "", err }