skeem 0.2.08 → 0.2.09

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 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