/
sabre.go
147 lines (123 loc) · 3.04 KB
/
sabre.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
// Package sabre provides data structures, reader for reading LISP source
// into data structures and functions for evluating forms against a context.
package internal
import (
"fmt"
"io"
"reflect"
"strings"
)
// Eval evaluates the given form against the scope and returns the result
// of evaluation.
func Eval(scope Scope, form Value) (Value, error) {
if form == nil {
return Nil{}, nil
}
v, err := form.Eval(scope)
if err != nil {
return v, err
}
return v, nil
}
// ReadEval consumes data from reader 'r' till EOF, parses into forms
// and evaluates all the forms obtained and returns the result.
func ReadEval(scope Scope, r io.Reader) (Value, error) {
mod, err := NewReader(r).All()
if err != nil {
return nil, err
}
err = hoistValues(scope, mod)
if err != nil {
return nil, err
}
return Eval(scope, mod)
}
func hoistValues(scope Scope, value Value) error {
hoistedVals := []string{"def", "defn", "defmacro", "defclass"}
for _, form := range value.(Module) {
list, ok := form.(*List);
if !ok {
continue
}
if list.Size() < 2 {
continue
}
def, isSymbol := list.Values[0].(Symbol)
if !isSymbol {
return fmt.Errorf("first argument must be symbol, not '%v'",
reflect.TypeOf(list.Values[0]))
}
if !includes(def.String(), hoistedVals) {
continue
}
sym, isSymbol := list.Values[1].(Symbol)
if !isSymbol {
return fmt.Errorf("first argument must be symbol, not '%v'",
reflect.TypeOf(list.Values[1]))
}
symbol := sym.String()
// only hoist for def that defines defn and defmacro
if def.String() == "def" && (symbol == "defn" || symbol == "defmacro") {
_, err := form.Eval(scope)
if err != nil {
return err
}
// do not hoist def
} else if def.String() == "def" {
continue
}
_, err := form.Eval(scope)
if err != nil {
return err
}
}
return nil
}
func includes(search string, content []string) bool {
for _, v := range content {
if v == search {
return true
}
}
return false
}
// ReadEvalStr is a convenience wrapper for Eval that reads forms from
// string and evaluates for result.
func ReadEvalStr(scope Scope, src string) (Value, error) {
return ReadEval(scope, strings.NewReader(src))
}
// Scope implementation is responsible for managing value bindings.
type Scope interface {
Push(Call)
Pop() Call
StackTrace() string
Parent() Scope
Bind(symbol string, v Value) error
Resolve(symbol string) (Value, error)
}
func newEvalErr(v Value, err error) EvalError {
if ee, ok := err.(EvalError); ok {
return ee
} else if ee, ok := err.(*EvalError); ok && ee != nil {
return *ee
}
return EvalError{
Position: getPosition(v),
Cause: err,
Form: v,
}
}
// EvalError represents error during evaluation.
type EvalError struct {
Position
Cause error
StackTrace string
Form Value
}
// Unwrap returns the underlying cause of this error.
func (ee EvalError) Unwrap() error { return ee.Cause }
func (ee EvalError) Error() string {
return fmt.Sprintf("%s\nin '%s' (at line %d:%d) %v",
ee.Cause, ee.File, ee.Line, ee.Column, ee.StackTrace,
)
}