befunge98 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/befunge98.gemspec +18 -0
  4. data/bin/befunge98 +9 -0
  5. data/lib/befunge98.rb +228 -0
  6. data/test.rb +164 -0
  7. metadata +64 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 01e0f95f403d53d6741cd5490d880b94e64a5f92
4
+ data.tar.gz: 43de87a5d275630c9f1616ce66e0ac336866660a
5
+ SHA512:
6
+ metadata.gz: 2f889c5f8c5e79f34effd2b81ffe1a2ce6b25e8c6b7b520dbae380a08e7e5c01fdce6cff813fa1033d2263cfe390c9f717b0cb6ce000b370021d81813f97646b
7
+ data.tar.gz: 0d5ddb8725d2203f18fafe2cc4f0fcfac044e681b0f69abc4871e8adb4b3f9332193620f03b037983681ab3281e3f84048008bb991d77ad82127d65dd529c496
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Victor Maslov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "befunge98"
3
+ spec.version = "0.0.1"
4
+ spec.summary = "[WIP] Probably the first Befunge-98 interpreter in Ruby"
5
+
6
+ spec.author = "Victor Maslov aka Nakilon"
7
+ spec.email = "nakilon@gmail.com"
8
+ spec.license = "MIT"
9
+ spec.metadata = {"source_code_uri" => "https://github.com/nakilon/befunge98"}
10
+
11
+ spec.require_path = "lib"
12
+ spec.add_development_dependency "minitest"
13
+
14
+ # spec.bindir = "bin"
15
+ # spec.executable = "befunge98"
16
+ spec.test_file = "test.rb"
17
+ spec.files = %w{ LICENSE befunge98.gemspec lib/befunge98.rb bin/befunge98 }
18
+ end
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ Signal.trap :INT do
4
+ abort "\n(interrupted by SIGINT)"
5
+ end
6
+
7
+ require_relative "../lib/befunge98"
8
+
9
+ exit Befunge98(File.read(ARGV.first), STDOUT).exitcode
@@ -0,0 +1,228 @@
1
+ Encoding::default_internal = Encoding::default_external = "ASCII-8BIT"
2
+
3
+ def Befunge98 source, stdout = StringIO.new, stdin = STDIN
4
+ code = source.tap{ |_| fail "empty source" if _.empty? }.split(?\n).map(&:bytes)
5
+
6
+ stacks = [stack = []]
7
+ pop = ->{ stack.pop || 0 }
8
+
9
+ sx = sy = ox = oy = 0
10
+ dx, dy = 1, 0
11
+ x, y = -1, 0
12
+ ds = [[0,1], [1,0], [0,-1], [-1,0]]
13
+ go_west = ->{ dx, dy = *ds[3] }
14
+ go_east = ->{ dx, dy = *ds[1] }
15
+ go_north = ->{ dx, dy = *ds[2] }
16
+ go_south = ->{ dx, dy = *ds[0] }
17
+ reflect = ->{ dy, dx = [-dy, -dx] }
18
+ move = lambda do
19
+ y, x = y + dy, x + dx
20
+ next if sy + oy + y >= 0 && code[sy + oy + y] && sx + ox + x >= 0 && code[sy + oy + y][sx + ox + x]
21
+ top = code. index{ |l| !(l - [32, nil]).empty? }
22
+ bottom = code.rindex{ |l| !(l - [32, nil]).empty? }
23
+ left = code.map{ |l| l. index{ |c| c && c != 32 } }.compact.min
24
+ right = code.map{ |l| l.rindex{ |c| c && c != 32 } }.compact.max
25
+ STDERR.puts [code, top..bottom, left..right, [y, x], [dy, dx]].inspect if ENV["DEBUG"]
26
+ ty, tx = y, x
27
+ next if loop do
28
+ zy, zx = sy + oy + ty, sx + ox + tx
29
+ break unless (top..bottom).include?(zy) && (left..right).include?(zx)
30
+ break true if code[zy] && code[zy][zx] && code[zy][zx] != 32
31
+ ty, tx = ty + dy, tx + dx
32
+ end
33
+ ty, tx = y - dy, x - dx
34
+ loop do
35
+ STDERR.puts [ty, tx].inspect if ENV["DEBUG"]
36
+ zy, zx = sy + oy + ty, sx + ox + tx
37
+ break unless (top..bottom).include?(zy) && (left..right).include?(zx)
38
+ y, x = ty, tx if code[zy] && code[zy][zx] && code[zy][zx] != 32
39
+ ty, tx = ty - dy, tx - dx
40
+ end
41
+ end
42
+
43
+ stringmode = jump_over = false
44
+ iterate = 0
45
+
46
+ get = ->{ (code[sy + oy + y] || [])[sx + ox + x] || 32 }
47
+ loop do
48
+ if iterate.zero?
49
+ move[]
50
+ char = get[]
51
+ else
52
+ iterate -= 1
53
+ end
54
+ STDERR.puts [[y, x], char.chr, stack].inspect if ENV["DEBUG"]
55
+
56
+ next stack << char if stringmode && char.chr != ?"
57
+ next unless (33..126).include? char
58
+ next if jump_over && char.chr != ?;
59
+ case char.chr
60
+ when ?; ; jump_over ^= true
61
+
62
+ ### 93
63
+ when ?" ; stringmode ^= true
64
+ when ?0..?9 ; stack << char.chr.to_i
65
+ when ?$ ; pop[]
66
+ when ?: ; stack.concat [pop[]] * 2
67
+ when ?\\ ; stack.concat [pop[], pop[]]
68
+ when ?# ; move[] # "adds the delta to the position"
69
+ when ?> ; go_east[]
70
+ when ?< ; go_west[]
71
+ when ?^ ; go_north[]
72
+ when ?v ; go_south[]
73
+ when ?? ; [go_east, go_west, go_north, go_south].sample[]
74
+ when ?+ ; stack << (pop[] + pop[])
75
+ when ?- ; stack << -(pop[] - pop[])
76
+ when ?* ; stack << (pop[] * pop[])
77
+ when ?/ ; b, a = pop[], pop[]; stack << (b.zero? ? 0 : a / b)
78
+ when ?% ; b, a = pop[], pop[]; stack << (b.zero? ? 0 : a % b)
79
+ when ?| ; pop[].zero? ? go_south[] : go_north[]
80
+ when ?_ ; pop[].zero? ? go_east[] : go_west[]
81
+ when ?~ ; if c = stdin.getbyte then stack << c else reflect[] end
82
+ when ?&
83
+ getc = ->{ stdin.getc or (reflect[]; throw nil) }
84
+ catch nil do
85
+ nil until (?0..?9).include? c = getc[]
86
+ while (?0..?9).include? cc = getc[] ; c << cc end
87
+ stack << c.to_i
88
+ end
89
+ when ?, ; stdout.print pop[].chr
90
+ when ?. ; stdout.print "#{pop[]} "
91
+ when ?! ; stack << (pop[].zero? ? 1 : 0)
92
+ when ?` ; stack << (pop[]<pop[] ? 1 : 0)
93
+ ### Funge-98 Final Specification:
94
+ # A Funge-98 program should also be able to rely on the memory mechanism acting as
95
+ # if a cell contains blank space (ASCII 32) if it is unallocated, and setting memory
96
+ # to be full of blank space cells upon actual allocation (program load, or p instruction)
97
+ when ?g
98
+ y, x = [y, x].tap{ y, x = pop[], pop[]; stack << get[] }
99
+ when ?p
100
+ py, px, v = pop[], pop[], pop[]
101
+ if 0 > (d = sx + ox + px)
102
+ sx -= d
103
+ code.map!{ |l| Array.new(-d, 32) + l }
104
+ end
105
+ if 0 > (d = sy + oy + py)
106
+ sy -= d
107
+ code = Array.new(-d, []) + code
108
+ end
109
+ code[sy + oy + py] ||= []
110
+ code[sy + oy + py][sx + ox + px] = v
111
+ when ?@ ; return Struct.new(:stdout, :stack, :exitcode).new(stdout, stack, 0)
112
+
113
+ ### 98
114
+ when ?q ; return Struct.new(:stdout, :stack, :exitcode).new(stdout, stack, pop[])
115
+ when ?a..?f ; stack << char - ?a.ord + 10
116
+ when ?n ; stack.clear
117
+ when ?'
118
+ move[]
119
+ stack << get[]
120
+ when ?s
121
+ move[]
122
+ code[sy + oy + y] ||= []
123
+ code[sy + oy + y][sx + ox + x] = pop[]
124
+ when ?[ ; dy, dx = ds[(ds.index([dy, dx]) - 1) % 4]
125
+ when ?] ; dy, dx = ds[(ds.index([dy, dx]) + 1) % 4]
126
+ when ?w ; dy, dx = ds[(ds.index([dy, dx]) + (pop[] > pop[] ? -1 : 1)) % 4]
127
+ when ?r ; reflect[]
128
+ when ?x ; dy, dx = [pop[], pop[]]
129
+ when ?j
130
+ if 0 < t = pop[]
131
+ t.times{ move[] }
132
+ else
133
+ reflect[]
134
+ t.times{ move[] }
135
+ reflect[]
136
+ end
137
+ when ?k
138
+ iterate = pop[]
139
+ fail if char != 32 || char.chr != ?;
140
+ move[]
141
+ char = get[]
142
+ when ?{
143
+ toss = if 0 > n = pop[]
144
+ stack.concat [0] * -n
145
+ []
146
+ else
147
+ stack.pop n
148
+ end
149
+ stack << ox << oy
150
+ ox += x + dx
151
+ oy += y + dy
152
+ stacks << stack = toss
153
+ when ?{
154
+ if 1 == stacks.size
155
+ reflect[]
156
+ elsif 0 > n = pop[]
157
+ stacks.pop
158
+ stack = stacks.last
159
+ oy, ox = pop[], pop[]
160
+ stack.pop -n
161
+ else
162
+ t = stack.pop n
163
+ stacks.pop
164
+ stack = stacks.last
165
+ oy, ox = pop[], pop[]
166
+ stack.concat t
167
+ end
168
+ when ?u
169
+ if 1 == stacks.size
170
+ reflect[]
171
+ elsif 0 > n = pop[]
172
+ -n.times{ stack[-2] << pop[] }
173
+ else
174
+ n.times{ stack << stack[-2].pop }
175
+ end
176
+ when ?i
177
+ fail "TODO"
178
+ s = ""
179
+ s << c.chr until (c = pop[]).zero?
180
+ f = pop[]
181
+ va = [pop[], pop[]]
182
+ # ...
183
+ when ?o
184
+ fail "TODO"
185
+ s = ""
186
+ s << c.chr until (c = pop[]).zero?
187
+ f = pop[]
188
+ # y, x = pop[], pop[]
189
+ # h, w = pop[], pop[]
190
+ # ...
191
+ when ?=
192
+ s = ""
193
+ s << c.chr until (c = pop[]).zero?
194
+ system s, out: File::NULL, err: File::NULL
195
+ stack << $?.exitstatus
196
+ when ?(, ?)
197
+ fail "TODO"
198
+ # pop[].times.inject(0){ |i,| i * 256 + pop[] }
199
+ # reflect[]
200
+ when ?y
201
+ y = pop[]
202
+ ss = stack.size
203
+ stack << 0
204
+ stack << 255
205
+ stack << 0
206
+ stack << Gem.loaded_specs.values.find{ |_| _.lib_dirs_glob == File.absolute_path(__dir__) }.version.segments.join.to_i
207
+ stacK << 0
208
+ stack << File::SEPARATOR.ord
209
+ stack << 2
210
+ stack << 0
211
+ stack << 0
212
+ stack << y << x
213
+ stack << dy << dx
214
+ stack << oy << ox
215
+ stack << [0, 0] # TODO: 1 vector containing the least point which contains a non-space cell, relative to the origin (env)
216
+ stack << [0, 0] # TODO: 1 vector containing the greatest point which contains a non-space cell, relative to the least point (env)
217
+ t = Time.now
218
+ stack << (t.year - 1900) * 256 * 256 + t.month * 256 + t.day
219
+ stack << t.hour * 256 * 256 + t.min * 256 + t.sec
220
+ stack << stacks.size
221
+ stack.concat stacks.map &:size; stack[-1] = ss
222
+ stack.concat ARGV.map{ |_| _.chars.map(&:ord) + [0] } + [0]
223
+ stack.concat ENV.map{ |k, v| "#{k}=#{v}".chars.map(&:ord) + [0] } + [0]
224
+ stack << stack[1-y].tap{ stack = stack.take ss } if y > 0
225
+
226
+ end
227
+ end
228
+ end
data/test.rb ADDED
@@ -0,0 +1,164 @@
1
+ require "minitest/autorun"
2
+ require_relative "lib/befunge98"
3
+
4
+ describe "lib" do
5
+ it "prints to STDOUT" do skip end
6
+ it "prints to String" do skip end
7
+
8
+ describe "Befunge-93" do
9
+ describe "(rely on @ and pop from empty stack)" do
10
+ before do
11
+ assert_equal 0, Befunge98(?@).exitcode
12
+ end
13
+
14
+ it '"' do
15
+ assert_equal [?@.ord], Befunge98("\"@").stack
16
+ end
17
+
18
+ describe "(rely on 0..9)" do
19
+ before do
20
+ assert_equal (0..9).to_a, Befunge98("0123456789@").stack
21
+ end
22
+
23
+ it "$" do
24
+ assert_equal [1], Befunge98("$12$@").stack
25
+ end
26
+ it ":" do
27
+ assert_equal [0, 0, 1, 1], Befunge98(":1:@").stack
28
+ end
29
+ it "\\" do
30
+ assert_equal [0, 1, 0], Befunge98("\\1\\@").stack
31
+ end
32
+ it "#" do
33
+ assert_equal [1], Befunge98('#@1#').stack
34
+ end
35
+ it "><^v" do
36
+ assert_equal [1, 2, 3, 4], Befunge98("<@^1\n"\
37
+ "3v>\n"\
38
+ "5425").stack
39
+ end
40
+ it "?" do
41
+ assert_equal [[1], [2], [3], [4]], 100.times.map{
42
+ Befunge98("?1@2\n"\
43
+ "4555\n"\
44
+ "@555\n"\
45
+ "3555").stack
46
+ }.uniq.sort
47
+ end
48
+ it "+" do
49
+ assert_equal [90000], Befunge98("++"+"9+"*10000+"@").stack
50
+ end
51
+ it "-" do
52
+ assert_equal [-90000], Befunge98("--"+"9-"*10000+"@").stack
53
+ end
54
+ it "*" do
55
+ assert_equal [0, 2**31], Befunge98("**2*2"+"2*"*30+"@").stack
56
+ end
57
+ it "/" do
58
+ assert_equal [0, 0, 1, 2], Befunge98("//12/22/21/@").stack
59
+ end
60
+ it "/ with -" do
61
+ assert_equal [-1, -2, -1, -2], Befunge98("1-2/02-1/102-/201-/@").stack
62
+ end
63
+ it "|_" do
64
+ assert_equal [1, 3], Befunge98("00|\n"\
65
+ "41_13@\n"\
66
+ "42_23@").stack
67
+ assert_equal [2, 4], Befunge98("11|\n"\
68
+ "41_13@\n"\
69
+ "42_23@").stack
70
+ assert_equal [2, 3], Befunge98("01|\n"\
71
+ "41_13@\n"\
72
+ "42_23@").stack
73
+ assert_equal [1, 4], Befunge98("10|\n"\
74
+ "41_13@\n"\
75
+ "42_23@").stack
76
+ end
77
+ it "-|_" do
78
+ assert_equal [2, 4], Befunge98("1-01-|\n"\
79
+ " 41_13@\n"\
80
+ " 42_23@").stack
81
+ end
82
+ it ", with +" do
83
+ assert_equal "\x00\x0A\xFF\x00".b, Befunge98(",55+,5"+"5+"*50+",,@").stdout.string
84
+ end
85
+ it ". with +" do
86
+ assert_equal "0 10 255 0 ", Befunge98(".55+.5"+"5+"*50+"..@").stdout.string
87
+ end
88
+ it "!" do
89
+ assert_equal [1, 1, 0, 0], Befunge98("!0!1!2!@").stack
90
+ end
91
+ it "! with -" do
92
+ assert_equal [0, 0], Befunge98("1-!02-!@").stack
93
+ end
94
+ it "`" do
95
+ assert_equal [0, 0, 1], Befunge98("`01`10`@").stack
96
+ end
97
+ it "pg with -" do
98
+ assert_equal [3, 4], Befunge98("302-01-p401-02-p02-01-g01-02-g@").stack
99
+ end
100
+ end
101
+ end
102
+
103
+ it "# test by @lifthrasiir" do
104
+ assert_equal "3 ", Befunge98(
105
+ <<~HEREDOC
106
+ #;v ; 1.@
107
+ ># ;2.@;3.@
108
+ HEREDOC
109
+ ).stdout.string
110
+ end
111
+ end
112
+
113
+ describe "Befunge98 operations" do
114
+ it "q" do
115
+ assert_equal 0, Befunge98(" q").exitcode
116
+ assert_equal 1, Befunge98("1q").exitcode
117
+ end
118
+
119
+ describe "(rely on @)" do
120
+ before do
121
+ assert_equal 0, Befunge98(?@).exitcode
122
+ end
123
+
124
+ it "~" do
125
+ assert_equal [2], Befunge98("~1@2", StringIO.new, StringIO.new).stack
126
+ assert_equal [0, 10, 255, 0], Befunge98("~~~~@", StringIO.new,
127
+ StringIO.new.tap{ |s| [0, 10, 255, 0].reverse_each &s.method(:ungetbyte) }
128
+ ).stack
129
+ end
130
+ it "&" do
131
+ assert_equal [2], Befunge98("&1@2", StringIO.new, StringIO.new).stack
132
+ [?\0, ?\xa, ?\xff].each do |c|
133
+ assert_equal [12, 34], Befunge98("&&@", StringIO.new,
134
+ StringIO.new.tap{ |s| "#{c}-12#{c}-34#{c}".bytes.reverse_each &s.method(:ungetbyte) }
135
+ ).stack
136
+ end
137
+ end
138
+
139
+ describe "(rely on a..f)" do
140
+ before do
141
+ assert_equal (10..15).to_a, Befunge98("abcdef@").stack
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ end
148
+
149
+ describe "bin" do
150
+ it "hello world" do
151
+ require "open3"
152
+ require "tempfile"
153
+ file = Tempfile.new "temp.b98"
154
+ begin
155
+ file.write '64+"!dlroW ,olleH">:#,_@'
156
+ file.flush
157
+ string, status = Open3.capture2 "bundle exec ruby bin/befunge98 #{file.path}"
158
+ ensure
159
+ file.close
160
+ file.unlink
161
+ end
162
+ assert_equal ["Hello, World!\n", 0], [string, status.exitstatus]
163
+ end
164
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: befunge98
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Victor Maslov aka Nakilon
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-09-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email: nakilon@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - LICENSE
34
+ - befunge98.gemspec
35
+ - bin/befunge98
36
+ - lib/befunge98.rb
37
+ - test.rb
38
+ homepage:
39
+ licenses:
40
+ - MIT
41
+ metadata:
42
+ source_code_uri: https://github.com/nakilon/befunge98
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.5.2.3
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: "[WIP] Probably the first Befunge-98 interpreter in Ruby"
63
+ test_files:
64
+ - test.rb