Improved type system

This commit is contained in:
2025-02-09 23:52:07 +01:00
parent 7634244c56
commit f19d9063a5
30 changed files with 132 additions and 85 deletions

@ -17,9 +17,10 @@ import (
func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
var (
pkg = f.Package
pkgNode *expression.Expression
name string
nameNode = root.Children[0]
fn *Function
name string
exists bool
)
@ -53,8 +54,11 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
return nil, f.CompileMemoryStore(root)
}
} else {
pkg = nameNode.Children[0].Token.Text(f.File.Bytes)
name = nameNode.Children[1].Token.Text(f.File.Bytes)
pkgNode = nameNode.Children[0]
nameNode = nameNode.Children[1]
pkg = pkgNode.Token.Text(f.File.Bytes)
name = nameNode.Token.Text(f.File.Bytes)
}
if pkg == "kernel32" || pkg == "user32" || pkg == "gdi32" || pkg == "comctl32" {
@ -74,13 +78,13 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
return nil, nil
} else if pkg != f.File.Package {
if f.File.Imports == nil {
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameNode.Token.Position)
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, pkgNode.Token.Position)
}
imp, exists := f.File.Imports[pkg]
if !exists {
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameNode.Token.Position)
return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, pkgNode.Token.Position)
}
imp.Used = true
@ -93,6 +97,11 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
}
parameters := root.Children[1:]
if len(parameters) != len(fn.Input) {
return nil, errors.New(&errors.ParameterCountMismatch{Function: fn.Name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, nameNode.Token.End())
}
registers := f.CPU.Input[:len(parameters)]
for i := len(parameters) - 1; i >= 0; i-- {
@ -107,9 +116,20 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
continue
}
encountered := ""
expected := ""
if typ != nil {
encountered = typ.Name()
}
if fn.Input[i].Type != nil {
expected = fn.Input[i].Type.Name()
}
return nil, errors.New(&errors.TypeMismatch{
Encountered: typ.Name(),
Expected: fn.Input[i].Type.Name(),
Encountered: encountered,
Expected: expected,
ParameterName: fn.Input[i].Name,
}, f.File, parameters[i].Token.Position)
}

@ -0,0 +1,19 @@
package errors
import "fmt"
// ParameterCountMismatch error is created when the number of parameters doesn't match the function prototype.
type ParameterCountMismatch struct {
Function string
Count int
ExpectedCount int
}
// Error generates the string representation.
func (err *ParameterCountMismatch) Error() string {
if err.Count > err.ExpectedCount {
return fmt.Sprintf("Too many parameters in '%s' function call", err.Function)
}
return fmt.Sprintf("Not enough parameters in '%s' function call", err.Function)
}

@ -9,6 +9,10 @@ type Array struct {
// Name returns the type name.
func (a *Array) Name() string {
if a.Of == Any {
return "[]Any"
}
return "[]" + a.Of.Name()
}

@ -10,25 +10,27 @@ func ByName(name string, pkg string, structs map[string]*Struct) Type {
to := strings.TrimPrefix(name, "*")
typ := ByName(to, pkg, structs)
if typ != nil {
return &Pointer{To: typ}
if typ == Any {
return AnyPointer
}
return nil
return &Pointer{To: typ}
}
if strings.HasPrefix(name, "[]") {
to := strings.TrimPrefix(name, "[]")
typ := ByName(to, pkg, structs)
if typ != nil {
return &Array{Of: typ}
if typ == Any {
return AnyArray
}
return nil
return &Array{Of: typ}
}
switch name {
case "Any":
return Any
case "Int":
return Int
case "Int64":
@ -45,8 +47,6 @@ func ByName(name string, pkg string, structs map[string]*Struct) Type {
return Float64
case "Float32":
return Float32
case "Pointer":
return AnyPointer
}
typ, exists := structs[pkg+"."+name]

18
src/types/Common.go Normal file

@ -0,0 +1,18 @@
package types
var (
Any = &Base{name: "Any", size: 0}
AnyArray = &Array{Of: Any}
AnyPointer = &Pointer{To: Any}
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}
)
var (
Int = Int64
Float = Float64
)

@ -1,7 +0,0 @@
package types
var (
Float64 = &Base{name: "Float64", size: 8}
Float32 = &Base{name: "Float32", size: 4}
Float = Float64
)

@ -1,9 +0,0 @@
package types
var (
Int64 = &Base{name: "Int64", size: 8}
Int32 = &Base{name: "Int32", size: 4}
Int16 = &Base{name: "Int16", size: 2}
Int8 = &Base{name: "Int8", size: 1}
Int = Int64
)

@ -2,30 +2,26 @@ 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 == nil {
return true
}
if a == b {
if a == b || b == Any || a == nil {
return true
}
aPointer, aIsPointer := a.(*Pointer)
bPointer, bIsPointer := b.(*Pointer)
if aIsPointer && bIsPointer && (bPointer.To == nil || aPointer.To == bPointer.To) {
if aIsPointer && bIsPointer && (bPointer.To == Any || aPointer.To == bPointer.To) {
return true
}
aArray, aIsArray := a.(*Array)
if aIsArray && bIsPointer && (bPointer.To == nil || aArray.Of == bPointer.To) {
if aIsArray && bIsPointer && (bPointer.To == Any || aArray.Of == bPointer.To) {
return true
}
bArray, bIsArray := b.(*Array)
if aIsArray && bIsArray && aArray.Of == bArray.Of {
if aIsArray && bIsArray && (bArray.Of == Any || aArray.Of == bArray.Of) {
return true
}

@ -1,7 +1,5 @@
package types
var AnyPointer = &Pointer{To: nil}
// Pointer is the address of an object.
type Pointer struct {
To Type
@ -10,7 +8,7 @@ type Pointer struct {
// Name returns the type name.
func (p *Pointer) Name() string {
if p.To == nil {
return "Pointer"
return "*Any"
}
return "*" + p.To.Name()

@ -9,7 +9,8 @@ import (
func TestName(t *testing.T) {
assert.Equal(t, types.Int.Name(), "Int64")
assert.Equal(t, types.AnyPointer.Name(), "Pointer")
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(), "[]Int8")
@ -21,6 +22,7 @@ func TestSize(t *testing.T) {
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)
@ -39,9 +41,12 @@ func TestStruct(t *testing.T) {
}
func TestBasics(t *testing.T) {
assert.True(t, types.Is(types.Int, types.Any))
assert.True(t, types.Is(types.Int, 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.Pointer{To: types.Int}, &types.Pointer{To: types.Float}))
}
@ -55,5 +60,9 @@ func TestSpecialCases(t *testing.T) {
// 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))
}