skeem 0.2.14 → 0.2.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +451 -195
  3. data/.travis.yml +27 -0
  4. data/CHANGELOG.md +35 -1
  5. data/Gemfile +2 -0
  6. data/README.md +125 -56
  7. data/Rakefile +2 -0
  8. data/appveyor.yml +3 -4
  9. data/bin/cubic.skm +4 -0
  10. data/bin/hello-world.skm +1 -0
  11. data/bin/skeem +72 -0
  12. data/lib/skeem/datum_dsl.rb +40 -30
  13. data/lib/skeem/element_visitor.rb +5 -2
  14. data/lib/skeem/grammar.rb +88 -26
  15. data/lib/skeem/interpreter.rb +9 -7
  16. data/lib/skeem/parser.rb +6 -4
  17. data/lib/skeem/primitive/primitive_builder.rb +148 -122
  18. data/lib/skeem/primitive/primitive_procedure.rb +23 -25
  19. data/lib/skeem/runtime.rb +17 -15
  20. data/lib/skeem/s_expr_builder.rb +49 -117
  21. data/lib/skeem/s_expr_nodes.rb +147 -132
  22. data/lib/skeem/skeem_exception.rb +1 -0
  23. data/lib/skeem/skm_binding.rb +9 -11
  24. data/lib/skeem/skm_compound_datum.rb +9 -6
  25. data/lib/skeem/skm_element.rb +15 -13
  26. data/lib/skeem/skm_empty_list.rb +6 -4
  27. data/lib/skeem/skm_exception.rb +9 -0
  28. data/lib/skeem/skm_expression.rb +3 -1
  29. data/lib/skeem/skm_frame.rb +3 -2
  30. data/lib/skeem/skm_pair.rb +26 -18
  31. data/lib/skeem/skm_procedure_exec.rb +11 -6
  32. data/lib/skeem/skm_simple_datum.rb +23 -20
  33. data/lib/skeem/skm_unary_expression.rb +34 -37
  34. data/lib/skeem/standard/base.skm +4 -0
  35. data/lib/skeem/tokenizer.rb +38 -28
  36. data/lib/skeem/version.rb +3 -1
  37. data/lib/skeem.rb +2 -0
  38. data/skeem.gemspec +9 -6
  39. data/spec/skeem/add4.skm +4 -0
  40. data/spec/skeem/datum_dsl_spec.rb +13 -12
  41. data/spec/skeem/element_visitor_spec.rb +14 -10
  42. data/spec/skeem/interpreter_spec.rb +84 -44
  43. data/spec/skeem/lambda_spec.rb +13 -11
  44. data/spec/skeem/parser_spec.rb +23 -19
  45. data/spec/skeem/primitive/primitive_builder_spec.rb +65 -48
  46. data/spec/skeem/primitive/primitive_procedure_spec.rb +14 -12
  47. data/spec/skeem/runtime_spec.rb +20 -18
  48. data/spec/skeem/s_expr_nodes_spec.rb +8 -6
  49. data/spec/skeem/skm_compound_datum_spec.rb +12 -10
  50. data/spec/skeem/skm_element_spec.rb +7 -5
  51. data/spec/skeem/skm_empty_list_spec.rb +7 -5
  52. data/spec/skeem/skm_frame_spec.rb +5 -4
  53. data/spec/skeem/skm_pair_spec.rb +9 -8
  54. data/spec/skeem/skm_procedure_exec_spec.rb +2 -0
  55. data/spec/skeem/skm_simple_datum_spec.rb +24 -22
  56. data/spec/skeem/skm_unary_expression_spec.rb +11 -9
  57. data/spec/skeem/tokenizer_spec.rb +54 -43
  58. data/spec/skeem_spec.rb +2 -0
  59. data/spec/spec_helper.rb +15 -10
  60. metadata +18 -10
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper'
2
4
  require_relative '../../lib/skeem/datum_dsl'
3
5
 
