stmt: track conversion state in a struct

This commit is contained in:
mappu 2020-04-05 18:45:14 +12:00
parent 2a00699b2f
commit 45b694434d
2 changed files with 84 additions and 80 deletions

View File

@ -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
}

161
node.go
View File

@ -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
}