rubylisp 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/rubylisp +14 -0
- data/lib/rubylisp/alist.rb +230 -0
- data/lib/rubylisp/assignment.rb +65 -0
- data/lib/rubylisp/atom.rb +149 -0
- data/lib/rubylisp/binding.rb +17 -0
- data/lib/rubylisp/boolean.rb +49 -0
- data/lib/rubylisp/builtins.rb +31 -0
- data/lib/rubylisp/character.rb +383 -0
- data/lib/rubylisp/cons_cell.rb +255 -0
- data/lib/rubylisp/environment_frame.rb +116 -0
- data/lib/rubylisp/equivalence.rb +118 -0
- data/lib/rubylisp/exception.rb +98 -0
- data/lib/rubylisp/ext.rb +122 -0
- data/lib/rubylisp/ffi_class.rb +162 -0
- data/lib/rubylisp/ffi_new.rb +32 -0
- data/lib/rubylisp/ffi_send.rb +83 -0
- data/lib/rubylisp/ffi_static.rb +22 -0
- data/lib/rubylisp/frame.rb +284 -0
- data/lib/rubylisp/function.rb +92 -0
- data/lib/rubylisp/io.rb +74 -0
- data/lib/rubylisp/list_support.rb +527 -0
- data/lib/rubylisp/logical.rb +38 -0
- data/lib/rubylisp/macro.rb +95 -0
- data/lib/rubylisp/math.rb +403 -0
- data/lib/rubylisp/number.rb +63 -0
- data/lib/rubylisp/object.rb +62 -0
- data/lib/rubylisp/parser.rb +184 -0
- data/lib/rubylisp/primitive.rb +45 -0
- data/lib/rubylisp/relational.rb +46 -0
- data/lib/rubylisp/special_forms.rb +454 -0
- data/lib/rubylisp/string.rb +841 -0
- data/lib/rubylisp/symbol.rb +56 -0
- data/lib/rubylisp/system.rb +19 -0
- data/lib/rubylisp/testing.rb +136 -0
- data/lib/rubylisp/tokenizer.rb +292 -0
- data/lib/rubylisp/type_checks.rb +58 -0
- data/lib/rubylisp/vector.rb +114 -0
- data/lib/rubylisp.rb +1 -0
- metadata +82 -0
@@ -0,0 +1,527 @@
|
|
1
|
+
module Lisp
|
2
|
+
|
3
|
+
class ListSupport
|
4
|
+
|
5
|
+
def self.register
|
6
|
+
%w(car cdr caar cadr cdar cddr
|
7
|
+
caaar caadr cadar caddr cdaar cdadr cddar cdddr
|
8
|
+
caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr
|
9
|
+
cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr).each do |name|
|
10
|
+
Primitive.register(name) {|args, env| ad_impl(args, env, name) }
|
11
|
+
end
|
12
|
+
Primitive.register("list") {|args, env| Lisp::ListSupport::list_impl(args, env) }
|
13
|
+
Primitive.register("vector") {|args, env| Lisp::ListSupport::vector_impl(args, env) }
|
14
|
+
Primitive.register("cons*") {|args, env| Lisp::ListSupport::cons_star_impl(args, env) }
|
15
|
+
Primitive.register("cons") {|args, env| Lisp::ListSupport::cons_impl(args, env) }
|
16
|
+
Primitive.register("make-list") {|args, env| Lisp::ListSupport::make_list_impl(args, env) }
|
17
|
+
Primitive.register("iota") {|args, env| Lisp::ListSupport::iota_impl(args, env) }
|
18
|
+
|
19
|
+
Primitive.register("length") {|args, env| Lisp::ListSupport::length_impl(args, env) }
|
20
|
+
Primitive.register("first") {|args, env| Lisp::ListSupport::first_impl(args, env) }
|
21
|
+
Primitive.register("head") {|args, env| Lisp::ListSupport::first_impl(args, env) }
|
22
|
+
Primitive.register("rest") {|args, env| Lisp::ListSupport::rest_impl(args, env) }
|
23
|
+
Primitive.register("tail") {|args, env| Lisp::ListSupport::rest_impl(args, env) }
|
24
|
+
Primitive.register("second") {|args, env| Lisp::ListSupport::second_impl(args, env) }
|
25
|
+
Primitive.register("third") {|args, env| Lisp::ListSupport::third_impl(args, env) }
|
26
|
+
Primitive.register("fourth") {|args, env| Lisp::ListSupport::fourth_impl(args, env) }
|
27
|
+
Primitive.register("fifth") {|args, env| Lisp::ListSupport::fifth_impl(args, env) }
|
28
|
+
Primitive.register("sixth") {|args, env| Lisp::ListSupport::sixth_impl(args, env) }
|
29
|
+
Primitive.register("seventh") {|args, env| Lisp::ListSupport::seventh_impl(args, env) }
|
30
|
+
Primitive.register("eighth") {|args, env| Lisp::ListSupport::eighth_impl(args, env) }
|
31
|
+
Primitive.register("ninth") {|args, env| Lisp::ListSupport::ninth_impl(args, env) }
|
32
|
+
Primitive.register("tenth") {|args, env| Lisp::ListSupport::tenth_impl(args, env) }
|
33
|
+
Primitive.register("nth") {|args, env| Lisp::ListSupport::nth_impl(args, env) }
|
34
|
+
|
35
|
+
Primitive.register("sublist") {|args, env| Lisp::ListSupport::sublist_impl(args, env) }
|
36
|
+
Primitive.register("list-head") {|args, env| Lisp::ListSupport::list_head_impl(args, env) }
|
37
|
+
Primitive.register("take") {|args, env| Lisp::ListSupport::take_impl(args, env) }
|
38
|
+
Primitive.register("list-tail") {|args, env| Lisp::ListSupport::list_tail_impl(args, env) }
|
39
|
+
Primitive.register("drop") {|args, env| Lisp::ListSupport::drop_impl(args, env) }
|
40
|
+
Primitive.register("last-pair") {|args, env| Lisp::ListSupport::last_pair_impl(args, env) }
|
41
|
+
|
42
|
+
Primitive.register("memq") {|args, env| Lisp::ListSupport::memq_impl(args, env) }
|
43
|
+
Primitive.register("memv") {|args, env| Lisp::ListSupport::memv_impl(args, env) }
|
44
|
+
Primitive.register("member") {|args, env| Lisp::ListSupport::member_impl(args, env) }
|
45
|
+
|
46
|
+
Primitive.register("filter") {|args, env| Lisp::ListSupport::filter_impl(args, env) }
|
47
|
+
Primitive.register("remove") {|args, env| Lisp::ListSupport::remove_impl(args, env) }
|
48
|
+
Primitive.register("partition") {|args, env| Lisp::ListSupport::partition_impl(args, env) }
|
49
|
+
Primitive.register("map") {|args, env| Lisp::ListSupport::map_impl(args, env) }
|
50
|
+
Primitive.register("reduce-left") {|args, env| Lisp::ListSupport::reduce_left_impl(args, env) }
|
51
|
+
Primitive.register("any") {|args, env| Lisp::ListSupport::any_impl(args, env) }
|
52
|
+
Primitive.register("every") {|args, env| Lisp::ListSupport::every_impl(args, env) }
|
53
|
+
Primitive.register("reverse") {|args, env| Lisp::ListSupport::reverse_impl(args, env) }
|
54
|
+
Primitive.register("append") {|args, env| Lisp::ListSupport::append_impl(args, env) }
|
55
|
+
Primitive.register("append!") {|args, env| Lisp::ListSupport::appendbang_impl(args, env) }
|
56
|
+
Primitive.register("flatten") {|args, env| Lisp::ListSupport::flatten_impl(args, env) }
|
57
|
+
# Primitive.register("flatten*") {|args, env| Lisp::ListSupport::recursive_flatten_impl(args, env) }
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def self.cons_impl(args, env)
|
62
|
+
raise "cons requires two arguments." unless args.length == 2
|
63
|
+
left = args.car.evaluate(env)
|
64
|
+
right = args.cadr.evaluate(env)
|
65
|
+
Lisp::ConsCell.cons(left, right)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def self.cons_star_impl(args, env)
|
70
|
+
vals = []
|
71
|
+
args.each {|item| vals << item.evaluate(env) }
|
72
|
+
Lisp::ConsCell::array_to_list(vals[0..-2], vals[-1])
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def self.list_impl(args, env)
|
77
|
+
vals = []
|
78
|
+
args.each {|item| vals << item.evaluate(env) }
|
79
|
+
if vals.size == 1 && vals[0].vector?
|
80
|
+
Lisp::ConsCell::array_to_list(vals[0].value)
|
81
|
+
else
|
82
|
+
Lisp::ConsCell::array_to_list(vals)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def self.make_list_impl(args, env)
|
88
|
+
raise "consmake-list requires one or two arguments." unless args.length == 1 || args.length == 2
|
89
|
+
arg1 = args.car.evaluate(env)
|
90
|
+
raise "make-list requires an integer for it's first argument, received: #{args.car}" unless arg1.integer?
|
91
|
+
count = arg1.value
|
92
|
+
val = if args.length == 1
|
93
|
+
nil
|
94
|
+
else
|
95
|
+
args.cadr.evaluate(env)
|
96
|
+
end
|
97
|
+
|
98
|
+
vals = Array.new(count, val)
|
99
|
+
Lisp::ConsCell::array_to_list(vals)
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def self.iota_impl(args, env)
|
104
|
+
raise "iota requires at least one argument." unless args.length > 0
|
105
|
+
arg1 = args.car.evaluate(env)
|
106
|
+
raise "iota requires an positive integer for it's first argument, received: #{args.car}" unless arg1.integer? && arg1.positive?
|
107
|
+
count = arg1.value
|
108
|
+
|
109
|
+
start = if args.length < 2
|
110
|
+
0
|
111
|
+
else
|
112
|
+
arg2 = args.cadr.evaluate(env)
|
113
|
+
raise "iota requires an number for it's second argument, received: #{args.cadr}" unless arg2.number?
|
114
|
+
arg2.value
|
115
|
+
end
|
116
|
+
|
117
|
+
step = if args.length < 3
|
118
|
+
1
|
119
|
+
else
|
120
|
+
arg3 = args.caddr.evaluate(env)
|
121
|
+
raise "iota requires an number for it's third argument, received: #{args.caddr}" unless arg3.number?
|
122
|
+
arg3.value
|
123
|
+
end
|
124
|
+
|
125
|
+
vals = []
|
126
|
+
count.times do |c|
|
127
|
+
vals << start
|
128
|
+
start += step
|
129
|
+
end
|
130
|
+
|
131
|
+
Lisp::ConsCell::array_to_list(vals.map {|v| Number.with_value(v) })
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def self.length_impl(args, env)
|
136
|
+
Lisp::Number.with_value(args.car.evaluate(env).length)
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
# in support of all the CxR functions
|
141
|
+
def self.ad_impl(args, env, f)
|
142
|
+
l = args.car.evaluate(env)
|
143
|
+
raise "list required." unless l.list?
|
144
|
+
l.send(f)
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def self.first_impl(args, env)
|
149
|
+
l = args.car.evaluate(env)
|
150
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
151
|
+
raise "list index out of bounds" unless l.length >= 1
|
152
|
+
l.nth(1)
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
def self.rest_impl(args, env)
|
157
|
+
l = args.car.evaluate(env)
|
158
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
159
|
+
if l.list?
|
160
|
+
l.cdr
|
161
|
+
else
|
162
|
+
Lisp::Vector.new(l.value[1..-1])
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
def self.second_impl(args, env)
|
168
|
+
l = args.car.evaluate(env)
|
169
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
170
|
+
raise "list index out of bounds" unless l.length >= 2
|
171
|
+
l.nth(2)
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.third_impl(args, env)
|
175
|
+
l = args.car.evaluate(env)
|
176
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
177
|
+
raise "list index out of bounds" unless l.length >= 3
|
178
|
+
l.nth(3)
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
def self.fourth_impl(args, env)
|
183
|
+
l = args.car.evaluate(env)
|
184
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
185
|
+
raise "list index out of bounds" unless l.length >= 4
|
186
|
+
l.nth(4)
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
def self.fifth_impl(args, env)
|
191
|
+
l = args.car.evaluate(env)
|
192
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
193
|
+
raise "list index out of bounds" unless l.length >= 5
|
194
|
+
l.nth(5)
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
def self.sixth_impl(args, env)
|
199
|
+
l = args.car.evaluate(env)
|
200
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
201
|
+
raise "list index out of bounds" unless l.length >= 6
|
202
|
+
l.nth(6)
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
def self.seventh_impl(args, env)
|
207
|
+
l = args.car.evaluate(env)
|
208
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
209
|
+
raise "list index out of bounds" unless l.length >= 7
|
210
|
+
l.nth(7)
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
def self.eighth_impl(args, env)
|
215
|
+
l = args.car.evaluate(env)
|
216
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
217
|
+
raise "list index out of bounds" unless l.length >= 8
|
218
|
+
l.nth(8)
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
def self.ninth_impl(args, env)
|
223
|
+
l = args.car.evaluate(env)
|
224
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
225
|
+
raise "list index out of bounds" unless l.length >= 9
|
226
|
+
l.nth(9)
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
def self.tenth_impl(args, env)
|
231
|
+
l = args.car.evaluate(env)
|
232
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
233
|
+
raise "list index out of bounds" unless l.length >= 10
|
234
|
+
l.nth(10)
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
def self.nth_impl(args, env)
|
239
|
+
raise "nth requires 2 arguments" unless args.length == 2
|
240
|
+
n = args.car.evaluate(env)
|
241
|
+
raise "The first argument of nth has to be an number." unless n.number?
|
242
|
+
raise "The first argument of nth has to be positive." unless n.value > 0
|
243
|
+
l = args.cadr.evaluate(env)
|
244
|
+
raise "rest requires a list or vector." unless l.list? || l.vector?
|
245
|
+
l.nth(n.value)
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
def self.make_same_kind_as(sequence, value)
|
250
|
+
if sequence.vector?
|
251
|
+
Lisp::Vector.new(value)
|
252
|
+
else
|
253
|
+
Lisp::ConsCell.array_to_list(value)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
def self.sublist_impl(args, env)
|
259
|
+
raise "sublist requires 3 arguments" unless args.length == 3
|
260
|
+
l = args.car.evaluate(env)
|
261
|
+
raise "sublist requires it's first argument to be a list or vector, but received #{args.car}" unless l.list? || l.vector?
|
262
|
+
st = args.cadr.evaluate(env)
|
263
|
+
raise "sublist requires it's second argument to be a positive integer, but received #{args.cadr}" unless st.number? && st.positive?
|
264
|
+
raise "sublist requires it's second argument to be <= the list length" unless st.value <= l.length
|
265
|
+
en = args.caddr.evaluate(env)
|
266
|
+
raise "sublist requires it's third argument to be a positive integer, but received #{args.caddr}" unless en.number? && en.positive?
|
267
|
+
raise "sublist requires it's third argument to be <= the list length" unless en.value <= l.length
|
268
|
+
raise "sublist requires it's second argument to be <= the third argument" unless st.value <= en.value
|
269
|
+
make_same_kind_as(l, l.to_a[(st.value - 1)...en.value])
|
270
|
+
end
|
271
|
+
|
272
|
+
|
273
|
+
def self.list_head_impl(args, env)
|
274
|
+
raise "list_head requires 2 arguments" unless args.length == 2
|
275
|
+
l = args.car.evaluate(env)
|
276
|
+
raise "list_head requires it's first argument to be a list, but received #{args.car}" unless l.list?
|
277
|
+
k = args.cadr.evaluate(env)
|
278
|
+
raise "list_head requires it's second argument to be a positive integer, but received #{args.cadr}" unless k.number? && k.positive?
|
279
|
+
raise "list_head requires it's second argument to be <= the list length" unless k.value <= l.length
|
280
|
+
Lisp::ConsCell.array_to_list(l.to_a[0...k.value])
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
def self.take_impl(args, env)
|
285
|
+
raise "take requires 2 arguments" unless args.length == 2
|
286
|
+
k = args.car.evaluate(env)
|
287
|
+
raise "take requires it's first argument to be an integer >= 0, but received #{args.car}" unless k.number? && !k.negative?
|
288
|
+
l = args.cadr.evaluate(env)
|
289
|
+
raise "take requires it's second argument to be a list or vector, but received #{args.cadr}" unless l.list? || l.vector?
|
290
|
+
raise "take requires it's first argument to be <= the list length" unless k.value <= l.length
|
291
|
+
puts l
|
292
|
+
make_same_kind_as(l, l.to_a[0...k.value])
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
def self.list_tail_impl(args, env)
|
297
|
+
raise "list_head requires 2 arguments" unless args.length == 2
|
298
|
+
l = args.car.evaluate(env)
|
299
|
+
raise "list_head requires it's first argument to be a list, but received #{args.car}" unless l.list?
|
300
|
+
k = args.cadr.evaluate(env)
|
301
|
+
raise "list_head requires it's second argument to be a positive integer, but received #{args.cadr}" unless k.number? && k.positive?
|
302
|
+
raise "list_head requires it's second argument to be <= the list length" unless k.value <= l.length
|
303
|
+
l.nth_tail(k.value + 1)
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
def self.drop_impl(args, env)
|
308
|
+
raise "drop requires 2 arguments" unless args.length == 2
|
309
|
+
k = args.car.evaluate(env)
|
310
|
+
raise "drop requires it's first argument to be an integer >= 0, but received #{args.car}" unless k.number? && !k.negative?
|
311
|
+
l = args.cadr.evaluate(env)
|
312
|
+
raise "drop requires it's second argument to be a list or vector, but received #{args.cadr}" unless l.list? || l.vector?
|
313
|
+
raise "drop requires it's first argument to be <= the list length" unless k.value <= l.length
|
314
|
+
l.nth_tail(k.value + 1)
|
315
|
+
end
|
316
|
+
|
317
|
+
|
318
|
+
def self.last_pair_impl(args, env)
|
319
|
+
raise "last_pair requires 1 arguments" unless args.length == 1
|
320
|
+
l = args.car.evaluate(env)
|
321
|
+
raise "last_pair requires it's argument to be a list, but received #{args.car}" unless l.list?
|
322
|
+
l.last
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
def self.memq_impl(args, env)
|
327
|
+
raise "memq requires 2 arguments but received #{args.length}." unless args.length == 2
|
328
|
+
item = args.car.evaluate(env)
|
329
|
+
collection = args.cadr.evaluate(env)
|
330
|
+
raise "memq requires a list as it's second argument." unless collection.list?
|
331
|
+
collection.length.times do |i|
|
332
|
+
if Lisp::Equivalence.eq_check(item, collection.nth(i + 1)).value
|
333
|
+
return collection.nth_tail(i + 1)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
Lisp::FALSE
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
def self.memv_impl(args, env)
|
341
|
+
raise "memv requires 2 arguments but received #{args.length}." unless args.length == 2
|
342
|
+
item = args.car.evaluate(env)
|
343
|
+
collection = args.cadr.evaluate(env)
|
344
|
+
raise "memv requires a list as it's second argument." unless collection.list?
|
345
|
+
collection.length.times do |i|
|
346
|
+
if Lisp::Equivalence.eqv_check(item, collection.nth(i + 1)).value
|
347
|
+
return collection.nth_tail(i + 1)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
Lisp::FALSE
|
351
|
+
end
|
352
|
+
|
353
|
+
|
354
|
+
def self.member_impl(args, env)
|
355
|
+
raise "member requires 2 arguments but received #{args.length}." unless args.length == 2
|
356
|
+
item = args.car.evaluate(env)
|
357
|
+
collection = args.cadr.evaluate(env)
|
358
|
+
raise "member requires a list as it's second argument." unless collection.list?
|
359
|
+
collection.length.times do |i|
|
360
|
+
if Lisp::Equivalence.equal_check(item, collection.nth(i + 1)).value
|
361
|
+
return collection.nth_tail(i + 1)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
Lisp::FALSE
|
365
|
+
end
|
366
|
+
|
367
|
+
|
368
|
+
def self.filter_impl(args, env)
|
369
|
+
raise "filter requires 2 arguments but received #{args.length}." unless args.length == 2
|
370
|
+
f = args.car.evaluate(env)
|
371
|
+
raise "filter requires a function as it's first argument but received #{args.car}." unless f.function? || f.primitive?
|
372
|
+
collection = args.cadr.evaluate(env)
|
373
|
+
raise "filter requires a list or vector as it's second argument but received #{args.cadr}." unless collection.list? || collection.vector?
|
374
|
+
results = collection.to_a.select {|item| f.apply_to_without_evaluating(Lisp::ConsCell.cons(item, nil), env).value }
|
375
|
+
make_same_kind_as(collection, results)
|
376
|
+
end
|
377
|
+
|
378
|
+
|
379
|
+
def self.remove_impl(args, env)
|
380
|
+
raise "remove requires 2 arguments but received #{args.length}." unless args.length == 2
|
381
|
+
f = args.car.evaluate(env)
|
382
|
+
raise "remove requires a function as it's first argument but received #{args.car}." unless f.function? || f.primitive?
|
383
|
+
collection = args.cadr.evaluate(env)
|
384
|
+
raise "remove requires a list or vector as it's second argument but received #{args.cadr}." unless collection.list? || collection.vector?
|
385
|
+
results = collection.to_a.reject {|item| f.apply_to_without_evaluating(Lisp::ConsCell.cons(item, nil), env).value }
|
386
|
+
make_same_kind_as(collection, results)
|
387
|
+
end
|
388
|
+
|
389
|
+
|
390
|
+
def self.partition_impl(args, env)
|
391
|
+
raise "partition requires 2 arguments but received #{args.length}." unless args.length == 2
|
392
|
+
f = args.car.evaluate(env)
|
393
|
+
raise "partition requires a function as it's first argument." unless f.function? || f.primitive?
|
394
|
+
collection = args.cadr.evaluate(env)
|
395
|
+
raise "partition requires a list as it's second argument." unless collection.list? | collection.vector?
|
396
|
+
results = collection.to_a.partition {|item| f.apply_to_without_evaluating(Lisp::ConsCell.cons(item, nil), env).value }
|
397
|
+
matches = make_same_kind_as(collection, results[0])
|
398
|
+
non_matches = make_same_kind_as(collection, results[1])
|
399
|
+
Lisp::ConsCell.array_to_list([matches, non_matches])
|
400
|
+
end
|
401
|
+
|
402
|
+
|
403
|
+
def self.map_impl(args, env)
|
404
|
+
raise "map requires at least 2 arguments but received #{args.length}." if args.length < 2
|
405
|
+
f = args.car.evaluate(env)
|
406
|
+
raise "map requires a function as it's first argument but received #{args.car}." unless f.function? || f.primitive?
|
407
|
+
collections = args.cdr.to_a.collect {|a| a.evaluate(env)}
|
408
|
+
raise "all requires all subsequent arguments to be lists or vectors" unless collections.all? {|l| l.list? || l.vector?}
|
409
|
+
all_vectors = collections.all? {|i| i.vector?}
|
410
|
+
lists = collections.collect {|l| l.to_a }
|
411
|
+
|
412
|
+
map_args = []
|
413
|
+
while (lists.all? {|l| !l.empty? })
|
414
|
+
map_args << Lisp::ConsCell.array_to_list(lists.map {|l| l.shift })
|
415
|
+
end
|
416
|
+
results = map_args.collect {|item| f.apply_to_without_evaluating(item, env) }
|
417
|
+
if all_vectors
|
418
|
+
Lisp::Vector.new(results)
|
419
|
+
else
|
420
|
+
Lisp::ConsCell.array_to_list(results)
|
421
|
+
end
|
422
|
+
|
423
|
+
end
|
424
|
+
|
425
|
+
|
426
|
+
def self.quote_if_required(thing)
|
427
|
+
return thing unless thing.list? || thing.symbol?
|
428
|
+
thing.quoted
|
429
|
+
end
|
430
|
+
|
431
|
+
|
432
|
+
def self.reduce_left_impl(args, env)
|
433
|
+
raise "reduce requires 3 arguments but received #{args.length}." unless args.length == 3
|
434
|
+
f = args.car.evaluate(env)
|
435
|
+
raise "map requires a function as it's first argument but received #{args.car}." unless f.function? || f.primitive?
|
436
|
+
initial = args.cadr.evaluate(env)
|
437
|
+
collection = args.caddr.evaluate(env)
|
438
|
+
raise "reduce requires a list or vector as it's third argument but received #{args.caddr}." unless collection.list? || collection.vector?
|
439
|
+
return initial if collection.empty?
|
440
|
+
return collection.nth(1) if collection.length == 1
|
441
|
+
result = collection.to_a.inject do |acc, item|
|
442
|
+
f.apply_to(Lisp::ConsCell.array_to_list([quote_if_required(acc), quote_if_required(item)]), env)
|
443
|
+
end
|
444
|
+
result
|
445
|
+
end
|
446
|
+
|
447
|
+
|
448
|
+
def self.any_impl(args, env)
|
449
|
+
raise "any requires at least two arguments" unless args.length >= 2
|
450
|
+
p = args.car.evaluate(env)
|
451
|
+
raise "any requires a function as it's first argument" unless p.function? || p.primitive?
|
452
|
+
lists = args.cdr.to_a.collect {|a| a.evaluate(env)}
|
453
|
+
raise "any requires all subsequent arguments to be lists or vectors" unless lists.all? {|l| l.list? || l.vector?}
|
454
|
+
|
455
|
+
while true
|
456
|
+
cars = lists.collect {|l| l.nth(1)}
|
457
|
+
return_val = p.apply_to(Lisp::ConsCell.array_to_list(cars), env)
|
458
|
+
return Lisp::TRUE if return_val.value
|
459
|
+
lists = lists.collect {|l| l.nth_tail(2)}
|
460
|
+
return Lisp::FALSE if lists.any? {|l| l.empty?}
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
|
465
|
+
def self.every_impl(args, env)
|
466
|
+
raise "all requires at least two arguments" unless args.length >= 2
|
467
|
+
p = args.car.evaluate(env)
|
468
|
+
raise "all requires a function as it's first argument" unless p.function? || p.primitive?
|
469
|
+
lists = args.cdr.to_a.collect {|a| a.evaluate(env)}
|
470
|
+
raise "all requires all subsequent arguments to be lists or vectors" unless lists.all? {|l| l.list? || l.vector?}
|
471
|
+
|
472
|
+
while true
|
473
|
+
cars = lists.collect {|l| l.nth(1)}
|
474
|
+
return_val = p.apply_to(Lisp::ConsCell.array_to_list(cars), env)
|
475
|
+
return Lisp::FALSE unless return_val.value
|
476
|
+
lists = lists.collect {|l| l.nth_tail(2)}
|
477
|
+
return Lisp::TRUE if lists.any? {|l| l.empty?}
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
|
482
|
+
def self.reverse_impl(args, env)
|
483
|
+
raise "reverse requires a single argument." unless args.length == 1
|
484
|
+
l = args.car.evaluate(env)
|
485
|
+
raise "reverse requires a list or vector" unless l.list? || l.vector?
|
486
|
+
make_same_kind_as(l, l.to_a.reverse)
|
487
|
+
end
|
488
|
+
|
489
|
+
|
490
|
+
def self.append_impl(args, env)
|
491
|
+
raise "append requires at least 1 argument." unless args.length >= 1
|
492
|
+
l = args.map {|i| i.evaluate(env)}
|
493
|
+
raise "append requires lists or vectors" unless l.all? {|i| i.list? || i.vector?}
|
494
|
+
all_vectors = l.all? {|i| i.vector?}
|
495
|
+
new_items = []
|
496
|
+
l.each do |sublist|
|
497
|
+
sublist.each {|item| new_items << item.copy}
|
498
|
+
end
|
499
|
+
|
500
|
+
if all_vectors
|
501
|
+
Lisp::Vector.new(new_items)
|
502
|
+
else
|
503
|
+
Lisp::ConsCell.array_to_list(new_items)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
|
508
|
+
def self.appendbang_impl(args, env)
|
509
|
+
raise "append! requires at least 1 argument." unless args.length >= 1
|
510
|
+
arg_array = args.to_a.map {|i| i.evaluate(env)}
|
511
|
+
raise "append! requires lists" unless arg_array.all? {|i| i.list?}
|
512
|
+
(0...(arg_array.length-1)). each do |i|
|
513
|
+
arg_array[i].last.set_cdr!(arg_array[i+1])
|
514
|
+
end
|
515
|
+
arg_array[0]
|
516
|
+
end
|
517
|
+
|
518
|
+
|
519
|
+
def self.flatten_impl(args, env)
|
520
|
+
raise "flatten requires 1 argument." unless args.length != 1
|
521
|
+
l = args.car.evaluate(env)
|
522
|
+
raise "flatten requires a list argument" unless l.list?
|
523
|
+
l.flatten
|
524
|
+
end
|
525
|
+
|
526
|
+
end
|
527
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Lisp
|
2
|
+
|
3
|
+
class Logical
|
4
|
+
|
5
|
+
def self.register
|
6
|
+
Primitive.register("or",
|
7
|
+
"(or expression ...\n\nThe expressions are evaluated from left to right, and the value of the first expression that evaluates to a true value is returned. Any remaining expressions are not evaluated. If all expressions evaluate to false values, the value of the last expression is returned. If there are no expressions then #f is returned.",
|
8
|
+
true) do |args, env|
|
9
|
+
Lisp::Logical::or_impl(args, env)
|
10
|
+
end
|
11
|
+
Primitive.register("and",
|
12
|
+
"(and expression ...)\n\nThe expressions are evaluated from left to right, and the value of the first expression that evaluates to a false value is returned. Any remaining expressions are not evaluated. If all the expressions evaluate to true values, the value of the last expression is returned. If there are no expressions then #t is returned.",
|
13
|
+
true) do |args, env|
|
14
|
+
Lisp::Logical::and_impl(args, env)
|
15
|
+
end
|
16
|
+
Primitive.register("not") {|args, env| Lisp::Logical::not_impl(args, env) }
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def self.or_impl(args, env)
|
21
|
+
raise "or needs at least 2 arguments" unless args.length > 1
|
22
|
+
value = !!args.inject(false) {|acc, item| acc || item.evaluate(env).value}
|
23
|
+
return Lisp::Boolean.with_value(value)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.and_impl(args, env)
|
27
|
+
raise "and needs at least 2 arguments" unless args.length > 1
|
28
|
+
value = !!args.inject(true) {|acc, item| acc && item.evaluate(env).value}
|
29
|
+
return Lisp::Boolean.with_value(value)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.not_impl(args, env)
|
33
|
+
raise "not needs a single argument" unless args.length == 1
|
34
|
+
return Lisp::Boolean.with_value(!(args.car.evaluate(env).value))
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Lisp
|
2
|
+
|
3
|
+
class Macro < Atom
|
4
|
+
|
5
|
+
attr_reader :doc
|
6
|
+
|
7
|
+
def compute_required_argument_count(args)
|
8
|
+
a = args
|
9
|
+
@required_argument_count = 0
|
10
|
+
@var_args = false
|
11
|
+
while a
|
12
|
+
if a.symbol?
|
13
|
+
@var_args = true
|
14
|
+
return
|
15
|
+
else
|
16
|
+
@required_argument_count += 1
|
17
|
+
end
|
18
|
+
a = a.cdr
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(name, arguments, doc, body, env)
|
23
|
+
sig = ([name] << arguments.to_a).flatten
|
24
|
+
@doc = "(#{(sig.collect {|e| e.to_s}).join(" ")})"
|
25
|
+
@name = name
|
26
|
+
@arguments = arguments
|
27
|
+
@doc = [@doc, doc].join("\n\n") unless doc.nil? || doc.to_s.empty?
|
28
|
+
@body = body
|
29
|
+
@env = env
|
30
|
+
@local_env = nil
|
31
|
+
compute_required_argument_count(@arguments)
|
32
|
+
end
|
33
|
+
|
34
|
+
def expand(parameters, env, should_eval)
|
35
|
+
if @var_args
|
36
|
+
raise "#{@name} expected at least #{@required_argument_count} parameters, received #{parameters.length}." if parameters.length < @required_argument_count
|
37
|
+
else
|
38
|
+
raise "#{@name} expected #{@required_argument_count} parameters, received #{parameters.length}." unless parameters.length == @required_argument_count
|
39
|
+
end
|
40
|
+
|
41
|
+
local_env = EnvironmentFrame.extending(@env, env.frame)
|
42
|
+
self_sym = Symbol.named("self")
|
43
|
+
if env.frame
|
44
|
+
local_env.bind_locally(self_sym, env.frame)
|
45
|
+
elsif env.local_binding_for(self_sym)
|
46
|
+
local_env.bind_locally(self_sym, env.value_of(self_sym))
|
47
|
+
end
|
48
|
+
arg = @arguments
|
49
|
+
param = parameters
|
50
|
+
accumulating_arg = nil
|
51
|
+
accumulated_params = []
|
52
|
+
while !param.nil?
|
53
|
+
param_value = should_eval ? param.car.evaluate(env) : param.car
|
54
|
+
if accumulating_arg
|
55
|
+
accumulated_params << param_value
|
56
|
+
else
|
57
|
+
local_env.bind_locally(arg.car, param_value) unless arg.car.nil?
|
58
|
+
end
|
59
|
+
param = param.cdr
|
60
|
+
arg = arg.cdr unless accumulating_arg
|
61
|
+
accumulating_arg = arg if arg.symbol?
|
62
|
+
end
|
63
|
+
local_env.bind_locally(accumulating_arg, Lisp::ConsCell.array_to_list(accumulated_params)) if accumulating_arg
|
64
|
+
|
65
|
+
@body.evaluate(local_env)
|
66
|
+
end
|
67
|
+
|
68
|
+
def internal_apply_to(parameters, env, should_eval)
|
69
|
+
expanded_macro = expand(parameters, env, should_eval)
|
70
|
+
expanded_macro.evaluate(env)
|
71
|
+
end
|
72
|
+
|
73
|
+
def apply_to(parameters, env)
|
74
|
+
internal_apply_to(parameters, env, false)
|
75
|
+
end
|
76
|
+
|
77
|
+
def apply_to_without_evaluating(parameters, env)
|
78
|
+
internal_apply_to(parameters, env, false)
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_s
|
82
|
+
"<macro: #{@name}>"
|
83
|
+
end
|
84
|
+
|
85
|
+
def macro?
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
89
|
+
def type
|
90
|
+
:macro
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|