fspl/analyzer/type.go

202 lines
5.3 KiB
Go

package analyzer
import "fmt"
import "github.com/alecthomas/participle/v2"
import "github.com/alecthomas/participle/v2/lexer"
import "git.tebibyte.media/sashakoshka/fspl/entity"
func (this *Tree) analyzeTypedef (
pos lexer.Position,
name string,
acceptIncomplete bool,
) (
*entity.Typedef,
error,
) {
// return if exists already
if definition, exists := this.Types[name]; exists {
return definition, nil
}
// deal with incomplete types
if definition, incomplete := this. incompleteTypes[name]; incomplete {
if acceptIncomplete {
return definition, nil
} else {
return nil, participle.Errorf (
pos, "type %s cannot be used in this context",
name)
}
}
// error if type is missing
definition, exists := this.rawTypes[name]
if !exists {
return nil, participle.Errorf(pos, "no type named %s", name)
}
var err error
definition.Type, err = this.analyzeTypeInternal (
definition.Type, definition, false)
return definition, err
}
func (this *Tree) analyzeType (
ty entity.Type,
acceptIncomplete bool,
) (
entity.Type,
error,
) {
return this.analyzeTypeInternal (ty, nil, acceptIncomplete)
}
func (this *Tree) analyzeTypeInternal (
ty entity.Type,
root *entity.Typedef,
acceptIncomplete bool,
) (
entity.Type,
error,
) {
// edge cases
if ty == nil { return nil, nil }
var err error
// if we are analyzing a typedef, we will need to update incomplete type
// information as we go in order for recursive type definitions to work
updateIncompleteInfo := func () {
if root != nil { this.incompleteTypes[root.Name] = root }
}
updatePseudoCompleteInfo := func () {
if root != nil { this.Types[root.Name] = root }
}
if root != nil { defer delete(this.incompleteTypes, root.Name) }
// ---------
switch ty.(type) {
// named type
case *entity.TypeNamed:
ty := ty.(*entity.TypeNamed)
updateIncompleteInfo()
if primitive, isPrimitive := primitiveTypes[ty.Name]; isPrimitive {
return primitive, nil
}
var def *entity.Typedef
def, err = this.analyzeTypedef(ty.Pos, ty.Name, acceptIncomplete)
if err == nil {
ty.Type = def.Type
}
return ty, err
// pointer type
case *entity.TypePointer:
ty := ty.(*entity.TypePointer)
updateIncompleteInfo()
ty.Referenced, err = this.analyzeType(ty.Referenced, true)
return ty, err
// slice type
case *entity.TypeSlice:
ty := ty.(*entity.TypeSlice)
updateIncompleteInfo()
ty.Element, err = this.analyzeType(ty.Element, false)
return ty, err
// array type
case *entity.TypeArray:
ty := ty.(*entity.TypeArray)
updateIncompleteInfo()
if ty.Length < 1 {
return ty, participle.Errorf (
ty.Pos, "array length must be > 0")
}
ty.Element, err = this.analyzeType(ty.Element, false)
return ty, err
// struct type
case *entity.TypeStruct:
ty := ty.(*entity.TypeStruct)
ty, err = this.assembleStructMap(ty)
updateIncompleteInfo()
if err != nil { return ty, err }
for name, member := range ty.MemberMap {
ty.MemberMap[name].Ty,
err = this.analyzeType(member.Ty, false)
if err != nil { return ty, err }
}
return ty, nil
// interface type
case *entity.TypeInterface:
ty := ty.(*entity.TypeInterface)
ty, err = this.assembleInterfaceMap(ty)
updatePseudoCompleteInfo()
if err != nil { return ty, err }
for name, behavior := range ty.BehaviorMap {
ty.BehaviorMap[name], err = this.analyzeBehavior(behavior)
if err != nil { return ty, err }
}
return ty, nil
// integer type
case *entity.TypeInt:
ty := ty.(*entity.TypeInt)
updateIncompleteInfo()
if ty.Width < 1 {
return ty, participle.Errorf (
ty.Pos, "integer width must be > 0")
}
return ty, nil
// floating point and word types
case *entity.TypeFloat, *entity.TypeWord:
return ty, nil
default: panic(fmt.Sprint("BUG: analyzer doesnt know about type ", ty))
}
}
func (this *Tree) assembleStructMap (ty *entity.TypeStruct) (*entity.TypeStruct, error) {
ty.MemberMap = make(map[string] *entity.Declaration)
ty.MemberOrder = make([]string, len(ty.Members))
for index, member := range ty.Members {
if previous, exists := ty.MemberMap[member.Name]; exists {
return ty, participle.Errorf (
member.Pos, "%s already listed in struct at %v",
member.Name, previous.Pos)
}
ty.MemberMap [member.Name] = member
ty.MemberOrder[index] = member.Name
ty.Members [index] = member
}
return ty, nil
}
func (this *Tree) assembleInterfaceMap (ty *entity.TypeInterface) (*entity.TypeInterface, error) {
ty.BehaviorMap = make(map[string] *entity.Signature)
ty.BehaviorOrder = make([]string, len(ty.Behaviors))
for index, method := range ty.Behaviors {
if previous, exists := ty.BehaviorMap[method.Name]; exists {
return ty, participle.Errorf (
method.Pos, "%s already listed in interface at %v",
method.Name, previous.Pos)
}
ty.BehaviorMap [method.Name] = method
ty.BehaviorOrder[index] = method.Name
ty.Behaviors [index] = method
}
return ty, nil
}
func (this *Tree) analyzeBehavior (behavior *entity.Signature) (*entity.Signature, error) {
behavior, err := this.assembleSignatureMap(behavior)
if err != nil { return behavior, nil }
for name, argument := range behavior.ArgumentMap {
behavior.ArgumentMap[name].Ty, err = this.analyzeType(argument.Ty, false)
if err != nil { return behavior, err }
}
behavior.Return, err = this.analyzeType(behavior.Return, false)
return behavior, err
}