-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathslang.go
172 lines (140 loc) · 3.71 KB
/
slang.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
// Package slang (short for Sabre lang) provides a tiny LISP dialect built
// using factilities provided by Sabre. See New() for initializing Slang
// and using it.
package slang
import (
"fmt"
"io"
"strings"
"sync"
"github.com/spy16/sabre"
)
const (
nsSeparator = '/'
defaultNS = "user"
)
// New returns a new instance of Slang interpreter.
func New() *Slang {
sl := &Slang{
mu: &sync.RWMutex{},
bindings: map[nsSymbol]sabre.Value{},
}
if err := BindAll(sl); err != nil {
panic(err)
}
sl.checkNS = true
_ = sl.SwitchNS(sabre.Symbol{Value: defaultNS})
_ = sl.BindGo("ns", sl.SwitchNS)
return sl
}
// Slang represents an instance of slang interpreter.
type Slang struct {
mu *sync.RWMutex
currentNS string
checkNS bool
bindings map[nsSymbol]sabre.Value
}
// Eval evaluates the given value in Slang context.
func (slang *Slang) Eval(v sabre.Value) (sabre.Value, error) {
return sabre.Eval(slang, v)
}
// ReadEval reads from the given reader and evaluates all the forms
// obtained in Slang context.
func (slang *Slang) ReadEval(r io.Reader) (sabre.Value, error) {
return sabre.ReadEval(slang, r)
}
// ReadEvalStr reads the source and evaluates it in Slang context.
func (slang *Slang) ReadEvalStr(src string) (sabre.Value, error) {
return sabre.ReadEvalStr(slang, src)
}
// Bind binds the given name to the given Value into the slang interpreter
// context.
func (slang *Slang) Bind(symbol string, v sabre.Value) error {
slang.mu.Lock()
defer slang.mu.Unlock()
nsSym, err := slang.splitSymbol(symbol)
if err != nil {
return err
}
if slang.checkNS && nsSym.NS != slang.currentNS {
return fmt.Errorf("cannot bind outside current namespace")
}
slang.bindings[*nsSym] = v
return nil
}
// Resolve finds the value bound to the given symbol and returns it if
// found in the Slang context and returns it.
func (slang *Slang) Resolve(symbol string) (sabre.Value, error) {
slang.mu.RLock()
defer slang.mu.RUnlock()
if symbol == "ns" {
symbol = "user/ns"
}
nsSym, err := slang.splitSymbol(symbol)
if err != nil {
return nil, err
}
return slang.resolveAny(symbol, *nsSym, nsSym.WithNS("core"))
}
// BindGo is similar to Bind but handles conversion of Go value 'v' to
// sabre Value type.
func (slang *Slang) BindGo(symbol string, v interface{}) error {
return slang.Bind(symbol, sabre.ValueOf(v))
}
// SwitchNS changes the current namespace to the string value of given symbol.
func (slang *Slang) SwitchNS(sym sabre.Symbol) error {
slang.mu.Lock()
slang.currentNS = sym.String()
slang.mu.Unlock()
return slang.Bind("*ns*", sym)
}
// CurrentNS returns the current active namespace.
func (slang *Slang) CurrentNS() string {
slang.mu.RLock()
defer slang.mu.RUnlock()
return slang.currentNS
}
// Parent always returns nil to represent this is the root scope.
func (slang *Slang) Parent() sabre.Scope {
return nil
}
func (slang *Slang) resolveAny(symbol string, syms ...nsSymbol) (sabre.Value, error) {
for _, s := range syms {
v, found := slang.bindings[s]
if found {
return v, nil
}
}
return nil, fmt.Errorf("unable to resolve symbol: %v", symbol)
}
func (slang *Slang) splitSymbol(symbol string) (*nsSymbol, error) {
sep := string(nsSeparator)
if symbol == sep {
return &nsSymbol{
NS: slang.currentNS,
Name: symbol,
}, nil
}
parts := strings.SplitN(symbol, sep, 2)
if len(parts) < 2 {
return &nsSymbol{
NS: slang.currentNS,
Name: symbol,
}, nil
}
if strings.Contains(parts[1], sep) && parts[1] != sep {
return nil, fmt.Errorf("invalid qualified symbol: '%s'", symbol)
}
return &nsSymbol{
NS: parts[0],
Name: parts[1],
}, nil
}
type nsSymbol struct {
NS string
Name string
}
func (s nsSymbol) WithNS(ns string) nsSymbol {
s.NS = ns
return s
}