diff --git a/builtins/builtins.go b/builtins/builtins.go index 331eb5b..bec2689 100644 --- a/builtins/builtins.go +++ b/builtins/builtins.go @@ -45,6 +45,11 @@ var Builtins = map[string]*object.Builtin{ "max": {Name: "max", Fn: Max}, "sorted": {Name: "sorted", Fn: Sorted}, "reversed": {Name: "reversed", Fn: Reversed}, + "open": {Name: "open", Fn: Open}, + "close": {Name: "close", Fn: Close}, + "write": {Name: "write", Fn: Write}, + "read": {Name: "read", Fn: Read}, + "seek": {Name: "seek", Fn: Seek}, } // BuiltinsIndex ... diff --git a/builtins/close.go b/builtins/close.go new file mode 100644 index 0000000..94b6019 --- /dev/null +++ b/builtins/close.go @@ -0,0 +1,27 @@ +package builtins + +import ( + "monkey/object" + "monkey/typing" + "syscall" +) + +// Close ... +func Close(args ...object.Object) object.Object { + if err := typing.Check( + "close", args, + typing.ExactArgs(1), + typing.WithTypes(object.INTEGER_OBJ), + ); err != nil { + return newError(err.Error()) + } + + fd := int(args[0].(*object.Integer).Value) + + err := syscall.Close(syscall.Handle(fd)) + if err != nil { + return newError("IOError: %s", err) + } + + return &object.Null{} +} diff --git a/builtins/open.go b/builtins/open.go new file mode 100644 index 0000000..125a21c --- /dev/null +++ b/builtins/open.go @@ -0,0 +1,86 @@ +package builtins + +import ( + "fmt" + "log" + "monkey/object" + "monkey/typing" + "os" + "syscall" +) + +// Close ... +func parseMode(mode string) (int, error) { + var flag int + for _, c := range mode { + switch c { + case 'r': + if (flag & os.O_WRONLY) != 0 { + flag |= os.O_RDWR + } else { + log.Printf("r2") + flag |= os.O_RDONLY + } + case 'w': + if (flag & os.O_RDONLY) != 0 { + flag |= os.O_RDWR + } else { + flag |= os.O_WRONLY + } + case 'a': + flag |= os.O_APPEND + default: + return 0, fmt.Errorf("ValueError: mode string must be one of 'r', 'w', 'a', not '%c'", c) + } + } + + if (flag&os.O_WRONLY) != 0 || (flag&os.O_RDWR) != 0 { + log.Printf("c1") + flag |= os.O_CREATE + if (flag & os.O_APPEND) == 0 { + log.Printf("t1") + flag |= os.O_TRUNC + } + } + + if !((flag == os.O_RDONLY) || (flag&os.O_WRONLY != 0) || (flag&os.O_RDWR != 0)) { + return 0, fmt.Errorf("ValueError: mode string must be at least one of 'r', 'w', or 'rw'") + } + + return flag, nil +} + +// Open ... +func Open(args ...object.Object) object.Object { + if err := typing.Check( + "open", args, + typing.RangeOfArgs(1, 2), + typing.WithTypes(object.STRING_OBJ, object.STRING_OBJ), + ); err != nil { + return newError(err.Error()) + } + + var ( + filename string + mode = "r" + perm uint32 = 0640 + ) + + filename = args[0].(*object.String).Value + + if len(args) == 2 { + mode = args[1].(*object.String).Value + } + + flag, err := parseMode(mode) + if err != nil { + return newError(err.Error()) + } + + fd, err := syscall.Open(filename, flag, perm) + if err != nil { + return newError("IOError: %s", err) + } + + return &object.Integer{Value: int64(fd)} +} diff --git a/builtins/read.go b/builtins/read.go new file mode 100644 index 0000000..135f9aa --- /dev/null +++ b/builtins/read.go @@ -0,0 +1,40 @@ +package builtins + +import ( + "monkey/object" + "monkey/typing" + "syscall" +) + +// DefaultBufferSize is the default buffer size +const DefaultBufferSize = 4096 + +// Read ... +func Read(args ...object.Object) object.Object { + if err := typing.Check( + "read", args, + typing.RangeOfArgs(1, 2), + typing.WithTypes(object.INTEGER_OBJ, object.INTEGER_OBJ), + ); err != nil { + return newError(err.Error()) + } + + var ( + fd int + n = DefaultBufferSize + ) + + fd = int(args[0].(*object.Integer).Value) + + if len(args) == 2 { + n = int(args[1].(*object.Integer).Value) + } + + buf := make([]byte, n) + n, err := syscall.Read(syscall.Handle(fd), buf) + if err != nil { + return newError("IOError: %s", err) + } + + return &object.String{Value: string(buf[:n])} +} diff --git a/builtins/seek.go b/builtins/seek.go new file mode 100644 index 0000000..e9b85d3 --- /dev/null +++ b/builtins/seek.go @@ -0,0 +1,37 @@ +package builtins + +import ( + "monkey/object" + "monkey/typing" + "syscall" +) + +// Seek ... +func Seek(args ...object.Object) object.Object { + if err := typing.Check( + "seek", args, + typing.RangeOfArgs(1, 3), + typing.WithTypes(object.INTEGER_OBJ, object.INTEGER_OBJ, object.INTEGER_OBJ), + ); err != nil { + return newError(err.Error()) + } + + var ( + fd int + whence = 0 + ) + + fd = int(args[0].(*object.Integer).Value) + offset := args[1].(*object.Integer).Value + + if len(args) == 3 { + whence = int(args[2].(*object.Integer).Value) + } + + offset, err := syscall.Seek(syscall.Handle(fd), offset, whence) + if err != nil { + return newError("IOError: %s", err) + } + + return &object.Integer{Value: offset} +} diff --git a/builtins/write.go b/builtins/write.go new file mode 100644 index 0000000..47772c3 --- /dev/null +++ b/builtins/write.go @@ -0,0 +1,28 @@ +package builtins + +import ( + "monkey/object" + "monkey/typing" + "syscall" +) + +// Write ... +func Write(args ...object.Object) object.Object { + if err := typing.Check( + "write", args, + typing.ExactArgs(2), + typing.WithTypes(object.INTEGER_OBJ, object.INTEGER_OBJ), + ); err != nil { + return newError(err.Error()) + } + + fd := int(args[0].(*object.Integer).Value) + data := []byte(args[1].(*object.String).Value) + + n, err := syscall.Write(syscall.Handle(fd), data) + if err != nil { + return newError("IOError: %s", err) + } + + return &object.Integer{Value: int64(n)} +} diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index cb12c27..cbdb16e 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -899,11 +899,11 @@ func TestBuiltins(t *testing.T) { `, expectedConstants: []interface{}{1}, expectedInstructions: []code.Instructions{ - code.Make(code.OpGetBuiltin, 18), + code.Make(code.OpGetBuiltin, 19), code.Make(code.OpArray, 0), code.Make(code.OpCall, 1), code.Make(code.OpPop), - code.Make(code.OpGetBuiltin, 27), + code.Make(code.OpGetBuiltin, 29), code.Make(code.OpArray, 0), code.Make(code.OpConstant, 0), code.Make(code.OpCall, 2), @@ -914,7 +914,7 @@ func TestBuiltins(t *testing.T) { input: `fn() { return len([]) }`, expectedConstants: []interface{}{ []code.Instructions{ - code.Make(code.OpGetBuiltin, 18), + code.Make(code.OpGetBuiltin, 19), code.Make(code.OpArray, 0), code.Make(code.OpCall, 1), code.Make(code.OpReturn),