-
Notifications
You must be signed in to change notification settings - Fork 1
/
envinit.go
130 lines (111 loc) · 2.59 KB
/
envinit.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
package runtime
import (
"context"
"fmt"
"io"
"os"
"strings"
value "github.com/glojurelang/glojure/pkg/lang"
"github.com/glojurelang/glojure/pkg/reader"
"github.com/glojurelang/glojure/pkg/stdlib"
)
type Program struct {
nodes []interface{}
}
type evalOptions struct {
stdout io.Writer
stderr io.Writer
loadPath []string
env *environment
}
type EvalOption func(*evalOptions)
func WithStdout(w io.Writer) EvalOption {
return func(opts *evalOptions) {
opts.stdout = w
}
}
func WithStderr(w io.Writer) EvalOption {
return func(opts *evalOptions) {
opts.stderr = w
}
}
func WithLoadPath(path []string) EvalOption {
return func(opts *evalOptions) {
opts.loadPath = path
}
}
func withEnv(env value.Environment) EvalOption {
e := env.(*environment)
return func(opts *evalOptions) {
opts.env = e
}
}
func NewEnvironment(opts ...EvalOption) value.Environment {
options := &evalOptions{
stdout: os.Stdout,
stderr: os.Stderr,
}
for _, opt := range opts {
opt(options)
}
env := options.env
if env == nil {
env = newEnvironment(context.Background(), options.stdout, options.stderr)
env.loadPath = options.loadPath
}
// TODO: this is rather rather hacky
value.GlobalEnv = env
// bootstrap namespace control
{
// bootstrap implementation of the ns macro
env.DefVar(value.NewSymbol("in-ns"), value.IFnFunc(func(args ...interface{}) interface{} {
if len(args) != 1 {
panic(fmt.Errorf("in-ns: expected namespace name"))
}
sym, ok := args[0].(*value.Symbol)
if !ok {
panic(fmt.Errorf("in-ns: expected symbol as namespace name"))
}
ns := value.FindOrCreateNamespace(sym)
env.SetCurrentNamespace(ns)
return ns
}))
}
{
// Add stdlib
evalFile := func(path string) {
core, err := stdlib.StdLib.ReadFile(path)
if err != nil {
panic(fmt.Sprintf("could not read stdlib core.glj: %v", err))
}
r := reader.New(strings.NewReader(string(core)), reader.WithFilename(path), reader.WithGetCurrentNS(func() *value.Namespace {
return env.CurrentNamespace()
}))
for {
expr, err := r.ReadOne()
if err == reader.ErrEOF {
break
}
if err != nil {
panic(fmt.Sprintf("error reading core lib %v: %v", path, err))
}
_, err = env.Eval(expr)
if err != nil {
panic(fmt.Sprintf("error evaluating core lib %v: %v", path, err))
}
}
}
evalFile("glojure/core.glj")
}
return env
}
func (p *Program) Eval(opts ...EvalOption) (interface{}, error) {
env := NewEnvironment(opts...)
for _, node := range p.nodes {
_, err := env.Eval(node)
if err != nil {
return nil, err
}
}
return nil, nil
}