From b8ceafa3c987f63f36fefb5518a1bbb7b61aab19 Mon Sep 17 00:00:00 2001 From: William Bell Date: Thu, 22 Jun 2023 09:27:21 +0100 Subject: [PATCH] add built in socket lib --- src/built-ins.go | 1 + src/socket.go | 132 ++++++++++++++++++++++++++++++++++++++++ tests/memoryLeakTest.ar | 2 +- 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 src/socket.go diff --git a/src/built-ins.go b/src/built-ins.go index e96f30c..525c2d3 100644 --- a/src/built-ins.go +++ b/src/built-ins.go @@ -11,6 +11,7 @@ func makeGlobal() ArObject { vars["term"] = ArTerm vars["number"] = builtinFunc{"number", ArgonNumber} vars["string"] = builtinFunc{"string", ArgonString} + vars["socket"] = builtinFunc{"boolean", ArSocket} vars["infinity"] = infinity vars["map"] = builtinFunc{"map", func(a ...any) (any, ArErr) { if len(a) == 0 { diff --git a/src/socket.go b/src/socket.go new file mode 100644 index 0000000..daa8209 --- /dev/null +++ b/src/socket.go @@ -0,0 +1,132 @@ +package main + +import ( + "fmt" + "net" +) + +func ArSocket(args ...any) (any, ArErr) { + if len(args) != 2 { + return ArObject{}, ArErr{ + TYPE: "SocketError", + message: "Socket takes exactly 2 arguments", + EXISTS: true, + } + } else if typeof(args[0]) != "string" { + return ArObject{}, ArErr{ + TYPE: "SocketError", + message: "Socket type must be a string", + EXISTS: true, + } + } else if typeof(args[1]) != "number" { + return ArObject{}, ArErr{ + TYPE: "SocketError", + message: "Socket port must be a number", + EXISTS: true, + } + } + networktype := ArValidToAny(args[0]).(string) + port := args[1].(number) + if port.Denom().Int64() != 1 { + return ArObject{}, ArErr{ + TYPE: "SocketError", + message: "Socket port must be an integer", + EXISTS: true, + } + } + ln, err := net.Listen(networktype, ":"+fmt.Sprint(port.Num().Int64())) + fmt.Println("Listening on port " + fmt.Sprint(port.Num().Int64())) + if err != nil { + return ArObject{}, ArErr{ + TYPE: "SocketError", + message: err.Error(), + EXISTS: true, + } + } + return Map(anymap{ + "accept": builtinFunc{ + "accept", + func(args ...any) (any, ArErr) { + conn, err := ln.Accept() + if err != nil { + return ArObject{}, ArErr{ + TYPE: "SocketError", + message: err.Error(), + EXISTS: true, + } + } + return Map(anymap{ + "read": builtinFunc{ + "read", + func(args ...any) (any, ArErr) { + if len(args) != 1 { + return ArObject{}, ArErr{ + TYPE: "SocketError", + message: "Socket.read() takes exactly 1 argument", + EXISTS: true, + } + } else if typeof(args[0]) != "number" { + return ArObject{}, ArErr{ + TYPE: "SocketError", + message: "Socket.read() argument must be a number", + EXISTS: true, + } + } + buf := make([]byte, args[0].(number).Num().Int64()) + n, err := conn.Read(buf) + if err != nil { + return ArObject{}, ArErr{ + TYPE: "SocketError", + message: err.Error(), + EXISTS: true, + } + } + return ArString(string(buf[:n])), ArErr{} + }, + }, + "write": builtinFunc{ + "write", + func(args ...any) (any, ArErr) { + if len(args) != 1 { + return ArObject{}, ArErr{ + TYPE: "SocketError", + message: "Socket.write() takes exactly 1 argument", + EXISTS: true, + } + } else if typeof(args[0]) != "string" { + return ArObject{}, ArErr{ + TYPE: "SocketError", + message: "Socket.write() argument must be a string", + EXISTS: true, + } + } + data := ArValidToAny(args[0]).(string) + conn.Write([]byte(data)) + return nil, ArErr{} + }, + }, + "close": builtinFunc{ + "close", + func(args ...any) (any, ArErr) { + conn.Close() + return nil, ArErr{} + }, + }, + "isClosed": builtinFunc{ + "isClosed", + func(args ...any) (any, ArErr) { + return conn == nil, ArErr{} + }, + }, + }), ArErr{} + }, + }, + "close": builtinFunc{ + "close", + func(args ...any) (any, ArErr) { + ln.Close() + return nil, ArErr{} + }, + }, + }), ArErr{} +} diff --git a/tests/memoryLeakTest.ar b/tests/memoryLeakTest.ar index 6be1570..62e0d40 100644 --- a/tests/memoryLeakTest.ar +++ b/tests/memoryLeakTest.ar @@ -1,6 +1,6 @@ f() = do let a = [] - for (i from 0 to 10000000) a.append(i) + for (i from 0 to 1000000) a.append(i) term.log("start") f()