skeem 0.2.08 → 0.2.09

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd3683235600881fa4fdbef83d8921aff335ee90388eef9f564b60e3d9217e49
4
- data.tar.gz: 90c6402cc9f6367438ff8121741222edeab5712f747d3e498dc4a55c50f7047b
3
+ metadata.gz: a85fc0a12d54eedba56c28e18d728d00ff35044c029e1a256d4381a98c6310a8
4
+ data.tar.gz: 934cc810162a891fdddffdbfb16466ed7d7c71da8d7c5cb77670c010b5bf83fe
5
5
  SHA512:
6
- metadata.gz: dc0e68b644a532d23a8dfac125a48cbb2cf6c13e83e051e7757bf516a80a0d2fc721ea8d85f50d00461d0fa90aaf3e9fac4536f4c2818fbf061ad35110b64288
7
- data.tar.gz: 7c8ea63a8b08532d8112ae85d0ea93d8c999f87a92ed43e5e6e8e97e0f168de7a69bdaa2d3f72fe0b42f756c158d4e3bcdbb0897312a6ac8be160f657c5a3fa3
6
+ metadata.gz: 6dcdf9eb8d85abdcd4d2f416b9e9fa6ed1d9b399b92c9921ff59fb8dc6d6f047e5064fd7ccffde3ae000771079a0a7d9f64643e0585ba0c39b0566b345a5b994
7
+ data.tar.gz: 60347248374f6723c91edc3699b1306056b02bfc7a91a52e9dcfa15690f72294bf89cfd1ef8551218a6e67751bdde59f972c6ec1ea0e42733436d7caa5b26bac
data/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ ## [0.2.09] - 2019-06-10
2
+ - New procedures: `complex?`, `exact-integer?`
3
+ - Support for `#| ... |#` block comments (including nesting)
4
+
5
+ ### Added
6
+ - File `base.skm`: Added integer number predicate `exact-integer?`
7
+ - File `primitive_builder.rb`: Added numeric type predicate `complex?`
8
+ - New method `Tokenizer#skip_block_comment` to handle "| ... |#" comments (including their nesting)
9
+ - New folder `test_skeem` containing a test suite in `Skeem`
10
+
11
+ ### Changed
12
+ - Method 'PrimitiveProcedure#do_call' whenever possible, the arguments are evaluated before executing the primitive.
13
+ - File `primitive_builder.rb`: Lambda expression refactoring as most argument evaluations became redundant.
14
+ - Method `Tokenizer#_next_token` numeric literals with only zero(es) in their fractional part are implicitly converted to integers `3.0` => 3
15
+ - Method `Tokenizer#skip_whitespaces` refactoring & detection of block comments
16
+
17
+ ### Fixed
18
+ - File `base.skm`: predicate `positive?` returned #t when argument was zero. Now: (positive? 0) returns #f as expected.
19
+
20
+ ### Removed
21
+ - Empty method `PrimitiveBuilder#add_binding` removed.
22
+
23
+
1
24
  ## [0.2.08] - 2019-06-02
2
25
  - New standard procedures implemented: `assq`, `assv`
3
26
 
data/README.md CHANGED
@@ -166,6 +166,10 @@ Here are a few pointers for the Scheme programming language:
166
166
 
167
167
 
168
168
  ## Currently implemented R7RS features
169
+ ### Comments
170
+ - Semi-colon delimited comments: `; This comment stops at the end of line`
171
+ - Block `#| ... |#` comments
172
+
169
173
  ### Data type literals
170
174
  - Booleans: `#t`, `#true`, `#f`, `#false`
171
175
  - Of the number hierarchy:
@@ -246,7 +250,8 @@ This section lists the implemented standard procedures
246
250
  * `boolean?`, `and`, `or`, `not`
247
251
 
248
252
  #### Numerical operations
249
- * Number-level: `number?`, `real?`, `integer?`, `zero?`, `+`, `-`, `*`, `/`, `=`, `square`, `number->string`
253
+ * Number-level: `number?`, `complex?`, `real?`, `integer?`, `zero?`, `exact?`, `inexact?`, `exact-integer?` , `+`, `-`, `*`, `/`,
254
+ `=`, `square`, `number->string`
250
255
  * Real-level: `positive?`, `negative?`, `<`, `>`, `<=`, `>=`, `abs`, `floor-remainder`
251
256
  * Integer-level: `even?`, `odd?`
252
257
 
@@ -1,14 +1,12 @@
1
1
  require_relative 'primitive_procedure'
2
2
  require_relative '../datum_dsl'
3
3
  require_relative '../skm_pair'
4
- # require_relative '../s_expr_nodes'
5
4
 
6
5
  module Skeem
7
6
  module Primitive
8
7
  module PrimitiveBuilder
9
8
  include DatumDSL
10
9
  def add_primitives(aRuntime)
11
- add_binding(aRuntime)
12
10
  add_arithmetic(aRuntime)
13
11
  add_comparison(aRuntime)
14
12
  add_number_procedures(aRuntime)
@@ -48,9 +46,6 @@ module Skeem
48
46
  SkmArity.new(1, '*')
49
47
  end
50
48
 
51
- def add_binding(aRuntime)
52
- end
53
-
54
49
  def add_arithmetic(aRuntime)
55
50
  create_plus(aRuntime)
56
51
  create_minus(aRuntime)
@@ -72,6 +67,7 @@ module Skeem
72
67
 
73
68
  def add_number_procedures(aRuntime)
74
69
  create_object_predicate(aRuntime, 'number?')
70
+ create_object_predicate(aRuntime, 'complex?')
75
71
  create_object_predicate(aRuntime, 'real?')
