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.
@@ -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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rus3
4
+ class Port
5
+ end
6
+ end
@@ -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