Slight modifications to analyzer

This commit is contained in:
Sasha Koshka 2023-11-21 15:04:01 -05:00
parent ec78adf42d
commit 72941e1ab4
8 changed files with 132 additions and 62 deletions

View File

@ -71,15 +71,15 @@ func (this *Tree) canAssign (
}
// then, check for array to slice assignment
if destination, ok := this.reduceToBase(destination).(*entity.TypeSlice); ok {
if source, ok := this.reduceToBase(source). (*entity.TypeArray); ok {
if destination, ok := ReduceToBase(destination).(*entity.TypeSlice); ok {
if source, ok := ReduceToBase(source). (*entity.TypeArray); ok {
return this.canAssignSliceArray(pos, destination, source)
}}
// if weak, only compare the first base types
if mode == weak {
destination = this.reduceToBase(destination)
source = this.reduceToBase(source)
destination = ReduceToBase(destination)
source = ReduceToBase(source)
}
switch destination.(type) {
@ -126,8 +126,8 @@ func (this *Tree) canAssignCoerce (
source, destination)
}
destination = this.reduceToBase(destination)
source = this.reduceToBase(source)
destination = ReduceToBase(destination)
source = ReduceToBase(source)
switch destination.(type) {
// no type
@ -215,21 +215,11 @@ func (this *Tree) canAssignInterface (
return nil
}
// reduceToBase takes in an analyzed type and reduces it to its first non-name
// type.
func (this *Tree) reduceToBase (ty entity.Type) entity.Type {
if namedty, ok := ty.(*entity.TypeNamed); ok {
return this.reduceToBase(namedty.Type)
} else {
return ty
}
}
// areStructurallyEquivalent tests whether two types are structurally equivalent
// to eachother.
func (this *Tree) areStructurallyEquivalent (left, right entity.Type) bool {
left = this.reduceToBase(left)
right = this.reduceToBase(right)
left = ReduceToBase(left)
right = ReduceToBase(right)
switch left.(type) {
case nil: return false
@ -353,10 +343,20 @@ func (this *Tree) isLocationExpression (expression entity.Expression) error {
}
}
// ReduceToBase takes in an analyzed type and reduces it to its first non-name
// type.
func ReduceToBase (ty entity.Type) entity.Type {
if namedty, ok := ty.(*entity.TypeNamed); ok {
return ReduceToBase(namedty.Type)
} else {
return ty
}
}
// isInterface takes in an analyzed type and returns true and an interface if
// that type refers to an interface. If not, it returns nil, false.
func (this *Tree) isInterface (ty entity.Type) (*entity.TypeInterface, bool) {
ty = this.reduceToBase(ty)
ty = ReduceToBase(ty)
switch ty.(type) {
case *entity.TypeInterface: return ty.(*entity.TypeInterface), true
default: return nil, false
@ -364,8 +364,8 @@ func (this *Tree) isInterface (ty entity.Type) (*entity.TypeInterface, bool) {
}
// isNumeric returns whether or not the specified type is a number.
func (this *Tree) isNumeric (ty entity.Type) bool {
ty = this.reduceToBase(ty)
func isNumeric (ty entity.Type) bool {
ty = ReduceToBase(ty)
switch ty.(type) {
case *entity.TypeInt, *entity.TypeFloat, *entity.TypeWord: return true
default: return false
@ -373,8 +373,8 @@ func (this *Tree) isNumeric (ty entity.Type) bool {
}
// isInteger returns whether or not the specified type is an integer.
func (this *Tree) isInteger (ty entity.Type) bool {
switch this.reduceToBase(ty).(type) {
func isInteger (ty entity.Type) bool {
switch ReduceToBase(ty).(type) {
case *entity.TypeInt, *entity.TypeWord: return true
default: return false
}
@ -382,12 +382,12 @@ func (this *Tree) isInteger (ty entity.Type) bool {
// isOrdered returns whether or not the values of the specified type can be
// ordered.
func (this *Tree) isOrdered (ty entity.Type) bool {
return this.isNumeric(ty)
func isOrdered (ty entity.Type) bool {
return isNumeric(ty)
}
// isBoolean returns whether or not the specified type is a boolean.
func (this *Tree) isBoolean (ty entity.Type) bool {
func isBoolean (ty entity.Type) bool {
for {
named, ok := ty.(*entity.TypeNamed)
if !ok { return false }
@ -396,16 +396,16 @@ func (this *Tree) isBoolean (ty entity.Type) bool {
}
// isFloat returns whether or not the specified type is a float.
func (this *Tree) isFloat (ty entity.Type) bool {
switch this.reduceToBase(ty).(type) {
func isFloat (ty entity.Type) bool {
switch ReduceToBase(ty).(type) {
case *entity.TypeFloat: return true
default: return false
}
}
// isUnsigned returns whether or not the specified type is an unsigned integer.
func (this *Tree) isUnsigned (ty entity.Type) bool {
switch this.reduceToBase(ty).(type) {
func isUnsigned (ty entity.Type) bool {
switch ReduceToBase(ty).(type) {
case *entity.TypeInt: return ty.(*entity.TypeInt).Signed
case *entity.TypeWord: return ty.(*entity.TypeInt).Signed
default: return false
@ -414,8 +414,8 @@ func (this *Tree) isUnsigned (ty entity.Type) bool {
// inRange returns whether the specified value can fit within the given integer
// type.
func (this *Tree) inRange (ty entity.Type, value int64) bool {
base := this.reduceToBase(ty)
func inRange (ty entity.Type, value int64) bool {
base := ReduceToBase(ty)
switch base.(type) {
case *entity.TypeInt:
base := base.(*entity.TypeInt)
@ -423,7 +423,7 @@ func (this *Tree) inRange (ty entity.Type, value int64) bool {
return value > integer.SignedMin(base.Width) &&
value < integer.SignedMax(base.Width)
} else {
return value > 0 &&
return value >= 0 &&
uint64(value) < integer.UnsignedMax(base.Width)
}
case *entity.TypeWord:
@ -431,7 +431,7 @@ func (this *Tree) inRange (ty entity.Type, value int64) bool {
if base.Signed {
return value > math.MinInt && value < math.MaxInt
} else {
return value > 0 && uint64(value) < math.MaxUint
return value >= 0 && uint64(value) < math.MaxUint
}
default: return false
}

View File

@ -206,6 +206,20 @@ BlueJay.[land] = { }
`)
}
func TestAssignmentInterfaceErrBadLayer (test *testing.T) {
testStringErr (test,
"no method named fly defined on this type", 7, 11,
`
Bird: ([fly distance:F64] [land])
BlueJay: Int
BlueJay.[land] = { }
BlueJayRef: *BlueJay
[main] = {
b:Bird = a:BlueJayRef
}
`)
}
func TestAssignmentInterface (test *testing.T) {
testString (test,
`
@ -215,6 +229,8 @@ BlueJay.[fly distance:F64] = { }
BlueJay.[land] = { }
[main] = {
b:Bird = a:BlueJay
ref:*Bird = [@ a]
c:Bird = ref
}
`)
}

View File

@ -353,7 +353,7 @@ func (this *Tree) analyzeOperation (
comparison := func (argConstraint func (entity.Type) bool) (entity.Expression, error) {
if len(operation.Arguments) < 2 { return wrongArgCount() }
if !this.isBoolean(into) { return wrongInto() }
if !isBoolean(into) { return wrongInto() }
operation.Ty = &entity.TypeNamed {
Name: "Bool",
Type: this.Types["Bool"].Type,
@ -404,30 +404,30 @@ func (this *Tree) analyzeOperation (
entity.OperatorDivide,
entity.OperatorIncrement,
entity.OperatorDecrement:
return nSameType(-1, this.isNumeric)
return nSameType(-1, isNumeric)
case entity.OperatorModulo:
return nSameType(2, this.isNumeric)
return nSameType(2, isNumeric)
// logic
case entity.OperatorLogicalNot:
return nSameType(1, this.isBoolean)
return nSameType(1, isBoolean)
case entity.OperatorLogicalOr,
entity.OperatorLogicalAnd,
entity.OperatorLogicalXor:
return nSameType(-1, this.isBoolean)
return nSameType(-1, isBoolean)
// bit manipulation
case entity.OperatorNot:
return nSameType(1, this.isInteger)
return nSameType(1, isInteger)
case entity.OperatorOr, entity.OperatorAnd, entity.OperatorXor:
return nSameType(-1, this.isInteger)
return nSameType(-1, isInteger)
case entity.OperatorLeftShift, entity.OperatorRightShift:
if len(operation.Arguments) != 2 { return wrongArgCount() }
if !this.isInteger(into) { return wrongInto() }
if !isInteger(into) { return wrongInto() }
arg, err := this.analyzeExpression(into, mode, operation.Arguments[0])
if err != nil { return nil, err }
@ -447,10 +447,10 @@ func (this *Tree) analyzeOperation (
entity.OperatorGreater,
entity.OperatorLessEqual,
entity.OperatorGreaterEqual:
return comparison(this.isOrdered)
return comparison(isOrdered)
case entity.OperatorEqual:
return comparison(this.isOrdered)
return comparison(isOrdered)
default:
panic(fmt.Sprint (
@ -510,7 +510,7 @@ func (this *Tree) analyzeMemberAccess (
if err != nil { return nil, err }
access.Source = source.(*entity.Variable)
sourceType, ok := this.reduceToBase(source.Type()).(*entity.TypeStruct)
sourceType, ok := ReduceToBase(source.Type()).(*entity.TypeStruct)
if !ok {
return nil, participle.Errorf (
access.Pos, "cannot access members of %v", source)

View File

@ -49,9 +49,11 @@ func (this *Tree) analyzeFunction (
this.Functions[name] = function
// analyze function body
body, err := this.analyzeExpression (
function.Signature.Return, strict, function.Body)
if err != nil { return nil, err }
function.Body = body
if function.Body != nil {
body, err := this.analyzeExpression (
function.Signature.Return, strict, function.Body)
if err != nil { return nil, err }
function.Body = body
}
return function, nil
}

View File

@ -12,13 +12,13 @@ func (this *Tree) analyzeLiteralInt (
entity.Expression,
error,
) {
if !this.isNumeric(into) {
if !isNumeric(into) {
return nil, participle.Errorf (
literal.Pos, "cannot use integer literal as %v",
into)
}
if this.isInteger(into) && !this.inRange(into, int64(literal.Value)) {
if isInteger(into) && !inRange(into, int64(literal.Value)) {
return nil, participle.Errorf (
literal.Pos, "integer literal out of range for type %v",
into)
@ -36,7 +36,7 @@ func (this *Tree) analyzeLiteralFloat (
entity.Expression,
error,
) {
if !this.isFloat(into) {
if !isFloat(into) {
return nil, participle.Errorf (
literal.Pos, "cannot use float literal as %v",
into)
@ -54,7 +54,7 @@ func (this *Tree) analyzeLiteralArray (
entity.Expression,
error,
) {
base := this.reduceToBase(into)
base := ReduceToBase(into)
var elementType entity.Type
switch base.(type) {
case *entity.TypeArray:
@ -69,6 +69,10 @@ func (this *Tree) analyzeLiteralArray (
case *entity.TypeSlice:
base := base.(*entity.TypeSlice)
elementType = base.Element
case *entity.TypePointer:
base := base.(*entity.TypePointer)
elementType = base.Referenced
default:
return nil, participle.Errorf (
literal.Pos, "cannot use array literal as %v",
@ -93,7 +97,7 @@ func (this *Tree) analyzeLiteralStruct (
entity.Expression,
error,
) {
base, ok := this.reduceToBase(into).(*entity.TypeStruct)
base, ok := ReduceToBase(into).(*entity.TypeStruct)
if !ok {
return nil, participle.Errorf (
literal.Pos, "cannot use struct literal as %v",
@ -128,7 +132,7 @@ func (this *Tree) analyzeLiteralBoolean (
entity.Expression,
error,
) {
if !this.isBoolean(into) {
if !isBoolean(into) {
return nil, participle.Errorf (
literal.Pos, "cannot use boolean literal as %v",
into)

View File

@ -56,10 +56,12 @@ func (this *Tree) analyzeMethod (
owner.Methods[name] = method
// analyze method body
body, err := this.analyzeExpression (
method.Signature.Return, strict, method.Body)
if err != nil { return nil, err }
method.Body = 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
}
@ -77,6 +79,18 @@ func (this *Tree) analyzeMethodOrBehavior (
) (
any,
error,
) {
return this.analyzeMethodOrBehaviorInternal(pos, ty, name, true)
}
func (this *Tree) analyzeMethodOrBehaviorInternal (
pos lexer.Position,
ty entity.Type,
name string,
pierceReference bool,
) (
any,
error,
) {
switch ty.(type) {
case *entity.TypeNamed:
@ -86,7 +100,8 @@ func (this *Tree) analyzeMethodOrBehavior (
if err != nil { return nil, err }
return method, nil
} else {
return this.analyzeMethodOrBehavior(pos, ty.Type, name)
return this.analyzeMethodOrBehaviorInternal (
pos, ty.Type, name, false)
}
case *entity.TypeInterface:
@ -100,8 +115,15 @@ func (this *Tree) analyzeMethodOrBehavior (
}
case *entity.TypePointer:
ty := ty.(*entity.TypePointer)
return this.analyzeMethodOrBehavior(pos, ty.Referenced, name)
if pierceReference {
ty := ty.(*entity.TypePointer)
return this.analyzeMethodOrBehaviorInternal (
pos, ty.Referenced, name, false)
} else {
return nil, participle.Errorf (
pos, "no method named %s defined on this type",
name)
}
case *entity.TypeSlice,
*entity.TypeArray,

View File

@ -15,6 +15,20 @@ Type.[something] = { }
`)
}
func TestMethodNameErrBadLayer (test *testing.T) {
testStringErr (test,
"no method named something defined on this type", 7, 2,
`
Type: Int
Type.[something] = { }
RefType: *Type
[main] = {
instance:RefType
instance.[something]
}
`)
}
func TestMethodName (test *testing.T) {
testString (test,
`
@ -23,6 +37,8 @@ Type.[world] = { }
[main] = {
instance:Type
instance.[world]
ref:*Type = [@ instance]
ref.[world]
}
`)
}

View File

@ -9,6 +9,10 @@ type Scoped interface {
// AddVariable adds a variable to this entity's scope.
AddVariable (declaration *Declaration)
// OverVariables calls callback for each defined variable, stopping if
// returns false.
OverVariables (callback func (*Declaration) bool)
}
// Scope implements a scope.
@ -29,3 +33,9 @@ func (this *Scope) AddVariable (declaration *Declaration) {
}
this.variables[declaration.Name] = declaration
}
func (this *Scope) OverVariables (callback func (*Declaration) bool) {
for _, declaration := range this.variables {
if callback(declaration) { break }
}
}