76
72
  create_object_predicate(aRuntime, 'integer?')
77
73
  create_object_predicate(aRuntime, 'exact?')
@@ -141,14 +137,13 @@ module Skeem
141
137
 
142
138
  def create_plus(aRuntime)
143
139
  # arglist should be a Ruby Array
144
- primitive = ->(runtime, arglist) do
140
+ primitive = ->(_runtime, arglist) do
145
141
  if arglist.empty?
146
142
  integer(0)
147
143
  else
148
- first_one = arglist.first.evaluate(runtime)
144
+ first_one = arglist.shift
149
145
  raw_result = first_one.value
150
- operands = remaining_args(arglist, runtime)
151
- operands.each { |elem| raw_result += elem.value }
146
+ arglist.each { |elem| raw_result += elem.value }
152
147
  to_datum(raw_result)
153
148
  end
154
149
  end
@@ -156,14 +151,12 @@ module Skeem
156
151
  end
157
152
 
158
153
  def create_minus(aRuntime)
159
- primitive = ->(runtime, first_operand, arglist) do
160
- first_one = first_operand.evaluate(runtime)
161
- raw_result = first_one.value
154
+ primitive = ->(_runtime, first_operand, arglist) do
155
+ raw_result = first_operand.value
162
156
  if arglist.empty?
163
157
  raw_result = -raw_result
164
158
  else
165
- operands = arglist.evaluate(runtime).to_a
166
- operands.each { |elem| raw_result -= elem.value }
159
+ arglist.each { |elem| raw_result -= elem.value }
167
160
  end
168
161
  to_datum(raw_result)
169
162
  end
@@ -172,14 +165,13 @@ module Skeem
172
165
  end
173
166
 
174
167
  def create_multiply(aRuntime)
175
- primitive = ->(runtime, arglist) do
168
+ primitive = ->(_runtime, arglist) do
176
169
  if arglist.empty?
177
170
  integer(1)
178
171
  else
179
- first_one = arglist.first.evaluate(runtime)
172
+ first_one = arglist.shift
180
173
  raw_result = first_one.value
181
- operands = remaining_args(arglist, runtime)
182
- operands.each { |elem| raw_result *= elem.value }
174
+ arglist.each { |elem| raw_result *= elem.value }
183
175
  to_datum(raw_result)
184
176
  end
185
177
  end
@@ -188,14 +180,12 @@ module Skeem
188
180
 
189
181
 
190
182
  def create_divide(aRuntime)
191
- primitive = ->(runtime, first_operand, arglist) do
192
- first_one = first_operand.evaluate(runtime)
193
- raw_result = first_one.value
183
+ primitive = ->(_runtime, first_operand, arglist) do
184
+ raw_result = first_operand.value
194
185
  if arglist.empty?
195
186
  raw_result = 1 / raw_result.to_f
196
187
  else
197
- operands = arglist.evaluate(runtime).to_a
198
- operands.each do |elem|
188
+ arglist.each do |elem|
199
189
  if raw_result > elem.value && raw_result.modulo(elem.value).zero?
200
190
  raw_result /= elem.value
201
191
  else
@@ -211,9 +201,7 @@ module Skeem
211
201
  end
212
202
 
213
203
  def create_modulo(aRuntime)
214
- primitive = ->(runtime, argument1, argument2) do
215
- operand_1 = argument1.evaluate(runtime)
216
- operand_2 = argument2.evaluate(runtime)
204
+ primitive = ->(_runtime, operand_1, operand_2) do
217
205
  raw_result = operand_1.value.modulo(operand_2.value)
218
206
  to_datum(raw_result)
219
207
  end
@@ -227,9 +215,7 @@ module Skeem
227
215
  end
228
216
 
229
217
  def create_eqv?(aRuntime)
230
- primitive = ->(runtime, argument1, argument2) do
231
- operand_1 = argument1.evaluate(runtime)
232
- operand_2 = argument2.evaluate(runtime)
218
+ primitive = ->(runtime, operand_1, operand_2) do
233
219
  core_eqv?(operand_1, operand_2)
234
220
  end
235
221
 
@@ -242,9 +228,7 @@ module Skeem
242
228
  end
243
229
 
244
230
  def create_eq?(aRuntime)
245
- primitive = ->(runtime, argument1, argument2) do
246
- operand_1 = argument1.evaluate(runtime)
247
- operand_2 = argument2.evaluate(runtime)
231
+ primitive = ->(_runtime, operand_1, operand_2) do
248
232
  core_eq?(operand1, operand2)
249
233
  end
250
234
 
@@ -252,9 +236,7 @@ module Skeem
252
236
  end
253
237
 
254
238
  def create_equal?(aRuntime)
255
- primitive = ->(runtime, argument1, argument2) do
256
- operand_1 = argument1.evaluate(runtime)
257
- operand_2 = argument2.evaluate(runtime)
239
+ primitive = ->(_runtime, operand_1, operand_2) do
258
240
  raw_result = operand_1.skm_equal?(operand_2)
259
241
  boolean(raw_result)
260
242
  end
@@ -263,14 +245,12 @@ module Skeem
263
245
  end
264
246
 
265
247
  def create_equal(aRuntime)
266
- primitive = ->(runtime, first_operand, arglist) do
267
- first_one = first_operand.evaluate(runtime)
248
+ primitive = ->(_runtime, first_operand, arglist) do
268
249
  if arglist.empty?
269
250
  boolean(true)
270
251
  else
