mirror of
https://github.com/wader/fq.git
synced 2024-12-01 02:30:32 +03:00
Merge pull request #143 from wader/paste
interp: Add paste function to allow pasting text into REPL etc
This commit is contained in:
commit
ca234bc1c9
@ -13,6 +13,7 @@
|
|||||||
- Rework cli/repl user interrupt (context cancel via ctrl-c), see comment in Interp.Main
|
- Rework cli/repl user interrupt (context cancel via ctrl-c), see comment in Interp.Main
|
||||||
- Optimize `Interp.Options` calls, now called per display. Cache per eval? needs to handle nested evals.
|
- Optimize `Interp.Options` calls, now called per display. Cache per eval? needs to handle nested evals.
|
||||||
- `<array decode value>[{start: ...: end: ...}]` syntax a bit broken.
|
- `<array decode value>[{start: ...: end: ...}]` syntax a bit broken.
|
||||||
|
- REPL completion might have side effcts. Make interp.Function type know and wrap somehow? input, inputs, open, ...
|
||||||
|
|
||||||
### TODO and ideas
|
### TODO and ideas
|
||||||
|
|
||||||
|
@ -418,6 +418,8 @@ you currently have to do `fq -d raw 'mp3({force: true})' file`.
|
|||||||
- `p`/`preview` show preview of field tree
|
- `p`/`preview` show preview of field tree
|
||||||
- `hd`/`hexdump` hexdump value
|
- `hd`/`hexdump` hexdump value
|
||||||
- `repl` nested REPL, must be last in a pipeline. `1 | repl`, can "slurp" outputs `1, 2, 3 | repl`.
|
- `repl` nested REPL, must be last in a pipeline. `1 | repl`, can "slurp" outputs `1, 2, 3 | repl`.
|
||||||
|
- `paste` read string from stdin until ^D. Useful for pasting text.
|
||||||
|
- Ex: `paste | frompem | asn1_ber | repl` read from stdin then decode and start a new sub-REPL with result.
|
||||||
|
|
||||||
## Color and unicode output
|
## Color and unicode output
|
||||||
|
|
||||||
|
@ -149,8 +149,8 @@ func (cr *CaseRun) ConfigDir() (string, error) { return "/config", nil }
|
|||||||
|
|
||||||
func (cr *CaseRun) FS() fs.FS { return cr.Case }
|
func (cr *CaseRun) FS() fs.FS { return cr.Case }
|
||||||
|
|
||||||
func (cr *CaseRun) Readline(prompt string, complete func(line string, pos int) (newLine []string, shared int)) (string, error) {
|
func (cr *CaseRun) Readline(opts interp.ReadlineOpts) (string, error) {
|
||||||
cr.ActualStdoutBuf.WriteString(prompt)
|
cr.ActualStdoutBuf.WriteString(opts.Prompt)
|
||||||
if cr.ReadlinesPos >= len(cr.Readlines) {
|
if cr.ReadlinesPos >= len(cr.Readlines) {
|
||||||
return "", io.EOF
|
return "", io.EOF
|
||||||
}
|
}
|
||||||
@ -165,7 +165,7 @@ func (cr *CaseRun) Readline(prompt string, complete func(line string, pos int) (
|
|||||||
cr.ActualStdoutBuf.WriteString(lineRaw + "\n")
|
cr.ActualStdoutBuf.WriteString(lineRaw + "\n")
|
||||||
|
|
||||||
l := len(line) - 1
|
l := len(line) - 1
|
||||||
newLine, shared := complete(line[0:l], l)
|
newLine, shared := opts.CompleteFn(line[0:l], l)
|
||||||
// TODO: shared
|
// TODO: shared
|
||||||
_ = shared
|
_ = shared
|
||||||
for _, nl := range newLine {
|
for _, nl := range newLine {
|
||||||
|
@ -158,7 +158,7 @@ func (stdOSFS) Open(name string) (fs.File, error) { return os.Open(name) }
|
|||||||
|
|
||||||
func (*stdOS) FS() fs.FS { return stdOSFS{} }
|
func (*stdOS) FS() fs.FS { return stdOSFS{} }
|
||||||
|
|
||||||
func (o *stdOS) Readline(prompt string, complete func(line string, pos int) (newLine []string, shared int)) (string, error) {
|
func (o *stdOS) Readline(opts interp.ReadlineOpts) (string, error) {
|
||||||
if o.rl == nil {
|
if o.rl == nil {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@ -179,9 +179,9 @@ func (o *stdOS) Readline(prompt string, complete func(line string, pos int) (new
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if complete != nil {
|
if opts.CompleteFn != nil {
|
||||||
o.rl.Config.AutoComplete = autoCompleterFn(func(line []rune, pos int) (newLine [][]rune, length int) {
|
o.rl.Config.AutoComplete = autoCompleterFn(func(line []rune, pos int) (newLine [][]rune, length int) {
|
||||||
names, shared := complete(string(line), pos)
|
names, shared := opts.CompleteFn(string(line), pos)
|
||||||
var runeNames [][]rune
|
var runeNames [][]rune
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
runeNames = append(runeNames, []rune(name[shared:]))
|
runeNames = append(runeNames, []rune(name[shared:]))
|
||||||
@ -191,7 +191,7 @@ func (o *stdOS) Readline(prompt string, complete func(line string, pos int) (new
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
o.rl.SetPrompt(prompt)
|
o.rl.SetPrompt(opts.Prompt)
|
||||||
line, err := o.rl.Readline()
|
line, err := o.rl.Readline()
|
||||||
if errors.Is(err, readline.ErrInterrupt) {
|
if errors.Is(err, readline.ErrInterrupt) {
|
||||||
return "", interp.ErrInterrupt
|
return "", interp.ErrInterrupt
|
||||||
|
@ -16,13 +16,14 @@ import (
|
|||||||
"github.com/wader/fq/internal/progressreadseeker"
|
"github.com/wader/fq/internal/progressreadseeker"
|
||||||
"github.com/wader/fq/pkg/bitio"
|
"github.com/wader/fq/pkg/bitio"
|
||||||
"github.com/wader/fq/pkg/ranges"
|
"github.com/wader/fq/pkg/ranges"
|
||||||
|
"github.com/wader/gojq"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
functionRegisterFns = append(functionRegisterFns, func(i *Interp) []Function {
|
functionRegisterFns = append(functionRegisterFns, func(i *Interp) []Function {
|
||||||
return []Function{
|
return []Function{
|
||||||
{"_tobits", 3, 3, i._toBits, nil},
|
{"_tobits", 3, 3, i._toBits, nil},
|
||||||
{"open", 0, 0, i._open, nil},
|
{"open", 0, 0, nil, i._open},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -175,7 +176,11 @@ func (of *openFile) ToBinary() (Binary, error) {
|
|||||||
// def open: #:: string| => binary
|
// def open: #:: string| => binary
|
||||||
// opens a file for reading from filesystem
|
// opens a file for reading from filesystem
|
||||||
// TODO: when to close? when br loses all refs? need to use finalizer somehow?
|
// TODO: when to close? when br loses all refs? need to use finalizer somehow?
|
||||||
func (i *Interp) _open(c interface{}, a []interface{}) interface{} {
|
func (i *Interp) _open(c interface{}, a []interface{}) gojq.Iter {
|
||||||
|
if i.evalContext.isCompleting {
|
||||||
|
return gojq.NewIter()
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var f fs.File
|
var f fs.File
|
||||||
var path string
|
var path string
|
||||||
@ -187,11 +192,11 @@ func (i *Interp) _open(c interface{}, a []interface{}) interface{} {
|
|||||||
default:
|
default:
|
||||||
path, err = toString(c)
|
path, err = toString(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", path, err)
|
return gojq.NewIter(fmt.Errorf("%s: %w", path, err))
|
||||||
}
|
}
|
||||||
f, err = i.os.FS().Open(path)
|
f, err = i.os.FS().Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return gojq.NewIter(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +206,7 @@ func (i *Interp) _open(c interface{}, a []interface{}) interface{} {
|
|||||||
fFI, err := f.Stat()
|
fFI, err := f.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
return err
|
return gojq.NewIter(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ctxreadseeker is used to make sure any io calls can be canceled
|
// ctxreadseeker is used to make sure any io calls can be canceled
|
||||||
@ -219,7 +224,7 @@ func (i *Interp) _open(c interface{}, a []interface{}) interface{} {
|
|||||||
buf, err := ioutil.ReadAll(ctxreadseeker.New(i.evalContext.ctx, &ioextra.ReadErrSeeker{Reader: f}))
|
buf, err := ioutil.ReadAll(ctxreadseeker.New(i.evalContext.ctx, &ioextra.ReadErrSeeker{Reader: f}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
return err
|
return gojq.NewIter(err)
|
||||||
}
|
}
|
||||||
fRS = bytes.NewReader(buf)
|
fRS = bytes.NewReader(buf)
|
||||||
bEnd = int64(len(buf))
|
bEnd = int64(len(buf))
|
||||||
@ -246,10 +251,10 @@ func (i *Interp) _open(c interface{}, a []interface{}) interface{} {
|
|||||||
|
|
||||||
bbf.br = bitio.NewIOBitReadSeeker(aheadRs)
|
bbf.br = bitio.NewIOBitReadSeeker(aheadRs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return gojq.NewIter(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return bbf
|
return gojq.NewIter(bbf)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Value = Binary{}
|
var _ Value = Binary{}
|
||||||
|
@ -162,7 +162,7 @@ func (i *Interp) _decode(c interface{}, a []interface{}) interface{} {
|
|||||||
c,
|
c,
|
||||||
opts.Progress,
|
opts.Progress,
|
||||||
nil,
|
nil,
|
||||||
ioextra.DiscardCtxWriter{Ctx: i.evalContext.ctx},
|
EvalOpts{output: ioextra.DiscardCtxWriter{Ctx: i.evalContext.ctx}},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
lastProgress := time.Now()
|
lastProgress := time.Now()
|
||||||
|
@ -305,3 +305,14 @@ def topem($label):
|
|||||||
| join("\n")
|
| join("\n")
|
||||||
);
|
);
|
||||||
def topem: topem("");
|
def topem: topem("");
|
||||||
|
|
||||||
|
def paste:
|
||||||
|
if _is_completing | not then
|
||||||
|
( [ _repeat_break(
|
||||||
|
try _stdin(64*1024)
|
||||||
|
catch if . == "eof" then error("break") end
|
||||||
|
)
|
||||||
|
]
|
||||||
|
| join("")
|
||||||
|
)
|
||||||
|
end;
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -22,6 +23,7 @@ import (
|
|||||||
"github.com/wader/fq/internal/bitioextra"
|
"github.com/wader/fq/internal/bitioextra"
|
||||||
"github.com/wader/fq/internal/colorjson"
|
"github.com/wader/fq/internal/colorjson"
|
||||||
"github.com/wader/fq/internal/ctxstack"
|
"github.com/wader/fq/internal/ctxstack"
|
||||||
|
"github.com/wader/fq/internal/gojqextra"
|
||||||
"github.com/wader/fq/internal/ioextra"
|
"github.com/wader/fq/internal/ioextra"
|
||||||
"github.com/wader/fq/internal/mathextra"
|
"github.com/wader/fq/internal/mathextra"
|
||||||
"github.com/wader/fq/internal/pos"
|
"github.com/wader/fq/internal/pos"
|
||||||
@ -53,11 +55,11 @@ var functionRegisterFns []func(i *Interp) []Function
|
|||||||
func init() {
|
func init() {
|
||||||
functionRegisterFns = append(functionRegisterFns, func(i *Interp) []Function {
|
functionRegisterFns = append(functionRegisterFns, func(i *Interp) []Function {
|
||||||
return []Function{
|
return []Function{
|
||||||
{"_readline", 0, 2, i._readline, nil},
|
{"_readline", 0, 1, nil, i._readline},
|
||||||
{"eval", 1, 2, nil, i.eval},
|
{"eval", 1, 2, nil, i.eval},
|
||||||
{"_stdin", 0, 0, nil, i.makeStdioFn(i.os.Stdin())},
|
{"_stdin", 0, 1, nil, i.makeStdioFn("stdin", i.os.Stdin())},
|
||||||
{"_stdout", 0, 0, nil, i.makeStdioFn(i.os.Stdout())},
|
{"_stdout", 0, 0, nil, i.makeStdioFn("stdout", i.os.Stdout())},
|
||||||
{"_stderr", 0, 0, nil, i.makeStdioFn(i.os.Stderr())},
|
{"_stderr", 0, 0, nil, i.makeStdioFn("stderr", i.os.Stderr())},
|
||||||
{"_extkeys", 0, 0, i._extKeys, nil},
|
{"_extkeys", 0, 0, i._extKeys, nil},
|
||||||
{"_exttype", 0, 0, i._extType, nil},
|
{"_exttype", 0, 0, i._extType, nil},
|
||||||
{"_global_state", 0, 1, i.makeStateFn(i.state), nil},
|
{"_global_state", 0, 1, i.makeStateFn(i.state), nil},
|
||||||
@ -65,6 +67,7 @@ func init() {
|
|||||||
{"_display", 1, 1, nil, i._display},
|
{"_display", 1, 1, nil, i._display},
|
||||||
{"_can_display", 0, 0, i._canDisplay, nil},
|
{"_can_display", 0, 0, i._canDisplay, nil},
|
||||||
{"_print_color_json", 0, 1, nil, i._printColorJSON},
|
{"_print_color_json", 0, 1, nil, i._printColorJSON},
|
||||||
|
{"_is_completing", 0, 1, i._isCompleting, nil},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -95,7 +98,7 @@ func (ce compileError) Value() interface{} {
|
|||||||
func (ce compileError) Error() string {
|
func (ce compileError) Error() string {
|
||||||
filename := ce.filename
|
filename := ce.filename
|
||||||
if filename == "" {
|
if filename == "" {
|
||||||
filename = "src"
|
filename = "expr"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s:%d:%d: %s: %s", filename, ce.pos.Line, ce.pos.Column, ce.what, ce.err.Error())
|
return fmt.Sprintf("%s:%d:%d: %s: %s", filename, ce.pos.Line, ce.pos.Column, ce.what, ce.err.Error())
|
||||||
}
|
}
|
||||||
@ -133,6 +136,11 @@ type Platform struct {
|
|||||||
Arch string
|
Arch string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReadlineOpts struct {
|
||||||
|
Prompt string
|
||||||
|
CompleteFn func(line string, pos int) (newLine []string, shared int)
|
||||||
|
}
|
||||||
|
|
||||||
type OS interface {
|
type OS interface {
|
||||||
Platform() Platform
|
Platform() Platform
|
||||||
Stdin() Input
|
Stdin() Input
|
||||||
@ -144,7 +152,7 @@ type OS interface {
|
|||||||
ConfigDir() (string, error)
|
ConfigDir() (string, error)
|
||||||
// FS.File returned by FS().Open() can optionally implement io.Seeker
|
// FS.File returned by FS().Open() can optionally implement io.Seeker
|
||||||
FS() fs.FS
|
FS() fs.FS
|
||||||
Readline(prompt string, complete func(line string, pos int) (newLine []string, shared int)) (string, error)
|
Readline(opts ReadlineOpts) (string, error)
|
||||||
History() ([]string, error)
|
History() ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,14 +291,14 @@ func toBytes(v interface{}) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryErrorPosition(src string, v error) pos.Pos {
|
func queryErrorPosition(expr string, v error) pos.Pos {
|
||||||
var offset int
|
var offset int
|
||||||
|
|
||||||
if tokIf, ok := v.(interface{ Token() (string, int) }); ok { //nolint:errorlint
|
if tokIf, ok := v.(interface{ Token() (string, int) }); ok { //nolint:errorlint
|
||||||
_, offset = tokIf.Token()
|
_, offset = tokIf.Token()
|
||||||
}
|
}
|
||||||
if offset >= 0 {
|
if offset >= 0 {
|
||||||
return pos.NewFromOffset(src, offset)
|
return pos.NewFromOffset(expr, offset)
|
||||||
}
|
}
|
||||||
return pos.Pos{}
|
return pos.Pos{}
|
||||||
}
|
}
|
||||||
@ -317,8 +325,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type evalContext struct {
|
type evalContext struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
output io.Writer
|
output io.Writer
|
||||||
|
isCompleting bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Interp struct {
|
type Interp struct {
|
||||||
@ -380,7 +389,7 @@ func (i *Interp) Main(ctx context.Context, output Output, versionStr string) err
|
|||||||
"arch": platform.Arch,
|
"arch": platform.Arch,
|
||||||
}
|
}
|
||||||
|
|
||||||
iter, err := i.EvalFunc(ctx, input, "_main", nil, output)
|
iter, err := i.EvalFunc(ctx, input, "_main", nil, EvalOpts{output: output})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(i.os.Stderr(), err)
|
fmt.Fprintln(i.os.Stderr(), err)
|
||||||
return err
|
return err
|
||||||
@ -414,28 +423,24 @@ func (i *Interp) Main(ctx context.Context, output Output, versionStr string) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Interp) _readline(c interface{}, a []interface{}) interface{} {
|
func (i *Interp) _readline(c interface{}, a []interface{}) gojq.Iter {
|
||||||
|
if i.evalContext.isCompleting {
|
||||||
|
return gojq.NewIter()
|
||||||
|
}
|
||||||
|
|
||||||
var opts struct {
|
var opts struct {
|
||||||
|
Promopt string `mapstructure:"prompt"`
|
||||||
Complete string `mapstructure:"complete"`
|
Complete string `mapstructure:"complete"`
|
||||||
Timeout float64 `mapstructure:"timeout"`
|
Timeout float64 `mapstructure:"timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
prompt := ""
|
|
||||||
|
|
||||||
if len(a) > 0 {
|
if len(a) > 0 {
|
||||||
prompt, err = toString(a[0])
|
_ = mapstructure.Decode(a[0], &opts)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("prompt: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(a) > 1 {
|
|
||||||
_ = mapstructure.Decode(a[1], &opts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
src, err := i.os.Readline(
|
expr, err := i.os.Readline(ReadlineOpts{
|
||||||
prompt,
|
Prompt: opts.Promopt,
|
||||||
func(line string, pos int) (newLine []string, shared int) {
|
CompleteFn: func(line string, pos int) (newLine []string, shared int) {
|
||||||
completeCtx := i.evalContext.ctx
|
completeCtx := i.evalContext.ctx
|
||||||
if opts.Timeout > 0 {
|
if opts.Timeout > 0 {
|
||||||
var completeCtxCancelFn context.CancelFunc
|
var completeCtxCancelFn context.CancelFunc
|
||||||
@ -450,7 +455,10 @@ func (i *Interp) _readline(c interface{}, a []interface{}) interface{} {
|
|||||||
c,
|
c,
|
||||||
opts.Complete,
|
opts.Complete,
|
||||||
[]interface{}{line, pos},
|
[]interface{}{line, pos},
|
||||||
ioextra.DiscardCtxWriter{Ctx: completeCtx},
|
EvalOpts{
|
||||||
|
output: ioextra.DiscardCtxWriter{Ctx: completeCtx},
|
||||||
|
isCompleting: true,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, pos, err
|
return nil, pos, err
|
||||||
@ -485,24 +493,24 @@ func (i *Interp) _readline(c interface{}, a []interface{}) interface{} {
|
|||||||
|
|
||||||
return names, shared
|
return names, shared
|
||||||
},
|
},
|
||||||
)
|
})
|
||||||
|
|
||||||
if errors.Is(err, ErrInterrupt) {
|
if errors.Is(err, ErrInterrupt) {
|
||||||
return valueError{"interrupt"}
|
return gojq.NewIter(valueError{"interrupt"})
|
||||||
} else if errors.Is(err, ErrEOF) {
|
} else if errors.Is(err, ErrEOF) {
|
||||||
return valueError{"eof"}
|
return gojq.NewIter(valueError{"eof"})
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return gojq.NewIter(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return src
|
return gojq.NewIter(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Interp) eval(c interface{}, a []interface{}) gojq.Iter {
|
func (i *Interp) eval(c interface{}, a []interface{}) gojq.Iter {
|
||||||
var err error
|
var err error
|
||||||
src, err := toString(a[0])
|
expr, err := toString(a[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gojq.NewIter(fmt.Errorf("src: %w", err))
|
return gojq.NewIter(fmt.Errorf("expr: %w", err))
|
||||||
}
|
}
|
||||||
var filenameHint string
|
var filenameHint string
|
||||||
if len(a) >= 2 {
|
if len(a) >= 2 {
|
||||||
@ -512,7 +520,10 @@ func (i *Interp) eval(c interface{}, a []interface{}) gojq.Iter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iter, err := i.Eval(i.evalContext.ctx, c, src, filenameHint, i.evalContext.output)
|
iter, err := i.Eval(i.evalContext.ctx, c, expr, EvalOpts{
|
||||||
|
filename: filenameHint,
|
||||||
|
output: i.evalContext.output,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gojq.NewIter(err)
|
return gojq.NewIter(err)
|
||||||
}
|
}
|
||||||
@ -547,25 +558,53 @@ func (i *Interp) makeStateFn(state *interface{}) func(c interface{}, a []interfa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Interp) makeStdioFn(t Terminal) func(c interface{}, a []interface{}) gojq.Iter {
|
func (i *Interp) makeStdioFn(name string, t Terminal) func(c interface{}, a []interface{}) gojq.Iter {
|
||||||
return func(c interface{}, a []interface{}) gojq.Iter {
|
return func(c interface{}, a []interface{}) gojq.Iter {
|
||||||
if c == nil {
|
if i.evalContext.isCompleting {
|
||||||
|
return gojq.NewIter("")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case len(a) == 1:
|
||||||
|
r, ok := t.(io.Reader)
|
||||||
|
if !ok {
|
||||||
|
return gojq.NewIter(fmt.Errorf("%s is not readable", name))
|
||||||
|
}
|
||||||
|
l, ok := gojqextra.ToInt(a[0])
|
||||||
|
if !ok {
|
||||||
|
return gojq.NewIter(gojqextra.FuncTypeError{Name: name, V: a[0]})
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, l)
|
||||||
|
n, err := io.ReadFull(r, buf)
|
||||||
|
s := string(buf[0:n])
|
||||||
|
|
||||||
|
vs := []interface{}{s}
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, io.EOF), errors.Is(err, io.ErrUnexpectedEOF):
|
||||||
|
vs = append(vs, valueError{"eof"})
|
||||||
|
default:
|
||||||
|
vs = append(vs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return gojq.NewIter(vs...)
|
||||||
|
case c == nil:
|
||||||
w, h := t.Size()
|
w, h := t.Size()
|
||||||
return gojq.NewIter(map[string]interface{}{
|
return gojq.NewIter(map[string]interface{}{
|
||||||
"is_terminal": t.IsTerminal(),
|
"is_terminal": t.IsTerminal(),
|
||||||
"width": w,
|
"width": w,
|
||||||
"height": h,
|
"height": h,
|
||||||
})
|
})
|
||||||
}
|
default:
|
||||||
|
w, ok := t.(io.Writer)
|
||||||
if w, ok := t.(io.Writer); ok {
|
if !ok {
|
||||||
|
return gojq.NewIter(fmt.Errorf("%v: it not writeable", c))
|
||||||
|
}
|
||||||
if _, err := fmt.Fprint(w, c); err != nil {
|
if _, err := fmt.Fprint(w, c); err != nil {
|
||||||
return gojq.NewIter(err)
|
return gojq.NewIter(err)
|
||||||
}
|
}
|
||||||
return gojq.NewIter()
|
return gojq.NewIter()
|
||||||
}
|
}
|
||||||
|
|
||||||
return gojq.NewIter(fmt.Errorf("%v: it not writeable", c))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,6 +634,11 @@ func (i *Interp) _display(c interface{}, a []interface{}) gojq.Iter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Interp) _canDisplay(c interface{}, a []interface{}) interface{} {
|
||||||
|
_, ok := c.(Display)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Interp) _printColorJSON(c interface{}, a []interface{}) gojq.Iter {
|
func (i *Interp) _printColorJSON(c interface{}, a []interface{}) gojq.Iter {
|
||||||
opts := i.Options(a[0])
|
opts := i.Options(a[0])
|
||||||
|
|
||||||
@ -609,9 +653,8 @@ func (i *Interp) _printColorJSON(c interface{}, a []interface{}) gojq.Iter {
|
|||||||
return gojq.NewIter()
|
return gojq.NewIter()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Interp) _canDisplay(c interface{}, a []interface{}) interface{} {
|
func (i *Interp) _isCompleting(c interface{}, a []interface{}) interface{} {
|
||||||
_, ok := c.(Display)
|
return i.evalContext.isCompleting
|
||||||
return ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type pathResolver struct {
|
type pathResolver struct {
|
||||||
@ -667,14 +710,20 @@ func (i *Interp) lookupPathResolver(filename string) (pathResolver, error) {
|
|||||||
return pathResolver{}, fmt.Errorf("could not resolve path: %s", filename)
|
return pathResolver{}, fmt.Errorf("could not resolve path: %s", filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Interp) Eval(ctx context.Context, c interface{}, src string, srcFilename string, output io.Writer) (gojq.Iter, error) {
|
type EvalOpts struct {
|
||||||
gq, err := gojq.Parse(src)
|
filename string
|
||||||
|
output io.Writer
|
||||||
|
isCompleting bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interp) Eval(ctx context.Context, c interface{}, expr string, opts EvalOpts) (gojq.Iter, error) {
|
||||||
|
gq, err := gojq.Parse(expr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p := queryErrorPosition(src, err)
|
p := queryErrorPosition(expr, err)
|
||||||
return nil, compileError{
|
return nil, compileError{
|
||||||
err: err,
|
err: err,
|
||||||
what: "parse",
|
what: "parse",
|
||||||
filename: srcFilename,
|
filename: opts.filename,
|
||||||
pos: p,
|
pos: p,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -827,18 +876,25 @@ func (i *Interp) Eval(ctx context.Context, c interface{}, src string, srcFilenam
|
|||||||
|
|
||||||
gc, err := gojq.Compile(gq, compilerOpts...)
|
gc, err := gojq.Compile(gq, compilerOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p := queryErrorPosition(src, err)
|
p := queryErrorPosition(expr, err)
|
||||||
return nil, compileError{
|
return nil, compileError{
|
||||||
err: err,
|
err: err,
|
||||||
what: "compile",
|
what: "compile",
|
||||||
filename: srcFilename,
|
filename: opts.filename,
|
||||||
pos: p,
|
pos: p,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output := opts.output
|
||||||
|
if opts.output == nil {
|
||||||
|
output = ioutil.Discard
|
||||||
|
}
|
||||||
|
|
||||||
runCtx, runCtxCancelFn := i.interruptStack.Push(ctx)
|
runCtx, runCtxCancelFn := i.interruptStack.Push(ctx)
|
||||||
ni.evalContext.ctx = runCtx
|
ni.evalContext.ctx = runCtx
|
||||||
ni.evalContext.output = ioextra.CtxWriter{Writer: output, Ctx: runCtx}
|
ni.evalContext.output = ioextra.CtxWriter{Writer: output, Ctx: runCtx}
|
||||||
|
// inherit or set
|
||||||
|
ni.evalContext.isCompleting = i.evalContext.isCompleting || opts.isCompleting
|
||||||
iter := gc.RunWithContext(runCtx, c, variableValues...)
|
iter := gc.RunWithContext(runCtx, c, variableValues...)
|
||||||
|
|
||||||
iterWrapper := iterFn(func() (interface{}, bool) {
|
iterWrapper := iterFn(func() (interface{}, bool) {
|
||||||
@ -853,7 +909,7 @@ func (i *Interp) Eval(ctx context.Context, c interface{}, src string, srcFilenam
|
|||||||
return iterWrapper, nil
|
return iterWrapper, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Interp) EvalFunc(ctx context.Context, c interface{}, name string, args []interface{}, output io.Writer) (gojq.Iter, error) {
|
func (i *Interp) EvalFunc(ctx context.Context, c interface{}, name string, args []interface{}, opts EvalOpts) (gojq.Iter, error) {
|
||||||
var argsExpr []string
|
var argsExpr []string
|
||||||
for i := range args {
|
for i := range args {
|
||||||
argsExpr = append(argsExpr, fmt.Sprintf("$_args[%d]", i))
|
argsExpr = append(argsExpr, fmt.Sprintf("$_args[%d]", i))
|
||||||
@ -870,15 +926,15 @@ func (i *Interp) EvalFunc(ctx context.Context, c interface{}, name string, args
|
|||||||
// _args to mark variable as internal and hide it from completion
|
// _args to mark variable as internal and hide it from completion
|
||||||
// {input: ..., args: [...]} | .args as {args: $_args} | .input | name[($_args[0]; ...)]
|
// {input: ..., args: [...]} | .args as {args: $_args} | .input | name[($_args[0]; ...)]
|
||||||
trampolineExpr := fmt.Sprintf(". as {args: $_args} | .input | %s%s", name, argExpr)
|
trampolineExpr := fmt.Sprintf(". as {args: $_args} | .input | %s%s", name, argExpr)
|
||||||
iter, err := i.Eval(ctx, trampolineInput, trampolineExpr, "", output)
|
iter, err := i.Eval(ctx, trampolineInput, trampolineExpr, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return iter, nil
|
return iter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Interp) EvalFuncValues(ctx context.Context, c interface{}, name string, args []interface{}, output io.Writer) ([]interface{}, error) {
|
func (i *Interp) EvalFuncValues(ctx context.Context, c interface{}, name string, args []interface{}, opts EvalOpts) ([]interface{}, error) {
|
||||||
iter, err := i.EvalFunc(ctx, c, name, args, output)
|
iter, err := i.EvalFunc(ctx, c, name, args, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ def _repl($opts): #:: a|(Opts) => @
|
|||||||
def _read_expr:
|
def _read_expr:
|
||||||
_repeat_break(
|
_repeat_break(
|
||||||
# both _prompt and _complete want input arrays
|
# both _prompt and _complete want input arrays
|
||||||
( _readline(_prompt; {complete: "_complete", timeout: 1})
|
( _readline({prompt: _prompt, complete: "_complete", timeout: 1})
|
||||||
| if trim == "" then empty
|
| if trim == "" then empty
|
||||||
else (., error("break"))
|
else (., error("break"))
|
||||||
end
|
end
|
||||||
@ -216,12 +216,15 @@ def _repl($opts): #:: a|(Opts) => @
|
|||||||
else error
|
else error
|
||||||
end
|
end
|
||||||
);
|
);
|
||||||
( _options_stack(. + [$opts]) as $_
|
if _is_completing | not then
|
||||||
| _finally(
|
( _options_stack(. + [$opts]) as $_
|
||||||
_repeat_break(_repl_loop);
|
| _finally(
|
||||||
_options_stack(.[:-1])
|
_repeat_break(_repl_loop);
|
||||||
|
_options_stack(.[:-1])
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
else empty
|
||||||
|
end;
|
||||||
|
|
||||||
def _repl_slurp($opts): _repl($opts);
|
def _repl_slurp($opts): _repl($opts);
|
||||||
def _repl_slurp: _repl({});
|
def _repl_slurp: _repl({});
|
||||||
@ -229,7 +232,7 @@ def _repl_slurp: _repl({});
|
|||||||
# TODO: introspect and show doc, reflection somehow?
|
# TODO: introspect and show doc, reflection somehow?
|
||||||
def help:
|
def help:
|
||||||
( "Type expression to evaluate"
|
( "Type expression to evaluate"
|
||||||
, "\\t Auto completion"
|
, "\\t Completion"
|
||||||
, "Up/Down History"
|
, "Up/Down History"
|
||||||
, "^C Interrupt execution"
|
, "^C Interrupt execution"
|
||||||
, "... | repl Start a new REPL"
|
, "... | repl Start a new REPL"
|
||||||
|
6
pkg/interp/testdata/paste.fqtest
vendored
Normal file
6
pkg/interp/testdata/paste.fqtest
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
$ fq -i
|
||||||
|
null> paste
|
||||||
|
"test\n"
|
||||||
|
null> ^D
|
||||||
|
stdin:
|
||||||
|
test
|
Loading…
Reference in New Issue
Block a user