202 lines
5.3 KiB
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
|
|
}
|