This repository has been archived by the owner on May 23, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathenv.go
155 lines (129 loc) · 3.53 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
package parens
import (
"context"
"sync"
)
var _ ConcurrentMap = (*mutexMap)(nil)
// Env represents the environment/context in which forms are evaluated
// for result. Env is not safe for concurrent use. Use fork() to get a
// child context for concurrent executions.
type Env struct {
ctx context.Context
analyzer Analyzer
expander Expander
globals ConcurrentMap
stack []stackFrame
maxDepth int
}
// ConcurrentMap is used by the Env to store variables in the global stack
// frame.
type ConcurrentMap interface {
// Store should store the key-value pair in the map.
Store(key string, val Any)
// Load should return the value associated with the key if it exists.
// Returns nil, false otherwise.
Load(key string) (Any, bool)
// Map should return a native Go map of all key-values in the concurrent
// map. This can be used for iteration etc.
Map() map[string]Any
}
// Eval performs macro-expansion if necessary, converts the expanded form
// to an expression and evaluates the resulting expression.
func (env *Env) Eval(form Any) (Any, error) {
expr, err := env.expandAnalyze(form)
if err != nil {
return nil, err
} else if expr == nil {
return nil, nil
}
return expr.Eval()
}
// Resolve a symbol.
func (env Env) Resolve(sym string) Any {
if len(env.stack) > 0 {
// check inside top of the stack for local bindings.
top := env.stack[len(env.stack)-1]
if v, found := top.Vars[sym]; found {
return v
}
}
// return the value from global bindings if found.
v, _ := env.globals.Load(sym)
return v
}
// Analyze performs syntax checks for special forms etc. and returns an Expr value that
// can be evaluated against the env.
func (env *Env) Analyze(form Any) (Expr, error) { return env.analyzer.Analyze(env, form) }
func (env *Env) expandAnalyze(form Any) (Expr, error) {
if expr, ok := form.(Expr); ok {
// Already an Expr, nothing to do.
return expr, nil
}
if expanded, err := env.expander.Expand(env, form); err != nil {
return nil, err
} else if expanded != nil {
// Expansion did happen. Throw away the old form and continue with
// the expanded version.
form = expanded
}
return env.analyzer.Analyze(env, form)
}
// Fork creates a child context from Env and returns it. The child context
// can be used as context for an independent thread of execution.
func (env *Env) Fork() *Env {
return &Env{
ctx: env.ctx,
globals: env.globals,
expander: env.expander,
analyzer: env.analyzer,
maxDepth: env.maxDepth,
}
}
func (env *Env) push(frame stackFrame) {
env.stack = append(env.stack, frame)
}
func (env *Env) pop() (frame *stackFrame) {
if len(env.stack) == 0 {
panic("pop from empty stack")
}
frame, env.stack = &env.stack[len(env.stack)-1], env.stack[:len(env.stack)-1]
return frame
}
func (env *Env) setGlobal(key string, value Any) {
env.globals.Store(key, value)
}
type stackFrame struct {
Name string
Args []Any
Vars map[string]Any
}
func newMutexMap() ConcurrentMap { return &mutexMap{} }
// mutexMap implements a simple ConcurrentMap using sync.RWMutex locks.
// Zero value is ready for use.
type mutexMap struct {
sync.RWMutex
vs map[string]Any
}
func (m *mutexMap) Load(name string) (v Any, ok bool) {
m.RLock()
defer m.RUnlock()
v, ok = m.vs[name]
return
}
func (m *mutexMap) Store(name string, v Any) {
m.Lock()
defer m.Unlock()
if m.vs == nil {
m.vs = map[string]Any{}
}
m.vs[name] = v
}
func (m *mutexMap) Map() map[string]Any {
m.RLock()
defer m.RUnlock()
native := make(map[string]Any, len(m.vs))
for k, v := range m.vs {
native[k] = v
}
return native
}