Piotr Praszmo
Piotr Praszmo

Reputation: 18340

import Math namespace in eval()

I have function source stored in a string eg.:

var source="(function(){return Math.sin(x);})";

I want to pass it to eval() and calculate it's value later:

var f=eval(source);
var v=f();

It works well. However, I want to be able to use functions from Math without Math. prefix. So I would be able to do:

var source="(function(){return sin(x);})";

Is there a simple way to achieve this? I already use WebGL, so it doesn't have to be compatible with older browsers.

Additional info:

I'm aware that eval() is bad. I'm writing function grapher, source is function entered by the user that is going to be drawn. That's also why want to get rid of Math. prefixes.

I know it's not going to be pretty but I'm not going to write my own parser. Maybe there is some better way?

Upvotes: 1

Views: 3368

Answers (4)

MRule
MRule

Reputation: 620

Debates about the safety and compatibility of eval aside, one can avoid the performance penalty in this use case via partial evaluation.

If fstring is a string containing user input like "x*cos(x)",

with (Math) f=eval("(function(x) {return "+fstring+";})");

will create a function f based on the code in fstring. f can be applied quickly to a range of values to generate the graphing calculator output. The performance penalty for doing namespace lookups via with happens only once, when f is created.

Upvotes: 1

pianoJames
pianoJames

Reputation: 484

If you're going to use eval(), maybe you could take the user-entered function and do a few global, case-insensitive replacements:

var source = document.getElementById("someInputBox").value;
source = source.replace(/sin/ig,"Math.sin");
source = nsource.replace(/cos/ig,"Math.cos");
source = source.replace(/sqrt/ig,"Math.sqrt");
source = source.replace(/pi/ig,"Math.PI");
source = source.replace(/^/ig,"Math.PI");
source = source.replace(/([a-z]|\d+(?:\.\d+)?)*\s*\^\s*([a-z]|\d+(?:\.\d+)?)/ig, "Math.pow($1, $2)");
result = eval(source);

IMPORTANT: The Math.pow() replacement won't work for advanced power expressions, such as 5^(5-3^(2*2)). You'll need to find a more advanced strategy for those. This website has a solution in PHP, which could possibly be converted to javascript.

Upvotes: 2

Rob W
Rob W

Reputation: 349142

You can use with for this purpose:

var source="(function(){return Math.sin(x);})";
with(Math){
    var f = eval(source);
    var v = f();
}

See also: MDN: with

Upvotes: 3

Lekensteyn
Lekensteyn

Reputation: 66465

You can use the ancient, slow, ugly with keyword to extend the scope:

var source="(function(){return sin(x);})";
with (Math) {
    var f=eval(source);
    var v=f();
}

See also: https://developer.mozilla.org/en/JavaScript/Reference/Statements/with

Upvotes: 7

Related Questions