fspl/generator/assignment.go

283 lines
8.7 KiB
Go

package generator
import "fmt"
import "errors"
import "git.tebibyte.media/fspl/fspl/llvm"
import "git.tebibyte.media/fspl/fspl/entity"
import "git.tebibyte.media/fspl/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)
sourceType, 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)
key := entity.Key {
Unit: sourceType.Unit(),
Name: sourceType.Name,
Method: name,
}
fromBehavior, err := this.method(key)
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)
key := entity.Key {
Unit: sourceType.Type.Unit(),
Name: sourceType.Name,
Method: name,
}
fromBehavior, err := this.method(key)
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 union
case *entity.TypeUnion:
// only proceed if the types are different
if entity.TypesEqual(source.Type(), destType) { break }
irHashType := llvm.I64
// create destination union, if necessary
irDestType, err := this.generateType(destType)
if err != nil { return nil, err }
if !destinationSpecified {
irDestLoc = this.blockManager.newAllocaFront(irDestType)
}
destTypeFieldLoc := this.getUnionTypeFieldLoc(irDestLoc, irDestType)
destDataFieldLoc := this.getUnionDataFieldLoc(irDestLoc, irDestType)
// store type hash
hash := source.Type().Hash()
this.blockManager.NewStore (
llvm.NewConstInt(irHashType, int64(hash.Number())),
destTypeFieldLoc)
// store data
source, err := this.generateExpressionVal(source)
if err != nil { return nil, err }
this.blockManager.NewStore(source, destDataFieldLoc)
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:
irIndexType, err := this.generateTypeIndex()
if err != nil { return nil, err }
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(irIndexType, int64(sourceTypeBase.Length)),
destLengthField)
if destinationSpecified {
return nil, nil
} else {
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
}
}