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
|
||||
// when we know the assignment is a top-level expression
|
||||
if a, ok := n.Expr.(*assign.Assign); ok {
|
||||
return this.convertAssignment(a, true)
|
||||
return this.convertAssignAssign(a, true)
|
||||
}
|
||||
|
||||
// Non-assignment expression
|
||||
@ -909,7 +909,71 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
|
||||
//
|
||||
|
||||
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
|
||||
@ -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
|
||||
// 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
|
||||
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 {
|
||||
return "", parseErr{n, err}
|
||||
}
|
||||
@ -1526,14 +1594,23 @@ func (this *conversionState) resolveName(n node.Node) (string, error) {
|
||||
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 {
|
||||
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
|
||||
// Transform into append()
|
||||
arrayVar, err := this.convert(dimf.Variable)
|
||||
@ -1551,7 +1628,7 @@ func (this *conversionState) convertAssignment(n *assign.Assign, isTopLevelState
|
||||
|
||||
// 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 {
|
||||
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
|
||||
// we need to propagate errors
|
||||
switch n.Expression.(type) {
|
||||
switch nRValue.(type) {
|
||||
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 += "if err != nil {\n"
|
||||
ret += this.currentErrHandler
|
||||
@ -1577,7 +1662,7 @@ func (this *conversionState) convertAssignment(n *assign.Assign, isTopLevelState
|
||||
}
|
||||
|
||||
} 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