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,308 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rus3::Procedure
4
+
5
+ module Predicate
6
+
7
+ include Rus3::EmptyList
8
+
9
+ # Returns true if the arguemnt represents a list structure.
10
+ # Note that an empty list is a list.
11
+ def list?(obj)
12
+ obj.instance_of?(Array)
13
+ end
14
+
15
+ # :stopdoc:
16
+
17
+ # Equivalence predicates:
18
+ #
19
+ # In R5RS, three equivalence predicates are defined such as eqv?,
20
+ # eq? and equal?.
21
+ #
22
+ # `equal?` has been defined in Object class and the reference
23
+ # manual says that this method must not re-defined. So,
24
+ # `equal?` is not defined here.
25
+
26
+ # :startdoc:
27
+
28
+ def eqv?(obj1, obj2)
29
+ obj1 == obj2
30
+ end
31
+
32
+ def eq?(obj1, obj2)
33
+ obj1.equal?(obj2)
34
+ end
35
+
36
+ # :stopdoc:
37
+
38
+ # Value types:
39
+ #
40
+ # R5RS says "no objects satiscies more than one of the following
41
+ # predicates". That is, Scheme has 9 value types at least.
42
+ #
43
+ # Most of them have suitable types in Ruby built-in classes. Rus3
44
+ # provides some classes for the rest of them.
45
+ #
46
+ # boolean? ---> FalseClass or TrueClass
47
+ # pair? ------> Rus3::Pair
48
+ # symbol? ----> Symbol
49
+ # number? ----> Numeric
50
+ # char? ------> Rus3::Char
51
+ # string? ----> String
52
+ # vector? ----> Array
53
+ # port? ------> Rus3::Port
54
+ # procedure? -> Proc
55
+
56
+ # :startdoc:
57
+
58
+ def boolean?(obj)
59
+ obj.instance_of?(FalseClass) or obj.instance_of?(TrueClass)
60
+ end
61
+
62
+ def pair?(obj)
63
+ obj.instance_of?(Array) or obj.instance_of?(Rus3::Pair)
64
+ end
65
+
66
+ def symbol?(obj)
67
+ obj.is_a?(Symbol) && obj != Rus3::UNDEF
68
+ end
69
+
70
+ def number?(obj)
71
+ obj.is_a?(Numeric)
72
+ end
73
+
74
+ def char?(obj)
75
+ false
76
+ end
77
+
78
+ def string?(obj)
79
+ obj.kind_of?(String)
80
+ end
81
+
82
+ def vector?(obj)
83
+ false
84
+ end
85
+
86
+ def port?(obj)
87
+ false
88
+ end
89
+
90
+ def procedure?(obj)
91
+ obj.instance_of?(Proc)
92
+ end
93
+
94
+ # :startdoc:
95
+
96
+ # :stopdoc:
97
+
98
+ # Numeric types:
99
+ #
100
+ # Scheme has more predicates for number values.
101
+ #
102
+ # complex
103
+ # real
104
+ # rational
105
+ # integer
106
+ #
107
+ # R5RS says, "Mathematically, numbers may be arranged into a tower
108
+ # of subtypes in which each level is a subset of the level above
109
+ # it:"
110
+ #
111
+ # That is, {integer} < {rational} < {real} < {complex}.
112
+
113
+ # :startdoc:
114
+
115
+ def complex?(num)
116
+ num.is_a?(Complex) || real?(num)
117
+ end
118
+
119
+ def real?(num)
120
+ num.is_a?(Float) || rational?(num)
121
+ end
122
+
123
+ def rational?(num)
124
+ num.is_a?(Rational) || integer?(num)
125
+ end
126
+
127
+ def integer?(num)
128
+ num.is_a?(Integer)
129
+ end
130
+
131
+ # :stopdoc:
132
+
133
+ # Tests a number for a particular property.
134
+
135
+ # :startdoc:
136
+
137
+ def zero?(z)
138
+ raise Rus3::NumberRequiredError, z unless number?(z)
139
+ z.zero?
140
+ end
141
+
142
+ def positive?(r)
143
+ raise Rus3::RealNumberRequiredError, r unless real?(r)
144
+ r.positive?
145
+ end
146
+
147
+ def negative?(r)
148
+ raise Rus3::RealNumberRequiredError, r unless real?(r)
149
+ r.negative?
150
+ end
151
+
152
+ def odd?(n)
153
+ raise Rus3::IntegerRequiredError, n unless integer?(n)
154
+ n.odd?
155
+ end
156
+
157
+ def even?(n)
158
+ raise Rus3::IntegerRequiredError, n unless integer?(n)
159
+ n.even?
160
+ end
161
+
162
+ # :stopdoc:
163
+
164
+ # Characters:
165
+ #
166
+ # ...
167
+
168
+ # :startdoc:
169
+
170
+ def char_eq?(char1, char2)
171
+ false
172
+ end
173
+
174
+ def char_lt?(char1, char2)
175
+ false
176
+ end
177
+
178
+ def char_gt?(char1, char2)
179
+ false
180
+ end
181
+
182
+ def char_le?(char1, char2)
183
+ false
184
+ end
185
+
186
+ def char_ge?(char1, char2)
187
+ false
188
+ end
189
+
190
+ def char_ci_eq?(char1, char2)
191
+ false
192
+ end
193
+
194
+ def char_ci_lt?(char1, char2)
195
+ false
196
+ end
197
+
198
+ def char_ci_gt?(char1, char2)
199
+ false
200
+ end
201
+
202
+ def char_ci_le?(char1, char2)
203
+ false
204
+ end
205
+
206
+ def char_ci_ge?(char1, char2)
207
+ false
208
+ end
209
+
210
+ def char_alphabetic?(char)
211
+ false
212
+ end
213
+
214
+ def char_numeric?(char)
215
+ false
216
+ end
217
+
218
+ def char_whitespace?(char)
219
+ false
220
+ end
221
+
222
+ def char_upper_case?(letter)
223
+ false
224
+ end
225
+
226
+ def char_lower_case?(letter)
227
+ false
228
+ end
229
+
230
+ # :stopdoc:
231
+
232
+ # Strings:
233
+
234
+ # :startdoc:
235
+
236
+ def check_string(*objs)
237
+ objs.each { |obj|
238
+ raise Rus3::StringRequiredError, obj unless string?(obj)
239
+ }
240
+ end
241
+ private :check_string
242
+
243
+ def string_eq?(str1, str2)
244
+ check_string(str1, str2)
245
+ str1 == str2
246
+ end
247
+
248
+ def string_ci_eq?(str1, str2)
249
+ check_string(str1, str2)
250
+ str1.downcase == str2.downcase
251
+ end
252
+
253
+ def string_lt?(str1, str2)
254
+ check_string(str1, str2)
255
+ str1 < str2
256
+ end
257
+
258
+ def string_gt?(str1, str2)
259
+ check_string(str1, str2)
260
+ str1 > str2
261
+ end
262
+
263
+ def string_le?(str1, str2)
264
+ check_string(str1, str2)
265
+ str1 <= str2
266
+ end
267
+
268
+ def string_ge?(str1, str2)
269
+ check_string(str1, str2)
270
+ str1 >= str2
271
+ end
272
+
273
+ def string_ci_lt?(str1, str2)
274
+ check_string(str1, str2)
275
+ str1.downcase < str2.downcase
276
+ end
277
+
278
+ def string_ci_gt?(str1, str2)
279
+ check_string(str1, str2)
280
+ str1.downcase > str2.downcase
281
+ end
282
+
283
+ def string_ci_le?(str1, str2)
284
+ check_string(str1, str2)
285
+ str1.downcase <= str2.downcase
286
+ end
287
+
288
+ def string_ci_ge?(str1, str2)
289
+ check_string(str1, str2)
290
+ str1.downcase >= str2.downcase
291
+ end
292
+
293
+ # :stopdoc:
294
+
295
+ # Ports:
296
+
297
+ # :startdoc:
298
+
299
+ def input_port?(obj)
300
+ false
301
+ end
302
+
303
+ def output_port?(obj)
304
+ false
305
+ end
306
+
307
+ end
308
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rus3::Procedure
4
+ module Write
5
+
6
+ include Predicate
7
+ include Rus3::EmptyList
8
+
9
+ def write(obj)
10
+ print any_to_string(obj)
11
+ Rus3::UNDEF
12
+ end
13
+
14
+ def display(obj)
15
+ puts any_to_string(obj)
16
+ Rus3::UNDEF
17
+ end
18
+
19
+ # :stopdoc:
20
+
21
+ private
22
+
23
+ TYPES = [
24
+ :null, # empty list
25
+ :boolean, # #f or #t
26
+ :pair, # (foo . bar)
27
+ :symbol,
28
+ :number,
29
+ :char,
30
+ :string,
31
+ :vector,
32
+ :port,
33
+ :procedure,
34
+ ]
35
+
36
+ def private_method(name)
37
+ m = nil
38
+ if self.class.private_method_defined?(name)
39
+ um = self.class.instance_method(name)
40
+ m = um.bind(self)
41
+ end
42
+ m
43
+ end
44
+
45
+ def type(obj)
46
+ obj_type = nil
47
+ TYPES.each { |type|
48
+ predicate_name = "#{type}?".intern
49
+ next unless respond_to?(predicate_name)
50
+
51
+ predicate_prc = method(predicate_name)
52
+ if predicate_prc.call(obj)
53
+ obj_type = type
54
+ break
55
+ end
56
+ }
57
+ obj_type || :undef
58
+ end
59
+
60
+ def any_to_string(obj)
61
+ prc_name = "#{type(obj)}_to_string".intern
62
+ prc = private_method(prc_name)
63
+ prc ? prc.call(obj) : obj.to_s
64
+ end
65
+
66
+ def null_to_string(obj)
67
+ "()"
68
+ end
69
+
70
+ def boolean_to_string(obj)
71
+ obj ? "#t" : "#f"
72
+ end
73
+
74
+ def pair_to_string(obj)
75
+ if null?(obj)
76
+ # ()
77
+ null_to_string(obj)
78
+ elsif list?(obj)
79
+ # (1 2 3 4)
80
+ result = obj.map {|e| any_to_string(e) }
81
+ "(#{result.join(' ')})"
82
+ else
83
+ # (1 2 3 . 4)
84
+ # ==> (1 . (2 . (3 . 4)))
85
+ result = any_to_string(obj.car)
86
+ cp = obj.cdr
87
+
88
+ while pair?(cp)
89
+ result += " "
90
+ result += any_to_string(cp.car)
91
+ cp = cp.cdr
92
+ end
93
+
94
+ result += " . "
95
+ result += any_to_string(cp)
96
+ "(#{result})"
97
+ end
98
+ end
99
+
100
+ def symbol_to_string(obj)
101
+ ":#{obj}"
102
+ end
103
+
104
+ def number_to_string(obj)
105
+ obj.to_s
106
+ end
107
+
108
+ def char_to_string(obj)
109
+ # TODO:
110
+ end
111
+
112
+ def string_to_string(obj)
113
+ "\"#{obj}\""
114
+ end
115
+
116
+ def vector_to_string(obj)
117
+ # TODO:
118
+ end
119
+
120
+ def port_to_string(obj)
121
+ # TODO:
122
+ end
123
+
124
+ def procedure_to_string(obj)
125
+ # TODO:
126
+ end
127
+
128
+ # :startdoc:
129
+ end
130
+ end
data/lib/rus3/repl.rb ADDED
@@ -0,0 +1,236 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rus3
4
+
5
+ # Provides a very simple Read Eval Print Loop mechanism of Rus3.
6
+ #
7
+ # Features:
8
+ #
9
+ # Each evaluated value is recorded into the value history.
10
+ #
11
+ # _, _last_value : retrieves the last evaluated value
12
+ # _his(n), _history(n) : retrieves the n-th value in the
13
+ # history.
14
+ # _his, _history : prints all values in the history
15
+ #
16
+ # Short usage:
17
+ #
18
+ # require "rus3"
19
+ # Rus3::Repl.start
20
+
21
+ class Repl
22
+
23
+ # Indicates the version of the Repl class.
24
+ VERSION = "0.1.0"
25
+
26
+ class << self
27
+
28
+ # Starts REPL.
29
+ def start(verbose: false)
30
+ repl = Repl.new(verbose: verbose)
31
+ repl.loop
32
+ end
33
+
34
+ end
35
+
36
+ # Hods major component names of the REPL.
37
+ COMPONENTS = {
38
+ :parser => Parser::DEFAULT_PARSER,
39
+ :evaluator => Evaluator,
40
+ :printer => nil,
41
+ }
42
+
43
+ # Prompt for input.
44
+ PROMPT = "Rus3> "
45
+
46
+ # A message to print at exitting.
47
+ FAREWELL_MESSAGE = "Bye!"
48
+
49
+ @@value_history = [] # :nodoc:
50
+
51
+ attr_accessor :verbose # :nodoc:
52
+
53
+ def initialize(verbose: false)
54
+ COMPONENTS.each { |name, klass|
55
+ instance_variable_set("@#{name}", klass.nil? ? self : klass.new)
56
+ }
57
+
58
+ @prompt = PROMPT
59
+ @parser.prompt = PROMPT unless @parser.nil?
60
+
61
+ @verbose = verbose
62
+ @evaluator.verbose = verbose
63
+ @printer.verbose = verbose
64
+
65
+ define_constants
66
+ define_help_feature
67
+ define_history_feature
68
+ define_load_feature
69
+
70
+ greeting
71
+ end
72
+
73
+ def loop
74
+ msg = Kernel.loop { # LOOP
75
+ begin
76
+ exp = @parser.read(STDIN) # READ
77
+ rescue SchemeSyntaxError => e
78
+ puts "ERROR: %s" % e
79
+ next
80
+ end
81
+ break FAREWELL_MESSAGE if exp.nil?
82
+
83
+ begin
84
+ value = @evaluator.eval(exp) # EVAL
85
+ rescue SyntaxError, StandardError => e
86
+ puts "ERROR: %s" % e
87
+ next
88
+ end
89
+
90
+ history_push(value)
91
+
92
+ @printer.print(value) # PRINT
93
+ }
94
+ puts "#{msg}" unless msg.nil?
95
+ end
96
+
97
+ # Shows the greeting message.
98
+ def greeting
99
+ puts "A simple REPL for Rus3:"
100
+ puts "- Rus3 version: #{Rus3::VERSION}"
101
+ puts " - REPL version: #{VERSION}"
102
+ COMPONENTS.keys.each { |comp_name|
103
+ Kernel.print " - "
104
+ print_version(comp_name)
105
+ }
106
+ end
107
+
108
+ # :stopdoc:
109
+
110
+ protected
111
+
112
+ require "readline"
113
+
114
+ def read(io = STDIN)
115
+ Readline::readline(@prompt, true)
116
+ end
117
+
118
+ def eval(exp)
119
+ exp
120
+ end
121
+
122
+ def print(obj)
123
+ prefix = "==> "
124
+ prefix += "[#{obj.class}]: " if @verbose
125
+ Kernel.print prefix
126
+ pp obj
127
+ end
128
+
129
+ private
130
+
131
+ def define_constants # :nodoc:
132
+ return if @evaluator.nil?
133
+
134
+ r = @evaluator.binding.receiver
135
+
136
+ r.instance_eval {
137
+ self.class.const_set(:RUS3_VERSION, "#{VERSION}")
138
+ }
139
+ end
140
+
141
+ def define_help_feature # :nodoc:
142
+ return if @evaluator.nil?
143
+
144
+ r = @evaluator.binding.receiver
145
+
146
+ r.instance_eval {
147
+ def _help
148
+ puts <<HELP
149
+ A simple REPL for Rus3.
150
+
151
+ FEATURES:
152
+
153
+ History of evaluated values:
154
+ - `_last_value` : refers the last evaluated value (short: `_`)
155
+ - `_history(n)` : refers the n-th value in the history (short: `_his(n)`)
156
+ - `_history` : prints all entries in the history (short: `_his`)
157
+
158
+ Help:
159
+ - `_help` : prints this message
160
+ HELP
161
+ end
162
+ }
163
+ end
164
+
165
+ def define_history_feature # :nodoc:
166
+ return if @evaluator.nil?
167
+
168
+ r = @evaluator.binding.receiver
169
+
170
+ r.instance_variable_set(:@value_history, @@value_history)
171
+ r.instance_eval {
172
+
173
+ def _last_value
174
+ @value_history[-1]
175
+ end
176
+ alias :_ :_last_value
177
+
178
+ def _history(arg = nil)
179
+ if arg.nil?
180
+ @value_history.each_with_index { |value, i|
181
+ Kernel.print "#{i}: "
182
+ display(value)
183
+ }
184
+ UNDEF
185
+ else
186
+ num = arg.to_i
187
+ if (0...@value_history.size).cover?(num)
188
+ @value_history[num]
189
+ else
190
+ raise OutOfRangeError, num
191
+ end
192
+ end
193
+ end
194
+ alias :_his :_history
195
+
196
+ }
197
+ end
198
+
199
+ def define_load_feature
200
+ return if @evaluator.nil?
201
+
202
+ r = @evaluator.binding.receiver
203
+
204
+ r.instance_variable_set(:@scm_parser, Parser::SchemeParser.new)
205
+ r.instance_eval {
206
+ def load_scm(path)
207
+ raise Rus3::CannotFindFileError, path unless FileTest.exist?(path)
208
+ scm_source = nil
209
+ File.open(path, "r") {|f| scm_source = f.readlines(chomp: true)}
210
+ s_exp = scm_source.join(" ")
211
+ r_exp = @scm_parser.parse(s_exp)
212
+ self.binding.eval(r_exp)
213
+ end
214
+ }
215
+ end
216
+
217
+ def print_version(comp_name)
218
+ component = instance_variable_get("@#{comp_name}")
219
+ if component.nil? or component == self
220
+ puts "using built-in #{comp_name.upcase}"
221
+ else
222
+ puts "#{component.version}"
223
+ end
224
+ end
225
+
226
+ def history_push(value)
227
+ prev_value = @@value_history[-1]
228
+ if prev_value != value and UNDEF != value
229
+ @@value_history << value if prev_value != value
230
+ end
231
+ end
232
+
233
+ # :startdoc:
234
+
235
+ end
236
+ end