-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathliterals.go
217 lines (184 loc) · 5.66 KB
/
literals.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
package golisp2
import "fmt"
type (
// IdentLiteral is a representation of an identifier in the interpreted
// environment, whose value is resolved by the context it is evaluated in.
IdentLiteral struct {
// note (bs): I'd like to eventually make it so that identifiers could be
// "compound lookups"; e.g. "Foo.Bar.A"; in which case I think this should
// not just be a string. Arguably, that should have it's own datatype
// anyway.
Val string
Pos ScannerPosition
}
// NumberLiteral is a representation of a number literal within the
// interpreted environment.
NumberLiteral struct {
Num float64
Pos ScannerPosition
}
// NilLiteral is a representation of an null literal within the interpreted
// environment.
NilLiteral struct {
Pos ScannerPosition
}
// StringLiteral is a representation of a string literal within the
// interpreted environment.
StringLiteral struct {
Str string
Pos ScannerPosition
}
// BoolLiteral is a representation of a boolean literal within the interpreted
// environment.
BoolLiteral struct {
Bool bool
Pos ScannerPosition
}
// FuncLiteral is a representation of a basic function declaration/assignment
// within the interpreted environment.
FuncLiteral struct {
// Name is the function identifier as it appears in the code.
Name string
// Fn is the function body the function value references.
Fn func(*EvalContext, ...Value) (Value, error)
Pos ScannerPosition
}
)
// NewIdentLiteral instantiates a new identifier literal with the given
// identifier token.
func NewIdentLiteral(ident string) *IdentLiteral {
return &IdentLiteral{
Val: ident,
}
}
// Eval will traverse the context for the identifier and return nil if the value
// is not defined.
//
// todo (bs): consider making failed resolution an error. In this case, it
// should be a "severe error" that bubbles back and most likely halts execution.
// It's *possible* the right way to handle that is by creating a modified value
// interface that can directly support the notion of error.
func (iv *IdentLiteral) Eval(ec *EvalContext) (Value, error) {
v, ok := ec.Resolve(iv.Val)
if !ok {
return &NilValue{}, nil
}
return v, nil
}
// CodeStr will return the code representation of the ident value.
func (iv *IdentLiteral) CodeStr() string {
return iv.Val
}
// SourcePos is the location in source this value came from.
func (iv *IdentLiteral) SourcePos() ScannerPosition {
return iv.Pos
}
// NewNumberLiteral instantiates a new number literal with the given value.
func NewNumberLiteral(v float64) *NumberLiteral {
return &NumberLiteral{
Num: v,
}
}
// Eval just returns itself.
func (nv *NumberLiteral) Eval(*EvalContext) (Value, error) {
return &NumberValue{
Val: nv.Num,
}, nil
}
// CodeStr will return the code representation of the number value.
func (nv *NumberLiteral) CodeStr() string {
// todo (bs): this isn't wrong, exactly, but consider printing integers as
// integers. Of course, that starts getting into the deeper issue of how just
// having floats is too primitive and there really need to be integers.
return fmt.Sprintf("%f", nv.Num)
}
// SourcePos is the location in source this value came from.
func (nv *NumberLiteral) SourcePos() ScannerPosition {
return nv.Pos
}
// NewNilLiteral creates a new nil value.
func NewNilLiteral() *NilLiteral {
return &NilLiteral{}
}
// Eval returns the nil value.
func (nv *NilLiteral) Eval(*EvalContext) (Value, error) {
// note (bs): not sure about this. In general, I feel like eval needs to be
// more intelligent
return &NilValue{}, nil
}
// CodeStr will return the code representation of the nil value.
func (nv *NilLiteral) CodeStr() string {
return fmt.Sprintf("nil")
}
// SourcePos is the location in source this value came from.
func (nv *NilLiteral) SourcePos() ScannerPosition {
return nv.Pos
}
// NewStringLiteral creates a new string literal from the given string.
func NewStringLiteral(str string) *StringLiteral {
return &StringLiteral{
Str: str,
}
}
// Eval returns the string value.
func (sv *StringLiteral) Eval(*EvalContext) (Value, error) {
return &StringValue{
Val: sv.Str,
}, nil
}
// CodeStr will return the code representation of the string value.
func (sv *StringLiteral) CodeStr() string {
// note (bs): this doesn't matter now as it's not supported, but just note
// that this doesn't work with multiline strings
return fmt.Sprintf("\"%s\"", sv.Str)
}
// SourcePos is the location in source this value came from.
func (sv *StringLiteral) SourcePos() ScannerPosition {
return sv.Pos
}
// NewBoolLiteral creates a bool literal with the given value.
func NewBoolLiteral(v bool) *BoolLiteral {
return &BoolLiteral{
Bool: v,
}
}
// Eval returns the bool value.
func (bv *BoolLiteral) Eval(*EvalContext) (Value, error) {
return &BoolValue{
Val: bv.Bool,
}, nil
}
// CodeStr will return the code representation of the boolean value.
func (bv *BoolLiteral) CodeStr() string {
if bv.Bool {
return "true"
}
return "false"
}
// SourcePos is the location in source this value came from.
func (bv *BoolLiteral) SourcePos() ScannerPosition {
return bv.Pos
}
// NewFuncLiteral creates a function literal with the given value.
func NewFuncLiteral(
name string,
fn func(*EvalContext, ...Value) (Value, error),
) *FuncLiteral {
return &FuncLiteral{
Fn: fn,
}
}
// Eval evaluates the function using the provided context.
func (fv *FuncLiteral) Eval(ec *EvalContext) (Value, error) {
return &FuncValue{
Fn: fv.Fn,
}, nil
}
// CodeStr will return the code representation of the function value.
func (fv *FuncLiteral) CodeStr() string {
return fv.Name
}
// SourcePos is the location in source this value came from.
func (fv *FuncLiteral) SourcePos() ScannerPosition {
return fv.Pos
}