rus3 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/.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
|