Skip to content

Commit

Permalink
adding read() and eof() for line by line read from stdin (#270)
Browse files Browse the repository at this point in the history
  • Loading branch information
ldemailly authored Dec 2, 2024
1 parent 47783c2 commit 225f9ba
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
1 change: 1 addition & 0 deletions extensions/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ func initInternal(c *Config) error {
if c.UnrestrictedIOs {
createShellFunctions()
}
createIOFunctions()
return nil
}

Expand Down
63 changes: 63 additions & 0 deletions extensions/io.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package extensions

import (
"context"
"errors"
"io"
"os"
"strings"

"grol.io/grol/eval"
"grol.io/grol/object"
)

var seenEOF = object.FALSE

func createIOFunctions() {
// This can hang so not to be used in wasm/discord/...
ioFn := object.Extension{
Name: "read",
MinArgs: 0,
MaxArgs: 0,
Help: "Reads one line from stdin",
Callback: func(env any, _ string, _ []object.Object) object.Object {
s := env.(*eval.State)
if s.Term != nil {
s.Term.Suspend()
//nolint:fatcontext // we do need to update/reset the context and its cancel function.
s.Context, s.Cancel = context.WithCancel(context.Background()) // no timeout.
}
var linebuf strings.Builder
// reading one byte at a time is pretty inefficient, but necessary because of the terminal raw mode switch/switchback.
var b [1]byte
for {
n, err := os.Stdin.Read(b[:])
if n == 1 {
linebuf.WriteByte(b[0])
}
if errors.Is(err, io.EOF) {
seenEOF = object.TRUE
break
}
if b[0] == '\n' {
break
}
if err != nil {
return s.Error(err)
}
}
if s.Term != nil {
s.Context, s.Cancel = s.Term.Resume(context.Background())
}
return object.String{Value: linebuf.String()}
},
DontCache: true,
}
MustCreate(ioFn)
ioFn.Name = "eof"
ioFn.Help = "Returns true if a previous read hit the end of file for stdin"
ioFn.Callback = func(_ any, _ string, _ []object.Object) object.Object {
return seenEOF
}
MustCreate(ioFn)
}

0 comments on commit 225f9ba

Please sign in to comment.