elvish/eval/compile_values_op.go

467 lines
10 KiB
Go
Raw Normal View History

2016-02-20 00:21:51 +08:00
package eval
import (
"bufio"
"errors"
"fmt"
"io"
"log"
"os"
"path"
"strings"
"github.com/elves/elvish/glob"
"github.com/elves/elvish/parse"
)
var outputCaptureBufferSize = 16
2016-02-20 07:48:13 +08:00
// ValuesOp is an operation on an EvalCtx that produce Value's.
type ValuesOp struct {
Func ValuesOpFunc
Begin, End int
}
func (op ValuesOp) Exec(ec *EvalCtx) []Value {
ec.begin, ec.end = op.Begin, op.End
return op.Func(ec)
}
type ValuesOpFunc func(*EvalCtx) []Value
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 {
return func(ec *EvalCtx) []Value {
return []Value{String(mustGetHome(""))}
}
}
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
return func(ec *EvalCtx) []Value {
// Accumulator.
2016-02-20 07:48:13 +08:00
vs := ops[0].Exec(ec)
2016-02-20 00:21:51 +08:00
// Logger.Printf("concatenating %v with %d more", vs, len(ops)-1)
for _, op := range ops[1:] {
2016-02-20 07:48:13 +08:00
us := op.Exec(ec)
2016-02-20 00:21:51 +08:00
vs = outerProduct(vs, us, cat)
// Logger.Printf("with %v => %v", us, vs)
}
if tilde {
newvs := make([]Value, len(vs))
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 {
newvs := make([]Value, 0, len(vs))
for _, v := range vs {
if gp, ok := v.(GlobPattern); ok {
// Logger.Printf("globbing %v", gp)
newvs = append(newvs, doGlob(gp)...)
} else {
newvs = append(newvs, v)
}
}
vs = newvs
}
return vs
}
}
func cat(lhs, rhs Value) Value {
switch lhs := lhs.(type) {
case String:
switch rhs := rhs.(type) {
case String:
return lhs + rhs
case GlobPattern:
segs := stringToSegments(string(lhs))
// We know rhs contains exactly one segment.
segs = append(segs, rhs.Segments[0])
return GlobPattern{segs, ""}
2016-02-20 00:21:51 +08:00
}
case GlobPattern:
// NOTE Modifies lhs in place.
switch rhs := rhs.(type) {
case String:
lhs.append(stringToSegments(string(rhs))...)
return lhs
case GlobPattern:
// We know rhs contains exactly one segment.
lhs.append(rhs.Segments[0])
return lhs
}
}
throw(fmt.Errorf("unsupported concat: %s and %s", lhs.Kind(), rhs.Kind()))
panic("unreachable")
}
func outerProduct(vs []Value, us []Value, f func(Value, Value) Value) []Value {
ws := make([]Value, len(vs)*len(us))
nu := len(us)
for i, v := range vs {
for j, u := range us {
ws[i*nu+j] = f(v, u)
}
}
return ws
}
var (
ErrBadGlobPattern = errors.New("bad GlobPattern; elvish bug")
ErrCannotDetermineUsername = errors.New("cannot determine user name from glob pattern")
)
2016-02-20 00:21:51 +08:00
func doTilde(v Value) Value {
switch v := v.(type) {
case String:
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)
return String(path.Join(dir, rest))
case GlobPattern:
if len(v.Segments) == 0 {
throw(ErrBadGlobPattern)
2016-02-20 00:21:51 +08:00
}
switch v.Segments[0].Type {
case glob.Literal:
s := v.Segments[0].Data
// 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.
v.Segments[0].Data = dir + s[i:]
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 {
return catValuesOps(cp.compoundOps(n.Compounds))
2016-02-20 00:21:51 +08:00
}
2016-02-20 07:48:13 +08:00
func catValuesOps(ops []ValuesOp) ValuesOpFunc {
2016-02-20 00:21:51 +08:00
return func(ec *EvalCtx) []Value {
// Use number of compound expressions as an estimation of the number
// of values
vs := make([]Value, 0, len(ops))
for _, op := range ops {
2016-02-20 07:48:13 +08:00
us := op.Exec(ec)
2016-02-20 00:21:51 +08:00
vs = append(vs, us...)
}
return vs
}
}
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
return func(ec *EvalCtx) []Value {
2016-02-20 07:48:13 +08:00
vs := headOp.Exec(ec)
2016-02-20 00:21:51 +08:00
for _, indexOp := range indexOps {
2016-02-20 07:48:13 +08:00
indicies := indexOp.Exec(ec)
2016-02-20 00:21:51 +08:00
newvs := make([]Value, 0, len(vs)*len(indicies))
for _, v := range vs {
newvs = append(newvs, mustIndexer(v, ec).Index(indicies)...)
}
vs = newvs
}
return vs
}
}
2016-02-20 07:48:13 +08:00
func literalValues(v ...Value) ValuesOpFunc {
2016-02-20 00:21:51 +08:00
return func(e *EvalCtx) []Value {
return v
}
}
2016-02-20 07:48:13 +08:00
func literalStr(text string) ValuesOpFunc {
2016-02-20 00:21:51 +08:00
return literalValues(String(text))
}
func variable(qname string) ValuesOpFunc {
2016-03-21 07:35:21 +08:00
splice, ns, name := ParseVariable(qname)
2016-02-20 00:21:51 +08:00
return func(ec *EvalCtx) []Value {
variable := ec.ResolveVar(ns, name)
if variable == nil {
2016-02-20 07:48:13 +08:00
ec.errorf("variable $%s not found", qname)
2016-02-20 00:21:51 +08:00
}
value := variable.Get()
if splice {
iterator, ok := value.(Iterator)
2016-02-20 00:21:51 +08:00
if !ok {
2016-02-20 07:48:13 +08:00
ec.errorf("variable $%s (kind %s) cannot be spliced", qname, value.Kind())
2016-02-20 00:21:51 +08:00
}
return collectFromIterator(iterator)
2016-02-20 00:21:51 +08:00
}
return []Value{value}
}
}
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
if !cp.registerVariableGet(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
}
return variable(qname)
2016-02-20 00:21:51 +08:00
case parse.Wildcard:
vs := []Value{GlobPattern{[]glob.Segment{
wildcardToSegment(n.SourceText())}, ""}}
2016-02-20 00:21:51 +08:00
return func(ec *EvalCtx) []Value {
return vs
}
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("~")
case parse.ErrorCapture:
return cp.errorCapture(n.Chunk)
case parse.OutputCapture:
return cp.outputCapture(n)
case parse.List:
2016-03-08 09:01:58 +08:00
return cp.list(n.List)
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())
}
}
2016-03-08 09:01:58 +08:00
func (cp *compiler) list(n *parse.Array) ValuesOpFunc {
if len(n.Semicolons) == 0 {
op := cp.arrayOp(n)
return func(ec *EvalCtx) []Value {
return []Value{NewList(op.Exec(ec)...)}
}
} else {
ns := len(n.Semicolons)
rowOps := make([]ValuesOpFunc, ns+1)
f := func(k, i, j int) {
rowOps[k] = catValuesOps(cp.compoundOps(n.Compounds[i:j]))
}
f(0, 0, n.Semicolons[0])
for i := 1; i < ns; i++ {
f(i, n.Semicolons[i-1], n.Semicolons[i])
}
f(ns, n.Semicolons[ns-1], len(n.Compounds))
return func(ec *EvalCtx) []Value {
rows := make([]Value, ns+1)
for i := 0; i <= ns; i++ {
rows[i] = NewList(rowOps[i](ec)...)
}
return []Value{List{&rows}}
}
}
}
2016-02-20 07:48:13 +08:00
func (cp *compiler) errorCapture(n *parse.Chunk) ValuesOpFunc {
op := cp.chunkOp(n)
2016-02-20 00:21:51 +08:00
return func(ec *EvalCtx) []Value {
return []Value{Error{ec.PEval(op)}}
}
}
2016-02-20 07:48:13 +08:00
func (cp *compiler) outputCapture(n *parse.Primary) ValuesOpFunc {
op := cp.chunkOp(n.Chunk)
2016-02-20 00:21:51 +08:00
return func(ec *EvalCtx) []Value {
return captureOutput(ec, op)
}
}
func captureOutput(ec *EvalCtx, op Op) []Value {
vs := []Value{}
2016-03-17 03:51:03 +08:00
newEc := ec.fork(fmt.Sprintf("output capture %v", op))
2016-02-20 00:21:51 +08:00
pipeRead, pipeWrite, err := os.Pipe()
if err != nil {
throw(fmt.Errorf("failed to create pipe: %v", err))
}
bufferedPipeRead := bufio.NewReader(pipeRead)
ch := make(chan Value, outputCaptureBufferSize)
bytesCollected := make(chan bool)
chCollected := make(chan bool)
newEc.ports[1] = &Port{Chan: ch, File: pipeWrite, CloseFile: true}
go func() {
for v := range ch {
vs = append(vs, v)
}
chCollected <- true
}()
go func() {
for {
line, err := bufferedPipeRead.ReadString('\n')
if err == io.EOF {
break
} else if err != nil {
// TODO report error
log.Println(err)
break
}
ch <- String(line[:len(line)-1])
}
bytesCollected <- true
}()
2016-02-20 07:48:13 +08:00
op.Exec(newEc)
2016-02-20 00:21:51 +08:00
ClosePorts(newEc.ports)
<-bytesCollected
close(ch)
<-chCollected
return vs
}
2016-02-20 07:48:13 +08:00
func (cp *compiler) lambda(n *parse.Primary) ValuesOpFunc {
2016-02-20 00:21:51 +08:00
// Collect argument names
var argNames []string
var restArg string
if n.List == nil {
// { chunk }
restArg = unnamedRestArg
} else {
2016-02-20 00:21:51 +08:00
// [argument list]{ chunk }
argNames = make([]string, len(n.List.Compounds))
for i, arg := range n.List.Compounds {
qname := mustString(cp, arg, "expect string")
2016-03-21 07:35:21 +08:00
splice, ns, name := ParseVariable(qname)
if ns != "" {
cp.errorpf(arg.Begin(), arg.End(), "must be unqualified")
}
if name == "" {
cp.errorpf(arg.Begin(), arg.End(), "argument name must not be empty")
}
if splice {
if i != len(n.List.Compounds)-1 {
cp.errorpf(arg.Begin(), arg.End(), "only the last argument may have @")
}
restArg = name
argNames = argNames[:i]
} else {
argNames[i] = name
}
2016-02-20 00:21:51 +08:00
}
}
// XXX The fiddlings with cp.capture is error-prone.
2016-02-20 00:21:51 +08:00
thisScope := cp.pushScope()
for _, argName := range argNames {
thisScope[argName] = true
}
if restArg != "" {
thisScope[restArg] = true
}
thisScope["args"] = true
thisScope["kwargs"] = true
2016-02-20 07:48:13 +08:00
op := cp.chunkOp(n.Chunk)
2016-02-20 00:21:51 +08:00
capture := cp.capture
cp.capture = scope{}
cp.popScope()
for name := range capture {
cp.registerVariableGet(name)
}
return func(ec *EvalCtx) []Value {
evCapture := make(map[string]Variable, len(capture))
for name := range capture {
evCapture[name] = ec.ResolveVar("", name)
}
return []Value{newClosure(argNames, restArg, op, evCapture)}
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-02-20 00:21:51 +08:00
npairs := len(n.MapPairs)
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-02-20 00:21:51 +08:00
for i, pair := range n.MapPairs {
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()
valuesOps[i] = ValuesOp{literalValues(Bool(true)), p, p}
2016-02-20 00:21:51 +08:00
} else {
2016-02-20 07:48:13 +08:00
valuesOps[i] = cp.compoundOp(n.MapPairs[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
}
return func(ec *EvalCtx) []Value {
m := make(map[Value]Value)
for i := 0; i < npairs; i++ {
2016-02-20 07:48:13 +08:00
keys := keysOps[i].Exec(ec)
values := valuesOps[i].Exec(ec)
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 {
m[key] = values[j]
}
}
return []Value{Map{&m}}
}
}
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
return catValuesOps(ops)
}