From f19d9063a5863b975eb00f679b65d602ead2398a Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Sun, 9 Feb 2025 23:52:07 +0100 Subject: [PATCH] Improved type system --- examples/array/array.q | 2 +- examples/echo/echo.q | 2 +- examples/point/point.q | 2 +- lib/log/number.q | 4 ++-- lib/mem/free.q | 4 ++-- lib/sys/fs_linux.q | 12 +++++----- lib/sys/io_linux.q | 8 +++---- lib/sys/io_mac.q | 8 +++---- lib/sys/io_windows.q | 4 ++-- lib/sys/mem_linux.q | 4 ++-- lib/sys/mem_mac.q | 4 ++-- lib/sys/mem_windows.q | 4 ++-- lib/sys/net_linux.q | 4 ++-- lib/sys/net_mac.q | 2 +- lib/sys/proc_linux.q | 6 ++--- lib/sys/proc_mac.q | 4 ++-- lib/thread/thread_linux.q | 2 +- src/core/CompileCall.go | 34 ++++++++++++++++++++++------ src/errors/ParameterCountMismatch.go | 19 ++++++++++++++++ src/types/Array.go | 4 ++++ src/types/ByName.go | 16 ++++++------- src/types/Common.go | 18 +++++++++++++++ src/types/Float.go | 7 ------ src/types/Int.go | 9 -------- src/types/Is.go | 12 ++++------ src/types/Pointer.go | 4 +--- src/types/types_test.go | 11 ++++++++- tests/errors/TypeMismatch.q | 2 +- tests/errors_test.go | 2 +- tests/programs/memory-free.q | 3 +-- 30 files changed, 132 insertions(+), 85 deletions(-) create mode 100644 src/errors/ParameterCountMismatch.go create mode 100644 src/types/Common.go delete mode 100644 src/types/Float.go delete mode 100644 src/types/Int.go diff --git a/examples/array/array.q b/examples/array/array.q index a57691a..4f17ef0 100644 --- a/examples/array/array.q +++ b/examples/array/array.q @@ -10,5 +10,5 @@ main() { address[3] = 'l' address[4] = 'o' sys.write(1, address, length) - mem.free(address, length) + mem.free(address) } \ No newline at end of file diff --git a/examples/echo/echo.q b/examples/echo/echo.q index fb1159e..7e0483b 100644 --- a/examples/echo/echo.q +++ b/examples/echo/echo.q @@ -9,7 +9,7 @@ main() { n := sys.read(0, address, length) if n <= 0 { - mem.free(address, length) + mem.free(address) return } diff --git a/examples/point/point.q b/examples/point/point.q index ccb57c9..4026af6 100644 --- a/examples/point/point.q +++ b/examples/point/point.q @@ -30,5 +30,5 @@ print(p *Point) { out[6] = '0' + p.y out[7] = '\n' sys.write(1, out, 8) - mem.free(out, 8) + mem.free(out) } \ No newline at end of file diff --git a/lib/log/number.q b/lib/log/number.q index 93b7388..0dc6e55 100644 --- a/lib/log/number.q +++ b/lib/log/number.q @@ -6,10 +6,10 @@ number(x Int) { buffer := mem.alloc(length) address, count := itoa(x, buffer, length) sys.write(1, address, count) - mem.free(buffer, length) + mem.free(buffer) } -itoa(x Int, buffer Pointer, length Int) -> (Pointer, Int) { +itoa(x Int, buffer *Any, length Int) -> (*Any, Int) { end := buffer + length tmp := end digit := 0 diff --git a/lib/mem/free.q b/lib/mem/free.q index 17be5bd..2568b2b 100644 --- a/lib/mem/free.q +++ b/lib/mem/free.q @@ -1,5 +1,5 @@ import sys -free(address Pointer, length Int) -> Int { - return sys.munmap(address-8, length+8) +free(address []Any) -> Int { + return sys.munmap(address-8, len(address)+8) } \ No newline at end of file diff --git a/lib/sys/fs_linux.q b/lib/sys/fs_linux.q index dc1bd11..11b7503 100644 --- a/lib/sys/fs_linux.q +++ b/lib/sys/fs_linux.q @@ -1,23 +1,23 @@ -getcwd(buffer Pointer, length Int) -> Int { +getcwd(buffer *Any, length Int) -> Int { return syscall(79, buffer, length) } -chdir(path Pointer) -> Int { +chdir(path *Any) -> Int { return syscall(80, path) } -rename(old Pointer, new Pointer) -> Int { +rename(old *Any, new *Any) -> Int { return syscall(82, old, new) } -mkdir(path Pointer, mode Int) -> Int { +mkdir(path *Any, mode Int) -> Int { return syscall(83, path, mode) } -rmdir(path Pointer) -> Int { +rmdir(path *Any) -> Int { return syscall(84, path) } -unlink(file Pointer) -> Int { +unlink(file *Any) -> Int { return syscall(87, file) } \ No newline at end of file diff --git a/lib/sys/io_linux.q b/lib/sys/io_linux.q index 0bfae7e..9a3b2c5 100644 --- a/lib/sys/io_linux.q +++ b/lib/sys/io_linux.q @@ -1,13 +1,13 @@ -read(fd Int, address Pointer, length Int) -> Int { +read(fd Int, address *Any, length Int) -> Int { return syscall(0, fd, address, length) } -write(fd Int, address Pointer, length Int) -> Int { +write(fd Int, address *Any, length Int) -> Int { return syscall(1, fd, address, length) } -open(file Pointer, flags Int, mode Int) -> Int { - return syscall(2, file, flags, mode) +open(path *Any, flags Int, mode Int) -> Int { + return syscall(2, path, flags, mode) } close(fd Int) -> Int { diff --git a/lib/sys/io_mac.q b/lib/sys/io_mac.q index 17b07aa..4ef451a 100644 --- a/lib/sys/io_mac.q +++ b/lib/sys/io_mac.q @@ -1,13 +1,13 @@ -read(fd Int, address Pointer, length Int) -> Int { +read(fd Int, address *Any, length Int) -> Int { return syscall(0x2000003, fd, address, length) } -write(fd Int, address Pointer, length Int) -> Int { +write(fd Int, address *Any, length Int) -> Int { return syscall(0x2000004, fd, address, length) } -open(file Pointer, flags Int, mode Int) -> Int { - return syscall(0x2000005, file, flags, mode) +open(path *Any, flags Int, mode Int) -> Int { + return syscall(0x2000005, path, flags, mode) } close(fd Int) -> Int { diff --git a/lib/sys/io_windows.q b/lib/sys/io_windows.q index c890b14..964a201 100644 --- a/lib/sys/io_windows.q +++ b/lib/sys/io_windows.q @@ -1,9 +1,9 @@ -read(fd Int, address Pointer, length Int) -> Int { +read(fd Int, address *Any, length Int) -> Int { kernel32.ReadFile(fd, address, length) return length } -write(fd Int, address Pointer, length Int) -> Int { +write(fd Int, address *Any, length Int) -> Int { fd = kernel32.GetStdHandle(-10 - fd) kernel32.WriteConsoleA(fd, address, length, 0) return length diff --git a/lib/sys/mem_linux.q b/lib/sys/mem_linux.q index 5d8bb7c..5821426 100644 --- a/lib/sys/mem_linux.q +++ b/lib/sys/mem_linux.q @@ -1,7 +1,7 @@ -mmap(address Int, length Int, protection Int, flags Int) -> Pointer { +mmap(address Int, length Int, protection Int, flags Int) -> *Any { return syscall(9, address, length, protection, flags) } -munmap(address Pointer, length Int) -> Int { +munmap(address *Any, length Int) -> Int { return syscall(11, address, length) } \ No newline at end of file diff --git a/lib/sys/mem_mac.q b/lib/sys/mem_mac.q index cbae713..6bf509d 100644 --- a/lib/sys/mem_mac.q +++ b/lib/sys/mem_mac.q @@ -1,7 +1,7 @@ -mmap(address Int, length Int, protection Int, flags Int) -> Pointer { +mmap(address Int, length Int, protection Int, flags Int) -> *Any { return syscall(0x20000C5, address, length, protection, flags) } -munmap(address Pointer, length Int) -> Int { +munmap(address *Any, length Int) -> Int { return syscall(0x2000049, address, length) } \ No newline at end of file diff --git a/lib/sys/mem_windows.q b/lib/sys/mem_windows.q index c9ada81..319e966 100644 --- a/lib/sys/mem_windows.q +++ b/lib/sys/mem_windows.q @@ -1,7 +1,7 @@ -mmap(address Int, length Int, protection Int, flags Int) -> Pointer { +mmap(address Int, length Int, protection Int, flags Int) -> *Any { return kernel32.VirtualAlloc(address, length, flags, protection) } -munmap(address Pointer, length Int) -> Int { +munmap(address *Any, length Int) -> Int { return kernel32.VirtualFree(address, length, 0x4000) } \ No newline at end of file diff --git a/lib/sys/net_linux.q b/lib/sys/net_linux.q index dc6b3eb..6e2e53c 100644 --- a/lib/sys/net_linux.q +++ b/lib/sys/net_linux.q @@ -9,7 +9,7 @@ socket(family Int, type Int, protocol Int) -> Int { return syscall(41, family, type, protocol) } -accept(fd Int, address Pointer, length Int) -> Int { +accept(fd Int, address *Any, length Int) -> Int { return syscall(43, fd, address, length) } @@ -21,6 +21,6 @@ listen(fd Int, backlog Int) -> Int { return syscall(50, fd, backlog) } -setsockopt(fd Int, level Int, optname Int, optval Pointer, optlen Int) -> Int { +setsockopt(fd Int, level Int, optname Int, optval *Any, optlen Int) -> Int { return syscall(54, fd, level, optname, optval, optlen) } \ No newline at end of file diff --git a/lib/sys/net_mac.q b/lib/sys/net_mac.q index 9a98067..ca0a735 100644 --- a/lib/sys/net_mac.q +++ b/lib/sys/net_mac.q @@ -10,7 +10,7 @@ socket(family Int, type Int, protocol Int) -> Int { return syscall(0x2000061, family, type, protocol) } -accept(fd Int, address Pointer, length Int) -> Int { +accept(fd Int, address *Any, length Int) -> Int { return syscall(0x200001E, fd, address, length) } diff --git a/lib/sys/proc_linux.q b/lib/sys/proc_linux.q index 2ef2ec7..3540b0c 100644 --- a/lib/sys/proc_linux.q +++ b/lib/sys/proc_linux.q @@ -1,4 +1,4 @@ -clone(flags Int, stack Pointer) -> Int { +clone(flags Int, stack *Any) -> Int { return syscall(56, flags, stack) } @@ -6,7 +6,7 @@ fork() -> Int { return syscall(57) } -execve(path Pointer, argv Pointer, envp Pointer) -> Int { +execve(path *Any, argv *Any, envp *Any) -> Int { return syscall(59, path, argv, envp) } @@ -14,6 +14,6 @@ exit(status Int) { syscall(60, status) } -waitid(type Int, id Int, info Pointer, options Int) -> Int { +waitid(type Int, id Int, info *Any, options Int) -> Int { return syscall(247, type, id, info, options) } \ No newline at end of file diff --git a/lib/sys/proc_mac.q b/lib/sys/proc_mac.q index bf70e55..aa3454a 100644 --- a/lib/sys/proc_mac.q +++ b/lib/sys/proc_mac.q @@ -6,10 +6,10 @@ fork() -> Int { return syscall(0x2000002) } -execve(path Pointer, argv Pointer, envp Pointer) -> Int { +execve(path *Any, argv *Any, envp *Any) -> Int { return syscall(0x200003B, path, argv, envp) } -waitid(type Int, id Int, info Pointer, options Int) -> Int { +waitid(type Int, id Int, info *Any, options Int) -> Int { return syscall(0x20000AD, type, id, info, options) } \ No newline at end of file diff --git a/lib/thread/thread_linux.q b/lib/thread/thread_linux.q index eaa873b..be70f4f 100644 --- a/lib/thread/thread_linux.q +++ b/lib/thread/thread_linux.q @@ -1,6 +1,6 @@ import sys -create(func Pointer) -> Int { +create(func *Any) -> Int { size := 4096 stack := sys.mmap(0, size, 0x1|0x2, 0x02|0x20|0x100|0x20000) rip := stack + size - 8 diff --git a/src/core/CompileCall.go b/src/core/CompileCall.go index 69db515..fa0ffce 100644 --- a/src/core/CompileCall.go +++ b/src/core/CompileCall.go @@ -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) } diff --git a/src/errors/ParameterCountMismatch.go b/src/errors/ParameterCountMismatch.go new file mode 100644 index 0000000..d626e10 --- /dev/null +++ b/src/errors/ParameterCountMismatch.go @@ -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) +} diff --git a/src/types/Array.go b/src/types/Array.go index fd6add2..d46b216 100644 --- a/src/types/Array.go +++ b/src/types/Array.go @@ -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() } diff --git a/src/types/ByName.go b/src/types/ByName.go index 9c43a19..365e55b 100644 --- a/src/types/ByName.go +++ b/src/types/ByName.go @@ -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] diff --git a/src/types/Common.go b/src/types/Common.go new file mode 100644 index 0000000..6f1918f --- /dev/null +++ b/src/types/Common.go @@ -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 +) diff --git a/src/types/Float.go b/src/types/Float.go deleted file mode 100644 index cbfc638..0000000 --- a/src/types/Float.go +++ /dev/null @@ -1,7 +0,0 @@ -package types - -var ( - Float64 = &Base{name: "Float64", size: 8} - Float32 = &Base{name: "Float32", size: 4} - Float = Float64 -) diff --git a/src/types/Int.go b/src/types/Int.go deleted file mode 100644 index f07d7c6..0000000 --- a/src/types/Int.go +++ /dev/null @@ -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 -) diff --git a/src/types/Is.go b/src/types/Is.go index 059086f..438b917 100644 --- a/src/types/Is.go +++ b/src/types/Is.go @@ -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 } diff --git a/src/types/Pointer.go b/src/types/Pointer.go index cc08bd3..1cf180b 100644 --- a/src/types/Pointer.go +++ b/src/types/Pointer.go @@ -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() diff --git a/src/types/types_test.go b/src/types/types_test.go index f8f3818..e19602c 100644 --- a/src/types/types_test.go +++ b/src/types/types_test.go @@ -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)) } diff --git a/tests/errors/TypeMismatch.q b/tests/errors/TypeMismatch.q index 4da2421..57bdd6b 100644 --- a/tests/errors/TypeMismatch.q +++ b/tests/errors/TypeMismatch.q @@ -2,6 +2,6 @@ main() { writeToMemory(42) } -writeToMemory(p Pointer) { +writeToMemory(p *Any) { p[0] = 'A' } \ No newline at end of file diff --git a/tests/errors_test.go b/tests/errors_test.go index 24c6b52..92f12a1 100644 --- a/tests/errors_test.go +++ b/tests/errors_test.go @@ -46,7 +46,7 @@ var errs = []struct { {"MissingParameter3.q", errors.MissingParameter}, {"MissingType.q", errors.MissingType}, {"ReturnCountMismatch.q", &errors.ReturnCountMismatch{Count: 1, ExpectedCount: 0}}, - {"TypeMismatch.q", &errors.TypeMismatch{Expected: "Pointer", Encountered: "Int64", ParameterName: "p"}}, + {"TypeMismatch.q", &errors.TypeMismatch{Expected: "*Any", Encountered: "Int64", ParameterName: "p"}}, {"UnknownFunction.q", &errors.UnknownFunction{Name: "unknown"}}, {"UnknownFunction2.q", &errors.UnknownFunction{Name: "f"}}, {"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}}, diff --git a/tests/programs/memory-free.q b/tests/programs/memory-free.q index a584ef3..7fb937b 100644 --- a/tests/programs/memory-free.q +++ b/tests/programs/memory-free.q @@ -2,6 +2,5 @@ import mem main() { address := mem.alloc(1024) - err := mem.free(address, 1024) - assert err == 0 + assert mem.free(address) == 0 } \ No newline at end of file