179 lines
4.5 KiB
Go
179 lines
4.5 KiB
Go
package analyzer
|
|
|
|
import "fmt"
|
|
import "git.tebibyte.media/fspl/fspl/errors"
|
|
import "git.tebibyte.media/fspl/fspl/entity"
|
|
|
|
|
|
// analyzeMethod analyzes a method that is directly owned by the specified type.
|
|
func (this *Tree) analyzeMethod (
|
|
pos errors.Position,
|
|
key entity.Key,
|
|
) (
|
|
*entity.Method,
|
|
error,
|
|
) {
|
|
// get parent typedef
|
|
typeKey := key.StripMethod()
|
|
owner, err := this.analyzeTypedef(pos, typeKey, false)
|
|
if err != nil { return nil, err }
|
|
|
|
// return if exists already
|
|
if method, exists := owner.Methods[key.Method]; exists {
|
|
return method, nil
|
|
}
|
|
|
|
// error if method is missing
|
|
method, exists := this.rawMethods[key]
|
|
if !exists {
|
|
return nil, errors.Errorf(pos, "no method named %v", key)
|
|
}
|
|
|
|
// set method's unit, very important information yes
|
|
method.Unt = owner.Unit()
|
|
|
|
// methods cannot be marked as opaque
|
|
if method.Acc == entity.AccessOpaque {
|
|
return nil, errors.Errorf(pos, "cannot mark method as opaque")
|
|
}
|
|
|
|
// create a new scope context for this method
|
|
this.pushScopeContext(method)
|
|
this.pushScope(method)
|
|
defer this.popScopeContext()
|
|
defer this.popScope()
|
|
|
|
// analyze signature and add arguments to root scope of method
|
|
method.Signature, err = this.assembleSignatureMap(method.Signature)
|
|
if err != nil { return method, err }
|
|
for name, argument := range method.Signature.ArgumentMap {
|
|
argument.Ty, err = this.analyzeType(argument.Ty, false)
|
|
this.addVariable(argument)
|
|
method.Signature.ArgumentMap[name] = argument
|
|
if err != nil { return method, err }
|
|
}
|
|
method.Signature.Return, err =
|
|
this.analyzeType(method.Signature.Return, false)
|
|
if err != nil { return method, err }
|
|
|
|
// add owner to method
|
|
method.This = &entity.Declaration {
|
|
Pos: method.Position(),
|
|
Name: "this",
|
|
Ty: &entity.TypePointer {
|
|
Pos: method.Position(),
|
|
Referenced: &entity.TypeNamed {
|
|
Pos: method.Position(),
|
|
Unt: key.Unit,
|
|
Name: key.Name,
|
|
Type: owner.Type,
|
|
},
|
|
},
|
|
}
|
|
this.addVariable(method.This)
|
|
|
|
// add incomplete method to complete methods because there is enough
|
|
// information for it to be complete from the point of view of other
|
|
// parts of the code
|
|
owner.Methods[key.Method] = method
|
|
|
|
// analyze method body
|
|
if method.Body != nil {
|
|
body, err := this.analyzeExpression (
|
|
method.Signature.Return, strict, method.Body)
|
|
if err != nil { return nil, err }
|
|
method.Body = body
|
|
}
|
|
return method, nil
|
|
}
|
|
|
|
func (this *Tree) methodExists (pos errors.Position, key entity.Key) (bool, error) {
|
|
// check raw map
|
|
_, existsInRaw := this.rawMethods[key]
|
|
if existsInRaw { return true, nil }
|
|
|
|
// check parent typedef
|
|
typeKey := key.StripMethod()
|
|
owner, err := this.analyzeTypedef(pos, typeKey, false)
|
|
if err != nil { return false, err }
|
|
_, existsInType := owner.Methods[key.Method]
|
|
return existsInType, nil
|
|
}
|
|
|
|
// analyzeMethodOrBehavior returns *entity.Signature if it found an interface
|
|
// behavior, and *entity.Method if it found a method.
|
|
func (this *Tree) analyzeMethodOrBehavior (
|
|
pos errors.Position,
|
|
ty entity.Type,
|
|
name string,
|
|
) (
|
|
any,
|
|
error,
|
|
) {
|
|
return this.analyzeMethodOrBehaviorInternal(pos, ty, name, true)
|
|
}
|
|
|
|
func (this *Tree) analyzeMethodOrBehaviorInternal (
|
|
pos errors.Position,
|
|
ty entity.Type,
|
|
name string,
|
|
pierceReference bool,
|
|
) (
|
|
any,
|
|
error,
|
|
) {
|
|
switch ty.(type) {
|
|
case *entity.TypeNamed:
|
|
ty := ty.(*entity.TypeNamed)
|
|
key := entity.Key {
|
|
Unit: ty.Type.Unit(),
|
|
Name: ty.Name,
|
|
Method: name,
|
|
}
|
|
exists, err := this.methodExists(pos, key)
|
|
if err != nil { return nil, err }
|
|
if exists {
|
|
method, err := this.analyzeMethod(pos, key)
|
|
if err != nil { return nil, err }
|
|
return method, nil
|
|
} else {
|
|
return this.analyzeMethodOrBehaviorInternal (
|
|
pos, ty.Type, name, false)
|
|
}
|
|
|
|
case *entity.TypeInterface:
|
|
ty := ty.(*entity.TypeInterface)
|
|
if behavior, ok := ty.BehaviorMap[name]; ok {
|
|
return behavior, nil
|
|
} else {
|
|
return nil, errors.Errorf (
|
|
pos, "no behavior or method named %s",
|
|
name)
|
|
}
|
|
|
|
case *entity.TypePointer:
|
|
if pierceReference {
|
|
ty := ty.(*entity.TypePointer)
|
|
return this.analyzeMethodOrBehaviorInternal (
|
|
pos, ty.Referenced, name, false)
|
|
} else {
|
|
return nil, errors.Errorf (
|
|
pos, "no method named %s defined on this type",
|
|
name)
|
|
}
|
|
|
|
case *entity.TypeSlice,
|
|
*entity.TypeArray,
|
|
*entity.TypeStruct,
|
|
*entity.TypeInt,
|
|
*entity.TypeFloat,
|
|
*entity.TypeWord:
|
|
|
|
return nil, errors.Errorf (
|
|
pos, "no method named %s defined on this type",
|
|
name)
|
|
|
|
default: panic(fmt.Sprint("BUG: analyzer doesnt know about type ", ty))
|
|
}
|
|
}
|