fspl/analyzer/method.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))
}
}