Murga-Projects Forums

Full Version: Interactive Fish interpreter
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I'm having trouble creating a text editor, for creating sort of an interactive editor/interpreter for the Fish language.

With Fluid, I've managed to generate the window etc, but in the textarea text can not be entered.

The code:

Code:
-- Declaration of global variables
make_window = make_window or nil
win = {}
function make_window()
  win.window = fltk:Fl_Double_Window(334, 210, 565, 355, "Fish interpreter")
  do
    win.edt_buffer = fltk:Fl_Text_Buffer()
    win.edt = fltk:Fl_Text_Editor(25, 25, 260, 280)
    win.edt:textfont(fltk.FL_SCREEN)
    win.edt:textsize(10) -- TODO un magic number
    win.edt:selection_color(fltk.FL_BLACK)

    local o = fltk:Fl_Text_Display(285, 25, 260, 280)
    local o = fltk:Fl_Roller(135, 320, 55, 25, "Delay")
    o:align(4)
    o:step(0)
    local o = fltk:Fl_Value_Output(190, 320, 25, 25)
    local o = fltk:Fl_Button(215, 320, 70, 25, "Back")
    local o = fltk:Fl_Button(285, 320, 70, 25, "Step")
    local o = fltk:Fl_Return_Button(355, 320, 70, 25, "Run")
  end
  win.window['endd'](win.window)
  return win.window
end

for _,i in ipairs {make_window()} do i:show() end
Fl:run()


Any ideas how to solve this?

Regards,

Jan-Pieter

win.edt_buffer = fltk:Fl_Text_Buffer()
win.edt = fltk:Fl_Text_Editor(25, 25, 260, 280)
win.edt:buffer(win.edt_buffer)


what does this do?: win.window['endd'](win.window)

mikshaw Wrote:
what does this do?: win.window['endd'](win.window)


No idea! It's just generated like that by the fluid to murgaLua converter (though as o['endd'](o)). I guess it's similar to fltk.Fl_End().

Thanks a lot,

Jan-Pieter

I've been coding this interactive fish interpreter, because it's a really nice language (http://esolangs.org/wiki/Fish). I'm almost there, but I can't get the output window to redraw when using the run button (which goes into a loop calling the main coroutine). So I can't see the individual characters being printed.

Using the step button (which just resumes the coroutine once) does work.

Any Ideas? This is the code:

Code:
-- configuration --
demo=[["hello, world"r> ?o?<;]]
size=10
font=fltk.FL_SCREEN
def_delay=500
-- end config --

-- parsing different commands
z=table
p=z.insert    -- push
P=z.remove    -- pop
W=function(...) win.output_buffer:append(...) end
t="><^v/\\|_#x+-*,%=)(!?:~$@&r}{gponi;[].mX"        -- all tokens
C=t.char
B=t.byte
F=t.match
M=setmetatable
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))",
"z=P(s.s)if z~=0 then p(s.s,P(s.s)/z)else error'Div by 0'end","y=P(s.s)z=P(s.s)p(s.s,z%y)", -- div
"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['!'](s) 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",
"if s.s==s.l then z=s.l s.l={}s.s=s.l else z=S S={}s.s=S end for k=1,#z do s.s[#z-k+1]=z[k]end",
"p(s.s,1,P(s.s))",
"p(s.s,P(s.s,1))",
"z=P(s.s) p(s.s,c[P(s.s)][z])",
"z,w=P(s.s),P(s.s) c[w][P(s.s)]=z",
"W(C(P(s.s)))",
"W(P(s.s))",
"z=io.read(1) if not z then p(s.s,4)else while z:match'%s'do z=io.read(1)if not z then z='\\4'end end p(s.s,B(z))end",
"fltk.fl_message('Fish program quit')step=coroutine.create(function()return end) coroutine.resume(step)", --"os.exit()", -- TODO modify!
"T.nt=true",
"P(T,s.id)for k=s.id,#T do T[k].id=k end", -- update self.id after removing a thread.
"s.s = s.s==S and s.l or S",
"z= s.s==S and s.l or S for k=#s.s,1,-1 do p(z,P(s.s,1))end",
"dbg(s)",
}
function dbg(s)
    print""
    print("Current Stack:",s.s==s.l and "Local Stack" or "Global Stack")
    print("Local stack:")
    for k,v in pairs(s.l) do print("",k,v,v>=0 and string.char(v) or "NEG") end
    print("Global stack:")
    for k,v in pairs(S) do print("",k,v,v>=0 and string.char(v) or "NEG") end
    print("Register: ",s.r)
    print("Codebox:")
    for k=0,#c do
        io.write(string.format("%0"..#tostring(#c).."i",k),": ")
        for l=0,#c[k] do
            io.write(string.char(c[k][l]))
        end
        io.write"\n"
    end
end

-- Linking commands to functions
z=1
for k in t:gmatch"." do -- will contain the tokens
    --TODO setfenv / setmetatable to avoid all indexing in functions.
    f[k]=assert(loadstring("s=...;"..f[z]))
    z=z+1
end

T={        -- table of threads
    --nt = new thread to be created.
    }
c={}    -- codebox IP wraps around
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.
function T.n(T,x,y,dx,dy) -- New thread function
    z={
    id=#T+1,                    -- keep number id
    l={},                    -- local stack
    dx=dx or 1,                    -- 1 for +x, -1 for -x, 0 for y/-y
    dy=dy 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
    -- r,                    -- registry
    }
    z.m=function(s)
        s.x,s.y=s.x+s.dx,s.y+s.dy
        if s.y > #c then
            s.y=0
        elseif s.y<0 then
            s.y=#c
        end
        if s.x>#c[s.y] and s.dx==1 then
            s.x=0
        elseif s.x<0 then
            s.x=#c[s.y]
        end
    end
    z.s=z.l -- current stack is local stack
    T[z.id]=z    -- add at next index
end
T:n(-1)

function run()
-- compile to codebox
--fh= arg[1] and io.open(arg[1]) or io.stdin    -- use file or stdin
-- Todo: find a way to "lock" the editor.
y=0;pos=0
while pos<=win.edt_buffer:length() do
    c[y]=M({},{__index=function()return 32 end})--default to space
    local l = win.edt_buffer:line_text(pos)
    for k=1,#l do
        local 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                -- verbatim string mode
            if z==i then i=nil end
            c[y][k-1]=B(z)
        end
    end
    pos=win.edt_buffer:line_end(pos)+1
    y=y+1
end

while #T>0 do
    for id=1,#T do --TODO make that f[q] uses the correct stacks etc.
        s=T[id]
        s:m()                                        -- move the IP
        n,o=s.dx,s.dy -- keep old directions for new thread detection
        q=C(c[s.y][s.x])
        if s.i then                        -- stringparsing mode        
            if F(q,"['\"]") then        -- end-quote
                s.i=nil
            else
                p(s.s,c[s.y][s.x])    -- push contents of box, then advance
            end
        else                             -- not in string parsing mode
            if F(q,"['\"]") then        -- start-quote
                s.i=q
            elseif F(q,"%x") then        -- parsing a number
                p(s.s,tonumber(q,16))
            elseif F(q,"[^ ]") then
                assert(f[q])
                f[q](s)    -- call, feed with state/thread
            end
        end
    end
    if T.nt and (n~=s.dx or o~=s.dy) then
        -- create new thread
        T:n(s.x,s.y,s.dx,s.dy)
        T.nt=nil
        s.dx,s.dy=n,o        -- restore directions of parent
    end
    coroutine.yield()
end
end

step = coroutine.create(run)

win = {}
  win.window = fltk:Fl_Double_Window(334, 210, 565, 355, "Fish interpreter")
  do
    win.edt_buffer = fltk:Fl_Text_Buffer()
    win.edt = fltk:Fl_Text_Editor(25, 25, 260, 280)
    win.edt:buffer(win.edt_buffer)
    win.edt:textfont(font)
    win.edt:textsize(size)
    win.edt:selection_color(fltk.FL_BLACK)

    win.output_buffer = fltk:Fl_Text_Buffer()
    win.output = fltk:Fl_Text_Display(285, 25, 260, 280)
    win.output:buffer(win.output_buffer)
    win.output:textfont(font)
    win.output:textsize(size)
    win.output:selection_color(fltk.FL_BLACK)

    win.rol = fltk:Fl_Roller(105, 320, 45, 25, "Delay")
    win.rol_output = fltk:Fl_Value_Output(160, 320, 50, 25)
    win.rol:align(4)
    win.rol:step(100)
    win.rol:bounds(0,10000)
    win.rol:value(500)
    win.rol:callback(function() win.rol_output:value(win.rol:value()) end)

    local o = fltk:Fl_Button(215, 320, 70, 25, "Back")
    o:callback( function() print"Back button not implemented" end)
    local o = fltk:Fl_Button(285, 320, 70, 25, "Step")
    o:callback( function() coroutine.resume(step) end)
    local o = fltk:Fl_Return_Button(355, 320, 70, 25, "Run")
    o:callback(function()
        stat=1
        while coroutine.status(step)~="dead" do
            print("step, sleeping for "..win.rol_output:value())
            coroutine.resume(step) win.window:show() win.output:redraw()
            murgaLua.sleepMilliseconds(win.rol_output:value())
        end
    end)
  end
  fltk.Fl_End()
  win.window:show()
  win.edt_buffer:text(demo)
Fl:run()


Regards,

Jan-Pieter

Still know very little about coroutines, but perhaps the "while" loop is interfering. It's my understanding that even though the coroutine is yielding, you're still in a loop, which as I understand it prevents the window from being redrawn.

Just a wild guess.
Allright! I found what it takes: a call to Fl:wait()! Apparently that makes the Fl app to wait until something happens (like a redraw Smile). Now I'm taking off the rough edges before publishing the final version.

Regards,
Jan-Pieter
Well, I had a lot of fun coding this one, and I think it can be quite entertaining, and perhaps serve for basis for writing other interactive interpreters (Though there should be a bit more separation between logic an UI). The code for the instructions might not be super-readable, but that's because this project started out as an answer in a codegolfing contest.

Anyway here it is, explications are at the top of the file, some demo programs are included and you can find some more here: http://esolangs.org/w/index.php?title=Fish&oldid=22153. Note that this program implements the "Old" Fish, where now the whole threading idea has been given up for a "multiple stack" concept. If anyone feels for updating this to the new specs, it shouldn't be too much work.

Any help with the TODO's in the file are also very welcome.
Have fun!

Regards,

JP
Reference URL's