-
-
Notifications
You must be signed in to change notification settings - Fork 27
/
object.go
205 lines (165 loc) · 4.63 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
// Package object contains code to store values passed to/from BASIC.
//
// Go allows a rich number of types, but when interpreting BASIC programs
// we only support numbers & strings, as well as two-dimensional arrays
// containing those values.
//
// Note that numbers are stored as `float64`, to allow holding both
// integers and floating-point numbers.
package object
import (
"fmt"
)
// Type describes the type of an object.
type Type string
// These are our object-types.
const (
ERROR = "ERROR"
NUMBER = "NUMBER"
STRING = "STRING"
ARRAY = "ARRAY"
)
// Object is the interface that our types must implement.
type Object interface {
// Type returns the type of the object.
Type() Type
// String converts the object to a printable version for debugging.
String() string
}
// ArrayObject holds an array.
//
// We allow only two-dimensional arrays, and the size is set at the time
// the array is constructed.
type ArrayObject struct {
// We store objects in our array
Contents []Object
// X is the X-size of the array, fixed at creation-time
X int
// Y is the Y-size of the array, fixed at creation-time.
Y int
}
// Array creates a new array of the given dimensions
func Array(x int, y int) *ArrayObject {
// Our semantics ensure that we allow "0-N".
if x != 0 {
x++
}
if y != 0 {
y++
}
// setup the sizes
a := &ArrayObject{X: x, Y: y}
// for each entry ensure we store a value.
var c int
if x == 0 {
c = y
} else {
c = x * y
}
// we default to "0"
for c >= 0 {
a.Contents = append(a.Contents, Number(0))
c--
}
return a
}
// Get the value at the given X,Y coordinate
func (a *ArrayObject) Get(x int, y int) Object {
offset := int(x*a.X + y)
if a.X == 0 && offset >= a.Y {
return &ErrorObject{Value: "Get-Array access out of bounds (Y)"}
}
if (a.X != 0) && (offset > a.X*a.Y) {
return &ErrorObject{Value: "Get-Array access out of bounds (X,Y)"}
}
if offset < 0 {
return &ErrorObject{Value: "Get-Array access out of bounds (negative index)"}
}
if offset > len(a.Contents) {
return &ErrorObject{Value: "Get-Array access out of bounds (LEN)"}
}
return (a.Contents[offset])
}
// Set the value at the given X,Y coordinate
func (a *ArrayObject) Set(x int, y int, obj Object) Object {
offset := int(x*a.X + y)
if a.X == 0 && offset >= a.Y {
return &ErrorObject{Value: "Set-Array access out of bounds (Y)"}
}
if (a.X != 0) && (offset > a.X*a.Y) {
return &ErrorObject{Value: "Set-Array access out of bounds (X,Y)"}
}
if offset < 0 {
return &ErrorObject{Value: "Set-Array access out of bounds (negative index)"}
}
if offset > len(a.Contents) {
return &ErrorObject{Value: "Set-Array access out of bounds (LEN)"}
}
a.Contents[offset] = obj
return obj
}
// String returns the string-contents of the string
func (a *ArrayObject) String() string {
out := fmt.Sprintf("Array{X:%d, Y:%d, <%v>}",
a.X, a.Y, a.Contents)
return (out)
}
// Type returns the type of this object.
func (a *ArrayObject) Type() Type {
return ARRAY
}
// StringObject holds a string.
type StringObject struct {
// Value is the value our object wraps.
Value string
}
// String returns a string representation of this object.
func (s *StringObject) String() string {
return (fmt.Sprintf("Object{Type:string, Value:%s}", s.Value))
}
// Type returns the type of this object.
func (s *StringObject) Type() Type {
return STRING
}
// NumberObject holds a number.
type NumberObject struct {
// Value is the value our object wraps.
Value float64
}
// String returns a string representation of this object.
func (no *NumberObject) String() string {
return (fmt.Sprintf("Object{Type:number, Value:%f}", no.Value))
}
// Type returns the type of this object.
func (no *NumberObject) Type() Type {
return NUMBER
}
// ErrorObject holds a string, which describes an error
type ErrorObject struct {
// Value is the message our object wraps.
Value string
}
// String returns a string representation of this object.
func (eo *ErrorObject) String() string {
return (fmt.Sprintf("Object{Type:error, Value:%s}", eo.Value))
}
// Type returns the type of this object.
func (eo *ErrorObject) Type() Type {
return ERROR
}
//
// Some simple constructors
//
// Error is a helper for creating a new error-object with the given message.
func Error(format string, args ...interface{}) *ErrorObject {
msg := fmt.Sprintf(format, args...)
return &ErrorObject{Value: msg}
}
// Number is a helper for creating a new number-object with the given value.
func Number(val float64) *NumberObject {
return &NumberObject{Value: val}
}
// String is a helper for creating a new string-object with the given value.
func String(val string) *StringObject {
return &StringObject{Value: val}
}