raheui 1.0.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 +15 -0
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.rubocop.yml +14 -0
- data/.rubocop_todo.yml +24 -0
- data/.travis.yml +17 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +64 -0
- data/Rakefile +16 -0
- data/bin/raheui +8 -0
- data/lib/raheui/cli.rb +27 -0
- data/lib/raheui/code.rb +106 -0
- data/lib/raheui/io.rb +60 -0
- data/lib/raheui/option.rb +44 -0
- data/lib/raheui/port.rb +14 -0
- data/lib/raheui/queue.rb +18 -0
- data/lib/raheui/runner.rb +212 -0
- data/lib/raheui/stack.rb +13 -0
- data/lib/raheui/store.rb +41 -0
- data/lib/raheui/version.rb +7 -0
- data/lib/raheui.rb +16 -0
- data/raheui.gemspec +26 -0
- data/spec/raheui/cli_spec.rb +39 -0
- data/spec/raheui/code_spec.rb +119 -0
- data/spec/raheui/io_spec.rb +94 -0
- data/spec/raheui/option_spec.rb +43 -0
- data/spec/raheui/port_spec.rb +6 -0
- data/spec/raheui/queue_spec.rb +148 -0
- data/spec/raheui/runner_spec.rb +516 -0
- data/spec/raheui/stack_spec.rb +145 -0
- data/spec/raheui/store_spec.rb +93 -0
- data/spec/raheui/version_spec.rb +8 -0
- data/spec/spec_helper.rb +75 -0
- data/spec/support/exit_code_matchers.rb +27 -0
- data/spec/support/file_helper.rb +38 -0
- data/spec/support/isolated_environment.rb +13 -0
- data/spec/support/shared_store.rb +47 -0
- metadata +168 -0
@@ -0,0 +1,212 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Raheui
|
3
|
+
# Run Aheui code.
|
4
|
+
class Runner
|
5
|
+
# Point class for execution cursor position and delta of it.
|
6
|
+
Point = Struct.new(:x, :y)
|
7
|
+
|
8
|
+
# Numbers of required elements for each command.
|
9
|
+
REQUIRED_STORE_SIZE = [
|
10
|
+
0, 0, 2, 2, 2, 2, 1, 0, 1, 0, # ㄱ ㄲ ㄴ ㄷ ㄸ ㄹ ㅁ ㅂ ㅃ ㅅ
|
11
|
+
1, 0, 2, 0, 1, 0, 2, 2, 0 # ㅆ ㅇ ㅈ ㅉ ㅊ ㅋ ㅌ ㅍ ㅎ
|
12
|
+
]
|
13
|
+
|
14
|
+
# Delta values of each medial consonant.
|
15
|
+
MEDIAL_DELTAS = [
|
16
|
+
[1, 0], nil, [2, 0], nil, # ㅏ ㅐ ㅑ ㅒ
|
17
|
+
[-1, 0], nil, [-2, 0], nil, # ㅓ ㅔ ㅕ ㅖ
|
18
|
+
[0, -1], nil, nil, nil, [0, -2], # ㅗ ㅘ ㅙ ㅚ ㅛ
|
19
|
+
[0, 1], nil, nil, nil, [0, 2], # ㅜ ㅝ ㅞ ㅟ ㅠ
|
20
|
+
[:+, :-], [:-, :-], [:-, :+] # ㅡ ㅢ ㅣ
|
21
|
+
]
|
22
|
+
|
23
|
+
# Numbers of strokes of each final consonant.
|
24
|
+
FINAL_STROKES = [
|
25
|
+
0, # No final consonant.
|
26
|
+
2, 4, 4, 2, 5, 5, 3, 5, 7, 9, # ㄱ ㄲ ㄳ ㄴ ㄵ ㄶ ㄷ ㄹ ㄺ ㄻ
|
27
|
+
9, 7, 9, 9, 8, 4, 4, 6, 2, 4, # ㄼ ㄽ ㄾ ㄿ ㅀ ㅁ ㅂ ㅄ ㅅ ㅆ
|
28
|
+
1, 3, 4, 3, 4, 4, 3 # ㅇ ㅈ ㅊ ㅋ ㅌ ㅍ ㅎ
|
29
|
+
]
|
30
|
+
|
31
|
+
private_constant :REQUIRED_STORE_SIZE, :MEDIAL_DELTAS, :FINAL_STROKES
|
32
|
+
|
33
|
+
# Initialize a Runner. Get a Code instance and initialize Stores.
|
34
|
+
#
|
35
|
+
# code - The Code instance to execute.
|
36
|
+
def initialize(code)
|
37
|
+
@code = code
|
38
|
+
end
|
39
|
+
|
40
|
+
# Run the Aheui Code.
|
41
|
+
#
|
42
|
+
# Returns the Integer exit code.
|
43
|
+
def run
|
44
|
+
reset
|
45
|
+
step until @finished
|
46
|
+
@selected_store.pop || 0
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Reset cursor and Stores. Select first Store.
|
52
|
+
#
|
53
|
+
# Returns nothing.
|
54
|
+
def reset
|
55
|
+
@cursor = Point.new(0, 0)
|
56
|
+
@delta = Point.new(0, 1)
|
57
|
+
@stores = Code::FINAL_CONSONANTS.times.map do |consonant|
|
58
|
+
case consonant
|
59
|
+
when 21 then Queue.new # ㅇ
|
60
|
+
when 27 then Port.new # ㅎ
|
61
|
+
else Stack.new
|
62
|
+
end
|
63
|
+
end
|
64
|
+
@selected_store = @stores[0]
|
65
|
+
@finished = false
|
66
|
+
end
|
67
|
+
|
68
|
+
# Process current character which cursor points to and move cursor.
|
69
|
+
#
|
70
|
+
# Returns nothing.
|
71
|
+
def step
|
72
|
+
consonants = @code[@cursor.x, @cursor.y]
|
73
|
+
unless consonants.empty?
|
74
|
+
initial, medial, final = consonants
|
75
|
+
@delta.x, @delta.y = delta(medial)
|
76
|
+
if @selected_store.size < REQUIRED_STORE_SIZE[initial]
|
77
|
+
@delta.x, @delta.y = -@delta.x, -@delta.y
|
78
|
+
else
|
79
|
+
process(initial, final)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
move
|
83
|
+
end
|
84
|
+
|
85
|
+
# Process a Korean alphabet.
|
86
|
+
#
|
87
|
+
# initial - An Integer index of initial consonant of the Korean alphabet.
|
88
|
+
# final - An Integer index of final consonant of the Korean alphabet.
|
89
|
+
#
|
90
|
+
# Returns nothing.
|
91
|
+
def process(initial, final)
|
92
|
+
case initial
|
93
|
+
when 2 # ㄴ
|
94
|
+
operate(:/)
|
95
|
+
when 3 # ㄷ
|
96
|
+
operate(:+)
|
97
|
+
when 4 # ㄸ
|
98
|
+
operate(:*)
|
99
|
+
when 5 # ㄹ
|
100
|
+
operate(:%)
|
101
|
+
when 6 # ㅁ
|
102
|
+
op = @selected_store.pop
|
103
|
+
if final == 21 # ㅇ
|
104
|
+
IO.print_int(op)
|
105
|
+
elsif final == 27 # ㅎ
|
106
|
+
IO.print_chr(op)
|
107
|
+
end
|
108
|
+
when 7 # ㅂ
|
109
|
+
op = if final == 21 # ㅇ
|
110
|
+
IO.read_int
|
111
|
+
elsif final == 27 # ㅎ
|
112
|
+
IO.read_chr
|
113
|
+
else
|
114
|
+
FINAL_STROKES[final]
|
115
|
+
end
|
116
|
+
@selected_store.push(op)
|
117
|
+
when 8 # ㅃ
|
118
|
+
@selected_store.push_dup
|
119
|
+
when 9 # ㅅ
|
120
|
+
@selected_store = @stores[final]
|
121
|
+
when 10 # ㅆ
|
122
|
+
op = @selected_store.pop
|
123
|
+
@stores[final].push(op)
|
124
|
+
when 12 # ㅈ
|
125
|
+
op1 = @selected_store.pop
|
126
|
+
op2 = @selected_store.pop
|
127
|
+
@selected_store.push(op2 >= op1 ? 1 : 0)
|
128
|
+
when 14 # ㅊ
|
129
|
+
op = @selected_store.pop
|
130
|
+
@delta.x, @delta.y = -@delta.x, -@delta.y if op == 0
|
131
|
+
when 16 # ㅌ
|
132
|
+
operate(:-)
|
133
|
+
when 17 # ㅍ
|
134
|
+
@selected_store.swap
|
135
|
+
when 18 # ㅎ
|
136
|
+
@finished = true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Helper method for basic operators. +, -, *, / and % can be processed.
|
141
|
+
#
|
142
|
+
# method - A Symbol method to execute.
|
143
|
+
#
|
144
|
+
# Examples
|
145
|
+
#
|
146
|
+
# operate(:+)
|
147
|
+
#
|
148
|
+
# operate(:-)
|
149
|
+
#
|
150
|
+
# operate(:*)
|
151
|
+
#
|
152
|
+
# operate(:/)
|
153
|
+
#
|
154
|
+
# operate(:%)
|
155
|
+
#
|
156
|
+
# Returns nothing.
|
157
|
+
def operate(method)
|
158
|
+
op1 = @selected_store.pop
|
159
|
+
op2 = @selected_store.pop
|
160
|
+
@selected_store.push([op2, op1].reduce(method))
|
161
|
+
end
|
162
|
+
|
163
|
+
# Get delta x and y position for next move.
|
164
|
+
#
|
165
|
+
# medial - An Integer index of medial consonant of the Korean alphabet.
|
166
|
+
#
|
167
|
+
# Examples
|
168
|
+
#
|
169
|
+
# delta(0)
|
170
|
+
# # => [1, 0]
|
171
|
+
#
|
172
|
+
# Returns an Array of Integer delta x and y position.
|
173
|
+
def delta(medial)
|
174
|
+
delta = MEDIAL_DELTAS[medial]
|
175
|
+
if delta
|
176
|
+
x, y = delta
|
177
|
+
x = x == :+ ? @delta.x : -@delta.x if x.is_a? Symbol
|
178
|
+
y = y == :+ ? @delta.y : -@delta.y if y.is_a? Symbol
|
179
|
+
[x, y]
|
180
|
+
else
|
181
|
+
[@delta.x, @delta.y]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Move cursor to proper position. Wrap the position if it goes to outside of
|
186
|
+
# the code.
|
187
|
+
#
|
188
|
+
# Returns nothing.
|
189
|
+
def move
|
190
|
+
@cursor.x = wrap(@cursor.x + @delta.x, @code.width)
|
191
|
+
@cursor.y = wrap(@cursor.y + @delta.y, @code.height)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Wrap a number to be between 0 and max value excluding max value. If the
|
195
|
+
# number is negative, it goes to max - 1. If the number is bigger than or
|
196
|
+
# equal to max value, it goes to zero.
|
197
|
+
#
|
198
|
+
# num - An Integer to be wrapped.
|
199
|
+
# max - An Integer max value.
|
200
|
+
#
|
201
|
+
# Returns an Integer between 0 and max - 1.
|
202
|
+
def wrap(num, max)
|
203
|
+
if num < 0
|
204
|
+
max - 1
|
205
|
+
elsif num >= max
|
206
|
+
0
|
207
|
+
else
|
208
|
+
num
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
data/lib/raheui/stack.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Raheui
|
3
|
+
# Stack class for Aheui.
|
4
|
+
class Stack < Store
|
5
|
+
# Delegates push, pop to @store.
|
6
|
+
delegate [:push, :pop] => :@store
|
7
|
+
|
8
|
+
# Swap the last two elements of Stack.
|
9
|
+
def swap
|
10
|
+
@store[-1], @store[-2] = @store[-2], @store[-1] if size > 1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/raheui/store.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Raheui
|
3
|
+
# Base Store class for Aheui. Every child classes should implement push, pop
|
4
|
+
# and swap method.
|
5
|
+
class Store
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
BASE_METHODS = [:push, :pop, :swap]
|
9
|
+
private_constant :BASE_METHODS
|
10
|
+
|
11
|
+
# Delegates size to @store.
|
12
|
+
def_delegator :@store, :size
|
13
|
+
|
14
|
+
# Initialize a Stack.
|
15
|
+
def initialize
|
16
|
+
check_base_methods
|
17
|
+
@store = []
|
18
|
+
end
|
19
|
+
|
20
|
+
# Push the last element to Store.
|
21
|
+
def push_dup
|
22
|
+
push(@store.last) if size > 0
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Check whether base methods are implemented.
|
28
|
+
#
|
29
|
+
# Returns nothing.
|
30
|
+
# Raises NotImplementedError if base methods are not implemented.
|
31
|
+
def check_base_methods
|
32
|
+
errors = []
|
33
|
+
BASE_METHODS.each do |method|
|
34
|
+
errors << method unless respond_to?(method)
|
35
|
+
end
|
36
|
+
return if errors.empty?
|
37
|
+
fail NotImplementedError, 'base methods are not implemented:' \
|
38
|
+
" #{errors.join(', ')}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/raheui.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'raheui/version'
|
3
|
+
|
4
|
+
require 'forwardable'
|
5
|
+
require 'raheui/store'
|
6
|
+
require 'raheui/stack'
|
7
|
+
require 'raheui/queue'
|
8
|
+
require 'raheui/port'
|
9
|
+
|
10
|
+
require 'raheui/io'
|
11
|
+
require 'raheui/code'
|
12
|
+
require 'raheui/runner'
|
13
|
+
|
14
|
+
require 'optparse'
|
15
|
+
require 'raheui/option'
|
16
|
+
require 'raheui/cli'
|
data/raheui.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'raheui/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'raheui'
|
8
|
+
spec.version = Raheui::Version::STRING
|
9
|
+
spec.authors = ['ChaYoung You']
|
10
|
+
spec.email = ['yousbe@gmail.com']
|
11
|
+
spec.summary = 'Aheui interpreter in Ruby.'
|
12
|
+
spec.description = 'Aheui interpreter in Ruby.'
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
24
|
+
spec.add_development_dependency 'rubocop', '~> 0.25.0'
|
25
|
+
spec.add_development_dependency 'simplecov', '~> 0.9'
|
26
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Raheui::CLI do
|
5
|
+
include FileHelper
|
6
|
+
include_context 'isolated environment'
|
7
|
+
|
8
|
+
subject(:cli) { described_class.new }
|
9
|
+
|
10
|
+
before(:example) { $stdout = StringIO.new }
|
11
|
+
after(:example) { $stdout = STDOUT }
|
12
|
+
|
13
|
+
it 'runs aheui file passed as an argument' do
|
14
|
+
create_file('helloworld.aheui', %w(밤밣따빠밣밟따뿌
|
15
|
+
빠맣파빨받밤뚜뭏
|
16
|
+
돋밬탕빠맣붏두붇
|
17
|
+
볻뫃박발뚷투뭏붖
|
18
|
+
뫃도뫃희멓뭏뭏붘
|
19
|
+
뫃봌토범더벌뿌뚜
|
20
|
+
뽑뽀멓멓더벓뻐뚠
|
21
|
+
뽀덩벐멓뻐덕더벅))
|
22
|
+
expect(cli.run(['helloworld.aheui'])).to be(0)
|
23
|
+
expect($stdout.string).to eq("Hello, world!\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'runs string passed as an stdin argument' do
|
27
|
+
allow($stdin).to receive(:read).once
|
28
|
+
.and_return(%w(밤밣따빠밣밟따뿌
|
29
|
+
빠맣파빨받밤뚜뭏
|
30
|
+
돋밬탕빠맣붏두붇
|
31
|
+
볻뫃박발뚷투뭏붖
|
32
|
+
뫃도뫃희멓뭏뭏붘
|
33
|
+
뫃봌토범더벌뿌뚜
|
34
|
+
뽑뽀멓멓더벓뻐뚠
|
35
|
+
뽀덩벐멓뻐덕더벅).join("\n"))
|
36
|
+
expect(cli.run([])).to be(0)
|
37
|
+
expect($stdout.string).to eq("Hello, world!\n")
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Raheui::Code do
|
5
|
+
shared_examples 'a code' do
|
6
|
+
subject(:code) { Raheui::Code.new(code_str) }
|
7
|
+
let(:consonants) { code.method(:consonants) }
|
8
|
+
|
9
|
+
it 'has width which is maximum line length of code with minimum value 1' do
|
10
|
+
width = code_str.lines.map { |line| line.chomp.size }.max || 1
|
11
|
+
expect(code.width).to be(width)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has height which is number of lines with minimum value 1' do
|
15
|
+
height = [code_str.lines.count, 1].max
|
16
|
+
expect(code.height).to be(height)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#[]' do
|
20
|
+
it 'returns item at given position' do
|
21
|
+
code_str.lines.each_with_index do |line, y|
|
22
|
+
line.chomp.chars.each_with_index do |ch, x|
|
23
|
+
expect(code[x, y]).to match_array(consonants.call(ch))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns item at given negative position' do
|
29
|
+
code_str.lines.each_with_index do |line, y|
|
30
|
+
max_x = line.chomp.chars.count
|
31
|
+
line.chomp.chars.each_with_index do |ch, x|
|
32
|
+
expected = consonants.call(ch)
|
33
|
+
expect(code[x - max_x, y]).to match_array(expected)
|
34
|
+
expect(code[x, y - code.height]).to match_array(expected)
|
35
|
+
expect(code[x - max_x, y - code.height]).to match_array(expected)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns an empty array if there is no item at given position' do
|
41
|
+
expect(code[0, code.height]).to match_array([])
|
42
|
+
expect(code[code.width, 0]).to match_array([])
|
43
|
+
expect(code[code.width, code.height]).to match_array([])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe Raheui::Code, 'with empty code' do
|
49
|
+
let(:code_str) { '' }
|
50
|
+
it_behaves_like 'a code'
|
51
|
+
end
|
52
|
+
|
53
|
+
describe Raheui::Code, 'with aheui code' do
|
54
|
+
let(:code_str) { '아희' }
|
55
|
+
it_behaves_like 'a code'
|
56
|
+
end
|
57
|
+
|
58
|
+
describe Raheui::Code, 'with hello world code' do
|
59
|
+
let(:code_str) do
|
60
|
+
<<-CODE
|
61
|
+
밤밣따빠밣밟따뿌
|
62
|
+
빠맣파빨받밤뚜뭏
|
63
|
+
돋밬탕빠맣붏두붇
|
64
|
+
볻뫃박발뚷투뭏붖
|
65
|
+
뫃도뫃희멓뭏뭏붘
|
66
|
+
뫃봌토범더벌뿌뚜
|
67
|
+
뽑뽀멓멓더벓뻐뚠
|
68
|
+
뽀덩벐멓뻐덕더벅
|
69
|
+
CODE
|
70
|
+
end
|
71
|
+
|
72
|
+
it_behaves_like 'a code'
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#consonants' do
|
76
|
+
subject { Raheui::Code.new('') }
|
77
|
+
let(:consonants) { subject.method(:consonants) }
|
78
|
+
before(:example) do
|
79
|
+
stub_const('INITIAL_CONSONANTS', Raheui::Code::INITIAL_CONSONANTS)
|
80
|
+
stub_const('MEDIAL_CONSONANTS', Raheui::Code::MEDIAL_CONSONANTS)
|
81
|
+
stub_const('FINAL_CONSONANTS', Raheui::Code::FINAL_CONSONANTS)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'returns consonants of Korean alphabet' do
|
85
|
+
examples = [
|
86
|
+
# 가
|
87
|
+
[0, 0, 0],
|
88
|
+
# 힣
|
89
|
+
[INITIAL_CONSONANTS - 1, MEDIAL_CONSONANTS - 1, FINAL_CONSONANTS - 1]
|
90
|
+
]
|
91
|
+
examples.concat(10.times.map do
|
92
|
+
[rand(INITIAL_CONSONANTS),
|
93
|
+
rand(MEDIAL_CONSONANTS),
|
94
|
+
rand(FINAL_CONSONANTS)]
|
95
|
+
end.uniq)
|
96
|
+
examples.each do |initial, medial, final|
|
97
|
+
alphabet = (
|
98
|
+
0xAC00 +
|
99
|
+
initial * MEDIAL_CONSONANTS * FINAL_CONSONANTS +
|
100
|
+
medial * FINAL_CONSONANTS +
|
101
|
+
final
|
102
|
+
).chr(Encoding::UTF_8)
|
103
|
+
expect(consonants.call(alphabet)).to match_array(
|
104
|
+
[initial, medial, final])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'returns an empty array for non-Korean alphabet' do
|
109
|
+
examples = [
|
110
|
+
*0..127,
|
111
|
+
0xAC00 + INITIAL_CONSONANTS * MEDIAL_CONSONANTS * FINAL_CONSONANTS
|
112
|
+
]
|
113
|
+
examples.concat(%w(あ 漢 å ★).map(&:ord))
|
114
|
+
examples.each do |ch|
|
115
|
+
expect(consonants.call(ch)).to match_array([])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Raheui::IO do
|
5
|
+
describe '.read_int' do
|
6
|
+
let(:value) { rand(-100..100) }
|
7
|
+
|
8
|
+
it { is_expected.to respond_to(:read_int) }
|
9
|
+
|
10
|
+
it 'reads an integer' do
|
11
|
+
allow($stdin).to receive(:gets).with(no_args).once
|
12
|
+
.and_return("#{value}\n")
|
13
|
+
expect(subject.read_int).to be(value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '.read_chr' do
|
18
|
+
it { is_expected.to respond_to(:read_chr) }
|
19
|
+
|
20
|
+
it 'reads an ASCII code' do
|
21
|
+
[0, *32..126].each do |value|
|
22
|
+
allow($stdin).to receive(:getc).with(no_args).once
|
23
|
+
.and_return(value.chr)
|
24
|
+
expect(subject.read_chr).to be(value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'reads an control character' do
|
29
|
+
[*1..31, *127..159].each do |value|
|
30
|
+
allow($stdin).to receive(:getc).with(no_args).once
|
31
|
+
.and_return(value.chr)
|
32
|
+
expect(subject.read_chr).to be(value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'reads an unicode character' do
|
37
|
+
[*10.times.map { rand(160..0xD7FF).chr(Encoding::UTF_8) }.uniq,
|
38
|
+
# Range 0xD800..0xDFFF is used by UTF-16.
|
39
|
+
*10.times.map { rand(0xE000..0x10FFFF).chr(Encoding::UTF_8) }.uniq,
|
40
|
+
'가', 'あ', '漢', ' ', 'å', '★'].each do |chr|
|
41
|
+
allow($stdin).to receive(:getc).with(no_args).once
|
42
|
+
.and_return(chr)
|
43
|
+
expect(subject.read_chr).to be(chr.ord)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '.print_int' do
|
49
|
+
let(:value) { rand(-100..100) }
|
50
|
+
|
51
|
+
it { is_expected.to respond_to(:print_int) }
|
52
|
+
|
53
|
+
it 'prints an integer' do
|
54
|
+
allow($stdout).to receive(:print).with(value).once
|
55
|
+
expect(subject.print_int(value)).to be_nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '.print_chr' do
|
60
|
+
it { is_expected.to respond_to(:print_chr) }
|
61
|
+
|
62
|
+
it 'prints an ASCII character corresponding character code' do
|
63
|
+
[0, *32..126].each do |value|
|
64
|
+
allow($stdout).to receive(:print).with(value.chr).once
|
65
|
+
expect(subject.print_chr(value)).to be_nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'prints an control character corresponding character code' do
|
70
|
+
[*1..31, *127..159].each do |value|
|
71
|
+
allow($stdout).to receive(:print).with(value.chr(Encoding::UTF_8)).once
|
72
|
+
expect(subject.print_chr(value)).to be_nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'prints an unicode character corresponding character code' do
|
77
|
+
[*10.times.map { rand(160..0xD7FF).chr(Encoding::UTF_8) }.uniq,
|
78
|
+
# Range 0xD800..0xDFFF is used by UTF-16.
|
79
|
+
*10.times.map { rand(0xE000..0x10FFFF).chr(Encoding::UTF_8) }.uniq,
|
80
|
+
'가', 'あ', '漢', ' ', 'å', '★'].each do |chr|
|
81
|
+
allow($stdout).to receive(:print).with(chr).once
|
82
|
+
expect(subject.print_chr(chr.ord)).to be_nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'prints an [U+%04X] string when RangeError is raised' do
|
87
|
+
[*10.times.map { rand(0xD800..0xDFFF) }.uniq,
|
88
|
+
0x110000].each do |value|
|
89
|
+
allow($stdout).to receive(:print).with(format('[U+%04X]', value)).once
|
90
|
+
expect(subject.print_chr(value)).to be_nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Raheui::Option do
|
5
|
+
include ExitCodeMatchers
|
6
|
+
|
7
|
+
subject(:option) { described_class.new }
|
8
|
+
|
9
|
+
before(:example) { $stdout = StringIO.new }
|
10
|
+
after(:example) { $stdout = STDOUT }
|
11
|
+
|
12
|
+
describe 'option' do
|
13
|
+
describe '-h/--help' do
|
14
|
+
it 'exits cleanly' do
|
15
|
+
expect { option.parse(['-h']) }.to exit_with_code(0)
|
16
|
+
expect { option.parse(['--help']) }.to exit_with_code(0)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'shows help text' do
|
20
|
+
begin
|
21
|
+
option.parse(['--help'])
|
22
|
+
rescue SystemExit # rubocop:disable Lint/HandleExceptions
|
23
|
+
end
|
24
|
+
|
25
|
+
expected_help = <<-END
|
26
|
+
Usage: raheui [options] [file]
|
27
|
+
-h, --help Print this message.
|
28
|
+
-v, --version Print version.
|
29
|
+
END
|
30
|
+
|
31
|
+
expect($stdout.string).to eq(expected_help)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '-v/--version' do
|
36
|
+
it 'exits cleanly' do
|
37
|
+
expect { option.parse(['-v']) }.to exit_with_code(0)
|
38
|
+
expect { option.parse(['--version']) }.to exit_with_code(0)
|
39
|
+
expect($stdout.string).to eq("#{Raheui::Version::STRING}\n" * 2)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|