298 lines
6.8 KiB
Go
298 lines
6.8 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.typeOpaque(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.typeOpaque(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.typeOpaque(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.typeOpaque(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.typeOpaque(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.typeOpaque(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.typeOpaque(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
|
|
}
|