Help Width My Code

yes that’s what my real code was about

my system only allows full strings

If you’re heart-set on using a string to denote your mathematical expression, you could always do this:

local expr = '1 + 1' 
local eval = loadstring(string.format('return %s', expr))() 
print(eval) --> 2

Beware though, using this in combination with user input will open an opportunity for code injection

1 Like

Because your using “” and trying to print a function (math in this case) - print(tonumber(1+1))
EDIT: you don’t have to use tonumber for this

You can probably just override the environment of the created function to prevent code injection

local func = loadstring(string.format('return %s', expr))
setfenv(func, {math = math})
local eval = func()
print(eval)
1 Like

The issue is that the string is not a number.

Computers do not understand context, so to a computer, the string “1 + 1” literally means “one space plus space one”.

tonumber only works on strings that contain ONLY numbers.

1 Like

better give me a solution to my problem

function StringCalculator(String)
    local operations = {
        ["+"] = function(a, b)
            return a + b
        end,
        
        ["-"] = function(a, b)
            return a - b
        end,
        
        ["*"] = function(a, b)
            return a * b
        end,
        
        ["/"] = function(a, b)
            return a / b
        end,
    }


    local values = string.split(test, " ")

    local x = values[1]
    local oper = values[2]
    local y = values[3]

    return operations[oper](x, y)
end

StringCalculator("1 + 1")

I made @itsLevande’s code into a function so it is better then it was

sadly this only works for two numbers and only one operator and you need spaces to separate the numbers and operator, this is a very very basic string calculator.

loadstring() or another code exucuter is probably better then this

also sorry this took me forever, train wifi is horrible

There’s a way to expand the capability to a string of any length with any number of operators. The Shunting-Yard algorithm is one that can parse string expressions with no risk of code injection.

While I would love to implement this algorithm in lua, I don’t want to.

I realize this, I was mainly talking about the code you gave and what limitations it had

I could probably make this if you wanted me to, I mean not anytime soon because I’m working on a game but when I start working on my math module again I could try and implement this into it

1 Like

Good idea. I also had the idea to incorporate string pattern matching as well. Here is the result of me combining that idea with yours:

local function parse(expr)
	local strcmp = ''
	
	for str in string.gmatch(expr, '[%(%)]*%d-[%.%d-]*%s*[%+%-%*/%^]*%s*') do
		strcmp = strcmp .. str
	end
	
	return strcmp ~= expr and 0 or setfenv(loadstring(string.format('return %s;', expr)), { })();
end

local badmath = '1.3 + 4 and print("a")'

print(badmath,'=',parse(badmath)) --> 0

local goodmath = '(1.3+4.6)^ 2 - 1'

print(goodmath,'=',parse(goodmath)) --> 33.81

Though OP would likely get the best benefit from learning about the algorithm that @itsLevande linked

honestly I think implementing this in lua should be a bit easier due to Rosetta code have a challenge about this

lua is not on there yet but you could look at a similar language and convert it into lua

I decided to try to solve it without leaving the Wikipedia page, using the example problem the page provided. Here is my result:

shunting yard
local OPERATOR_PRECEDENCE = {
    -- [operator] = { [precedence], [is left assoc.] }
    ['-'] = { 2, true };
    ['+'] = { 2, true };
    ['/'] = { 3, true };
    ['*'] = { 3, true };
    ['^'] = { 4, false };   
}

local function shuntingYard(expression)
    local outputStack = { }
    local operatorStack = { }
    
    local number, operator, parenthesis, fcall

    while #expression > 0 do
        local nStartPos, nEndPos = string.find(expression, '(%d+%.?%d*)')
        
        if nStartPos == 1 and nEndPos > 0 then
            number, expression = string.sub(expression, nStartPos, nEndPos), string.sub(expression, nEndPos + 1)
            table.insert(outputStack, tonumber(number))
        else
            local oStartPos, oEndPos = string.find(expression, '([%-%+%*/%^])')
            
            if oStartPos == 1 and oEndPos > 0 then
                operator, expression = string.sub(expression, oStartPos, oEndPos), string.sub(expression, oEndPos + 1)
                
                if #operatorStack > 0 then
                    while operatorStack[1] ~= '(' do
                        local operator1Precedence = OPERATOR_PRECEDENCE[operator]
                        local operator2Precedence = OPERATOR_PRECEDENCE[operatorStack[1]]
                    
                        if operator2Precedence and ((operator2Precedence[1] > operator1Precedence[1]) or (operator2Precedence[1] == operator1Precedence[1] and operator1Precedence[2])) then
                            table.insert(outputStack, table.remove(operatorStack, 1))
                        else
                           break 
                        end
                    end
                end
                
                table.insert(operatorStack, 1, operator)
            else
                local pStartPos, pEndPos = string.find(expression, '[%(%)]')
                
                if pStartPos == 1 and pEndPos > 0 then
                    parenthesis, expression = string.sub(expression, pStartPos, pEndPos), string.sub(expression, pEndPos + 1)
                    
                    if parenthesis == ')' then
                        while operatorStack[1] ~= '(' do
                            assert(#operatorStack > 0)
                            table.insert(outputStack, table.remove(operatorStack, 1))
                        end
                        
                        assert(operatorStack[1] == '(')
                        table.remove(operatorStack, 1)
                    else
                        table.insert(operatorStack, 1, parenthesis)
                    end
                else
                    local wStartPos, wEndPos = string.find(expression, '%s+')
                    
                    if wStartPos == 1 and wEndPos > 0 then
                        expression = string.sub(expression, wEndPos + 1)
                    else
                        error('Invalid character set: '.. expression)
                    end
                end
            end
        end
    end
    
    while #operatorStack > 0 do
        assert(operatorStack[1] ~= '(')
        table.insert(outputStack, table.remove(operatorStack, 1))
    end
    
    return table.concat(outputStack, ' ')
end


local goodmath = '3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3'

print('shunting yard:', shuntingYard(goodmath)) --> output: 3 4 2 * 1 5 - 2 3 ^ ^ / +

Unfortunately I don’t have enough time to deal with functions, as I have to get back to writing code for my own game. But this sure was fun! brings back memories of competing against peers in my comp. sci classes. Maybe someone will write a more efficient implementation now that I threw my hat into the ring?

1 Like