diff --git a/examples/collatz/collatz.q b/examples/collatz/collatz.q index f7d9037..5f71d2c 100644 --- a/examples/collatz/collatz.q +++ b/examples/collatz/collatz.q @@ -1,5 +1,5 @@ +import io import log -import sys main() { collatz(12) @@ -19,6 +19,6 @@ collatz(x Int) { return } - sys.write(1, " ", 1) + io.out(" ") } } \ No newline at end of file diff --git a/examples/fizzbuzz/fizzbuzz.q b/examples/fizzbuzz/fizzbuzz.q index 5cca2d5..7823731 100644 --- a/examples/fizzbuzz/fizzbuzz.q +++ b/examples/fizzbuzz/fizzbuzz.q @@ -1,5 +1,5 @@ +import io import log -import sys main() { fizzbuzz(15) @@ -10,9 +10,9 @@ fizzbuzz(n Int) { loop { switch { - x % 15 == 0 { print("FizzBuzz", 8) } - x % 5 == 0 { print("Buzz", 4) } - x % 3 == 0 { print("Fizz", 4) } + x % 15 == 0 { io.out("FizzBuzz") } + x % 5 == 0 { io.out("Buzz") } + x % 3 == 0 { io.out("Fizz") } _ { log.number(x) } } @@ -22,10 +22,6 @@ fizzbuzz(n Int) { return } - print(" ", 1) + io.out(" ") } -} - -print(address Pointer, length Int) { - sys.write(1, address, length) } \ No newline at end of file diff --git a/examples/hello/hello.q b/examples/hello/hello.q index 1d9220d..0475212 100644 --- a/examples/hello/hello.q +++ b/examples/hello/hello.q @@ -1,9 +1,5 @@ -import sys +import io main() { - print("Hello\n", 6) -} - -print(address Pointer, length Int) { - sys.write(1, address, length) + io.out("Hello\n") } \ No newline at end of file diff --git a/examples/prime/prime.q b/examples/prime/prime.q index 836c831..76e3d77 100644 --- a/examples/prime/prime.q +++ b/examples/prime/prime.q @@ -1,5 +1,5 @@ +import io import log -import sys main() { n := 100 @@ -12,7 +12,7 @@ main() { if isPrime(i) == 1 { if i != 2 { - sys.write(1, " ", 1) + io.out(" ") } log.number(i) diff --git a/examples/server/server.q b/examples/server/server.q index a729032..6ac4ee3 100644 --- a/examples/server/server.q +++ b/examples/server/server.q @@ -1,6 +1,7 @@ // Open server and client in 2 terminals: // [1] q run examples/server // [2] curl http://127.0.0.1:8080 +import io import net import sys @@ -8,30 +9,30 @@ main() { socket := sys.socket(2, 1, 0) if socket < 0 { - sys.write(1, "socket error\n", 13) + io.error("socket error\n") sys.exit(1) } if net.bind(socket, 8080) != 0 { - sys.write(1, "bind error\n", 11) + io.error("bind error\n") sys.exit(1) } if sys.listen(socket, 128) != 0 { - sys.write(1, "listen error\n", 13) + io.error("listen error\n") sys.exit(1) } - sys.write(1, "listening...\n", 13) + io.out("listening...\n") loop { conn := sys.accept(socket, 0, 0) if conn >= 0 { - sys.write(conn, "HTTP/1.0 200 OK\r\nContent-Length: 6\r\n\r\nHello\n", 44) + io.write(conn, "HTTP/1.0 200 OK\r\nContent-Length: 6\r\n\r\nHello\n") sys.close(conn) } else { - sys.write(1, "error\n", 6) + io.error("accept error\n") } } } \ No newline at end of file diff --git a/examples/shell/shell.q b/examples/shell/shell.q index 0b094f6..3353391 100644 --- a/examples/shell/shell.q +++ b/examples/shell/shell.q @@ -1,3 +1,4 @@ +import io import mem import sys @@ -9,8 +10,8 @@ main() { info := mem.alloc(24) loop { - sys.write(1, "$ ", 2) - n := sys.read(0, command, length) + io.out("$ ") + n := io.in(command) if n <= 0 { return diff --git a/examples/thread/thread.q b/examples/thread/thread.q index 929ebbb..22c9d05 100644 --- a/examples/thread/thread.q +++ b/examples/thread/thread.q @@ -1,3 +1,4 @@ +import io import sys import thread import time @@ -10,8 +11,8 @@ main() { } work() { - sys.write(1, "[ ] start\n", 10) + io.out("[ ] start\n") time.sleep(10 * 1000 * 1000) - sys.write(1, "[x] end\n", 8) + io.out("[x] end\n") sys.exit(0) } \ No newline at end of file diff --git a/lib/io/io.q b/lib/io/io.q new file mode 100644 index 0000000..4349f94 --- /dev/null +++ b/lib/io/io.q @@ -0,0 +1,21 @@ +import sys + +in(buffer Pointer) -> Int { + return sys.read(0, buffer, len(buffer)) +} + +out(message Pointer) -> Int { + return sys.write(1, message, len(message)) +} + +error(message Pointer) -> Int { + return sys.write(2, message, len(message)) +} + +read(fd Int, buffer Pointer) -> Int { + return sys.read(fd, buffer, len(buffer)) +} + +write(fd Int, message Pointer) -> Int { + return sys.write(fd, message, len(message)) +} \ No newline at end of file diff --git a/lib/mem/alloc_linux.q b/lib/mem/alloc_linux.q index 70929fe..df8fe5b 100644 --- a/lib/mem/alloc_linux.q +++ b/lib/mem/alloc_linux.q @@ -1,5 +1,12 @@ import sys alloc(length Int) -> Pointer { - return sys.mmap(0, length, 0x1|0x2, 0x02|0x20) + x := sys.mmap(0, length+8, 0x1|0x2, 0x02|0x20) + + if x <= 0 { + return x + } + + store(x, 8, length) + return x + 8 } \ No newline at end of file diff --git a/lib/mem/alloc_mac.q b/lib/mem/alloc_mac.q index bdeb356..42da24b 100644 --- a/lib/mem/alloc_mac.q +++ b/lib/mem/alloc_mac.q @@ -1,5 +1,12 @@ import sys alloc(length Int) -> Pointer { - return sys.mmap(0, length, 0x1|0x2, 0x02|0x1000) + x := sys.mmap(0, length, 0x1|0x2, 0x02|0x1000) + + if x <= 0 { + return x + } + + store(x, 8, length) + return x + 8 } \ No newline at end of file diff --git a/lib/mem/alloc_windows.q b/lib/mem/alloc_windows.q index a6fafde..616fb84 100644 --- a/lib/mem/alloc_windows.q +++ b/lib/mem/alloc_windows.q @@ -1,5 +1,12 @@ import sys alloc(length Int) -> Pointer { - return sys.mmap(0, length, 0x0004, 0x3000) + x := sys.mmap(0, length, 0x0004, 0x3000) + + if x <= 0 { + return x + } + + store(x, 8, length) + return x + 8 } \ No newline at end of file diff --git a/lib/sys/io_windows.q b/lib/sys/io_windows.q index 0bf7b09..a07a586 100644 --- a/lib/sys/io_windows.q +++ b/lib/sys/io_windows.q @@ -1,3 +1,9 @@ +read(fd Int, address Pointer, length Int) -> Int { + kernel32.ReadFile(fd, address, length) + return length +} + write(fd Int, address Pointer, length Int) -> Int { - return kernel32.WriteFile(fd, address, length) + kernel32.WriteFile(fd, address, length) + return length } \ No newline at end of file diff --git a/src/asm/Assembler.go b/src/asm/Assembler.go index c5c9b8d..3f3cb88 100644 --- a/src/asm/Assembler.go +++ b/src/asm/Assembler.go @@ -24,5 +24,5 @@ func (a *Assembler) SetData(label string, bytes []byte) { a.Data = data.Data{} } - a.Data[label] = bytes + a.Data.Insert(label, bytes) } diff --git a/src/asm/Memory.go b/src/asm/Memory.go index 1c0cc4e..e97924e 100644 --- a/src/asm/Memory.go +++ b/src/asm/Memory.go @@ -4,7 +4,7 @@ import "git.akyoto.dev/cli/q/src/cpu" type Memory struct { Base cpu.Register - Offset byte + Offset int8 OffsetRegister cpu.Register Length byte } diff --git a/src/asmc/resolvePointers.go b/src/asmc/resolvePointers.go index 748af18..0dc879e 100644 --- a/src/asmc/resolvePointers.go +++ b/src/asmc/resolvePointers.go @@ -80,7 +80,7 @@ restart: dataStart, _ := fs.Align(c.codeStart+Address(len(c.code)), config.Align) for _, pointer := range c.dataPointers { - address := config.BaseAddress + dataStart + pointer.Resolve() + address := config.BaseAddress + dataStart + pointer.Resolve() + 8 slice := c.code[pointer.Position : pointer.Position+4] binary.LittleEndian.PutUint32(slice, uint32(address)) } diff --git a/src/core/CompileAssignArray.go b/src/core/CompileAssignArray.go index 08f73e6..1cab08a 100644 --- a/src/core/CompileAssignArray.go +++ b/src/core/CompileAssignArray.go @@ -40,7 +40,7 @@ func (f *Function) CompileAssignArray(node *ast.Assign) error { return err } - memory.Offset = byte(offset) + memory.Offset = int8(offset) } else { _, indexRegister, isTemporary, err := f.Evaluate(index) diff --git a/src/core/CompileAssignField.go b/src/core/CompileAssignField.go index 852ae2d..412be15 100644 --- a/src/core/CompileAssignField.go +++ b/src/core/CompileAssignField.go @@ -32,7 +32,7 @@ func (f *Function) CompileAssignField(node *ast.Assign) error { memory := asm.Memory{ Base: variable.Register, - Offset: byte(field.Offset), + Offset: int8(field.Offset), OffsetRegister: math.MaxUint8, Length: byte(field.Type.Size()), } diff --git a/src/core/CompileCall.go b/src/core/CompileCall.go index 46b687c..69db515 100644 --- a/src/core/CompileCall.go +++ b/src/core/CompileCall.go @@ -27,6 +27,14 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) { name = nameNode.Token.Text(f.File.Bytes) switch name { + case "len": + return &Function{ + Output: []*Output{ + { + Type: types.Int, + }, + }, + }, f.CompileLen(root) case "syscall": return nil, f.CompileSyscall(root) case "new": diff --git a/src/core/CompileLen.go b/src/core/CompileLen.go new file mode 100644 index 0000000..cb7cb44 --- /dev/null +++ b/src/core/CompileLen.go @@ -0,0 +1,26 @@ +package core + +import ( + "math" + + "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/expression" +) + +// CompileLen returns the length of a slice. +func (f *Function) CompileLen(root *expression.Expression) error { + _, register, isTemporary, err := f.Evaluate(root.Children[1]) + + if err != nil { + return err + } + + f.SaveRegister(f.CPU.Output[0]) + f.MemoryRegister(asm.LOAD, asm.Memory{Base: register, Offset: -8, OffsetRegister: math.MaxUint8, Length: 8}, f.CPU.Output[0]) + + if isTemporary { + f.FreeRegister(register) + } + + return nil +} diff --git a/src/core/ExpressionToRegister.go b/src/core/ExpressionToRegister.go index 17f3feb..a72b381 100644 --- a/src/core/ExpressionToRegister.go +++ b/src/core/ExpressionToRegister.go @@ -40,7 +40,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp if node.Token.Kind == token.Array { array := f.VariableByName(node.Children[0].Token.Text(f.File.Bytes)) offset, err := f.Number(node.Children[1].Token) - f.MemoryRegister(asm.LOAD, asm.Memory{Base: array.Register, Offset: byte(offset), Length: 1}, register) + f.MemoryRegister(asm.LOAD, asm.Memory{Base: array.Register, Offset: int8(offset), Length: 1}, register) return types.Int, err } @@ -51,7 +51,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp right := node.Children[1] name = right.Token.Text(f.File.Bytes) field := variable.Type.(*types.Pointer).To.(*types.Struct).FieldByName(name) - f.MemoryRegister(asm.LOAD, asm.Memory{Base: variable.Register, Offset: byte(field.Offset), Length: byte(field.Type.Size())}, register) + f.MemoryRegister(asm.LOAD, asm.Memory{Base: variable.Register, Offset: int8(field.Offset), Length: byte(field.Type.Size())}, register) return field.Type, nil } diff --git a/src/core/TokenToRegister.go b/src/core/TokenToRegister.go index af65813..d2daab6 100644 --- a/src/core/TokenToRegister.go +++ b/src/core/TokenToRegister.go @@ -1,6 +1,8 @@ package core import ( + "encoding/binary" + "git.akyoto.dev/cli/q/src/asm" "git.akyoto.dev/cli/q/src/cpu" "git.akyoto.dev/cli/q/src/errors" @@ -45,7 +47,12 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (types. case token.String: data := t.Bytes(f.File.Bytes) data = String(data) - label := f.AddBytes(data) + + slice := make([]byte, len(data)+8) + binary.LittleEndian.PutUint64(slice, uint64(len(data))) + copy(slice[8:], data) + + label := f.AddBytes(slice) f.SaveRegister(register) f.RegisterLabel(asm.MOVE, register, label) return types.PointerAny, nil diff --git a/src/register/SaveRegister.go b/src/register/SaveRegister.go index c5a56c9..c0aa17b 100644 --- a/src/register/SaveRegister.go +++ b/src/register/SaveRegister.go @@ -26,4 +26,5 @@ func (f *Machine) SaveRegister(register cpu.Register) { newRegister := f.NewRegister() f.RegisterRegister(asm.MOVE, newRegister, register) variable.Register = newRegister + f.FreeRegister(register) } diff --git a/src/x86/Load.go b/src/x86/Load.go index ae32daa..1a56236 100644 --- a/src/x86/Load.go +++ b/src/x86/Load.go @@ -3,6 +3,6 @@ package x86 import "git.akyoto.dev/cli/q/src/cpu" // LoadRegister loads from memory into a register. -func LoadRegister(code []byte, destination cpu.Register, offset byte, length byte, source cpu.Register) []byte { +func LoadRegister(code []byte, destination cpu.Register, offset int8, length byte, source cpu.Register) []byte { return memoryAccess(code, 0x8A, 0x8B, source, offset, length, destination) } diff --git a/src/x86/Load_test.go b/src/x86/Load_test.go index 2633e09..32d4405 100644 --- a/src/x86/Load_test.go +++ b/src/x86/Load_test.go @@ -12,7 +12,7 @@ func TestLoadRegister(t *testing.T) { usagePatterns := []struct { Destination cpu.Register Source cpu.Register - Offset byte + Offset int8 Length byte Code []byte }{ diff --git a/src/x86/Store.go b/src/x86/Store.go index 0bf06e0..c0fc055 100644 --- a/src/x86/Store.go +++ b/src/x86/Store.go @@ -7,7 +7,7 @@ import ( ) // StoreNumber stores a number into the memory address included in the given register. -func StoreNumber(code []byte, register cpu.Register, offset byte, length byte, number int) []byte { +func StoreNumber(code []byte, register cpu.Register, offset int8, length byte, number int) []byte { code = memoryAccess(code, 0xC6, 0xC7, register, offset, length, 0b000) switch length { @@ -22,6 +22,6 @@ func StoreNumber(code []byte, register cpu.Register, offset byte, length byte, n } // StoreRegister stores the contents of the `source` register into the memory address included in the given register. -func StoreRegister(code []byte, register cpu.Register, offset byte, length byte, source cpu.Register) []byte { +func StoreRegister(code []byte, register cpu.Register, offset int8, length byte, source cpu.Register) []byte { return memoryAccess(code, 0x88, 0x89, register, offset, length, source) } diff --git a/src/x86/Store_test.go b/src/x86/Store_test.go index e9e1e26..7d34503 100644 --- a/src/x86/Store_test.go +++ b/src/x86/Store_test.go @@ -11,7 +11,7 @@ import ( func TestStoreNumber(t *testing.T) { usagePatterns := []struct { Register cpu.Register - Offset byte + Offset int8 Length byte Number int Code []byte @@ -159,7 +159,7 @@ func TestStoreNumber(t *testing.T) { func TestStoreRegister(t *testing.T) { usagePatterns := []struct { RegisterTo cpu.Register - Offset byte + Offset int8 Length byte RegisterFrom cpu.Register Code []byte diff --git a/src/x86/memoryAccess.go b/src/x86/memoryAccess.go index b1dd605..af526a0 100644 --- a/src/x86/memoryAccess.go +++ b/src/x86/memoryAccess.go @@ -3,7 +3,7 @@ package x86 import "git.akyoto.dev/cli/q/src/cpu" // memoryAccess encodes a memory access. -func memoryAccess(code []byte, opCode8 byte, opCode32 byte, register cpu.Register, offset byte, numBytes byte, source cpu.Register) []byte { +func memoryAccess(code []byte, opCode8 byte, opCode32 byte, register cpu.Register, offset int8, numBytes byte, source cpu.Register) []byte { opCode := opCode32 if numBytes == 1 { @@ -27,7 +27,7 @@ func memoryAccess(code []byte, opCode8 byte, opCode32 byte, register cpu.Registe } if mod == AddressMemoryOffset8 { - code = append(code, offset) + code = append(code, byte(offset)) } return code