package interp

import (
	
	
	
)

// A sKind represents the kind of symbol.
type sKind uint

// Symbol kinds for the Go interpreter.
const (
	undefSym   sKind = iota
	binSym           // Binary from runtime
	bltnSym          // Builtin
	constSym         // Constant
	funcSym          // Function
	labelSym         // Label
	pkgSym           // Package
	typeSym          // Type
	varTypeSym       // Variable type (generic)
	varSym           // Variable
)

var symKinds = [...]string{
	undefSym:   "undefSym",
	binSym:     "binSym",
	bltnSym:    "bltnSym",
	constSym:   "constSym",
	funcSym:    "funcSym",
	labelSym:   "labelSym",
	pkgSym:     "pkgSym",
	typeSym:    "typeSym",
	varTypeSym: "varTypeSym",
	varSym:     "varSym",
}

func ( sKind) () string {
	if  < sKind(len(symKinds)) {
		return symKinds[]
	}
	return "SymKind(" + strconv.Itoa(int()) + ")"
}

// A symbol represents an interpreter object such as type, constant, var, func,
// label, builtin or binary object. Symbols are defined within a scope.
type symbol struct {
	kind    sKind
	typ     *itype        // Type of value
	node    *node         // Node value if index is negative
	from    []*node       // list of goto nodes jumping to this label node, or nil
	recv    *receiver     // receiver node value, if sym refers to a method
	index   int           // index of value in frame or -1
	rval    reflect.Value // default value (used for constants)
	builtin bltnGenerator // Builtin function or nil
	global  bool          // true if symbol is defined in global space
}

// scope type stores symbols in maps, and frame layout as array of types
// The purposes of scopes are to manage the visibility of each symbol
// and to store the memory frame layout information (type and index in frame)
// at each level (global, package, functions)
//
// scopes are organized in a stack fashion: a first scope (universe) is created
// once at global level, and for each block (package, func, for, etc...), a new
// scope is pushed at entry, and poped at exit.
//
// Nested scopes with the same level value use the same frame: it allows to have
// exactly one frame per function, with a fixed position for each variable (named
// or not), no matter the inner complexity (number of nested blocks in the function)
//
// In symbols, the index value corresponds to the index in scope.types, and at
// execution to the index in frame, created exactly from the types layout.
type scope struct {
	anc         *scope             // ancestor upper scope
	child       []*scope           // included scopes
	def         *node              // function definition node this scope belongs to, or nil
	loop        *node              // loop exit node for break statement
	loopRestart *node              // loop restart node for continue statement
	pkgID       string             // unique id of package in which scope is defined
	pkgName     string             // package name for the package
	types       []reflect.Type     // frame layout, may be shared by same level scopes
	level       int                // frame level: number of frame indirections to access var during execution
	sym         map[string]*symbol // map of symbols defined in this current scope
	global      bool               // true if scope refers to global space (single frame for universe and package level scopes)
	iota        int                // iota value in this scope
}

// push creates a new child scope and chain it to the current one.
func ( *scope) ( bool) *scope {
	 := &scope{anc: , level: .level, sym: map[string]*symbol{}}
	.child = append(.child, )
	if  {
		.types = []reflect.Type{}
		.level = .level + 1
	} else {
		// Propagate size, types, def and global as scopes at same level share the same frame.
		.types = .types
		.def = .def
		.global = .global
		.level = .level
	}
	// inherit loop state and pkgID from ancestor
	.loop, .loopRestart, .pkgID = .loop, .loopRestart, .pkgID
	return 
}

func ( *scope) () *scope { return .push(false) }
func ( *scope) () *scope { return .push(true) }

func ( *scope) () *scope {
	if .level == .anc.level {
		// Propagate size and types, as scopes at same level share the same frame.
		.anc.types = .types
	}
	return .anc
}

func ( *scope) () *scope {
	 := .level
	for  != nil && .level ==  {
		 = .anc
	}
	return 
}

// lookup searches for a symbol in the current scope, and upper ones if not found
// it returns the symbol, the number of indirections level from the current scope
// and status (false if no result).
func ( *scope) ( string) (*symbol, int, bool) {
	 := .level
	for {
		if ,  := .sym[];  {
			if .global {
				return , globalFrame, true
			}
			return ,  - .level, true
		}
		if .anc == nil {
			break
		}
		 = .anc
	}
	return nil, 0, false
}

func ( *scope) ( *node) *itype {
	if , ,  := .lookup(.child[1].ident);  {
		if  := .typ; len(.child) == 3 &&  != nil && (.cat == chanT || .cat == chanRecvT) {
			return 
		}
	}

	 := .child[1]
	if .typ == nil {
		return nil
	}
	switch {
	case .typ.cat == chanT, .typ.cat == chanRecvT:
		return .typ
	case .typ.cat == valueT && .typ.rtype.Kind() == reflect.Chan:
		 := chanSendRecv
		switch .typ.rtype.ChanDir() {
		case reflect.RecvDir:
			 = chanRecv
		case reflect.SendDir:
			 = chanSend
		}
		return chanOf(valueTOf(.typ.rtype.Elem()), )
	}

	return nil
}

// fixType returns the input type, or a valid default type for untyped constant.
func ( *scope) ( *itype) *itype {
	if !.untyped || .cat != valueT {
		return 
	}
	switch  := .TypeOf(); .Kind() {
	case reflect.Int64:
		return .getType("int")
	case reflect.Uint64:
		return .getType("uint")
	case reflect.Float64:
		return .getType("float64")
	case reflect.Complex128:
		return .getType("complex128")
	}
	return 
}

func ( *scope) ( string) *itype {
	var  *itype
	if , ,  := .lookup();  {
		if .kind == typeSym {
			 = .typ
		}
	}
	return 
}

// add adds a type to the scope types array, and returns its index.
func ( *scope) ( *itype) ( int) {
	if  == nil {
		log.Panic("nil type")
	}
	 = len(.types)
	 := .frameType()
	if  == nil {
		log.Panic("nil reflect type")
	}
	.types = append(.types, )
	return
}

func ( *Interpreter) (,  string) *scope {
	 := .universe

	.mutex.Lock()
	if ,  := .scopes[]; ! {
		.scopes[] = .pushBloc()
	}
	 = .scopes[]
	.pkgID = 
	.pkgName = 
	.mutex.Unlock()
	return 
}

// Globals returns a map of global variables and constants in the main package.
func ( *Interpreter) () map[string]reflect.Value {
	 := map[string]reflect.Value{}
	.mutex.RLock()
	defer .mutex.RUnlock()

	,  := .srcPkg["main"]
	if ! {
		return 
	}

	for ,  := range  {
		switch .kind {
		case constSym:
			[] = .rval
		case varSym:
			[] = .frame.data[.index]
		}
	}

	return 
}