lisp-interpreter 0.1.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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +75 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +173 -0
- data/Rakefile +6 -0
- data/bin/console.bat +2 -0
- data/bin/setup +8 -0
- data/bin/start.rb +2 -0
- data/lib/lisp/interpreter.rb +9 -0
- data/lib/lisp/interpreter/boolean.rb +42 -0
- data/lib/lisp/interpreter/checker.rb +45 -0
- data/lib/lisp/interpreter/errors.rb +13 -0
- data/lib/lisp/interpreter/functional.rb +272 -0
- data/lib/lisp/interpreter/list.rb +162 -0
- data/lib/lisp/interpreter/numbers.rb +161 -0
- data/lib/lisp/interpreter/object.rb +51 -0
- data/lib/lisp/interpreter/parser.rb +72 -0
- data/lib/lisp/interpreter/run.rb +4 -0
- data/lib/lisp/interpreter/strings.rb +131 -0
- data/lib/lisp/interpreter/tokenizer.rb +185 -0
- data/lib/lisp/interpreter/validator.rb +53 -0
- data/lib/lisp/interpreter/value_finder.rb +60 -0
- data/lib/lisp/interpreter/version.rb +5 -0
- data/lisp-interpreter.gemspec +35 -0
- data/spec/lisp/interpreter_spec.rb +987 -0
- data/spec/spec_helper.rb +14 -0
- metadata +116 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
# Validator module is used to validate if the user input is correct
|
2
|
+
module Validator
|
3
|
+
def balanced_brackets?(token)
|
4
|
+
strim = token.gsub(/[^\[\]\(\)\{\}]/, '')
|
5
|
+
return true if strim.empty?
|
6
|
+
return false if strim.size.odd?
|
7
|
+
loop do
|
8
|
+
s = strim.gsub('()', '').gsub('[]', '').gsub('{}', '')
|
9
|
+
return true if s.empty?
|
10
|
+
return false if s == strim
|
11
|
+
strim = s
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def balanced_quotes?(token)
|
16
|
+
token.count('"').even?
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid_var_name(var)
|
20
|
+
!var.match(/^[[:alpha:]]+$/).nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid_var(var)
|
24
|
+
(valid_literals var) || (valid_objects var)
|
25
|
+
end
|
26
|
+
|
27
|
+
def valid_function(fn)
|
28
|
+
idx = fn[0] == '(' ? (find_bracket_idx fn, 0) : 0
|
29
|
+
func =
|
30
|
+
if idx.zero?
|
31
|
+
predefined_method_caller [fn[idx]]
|
32
|
+
else
|
33
|
+
calc_input_val fn[0..idx]
|
34
|
+
end
|
35
|
+
raise 'No such procedure' if func.nil? && (!func.is_a? Proc)
|
36
|
+
[func, fn[idx + 1..-1]]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def valid_literals(var)
|
42
|
+
number = check_for_number var
|
43
|
+
string = check_for_string var
|
44
|
+
boolean = check_for_bool var
|
45
|
+
symbol = check_for_symbol var
|
46
|
+
quote = check_for_quote var
|
47
|
+
number || string || boolean || symbol || quote
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid_objects(var)
|
51
|
+
var.list? || var.pair?
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Value finder module
|
2
|
+
module ValueFinder
|
3
|
+
def find_all_values(other)
|
4
|
+
result = []
|
5
|
+
until other.empty?
|
6
|
+
x, other = find_next_value other
|
7
|
+
result << x
|
8
|
+
end
|
9
|
+
result
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_bracket_idx(other, first_bracket)
|
13
|
+
open_br = 0
|
14
|
+
other[first_bracket..other.size - 1].each_with_index do |token, idx|
|
15
|
+
open_br += 1 if token == '('
|
16
|
+
open_br -= 1 if token == ')'
|
17
|
+
return idx + first_bracket if open_br.zero?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_next_function_value(other)
|
22
|
+
idx = (find_bracket_idx other, 0)
|
23
|
+
value = calc_input_val other[0..idx]
|
24
|
+
other = other[idx + 1..other.size]
|
25
|
+
[value, other]
|
26
|
+
end
|
27
|
+
|
28
|
+
def size_for_list_elem(values)
|
29
|
+
result = []
|
30
|
+
values.each do |v|
|
31
|
+
if v.include?('(') || v.include?(')')
|
32
|
+
v.split(/(\(|\))|\ /).each { |t| result << t unless t == '' }
|
33
|
+
else
|
34
|
+
result << v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
result.size
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_next_value_helper(other)
|
41
|
+
value = no_eval_list other[2..(find_bracket_idx other, 1) - 1]
|
42
|
+
[(build_list value), other[3 + (size_for_list_elem value)..-1]]
|
43
|
+
end
|
44
|
+
|
45
|
+
def find_value_helper(other)
|
46
|
+
if other[0] == '('
|
47
|
+
find_next_function_value other
|
48
|
+
elsif other[0..1].join == '\'('
|
49
|
+
find_next_value_helper other
|
50
|
+
else
|
51
|
+
value = get_var other[0].to_s
|
52
|
+
[value, other[1..-1]]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_next_value(other)
|
57
|
+
return [other[0], other[1..-1]] if other[0].is_a? Proc
|
58
|
+
find_value_helper other
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'lisp/interpreter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'lisp-interpreter'
|
8
|
+
spec.version = Lisp::Interpreter::VERSION
|
9
|
+
spec.authors = ['Zaki Petrov']
|
10
|
+
spec.email = ['zaki1993@abv.bg\n']
|
11
|
+
|
12
|
+
spec.summary = %q{Lisp interpreter gem}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
20
|
+
else
|
21
|
+
raise 'RubyGems 2.0 or newer is required to protect against " \
|
22
|
+
"public gem pushes.'
|
23
|
+
end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
26
|
+
f.match(%r{^(test)/})
|
27
|
+
end
|
28
|
+
spec.bindir = 'exe'
|
29
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
spec.add_development_dependency 'bundler', '~> 1.15'
|
33
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
34
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
35
|
+
end
|
@@ -0,0 +1,987 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Lisp::Interpreter do
|
4
|
+
before do
|
5
|
+
@p = Parser.new
|
6
|
+
@p.parse('(define x 5)')
|
7
|
+
@p.parse('(define y 0.999)')
|
8
|
+
@msg =
|
9
|
+
{
|
10
|
+
'inc_number' => 'Incorrect number of arguments',
|
11
|
+
'zero_div' => 'divided by 0',
|
12
|
+
'inv_type' => 'Invalid data type'
|
13
|
+
}
|
14
|
+
|
15
|
+
def car_cdr_err(got, fn)
|
16
|
+
'Cannot apply ' + fn + ' on ' + got
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_lst(arr)
|
20
|
+
'(' + arr.join(' ') + ')'
|
21
|
+
end
|
22
|
+
|
23
|
+
@p.parse('(define xl (lambda (x) (* 2 x)))')
|
24
|
+
@p.parse('(define yl (lambda () 5))')
|
25
|
+
@p.parse('(define zl (lambda ()))')
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'exceptions' do
|
29
|
+
context 'wrong number of arguments' do
|
30
|
+
it 'throws error when less arguments are provided' do
|
31
|
+
expect(@p.parse('(cons 1)')).to eq @msg['inc_number']
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'throws error when more arguments are provided' do
|
35
|
+
expect(@p.parse('(xl 6 6)')).to eq @msg['inc_number']
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'throws error when no arguments are expected' do
|
39
|
+
expect(@p.parse('(yl 5)')).to eq @msg['inc_number']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'incorrect argument type' do
|
44
|
+
it 'throws error when <number> is expected' do
|
45
|
+
expect(@p.parse('(+ #t)')).to eq @msg['inv_type']
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'throws error when <string> is expected' do
|
49
|
+
expect(@p.parse('(string-length 1)')).to eq @msg['inv_type']
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'throws error when <boolean> is expected' do
|
53
|
+
expect(@p.parse('(not \'apple)')).to eq @msg['inv_type']
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'throws error when <list> is expected' do
|
57
|
+
expect(@p.parse('(length "not list")')).to eq @msg['inv_type']
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'literals' do
|
63
|
+
it 'throws invalid variable error when the data is invalid' do
|
64
|
+
expect(@p.parse('#\invalid')).to eq @msg['inv_type']
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'can parse integers' do
|
68
|
+
expect(@p.parse('1')).to eq '1'
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'can parse floats' do
|
72
|
+
expect(@p.parse('1.5')).to eq '1.5'
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'can parse strings' do
|
76
|
+
expect(@p.parse('"Sample"')).to eq '"Sample"'
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'can parse booleans' do
|
80
|
+
expect(@p.parse('#t')).to eq '#t'
|
81
|
+
expect(@p.parse('#f')).to eq '#f'
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'can parse characters' do
|
85
|
+
expect(@p.parse('#\t')).to eq '#\t'
|
86
|
+
expect(@p.parse('#\space')).to eq '#\space'
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'can parse quotes' do
|
90
|
+
expect(@p.parse('\'(1 2)')).to eq '(1 2)'
|
91
|
+
expect(@p.parse('\'QUOTE')).to eq 'QUOTE'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '(+ z ...)' do
|
96
|
+
it 'returns 0 when <z> is not provided' do
|
97
|
+
expect(@p.parse('(+)')).to eq 0
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'returns <z> when <z> is single argument' do
|
101
|
+
expect(@p.parse('(+ 1)')).to eq 1
|
102
|
+
expect(@p.parse('(+ 5.0)')).to eq 5.0
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'returns the sum of <z>s when <z> are multiple arguments' do
|
106
|
+
expect(@p.parse('(+ 1 2)')).to eq 3
|
107
|
+
expect(@p.parse('(+ 1 2.0 3)')).to eq 6.0
|
108
|
+
expect(@p.parse('(+ 1 0 3 4.4)')).to eq 8.4
|
109
|
+
expect(@p.parse('(+ 1 2 3 4 5)')).to eq 15
|
110
|
+
expect(@p.parse('(+ 1 (+ 2 0) (+ 2 1) (+ 2 2) 5)')).to eq 15
|
111
|
+
expect(@p.parse('(+ 1.2 (*) (- 0) (+))')).to eq 2.2
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '(- z w ...+)' do
|
116
|
+
it 'returns 0 if no <z> and <w>s are provided' do
|
117
|
+
expect(@p.parse('(-)')).to eq 0
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'returns <-z> if no <w>s are provided' do
|
121
|
+
expect(@p.parse('(- 1)')).to be '-1'.to_i
|
122
|
+
expect(@p.parse('(- 5.0)')).to eq '-5'.to_f
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'returns the subtractions of <w>s from <z>' do
|
126
|
+
expect(@p.parse('(- 1 2)')).to eq '-1'.to_i
|
127
|
+
expect(@p.parse('(- 1 2 0.0)')).to eq '-1'.to_f
|
128
|
+
expect(@p.parse('(- 1 2 3 4 5)')).to eq '-13'.to_i
|
129
|
+
expect(@p.parse('(- -1 2 3 4 5)')).to eq '-15'.to_i
|
130
|
+
expect(@p.parse('(- 1 (+ 2 0) (+ 2 1) (+ 2 2) 5)')).to eq '-13'.to_i
|
131
|
+
expect(@p.parse('(- 1.2 (*) (+))')).to eq 0.19999999999999996
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe '(* z ...)' do
|
136
|
+
it 'returns 1 if <z> is not provided' do
|
137
|
+
expect(@p.parse('(*)')).to eq 1
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'return <z> if <z> is single argument' do
|
141
|
+
expect(@p.parse('(* 1)')).to eq 1
|
142
|
+
expect(@p.parse('(* 0.0)')).to eq 0.0
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'returns the product of <z> if <z> is not single argument' do
|
146
|
+
expect(@p.parse('(* 8.0 9)')).to eq 72.0
|
147
|
+
expect(@p.parse('(* 1 2 0.0)')).to eq 0.0
|
148
|
+
expect(@p.parse('(* 1 2 3 4)')).to eq 24
|
149
|
+
expect(@p.parse('(* 1 2 3 4.0 5)')).to eq 120.0
|
150
|
+
expect(@p.parse('(* 1 (+ 2 0) (+ 2 1) (+ 2 2) 5)')).to eq 120
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe '(/ z w ...+)' do
|
155
|
+
it 'throws ZeroDivisionError when <z> is 0 and no <w>s are provided' do
|
156
|
+
expect(@p.parse('(/ 0)')).to eq @msg['zero_div']
|
157
|
+
expect(@p.parse('(/ 0.0)')).to eq @msg['zero_div']
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'returns <z> when no <w>s are provided' do
|
161
|
+
expect(@p.parse('(/ 1)')).to eq 1
|
162
|
+
expect(@p.parse('(/ 10.0)')).to eq 0.1
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'returns (/ <z> <w>s) when <w>s are provided' do
|
166
|
+
expect(@p.parse('(/ 81 3.0)')).to eq 27.0
|
167
|
+
expect(@p.parse('(/ 1 2 3 4 5)')).to eq 0.008333333333333333
|
168
|
+
expect(@p.parse('(/ 1 (+ 2) (+ 2 1))')).to eq 0.16666666666666666
|
169
|
+
expect(@p.parse('(/ (* 0.33 6) (/ 22 7))')).to eq 0.63
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe '(not v)' do
|
174
|
+
it 'returns #t when <v> is #f' do
|
175
|
+
expect(@p.parse('(not #f)')).to eq '#t'
|
176
|
+
expect(@p.parse('(not (not #t))')).to eq '#t'
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'returns #f when <v> is #t' do
|
180
|
+
expect(@p.parse('(not #t)')).to eq '#f'
|
181
|
+
expect(@p.parse('(not (not #f))')).to eq '#f'
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe '(equal? v1 v2)' do
|
186
|
+
it 'returns #t when <v1> = <v2>' do
|
187
|
+
expect(@p.parse('(equal? 1 1)')).to eq '#t'
|
188
|
+
expect(@p.parse('(equal? "Sample" "Sample")')).to eq '#t'
|
189
|
+
expect(@p.parse('(equal? \'yes \'yes)')).to eq '#t'
|
190
|
+
expect(@p.parse('(equal? (cons 1 2) (cons 1 2))')).to eq '#t'
|
191
|
+
expect(@p.parse('(equal? (cons 1 2) \'(1 . 2))')).to eq '#t'
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'returns #f when <v1> != <v2>' do
|
195
|
+
expect(@p.parse('(equal? 1 1.0)')).to eq '#f'
|
196
|
+
expect(@p.parse('(equal? #t #f)')).to eq '#f'
|
197
|
+
expect(@p.parse('(equal? #\a #\b)')).to eq '#f'
|
198
|
+
expect(@p.parse('(equal? (not #t) (not #f))')).to eq '#f'
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe '(quotient n m)' do
|
203
|
+
it 'throws ZeroDivisionError if second argument is 0' do
|
204
|
+
expect(@p.parse('(quotient 1 0)')).to eq @msg['zero_div']
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'finds the quotient of <n> and <m> when they are integers' do
|
208
|
+
expect(@p.parse('(quotient 10 3)')).to eq 3
|
209
|
+
expect(@p.parse('(quotient -10 3)')).to eq '-3'.to_i
|
210
|
+
expect(@p.parse('(quotient 10 -3)')).to eq '-3'.to_i
|
211
|
+
expect(@p.parse('(quotient -10 -3)')).to eq 3
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'finds the quotient of <n> and <m> when they are floats' do
|
215
|
+
expect(@p.parse('(quotient 10 3.0)')).to eq 3.0
|
216
|
+
expect(@p.parse('(quotient -10.0 3)')).to eq '-3'.to_f
|
217
|
+
expect(@p.parse('(quotient -10 3.0)')).to eq '-3'.to_f
|
218
|
+
expect(@p.parse('(quotient -10.0 -3.0)')).to eq 3.0
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe '(remainder n m)' do
|
223
|
+
it 'throws ZeroDivisionError if <m> is 0' do
|
224
|
+
expect(@p.parse('(remainder 1 0)')).to eq @msg['zero_div']
|
225
|
+
expect(@p.parse('(remainder 1 0.0)')).to eq @msg['zero_div']
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'finds the remainder of <n> and <m> when they are integers' do
|
229
|
+
expect(@p.parse('(remainder 10 3)')).to eq 1
|
230
|
+
expect(@p.parse('(remainder -10 3)')).to eq '-1'.to_i
|
231
|
+
expect(@p.parse('(remainder 10 -3)')).to eq 1
|
232
|
+
expect(@p.parse('(remainder -10 -3)')).to eq '-1'.to_i
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'finds the remainder of <n> and <m> when they are floats' do
|
236
|
+
expect(@p.parse('(remainder 10 3.0)')).to eq 1.0
|
237
|
+
expect(@p.parse('(remainder -10.0 3)')).to eq '-1'.to_f
|
238
|
+
expect(@p.parse('(remainder 10 -3.0)')).to eq 1.0
|
239
|
+
expect(@p.parse('(remainder -10.0 -3.0)')).to eq '-1'.to_f
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe '(modulo n m)' do
|
244
|
+
it 'throws ZeroDivisionError if <m> is 0' do
|
245
|
+
expect(@p.parse('(modulo 1 0)')).to eq @msg['zero_div']
|
246
|
+
expect(@p.parse('(modulo 1 0.0)')).to eq @msg['zero_div']
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'finds the modulo of <n> and <m> when they are integers' do
|
250
|
+
expect(@p.parse('(modulo 10 3)')).to eq 1
|
251
|
+
expect(@p.parse('(modulo -10 3)')).to eq 2
|
252
|
+
expect(@p.parse('(modulo 10 -3)')).to eq '-2'.to_i
|
253
|
+
expect(@p.parse('(modulo -10 -3)')).to eq '-1'.to_i
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'finds the modulo of <n> and <m> when they are floats' do
|
257
|
+
expect(@p.parse('(modulo 10 3.0)')).to eq 1.0
|
258
|
+
expect(@p.parse('(modulo -10.0 3)')).to eq 2.0
|
259
|
+
expect(@p.parse('(modulo 10 -3.0)')).to eq '-2'.to_f
|
260
|
+
expect(@p.parse('(modulo -10.0 -3.0)')).to eq '-1'.to_f
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe '(numerator q)' do
|
265
|
+
it 'returns the numerator of <q> when <q> is integer' do
|
266
|
+
expect(@p.parse('(numerator 5)')).to eq 5
|
267
|
+
expect(@p.parse('(numerator 1)')).to eq 1
|
268
|
+
expect(@p.parse('(numerator 0)')).to eq 0
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'returns the numerator of <q> when <q> is float' do
|
272
|
+
expect(@p.parse('(numerator 5.3)')).to eq 5.3
|
273
|
+
expect(@p.parse('(numerator 1.5)')).to eq 1.5
|
274
|
+
expect(@p.parse('(numerator 0.7)')).to eq 0.7
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'returns the numerator of <q> when <q> is rational' do
|
278
|
+
expect(@p.parse('(numerator 5/6)')).to eq 5
|
279
|
+
expect(@p.parse('(numerator 1/1)')).to eq 1
|
280
|
+
expect(@p.parse('(numerator 1/0)')).to eq 1
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe '(denominator q)' do
|
285
|
+
it 'returns the denominator of <q> when <q> is integer' do
|
286
|
+
expect(@p.parse('(denominator 5)')).to eq 1
|
287
|
+
expect(@p.parse('(denominator 1)')).to eq 1
|
288
|
+
expect(@p.parse('(denominator 0)')).to eq 1
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'returns the denominator of <q> when <q> is float' do
|
292
|
+
expect(@p.parse('(denominator 5.3)')).to eq 1
|
293
|
+
expect(@p.parse('(denominator 1.5)')).to eq 1
|
294
|
+
expect(@p.parse('(denominator 0.7)')).to eq 1
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'returns the denominator of <q> when <q> is rational' do
|
298
|
+
expect(@p.parse('(denominator 5/6)')).to eq 6
|
299
|
+
expect(@p.parse('(denominator 1/1.5)')).to eq 1.5
|
300
|
+
expect(@p.parse('(denominator 1/0)')).to eq 0
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
describe '(abs z)' do
|
305
|
+
it 'returns the absolute value of <z> when <z> is integer' do
|
306
|
+
expect(@p.parse('(abs 1)')).to eq 1
|
307
|
+
expect(@p.parse('(abs -10)')).to eq 10
|
308
|
+
expect(@p.parse('(abs 0)')).to eq 0
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'returns the absolute value of <z> when <z> is float' do
|
312
|
+
expect(@p.parse('(abs 1.0)')).to eq 1.0
|
313
|
+
expect(@p.parse('(abs -10.7)')).to eq 10.7
|
314
|
+
expect(@p.parse('(abs 0.0)')).to eq 0.0
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
describe '(add1 z)' do
|
319
|
+
it 'returns <z> + 1 when <z> is integer' do
|
320
|
+
expect(@p.parse('(add1 1)')).to eq 2
|
321
|
+
expect(@p.parse('(add1 -1)')).to eq 0
|
322
|
+
expect(@p.parse('(add1 -2)')).to eq '-1'.to_i
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'returns <z> + 1 when <z> is float' do
|
326
|
+
expect(@p.parse('(add1 1.0)')).to eq 2.0
|
327
|
+
expect(@p.parse('(add1 -1.0)')).to eq 0.0
|
328
|
+
expect(@p.parse('(add1 -2.5)')).to eq '-1.5'.to_f
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
describe '(sub1 z)' do
|
333
|
+
it 'returns <z> - 1 when <z> is integer' do
|
334
|
+
expect(@p.parse('(sub1 1)')).to eq 0
|
335
|
+
expect(@p.parse('(sub1 -1)')).to eq '-2'.to_i
|
336
|
+
expect(@p.parse('(sub1 -2)')).to eq '-3'.to_i
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'returns <z> - 1 when <z> is float' do
|
340
|
+
expect(@p.parse('(sub1 1.0)')).to eq 0.0
|
341
|
+
expect(@p.parse('(sub1 -1.0)')).to eq '-2'.to_f
|
342
|
+
expect(@p.parse('(sub1 -2.5)')).to eq '-3.5'.to_f
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
describe '(min x ...+)' do
|
347
|
+
it 'returns <x> if <x> is single argument' do
|
348
|
+
expect(@p.parse('(min -5)')).to eq '-5'.to_i
|
349
|
+
expect(@p.parse('(min 1.5)')).to eq 1.5
|
350
|
+
expect(@p.parse('(min 1)')).to eq 1
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'returns the smallest of <x>s' do
|
354
|
+
expect(@p.parse('(min 0 0)')).to eq 0
|
355
|
+
expect(@p.parse('(min 4 2.1 9.5)')).to eq 2.1
|
356
|
+
expect(@p.parse('(min 5 3 1 2 4)')).to eq 1
|
357
|
+
expect(@p.parse('(min 1.99 1.98002 1.98001 2)')).to eq 1.98001
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
describe '(max x ...+)' do
|
362
|
+
it 'returns <x> if <x>s is single argument' do
|
363
|
+
expect(@p.parse('(max -5)')).to eq '-5'.to_i
|
364
|
+
expect(@p.parse('(max 1.5)')).to eq 1.5
|
365
|
+
expect(@p.parse('(max 1)')).to eq 1
|
366
|
+
end
|
367
|
+
|
368
|
+
it 'returns the largest of <x>' do
|
369
|
+
expect(@p.parse('(max 0 0)')).to eq 0
|
370
|
+
expect(@p.parse('(max 4 2.1 9.5)')).to eq 9.5
|
371
|
+
expect(@p.parse('(max 5 3 1 2 4)')).to eq 5
|
372
|
+
expect(@p.parse('(max 1.9999999 2.000001 2)')).to eq 2.000001
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
describe '(< x y ...+)' do
|
377
|
+
it 'returns true when called with <smaller> and <bigger> arguments' do
|
378
|
+
expect(@p.parse('(< 1 2)')).to eq '#t'
|
379
|
+
expect(@p.parse('(< 1.7 2.8 3)')).to eq '#t'
|
380
|
+
end
|
381
|
+
|
382
|
+
it 'returns false when called with <bigger> and <smaller> arguments' do
|
383
|
+
expect(@p.parse('(< 3 2)')).to eq '#f'
|
384
|
+
expect(@p.parse('(< 3.1 2.5 1.2)')).to eq '#f'
|
385
|
+
end
|
386
|
+
|
387
|
+
it 'returns false when called with equal arguments' do
|
388
|
+
expect(@p.parse('(< 2 2.0)')).to eq '#f'
|
389
|
+
expect(@p.parse('(< 2 2 2 2 2)')).to eq '#f'
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
describe '(> x y ...+)' do
|
394
|
+
it 'returns true when called with <smaller> and <bigger> arguments' do
|
395
|
+
expect(@p.parse('(> 3 2)')).to eq '#t'
|
396
|
+
expect(@p.parse('(> 3.2 2.99 1.1)')).to eq '#t'
|
397
|
+
end
|
398
|
+
|
399
|
+
it 'returns false when called with <bigger> and <smaller> arguments' do
|
400
|
+
expect(@p.parse('(> 1 2)')).to eq '#f'
|
401
|
+
expect(@p.parse('(> 1.5 2.1 3.7)')).to eq '#f'
|
402
|
+
end
|
403
|
+
|
404
|
+
it 'returns false when called with equal arguments' do
|
405
|
+
expect(@p.parse('(> 2 2.0)')).to eq '#f'
|
406
|
+
expect(@p.parse('(> 2 2 2 2 2)')).to eq '#f'
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
describe '(<= x y ...+)' do
|
411
|
+
it 'returns true when called with <smaller> and <bigger> arguments' do
|
412
|
+
expect(@p.parse('(<= 1 2)')).to eq '#t'
|
413
|
+
expect(@p.parse('(<= 1.5 2.1 3.6)')).to eq '#t'
|
414
|
+
end
|
415
|
+
|
416
|
+
it 'returns false when called with <bigger> and <smaller> arguments' do
|
417
|
+
expect(@p.parse('(<= 3.1 2.1)')).to eq '#f'
|
418
|
+
expect(@p.parse('(<= 3.2 2.2 1.2)')).to eq '#f'
|
419
|
+
end
|
420
|
+
|
421
|
+
it 'returns true when called with equal arguments' do
|
422
|
+
expect(@p.parse('(<= 2 2.0)')).to eq '#t'
|
423
|
+
expect(@p.parse('(<= 2 2 2 2 2)')).to eq '#t'
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
describe '(>= x y ...+)' do
|
428
|
+
it 'returns true when called with <smaller> and <bigger> arguments' do
|
429
|
+
expect(@p.parse('(>= 3 2)')).to eq '#t'
|
430
|
+
expect(@p.parse('(>= 3.5 2.5 1.5)')).to eq '#t'
|
431
|
+
end
|
432
|
+
|
433
|
+
it 'returns false when called with <bigger> and <smaller> arguments' do
|
434
|
+
expect(@p.parse('(>= 1 2)')).to eq '#f'
|
435
|
+
expect(@p.parse('(>= 1.5 2.5 3.5)')).to eq '#f'
|
436
|
+
end
|
437
|
+
|
438
|
+
it 'returns true when called with equal arguments' do
|
439
|
+
expect(@p.parse('(>= 2 2.0)')).to eq '#t'
|
440
|
+
expect(@p.parse('(>= 2 2 2 2 2)')).to eq '#t'
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
describe '(string-length str)' do
|
445
|
+
it 'returns 0 whem <str> is the empty string' do
|
446
|
+
expect(@p.parse('(string-length "")')).to eq 0
|
447
|
+
end
|
448
|
+
|
449
|
+
it 'returns the length of <str> when <str> is not the empty string' do
|
450
|
+
expect(@p.parse('(string-length "Hello world")')).to eq 11
|
451
|
+
expect(@p.parse('(string-length "Sample")')).to eq 6
|
452
|
+
expect(@p.parse('(string-length " ")')).to eq 3
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
describe '(substring str from [to])' do
|
457
|
+
it 'finds the substring of <str> when only <from> is provided' do
|
458
|
+
expect(@p.parse('(substring "Apple" 1)')).to eq '"pple"'
|
459
|
+
expect(@p.parse('(substring "Hello world" 4)')).to eq '"o world"'
|
460
|
+
expect(@p.parse('(substring "" 4)')).to eq '""'
|
461
|
+
expect(@p.parse('(substring "Sample" 15)')).to eq '""'
|
462
|
+
expect(@p.parse('(substring "Sample" 0)')).to eq '"Sample"'
|
463
|
+
end
|
464
|
+
|
465
|
+
it 'finds the substring of <str> when <from> and <to> are provided' do
|
466
|
+
expect(@p.parse('(substring "Apple" 1 4)')).to eq '"ppl"'
|
467
|
+
expect(@p.parse('(substring "Hello world" 4 7)')).to eq '"o w"'
|
468
|
+
expect(@p.parse('(substring "Apple" 4 2)')).to eq '""'
|
469
|
+
expect(@p.parse('(substring "Sample" 15 16)')).to eq '""'
|
470
|
+
expect(@p.parse('(substring "Sample" 0 0)')).to eq '"Sample"'
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
describe '(string-upcase str)' do
|
475
|
+
it 'returns <str> if <str> is the empty string' do
|
476
|
+
expect(@p.parse('(string-upcase "")')).to eq '""'
|
477
|
+
end
|
478
|
+
|
479
|
+
it 'converts <str> to upcase if <str> is not the empty string' do
|
480
|
+
expect(@p.parse('(string-upcase "Apple")')).to eq '"APPLE"'
|
481
|
+
expect(@p.parse('(string-upcase "sample")')).to eq '"SAMPLE"'
|
482
|
+
expect(@p.parse('(string-upcase "APPLE")')).to eq '"APPLE"'
|
483
|
+
expect(@p.parse('(string-upcase "SaMpLe")')).to eq '"SAMPLE"'
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
describe '(string-downcase str)' do
|
488
|
+
it 'returns <str> if <str> is the empty string' do
|
489
|
+
expect(@p.parse('(string-downcase "")')).to eq '""'
|
490
|
+
end
|
491
|
+
|
492
|
+
it 'converts <str> to downcase if <str> is not the empty string' do
|
493
|
+
expect(@p.parse('(string-downcase "Apple")')).to eq '"apple"'
|
494
|
+
expect(@p.parse('(string-downcase "Sample")')).to eq '"sample"'
|
495
|
+
expect(@p.parse('(string-downcase "APPLE")')).to eq '"apple"'
|
496
|
+
expect(@p.parse('(string-downcase "SaMpLe")')).to eq '"sample"'
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
describe '(string-contains? s contained)' do
|
501
|
+
it 'returns true if <contained> is prefix of <s>' do
|
502
|
+
expect(@p.parse('(string-contains? "Racket" "Rac")')).to eq '#t'
|
503
|
+
expect(@p.parse('(string-contains? "racket" "rac")')).to eq '#t'
|
504
|
+
end
|
505
|
+
|
506
|
+
it 'returns true if <contained> is sufix of <s>' do
|
507
|
+
expect(@p.parse('(string-contains? "Racket" "ket")')).to eq '#t'
|
508
|
+
expect(@p.parse('(string-contains? "racket" "ket")')).to eq '#t'
|
509
|
+
end
|
510
|
+
|
511
|
+
it 'returns true if <contained> is infix of <s>' do
|
512
|
+
expect(@p.parse('(string-contains? "Racket" "acke")')).to eq '#t'
|
513
|
+
expect(@p.parse('(string-contains? "racket" "acke")')).to eq '#t'
|
514
|
+
expect(@p.parse('(string-contains? "Racket" "Racket")')).to eq '#t'
|
515
|
+
end
|
516
|
+
|
517
|
+
it 'returns false if <s> does not contain <contained>' do
|
518
|
+
expect(@p.parse('(string-contains? "Racket" "Rackett")')).to eq '#f'
|
519
|
+
expect(@p.parse('(string-contains? "racket" "sample")')).to eq '#f'
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
describe '(string->list str)' do
|
524
|
+
it 'return the empty list if <str> is the empty string' do
|
525
|
+
expect(@p.parse('(string->list "")')).to eq '()'
|
526
|
+
end
|
527
|
+
|
528
|
+
it 'returns a list of <str> characters when <str> is not the empty list' do
|
529
|
+
result = '(#\S #\a #\m #\p #\l #\e)'
|
530
|
+
expect(@p.parse('(string->list "Sample")')).to eq result
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
describe '(string-split str [sep])' do
|
535
|
+
it 'splits the empty string' do
|
536
|
+
expect(@p.parse('(string-split "")')).to eq '()'
|
537
|
+
end
|
538
|
+
|
539
|
+
it 'splits string only with spaces' do
|
540
|
+
expect(@p.parse('(string-split " ")')).to eq '()'
|
541
|
+
end
|
542
|
+
|
543
|
+
it 'removes the carriage when splitting' do
|
544
|
+
result = '("foo" "bar" "baz")'
|
545
|
+
expect(@p.parse('(string-split " foo bar baz \r\n\t")')).to eq result
|
546
|
+
end
|
547
|
+
|
548
|
+
it 'can split non empty string' do
|
549
|
+
expect(@p.parse('(string-split "p e n")')).to eq '("p" "e" "n")'
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
describe '(string? v)' do
|
554
|
+
it 'returns true if <v> is string' do
|
555
|
+
expect(@p.parse('(string? "str")')).to eq '#t'
|
556
|
+
expect(@p.parse('(string? "")')).to eq '#t'
|
557
|
+
end
|
558
|
+
|
559
|
+
it 'returns false if <v> is not string' do
|
560
|
+
expect(@p.parse('(string? 1)')).to eq '#f'
|
561
|
+
expect(@p.parse('(string? 1.5)')).to eq '#f'
|
562
|
+
expect(@p.parse('(string? \'apple)')).to eq '#f'
|
563
|
+
expect(@p.parse('(string? \'(1 . 2))')).to eq '#f'
|
564
|
+
expect(@p.parse('(string? #\a)')).to eq '#f'
|
565
|
+
expect(@p.parse('(string? #t)')).to eq '#f'
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
describe '(string-replace str from to)' do
|
570
|
+
it 'can replace the empty string' do
|
571
|
+
expect(@p.parse('(string-replace "pen" "" " ")')).to eq '" p e n "'
|
572
|
+
end
|
573
|
+
|
574
|
+
it 'can replace non empty strings' do
|
575
|
+
res = '"foo blah baz"'
|
576
|
+
expect(@p.parse('(string-replace "foo bar baz" "bar" "blah")')).to eq res
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
describe '(string-prefix? s prefix)' do
|
581
|
+
it 'returns true if <s> starts with <prefix>' do
|
582
|
+
expect(@p.parse('(string-prefix? "Racket" "Rac")')).to eq '#t'
|
583
|
+
expect(@p.parse('(string-prefix? "racket" "rac")')).to eq '#t'
|
584
|
+
end
|
585
|
+
|
586
|
+
it 'returns true if <prefix> is equal to <s>' do
|
587
|
+
expect(@p.parse('(string-prefix? "Racket" "Racket")')).to eq '#t'
|
588
|
+
end
|
589
|
+
|
590
|
+
it 'returns false if <s> does not start with <prefix>' do
|
591
|
+
expect(@p.parse('(string-prefix? "Racket" "Rackett")')).to eq '#f'
|
592
|
+
expect(@p.parse('(string-prefix? "racket" "sample")')).to eq '#f'
|
593
|
+
expect(@p.parse('(string-prefix? "racket" "ket")')).to eq '#f'
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
describe '(string-sufix? s suffix)' do
|
598
|
+
it 'returns true if <s> ends with <suffix>' do
|
599
|
+
expect(@p.parse('(string-sufix? "Racket" "cket")')).to eq '#t'
|
600
|
+
expect(@p.parse('(string-sufix? "racket" "cket")')).to eq '#t'
|
601
|
+
end
|
602
|
+
|
603
|
+
it 'returns true if <suffix> is equal to <s>' do
|
604
|
+
expect(@p.parse('(string-sufix? "Racket" "Racket")')).to eq '#t'
|
605
|
+
end
|
606
|
+
|
607
|
+
it 'returns false if <s> does not end with <suffix>' do
|
608
|
+
expect(@p.parse('(string-sufix? "Racket" "Rackett")')).to eq '#f'
|
609
|
+
expect(@p.parse('(string-sufix? "racket" "sample")')).to eq '#f'
|
610
|
+
expect(@p.parse('(string-sufix? "racket" "rack")')).to eq '#f'
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
describe '(string-join strs [sep])' do
|
615
|
+
it 'appends the strings in <strs> when <sep> is not provided' do
|
616
|
+
expect(@p.parse('(string-join \'(1 2))')).to eq '"1 2"'
|
617
|
+
expect(@p.parse('(string-join \'())')).to eq '""'
|
618
|
+
expect(@p.parse('(string-join (list 1 2))')).to eq '"1 2"'
|
619
|
+
end
|
620
|
+
|
621
|
+
it 'appends the strings in <strs> when <sep> is provided' do
|
622
|
+
expect(@p.parse('(string-join \'(1 2) "potato")')).to eq '"1potato2"'
|
623
|
+
expect(@p.parse('(string-join \'() "potato")')).to eq '""'
|
624
|
+
expect(@p.parse('(string-join (list 1 2) "potato")')).to eq '"1potato2"'
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
describe '(null? v)' do
|
629
|
+
it 'returns true when <v> is the empty list' do
|
630
|
+
expect(@p.parse('(null? \'())')).to eq '#t'
|
631
|
+
expect(@p.parse('(null? null)')).to eq '#t'
|
632
|
+
expect(@p.parse('(null? (list))')).to eq '#t'
|
633
|
+
end
|
634
|
+
|
635
|
+
it 'returns false when <v> is not the empty list' do
|
636
|
+
expect(@p.parse('(null? \'(1 2))')).to eq '#f'
|
637
|
+
expect(@p.parse('(null? (cons 1 \'()))')).to eq '#f'
|
638
|
+
expect(@p.parse('(null? (list 1 2))')).to eq '#f'
|
639
|
+
expect(@p.parse('(null? 1)')).to eq '#f'
|
640
|
+
expect(@p.parse('(null? 1.5)')).to eq '#f'
|
641
|
+
expect(@p.parse('(null? "string")')).to eq '#f'
|
642
|
+
expect(@p.parse('(null? #t)')).to eq '#f'
|
643
|
+
expect(@p.parse('(null? \'quote)')).to eq '#f'
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
describe '(cons a d)' do
|
648
|
+
it 'returns pair of <a> and <d> when <d> is not list' do
|
649
|
+
expect(@p.parse('(cons 1 2)')).to eq '(1 . 2)'
|
650
|
+
expect(@p.parse('(cons 1 (cons 2 3))')).to eq '(1 2 . 3)'
|
651
|
+
end
|
652
|
+
|
653
|
+
it 'returns pair when <d> is list' do
|
654
|
+
expect(@p.parse('(cons 1 \'())')).to eq '(1)'
|
655
|
+
expect(@p.parse('(cons 1 null)')).to eq '(1)'
|
656
|
+
expect(@p.parse('(cons 1 (list))')).to eq '(1)'
|
657
|
+
expect(@p.parse('(cons 1 (cons 2 \'()))')).to eq '(1 2)'
|
658
|
+
expect(@p.parse('(cons 1 (list 2 3))')).to eq '(1 2 3)'
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
describe '#reserverd_keywords' do
|
663
|
+
context '#null' do
|
664
|
+
it 'returns empty list' do
|
665
|
+
expect(@p.parse('null')).to eq '()'
|
666
|
+
end
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
describe '(list v ...)' do
|
671
|
+
it 'returns empty list when <v> is not provided' do
|
672
|
+
expect(@p.parse('(list)')).to eq '()'
|
673
|
+
end
|
674
|
+
|
675
|
+
it 'returns non empty list when <v> has 1 or more elements' do
|
676
|
+
expect(@p.parse('(list 1)')).to eq '(1)'
|
677
|
+
expect(@p.parse('(list 1 "s")')).to eq '(1 "s")'
|
678
|
+
result = '(1 (#t . #f) quote)'
|
679
|
+
expect(@p.parse('(list 1 (cons #t #f) \'quote)')).to eq result
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
describe '(car p)' do
|
684
|
+
it 'throws error when <p> is the empty list' do
|
685
|
+
expect(@p.parse('(car null)')).to eq car_cdr_err '\'()', 'car'
|
686
|
+
expect(@p.parse('(car (list))')).to eq car_cdr_err '\'()', 'car'
|
687
|
+
expect(@p.parse('(car \'())')).to eq car_cdr_err '\'()', 'car'
|
688
|
+
end
|
689
|
+
|
690
|
+
it 'returns the first element of <p>' do
|
691
|
+
expect(@p.parse('(car (cons 1 2))')).to eq '1'
|
692
|
+
expect(@p.parse('(car \'( #t . 2))')).to eq '#t'
|
693
|
+
expect(@p.parse('(car (list #f 2 3 4))')).to eq '#f'
|
694
|
+
expect(@p.parse('(car \'(1 2 3 4))')).to eq '1'
|
695
|
+
expect(@p.parse('(car \'(1))')).to eq '1'
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
describe '(cdr p)' do
|
700
|
+
it 'throws error when <p> is the empty list' do
|
701
|
+
expect(@p.parse('(cdr null)')).to eq car_cdr_err '\'()', 'cdr'
|
702
|
+
expect(@p.parse('(cdr (list))')).to eq car_cdr_err '\'()', 'cdr'
|
703
|
+
expect(@p.parse('(cdr \'())')).to eq car_cdr_err '\'()', 'cdr'
|
704
|
+
end
|
705
|
+
|
706
|
+
it 'returns the second element of <p>' do
|
707
|
+
expect(@p.parse('(cdr (cons 1 2))')).to eq '(2)'
|
708
|
+
expect(@p.parse('(cdr \'( "sample" . #t))')).to eq '(#t)'
|
709
|
+
expect(@p.parse('(cdr (cons 1 (cons 2 3)))')).to eq '(2 . 3)'
|
710
|
+
expect(@p.parse('(cdr (list #f 2 3 4))')).to eq '(2 3 4)'
|
711
|
+
expect(@p.parse('(cdr \'(1 2 3 4))')).to eq '(2 3 4)'
|
712
|
+
expect(@p.parse('(cdr \'(1))')).to eq '()'
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
716
|
+
describe '(list? v)' do
|
717
|
+
it 'returns true if <v> is empty list' do
|
718
|
+
expect(@p.parse('(list? null)')).to eq '#t'
|
719
|
+
expect(@p.parse('(list? (list))')).to eq '#t'
|
720
|
+
expect(@p.parse('(list? \'())')).to eq '#t'
|
721
|
+
end
|
722
|
+
|
723
|
+
it 'returns true if <v> is not empty list' do
|
724
|
+
expect(@p.parse('(list? \'(1 2))')).to eq '#t'
|
725
|
+
expect(@p.parse('(list? (list 1 2))')).to eq '#t'
|
726
|
+
expect(@p.parse('(list? \'(1 #t "str"))')).to eq '#t'
|
727
|
+
end
|
728
|
+
|
729
|
+
it 'returns false if <v> is not list' do
|
730
|
+
expect(@p.parse('(list? #t)')).to eq '#f'
|
731
|
+
expect(@p.parse('(list? 1)')).to eq '#f'
|
732
|
+
expect(@p.parse('(list? \'quote)')).to eq '#f'
|
733
|
+
expect(@p.parse('(list? "string")')).to eq '#f'
|
734
|
+
expect(@p.parse('(list? \'(1 . 2))')).to eq '#f'
|
735
|
+
end
|
736
|
+
end
|
737
|
+
|
738
|
+
describe '(pair? v)' do
|
739
|
+
it 'returns true if <v> is not empty list' do
|
740
|
+
expect(@p.parse('(pair? \'(1 2))')).to eq '#t'
|
741
|
+
expect(@p.parse('(pair? \'(1))')).to eq '#t'
|
742
|
+
expect(@p.parse('(pair? (list 1 2))')).to eq '#t'
|
743
|
+
expect(@p.parse('(pair? \'(1 #t "str"))')).to eq '#t'
|
744
|
+
end
|
745
|
+
|
746
|
+
it 'returns false if <v> is empty list' do
|
747
|
+
expect(@p.parse('(pair? null)')).to eq '#f'
|
748
|
+
expect(@p.parse('(pair? (list))')).to eq '#f'
|
749
|
+
expect(@p.parse('(pair? \'())')).to eq '#f'
|
750
|
+
end
|
751
|
+
|
752
|
+
it 'returns false if <v> is not pair' do
|
753
|
+
expect(@p.parse('(pair? #t)')).to eq '#f'
|
754
|
+
expect(@p.parse('(pair? 1)')).to eq '#f'
|
755
|
+
expect(@p.parse('(pair? \'quote)')).to eq '#f'
|
756
|
+
expect(@p.parse('(pair? "string")')).to eq '#f'
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
describe '(length lst)' do
|
761
|
+
it 'returns 0 if <lst> is the empty list' do
|
762
|
+
expect(@p.parse('(length null)')).to eq 0
|
763
|
+
expect(@p.parse('(length (list))')).to eq 0
|
764
|
+
expect(@p.parse('(length \'())')).to eq 0
|
765
|
+
end
|
766
|
+
|
767
|
+
it 'returns the number of elements in <lst> if <lst> is not empty list' do
|
768
|
+
expect(@p.parse('(length \'(1 2))')).to eq 2
|
769
|
+
expect(@p.parse('(length (list 1 2 3))')).to eq 3
|
770
|
+
expect(@p.parse('(length (cons 1 \'(2 3 4)))')).to eq 4
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
describe '(reverse lst)' do
|
775
|
+
it 'returns <lst> if <lst> is the empty list' do
|
776
|
+
expect(@p.parse('(reverse null)')).to eq '()'
|
777
|
+
expect(@p.parse('(reverse (list))')).to eq '()'
|
778
|
+
expect(@p.parse('(reverse \'())')).to eq '()'
|
779
|
+
end
|
780
|
+
|
781
|
+
it 'returns <lst> backwards if <lst> is not the empty list' do
|
782
|
+
expect(@p.parse('(reverse \'(1 2))')).to eq '(2 1)'
|
783
|
+
expect(@p.parse('(reverse (list 1 2 3))')).to eq '(3 2 1)'
|
784
|
+
expect(@p.parse('(reverse (cons 1 \'(2 3 4)))')).to eq '(4 3 2 1)'
|
785
|
+
expect(@p.parse('(reverse \'(1 \'(2 3 4) 5))')).to eq '(5 (2 3 4) 1)'
|
786
|
+
end
|
787
|
+
end
|
788
|
+
|
789
|
+
describe '(remove v lst)' do
|
790
|
+
it 'returns <lst> if the <v> is not found in <lst>' do
|
791
|
+
expect(@p.parse('(remove 9 (list 1 2 3))')).to eq '(1 2 3)'
|
792
|
+
expect(@p.parse('(remove (list 1 2 3) (cons 1 \'(2 3)))')).to eq '(1 2 3)'
|
793
|
+
expect(@p.parse('(remove #t \'(1 2 3))')).to eq '(1 2 3)'
|
794
|
+
end
|
795
|
+
|
796
|
+
it 'returns <lst> ommiting the first element that is equal to <v>' do
|
797
|
+
expect(@p.parse('(remove 1 (list 1 2 3))')).to eq '(2 3)'
|
798
|
+
expect(@p.parse('(remove \'(1) (list \'(1) 2 3))')).to eq '(2 3)'
|
799
|
+
expect(@p.parse('(remove #t \'(1 2 #t 3))')).to eq '(1 2 3)'
|
800
|
+
expect(@p.parse('(remove 1 (list 1 2 1 3))')).to eq '(2 1 3)'
|
801
|
+
expect(@p.parse('(remove #t \'(#t #t #t))')).to eq '(#t #t)'
|
802
|
+
expect(@p.parse('(remove "str" \'("str"))')).to eq '()'
|
803
|
+
end
|
804
|
+
end
|
805
|
+
|
806
|
+
describe '(shuffle lst)' do
|
807
|
+
it 'returns <lst> if <lst> is the empty list' do
|
808
|
+
expect(@p.parse('(shuffle \'())')).to eq '()'
|
809
|
+
expect(@p.parse('(shuffle (list))')).to eq '()'
|
810
|
+
expect(@p.parse('(shuffle null)')).to eq '()'
|
811
|
+
end
|
812
|
+
|
813
|
+
it 'returns <lst> with randomly shuffled elements' do
|
814
|
+
permuts = [1, 2, 3].permutation(3).to_a
|
815
|
+
permuts = permuts.map { |p| build_lst p }
|
816
|
+
expect(permuts).to include(@p.parse('(shuffle (list 1 2 3))'))
|
817
|
+
expect(@p.parse('(shuffle (list 1 1 1))')).to eq '(1 1 1)'
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
describe '(map proc lst ...+)' do
|
822
|
+
it 'returns <lst> if <lst> is the empty list' do
|
823
|
+
expect(@p.parse('(map + \'())')).to eq '()'
|
824
|
+
expect(@p.parse('(map (lambda ()) null)')).to eq '()'
|
825
|
+
end
|
826
|
+
|
827
|
+
it 'applies <proc> to the elements of the <lst> when <proc> is lambda' do
|
828
|
+
expr1 = '(map (lambda (n)(+ 1 n))\'(1 2 3 4))'
|
829
|
+
expr2 = '(map (lambda (x y)(+ x y))\'(1 2 3 4)\'(10 100 1000 10000))'
|
830
|
+
expr3 = '(map xl \'(1 2 3 4))'
|
831
|
+
expect(@p.parse(expr1)).to eq '(2 3 4 5)'
|
832
|
+
expect(@p.parse(expr2)).to eq '(11 102 1003 10004)'
|
833
|
+
expect(@p.parse(expr3)).to eq '(2 4 6 8)'
|
834
|
+
end
|
835
|
+
|
836
|
+
it 'applies <proc> to the elements of the <lst> when <proc> is function' do
|
837
|
+
expr1 = '(map list \'(1 2 3 4))'
|
838
|
+
expr2 = '(map cons \'(1 2 3 4)\'(1 10 100 1000))'
|
839
|
+
expect(@p.parse(expr1)).to eq '((1) (2) (3) (4))'
|
840
|
+
expect(@p.parse(expr2)).to eq '((1 . 1) (2 . 10) (3 . 100) (4 . 1000))'
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
844
|
+
describe '(foldl proc init lst ...+)' do
|
845
|
+
it 'applies <proc> to <lst> when <proc> is function' do
|
846
|
+
expect(@p.parse('(foldl cons \'() \'(1 2 3 4))')).to eq '(4 3 2 1)'
|
847
|
+
expect(@p.parse('(foldl + 0 \'(1 2 3 4 5))')).to eq '15'
|
848
|
+
end
|
849
|
+
|
850
|
+
it 'applies <proc> to <lst> when <proc> is lambda expression' do
|
851
|
+
expr1 = '(foldl (lambda (a b r)(* r (- a b))) 1 \'(1 2 3) \'(4 5 6))'
|
852
|
+
expr2 = '(foldl (lambda (a b) (+ a b)) 0 \'(1 2 3 4 5))'
|
853
|
+
expect(@p.parse(expr1)).to eq '-27'
|
854
|
+
expect(@p.parse(expr2)).to eq '15'
|
855
|
+
end
|
856
|
+
end
|
857
|
+
|
858
|
+
describe '(foldr proc init lst ...+)' do
|
859
|
+
it 'applies <proc> to <lst> when <proc> is function' do
|
860
|
+
expect(@p.parse('(foldr cons \'() \'(1 2 3 4))')).to eq '(1 2 3 4)'
|
861
|
+
expect(@p.parse('(foldr + 0 \'(1 2 3 4 5))')).to eq 15
|
862
|
+
end
|
863
|
+
|
864
|
+
it 'applies <proc> to <lst> when <proc> is lambda expression' do
|
865
|
+
expr1 = '(foldr (lambda (a b r)(* r (- a b))) 1 \'(1 2 3) \'(4 5 6))'
|
866
|
+
expr2 = '(foldr (lambda (a b) (+ a b)) 0 \'(1 2 3 4 5))'
|
867
|
+
expect(@p.parse(expr1)).to eq(-27)
|
868
|
+
expect(@p.parse(expr2)).to eq 15
|
869
|
+
end
|
870
|
+
end
|
871
|
+
|
872
|
+
describe '(filter pred lst)' do
|
873
|
+
it 'returns the empty list if <pred> is false for all elements of <lst>' do
|
874
|
+
expr1 = '(filter (lambda (x) (< x 3)) \'(3 3 3))'
|
875
|
+
expect(@p.parse(expr1)).to eq '()'
|
876
|
+
end
|
877
|
+
|
878
|
+
it 'returns a <lst> with elements for which <pred> is true' do
|
879
|
+
expr1 = '(filter (lambda (x) (< x 3)) \'(1 3 -4 0 5))'
|
880
|
+
expect(@p.parse(expr1)).to eq '(1 -4 0)'
|
881
|
+
end
|
882
|
+
end
|
883
|
+
|
884
|
+
describe '(member v lst)' do
|
885
|
+
it 'returns false if <v> is not found in <lst>' do
|
886
|
+
expect(@p.parse('(member 9 (list 1 2 3 4))')).to eq '#f'
|
887
|
+
expect(@p.parse('(member 2 (list))')).to eq '#f'
|
888
|
+
expect(@p.parse('(member 2.0 (list 1 2 3 4))')).to eq '#f'
|
889
|
+
end
|
890
|
+
|
891
|
+
it 'returns <lst> with elements after first occurance of <v> in <lst>' do
|
892
|
+
expect(@p.parse('(member 2 (list 1 2 3 4))')).to eq '(2 3 4)'
|
893
|
+
expect(@p.parse('(member \'(1) (list 3 (list 1) 2))')).to eq '((1) 2)'
|
894
|
+
expect(@p.parse('(member 1 (list 1 1 1))')).to eq '(1 1 1)'
|
895
|
+
end
|
896
|
+
end
|
897
|
+
|
898
|
+
describe '(lambda params body ...+)' do
|
899
|
+
it 'returns procedure when there are no <params>' do
|
900
|
+
expect(@p.parse('(lambda ())')).to be_instance_of(Proc)
|
901
|
+
expect(@p.parse('(lambda () 5)')).to be_instance_of(Proc)
|
902
|
+
end
|
903
|
+
|
904
|
+
it 'returns procedure when there are <params>' do
|
905
|
+
expect(@p.parse('(lambda (x))')).to be_an_instance_of(Proc)
|
906
|
+
expect(@p.parse('(lambda (x) x)')).to be_an_instance_of(Proc)
|
907
|
+
end
|
908
|
+
|
909
|
+
it 'calls the procedure by given values for the <params>' do
|
910
|
+
expect(@p.parse('((lambda (x) x) 10)')).to eq '10'
|
911
|
+
expect(@p.parse('((lambda (x y) (* x y)) 10 10)')).to eq 100
|
912
|
+
end
|
913
|
+
end
|
914
|
+
|
915
|
+
describe '(apply proc v ... lst)' do
|
916
|
+
it 'applies <proc> to <lst> when no <v>s are supplied' do
|
917
|
+
expect(@p.parse('(apply + \'(1 2 3))')).to eq 6
|
918
|
+
expect(@p.parse('(apply + \'())')).to eq 0
|
919
|
+
end
|
920
|
+
|
921
|
+
it 'applies <proc> to <lst> when <v>s are supplied' do
|
922
|
+
expect(@p.parse('(apply + 1 2 \'(3))')).to eq 6
|
923
|
+
expr2 = '(apply map list \'(\'(a b c) \'(1 2 3)))'
|
924
|
+
expect(@p.parse(expr2)).to eq '((a 1) (b 2) (c 3))'
|
925
|
+
end
|
926
|
+
|
927
|
+
it 'applies <proc> to <lst> when <proc> is lambda expression' do
|
928
|
+
expr1 = '(apply (lambda (x) (* x x x)) (list 2))'
|
929
|
+
expr2 = '(apply xl (list 5))'
|
930
|
+
expect(@p.parse(expr1)).to eq 8
|
931
|
+
expect(@p.parse(expr2)).to eq 10
|
932
|
+
end
|
933
|
+
end
|
934
|
+
|
935
|
+
describe '(compose proc ...)' do
|
936
|
+
it 'it returns Proc if called with only functions as argument' do
|
937
|
+
@p.parse('(define y (compose xl xl))')
|
938
|
+
expr1 = '(define x (lambda (x y) (compose x y)))'
|
939
|
+
expect(@p.parse('y')).to be_instance_of(Proc)
|
940
|
+
expect(@p.parse('(compose xl xl)')).to be_instance_of(Proc)
|
941
|
+
expect(@p.parse(expr1)).to be_instance_of(Proc)
|
942
|
+
end
|
943
|
+
|
944
|
+
it 'returns value if values are parsed to the proc' do
|
945
|
+
@p.parse('(define y (compose xl xl))')
|
946
|
+
expr1 = '(define x (lambda (x y) (compose x y)))'
|
947
|
+
expect(@p.parse('(y 5)')).to eq 20
|
948
|
+
expect(@p.parse('((compose xl xl) 5)')).to eq 20
|
949
|
+
expect(@p.parse(expr1)).to be_instance_of(Proc)
|
950
|
+
expect(@p.parse('((x xl xl) 5)')).to eq 20
|
951
|
+
end
|
952
|
+
end
|
953
|
+
|
954
|
+
describe 'define' do
|
955
|
+
it 'can define variable' do
|
956
|
+
expect(@p.parse('(define x 5)')).to eq '5'
|
957
|
+
expect(@p.parse('x')).to eq '5'
|
958
|
+
end
|
959
|
+
|
960
|
+
it 'can define function' do
|
961
|
+
expr1 = '(define (prod x y)(* x y))'
|
962
|
+
expect(@p.parse(expr1)).to be_instance_of(Proc)
|
963
|
+
expect(@p.parse('(prod 2 3)')).to eq 6
|
964
|
+
end
|
965
|
+
|
966
|
+
it 'can define lambda' do
|
967
|
+
expr1 = '(define x (lambda (x) (* 2 x)))'
|
968
|
+
expect(@p.parse(expr1)).to be_instance_of(Proc)
|
969
|
+
expect(@p.parse('(x 5)')).to eq 10
|
970
|
+
end
|
971
|
+
end
|
972
|
+
|
973
|
+
describe 'scopes' do
|
974
|
+
it 'uses inner scope variables with the same name as in outer scope' do
|
975
|
+
expr1 = '(define (prod x y) ((lambda (x y) (* x y)) x y))'
|
976
|
+
expect(@p.parse(expr1)).to be_instance_of(Proc)
|
977
|
+
expect(@p.parse('(prod 2 3)')).to eq 6
|
978
|
+
end
|
979
|
+
|
980
|
+
it 'defines variables visible only in the scope of deffinition or lower' do
|
981
|
+
expr1 = '(define prodfive (lambda (x)(define yy 5)(* x yy)))'
|
982
|
+
expect(@p.parse(expr1)).to be_instance_of(Proc)
|
983
|
+
expect(@p.parse('(prodfive 6)')).to eq 30
|
984
|
+
expect(@p.parse('yy')).to eq @msg['inv_type']
|
985
|
+
end
|
986
|
+
end
|
987
|
+
end
|