@@ -7,12 +9,14 @@ require_relative '../../lib/skeem/element_visitor'
7
9
  module Skeem
8
10
  describe SkmElementVisitor do
9
11
  include DatumDSL
10
- let(:simple_datum) { integer 42 }
11
- let(:listener) do
12
- fake = double('fake-subscriber')
13
- fake.define_singleton_method(:accept_all) {}
14
- fake
15
- end
12
+ let(:simple_datum) { integer 42 }
13
+ let(:listener) do
14
+ fake = double('fake-subscriber')
15
+ fake.define_singleton_method(:accept_all) do
16
+ # Dummy block
17
+ end
18
+ fake
19
+ end
16
20
 
17
21
  # Default instantiation rule
18
22
  subject { SkmElementVisitor.new(simple_datum) }
@@ -122,7 +126,7 @@ module Skeem
122
126
  end
123
127
 
124
128
  it 'should allow the visit of a nested compound datum' do
125
- nested_list = list ['uno', 'twei', 'three']
129
+ nested_list = list %w[uno twei three']
126
130
  vec = vector ['#false', 3, nested_list, 'foo']
127
131
  instance = SkmElementVisitor.new(vec)
128
132
  instance.subscribe(listener)
@@ -132,7 +136,7 @@ module Skeem
132
136
  expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[0]).ordered
133
137
  expect(listener).to receive(:before_simple_datum).with(runtime, vec.members[1]).ordered
134
138
  expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[1]).ordered
135
-
139
+
136
140
  expect(listener).to receive(:before_pair).with(runtime, nested_list).ordered
137
141
  expect(listener).to receive(:before_car).with(runtime, nested_list, nested_list.car).ordered
138
142
  expect(nested_list.car).to eq('uno')
@@ -159,7 +163,7 @@ module Skeem
159
163
  expect(listener).to receive(:after_cdr).with(runtime, nested_list.cdr, nested_list.cdr.cdr).ordered
160
164
  expect(listener).to receive(:after_pair).with(runtime, nested_list.cdr).ordered
161
165
  expect(listener).to receive(:after_cdr).with(runtime, nested_list, nested_list.cdr).ordered
162
- expect(listener).to receive(:after_pair).with(runtime, nested_list).ordered
166
+ expect(listener).to receive(:after_pair).with(runtime, nested_list).ordered
163
167
  expect(listener).to receive(:before_simple_datum).with(runtime, vec.members[3]).ordered
164
168
  expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[3]).ordered
165
169
  expect(listener).to receive(:after_children).with(runtime, vec, vec.members).ordered
@@ -168,4 +172,4 @@ module Skeem
168
172
  end
169
173
  end # context
170
174
  end # describe
171
- end # module
175
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'stringio'
2
4
  require_relative '../spec_helper' # Use the RSpec framework
3
5
  require_relative '../../lib/skeem/datum_dsl'
@@ -14,7 +16,7 @@ module Skeem
14
16
  end
15
17
 
16
18
  it 'could be initialized with a block argument' do
17
- expect { Interpreter.new { |interp| } }.not_to raise_error
19
+ expect { Interpreter.new(&:runtime) }.not_to raise_error
18
20
  end
19
21
 
20
22
  it 'should have a parser' do
@@ -65,6 +67,7 @@ module Skeem
65
67
  end
66
68
  end
67
69
 
70
+ # rubocop: disable Style/ExponentialNotation
68
71
  it 'should evaluate isolated real numbers' do
