Lua interpreter

Hello all,
Has anyone played with the idea of creating a text box that acts just like the stand-alone lua interpreter that is packaged with the Lua source code itself? Simple to implement, or will I be in over my head with such an idea? Thanks!

simple to to, but what for?

For doing it, once you have your text box done, to run the code I believe you just call

loadstring(<string>)()

@iam3o5am If your letting someone type in lua you might also want to think about things that could go wrong. You can use xpcall or pcall to catch errors. :slight_smile:

local luaString = <string>

local func,result

result = xpcall(function() func = loadstring(luaString) end, 
          function(err)   
            -- called on error with loadstring

           end)

if result == true then

  -- loadstring worked, 

   xpcall(func,function(err)

      -- called on error running entered lua in luaString

   end)
end

Thanks for the comments. Jmv38: great question, though same could be asked of the original stand-alone Lua interpreter. Just makes for a good way to get instant feedback when working through learning Lua itself.

I’d use it as a calculator

Here’s your calculator

parameter.text("Input")
parameter.action("Calculate",function() print(loadstring("return "..Input)()) end)

when doing this in a program you have to be careful as the end user could mess with the main code of your program

I’ve not experimented with loadstring yet. Putting it inside an xpcall is a good idea I guess. Now we just need a retro green-on-black typeface…

So, judgement of my Codea noob status aside, would anyone be willing to lay out a starter project step by step on how to make such a full-screen console-style lua interpreter? How do I redirect the output to the full screen? And, for those concerned, the end user is, well, me - so I’m not worried about users modifying my program - though I’m interested in how that happens too! But first things first…

maybe you can use this http://codea.io/talk/discussion/comment/61495/#Comment_61495 as a template

thanks @Jmv38 as always. Your patience with my beginner questions is appreciated :slight_smile:
So ok, plenty of text box examples…but getting the output to show not just on the side (on the parameter/output window) but in my newly minted textbox? I’m cringing at the thought that it’s easy and I am missing something, but whats the worst that can happen by asking…

Ok, to get the ball rolling…

A full screen interpreter with cursor, type-writer scrolling etc

Of course, without the special keyboard it’s nowhere near as nice to type in as the Codea editor. The next step would be to suggest autocomplete for Lua commands. I guess you could do this by querying _G?

I also created a sandbox to prevent the user from overwriting the code.


--# Main
-- Lua Interpreter

function setup()
    lines = {""}
    margin = WIDTH * 0.1
    typeWriter = HEIGHT*0.5
    screeny, targety =0,0
    textWrapWidth(WIDTH - margin * 2)
    font("Inconsolata")
    fill(6, 255, 0, 255)
    fontSize(30)
    textMode(CORNER)
    showKeyboard()
end

