Add file operations
Some checks failed
Build / build (push) Failing after 6m4s
Test / build (push) Has been cancelled

This commit is contained in:
Chuck Smith
2024-03-28 14:00:21 -04:00
parent 110152a139
commit 67c5b4cd66
7 changed files with 226 additions and 3 deletions

View File

@@ -45,6 +45,11 @@ var Builtins = map[string]*object.Builtin{
"max": {Name: "max", Fn: Max}, "max": {Name: "max", Fn: Max},
"sorted": {Name: "sorted", Fn: Sorted}, "sorted": {Name: "sorted", Fn: Sorted},
"reversed": {Name: "reversed", Fn: Reversed}, "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 ... // BuiltinsIndex ...

27
builtins/close.go Normal file
View File

@@ -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{}
}

86
builtins/open.go Normal file
View File

@@ -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)}
}

40
builtins/read.go Normal file
View File

@@ -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])}
}

37
builtins/seek.go Normal file
View File

@@ -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}
}

28
builtins/write.go Normal file
View File

@@ -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)}
}

View File

@@ -899,11 +899,11 @@ func TestBuiltins(t *testing.T) {
`, `,
expectedConstants: []interface{}{1}, expectedConstants: []interface{}{1},
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpGetBuiltin, 18), code.Make(code.OpGetBuiltin, 19),
code.Make(code.OpArray, 0), code.Make(code.OpArray, 0),
code.Make(code.OpCall, 1), code.Make(code.OpCall, 1),
code.Make(code.OpPop), code.Make(code.OpPop),
code.Make(code.OpGetBuiltin, 27), code.Make(code.OpGetBuiltin, 29),
code.Make(code.OpArray, 0), code.Make(code.OpArray, 0),
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpCall, 2), code.Make(code.OpCall, 2),
@@ -914,7 +914,7 @@ func TestBuiltins(t *testing.T) {
input: `fn() { return len([]) }`, input: `fn() { return len([]) }`,
expectedConstants: []interface{}{ expectedConstants: []interface{}{
[]code.Instructions{ []code.Instructions{
code.Make(code.OpGetBuiltin, 18), code.Make(code.OpGetBuiltin, 19),
code.Make(code.OpArray, 0), code.Make(code.OpArray, 0),
code.Make(code.OpCall, 1), code.Make(code.OpCall, 1),
code.Make(code.OpReturn), code.Make(code.OpReturn),