rubylisp 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +82 -0
- data/bin/rubylisp +6 -0
- data/lib/rubylisp/alist.rb +17 -17
- data/lib/rubylisp/assignment.rb +7 -7
- data/lib/rubylisp/builtins.rb +1 -0
- data/lib/rubylisp/character.rb +21 -20
- data/lib/rubylisp/cons_cell.rb +46 -3
- data/lib/rubylisp/debug.rb +238 -0
- data/lib/rubylisp/environment_frame.rb +47 -4
- data/lib/rubylisp/exception.rb +8 -8
- data/lib/rubylisp/ext.rb +4 -0
- data/lib/rubylisp/ffi_class.rb +10 -10
- data/lib/rubylisp/ffi_send.rb +3 -3
- data/lib/rubylisp/frame.rb +51 -31
- data/lib/rubylisp/function.rb +4 -3
- data/lib/rubylisp/io.rb +6 -6
- data/lib/rubylisp/list_support.rb +93 -94
- data/lib/rubylisp/logical.rb +3 -3
- data/lib/rubylisp/macro.rb +4 -2
- data/lib/rubylisp/math.rb +58 -56
- data/lib/rubylisp/object.rb +1 -1
- data/lib/rubylisp/parser.rb +5 -5
- data/lib/rubylisp/primitive.rb +1 -1
- data/lib/rubylisp/relational.rb +4 -4
- data/lib/rubylisp/special_forms.rb +29 -27
- data/lib/rubylisp/string.rb +75 -75
- data/lib/rubylisp/system.rb +3 -2
- data/lib/rubylisp/testing.rb +3 -3
- data/lib/rubylisp/type_checks.rb +10 -8
- data/lib/rubylisp/vector.rb +1 -1
- data/lib/rubylisp.rb +1 -0
- metadata +6 -4
data/lib/rubylisp/math.rb
CHANGED
@@ -120,13 +120,13 @@ largest integer less than or equal to `number` is returned.") do |args, env|
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def self.add_impl(args, env)
|
123
|
-
|
123
|
+
return Lisp::Debug.process_error("add needs at least 1 argument", env) if args.empty?
|
124
124
|
|
125
125
|
acc = 0
|
126
126
|
c = args
|
127
127
|
while !c.nil?
|
128
128
|
n = c.car.evaluate(env)
|
129
|
-
|
129
|
+
return Lisp::Debug.process_error("add needs number arguments but was given a #{n.type}: #{n}", env) unless n.type == :number
|
130
130
|
acc += n.value
|
131
131
|
c = c.cdr
|
132
132
|
end
|
@@ -136,17 +136,17 @@ largest integer less than or equal to `number` is returned.") do |args, env|
|
|
136
136
|
|
137
137
|
|
138
138
|
def self.subtract_impl(args, env)
|
139
|
-
|
139
|
+
return Lisp::Debug.process_error("subtract needs at least 1 argument", env) if args.empty?
|
140
140
|
|
141
141
|
return Number.with_value(-1 * args.car.evaluate(env).value) if args.length == 1
|
142
142
|
|
143
143
|
first = args.car.evaluate(env)
|
144
|
-
|
144
|
+
return Lisp::Debug.process_error("subtract needs number arguments, but received #{first}", env) unless first.type == :number
|
145
145
|
acc = first.value
|
146
146
|
c = args.cdr
|
147
147
|
while !c.nil?
|
148
148
|
n = c.car.evaluate(env)
|
149
|
-
|
149
|
+
return Lisp::Debug.process_error("subtract needs number arguments, but received #{n}", env) unless n.type == :number
|
150
150
|
acc -= n.value
|
151
151
|
c = c.cdr
|
152
152
|
end
|
@@ -156,13 +156,13 @@ largest integer less than or equal to `number` is returned.") do |args, env|
|
|
156
156
|
|
157
157
|
|
158
158
|
def self.multiply_impl(args, env)
|
159
|
-
|
159
|
+
return Lisp::Debug.process_error("multiply needs at least 1 argument", env) if args.empty?
|
160
160
|
|
161
161
|
acc = 1
|
162
162
|
c = args
|
163
163
|
while !c.nil?
|
164
164
|
n = c.car.evaluate(env)
|
165
|
-
|
165
|
+
return Lisp::Debug.process_error("multiply needs number arguments, but received #{n}", env) unless n.type == :number
|
166
166
|
acc *= n.value
|
167
167
|
c = c.cdr
|
168
168
|
end
|
@@ -172,16 +172,17 @@ largest integer less than or equal to `number` is returned.") do |args, env|
|
|
172
172
|
|
173
173
|
|
174
174
|
def self.quotient_impl(args, env)
|
175
|
-
|
175
|
+
return Lisp::Debug.process_error("quotient needs at least 1 argument", env) if args.empty?
|
176
176
|
|
177
177
|
first = args.car.evaluate(env)
|
178
|
-
|
178
|
+
return Lisp::Debug.process_error("quotient needs number arguments, but received #{first}", env) unless first.type == :number
|
179
179
|
return first if args.length == 1
|
180
180
|
acc = first.value
|
181
181
|
c = args.cdr
|
182
182
|
while !c.nil?
|
183
183
|
n = c.car.evaluate(env)
|
184
|
-
|
184
|
+
return Lisp::Debug.process_error("quotient needs number arguments, but received #{n}", env) unless n.type == :number
|
185
|
+
return Lisp::Debug.process_error("divide by zero", env) if n.value == 0
|
185
186
|
acc /= n.value
|
186
187
|
c = c.cdr
|
187
188
|
end
|
@@ -191,16 +192,17 @@ largest integer less than or equal to `number` is returned.") do |args, env|
|
|
191
192
|
|
192
193
|
|
193
194
|
def self.remainder_impl(args, env)
|
194
|
-
|
195
|
+
return Lisp::Debug.process_error("remainder needs at least 1 argument", env) if args.empty?
|
195
196
|
|
196
197
|
first = args.car.evaluate(env)
|
197
|
-
|
198
|
+
return Lisp::Debug.process_error("remainder needs number arguments, but received #{first}", env) unless first.type == :number
|
198
199
|
return first if args.length == 1
|
199
200
|
acc = first.value
|
200
201
|
c = args.cdr
|
201
202
|
while !c.nil?
|
202
203
|
n = c.car.evaluate(env)
|
203
|
-
|
204
|
+
return Lisp::Debug.process_error("remainder needs number arguments, but received #{n}", env) unless n.type == :number
|
205
|
+
return Lisp::Debug.process_error("divide by zero", env) if n.value == 0
|
204
206
|
acc %= n.value
|
205
207
|
c = c.cdr
|
206
208
|
end
|
@@ -209,17 +211,17 @@ largest integer less than or equal to `number` is returned.") do |args, env|
|
|
209
211
|
end
|
210
212
|
|
211
213
|
def self.truncate_impl(args, env)
|
212
|
-
|
214
|
+
return Lisp::Debug.process_error("truncate needs 1 argument, but received #{args.length}", env) if args.length != 1
|
213
215
|
arg = args.car.evaluate(env)
|
214
|
-
|
216
|
+
return Lisp::Debug.process_error("truncate needs a number argument, but received #{arg}", env) unless arg.type == :number
|
215
217
|
Number.with_value(arg.value.truncate)
|
216
218
|
end
|
217
219
|
|
218
220
|
|
219
221
|
def self.round_impl(args, env)
|
220
|
-
|
222
|
+
return Lisp::Debug.process_error("round needs 1 argument, but received #{args.length}", env) if args.length != 1
|
221
223
|
arg = args.car.evaluate(env)
|
222
|
-
|
224
|
+
return Lisp::Debug.process_error("round needs a number argument, but received #{arg}", env) unless arg.type == :number
|
223
225
|
num = arg.value
|
224
226
|
int = num.to_i
|
225
227
|
Number.with_value(if (num - int).abs == 0.5
|
@@ -235,121 +237,121 @@ largest integer less than or equal to `number` is returned.") do |args, env|
|
|
235
237
|
|
236
238
|
|
237
239
|
def self.ceiling_impl(args, env)
|
238
|
-
|
240
|
+
return Lisp::Debug.process_error("ceiling needs 1 argument, but received #{args.length}", env) if args.length != 1
|
239
241
|
arg = args.car.evaluate(env)
|
240
|
-
|
242
|
+
return Lisp::Debug.process_error("ceiling needs a number argument, but received #{arg}", env) unless arg.type == :number
|
241
243
|
Number.with_value(arg.value.ceil)
|
242
244
|
end
|
243
245
|
|
244
246
|
|
245
247
|
def self.floor_impl(args, env)
|
246
|
-
|
248
|
+
return Lisp::Debug.process_error("floor needs 1 argument, but received #{args.length}", env) if args.length != 1
|
247
249
|
arg = args.car.evaluate(env)
|
248
|
-
|
250
|
+
return Lisp::Debug.process_error("floor needs a number argument, but received #{arg}", env) unless arg.type == :number
|
249
251
|
Number.with_value(arg.value.floor)
|
250
252
|
end
|
251
253
|
|
252
254
|
|
253
255
|
def self.even_impl(args, env)
|
254
|
-
|
256
|
+
return Lisp::Debug.process_error("even? needs 1 argument, but received #{args.length}", env) if args.length != 1
|
255
257
|
arg = args.car.evaluate(env)
|
256
|
-
|
258
|
+
return Lisp::Debug.process_error("even? needs a number argument, but received #{arg}", env) unless arg.type == :number
|
257
259
|
Boolean.with_value(arg.value.even?)
|
258
260
|
end
|
259
261
|
|
260
262
|
|
261
263
|
def self.odd_impl(args, env)
|
262
|
-
|
264
|
+
return Lisp::Debug.process_error("odd? needs 1 argument, but received #{args.length}", env) if args.length != 1
|
263
265
|
arg = args.car.evaluate(env)
|
264
|
-
|
266
|
+
return Lisp::Debug.process_error("odd? needs a number argument, but received #{arg}", env) unless arg.type == :number
|
265
267
|
Boolean.with_value(arg.value.odd?)
|
266
268
|
end
|
267
269
|
|
268
270
|
|
269
271
|
def self.zero_impl(args, env)
|
270
|
-
|
272
|
+
return Lisp::Debug.process_error("zero? needs 1 argument, but received #{args.length}", env) if args.length != 1
|
271
273
|
arg = args.car.evaluate(env)
|
272
|
-
|
274
|
+
return Lisp::Debug.process_error("zero? needs a number argument, but received #{arg}", env) unless arg.type == :number
|
273
275
|
Boolean.with_value(arg.value.zero?)
|
274
276
|
end
|
275
277
|
|
276
278
|
|
277
279
|
def self.positive_impl(args, env)
|
278
|
-
|
280
|
+
return Lisp::Debug.process_error("positive? needs 1 argument, but received #{args.length}", env) if args.length != 1
|
279
281
|
arg = args.car.evaluate(env)
|
280
|
-
|
282
|
+
return Lisp::Debug.process_error("positive? needs a number argument, but received #{arg}", env) unless arg.type == :number
|
281
283
|
Boolean.with_value(arg.value > 0)
|
282
284
|
end
|
283
285
|
|
284
286
|
|
285
287
|
def self.negative_impl(args, env)
|
286
|
-
|
288
|
+
return Lisp::Debug.process_error("negative? needs 1 argument, but received #{args.length}", env) if args.length != 1
|
287
289
|
arg = args.car.evaluate(env)
|
288
|
-
|
290
|
+
return Lisp::Debug.process_error("negative? needs a number argument, but received #{arg}", env) unless arg.type == :number
|
289
291
|
Boolean.with_value(arg.value < 0)
|
290
292
|
end
|
291
293
|
|
292
294
|
|
293
295
|
def self.interval_impl(args, env)
|
294
|
-
|
296
|
+
return Lisp::Debug.process_error("interval needs 2 arguments, but received #{args.length}", env) if args.length != 2
|
295
297
|
initial = args.car.evaluate(env)
|
296
|
-
|
298
|
+
return Lisp::Debug.process_error("interval needs number arguments, but received #{initial}", env) unless initial.type == :number
|
297
299
|
final = args.cadr.evaluate(env)
|
298
|
-
|
299
|
-
|
300
|
+
return Lisp::Debug.process_error("interval needs number arguments, but received #{final}", env) unless final.type == :number
|
301
|
+
return Lisp::Debug.process_error("interval's arguments need to be in natural order", env) unless initial.value <= final.value
|
300
302
|
Lisp::ConsCell.array_to_list((initial.value..final.value).to_a.map {|n| Number.with_value(n)})
|
301
303
|
end
|
302
304
|
|
303
305
|
|
304
306
|
def self.random_impl(args, env)
|
305
307
|
arg = args.car.evaluate(env)
|
306
|
-
|
308
|
+
return Lisp::Debug.process_error("random needs a number argument, but received #{arg}", env) unless arg.nil? || arg.type == :number
|
307
309
|
Number.with_value(arg.nil? ? rand() : rand(arg.value))
|
308
310
|
end
|
309
311
|
|
310
312
|
|
311
313
|
def self.float_impl(args, env)
|
312
|
-
|
314
|
+
return Lisp::Debug.process_error("float needs 1 argument, but received #{args.length}", env) if args.length != 1
|
313
315
|
arg = args.car.evaluate(env)
|
314
|
-
|
316
|
+
return Lisp::Debug.process_error("float needs a numeric or string argument, but received #{arg}", env) unless arg.number? || arg.string?
|
315
317
|
Number.with_value(arg.value.to_f)
|
316
318
|
end
|
317
319
|
|
318
320
|
|
319
321
|
def self.integer_impl(args, env)
|
320
|
-
|
322
|
+
return Lisp::Debug.process_error("integer needs 1 argument, but received #{args.length}", env) if args.length != 1
|
321
323
|
arg = args.car.evaluate(env)
|
322
|
-
|
324
|
+
return Lisp::Debug.process_error("integer needs a numeric or string argument, but received #{arg}", env) unless arg.number? || arg.string?
|
323
325
|
Number.with_value(arg.value.to_i)
|
324
326
|
end
|
325
327
|
|
326
328
|
|
327
329
|
def self.abs_impl(args, env)
|
328
|
-
|
330
|
+
return Lisp::Debug.process_error("abs needs 1 argument, but received #{args.length}", env) if args.length != 1
|
329
331
|
arg = args.car.evaluate(env)
|
330
|
-
|
332
|
+
return Lisp::Debug.process_error("abs needs a numeric argument, but received #{arg}", env) unless arg.number?
|
331
333
|
Number.with_value(arg.value.abs)
|
332
334
|
end
|
333
335
|
|
334
336
|
|
335
337
|
def self.sqrt_impl(args, env)
|
336
|
-
|
338
|
+
return Lisp::Debug.process_error("sqrt needs 1 argument, but received #{args.length}", env) if args.length != 1
|
337
339
|
arg = args.car.evaluate(env)
|
338
|
-
|
340
|
+
return Lisp::Debug.process_error("sqrt needs a numeric argument, but received #{arg}", env) unless arg.number?
|
339
341
|
Number.with_value(::Math.sqrt(arg.value).round(5))
|
340
342
|
end
|
341
343
|
|
342
344
|
|
343
345
|
def self.min_impl(args, env)
|
344
|
-
|
346
|
+
return Lisp::Debug.process_error("min needs at least 1 argument", env) if args.length == 0
|
345
347
|
|
346
348
|
initial = args.car.evaluate(env)
|
347
|
-
|
349
|
+
return Lisp::Debug.process_error("min requires numeric arguments, but received #{initial}", env) unless initial.type ==:number
|
348
350
|
acc = initial.value
|
349
351
|
c = args.cdr
|
350
352
|
while !c.nil?
|
351
353
|
n = c.car.evaluate(env)
|
352
|
-
|
354
|
+
return Lisp::Debug.process_error("min needs number arguments, but received #{n}", env) unless n.type == :number
|
353
355
|
acc = n.value if n.value < acc
|
354
356
|
c = c.cdr
|
355
357
|
end
|
@@ -359,14 +361,14 @@ largest integer less than or equal to `number` is returned.") do |args, env|
|
|
359
361
|
|
360
362
|
|
361
363
|
def self.max_impl(args, env)
|
362
|
-
|
364
|
+
return Lisp::Debug.process_error("max needs at least 1 argumenta", env) if args.length == 0
|
363
365
|
initial = args.car.evaluate(env)
|
364
|
-
|
366
|
+
return Lisp::Debug.process_error("max requires numeric arguments, but received #{initial}", env) unless initial.type ==:number
|
365
367
|
acc = initial.value
|
366
368
|
c = args.cdr
|
367
369
|
while !c.nil?
|
368
370
|
n = c.car.evaluate(env)
|
369
|
-
|
371
|
+
return Lisp::Debug.process_error("max needs number arguments, but received #{n}", env) unless n.type == :number
|
370
372
|
acc = n.value if n.value > acc
|
371
373
|
c = c.cdr
|
372
374
|
end
|
@@ -376,25 +378,25 @@ largest integer less than or equal to `number` is returned.") do |args, env|
|
|
376
378
|
|
377
379
|
|
378
380
|
def self.sin_impl(args, env)
|
379
|
-
|
381
|
+
return Lisp::Debug.process_error("sin needs 1 argument, but received #{args.length}", env) if args.length != 1
|
380
382
|
arg = args.car.evaluate(env)
|
381
|
-
|
383
|
+
return Lisp::Debug.process_error("sin needs a numeric argument, but received #{arg}", env) unless arg.number?
|
382
384
|
Number.with_value(::Math.sin(arg.value).round(5))
|
383
385
|
end
|
384
386
|
|
385
387
|
|
386
388
|
def self.cos_impl(args, env)
|
387
|
-
|
389
|
+
return Lisp::Debug.process_error("cos needs 1 argument, but received #{args.length}", env) if args.length != 1
|
388
390
|
arg = args.car.evaluate(env)
|
389
|
-
|
391
|
+
return Lisp::Debug.process_error("cos needs a numeric argument, but received #{arg}", env) unless arg.number?
|
390
392
|
Number.with_value(::Math.cos(arg.value).round(5))
|
391
393
|
end
|
392
394
|
|
393
395
|
|
394
396
|
def self.tan_impl(args, env)
|
395
|
-
|
397
|
+
return Lisp::Debug.process_error("tan needs 1 argument, but received #{args.length}", env) if args.length != 1
|
396
398
|
arg = args.car.evaluate(env)
|
397
|
-
|
399
|
+
return Lisp::Debug.process_error("tan needs a numeric argument, but received #{arg}", env) unless arg.number?
|
398
400
|
Number.with_value(::Math.tan(arg.value).round(5))
|
399
401
|
end
|
400
402
|
|
data/lib/rubylisp/object.rb
CHANGED
@@ -7,7 +7,7 @@ module Lisp
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.wrap_impl(args, env)
|
10
|
-
|
10
|
+
return Lisp::Debug.process_error("wrap-object requires 1 argument", env) unless args.length == 1
|
11
11
|
raw_val = args.car.evaluate(env)
|
12
12
|
val = if raw_val.list?
|
13
13
|
raw_val.to_a
|
data/lib/rubylisp/parser.rb
CHANGED
@@ -45,12 +45,12 @@ module Lisp
|
|
45
45
|
cdr = self.parse_sexpr(tokens)
|
46
46
|
return nil if tokens.next_token[0] == :EOF
|
47
47
|
tok, lit = tokens.next_token
|
48
|
-
|
48
|
+
return Lisp::Debug.process_error("Expected ')' to follow a dotted tail on line #{tokens.line_number}", env) if tok != :RPAREN
|
49
49
|
tokens.consume_token
|
50
50
|
return Lisp::ConsCell.array_to_list(cells, cdr)
|
51
51
|
else
|
52
52
|
car = self.parse_sexpr(tokens)
|
53
|
-
|
53
|
+
return Lisp::Debug.process_error("Unexpected EOF (expected ')') on line #{tokens.line_number}", env) if tokens.next_token[0] == :EOF
|
54
54
|
cells << car
|
55
55
|
end
|
56
56
|
tok, lit = tokens.next_token
|
@@ -71,7 +71,7 @@ module Lisp
|
|
71
71
|
cells = []
|
72
72
|
while tok != :RBRACE
|
73
73
|
item = self.parse_sexpr(tokens)
|
74
|
-
|
74
|
+
return Lisp::Debug.process_error("Unexpected EOF (expected '}') on line #{tokens.line_number}", env) if tokens.next_token[0] == :EOF
|
75
75
|
cells << item
|
76
76
|
tok, lit = tokens.next_token
|
77
77
|
end
|
@@ -92,7 +92,7 @@ module Lisp
|
|
92
92
|
cells = []
|
93
93
|
while tok != :RBRACKET
|
94
94
|
item = self.parse_sexpr(tokens)
|
95
|
-
|
95
|
+
return Lisp::Debug.process_error("Unexpected EOF (expected ']') on line #{tokens.line_number}", env) if tokens.next_token[0] == :EOF
|
96
96
|
cells << item
|
97
97
|
tok, lit = tokens.next_token
|
98
98
|
end
|
@@ -152,7 +152,7 @@ module Lisp
|
|
152
152
|
expr = parse_sexpr(tokens)
|
153
153
|
return ConsCell.array_to_list([Symbol.named('unquote-splicing'), expr])
|
154
154
|
when :ILLEGAL
|
155
|
-
|
155
|
+
return Lisp::Debug.process_error("Illegal token: #{lit} on line #{tokens.line_number}", Lisp::EnvironmentFrame.global)
|
156
156
|
else
|
157
157
|
return make_symbol(lit)
|
158
158
|
end
|
data/lib/rubylisp/primitive.rb
CHANGED
data/lib/rubylisp/relational.rb
CHANGED
@@ -22,22 +22,22 @@ module Lisp
|
|
22
22
|
|
23
23
|
|
24
24
|
def self.lt_impl(args, env)
|
25
|
-
|
25
|
+
return Lisp::Debug.process_error("< needs at least 2 arguments", env) unless args.length > 1
|
26
26
|
return Lisp::Boolean.with_value(args.car.evaluate(env).value < args.cadr.evaluate(env).value)
|
27
27
|
end
|
28
28
|
|
29
29
|
def self.gt_impl(args, env)
|
30
|
-
|
30
|
+
return Lisp::Debug.process_error("> needs at least 2 arguments", env) unless args.length > 1
|
31
31
|
return Lisp::Boolean.with_value(args.car.evaluate(env).value > args.cadr.evaluate(env).value)
|
32
32
|
end
|
33
33
|
|
34
34
|
def self.lteq_impl(args, env)
|
35
|
-
|
35
|
+
return Lisp::Debug.process_error("<= needs at least 2 arguments", env) unless args.length > 1
|
36
36
|
return Lisp::Boolean.with_value(args.car.evaluate(env).value <= args.cadr.evaluate(env).value)
|
37
37
|
end
|
38
38
|
|
39
39
|
def self.gteq_impl(args, env)
|
40
|
-
|
40
|
+
return Lisp::Debug.process_error(">= needs at least 2 arguments", env) unless args.length > 1
|
41
41
|
return Lisp::Boolean.with_value(args.car.evaluate(env).value >= args.cadr.evaluate(env).value)
|
42
42
|
end
|
43
43
|
|
@@ -161,7 +161,7 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
161
161
|
return result
|
162
162
|
end
|
163
163
|
else
|
164
|
-
|
164
|
+
return Lisp::Debug.process_error("Case requires non-atomic clauses", env)
|
165
165
|
end
|
166
166
|
end
|
167
167
|
return nil
|
@@ -169,7 +169,7 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
169
169
|
|
170
170
|
|
171
171
|
def self.if_impl(args, env)
|
172
|
-
|
172
|
+
return Lisp::Debug.process_error("IF requires a condition, true action, and possibly an else action", env) unless args.length == 2 || args.length == 3
|
173
173
|
condition = args.car.evaluate(env)
|
174
174
|
if condition.true?
|
175
175
|
args.cadr.evaluate(env)
|
@@ -182,7 +182,7 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
182
182
|
|
183
183
|
|
184
184
|
def self.when_impl(args, env)
|
185
|
-
|
185
|
+
return Lisp::Debug.process_error("WHEN requires a condition and sexprs to evaluate.", env) unless args.length >= 2
|
186
186
|
condition = args.car.evaluate(env)
|
187
187
|
return args.cdr.evaluate_each(env) if condition.true?
|
188
188
|
nil
|
@@ -190,7 +190,7 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
190
190
|
|
191
191
|
|
192
192
|
def self.unless_impl(args, env)
|
193
|
-
|
193
|
+
return Lisp::Debug.process_error("UNLESS requires a condition and sexprs to evaluate.", env) unless args.length >= 2
|
194
194
|
condition = args.car.evaluate(env)
|
195
195
|
return args.cdr.evaluate_each(env) unless condition.true?
|
196
196
|
nil
|
@@ -205,7 +205,7 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
205
205
|
|
206
206
|
|
207
207
|
def self.define_variable(definition, value, env)
|
208
|
-
|
208
|
+
return Lisp::Debug.process_error("Variable names must be literal symbols.", env) unless definition.symbol?
|
209
209
|
|
210
210
|
ev = value.evaluate(env)
|
211
211
|
Lisp::EnvironmentFrame.global.bind(definition, ev)
|
@@ -215,7 +215,7 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
215
215
|
|
216
216
|
def self.define_function(definition, body, env)
|
217
217
|
name = definition.car
|
218
|
-
|
218
|
+
return Lisp::Debug.process_error("Function name must be a symbol", env) unless name.symbol?
|
219
219
|
arguments = definition.cdr
|
220
220
|
doc = nil
|
221
221
|
if body.car.string?
|
@@ -230,7 +230,7 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
230
230
|
|
231
231
|
def self.defun_impl(args, env)
|
232
232
|
definition = args.car
|
233
|
-
|
233
|
+
return Lisp::Debug.process_error("Function definition must specify name and parameters in a list", env) unless definition.list?
|
234
234
|
define_function(definition, args.cdr, env)
|
235
235
|
end
|
236
236
|
|
@@ -240,16 +240,16 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
240
240
|
if definition.list?
|
241
241
|
define_function(definition, args.cdr, env)
|
242
242
|
else
|
243
|
-
|
243
|
+
return Lisp::Debug.process_error("A symbol can be bound to only a single value.", env) unless args.cdr.length == 1
|
244
244
|
define_variable(definition, args.cadr, env)
|
245
245
|
end
|
246
246
|
end
|
247
247
|
|
248
248
|
|
249
249
|
def self.defmacro_impl(args, env)
|
250
|
-
|
250
|
+
return Lisp::Debug.process_error("defmacro requires 2 or 3 arguments: a name and argument list, and a template expression.", env) unless args.length == 2 || args.length == 3
|
251
251
|
definition = args.car
|
252
|
-
|
252
|
+
return Lisp::Debug.process_error("defmacro requires macro name and args in a list as it's first argument.", env) if definition.nil? || !definition.list?
|
253
253
|
name = definition.car
|
254
254
|
arguments = definition.cdr
|
255
255
|
doc = nil
|
@@ -270,11 +270,11 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
270
270
|
|
271
271
|
|
272
272
|
def self.gensym_impl(args, env)
|
273
|
-
|
273
|
+
return Lisp::Debug.process_error("gensym requires 0 or 1 argument", env) if args.length > 1
|
274
274
|
prefix = if args.length == 0
|
275
275
|
"GENSYM"
|
276
276
|
else
|
277
|
-
|
277
|
+
return Lisp::Debug.process_error("gensym's argument must be a string", env) unless args.car.string?
|
278
278
|
args.car.to_s
|
279
279
|
end
|
280
280
|
sym = Lisp::Symbol.named("#{prefix}-#{@@SYMBOL_COUNT}")
|
@@ -285,7 +285,7 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
285
285
|
|
286
286
|
def self.expand_impl(args, env)
|
287
287
|
macro = args.car.evaluate(env)
|
288
|
-
|
288
|
+
return Lisp::Debug.process_error("The first argument to expand must be a macro", env) unless macro.macro?
|
289
289
|
macro.expand(args.cdr, env, true)
|
290
290
|
end
|
291
291
|
|
@@ -321,9 +321,9 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
321
321
|
|
322
322
|
def self.do_let_bindings(bindings, binding_env, local_env)
|
323
323
|
bindings.each do |binding_pair|
|
324
|
-
|
324
|
+
return Lisp::Debug.process_error("let requires a list of bindings (that are 2 element lists) as it's first argument", env) unless binding_pair.list?
|
325
325
|
name = binding_pair.car
|
326
|
-
|
326
|
+
return Lisp::Debug.process_error("the first part of a let binding pair must be a symbol", env) unless name.symbol?
|
327
327
|
value = binding_pair.cadr.evaluate(binding_env)
|
328
328
|
local_env.bind_locally(name, value)
|
329
329
|
end
|
@@ -332,8 +332,9 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
332
332
|
|
333
333
|
def self.let_impl(args, env)
|
334
334
|
bindings = args.car || Lisp::ConsCell.new
|
335
|
-
|
335
|
+
return Lisp::Debug.process_error("let requires a list of bindings as it's firest argument", env) unless bindings.list?
|
336
336
|
local_frame = EnvironmentFrame.extending(env)
|
337
|
+
local_frame.previous = env
|
337
338
|
do_let_bindings(bindings, env, local_frame)
|
338
339
|
args.cdr.evaluate_each(local_frame)
|
339
340
|
end
|
@@ -341,7 +342,7 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
341
342
|
|
342
343
|
def self.letstar_impl(args, env)
|
343
344
|
bindings = args.car || Lisp::ConsCell.new
|
344
|
-
|
345
|
+
return Lisp::Debug.process_error("let requires a list of bindings as it's firest argument", env) unless bindings.list?
|
345
346
|
local_frame = EnvironmentFrame.extending(env)
|
346
347
|
do_let_bindings(bindings, local_frame, local_frame)
|
347
348
|
args.cdr.evaluate_each(local_frame)
|
@@ -354,19 +355,20 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
354
355
|
|
355
356
|
|
356
357
|
def self.do_impl(args, env)
|
357
|
-
|
358
|
+
return Lisp::Debug.process_error("Do requires at least a list of bindings and a test clause", env) if args.length < 2
|
358
359
|
bindings = args.car
|
359
|
-
|
360
|
+
return Lisp::Debug.process_error("Do requires a list of bindings as it's first argument", env) unless bindings.list?
|
360
361
|
test_clause = args.cadr
|
361
|
-
|
362
|
+
return Lisp::Debug.process_error("Do requires a list of termination condition and result expressions as it's second argument", env) unless test_clause.list?
|
362
363
|
body = args.cddr
|
363
364
|
|
364
365
|
local_frame = EnvironmentFrame.extending(env)
|
366
|
+
local_frame.previous = env
|
365
367
|
|
366
368
|
bindings.each do |binding|
|
367
|
-
|
369
|
+
return Lisp::Debug.process_error("do bindings must be (name initial next)", env) unless binding.list?
|
368
370
|
name = binding.car
|
369
|
-
|
371
|
+
return Lisp::Debug.process_error("binding name must be a symbol", env) unless name.symbol?
|
370
372
|
value = binding.cadr.evaluate(local_frame)
|
371
373
|
local_frame.bind_locally(name, value)
|
372
374
|
end
|
@@ -391,16 +393,16 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
391
393
|
|
392
394
|
|
393
395
|
def self.eval_impl(args, env)
|
394
|
-
|
396
|
+
return Lisp::Debug.process_error("eval expects a single argument, received #{args.length}.", env) if args.length != 1
|
395
397
|
arg = args.car.evaluate(env)
|
396
|
-
|
398
|
+
return Lisp::Debug.process_error("eval expect a list argument, received a #{arg.type}.", env) unless arg.list?
|
397
399
|
arg.evaluate(env)
|
398
400
|
end
|
399
401
|
|
400
402
|
|
401
403
|
def self.apply_impl(args, env)
|
402
404
|
func = args.car.evaluate(env)
|
403
|
-
|
405
|
+
return Lisp::Debug.process_error("Expected #{args.car} to evaluate to a function.", env) unless func.primitive? || func.function?
|
404
406
|
|
405
407
|
a = args.cdr.to_a.collect {|sexpr| sexpr.evaluate(env)}
|
406
408
|
arg_list = if a[-1].list?
|
@@ -413,7 +415,7 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
413
415
|
|
414
416
|
|
415
417
|
def self.chain_impl(args, env)
|
416
|
-
|
418
|
+
return Lisp::Debug.process_error("-> requires at the very least an initial value.", env) unless args.length > 0
|
417
419
|
value = args.car.evaluate(env)
|
418
420
|
cell = args.cdr
|
419
421
|
while !cell.nil?
|
@@ -432,7 +434,7 @@ useful in macros when you need a unique name for something.") do |args, env|
|
|
432
434
|
|
433
435
|
|
434
436
|
def self.tap_impl(args, env)
|
435
|
-
|
437
|
+
return Lisp::Debug.process_error("tap requires at the very least an initial value.", env) unless args.length > 0
|
436
438
|
value = args.car.evaluate(env)
|
437
439
|
cell = args.cdr
|
438
440
|
while !cell.nil?
|