271
- operands = arglist.evaluate(runtime).to_a
272
- first_value = first_one.value
273
- all_equal = operands.all? { |elem| first_value == elem.value }
252
+ first_value = first_operand.value
253
+ all_equal = arglist.all? { |elem| first_value == elem.value }
274
254
  boolean(all_equal)
275
255
  end
276
256
  end
@@ -279,82 +259,70 @@ module Skeem
279
259
  end
280
260
 
281
261
  def create_lt(aRuntime)
282
- primitive = ->(runtime, first_operand, arglist) do
262
+ primitive = ->(_runtime, first_operand, arglist) do
283
263
  if arglist.empty?
284
- boolean(false)
264
+ result = false
285
265
  else
286
- operands = [first_operand.evaluate(runtime)]
287
- operands.concat(arglist.evaluate(runtime).to_a)
288
- result = true
289
- operands.each_cons(2) do |(elem1, elem2)|
290
- result &&= elem1.value < elem2.value
291
- end
292
- boolean(result)
266
+ result = primitive_comparison(:<, _runtime, first_operand, arglist)
293
267
  end
268
+ boolean(result)
294
269
  end
295
270
 
296
271
  define_primitive_proc(aRuntime, '<', one_or_more, primitive)
297
272
  end
298
273
 
299
274
  def create_gt(aRuntime)
300
- primitive = ->(runtime, first_operand, arglist) do
275
+ primitive = ->(_runtime, first_operand, arglist) do
301
276
  if arglist.empty?
302
- boolean(false)
277
+ result = false
303
278
  else
304
- operands = [first_operand.evaluate(runtime)]
305
- operands.concat(arglist.evaluate(runtime).to_a)
306
- result = true
307
- operands.each_cons(2) do |(elem1, elem2)|
308
- result &&= elem1.value > elem2.value
309
- end
310
- boolean(result)
279
+ result = primitive_comparison(:>, _runtime, first_operand, arglist)
311
280
  end
281
+ boolean(result)
312
282
  end
313
283
 
314
284
  define_primitive_proc(aRuntime, '>', one_or_more, primitive)
315
285
  end
316
286
 
317
287
  def create_lte(aRuntime)
318
- primitive = ->(runtime, first_operand, arglist) do
288
+ primitive = ->(_runtime, first_operand, arglist) do
319
289
  if arglist.empty?
320
- boolean(true)
321
- else
322
- operands = [first_operand.evaluate(runtime)]
323
- operands.concat(arglist.evaluate(runtime).to_a)
324
290
  result = true
325
- operands.each_cons(2) do |(elem1, elem2)|
326
- result &&= elem1.value <= elem2.value
327
- end
328
- boolean(result)
291
+ else
292
+ result = primitive_comparison(:<=, _runtime, first_operand, arglist)
329
293
  end
294
+ boolean(result)
330
295
  end
331
296
 
332
297
  define_primitive_proc(aRuntime, '<=', one_or_more, primitive)
333
298
  end
334
299
 
335
300
  def create_gte(aRuntime)
336
- primitive = ->(runtime, first_operand, arglist) do
301
+ primitive = ->(_runtime, first_operand, arglist) do
337
302
  if arglist.empty?
338
- boolean(true)
339
- else
340
- operands = [first_operand.evaluate(runtime)]
341
- operands.concat(arglist.evaluate(runtime).to_a)
342
303
  result = true
343
- operands.each_cons(2) do |(elem1, elem2)|
344
- result &&= elem1.value >= elem2.value
345
- end
346
-
347
- boolean(result)
304
+ else
305
+ result = primitive_comparison(:>=, _runtime, first_operand, arglist)
348
306
  end
307
+ boolean(result)
349
308
  end
350
309
 
351
310
  define_primitive_proc(aRuntime, '>=', one_or_more, primitive)
352
311
  end
312
+
313
+ def primitive_comparison(operator, _runtime, first_operand, arglist)
314
+ operands = [first_operand].concat(arglist)
315
+ result = true
316
+ operands.each_cons(2) do |(elem1, elem2)|
317
+ result &&= elem1.value.send(operator, elem2.value)
318
+ end
319
+
320
+ boolean(result)
321
+ end
353
322
 
354
323
  def create_number2string(aRuntime)
355
324
  # TODO: add support for radix argument
356
- primitive = ->(runtime, arg) do
357
- arg_evaluated = arg.evaluate(runtime)
325
+ primitive = ->(_runtime, arg_evaluated) do
358
326
  check_argtype(arg_evaluated, SkmNumber, 'number', 'number->string')
359
327
  string(arg_evaluated.value)
360
328
  end
@@ -364,6 +332,7 @@ module Skeem
364
332
 
365
333
  def create_and(aRuntime)
366
334
  # arglist should be a Ruby Array
335
+ # Arguments aren't evaluated yet!...
367
336
  primitive = ->(runtime, arglist) do
368
337
  if arglist.empty?
369
338
  boolean(true) # in conformance with 4.2.1
@@ -389,6 +358,7 @@ module Skeem
389
358
 
390
359
  def create_or(aRuntime)
391
360
  # arglist should be a Ruby Array
361
+ # Arguments aren't evaluated yet!...
392
362
  primitive = ->(runtime, arglist) do
393
363
  if arglist.empty?
394
364
  boolean(false) # in conformance with 4.2.1
@@ -409,14 +379,12 @@ module Skeem
409
379
  end
410
380
 
411
381
  def create_string_equal(aRuntime)
412
- primitive = ->(runtime, first_operand, arglist) do
413
- first_one = first_operand.evaluate(runtime)
382
+ primitive = ->(_runtime, first_operand, arglist) do
414
383
  if arglist.empty?
415
384
  boolean(true)
416
385
  else
