The impossible pretty solution

2013-08-11

I'm working on ZeParser2, implementing the esoteric for-in lhs cases, and am kind of running into a wall. I want to disallow for(x=1 in y); (under a flag), but it must allow for(x=1;;);. And that seems difficult. The problem is that some functions would need to return two values, which is not possible in JS unless you use some kind of object. Which we really don't want to do for perf reasons.

ZeParser2 is built up of parser functions. Each function either gobbles up a certain amount of tokens (smallest atom of source code), or it calls other parsers that do. None of the parsers need to return anything so they are free to use that "channel" for other reasons.

One such reason is for detecting whether some expression is a valid assignee. A valid assignee is anything to which you could validly assign a value. In JS that means variables and properties (ok, and calls..). This also means groups that wrap a single expression that's a valid assignee. Or groups that wrap such a group. The parser functions that might be called while parsing the LHS of an assignment will return whether whatever they parsed is a valid assignee. This works well.

Another thing to use the return value for is to determine whether an assignment has been parsed in the LHS of a for header. The reason is that this is valid for regular for loops but not for-in.

And therein lies the problem. The assignment parser, which optional parses the operator and RHS of an assignment, needs to return whether it parsed an assignment at all, but if so it also needs to return whether the RHS could be a valid assignee. We can't assert an error right here yet because we don't know yet whether it'll be a loop or a for-in... For example: for (a=b*c=d/e in f); would need to throw for trying to assign to b*c, which is never valid. But at the same time, there's an assignment in the LHS of in, AND the RHS of the assignment is not a valid assignee so could not be used with for-in anyways. I'll leave it as an exercise for the reader to generate the examples where only one or two of those issues exist. Then you'll see that it's not that straightforward to pass or reject those snippets with these parsers.

For this to work we basically need to return multiple boolean values. While we could use an object or array literal for this purpose, it would be not efficient because it would create (otherwise) useless objects for the GC. And many of them.

But since we don't care about actual values, just booleans, we can cheat a little with flags. If we return a number and & them, we can treat the number as a flag and distill the values from it that way. It's still not pretty, but at least perf won't suffer a whole lot...