node: implement shorthand assignment operators, callable rvalues
This commit is contained in:
parent
86266ae676
commit
b2ef112f50
103
node.go
103
node.go
@ -644,7 +644,7 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
|
|||||||
// Assignment expressions can take on better error-handling behaviour
|
// Assignment expressions can take on better error-handling behaviour
|
||||||
// when we know the assignment is a top-level expression
|
// when we know the assignment is a top-level expression
|
||||||
if a, ok := n.Expr.(*assign.Assign); ok {
|
if a, ok := n.Expr.(*assign.Assign); ok {
|
||||||
return this.convertAssignment(a, true)
|
return this.convertAssignAssign(a, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-assignment expression
|
// Non-assignment expression
|
||||||
@ -909,7 +909,71 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
|
|||||||
//
|
//
|
||||||
|
|
||||||
case *assign.Assign:
|
case *assign.Assign:
|
||||||
return this.convertAssignment(n, false /* no reason to believe we are a top-level statement */)
|
return this.convertAssignAssign(n, false /* no reason to believe we are a top-level statement */)
|
||||||
|
|
||||||
|
// Go has a limited set of assignment shorthands:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// assign_op = [ add_op | mul_op ] "=" .
|
||||||
|
//
|
||||||
|
// [...]
|
||||||
|
//
|
||||||
|
// add_op = "+" | "-" | "|" | "^" .
|
||||||
|
// mul_op = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .
|
||||||
|
// ```
|
||||||
|
|
||||||
|
case *assign.BitwiseAnd:
|
||||||
|
return this.convertAssignment(n, n.Variable, n.Expression, false, `&=`)
|
||||||
|
|
||||||
|
case *assign.BitwiseOr:
|
||||||
|
return this.convertAssignment(n, n.Variable, n.Expression, false, `|=`)
|
||||||
|
|
||||||
|
case *assign.BitwiseXor:
|
||||||
|
return this.convertAssignment(n, n.Variable, n.Expression, false, `^=`)
|
||||||
|
|
||||||
|
case *assign.Coalesce:
|
||||||
|
// Null coaslecing assignment expression - $x ??= $y;
|
||||||
|
// We don't have a natural equivalent for the coalescing operator
|
||||||
|
// We don't have an implementation of *binary.Coalesce yet either
|
||||||
|
// But when we do this will need an rvalue transformation to use it
|
||||||
|
|
||||||
|
return this.convert(assign.NewAssign(n.Variable, binary.NewCoalesce(n.Variable, n.Expression))) // FIXME also handle position/freefloating
|
||||||
|
|
||||||
|
case *assign.Concat:
|
||||||
|
return this.convertAssignment(n, n.Variable, n.Expression, false, `+=`)
|
||||||
|
|
||||||
|
case *assign.Div:
|
||||||
|
return this.convertAssignment(n, n.Variable, n.Expression, false, `/=`)
|
||||||
|
|
||||||
|
case *assign.Minus:
|
||||||
|
return this.convertAssignment(n, n.Variable, n.Expression, false, `-=`)
|
||||||
|
|
||||||
|
//case *assign.Mod:
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
case *assign.Mul:
|
||||||
|
return this.convertAssignment(n, n.Variable, n.Expression, false, `*=`)
|
||||||
|
|
||||||
|
case *assign.Plus:
|
||||||
|
return this.convertAssignment(n, n.Variable, n.Expression, false, `+=`)
|
||||||
|
|
||||||
|
//case *assign.Pow:
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
//case *assign.Reference:
|
||||||
|
|
||||||
|
case *assign.ShiftLeft:
|
||||||
|
return this.convertAssignment(n, n.Variable, n.Expression, false, `<<=`)
|
||||||
|
|
||||||
|
case *assign.ShiftRight:
|
||||||
|
return this.convertAssignment(n, n.Variable, n.Expression, false, `>>=`)
|
||||||
|
|
||||||
|
//
|
||||||
|
// name
|
||||||
|
//
|
||||||
|
|
||||||
|
case *name.Name:
|
||||||
|
return this.resolveName(n)
|
||||||
|
|
||||||
//
|
//
|
||||||
// expr
|
// expr
|
||||||
@ -919,7 +983,11 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
|
|||||||
// All our generated functions return err, but this AST node may be in a single-rvalue context
|
// All our generated functions return err, but this AST node may be in a single-rvalue context
|
||||||
// TODO do something more intelligent here
|
// TODO do something more intelligent here
|
||||||
// We can't necessarily hoist the whole call, in case we are on the right-hand side of a && operator
|
// We can't necessarily hoist the whole call, in case we are on the right-hand side of a && operator
|
||||||
funcName, err := this.resolveName(n.Function)
|
|
||||||
|
// We might be calling by name, or, we might be calling by an rvalue expression
|
||||||
|
// e.g. foo()()() or $x()
|
||||||
|
// Use full conversion logic for name resolution
|
||||||
|
funcName, err := this.convert(n.Function)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", parseErr{n, err}
|
return "", parseErr{n, err}
|
||||||
}
|
}
|
||||||
@ -1526,14 +1594,23 @@ func (this *conversionState) resolveName(n node.Node) (string, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *conversionState) convertAssignment(n *assign.Assign, isTopLevelStatement bool) (string, error) {
|
func (this *conversionState) convertAssignAssign(n *assign.Assign, isTopLevelStatement bool) (string, error) {
|
||||||
|
return this.convertAssignment(n, n.Variable, n.Expression, isTopLevelStatement, `=`)
|
||||||
|
}
|
||||||
|
|
||||||
rvalue, err := this.convert(n.Expression)
|
func (this *conversionState) convertAssignment(n, nLValue, nRValue node.Node, isTopLevelStatement bool, operator string) (string, error) {
|
||||||
|
|
||||||
|
rvalue, err := this.convert(nRValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", parseErr{n, err}
|
return "", parseErr{n, err}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dimf, ok := n.Variable.(*expr.ArrayDimFetch); ok && dimf.Dim == nil {
|
if dimf, ok := nLValue.(*expr.ArrayDimFetch); ok && dimf.Dim == nil {
|
||||||
|
|
||||||
|
if operator != `=` {
|
||||||
|
return "", parseErr{n, fmt.Errorf("can't yet handle complex shorthand append assignment operator statement")}
|
||||||
|
}
|
||||||
|
|
||||||
// Special handling for the case of foo[] = bar
|
// Special handling for the case of foo[] = bar
|
||||||
// Transform into append()
|
// Transform into append()
|
||||||
arrayVar, err := this.convert(dimf.Variable)
|
arrayVar, err := this.convert(dimf.Variable)
|
||||||
@ -1551,7 +1628,7 @@ func (this *conversionState) convertAssignment(n *assign.Assign, isTopLevelState
|
|||||||
|
|
||||||
// Normal assignment
|
// Normal assignment
|
||||||
|
|
||||||
lvalue, err := this.convert(n.Variable) // might be a more complicated lvalue
|
lvalue, err := this.convert(nLValue) // might be a more complicated lvalue
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", parseErr{n, err}
|
return "", parseErr{n, err}
|
||||||
}
|
}
|
||||||
@ -1563,8 +1640,16 @@ func (this *conversionState) convertAssignment(n *assign.Assign, isTopLevelState
|
|||||||
|
|
||||||
// If this is a simple expression (func call; indirect/method call; assignment of func/method call) then
|
// If this is a simple expression (func call; indirect/method call; assignment of func/method call) then
|
||||||
// we need to propagate errors
|
// we need to propagate errors
|
||||||
switch n.Expression.(type) {
|
switch nRValue.(type) {
|
||||||
case *expr.FunctionCall, *expr.StaticCall, *expr.New:
|
case *expr.FunctionCall, *expr.StaticCall, *expr.New:
|
||||||
|
|
||||||
|
if operator != `=` {
|
||||||
|
// We need to perform the variable assignment + err check, but we are in shorthand assignment context
|
||||||
|
// and can't just mess with the left hand of the expression
|
||||||
|
// Need to create a temporary accumulator variable
|
||||||
|
return "", parseErr{n, fmt.Errorf("can't yet handle err value extraction for function call in shorthand append assignment operator statement")}
|
||||||
|
}
|
||||||
|
|
||||||
ret := lvalue + ", err = " + rvalue + "\n"
|
ret := lvalue + ", err = " + rvalue + "\n"
|
||||||
ret += "if err != nil {\n"
|
ret += "if err != nil {\n"
|
||||||
ret += this.currentErrHandler
|
ret += this.currentErrHandler
|
||||||
@ -1577,7 +1662,7 @@ func (this *conversionState) convertAssignment(n *assign.Assign, isTopLevelState
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return lvalue + " = " + rvalue, nil // Just the basic assignment - without trailing NL
|
return lvalue + " " + operator + " " + rvalue, nil // Just the basic assignment - without trailing NL
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user