rus3 0.1.1 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfda189d1f2499f00d441efe70f0e8db49ecc443a8c414ec7b4f9282caf28fe2
4
- data.tar.gz: 1a0c858ec768aec68e79b66815fd8b5cdd2c6eef2f4e688e55f90fbcd1fde558
3
+ metadata.gz: e00bc6347634a3d6fd3ee9da17b18334e49373bab389c9c0126e5ee983504b56
4
+ data.tar.gz: 369f1e8627bdcb5c8ece22d0ba4971b417fa4924fa67ca31da3665bebb6a3aa8
5
5
  SHA512:
6
- metadata.gz: cb42c290cdcea8dacd9ae0b024ce0c2b266ae912ddc4178c40c5134fda5a79507d420ab959fc10a9860dd8bf3d805e16347a9cd791c4e90ddffaf1e7afd29932
7
- data.tar.gz: 16450eb77a484889dc5572a62fd48670b278fac5f4845111d6b192dbf8820bac54854a150a628004b9461b995a166f1957876d72636b732b11cf7c6db3dbc6c6
6
+ metadata.gz: 4eac508d23d36f6f1f7f6b60879bfbb80921c16ed669ed2d60e5d4d8926a62189269c8cef2373e6489d3dae3b47af01ee888116ecd8a85070f84ce3c8493e57a
7
+ data.tar.gz: ac9f76be872b9a5d70822f4bf0d4fab3da83d931002fd2f7b749648f704e65b7063487f9e9d095630a8c5bb8e9dd2f3e3b24fef3f24338d83e49cc73404a603c
data/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
  ## [Unreleased]
8
8
  - (nothing to record)
9
9
 
10
+ ## [0.1.2] - 2021-04-23
11
+ ### Added
12
+ - Add `char`:
13
+ - add Rus3::Char class,
14
+ - modify Rus3::Parser::Lexer to accept a character literal,
15
+ - modify Rus3::Parser::SchemeParser to parse and translate a
16
+ character literal,
17
+ - Add new error classes (CharRequiredError),
18
+ - Add tests around `char`.
19
+
10
20
  ## [0.1.1] - 2021-04-22
11
21
  ### Added
