package types import "github.com/marzeq/qk/shared" type Type interface { Equals(Type) bool CanCoerceTo(Type) bool CanCastTo(Type) bool } type PrimitiveType string const ( PRIMITIVE_I8 PrimitiveType = "i8" PRIMITIVE_I16 PrimitiveType = "i16" PRIMITIVE_I32 PrimitiveType = "i32" PRIMITIVE_I64 PrimitiveType = "i64" PRIMITIVE_U8 PrimitiveType = "u8" PRIMITIVE_U16 PrimitiveType = "u16" PRIMITIVE_U32 PrimitiveType = "u32" PRIMITIVE_U64 PrimitiveType = "u64" PRIMITIVE_F32 PrimitiveType = "f32" PRIMITIVE_F64 PrimitiveType = "f64" PRIMITIVE_ISZ PrimitiveType = "isz" PRIMITIVE_USZ PrimitiveType = "usz" PRIMITIVE_VOID PrimitiveType = "void" PRIMITIVE_CHAR PrimitiveType = "char" ) func (p PrimitiveType) IsInteger() bool { return p == PRIMITIVE_I8 || p == PRIMITIVE_I16 || p == PRIMITIVE_I32 || p == PRIMITIVE_I64 || p == PRIMITIVE_U8 || p == PRIMITIVE_U16 || p == PRIMITIVE_U32 || p == PRIMITIVE_U64 || p == PRIMITIVE_ISZ || p == PRIMITIVE_USZ } func (p PrimitiveType) IsFloat() bool { return p == PRIMITIVE_F32 || p == PRIMITIVE_F64 } func (p PrimitiveType) IsNumeric() bool { return p.IsInteger() || p.IsFloat() } func IntegerRank(p PrimitiveType) int { switch p { case PRIMITIVE_I8, PRIMITIVE_U8: return 1 case PRIMITIVE_I16, PRIMITIVE_U16: return 2 case PRIMITIVE_I32, PRIMITIVE_U32: return 3 case PRIMITIVE_I64, PRIMITIVE_U64: return 4 case PRIMITIVE_ISZ, PRIMITIVE_USZ: return 5 default: return 0 } } func FloatRank(p PrimitiveType) int { switch p { case PRIMITIVE_F32: return 1 case PRIMITIVE_F64: return 2 default: return 0 } } func (p PrimitiveType) Equals(other Type) bool { if otherPrimitive, ok := other.(PrimitiveType); ok { return p == otherPrimitive } return false } func (p PrimitiveType) CanCoerceTo(other Type) bool { if p.Equals(other) { return true } otherPrimitive, ok := other.(PrimitiveType) if !ok { return false } if p.IsInteger() && otherPrimitive.IsInteger() { return IntegerRank(p) <= IntegerRank(otherPrimitive) } if p.IsInteger() && otherPrimitive.IsFloat() { return true } if p.IsFloat() && otherPrimitive.IsFloat() { return FloatRank(p) <= FloatRank(otherPrimitive) } return false } func (p PrimitiveType) CanCastTo(other Type) bool { if p.Equals(other) { return true } otherPrimitive, ok := other.(PrimitiveType) if !ok { return false } if p.IsNumeric() && otherPrimitive.IsNumeric() { return true } if (p == PRIMITIVE_CHAR && otherPrimitive.IsInteger()) || (otherPrimitive == PRIMITIVE_CHAR && p.IsInteger()) { return true } if p.IsInteger() { if _, ok := other.(PointerType); ok { return true } } return false } type StructType struct { Fields []shared.Pair[string, Type] } func (s StructType) Equals(other Type) bool { otherStruct, ok := other.(StructType) if !ok { return false } if len(s.Fields) != len(otherStruct.Fields) { return false } for i, field := range s.Fields { otherField := otherStruct.Fields[i] if field.L != otherField.L || !field.R.Equals(otherField.R) { return false } } return true } func (s StructType) CanCoerceTo(other Type) bool { if s.Equals(other) { return true } return false } func (s StructType) CanCastTo(other Type) bool { return s.Equals(other) } type PointerType struct { Base Type } func (p PointerType) Equals(other Type) bool { if otherPointer, ok := other.(PointerType); ok { return p.Base.Equals(otherPointer.Base) } return false } func (p PointerType) CanCoerceTo(other Type) bool { if p.Equals(other) { return true } otherPointer, ok := other.(PointerType) if !ok { return false } if p.Base.Equals(PRIMITIVE_VOID) || otherPointer.Base.Equals(PRIMITIVE_VOID) { return true } return p.Base.CanCoerceTo(otherPointer.Base) } func (p PointerType) CanCastTo(other Type) bool { if p.Equals(other) { return true } switch t := other.(type) { case PointerType: return true case PrimitiveType: return t.IsInteger() } return false } type ArrayType struct { Base Type Size int } func (a ArrayType) Equals(other Type) bool { otherArray, ok := other.(ArrayType) if !ok { return false } if a.Size != otherArray.Size { return false } return a.Base.Equals(otherArray.Base) } func (a ArrayType) CanCoerceTo(other Type) bool { if a.Equals(other) { return true } otherArray, ok := other.(ArrayType) if !ok { return false } if a.Size != otherArray.Size { return false } return a.Base.CanCoerceTo(otherArray.Base) } func (a ArrayType) CanCastTo(other Type) bool { return a.Equals(other) } type FunctionType struct { Parameters []Type ReturnType Type } func (f FunctionType) Equals(other Type) bool { otherFunction, ok := other.(FunctionType) if !ok { return false } if len(f.Parameters) != len(otherFunction.Parameters) { return false } for i, param := range f.Parameters { if !param.Equals(otherFunction.Parameters[i]) { return false } } return f.ReturnType.Equals(otherFunction.ReturnType) } func (f FunctionType) CanCoerceTo(other Type) bool { otherFunction, ok := other.(FunctionType) if !ok { return false } if len(f.Parameters) != len(otherFunction.Parameters) { return false } for i := range f.Parameters { if !otherFunction.Parameters[i].CanCoerceTo(f.Parameters[i]) { return false } } return f.ReturnType.CanCoerceTo(otherFunction.ReturnType) } func (f FunctionType) CanCastTo(other Type) bool { return f.Equals(other) } type ErrorType struct{} func (e ErrorType) Equals(other Type) bool { _, ok := other.(ErrorType) return ok } func (e ErrorType) CanCoerceTo(other Type) bool { return true } func (e ErrorType) CanCastTo(other Type) bool { return true } type UntypedInt struct{} func (u UntypedInt) Equals(other Type) bool { _, ok := other.(UntypedInt) return ok } func (u UntypedInt) CanCoerceTo(other Type) bool { switch t := other.(type) { case PrimitiveType: return t.IsInteger() || t.IsFloat() case UntypedInt: return true case UntypedFloat: return true default: return false } } type UntypedFloat struct{} func (u UntypedFloat) Equals(other Type) bool { _, ok := other.(UntypedFloat) return ok } func (u UntypedFloat) CanCoerceTo(other Type) bool { switch t := other.(type) { case PrimitiveType: return t.IsFloat() case UntypedFloat: return true default: return false } } func IsUntyped(t Type) bool { switch t.(type) { case UntypedInt, UntypedFloat: return true } return false } func PromoteNumeric(a, b Type) Type { if a.Equals(b) { return a } if _, ok := a.(UntypedInt); ok { return b } if _, ok := b.(UntypedInt); ok { return a } if _, ok := a.(UntypedFloat); ok { return b } if _, ok := b.(UntypedFloat); ok { return a } pa, aok := a.(PrimitiveType) pb, bok := b.(PrimitiveType) if !aok || !bok { return ErrorType{} } if pa.IsFloat() || pb.IsFloat() { if pa == PRIMITIVE_F64 || pb == PRIMITIVE_F64 { return PRIMITIVE_F64 } return PRIMITIVE_F32 } return WiderInteger(pa, pb) } func WiderInteger(a, b PrimitiveType) PrimitiveType { rank := func(p PrimitiveType) int { switch p { case PRIMITIVE_I8, PRIMITIVE_U8: return 1 case PRIMITIVE_I16, PRIMITIVE_U16: return 2 case PRIMITIVE_I32, PRIMITIVE_U32: return 3 case PRIMITIVE_I64, PRIMITIVE_U64: return 4 case PRIMITIVE_ISZ, PRIMITIVE_USZ: return 5 default: return 0 } } if rank(a) >= rank(b) { return a } return b } func DefaultUntyped(t Type) Type { switch t.(type) { case UntypedInt: return PRIMITIVE_I32 case UntypedFloat: return PRIMITIVE_F32 default: return t } } func (u UntypedInt) CanCastTo(other Type) bool { switch t := other.(type) { case PrimitiveType: return t.IsNumeric() default: return false } } func (u UntypedFloat) CanCastTo(other Type) bool { switch t := other.(type) { case PrimitiveType: return t.IsFloat() || t.IsInteger() default: return false } }