rubyfunge 0.0.1
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.
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/bin/rubyfunge +4 -0
- data/lib/rubyfunge.rb +2 -0
- data/lib/rubyfunge/rubyfunge.rb +206 -0
- data/lib/rubyfunge/version.rb +3 -0
- data/rubyfunge.gemspec +22 -0
- data/test/rubyfunge.spec +261 -0
- metadata +92 -0
data/Gemfile
ADDED
data/Rakefile
ADDED
data/bin/rubyfunge
ADDED
data/lib/rubyfunge.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
module RubyFunge
|
2
|
+
class RubyFunge
|
3
|
+
attr_reader :x
|
4
|
+
attr_reader :y
|
5
|
+
attr_reader :stack
|
6
|
+
attr_reader :code
|
7
|
+
|
8
|
+
DIRS = [:left, :right, :up, :down]
|
9
|
+
|
10
|
+
def initialize()
|
11
|
+
@x, @y = 0, 0
|
12
|
+
@dir = :right
|
13
|
+
@code = []
|
14
|
+
@stack = []
|
15
|
+
@str_mode = false
|
16
|
+
@skip = false
|
17
|
+
|
18
|
+
@synt = {
|
19
|
+
'+'=>:op_add, '-'=>:op_sub, '*'=>:op_mul, '/'=>:op_div, '%'=>:op_mod,
|
20
|
+
'!'=>:op_not, '`'=>:op_gt,
|
21
|
+
'>'=>:op_right, '<'=>:op_left, '^'=>:op_up, 'v'=>:op_down,
|
22
|
+
'?'=>:op_rnd,
|
23
|
+
'_'=>:op_lr, '|'=>:op_ud,
|
24
|
+
'"'=>:op_str,
|
25
|
+
':'=>:op_dup, '\\'=>:op_swp,
|
26
|
+
'$'=>:op_pop, '.'=>:op_outi, ','=>:op_outc,
|
27
|
+
'#'=>:op_skp, 'p'=>:op_put, 'g'=>:op_get,
|
28
|
+
'&'=>:op_aski, '~'=>:op_askc
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_file(filename)
|
33
|
+
if (!File.exist?(filename))
|
34
|
+
raise "File does not exist"
|
35
|
+
else
|
36
|
+
run(File.open(filename).read())
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def run(code)
|
41
|
+
i = 0
|
42
|
+
@code = []
|
43
|
+
code.each_line {|l|
|
44
|
+
@code[i] = l
|
45
|
+
i += 1
|
46
|
+
}
|
47
|
+
@x, @y = 0,0
|
48
|
+
while (@code[@y][@x].chr != '@')
|
49
|
+
op = @code[@y][@x].chr
|
50
|
+
if (@str_mode)
|
51
|
+
if (op == '"')
|
52
|
+
op_str
|
53
|
+
else
|
54
|
+
push op[0]
|
55
|
+
end
|
56
|
+
elsif (op=='#')
|
57
|
+
@skip = true
|
58
|
+
elsif (op=~/\d/ && !@skip)
|
59
|
+
self.send :push, op.to_i
|
60
|
+
@skip = false
|
61
|
+
else
|
62
|
+
self.send @synt[op] if @synt.include?(op) && !@skip
|
63
|
+
@skip = false
|
64
|
+
end
|
65
|
+
case @dir
|
66
|
+
when :right
|
67
|
+
@x += 1
|
68
|
+
@x = 0 if @x > @code[@y].length - 1
|
69
|
+
when :left
|
70
|
+
@x -= 1
|
71
|
+
@x = @code[@y].length - 1 if @x < 0
|
72
|
+
when :up
|
73
|
+
@y -= 1
|
74
|
+
@y = @code.length - 1 if @y < 0
|
75
|
+
when :down
|
76
|
+
@y += 1
|
77
|
+
@y = 0 if @y > @code.length - 1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Instructions
|
83
|
+
private
|
84
|
+
def push(num)
|
85
|
+
@stack.push num
|
86
|
+
end
|
87
|
+
|
88
|
+
def op_right
|
89
|
+
@dir = :right
|
90
|
+
end
|
91
|
+
|
92
|
+
def op_down
|
93
|
+
@dir = :down
|
94
|
+
end
|
95
|
+
|
96
|
+
def op_up
|
97
|
+
@dir = :up
|
98
|
+
end
|
99
|
+
|
100
|
+
def op_left
|
101
|
+
@dir = :left
|
102
|
+
end
|
103
|
+
|
104
|
+
def op_pop
|
105
|
+
pop
|
106
|
+
end
|
107
|
+
|
108
|
+
def op_add
|
109
|
+
push pop+pop
|
110
|
+
end
|
111
|
+
|
112
|
+
def op_sub
|
113
|
+
push -pop+pop
|
114
|
+
end
|
115
|
+
|
116
|
+
def op_mul
|
117
|
+
push pop*pop
|
118
|
+
end
|
119
|
+
|
120
|
+
def op_div
|
121
|
+
a,b=pop,pop
|
122
|
+
if a == 0
|
123
|
+
push $stdin.readline.to_i
|
124
|
+
else
|
125
|
+
push b/a
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def op_mod
|
130
|
+
a,b=pop,pop
|
131
|
+
if a == 0
|
132
|
+
push $stdin.readline.to_i
|
133
|
+
else
|
134
|
+
push b%a
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def op_not
|
139
|
+
push pop == 0 ? 1 : 0
|
140
|
+
end
|
141
|
+
|
142
|
+
def op_gt
|
143
|
+
push pop <= pop ? 0 : 1
|
144
|
+
end
|
145
|
+
|
146
|
+
def op_rnd
|
147
|
+
@dir = DIRS[rand DIRS.length]
|
148
|
+
end
|
149
|
+
|
150
|
+
def op_lr
|
151
|
+
@dir = pop == 0 ? :right : :left
|
152
|
+
end
|
153
|
+
|
154
|
+
def op_ud
|
155
|
+
@dir = pop == 0 ? :down : :up
|
156
|
+
end
|
157
|
+
|
158
|
+
def op_str
|
159
|
+
@str_mode = !@str_mode
|
160
|
+
end
|
161
|
+
|
162
|
+
def op_dup
|
163
|
+
a = pop
|
164
|
+
push a; push a
|
165
|
+
end
|
166
|
+
|
167
|
+
def op_swp
|
168
|
+
a,b = pop, pop
|
169
|
+
push a; push b
|
170
|
+
end
|
171
|
+
|
172
|
+
def op_outi
|
173
|
+
print pop
|
174
|
+
end
|
175
|
+
|
176
|
+
def op_outc
|
177
|
+
print pop.chr
|
178
|
+
end
|
179
|
+
|
180
|
+
def op_get
|
181
|
+
y,x = pop, pop
|
182
|
+
push @code[x][y]
|
183
|
+
end
|
184
|
+
|
185
|
+
def op_put
|
186
|
+
v,y,x = pop, pop,pop
|
187
|
+
@code[x][y] = v.chr
|
188
|
+
end
|
189
|
+
|
190
|
+
def op_aski
|
191
|
+
c = $stdin.readline
|
192
|
+
push c.to_i
|
193
|
+
end
|
194
|
+
|
195
|
+
def op_askc
|
196
|
+
c = $stdin.getc
|
197
|
+
push c
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
def pop
|
202
|
+
return 0 if @stack.length == 0
|
203
|
+
@stack.pop
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
data/rubyfunge.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path("../lib/rubyfunge/version", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "rubyfunge"
|
6
|
+
s.version = Rubyfunge::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ['Zoltan Dezso']
|
9
|
+
s.email = ['dezso.zoltan@gmail.com']
|
10
|
+
s.homepage = "http://github.com/zaki/rubyfunge"
|
11
|
+
s.summary = "A Befunge-93 interpreter written in ruby."
|
12
|
+
s.description = "A Befunge-93 interpreter written in ruby."
|
13
|
+
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
s.rubyforge_project = "rubyfunge"
|
16
|
+
|
17
|
+
s.add_development_dependency "bundler", ">= 1.0.0"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
21
|
+
s.require_path = 'lib'
|
22
|
+
end
|
data/test/rubyfunge.spec
ADDED
@@ -0,0 +1,261 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'rubyfunge')
|
2
|
+
require "spec"
|
3
|
+
describe "RubyFunge" do
|
4
|
+
before(:each) do
|
5
|
+
@rbf = RubyFunge::RubyFunge.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should go right" do
|
9
|
+
@rbf.run('>>>>>>>>>>@')
|
10
|
+
@rbf.x.should == 10
|
11
|
+
@rbf.y.should == 0
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should go down" do
|
15
|
+
@rbf.run(
|
16
|
+
<<-EOS
|
17
|
+
v
|
18
|
+
@
|
19
|
+
EOS
|
20
|
+
)
|
21
|
+
@rbf.x.should == 0
|
22
|
+
@rbf.y.should == 1
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should push numbers on the stack" do
|
26
|
+
@rbf.run('0123456789@')
|
27
|
+
@rbf.x.should == 10
|
28
|
+
@rbf.y.should == 0
|
29
|
+
@rbf.stack.should == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should pop numbers from the stack" do
|
33
|
+
@rbf.run('0123456789$$$$$@')
|
34
|
+
@rbf.x.should == 15
|
35
|
+
@rbf.y.should == 0
|
36
|
+
@rbf.stack.should == [0, 1, 2, 3, 4]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should add numbers on the stack" do
|
40
|
+
@rbf.run('35+@')
|
41
|
+
@rbf.stack.should == [8]
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should subtract numbers on the stack" do
|
45
|
+
@rbf.run('53-@')
|
46
|
+
@rbf.stack.should == [2]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should return 0 when stack is empty" do
|
50
|
+
@rbf.run('+@')
|
51
|
+
@rbf.stack.should == [0]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should multiply numbers on the stack" do
|
55
|
+
@rbf.run('35*@')
|
56
|
+
@rbf.stack.should == [15]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should divide numbers on the stack" do
|
60
|
+
@rbf.run('62/@')
|
61
|
+
@rbf.stack.should == [3]
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should modulo divide numbers on the stack" do
|
65
|
+
@rbf.run('72%@')
|
66
|
+
@rbf.stack.should == [1]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should push result of not" do
|
70
|
+
@rbf.run('0!5!@')
|
71
|
+
@rbf.stack.should == [1, 0]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should push result of greater than" do
|
75
|
+
@rbf.run('91`19`@')
|
76
|
+
@rbf.stack.should == [0, 1]
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should go in a random direction" do
|
80
|
+
@rbf.run(<<EOS
|
81
|
+
>v
|
82
|
+
^?012@
|
83
|
+
^<
|
84
|
+
EOS
|
85
|
+
)
|
86
|
+
@rbf.stack.should == [0, 1, 2]
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should honor left-right selector" do
|
90
|
+
@rbf.run(<<EOS
|
91
|
+
0_>>>>>v
|
92
|
+
@_1<<<<<
|
93
|
+
EOS
|
94
|
+
)
|
95
|
+
@rbf.stack.length.should == 0
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should honor up-down selector" do
|
99
|
+
@rbf.run(<<EOS
|
100
|
+
0|1@ >@
|
101
|
+
>>>>1|
|
102
|
+
>3@
|
103
|
+
EOS
|
104
|
+
)
|
105
|
+
@rbf.stack.length.should == 0
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should start ascii mode" do
|
109
|
+
@rbf.run('"!dlrow ,olleH"123@')
|
110
|
+
@rbf.stack.should == 'Hello, world!'.reverse.split(//).map {|c| c[0]} + [1, 2, 3]
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should duplicate values on the stack" do
|
114
|
+
@rbf.run('5:@')
|
115
|
+
@rbf.stack.should == [5, 5]
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should swap values on the stack" do
|
119
|
+
@rbf.run('51\@')
|
120
|
+
@rbf.stack.should == [1, 5]
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should print integer from stack" do
|
124
|
+
result = redirect_stdout do
|
125
|
+
@rbf.run('15.@')
|
126
|
+
end
|
127
|
+
result.should == '5'
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should print string from stack" do
|
131
|
+
result = redirect_stdout do
|
132
|
+
@rbf.run('91+6*5+,@')
|
133
|
+
end
|
134
|
+
result.should == 'A'
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should skip next instruction" do
|
138
|
+
@rbf.run('1#23#45#6@')
|
139
|
+
@rbf.stack.should == [1,3,5]
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should skip next instruction and keep skipping if its also #" do
|
143
|
+
@rbf.run('1#2#3#4#5678@')
|
144
|
+
@rbf.stack.should == [1, 6, 7, 8]
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should get value of program instruction" do
|
148
|
+
@rbf.run('09g@<<<<<0')
|
149
|
+
@rbf.stack.should == [48]
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should put value of program instruction" do
|
153
|
+
@rbf.run('0991+6*5+p@<<<<<0')
|
154
|
+
@rbf.stack.should == []
|
155
|
+
@rbf.code[0][9].chr.should == 'A'
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should ask the user for a number" do
|
159
|
+
redirect_stdin("0009") do
|
160
|
+
@rbf.run('01234&@')
|
161
|
+
@rbf.stack.should == [0, 1, 2, 3, 4, 9]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should ask the user for a character" do
|
166
|
+
redirect_stdin("ABCD") do
|
167
|
+
@rbf.run('01234~@')
|
168
|
+
@rbf.stack.should == [0, 1, 2, 3, 4, 65]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should solve fizzbuzz" do
|
173
|
+
code = <<-EOS
|
174
|
+
55*4*v _ v )
|
175
|
+
v <>:1-:^ )
|
176
|
+
|:<$ < ,*48 < )
|
177
|
+
@>0"zzif">:#,_$ v )
|
178
|
+
>:3%!| >0"zzub">:#,_$^ )
|
179
|
+
>:5%!| )
|
180
|
+
v "buzz"0<>:. ^ )
|
181
|
+
|!%5: < )
|
182
|
+
>:#,_ $> ^)
|
183
|
+
EOS
|
184
|
+
result = redirect_stdout do
|
185
|
+
@rbf.run(code)
|
186
|
+
end
|
187
|
+
|
188
|
+
result.should == "1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz " +
|
189
|
+
"16 17 fizz 19 buzz fizz 22 23 fizz buzz 26 fizz 28 29 fizzbuzz " +
|
190
|
+
"31 32 fizz 34 buzz fizz 37 38 fizz buzz 41 fizz 43 44 fizzbuzz " +
|
191
|
+
"46 47 fizz 49 buzz fizz 52 53 fizz buzz 56 fizz 58 59 fizzbuzz " +
|
192
|
+
"61 62 fizz 64 buzz fizz 67 68 fizz buzz 71 fizz 73 74 fizzbuzz 76 " +
|
193
|
+
"77 fizz 79 buzz fizz 82 83 fizz buzz 86 fizz 88 89 fizzbuzz 91 92 " +
|
194
|
+
"fizz 94 buzz fizz 97 98 fizz buzz "
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should wrap around to the right" do
|
198
|
+
@rbf.run <<-EOS
|
199
|
+
>>v
|
200
|
+
1@>>>
|
201
|
+
EOS
|
202
|
+
@rbf.stack.should == [1]
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should wrap around to the left" do
|
206
|
+
@rbf.run <<-EOS
|
207
|
+
>>>>>>v
|
208
|
+
<<<<<<< @1
|
209
|
+
EOS
|
210
|
+
@rbf.stack.should == [1]
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should wrap around at the bottom" do
|
214
|
+
@rbf.run <<-EOS
|
215
|
+
v1
|
216
|
+
v@
|
217
|
+
>v
|
218
|
+
v
|
219
|
+
v
|
220
|
+
EOS
|
221
|
+
@rbf.stack.should == [1]
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should wrap around at the top" do
|
225
|
+
@rbf.run <<-EOS
|
226
|
+
^
|
227
|
+
@
|
228
|
+
1
|
229
|
+
EOS
|
230
|
+
@rbf.stack.should == [1]
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should ask the user for a number when dividing by zero" do
|
234
|
+
redirect_stdin '5' do
|
235
|
+
@rbf.run('00/@')
|
236
|
+
@rbf.stack.should == [5]
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should ask the user for a number when modulo dividing by zero" do
|
241
|
+
redirect_stdin '5' do
|
242
|
+
@rbf.run('00%@')
|
243
|
+
@rbf.stack.should == [5]
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def redirect_stdout
|
248
|
+
oldstdout, $stdout = $stdout, StringIO.new
|
249
|
+
yield
|
250
|
+
$stdout.string
|
251
|
+
ensure
|
252
|
+
$stdout = oldstdout
|
253
|
+
end
|
254
|
+
|
255
|
+
def redirect_stdin(str)
|
256
|
+
oldstdin, $stdin = $stdin, StringIO.new(str)
|
257
|
+
yield
|
258
|
+
ensure
|
259
|
+
$stdin = oldstdin
|
260
|
+
end
|
261
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubyfunge
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Zoltan Dezso
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-19 00:00:00 +09:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: bundler
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 1.0.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
description: A Befunge-93 interpreter written in ruby.
|
38
|
+
email:
|
39
|
+
- dezso.zoltan@gmail.com
|
40
|
+
executables:
|
41
|
+
- rubyfunge
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files: []
|
45
|
+
|
46
|
+
files:
|
47
|
+
- Gemfile
|
48
|
+
- Rakefile
|
49
|
+
- bin/rubyfunge
|
50
|
+
- lib/rubyfunge.rb
|
51
|
+
- lib/rubyfunge/rubyfunge.rb
|
52
|
+
- lib/rubyfunge/version.rb
|
53
|
+
- rubyfunge.gemspec
|
54
|
+
- test/rubyfunge.spec
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: http://github.com/zaki/rubyfunge
|
57
|
+
licenses: []
|
58
|
+
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 3
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 23
|
79
|
+
segments:
|
80
|
+
- 1
|
81
|
+
- 3
|
82
|
+
- 6
|
83
|
+
version: 1.3.6
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project: rubyfunge
|
87
|
+
rubygems_version: 1.3.7
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: A Befunge-93 interpreter written in ruby.
|
91
|
+
test_files: []
|
92
|
+
|