417
- operands = evaluate_arguments(arglist, runtime)
418
- first_value = first_one.value
419
- all_equal = operands.all? { |elem| first_value == elem.value }
386
+ first_value = first_operand.value
387
+ all_equal = arglist.all? { |elem| first_value == elem.value }
420
388
  boolean(all_equal)
421
389
  end
422
390
  end
@@ -425,12 +393,11 @@ module Skeem
425
393
  end
426
394
 
427
395
  def create_string_append(aRuntime)
428
- primitive = ->(runtime, arglist) do
396
+ primitive = ->(_runtime, arglist) do
429
397
  if arglist.empty?
430
398
  value = ''
431
399
  else
432
- parts = evaluate_arguments(arglist, aRuntime)
433
- value = parts.reduce('') { |interim, substr| interim << substr.value }
400
+ value = arglist.reduce('') { |interim, substr| interim << substr.value }
434
401
  end
435
402
 
436
403
  string(value)
@@ -440,8 +407,7 @@ module Skeem
440
407
  end
441
408
 
442
409
  def create_string_length(aRuntime)
443
- primitive = ->(runtime, arg) do
444
- arg_evaluated = arg.evaluate(runtime)
410
+ primitive = ->(runtime, arg_evaluated) do
445
411
  check_argtype(arg_evaluated, SkmString, 'string', 'string-length')
446
412
  integer(arg_evaluated.length)
447
413
  end
@@ -450,8 +416,7 @@ module Skeem
450
416
  end
451
417
 
452
418
  def create_string2symbol(aRuntime)
453
- primitive = ->(runtime, arg) do
454
- arg_evaluated = arg.evaluate(runtime)
419
+ primitive = ->(runtime, arg_evaluated) do
455
420
  check_argtype(arg_evaluated, SkmString, 'string', 'string->symbol')
456
421
  identifier(arg_evaluated)
457
422
  end
@@ -460,8 +425,7 @@ module Skeem
460
425
  end
461
426
 
462
427
  def create_symbol2string(aRuntime)
463
- primitive = ->(runtime, arg) do
464
- arg_evaluated = arg.evaluate(runtime)
428
+ primitive = ->(runtime, arg_evaluated) do
465
429
  check_argtype(arg_evaluated, SkmIdentifier, 'symbol', 'symbol->string')
466
430
  string(arg_evaluated)
467
431
  end
@@ -470,8 +434,7 @@ module Skeem
470
434
  end
471
435
 
472
436
  def create_car(aRuntime)
473
- primitive = ->(runtime, arg) do
474
- arg_evaluated = arg.evaluate(runtime)
437
+ primitive = ->(runtime, arg_evaluated) do
475
438
  check_argtype(arg_evaluated, SkmPair, 'pair', 'car')
476
439
  arg_evaluated.car
477
440
  end
@@ -480,8 +443,7 @@ module Skeem
480
443
  end
481
444
 
482
445
  def create_cdr(aRuntime)
483
- primitive = ->(runtime, arg) do
484
- arg_evaluated = arg.evaluate(runtime)
446
+ primitive = ->(_runtime, arg_evaluated) do
485
447
  check_argtype(arg_evaluated, SkmPair, 'pair', 'cdr')
486
448
  arg_evaluated.cdr
487
449
  end
@@ -490,16 +452,15 @@ module Skeem
490
452
  end
491
453
 
492
454
  def create_cons(aRuntime)
493
- primitive = ->(runtime, obj1, obj2) do
494
- SkmPair.new(obj1.evaluate(aRuntime), obj2.evaluate(aRuntime))
455
+ primitive = ->(_runtime, obj1, obj2) do
456
+ SkmPair.new(obj1, obj2)
495
457
  end
496
458
 
497
459
  define_primitive_proc(aRuntime, 'cons', binary, primitive)
498
460
  end
499
461
 
500
462
  def create_length(aRuntime)
501
- primitive = ->(runtime, arg) do
502
- arg_evaluated = arg.evaluate(runtime)
463
+ primitive = ->(_runtime, arg_evaluated) do
503
464
  check_argtype(arg_evaluated, [SkmPair, SkmEmptyList], 'list', 'length')
504
465
  integer(arg_evaluated.length)
505
466
  end
@@ -508,8 +469,7 @@ module Skeem
508
469
  end
509
470
 
510
471
  def create_list2vector(aRuntime)
511
- primitive = ->(runtime, arg) do
512
- arg_evaluated = arg.evaluate(runtime)
472
+ primitive = ->(_runtime, arg_evaluated) do
513
473
  check_argtype(arg_evaluated, [SkmPair, SkmEmptyList], 'list', 'list->vector')
514
474
  vector(arg_evaluated.to_a)
515
475
  end
@@ -555,6 +515,7 @@ module Skeem
555
515
  end
556
516
 
557
517
  def create_append(aRuntime)
518
+ # Arguments aren't evaluated yet!...
558
519
  primitive = ->(runtime, arglist) do
559
520
  if arglist.size > 1
560
521
  arguments = evaluate_arguments(arglist, aRuntime)
@@ -569,6 +530,7 @@ module Skeem
569
530
  end
570
531
 
571
532
  def create_setcar(aRuntime)
533
+ # Arguments aren't evaluated yet!...
572
534
  primitive = ->(runtime, pair_arg, obj_arg) do
573
535
  case pair_arg
574
536
  when SkmPair
@@ -593,6 +555,7 @@ module Skeem
593
555
  end
594
556
 
595
557
  def create_setcdr(aRuntime)
558
+ # Arguments aren't evaluated yet!...
596
559
  primitive = ->(runtime, pair_arg, obj_arg) do
