...

View Full Version : convert string to executable code?



anotherJEK
10-09-2012, 10:38 PM
I have a calculator program that takes a string entered into a text field,
runs it through a syntax screening function and then executes the
string via eval(), which I told I should never use.

So the problem is how do I convert a string representing an arithmetic
operation to executable code without using eval? (hopefully without
another 50 -100 lines of code)

as such


// screen code... assuming success..
sample = '3*(42/9.8)'; // for instance
return eval(sample)
// change to ????

xelawho
10-09-2012, 11:44 PM
I have no idea if this is more or less evil than the evil eval()...



<body>
<input id="sums"/>
<input type="button" value="calculate" onclick="calcIt()"/>
<script type="text/javascript">
function calcIt(){
var sample = document.getElementById("sums").value;
if(document.getElementById("myscript")){
document.body.removeChild(document.getElementById("myscript"));
}
var scr=document.createElement("script");
scr.id="myscript";
scr.text='var myFunc=function(){return '+sample+'}';
document.body.appendChild(scr);
alert(myFunc())
}
</script>
</body>

mrhoo
10-10-2012, 01:12 AM
Clean up the user input before eval-
only allow integers, decimal points, parens and operators.

And put down that scissors, you'll poke your eye out!


function calcuval(string){
try{
return eval(string.replace(/[^1-9.()/*+-]+/g, ''));
}
catch(er){
return NaN;
}
}
calcuval('3*(42/9.8)');>>> returned value: (Number)
12.857142857142858
*/

anotherJEK
10-10-2012, 01:57 AM
I have a function that actually crawls char for char through the string and detects all infractions to syntax rules I developed for this. (see below..
I won't show the code that checks syntax)
So, I take it so far that there is no 'safer', shorter way.
All I can see is a bunch of code that will analyse the string and break into
components and construct a function on the fly that will execute the calculation.

Actually, I modified the program to allow spaces since this was written.

This application only does arithmetic.

1: Only one decimal point in a number.

2: Only one operator in a row: +, -, /, *.

3: Open parenthesis must be preceded immediately
by an operator or another open parenthesis.

4: For every open parenthesis there must be a closing parenthesis.
First parenthesis must be an open parenthesis and the last must be
a close parenthesis.
For example 5*(9*8)) would give an imbalance parenthesis error
where as 8*)+10 will give first parenthesis is not an open parenthesis

5: Close parenthesis must be followed by an operator or
another close parenthesis. 2*(2+9)(4/5) is an error.
There must be an operator between (2+9) and (4/5)
The one exeption is, of course, the last character in
the expression can be a close parenthesis if it is
need to balance an open parenthesis.

6: Close parenthesis must be preceded by a number or closed
parenthesis (preceded by a number)
For example 4*(4-) would be an error

7: When the expression begins with open parenthesis,
precede it with the proper + or - sign.
For example: (5-3)*(45+2); enter +(5-3)*(45+2)
or -(5-3)*(45+2) if the sign for the first factor is negative

8: Do not enter spaces anywhere.

9: Do not enter empty parenthesis, with or without spaces: () or ( ).

10: Any character other than digits (0 - 9), . (decimal point), + - * /, ( )
is not allowed. * is for multiplication, / is for division.
This includes $, or any money symbol, %, square root symbol, =,
< (less than), > (greater than), ' or " for foot/inch or min/sec
in angular measurements.

Please note that 31/2 is read as 31 divided by 2, not 3 1/2.
Convert fractions to decimals or express 3 1/2 as 3+(1/2);
in context; 2*(3+(1/2)). 2*3.5 is cleaner.

11: The last character cannot be an operator.

12: If there is no fractional part of a factor, do not use a decimal point without trailing zero (0)

Notes:
The use of parenthesis can become confusing when used too much.
Keeping track of open and close sets can be troublesome and give
wrong results when not used correctly (even though no errors have
occurred). In general use parenthesis to separate calculations
to insure the proper order is maintained. In the example above;
+(5-3)*(45+2), if the parenthesis where not used 3*45 would be
done first, then 5-(3*45), then (5-(3*45)) - 2, which would be the
wrong result. Like wise, in the example above, 2*(3+(1/2)), without
the parenthesis, 2*3 and 1/2 would be done first, then (2*3)+(1/2).
The first calculation gives 7, while the second gives 5.5 (without
parenthetic isolation).

In programming languages, the % (percent) sign is a valid operator.
It is called modulus and will result in the fractional part of a division
operation that does not result in a whole number.
For instance: 5.5%1=.5
It has some clever uses and I have used it successfully in programming
logic. But it has little or no place in common arithmetic operations.

jmrker
10-10-2012, 04:14 AM
Clean up the user input before eval-
only allow integers, decimal points, parens and operators.

And put down that scissors, you'll poke your eye out!


function calcuval(string){
try{
return eval(string.replace(/[^1-9.()/*+-]+/g, ''));
}
catch(er){
return NaN;
}
}
calcuval('3*(42/9.8)');>>> returned value: (Number)
12.857142857142858
*/

@mrhoo
Could you explain how that works?
In particular the string.replace() argument.
I thought it would replace any of the characters within the [brackets] with a space, but that is obviously not what it is doing.

Is it replacing anything that is not within the [brackets] with a space? If yes, how or why?

I thought I understood the .replace function, but this version seems to work backward from my understanding. :confused:


Also, what is the (er) in the catch portion of the function? How might that be used or be of use elsewhere?

mrhoo
10-10-2012, 04:54 AM
starting the replace regexp group with a caret ([^) negates the match, so anything not in the group is replaced with the empty string.


The (er) is just a habit, I can't type a catch without it. Not required here.

Philip M
10-10-2012, 07:55 AM
jmrker - you have interpreted '' as a space, not nothing. That is why I always use double quotes "" in preference.

But my understanding is that eval is evil, not so much because it is slow, but because (as here) it allows the execution of arbitrary code (although here stripped to numbers and maths symbols). But perhaps that is not a risk with client-side Javascript running in the browser.

For clarity I would do:-


try {
string = string.replace(/[^1-9.()/*+-]+/g, ""); // clean up input
return eval(string);
}

jmrker
10-10-2012, 03:01 PM
starting the replace regexp group with a caret ([^) negates the match, so anything not in the group is replaced with the empty string.


The (er) is just a habit, I can't type a catch without it. Not required here.

Thank you. That now makes sense. :D

I was under the impression that the ^ character meant to start the replacement analysis from the beginning of the string. I thought also that you could start the analysis from the end of the string by using a $ character. Perhaps I am mixing other program language syntax again. :eek:

Philip M
10-10-2012, 03:59 PM
The caret ^ has two meanings depending on the context.

Within a pattern it means "the start of the string"


var str = "Philip";
if (/^p/i.test(str)) {
alert ("The first character of the string is a P");
}

But within a character class [square brackets] it means negation (not)


var str = "I have 3 sisters"
if (/[^a-z\s]/gi.test(str)) {
alert ("The string contains a character which is not a-z or space");
}

$ also has two meanings. One is, as you say, the end of the string. Also when using replace you can reference groups using ‘$1′, ‘$2′, ‘$3′ etc. in the replacement string.


var str = "I wonder how I can remove (these brackets)..."
str .replace(/\((.+?)\)/, '$1'); // Removes the brackets
alert (str);


https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Regular_Expressions

jmrker
10-10-2012, 05:29 PM
The caret ^ has two meanings depending on the context.

Within a pattern it means "the start of the string"


...

But within a character class [square brackets] it means negation (not)


...

https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Regular_Expressions

Thanks. It was the second explanation I was missing from my limited knowledge base. :thumbsup:

anotherJEK
10-10-2012, 08:40 PM
Well, I started a conversation... about use of regex. But all the replies still
use eval. I have been avoiding regex as much as possible, in favor of
string methods, as, assume the same is true for javascript as it is for php,
that string functions/methods are much faster than regex.
for instance:


// probably inside of a loop that looks at every char in a string
var testChar = '?';
if(('!@#$%^&*()_+').indexOf(testChar) > -1)
{
alert ('BOO...')
}
else
{
alert('Yay...')
}

xelawho
10-10-2012, 09:00 PM
But all the replies still use eval.

are you sure about that?

jmrker
10-10-2012, 09:33 PM
Well, I started a conversation... about use of regex. But all the replies still
use eval. I have been avoiding regex as much as possible, in favor of
string methods, as, assume the same is true for javascript as it is for php,
that string functions/methods are much faster than regex.
for instance:


Unless you are in a multiple executed loop, I don't think the speed of a regex or eval() commands will make much difference to your outcome.

I thought your original question was if there was a way to avoid using eval() without a ton of code for mathematical calculations. Probably not.

The danger of using eval(), as I see it, is allowing users to execute non-math related code. To that end, the use of a regex comparison to filter out all but math related characters is probably safe.



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum