JScript has an extremely powerful (and almost always misused, but that's another story) feature: eval takes a string at runtime and treats that string as though it were part of the compile-time text of the program. I added that feature to VBScript version 5, but I did it with three methods: Execute, ExecuteGlobal and Eval. Why three, when JScript makes do with one? Let's start by examining in detail what the JScript "eval" function does.
The JScript "eval" function takes a string, treats the string as JScript code, compiles the string, executes the resulting code, and returns the value of the last expression evaluated while running the code.
Now, in JScript (and many other C-like languages) there is a fairly weak distinction between a statement and an expression. For example, this is perfectly legal JScript:
function foo()
{
1;
2;
3 + 4;
}
This doesn't do much; it doesn't even return a value! But that's not the compiler's problem. The behaviour of the line 3 + 4; for instance is to add three to four and discard the result.
Also note that semicolons are optional in JScript (sort of -- I'll post more about that later.)
So when you say eval("3 + 4") in JScript you get seven -- the compiler adds the semicolon on, executes the statement 3 + 4; and returns the result of the last expression computed.
Now consider how JScript evaluates expressions that reference variables. The implementation of eval is smart enough to follow JScript's rules for inner scopes shadowing outer scopes:
var x = 20;
var x = 10;
print(eval("x")); // 10 -- eval uses local
This seems reasonable, right? But what if you evaluate a declaration?
eval("var x = 10");
foo();
print(x); // 20 -- declaration was local to foo.
Well, maybe it's silly to want to add a declaration using eval. But hold on, as we already discussed, named functions are basically just variables. Suppose you wanted to add a function dynamically:
eval("function bar(){ return 123; }");
print(bar()); // 123
print(bar()); // fails
Why does the latter fail? Because the eval is done in the scope of the function foo, so function bar is local to foo. When foo goes away, so does bar.
The long and short of it is that if you want to affect the global name space, you have to do it explicitly. For example:
var bar;
eval("function barlocal(){ return 123; } bar = barlocal;");
print(bar()); // succeeds, bar is a global function.
(And of course, bar now refers to a closure. If foo took any arguments, they would be captured by barlocal.)
Now, suppose you were tasked with implementing eval in VBScript. A few salient facts might spring to mind:
1) VBScript doesn't have first class functions, so this trick with assigning a local function into global scope won't work. (Well, VBScript has a very weak form of first class functions that I'll discuss later.)
2) But contrariwise, it would be a real pain in the rear if the VBScript's "eval" couldn't access local variables, and worked ONLY at global scope.
3) VBScript does not have this weird property that expressions are statements. In fact, you can't determine whether you're looking at an expression or a statement lexically thanks to the assignment and equality operators being the same. Suppose you see this string:X = Y Does that mean "set variable X to the value of Y" or does that mean "compare X to Y, leave both the same, and return True or False"? Obviously we want to be able to do both, but how do we tell the difference?
There are three things that we need to do: evaluate expressions, execute statements using local scope and execute statements using global scope. My philosophy is when you have three things to do, implement three methods. Hence, we have Eval, which takes an expression and returns its value, Execute, which takes a group of statements and executes them in local scope, and ExecuteGlobal which executes them in global scope.
"I understand why you need to distinguish between Eval and Execute," I hear you say, "but why have both Execute and ExecuteGlobal? Why not just add an optional -IsGlobal- flag to Execute?"
Good question. First of all, in my opinion it is bad coding style to implement public methods which have very different behaviour based on the value of a flag. You do two things, have two methods.
Second, Boolean flags are a bad idea because sometimes you want to extend the method even more. VBScript has this problem in the runtime -- a lot of the methods take an argument which is either 0 for "case insensitive" or 1 for "case sensitive" or a valid LCID, for "case sensitive in this locale". What a mess! (Worse, 1 is a valid locale identifier: Arabic-with-neutral-sublanguage.)
In the case at hand, I suppose an enumerated type would be superior to a Boolean and extensible to boot, but still, it makes my skin crawl to see one public method that does two things based on a flag where two methods will do.
And third, when I wrote the code I did not know that VB .NET was the future and that we would not continue to add features to VBScript indefinitely. I figured that by having knowledge at compile time whether the dynamic code is going to affect the local or global scope, one might be able to do certain optimizations at compile time in a hypothetical future compiler.
Hi i have to execute "sub procedure" and "calling sub" string statements as blow.
Call abc
sub abc
msgbox("i am in sub")
End sub
i am reading this codes as string form other file .
can i execute this Sub using Eval or Execute methods?