597
560
  case pair_arg
598
561
  when SkmPair
@@ -620,7 +583,7 @@ module Skeem
620
583
  primitive = ->(runtime, obj_arg, alist_arg) do
621
584
  assoc_list = alist_arg.evaluate(runtime)
622
585
  check_assoc_list(assoc_list, 'assq')
623
- obj = obj_arg.evaluate(runtime)
586
+ obj = obj_arg.evaluate(runtime)
624
587
  result = boolean(false)
625
588
  unless assoc_list.empty?
626
589
  pair = assoc_list
@@ -638,12 +601,10 @@ module Skeem
638
601
  end
639
602
  define_primitive_proc(aRuntime, 'assq', binary, primitive)
640
603
  end
641
-
604
+
642
605
  def create_assv(aRuntime)
643
- primitive = ->(runtime, obj_arg, alist_arg) do
644
- assoc_list = alist_arg.evaluate(runtime)
606
+ primitive = ->(runtime, obj, assoc_list) do
645
607
  check_assoc_list(assoc_list, 'assq')
646
- obj = obj_arg.evaluate(runtime)
647
608
  result = boolean(false)
648
609
  unless assoc_list.empty?
649
610
  pair = assoc_list
@@ -659,8 +620,8 @@ module Skeem
659
620
 
660
621
  result
661
622
  end
662
- define_primitive_proc(aRuntime, 'assv', binary, primitive)
663
- end
623
+ define_primitive_proc(aRuntime, 'assv', binary, primitive)
624
+ end
664
625
 
665
626
  def check_assoc_list(alist, proc_name)
666
627
  check_argtype(alist, [SkmPair, SkmEmptyList], 'association list', proc_name)
@@ -675,24 +636,16 @@ module Skeem
675
636
  end
676
637
 
677
638
  def create_list_copy(aRuntime)
678
- primitive = ->(runtime, arg) do
679
- arg_evaluated = arg.evaluate(runtime)
639
+ primitive = ->(runtime, arg_evaluated) do
680
640
  check_argtype(arg_evaluated, [SkmPair, SkmEmptyList], 'list', 'list-copy')
681
- arg.klone
641
+ arg_evaluated.klone # Previously: arg.klone
682
642
  end
683
643
 
684
644
  define_primitive_proc(aRuntime, 'list-copy', unary, primitive)
685
645
  end
686
646
 
687
-
688
647
  def create_vector(aRuntime)
689
- primitive = ->(runtime, arglist) do
690
- if arglist.empty?
691
- elements = []
692
- else
693
- elements = evaluate_arguments(arglist, aRuntime)
694
- end
695
-
648
+ primitive = ->(_runtime, elements) do
696
649
  vector(elements)
697
650
  end
698
651
 
@@ -700,8 +653,7 @@ module Skeem
700
653
  end
701
654
 
702
655
  def create_vector_length(aRuntime)
703
- primitive = ->(runtime, arg) do
704
- arg_evaluated = arg.evaluate(runtime)
656
+ primitive = ->(_runtime, arg_evaluated) do
705
657
  check_argtype(arg_evaluated, SkmVector, 'vector', 'vector-length')
706
658
  integer(arg_evaluated.length)
707
659
  end
@@ -716,7 +668,7 @@ module Skeem
716
668
  if arglist.empty?
717
669
  filler = SkmUndefined.instance
718
670
  else
719
- filler = arglist.car.evaluate(runtime)
671
+ filler = arglist.first.evaluate(runtime)
720
672
  end
721
673
  elements = Array.new(count.value, filler)
722
674
 
@@ -728,10 +680,8 @@ module Skeem
728
680
 
729
681
  def create_vector_ref(aRuntime)
730
682
  # argument 1: a vector, argument 2: an index(integer)
731
- primitive = ->(runtime, aVector, anIndex) do
732
- vector = aVector.evaluate(runtime)
683
+ primitive = ->(runtime, vector, index) do
733
684
  check_argtype(vector, SkmVector, 'vector', 'vector-ref')
734
- index = anIndex.evaluate(runtime)
735
685
  check_argtype(index, SkmInteger, 'integer', 'vector-ref')
736
686
  # TODO: index checking
737
687
  raw_result = vector.members[index.value]
@@ -742,8 +692,7 @@ module Skeem
742
692
  end
743
693
 
744
694
  def create_vector2list(aRuntime)
745
- primitive = ->(runtime, arg) do
746
- arg_evaluated = arg.evaluate(runtime)
695
+ primitive = ->(runtime, arg_evaluated) do
747
696
  check_argtype(arg_evaluated, SkmVector, 'vector', 'vector->list')
748
697
  SkmPair.create_from_a(arg_evaluated.members)
749
698
  end
@@ -752,13 +701,11 @@ module Skeem
752
701
  end
753
702
 
754
703
  def create_apply(aRuntime)
755
- primitive = ->(runtime, first_operand, arglist) do
756
- proc_arg = first_operand.evaluate(runtime)
704
+ primitive = ->(runtime, proc_arg, arglist) do
757
705
  if arglist.empty?
758
706
  result = SkmEmptyList.instance
759
707
  else
760
- arguments = evaluate_arguments(arglist, runtime)
761
- single_list = append_core(arguments)
708
+ single_list = append_core(arglist)
762
709
  invoke = ProcedureCall.new(nil, proc_arg, single_list.to_a)
763
710
  result = invoke.evaluate(runtime)
764
711
  end
@@ -768,13 +715,11 @@ module Skeem
768
715
  end
769
716
 
770
717
  def create_map(aRuntime)
