fish.rb 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +1 -0
- data/README.md +23 -0
- data/Rakefile +24 -0
- data/examples/fibonacci.fsh +2 -0
- data/examples/hello.fsh +2 -0
- data/examples/quine.fsh +1 -0
- data/fish.rb +194 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MjA2MGU2ZDJlODdkMmMyMjY4NzUxMTQ0ZGU2YzIwYTY1ODNiNzdmNg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
M2M3MTYyYWY0MDkyZjA4YzU1MWYwNzM3NzJiY2NmYjRlZTk2ZmQ4Nw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NjFiMjE3ZGNlMGViMzRiNGZmNGY1ZWIwYmI0M2ViNjg2NGZmZjE1OWI2N2Zm
|
10
|
+
Y2Q3NjA2N2UxYmU1YTdmMmIwZDEyYzQyMTFhZGM0NjY5YTk3ZjVlYjg2MTk4
|
11
|
+
OTMzZjA0NmExY2ZjYTY4YTNlNjRiNjljZTY0OTkwMzlhNDM0Nzg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YWI3NzgxYzI1Mzk4ZDhiOTBhZTI5ODFkMjNmY2Y4MThlYWQ3NDlhMGQ5NDIw
|
14
|
+
NTIzZDk2MmU1NmU4NTJlY2I5OGE1NDk1M2NlMjU3Zjc3MTdlYWM2MjQxMWI4
|
15
|
+
YzI3NDcyODNkNjcwY2VjNmMzOTA3N2M2ODg0ZGU5OGMwMjA2YmY=
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/pkg
|
data/README.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
fish.rb
|
2
|
+
=======
|
3
|
+
|
4
|
+
A [fish](http://esolangs.org/wiki/Fish) interpreter in ruby.
|
5
|
+
|
6
|
+
There is a slew of examples in the examples directory.
|
7
|
+
|
8
|
+
Todo
|
9
|
+
----
|
10
|
+
|
11
|
+
Things are mostly done, it needs some thorough testing
|
12
|
+
|
13
|
+
|
14
|
+
Thoughts
|
15
|
+
--------
|
16
|
+
|
17
|
+
Something that would be fun to implment is jit. While a straight
|
18
|
+
compiler would be very dificult due to reflection I don't think
|
19
|
+
jit would be to hard if implemented the way rubinius does thiers.
|
20
|
+
It would take a lot of time though, so it is doubtful it will happen.
|
21
|
+
|
22
|
+
If you have trouble with fish.rb for whatever reason just open an issue.
|
23
|
+
I love it when poeple use my software so give it a go.
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Created by GitVers - Fill the commented stuff out yourself
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rubygems/package_task'
|
5
|
+
|
6
|
+
spec = Gem::Specification.new do |gem|
|
7
|
+
gem.name = File.basename(`git rev-parse --show-toplevel`).chop
|
8
|
+
gem.version = `gitver version`
|
9
|
+
|
10
|
+
gem.author = `git config --get user.name`
|
11
|
+
gem.email = `git config --get user.email`
|
12
|
+
gem.homepage = "https://github.com/zerocool/fish.rb"
|
13
|
+
gem.summary = "A fish interpreter"
|
14
|
+
gem.description = "Ruby fish interpreter for the newer multi-stack version of fish"
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($\)
|
17
|
+
|
18
|
+
gem.bindir = "./"
|
19
|
+
gem.executables = ["fish.rb"]
|
20
|
+
end
|
21
|
+
|
22
|
+
Gem::PackageTask.new(spec) do |pkg|
|
23
|
+
pkg.gem_spec = spec
|
24
|
+
end
|
data/examples/hello.fsh
ADDED
data/examples/quine.fsh
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
"ar00g!;oooooooooo|
|
data/fish.rb
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
class CodeBox
|
7
|
+
def initialize lines
|
8
|
+
@data = {}
|
9
|
+
lines.each_with_index do |line, y_idx|
|
10
|
+
x_idx = 0
|
11
|
+
line.each_char do |c|
|
12
|
+
@data[[x_idx, y_idx]] = c
|
13
|
+
x_idx += 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.[] x, y; @data[[x, y]] end
|
18
|
+
def self.at pt; self[pt[0], pt[1]] end
|
19
|
+
def self.set pt, what; @data[[pt[0], pt[1]]] = what end
|
20
|
+
|
21
|
+
def self.r pt
|
22
|
+
a = @data.select { |k, v| k[1] == pt[1] }
|
23
|
+
xs = (a.keys.collect { |k| k[0] }).sort
|
24
|
+
tmp = xs.select { |v| v > pt[0] }
|
25
|
+
pt.replace [(tmp == [] ? xs.min : tmp.min), pt[1]]
|
26
|
+
at pt
|
27
|
+
end
|
28
|
+
def self.l pt
|
29
|
+
a = @data.select { |k, v| k[1] == pt[1] }
|
30
|
+
xs = (a.keys.collect { |k| k[0] }).sort
|
31
|
+
tmp = xs.select { |v| v < pt[0] }
|
32
|
+
pt.replace [(tmp == [] ? xs.max : tmp.max), pt[1]]
|
33
|
+
at pt
|
34
|
+
end
|
35
|
+
def self.d pt
|
36
|
+
a = @data.select { |k, v| k[0] == pt[0] }
|
37
|
+
ys = (a.keys.collect { |k| k[1] }).sort
|
38
|
+
tmp = ys.select { |v| v > pt[1] }
|
39
|
+
pt.replace [pt[0], (tmp == [] ? ys.min : tmp.min)]
|
40
|
+
at pt
|
41
|
+
end
|
42
|
+
def self.u pt
|
43
|
+
a = @data.select { |k, v| k[0] == pt[0] }
|
44
|
+
ys = (a.keys.collect { |k| k[1] }).sort
|
45
|
+
tmp = ys.select { |v| v < pt[1] }
|
46
|
+
pt.replace [pt[0], (tmp == [] ? ys.max : tmp.max)]
|
47
|
+
at pt
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end # class CodeBox
|
51
|
+
|
52
|
+
class Stack
|
53
|
+
attr_accessor :data
|
54
|
+
def initialize data
|
55
|
+
@data = data
|
56
|
+
|
57
|
+
def self.pop cnt
|
58
|
+
if cnt > @data.length; abort 'something smells fishy... (stack error)' end
|
59
|
+
@data.pop cnt
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.safe_single_pop; @data.pop end
|
63
|
+
def self.push value; @data << value end
|
64
|
+
|
65
|
+
@reg = nil
|
66
|
+
def self.reg
|
67
|
+
if @reg.is_nil?
|
68
|
+
@reg = self.pop 1
|
69
|
+
else
|
70
|
+
self.push @reg
|
71
|
+
@reg = nil
|
72
|
+
ret
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end # class Stack
|
77
|
+
|
78
|
+
class Interpreter
|
79
|
+
@@prng = Random.new
|
80
|
+
@@stdout = $stdout
|
81
|
+
|
82
|
+
@@mirrors = {
|
83
|
+
'_' => { 'r' => 'r', 'l' => 'l', 'u' => 'd', 'd' => 'u' },
|
84
|
+
'|' => { 'r' => 'l', 'l' => 'r', 'u' => 'u', 'd' => 'd' },
|
85
|
+
'/' => { 'r' => 'u', 'l' => 'd', 'u' => 'r', 'd' => 'l' },
|
86
|
+
'\\' => { 'r' => 'd', 'l' => 'u', 'u' => 'l', 'd' => 'r' },
|
87
|
+
'#' => { 'r' => 'l', 'l' => 'r', 'u' => 'd', 'd' => 'u' },
|
88
|
+
}
|
89
|
+
|
90
|
+
@@div_chk = lambda do |d|
|
91
|
+
abort 'something smells fishy... (division by zero)' unless d != 0
|
92
|
+
end
|
93
|
+
|
94
|
+
@@ops = {
|
95
|
+
# control flow
|
96
|
+
'>' => lambda { |pt, dir, stks, box, cntl| dir.replace 'r' },
|
97
|
+
'<' => lambda { |pt, dir, stks, box, cntl| dir.replace 'l' },
|
98
|
+
'v' => lambda { |pt, dir, stks, box, cntl| dir.replace 'd' },
|
99
|
+
'^' => lambda { |pt, dir, stks, box, cntl| dir.replace 'u' },
|
100
|
+
'x' => lambda { |pt, dir, stks, box, cntl| dir.replace 'rlud'[@@prng.rand 4] },
|
101
|
+
'!' => lambda { |pt, dir, stks, box, cntl| box.send dir, pt },
|
102
|
+
'?' => lambda { |pt, dir, stks, box, cntl| v = stks[-1].safe_single_pop; box.send dir, pt unless !v.nil? and v != 0 },
|
103
|
+
'.' => lambda { |pt, dir, stks, box, cntl| pt.replace stks[-1].pop 2 },
|
104
|
+
# operators
|
105
|
+
'+' => lambda { |pt, dir, stks, box, cntl| x, y = stks[-1].pop 2; stks[-1].push x + y },
|
106
|
+
'-' => lambda { |pt, dir, stks, box, cntl| x, y = stks[-1].pop 2; stks[-1].push x - y },
|
107
|
+
'*' => lambda { |pt, dir, stks, box, cntl| x, y = stks[-1].pop 2; stks[-1].push x * y },
|
108
|
+
',' => lambda { |pt, dir, stks, box, cntl| x, y = stks[-1].pop 2; @@div_chk.call y; stks[-1].push x.to_f / y.to_f },
|
109
|
+
'%' => lambda { |pt, dir, stks, box, cntl| x, y = stks[-1].pop 2; @@div_chk.call y; stks[-1].push x % y },
|
110
|
+
'=' => lambda { |pt, dir, stks, box, cntl| x, y = stks[-1].pop 2; stks[-1].push (x == y ? 1 : 0) },
|
111
|
+
')' => lambda { |pt, dir, stks, box, cntl| x, y = stks[-1].pop 2; stks[-1].push (x > y ? 1 : 0) },
|
112
|
+
'(' => lambda { |pt, dir, stks, box, cntl| x, y = stks[-1].pop 2; stks[-1].push (x < y ? 1 : 0) },
|
113
|
+
# strings
|
114
|
+
'\'' => lambda { |pt, dir, stks, box, cntl| while ((a = box.send dir, pt) != '\'') do; stks[-1].push a.ord end },
|
115
|
+
'"' => lambda { |pt, dir, stks, box, cntl| while ((a = box.send dir, pt) != '"') do; stks[-1].push a.ord end },
|
116
|
+
# stack manipulation
|
117
|
+
':' => lambda { |pt, dir, stks, box, cntl| stks[-1].push stks[-1].data[-1] },
|
118
|
+
'~' => lambda { |pt, dir, stks, box, cntl| stks[-1].pop 1 },
|
119
|
+
'$' => lambda { |pt, dir, stks, box, cntl| x, y = stks[-1].pop 2; stks[-1].push y; stks[-1].push x },
|
120
|
+
'@' => lambda { |pt, dir, stks, box, cntl| x, y, z = stks[-1].pop 3; stks[-1].push z; stks[-1].push x; stks[-1].push y },
|
121
|
+
'}' => lambda { |pt, dir, stks, box, cntl| stks[-1].data.rotate! 1 },
|
122
|
+
'{' => lambda { |pt, dir, stks, box, cntl| stks[-1].data.rotate! -1 },
|
123
|
+
'r' => lambda { |pt, dir, stks, box, cntl| stks[-1].data.reverse! },
|
124
|
+
'l' => lambda { |pt, dir, stks, box, cntl| stks[-1].push stks[-1].data.length },
|
125
|
+
'[' => lambda { |pt, dir, stks, box, cntl| stks << (Stack.new stks[-1].pop stks[-1].pop 1) },
|
126
|
+
']' => lambda { |pt, dir, stks, box, cntl| stks[-1].data.concat stks.pop.data },
|
127
|
+
# io
|
128
|
+
'o' => lambda { |pt, dir, stks, box, cntl| @@stdout.write (stks[-1].pop 1)[0].round.chr },
|
129
|
+
'n' => lambda { |pt, dir, stks, box, cntl| @@stdout.write (stks[-1].pop 1)[0].to_s },
|
130
|
+
'i' => lambda { |pt, dir, stks, box, cntl| stks[-1].push (cntl[:ibuf].empty? ? -1 : cntl[:ibuf][0].ord); cntl[:ibuf] = cntl[:ibuf][1..-1] unless cntl[:ibuf].length < 2 },
|
131
|
+
# reflection
|
132
|
+
'g' => lambda { |pt, dir, stks, box, cntl| stks[-1].push (box.at stks[-1].pop 2).ord },
|
133
|
+
'p' => lambda { |pt, dir, stks, box, cntl| box.set (stks[-1].pop 2), (stks[-1].pop 1) },
|
134
|
+
# miscellaneous
|
135
|
+
'&' => lambda { |pt, dir, stks, box, cntl| stks[-1].reg },
|
136
|
+
';' => lambda { |pt, dir, stks, box, cntl| cntl[:done] = true },
|
137
|
+
' ' => lambda { |pt, dir, stks, box, cntl| }
|
138
|
+
}
|
139
|
+
|
140
|
+
@@mirrors.each do |k, v|
|
141
|
+
@@ops.merge! k => lambda { |pt, dir, stks, box, cntl| dir.replace v[dir] }
|
142
|
+
end
|
143
|
+
|
144
|
+
(0..9).to_a.each do |v|
|
145
|
+
@@ops.merge! v.to_s => lambda { |pt, dir, stks, box, cntl| stks[-1].push v }
|
146
|
+
end
|
147
|
+
|
148
|
+
('a'..'f').to_a.each do |v|
|
149
|
+
@@ops.merge! v => lambda { |pt, dir, stks, box, cntl| stks[-1].push (v.ord - 0x57) }
|
150
|
+
end
|
151
|
+
|
152
|
+
def initialize lines, options
|
153
|
+
@box = CodeBox.new lines.collect { |line| line.chomp.chomp }
|
154
|
+
|
155
|
+
@pt = [0, 0]
|
156
|
+
@dir = 'r'
|
157
|
+
@stks = []
|
158
|
+
@stks << (Stack.new [])
|
159
|
+
@cntl = {
|
160
|
+
:ibuf => options[:stdin],
|
161
|
+
:done => false,
|
162
|
+
}
|
163
|
+
|
164
|
+
|
165
|
+
op = @box.at @pt
|
166
|
+
while !@cntl[:done] do
|
167
|
+
op = ' ' if op.nil?
|
168
|
+
if options[:debug] >= 3; puts op end
|
169
|
+
func = @@ops[op]
|
170
|
+
abort 'something smells fishy... (invalid instruction)' if func.nil?
|
171
|
+
func.call @pt, @dir, @stks, @box, @cntl
|
172
|
+
op = @box.send @dir, @pt
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end # class Interpreter
|
176
|
+
|
177
|
+
|
178
|
+
options = {}
|
179
|
+
optparse = OptionParser.new do |opts|
|
180
|
+
opts.banner = "fish.rb <options> <files>"
|
181
|
+
|
182
|
+
opts.on '-h', '--help', 'Display help' do; puts opts; exit end
|
183
|
+
options[:debug] = 0
|
184
|
+
opts.on '-d [level]', '--debug [level]', 'Print debug messages (max of 3, defaults to 1)' do |opt|
|
185
|
+
options[:debug] = opt.to_i || options[:debug] = 1
|
186
|
+
end
|
187
|
+
options[:stdin] = ""
|
188
|
+
opts.on '-s str', '--stdin str', 'Stdin for fish script' do |opt|; options[:stdin] = opt end
|
189
|
+
opts.on '-r file', '--redir-stdout file', 'Redirect fish stdout to file' do |opt|
|
190
|
+
options[:stdout] = File.open opt, 'w'
|
191
|
+
end
|
192
|
+
end.parse!
|
193
|
+
|
194
|
+
Interpreter.new (IO.readlines ARGV.shift), options
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fish.rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- ! 'floomby
|
8
|
+
|
9
|
+
'
|
10
|
+
autorequire:
|
11
|
+
bindir: ./
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-07-09 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
15
|
+
description: Ruby fish interpreter for the newer multi-stack version of fish
|
16
|
+
email: ! 'floomby@nmt.edu
|
17
|
+
|
18
|
+
'
|
19
|
+
executables:
|
20
|
+
- fish.rb
|
21
|
+
extensions: []
|
22
|
+
extra_rdoc_files: []
|
23
|
+
files:
|
24
|
+
- ./fish.rb
|
25
|
+
- .gitignore
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- examples/fibonacci.fsh
|
29
|
+
- examples/hello.fsh
|
30
|
+
- examples/quine.fsh
|
31
|
+
- fish.rb
|
32
|
+
homepage: https://github.com/zerocool/fish.rb
|
33
|
+
licenses: []
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 2.2.2
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: A fish interpreter
|
55
|
+
test_files: []
|