-
Notifications
You must be signed in to change notification settings - Fork 0
/
object.go
254 lines (227 loc) · 6.77 KB
/
object.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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
package vesper
import (
"fmt"
"strconv"
)
// Object represents all objects in Vesper
type Object struct {
Type *Object // i.e. <string>
code *Code // non-nil for closure, code
frame *frame // non-nil for closure, continuation
primitive *primitive // non-nil for primitives
continuation *continuation // non-nil for continuation
car *Object // non-nil for instances and lists
cdr *Object // non-nil for lists, nil for everything else
bindings map[*Object]*Object // non-nil for struct
elements []*Object // non-nil for array
fval float64 // number
text string // string, symbol, keyword, type
Value interface{} // the rest of the data for more complex things
}
type stringable interface {
String() string
}
var (
// TypeType is the metatype, the type of all types
TypeType *Object // bootstrapped in initSymbolTable => Intern("<type>")
// KeywordType is the type of all keywords
KeywordType *Object // bootstrapped in initSymbolTable => Intern("<keyword>")
// SymbolType is the type of all symbols
SymbolType *Object // bootstrapped in initSymbolTable = Intern("<symbol>")
// StringType is the type of all strings
StringType *Object // bootstrapped in initSymbolTable = Intern("<string>")
// CharacterType is the type of all characters
CharacterType = defaultVM.Intern("<character>")
// NumberType is the type of all numbers
NumberType = defaultVM.Intern("<number>")
// BlobType is the type of all bytearrays
BlobType = defaultVM.Intern("<blob>")
// ListType is the type of all lists
ListType = defaultVM.Intern("<list>")
// ArrayType is the type of all arrays
ArrayType = defaultVM.Intern("<array>")
// StructType is the type of all structs
StructType = defaultVM.Intern("<struct>")
// FunctionType is the type of all functions
FunctionType = defaultVM.Intern("<function>")
// CodeType is the type of compiled code
CodeType = defaultVM.Intern("<code>")
// ErrorType is the type of all errors
ErrorType = defaultVM.Intern("<error>")
// AnyType is a pseudo type specifier indicating any type
AnyType = defaultVM.Intern("<any>")
)
// RuneValue - return native rune value of the object
func RuneValue(obj *Object) rune {
return rune(obj.fval)
}
// IntValue - return native int value of the object
func IntValue(obj *Object) int {
return int(obj.fval)
}
// Int64Value - return native int64 value of the object
func Int64Value(obj *Object) int64 {
return int64(obj.fval)
}
// Float64Value - return native float64 value of the object
func Float64Value(obj *Object) float64 {
return obj.fval
}
// StringValue - return native string value of the object
func StringValue(obj *Object) string {
return obj.text
}
// BlobValue - return native []byte value of the object
func BlobValue(obj *Object) []byte {
b, _ := obj.Value.([]byte)
return b
}
// NewObject is the constructor for externally defined objects, where the
// value is an interface{}.
func NewObject(variant *Object, value interface{}) *Object {
return &Object{Type: variant, Value: value}
}
// Identical - return if two objects are identical
func Identical(o1 *Object, o2 *Object) bool {
return o1 == o2
}
// String returns the string representation of the object
func (lob *Object) String() string {
switch lob.Type {
case NullType:
return "null"
case BooleanType:
if lob == True {
return "true"
}
return "false"
case CharacterType:
return string([]rune{rune(lob.fval)})
case NumberType:
return strconv.FormatFloat(lob.fval, 'f', -1, 64)
case BlobType:
return fmt.Sprintf("#[blob %d bytes]", len(BlobValue(lob)))
case StringType, SymbolType, KeywordType, TypeType:
return lob.text
case ListType:
return listToString(lob)
case ArrayType:
return arrayToString(lob)
case StructType:
return structToString(lob)
case FunctionType:
return functionToString(lob)
case CodeType:
return lob.code.String(lob.code.vm)
case ErrorType:
return "#<error>" + Write(lob.car)
case ChannelType:
return lob.Value.(*channel).String()
default:
if lob.Value != nil {
if s, ok := lob.Value.(stringable); ok {
return s.String()
}
return "#[" + typeNameString(lob.Type.text) + "]"
}
return "#" + lob.Type.text + Write(lob.car)
}
}
// IsCharacter returns true if the object is a character
func IsCharacter(obj *Object) bool {
return obj.Type == CharacterType
}
// IsNumber returns true if the object is a number
func IsNumber(obj *Object) bool {
return obj.Type == NumberType
}
// IsList returns true if the object is a list
func IsList(obj *Object) bool {
return obj.Type == ListType
}
// IsStruct returns true if the object is a struct
func IsStruct(obj *Object) bool {
return obj.Type == StructType
}
// IsSymbol returns true if the object is a symbol
func IsSymbol(obj *Object) bool {
return obj.Type == SymbolType
}
// IsKeyword returns true if the object is a keyword
func IsKeyword(obj *Object) bool {
return obj.Type == KeywordType
}
// IsType returns true if the object is a type
func IsType(obj *Object) bool {
return obj.Type == TypeType
}
// IsInstance returns true if the object is an instance.
// Since instances have arbitrary Type symbols, all we can check is that the car value is set
func IsInstance(obj *Object) bool {
return obj.car != nil && obj.cdr == nil
}
// Equal checks if two objects are equal
func Equal(o1 *Object, o2 *Object) bool {
if o1 == o2 {
return true
}
if o1.Type != o2.Type {
return false
}
switch o1.Type {
case BooleanType, CharacterType:
return int(o1.fval) == int(o2.fval)
case NumberType:
return NumberEqual(o1.fval, o2.fval)
case StringType:
return o1.text == o2.text
case ListType:
return ListEqual(o1, o2)
case ArrayType:
return ArrayEqual(o1, o2)
case StructType:
return StructEqual(o1, o2)
case SymbolType, KeywordType, TypeType:
return o1 == o2
case NullType:
return true // singleton
default:
o1a := Value(o1)
if o1a != o1 {
o2a := Value(o2)
return Equal(o1a, o2a)
}
return false
}
}
// IsPrimitiveType returns true if the object is a primitive
func IsPrimitiveType(tag *Object) bool {
switch tag {
case NullType, BooleanType, CharacterType, NumberType,
StringType, ListType, ArrayType, StructType,
SymbolType, KeywordType, TypeType, FunctionType:
return true
default:
return false
}
}
// Instance returns a new instance of the type and value
func Instance(tag *Object, val *Object) (*Object, error) {
if !IsType(tag) {
return nil, Error(ArgumentErrorKey, TypeType.text, tag)
}
if IsPrimitiveType(tag) {
return val, nil
}
return &Object{
Type: tag,
car: val,
}, nil
}
// Value returns the result of dereferencing the object
func Value(obj *Object) *Object {
if obj.cdr == nil && obj.car != nil {
return obj.car
}
return obj
}