diff --git a/fixtures/io.txt b/fixtures/io.txt new file mode 100644 index 0000000..b77d79b --- /dev/null +++ b/fixtures/io.txt @@ -0,0 +1,5 @@ +A banana contains 75% water. +The most consumed fruit in America is the banana +Bananas are a good source of vitamin C, potassium and fiber. +Fresh apples float because they contain 25% air. +Bananas contain no fat, cholesterol or sodium. diff --git a/fixtures/number.txt b/fixtures/number.txt new file mode 100644 index 0000000..e56e15b --- /dev/null +++ b/fixtures/number.txt @@ -0,0 +1 @@ +12345 diff --git a/fixtures/read_all.lua b/fixtures/read_all.lua new file mode 100644 index 0000000..75e4561 --- /dev/null +++ b/fixtures/read_all.lua @@ -0,0 +1,3 @@ +file = io.open("fixtures/io.txt", "r") +print(file:read("*a")) +file:close() diff --git a/fixtures/read_bytes.lua b/fixtures/read_bytes.lua new file mode 100644 index 0000000..24c7b45 --- /dev/null +++ b/fixtures/read_bytes.lua @@ -0,0 +1,3 @@ +file = io.open("fixtures/io.txt", "r") +print(file:read(20)) +file:close() diff --git a/fixtures/read_lines.lua b/fixtures/read_lines.lua new file mode 100644 index 0000000..e828bbd --- /dev/null +++ b/fixtures/read_lines.lua @@ -0,0 +1,4 @@ +file = io.open("fixtures/io.txt", "r") +print(file:read("*l")) +print(file:read("*l")) +file:close() diff --git a/fixtures/read_number.lua b/fixtures/read_number.lua new file mode 100644 index 0000000..4dacfb5 --- /dev/null +++ b/fixtures/read_number.lua @@ -0,0 +1,3 @@ +file = io.open("fixtures/number.txt", "r") +print(file:read("*n")) +file:close() diff --git a/io.go b/io.go index 2bacd86..031ff5d 100644 --- a/io.go +++ b/io.go @@ -1,10 +1,12 @@ package lua import ( + "bufio" "fmt" "io" "io/ioutil" "os" + "strconv" ) const fileHandle = "FILE*" @@ -114,14 +116,65 @@ func readNumber(l *State, f *os.File) (err error) { return } +func readAll(l *State, f *os.File) error { + bytes, err := ioutil.ReadAll(f) + if err == nil { + l.PushString(string(bytes)) + } + return err +} + +func readLineHelper(l *State, f *os.File) error { + originalFileOffset, err := f.Seek(0, 1) + if err != nil { + return err + } + + reader := bufio.NewReader(f) + bytes, err := reader.ReadBytes('\n') + if err == nil { + l.PushString(string(bytes)) + length := int64(len(bytes)) + f.Seek(originalFileOffset+length, 0) // bufio loads the entire file. This is a lazy hack to get around the problem. + } + return err +} + +func readBytes(l *State, f *os.File, i int) error { + buf := make([]byte, i) + _, err := f.Read(buf) + if err == nil { + l.PushString(string(buf)) + } + return err +} + func read(l *State, f *os.File, argIndex int) int { resultCount := 0 var err error if argCount := l.Top() - 1; argCount == 0 { - // err = readLineHelper(l, f, true) + err = readLineHelper(l, f) resultCount = argIndex + 1 } else { - // TODO + if !l.CheckStack(l.Top() - 1) { + Errorf(l, "too many arguments") + } + + p := CheckString(l, l.Top()) + switch p[0:2] { + case "*a": + err = readAll(l, f) + case "*n": + err = readNumber(l, f) + case "*l": + err = readLineHelper(l, f) + default: + i, err := strconv.Atoi(p) + if err != nil { + Errorf(l, "invalid format") + } + err = readBytes(l, f, i) + } } if err != nil { return FileResult(l, err, "") @@ -256,7 +309,7 @@ var fileHandleMethods = []RegistryFunction{ {"close", close}, {"flush", func(l *State) int { return FileResult(l, toFile(l).Sync(), "") }}, {"lines", func(l *State) int { toFile(l); lines(l, false); return 1 }}, - {"read", func(l *State) int { return read(l, toFile(l), 2) }}, + {"read", func(l *State) int { read(l, toFile(l), 2); return 1 }}, {"seek", func(l *State) int { whence := []int{os.SEEK_SET, os.SEEK_CUR, os.SEEK_END} f := toFile(l) diff --git a/io_test.go b/io_test.go new file mode 100644 index 0000000..046b0e7 --- /dev/null +++ b/io_test.go @@ -0,0 +1,85 @@ +package lua + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + "testing" +) + +// TODO: add missing tests + +func TestReadAll(t *testing.T) { + l := NewState() + OpenLibraries(l) + output := captureOutput(func() { + DoFile(l, "fixtures/read_all.lua") + }) + + expected := `A banana contains 75% water. +The most consumed fruit in America is the banana +Bananas are a good source of vitamin C, potassium and fiber. +Fresh apples float because they contain 25% air. +Bananas contain no fat, cholesterol or sodium.` + + if strings.Trim(output, "\n") != expected { + t.Errorf("Expecting:\n%s\nbut received:\n%s\n", expected, output) + } +} + +func TestReadLines(t *testing.T) { + l := NewState() + OpenLibraries(l) + output := captureOutput(func() { + DoFile(l, "fixtures/read_lines.lua") + }) + + expected := `A banana contains 75% water. + +The most consumed fruit in America is the banana` + + if strings.Trim(output, "\n") != expected { + t.Errorf("Expecting:\n%s\nbut received:\n%s\n", expected, output) + } +} + +func TestReadNumber(t *testing.T) { + l := NewState() + OpenLibraries(l) + output := captureOutput(func() { + DoFile(l, "fixtures/read_number.lua") + }) + + expected := "12345" + if strings.Trim(output, "\n") != expected { + t.Errorf("Expecting:\n%s\nbut received:\n%s\n", expected, output) + } +} + +func TestReadBytes(t *testing.T) { + l := NewState() + OpenLibraries(l) + output := captureOutput(func() { + DoFile(l, "fixtures/read_bytes.lua") + }) + + expected := "A banana contains 75" + if strings.Trim(output, "\n") != expected { + t.Errorf("Expecting:\n%s\nbut received:\n%s\n", expected, output) + } +} + +func captureOutput(f func()) string { + rescueStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + f() + + w.Close() + out, _ := ioutil.ReadAll(r) + fmt.Println(string(out)) + os.Stdout = rescueStdout + return string(out) +}