-
Notifications
You must be signed in to change notification settings - Fork 0
/
iroha.rb
144 lines (135 loc) · 3.18 KB
/
iroha.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# coding:utf-8
class ProgramingError < StandardError; end
class Iroha
class << self
def run(src)
self.new(src).run
end
end
def initialize(src)
@src = src
@stack = []
end
def run()
pc = 0
order = parse()
labels = find_label(order)
while order.size > pc
o, arg = order[pc]
case o
when :push
push(arg)
when :pop
pop
when :dup
push(@stack[-1])
when :swap
y, x = pop, pop
push(y)
push(x)
when :+
y, x = pop, pop
push(x + y)
when :-
y, x = pop, pop
push(x - y)
when :*
y, x = pop, pop
push(x * y)
when :/
y, x = pop, pop
push(x / y)
when :%
y, x = pop, pop
push(x % y)
when :char_in
push(gets.chomp.ord)
when :int_in
push(gets.to_i)
when :char_out
print pop.chr("utf-8")
when :int_out
print pop
when :label
when :jump
if pop != 0
pc = labels[arg]
raise ProgramingError,"ジャンプ先(#{arg.inspect})が見つかりません。" if pc.nil?
end
else
raise ProgramingError, "そのような命令は存在しません。"
end
pc += 1
end
end
def parse
order = []
@src.each_line.with_index do |line, i|
case line.chomp
when /\Aい+([あ-ん]+)。\z/
order << [:push, $1.size]
when /\Aろ+([あ-ん]*)。\z/
order << [:pop]
when /\Aは+([あ-ん]*)。\z/
order << [:dup]
when /\Aに+([あ-ん]*)。\z/
order << [:swap]
when /\Aほ+([あ-ん]*)。\z/
order << [:+]
when /\Aへ+([あ-ん]*)。\z/
order << [:-]
when /\Aと+([あ-ん]*)。\z/
order << [:*]
when /\Aち+([あ-ん]*)。\z/
order << [:/]
when /\Aり+([あ-ん]*)。\z/
order << [:%]
when /\Aぬ+([あ-ん]*)。\z/
order << [:char_in]
when /\Aる+([あ-ん]*)。\z/
order << [:int_in]
when /\Aを+([あ-ん]*)。\z/
order << [:char_out]
when /\Aわ+([あ-ん]*)。\z/
order << [:int_out]
when /\Aゑ+([あ-ん]+)。\z/
order << [:label, $1]
when /\Aゐ+([あ-ん]+)。\z/
order << [:jump, $1]
when ""
when /#.*/
else
raise ProgramingError, "#{i + 1}行目で構文エラーです。"
end
end
order
end
def push(item)
unless item.is_a?(Integer)
raise ProgamError, "整数以外(#{item})をプッシュしようとしました。"
end
@stack.push(item)
end
def pop
item = @stack.pop
raise ProgramingError, "空のスタックからポップしようとしました。" if item.nil?
item
end
def find_label(orders)
labels = {}
orders.each_with_index do |(order, arg), i|
raise ProgramingError, "同じラベルは使用できません。" if order == :label && !labels[arg].nil?
if order == :label
labels[arg] = i
end
end
labels
end
end
if $0 == __FILE__
begin
Iroha.run(ARGF.read)
rescue ProgramingError => exception
puts exception.message
end
end