fspl/generator/assignment.go

122 lines
3.8 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,
) {
source, err := this.generateExpression(from)
if err != nil { return nil, err }
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
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, source,
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
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, source,
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
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, source,
irToType, name)
fromBehavior, err := this.method(fromType.Name, name)
if err != nil { return nil, err }
this.blockManager.NewStore(fromBehavior, toBehaviorField)
}
}
// conversion from array to slice
case *entity.TypeSlice:
switch fromBase := analyzer.ReduceToBase(from.Type()).(type) {
case *entity.TypeArray:
return this.generateSlice(to, source, int64(fromBase.Length))
}
}
return source, nil
}