rubyfunge 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|