elvish/pkg/eval/compile_value.go

628 lines
14 KiB
Go
Raw Normal View History

2016-02-20 00:21:51 +08:00
package eval
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"strings"
"sync"
2016-02-20 00:21:51 +08:00
"github.com/elves/elvish/pkg/diag"
2019-12-24 04:00:59 +08:00
"github.com/elves/elvish/pkg/eval/vals"
"github.com/elves/elvish/pkg/eval/vars"
"github.com/elves/elvish/pkg/fsutil"
2019-12-24 04:00:59 +08:00
"github.com/elves/elvish/pkg/glob"
"github.com/elves/elvish/pkg/parse"
"github.com/elves/elvish/pkg/strutil"
2016-02-20 00:21:51 +08:00
)
// An operation that produces values.
type valuesOp interface {
diag.Ranger
exec(*Frame) ([]interface{}, error)
}
var outputCaptureBufferSize = 16
func (cp *compiler) compoundOp(n *parse.Compound) valuesOp {
2016-02-20 00:21:51 +08:00
if len(n.Indexings) == 0 {
return literalValues(n, "")
2016-02-20 00:21:51 +08:00
}
tilde := false
indexings := n.Indexings
if n.Indexings[0].Head.Type == parse.Tilde {
// A lone ~.
if len(n.Indexings) == 1 {
return loneTildeOp{n.Range()}
2016-02-20 00:21:51 +08:00
}
tilde = true
indexings = indexings[1:]
}
return compoundOp{n.Range(), tilde, cp.indexingOps(indexings)}
}
type loneTildeOp struct{ diag.Ranging }
func (loneTildeOp) exec(fm *Frame) ([]interface{}, error) {
home, err := fsutil.GetHome("")
if err != nil {
return nil, err
}
return []interface{}{home}, nil
}
func (cp *compiler) compoundOps(ns []*parse.Compound) []valuesOp {
ops := make([]valuesOp, len(ns))
for i, n := range ns {
ops[i] = cp.compoundOp(n)
}
return ops
}
type compoundOp struct {
diag.Ranging
tilde bool
subops []valuesOp
}
func (op compoundOp) exec(fm *Frame) ([]interface{}, error) {
// Accumulator.
vs, err := op.subops[0].exec(fm)
if err != nil {
return nil, err
}
2016-02-20 00:21:51 +08:00
for _, subop := range op.subops[1:] {
us, err := subop.exec(fm)
if err != nil {
return nil, err
}
vs, err = outerProduct(vs, us, vals.Concat)
if err != nil {
return nil, fm.errorp(op, err)
2016-02-20 00:21:51 +08:00
}
}
if op.tilde {
newvs := make([]interface{}, len(vs))
for i, v := range vs {
2018-09-27 22:32:41 +08:00
tilded, err := doTilde(v)
if err != nil {
return nil, fm.errorp(op, err)
2018-09-27 22:32:41 +08:00
}
newvs[i] = tilded
2016-02-20 00:21:51 +08:00
}
vs = newvs
}
hasGlob := false
for _, v := range vs {
if _, ok := v.(GlobPattern); ok {
hasGlob = true
break
2016-02-20 00:21:51 +08:00
}
}
if hasGlob {
newvs := make([]interface{}, 0, len(vs))
for _, v := range vs {
if gp, ok := v.(GlobPattern); ok {
2018-09-27 08:48:44 +08:00
results, err := doGlob(gp, fm.Interrupts())
if err != nil {
return nil, fm.errorp(op, err)
2018-09-27 08:48:44 +08:00
}
newvs = append(newvs, results...)
} else {
newvs = append(newvs, v)
2016-02-20 00:21:51 +08:00
}
}
vs = newvs
2016-02-20 00:21:51 +08:00
}
return vs, nil
2016-02-20 00:21:51 +08:00
}
func outerProduct(vs []interface{}, us []interface{}, f func(interface{}, interface{}) (interface{}, error)) ([]interface{}, error) {
ws := make([]interface{}, len(vs)*len(us))
2016-02-20 00:21:51 +08:00
nu := len(us)
for i, v := range vs {
for j, u := range us {
var err error
ws[i*nu+j], err = f(v, u)
if err != nil {
return nil, err
}
2016-02-20 00:21:51 +08:00
}
}
return ws, nil
2016-02-20 00:21:51 +08:00
}
2017-06-11 07:04:53 +08:00
// Errors thrown when globbing.
var (
ErrBadGlobPattern = errors.New("bad GlobPattern; elvish bug")
ErrCannotDetermineUsername = errors.New("cannot determine user name from glob pattern")
)
2018-09-27 22:32:41 +08:00
func doTilde(v interface{}) (interface{}, error) {
2016-02-20 00:21:51 +08:00
switch v := v.(type) {
case string:
s := v
2016-02-20 00:21:51 +08:00
i := strings.Index(s, "/")
var uname, rest string
if i == -1 {
uname = s
} else {
uname = s[:i]
rest = s[i+1:]
}
dir, err := fsutil.GetHome(uname)
if err != nil {
return nil, err
}
// We do not use path.Join, as it removes trailing slashes.
//
// TODO(xiaq): Make this correct on Windows.
return dir + "/" + rest, nil
2016-02-20 00:21:51 +08:00
case GlobPattern:
if len(v.Segments) == 0 {
2018-09-27 22:32:41 +08:00
return nil, ErrBadGlobPattern
2016-02-20 00:21:51 +08:00
}
2017-02-03 03:15:42 +08:00
switch seg := v.Segments[0].(type) {
case glob.Literal:
if len(v.Segments) == 1 {
return nil, ErrBadGlobPattern
}
_, isSlash := v.Segments[1].(glob.Slash)
if isSlash {
// ~username or ~username/xxx. Replace the first segment with
// the home directory of the specified user.
dir, err := fsutil.GetHome(seg.Data)
if err != nil {
return nil, err
}
v.Segments[0] = glob.Literal{Data: dir}
return v, nil
}
case glob.Slash:
dir, err := fsutil.GetHome("")
if err != nil {
return nil, err
}
v.DirOverride = dir
return v, nil
2016-02-20 00:21:51 +08:00
}
return nil, ErrCannotDetermineUsername
2016-02-20 00:21:51 +08:00
default:
2018-09-27 22:32:41 +08:00
return nil, fmt.Errorf("tilde doesn't work on value of type %s", vals.Kind(v))
2016-02-20 00:21:51 +08:00
}
}
func (cp *compiler) arrayOp(n *parse.Array) valuesOp {
return seqValuesOp{n.Range(), cp.compoundOps(n.Compounds)}
}
func (cp *compiler) arrayOps(ns []*parse.Array) []valuesOp {
ops := make([]valuesOp, len(ns))
for i, n := range ns {
ops[i] = cp.arrayOp(n)
}
return ops
2016-02-20 00:21:51 +08:00
}
func (cp *compiler) indexingOp(n *parse.Indexing) valuesOp {
2016-02-20 00:21:51 +08:00
if len(n.Indicies) == 0 {
return cp.primaryOp(n.Head)
2016-02-20 00:21:51 +08:00
}
return &indexingOp{n.Range(), cp.primaryOp(n.Head), cp.arrayOps(n.Indicies)}
}
2016-02-20 00:21:51 +08:00
func (cp *compiler) indexingOps(ns []*parse.Indexing) []valuesOp {
ops := make([]valuesOp, len(ns))
for i, n := range ns {
ops[i] = cp.indexingOp(n)
}
return ops
2016-02-20 00:21:51 +08:00
}
type indexingOp struct {
diag.Ranging
headOp valuesOp
indexOps []valuesOp
2016-02-20 00:21:51 +08:00
}
func (op *indexingOp) exec(fm *Frame) ([]interface{}, error) {
vs, err := op.headOp.exec(fm)
if err != nil {
return nil, err
}
for _, indexOp := range op.indexOps {
2020-10-06 02:35:52 +08:00
indices, err := indexOp.exec(fm)
if err != nil {
return nil, err
2016-02-20 00:21:51 +08:00
}
2020-10-06 02:35:52 +08:00
newvs := make([]interface{}, 0, len(vs)*len(indices))
for _, v := range vs {
2020-10-06 02:35:52 +08:00
for _, index := range indices {
2018-02-15 17:14:05 +08:00
result, err := vals.Index(v, index)
if err != nil {
return nil, fm.errorp(op, err)
}
deprecation := vals.CheckDeprecatedIndex(v, index)
if deprecation != "" {
fm.Deprecate(deprecation, diag.NewContext(fm.srcMeta.Name, fm.srcMeta.Code, indexOp))
}
newvs = append(newvs, result)
2016-02-20 00:21:51 +08:00
}
}
vs = newvs
2016-02-20 00:21:51 +08:00
}
return vs, nil
2016-02-20 00:21:51 +08:00
}
func (cp *compiler) primaryOp(n *parse.Primary) valuesOp {
2016-02-20 00:21:51 +08:00
switch n.Type {
case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
return literalValues(n, n.Value)
2016-02-20 00:21:51 +08:00
case parse.Variable:
sigil, qname := SplitVariableRef(n.Value)
ref := resolveVarRef(cp, qname, n)
if ref == nil {
cp.errorpf(n, "variable $%s not found", qname)
2016-02-20 00:21:51 +08:00
}
return &variableOp{n.Range(), sigil != "", qname, ref}
2016-02-20 00:21:51 +08:00
case parse.Wildcard:
seg, err := wildcardToSegment(parse.SourceText(n))
2017-06-27 01:47:10 +08:00
if err != nil {
cp.errorpf(n, "%s", err)
2017-06-27 01:47:10 +08:00
}
vs := []interface{}{
GlobPattern{Pattern: glob.Pattern{Segments: []glob.Segment{seg}, DirOverride: ""},
Flags: 0, Buts: nil, TypeCb: nil}}
return literalValues(n, vs...)
2016-02-20 00:21:51 +08:00
case parse.Tilde:
cp.errorpf(n, "compiler bug: Tilde not handled in .compound")
return literalValues(n, "~")
2017-02-18 12:43:27 +08:00
case parse.ExceptionCapture:
return exceptionCaptureOp{n.Range(), cp.chunkOp(n.Chunk)}
2016-02-20 00:21:51 +08:00
case parse.OutputCapture:
return outputCaptureOp{n.Range(), cp.chunkOp(n.Chunk)}
2016-02-20 00:21:51 +08:00
case parse.List:
return listOp{n.Range(), cp.compoundOps(n.Elements)}
2016-02-20 00:21:51 +08:00
case parse.Lambda:
return cp.lambda(n)
2016-02-20 00:21:51 +08:00
case parse.Map:
return mapOp{n.Range(), cp.mapPairs(n.MapPairs)}
2016-02-20 00:21:51 +08:00
case parse.Braced:
return seqValuesOp{n.Range(), cp.compoundOps(n.Braced)}
2016-02-20 00:21:51 +08:00
default:
cp.errorpf(n, "bad PrimaryType; parser bug")
return literalValues(n, parse.SourceText(n))
}
}
func (cp *compiler) primaryOps(ns []*parse.Primary) []valuesOp {
ops := make([]valuesOp, len(ns))
for i, n := range ns {
ops[i] = cp.primaryOp(n)
2016-02-20 00:21:51 +08:00
}
return ops
2016-02-20 00:21:51 +08:00
}
type variableOp struct {
diag.Ranging
explode bool
qname string
ref *varRef
}
func (op variableOp) exec(fm *Frame) ([]interface{}, error) {
variable := deref(fm, op.ref)
if variable == nil {
return nil, fm.errorpf(op, "variable $%s not found", op.qname)
}
value := variable.Get()
if op.explode {
vs, err := vals.Collect(value)
return vs, fm.errorp(op, err)
2016-03-08 09:01:58 +08:00
}
return []interface{}{value}, nil
2016-03-08 09:01:58 +08:00
}
type listOp struct {
diag.Ranging
subops []valuesOp
}
func (op listOp) exec(fm *Frame) ([]interface{}, error) {
2018-02-15 17:14:05 +08:00
list := vals.EmptyList
2018-01-21 21:24:00 +08:00
for _, subop := range op.subops {
moreValues, err := subop.exec(fm)
2018-01-21 21:24:00 +08:00
if err != nil {
return nil, err
}
for _, moreValue := range moreValues {
list = list.Cons(moreValue)
2018-01-21 21:24:00 +08:00
}
2016-02-20 00:21:51 +08:00
}
return []interface{}{list}, nil
2016-02-20 00:21:51 +08:00
}
type exceptionCaptureOp struct {
diag.Ranging
subop effectOp
}
func (op exceptionCaptureOp) exec(fm *Frame) ([]interface{}, error) {
2020-04-10 04:01:14 +08:00
err := op.subop.exec(fm)
if err == nil {
return []interface{}{OK}, nil
2016-02-20 00:21:51 +08:00
}
return []interface{}{err.(*Exception)}, nil
2016-02-20 00:21:51 +08:00
}
type outputCaptureOp struct {
diag.Ranging
subop effectOp
}
func (op outputCaptureOp) exec(fm *Frame) ([]interface{}, error) {
return captureOutput(fm, op.subop.exec)
}
func captureOutput(fm *Frame, f func(*Frame) error) ([]interface{}, error) {
vs := []interface{}{}
var m sync.Mutex
err := pipeOutput(fm, f,
func(ch <-chan interface{}) {
for v := range ch {
m.Lock()
vs = append(vs, v)
m.Unlock()
}
},
func(r *os.File) {
buffered := bufio.NewReader(r)
for {
line, err := buffered.ReadString('\n')
if line != "" {
v := strutil.ChopLineEnding(line)
m.Lock()
vs = append(vs, v)
m.Unlock()
}
if err != nil {
if err != io.EOF {
logger.Println("error on reading:", err)
}
break
}
2016-02-20 00:21:51 +08:00
}
})
return vs, err
}
func pipeOutput(fm *Frame, f func(*Frame) error, valuesCb func(<-chan interface{}), bytesCb func(*os.File)) error {
newFm := fm.fork("[output capture]")
ch := make(chan interface{}, outputCaptureBufferSize)
r, w, err := os.Pipe()
if err != nil {
return err
}
newFm.ports[1] = &Port{
Chan: ch, CloseChan: true,
File: w, CloseFile: true,
}
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
valuesCb(ch)
}()
go func() {
defer wg.Done()
defer r.Close()
bytesCb(r)
2016-02-20 00:21:51 +08:00
}()
err = f(newFm)
newFm.Close()
wg.Wait()
return err
2016-02-20 00:21:51 +08:00
}
func (cp *compiler) lambda(n *parse.Primary) valuesOp {
// Parse signature.
var (
argNames []string
restArg int = -1
optNames []string
optDefaultOps []valuesOp
)
2017-08-06 03:49:50 +08:00
if len(n.Elements) > 0 {
// Argument list.
2017-08-06 03:49:50 +08:00
argNames = make([]string, len(n.Elements))
for i, arg := range n.Elements {
ref := mustString(cp, arg, "argument name must be literal string")
sigil, qname := SplitVariableRef(ref)
ns, name := SplitQNameNs(qname)
if ns != "" {
cp.errorpf(arg, "argument name must be unqualified")
}
if name == "" {
cp.errorpf(arg, "argument name must not be empty")
}
if sigil == "@" {
if restArg != -1 {
cp.errorpf(arg, "only one argument may have @")
}
restArg = i
}
argNames[i] = name
2016-02-20 00:21:51 +08:00
}
}
if len(n.MapPairs) > 0 {
optNames = make([]string, len(n.MapPairs))
optDefaultOps = make([]valuesOp, len(n.MapPairs))
for i, opt := range n.MapPairs {
qname := mustString(cp, opt.Key, "option name must be literal string")
ns, name := SplitQNameNs(qname)
if ns != "" {
cp.errorpf(opt.Key, "option name must be unqualified")
}
if name == "" {
cp.errorpf(opt.Key, "option name must not be empty")
}
optNames[i] = name
if opt.Value == nil {
cp.errorpf(opt.Key, "option must have default value")
} else {
optDefaultOps[i] = cp.compoundOp(opt.Value)
}
}
}
2016-02-20 00:21:51 +08:00
thisScope, thisUp := cp.pushScope()
2016-02-20 00:21:51 +08:00
for _, argName := range argNames {
thisScope.add(argName)
2016-02-20 00:21:51 +08:00
}
for _, optName := range optNames {
thisScope.add(optName)
}
scopeSizeInit := len(thisScope.names)
chunkOp := cp.chunkOp(n.Chunk)
scopeOp := wrapScopeOp(chunkOp, thisScope.names[scopeSizeInit:])
2016-02-20 00:21:51 +08:00
cp.popScope()
return &lambdaOp{n.Range(), argNames, restArg, optNames, optDefaultOps, thisUp, scopeOp, cp.srcMeta}
}
type lambdaOp struct {
diag.Ranging
argNames []string
restArg int
optNames []string
optDefaultOps []valuesOp
capture *staticUpNs
subop effectOp
srcMeta parse.Source
}
func (op *lambdaOp) exec(fm *Frame) ([]interface{}, error) {
capture := &Ns{
names: op.capture.names, slots: make([]vars.Var, len(op.capture.names))}
for i := range op.capture.names {
if op.capture.local[i] {
capture.slots[i] = fm.local.slots[op.capture.index[i]]
} else {
capture.slots[i] = fm.up.slots[op.capture.index[i]]
}
}
optDefaults := make([]interface{}, len(op.optDefaultOps))
for i, op := range op.optDefaultOps {
defaultValue, err := evalForValue(fm, op, "option default value")
if err != nil {
return nil, err
}
optDefaults[i] = defaultValue
2016-02-20 00:21:51 +08:00
}
return []interface{}{&closure{op.argNames, op.restArg, op.optNames, optDefaults, op.subop, capture, op.srcMeta, op.Range()}}, nil
2016-02-20 00:21:51 +08:00
}
type mapOp struct {
diag.Ranging
pairsOp *mapPairsOp
}
func (op mapOp) exec(fm *Frame) ([]interface{}, error) {
m := vals.EmptyMap
err := op.pairsOp.exec(fm, func(k, v interface{}) error {
m = m.Assoc(k, v)
return nil
})
if err != nil {
return nil, err
}
return []interface{}{m}, nil
}
func (cp *compiler) mapPairs(pairs []*parse.MapPair) *mapPairsOp {
npairs := len(pairs)
keysOps := make([]valuesOp, npairs)
valuesOps := make([]valuesOp, npairs)
2016-02-20 07:48:13 +08:00
begins, ends := make([]int, npairs), make([]int, npairs)
for i, pair := range pairs {
2016-02-20 07:48:13 +08:00
keysOps[i] = cp.compoundOp(pair.Key)
2016-02-20 00:21:51 +08:00
if pair.Value == nil {
p := pair.Range().To
valuesOps[i] = literalValues(diag.PointRanging(p), true)
2016-02-20 00:21:51 +08:00
} else {
valuesOps[i] = cp.compoundOp(pairs[i].Value)
2016-02-20 00:21:51 +08:00
}
begins[i], ends[i] = pair.Range().From, pair.Range().To
2016-02-20 00:21:51 +08:00
}
return &mapPairsOp{keysOps, valuesOps, begins, ends}
}
type mapPairsOp struct {
keysOps []valuesOp
valuesOps []valuesOp
begins []int
ends []int
}
func (op *mapPairsOp) exec(fm *Frame, f func(k, v interface{}) error) error {
for i := range op.keysOps {
keys, err := op.keysOps[i].exec(fm)
if err != nil {
return err
}
values, err := op.valuesOps[i].exec(fm)
if err != nil {
return err
}
if len(keys) != len(values) {
return fm.errorpf(diag.Ranging{From: op.begins[i], To: op.ends[i]},
"%d keys but %d values", len(keys), len(values))
}
for j, key := range keys {
err := f(key, values[j])
if err != nil {
return err
}
2016-02-20 00:21:51 +08:00
}
}
return nil
2016-02-20 00:21:51 +08:00
}
type literalValuesOp struct {
diag.Ranging
values []interface{}
}
func (op literalValuesOp) exec(*Frame) ([]interface{}, error) {
return op.values, nil
}
func literalValues(r diag.Ranger, vs ...interface{}) valuesOp {
return literalValuesOp{r.Range(), vs}
}
type seqValuesOp struct {
diag.Ranging
subops []valuesOp
}
func (op seqValuesOp) exec(fm *Frame) ([]interface{}, error) {
var values []interface{}
for _, subop := range op.subops {
moreValues, err := subop.exec(fm)
if err != nil {
return nil, err
}
values = append(values, moreValues...)
}
return values, nil
2016-02-20 00:21:51 +08:00
}