Slight modifications to analyzer
This commit is contained in:
parent
ec78adf42d
commit
72941e1ab4
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue