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