fspl/generator/literal.go

280 lines
9.3 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) generateLiteralInt (literal *entity.LiteralInt) (llvm.Value, error) {
irType, err := this.generateType(analyzer.ReduceToBase(literal.Type()))
if err != nil { return nil, err }
base := analyzer.ReduceToBase(literal.Type())
switch base.(type) {
case *entity.TypeInt, *entity.TypeWord:
return llvm.NewConstInt(irType.(*llvm.TypeInt), int64(literal.Value)), nil
case *entity.TypeFloat:
return llvm.NewConstFloat(irType.(*llvm.TypeFloat), float64(literal.Value)), nil
default:
return nil, errors.New(fmt.Sprintln("int can't be used as", base))
}
}
func (this *generator) generateLiteralFloat (literal *entity.LiteralFloat) (llvm.Value, error) {
irType, err := this.generateType(analyzer.ReduceToBase(literal.Type()))
if err != nil { return nil, err }
return llvm.NewConstFloat(irType.(*llvm.TypeFloat), literal.Value), nil
}
// generateLiteralArrayLoc generates an array literal. irDestLoc specifies the
// location to assign the data to. If it is nil, this function will allocate a
// destination (if necessary) and return its value as a register.
func (this *generator) generateLiteralArrayLoc (literal *entity.LiteralArray, irDestLoc llvm.Value) (llvm.Value, error) {
destinationSpecified := irDestLoc != nil
makeDataType := func (elementType entity.Type, nullTerminator bool) (llvm.Type, int, error) {
length := len(literal.Elements)
if nullTerminator { length += 1 }
irDataType, err := this.generateType(&entity.TypeArray {
Element: elementType,
Length: length,
})
return irDataType, length, err
}
populateData := func (elementType entity.Type, irDestLoc llvm.Value, length int, nullTerminator bool) error {
irDataType, _, err := makeDataType(elementType, nullTerminator)
if err != nil { return err }
irElementType, err := this.generateType(elementType)
if err != nil { return err }
for index := 0; index < length; index ++ {
elementPointer := this.blockManager.NewGetElementPtr (
irDataType, irDestLoc,
llvm.NewConstInt(llvm.I32, 0),
llvm.NewConstInt(llvm.I32, int64(index)))
if index >= len(literal.Elements) {
this.blockManager.NewStore (
llvm.NewConstZeroInitializer(irElementType),
elementPointer)
} else {
_, err := this.generateAssignmentToDestination (
literal.Elements[index],
elementType,
elementPointer)
if err != nil { return err }
}
}
return nil
}
irDestType, err := this.generateType(literal.Type())
if err != nil { return nil, err }
if !destinationSpecified {
irDestLoc = this.blockManager.newAllocaFront(irDestType)
}
base := analyzer.ReduceToBase(literal.Type())
switch base := base.(type) {
case *entity.TypeArray:
populateData(base.Element, irDestLoc, base.Length, false)
case *entity.TypeSlice:
irDataType, length, err := makeDataType(base.Element, false)
if err != nil { return nil, err }
destDataLoc := this.blockManager.newAllocaFront(irDataType)
err = populateData(base.Element, destDataLoc, length, false)
if err != nil { return nil, err }
this.sliceSetData(literal.Type(), irDestLoc, destDataLoc)
this.sliceSetDefinedLength(literal.Type(), irDestLoc, int64(len(literal.Elements)))
case *entity.TypePointer:
irDataType, length, err := makeDataType(base.Referenced, true)
if err != nil { return nil, err }
destDataLoc := this.blockManager.newAllocaFront(irDataType)
err = populateData(base.Referenced, destDataLoc, length, true)
if err != nil { return nil, err }
this.blockManager.NewStore(destDataLoc, irDestLoc)
default:
return nil, errors.New(fmt.Sprintln("array can't be used as ", base))
}
if destinationSpecified {
return nil, nil
} else {
return irDestLoc, nil
}
}
func (this *generator) generateLiteralStringLoc (literal *entity.LiteralString, irDestLoc llvm.Value) (llvm.Value, error) {
destinationSpecified := irDestLoc != nil
makeDataType := func (anyElementType entity.Type, nullTerminator bool) (llvm.Type, int, error) {
elementType, ok := analyzer.ReduceToBase(anyElementType).(*entity.TypeInt)
if !ok {
return nil, 0, errors.New(fmt.Sprintln (
"string can't be used with element type",
anyElementType))
}
var length int; switch {
case elementType.Width >= 32: length = len(literal.ValueUTF32)
case elementType.Width >= 16: length = len(literal.ValueUTF16)
default: length = len(literal.ValueUTF8)
}
if nullTerminator { length += 1 }
irDataType, err := this.generateType(&entity.TypeArray {
Element: anyElementType,
Length: length,
})
return irDataType, length, err
}
populateData := func (anyElementType entity.Type, irDestLoc llvm.Value, length int, nullTerminator bool) error {
elementType, ok := analyzer.ReduceToBase(anyElementType).(*entity.TypeInt)
if !ok {
return errors.New(fmt.Sprintln (
"string can't be used with element type",
anyElementType))
}
irArrayType, _, err := makeDataType(anyElementType, nullTerminator)
if err != nil { return err }
for index := 0; index < length; index ++ {
var element llvm.Value; switch {
case elementType.Width >= 32:
if index >= len(literal.ValueUTF32) {
element = llvm.NewConstZeroInitializer(llvm.I32)
} else {
element = llvm.NewConstInt(llvm.I32, int64(literal.ValueUTF32[index]))
}
case elementType.Width >= 16:
if index >= len(literal.ValueUTF16) {
element = llvm.NewConstZeroInitializer(llvm.I16)
} else {
element = llvm.NewConstInt(llvm.I16, int64(literal.ValueUTF16[index]))
}
default:
if index >= len(literal.ValueUTF8) {
element = llvm.NewConstZeroInitializer(llvm.I8)
} else {
element = llvm.NewConstInt(llvm.I8, int64(literal.ValueUTF8[index]))
}
}
elementPointer := this.blockManager.NewGetElementPtr (
irArrayType, irDestLoc,
llvm.NewConstInt(llvm.I32, 0),
llvm.NewConstInt(llvm.I32, int64(index)))
this.blockManager.NewStore(element, elementPointer)
}
return nil
}
irDestType, err := this.generateType(literal.Type())
if err != nil { return nil, err }
if !destinationSpecified {
irDestLoc = this.blockManager.newAllocaFront(irDestType)
}
base := analyzer.ReduceToBase(literal.Type())
switch base := base.(type) {
case *entity.TypeInt:
var value llvm.Value; switch {
case base.Width >= 32:
value = llvm.NewConstInt(llvm.I32, int64(literal.ValueUTF32[0]))
case base.Width >= 16:
value = llvm.NewConstInt(llvm.I16, int64(literal.ValueUTF16[0]))
default:
value = llvm.NewConstInt(llvm.I8, int64(literal.ValueUTF8[0]))
}
this.blockManager.NewStore(value, irDestLoc)
case *entity.TypeArray:
err := populateData(base.Element, irDestLoc, base.Length, false)
if err != nil { return nil, err }
case *entity.TypeSlice:
irDataType, length, err := makeDataType(base.Element, false)
if err != nil { return nil, err }
destDataLoc := this.blockManager.newAllocaFront(irDataType)
err = populateData(base.Element, destDataLoc, length, false)
if err != nil { return nil, err }
this.sliceSetData(literal.Type(), irDestLoc, destDataLoc)
this.sliceSetDefinedLength(literal.Type(), irDestLoc, int64(length))
case *entity.TypePointer:
irDataType, length, err := makeDataType(base.Referenced, true)
if err != nil { return nil, err }
destDataLoc := this.blockManager.newAllocaFront(irDataType)
err = populateData(base.Referenced, destDataLoc, length, true)
if err != nil { return nil, err }
this.blockManager.NewStore(destDataLoc, irDestLoc)
default:
return nil, errors.New(fmt.Sprintln("string can't be used as ", base))
}
if destinationSpecified {
return nil, nil
} else {
return irDestLoc, nil
}
}
func (this *generator) generateLiteralStructLoc (literal *entity.LiteralStruct, irDestLoc llvm.Value) (llvm.Value, error) {
destinationSpecified := irDestLoc != nil
base, ok := analyzer.ReduceToBase(literal.Type()).(*entity.TypeStruct)
if !ok { return nil, errors.New(fmt.Sprintln("struct can't be used as ", literal.Type())) }
irDestType, err := this.generateType(literal.Type())
if err != nil { return nil, err }
if !destinationSpecified {
irDestLoc = this.blockManager.newAllocaFront(irDestType)
}
for index, member := range base.Members {
elementPointer := this.blockManager.NewGetElementPtr (
irDestType, irDestLoc,
llvm.NewConstInt(llvm.I32, 0),
llvm.NewConstInt(llvm.I32, int64(index)))
if pair, ok := literal.MemberMap[member.Name]; ok {
_, err = this.generateAssignmentToDestination (
pair.Value,
member.Type(),
elementPointer)
if err != nil { return nil, err }
} else {
irMemberType, err := this.generateType(member.Type())
if err != nil { return nil, err }
this.blockManager.NewStore (
llvm.NewConstZeroInitializer(irMemberType),
elementPointer)
}
}
if destinationSpecified {
return nil, nil
} else {
return irDestLoc, nil
}
}
func (this *generator) generateLiteralBoolean (literal *entity.LiteralBoolean) (llvm.Value, error) {
return llvm.NewConstBool(bool(literal.Value)), nil
}
func (this *generator) generateLiteralNil (literal *entity.LiteralNil) (llvm.Value, error) {
ty, err := this.generateType(literal.Type())
if err != nil { return nil, err }
return llvm.NewConstZeroInitializer(ty), nil
}