Create a table of words that start a block. When one of those words is keyed, add 1 to a counter. Subtract 1 when an end is keyed. If the counter is 0, the block is complete and can be run.
This is a suggestion rather than a criticism, but why not do a project that uses animated graphics, rather than replicating the Lua interpreter? Graphics is where Codea’s unique features really shine.
Have you not seen the gorgeous green-on-black typography, the silky smooth typewriter scrolling, the blinking cursor beckoning the user? How dare you say my interpreter does not have good graphics!
crawl back into your DOS cave, you neanderthal, you :-B
@dave1707 yeah that’s the approach I’ve gone with here. I put a space at the beginning and end of the line, and then look for the keyword preceded by a space and succeeded by a non-letter character, "%s"..keyword.."%A+"
(in order to catch function(
but not catch a variable name that includes a keyword like doGraphics
or whatever. I guess that as the string currently stands do_graphics
would return a false positive ). It’s these kinds of fiddly limit cases that made me wonder whether there was some way to use the Lua interpreter to parse the code, I dunno, using pcall
and then examining the error message to work out whether there’s a missing end
.
Oh well, I guess this works well enough.
@iam3to5am
I think this is close to what you asked. You don’t have to use do
to initiate a block, you can use for
if
function
while
repeat
.
Next up: query _G
to handle autocomplete!
--# Main
-- Lua Interpreter
oldPrint = print
function setup()
lines = {{level=1, str=""}} --indent level, string
level = 1 --current indent level
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
end
function print(...)
local out = {...}
lines[#lines+1]={level=0, str=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
local out = v.str
if i==#lines then out = out..cursor end
local indent = string.rep("> ", v.level)
local w,h = textSize(indent..out)
y = y - h
text(indent..out, margin, y)
end
targety = math.max(0, typeWriter-y) --scroll screen
screeny = screeny + (targety - screeny) * 0.1
end
local block = {"function", "if", "do", "repeat"} --for, while statements end in do
function keyboard(key)
if key == BACKSPACE then
lines[#lines].str=string.sub(lines[#lines].str, 1, -2)
elseif key == RETURN then
lines[#lines].str = lines[#lines].str.." " --add space to end (and later to start of string) to check for discrete words
--check for block start
for _,v in ipairs(block) do
if string.match(" "..lines[#lines].str, "%s"..v.."%A+") then
if level==1 then blockStart = #lines end --start of block
level = level + 1
end
end
--check for block end
if string.match(" "..lines[#lines].str, "%send%A+") or string.match(" "..lines[#lines].str, "%suntil%A+") then
level = math.max(level - 1, 1)
lines[#lines].level = level
end
--interpret
if level == 1 then
if blockStart then
local str = "" --concatenate block
for i = blockStart, #lines do
str = str..lines[i].str
end
interpret(str)
blockStart=nil
else
interpret(lines[#lines].str) --interpret just this line
end
end
lines[#lines+1]={level=level, str=""}
else
lines[#lines].str = lines[#lines].str..key
end
end
--# Sandbox
--Sandbox, prevent user from overwriting code
function null() end
local saveTab = saveProjectTab()
saveProjectTab = null
I think that [^_%w]
should be a pretty good way of delimiting terms in Lua. Not an underscore or an alphanumeric.
Here’s a quick video of where I’m at:
Autocomplete, buttons for frequently used characters, cursor placement. It’s almost at feature-parity with the Codea editor!
(Exaggerating enormously…)
Still, it’s at the stage now where it’s actually reasonably pleasant to use. I’m not really sure how you would go about sandboxing it though. i.e. the user just has to reuse one of the function names as a variable to break everything. So it’s very far from idiot-proof. But for personal use it’s quite nice. And less than 300 lines!
https://github.com/Utsira/Codea/blob/master/Lua%20Interpreter.lua
(ps the GitHub repository is being maintained by Working Copy and the Codea client I wrote for it)
Has anyone made an idiot’s guide to using Working Copy and the Codea client?
Do you mean an idiot’s guide to Git?
No, an idiot’s guide to using your backup system to save Codea projects.
For someone like me.
There’s the installation and usage instructions here (at the top of the code block)
http://codea.io/talk/discussion/comment/61757/#Comment_61757
I plan eventually on blogging about it, if you’d like more detail.
It’s interesting that all guides to using Git stress using the command line, when it’s perfectly possible to just use GUIs/clients.
The Codea client is still in the “alpha” stage, feedback is welcome. I’ve just noticed I have a duplicate file in my repository, so there are some kinks to be ironed out. There’s an update to Working Copy arriving soon (I’m using the testflight beta) that allows multi-file write (ie write each tab to a separate file). Multi-file read is still a problem though. So for the moment the Codea client just reads and writes a single file, concatena-ing the project using the “paste into project” format.
very nice
Just to answer @Jmv38 and @Ignatz , why an interpreter/ console, I suppose it’s something that lots of languages come with (desktop Lua, Pythonista etc), so it’s a paradigm that lots of people are used to. Sometimes you just need to test something really quickly. Eg the other day I wanted to check whether an empty string ""
returns nil. I guessed that it wouldn’t, but I still wanted to check before basing some logic on that. I used to have a project called tests with loads of these kinds of little tests. Or if it’s something related to a specific project you can have a test tab with a separate setup and draw function. Or you could create a “delete me” project and then delete it. But I think the most natural environment to do this kind of testing, usually where you’re just testing some behaviour you’re not sure of, and want feedback as soon as you hit return, is an interpreter/ console.
In any case it was a lot of fun to build, with lots of great feedback and tips from this thread. I’m constantly amazed at how much you can do with just a few hundred lines.
I haven’t yet found a way to fully sandbox it (other than manually testing for variables with the names draw, touched, keyboard, etc and not interpreting those lines). Although code being executed via loadstring
can’t access upvalues, eg the local variables/ functions in the same tab as the loadstring call, which is handy. So you can make as many of the functions local as you can. Perhaps there’s some advanced technique (using environments?) for sealing off draw, touched etc from the user?
I just keep a tab on the right, called Test, with a minimal setup/draw function, and do my testing in there. One keystroke comments it out or makes it live.
The advantage is that when I write new functions, I can quickly test them - and it is a Lua interpreter as well, of course.
@yojimbo2000 I wrote a dynamic lua loader which uses function environments for sandboxing a while ago
http://codea.io/talk/discussion/3114/include-a-lightweight-dynamic-tab-and-project-loader#latest
A version compatible with lua 5.3 is also in my entity component system framework
https://github.com/XanDDemoX/Codea-Xile/blob/master/Xile.codea/Include.lua
Thanks, will have a look at those! I’ve not done anything with environments yet.