Jump to content




Calculate user's input.


  • You cannot reply to this topic
19 replies to this topic

#1 joshmanisdabomb

  • Members
  • 42 posts
  • LocationHell

Posted 07 July 2013 - 06:19 AM

Title: Calculate user's input.
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)
I think you guys get the idea. Is there any way to do this? Thank you.

Edit: String.

#2 Lyqyd

    Lua Liquidator

  • Moderators
  • 8,465 posts

Posted 07 July 2013 - 11:19 AM

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.

#3 TheOddByte

    Lazy Coder

  • Members
  • 1,607 posts
  • LocationSweden

Posted 07 July 2013 - 12:18 PM

Uhmm do you mean something like this?
Spoiler
Not sure if it works since I typed this on my phone and haven't tested it..

#4 joshmanisdabomb

  • Members
  • 42 posts
  • LocationHell

Posted 07 July 2013 - 12:54 PM

Thank you for the relatively fast responses!

 Lyqyd, on 07 July 2013 - 11:19 AM, said:

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.

Hmmmm. It seems risky...
Could I make it so it filters out any code and only does math..? Somehow?

 Hellkid98, on 07 July 2013 - 12:18 PM, said:

Uhmm do you mean something like this?
Spoiler
Not sure if it works since I typed this on my phone and haven't tested it..

That's not what I mean. I don't want 3 read functions, I want the user to be able to type something like "8+8" in one read function and show what it equals.

#5 GopherAtl

  • Members
  • 888 posts

Posted 08 July 2013 - 06:43 PM

If you only want arithmetic operations and numerical constants, you could test the input first with a lua pattern, like this...
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

Tested, but not in-game... The lua pattern will match strings containing only combinations of white space, numbers, and the basic operators (+, -, *, /, %, ^)

I'm not at all sure the pcall is even necessary; I can't think of any way to make a string that matches the pattern and succeeds in loadstring, but errors on running. But, seems better safe than sorry, I may be overlooking something!

#6 Pharap

  • Members
  • 816 posts
  • LocationEngland

Posted 09 July 2013 - 05:59 PM

Best method I can think of is handling the key events yourself.
Make it so you can only accept numeric or symbol input by only handling those keys.
Then you'd just have to use tables to cache user input and then do a bit of string parsing to deliver the result.
Your real problem would come from what order to handle the expressions in (ie what order to process statements in)

#7 ElvishJerricco

  • Members
  • 803 posts

Posted 09 July 2013 - 06:12 PM

If you really want to make yourself a much smarter programmer and go way over the top with this, you could learn compiler theory and develop an extremely simple, math only language, and compile users' input and return the result of running that program.

#8 Engineer

  • Members
  • 1,378 posts
  • LocationThe Netherlands

Posted 09 July 2013 - 07:08 PM

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


#9 ElvishJerricco

  • Members
  • 803 posts

Posted 09 July 2013 - 07:50 PM

 Engineer, on 09 July 2013 - 07:08 PM, said:

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

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.

#10 Pharap

  • Members
  • 816 posts
  • LocationEngland

Posted 09 July 2013 - 07:55 PM

Why don't you just make a GUI calculator? It would be easier anyway.
Plus the "lua" program can already act as a calculator.

#11 MysticT

    Lua Wizard

  • Members
  • 1,597 posts

Posted 09 July 2013 - 07:59 PM

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.

#12 ElvishJerricco

  • Members
  • 803 posts

Posted 09 July 2013 - 08:05 PM

 MysticT, on 09 July 2013 - 07:59 PM, said:

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.

This is probably the best, easiest solution. And maybe the environment could __index to the math API to allow for math functions?

setfenv(func, setmetatable({}, {__index=math}))


#13 Engineer

  • Members
  • 1,378 posts
  • LocationThe Netherlands

Posted 10 July 2013 - 01:01 AM

 ElvishJerricco, on 09 July 2013 - 07:50 PM, said:

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)

#14 ElvishJerricco

  • Members
  • 803 posts

Posted 10 July 2013 - 02:10 AM

 Engineer, on 10 July 2013 - 01:01 AM, said:

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).

#15 Engineer

  • Members
  • 1,378 posts
  • LocationThe Netherlands

Posted 10 July 2013 - 02:34 AM

 ElvishJerricco, on 10 July 2013 - 02:10 AM, said:

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.

#16 ElvishJerricco

  • Members
  • 803 posts

Posted 10 July 2013 - 02:38 AM

 Engineer, on 10 July 2013 - 02:34 AM, said:

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.

Well my original point was that to do this well without using loadstring, you're going to have to know compiler theory. A knowledge of deterministic grammars will be essential, the ability to tokenize the input so that you don't have to check each character while parsing, the ability to create a parse tree, the ability to make an operation-based syntax tree. It'll all be there if you want the program to be A ) Functional, B ) Well-organized, and C ) easier to fix bugs in.

#17 GopherAtl

  • Members
  • 888 posts

Posted 10 July 2013 - 09:40 AM

None of that is required for a basic arithmetic expression parser. Not grammars, not tokenizing the input, not checking each character while parsing, not parse trees, not syntax trees, none of it. I wrote several, in a handful of different languages, back in high school before I'd even learned what most of those things were.

#18 joshmanisdabomb

  • Members
  • 42 posts
  • LocationHell

Posted 10 July 2013 - 11:50 AM

 Pharap, on 09 July 2013 - 07:55 PM, said:

Why don't you just make a GUI calculator? It would be easier anyway.
Plus the "lua" program can already act as a calculator.

Because I don't want a GUI calculator.

 MysticT, on 09 July 2013 - 07:59 PM, said:

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.

This seems like the most reasonable way of doing this! :D/> I'll say thank you in advance, but I really can't check right now because I'm not on a computer that runs Minecraft well enough. I only just remembered I made this topic! :wacko:/>

Edited by Lyqyd, 10 July 2013 - 12:35 PM.
cleanup.


#19 Engineer

  • Members
  • 1,378 posts
  • LocationThe Netherlands

Posted 10 July 2013 - 12:53 PM

For those who are interested, I did make a function for a stringed calculation input. However, sadly, it doesnt quite work yet. I know what is failing, but Im not bothered to fix it properly because this function is pointless. I'd rather use loadstring with an environment where you cant even input numbers etc.

Here is the "not-working" code:
Spoiler



#20 joshmanisdabomb

  • Members
  • 42 posts
  • LocationHell

Posted 10 July 2013 - 05:16 PM

Thank you for all your help guys! The loadstring method worked! Big thanks to everybody who contributed.

Posted Image


The magenta number is where 5+5 was typed. I hope to make it so it reads variables.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users