Generators Grammar and Yield

# Kevin Smith (10 years ago)

Apologies for this remedial question, but I find the new grammar parameterized around [Yield] to be confusing. If I understand correctly, this is to allow yield within non-strict generators. If that's correct, then why are non-strict generators a good idea?

# Matthew Robb (10 years ago)

Where can I see this new grammar? Is this up somewhere?

# Erik Arvidsson (10 years ago)

It is in the latest spec draft at harmony:specification_drafts

# Waldemar Horwat (10 years ago)

On 11/25/2013 02:03 PM, Kevin Smith wrote:

Apologies for this remedial question, but I find the new grammar parameterized around [Yield] to be confusing. If I understand correctly, this is to allow yield within non-strict generators. If that's correct, then why are non-strict generators a good idea?

It's easier to grammatically distinguish between being inside and outside a generator than it is to distinguish strict vs. non-strict.

To tell whether you're a strict function or not you need to lex the input. To lex you need to parse it. To parse it you need to figure out how to parse yield. Hence you get an obnoxious circularity.

Think of cases such as the following non-strict code snippet:

function(a = yield+b) {
   "use strict";
}
# Kevin Smith (10 years ago)
function(a = yield+b) {
  "use strict";
}

Ah, thanks for pointing out the function-head issue. So for non-strict regular functions, is yield allowed in parameter initializers or not?

# Brendan Eich (10 years ago)

Sure -- yield is just an identifier there.

This makes for wtfjs additions, but they all seem non-wtf on reflection (or did to us when Waldemar threw them up on a whiteboard last week). By non-wtf, I mean anyone who groks that yield is reserved only in function* can work them out.

The star after function really helps. ES5's "use strict" directive prologue in the body applying to its left (even in ES5 -- duplicate formals are a strict error) is goofy.

# Kevin Smith (10 years ago)

Agree on all counts, but not quite understanding yet.

Say I'm parsing this, and the token stream is paused at the "#":

function(a = # yield

I assume that we're not un-reserving yield in strict mode. That means that I don't know whether to treat yield as an identifier or reserved word until I get to that goofy prologue.

Previously, I've just naively parsed parameter initializers using the same strictness as the code which contains the function.

# Brendan Eich (10 years ago)

Ouch, you're right. We can't handle this without backtracking. Waldemar should weigh in.

# Brendan Eich (10 years ago)

Well, we can handle it. We know due to lack of * after function that yield, whether reserved (due to "use strict"; in body prologue) or not, can't be yield-the-operator. So it's either an identifier (no "use strict";) or a reserved word (and an error due to lack of * after function).

So we parse it as an identifier, just as we parse duplicate formal parameters. Then if we see "use strict", we must post-process the parse tree and throw an error. Kind of a shame, but there it is.

At least reserving 'let' in ES5 strict did some good!

# Kevin Smith (10 years ago)

So we parse it as an identifier, just as we parse duplicate formal parameters. Then if we see "use strict", we must post-process the parse tree and throw an error. Kind of a shame, but there it is.

To use Waldemar's word, I wonder if that might get obnoxious:

function(a = function(yield = function(arguments = function(eval) {}) {}) {}) {
  "use strict"; /* Doh! */
}

: )

# Allen Wirfs-Brock (10 years ago)

On Nov 25, 2013, at 4:21 PM, Kevin Smith wrote:

Say I'm parsing this, and the token stream is paused at the "#":

