387 lines
10 KiB
Go
387 lines
10 KiB
Go
package analyzer
|
|
|
|
import "fmt"
|
|
import "git.tebibyte.media/fspl/fspl/errors"
|
|
import "git.tebibyte.media/fspl/fspl/entity"
|
|
|
|
func (this *Tree) analyzeTypedef (
|
|
pos errors.Position,
|
|
key entity.Key,
|
|
acceptIncomplete bool,
|
|
) (
|
|
*entity.Typedef,
|
|
error,
|
|
) {
|
|
// return a builtin if it exists (search types with zero uuid)
|
|
if definition, exists := this.Types[entity.Key { Name: key.Name }]; exists {
|
|
return definition, nil
|
|
}
|
|
|
|
// return if exists already
|
|
if definition, exists := this.Types[key]; exists {
|
|
return definition, nil
|
|
}
|
|
|
|
// check if incomplete
|
|
if definition, incomplete := this. incompleteTypes[key]; incomplete {
|
|
if acceptIncomplete {
|
|
return definition, nil
|
|
} else {
|
|
return nil, errors.Errorf (
|
|
pos, "type %s cannot be used in this context",
|
|
key.Name)
|
|
}
|
|
}
|
|
|
|
// check if it is even real
|
|
definition, exists := this.rawTypes[key]
|
|
if !exists {
|
|
return nil, errors.Errorf(pos, "no type named %s", key.Name)
|
|
}
|
|
|
|
// update unit
|
|
definition.Unt = key.Unit
|
|
|
|
// analyze
|
|
var err error
|
|
definition.Type, err = this.analyzeTypeInternal (
|
|
definition.Type, definition, false)
|
|
|
|
// assemble constant map
|
|
definition, err = this.assembleTypedefConstantMap(definition)
|
|
if err != nil { return nil, err }
|
|
|
|
// analyze constants
|
|
fillerValue := 0
|
|
for index, constant := range definition.Constants {
|
|
constant, err := this.analyzeConstantDeclaration(constant, definition.Type)
|
|
if err != nil { return nil, err }
|
|
if constant.Value == nil {
|
|
if !isNumeric(definition.Type) {
|
|
return nil, errors.Errorf (
|
|
constant.Position(),
|
|
"cannot fill in constant value for non-numeric type %v",
|
|
definition.Name)
|
|
}
|
|
constant.Value = &entity.LiteralInt {
|
|
Pos: pos,
|
|
Value: fillerValue,
|
|
Ty: definition.Type,
|
|
}
|
|
fillerValue ++
|
|
}
|
|
|
|
definition.ConstantMap[constant.Name] = constant
|
|
definition.ConstantOrder[index] = constant.Name
|
|
definition.Constants[index] = constant
|
|
}
|
|
|
|
return definition, err
|
|
}
|
|
|
|
func (this *Tree) analyzeConstantDeclaration (
|
|
constant *entity.ConstantDeclaration,
|
|
into entity.Type,
|
|
) (
|
|
*entity.ConstantDeclaration,
|
|
error,
|
|
) {
|
|
constant.Ty = into
|
|
|
|
// analyze the value, if given
|
|
if constant.Value != nil {
|
|
// create new scope context for this constant
|
|
this.pushScopeContext(constant)
|
|
this.pushScope(constant)
|
|
defer this.popScopeContext()
|
|
defer this.popScope()
|
|
|
|
// analyze value
|
|
value, err := this.analyzeExpression(into, strict, constant.Value)
|
|
if err != nil { return nil, err }
|
|
|
|
// only allow values that can be used as constants
|
|
err = this.isConstant(value)
|
|
if err != nil { return nil, err }
|
|
}
|
|
|
|
// TODO after analysis, check if constant
|
|
return constant, nil
|
|
}
|
|
|
|
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
|
|
rootKey := func () entity.Key {
|
|
return entity.Key {
|
|
Unit: this.unit,
|
|
Name: root.Name,
|
|
}
|
|
}
|
|
updateIncompleteInfo := func () {
|
|
if root != nil { this.incompleteTypes[rootKey()] = root }
|
|
}
|
|
updatePseudoCompleteInfo := func () {
|
|
if root != nil { this.Types[rootKey()] = root }
|
|
}
|
|
if root != nil { defer delete(this.incompleteTypes, rootKey()) }
|
|
// ---------
|
|
|
|
// determine the access permission for the type
|
|
access := entity.AccessPublic; if root != nil {
|
|
access = root.Acc
|
|
}
|
|
|
|
switch ty := ty.(type) {
|
|
// named type
|
|
case *entity.TypeNamed:
|
|
ty.Unt = this.unit
|
|
ty.Acc = access
|
|
updateIncompleteInfo()
|
|
|
|
// resolve nickname of the unit where the typedef is
|
|
if primitive, isPrimitive := primitiveTypes[ty.Name]; isPrimitive {
|
|
return primitive, nil
|
|
}
|
|
unit, err := this.resolveNickname(ty.Position(), ty.UnitNickname)
|
|
if err != nil { return nil, err }
|
|
|
|
// analyze the typedef
|
|
def, err := this.analyzeTypedef(ty.Position(), entity.Key {
|
|
Unit: unit,
|
|
Name: ty.Name,
|
|
}, acceptIncomplete)
|
|
if err != nil { return nil, err }
|
|
|
|
ty.Type = def.Type
|
|
|
|
// check access permissions
|
|
err = this.typePrivate(ty.Position(), ty)
|
|
if err != nil { return nil, err }
|
|
|
|
return ty, nil
|
|
|
|
// pointer type
|
|
case *entity.TypePointer:
|
|
ty.Unt = this.unit
|
|
ty.Acc = access
|
|
updateIncompleteInfo()
|
|
ty.Referenced, err = this.analyzeType(ty.Referenced, true)
|
|
return ty, err
|
|
|
|
// slice type
|
|
case *entity.TypeSlice:
|
|
ty.Unt = this.unit
|
|
ty.Acc = access
|
|
updateIncompleteInfo()
|
|
ty.Element, err = this.analyzeType(ty.Element, false)
|
|
return ty, err
|
|
|
|
// array type
|
|
case *entity.TypeArray:
|
|
ty.Unt = this.unit
|
|
ty.Acc = access
|
|
updateIncompleteInfo()
|
|
if ty.Length < 1 {
|
|
return ty, errors.Errorf (
|
|
ty.Position(), "array length must be > 0")
|
|
}
|
|
ty.Element, err = this.analyzeType(ty.Element, false)
|
|
return ty, err
|
|
|
|
// struct type
|
|
case *entity.TypeStruct:
|
|
ty.Unt = this.unit
|
|
ty.Acc = access
|
|
ty, err = this.assembleStructMap(ty)
|
|
updateIncompleteInfo()
|
|
if err != nil { return nil, err }
|
|
for name, member := range ty.MemberMap {
|
|
ty.MemberMap[name].Ty,
|
|
err = this.analyzeType(member.Ty, false)
|
|
if err != nil { return nil, err }
|
|
}
|
|
return ty, nil
|
|
|
|
// interface type
|
|
case *entity.TypeInterface:
|
|
ty.Unt = this.unit
|
|
ty.Acc = access
|
|
ty, err = this.assembleInterfaceMap(ty)
|
|
updatePseudoCompleteInfo()
|
|
if err != nil { return nil, err }
|
|
for name, behavior := range ty.BehaviorMap {
|
|
ty.BehaviorMap[name], err = this.analyzeBehavior(behavior)
|
|
if err != nil { return nil, err }
|
|
}
|
|
return ty, nil
|
|
|
|
// union type
|
|
case *entity.TypeUnion:
|
|
ty.Unt = this.unit
|
|
ty.Acc = access
|
|
updateIncompleteInfo()
|
|
return this.analyzeTypeUnion(ty)
|
|
|
|
// integer type
|
|
case *entity.TypeInt:
|
|
ty.Unt = this.unit
|
|
ty.Acc = access
|
|
updateIncompleteInfo()
|
|
if ty.Width < 1 {
|
|
return ty, errors.Errorf (
|
|
ty.Position(), "integer width must be > 0")
|
|
}
|
|
return ty, nil
|
|
|
|
// floating point and word types
|
|
case *entity.TypeFloat:
|
|
ty.Unt = this.unit
|
|
ty.Acc = access
|
|
return ty, nil
|
|
case *entity.TypeWord:
|
|
ty.Unt = this.unit
|
|
ty.Acc = access
|
|
return ty, nil
|
|
|
|
default: panic(fmt.Sprint("BUG: analyzer doesnt know about type ", ty))
|
|
}
|
|
}
|
|
|
|
// typePrivate checks if the given type is private.
|
|
func (this *Tree) typePrivate (pos errors.Position, ty entity.Type) error {
|
|
if ty == nil { return nil }
|
|
|
|
original := ty
|
|
if named, ok := ty.(*entity.TypeNamed); ok {
|
|
ty = named.Type
|
|
}
|
|
if ty.Unit() != this.unit && ty.Access() == entity.AccessPrivate {
|
|
return errors.Errorf(pos, "type %v is private", entity.FormatType(original))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// typeOpaque checks if the given type is opaque or private.
|
|
func (this *Tree) typeOpaque (pos errors.Position, ty entity.Type) error {
|
|
if ty == nil { return nil }
|
|
|
|
err := this.typePrivate(pos, ty)
|
|
if err != nil { return err }
|
|
|
|
original := ty
|
|
if named, ok := ty.(*entity.TypeNamed); ok {
|
|
ty = named.Type
|
|
}
|
|
if ty.Unit() != this.unit && ty.Access() == entity.AccessOpaque {
|
|
return errors.Errorf(pos, "type %v is opaque", entity.FormatType(original))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
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, errors.Errorf (
|
|
member.Position(), "%s already listed in struct at %v",
|
|
member.Name, previous.Position())
|
|
}
|
|
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, errors.Errorf (
|
|
method.Position(), "%s already listed in interface at %v",
|
|
method.Name, previous.Position())
|
|
}
|
|
ty.BehaviorMap [method.Name] = method
|
|
ty.BehaviorOrder[index] = method.Name
|
|
ty.Behaviors [index] = method
|
|
}
|
|
return ty, nil
|
|
}
|
|
|
|
func (this *Tree) analyzeTypeUnion (ty *entity.TypeUnion) (*entity.TypeUnion, error) {
|
|
ty.AllowedMap = make(map[entity.Hash] entity.Type)
|
|
ty.AllowedOrder = make([]entity.Hash, len(ty.Allowed))
|
|
for index, allowed := range ty.Allowed {
|
|
allowed, err := this.analyzeType(allowed, true)
|
|
if err != nil { return nil, err }
|
|
|
|
hash := allowed.Hash()
|
|
if previous, exists := ty.AllowedMap[hash]; exists {
|
|
return ty, errors.Errorf (
|
|
allowed.Position(), "%v already listed in union at %v",
|
|
allowed, previous.Position())
|
|
}
|
|
ty.AllowedMap [hash] = allowed
|
|
ty.AllowedOrder[index] = hash
|
|
ty.Allowed [index] = allowed
|
|
}
|
|
return ty, nil
|
|
}
|
|
|
|
func (this *Tree) analyzeBehavior (behavior *entity.Signature) (*entity.Signature, error) {
|
|
behavior, err := this.assembleSignatureMap(behavior)
|
|
behavior.Unt = this.unit
|
|
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
|
|
}
|
|
|
|
func (this *Tree) assembleTypedefConstantMap (
|
|
typedef *entity.Typedef,
|
|
) (
|
|
*entity.Typedef,
|
|
error,
|
|
) {
|
|
typedef.ConstantMap = make(map[string] *entity.ConstantDeclaration)
|
|
typedef.ConstantOrder = make([]string, len(typedef.Constants))
|
|
for index, constant := range typedef.Constants {
|
|
if previous, exists := typedef.ConstantMap[constant.Name]; exists {
|
|
return nil, errors.Errorf (
|
|
constant.Position(), "%s already defined at %v",
|
|
constant.Name, previous.Position())
|
|
}
|
|
typedef.ConstantMap [constant.Name] = constant
|
|
typedef.ConstantOrder[index] = constant.Name
|
|
typedef.Constants [index] = constant
|
|
}
|
|
return typedef, nil
|
|
}
|