69
72
  samples = [
70
73
  ['0.0', 0.0],
@@ -78,6 +81,7 @@ module Skeem
78
81
  expect(result).to eq(predicted)
79
82
  end
80
83
  end
84
+ # rubocop: enable Style/ExponentialNotation
81
85
 
82
86
  it 'should evaluate isolated strings' do
83
87
  samples = [
@@ -90,6 +94,7 @@ module Skeem
90
94
  end
91
95
 
92
96
  it 'should evaluate vector of constants' do
97
+ require 'benchmark'
93
98
  source = '#(2018 10 20 "Sat")'
94
99
  result = subject.run(source)
95
100
  expect(result).to be_kind_of(SkmVector)
@@ -108,7 +113,7 @@ module Skeem
108
113
 
109
114
  context 'Built-in primitives' do
110
115
  it 'should implement variable definition' do
111
- result = subject.run('(define x 28)')
116
+ subject.run('(define x 28)')
112
117
  expect(subject.fetch('x')).to eq(28)
113
118
  end
114
119
 
@@ -125,7 +130,7 @@ SKEEM
125
130
  end
126
131
 
127
132
  it 'should implement the simple conditional form' do
128
- checks = [
133
+ checks = [
129
134
  ['(if (> 3 2) "yes")', 'yes'],
130
135
  ['(if (> 2 3) "yes")', SkmUndefined.instance]
131
136
  ]
@@ -157,7 +162,7 @@ SKEEM
157
162
  ((< x 0) -1)
158
163
  )))
159
164
  SKEEM
160
- result = subject.run(source)
165
+ subject.run(source)
161
166
  checks = [
162
167
  ['(signum 3)', 1],
163
168
  ['(signum 0)', 0],
@@ -175,7 +180,7 @@ SKEEM
175
180
  ((< x 0) => -1)
176
181
  )))
177
182
  SKEEM
178
- result = subject.run(source)
183
+ subject.run(source)
179
184
  checks = [
180
185
  ['(signum 3)', 1],
181
186
  ['(signum 0)', 0],
@@ -193,7 +198,7 @@ SKEEM
193
198
  (else -1)
194
199
  )))
195
200
  SKEEM