function(a = # yield

I assume that we're not un-reserving yield in strict mode. That means that I don't know whether to treat yield as an identifier or reserved word until I get to that goofy prologue.

Previously, I've just naively parsed parameter initializers using the same strictness as the code which contains the function.

Here is a simplified explanation of how it actually works in the specification:

  1. yield is a reserved keyword at the level of the lexical grammar 1. This applies to both strict and non-strict mode.
  2. The GeneratorDeclaration and GeneratorExpression productions includes the [Yield] grammar parameter on their RHS FormalParameter and FunctionBody nonterminals. 2 This parameterization makes 'yield' legal as an AssignmentExpression operator 3. That use of 'yield' is only enabled within generator definitions and is independent of the strictness.
  3. When the [Yield] parameter is not present, the 'yield' keyword is one of the expansions of the IdentifierReference (via UnresolvedIdentifier) 4 and BindingIdentifier5 productions. This permits 'yield' to be appear outside of Generator definitions in any syntactically allowed identifier position. Again, this is independent of strictness.
  4. There are static semantic early error rules 6, 7 that require an error if 'yield' actually shows up in an IdentiferReference or IdentiferBinding expansion within strict code.
# Brendan Eich (10 years ago)

Kevin Smith wrote:

To use Waldemar's word, I wonder if that might get obnoxious:

 function(a = function(yield = function(arguments = function(eval) {}) {}) {}) {
   "use strict"; /* Doh! */
 }

That's no different in kind from the ES5 + default parameters combo:

function f(a = function(b = function(c, c) {}) {}) { "use strict"; }

Waldemar's general observation was

"It's easier to grammatically distinguish between being inside and outside a generator than it is to distinguish strict vs. non-strict.

To tell whether you're a strict function or not you need to lex the input. To lex you need to parse it. To parse it you need to figure out how to parse yield. Hence you get an obnoxious circularity."

The obnoxiousness with 'yield' in a default parameter is obnoxious, but not by Waldemar's criterion, because (as my self-followup argued) it must be parsed as an identifier (whether or not "use strict" follows in the governing body). There is no parser -> lexer feedback, just deferred error checking.

# Allen Wirfs-Brock (10 years ago)

On Nov 25, 2013, at 5:00 PM, Kevin Smith wrote:

So we parse it as an identifier, just as we parse duplicate formal parameters. Then if we see "use strict", we must post-process the parse tree and throw an error. Kind of a shame, but there it is.

To use Waldemar's word, I wonder if that might get obnoxious:

function(a = function(yield = function(arguments = function(eval) {}) {}) {}) { 
  "use strict"; /* Doh! */
}

: )

The handling of 'yield' in this case is no more obnoxious than any of the other strict mode restriction that apply to those functions that are defined in the parameter list.

# Kevin Smith (10 years ago)

The handling of 'yield' in this case is no more obnoxious than any of the other strict mode restriction that apply to those functions that are defined in the parameter list.

Agreed. My Homer Simpson joke was just trying to point out that (irrespective of yield, per se), post-validating default arguments in light of a later-seen "use strict" directive could require re-validation of the entire parse tree for function expressions included in those defaults. Does that follow?

Not that it's impossible, I just haven't really considered it until now.

Apologies if the joke was obnoxious. : )

# Kevin Smith (10 years ago)

Thanks Allen, this clears up the "yield" situation for me.

# Brendan Eich (10 years ago)

Kevin Smith wrote:

Agreed. My Homer Simpson joke was just trying to point out that (irrespective of yield, per se), post-validating default arguments in light of a later-seen "use strict" directive could require re-validation of the entire parse tree for function expressions included in those defaults. Does that follow?

Yes, "use strict" covers nested functions and there's no "no strict". It's strictly (heh) more wtf-generating (in the ultimately-non-wtf sense) than function*.

# Kevin Smith (10 years ago)

The other option (I say this bracing for pain), is to avoid potential full-tree re-validation by applying strictness to parameter bindings only, and not to default expressions.

This option might not be so bad if you consider that (a) code will gradually be moving into implicitly strict contexts (classes and modules) and (b) users generally don't like the "use strict" prologue anyway.

# Kevin Smith (10 years ago)

Allen,

What's the rationale for not making generator functions implicitly strict? Implicit strictness seems natural for new function forms, minus arrows. Looking forward, I have a hard time imagining that we'll want, say, async functions to be non-strict.

I probably just missed some discussion on this point.

# Allen Wirfs-Brock (10 years ago)

The idea was to treat them as similarly to 'function' as possible, because * is such a small syntactic difference

# Kevin Smith (10 years ago)

The idea was to treat them as similarly to 'function' as possible, because * is such a small syntactic difference

