github.com/mattn/anko@v0.1.10/env/env.go (about)

     1  package env
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  	"sync"
     9  )
    10  
    11  type (
    12  	// ExternalLookup for Env external lookup of values and types.
    13  	ExternalLookup interface {
    14  		Get(string) (reflect.Value, error)
    15  		Type(string) (reflect.Type, error)
    16  	}
    17  
    18  	// Env is the environment needed for a VM to run in.
    19  	Env struct {
    20  		rwMutex        *sync.RWMutex
    21  		parent         *Env
    22  		values         map[string]reflect.Value
    23  		types          map[string]reflect.Type
    24  		externalLookup ExternalLookup
    25  	}
    26  )
    27  
    28  var (
    29  	// Packages is a where packages can be stored so VM import command can be used to import them.
    30  	// reflect.Value must be valid or VM may crash.
    31  	// For nil must use NilValue.
    32  	Packages = make(map[string]map[string]reflect.Value)
    33  	// PackageTypes is a where package types can be stored so VM import command can be used to import them
    34  	// reflect.Type must be valid or VM may crash.
    35  	// For nil type must use NilType.
    36  	PackageTypes = make(map[string]map[string]reflect.Type)
    37  
    38  	// NilType is the reflect.type of nil
    39  	NilType = reflect.TypeOf(nil)
    40  	// NilValue is the reflect.value of nil
    41  	NilValue = reflect.New(reflect.TypeOf((*interface{})(nil)).Elem()).Elem()
    42  
    43  	basicTypes = map[string]reflect.Type{
    44  		"interface": reflect.ValueOf([]interface{}{int64(1)}).Index(0).Type(),
    45  		"bool":      reflect.TypeOf(true),
    46  		"string":    reflect.TypeOf("a"),
    47  		"int":       reflect.TypeOf(int(1)),
    48  		"int32":     reflect.TypeOf(int32(1)),
    49  		"int64":     reflect.TypeOf(int64(1)),
    50  		"uint":      reflect.TypeOf(uint(1)),
    51  		"uint32":    reflect.TypeOf(uint32(1)),
    52  		"uint64":    reflect.TypeOf(uint64(1)),
    53  		"byte":      reflect.TypeOf(byte(1)),
    54  		"rune":      reflect.TypeOf('a'),
    55  		"float32":   reflect.TypeOf(float32(1)),
    56  		"float64":   reflect.TypeOf(float64(1)),
    57  	}
    58  
    59  	// ErrSymbolContainsDot symbol contains .
    60  	ErrSymbolContainsDot = errors.New("symbol contains '.'")
    61  )
    62  
    63  // NewEnv creates new global scope.
    64  func NewEnv() *Env {
    65  	return &Env{
    66  		rwMutex: &sync.RWMutex{},
    67  		values:  make(map[string]reflect.Value),
    68  	}
    69  }
    70  
    71  // NewEnv creates new child scope.
    72  func (e *Env) NewEnv() *Env {
    73  	return &Env{
    74  		rwMutex: &sync.RWMutex{},
    75  		parent:  e,
    76  		values:  make(map[string]reflect.Value),
    77  	}
    78  }
    79  
    80  // NewModule creates new child scope and define it as a symbol.
    81  // This is a shortcut for calling e.NewEnv then Define that new Env.
    82  func (e *Env) NewModule(symbol string) (*Env, error) {
    83  	module := &Env{
    84  		rwMutex: &sync.RWMutex{},
    85  		parent:  e,
    86  		values:  make(map[string]reflect.Value),
    87  	}
    88  	return module, e.Define(symbol, module)
    89  }
    90  
    91  // SetExternalLookup sets an external lookup
    92  func (e *Env) SetExternalLookup(externalLookup ExternalLookup) {
    93  	e.externalLookup = externalLookup
    94  }
    95  
    96  // String returns string of values and types in current scope.
    97  func (e *Env) String() string {
    98  	var buffer bytes.Buffer
    99  	e.rwMutex.RLock()
   100  
   101  	if e.parent == nil {
   102  		buffer.WriteString("No parent\n")
   103  	} else {
   104  		buffer.WriteString("Has parent\n")
   105  	}
   106  
   107  	for symbol, value := range e.values {
   108  		buffer.WriteString(fmt.Sprintf("%v = %#v\n", symbol, value))
   109  	}
   110  
   111  	for symbol, aType := range e.types {
   112  		buffer.WriteString(fmt.Sprintf("%v = %v\n", symbol, aType))
   113  	}
   114  
   115  	e.rwMutex.RUnlock()
   116  	return buffer.String()
   117  }
   118  
   119  // GetEnvFromPath returns Env from path
   120  func (e *Env) GetEnvFromPath(path []string) (*Env, error) {
   121  	if len(path) < 1 {
   122  		return e, nil
   123  	}
   124  
   125  	var value reflect.Value
   126  	var ok bool
   127  	for {
   128  		// find starting env
   129  		value, ok = e.values[path[0]]
   130  		if ok {
   131  			e, ok = value.Interface().(*Env)
   132  			if ok {
   133  				break
   134  			}
   135  		}
   136  		if e.parent == nil {
   137  			return nil, fmt.Errorf("no namespace called: %v", path[0])
   138  		}
   139  		e = e.parent
   140  	}
   141  
   142  	for i := 1; i < len(path); i++ {
   143  		// find child env
   144  		value, ok = e.values[path[i]]
   145  		if ok {
   146  			e, ok = value.Interface().(*Env)
   147  			if ok {
   148  				continue
   149  			}
   150  		}
   151  		return nil, fmt.Errorf("no namespace called: %v", path[i])
   152  	}
   153  
   154  	return e, nil
   155  }
   156  
   157  // Copy the Env for current scope
   158  func (e *Env) Copy() *Env {
   159  	e.rwMutex.RLock()
   160  	copy := Env{
   161  		rwMutex:        &sync.RWMutex{},
   162  		parent:         e.parent,
   163  		values:         make(map[string]reflect.Value, len(e.values)),
   164  		externalLookup: e.externalLookup,
   165  	}
   166  	for name, value := range e.values {
   167  		copy.values[name] = value
   168  	}
   169  	if e.types != nil {
   170  		copy.types = make(map[string]reflect.Type, len(e.types))
   171  		for name, t := range e.types {
   172  			copy.types[name] = t
   173  		}
   174  	}
   175  	e.rwMutex.RUnlock()
   176  	return &copy
   177  }
   178  
   179  // DeepCopy the Env for current scope and parent scopes.
   180  // Note that each scope is a consistent snapshot but not the whole.
   181  func (e *Env) DeepCopy() *Env {
   182  	e = e.Copy()
   183  	if e.parent != nil {
   184  		e.parent = e.parent.DeepCopy()
   185  	}
   186  	return e
   187  }