function hoisting like var
Ingvar von Schoultz
ingvar-v-s at comhem.se
Mon Jul 28 11:05:15 PDT 2008
Ingvar von Schoultz wrote:
> In theory {{ code }} could be converted to a function that
> returns information about whatever break/continue/return was
> reached. But I'd be quite surprised if that is easy.
>
> Something like this. Written with syntax sugar:
>
> Outer:
> for (var OutName in OutThing)
> for (var InName in InThing)
> {{
> break Outer;
> }}
>
> Translation:
>
> Outer:
> for (var OutName in OutThing)
> {
> var _Result = (function (_InThing)
> {
> for (var InName in _InThing)
> {
> return ({JumpSpot: 'break Outer'})
> }
> })(InThing);
> if (_Result.JumpSpot == 'break Outer')
> break Outer;
> }
>
> The inner for() is part of the scoping block, so it belongs
> inside, even though the original code has it above.
>
> We must make sure the value of InThing is available inside
> even if the name is declared for a different variable inside.
>
> I use initial underscore to indicate something internal and
> invisible.
>
> It doesn't look complicated here! Unfortunately these things
> have a terrible tendency to grow in complexity...
I now think this could be made very simple.
The solution is to make this version slightly limited. Don't
support break/continue statements that mention any label
outside {{ }}. Leave that for a future version.
This simplifies things tremendously! And you don't need that
kind of break/continue all that often. (But a future version
must include it.)
This limited solution only needs break/continue functionality
that is already supported. Even the necessary label checking
is already supported. An unsupported break such as the following
gives a syntax error if you translate {{ }} to a one-shot
function:
Outer:
for (var OutName in OutThing)
for (var InName in InThing)
{{
break Outer;
}}
The error message that I get says "undefined label", which
is misleading, but apart from that it works perfectly.
If people find the above limited support for break/continue
acceptable for this version, all that remains is the return
statement.
It seems easiest to use a solution that leaves all the return
statements in the original code intact. This makes the translation
from {{ }} to function a little elaborate and odd, but everything
is nicely contained at one spot.
Within the created function, if control reaches the final }},
throw a special error that isn't really an error, rather the
opposite, it's a signal that indicates that this is a normal
termination. When this signal is received, continue below }}.
If instead, within the created function, control reaches a
return statement that appears in the original code, the result
is a regular return, without the above thrown signal. This
indicates that whatever value was returned should be passed
along as return value of the enclosing function.
This means that {{ }} is essentially translated into this:
try
{ var ReturnValue = (function()
{ // The code that was written between {{ }} goes here.
throw NormalEndSignal;
})();
return ReturnValue;
}
catch (Sig)
{ if (Sig != NormalEndSignal)
throw Sig;
}
It seems to me that this becomes very nicely contained,
very manageable.
Implementations will probably want to optimize away this
entire elaborate arrangement and replace it with plain and
simple scoping blocks. But they can take their time. The
above provides the functionality in a simple way.
That is, unless I've missed something, and the above solution
doesn't work the way I think it does.
--
Ingvar von Schoultz
------- (My quirky use of capitals in code comes from my opinion that
reserved and predefined words should all start with lowercase, and
user-defined should all start with uppercase, because this will easily
and elegantly prevent a host of name-collision problems when things
like programming languages are upgraded with new labels.)
More information about the Es4-discuss
mailing list