package eval

import (
	
	
	
	
	

	
	
	
	
	
	
)

// A user-defined function in Elvish code. Each closure has its unique identity.
type closure struct {
	ArgNames []string
	// The index of the rest argument. -1 if there is no rest argument.
	RestArg     int
	OptNames    []string
	OptDefaults []interface{}
	Op          effectOp
	NewLocal    []string
	Captured    *Ns
	SrcMeta     parse.Source
	DefRange    diag.Ranging
}

var _ Callable = &closure{}

// Kind returns "fn".
func (*closure) () string {
	return "fn"
}

// Equal compares by address.
func ( *closure) ( interface{}) bool {
	return  == 
}

// Hash returns the hash of the address of the closure.
func ( *closure) () uint32 {
	return hash.Pointer(unsafe.Pointer())
}

// Repr returns an opaque representation "<closure 0x23333333>".
func ( *closure) (int) string {
	return fmt.Sprintf("<closure %p>", )
}

// Call calls a closure.
func ( *closure) ( *Frame,  []interface{},  map[string]interface{}) error {
	// Check number of arguments.
	if .RestArg != -1 {
		if len() < len(.ArgNames)-1 {
			return errs.ArityMismatch{
				What:     "arguments here",
				ValidLow: len(.ArgNames) - 1, ValidHigh: -1, Actual: len()}
		}
	} else {
		if len() != len(.ArgNames) {
			return errs.ArityMismatch{
				What:     "arguments here",
				ValidLow: len(.ArgNames), ValidHigh: len(.ArgNames), Actual: len()}
		}
	}
	// Check whether all supplied options are supported. This map contains the
	// subset of keys from opts that can be found in c.OptNames.
	 := make(map[string]struct{})
	for ,  := range .OptNames {
		,  := []
		if  {
			[] = struct{}{}
		}
	}
	if len() < len() {
		// Report all the options that are not supported.
		 := make([]string, 0, len()-len())
		for  := range  {
			,  := []
			if ! {
				 = append(, parse.Quote())
			}
		}
		sort.Strings()
		return UnsupportedOptionsError{}
	}

	// This Frame is dedicated to the current form, so we can modify it in place.

	// BUG(xiaq): When evaluating closures, async access to global variables
	// and ports can be problematic.

	// Make upvalue namespace and capture variables.
	.up = .Captured

	// Populate local scope with arguments, options, and newly created locals.
	 := len(.ArgNames) + len(.OptNames) + len(.NewLocal)
	 := &Ns{make([]vars.Var, ), make([]string, ), make([]bool, )}

	for ,  := range .ArgNames {
		.names[] = 
	}
	if .RestArg == -1 {
		for  := range .ArgNames {
			.slots[] = vars.FromInit([])
		}
	} else {
		for  := 0;  < .RestArg; ++ {
			.slots[] = vars.FromInit([])
		}
		 := len() - len(.ArgNames)
		.slots[.RestArg] = vars.FromInit(
			vals.MakeList([.RestArg : .RestArg++1]...))
		for  := .RestArg + 1;  < len(.ArgNames); ++ {
			.slots[] = vars.FromInit([+])
		}
	}

	 := len(.ArgNames)
	for ,  := range .OptNames {
		,  := []
		if ! {
			 = .OptDefaults[]
		}
		.names[+] = 
		.slots[+] = vars.FromInit()
	}

	 += len(.OptNames)
	for ,  := range .NewLocal {
		.names[+] = 
		.slots[+] = MakeVarFromName()
	}

	.local = 
	.srcMeta = .SrcMeta
	return .Op.exec()
}

// MakeVarFromName creates a Var with a suitable type constraint inferred from
// the name.
func ( string) vars.Var {
	switch {
	case strings.HasSuffix(, FnSuffix):
		 := NewGoFn("nop~", nop)
		return vars.FromPtr(&)
	case strings.HasSuffix(, NsSuffix):
		 := (*Ns)(nil)
		return vars.FromPtr(&)
	default:
		return vars.FromInit(nil)
	}
}

// UnsupportedOptionsError is an error returned by a closure call when there are
// unsupported options.
type UnsupportedOptionsError struct {
	Options []string
}

func ( UnsupportedOptionsError) () string {
	if len(.Options) == 1 {
		return fmt.Sprintf("unsupported option: %s", .Options[0])
	}
	return fmt.Sprintf("unsupported options: %s", strings.Join(.Options, ", "))
}

func ( *closure) () vals.StructMap { return closureFields{} }

type closureFields struct{ c *closure }

func (closureFields) () {}

func ( closureFields) () vals.List { return listOfStrings(.c.ArgNames) }
func ( closureFields) () string     { return strconv.Itoa(.c.RestArg) }
func ( closureFields) () vals.List { return listOfStrings(.c.OptNames) }
func ( closureFields) () parse.Source   { return .c.SrcMeta }

func ( closureFields) () vals.List {
	return vals.MakeList(.c.OptDefaults...)
}

func ( closureFields) () string {
	 := .c.Op.(diag.Ranger).Range()
	return .c.SrcMeta.Code[.From:.To]
}

func ( closureFields) () string {
	return .c.SrcMeta.Code[.c.DefRange.From:.c.DefRange.To]
}

func ( []string) vals.List {
	 := vals.EmptyList
	for ,  := range  {
		 = .Cons()
	}
	return 
}