func (scopes *scopes) CurrentFunction() *ast.Func {
	c := len(scopes.s) - 1
	return scopes.s[c].fn.node
}
CurrentFunction returns the function of the current scope or nil if there
is no function. There is no function for the main block of scripts.
package compiler
import (
	"reflect"
	"github.com/open2b/scriggo/ast"
)
type scopes struct {
	s           []scope
	path        string
	allowUnused bool
}
type scope struct {
	fn scopeFunc
	block *ast.Position
	names map[string]scopeName
}
type scopeFunc struct {
	node *ast.Func
	labels map[string]scopeLabel
}
type scopeName struct {
	ti *typeInfo
	decl *ast.Identifier
	impor *ast.Import
	used bool
}
type scopeLabel struct {
	block *ast.Position
	node *ast.Label
	used bool
	gotos []scopeGoto
}
type scopeGoto struct {
	pos *ast.Position
	blocks []*ast.Position
}
func newScopes(path string, formats map[ast.Format]reflect.Type, global map[string]scopeName) *scopes {
	var formatScope scope
	if len(formats) > 0 {
		formatScope = scope{names: map[string]scopeName{}}
		for f, t := range formats {
			name := formatTypeName[f]
			formatScope.names[name] = scopeName{ti: &typeInfo{
				Type:       t,
				Properties: propertyIsType | propertyIsFormatType | propertyUniverse,
			}}
		}
	}
	return &scopes{
		path: path,
		s:    []scope{{names: universe}, formatScope, {names: global}, {names: map[string]scopeName{}}},
	}
}
func (scopes *scopes) AllowUnused() {
	scopes.allowUnused = true
}
func (scopes *scopes) Universe(name string) (*typeInfo, bool) {
	n, ok := universe[name]
	if ok {
		return n.ti, true
	}
	n, ok = scopes.s[1].names[name]
	return n.ti, ok
}
func (scopes *scopes) Global(name string) (*typeInfo, bool) {
	n, ok := scopes.s[2].names[name]
	return n.ti, ok
}
func (scopes *scopes) FilePackage(name string) (*typeInfo, bool) {
	n, ok := scopes.s[3].names[name]
	return n.ti, ok
}
func (scopes *scopes) Current(name string) (*ast.Identifier, bool) {
	n, ok := scopes.s[len(scopes.s)-1].names[name]
	return n.decl, ok
}
func (scopes *scopes) ExportedDeclarations() map[string]*typeInfo {
	decls := map[string]*typeInfo{}
	for name, n := range scopes.s[3].names {
		if n.impor == nil && isExported(name) {
			decls[name] = n.ti
		}
	}
	return decls
}
func (scopes *scopes) ExportedDeclarationNodes() map[string]*ast.Identifier {
	decls := map[string]*ast.Identifier{}
	for name, n := range scopes.s[3].names {
		if n.impor == nil && isExported(name) {
			decls[name] = n.decl
		}
	}
	return decls
}
func (scopes *scopes) Declare(name string, ti *typeInfo, decl *ast.Identifier, impor *ast.Import) bool {
	c := len(scopes.s) - 1
	n := scopeName{ti: ti}
	n.decl = decl
	n.impor = impor
	if names := scopes.s[c].names; names == nil {
		scopes.s[c].names = map[string]scopeName{name: n}
	} else if _, ok := names[name]; ok {
		return false
	} else {
		names[name] = n
	}
	return true
}
func (scopes *scopes) DeclareLabel(label *ast.Label) {
	c := len(scopes.s) - 1
	current := scopes.s[c]
	name := label.Ident.Name
	lbl, used := current.fn.labels[name]
	lbl.block = current.block
	lbl.node = label
	current.fn.labels[name] = lbl
	if !used {
		return
	}
	for _, g := range lbl.gotos {
		if i := c - 4; len(g.blocks) <= i || g.blocks[i] != current.block {
			panic(checkError(scopes.path, g.pos, "goto %s jumps into block starting at %s:%s",
				name, scopes.path, current.block))
		}
		var v *ast.Identifier
		s := g.pos.Start
		for _, n := range current.names {
			if n.ti.Addressable() && n.decl.Position.Start > s {
				v = n.decl
				s = n.decl.Position.Start
			}
		}
		if v != nil {
			panic(checkError(scopes.path, g.pos, "goto %s jumps over declaration of %s at %s:%s",
				name, v.Name, scopes.path, v.Position))
		}
	}
	return
}
func (scopes *scopes) Lookup(name string) (*typeInfo, ast.Node, bool) {
	n, i := scopes.lookup(name, 0)
	var node ast.Node = n.decl
	if n.decl == nil {
		node = n.impor
	}
	return n.ti, node, i != -1
}
func (scopes *scopes) LookupImport(name string) (*ast.Import, bool) {
	n, _ := scopes.lookup(name, 3)
	if n.impor == nil {
		return nil, false
	}
	return n.impor, true
}
func (scopes *scopes) LookupInFunc(name string) (*typeInfo, *ast.Identifier, bool) {
	n, i := scopes.lookup(name, 4)
	return n.ti, n.decl, i != -1
}
func (scopes *scopes) LookupLabel(name string) (*ast.Label, bool) {
	c := len(scopes.s) - 1
	lbl := scopes.s[c].fn.labels[name]
	if lbl.node == nil {
		return nil, false
	}
	return lbl.node, true
}
func (scopes *scopes) lookup(name string, start int) (scopeName, int) {
	for i := len(scopes.s) - 1; i >= start; i-- {
		if n, ok := scopes.s[i].names[name]; ok {
			return n, i
		}
	}
	return scopeName{}, -1
}
func (scopes *scopes) Use(name string) bool {
	n, i := scopes.lookup(name, 3)
	if i != -1 && !n.used {
		n.used = true
		scopes.s[i].names[name] = n
		return false
	}
	return true
}
func (scopes *scopes) UseLabel(stmt string, ident *ast.Identifier) *ast.Label {
	if ident == nil {
		return nil
	}
	c := len(scopes.s) - 1
	current := scopes.s[c]
	name := ident.Name
	lbl := current.fn.labels[name]
	defer func() {
		current.fn.labels[name] = lbl
	}()
	lbl.used = true
	if stmt == "goto" {
		if lbl.node == nil {
			blocks := make([]*ast.Position, len(scopes.s)-4)
			for i, n := range scopes.s[4:] {
				blocks[i] = n.block
			}
			lbl.gotos = append(lbl.gotos, scopeGoto{pos: ident.Position, blocks: blocks})
			return nil
		}
		for i := c; i >= 4; i-- {
			if scopes.s[i].block == lbl.block {
				return lbl.node
			}
		}
		panic(checkError(scopes.path, lbl.block, "goto %s jumps into block starting at :%s", name, lbl.block))
	}
	if lbl.node == nil {
		panic(checkError(scopes.path, ident, "%s label not defined: %s", stmt, name))
	}
	return lbl.node
}
func (scopes *scopes) UnusedImport() *ast.Import {
	unused := map[*ast.Import]bool{}
	for _, n := range scopes.s[3].names {
		if n.impor == nil {
			continue
		}
		if n.ti.IsPackage() {
			if !n.used {
				unused[n.impor] = true
			}
			continue
		}
		if _, ok := unused[n.impor]; !ok || n.used {
			unused[n.impor] = !n.used
		}
	}
	var node *ast.Import
	for im, ok := range unused {
		if ok && (node == nil || im.Position.Start < node.Start) {
			node = im
		}
	}
	return node
}