771
- primitive = ->(runtime, first_operand, list_of_lists) do
772
- proc_arg = first_operand.evaluate(runtime)
773
- if list_of_lists.empty?
718
+ primitive = ->(runtime, proc_arg, arglist) do
719
+ if arglist.empty?
774
720
  result = SkmEmptyList.instance
775
721
  else
776
- arguments = evaluate_arguments(list_of_lists, runtime)
777
- curr_cells = arguments.to_a
722
+ curr_cells = arglist
778
723
  arity = curr_cells.size
779
724
  initial_result = nil
780
725
  curr_result = nil
@@ -813,14 +758,13 @@ module Skeem
813
758
  end
814
759
 
815
760
  def create_test_assert(aRuntime)
816
- primitive = ->(runtime, arg) do
817
- arg_evaluated = arg.evaluate(runtime)
761
+ primitive = ->(runtime, arg_evaluated) do
818
762
  if arg_evaluated.boolean? && arg_evaluated.value == false
819
763
  assert_call = aRuntime.caller
820
764
  pos = assert_call.call_site
821
765
  # Error: assertion failed: (> 1 2)
822
766
  msg1 = "assertion failed on line #{pos.line}, column #{pos.column}"
823
- msg2 = ", with #{arg.inspect}"
767
+ msg2 = ", with #{arg_evaluated.inspect}"
824
768
  raise StandardError, 'Error: ' + msg1 + msg2
825
769
  else
826
770
  boolean(true)
@@ -843,8 +787,7 @@ module Skeem
843
787
  # DON'T USE IT
844
788
  # Non-standard procedure reserved for internal testing/debugging purposes.
845
789
  def create_inspect(aRuntime)
846
- primitive = ->(runtime, arg) do
847
- arg_evaluated = arg.evaluate(runtime)
790
+ primitive = ->(runtime, arg_evaluated) do
848
791
  $stderr.puts 'INSPECT>' + arg_evaluated.inspect
849
792
  Skeem::SkmUndefined.instance
850
793
  end
@@ -853,21 +796,13 @@ module Skeem
853
796
 
854
797
  def create_object_predicate(aRuntime, predicate_name, msg_name = nil)
855
798
  msg_name = predicate_name if msg_name.nil?
856
- primitive = ->(runtime, arg) do
857
- arg_evaluated = arg.evaluate(runtime)
799
+ primitive = ->(runtime, arg_evaluated) do
858
800
  to_datum(arg_evaluated.send(msg_name))
859
801
  end
860
802
 
861
803
  define_primitive_proc(aRuntime, predicate_name, unary, primitive)
862
804
  end
863
805
 
864
- # def def_procedure(aRuntime, pairs)
865
- # pairs.each_slice(2) do |(name, code)|
866
- # func = PrimitiveProcedure.new(name, code)
867
- # define(aRuntime, func.identifier, func)
868
- # end
869
- # end
870
-
871
806
  def define_primitive_proc(aRuntime, anIdentifier, anArity, aRubyLambda)
872
807
  primitive = PrimitiveProcedure.new(anIdentifier, anArity, aRubyLambda)
873
808
  @primitive_map = {} unless @primitives_map
@@ -114,19 +114,35 @@ module Skeem
114
114
  result = code.call(aRuntime)
115
115
  elsif arity.variadic? || (arity.low < arity.high)
116
116
  if arity.low.zero?
117
- result = code.call(aRuntime, operands)
117
+ if ['and', 'or', 'append'].include? identifier
118
+ # Defer the evaluation of arguments to the primitive
119
+ result = code.call(aRuntime, operands)
120
+ else
121
+ evaluated_args = operands.map {|opernd| opernd.evaluate(aRuntime) }
122
+ result = code.call(aRuntime, evaluated_args)
123
+ end
118
124
  else
119
- arguments = []
120
- arguments << operands.take(arity.low).flatten
125
+ # require 'debug'
126
+ args = operands.take(arity.low)
127
+ args.map! { |arg| arg.evaluate(aRuntime) } unless args.empty?
121
128
  count_delta = operands.size - arity.low
122
- arguments << SkmPair.create_from_a(operands.slice(-count_delta, count_delta))
129
+ remaining = operands.slice(-count_delta, count_delta).map do |arg|
130
+ arg.evaluate(aRuntime)
131
+ end
132
+ args << remaining.flatten
123
133
  # p operands.size
124
134
  # p count_delta
125
- # p arguments.inspect
126
- result = code.send(:call, aRuntime, *arguments.flatten)
135
+ # p args.inspect
136
+ result = code.send(:call, aRuntime, *args)
127
137
  end
128
138
  else # Fixed arity...
129
- result = code.send(:call, aRuntime, *operands)
139
+ if identifier == 'set-car!' || identifier == 'set-cdr!'
140
+ # Defer evaluation inside the primitive
141
+ result = code.send(:call, aRuntime, *operands)
142
+ else
143
+ evaluated_args = operands.map {|opernd| opernd.evaluate(aRuntime) }
144
+ result = code.send(:call, aRuntime, *evaluated_args)
145
+ end
130
146
  end
131
147
 
132
148
  result
@@ -97,6 +97,10 @@ module Skeem
97
97
  def number?
98
98
  true
99
99
  end
100
+
101
+ def complex?
102
+ false
103
+ end
100
104
 
101
105
  def eqv?(other)
102
106
  return true if self.equal?(other)
@@ -119,6 +123,10 @@ module Skeem
119
123
  def real?
120
124
  true
121
125
  end
126
+
127
+ def complex?
128
+ true
129
+ end
122
130
 
123
131
  def exact?
124
132
  false
