rus3 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +18 -0
- data/.gitignore +56 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +21 -0
- data/LICENSE +21 -0
- data/README.md +182 -0
- data/Rakefile +20 -0
- data/bin/console +16 -0
- data/bin/setup +8 -0
- data/examples/fact.scm +10 -0
- data/examples/fib.scm +9 -0
- data/examples/iota.scm +13 -0
- data/exe/rus3 +5 -0
- data/lib/rus3.rb +52 -0
- data/lib/rus3/char.rb +6 -0
- data/lib/rus3/error.rb +127 -0
- data/lib/rus3/evaluator.rb +75 -0
- data/lib/rus3/pair.rb +67 -0
- data/lib/rus3/parser.rb +71 -0
- data/lib/rus3/parser/lexer.rb +119 -0
- data/lib/rus3/parser/scheme_parser.rb +322 -0
- data/lib/rus3/port.rb +6 -0
- data/lib/rus3/printer.rb +28 -0
- data/lib/rus3/procedure/control.rb +35 -0
- data/lib/rus3/procedure/list.rb +289 -0
- data/lib/rus3/procedure/predicate.rb +308 -0
- data/lib/rus3/procedure/write.rb +130 -0
- data/lib/rus3/repl.rb +236 -0
- data/lib/rus3/version.rb +5 -0
- data/rus3.gemspec +34 -0
- metadata +77 -0
@@ -0,0 +1,322 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rus3::Parser
|
4
|
+
|
5
|
+
# A simple parser to read a s-expression for Scheme.
|
6
|
+
class SchemeParser < Parser
|
7
|
+
|
8
|
+
# Indicates the version of the parser class
|
9
|
+
PARSER_VERSION = "0.1.0"
|
10
|
+
|
11
|
+
# Constructs the version string.
|
12
|
+
|
13
|
+
def version
|
14
|
+
vmsg = "(scheme-parser-version . #{PARSER_VERSION})"
|
15
|
+
vmsg += " (scheme-lexer-version . #{Lexer::LEXER_VERSION})"
|
16
|
+
super + " (#{vmsg})"
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
# Set the prompt which includes "scheme" label.
|
24
|
+
|
25
|
+
def prompt=(str)
|
26
|
+
index = str.index(">")
|
27
|
+
@prompt = str[0...index] + "(scheme)" + str[index..-1]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Parses a s-expression then translates it to a expression for Ruby.
|
31
|
+
#
|
32
|
+
# Converts a s-expression (scheme-expression or just S expression)
|
33
|
+
# to an i-expression (intermediate-expression), then translate an
|
34
|
+
# i-expression into a r-expression (ruby-expression). An
|
35
|
+
# i-expression is represented with an Array object in Ruby. A
|
36
|
+
# r-expression is a String object which could be directly
|
37
|
+
# evaluated by `Kernel.eval`.
|
38
|
+
#
|
39
|
+
# Supported S-expression type:
|
40
|
+
#
|
41
|
+
# - primitive expression (s_exp -> r_exp)
|
42
|
+
# - empty list
|
43
|
+
# - "()" -> "[]"
|
44
|
+
#
|
45
|
+
# - variable reference
|
46
|
+
# - identifier (symbol) -> "foo"
|
47
|
+
#
|
48
|
+
# - literal expression
|
49
|
+
# - boolean: #f or #t -> `false` or `true`
|
50
|
+
# - string -> "hoge", "bogo", ...
|
51
|
+
# - number -> 1.23, Rational(4, 5), Complex(6, 7), ...
|
52
|
+
#
|
53
|
+
# - procedure call (s_exp -> i_exp -> r_exp)
|
54
|
+
# - (+ (* 3 4) (/ 5 6)) ... s_exp
|
55
|
+
# -> ["add", ["mult", "3", "4"],
|
56
|
+
# ["div", "5", "6"]] ... i_exp
|
57
|
+
# -> "add(mul(3, 4), div(5, 6))" ... r_exp
|
58
|
+
#
|
59
|
+
# - ((lambda (x) (+ x x)) 4) ... s_exp
|
60
|
+
# -> [["lambda", ["x"], ["add", "x", "x"]], 4] ... i_exp
|
61
|
+
# -> "lambda { |x| add(x, x) }.call(4)" ... r_exp
|
62
|
+
#
|
63
|
+
# - procedure (s_exp -> i_exp -> r_exp)
|
64
|
+
# - (lambda (x) (+ x x)) ... s_exp
|
65
|
+
# -> ["lambda", ["x"], ["add", "x", "x"]] ... i_exp
|
66
|
+
# -> "lambda { |x| add(x, x) }" ... r_exp
|
67
|
+
#
|
68
|
+
# - conditionals (s_exp -> i_exp -> r_exp)
|
69
|
+
# - (if (= n 0) 1 (* n (- n 1))) ... s_exp
|
70
|
+
# -> ["if", ["eqv?", "n", "0"],
|
71
|
+
# "1" ["mul", "n", ["subtract", "n", "1"]]] ... i_exp
|
72
|
+
# -> "if eqv?(n, 0);
|
73
|
+
# 1;
|
74
|
+
# else;
|
75
|
+
# mul(n, subtract(n, 1));
|
76
|
+
# end" ... r_exp
|
77
|
+
#
|
78
|
+
# - assignment (s_exp -> i_exp -> r_exp)
|
79
|
+
# - (set! x 2) ... s_exp
|
80
|
+
# -> ["set!", "x", "2"] ... i_exp
|
81
|
+
# -> "x = 2" ... r_exp
|
82
|
+
#
|
83
|
+
# - define procedure (s_exp -> i_exp -> r_exp)
|
84
|
+
# - (define (fact n) (if (= n 0) 1 (* n (fact (- n 1))))) ... s_exp
|
85
|
+
# -> ["define", ["fact", "n"],
|
86
|
+
# ["if", ["eqv?", "n", "0"], "1",
|
87
|
+
# ["mul", "n", ["fact", ["subtract", "n", "1"]]]]] ... i_exp
|
88
|
+
# -> "def fact(n);
|
89
|
+
# if n == 0;
|
90
|
+
# 1;
|
91
|
+
# else;
|
92
|
+
# n * fact((n - 1));
|
93
|
+
# end" ... r_exp
|
94
|
+
#
|
95
|
+
# - derived expression
|
96
|
+
# - conditionals (s_exp -> i_exp -> r_exp)
|
97
|
+
# - (cond ((> 3 2) "greater")
|
98
|
+
# ((< 3 2) "less")
|
99
|
+
# (else "equal")) ... s_exp
|
100
|
+
# -> ["cond", [[["gt?", "3", "2"], "\"greater\""],
|
101
|
+
# [["lt?", "3", "2"], "\"less\""],
|
102
|
+
# ["else", "\"equal\""]]] ... i_exp
|
103
|
+
# -> "if gt?(3,2);
|
104
|
+
# 'greater';
|
105
|
+
# elsif lt?(3,2);
|
106
|
+
# 'less';
|
107
|
+
# else;
|
108
|
+
# 'equal';
|
109
|
+
# end" ... r_exp
|
110
|
+
#
|
111
|
+
# - building construct (s_exp -> i_exp -> r_exp)
|
112
|
+
# - (let ((x 2) (y 3))
|
113
|
+
# (* x y)) ... s_exp
|
114
|
+
# -> ["let", [["x", "2"], ["y", "3"]],
|
115
|
+
# ["mul", "x", "y"]] ... i_exp
|
116
|
+
# -> "lambda { |x, y| mul(x, y) }.call(2, 3)" ... r_exp
|
117
|
+
#
|
118
|
+
# - list (s_exp -> r_exp)
|
119
|
+
# - (1 2 3 (4 5) (6 7 8) 9 0)
|
120
|
+
# -> "[1, 2, 3, [4, 5], [6, 7, 8], 9, 0]"
|
121
|
+
|
122
|
+
def parse(s_exp)
|
123
|
+
parse_tokens(Lexer.new(s_exp))
|
124
|
+
end
|
125
|
+
|
126
|
+
def parse_tokens(lexer) # :nodoc:
|
127
|
+
r_exps = []
|
128
|
+
loop {
|
129
|
+
token = lexer.next
|
130
|
+
if token.type == :lparen
|
131
|
+
i_exp = parse_compound(lexer)
|
132
|
+
r_exps << translate(i_exp)
|
133
|
+
else
|
134
|
+
r_exps << parse_primitive(token)
|
135
|
+
end
|
136
|
+
}
|
137
|
+
r_exps.join("\n")
|
138
|
+
end
|
139
|
+
|
140
|
+
def parse_primitive(token)
|
141
|
+
r_exp = nil
|
142
|
+
case token.type
|
143
|
+
when *Lexer::KEYWORDS.values
|
144
|
+
r_exp = translate_ident(token.literal)
|
145
|
+
when :string
|
146
|
+
r_exp = token.literal
|
147
|
+
when :ident, :boolean, :number, :op_proc
|
148
|
+
trans_method_name = "translate_#{token.type}".intern
|
149
|
+
r_exp = self.send(trans_method_name, token.literal)
|
150
|
+
else
|
151
|
+
raise Rus3::SchemeSyntaxError, token.literal
|
152
|
+
end
|
153
|
+
r_exp
|
154
|
+
end
|
155
|
+
|
156
|
+
def parse_compound(lexer)
|
157
|
+
i_exp = []
|
158
|
+
Kernel.loop {
|
159
|
+
token = lexer.next
|
160
|
+
case token.type
|
161
|
+
when :lparen
|
162
|
+
i_exp << parse_compound(lexer)
|
163
|
+
when :rparen
|
164
|
+
break
|
165
|
+
else
|
166
|
+
i_exp << parse_primitive(token)
|
167
|
+
end
|
168
|
+
}
|
169
|
+
i_exp
|
170
|
+
end
|
171
|
+
|
172
|
+
def translate_ident(s_exp_literal)
|
173
|
+
"#{s_exp_literal}"
|
174
|
+
end
|
175
|
+
|
176
|
+
def translate_boolean(s_exp_literal)
|
177
|
+
# literal == "#f" or #t"
|
178
|
+
(s_exp_literal[1] == "f") ? "false" : "true"
|
179
|
+
end
|
180
|
+
|
181
|
+
def translate_number(s_exp_literal)
|
182
|
+
if s_exp_literal.include?("/") # rational?
|
183
|
+
denominator, numerator = s_exp_literal.split("/").map{|s| Kernel.eval(s)}
|
184
|
+
"Rational(#{denominator}, #{numerator})"
|
185
|
+
else
|
186
|
+
Kernel.eval(s_exp_literal).to_s
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
OP_PROCS = {
|
191
|
+
"+" => "add",
|
192
|
+
"-" => "subtract",
|
193
|
+
"*" => "mul",
|
194
|
+
"/" => "div",
|
195
|
+
"%" => "mod",
|
196
|
+
"<" => "lt?",
|
197
|
+
"<=" => "le?",
|
198
|
+
">" => "gt?",
|
199
|
+
">=" => "ge?",
|
200
|
+
"==" => "eqv?",
|
201
|
+
}
|
202
|
+
|
203
|
+
def translate_op_proc(s_exp_literal)
|
204
|
+
OP_PROCS[s_exp_literal]
|
205
|
+
end
|
206
|
+
|
207
|
+
def translate(i_exp)
|
208
|
+
r_exp = nil
|
209
|
+
|
210
|
+
if i_exp.instance_of?(Array)
|
211
|
+
return "[]" if i_exp.empty? # an empty list
|
212
|
+
|
213
|
+
case i_exp[0]
|
214
|
+
when "lambda", "if", "set!", "define", "cond", "let"
|
215
|
+
keyword = i_exp[0]
|
216
|
+
trans_method_name = "translate_#{keyword}".intern
|
217
|
+
r_exp = self.send(trans_method_name, i_exp)
|
218
|
+
else # procedure call
|
219
|
+
r_exp = translate_proc_call(i_exp)
|
220
|
+
end
|
221
|
+
else
|
222
|
+
r_exp = i_exp
|
223
|
+
end
|
224
|
+
r_exp
|
225
|
+
end
|
226
|
+
|
227
|
+
def translate_proc_call(i_exp)
|
228
|
+
proc = i_exp[0]
|
229
|
+
|
230
|
+
if proc.instance_of?(Array)
|
231
|
+
raise Rus3::SchemeSyntaxError, i_exp if i_exp[0][0] != "lambda"
|
232
|
+
lambda_proc = translate_lambda(proc)
|
233
|
+
proc = "#{lambda_proc}.call"
|
234
|
+
end
|
235
|
+
|
236
|
+
args = i_exp[1..-1].map {|e| translate(e) }
|
237
|
+
|
238
|
+
"#{proc}(#{args.join(', ')})"
|
239
|
+
end
|
240
|
+
|
241
|
+
def translate_lambda(i_exp)
|
242
|
+
formals = i_exp[1]
|
243
|
+
body = i_exp[2..-1]
|
244
|
+
|
245
|
+
if body.instance_of?(Array)
|
246
|
+
body = translate_body(body)
|
247
|
+
end
|
248
|
+
|
249
|
+
"lambda {|#{formals.join(', ')}| #{body}}"
|
250
|
+
end
|
251
|
+
|
252
|
+
def translate_if(i_exp)
|
253
|
+
test = translate(i_exp[1])
|
254
|
+
consequent = translate(i_exp[2])
|
255
|
+
alternate = translate(i_exp[3])
|
256
|
+
|
257
|
+
if_exp = "if #{test}; #{consequent}"
|
258
|
+
if_exp += "; else; #{alternate}" if alternate
|
259
|
+
if_exp += "; end"
|
260
|
+
|
261
|
+
if_exp
|
262
|
+
end
|
263
|
+
|
264
|
+
def translate_set!(i_exp)
|
265
|
+
ident = i_exp[1]
|
266
|
+
value = translate(i_exp[2])
|
267
|
+
"#{ident} = #{value}"
|
268
|
+
end
|
269
|
+
|
270
|
+
def translate_define(i_exp)
|
271
|
+
if i_exp[1].instance_of?(Array)
|
272
|
+
name = i_exp[1][0]
|
273
|
+
params = i_exp[1][1..-1]
|
274
|
+
body = translate_body(i_exp[2..-1])
|
275
|
+
|
276
|
+
"def #{name}(#{params.join(', ')}); #{body}; end"
|
277
|
+
else
|
278
|
+
ident = i_exp[1]
|
279
|
+
value = translate(i_exp[2])
|
280
|
+
"#{ident} = #{value}"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def translate_cond(i_exp)
|
285
|
+
test = translate(i_exp[1][0])
|
286
|
+
exp = translate(i_exp[1][1])
|
287
|
+
r_exp = "if #{test}; #{exp}"
|
288
|
+
|
289
|
+
i_exp[2..-1].each { |clause|
|
290
|
+
exp = translate(clause[1])
|
291
|
+
if clause[0] == "else"
|
292
|
+
r_exp += "; else; #{exp}"
|
293
|
+
else
|
294
|
+
test = translate(clause[0])
|
295
|
+
r_exp += "; elsif #{test}; #{exp}"
|
296
|
+
end
|
297
|
+
}
|
298
|
+
r_exp += "; end"
|
299
|
+
|
300
|
+
r_exp
|
301
|
+
end
|
302
|
+
|
303
|
+
def translate_let(i_exp)
|
304
|
+
bindings = i_exp[1].to_h
|
305
|
+
body = translate(i_exp[2])
|
306
|
+
|
307
|
+
params = bindings.keys.join(", ")
|
308
|
+
args = bindings.values.map{|e| translate(e)}.join(", ")
|
309
|
+
|
310
|
+
"lambda {|#{params}| #{body}}.call(#{args})"
|
311
|
+
end
|
312
|
+
|
313
|
+
def translate_body(i_exps)
|
314
|
+
r_exps = []
|
315
|
+
i_exps.map { |i_exp|
|
316
|
+
r_exps << translate(i_exp)
|
317
|
+
}
|
318
|
+
r_exps.join(";")
|
319
|
+
end
|
320
|
+
|
321
|
+
end
|
322
|
+
end
|
data/lib/rus3/port.rb
ADDED
data/lib/rus3/printer.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rus3
|
4
|
+
module Printer
|
5
|
+
|
6
|
+
# Indicates the version of the printer module.
|
7
|
+
VERSION = "0.1.0"
|
8
|
+
|
9
|
+
class Printer
|
10
|
+
include Rus3::Procedure::Write
|
11
|
+
|
12
|
+
attr_accessor :verbose
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@verbose = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def print(obj)
|
19
|
+
display(obj)
|
20
|
+
end
|
21
|
+
|
22
|
+
def version
|
23
|
+
"Printer version #{VERSION}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rus3::Procedure
|
4
|
+
module Control
|
5
|
+
|
6
|
+
include Predicate
|
7
|
+
include Rus3::EmptyList
|
8
|
+
|
9
|
+
def map(prc, *lists)
|
10
|
+
case lists.size
|
11
|
+
when 0
|
12
|
+
EMPTY_LIST
|
13
|
+
when 1
|
14
|
+
raise Rus3::ListRequiredError, lists[0] unless list?(lists[0])
|
15
|
+
lists[0].map(&prc)
|
16
|
+
else
|
17
|
+
zip(*lists).map {|args|
|
18
|
+
prc.call(*args)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def zip(*lists)
|
24
|
+
case lists.size
|
25
|
+
when 0
|
26
|
+
EMPTY_LIST
|
27
|
+
when 1
|
28
|
+
lists[0]
|
29
|
+
else
|
30
|
+
lists[0].zip(*lists[1..-1])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,289 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rus3::Procedure
|
4
|
+
|
5
|
+
# The module holds list operation proceduress of Scheme. Most of
|
6
|
+
# those procedures are defined in R(n)RS (the specification of
|
7
|
+
# Scheme language).
|
8
|
+
#
|
9
|
+
# - R5RS 6.3.2 Pairs and lists
|
10
|
+
# - R7RS 6.4 Pairs and lists
|
11
|
+
|
12
|
+
module List
|
13
|
+
|
14
|
+
include Predicate
|
15
|
+
include Rus3::EmptyList
|
16
|
+
|
17
|
+
# Constructs a Pair object with arguments.
|
18
|
+
#
|
19
|
+
# - R5RS procedure: (cons obj1 obj2)
|
20
|
+
|
21
|
+
def cons(obj1, obj2)
|
22
|
+
case obj2
|
23
|
+
when Rus3::Pair
|
24
|
+
if null?(obj2.cdr) # (foo . ())
|
25
|
+
[obj1, obj2.car]
|
26
|
+
else
|
27
|
+
Rus3::Pair.new(obj1, obj2)
|
28
|
+
end
|
29
|
+
when Array
|
30
|
+
[obj1].concat(obj2)
|
31
|
+
else
|
32
|
+
Rus3::Pair.new(obj1, obj2)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the CAR part of the argument. If the arguemnt is not
|
37
|
+
# a pair, raises PairRequiredError.
|
38
|
+
#
|
39
|
+
# - R5RS procedure: (car pair)
|
40
|
+
|
41
|
+
def car(pair)
|
42
|
+
case pair
|
43
|
+
when Rus3::Pair
|
44
|
+
pair.car
|
45
|
+
when Array
|
46
|
+
raise Rus3::PairOrListRequiredError, pair if pair.empty?
|
47
|
+
pair[0]
|
48
|
+
else
|
49
|
+
raise Rus3::PairOrListRequiredError, pair
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the CDR part of the argument. If the arguemnt is not
|
54
|
+
# a pair, raises PairRequiredError.
|
55
|
+
#
|
56
|
+
# - R5RS procedure: (cdr pair)
|
57
|
+
|
58
|
+
def cdr(pair)
|
59
|
+
case pair
|
60
|
+
when Rus3::Pair
|
61
|
+
pair.cdr
|
62
|
+
when Array
|
63
|
+
raise Rus3::PairOrListRequiredError, pair if pair.empty?
|
64
|
+
pair[1..-1]
|
65
|
+
else
|
66
|
+
raise Rus3::PairOrListRequiredError, pair
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Replaces the CAR part with the 2nd argument and returns UNDEF.
|
71
|
+
# If the 1st arguemnt is not a pair, raises PairRequiredError.
|
72
|
+
#
|
73
|
+
# - R5RS procedure: (set-car! pair obj)
|
74
|
+
|
75
|
+
def set_car!(pair, obj)
|
76
|
+
case pair
|
77
|
+
when Rus3::Pair
|
78
|
+
pair.set_car!(obj)
|
79
|
+
when Array
|
80
|
+
pair[0] = obj
|
81
|
+
else
|
82
|
+
raise Rus3::PairOrListRequiredError, pair
|
83
|
+
end
|
84
|
+
Rus3::UNDEF
|
85
|
+
end
|
86
|
+
|
87
|
+
# Replaces the CDR part with the 2nd argument and returns UNDEF.
|
88
|
+
# If the 1st arguemnt is not a pair, raises PairRequiredError.
|
89
|
+
#
|
90
|
+
# - R5RS procedure: (set-cdr! pair obj)
|
91
|
+
|
92
|
+
def set_cdr!(pair, obj)
|
93
|
+
case pair
|
94
|
+
when Rus3::Pair
|
95
|
+
pair.set_cdr!(obj)
|
96
|
+
when Array
|
97
|
+
case obj
|
98
|
+
when Array
|
99
|
+
pair.slice!(1, pair.size - 1)
|
100
|
+
pair.concat(obj)
|
101
|
+
else
|
102
|
+
# If `obj` was not a proper list of Scheme, the result of
|
103
|
+
# `set_cdr!` would be a Pair instance. However, in this
|
104
|
+
# case, the given `pair` is an Array instance, there is no
|
105
|
+
# way to replace it with a new Pair instance.
|
106
|
+
raise UnsupportedFeatureError.new("set_cdr!", obj)
|
107
|
+
end
|
108
|
+
else
|
109
|
+
raise Rus3::PairOrListRequiredError, pair
|
110
|
+
end
|
111
|
+
Rus3::UNDEF
|
112
|
+
end
|
113
|
+
|
114
|
+
# Retrieves the CAR part of the CAR part of the given pair.
|
115
|
+
#
|
116
|
+
# - R5RS library procedure: (caar pair)
|
117
|
+
# - R7RS procedure: (caar pair)
|
118
|
+
|
119
|
+
def caar(pair)
|
120
|
+
car(car(pair))
|
121
|
+
end
|
122
|
+
|
123
|
+
# Retrieves the CAR part of the CDR part of the given pair.
|
124
|
+
#
|
125
|
+
# - R5RS library procedure: (cadr pair)
|
126
|
+
# - R7RS procedure: (cadr pair)
|
127
|
+
|
128
|
+
def cadr(pair)
|
129
|
+
car(cdr(pair))
|
130
|
+
end
|
131
|
+
|
132
|
+
# Retrieves the CDR part of the CAR part of the given pair.
|
133
|
+
#
|
134
|
+
# - R5RS library procedure: (cdar pair)
|
135
|
+
# - R7RS procedure: (cdar pair)
|
136
|
+
|
137
|
+
def cdar(pair)
|
138
|
+
cdr(car(pair))
|
139
|
+
end
|
140
|
+
|
141
|
+
# Retrieves the CDR part of the CDR part of the given pair.
|
142
|
+
#
|
143
|
+
# - R5RS library procedure: (cddr pair)
|
144
|
+
# - R7RS procedure: (cddr pair)
|
145
|
+
|
146
|
+
def cddr(pair)
|
147
|
+
cdr(cdr(pair))
|
148
|
+
end
|
149
|
+
|
150
|
+
# :stopdoc:
|
151
|
+
|
152
|
+
# - R7RS: procedure: (make-list k)
|
153
|
+
|
154
|
+
# - R7RS: procedure: (make-list k fill)
|
155
|
+
|
156
|
+
# :startdoc:
|
157
|
+
|
158
|
+
# Constructs a list from arguments in its order.
|
159
|
+
#
|
160
|
+
# - R5RS library procedure: (list obj ...)
|
161
|
+
|
162
|
+
def list(*objs)
|
163
|
+
Array[*objs]
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns the length of the arguemnt. If the argument is not a
|
167
|
+
# proper list, raises ListRequiredError.
|
168
|
+
#
|
169
|
+
# - R5RS library procedure: (length list)
|
170
|
+
# - R7RS procedure: (length list)
|
171
|
+
|
172
|
+
def length(lst)
|
173
|
+
check_list(lst)
|
174
|
+
lst.size
|
175
|
+
end
|
176
|
+
|
177
|
+
# Concatenates given lists into a single list. Each argument
|
178
|
+
# must be a proper list, otherwise raises ListRequiredError.
|
179
|
+
#
|
180
|
+
# - R5RS library procedure: (append list ...)
|
181
|
+
# - R7RS procedure: (append list ...)
|
182
|
+
|
183
|
+
def append(*lists)
|
184
|
+
lists.each { |lst|
|
185
|
+
check_list(lst)
|
186
|
+
}
|
187
|
+
[].concat(*lists)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns a list of the same elements in reverse order.
|
191
|
+
#
|
192
|
+
# - R5RS library procedure: (reverse list)
|
193
|
+
# - R7RS procedure: (reverse list)
|
194
|
+
|
195
|
+
def reverse(lst)
|
196
|
+
check_list(lst)
|
197
|
+
lst.sort {|a, b| b <=> a}
|
198
|
+
end
|
199
|
+
|
200
|
+
# Returns the sublist of the arguemnt by omitting the first k
|
201
|
+
# elements. The 2nd argument, k must be in 0..length(lst),
|
202
|
+
# otherwise raises OutOfRangeError.
|
203
|
+
#
|
204
|
+
# This implementation logic comes from R5RS 6.3.2.
|
205
|
+
#
|
206
|
+
# - R5RS library procedure: (list-tail list k)
|
207
|
+
# - R7RS procedure: (list-tail list k)
|
208
|
+
|
209
|
+
def list_tail(lst, k)
|
210
|
+
check_list(lst)
|
211
|
+
check_upper_limit(k, length(lst)+1)
|
212
|
+
|
213
|
+
lst.drop(k)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Returns kth element of the argument. k must be less than the
|
217
|
+
# length of the list, otherwise, raises OutOfRangeError.
|
218
|
+
#
|
219
|
+
# - R5RS library procedure: (list-ref list k)
|
220
|
+
# - R7RS procedure: (list-ref list k)
|
221
|
+
|
222
|
+
def list_ref(lst, k)
|
223
|
+
check_list(lst)
|
224
|
+
check_upper_limit(k, length(lst))
|
225
|
+
|
226
|
+
lst[k]
|
227
|
+
end
|
228
|
+
|
229
|
+
# :stopdoc:
|
230
|
+
|
231
|
+
# - R7RS procedure: (list-set! list k obj)
|
232
|
+
|
233
|
+
# :startdoc:
|
234
|
+
|
235
|
+
# :stopdpc:
|
236
|
+
|
237
|
+
# - R5RS library procedure: (memq obj list)
|
238
|
+
# - R7RS procedure: (memq obj list)
|
239
|
+
|
240
|
+
# - R5RS library procedure: (memv obj list)
|
241
|
+
# - R7RS procedure: (memv obj list)
|
242
|
+
|
243
|
+
# - R5RS library procedure: (member obj list)
|
244
|
+
# - R7RS procedure: (member obj list)
|
245
|
+
|
246
|
+
# - R7RS procedure: (member obj list compare)
|
247
|
+
|
248
|
+
# :startdoc:
|
249
|
+
|
250
|
+
# :stopdpc:
|
251
|
+
|
252
|
+
# - R5RS library procedure: (assq obj alist)
|
253
|
+
# - R7RS procedure: (assq obj alist)
|
254
|
+
|
255
|
+
# - R5RS library procedure: (assv obj alist)
|
256
|
+
# - R7RS procedure: (assv obj alist)
|
257
|
+
|
258
|
+
# - R5RS library procedure: (assoc obj alist)
|
259
|
+
# - R7RS procedure: (assoc obj alist)
|
260
|
+
|
261
|
+
# - R7RS procedure: (assoc obj alist compare)
|
262
|
+
|
263
|
+
# :startdoc:
|
264
|
+
|
265
|
+
# :stopdoc:
|
266
|
+
|
267
|
+
# - R7RS procedure: (list-copy obj)
|
268
|
+
|
269
|
+
# :startdoc:
|
270
|
+
|
271
|
+
private
|
272
|
+
|
273
|
+
def check_pair(pair) # :nodoc:
|
274
|
+
if !pair.instance_of?(Rus3::Pair) and !pair.instance_of?(Array)
|
275
|
+
raise Rus3::PairRequiredError, pair
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def check_list(lst) # :nodoc:
|
280
|
+
raise Rus3::ListRequiredError, lst unless list?(lst)
|
281
|
+
end
|
282
|
+
|
283
|
+
# To make sure the number is less than its upper limit.
|
284
|
+
def check_upper_limit(k, limit) # :nodoc:
|
285
|
+
raise Rus3::OutOfRangeError, k if k >= limit
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
end
|