fspl/analyzer/literal.go

298 lines
6.7 KiB
Go

package analyzer
import "unicode/utf16"
import "git.tebibyte.media/fspl/fspl/errors"
import "git.tebibyte.media/fspl/fspl/entity"
func (this *Tree) analyzeLiteralInt (
into entity.Type,
mode strictness,
literal *entity.LiteralInt,
) (
entity.Expression,
error,
) {
err := this.typeRestricted(literal.Position, into)
if err != nil { return nil, err }
if !isNumeric(into) {
return nil, errors.Errorf (
literal.Position, "cannot use integer literal as %v",
entity.FormatType(into))
}
if isInteger(into) && !inRange(into, int64(literal.Value)) {
return nil, errors.Errorf (
literal.Position, "integer literal out of range for type %v",
entity.FormatType(into))
}
literal.Ty = into
return literal, nil
}
func (this *Tree) analyzeLiteralFloat (
into entity.Type,
mode strictness,
literal *entity.LiteralFloat,
) (
entity.Expression,
error,
) {
err := this.typeRestricted(literal.Position, into)
if err != nil { return nil, err }
if !isFloat(into) {
return nil, errors.Errorf (
literal.Position, "cannot use float literal as %v",
entity.FormatType(into))
}
literal.Ty = into
return literal, nil
}
func (this *Tree) analyzeLiteralString (
into entity.Type,
mode strictness,
literal *entity.LiteralString,
) (
entity.Expression,
error,
) {
err := this.typeRestricted(literal.Position, into)
if err != nil { return nil, err }
base := ReduceToBase(into)
errCantUse := func () error {
return errors.Errorf (
literal.Position, "cannot use string literal as %v",
entity.FormatType(into))
}
fillUTF32 := func () {
literal.ValueUTF32 = []rune(literal.ValueUTF8)
}
fillUTF16 := func () {
literal.ValueUTF16 = utf16.Encode([]rune(literal.ValueUTF8))
}
switch base := base.(type) {
case *entity.TypeInt:
var length int; switch {
case base.Width >= 32:
fillUTF32()
length = len(literal.ValueUTF32)
case base.Width >= 16:
fillUTF16()
length = len(literal.ValueUTF16)
default:
length = len(literal.ValueUTF8)
}
if length > 1 {
return nil, errors.Errorf (
literal.Position,
"cannot fit string literal of length " +
"%v into %v",
length, entity.FormatType(into))
} else if length < 1 {
return nil, errors.Errorf (
literal.Position,
"string literal must have data when " +
"assigning to %v",
entity.FormatType(into))
}
case *entity.TypeArray:
element := ReduceToBase(base.Element)
if element, ok := element.(*entity.TypeInt); ok {
var length int; switch {
case element.Width >= 32:
fillUTF32()
length = len(literal.ValueUTF32)
case element.Width >= 16:
fillUTF16()
length = len(literal.ValueUTF16)
default:
length = len(literal.ValueUTF8)
}
if length > base.Length {
return nil, errors.Errorf (
literal.Position,
"cannot fit string literal of length " +
"%v into array of length %v",
length, base.Length)
}
} else {
return nil, errCantUse()
}
case *entity.TypeSlice:
element := ReduceToBase(base.Element)
if element, ok := element.(*entity.TypeInt); ok {
if element.Width >= 32 {
fillUTF32()
} else if element.Width >= 16 {
fillUTF16()
}
} else {
return nil, errCantUse()
}
case *entity.TypePointer:
referenced := ReduceToBase(base.Referenced)
if referenced, ok := referenced.(*entity.TypeInt); ok {
if referenced.Width != 8 {
return nil, errCantUse()
}
} else {
return nil, errCantUse()
}
default:
return nil, errCantUse()
}
literal.Ty = into
return literal, nil
}
func (this *Tree) analyzeLiteralArray (
into entity.Type,
mode strictness,
literal *entity.LiteralArray,
) (
entity.Expression,
error,
) {
err := this.typeRestricted(literal.Position, into)
if err != nil { return nil, err }
base := ReduceToBase(into)
var elementType entity.Type
switch base.(type) {
case *entity.TypeArray:
base := base.(*entity.TypeArray)
if base.Length < len(literal.Elements) {
return nil, errors.Errorf (
literal.Position, "expected %v elements or less",
base.Length)
}
elementType = base.Element
case *entity.TypeSlice:
base := base.(*entity.TypeSlice)
elementType = base.Element
case *entity.TypePointer:
base := base.(*entity.TypePointer)
elementType = base.Referenced
default:
return nil, errors.Errorf (
literal.Position, "cannot use array literal as %v",
entity.FormatType(into))
}
for index, element := range literal.Elements {
element, err := this.analyzeExpression(elementType, strict, element)
if err != nil { return nil, err }
literal.Elements[index] = element
}
literal.Ty = into
return literal, nil
}
func (this *Tree) analyzeLiteralStruct (
into entity.Type,
mode strictness,
literal *entity.LiteralStruct,
) (
entity.Expression,
error,
) {
err := this.typeRestricted(literal.Position, into)
if err != nil { return nil, err }
base, ok := ReduceToBase(into).(*entity.TypeStruct)
if !ok {
return nil, errors.Errorf (
literal.Position, "cannot use struct literal as %v",
entity.FormatType(into))
}
literal, err = this.assembleStructLiteralMap(literal)
if err != nil { return nil, err }
for name, member := range literal.MemberMap {
correct, ok := base.MemberMap[name]
if !ok {
return nil, errors.Errorf (
literal.Position, "no member %v.%s",
into, name)
}
value, err := this.analyzeExpression(correct.Type(), strict, member.Value)
if err != nil { return nil, err }
member.Value = value
}
literal.Ty = into
return literal, nil
}
func (this *Tree) analyzeLiteralBoolean (
into entity.Type,
mode strictness,
literal *entity.LiteralBoolean,
) (
entity.Expression,
error,
) {
err := this.typeRestricted(literal.Position, into)
if err != nil { return nil, err }
if !isBoolean(into) {
return nil, errors.Errorf (
literal.Position, "cannot use boolean literal as %v",
entity.FormatType(into))
}
literal.Ty = into
return literal, nil
}
func (this *Tree) analyzeLiteralNil (
into entity.Type,
mode strictness,
literal *entity.LiteralNil,
) (
entity.Expression,
error,
) {
err := this.typeRestricted(literal.Position, into)
if err != nil { return nil, err }
literal.Ty = into
return literal, nil
}
func (this *Tree) assembleStructLiteralMap (
literal *entity.LiteralStruct,
) (
*entity.LiteralStruct,
error,
) {
literal.MemberMap = make(map[string] *entity.Member)
literal.MemberOrder = make([]string, len(literal.Members))
for index, member := range literal.Members {
if previous, exists := literal.MemberMap[member.Name]; exists {
return literal, errors.Errorf (
member.Position, "%s already listed in struct literal at %v",
member.Name, previous.Position)
}
literal.MemberMap [member.Name] = member
literal.MemberOrder[index] = member.Name
literal.Members [index] = member
}
return literal, nil
}