function interpret(command)
    local result
    local ok, err = xpcall(function() result=loadstring("return "..command)() end, function(err) result = err end) --result=loadstring("return "..command)()
    if result then
        lines[#lines+1]="> "..tostring(result)
    else
        lines[#lines+1]="> Not a valid command"
    end
    lines[#lines+1]=""
end

function draw()
    background(40, 40, 50)
    translate(0,screeny) 
    
        --cursor
    local cursorSpeed = 4
    local cursorLen = (ElapsedTime*cursorSpeed%2)//1 --a number that regularly alternates between 0 and 1 
    local cursor=string.rep("\\u{25ae}", cursorLen)..string.rep("\\u{25af}", 1-cursorLen) --cursor symbol..empty cursor symbol. The empty symbol is necessary otherwise the cursor blinking will push a word past the word wrap limit if you're at the end of a line. One of the disadvantages of a text-based cursor
    
    local y = HEIGHT
    for i,v in ipairs(lines) do
        if i==#lines then v = v..cursor end
        local w,h = textSize(v)
        y = y - h
        text(v, margin, y)
    end
    targety = math.max(0, typeWriter-y) --scroll screen
    screeny = screeny + (targety - screeny) * 0.1  
end

function keyboard(key)
    if key == BACKSPACE then
        lines[#lines]=string.sub(lines[#lines], 1, -2)
    elseif key == RETURN then
        interpret(lines[#lines])
    else
        lines[#lines] = lines[#lines]..key
    end
end


--# Sandbox
--Sandbox, prevent user from overwriting code

function null() end

local saveTab = saveProjectTab()

saveProjectTab = null

Tantalizingly close to what I’ve wanted - @yojimbo2000 , you’re good! Perhaps there’s much more to getting it to do the things that the PUC-Rio Lua interpreter does? Some ‘simple’ things like, type ‘a’ and get ‘nil’. then type ‘a = 4’, then type ‘a’ and get ‘4’. or type ‘do’ and get ‘>>’, allowing you to type in a block of code until you type ‘end’. Or to create functions that can then be called later in the interpreter, or create tables to use in the current session, etc…It would have taken me much MUCH longer than @yojimbo2000 to create what he did (alas, I have so much to learn…), so maybe my lua interpreter-on-the-go idea is a bit much for me! lol…

Yeah, as it stands mine is mainly calculator style. So you can type math.sqrt(9) or whatever. If you remove the "return ".. then it will behave more as you want, ie you can define variables and then refer back to them. I should also overwrite the print command…

Ok here’s a version that redefines the print command. You need to explicitly type print to see a sum eg print(5-1). But you can define variables, functions etc, and then call them much later. You could even type in a code block in one line, just don’t hit enter between statements. I don’t think it would be that hard to implement the do behaviour you describe, it would just be a case of deferring execution.

Did I get the prompt right? I put “>” for the responses. It should be for the input I guess? Edit: prompt changed, uses pcall rather than xpcall


--# Main
-- Lua Interpreter
oldPrint = print

function setup()
    lines = {"> "}
    margin = WIDTH * 0.1
    typeWriter = HEIGHT*0.5
    screeny, targety =0,0
    textWrapWidth(WIDTH - margin * 2)
    font("Inconsolata")
    fill(6, 255, 0, 255)
    fontSize(30)
    textMode(CORNER)
    showKeyboard()
end

function interpret(command)
    local ok, result = pcall(function() return loadstring(command)() end)
    if not ok then --attempt to handle fragments like "5-2"
        ok, result = pcall(function() return loadstring("return "..command)() end)
    end
    if result then
        print(tostring(result))
    end
    lines[#lines+1]="> "
end

function print(...)
    local out = {...}
    lines[#lines+1]=table.concat(out, "  ")
end

function draw()
    background(40, 40, 50)
    translate(0,screeny) 
    
        --cursor
    local cursorSpeed = 4
    local cursorLen = (ElapsedTime*cursorSpeed%2)//1 --a number that regularly alternates between 0 and 1 
    local cursor=string.rep("\\u{25ae}", cursorLen)..string.rep("\\u{25af}", 1-cursorLen) --cursor symbol..empty cursor symbol. The empty symbol is necessary otherwise the cursor blinking will push a word past the word wrap limit if you're at the end of a line. One of the disadvantages of a text-based cursor
    
    local y = HEIGHT
    for i,v in ipairs(lines) do
        if i==#lines then v = v..cursor end
        local w,h = textSize(v)
        y = y - h
        text(v, margin, y)
    end
    targety = math.max(0, typeWriter-y) --scroll screen
    screeny = screeny + (targety - screeny) * 0.1  
end

function keyboard(key)
    if key == BACKSPACE then
        lines[#lines]=string.sub(lines[#lines], 1, -2)
    elseif key == RETURN then
        interpret(string.match(lines[#lines], "^%>%s*(.+)"))
    else
        lines[#lines] = lines[#lines]..key
    end
end


--# Sandbox
--Sandbox, prevent user from overwriting code

function null() end

local saveTab = saveProjectTab()

saveProjectTab = null

Wow. Did I mention you are good? I’ll be chewing on your code here for a bit…I’m sure to others this is all simpleton, but this is a key learning step for me regarding Lua, Codea…
You’ve spoon-fed me enough to get me really going I think. The Lua interpreter is open source - it would be excellent for me to try to trace that and understand it (my ideas are always steps beyond my ability, but anyways…), and then work from the code and methods you’ve shown to then emulate that behavior in Codea. There are still differences, but it seems you are showing that anything the original Lua interpreter does can be done within Codea…
Thanks @yojimbo2000

I’m not sure xpcall is necessary, error-catching might work just as well with assert. Suggestions for improvement are welcome. The next stage I guess is to recognise chunks, and not execute until a matching end statement is found.

Made a slight change to the above code. The interpret function can now handle calculator-like statements 5-2 as well as standalone commands print(5-2) or even function subtractTwo(n) return n-2 end

Anyone have any good ideas for how to recognise blocks (i.e. and delay interpreting until a block end statement has been found)? I was thinking you could try to do it manually, by searching for “function”, “if”, “for”, “do”, “while”, “repeat”, but I wonder whether there isn’t a more elegant solution?

My ultimate goal is a piece of software that emulates exactly the stand-alone interpreter that ‘ships’ with the Lua source code from lua.org . @yojimbo2000 , check out that interpreter and see what you think, esp. how the do…end feature is set up.