rubylisp 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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?
|