Why does VBScript have Execute, ExecuteGlobal and Eval?

JavaScript 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 ExecuteGlobaland Eval

Why three, when JavaScript makes do with one? Let’s start by examining in detail what the JavaScript eval function does.

The JavaScript 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.

In JavaScript (and many other C-like languages) there is a fairly weak distinction between a statement and an expression. For example, this is perfectly legal JavaScript:

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 statement 3+4; for instance is to add three to four and discard the result.

Also note that semicolons are semi-optional; more on that in a later post.

When you say eval("3+4") in JavaScript 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 JavaScript evaluates expressions that reference variables. The implementation of evalis smart enough to follow JScript’s rules for inner scopes shadowing outer scopes:

var x = 20;
function foo()
{
  var x = 10;
  print(eval("x"));
  // 10 -- eval uses local
}

This seems reasonable. But what if you evaluate a declaration?

var x = 20;
function foo()
{
  eval("var x = 10");
}
foo();
print(x);
// 20 -- declaration was local to foo.

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 with functions as values. Suppose you wanted to add a function dynamically:

function foo()
{
  eval("function bar(){ return 123; }");
  print(bar()); // 123
}
foo();
print(bar()); // fails

Why does the latter fail? Because the eval is done in the activation of the function foo, so function bar is local to foo. When the activation of foo goes away, so does local 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;
function foo()
{
  eval("function barlocal(){ return 123; } bar = barlocal;");
}
foo();
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, as I was. A few salient facts might spring to mind:

  • VBScript doesn’t have first class functions, so this trick with assigning a local function into global scope won’t work. (VBScript has a very weak form of first class functions that I’ll discuss later.)
  • But contrariwise, it would be a real pain in the rear if the VBScript’s eval equivalent couldn’t access local variables, and worked only at global scope.
  • 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 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 produce 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
  • 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.


Commentary from 2019:

This article produced some interesting reader responses:


You do have a ExecuteGlobal of sorts in JavaScript using the new Function(…) notation. When you define a function in this way, it uses in the global scope rather than the containing scope. Add to that the JavaScript “feature” where variables declared without var are added to the global scope, and you get the effect of a ExecuteGlobal.

Good point; use it like this:

var x = 20;
function foo()
{
  var x = 30;
  var bar = new Function("x = 10");
  bar();
}
print(x); // 20

It seems that eval cannot distinguish between block statements and object literals! Compare eval("value={1:2,3:4}") to value=eval("{1:2,3:4}");

Great example! eval takes a statement; An object literal is not a legal statement, and so automatic semi insertion will not insert a semi.

Advertisements

1 thought on “Why does VBScript have Execute, ExecuteGlobal and Eval?

  1. Pingback: Porting old posts, part 2 | Fabulous adventures in coding

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s