@@ -1,7 +1,14 @@
1
1
  ; Standard R7RS procedures from section 6.2.6
2
2
 
3
- ; (zero? z)
4
- ; Returns true iff z is zero
3
+ ;; (exact-integer? z)
4
+ (define exact-integer?
5
+ (lambda (z)
6
+ (and
7
+ (exact? z)
8
+ (integer? z))))
9
+
10
+ ;; (zero? z)
11
+ ;; Returns true iff z is zero
5
12
  (define zero?
6
13
  (lambda (z)
7
14
  (if (= z 0)
@@ -9,10 +16,10 @@
9
16
  #f)))
10
17
 
11
18
  ; (positive? x)
12
- ; Return true if x greater or equal to zero; false otherwise
19
+ ; Return true if x greater than zero; false otherwise
13
20
  (define positive?
14
21
  (lambda (x)
15
- (if (>= x 0)
22
+ (if (> x 0)
16
23
  #t
17
24
  #f)))
18
25
 
@@ -105,7 +112,7 @@
105
112
  ;; (test-eqv expected test-expr)
106
113
  (define test-eqv
107
114
  (lambda (x y)
108
- (test-assert (equal? x y))))
115
+ (test-assert (eqv? x y))))
109
116
 
110
117
  ;; Test the equality (with equal? predicate) between an expected value and
111
118
  ;; an expression
@@ -92,10 +92,10 @@ module Skeem
92
92
  elsif (lexeme = scanner.scan(/(?:,@?)|(?:=>)/))
93
93
  token = build_token(@@lexeme2name[lexeme], lexeme)
94
94
  elsif (lexeme = scanner.scan(/#(?:(?:true)|(?:false)|(?:u8)|[\\\(tfeiodx]|(?:\d+[=#]))/))
95
- token = cardinal_token(lexeme)
96
- elsif (lexeme = scanner.scan(/[+-]?[0-9]+(?=\s|[|()";]|$)/))
95
+ token = cardinal_token(lexeme)
96
+ elsif (lexeme = scanner.scan(/[+-]?[0-9]+(?:.0+)?(?=\s|[|()";]|$)/))
97
97
  token = build_token('INTEGER', lexeme) # Decimal radix
98
- elsif (lexeme = scanner.scan(/[+-]?[0-9]+(?:\.[0-9]+)?(?:(?:e|E)[+-]?[0-9]+)?/))
98
+ elsif (lexeme = scanner.scan(/[+-]?[0-9]+(?:\.[0-9]*)?(?:(?:e|E)[+-]?[0-9]+)?/))
99
99
  # Order dependency: must be tested after INTEGER case
100
100
  token = build_token('REAL', lexeme)
101
101
  elsif (lexeme = scanner.scan(/"(?:\\"|[^"])*"/)) # Double quotes literal?
@@ -124,7 +124,7 @@ module Skeem
124
124
 
125
125
  return token
126
126
  end
127
-
127
+
128
128
  =begin
129
129
  #t #f These are the boolean constants (section 6.3), along
130
130
  with the alternatives #true and #false.
@@ -138,7 +138,7 @@ numbers (section 6.2.5).
138
138
  #<n>= #<n># These are used for labeling and referencing
139
139
  other literal data (section 2.4).
140
140
  # token = build_token('BOOLEAN', lexeme)
141
- =end
141
+ =end
142
142
  def cardinal_token(aLexeme)
143
143
  case aLexeme
144
144
  when /^#true|false|t|f$/
@@ -146,7 +146,7 @@ other literal data (section 2.4).
146
146
  when '#('
147
147
  token = build_token(@@lexeme2name[aLexeme], aLexeme)
148
148
  end
149
-
149
+
150
150
  return token
151
151
  end
152
152
 
@@ -175,7 +175,7 @@ other literal data (section 2.4).
175
175
  when 'STRING_LIT'
176
176
  value = to_string(aLexeme, aFormat)
177
177
  when 'IDENTIFIER'
178
- value = to_identifier(aLexeme, aFormat)
178
+ value = to_identifier(aLexeme, aFormat)
179
179
  else
180
180
  value = aLexeme
181
181
  end
@@ -204,7 +204,7 @@ other literal data (section 2.4).
204
204
 
205
205
  return value
206
206
  end
207
-
207
+
208
208
  def to_string(aLexeme, aFormat)
209
209
  case aFormat
210
210
  when :default
@@ -212,7 +212,7 @@ other literal data (section 2.4).
212
212
  end
213
213
 
214
214
  return value
215
- end
215
+ end
216
216
 
217
217
  def to_identifier(aLexeme, aFormat)
218
218
  case aFormat
@@ -221,26 +221,27 @@ other literal data (section 2.4).
221
221
  end
222
222
 
223
223
  return value
224
- end
225
-
224
+ end
225
+
226
226
  def skip_whitespaces
227
227
  pre_pos = scanner.pos
228
228
 
229
229
  loop do
230
- ws_found = false
231
- cmt_found = false
232
- found = scanner.skip(/[ \t\f]+/)
233
- ws_found = true if found
234
- found = scanner.skip(/(?:\r\n)|\r|\n/)
235
- if found
230
+ ws_found = scanner.skip(/[ \t\f]+/) ? true : false
231
+ nl_found = scanner.skip(/(?:\r\n)|\r|\n/)
232
+ if nl_found
236
233
  ws_found = true
237
234
  next_line
238
235
  end
236
+ cmt_found = false
239
237
  next_ch = scanner.peek(1)
240
238
  if next_ch == ';'
241
239
  cmt_found = true
242
240
  scanner.skip(/;[^\r\n]*(?:(?:\r\n)|\r|\n)?/)
243
241
  next_line
242
+ elsif scanner.peek(2) == '#|'
243
+ skip_block_comment
244
+ next
244
245
  end
245
246
  break unless ws_found or cmt_found
246
247
  end
@@ -248,7 +249,28 @@ other literal data (section 2.4).
248
249
  curr_pos = scanner.pos
249
250
  return if curr_pos == pre_pos
250
251
  end
251
-
252
+
253
+ def skip_block_comment()
254
+ # require 'debug'
255
+ scanner.skip(/#\|/)
256
+ nesting_level = 1
257
+ loop do
258
+ comment_part = scanner.scan_until(/(?:\|\#)|(?:\#\|)|(?:(?:\r\n)|\r|\n)/)
259
+ unless comment_part
260
+ raise ScanError, "Unterminated '#| ... |#' comment on line #{lineno}"
261
+ end
262
+ case scanner.matched
263
+ when /(?:(?:\r\n)|\r|\n)/
264
+ next_line
265
+ when '|#'
266
+ nesting_level -= 1
267
+ break if nesting_level.zero?
268
+ when '#|'
269
+ nesting_level += 1
270
+ end
271
+ end
272
+ end
273
+
252
274
  def next_line
253
275
  @lineno += 1
254
276
  @line_start = scanner.pos
data/lib/skeem/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Skeem
2
- VERSION = '0.2.08'.freeze
2
+ VERSION = '0.2.09'.freeze
3
3
  end
@@ -664,23 +664,8 @@ SKEEM
664
664
  checks = [
665
665
  ['(positive? 3.1)', true],
666
666
  ['(positive? -3.1)', false],
667
- ['(positive? 0)', true],
668
- ['(positive? 0.0)', true],
669
- ['(positive? 3)', true],
670
- ['(positive? -3)', false]
671
- ]
672
- checks.each do |(skeem_expr, expectation)|
673
- result = subject.run(skeem_expr)
674
- expect(result).to eq(expectation)
675
- end
676
- end
677
-
678
- it 'should implement the positive? predicate' do
679
- checks = [
680
- ['(positive? 3.1)', true],
681
- ['(positive? -3.1)', false],
682
- ['(positive? 0)', true],
683
- ['(positive? 0.0)', true],
667
+ ['(positive? 0)', false],
668
+ ['(positive? 0.0)', false],
684
669
  ['(positive? 3)', true],
685
670
  ['(positive? -3)', false]
686
671
  ]
@@ -100,7 +100,7 @@ SKEEM
100
100
  ["(eqv? 'a 'a)", true],
101
101
  ["(eqv? 'a 'b)", false],
102
102
  ['(eqv? 2 2)', true],
103
- ['(eqv? 2 2.0)', false],
103
+ ['(eqv? 2 2.0)', true],
104
104
  ['(eqv? 3 2)', false],
105
105
  ["(eqv? '() '())", true],
106
106
  ['(eqv? 100000000 100000000)', true],
@@ -146,7 +146,8 @@ module Skeem
146
146
  no_arg = []
147
147
  expect(pproc.call(rtime, no_arg)).to eq(0)
148
148
 
149
- many = ['foo', 'bar', 'quux']
149
+ many = [SkmString.create('foo'), SkmString.create('bar'),
150
+ SkmString.create('quux')]
150
151
  expect( pproc.call(rtime, many)).to eq(3)
151
152
  end
152
153
  end # context
@@ -69,6 +69,7 @@ module Skeem
69
69
  [' 3', 3],
70
70
  ['+3 ', +3],
71
71
  ['-3', -3],
72
+ ['-3.0', -3],
72
73
  ['-1234', -1234]
73
74
  ]
74
75
 
@@ -167,7 +168,7 @@ module Skeem
167
168
  end
168
169
  end
169
170
 
170
- context 'Semi-colon comments:' do
171
+ context 'Comments:' do
171
172
  it 'should skip heading comments' do
172
173
  input = "; Starting comment\n \"Some text\""
173
174
  subject.reinitialize(input)
@@ -197,6 +198,32 @@ module Skeem
197
198
  expect(token.terminal).to eq('STRING_LIT')
198
199
  expect(token.lexeme).to eq('Second text')
199
200
  end
201
+
202
+ it 'should skip block comments' do
203
+ input = '"First text" #| Middle comment |# "Second text"'
204
+ subject.reinitialize(input)
205
+ tokens = subject.tokens
206
+ expect(tokens.size).to eq(2)
207
+ token = tokens[0]
208
+ expect(token.terminal).to eq('STRING_LIT')
209
+ expect(token.lexeme).to eq('First text')
210
+ token = tokens[1]
211
+ expect(token.terminal).to eq('STRING_LIT')
212
+ expect(token.lexeme).to eq('Second text')
213
+ end
214
+
215
+ it 'should cope with nested block comments' do
216
+ input = '"First text" #| One #| Two |# comment #| Three |# |# "Second text"'
217
+ subject.reinitialize(input)
218
+ tokens = subject.tokens
219
+ expect(tokens.size).to eq(2)
220
+ token = tokens[0]
221
+ expect(token.terminal).to eq('STRING_LIT')
222
+ expect(token.lexeme).to eq('First text')
223
+ token = tokens[1]
224
+ expect(token.terminal).to eq('STRING_LIT')
225
+ expect(token.lexeme).to eq('Second text')
226
+ end
200
227
  end
201
228
 
202
229
  context 'Scanning Scheme sample code' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skeem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.08
4
+ version: 0.2.09
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-02 00:00:00.000000000 Z
11
+ date: 2019-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley