132 lines
4.3 KiB
Go
132 lines
4.3 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) {
|
|
source, err := this.generateAssignmentSource (
|
|
assignment.Value,
|
|
assignment.Location.Type())
|
|
if err != nil { return nil, err }
|
|
destination, err := this.generateExpressionLoc(assignment.Location)
|
|
if err != nil { return nil, err }
|
|
this.blockManager.NewStore(source, destination)
|
|
return nil, nil
|
|
}
|
|
|
|
// generateAssignmentSource performs type coercions if necessary, mainly
|
|
// interface assignment. this should be called instead of generateExpression
|
|
// when the type to assign to is known.
|
|
func (this *generator) generateAssignmentSource (
|
|
from entity.Expression,
|
|
to entity.Type,
|
|
) (
|
|
llvm.Value,
|
|
error,
|
|
) {
|
|
toBase := analyzer.ReduceToBase(to)
|
|
|
|
switch toBase := toBase.(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
|
|
irToType, err := this.generateType(to)
|
|
if err != nil { return nil, err }
|
|
destination := this.blockManager.newAllocaFront(irToType)
|
|
toDataField := this.getInterfaceData(destination, irToType)
|
|
|
|
fromType := from.Type()
|
|
switch fromBase := analyzer.ReduceToBase(fromType).(type) {
|
|
// conversion from interface to interface
|
|
case *entity.TypeInterface:
|
|
// re-assign data
|
|
source, err := this.generateExpressionLoc(from)
|
|
if err != nil { return nil, err }
|
|
irFromType, err := this.generateType(fromType)
|
|
if err != nil { return nil, err }
|
|
fromDataField := this.getInterfaceData(source, irFromType)
|
|
fromData := this.blockManager.NewLoad(new(llvm.TypePointer), fromDataField)
|
|
this.blockManager.NewStore(fromData, toDataField)
|
|
|
|
// re-assign behaviors
|
|
for _, name := range toBase.BehaviorOrder {
|
|
fromBehaviorField := this.getInterfaceBehavior (
|
|
fromBase, source,
|
|
irFromType, name)
|
|
toBehaviorField := this.getInterfaceBehavior (
|
|
toBase, destination,
|
|
irToType, 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(from)
|
|
if err != nil { return nil, err }
|
|
fromData := this.blockManager.NewLoad(new(llvm.TypePointer), source)
|
|
this.blockManager.NewStore(fromData, toDataField)
|
|
|
|
fromReferenced, ok := fromBase.Referenced.(*entity.TypeNamed)
|
|
if !ok {
|
|
return nil, errors.New(fmt.Sprint(
|
|
"can't assign", fromBase, "to interface"))
|
|
}
|
|
|
|
// assign behaviors
|
|
for _, name := range toBase.BehaviorOrder {
|
|
toBehaviorField := this.getInterfaceBehavior (
|
|
toBase, destination,
|
|
irToType, name)
|
|
fromBehavior, err := this.method(fromReferenced.Name, name)
|
|
if err != nil { return nil, err }
|
|
this.blockManager.NewStore(fromBehavior, toBehaviorField)
|
|
}
|
|
|
|
// conversion from other type to interface
|
|
default:
|
|
// assign data
|
|
source, err := this.generateExpressionLoc(from)
|
|
if err != nil { return nil, err }
|
|
this.blockManager.NewStore(source, toDataField)
|
|
|
|
fromType, ok := fromType.(*entity.TypeNamed)
|
|
if !ok {
|
|
return nil, errors.New(fmt.Sprint(
|
|
"can't assign", fromBase, "to interface"))
|
|
}
|
|
|
|
// assign behaviors
|
|
for _, name := range toBase.BehaviorOrder {
|
|
toBehaviorField := this.getInterfaceBehavior (
|
|
toBase, destination,
|
|
irToType, name)
|
|
fromBehavior, err := this.method(fromType.Name, name)
|
|
if err != nil { return nil, err }
|
|
this.blockManager.NewStore(fromBehavior, toBehaviorField)
|
|
}
|
|
}
|
|
return this.blockManager.NewLoad(irToType, destination), nil
|
|
|
|
// conversion from array to slice
|
|
case *entity.TypeSlice:
|
|
switch fromBase := analyzer.ReduceToBase(from.Type()).(type) {
|
|
case *entity.TypeArray:
|
|
source, err := this.generateExpressionVal(from)
|
|
if err != nil { return nil, err }
|
|
return this.generateSliceDefinedLength(to, source, int64(fromBase.Length))
|
|
}
|
|
}
|
|
|
|
source, err := this.generateExpressionVal(from)
|
|
if err != nil { return nil, err }
|
|
return source, nil
|
|
}
|