mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-04 10:57:50 +08:00
parent
9f4a6413c1
commit
00290a6f2b
|
@ -7,20 +7,25 @@ import (
|
|||
|
||||
var ErrArityMismatch = errors.New("arity mismatch")
|
||||
|
||||
var unnamedRestArg = ":"
|
||||
|
||||
// Closure is a closure.
|
||||
type Closure struct {
|
||||
ArgNames []string
|
||||
// The name for the rest argument. If empty, the function has fixed arity.
|
||||
// If equal to unnamedRestArg, the rest argument is unnamed but can be
|
||||
// accessed via $args.
|
||||
RestArg string
|
||||
Op Op
|
||||
Captured map[string]Variable
|
||||
Variadic bool
|
||||
}
|
||||
|
||||
func (*Closure) Kind() string {
|
||||
return "fn"
|
||||
}
|
||||
|
||||
func newClosure(a []string, op Op, e map[string]Variable, v bool) *Closure {
|
||||
return &Closure{a, op, e, v}
|
||||
func newClosure(a []string, r string, op Op, e map[string]Variable) *Closure {
|
||||
return &Closure{a, r, op, e}
|
||||
}
|
||||
|
||||
func (c *Closure) Repr(int) string {
|
||||
|
@ -29,10 +34,15 @@ func (c *Closure) Repr(int) string {
|
|||
|
||||
// Call calls a closure.
|
||||
func (c *Closure) Call(ec *EvalCtx, args []Value) {
|
||||
// TODO Support optional/rest argument
|
||||
// TODO Support keyword arguments
|
||||
if !c.Variadic && len(args) != len(c.ArgNames) {
|
||||
throw(ErrArityMismatch)
|
||||
if c.RestArg != "" {
|
||||
if len(c.ArgNames) > len(args) {
|
||||
throw(ErrArityMismatch)
|
||||
}
|
||||
} else {
|
||||
if len(c.ArgNames) != len(args) {
|
||||
throw(ErrArityMismatch)
|
||||
}
|
||||
}
|
||||
|
||||
// This evalCtx is dedicated to the current form, so we modify it in place.
|
||||
|
@ -46,10 +56,11 @@ func (c *Closure) Call(ec *EvalCtx, args []Value) {
|
|||
}
|
||||
// Make local namespace and pass arguments.
|
||||
ec.local = make(map[string]Variable)
|
||||
if !c.Variadic {
|
||||
for i, name := range c.ArgNames {
|
||||
ec.local[name] = NewPtrVariable(args[i])
|
||||
}
|
||||
for i, name := range c.ArgNames {
|
||||
ec.local[name] = NewPtrVariable(args[i])
|
||||
}
|
||||
if c.RestArg != "" && c.RestArg != unnamedRestArg {
|
||||
ec.local[c.RestArg] = NewPtrVariable(NewList(args[len(c.ArgNames):]...))
|
||||
}
|
||||
ec.local["args"] = NewPtrVariable(List{&args})
|
||||
ec.local["kwargs"] = NewPtrVariable(Map{&map[Value]Value{}})
|
||||
|
|
|
@ -346,26 +346,44 @@ func captureOutput(ec *EvalCtx, op Op) []Value {
|
|||
func (cp *compiler) lambda(n *parse.Primary) ValuesOpFunc {
|
||||
// Collect argument names
|
||||
var argNames []string
|
||||
var variadic bool
|
||||
if n.List != nil {
|
||||
var restArg string
|
||||
if n.List == nil {
|
||||
// { chunk }
|
||||
restArg = unnamedRestArg
|
||||
} else {
|
||||
// [argument list]{ chunk }
|
||||
argNames = make([]string, len(n.List.Compounds))
|
||||
for i, arg := range n.List.Compounds {
|
||||
name := mustString(cp, arg, "expect string")
|
||||
argNames[i] = name
|
||||
qname := mustString(cp, arg, "expect string")
|
||||
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
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// { chunk }
|
||||
variadic = true
|
||||
}
|
||||
|
||||
// XXX The fiddlings with cp.capture is likely wrong.
|
||||
// XXX The fiddlings with cp.capture is error-prone.
|
||||
thisScope := cp.pushScope()
|
||||
thisScope["args"] = true
|
||||
thisScope["kwargs"] = true
|
||||
for _, argName := range argNames {
|
||||
thisScope[argName] = true
|
||||
}
|
||||
if restArg != "" {
|
||||
thisScope[restArg] = true
|
||||
}
|
||||
thisScope["args"] = true
|
||||
thisScope["kwargs"] = true
|
||||
op := cp.chunkOp(n.Chunk)
|
||||
capture := cp.capture
|
||||
cp.capture = scope{}
|
||||
|
@ -380,7 +398,7 @@ func (cp *compiler) lambda(n *parse.Primary) ValuesOpFunc {
|
|||
for name := range capture {
|
||||
evCapture[name] = ec.ResolveVar("", name)
|
||||
}
|
||||
return []Value{newClosure(argNames, op, evCapture, variadic)}
|
||||
return []Value{newClosure(argNames, restArg, op, evCapture)}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -150,11 +150,21 @@ var evalTests = []struct {
|
|||
{inc2,put2}=(f); $put2; $inc2; $put2`,
|
||||
strs("0", "1", "0", "1"), nomore},
|
||||
|
||||
// fn and return.
|
||||
// fn.
|
||||
{"fn f [x]{ put x=$x'.' }; f lorem; f ipsum",
|
||||
strs("x=lorem.", "x=ipsum."), nomore},
|
||||
// return.
|
||||
{"fn f []{ put a; return; put b }; f", strs("a"), nomore},
|
||||
|
||||
// rest args and $args.
|
||||
{"[x @xs]{ put $x $xs $args } a b c",
|
||||
[]Value{String("a"),
|
||||
NewList(String("b"), String("c")),
|
||||
NewList(String("a"), String("b"), String("c"))}, nomore},
|
||||
// $args.
|
||||
{"{ put $args } lorem ipsum",
|
||||
[]Value{NewList(String("lorem"), String("ipsum"))}, nomore},
|
||||
|
||||
// Namespaces
|
||||
// Pseudo-namespaces local: and up:
|
||||
{"x=lorem; []{local:x=ipsum; put $up:x $local:x}",
|
||||
|
|
Loading…
Reference in New Issue
Block a user