236 lines
7.4 KiB
Go
236 lines
7.4 KiB
Go
package generator
|
|
|
|
import "fmt"
|
|
import "errors"
|
|
import "git.tebibyte.media/sashakoshka/fspl/llvm"
|
|
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
|
import "git.tebibyte.media/sashakoshka/fspl/analyzer"
|
|
|
|
func (this *generator) generateAssignment (assignment *entity.Assignment) (llvm.Value, error) {
|
|
destination, err := this.generateExpressionLoc(assignment.Location)
|
|
if err != nil { return nil, err }
|
|
|
|
return this.generateAssignmentToDestination (
|
|
assignment.Value,
|
|
assignment.Location.Type(),
|
|
destination)
|
|
}
|
|
|
|
// TODO possibly break generateAssignmentToDestination into several routines,
|
|
// each handling one destination type
|
|
|
|
// generateAssignmentToDestination performs type coercions if necessary, mainly
|
|
// interface assignment. This should be called when the user is assigning a
|
|
// value to something via an assignment statement, a function/method call, or a
|
|
// field in a composite literal.
|
|
// irDestLoc specifies the location to assign the source to. If it is nil, this
|
|
// function will allocate a destination (if necessary) and return its value as a
|
|
// register.
|
|
func (this *generator) generateAssignmentToDestination ( // TODO: add -Val suffix
|
|
source entity.Expression,
|
|
destType entity.Type,
|
|
irDestLoc llvm.Value,
|
|
) (
|
|
llvm.Value,
|
|
error,
|
|
) {
|
|
destinationSpecified := irDestLoc != nil
|
|
destTypeBase := analyzer.ReduceToBase(destType)
|
|
|
|
switch destTypeBase := destTypeBase.(type) {
|
|
// conversion from any type to interface
|
|
case *entity.TypeInterface:
|
|
// methods defined on interface or pointer types cannot pass
|
|
// through an interface!
|
|
|
|
// create destination interface, if necessary
|
|
irDestType, err := this.generateType(destType)
|
|
if err != nil { return nil, err }
|
|
if !destinationSpecified {
|
|
irDestLoc = this.blockManager.newAllocaFront(irDestType)
|
|
}
|
|
destDataFieldLoc := this.getInterfaceDataFieldLoc(irDestLoc, irDestType)
|
|
|
|
sourceType := source.Type()
|
|
switch sourceTypeBase := analyzer.ReduceToBase(sourceType).(type) {
|
|
// conversion from interface to interface
|
|
case *entity.TypeInterface:
|
|
// re-assign data
|
|
source, err := this.generateExpressionLoc(source)
|
|
if err != nil { return nil, err }
|
|
irFromType, err := this.generateType(sourceType)
|
|
if err != nil { return nil, err }
|
|
sourceDataFieldLoc := this.getInterfaceDataFieldLoc(source, irFromType)
|
|
sourceDataLoc := this.blockManager.NewLoad(new(llvm.TypePointer), sourceDataFieldLoc)
|
|
this.blockManager.NewStore(sourceDataLoc, destDataFieldLoc)
|
|
|
|
// re-assign behaviors
|
|
for _, name := range destTypeBase.BehaviorOrder {
|
|
fromBehaviorField := this.getInterfaceBehaviorFieldLoc (
|
|
sourceTypeBase, source,
|
|
irFromType, name)
|
|
toBehaviorField := this.getInterfaceBehaviorFieldLoc (
|
|
destTypeBase, irDestLoc,
|
|
irDestType, name)
|
|
fromBehavior := this.blockManager.NewLoad(new(llvm.TypePointer), fromBehaviorField)
|
|
this.blockManager.NewStore(fromBehavior, toBehaviorField)
|
|
}
|
|
|
|
// conversion from pointer to interface
|
|
case *entity.TypePointer:
|
|
// assign data
|
|
source, err := this.generateExpressionVal(source)
|
|
if err != nil { return nil, err }
|
|
sourceDataLoc := this.blockManager.NewLoad(new(llvm.TypePointer), source)
|
|
this.blockManager.NewStore(sourceDataLoc, destDataFieldLoc)
|
|
|
|
fromReferenced, ok := sourceTypeBase.Referenced.(*entity.TypeNamed)
|
|
if !ok {
|
|
return nil, errors.New(fmt.Sprint(
|
|
"can't assign", sourceTypeBase, "to interface"))
|
|
}
|
|
|
|
// assign behaviors
|
|
for _, name := range destTypeBase.BehaviorOrder {
|
|
toBehaviorFieldAddress := this.getInterfaceBehaviorFieldLoc (
|
|
destTypeBase, irDestLoc,
|
|
irDestType, name)
|
|
fromBehavior, err := this.method(fromReferenced.Name, name)
|
|
if err != nil { return nil, err }
|
|
this.blockManager.NewStore(fromBehavior, toBehaviorFieldAddress)
|
|
}
|
|
|
|
// conversion from other type to interface
|
|
default:
|
|
// assign data
|
|
sourceLoc, err := this.generateExpressionLoc(source)
|
|
if err != nil { return nil, err }
|
|
this.blockManager.NewStore(sourceLoc, destDataFieldLoc)
|
|
|
|
sourceType, ok := sourceType.(*entity.TypeNamed)
|
|
if !ok {
|
|
return nil, errors.New(fmt.Sprint(
|
|
"can't assign", sourceType, "to interface"))
|
|
}
|
|
|
|
// assign behaviors
|
|
for _, name := range destTypeBase.BehaviorOrder {
|
|
toBehaviorFieldAddress := this.getInterfaceBehaviorFieldLoc (
|
|
destTypeBase, irDestLoc,
|
|
irDestType, name)
|
|
fromBehavior, err := this.method(sourceType.Name, name)
|
|
if err != nil { return nil, err }
|
|
this.blockManager.NewStore(fromBehavior, toBehaviorFieldAddress)
|
|
}
|
|
}
|
|
if destinationSpecified {
|
|
return nil, nil
|
|
} else {
|
|
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
|
}
|
|
|
|
// conversion from any type to pointer
|
|
case *entity.TypePointer:
|
|
// assignments
|
|
switch source := source.(type) {
|
|
// assignment from array literal to pointer
|
|
case *entity.LiteralArray:
|
|
if destinationSpecified {
|
|
_, err := this.generateLiteralArrayLoc(source, irDestLoc)
|
|
return nil, err
|
|
}
|
|
|
|
// assignment from string literal to pointer
|
|
case *entity.LiteralString:
|
|
if destinationSpecified {
|
|
_, err := this.generateLiteralStringLoc(source, irDestLoc)
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// conversion from any type to slice
|
|
case *entity.TypeSlice:
|
|
// conversions
|
|
switch sourceTypeBase := analyzer.ReduceToBase(source.Type()).(type) {
|
|
// conversion from array to slice
|
|
case *entity.TypeArray:
|
|
array, err := this.generateExpressionLoc(source)
|
|
if err != nil { return nil, err }
|
|
irDestType, err := this.generateType(destType)
|
|
if err != nil { return nil, err }
|
|
|
|
if !destinationSpecified {
|
|
irDestLoc = this.blockManager.newAllocaFront(irDestType)
|
|
}
|
|
destDataField := this.getSliceDataFieldLoc(irDestLoc, irDestType)
|
|
destLengthField := this.getSliceLengthFieldLoc(irDestLoc, irDestType)
|
|
this.blockManager.NewStore(array, destDataField)
|
|
this.blockManager.NewStore (
|
|
llvm.NewConstInt(llvm.I32, int64(sourceTypeBase.Length)),
|
|
destLengthField)
|
|
|
|
if !destinationSpecified {
|
|
return this.blockManager.NewLoad(irDestType, irDestLoc), nil
|
|
}
|
|
}
|
|
|
|
// assignments
|
|
switch source := source.(type) {
|
|
// assignment from array literal to slice
|
|
case *entity.LiteralArray:
|
|
if destinationSpecified {
|
|
_, err := this.generateLiteralArrayLoc(source, irDestLoc)
|
|
return nil, err
|
|
}
|
|
|
|
// assignment from string literal to slice
|
|
case *entity.LiteralString:
|
|
if destinationSpecified {
|
|
_, err := this.generateLiteralStringLoc(source, irDestLoc)
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// conversion from any type to array
|
|
case *entity.TypeArray:
|
|
// assignments
|
|
switch source := source.(type) {
|
|
// assignment from array literal to array
|
|
case *entity.LiteralArray:
|
|
if destinationSpecified {
|
|
_, err := this.generateLiteralArrayLoc(source, irDestLoc)
|
|
return nil, err
|
|
}
|
|
|
|
// assignment from string literal to array
|
|
case *entity.LiteralString:
|
|
if destinationSpecified {
|
|
_, err := this.generateLiteralStringLoc(source, irDestLoc)
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// conversion from any type to struct
|
|
case *entity.TypeStruct:
|
|
// assignments
|
|
switch source := source.(type) {
|
|
// assignment from struct literal to struct
|
|
case *entity.LiteralStruct:
|
|
if destinationSpecified {
|
|
_, err := this.generateLiteralStructLoc(source, irDestLoc)
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
|
|
irSource, err := this.generateExpressionVal(source)
|
|
if err != nil { return nil, err }
|
|
|
|
if destinationSpecified {
|
|
this.blockManager.NewStore(irSource, irDestLoc)
|
|
return nil, nil
|
|
} else {
|
|
return irSource, nil
|
|
}
|
|
}
|