I would like to know if there is a way to work out maths when the user inputs a string like:
- "1+1" (would return 2)
- "2+2" (4)
- "5-4" (1)
- "5-6" (-1)
- "6*2.5" (15)
- "6/2.5" (2.4)
- "6+4+5-2" (13)
Edit: String.
function clear()
term.clear()
term.setCursorPos(1,1)
end
while true do
clear()
print("Method [+ -]")
repeat
local input = read()
until input == "+" or input == "-"
if input == "+" then
mode = "add"
else
mode = "sub"
end
clear()
write("First number: ")
repeat
fNum = read()
until tonumber(fNum)
fNum = tonumber(fNum)
write("Second number: ")
repeat
sNum = read()
until tonumber(sNum)
sNum = tonumber(sNum)
if mode = "add" then
local answ = fNum + sNum
clear()
print(fNum.." + "..sNum.." = "..answ)
os.pullEvent("key")
elseif mode == "sub" then
clear()
local answ = fNum - sNum
print(fNum.." - "..sNum.." = "..answ)
os.pullEvent("key")
end
end
Split into new topic.
There is a very simple way, but it's rather dangerous. You can just loadstring it and use that to calculate the string as if it were a Lua statement, but then users can execute any arbitrary code in your program. There are other ways, but they're a fair bit more work, especially if you want anything beyond basic arithmetic.
Uhmm do you mean something like this?Not sure if it works since I typed this on my phone and haven't tested it..Spoiler
function clear() term.clear() term.setCursorPos(1,1) end while true do clear() print("Method [+ -]") repeat local input = read() until input == "+" or input == "-" if input == "+" then mode = "add" else mode = "sub" end clear() write("First number: ") repeat fNum = read() until tonumber(fNum) fNum = tonumber(fNum) write("Second number: ") repeat sNum = read() until tonumber(sNum) sNum = tonumber(sNum) if mode = "add" then local answ = fNum + sNum clear() print(fNum.." + "..sNum.." = "..answ) os.pullEvent("key") elseif mode == "sub" then clear() local answ = fNum - sNum print(fNum.." - "..sNum.." = "..answ) os.pullEvent("key") end end
while true do
write(">")
local inStr=read()
if inStr==nil or inStr=="" then
break
end
if inStr:match("^[%s%d%.%-%+%*%/%%%^]+$") then
local f,err=loadstring("return "..inStr)
if f then
local succ, res=pcall(f)
print(res)
else
print(err)
end
else
print("Invalid Expression!")
end
end
local calc = ('calc string here'):gsub(' ', '')
if not calc:find('^[0-9%-%+/%*%%%^]') then
local digits = {}
for i = 1, calc:len() do
table.insert( digits, calc:sub( i, i )
end
-- actual calculating here
end
This would require parsing and all that fancy stuff. Probably not the right term though.. Example:
(untested and from my phone)local calc = ('calc string here'):gsub(' ', '') if not calc:find('^[0-9%-%+/%*%%%^]') then local digits = {} for i = 1, calc:len() do table.insert( digits, calc:sub( i, i ) end -- actual calculating here end
local input = read()
local func = loadstring("return "..input)
if func then
setfenv(func, {})
local ok, result = pcall(func)
if ok then
print(result)
else
printError("Error: ", result)
end
else
printError("Invalid input")
end
Well, the easiest way is what Lyqyd said, and you can use a custom environment that doesn't have any apis or global functions (so people can't use something like "os.reboot()" or whatever).
Using an empty environment like this should work:local input = read() local func = loadstring("return "..input) if func then setfenv(func, {}) local ok, result = pcall(func) if ok then print(result) else printError("Error: ", result) end else printError("Invalid input") end
This way you could also make an environment with some math functions and constants.
setfenv(func, setmetatable({}, {__index=math}))
Yea I've been working on a compiler for a new implementation of Lua for a while now (so that I can extend the language however I like). I've read several chapters out of a number of books, and have read most of the infamous Dragon Book. Trust me, that's not enough for something like that. You've got to do lexical analysis to turn all the input characters into individual tokens, a full finite state machine to turn the input string into a parse tree, then you compact that down to an abstract syntax tree, and the rest should be easy calculation. But all that is rather complex.
What you've done is made sure the input string is legal in terms of what characters are include, but not in terms of actual syntax (for example, 1 ^* 3 shouldn't be legal)(also, you did this wrong. You should be putting the ^ after the square bracket in order to mean "not in this set". What you did means "the following set is at the beginning"). Then you've turned the input string into an array of all the characters. Then you say "Do the rest here…." Rather useless.
For a matter of fact, I know That 'do the rest here' is rather useless. But Im not going to write it out on my phone, so I decided to do get it started. Im really up for the challenge though when Im on an actual computer, where I can test and stuff.
I will definitely post a wroking piece of code right here, you just have to wait :)/> (I dont know how long its going to take to actually write it and when I can get to a computer)
For simple math expressions, it'll likely be much easier. Can't tell you quite how much easier. But chances are you can skip some of the proper compiler steps. I do however challenge you to write an actual Lua compiler that compiles down to either the Lua bytecode for LuaJ, or LASM (recommending LASM because it'd be a thousand times easier, and compiling to assembly is what real compilers do).
Hold on for a minute, a compiler?
I was actually more thinking about a function that calculates your string, nothing much more. Im actually very, very unfamilair with compiler theory and that stuff. So to clearify, Im going to make a function to calculate a string, nothing to due with compilers.
Why don't you just make a GUI calculator? It would be easier anyway.
Plus the "lua" program can already act as a calculator.
Well, the easiest way is what Lyqyd said, and you can use a custom environment that doesn't have any apis or global functions (so people can't use something like "os.reboot()" or whatever).
Using an empty environment like this should work:local input = read() local func = loadstring("return "..input) if func then setfenv(func, {}) local ok, result = pcall(func) if ok then print(result) else printError("Error: ", result) end else printError("Invalid input") end
This way you could also make an environment with some math functions and constants.
local calcString = function( s )
assert( type(s) == "string", "Bad Argument: String expected, got " .. type( s ), 2 )
s = s:gsub( "[^0-9%%%+%-%*/%^]", "" )
assert( s:len() >= 3 and s:sub( -1 ):find( "[^%%%+%-%*/%^]" ), "Valid calculation expected", 2 )
local combo = {
[ "-+" ] = "-"; [ "+-" ] = "-";
[ "--" ] = "+"; [ "/-" ] = "/ n";
[ "*-" ] = "* n"; [ "%-" ] = "% n";
[ "^-" ] = "% n";
}
local numbers = {}
local operators = {}
local mutate = function( operator, operatorIndex )
local calc = nil
if operator == "^" then
calc = numbers[ operatorIndex ] ^ numbers[ operatorIndex + 1 ]
elseif operator == "*" then
calc = numbers[ operatorIndex ] * numbers[ operatorIndex + 1 ]
elseif operator == "/" then
calc = numbers[ operatorIndex ] / numbers[ operatorIndex + 1 ]
elseif operator == "+" then
calc = numbers[ operatorIndex ] + numbers[ operatorIndex + 1 ]
elseif operator == "-" then
calc = numbers[ operatorIndex ] - numbers[ operatorIndex + 1 ]
end
if calc then -- To avoid possible errors
operators[ operatorIndex ] = "AaP"
table.remove( numbers, operatorIndex )
table.remove( numbers, operatorIndex + 1 )
table.insert( numbers, operatorIndex, calc )
end
end
local sequence = {
[ 1 ] = "^" ; [ 2 ] = "*/";
[ 3 ] = "+-";
}
for number in s:gmatch( "[0-9]+" ) do
table.insert( numbers, tonumber(number) )
end
local first = true
for operator in s:gmatch( "[%%%+%-%*/%^]+" ) do
if operator:len() == 2 then
if combo[ operator ] then
if combo[ operator ]:find("n") then
numbers[ #operators + 2 ] = -numbers[ #operators + 2 ]
end
table.insert( operators, combo[ operator ]:sub( 1, 1 ))
else
error( "Unexpected symbol in calculation.", 2 )
end
else
table.insert( operators, operator )
if first then
if operator == "-" and s:sub( 1, 1 ) == "-" then
numbers[ 1 ] = -numbers[ 1 ]
table.remove( operators, #operators )
elseif operator == "-" and s:sub( 1, 1 ) ~= "-" then
error( "Unexpected symbol in calculation.", 2 )
end
first = false
end
end
end
for i = 1, 3 do
for key, operator in ipairs( operators ) do
if operator:find( "[" ..
(function( sPattern )
local mChars = {
[ "^" ] = true; [ "*" ] = true;
[ "+" ] = true; [ "-" ] = true;
}
local safePattern = ""
for i = 1, sPattern:len() do
if mChars[sPattern:sub( i, i )] then
safePattern = safePattern .. "%" .. sPattern:sub( i, i )
else
safePattern = safePattern .. sPattern:sub( i, i )
end
end
return safePattern
end)( sequence[ i ] ).. "]" ) then
if sequence[ i ]:len() == 2 then
if operator == sequence[ i ]:sub( 1, 1 ) then
mutate( sequence[ i ]:sub( 1, 1 ), key )
elseif operator == sequence[ i ]:sub( 2, 2 ) then
mutate( sequence[ i ]:sub( 2, 2 ), key )
end
else
mutate( sequence[ i ], key )
end
end
end
end
return numbers[ 1 ]
end
I think it's not the best code, but it will do the job once that little bug is fixed. Don't use this!