Why is this string not splitting in lua - lua

So I am working on a project and I need to split a string that would look something like this:
if (x == 2){ output("Hello") }
This is my code:
local function splitIfStatement(str)
local t = {}
t[1] = ""
t[2] = ""
t[3] = ""
local firstSplit = false
local secondSplit = false
local thirdSplit = false
str:gsub(".", function(c)
if c == "(" then
firstSplit = true
end
if firstSplit == true then
if c == "=" then
firstSplit = false
secondSplit = true
else
if c == "(" then
else
t[1] = t[1] .. c
end
end
end
if secondSplit == true then
if c == ")" then
secondSplit = false
thirdSplit = true
else
if c == "=" then
else
t[2] = t[2] .. c
end
end
end
end)
return t
end
I need to split the string at "(" so t[1] is only equal to "x" and t[2] is equal to 2 and then t[3] is equal to the "output()"
But when I run my code(note I haven't added the t[3]) t[1] returns: "x "Hello") }" and t[2] returns 2 like it should.
Anyways why isn't the split function working on the first split but it works on the second.
Thanks!

In your loop you set firstSplit true if it hits a ( this happens in 2 places in your example, before x and right before "Hello"
you can fix this by setting firstSplit true and ignore the leading if ( before you beginning the loop. Then you allow the logic you have to handle the rest.
I also notice you dont have any logic that references t[3] right now.
That all said you really should use a pattern to parse something like this.
local function splitIfStatement(str)
t = {str:match("if%s*%((%w+)%s*[=<>]+%s*(%d+)%)%s*{(.+)}")}
return t
end
this pattern is very narrow and expects a specific type of if statement, you can learn more about lua patterns here: Understanding Lua Patterns

If the input is of the form
if (AAA == BBB){ CCC("Hello") }
with possible whitespace around the fields in question, then this code works:
S=[[if (x == 2){ output("Hello") } ]]
a,b,c = S:match('%(%s*(.-)%s.-%s+(.-)%)%s*{%s*(.-)%(')
print(a,b,c)

Related

How to implement the exercise 15.5 in pil4?

I am working on this exercise in pil4.
Exercise 15.5:
The approach of avoiding constructors when saving tables with cycles is too radical. It is
possible to save the table in a more pleasant format using constructors for the simple case, and to use
assignments later only to fix sharing and loops. Reimplement the function save (Figure 15.3, “Saving
tables with cycles”) using this approach. Add to it all the goodies that you have implemented in the previous
exercises (indentation, record syntax, and list syntax).
I have tried this with the code below, but it seems not to work on the nested table with a string key.
local function basicSerialize(o)
-- number or string
return string.format("%q",o)
end
local function save(name,value,saved,indentation,isArray)
indentation = indentation or 0
saved = saved or {}
local t = type(value)
local space = string.rep(" ",indentation + 2)
local space2 = string.rep(" ",indentation + 4)
if not isArray then io.write(name," = ") end
if t == "number" or t == "string" or t == "boolean" or t == "nil" then
io.write(basicSerialize(value),"\n")
elseif t == "table" then
if saved[value] then
io.write(saved[value],"\n")
else
if #value > 0 then
if indentation > 0 then io.write(space) end
io.write("{\n")
end
local indexes = {}
for i = 1,#value do
if type(value[i]) ~= "table" then
io.write(space2)
io.write(basicSerialize(value[i]))
else
local fname = string.format("%s[%s]",name,i)
save(fname,value[i],saved,indentation + 2,true)
end
io.write(",\n")
indexes[i] = true
end
if #value > 0 then
if indentation > 0 then io.write(space) end
io.write("}\n")
else
io.write("{}\n")
end
saved[value] = name
for k,v in pairs(value) do
if not indexes[k] then
k = basicSerialize(k)
local fname = string.format("%s[%s]",name,k)
save(fname,v,saved,indentation + 2)
io.write("\n")
end
end
end
else
error("cannot save a " .. t)
end
end
local a = { 1,2,3, {"one","Two"} ,5, {4,b = 4,5,6} ,a = "ddd"}
local b = { k = a[4]}
local t = {}
save("a",a,t)
save("b",b,t)
print()
And I got the wrong ouput.
a = {
1,
2,
3,
{
"one",
"Two",
}
,
5,
{
4,
5,
6,
}
a[6]["b"] = 4
,
}
a["a"] = "ddd"
b = {}
b["k"] = a[4]
How could I make the text ' a[6]["b"] = 4 ' jump out of the table constructor?

How can I get only the decimal from a float in lua?

How would I be able to do the following?
local d = getdecimal(4.2) --> .2
Assuming you're only working with numbers greater than 0, modulus is the best way to go:
print(4.2%1)
Otherwise the fmod function in the math library should do the trick.
print(math.fmod(4.2,1))
You can take a little bit of a non-paradigmatic approach to this by taking the number and turning it into a string:
function getDec(num)
return tostring(num):match("%.(%d+)")
end
print(getDec(-3.2))
--2
function getDecimal(inp)
local x = tostring(inp)
local found_decimal = false
local output_stream = ""
for i = 1, string.len(x) do
if found_decimal == false then
if string.sub(x, i+1, i+1) == "." then
found_decimal = true
end
else
output_stream = output_stream .. string.sub(x,i, i)
end
end
return output_stream
end
What that does is it basically returns everything after the decimal it found as a string.
And if you want to turn the return back into a number do this:
return tonumber("0" .. output_stream)

Syntax error: player.lua:11: '=' expected near '<eof>'

I have recently been learning Lua with Love2d, so I decided to start making a simple RPG. It's quite simple. The console is where you play, and the extra window is where you can see your stats, equip items, ect.
But, I have run into a problem! Whenever I run the code, I see this main.lua:15: '=' expected near 'else'
I will include the code (all 3 files) below.
This is main.lua
function love.load()
love.graphics.setBackgroundColor( 255, 255, 255 )
require("player")
print("Enter your name")
pcStats.Name = io.read()
print("What class are you, " .. pcStats.Name .. "?")
pcStats.Class = io.read()
if pcStats.Class == "Ranger" then
os.execute("cls")
pcInv.InvSpace = 10
pcInv.Items.basicBow = Basic Bow
else
print("Error: Invalid Class. Please restart game")
end
print("What would you like to do? CODE END")
input = io.read()
end
function love.draw()
love.graphics.setColor( 0, 0, 0 )
love.graphics.print( "Level: " .. pcStats.Level, 1, 1 )
love.graphics.print( "Inv Space: " .. pcInv.InvSpace, 1, 20 )
love.graphics.print( "Inv: " .. pcInv.Items, 1, 40 )
end
Here is player.lua This is where the game variables are stored
pcStats = {}
pcStats.Level = 1
pcStats.XP = nil
pcStats.Name = nil
pcStats.Class = nil
pcStats.Atk = nil
pcStats.Def = nil
pcInv = {}
pcInv.InvSpace = nil
pcInv.Items.testsword = testing sword
And last but not least, here is the conf.lua used for love2d
function love.conf(t)
t.modules.joystick = true
t.modules.audio = true
t.modules.keyboard = true
t.modules.event = true
t.modules.image = true
t.modules.graphics = true
t.modules.timer = true
t.modules.mouse = true
t.modules.sound = true
t.modules.thread = true
t.modules.physics = true
t.console = true
t.title = "Lua RPG Alpha v0.0.1"
t.author = "Zach Herzer"
end
Line 15 is this:
pcInv.Items.basicBow = Basic Bow
Basic Bow isn't valid Lua code. I am pretty sure you meant something else - perhaps a string?
pcInv.Items.basicBow = "Basic Bow"
While we're at it,
pcInv.Items.testsword = testing sword
has a similar problem.

Check if a Lua table member exists at any level

I need to check if a member exists in a table that isn't at the next level, but along a path of members.
foo = {}
if foo.bar.joe then
print(foo.bar.joe)
end
this will cast an attempt to index field 'bar' (a nil value) because bar isn't defined.
My usual solution is to test the chain, piece-by-piece.
foo = {}
if foo.bar and foo.bar.joe then
print(foo.bar.joe)
end
but this can be very tedious when there are many nested tables. Are there a better way to do this test than piece-by-piece?
I don't understand what you try to mean by "along a path of members". From the example, I assume you are trying to find a value in a "subtable"?
local function search(master, target) --target is a string
for k,v in next, master do
if type(v)=="table" and v[target] then return true end
end
end
A simple example. If you use such a function, you can pass the foo table and the joe string to see if foo.*.joe exists. Hope this helps.
debug.setmetatable(nil, {__index = {}})
foo = {}
print(foo.bar.baz.quux)
print(({}).prd.krt.skrz.drn.zprv.zhlt.hrst.zrn) -- sorry ))
To search for an element that is at any level of a table, I would use a method such as this one:
function exists(tab, element)
local v
for _, v in pairs(tab) do
if v == element then
return true
elseif type(v) == "table" then
return exists(v, element)
end
end
return false
end
testTable = {{"Carrot", {"Mushroom", "Lettuce"}, "Mayonnaise"}, "Cinnamon"}
print(exists(testTable, "Mushroom")) -- true
print(exists(testTable, "Apple")) -- false
print(exists(testTable, "Cinnamon")) -- true
I think you're looking for something along these lines:
local function get(Obj, Field, ...)
if Obj == nil or Field == nil then
return Obj
else
return get(Obj[Field], ...)
end
end
local foo = {x = {y = 7}}
assert(get() == nil)
assert(get(foo) == foo)
assert(get(foo, "x") == foo.x)
assert(get(foo, "x", "y") == 7)
assert(get(foo, "x", "z") == nil)
assert(get(foo, "bar", "joe") == nil)
assert(get(foo, "x", "y") or 41 == 7)
assert(get(foo, "bar", "joe") or 41 == 41)
local Path = {foo, "x", "y"}
assert(get(table.unpack(Path)) == 7)
get simply traverses the given path until a nil is encountered. Seems to do the job. Feel free to think up a better name than "get" though.
As usual, exercise care when combining with or.
I'm impressed by Egor's clever answer, but in general I think we ought to not rely on such hacks.
See also
The 'Safe Table Navigation' patch for Lua 5.2 : http://lua-users.org/wiki/LuaPowerPatches
Lengthy discussion on this matter : http://lua-users.org/lists/lua-l/2010-08/threads.html#00519
Related technique : http://lua-users.org/wiki/AutomagicTables
I suspect something relevant has been implemented in MetaLua, but I can't find at the moment.
If I understood your problem correctly, here's one possibility:
function isField(s)
local t
for key in s:gmatch('[^.]+') do
if t == nil then
if _ENV[ key ] == nil then return false end
t = _ENV[ key ]
else
if t[ key ] == nil then return false end
t = t[ key ]
end
--print(key) --for DEBUGGING
end
return true
end
-- To test
t = {}
t.a = {}
t.a.b = {}
t.a.b.c = 'Found me'
if isField('t.a.b.c') then print(t.a.b.c) else print 'NOT FOUND' end
if isField('t.a.b.c.d') then print(t.a.b.c.d) else print 'NOT FOUND' end
UPDATE: As per cauterite's suggestion, here's a version that also works with locals but has to take two arguments :(
function isField(t,s)
if t == nil then return false end
local t = t
for key in s:gmatch('[^.]+') do
if t[ key ] == nil then return false end
t = t[ key ]
end
return true
end
-- To test
local
t = {}
t.a = {}
t.a.b = {}
t.a.b.c = 'Found me'
if isField(t,'a.b.c') then print(t.a.b.c) else print 'NOT FOUND' end
if isField(t,'a.b.c.d') then print(t.a.b.c.d) else print 'NOT FOUND' end
foo = {}
foo.boo = {}
foo.boo.jeo = {}
foo.boo.joe is foo['boo']['joe'] and so
i make next function
function exist(t)
local words = {}
local command
for i,v in string.gmatch(t, '%w+') do words[#words+1] = i end
command = string.format('a = %s', words[1])
loadstring(command)()
if a == nil then return false end
for count=2, #words do
a = a[words[count]]
if a == nil then return false end
end
a = nil
return true
end
foo = {}
foo.boo = {}
foo.boo.joe = {}
print(exist('foo.boo.joe.b.a'))
using loadstring to make temp variable. my lua ver is 5.1
remove loadstring at 5.2 5.3, instead using load

if statement not working in Lua for io.read

I'm trying to make a 'simple' Y/N answer choice thing. (That you saw all the time on old programs) But an If Statement I'm using doesn't seem to want to work. I even print out the variable and it is nowhere near what i want to compare yet it still passes it.
--Porgram Functions
function check()
--Local Variables
local num = 0
local loop = true
io.write("Continue? (Y/N):")
--User input
local input = io.read()
while(loop==true) do
if (input=="y" or "Y") then
print("Ok!")
loop = true
num = 1
elseif (input=="n" or "N") then
print("Fine...")
num = 2
else
print("Invalid Answser!")
loop = true
num = 0
end
end
print(input)
return(num)
end
print (check())
I would've written your function like this:
function check()
io.write("Continue? (Y/N): ")
answer = io.read()
while( not (answer == "Y" or answer == "N") ) do
io.write("Invalid Answer! Try again (Y/N): ")
answer = io.read()
end
if answer == "Y" then
print("Ok!")
return 1
else
print("Fine...")
return 2
end
end
print(check())
Some examples of its use:
Continue? (Y/N): Huh?
Invalid Answer! Try again (Y/N): N
Fine...
2
>Exit code: 0
>lua -e "io.stdout:setvbuf 'no'" "a.lua"
Continue? (Y/N): Huh?
Invalid Answer! Try again (Y/N): Y
Ok!
1
A working version of your code would be:
function check()
local num = 0
local loop = true
io.write("Continue? (Y/N):")
while(loop==true) do
--User input
local input = io.read()
if (input == "y" or input == "Y") then
print("Ok!")
num = 1
loop = false --we want to stop looping if input is valid
elseif (input == "n" or input == "N") then
print("Fine...")
num = 2
loop = false --we want to stop looping if input is valid
else
print("Invalid Answser!")
-- loop = true no need to set looping to true again
num = 0
end
end
return(num)
end
The changes made were:
Get the user input inside the while loop, this way if the input is invalid and the loop goes again the same logic behind getting the input is used, we don't have to code two cases for getting input; one outside the loop the other within. It also pauses execution when the loop starts again, this was what was producing all that output!
input == "y" or "Y" doesn't do what you think. Instead it evaluates to (input == "y") or ("Y"), what you want it input == "y" or input == "Y".
You needed to set loop to false when the input was either "y" or "Y" or "n" or "N", otherwise the loop would continue.
Fourthly setting the loop to true inside the loop is unnecessary, it begins as true, and the only change you can make is to set it to false. And since each of the conditions are mutually exclusive i.e input being "y" or "Y" mutually exclusive to input being "n" or "N" or it being neither "y" or "Y" or "n" or "N". You don't need to worry about it being set to false unless you wanted the loop to end.
local function check()
io.write"Continue? (Y/N): "
local ans, num = {y = 1, n = 2}
repeat
num = ans[io.read():lower()] or 3
io.write(({"Ok!\n","Fine...\n","Invalid Answer! Try again (Y/N): "})[num])
until num < 3
return num
end
print (check())

Resources