Makes sense, although on the other side you have a huge semantic difference, little refactoring hazard, and yield already reserved.

Thanks again!

# Brendan Eich (10 years ago)

Kevin Smith wrote:

The idea was to treat them as similarly to 'function' as possible,
because * is such a small syntactic difference

Makes sense, although on the other side you have a huge semantic difference, little refactoring hazard, and yield already reserved.

Yes, it's arguable. I was pushing for more implicitly strict bodies, as you may recall ;-). We got module and class, not function (with or without *) or arrow. I buy it, and the consensus seems stable.

Validating default parameters against strict mode seems no worse in kind (if not degree) than validating parameter lists against duplicates was in ES5 days. Gross, but doable and I think better than leaving default expressions non-strict.

# Kevin Smith (10 years ago)

Validating default parameters against strict mode seems no worse in kind (if not degree) than validating parameter lists against duplicates was in ES5 days. Gross, but doable and I think better than leaving default expressions non-strict.

Perhaps you're right. In addition to what we're doing already in ES5 and now ES6 destructuring patterns, I suppose you'd need to recurse into default expressions and look for any bad delete expressions, any WithStatements, and any bad BindingIdentifiers. And rinse and repeat with any FormalParameters encountered.

# Kevin Smith (10 years ago)

Perhaps you're right. In addition to what we're doing already in ES5 and now ES6 destructuring patterns, I suppose you'd need to recurse into default expressions and look for any bad delete expressions, any WithStatements, and any bad BindingIdentifiers. And rinse and repeat with any FormalParameters encountered.

I wonder if you'd need to go back to the lexer in some cases:

function(a = let) {
  "use strict";
}

After seeing the "use strict", I'd have to go back and re-scan the "let" to find out that it was actually a keyword in this context (and hence a syntax error).

# Brendan Eich (10 years ago)

Kevin Smith wrote:

I wonder if you'd need to go back to the lexer in some cases:

function(a = let) {
  "use strict";
}

After seeing the "use strict", I'd have to go back and re-scan the "let" to find out that it was actually a keyword in this context (and hence a syntax error).

The current spec definitely makes 'let' reserved only in strict mode, but we have no let expression forms, so this looks like another case of parsing the kind of example you show above with 'let' in the parameter default value lexed as an Identifier but then rejected during the static semantics phase as an always-an-error-in-expressions reserved word. Good catch. Still tolerable, I think.

# Brendan Eich (10 years ago)

You may be thinking, "Argh, strict mode is deep, so this ought to parse, does it require relexing?"

function f(a = function(){let x = 42; return x}()) {
  "use strict";
  return a;
}

But IIRC (and from reading people.mozilla.org/~jorendorff/es6-draft.html, looking for LetOrConst uses), ES6 supports let in all contexts, reserved word or not.

Did we really do any good trying to reserve yield or let in ES5? I don't see it, now.

# Kevin Smith (10 years ago)

But IIRC (and from reading people.mozilla.org/~jorendorff/es6-draft.html, looking for LetOrConst uses), ES6 supports let in all contexts, reserved word or not.

Yes, I see that now. Has anyone done compatibility-hazard analysis for this breaking change?

;
let["a"].foo() // Fine in ES5 non-strict, fails in ES6?

Did we really do any good trying to reserve yield or let in ES5? I don't see it, now.

It looks like the combination of context-sensitive lexing and the placement of the "use strict" prologue interacts...poorly. (Cue Indiana Jones and the Last Crusade.)

I see now that Allen has moved strict reserved words out of the lexical grammar. Cool!

# Jason Orendorff (10 years ago)

On Mon, Nov 25, 2013 at 7:00 PM, Kevin Smith <zenparsing at gmail.com> wrote:

To use Waldemar's word, I wonder if that might get obnoxious:

 function(a = function(yield = function(arguments = function(eval) {}) {}) {}) {
   "use strict"; /* Doh! */
 }

Implementation note: It is super obnoxious.

# Kevin Smith (10 years ago)

Implementation note: It is super obnoxious.

