I forgor
This commit is contained in:
parent
5ac046d5b1
commit
ee8eabcfc4
|
@ -13,6 +13,10 @@ func (this *Tree) canAssignStrict (
|
|||
) error {
|
||||
can := false
|
||||
switch destination.(type) {
|
||||
// yes if unspecified
|
||||
case nil:
|
||||
can = true
|
||||
|
||||
// named type
|
||||
case *entity.TypeNamed:
|
||||
// try to compare names first
|
||||
|
@ -32,12 +36,9 @@ func (this *Tree) canAssignStrict (
|
|||
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)
|
||||
}
|
||||
method, err := this.analyzeMethodOrBehavior (
|
||||
pos, source, name)
|
||||
if err != nil { return err }
|
||||
|
||||
// extract signature
|
||||
var signature *entity.Signature
|
||||
|
@ -48,7 +49,7 @@ func (this *Tree) canAssignStrict (
|
|||
signature = method.(*entity.Method).Signature
|
||||
default:
|
||||
panic(fmt.Sprint (
|
||||
"Tree.methodOrBehaviorOf returned ",
|
||||
"Tree.analyzeMethodOrBehavior returned ",
|
||||
method))
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import "git.tebibyte.media/sashakoshka/fspl/entity"
|
|||
|
||||
func (this *Tree) analyzeExpression (
|
||||
into entity.Type,
|
||||
strict bool,
|
||||
expression entity.Expression,
|
||||
) (
|
||||
entity.Expression,
|
||||
|
@ -12,15 +13,15 @@ func (this *Tree) analyzeExpression (
|
|||
) {
|
||||
switch expression.(type) {
|
||||
case *entity.Variable:
|
||||
return this.analyzeVariable(into, expression.(*entity.Variable))
|
||||
return this.analyzeVariable(into, strict, expression.(*entity.Variable))
|
||||
case *entity.Declaration:
|
||||
return this.analyzeDeclaration(into, expression.(*entity.Declaration))
|
||||
return this.analyzeDeclaration(into, strict, expression.(*entity.Declaration))
|
||||
case *entity.Call:
|
||||
return this.analyzeCall(into, expression.(*entity.Call))
|
||||
return this.analyzeCall(into, strict, expression.(*entity.Call))
|
||||
case *entity.MethodCall:
|
||||
return this.analyzeMethodCall(into, expression.(*entity.MethodCall))
|
||||
// case *entity.Subscript:
|
||||
// return this.analyzeSubscript(into, expression.(*entity.Subscript))
|
||||
return this.analyzeMethodCall(into, strict, expression.(*entity.MethodCall))
|
||||
case *entity.Subscript:
|
||||
return this.analyzeSubscript(into, strict, expression.(*entity.Subscript))
|
||||
// case *entity.Slice:
|
||||
// return this.analyzeSlice(into, expression.(*entity.Slice))
|
||||
// case *entity.Dereference:
|
||||
|
@ -61,7 +62,7 @@ func (this *Tree) analyzeStatement (
|
|||
if assignment, ok := statement.(*entity.Assignment); ok {
|
||||
return this.analyzeAssignment(assignment)
|
||||
} else if expression, ok := statement.(entity.Expression); ok {
|
||||
expression, err := this.analyzeExpression(nil, expression)
|
||||
expression, err := this.analyzeExpression(nil, false, expression)
|
||||
return expression.(entity.Statement), err
|
||||
} else {
|
||||
panic(fmt.Sprint (
|
||||
|
@ -69,4 +70,3 @@ func (this *Tree) analyzeStatement (
|
|||
statement))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package analyzer
|
||||
|
||||
// import "fmt"
|
||||
import "fmt"
|
||||
import "github.com/alecthomas/participle/v2"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
|
||||
|
@ -11,6 +11,7 @@ import "git.tebibyte.media/sashakoshka/fspl/entity"
|
|||
|
||||
func (this *Tree) analyzeVariable (
|
||||
into entity.Type,
|
||||
strict bool,
|
||||
variable *entity.Variable,
|
||||
) (
|
||||
entity.Expression,
|
||||
|
@ -32,6 +33,7 @@ func (this *Tree) analyzeVariable (
|
|||
|
||||
func (this *Tree) analyzeDeclaration (
|
||||
into entity.Type,
|
||||
strict bool,
|
||||
declaration *entity.Declaration,
|
||||
) (
|
||||
entity.Expression,
|
||||
|
@ -61,19 +63,23 @@ func (this *Tree) analyzeDeclaration (
|
|||
}
|
||||
|
||||
func (this *Tree) analyzeCall (
|
||||
into entity.Type,
|
||||
call *entity.Call,
|
||||
into entity.Type,
|
||||
strict bool,
|
||||
call *entity.Call,
|
||||
) (
|
||||
entity.Expression,
|
||||
error,
|
||||
) {
|
||||
// get function
|
||||
function, err := this.analyzeFunction(call.Pos, call.Name)
|
||||
call.Function = function
|
||||
if err != nil { return nil, err }
|
||||
|
||||
// check return result
|
||||
err = this.canAssignStrict(call.Pos, into, function.Signature.Return)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
// check arg count
|
||||
if len(call.Arguments) > len(function.Signature.Arguments) {
|
||||
return nil, participle.Errorf (
|
||||
call.Pos, "too many arguments in call to %s",
|
||||
|
@ -84,13 +90,81 @@ func (this *Tree) analyzeCall (
|
|||
call.Name)
|
||||
}
|
||||
|
||||
// check arg types
|
||||
for index, argument := range call.Arguments {
|
||||
signature := function.Signature
|
||||
correct := signature.ArgumentMap[signature.ArgumentOrder[index]]
|
||||
argument, err := this.analyzeExpression(correct.Type, argument)
|
||||
argument, err := this.analyzeExpression(correct.Type, true, argument)
|
||||
if err != nil { return nil, err }
|
||||
call.Arguments[index] = argument
|
||||
}
|
||||
|
||||
return call, nil
|
||||
}
|
||||
|
||||
func (this *Tree) analyzeMethodCall (
|
||||
into entity.Type,
|
||||
strict bool,
|
||||
call *entity.MethodCall,
|
||||
) (
|
||||
entity.Expression,
|
||||
error,
|
||||
) {
|
||||
// get method
|
||||
sourceExpr, err := this.analyzeVariable(nil, false, call.Source)
|
||||
source := sourceExpr.(*entity.Variable)
|
||||
if err != nil { return nil, err }
|
||||
method, err := this.analyzeMethodOrBehavior (
|
||||
call.Pos, source.Declaration.Type, call.Name)
|
||||
|
||||
// extract signature
|
||||
var signature *entity.Signature
|
||||
switch method.(type) {
|
||||
case *entity.Signature:
|
||||
signature = method.(*entity.Signature)
|
||||
call.Behavior = signature
|
||||
case *entity.Method:
|
||||
signature = method.(*entity.Method).Signature
|
||||
call.Method = method.(*entity.Method)
|
||||
default:
|
||||
panic(fmt.Sprint (
|
||||
"Tree.analyzeMethodOrBehavior returned ",
|
||||
method))
|
||||
}
|
||||
|
||||
// check return result
|
||||
err = this.canAssignStrict(call.Pos, into, signature.Return)
|
||||
if err != nil { return nil, err }
|
||||
|
||||
// check arg count
|
||||
if len(call.Arguments) > len(signature.Arguments) {
|
||||
return nil, participle.Errorf (
|
||||
call.Pos, "too many arguments in call to %s",
|
||||
call.Name)
|
||||
} else if len(call.Arguments) < len(signature.Arguments) {
|
||||
return nil, participle.Errorf (
|
||||
call.Pos, "too few arguments in call to %s",
|
||||
call.Name)
|
||||
}
|
||||
|
||||
// check arg types
|
||||
for index, argument := range call.Arguments {
|
||||
correct := signature.ArgumentMap[signature.ArgumentOrder[index]]
|
||||
argument, err := this.analyzeExpression(correct.Type, true, argument)
|
||||
if err != nil { return nil, err }
|
||||
call.Arguments[index] = argument
|
||||
}
|
||||
|
||||
return call, nil
|
||||
}
|
||||
|
||||
func (this *Tree) analyzeSubscript (
|
||||
into entity.Type,
|
||||
strict bool,
|
||||
subscript *entity.Subscript,
|
||||
) (
|
||||
entity.Expression,
|
||||
error,
|
||||
) {
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package analyzer
|
||||
|
||||
import "fmt"
|
||||
import "github.com/alecthomas/participle/v2"
|
||||
import "github.com/alecthomas/participle/v2/lexer"
|
||||
import "git.tebibyte.media/sashakoshka/fspl/entity"
|
||||
|
||||
// analyzeMethod analyzes a method that is directly owned by the specified type.
|
||||
func (this *Tree) analyzeMethod (
|
||||
pos lexer.Position,
|
||||
typeName string,
|
||||
|
@ -56,3 +58,59 @@ func (this *Tree) analyzeMethod (
|
|||
// TODO: analyze method body
|
||||
return method, nil
|
||||
}
|
||||
|
||||
func (this *Tree) methodExists (typeName, name string) bool {
|
||||
_, exists := this.rawMethods[typeName + "." + name]
|
||||
return exists
|
||||
}
|
||||
|
||||
// analyzeMethodOrBehavior returns *entity.Signature if it found an interface
|
||||
// behavior, and *entity.Method if it found a method.
|
||||
func (this *Tree) analyzeMethodOrBehavior (
|
||||
pos lexer.Position,
|
||||
ty entity.Type,
|
||||
name string,
|
||||
) (
|
||||
any,
|
||||
error,
|
||||
) {
|
||||
switch ty.(type) {
|
||||
case *entity.TypeNamed:
|
||||
ty := ty.(*entity.TypeNamed)
|
||||
if this.methodExists(ty.Name, name) {
|
||||
method, err := this.analyzeMethod(pos, ty.Name, name)
|
||||
if err != nil { return nil, err }
|
||||
return method, nil
|
||||
} else {
|
||||
return this.analyzeMethodOrBehavior(pos, ty.Type, name)
|
||||
}
|
||||
|
||||
case *entity.TypeInterface:
|
||||
ty := ty.(*entity.TypeInterface)
|
||||
if behavior, ok := ty.BehaviorMap[name]; ok {
|
||||
return behavior, nil
|
||||
} else {
|
||||
return nil, participle.Errorf (
|
||||
pos, "no behavior or method named %s",
|
||||
name)
|
||||
}
|
||||
|
||||
case *entity.TypePointer:
|
||||
ty := ty.(*entity.TypePointer)
|
||||
return this.analyzeMethodOrBehavior(pos, ty.Referenced, name)
|
||||
|
||||
case *entity.TypeVoid,
|
||||
*entity.TypeSlice,
|
||||
*entity.TypeArray,
|
||||
*entity.TypeStruct,
|
||||
*entity.TypeInt,
|
||||
*entity.TypeFloat,
|
||||
*entity.TypeWord:
|
||||
|
||||
return nil, participle.Errorf (
|
||||
pos, "no method named %s defined on this type",
|
||||
name)
|
||||
|
||||
default: panic(fmt.Sprint("BUG: analyzer doesnt know about type ", ty))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,8 +60,9 @@ func (this *Tree) analyzeTypeInternal (
|
|||
error,
|
||||
) {
|
||||
// edge cases
|
||||
if ty == entity.Void() { return ty, nil }
|
||||
if ty == nil { return entity.Void(), nil }
|
||||
void := &entity.TypeVoid { }
|
||||
if ty.Equals(void) { return ty, nil }
|
||||
if ty == nil { return void, nil }
|
||||
var err error
|
||||
|
||||
// if we are analyzing a typedef, we will need to update incomplete type
|
||||
|
@ -200,43 +201,3 @@ 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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,10 +80,15 @@ func (this *Call) String () string {
|
|||
// inherent type information, it may be directly assigned to an interface.
|
||||
// A method call is never a valid location expression.
|
||||
type MethodCall struct {
|
||||
// Syntax
|
||||
Pos lexer.Position
|
||||
Source *Variable `parser:" @@ '.' "`
|
||||
Name string `parser:" '[' @Ident "`
|
||||
Arguments []Expression `parser:" @@* ']' "`
|
||||
|
||||
// Semantics
|
||||
Method *Method
|
||||
Behavior *Signature
|
||||
}
|
||||
func (*MethodCall) expression(){}
|
||||
func (*MethodCall) statement(){}
|
||||
|
|
Loading…
Reference in New Issue