diff --git a/src/types/Array.go b/src/types/Array.go new file mode 100644 index 0000000..75e5f13 --- /dev/null +++ b/src/types/Array.go @@ -0,0 +1,16 @@ +package types + +// Array is the address of an object. +type Array struct { + Of Type +} + +// Name returns the type name. +func (a *Array) Name() string { + return "[]" + a.Of.Name() +} + +// Size returns the total size in bytes. +func (a *Array) Size() int { + return 8 +} \ No newline at end of file diff --git a/src/types/Base.go b/src/types/Base.go new file mode 100644 index 0000000..520e318 --- /dev/null +++ b/src/types/Base.go @@ -0,0 +1,17 @@ +package types + +// Base is used to describe basic types like integers and floats. +type Base struct { + name string + size int +} + +// Name returns the name of the type. +func (s *Base) Name() string { + return s.name +} + +// Size returns the total size in bytes. +func (s *Base) Size() int { + return s.size +} \ No newline at end of file diff --git a/src/types/Common.go b/src/types/Common.go new file mode 100644 index 0000000..6af5a2f --- /dev/null +++ b/src/types/Common.go @@ -0,0 +1,27 @@ +package types + +var ( + Any = &Base{name: "any", size: 0} + AnyArray = &Array{Of: Any} + AnyInt = &Base{name: "int"} + AnyPointer = &Pointer{To: Any} + Bool = &Base{name: "bool", size: 1} + Int64 = &Base{name: "int64", size: 8} + Int32 = &Base{name: "int32", size: 4} + Int16 = &Base{name: "int16", size: 2} + Int8 = &Base{name: "int8", size: 1} + Float64 = &Base{name: "float64", size: 8} + Float32 = &Base{name: "float32", size: 4} + String = &Array{Of: Byte} + UInt64 = &Base{name: "uint64", size: 8} + UInt32 = &Base{name: "uint32", size: 4} + UInt16 = &Base{name: "uint16", size: 2} + UInt8 = &Base{name: "uint8", size: 1} +) + +var ( + Byte = UInt8 + Float = Float64 + Int = Int64 + UInt = UInt64 +) \ No newline at end of file diff --git a/src/types/Is.go b/src/types/Is.go new file mode 100644 index 0000000..3a54a64 --- /dev/null +++ b/src/types/Is.go @@ -0,0 +1,47 @@ +package types + +// Is returns true if the encountered type `a` can be converted into the expected type `b`. +func Is(a Type, b Type) bool { + if a == b || b == Any || a == nil { + return true + } + + aPointer, aIsPointer := a.(*Pointer) + bPointer, bIsPointer := b.(*Pointer) + + if aIsPointer && bIsPointer && (bPointer.To == Any || aPointer.To == bPointer.To) { + return true + } + + aArray, aIsArray := a.(*Array) + + if aIsArray && bIsPointer && (bPointer.To == Any || aArray.Of == bPointer.To) { + return true + } + + bArray, bIsArray := b.(*Array) + + if aIsArray && bIsArray && (bArray.Of == Any || aArray.Of == bArray.Of) { + return true + } + + if a == AnyInt { + switch b { + case Int64, Int32, Int16, Int8, UInt64, UInt32, UInt16, UInt8, AnyInt: + return true + default: + return false + } + } + + if b == AnyInt { + switch a { + case Int64, Int32, Int16, Int8, UInt64, UInt32, UInt16, UInt8, AnyInt: + return true + default: + return false + } + } + + return false +} \ No newline at end of file diff --git a/src/types/Pointer.go b/src/types/Pointer.go new file mode 100644 index 0000000..c836613 --- /dev/null +++ b/src/types/Pointer.go @@ -0,0 +1,16 @@ +package types + +// Pointer is the address of an object. +type Pointer struct { + To Type +} + +// Name returns the type name. +func (p *Pointer) Name() string { + return "*" + p.To.Name() +} + +// Size returns the total size in bytes. +func (p *Pointer) Size() int { + return 8 +} \ No newline at end of file diff --git a/src/types/Type.go b/src/types/Type.go index 4d6d132..c2e24c0 100644 --- a/src/types/Type.go +++ b/src/types/Type.go @@ -1,3 +1,7 @@ package types -type Type interface{} \ No newline at end of file +// Type is the generic interface for different data types. +type Type interface { + Name() string + Size() int +} \ No newline at end of file diff --git a/src/types/types_test.go b/src/types/types_test.go new file mode 100644 index 0000000..769a24b --- /dev/null +++ b/src/types/types_test.go @@ -0,0 +1,61 @@ +package types_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/types" + "git.urbach.dev/go/assert" +) + +func TestName(t *testing.T) { + assert.Equal(t, types.Int.Name(), "int64") + assert.Equal(t, types.AnyArray.Name(), "[]any") + assert.Equal(t, types.AnyPointer.Name(), "*any") + assert.Equal(t, (&types.Pointer{To: types.Int}).Name(), "*int64") + assert.Equal(t, (&types.Array{Of: types.Int}).Name(), "[]int64") + assert.Equal(t, types.String.Name(), "[]uint8") +} + +func TestSize(t *testing.T) { + assert.Equal(t, types.Int.Size(), 8) + assert.Equal(t, types.Int8.Size(), 1) + assert.Equal(t, types.Int16.Size(), 2) + assert.Equal(t, types.Int32.Size(), 4) + assert.Equal(t, types.Int64.Size(), 8) + assert.Equal(t, types.AnyArray.Size(), 8) + assert.Equal(t, types.AnyPointer.Size(), 8) + assert.Equal(t, types.String.Size(), 8) + assert.Equal(t, (&types.Pointer{To: types.Int}).Size(), 8) +} + +func TestBasics(t *testing.T) { + assert.True(t, types.Is(types.Int, types.Int)) + assert.True(t, types.Is(types.Int, types.Any)) + assert.True(t, types.Is(types.Int, types.AnyInt)) + assert.True(t, types.Is(types.AnyInt, types.AnyInt)) + assert.True(t, types.Is(types.AnyInt, types.Int)) + assert.True(t, types.Is(types.AnyPointer, types.AnyPointer)) + assert.True(t, types.Is(&types.Array{Of: types.Int}, types.AnyArray)) + assert.False(t, types.Is(types.Int, types.Float)) + assert.False(t, types.Is(types.Any, types.Int)) + assert.False(t, types.Is(types.AnyPointer, types.AnyInt)) + assert.False(t, types.Is(types.AnyInt, types.AnyPointer)) + assert.False(t, types.Is(&types.Pointer{To: types.Int}, &types.Pointer{To: types.Float})) +} + +func TestSpecialCases(t *testing.T) { + // Case #1: + // For syscalls whose return type is `nil` we currently allow casting them to anything. + assert.True(t, types.Is(nil, types.Int)) + assert.True(t, types.Is(nil, types.Float)) + + // Case #2: + // A pointer pointing to a known type fulfills the requirement of a pointer to anything. + assert.True(t, types.Is(&types.Pointer{To: types.Int}, types.AnyPointer)) + assert.True(t, types.Is(&types.Pointer{To: types.Float}, types.AnyPointer)) + + // Case #3: + // Arrays are also just pointers. + assert.True(t, types.Is(&types.Array{Of: types.Int}, types.AnyPointer)) + assert.True(t, types.Is(&types.Array{Of: types.Float}, types.AnyPointer)) +} \ No newline at end of file