+1 for adding that note to the spec : ) j/k

It all just makes me wonder if we're better off saying: Forget the 'use strict' prologue, it's foobar. For strictness, use classes and modules instead.

In no way am I trying to recall a sailed ship though.

# Brendan Eich (10 years ago)

Kevin Smith wrote:

Yes, I see that now. Has anyone done compatibility-hazard analysis for this breaking change?

;
let["a"].foo() // Fine in ES5 non-strict, fails in ES6?

That old chestnut! (Unquote "a" for better ambiguity.)

esdiscuss.org/topic/let-and-strict-mode

I honestly forget how we decided to go for let at start of statement followed by [ -- but I do recall we agreed not to have name-binding-sensitive parsing(!). I.e., we would not look for 'var let = ...' and treat 'let' in that (hoisted) scope as a non-keyword.

# Brendan Eich (10 years ago)

Jason Orendorff wrote:

Implementation note: It is super obnoxious.

Just to confirm, not in the backtracking/re-lexing-based-on-parser-feedback sense, right? Just on the "must implement strict checks in a parse tree walker as well as during parsing" sense?

Duplicating strict mode implementation to be both during parsing, and post-processing on a parse tree, does sound bad. How bad, can you say more?

# Brendan Eich (10 years ago)

Brendan Eich wrote:

let["a"].foo() // Fine in ES5 non-strict, fails in ES6?

That old chestnut! (Unquote "a" for better ambiguity.)

Truly hard case:

let[x] = y;

where var let or something worse, e.g., at global scope, this.let = [], has been evaluated.

# Allen Wirfs-Brock (10 years ago)

On Nov 26, 2013, at 11:36 AM, Brendan Eich wrote:

I honestly forget how we decided to go for let at start of statement followed by [ -- but I do recall we agreed not to have name-binding-sensitive parsing(!). I.e., we would not look for 'var let = ...' and treat 'let' in that (hoisted) scope as a non-keyword.

people.mozilla.org/~jorendorff/es6-draft.html#sec-expression-statement

let [...

is not an ExpressionStatement. I requires two token look-ahead for this one special case.

# Allen Wirfs-Brock (10 years ago)

On Nov 26, 2013, at 11:39 AM, Brendan Eich wrote:

Truly hard case:

 let[x] = y;

where var let or something worse, e.g., at global scope, this.let = [], has been evaluated.

We decided (at a TC39 meeting) and after Brian reported some web crawling data that we could probably get away with the breaking change that disallows this formulation as an ExpressionStatement. If anyone actually has such code, then need to change it to:

(let[x])  = y;

or an equivalent formulation.

# Brendan Eich (10 years ago)

Allen Wirfs-Brock wrote:

people.mozilla.org/~jorendorff/es6-draft.html#sec-expression-statement

let [...

is not an ExpressionStatement. I requires two token look-ahead for this one special case.

Thanks. I should have looked -- at this point es6-draft seems to have almost all of the wanted answers. Great work, keep going!

# Kevin Smith (10 years ago)

Thanks. I should have looked -- at this point es6-draft seems to have almost all of the wanted answers. Great work, keep going!

Agreed, thank you Allen for bearing with me!

# Waldemar Horwat (10 years ago)

On 11/25/2013 04:48 PM, Brendan Eich wrote:

Well, we can handle it. We know due to lack of * after function that yield, whether reserved (due to "use strict"; in body prologue) or not, can't be yield-the-operator. So it's either an identifier (no "use strict";) or a reserved word (and an error due to lack of * after function).

So we parse it as an identifier, just as we parse duplicate formal parameters. Then if we see "use strict", we must post-process the parse tree and throw an error. Kind of a shame, but there it is.

At least reserving 'let' in ES5 strict did some good!

For another example of why keying off generator/non-generator instead of strict mode for the parsing of yield is the right thing to do:

function*(a = yield/b/g) {
   a = yield/b/g;
}

One of these is a regexp. The other is a couple divisions.

Get this wrong and you can introduce security problems.

# Jason Orendorff (10 years ago)

On Tue, Nov 26, 2013 at 1:38 PM, Brendan Eich <brendan at mozilla.com> wrote:

Jason Orendorff wrote:

Implementation note: It is super obnoxious.

Just to confirm, not in the backtracking/re-lexing-based-on-parser-feedback sense, right? Just on the "must implement strict checks in a parse tree walker as well as during parsing" sense?

SpiderMonkey rewinds the token stream and re-parses.

I'm past the point of feeling sheepish about it, honestly. The code would have to be super strange for us to rewind much (but yes, in the worst case we can be O(n^2)). The rewinding code is fairly clean and localized. It's also "obviously correct", as long as strict mode is syntactically a subset of sloppy mode. This was true in ES5 and I think it's true in ES6.

Doing it the other way effectively makes a third mode in the parser: strict, non-strict, and "we don't know yet". That's the obnoxious bit. Not hard to make it correct, perhaps; but impossible to make it obviously correct.

Duplicating strict mode implementation to be both during parsing, and post-processing on a parse tree, does sound bad. How bad, can you say more?

I'd do it by noting syntactic strict violations during parsing, when in "we don't know yet" mode, and storing them in the parser until we become sure we weren't in strict mode. We might also need to walk the AST to mark descendent functions as strict retroactively. As long as it's impossible to have code that parses in both modes, with different meanings, it's not so bad.

# Brendan Eich (10 years ago)

Jason Orendorff wrote:

As long as it's impossible to have code that parses in both modes, with different meanings, it's not so bad.

Then we could have trouble, but perhaps do not:

function f(a = arguments[0] = 42) {
   "use strict";
   ...
}

The "use strict" defeats arguments aliasing at runtime. Of course nothing differs in static semantics here, so this is actually easier to deal with than recasting lambdas in defaults as strict functions, e.g. The arguments object's semantic differences are entirely runtime semantic diffs.

Still, it's bothersome. We need to enforce the subset relationships you've identified: ES5 strict < ES6, and no static semantic deviations down the road. Summoning Mark along with Allen.

# Brendan Eich (10 years ago)

Jason Orendorff wrote:

SpiderMonkey rewinds the token stream and re-parses.

I'm past the point of feeling sheepish about it, honestly. The code would have to be super strange for us to rewind much (but yes, in the worst case we can be O(n^2)). The rewinding code is fairly clean and localized. It's also "obviously correct", as long as strict mode is syntactically a subset of sloppy mode. This was true in ES5 and I think it's true in ES6.

Doing it the other way effectively makes a third mode in the parser: strict, non-strict, and "we don't know yet". That's the obnoxious bit. Not hard to make it correct, perhaps; but impossible to make it obviously correct.

Solid reasoning, nothing to feel sheepish about.

# Brendan Eich (10 years ago)

Brendan Eich wrote:

function f(a = arguments[1] = 42, b) {
  "use strict";
  ...
} 

Fixed as cited above -- what I'd written had no observably different outcome in sloppy vs. strict mode.

# Mark S. Miller (10 years ago)

I'm not sure I understand the question. Statically, strict <= sloppy. Dynamically, these are strict arguments, so there's no aliasing. The code parses one way independent of strictness. Strictness determines in retrospect what it means, but does not need to change in retrospect how it parsed. I think I'm just reiterating what's already been said, but does that answer the question?

# Allen Wirfs-Brock (10 years ago)

On Nov 26, 2013, at 5:21 PM, Brendan Eich wrote:

Still, it's bothersome. We need to enforce the subset relationships you've identified: ES5 strict < ES6, and no static semantic deviations down the road. Summoning Mark along with Allen.

In this case there are no static semantic differences between strict and sloppy mode. The difference is in the runtime semantics associated with s [[Set]] on the arguments object.

In spec-land, this distinction is made during the FunctionDeclarationInstantiation runtime operation and manifest as what kind of object is create as the value of arguments. Certainly runtime evaluation takes place after it was statically determined that f was strict. If the generated code is going to differ between strict and sloppy mode then code generation also needs to be delayed until after that determination.

# Brendan Eich (10 years ago)

Mark S. Miller wrote:

Hi Brendan, I'm not sure I understand the question. Statically, strict <= sloppy. Dynamically, these are strict arguments, so there's no aliasing. The code parses one way independent of strictness. Strictness determines in retrospect what it means, but does not need to change in retrospect how it parsed. I think I'm just reiterating what's already been said, but does that answer the question?

Sure, my point is that this constraint applies in the future. We should not evolve strict mode (I think we've agreed to this, not sure how fully in-consensus it is). That plus 1JS should keep the static subset relation in force.

# Brendan Eich (10 years ago)

Allen Wirfs-Brock wrote:

In this case there are no static semantic differences between strict and sloppy mode. The difference is in the runtime semantics associated with s [[Set]] on the arguments object.

In spec-land, this distinction is made during the FunctionDeclarationInstantiation runtime operation and ...

Yeah, I noted that the example is not a problem. The point was big-picture, not that (non-counter-)example.

If anyone knows of a static semantic subset violation, please holler.

Do you think freezing strict mode and keeping to 1JS are enough to preserve the subset relation? I hope so, but things like function-in-block and let-in-sloppy make me wonder. Perhaps you've thought these through more fully.

# Kevin Smith (10 years ago)
function*(a = yield/b/g) {
  a = yield/b/g;
}

Why allow yield as an identifier in parameter defaults of generators? Wouldn't it be simpler just to disallow there as well?

# Brendan Eich (10 years ago)

When proposing an irregularity, you need a better reason than "why allow?"

Parameter default values pave a cowpath of the form

function f(a) {
  a = a || default_a;
  ...
}

(yes, falsy test and all).

The new form does not desugar that way, of course. But defaults are evaluated on each activation (contrast with Python, the mutable value default shared singleton side-channel). So why shouldn't yield in a default work? There could be a reason, but we don't have it yet.

Allen may have a thought.

# Allen Wirfs-Brock (10 years ago)

yield is already disallowed in generator function default initializer expressions (although I was reviewing the latest spec. revision yesterday and there may still be still bugs I have to fix in that regard).

The reason it isn't allowed is that the generator object is not yet instantiated and active at the point where a default value initializer would be evaluated. (note you yield from a invocation on the generator object, not the generator function that creates a generator object) .

# Brendan Eich (10 years ago)

On Nov 28, 2013, at 11:24 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

The reason it isn't allowed is that the generator object is not yet instantiated and active at the point where a default value initializer would be evaluated.

But just to be clear, defaults are evaluated per generator function invocation. Right?

This is a fine reason for a static error on yield in parameter default, but, but to hammer on the virtue of function*, it still does not help with parsing. Good old * after function does.

# Allen Wirfs-Brock (10 years ago)

On Nov 28, 2013, at 11:47 AM, Brendan Eich wrote:

On Nov 28, 2013, at 11:24 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

The reason it isn't allowed is that the generator object is not yet instantiated and active at the point where a default value initializer would be evaluated.

But just to be clear, defaults are evaluated per generator function invocation. Right?

right, but before the implicit yield at the top of the function body

# Kevin Smith (10 years ago)

'yield' is already disallowed in generator function default initializer expressions (although I was reviewing the latest spec. revision yesterday and there may still be still bugs I have to fix in that regard).

The reason it isn't allowed is that the generator object is not yet instantiated and active at the point where a default value initializer would be evaluated. (note you yield from a invocation on the generator object, not the generator function that creates a generator object) .

Yes, 'yield' the operator is disallowed in parameter default expressions. But what about 'yield' the identifier? My current reading of the spec is that it is allowed, but I could be misreading. It seems more natural to me to disallow 'yield' the identifier in the whole generator (formal parameters and body, both).

# Allen Wirfs-Brock (10 years ago)

Yes, the spec. isn't quite right yet in that regard. The intent is that 'yield' is illegal both as an operator and as an identifier in the formal parameter lists of a generator function.

# Kevin Smith (10 years ago)

OK. One last question: I'm reading that the strictness of arrow functions is determined only the strictness of their enclosing context, and not by any prologues inside of them. True?

x => { "use strict"; with (x) {} } // Not a strict-mode error
# Brendan Eich (10 years ago)

Spec bug if so -- that should make the arrow function so stated have strict code.

# Kevin Smith (10 years ago)

That would probably be the expectation, and I'm possibly just missing something in the spec. It does add an extra degree of complexity, though. Consider this edge case:

(x = function z(x) { delete x }) => { "use strict" }

When we parse the invalid unary expression in function 'z', we don't even know that we're in a "maybe strict" context yet. It's only after we see the "=>" that we know. It's still doable without an AST walk though, I think.

# Brendan Eich (10 years ago)

This is no different from

function f(x = function(y) { delete y }) { "use strict"; }

it seems to me. Really it's yet another parameter defaults vs. strict mode. The primal sin is the directive prologue coming in the body instead of before the parameter list, but that's ES5 strict, and arguably it's better to have a useless expression statement there (no new syntax was the rule), than new syntax or a magic comment or string literal expression statement "decorator" before the function.

It's still doable without an AST walk though, I think.

Or a token stream rewind, as Jason pointed out.

# Kevin Smith (10 years ago)

This is no different from

function f(x = function(y) { delete y }) { "use strict"; }

It is in the sense that in the case you present, you already known by virtue of the "function" keyword that you are entering a "maybe" strict context.

But I was able to program around it - not a huge deal.

# Brendan Eich (10 years ago)

Kevin Smith wrote:

sense that in the case you present, you already known by virtue of the "function" keyword that you are entering a "maybe" strict context.

True, with arrows, there's a bit more "No one expects the Spanish Inquisition" ;-). But only a bit more.

# Allen Wirfs-Brock (10 years ago)

On Nov 29, 2013, at 5:21 PM, Kevin Smith wrote:

x => { "use strict"; with (x) {} } // Not a strict-mode error

Spec bug if so -- that should make the arrow function so stated have strict code.

That would probably be the expectation, and I'm possibly just missing something in the spec.

Or the spec. is incomplete...

I actually haven't updated the Directive Prologue section yet, from the ES5 text.

It would be appropriate to file bug reports at bugs.ecmascript.org whenever you find something like this missing or even if it just isn't clear. I use such bugs as todo items WRT improving the draft.

It does add an extra degree of complexity, though. Consider this edge case:

(x = function z(x) { delete x }) => { "use strict" }

When we parse the invalid unary expression in function 'z', we don't even know that we're in a "maybe strict" context yet. It's only after we see the "=>" that we know. It's still doable without an AST walk though, I think.

And you don't know if the parens are delimiting a ParenthesisedExpression or an ArrowParameterList until you do/don't see the =>

Jason suggests keeping the token stream and backing up. Another alternative is remembering the source position of the start of the parameter list and reparsing when the ambiguity and strictness is resolved. Whether text, token, or AST based reprocessing is better is an implementation decision.

# Kevin Smith (10 years ago)

Jason suggests keeping the token stream and backing up. Another alternative is remembering the source position of the start of the parameter list and reparsing when the ambiguity and strictness is resolved. Whether text, token, or AST based reprocessing is better is an implementation decision.

For the case of strictness, I believe it can also be done without reparsing or AST walking 1.

Interestingly, using this approach it turns out that having the directive as the first statement offers no advantage in parsing whatsoever - it might just as well be at the end! : )

# Waldemar Horwat (10 years ago)

On 12/02/2013 09:13 AM, Allen Wirfs-Brock wrote:

Jason suggests keeping the token stream and backing up. Another alternative is remembering the source position of the start of the parameter list and reparsing when the ambiguity and strictness is resolved. Whether text, token, or AST based reprocessing is better is an implementation decision.

It does matter whether it's re-parsed or re-lexed, and there lie sleeping dragons. From the other thread that was exploring the =>* extension that would wake one up:

(a = yield/"/g, b = yield/"/g) =>* {}

Does this generator have one or two parameters?