2016-02-20 00:21:51 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"strings"
|
2017-08-13 06:03:28 +08:00
|
|
|
"sync"
|
2016-02-20 00:21:51 +08:00
|
|
|
|
2018-01-01 04:31:45 +08:00
|
|
|
"github.com/elves/elvish/eval/types"
|
2016-02-20 00:21:51 +08:00
|
|
|
"github.com/elves/elvish/glob"
|
|
|
|
"github.com/elves/elvish/parse"
|
2018-01-21 09:14:39 +08:00
|
|
|
"github.com/elves/elvish/util"
|
2017-08-31 04:20:18 +08:00
|
|
|
"github.com/xiaq/persistent/hashmap"
|
2016-02-20 00:21:51 +08:00
|
|
|
)
|
|
|
|
|
2016-02-22 02:25:49 +08:00
|
|
|
var outputCaptureBufferSize = 16
|
|
|
|
|
2018-01-21 06:34:38 +08:00
|
|
|
// ValuesOp is an operation on an Frame that produce Value's.
|
2016-02-20 07:48:13 +08:00
|
|
|
type ValuesOp struct {
|
|
|
|
Func ValuesOpFunc
|
|
|
|
Begin, End int
|
|
|
|
}
|
|
|
|
|
2017-06-11 07:04:53 +08:00
|
|
|
// ValuesOpFunc is the body of ValuesOp.
|
2018-01-21 09:14:39 +08:00
|
|
|
type ValuesOpFunc func(*Frame) ([]types.Value, error)
|
2017-06-11 07:04:53 +08:00
|
|
|
|
|
|
|
// Exec executes a ValuesOp and produces Value's.
|
2018-01-21 09:14:39 +08:00
|
|
|
func (op ValuesOp) Exec(ec *Frame) ([]types.Value, error) {
|
2016-02-20 07:48:13 +08:00
|
|
|
ec.begin, ec.end = op.Begin, op.End
|
|
|
|
return op.Func(ec)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cp *compiler) compound(n *parse.Compound) ValuesOpFunc {
|
2016-02-20 00:21:51 +08:00
|
|
|
if len(n.Indexings) == 0 {
|
|
|
|
return literalStr("")
|
|
|
|
}
|
|
|
|
|
|
|
|
tilde := false
|
|
|
|
indexings := n.Indexings
|
|
|
|
|
|
|
|
if n.Indexings[0].Head.Type == parse.Tilde {
|
|
|
|
// A lone ~.
|
|
|
|
if len(n.Indexings) == 1 {
|
2018-01-21 09:14:39 +08:00
|
|
|
return func(ec *Frame) ([]types.Value, error) {
|
|
|
|
home, err := util.GetHome("")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return []types.Value{types.String(home)}, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
tilde = true
|
|
|
|
indexings = indexings[1:]
|
|
|
|
}
|
|
|
|
|
2016-02-20 07:48:13 +08:00
|
|
|
ops := cp.indexingOps(indexings)
|
2016-02-20 00:21:51 +08:00
|
|
|
|
2018-01-21 09:14:39 +08:00
|
|
|
return func(ec *Frame) ([]types.Value, error) {
|
2016-02-20 00:21:51 +08:00
|
|
|
// Accumulator.
|
2018-01-21 09:14:39 +08:00
|
|
|
vs, err := ops[0].Exec(ec)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-20 00:21:51 +08:00
|
|
|
|
|
|
|
// Logger.Printf("concatenating %v with %d more", vs, len(ops)-1)
|
|
|
|
|
|
|
|
for _, op := range ops[1:] {
|
2018-01-21 09:14:39 +08:00
|
|
|
us, err := op.Exec(ec)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-21 19:17:17 +08:00
|
|
|
vs, err = outerProduct(vs, us, cat)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
if tilde {
|
2018-01-01 04:31:45 +08:00
|
|
|
newvs := make([]types.Value, len(vs))
|
2016-02-20 00:21:51 +08:00
|
|
|
for i, v := range vs {
|
|
|
|
newvs[i] = doTilde(v)
|
|
|
|
}
|
|
|
|
vs = newvs
|
|
|
|
}
|
|
|
|
hasGlob := false
|
|
|
|
for _, v := range vs {
|
|
|
|
if _, ok := v.(GlobPattern); ok {
|
|
|
|
hasGlob = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if hasGlob {
|
2018-01-01 04:31:45 +08:00
|
|
|
newvs := make([]types.Value, 0, len(vs))
|
2016-02-20 00:21:51 +08:00
|
|
|
for _, v := range vs {
|
|
|
|
if gp, ok := v.(GlobPattern); ok {
|
|
|
|
// Logger.Printf("globbing %v", gp)
|
2016-10-12 23:11:09 +08:00
|
|
|
newvs = append(newvs, doGlob(gp, ec.Interrupts())...)
|
2016-02-20 00:21:51 +08:00
|
|
|
} else {
|
|
|
|
newvs = append(newvs, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vs = newvs
|
|
|
|
}
|
2018-01-21 09:14:39 +08:00
|
|
|
return vs, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:17:17 +08:00
|
|
|
func cat(lhs, rhs types.Value) (types.Value, error) {
|
2016-02-20 00:21:51 +08:00
|
|
|
switch lhs := lhs.(type) {
|
2018-01-02 09:34:09 +08:00
|
|
|
case types.String:
|
2016-02-20 00:21:51 +08:00
|
|
|
switch rhs := rhs.(type) {
|
2018-01-02 09:34:09 +08:00
|
|
|
case types.String:
|
2018-01-21 19:17:17 +08:00
|
|
|
return lhs + rhs, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
case GlobPattern:
|
|
|
|
segs := stringToSegments(string(lhs))
|
|
|
|
// We know rhs contains exactly one segment.
|
|
|
|
segs = append(segs, rhs.Segments[0])
|
2018-01-21 19:17:17 +08:00
|
|
|
return GlobPattern{glob.Pattern{segs, ""}, rhs.Flags, rhs.Buts}, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
case GlobPattern:
|
|
|
|
// NOTE Modifies lhs in place.
|
|
|
|
switch rhs := rhs.(type) {
|
2018-01-02 09:34:09 +08:00
|
|
|
case types.String:
|
2016-02-20 00:21:51 +08:00
|
|
|
lhs.append(stringToSegments(string(rhs))...)
|
2018-01-21 19:17:17 +08:00
|
|
|
return lhs, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
case GlobPattern:
|
|
|
|
// We know rhs contains exactly one segment.
|
|
|
|
lhs.append(rhs.Segments[0])
|
2017-02-03 05:23:20 +08:00
|
|
|
lhs.Flags |= rhs.Flags
|
2017-02-16 12:10:31 +08:00
|
|
|
lhs.Buts = append(lhs.Buts, rhs.Buts...)
|
2018-01-21 19:17:17 +08:00
|
|
|
return lhs, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
2018-01-21 19:17:17 +08:00
|
|
|
return nil, fmt.Errorf("unsupported concat: %s and %s", lhs.Kind(), rhs.Kind())
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:17:17 +08:00
|
|
|
func outerProduct(vs []types.Value, us []types.Value, f func(types.Value, types.Value) (types.Value, error)) ([]types.Value, error) {
|
2018-01-01 04:31:45 +08:00
|
|
|
ws := make([]types.Value, 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 {
|
2018-01-21 19:17:17 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2018-01-21 19:17:17 +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.
|
2016-02-22 23:05:14 +08:00
|
|
|
var (
|
|
|
|
ErrBadGlobPattern = errors.New("bad GlobPattern; elvish bug")
|
|
|
|
ErrCannotDetermineUsername = errors.New("cannot determine user name from glob pattern")
|
|
|
|
)
|
|
|
|
|
2018-01-01 04:31:45 +08:00
|
|
|
func doTilde(v types.Value) types.Value {
|
2016-02-20 00:21:51 +08:00
|
|
|
switch v := v.(type) {
|
2018-01-02 09:34:09 +08:00
|
|
|
case types.String:
|
2016-02-20 00:21:51 +08:00
|
|
|
s := string(v)
|
|
|
|
i := strings.Index(s, "/")
|
|
|
|
var uname, rest string
|
|
|
|
if i == -1 {
|
|
|
|
uname = s
|
|
|
|
} else {
|
|
|
|
uname = s[:i]
|
|
|
|
rest = s[i+1:]
|
|
|
|
}
|
|
|
|
dir := mustGetHome(uname)
|
2018-01-02 09:34:09 +08:00
|
|
|
return types.String(path.Join(dir, rest))
|
2016-02-20 00:21:51 +08:00
|
|
|
case GlobPattern:
|
2016-02-22 23:05:14 +08:00
|
|
|
if len(v.Segments) == 0 {
|
|
|
|
throw(ErrBadGlobPattern)
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
2017-02-03 03:15:42 +08:00
|
|
|
switch seg := v.Segments[0].(type) {
|
2016-02-22 23:05:14 +08:00
|
|
|
case glob.Literal:
|
2017-02-03 03:15:42 +08:00
|
|
|
s := seg.Data
|
2016-02-22 23:05:14 +08:00
|
|
|
// Find / in the first segment to determine the username.
|
|
|
|
i := strings.Index(s, "/")
|
|
|
|
if i == -1 {
|
|
|
|
throw(ErrCannotDetermineUsername)
|
|
|
|
}
|
|
|
|
uname := s[:i]
|
|
|
|
dir := mustGetHome(uname)
|
|
|
|
// Replace ~uname in first segment with the found path.
|
2017-02-03 03:15:42 +08:00
|
|
|
v.Segments[0] = glob.Literal{dir + s[i:]}
|
2016-02-22 23:05:14 +08:00
|
|
|
case glob.Slash:
|
|
|
|
v.DirOverride = mustGetHome("")
|
|
|
|
default:
|
|
|
|
throw(ErrCannotDetermineUsername)
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
return v
|
|
|
|
default:
|
|
|
|
throw(fmt.Errorf("tilde doesn't work on value of type %s", v.Kind()))
|
|
|
|
panic("unreachable")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 07:48:13 +08:00
|
|
|
func (cp *compiler) array(n *parse.Array) ValuesOpFunc {
|
2018-01-21 09:14:39 +08:00
|
|
|
return newSeqValuesOp(cp.compoundOps(n.Compounds))
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
|
2016-02-20 07:48:13 +08:00
|
|
|
func (cp *compiler) indexing(n *parse.Indexing) ValuesOpFunc {
|
2016-02-20 00:21:51 +08:00
|
|
|
if len(n.Indicies) == 0 {
|
|
|
|
return cp.primary(n.Head)
|
|
|
|
}
|
|
|
|
|
2016-02-20 07:48:13 +08:00
|
|
|
headOp := cp.primaryOp(n.Head)
|
|
|
|
indexOps := cp.arrayOps(n.Indicies)
|
2016-02-20 00:21:51 +08:00
|
|
|
|
2018-01-21 09:14:39 +08:00
|
|
|
return func(ec *Frame) ([]types.Value, error) {
|
|
|
|
vs, err := headOp.Exec(ec)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-20 00:21:51 +08:00
|
|
|
for _, indexOp := range indexOps {
|
2018-01-21 09:14:39 +08:00
|
|
|
indicies, err := indexOp.Exec(ec)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-01 04:31:45 +08:00
|
|
|
newvs := make([]types.Value, 0, len(vs)*len(indicies))
|
2016-02-20 00:21:51 +08:00
|
|
|
for _, v := range vs {
|
2018-01-21 07:35:31 +08:00
|
|
|
indexer, ok := v.(types.Indexer)
|
|
|
|
if !ok {
|
2018-01-21 09:14:39 +08:00
|
|
|
return nil, fmt.Errorf("a %s not indexable", v.Kind())
|
2018-01-21 07:35:31 +08:00
|
|
|
}
|
|
|
|
for _, index := range indicies {
|
|
|
|
result, err := indexer.Index(index)
|
2018-01-21 09:14:39 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-21 07:35:31 +08:00
|
|
|
newvs = append(newvs, result)
|
|
|
|
}
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
vs = newvs
|
|
|
|
}
|
2018-01-21 09:14:39 +08:00
|
|
|
return vs, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-01 04:31:45 +08:00
|
|
|
func literalValues(v ...types.Value) ValuesOpFunc {
|
2018-01-21 09:14:39 +08:00
|
|
|
return func(e *Frame) ([]types.Value, error) {
|
|
|
|
return v, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 07:48:13 +08:00
|
|
|
func literalStr(text string) ValuesOpFunc {
|
2018-01-02 09:34:09 +08:00
|
|
|
return literalValues(types.String(text))
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
|
2016-02-20 19:25:51 +08:00
|
|
|
func variable(qname string) ValuesOpFunc {
|
2017-12-23 22:28:30 +08:00
|
|
|
explode, ns, name := ParseVariable(qname)
|
2018-01-21 09:14:39 +08:00
|
|
|
return func(ec *Frame) ([]types.Value, error) {
|
2016-02-20 00:21:51 +08:00
|
|
|
variable := ec.ResolveVar(ns, name)
|
|
|
|
if variable == nil {
|
2018-01-21 09:14:39 +08:00
|
|
|
return nil, fmt.Errorf("variable $%s not found", qname)
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
value := variable.Get()
|
2016-10-13 14:10:10 +08:00
|
|
|
if explode {
|
2018-01-01 04:31:45 +08:00
|
|
|
iterator, ok := value.(types.Iterator)
|
2016-02-20 00:21:51 +08:00
|
|
|
if !ok {
|
2016-09-08 02:17:51 +08:00
|
|
|
// Use qname[1:] to skip the leading "@"
|
2018-01-21 09:14:39 +08:00
|
|
|
return nil, fmt.Errorf("variable $%s (kind %s) cannot be exploded", qname[1:], value.Kind())
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
2018-01-21 09:14:39 +08:00
|
|
|
return types.CollectFromIterator(iterator), nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
2018-01-21 09:14:39 +08:00
|
|
|
return []types.Value{value}, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 07:48:13 +08:00
|
|
|
func (cp *compiler) primary(n *parse.Primary) ValuesOpFunc {
|
2016-02-20 00:21:51 +08:00
|
|
|
switch n.Type {
|
|
|
|
case parse.Bareword, parse.SingleQuoted, parse.DoubleQuoted:
|
|
|
|
return literalStr(n.Value)
|
|
|
|
case parse.Variable:
|
|
|
|
qname := n.Value
|
2017-12-24 07:51:32 +08:00
|
|
|
if !cp.registerVariableGetQname(qname) {
|
2016-02-20 04:16:23 +08:00
|
|
|
cp.errorf("variable $%s not found", n.Value)
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
2016-02-20 19:25:51 +08:00
|
|
|
return variable(qname)
|
2016-02-20 00:21:51 +08:00
|
|
|
case parse.Wildcard:
|
2017-06-27 01:47:10 +08:00
|
|
|
seg, err := wildcardToSegment(n.SourceText())
|
|
|
|
if err != nil {
|
|
|
|
cp.errorf("%s", err)
|
|
|
|
}
|
2018-01-01 04:31:45 +08:00
|
|
|
vs := []types.Value{
|
2017-06-27 01:47:10 +08:00
|
|
|
GlobPattern{glob.Pattern{[]glob.Segment{seg}, ""}, 0, nil}}
|
2018-01-21 09:14:39 +08:00
|
|
|
return literalValues(vs...)
|
2016-02-20 00:21:51 +08:00
|
|
|
case parse.Tilde:
|
2016-02-20 04:16:23 +08:00
|
|
|
cp.errorf("compiler bug: Tilde not handled in .compound")
|
2016-02-20 00:21:51 +08:00
|
|
|
return literalStr("~")
|
2017-02-18 12:43:27 +08:00
|
|
|
case parse.ExceptionCapture:
|
|
|
|
return cp.exceptionCapture(n.Chunk)
|
2016-02-20 00:21:51 +08:00
|
|
|
case parse.OutputCapture:
|
|
|
|
return cp.outputCapture(n)
|
|
|
|
case parse.List:
|
2017-08-06 03:49:50 +08:00
|
|
|
return cp.list(n)
|
2016-02-20 00:21:51 +08:00
|
|
|
case parse.Lambda:
|
|
|
|
return cp.lambda(n)
|
|
|
|
case parse.Map:
|
|
|
|
return cp.map_(n)
|
|
|
|
case parse.Braced:
|
|
|
|
return cp.braced(n)
|
|
|
|
default:
|
2016-02-20 04:16:23 +08:00
|
|
|
cp.errorf("bad PrimaryType; parser bug")
|
2016-02-20 00:21:51 +08:00
|
|
|
return literalStr(n.SourceText())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-06 03:49:50 +08:00
|
|
|
func (cp *compiler) list(n *parse.Primary) ValuesOpFunc {
|
|
|
|
// TODO(xiaq): Use Vector.Cons to build the list, instead of building a
|
|
|
|
// slice and converting to Vector.
|
2018-01-21 09:14:39 +08:00
|
|
|
op := newSeqValuesOp(cp.compoundOps(n.Elements))
|
|
|
|
return func(ec *Frame) ([]types.Value, error) {
|
|
|
|
values, err := op(ec)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return []types.Value{types.MakeList(values...)}, nil
|
2016-03-08 09:01:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-18 12:43:27 +08:00
|
|
|
func (cp *compiler) exceptionCapture(n *parse.Chunk) ValuesOpFunc {
|
2016-02-20 07:48:13 +08:00
|
|
|
op := cp.chunkOp(n)
|
2018-01-21 09:14:39 +08:00
|
|
|
return func(ec *Frame) ([]types.Value, error) {
|
2017-02-16 12:37:13 +08:00
|
|
|
err := ec.PEval(op)
|
|
|
|
if err == nil {
|
2018-01-21 09:14:39 +08:00
|
|
|
return []types.Value{OK}, nil
|
2017-02-16 12:37:13 +08:00
|
|
|
}
|
2018-01-21 09:14:39 +08:00
|
|
|
return []types.Value{err.(*Exception)}, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 07:48:13 +08:00
|
|
|
func (cp *compiler) outputCapture(n *parse.Primary) ValuesOpFunc {
|
|
|
|
op := cp.chunkOp(n.Chunk)
|
2018-01-21 09:14:39 +08:00
|
|
|
return func(ec *Frame) ([]types.Value, error) {
|
|
|
|
return captureOutput(ec, op), nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-01 04:31:45 +08:00
|
|
|
func captureOutput(ec *Frame, op Op) []types.Value {
|
2016-10-22 21:02:27 +08:00
|
|
|
vs, err := pcaptureOutput(ec, op)
|
|
|
|
maybeThrow(err)
|
|
|
|
return vs
|
|
|
|
}
|
|
|
|
|
2018-01-01 04:31:45 +08:00
|
|
|
func pcaptureOutput(ec *Frame, op Op) ([]types.Value, error) {
|
|
|
|
vs := []types.Value{}
|
2017-08-13 06:03:28 +08:00
|
|
|
var m sync.Mutex
|
2018-01-01 04:31:45 +08:00
|
|
|
valueCb := func(ch <-chan types.Value) {
|
2016-02-20 00:21:51 +08:00
|
|
|
for v := range ch {
|
2017-08-13 06:03:28 +08:00
|
|
|
m.Lock()
|
2016-02-20 00:21:51 +08:00
|
|
|
vs = append(vs, v)
|
2017-08-13 06:03:28 +08:00
|
|
|
m.Unlock()
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
2017-08-13 06:03:28 +08:00
|
|
|
}
|
|
|
|
bytesCb := func(r *os.File) {
|
|
|
|
buffered := bufio.NewReader(r)
|
2016-02-20 00:21:51 +08:00
|
|
|
for {
|
2017-08-13 06:03:28 +08:00
|
|
|
line, err := buffered.ReadString('\n')
|
2017-07-11 01:00:32 +08:00
|
|
|
if line != "" {
|
2018-01-02 09:34:09 +08:00
|
|
|
v := types.String(strings.TrimSuffix(line, "\n"))
|
2017-08-13 06:03:28 +08:00
|
|
|
m.Lock()
|
|
|
|
vs = append(vs, v)
|
|
|
|
m.Unlock()
|
2017-07-11 01:00:32 +08:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
if err != io.EOF {
|
|
|
|
logger.Println("error on reading:", err)
|
|
|
|
}
|
2016-02-20 00:21:51 +08:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2017-08-13 06:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
err := pcaptureOutputInner(ec, op, valueCb, bytesCb)
|
|
|
|
return vs, err
|
|
|
|
}
|
|
|
|
|
2018-01-01 04:31:45 +08:00
|
|
|
func pcaptureOutputInner(ec *Frame, op Op, valuesCb func(<-chan types.Value), bytesCb func(*os.File)) error {
|
2017-08-13 06:03:28 +08:00
|
|
|
|
2017-09-23 05:06:27 +08:00
|
|
|
newEc := ec.fork("[output capture]")
|
2017-08-13 06:03:28 +08:00
|
|
|
|
2018-01-01 04:31:45 +08:00
|
|
|
ch := make(chan types.Value, outputCaptureBufferSize)
|
2017-08-13 06:03:28 +08:00
|
|
|
pipeRead, pipeWrite, err := os.Pipe()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create pipe: %v", err)
|
|
|
|
}
|
|
|
|
newEc.ports[1] = &Port{
|
|
|
|
Chan: ch, CloseChan: true,
|
|
|
|
File: pipeWrite, CloseFile: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
bytesCollected := make(chan struct{})
|
|
|
|
chCollected := make(chan struct{})
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
valuesCb(ch)
|
|
|
|
close(chCollected)
|
|
|
|
}()
|
|
|
|
go func() {
|
|
|
|
bytesCb(pipeRead)
|
|
|
|
pipeRead.Close()
|
|
|
|
close(bytesCollected)
|
2016-02-20 00:21:51 +08:00
|
|
|
}()
|
|
|
|
|
2016-10-22 21:02:27 +08:00
|
|
|
err = newEc.PEval(op)
|
2016-02-20 00:21:51 +08:00
|
|
|
|
2017-08-13 06:03:28 +08:00
|
|
|
ClosePorts(newEc.ports)
|
2016-02-20 00:21:51 +08:00
|
|
|
<-bytesCollected
|
|
|
|
<-chCollected
|
|
|
|
|
2017-08-13 06:03:28 +08:00
|
|
|
return err
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
|
2016-02-20 07:48:13 +08:00
|
|
|
func (cp *compiler) lambda(n *parse.Primary) ValuesOpFunc {
|
2017-08-06 04:27:44 +08:00
|
|
|
// Parse signature.
|
|
|
|
var (
|
|
|
|
argNames []string
|
|
|
|
restArgName string
|
|
|
|
optNames []string
|
|
|
|
optDefaultOps []ValuesOp
|
|
|
|
)
|
2017-08-06 03:49:50 +08:00
|
|
|
if len(n.Elements) > 0 {
|
2017-08-06 04:27:44 +08:00
|
|
|
// Argument list.
|
2017-08-06 03:49:50 +08:00
|
|
|
argNames = make([]string, len(n.Elements))
|
|
|
|
for i, arg := range n.Elements {
|
2017-08-06 04:27:44 +08:00
|
|
|
qname := mustString(cp, arg, "argument name must be literal string")
|
2017-12-23 22:28:30 +08:00
|
|
|
explode, ns, name := ParseVariable(qname)
|
2016-02-26 08:54:27 +08:00
|
|
|
if ns != "" {
|
2017-08-06 04:27:44 +08:00
|
|
|
cp.errorpf(arg.Begin(), arg.End(), "argument name must be unqualified")
|
2016-02-26 08:54:27 +08:00
|
|
|
}
|
|
|
|
if name == "" {
|
|
|
|
cp.errorpf(arg.Begin(), arg.End(), "argument name must not be empty")
|
|
|
|
}
|
2016-10-13 14:10:10 +08:00
|
|
|
if explode {
|
2017-08-06 03:49:50 +08:00
|
|
|
if i != len(n.Elements)-1 {
|
2016-02-26 08:54:27 +08:00
|
|
|
cp.errorpf(arg.Begin(), arg.End(), "only the last argument may have @")
|
|
|
|
}
|
2017-08-06 04:27:44 +08:00
|
|
|
restArgName = name
|
2016-02-26 08:54:27 +08:00
|
|
|
argNames = argNames[:i]
|
|
|
|
} else {
|
|
|
|
argNames[i] = name
|
|
|
|
}
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
2017-08-06 04:27:44 +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")
|
2017-12-23 22:28:30 +08:00
|
|
|
_, ns, name := ParseVariable(qname)
|
2017-08-06 04:27:44 +08:00
|
|
|
if ns != "" {
|
|
|
|
cp.errorpf(opt.Key.Begin(), opt.Key.End(), "option name must be unqualified")
|
|
|
|
}
|
|
|
|
if name == "" {
|
|
|
|
cp.errorpf(opt.Key.Begin(), opt.Key.End(), "option name must not be empty")
|
|
|
|
}
|
|
|
|
optNames[i] = name
|
|
|
|
if opt.Value == nil {
|
|
|
|
cp.errorpf(opt.End(), opt.End(), "option must have default value")
|
|
|
|
} else {
|
|
|
|
optDefaultOps[i] = cp.compoundOp(opt.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-20 00:21:51 +08:00
|
|
|
|
|
|
|
thisScope := cp.pushScope()
|
|
|
|
for _, argName := range argNames {
|
2017-12-24 07:51:32 +08:00
|
|
|
thisScope.set(argName)
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
2017-08-06 04:27:44 +08:00
|
|
|
if restArgName != "" {
|
2017-12-24 07:51:32 +08:00
|
|
|
thisScope.set(restArgName)
|
2017-08-06 04:27:44 +08:00
|
|
|
}
|
|
|
|
for _, optName := range optNames {
|
2017-12-24 07:51:32 +08:00
|
|
|
thisScope.set(optName)
|
2016-02-26 08:54:27 +08:00
|
|
|
}
|
2017-12-24 07:51:32 +08:00
|
|
|
thisScope.set("opts")
|
2017-08-06 04:27:44 +08:00
|
|
|
|
2016-02-20 07:48:13 +08:00
|
|
|
op := cp.chunkOp(n.Chunk)
|
2017-08-06 04:27:44 +08:00
|
|
|
|
|
|
|
// XXX The fiddlings with cp.capture is error-prone.
|
2016-02-20 00:21:51 +08:00
|
|
|
capture := cp.capture
|
2017-12-24 07:51:32 +08:00
|
|
|
cp.capture = make(staticNs)
|
2016-02-20 00:21:51 +08:00
|
|
|
cp.popScope()
|
|
|
|
|
2017-12-24 07:51:32 +08:00
|
|
|
for name := range capture {
|
|
|
|
cp.registerVariableGetQname(name)
|
2017-09-21 07:10:21 +08:00
|
|
|
}
|
2016-02-20 00:21:51 +08:00
|
|
|
|
2018-01-05 12:07:11 +08:00
|
|
|
srcMeta := cp.srcMeta
|
2016-10-11 21:37:27 +08:00
|
|
|
|
2018-01-21 09:14:39 +08:00
|
|
|
return func(ec *Frame) ([]types.Value, error) {
|
2017-12-24 07:51:32 +08:00
|
|
|
evCapture := make(Ns)
|
|
|
|
for name := range capture {
|
|
|
|
evCapture[name] = ec.ResolveVar("", name)
|
2017-09-21 07:10:21 +08:00
|
|
|
}
|
2018-01-01 04:31:45 +08:00
|
|
|
optDefaults := make([]types.Value, len(optDefaultOps))
|
2017-08-06 04:27:44 +08:00
|
|
|
for i, op := range optDefaultOps {
|
2017-09-05 07:15:06 +08:00
|
|
|
defaultValue := ec.ExecAndUnwrap("option default value", op).One().Any()
|
|
|
|
optDefaults[i] = defaultValue
|
2017-08-06 04:27:44 +08:00
|
|
|
}
|
2017-09-20 20:43:28 +08:00
|
|
|
// XXX(xiaq): Capture uses.
|
2018-01-21 09:14:39 +08:00
|
|
|
return []types.Value{&Closure{argNames, restArgName, optNames, optDefaults, op, evCapture, srcMeta}}, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 07:48:13 +08:00
|
|
|
func (cp *compiler) map_(n *parse.Primary) ValuesOpFunc {
|
2016-09-15 05:34:10 +08:00
|
|
|
return cp.mapPairs(n.MapPairs)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cp *compiler) mapPairs(pairs []*parse.MapPair) ValuesOpFunc {
|
|
|
|
npairs := len(pairs)
|
2016-02-20 00:21:51 +08:00
|
|
|
keysOps := make([]ValuesOp, npairs)
|
|
|
|
valuesOps := make([]ValuesOp, npairs)
|
2016-02-20 07:48:13 +08:00
|
|
|
begins, ends := make([]int, npairs), make([]int, npairs)
|
2016-09-15 05:34:10 +08:00
|
|
|
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 {
|
2016-02-20 07:48:13 +08:00
|
|
|
p := pair.End()
|
2018-01-01 23:21:15 +08:00
|
|
|
valuesOps[i] = ValuesOp{literalValues(types.Bool(true)), p, p}
|
2016-02-20 00:21:51 +08:00
|
|
|
} else {
|
2016-09-15 05:34:10 +08:00
|
|
|
valuesOps[i] = cp.compoundOp(pairs[i].Value)
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
2016-02-20 07:48:13 +08:00
|
|
|
begins[i], ends[i] = pair.Begin(), pair.End()
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
2018-01-21 09:14:39 +08:00
|
|
|
return func(ec *Frame) ([]types.Value, error) {
|
2017-08-31 04:20:18 +08:00
|
|
|
m := hashmap.Empty
|
2016-02-20 00:21:51 +08:00
|
|
|
for i := 0; i < npairs; i++ {
|
2018-01-21 09:14:39 +08:00
|
|
|
keys, err := keysOps[i].Exec(ec)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
values, err := valuesOps[i].Exec(ec)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-20 00:21:51 +08:00
|
|
|
if len(keys) != len(values) {
|
2016-02-20 07:48:13 +08:00
|
|
|
ec.errorpf(begins[i], ends[i],
|
|
|
|
"%d keys but %d values", len(keys), len(values))
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
for j, key := range keys {
|
2017-08-31 04:20:18 +08:00
|
|
|
m = m.Assoc(key, values[j])
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
2018-01-21 09:14:39 +08:00
|
|
|
return []types.Value{types.NewMap(m)}, nil
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 07:48:13 +08:00
|
|
|
func (cp *compiler) braced(n *parse.Primary) ValuesOpFunc {
|
|
|
|
ops := cp.compoundOps(n.Braced)
|
2016-02-20 00:21:51 +08:00
|
|
|
// TODO: n.IsRange
|
|
|
|
// isRange := n.IsRange
|
2018-01-21 09:14:39 +08:00
|
|
|
return newSeqValuesOp(ops)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newSeqValuesOp(ops []ValuesOp) ValuesOpFunc {
|
|
|
|
return func(ec *Frame) ([]types.Value, error) {
|
|
|
|
var values []types.Value
|
|
|
|
for _, op := range ops {
|
|
|
|
moreValues, err := op.Exec(ec)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
values = append(values, moreValues...)
|
|
|
|
}
|
|
|
|
return values, nil
|
|
|
|
}
|
2016-02-20 00:21:51 +08:00
|
|
|
}
|