196
- result = subject.run(source)
201
+ subject.run(source)
197
202
  checks = [
198
203
  ['(signum 3)', 1],
199
204
  ['(signum 0)', 0],
@@ -211,7 +216,7 @@ SKEEM
211
216
  end
212
217
 
213
218
  it 'should implement the quotation of constant literals' do
214
- checks = [
219
+ checks = [
215
220
  ['(quote a)', 'a'],
216
221
  ['(quote 145932)', 145932],
217
222
  ['(quote "abc")', 'abc'],
@@ -367,8 +372,6 @@ SKEEM
367
372
  result = subject.run(source)
368
373
  expect(result.last.value).to eq(20)
369
374
  end
370
-
371
-
372
375
  end # context
373
376
 
374
377
  context 'Binding constructs:' do
@@ -406,7 +409,7 @@ SKEEM
406
409
  end
407
410
 
408
411
  it 'should support the nesting of a lambda in a let expression' do
409
- source =<<-SKEEM
412
+ source = <<-SKEEM
410
413
  (define make-counter
411
414
  (lambda ()
412
415
  (let ((count 0))
@@ -477,7 +480,7 @@ SKEEM
477
480
 
478
481
  context 'Quasiquotation:' do
479
482
  it 'should implement the quasiquotation of constant literals' do
480
- checks = [
483
+ checks = [
481
484
  ['(quasiquote a)', 'a'],
482
485
  ['(quasiquote 145932)', 145932],
483
486
  ['(quasiquote "abc")', 'abc'],
@@ -518,7 +521,7 @@ SKEEM
518
521
  expect(result.members[index]).to eq(value)
519
522
  end
520
523
 
521
- source = "`#()"
524
+ source = '`#()'
522
525
  result = subject.run(source)
523
526
  expect(result).to be_kind_of(SkmVector)
524
527
  expect(result).to be_empty
@@ -555,7 +558,7 @@ SKEEM
555
558
  result = result.cdr
556
559
  end
557
560
 
558
- source = "`()"
561
+ source = '`()'
559
562
  result = subject.run(source)
560
563
  expect(result).to be_kind_of(SkmEmptyList)
561
564
  expect(result).to be_null
@@ -576,7 +579,7 @@ SKEEM
576
579
  result = result.cdr
577
580
  end
578
581
 
579
- source = "`()"
582
+ source = '`()'
580
583
  result = subject.run(source)
581
584
  expect(result).to be_kind_of(SkmEmptyList)
582
585
  expect(result).to be_null
@@ -801,42 +804,62 @@ SKEEM
801
804
  compare_to_predicted(checks)
802
805
  end
803
806
 
804
- it 'should implement the truncate-quotient procedure' do
805
- checks = [
806
- ['(truncate-quotient 5 2)', 2],
807
- ['(truncate-quotient -5 2)', -2],
808
- ['(truncate-quotient 5 -2)', -2],
809
- ['(truncate-quotient -5 -2)', 2],
810
- ['(quotient 5 2)', 2],
811
- ['(quotient -5 2)', -2],
812
- ['(quotient 5 -2)', -2],
813
- ['(quotient -5 -2)', 2]
814
- ]
815
- compare_to_predicted(checks)
816
- end
807
+ it 'should implement the truncate-quotient procedure' do
808
+ checks = [
809
+ ['(truncate-quotient 5 2)', 2],
810
+ ['(truncate-quotient -5 2)', -2],
811
+ ['(truncate-quotient 5 -2)', -2],
812
+ ['(truncate-quotient -5 -2)', 2],
813
+ ['(quotient 5 2)', 2],
814
+ ['(quotient -5 2)', -2],
815
+ ['(quotient 5 -2)', -2],
816
+ ['(quotient -5 -2)', 2]
817
+ ]
818
+ compare_to_predicted(checks)
819
+ end
817
820
 
818
- it 'should implement the truncate-remainder procedure' do
819
- checks = [
820
- ['(truncate-remainder 5 2)', 1],
821
- ['(truncate-remainder -5 2)', -1],
822
- ['(truncate-remainder 5 -2)', 1],
823
- ['(truncate-remainder -5 -2)', -1],
824
- ['(remainder 5 2)', 1],
825
- ['(remainder -5 2)', -1],
826
- ['(remainder 5 -2)', 1],
827
- ['(remainder -5 -2)', -1]
828
- ]
829
- compare_to_predicted(checks)
830
- end
821
+ it 'should implement the truncate-remainder procedure' do
822
+ checks = [
823
+ ['(truncate-remainder 5 2)', 1],
824
+ ['(truncate-remainder -5 2)', -1],
825
+ ['(truncate-remainder 5 -2)', 1],
826
+ ['(truncate-remainder -5 -2)', -1],
827
+ ['(remainder 5 2)', 1],
828
+ ['(remainder -5 2)', -1],
829
+ ['(remainder 5 -2)', 1],
830
+ ['(remainder -5 -2)', -1]
831
+ ]
832
+ compare_to_predicted(checks)
833
+ end
831
834
 
832
835
  it 'should implement the test-equal procedure' do
833
836
  checks = [
834
- ["(test-equal (cons 1 2) (cons 1 2))", true]
837
+ ['(test-equal (cons 1 2) (cons 1 2))', true]
835
838
  ]
836
839
  compare_to_predicted(checks)
837
840
  end
838
841
  end # context
839
842
 
843
+ context 'Input/output:' do
844
+ it 'should implement the include expression' do
845
+ initial_dir = Dir.pwd
846
+ filedir = File.dirname(__FILE__)
847
+ Dir.chdir(filedir)
848
+ source = '(include "add4.skm")' # Path is assumed to be relative to pwd
849
+ result = subject.run(source)
850
+ expect(result.last).to eq(10)
851
+ Dir.chdir(initial_dir)
852
+ end
853
+
854
+ it 'should implement the newline procedure' do
855
+ default_stdout = $stdout
856
+ $stdout = StringIO.new
857
+ subject.run('(newline) (newline) (newline)')
858
+ expect($stdout.string).to match(/\n\n\n$/)
859
+ $stdout = default_stdout
860
+ end
861
+ end # context
862
+
840
863
  context 'Second-order functions' do
841
864
  it 'should implement lambda that calls second-order function' do
842
865
  source = <<-SKEEM
@@ -885,7 +908,7 @@ SKEEM
885
908
  SKEEM
886
909
  result = subject.run(source)
887
910
  expect(result).to eq([0, 1, 2, 3, 4])
888
-
911
+
889
912
  source = <<-SKEEM
890
913
  (let ((x '(1 3 5 7 9)))
891
914
  (do (
@@ -894,8 +917,25 @@ SKEEM
894
917
  ((null? x) sum))) ; => 25
895
918
  SKEEM
896
919
  result = subject.run(source)
897
- expect(result).to eq(25)
920
+ expect(result).to eq(25)
898
921
  end
899
922
  end # context
923
+
924
+ context 'Macro processing:' do
925
+ # it 'should parse macro expressions' do
926
+ # source = <<-SKEEM
927
+ # (define-syntax while
928
+ # (syntax-rules ()
929
+ # ((while condition body ...)
930
+ # (let loop ()
931
+ # (if condition
932
+ # (begin
933
+ # body ...
934
+ # (loop))
935
+ # #f)))))
936
+ # SKEEM
937
+ # ptree = subject.parse(source)
938
+ # end
939
+ end
900
940
  end # describe
901
- end # module
941
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper' # Use the RSpec framework
2
4
 
3
5
  # Load the class under test
@@ -29,7 +31,7 @@ SKEEM
29
31
 
30
32
  context 'Defining compound procedures:' do
31
33
  it 'should accept the definition of simple procedure with arity 1' do
32
- source = definition_set + "\n" + 'square'
34
+ source = "#{definition_set}\nsquare"
33
35
  result = subject.run(source)
34
36
 
35
37
  square = result.last
@@ -39,7 +41,7 @@ SKEEM
39
41
  end
40
42
 
41
43
  it 'should accept the definition of simple procedure with arity 2' do
42
- source = definition_set + "\n" + 'sum-of-squares'
44
+ source = "#{definition_set}\nsum-of-squares"
43
45
  result = subject.run(source)
44
46
 
45
47
  square = result.last
@@ -63,25 +65,25 @@ SKEEM
63
65
  end
64
66
 
65
67
  it 'should support the call to a simple procedure with arity 2' do
66
- source = definition_set + "\n" + '(sum-of-squares 3 4)'
68
+ source = "#{definition_set}\n(sum-of-squares 3 4)"
67
69
  result = subject.run(source)
68
70
 
69
71
  expect(result.last).to eq(25)
70
72
  end
71
73
 
72
74
  it 'should support the call to a nested lambda procedure' do
73
- source = definition_set + "\n" + '(f 5)'
75
+ source = "#{definition_set}\n(f 5)"
74
76
  result = subject.run(source)
75
77
 
76
78
  expect(result.last).to eq(136)
77
79
  end
78
-
80
+
79
81
  it 'should accept calls to anonymous procedures' do
80
82
  source = '((lambda (x) (+ x x)) 4)'
81
83
  result = subject.run(source)
82
84
  expect(result).to eq(8)
83
85
  end
84
-
86
+
85
87
  it 'should accept unary second-order lambdas' do
86
88
  source = <<-SKEEM
87
89
  (define add-with
@@ -94,10 +96,10 @@ SKEEM
94
96
  expect(result).to eq(7)
95
97
  end
96
98
  end # context
97
-
99
+
98
100
  context 'More advanced features:' do
99
101
  subject { Interpreter.new }
100
-
102
+
101
103
  it 'should implement binary second-order functions' do
102
104
  source = <<-SKEEM
103
105
  (define compose
@@ -105,10 +107,10 @@ SKEEM
105
107
  (lambda (x)
106
108
  (f (g x)))))
107
109
  SKEEM
108
- result = subject.run(source)
110
+ subject.run(source)
109
111
  result = subject.run('((compose list square) 5)')
110
112
  expect(result.last).to eq(25)
111
- end
113
+ end
112
114
  end # context
113
115
  end # describe
114
- end # module
116
+ end # module
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../spec_helper' # Use the RSpec framework
2
4
  require_relative '../../lib/skeem/tokenizer' # Load the class under test
3
5
 
@@ -5,7 +7,7 @@ module Skeem
5
7
  describe Parser do
6
8
  context 'Initialization:' do
7
9
  it 'should be initialized without argument' do
8
- expect { Parser.new() }.not_to raise_error
10
+ expect { Parser.new }.not_to raise_error
9
11
  end
10
12
 
11
13
  it 'should have its parse engine initialized' do
@@ -16,11 +18,11 @@ module Skeem
16
18
  context 'Parsing literals:' do
17
19
  it 'should parse isolated booleans' do
18
20
  samples = [
19
- ['#f', false],
21
+ ['#f', false]
20
22
  # ['#false', false],
21
23
  # ['#t', true],
22
24
  # ['#true', true]
23
- ]
25
+ ]
24
26
  samples.each do |source, predicted|
25
27
  ptree = subject.parse(source)
26
28
  expect(ptree.root).to be_kind_of(SkmBoolean)
@@ -30,12 +32,12 @@ module Skeem
30
32
 
31
33
  it 'should parse isolated integers' do
32
34
  samples = [
33
- ['0', 0],
34
- ['3', 3],
35
- ['-3', -3],
36
- ['+12345', 12345],
37
- ['-12345', -12345]
38
- ]
35
+ ['0', 0],
36
+ ['3', 3],
37
+ ['-3', -3],
38
+ ['+12345', 12345],
39
+ ['-12345', -12345]
40
+ ]
39
41
  samples.each do |source, predicted|
40
42
  ptree = subject.parse(source)
41
43
  expect(ptree.root).to be_kind_of(SkmInteger)
@@ -43,25 +45,27 @@ module Skeem
43
45
  end
44
46
  end
45
47
 
48
+ # rubocop: disable Style/ExponentialNotation
46
49
  it 'should parse isolated real numbers' do
47
50
  samples = [
48
- ['0.0', 0.0],
49
- ['3.14', 3.14],
50
- ['-3.14', -3.14],
51
- ['+123e+45', 123e+45],
52
- ['-123e-45', -123e-45]
53
- ]
51
+ ['0.0', 0.0],
52
+ ['3.14', 3.14],
53
+ ['-3.14', -3.14],
54
+ ['+123e+45', 123e+45],
55
+ ['-123e-45', -123e-45]
56
+ ]
54
57
  samples.each do |source, predicted|
55
58
  ptree = subject.parse(source)
56
59
  expect(ptree.root).to be_kind_of(SkmReal)
57
60
  expect(ptree.root.value).to eq(predicted)
58
61
  end
59
62
  end
63
+ # rubocop: enable Style/ExponentialNotation
60
64
 
61
65
  it 'should parse isolated strings' do
62
66
  samples = [
63
- ['"Hello world!"', 'Hello world!']
64
- ]
67
+ ['"Hello world!"', 'Hello world!']
68
+ ]
65
69
  samples.each do |source, predicted|
66
70
  ptree = subject.parse(source)
67
71
  expect(ptree.root).to be_kind_of(SkmString)
@@ -71,8 +75,8 @@ module Skeem
71
75
 
72
76
  it 'should parse isolated identifiers' do
73
77
  samples = [
74
- ['the-word-recursion-has-many-meanings', 'the-word-recursion-has-many-meanings']
75
- ]
78
+ %w[the-word-recursion-has-many-meanings the-word-recursion-has-many-meanings]
79
+ ]
76
80
  samples.each do |source, predicted|
77
81
  ptree = subject.parse(source)
78
82
  expect(ptree.root).to be_kind_of(SkmVariableReference)