12
22
  - Add `vector`:
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rus3 (0.1.1)
4
+ rus3 (0.1.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -54,6 +54,11 @@ Rus3(scheme)> 3+4i
54
54
  ==> (3+4i)
55
55
  Rus3(scheme)> #(1 2 3)
56
56
  ==> #<Rus3::Vector:0x00007facf12d9fb0 @content=[1, 2, 3]>
57
+ Rus3(scheme)> #\a
58
+ ==> #<Rus3::Char:0x00007fc41f1059d8
59
+ @char="a",
60
+ @codepoint=97,
61
+ @encoding=#<Encoding:UTF-8>>
57
62
  ```
58
63
 
59
64
  ### Procedure call
@@ -117,14 +122,18 @@ Following procedures described in the spec is available.
117
122
  - Predicates:
118
123
  - null?, list?
119
124
  - eqv?, eq?
120
- - boolean?, pair?, symbol?, number?, string?, vector?
121
- - char? and port? are defined but it always returns `false`.
125
+ - boolean?, pair?, symbol?, number?, string?, vector?, char?
126
+ - port? are defined but it always returns `false`.
122
127
  - complex?, real?, rational?, integer?
123
128
  - zero?, positive?, negative?, odd?, even?
124
129
  - string-eq?, string-ci-eq?, string-lt?, string-gt?, string-le?,
125
130
  string-ge?, string-ci-lt?, string-ci-gt?, string-ci-le?, string-ci-ge?
126
- - some predicates for `char` and `port` are defined but it always
127
- returns `false`.
131
+ - char-eq?, char-lt?, char-gt?, char-le?, char-ge?,
132
+ char-ci-eq?, char-ci-lt?, char-ci-gt?, char-ci-le?, char-ci-ge?,
133
+ char-alphabetic?, char-numeric?, char-whitespace?,
134
+ char-upper-case?, char-lower-case?
135
+ - some predicates for `port` are defined but it always returns
136
+ `false`.
128
137
 
129
138
  - List operations
130
139
  - cons, car, cdr, set-car!, set-cdr!, cxxr (caar and so on)
@@ -134,6 +143,9 @@ Following procedures described in the spec is available.
134
143
  - make-vector, vector, vector-length, vector-ref, vector-set!,
135
144
  vector->list, list->vector, vector-fill!
136
145
 
146
+ - Char operations
147
+ - char->integer, integer->char, char-upcase, char-downcase
148
+
137
149
  - Write values
138
150
  - write
139
151
  - display
@@ -169,6 +181,7 @@ deviations from the Scheme specification (R5RS or R7RS-small).
169
181
  identifier in Ruby. So, it is possible that some collision of
170
182
  identifiers.
171
183
  - Nested definition of procedure can use outside.
184
+ - All characters and strings are treated as encoded in UTF-8.
172
185
 
173
186
  Some of these may be disappeared in the future version, or may not be.
174
187
 
data/lib/rus3.rb CHANGED
@@ -43,6 +43,7 @@ module Rus3
43
43
 
44
44
  require_relative "rus3/procedure/utils"
45
45
  require_relative "rus3/procedure/predicate"
46
+ require_relative "rus3/procedure/char"
46
47
  require_relative "rus3/procedure/list"
47
48
  require_relative "rus3/procedure/vector"
48
49
  require_relative "rus3/procedure/control"
data/lib/rus3/char.rb CHANGED
@@ -1,6 +1,102 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rus3
4
+
4
5
  class Char
6
+ include Comparable
7
+
8
+ class << self
9
+
10
+ def alphabetic?(char)
11
+ raise CharRequiredError, char unless char.instance_of?(Char)
12
+ /[A-Za-z]/ === char.to_s
13
+ end
14
+
15
+ def numeric?(char)
16
+ raise CharRequiredError, char unless char.instance_of?(Char)
17
+ /[0-9]/ === char.to_s
18
+ end
19
+
20
+ def whitespace?(char)
21
+ raise CharRequiredError, char unless char.instance_of?(Char)
22
+ /[\s]/ === char.to_s
23
+ end
24
+
25
+ def upper_case?(char)
26
+ raise CharRequiredError, char unless char.instance_of?(Char)
27
+ /[A-Z]/ === char.to_s
28
+ end
29
+
30
+ def lower_case?(char)
31
+ raise CharRequiredError, char unless char.instance_of?(Char)
32
+ /[a-z]/ === char.to_s
33
+ end
34
+
35
+ def char_to_integer(char)
36
+ raise CharRequiredError, char unless char.instance_of?(Char)
37
+ char.codepoint
38
+ end
39
+
40
+ def integer_to_char(n, encoding: Encoding::UTF_8)
41
+ self.new(n.chr(encoding))
42
+ end
43
+
44
+ def upcase(char)
45
+ raise CharRequiredError, char unless char.instance_of?(Char)
46
+ self.new(char.to_s.upcase)
47
+ end
48
+
49
+ def downcase(char)
50
+ raise CharRequiredError, char unless char.instance_of?(Char)
51
+ self.new(char.to_s.downcase)
52
+ end
53
+
54
+ def compare_chars(char1, char2, comp_op, ignore_case: false)
55
+ if !char1.instance_of?(self)
56
+ raise CharRequiredError, char1
57
+ elsif !char2.instance_of?(self)
58
+ raise CharRequiredError, char2
59
+ end
60
+
61
+ if ignore_case
62
+ char1 = downcase(char1)
63
+ char2 = downcase(char2)
64
+ end
65
+ char1.send(comp_op, char2)
66
+ end
67
+
68
+ end
69
+
70
+ LITERAL_PREFIX = "#\\"
71
+ LITERAL_SPACE = "#\\space"
72
+ LITERAL_NEWLINE = "#\\newline"
73
+
74
+ attr_reader :codepoint
75
+ attr_reader :encoding
76
+
77
+ def initialize(str)
78
+ @char = str[0].dup.freeze
79
+ @encoding = str.encoding
80
+ @codepoint = @char.ord
81
+ end
82
+
83
+ def ==(other)
84
+ other.instance_of?(Char) and to_s == other.to_s
85
+ end
86
+
87
+ def <=>(other)
88
+ raise CharRequiredError, other unless other.instance_of?(Char)
89
+ @codepoint <=> other.codepoint
90
+ end
91
+
92
+ def to_literal
93
+ LITERAL_PREFIX + @char
94
+ end
95
+
96
+ def to_s
97
+ @char
98
+ end
99
+
5
100
  end
101
+
6
102
  end
data/lib/rus3/error.rb CHANGED
@@ -30,95 +30,96 @@ module Rus3
30
30
  # :stopdoc:
31
31
 
32
32
  EMSG = {
33
+ :number_required => "number required: got=%s",
34
+ :integer_required => "integer required: got=%s",
35
+ :real_number_required => "real number required: got=%s",
36
+ :char_required => "char required: got=%s",
37
+ :string_required => "string required: got=%s",
38
+ :vector_required => "vector required: got=%s",
33
39
  :pair_required => "pair required: got=%s",
34
40
  :list_required => "proper list required: got=%s",
35
41
  :pair_or_list_required => "pair or proper list required: got=%s",
36
- :vector_required => "vector required: got=%s",
37
42
  :out_of_range => "argument out of range: got=%s",
38
43
  :exceed_upper_limit => "argument exceeds its upper limit (%d): got=%d",
39
- :unsupported_method => "specified method does not work now.",
40
44
  :wrong_type => "wrong type argument: got=%s, wants=%s",
41
- :integer_required => "integer required: got=%s",
42
- :real_number_required => "real number required: got=%s",
43
- :number_required => "number required: got=%s",
44
- :string_required => "string required: got=%s",
45
45
  :scheme_syntax_error => "syntax error as Scheme: got=%s",
46
+ :unsupported_method => "specified method does not work now.",
47
+ :unsupported_feature => "specified feature (`%s`) does not support for %s",
46
48
  :cannot_find_file => "cannot find %s",
47
- :unsupported_feature => "specified feature (`%s`) does not support for %s"
48
49
  }
49
50
 
50
51
  # :startdoc:
51
52
 
52
- class PairRequiredError < Error
53
+ class NumberRequiredError < Error
53
54
  def initialize(obj)
54
- super(EMSG[:pair_required] % smart_error_value(obj))
55
+ super(EMSG[:number_required] % smart_error_value(obj))
55
56
  end
56
57
  end
57
58
 
58
- class ListRequiredError < Error
59
+ class IntegerRequiredError < Error
59
60
  def initialize(obj)
60
- super(EMSG[:list_required] % smart_error_value(obj))
61
+ super(EMSG[:integer_required] % smart_error_value(obj))
61
62
  end
62
63
  end
63
64
 
64
- class PairOrListRequiredError < Error
65
+ class RealNumberRequiredError < Error
65
66
  def initialize(obj)
66
- super(EMSG[:pair_or_list_required] % smart_error_value(obj))
67
+ super(EMSG[:real_number_required] % smart_error_value(obj))
67
68
  end
68
69
  end
69
70
 
70
- class VectorRequiredError < Error
71
+ class CharRequiredError < Error
71
72
  def initialize(obj)
72
- super(EMSG[:vector_required] % smart_error_value(obj))
73
+ super(EMSG[:char_required] % smart_error_value(obj))
73
74
  end
74
75
  end
75
76
 
76
- class OutOfRangeError < Error
77
+ class StringRequiredError < Error
77
78
  def initialize(obj)
78
- super(EMSG[:out_of_range] % smart_error_value(obj))
79
+ super(EMSG[:string_required] % smart_error_value(obj))
79
80
  end
80
81
  end
81
82
 
82
- class ExceedUpperLimitError < Error
83
- def initialize(value, limit)
84
- super(EMSG[:exceed_upper_limit] % [limit, value])
83
+ class VectorRequiredError < Error
84
+ def initialize(obj)
85
+ super(EMSG[:vector_required] % smart_error_value(obj))
85
86
  end
86
87
  end
87
88
 
88
- class UnsupportedMethodError < Error
89
- def initialize
90
- super(EMSG[:unsupported_method])
89
+ class PairRequiredError < Error
90
+ def initialize(obj)
91
+ super(EMSG[:pair_required] % smart_error_value(obj))
91
92
  end
92
93
  end
93
94
 
94
- class WrongTypeError < Error
95
- def initialize(obj, expected)
96
- emsg = EMSG[:wrong_type] % [obj, expected]
97
- super(emsg)
95
+ class ListRequiredError < Error
96
+ def initialize(obj)
97
+ super(EMSG[:list_required] % smart_error_value(obj))
98
98
  end
99
99
  end
100
100
 
101
- class IntegerRequiredError < Error
101
+ class PairOrListRequiredError < Error
102
102
  def initialize(obj)
103
- super(EMSG[:integer_required] % smart_error_value(obj))
103
+ super(EMSG[:pair_or_list_required] % smart_error_value(obj))
104
104
  end
105
105
  end
106
106
 
107
- class RealNumberRequiredError < Error
107
+ class OutOfRangeError < Error
108
108
  def initialize(obj)
109
- super(EMSG[:real_number_required] % smart_error_value(obj))
109
+ super(EMSG[:out_of_range] % smart_error_value(obj))
110
110
  end
111
111
  end
112
112
 
113
- class NumberRequiredError < Error
114
- def initialize(obj)
115
- super(EMSG[:number_required] % smart_error_value(obj))
113
+ class ExceedUpperLimitError < Error
114
+ def initialize(value, limit)
115
+ super(EMSG[:exceed_upper_limit] % [limit, value])
116
116
  end
117
117
  end
118
118
 
119
- class StringRequiredError < Error
120
- def initialize(obj)
121
- super(EMSG[:string_required] % smart_error_value(obj))
119
+ class WrongTypeError < Error
120
+ def initialize(obj, expected)
121
+ emsg = EMSG[:wrong_type] % [obj, expected]
122
+ super(emsg)
122
123
  end
123
124
  end
124
125
 
@@ -128,9 +129,9 @@ module Rus3
128
129
  end
129
130
  end
130
131
 
131
- class CannotFindFileError < Error
132
- def initialize(obj)
133
- super(EMSG[:cannot_find_file] % smart_error_value(obj))
132
+ class UnsupportedMethodError < Error
133
+ def initialize
134
+ super(EMSG[:unsupported_method])
134
135
  end
135
136
  end
136
137
 
@@ -140,4 +141,10 @@ module Rus3
140
141
  end
141
142
  end
142
143
 
144
+ class CannotFindFileError < Error
145
+ def initialize(obj)
146
+ super(EMSG[:cannot_find_file] % smart_error_value(obj))
147
+ end
148
+ end
149
+
143
150
  end
@@ -16,6 +16,7 @@ module Rus3
16
16
  include Rus3::Procedure::Write
17
17
  include Rus3::Procedure::Vector
18
18
  include Rus3::Procedure::List
19
+ include Rus3::Procedure::Char
19
20
  include Rus3::Procedure::Predicate
20
21
  include Rus3::EmptyList
21
22
 
@@ -17,6 +17,7 @@ module Rus3::Parser
17
17
  # value types
18
18
  :boolean,
19
19
  :ident,
20
+ :char,
20
21
  :string,
21
22
  :number,
22
23
  # operators
@@ -55,6 +56,15 @@ module Rus3::Parser
55
56
  COMPLEX = Regexp.new("\\A[+-]?#{COMP_PAT}\\Z")
56
57
  PURE_IMAG = Regexp.new("\\A[+-](#{C_IMAG_PAT})?i\\Z")
57
58
 
59
+ # char
60
+ SINGLE_CHAR_PAT = "."
61
+ SPACE_PAT = "space"
62
+ NEWLINE_PAT = "newline"
63
+
64
+ CHAR_PREFIX = "\#\\\\"
65
+ CHAR_PAT = "(#{SINGLE_CHAR_PAT}|#{SPACE_PAT}|#{NEWLINE_PAT})"
66
+ CHAR = Regexp.new("\\A#{CHAR_PREFIX}#{CHAR_PAT}\\Z")
67
+
58
68
  KEYWORDS = {
59
69
  "LAMBDA" => :lambda,
60
70
  "IF" => :if,
@@ -102,6 +112,8 @@ module Rus3::Parser
102
112
  else
103
113
  Token.new(:ident, literal.gsub(EXTENDED_REGEXP, EXTENDED_MAP))
104
114
  end
115
+ when CHAR
116
+ Token.new(:char, literal)
105
117
  when STRING
106
118
  Token.new(:string, literal)
107
119
  when "="
@@ -155,7 +155,7 @@ module Rus3::Parser
155
155
  r_exp = translate_ident(token.literal)
156
156
  when :string
157
157
  r_exp = token.literal
158
- when :ident, :boolean, :number, :op_proc
158
+ when :ident, :boolean, :char, :number, :op_proc
159
159
  trans_method_name = "translate_#{token.type}".intern
160
160
  r_exp = self.send(trans_method_name, token.literal)
161
161
  else
@@ -213,6 +213,17 @@ module Rus3::Parser
213
213
  (s_exp_literal[1] == "f") ? "false" : "true"
214
214
  end
215
215
 
216
+ def translate_char(s_exp_literal)
217
+ c = s_exp_literal[2..-1]
218
+ case c
219
+ when "space"
220
+ c = " "
221
+ when "newline"
222
+ c = "\n"
223
+ end
224
+ "Char.new(\"#{c}\")"
225
+ end
226
+
216
227
  def translate_number(s_exp_literal)
217
228
  if s_exp_literal.include?("/") # rational?
218
229
  denominator, numerator = s_exp_literal.split("/").map{|s| Kernel.eval(s)}
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rus3::Procedure
4
+ module Char
5
+
6
+ # - procedure (R5RS): (char->integer char)
7
+
8
+ def char_to_integer(char)
9
+ Rus3::Char.char_to_integer(char)
10
+ end
11
+
12
+ # - procedure (R5RS): (integer->char n)
13
+
14
+ def integer_to_char(n)
15
+ Rus3::Char.integer_to_char(n)
16
+ end
17
+
18
+ # - library procedure (R5RS): (char-upcase char)
19
+
20
+ def char_upcase(char)
21
+ Rus3::Char.upcase(char)
22
+ end
23
+
24
+ # - library procedure (R5RS): (char-downcase char)
25
+
26
+ def char_downcase(char)
27
+ Rus3::Char.downcase(char)
28
+ end
29
+
30
+ end
31
+ end
@@ -72,7 +72,7 @@ module Rus3::Procedure
72
72
  end
73
73
 
74
74
  def char?(obj)
75
- false
75
+ obj.instance_of?(Rus3::Char)
76
76
  end
77
77
 
78
78
  def string?(obj)
@@ -163,69 +163,67 @@ module Rus3::Procedure
163
163
  # :stopdoc:
164
164
 
165
165
  # Characters:
166
- #
167
- # ...
168
166
 
169
167
  # :startdoc:
170
168
 
171
169
  def char_eq?(char1, char2)
172
- false
170
+ Rus3::Char.compare_chars(char1, char2, :==)
173
171
  end
174
172
 
175
173
  def char_lt?(char1, char2)
176
- false
174
+ Rus3::Char.compare_chars(char1, char2, :<)
177
175
  end
178
176
 
179
177
  def char_gt?(char1, char2)
180
- false
178
+ Rus3::Char.compare_chars(char1, char2, :>)
181
179
  end
182
180
 
183
181
  def char_le?(char1, char2)
184
- false
182
+ Rus3::Char.compare_chars(char1, char2, :<=)
185
183
  end
186
184
 
187
185
  def char_ge?(char1, char2)
188
- false
186
+ Rus3::Char.compare_chars(char1, char2, :>=)
189
187
  end
190
188
 
191
189
  def char_ci_eq?(char1, char2)
192
- false
190
+ Rus3::Char.compare_chars(char1, char2, :==, ignore_case: true)
193
191
  end
194
192
 
195
193
  def char_ci_lt?(char1, char2)
196
- false
194
+ Rus3::Char.compare_chars(char1, char2, :<, ignore_case: true)
197
195
  end
198
196
 
199
197
  def char_ci_gt?(char1, char2)
200
- false
198
+ Rus3::Char.compare_chars(char1, char2, :>, ignore_case: true)
201
199
  end
202
200
 
203
201
  def char_ci_le?(char1, char2)
204
- false
202
+ Rus3::Char.compare_chars(char1, char2, :<=, ignore_case: true)
205
203
  end
206
204
 
207
205
  def char_ci_ge?(char1, char2)
208
- false
206
+ Rus3::Char.compare_chars(char1, char2, :>=, ignore_case: true)
209
207
  end
210
208
 
211
209
  def char_alphabetic?(char)
212
- false
210
+ Rus3::Char.alphabetic?(char)
213
211
  end
214
212
 
215
213
  def char_numeric?(char)
216
- false
214
+ Rus3::Char.numeric?(char)
217
215
  end
218
216
 
219
217
  def char_whitespace?(char)
220
- false
218
+ Rus3::Char.whitespace?(char)
221
219
  end
222
220
 
223
221
  def char_upper_case?(letter)
224
- false
222
+ Rus3::Char.upper_case?(letter)
225
223
  end
226
224
 
227
225
  def char_lower_case?(letter)
228
- false
226
+ Rus3::Char.lower_case?(letter)
229
227
  end
230
228
 
231
229
  # :stopdoc:
data/lib/rus3/repl.rb CHANGED
@@ -75,7 +75,7 @@ module Rus3
75
75
  begin
76
76
  exp = @parser.read(STDIN) # READ
77
77
  rescue SchemeSyntaxError => e
78
- puts "ERROR: %s" % e
78
+ puts "ERROR" + (@verbose ? "(READ)" : "") + ": %s" % e
79
79
  next
80
80
  end
81
81
  break FAREWELL_MESSAGE if exp.nil?
@@ -83,7 +83,7 @@ module Rus3
83
83
  begin
84
84
  value = @evaluator.eval(exp) # EVAL
85
85
  rescue SyntaxError, StandardError => e
86
- puts "ERROR: %s" % e
86
+ puts "ERROR" + (@verbose ? "(EVAL)" : "") + ": %s" % e
87
87
  next
88
88
  end
89
89
 
data/lib/rus3/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rus3
4
- VERSION = "0.1.1"
5
- RELEASE = "2021-04-22"
4
+ VERSION = "0.1.2"
5
+ RELEASE = "2021-04-23"
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rus3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - mnbi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-22 00:00:00.000000000 Z
11
+ date: 2021-04-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Ruby with Syntax Sugar of Scheme
14
14
  email:
@@ -42,6 +42,7 @@ files:
42
42
  - lib/rus3/parser/scheme_parser.rb
43
43
  - lib/rus3/port.rb
44
44
  - lib/rus3/printer.rb
45
+ - lib/rus3/procedure/char.rb
45
46
  - lib/rus3/procedure/control.rb
46
47
  - lib/rus3/procedure/list.rb
47
48
  - lib/rus3/procedure/predicate.rb