rus3 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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