Function call analysis
This commit is contained in:
parent
99c09d9b07
commit
5ac046d5b1
@ -1,48 +1,84 @@
|
||||
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) canAssignStrict (
|
||||
pos lexer.Position,
|
||||
// assuming both are analyzed already
|
||||
ty entity.Type,
|
||||
source entity.Type
|
||||
destination entity.Type,
|
||||
source entity.Type,
|
||||
) error {
|
||||
can := false
|
||||
switch ty.(type) {
|
||||
switch destination.(type) {
|
||||
// named type
|
||||
case *entity.TypeNamed:
|
||||
// try to compare names first
|
||||
ty := ty.(*entity.TypeNamed)
|
||||
destination := destination.(*entity.TypeNamed)
|
||||
if source, ok := source.(*entity.TypeNamed); ok {
|
||||
if ty.Name == source.Name {
|
||||
if destination.Name == source.Name {
|
||||
can = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if not, see if the first is an interface and the second can
|
||||
// be assigned to it TODO
|
||||
// if not, types may be assignmable further down:
|
||||
return this.canAssignStrict(pos, destination.Type, source)
|
||||
|
||||
// interface tyoe
|
||||
case *entity.TypeInterface:
|
||||
destination := destination.(*entity.TypeInterface)
|
||||
for name, behavior := range destination.BehaviorMap {
|
||||
// get method
|
||||
method := this.methodOrBehaviorOf(source, name)
|
||||
if method == nil {
|
||||
return participle.Errorf (
|
||||
pos, "%v is missing method %v",
|
||||
source, name)
|
||||
}
|
||||
|
||||
// extract signature
|
||||
var signature *entity.Signature
|
||||
switch method.(type) {
|
||||
case *entity.Signature:
|
||||
signature = method.(*entity.Signature)
|
||||
case *entity.Method:
|
||||
signature = method.(*entity.Method).Signature
|
||||
default:
|
||||
panic(fmt.Sprint (
|
||||
"Tree.methodOrBehaviorOf returned ",
|
||||
method))
|
||||
}
|
||||
|
||||
// if ty is not named or not an interface, we can test for structural
|
||||
// check equivalence
|
||||
if !signature.Equals(behavior) {
|
||||
return participle.Errorf (
|
||||
pos, "%v has wrong signature for method %v",
|
||||
source, name)
|
||||
}
|
||||
}
|
||||
|
||||
// if ty is not named nor an interface, we can test for structural
|
||||
// equivalence:
|
||||
case *entity.TypeVoid,
|
||||
*entity.TypePointer,
|
||||
*entity.TypeSlice,
|
||||
*entity.TypeArray,
|
||||
*entity.TypeStruct,
|
||||
*entity.TypeInt,
|
||||
*entity.TypeFloat,
|
||||
*entity.TypeWord:
|
||||
|
||||
|
||||
// TODO have equals methods
|
||||
case *entity.TypePointer:
|
||||
case *entity.TypeSlice:
|
||||
case *entity.TypeArray:
|
||||
case *entity.TypeStruct:
|
||||
case *entity.TypeInt:
|
||||
case *entity.TypeFloat, *entity.TypeWord:
|
||||
default: panic(fmt.Sprint("BUG: analyzer doesnt know about type ", ty))
|
||||
can = destination.Equals(source)
|
||||
|
||||
default: panic(fmt.Sprint("BUG: analyzer doesnt know about type ", destination))
|
||||
}
|
||||
|
||||
if can {
|
||||
return nil
|
||||
} else {
|
||||
return participle.Errorf (
|
||||
call.Position, "expected %v",
|
||||
destination)
|
||||
return participle.Errorf(pos, "expected %v", destination)
|
||||
}
|
||||
}
|
||||
|
@ -19,32 +19,32 @@ func (this *Tree) analyzeExpression (
|
||||
return this.analyzeCall(into, expression.(*entity.Call))
|
||||
case *entity.MethodCall:
|
||||
return this.analyzeMethodCall(into, expression.(*entity.MethodCall))
|
||||
case *entity.Subscript:
|
||||
return this.analyzeSubscript(into, expression.(*entity.Subscript))
|
||||
case *entity.Slice:
|
||||
return this.analyzeSlice(into, expression.(*entity.Slice))
|
||||
case *entity.Dereference:
|
||||
return this.analyzeDereference(into, expression.(*entity.Dereference))
|
||||
case *entity.Reference:
|
||||
return this.analyzeReference(into, expression.(*entity.Reference))
|
||||
case *entity.ValueCast:
|
||||
return this.analyzeValueCast(into, expression.(*entity.ValueCast))
|
||||
case *entity.BitCast:
|
||||
return this.analyzeBitCast(into, expression.(*entity.BitCast))
|
||||
case *entity.Operation:
|
||||
return this.analyzeOperation(into, expression.(*entity.Operation))
|
||||
case *entity.Block:
|
||||
return this.analyzeBlock(into, expression.(*entity.Block))
|
||||
case *entity.MemberAccess:
|
||||
return this.analyzeMemberAccess(into, expression.(*entity.MemberAccess))
|
||||
case *entity.IfElse:
|
||||
return this.analyzeIfElse(into, expression.(*entity.IfElse))
|
||||
case *entity.Loop:
|
||||
return this.analyzeLoop(into, expression.(*entity.Loop))
|
||||
case *entity.Break:
|
||||
return this.analyzeBreak(into, expression.(*entity.Break))
|
||||
case *entity.Return:
|
||||
return this.analyzeReturn(into, expression.(*entity.Return))
|
||||
// case *entity.Subscript:
|
||||
// return this.analyzeSubscript(into, expression.(*entity.Subscript))
|
||||
// case *entity.Slice:
|
||||
// return this.analyzeSlice(into, expression.(*entity.Slice))
|
||||
// case *entity.Dereference:
|
||||
// return this.analyzeDereference(into, expression.(*entity.Dereference))
|
||||
// case *entity.Reference:
|
||||
// return this.analyzeReference(into, expression.(*entity.Reference))
|
||||
// case *entity.ValueCast:
|
||||
// return this.analyzeValueCast(into, expression.(*entity.ValueCast))
|
||||
// case *entity.BitCast:
|
||||
// return this.analyzeBitCast(into, expression.(*entity.BitCast))
|
||||
// case *entity.Operation:
|
||||
// return this.analyzeOperation(into, expression.(*entity.Operation))
|
||||
// case *entity.Block:
|
||||
// return this.analyzeBlock(into, expression.(*entity.Block))
|
||||
// case *entity.MemberAccess:
|
||||
// return this.analyzeMemberAccess(into, expression.(*entity.MemberAccess))
|
||||
// case *entity.IfElse:
|
||||
// return this.analyzeIfElse(into, expression.(*entity.IfElse))
|
||||
// case *entity.Loop:
|
||||
// return this.analyzeLoop(into, expression.(*entity.Loop))
|
||||
// case *entity.Break:
|
||||
// return this.analyzeBreak(into, expression.(*entity.Break))
|
||||
// case *entity.Return:
|
||||
// return this.analyzeReturn(into, expression.(*entity.Return))
|
||||
default:
|
||||
panic(fmt.Sprint (
|
||||
"BUG: analyzer doesnt know about expression ",
|
||||
|
@ -1,6 +1,6 @@
|
||||
package analyzer
|
||||
|
||||
import "fmt"
|
||||
// import "fmt"
|
||||
import "github.com/alecthomas/participle/v2"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
|
||||
@ -19,11 +19,11 @@ func (this *Tree) analyzeVariable (
|
||||
declaration := this.Variable(variable.Name)
|
||||
if declaration == nil {
|
||||
return nil, participle.Errorf (
|
||||
variable.Position, "no variable named %s",
|
||||
variable.Pos, "no variable named %s",
|
||||
variable.Name)
|
||||
}
|
||||
|
||||
err := this.canAssignStrict(variable.Pos, into, variable.Type)
|
||||
err := this.canAssignStrict(variable.Pos, into, declaration.Type)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
variable.Declaration = declaration
|
||||
@ -40,16 +40,16 @@ func (this *Tree) analyzeDeclaration (
|
||||
existing := this.TopScope().Variable(declaration.Name)
|
||||
if existing != nil {
|
||||
return nil, participle.Errorf (
|
||||
declaration.Position,
|
||||
declaration.Pos,
|
||||
"%s already declared in block at %v",
|
||||
declaration.Name, exising.Pos)
|
||||
declaration.Name, existing.Pos)
|
||||
}
|
||||
|
||||
ty, err := this.analyzeType(declaration.Type)
|
||||
declaration.ty = ty
|
||||
ty, err := this.analyzeType(declaration.Type, false)
|
||||
declaration.Type = ty
|
||||
if err != nil { return nil, err }
|
||||
|
||||
err := this.canAssignStrict(declaration.Pos, into, declaration.Type)
|
||||
err = this.canAssignStrict(declaration.Pos, into, declaration.Type)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
this.AddVariable(declaration)
|
||||
@ -71,22 +71,26 @@ func (this *Tree) analyzeCall (
|
||||
call.Function = function
|
||||
if err != nil { return nil, err }
|
||||
|
||||
err := this.canAssignStrict(call.Pos, into, function.Type)
|
||||
err = this.canAssignStrict(call.Pos, into, function.Signature.Return)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
if len(call.Arguments) > len(function.Signature.Arguments) {
|
||||
return nil, participle.Errorf (
|
||||
call.Position, "too many arguments in call to %s",
|
||||
call.Pos, "too many arguments in call to %s",
|
||||
call.Name)
|
||||
} else if len(call.Arguments) < len(function.Signature.Arguments) {
|
||||
return nil, participle.Errorf (
|
||||
call.Position, "too few arguments in call to %s",
|
||||
call.Pos, "too few arguments in call to %s",
|
||||
call.Name)
|
||||
}
|
||||
|
||||
for index, argument := range call.Arguments {
|
||||
correct := function.ArgumentMap[function.ArgumentOrder[index]]
|
||||
err := this.analyzeExpression(correct.Type, argument)
|
||||
signature := function.Signature
|
||||
correct := signature.ArgumentMap[signature.ArgumentOrder[index]]
|
||||
argument, err := this.analyzeExpression(correct.Type, argument)
|
||||
if err != nil { return nil, err }
|
||||
call.Arguments[index] = argument
|
||||
}
|
||||
|
||||
return call, nil
|
||||
}
|
||||
|
@ -14,8 +14,7 @@ func (this *Tree) assembleSignatureMap (signature *entity.Signature) (*entity.Si
|
||||
}
|
||||
signature.ArgumentMap [member.Name] = member
|
||||
signature.ArgumentOrder[index] = member.Name
|
||||
signature.Arguments [index] = member
|
||||
}
|
||||
|
||||
signature.Arguments = nil
|
||||
return signature, nil
|
||||
}
|
||||
|
@ -169,9 +169,8 @@ func (this *Tree) assembleStructMap (ty *entity.TypeStruct) (*entity.TypeStruct,
|
||||
}
|
||||
ty.MemberMap [member.Name] = member
|
||||
ty.MemberOrder[index] = member.Name
|
||||
ty.Members [index] = member
|
||||
}
|
||||
|
||||
ty.Members = nil
|
||||
return ty, nil
|
||||
}
|
||||
|
||||
@ -186,9 +185,8 @@ func (this *Tree) assembleInterfaceMap (ty *entity.TypeInterface) (*entity.TypeI
|
||||
}
|
||||
ty.BehaviorMap [method.Name] = method
|
||||
ty.BehaviorOrder[index] = method.Name
|
||||
ty.Behaviors [index] = method
|
||||
}
|
||||
|
||||
ty.Behaviors = nil
|
||||
return ty, nil
|
||||
}
|
||||
|
||||
@ -202,3 +200,43 @@ func (this *Tree) analyzeBehavior (behavior *entity.Signature) (*entity.Signatur
|
||||
behavior.Return, err = this.analyzeType(behavior.Return, false)
|
||||
return behavior, err
|
||||
}
|
||||
|
||||
// methodOrBehaviorOf returns *entity.Signature if it found an interface
|
||||
// behavior, and *entity.Method if it found a method.
|
||||
func (this *Tree) methodOrBehaviorOf (ty entity.Type, name string) any {
|
||||
switch destination.(type) {
|
||||
case *entity.TypeNamed:
|
||||
ty := ty.(*entity.TypeNamed)
|
||||
method, _ := this.analyzeMethod(pos, ty.Name, name)
|
||||
if method != nil {
|
||||
return method
|
||||
} else {
|
||||
return this.methodOrBehaviorOf(ty.Type, name)
|
||||
}
|
||||
|
||||
case *entity.TypeInterface:
|
||||
ty := ty.(*entity.TypeInterface)
|
||||
if behavior, ok := ty.BehaviorMap[name]; ok {
|
||||
return behavior
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case *entity.TypePointer:
|
||||
ty := ty.(*entity.TypePointer)
|
||||
return this.methodOrBehaviorOf(ty.Referenced)
|
||||
|
||||
case *entity.TypeVoid,
|
||||
*entity.TypePointer,
|
||||
*entity.TypeSlice,
|
||||
*entity.TypeArray,
|
||||
*entity.TypeStruct,
|
||||
*entity.TypeInt,
|
||||
*entity.TypeFloat,
|
||||
*entity.TypeWord:
|
||||
|
||||
return nil
|
||||
|
||||
default: panic(fmt.Sprint("BUG: analyzer doesnt know about type ", ty))
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,14 @@ type Type interface {
|
||||
ty ()
|
||||
}
|
||||
|
||||
// Void represents the absence of a type.
|
||||
type Void struct { }
|
||||
func (*Void) ty(){}
|
||||
func (*Void) Equals (ty Type) bool {
|
||||
_, equals := ty.(*Void)
|
||||
// TypeVoid represents the absence of a type.
|
||||
type TypeVoid struct { }
|
||||
func (*TypeVoid) ty(){}
|
||||
func (*TypeVoid) Equals (ty Type) bool {
|
||||
_, equals := ty.(*TypeVoid)
|
||||
return equals
|
||||
}
|
||||
func (*Void) String () string { return "Void" }
|
||||
func (*TypeVoid) String () string { return "Void" }
|
||||
|
||||
// TypeNamed refers to a user-defined or built in named type.
|
||||
type TypeNamed struct {
|
||||
|
Loading…
Reference in New Issue
Block a user