Lua 1640 (1558 non threading) chars
Threading version, golfed (1640 characters):
L=loadstring L(([[z=table p=z.insert P=z.remove W=io.write t="><^v/\\|_#x+-*,%=)(!?:~$@&r}{gponi;[].m"C=t.char B=t.byte F=t.match M=setmetatable Q=getfenv R=setfenv I=io.read w="@h1,0@h-1,0@h0,-1@h0,1@h-Y,-X@hY,X|X=-X|Y=-Y@h-X,-Y|z=math.random(1,4)R(f[('><^v'):sub(z,z)],Q())()|@c@a+@a)|@c-@a+@a)|@c@a*@a)|z=@a@pz~=0 @b@c@a/z)@rerror'Div by 0'@d|y=@az=@a@cz%y)|@c@a==@a@n1@g0)|@c@a>@a@n1@g0)|@c@a<@a@n1@g0)|@i|@p#s==0@gs[#s]==0 @b@i@d|@cs[#s])|@a|z=#s s[z@k-1]=s[z-1@k]|z=#s s[z@k-1@k-2]=s[z-1@k-2@k]|@pr @b@cr)r=N @rr=@a@d|z={}@o=1,#s@qz[#s-k+1]=s[k]@ds=z|@c1,@a)|@cP(s,1))|z=@a@cc[@a][z])|z,w=@a,@ac[@a][w]=z|W(C(@a))|W(@a)|z=I(1)while F(z,'%s')@qz=I(1)@d@cB(z))|os.exit()|T.N=1|P(T,I)@o=I,#T@qT[k].I=k@d|s=s==S@nl@gS|z=s==S@nl@gS @o=#s,1,-1@qp(z,P(s,1))@d|"z=1 f={}@o in t:gmatch"."@q_,z,s=w:find("|(.-)|",z)f[k]=L(s)@dT={m@j)@i @py>#c @by=0 @fy<0 @by=#c@d@px>#c[y]@nX==1 @bx=0 @fx<0 @bx=#c[y]@d@d,n@jx,y,X,Y)z=M({I=#T+1,l={},X=X@g1,Y=Y@g0,x=x@g0,y=y@g0},{__index=_G})z.s=z.l T[z.I]=z@d}c={}S={}T.n(-1)fh=arg[1]@nio.open(arg[1])@gio.stdin y=0 for l in fh:lines()@qc[y]=M({},{__index@j)return 32@d})@o=1,#l@qz=l:sub(k,k) @pnot i @b@pF(z@l@bi=z@d@pF(z,"[^\n\r]")@b@m@d@r@pz==i @bi=N@d@m@d@dy=y+1@dwhile #T>0@qfor I=1,#T@qt=T[I]R(1,t)R(T.m,t)()n,o=X,Y q=C(c[y][x])@pi @b@pF(q@l@bi=N @r@cc[y][x])@d@fF(q@l @bi=q @fF(q,"%x")@b@ctonumber(q,16))@fF(q,"[^ ]")@bsetfenv(f[q],t)()@d@d@pT.N@n(n~=X@go~=Y)@bT.n(x,y,X,Y)T.N=N X,Y=n,o@d@d]]):gsub("@(.)",{a="P(s)",b="then ",c="p(s,",d=" end ",f="elseif ",g=" or ",h="|X,Y=",i="x,y=x+X,y+Y",j="=function(",k="],s[z",l=[[,"['\"]")]],m="c[y][k-1]=B(z)",n=" and ",o="for k",p="if ",q=" do ",r="else "}))()
The threading version does some nasty hacks with setfenv and getfenv to eliminate the need for indexing for the different threads.
Threading version readable:
-- http://codegolf.stackexchange.com/questions/1595/interpret-fish
z=table
p=z.insert -- push
P=z.remove -- pop
W=io.write
t="><^v/\\|_#x+-*,%=)(!?:~$@&r}{gponi;[].m" -- all tokens
C=t.char
B=t.byte
F=t.match
M=setmetatable
Q=getfenv
R=setfenv
I=io.read
--w=("@d1,0@d-1,0@d0,-1@d0,1@d-Y,-X@dY,X|X=-X|Y=-Y@d-X,-Y|z=math.random(1,4)R(f[('><^v'):sub(z,z)],Q())()|@b@a+@a)|@b-@a+@a)|@b@a*@a)|z=@aif z~=0@i@b@a/z)else error'Div by 0'end|y=@az=@a@bz%y)|@b@a==@a @c@b@a>@a@c@b@a<@a@c@h|if #s==0 or s[#s]==0@i@h end|@bs[#s])|@a@gs[z-1]=@e]@g@e-2]=@e-2],s[z]|if r@i@br)r=N else r=@aend|z={}for k=1,#s do z[#s-k+1]=s[k]end s=z|@b1,@a)|@bP(s,1))|z=@a@bc[@a][z])|z,w=@a,@ac[@a][w]=z|W(C(@a))|W(@a)|z=I(1)while F(z,'%s')do z=I(1)end @bB(z))|os.exit()|T.N=1|P(T,I)for k=I,#T do T[k].I=k end|s@f|z@f for k=#s,1,-1 do p(z,P(s,1))end|"):gsub("@(.)",{a="P(s)",b="p(s,",c="and 1 or 0)|",d="|X,Y=",e="s[z-1],s[z",f="=s==S and l or S",g="|z=#s s[z],",h="x,y=x+X,y+Y",i=" then "})
w="|X,Y=1,0|X,Y=-1,0|X,Y=0,-1|X,Y=0,1|X,Y=-Y,-X|X,Y=Y,X|X=-X|Y=-Y|X,Y=-X,-Y|z=math.random(1,4)R(f[('><^v'):sub(z,z)],Q())()|p(s,P(s)+P(s))|p(s,-P(s)+P(s))|p(s,P(s)*P(s))|z=P(s)if z~=0 then p(s,P(s)/z)else error'Div by 0'end|y=P(s)z=P(s)p(s,z%y)|p(s,P(s)==P(s) and 1 or 0)|p(s,P(s)>P(s)and 1 or 0)|p(s,P(s)<P(s)and 1 or 0)|x,y=x+X,y+Y|if #s==0 or s[#s]==0 then x,y=x+X,y+Y end|p(s,s[#s])|P(s)|z=#s s[z],s[z-1]=s[z-1],s[z]|z=#s s[z],s[z-1],s[z-2]=s[z-1],s[z-2],s[z]|if r then p(s,r)r=N else r=P(s)end|z={}for k=1,#s do z[#s-k+1]=s[k]end s=z|p(s,1,P(s))|p(s,P(s,1))|z=P(s)p(s,c[P(s)][z])|z,w=P(s),P(s)c[P(s)][w]=z|W(C(P(s)))|W(P(s))|z=I(1)while F(z,'%s')do z=I(1)end p(s,B(z))|os.exit()|T.N=1|P(T,I)for k=I,#T do T[k].I=k end|s=s==S and l or S|z=s==S and l or S for k=#s,1,-1 do p(z,P(s,1))end|"
z=1
f={}
for k in t:gmatch"." do -- will contain the tokens
_,z,s=w:find("|(.-)|",z)
f[k]=loadstring(s)
end
T={ -- table of threads
--N = new thread to be created.
m=function()
x,y=x+X,y+Y
if y > #c then
y=0
elseif y<0 then
y=#c
end
if x>#c[y] and X==1 then
x=0
elseif x<0 then
x=#c[y]
end
end,
n=function(x,y,X,Y)
z=M({
I=#T+1, -- keep number id
l={}, -- local stack
X=X or 1, -- 1 for +x, -1 for -x, 0 for y/-y
Y=Y or 0, -- 1 for +y, -1 for -y, 0 for x/-x
x=x or 0, -- X of IP
y=y or 0, -- Y of IP
-- i, -- will contain type of quote when reading in a string --TODO keep local
-- r, -- registry --TODO make global
},{__index=_G}) -- Enable lookup of functions in global table.
z.s=z.l -- current stack is local stack
T[z.I]=z -- add at next index
end
}
c={} -- codebox IP wraps around -- TODO make codebox global in code
S={} -- global stack
-- codebox layout
-- -----> +x
-- @ |line of text -- wrap around to second line
-- |second line of text. -- negative indices can be used for variables
-- |
-- V +Y
-- y first coord, x second
-- wrap around rows if nil row
-- wrap around cols if nil char.
T.n(-1)
-- compile to codebox
fh= arg[1] and io.open(arg[1]) or io.stdin -- use file or stdin
y=0
for l in fh:lines() do
c[y]=M({},{__index=function()return 32 end})--default to space
for k=1,#l do
z=l:sub(k,k)
if not i then -- normal mode
if F(z,"['\"]") then i=z end
if F(z,"[^\n\r]")then --filter out only newlines
c[y][k-1]=B(z)
end -- any spacing allowed.
else
if z==i then i=N end-- verbatim string mode
c[y][k-1]=B(z)
end
end
y=y+1
end
io.stdout:setvbuf("no") -- direct output
while #T>0 do
for I=1,#T do
t=T[I]
R(1,t)
R(T.m,t)()
n,o=X,Y -- keep old directions for new thread detection
q=C(c[y][x])
if i then -- stringparsing mode
if F(q,"['\"]") then -- end-quote
i=N
else
p(s,c[y][x]) -- push contents of box, then advance
end
elseif F(q,"['\"]") then -- start-quote
i=q
elseif F(q,"%x") then -- parsing a number
p(s,tonumber(q,16))
elseif F(q,"[^ ]") then
assert(setfenv(f[q],t))
f[q]() -- call, feed with state/thread
end
end
if T.N and (n~=X or o~=Y) then
-- create new thread
T.n(x,y,X,Y)
T.N=N
X,Y=n,o -- restore directions of parent
end
end
Nonthreading version, golfed (1558 chars, but can be shrunk a bit more if the non-threading version is going to be the criterion):
T=table p=T.insert P=T.remove I=io.read W=io.write A=assert t="><^v/\\|_#x+-*,%=)(!?:~$@&r}{gponi;"M=t.match B=t.byte C=t.char f={"X,Y=1,0","X,Y=-1,0","X,Y=0,-1","X,Y=0,1","X,Y=-Y,-X","X,Y=Y,X","X=-X","Y=-Y","X,Y=-X,-Y","z=math.random(1,4)f[('><^v'):sub(z,z)]()","p(s,P(s)+P(s))","p(s,-P(s)+P(s))","p(s,P(s)*P(s))","p(s,(1/P(s) or error'Div by 0')*P(s))","y=P(s)z=P(s)p(s,z%y)","p(s,P(s)==P(s) and 1 or 0)","p(s,P(s)>P(s) and 1 or 0)","p(s,P(s)<P(s) and 1 or 0)","x,y=x+X,y+Y","if #s==0 or s[#s]==0 then f['!']()end","p(s,s[#s])","P(s)","z=#s s[z],s[z-1]=s[z-1],s[z]","z=#s s[z],s[z-1],s[z-2]=s[z-1],s[z-2],s[z]","if r then p(s,r)r=N else r=P(s)end","z={}for k=1,#s do z[#s-k+1]=s[k]end s=z","p(s,1,P(s))","p(s,P(s,1))","z=P(s) p(c[P(s)][z])","z,w=P(s),P(s) c[P(s)][w]=z","W(C(P(s)))","W(P(s))","z=I(1) while M(z,'%s')do z=I(1)end p(s,B(z))","os.exit()"}z=1 for k in t:gmatch"."do f[k]=A(loadstring(f[z]))z=z+1 end c={}s={}X=1 Y=0 x=0 y=0 m=function(s)x,y=x+X,y+Y if y>#c then y=0 elseif y<0 then y=#c end if x>#c[y]and X==1 then x=0 elseif x<0 then x=#c[y]end end F=arg[1]and io.open(arg[1])or io.stdin l=0 for line in F:lines()do c[l]=setmetatable({},{__index=function()return 0 end})for k=1,#line do z=line:sub(k,k)if not i then if M(z,"['\"]")then i=z end if M(z,"[^\n\r]")then c[l][k-1]=B(z)end else if z==i then i=N end c[l][k-1]=B(z)end end l=l+1 end while 1 do q=C(c[y][x])if i then if M(q,"['\"]")then i=N else p(s,c[y][x])end else if M(q,"['\"]")then i=q elseif M(q,"%x")then p(s,tonumber(q,16)) elseif M(q,"[^ %z]")then A(f[q])f[q]()end end m()end
Readable version:
-- http://codegolf.stackexchange.com/questions/1595/interpret-fish
--
-- TODO's
-- threading instructions:
-- * [ start thread at next change in direction.
-- * ] end thread
-- * . switch between global and local stack
-- * m copy global to local stack
--
p=table.insert -- push
P=table.remove -- pop
t=table.concat{
"><^v", -- Direction DONE
"/\\|_#", -- Mirror DONE
"x", -- random direction DONE
"+-*,%", -- arithm. DONE
"=)(", -- pops A and B of the stack if A==B then push 1 else push 0,same for greater than, lesser than. (result on stack) -- DONE
"!", -- skip next DONE
"?", -- if s[#s]==0 then skip next, else continue DONE
":", -- duplicate top DONE
"~", -- remove top DONE
"$", -- rotate top 2 values DONE
"@", -- rotate top 3 values DONE
"&", -- poptop to registry or read from registry DONE
"r", -- reverse stack DONE
"}{", -- shift stack right, (or up)/ shift stack left (or down) DONE
"g", -- pops A,B push values at B,A in the codebox on the stack DONE
"p", -- pops A,B,C from stack, and change value at C,B to A DONE
"o", -- pops from stack and output character DONE
"n", -- pops from stack, outputs number DONE
"i", -- take 1 char of input, and push the ASCII value on stack DONE
";", -- os.exit() DONE
--[["[", -- start new thread at next direction change
"]", -- end thread
".", -- switch between global and local stack
"m", -- Copy global stack to local one --]]
}
f={
"s.dx,s.dy=1,0","s.dx,s.dy=-1,0","s.dx,s.dy=0,-1","s.dx,s.dy=0,1",
"s.dx,s.dy=-s.dy,-s.dx","s.dx,s.dy=s.dy,s.dx","s.dx=-s.dx","s.dy=-s.dy","s.dx,s.dy=-s.dx,-s.dy",
"z=math.random(1,4)f[('><^v'):sub(z,z)]()",
"p(s.s,P(s.s)+P(s.s))","p(s.s,-P(s.s)+P(s.s))","p(s.s,P(s.s)*P(s.s))","p(s.s,(1/P(s.s) or error'Div by 0')*P(s.s))","y=P(s.s)z=P(s.s)p(s.s,z%y)",
"p(s.s,P(s.s)==P(s.s) and 1 or 0)",
"p(s.s,P(s.s)>P(s.s) and 1 or 0)",
"p(s.s,P(s.s)<P(s.s) and 1 or 0)",
"s.x,s.y=s.x+s.dx,s.y+s.dy",
"if #s.s==0 or s.s[#s.s]==0 then f['!']()end",
"p(s.s,s.s[#s.s])",
"P(s.s)",
"z=#s.s s.s[z],s.s[z-1]=s.s[z-1],s.s[z]",
"z=#s.s s.s[z],s.s[z-1],s.s[z-2]=s.s[z-1],s.s[z-2],s.s[z]",
"if s.r then p(s.s,s.r)s.r=nil else s.r=P(s.s)end",
"z={}for k=1,#s.s do z[#s.s-k+1]=s.s[k]end s.s=z",
"p(s.s,1,P(s.s))",
"p(s.s,P(s.s,1))",
"z=P(s.s) p(s.s,s.c[P(s.s)][z])",
"z,w=P(s.s),P(s.s) s.c[P()][w]=z",
"io.write(string.char(P(s.s)))",
"io.write(P(s.s))",
"z=io.read(1) while z:match'%s'do z=io.read(1)end p(s.s,z:byte())",
"os.exit()"
}
z=1
for k in t:gmatch"." do -- will contain the tokens
f[k]=assert(loadstring(f[z]))
z=z+1
end
s={ -- state
c={}, -- codebox IP wraps around
s={}, -- stack
dx=1, -- 1 for +x, -1 for -x, 0 for y/-y
dy=0, -- 1 for +y, -1 for -y, 0 for x/-x
x=0, -- X of IP
y=0, -- Y of IP
-- i, -- will contain type of quote when reading in a string
-- r, -- registry
-- codebox implementation
-- codebox layout
--
--
-- -----> +x
-- @ |line of text -- wrap around to second line
-- |second line of text. -- negative indices can be used for variables
-- |
-- V +Y
-- y first coord, x second
-- wrap around rows if nil row
-- wrap around cols if nil char.
move=function(s)
s.x,s.y=s.x+s.dx,s.y+s.dy
if s.y > #s.c then
s.y=0
elseif s.y<0 then
s.y=#s.c
end
if s.x>#s.c[s.y] and s.dx==1 then
s.x=0
elseif s.x<0 then
s.x=#s.c[s.y]
end
end
}
-- compile to codebox
fh= arg[1] and io.open(arg[1]) or io.stdin -- use file or stdin
y=0
for line in fh:lines() do
s.c[y]=setmetatable({},{__index=function() return 0 end})
for k=1,#line do
z=line:sub(k,k)
--print(y,k,"|"..z.."|")
if not s.i then -- normal mode
if z:match"['\"]" then s.i=z end
if z:match"[^\n\r]"then --filter out only newlines
s.c[y][k-1]=string.byte(z)
end -- any spacing allowed.
else -- verbatim string mode
if z==s.i then s.i=nil end
s.c[y][k-1]=string.byte(z)
end
end
y=y+1
end
io.stdout:setvbuf("no") -- direct output
function dbg()
print("\nIP",s.y,s.x)
print("command",string.char(s.c[s.y][s.x]))
print("codebox:",#s.c)
for y=0,#s.c do
print("\tline",y)
io.write"\t"
for x=0,#s.c[y] do
--io.write(string.char(s.c[y][x]),",\t")
io.write(tostring(s.c[y][x]),",\t")
end
io.write"\n"
end
print("stack:")
for k,v in pairs(s.s) do print("",k,v) end
end
function run()
while 1 do
r,e = pcall(string.char,s.c[s.y][s.x]) -- look up command in codebox
if not r then print("Error happened reading command",s.c[s.y][s.x])
dbg()
end
q=string.char(s.c[s.y][s.x])
--print(s.y,s.x,q)
if s.i then -- stringparsing mode
if q:match"['\"]" then -- end-quote
s.i=nil
else
p(s.s,s.c[s.y][s.x]) -- push contents of box, then advance
end
else -- not in string parsing mode
if q:match"['\"]" then -- start-quote
s.i=q
elseif q:match"%x" then -- parsing a number
p(s.s,tonumber(q,16))
elseif q:match"[^ %z]" then
assert(f[q])
r,e= pcall(f[q]) -- call
if not r then print("Error calling function for "..q..": \n",e) -- error happened, clarify
end
end
end
s:move() -- move the IP
end
end
r,e=pcall(run)
if not r then print("Error occured:",e)
dbg()
end
Not putting the next as result, as using compression directly isn't the goal I guess ;).
Using murgaLua (or any Lua version with lzlib and luaSocket(for base64 decoding)), at the magic count of 1333:
L=loadstring L(zlib.decompress(mime.unb64("eJxtVW1z4jYQ/iuqaYp0yD587fQDzaadS+c6zPQyd4lnjgw4DC8ieACZyE6wDeS3d1fCB6H3AbTa12ff5Ary0Xip2BqqINGZMjn7gqRRq/RFsW+QpMHGJLliOXhXlw8v7weD3bBRtPx38gIE/+nPzuvPf/1i9tvHdaqTP/pxsPKuIQ8m85FhH5EYl2j8CYnVKJ/M2WfIVL5S+ciF/QqPKp8p/cJuSWCpLgU1ajRlG/B2PXkPoWzb06+JtvTDA+FO/176PUvdSzwBL8R0sp5EqgIEMA/MSE/TFQ/lb+KWz/q8SUk1RSd7HvNKViKWX7kQXOzWPJNfeCZa9Oeu/tmdqHfuWgGdyYxVr9Bm+VxpVmu8r4RaZoopY1LT/Dt5YeOStZtKT3eltXK2pF5dlEfPYNkM8bKQpYa1j6Ir+vuR4PJcUMgSilZPlq37HaJrZIDwUJT1G1kMNdQTLUa4yJ3VEDtyiNk1MjSpYuRWfhiDO+gW/09ojw+nOnhzqojAHItjhIEbZmtjbK4UuoLtfoYAF9h09DtNWYVA/EXLRl3EqMMyqCzEUL7phQy/N4I4kz5RMcZFrtxYvjWoBZsY/Xzj19x6EUjWvezyUGzmCc7nJxyK5kXWFATE8gkAuf/IK9RNs0AVSY7zEgU3EGK5ItkVLoGubESUQISwgy4sbGkzwBbc2a4uqRF3GO6Mw5x5gxL0Q/KwRhSHBMmHV0HIZnWhWKJZ3nm06+UFHqoPZSUz2HRmiZ5yb8cDX+w8nO0ZAoF/XaFZBNsVzJ71JE9SzcXpCGCbyqvGxHWqxCGhHhHzsl3zUEpOkFmgZr+MCX4PEJcbqKNRURsVYBXJjJGx1MfwGF3iquIqfObbLjSiViiXmKDsQY9qEtJi25GWBRSOKKG0xF5uh0PMVBUw/GcvqgDHI1hi1augix2mUPsJ+rrDXxRo7odiNoeReeyHFjU+Nulaae44Al0iJ8unicudarykGs/mnWWiVcZpFigTwnoS/FhLo/Jno9mvH2xs8X2cl3acYWkfm4VcCKqfTnOWuArhjebN6zcHXuwJx3MZHGUPAz0wtZRg9Be0kTSOpGfXid4hgNorLRlKfqCLP6xiK7SUG/hGdNUmmAS6S6DtCOcQ9bvxLT6bOT6bUbDCkwstU8CusSe45tZ7EdMTeJrN03k2h4V3C+pMathnBjX6pzfCi+LgijzkqX5ejZVBQfi7EG+cPLA66OG7gq/9U2xx17mjLm4tbR7Xr27Q0le4d1Y0KvVY0m7fMPqWYMrsYP4fCvRCgw==")))()
1There's also
l
for pushing stack length. And as far as I know,?
does pop the value. – JNF – 2015-05-26T11:16:34.643@JNF This question predates those additions to the language. – Kevin Brown – 2015-05-26T11:18:29.713
@KevinBrown, how about that :). BTW, you know where esolangs.org went? – JNF – 2015-05-26T11:21:52.993
3Can't Fish be implemented in Fish itself? – Vi. – 2013-11-11T11:29:53.900
4Wow! What a nice task! – FUZxxl – 2011-03-14T19:29:48.080
Are you sure about the factorial output? My interpreter outputs '1 2 2 6 12 48 144 720 2880 17280 20864' (after that, the 16 bit integer wraps). (Could well be a fault in my code, so I'm just asking...) – PatrickvL – 2011-03-15T13:31:49.863
@Patrickvl, my interpreter outputs
1 2 6 24 120 720 ...
and should be what the Python interpreter output too. – Kevin Brown – 2011-03-15T21:48:42.453@Bass5098 : You're right. I made an error in '@' (I inserted the popped value one position too far to the left). Luckily, the fix doesn't cost any extra characters; I believe my code handles all instructions correctly now. – PatrickvL – 2011-03-15T22:08:10.233
What's wrong with converting to native code? (just asking) – J B – 2011-03-17T06:27:47.937
@J-B : Fish can self-modify it's 'codebook', which is kinda hard to do in a golfed to-native translator. – PatrickvL – 2011-03-17T19:51:09.720
Did you mean f=15 ? – Oleh Prypin – 2011-04-03T21:34:04.473
The definition has so many spots where I'm really unsure what my program should do... – Oleh Prypin – 2011-04-04T19:18:18.400
@BlaXpirit : Changed that to 15 here and at the wiki. – Kevin Brown – 2011-04-04T19:27:38.360
Why don't you accept the shortest answer? – Oleh Prypin – 2011-04-06T11:30:06.513