diff --git a/README.md b/README.md index 91e7060..081d4d0 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,13 @@ A `throw` expression allows you to throw exceptions in expression contexts. For * Parameter initializers ```js - function save(filename = throw new TypeError("Argument required")) { + function save(filename = (throw new TypeError("Argument required"))) { } ``` * Arrow function bodies ```js lint(ast, { - with: () => throw new Error("avoid using 'with' statements.") + with: () => (throw new Error("avoid using 'with' statements.")) }); ``` * Conditional expressions @@ -34,67 +34,80 @@ A `throw` expression allows you to throw exceptions in expression contexts. For const encoder = encoding === "utf8" ? new UTF8Encoder() : encoding === "utf16le" ? new UTF16Encoder(false) : encoding === "utf16be" ? new UTF16Encoder(true) - : throw new Error("Unsupported encoding"); + : (throw new Error("Unsupported encoding")); } ``` * Logical operations ```js class Product { get id() { return this._id; } - set id(value) { this._id = value || throw new Error("Invalid value"); } + set id(value) { this._id = value || (throw new Error("Invalid value")); } } ``` -A `throw` expression *does not* replace a `throw` statement due to the difference -in the precedence of their values. To maintain the precedence of the `throw` statement, -we must add a lookahead restriction to `ExpressionStatement` to avoid ambiguity. - -Due to the difference in precedence between a `throw` expression and a _ThrowStatement_, certain operators to the right -of the expression would parse differently between the two which could cause ambiguity and confusion: +In order to avoid a difference in precedence between `throw` expression and a _ThrowStatement_, we want to ensure that +operators within the expression are parsed in the same way in order to avoid ambiguity and confusion: ```js throw a ? b : c; // evaluates 'a' and throws either 'b' or 'c' -(throw a ? b : c); // without restriction would throw 'a', so `?` is forbidden +(throw a ? b : c); throw a, b; // evaluates 'a', throws 'b' -(throw a, b); // would throw 'a', not 'b', so `,` is forbidden +(throw a, b); throw a && b; // throws 'a' if 'a' is falsy, otherwise throws 'b' -(throw a && b); // would always throw 'a', so `&&` is forbidden +(throw a && b); throw a || b; // throws 'a' if 'a' is truthy, otherwise throws 'b' -(throw a || b); // would always throw 'a', so `||` is forbidden +(throw a || b); // ... etc. ``` -As a result, all binary operators and the `?` operator are forbidden to the right of a `throw` expression. To use these -operators inside of a `throw` expression, the expression must be surrounded with parentheses: +As a result, `throw` expressions are only parsed inside of a _ParenthesizedExpression_, and thus will always require +outer parentheses to be parsed correctly: ```js -(throw (a, b)); // evaluates 'a', throws 'b' -(throw (a ? b : c)); // evaluates 'a' and throws either 'b' or 'c' -``` - -However, we do not forbid `:` so that a `throw` expression can still be easily used in a ternary: - -```js -const x = a ? throw b : c; // if 'a' then throw 'b', else evaluate 'c' +(throw a, b); // evaluates 'a', throws 'b' +(throw a ? b : c); // evaluates 'a' and throws either 'b' or 'c' +a ? (throw b) : c; + +// mostly nonsensical uses, requires extra parenthesis: +if ((throw a)) { } +while ((throw a)) { } +do { } while ((throw a)); +for ((throw a);;) { } +for (; (throw a);) {} +for (;; (throw a)) {} +for (let x in (throw a)) { } +switch ((throw a)) { } +switch (a) { case (throw b): } +return (throw a); +throw (throw a); +`${(throw a)}`; +a[(throw b)]; +a?.[(throw b)]; ``` # Grammar ```diff grammarkdown -++ThrowExpressionInvalidPunctuator : one of - `,` `<` `>` `<=` `>=` `==` `!=` `===` `!==` `+` `-` `*` `/` `%` `**` `<<` `>>` `>>>` `&` `|` `^` `&&` `||` `??` - `=` `+=` `-=` `*=` `%=` `**=` `<<=` `>>=` `>>>=` `&=` `|=` `^=` `&&=` `||=` `??=` `?` - - UnaryExpression[Yield, Await] : -++ `throw` UnaryExpression[?Yield, ?Await] [lookahead ∉ ThrowExpressionInvalidPunctuator] - - ExpressionStatement[Yield, Await] : --- [lookahead ∉ {`{`, `function`, `async` [no |LineTerminator| here] `function`, `class`, `let [`}] Expression[+In, ?Yield, ?Await] `;` -++ [lookahead ∉ {`{`, `function`, `async` [no |LineTerminator| here] `function`, `class`, `let [`, `throw`}] Expression[+In, ?Yield, ?Await] `;` + CoverParenthesizedExpressionAndArrowParameterList[Yield, Await] : + `(` Expression[+In, ?Yield, ?Await] `)` + `(` Expression[+In, ?Yield, ?Await] `,` `)` ++ `(` ThrowExpression[?Yield, ?Await] `)` + `(` `)` + `(` `...` BindingIdentifier[?Yield, ?Await] `)` + `(` `...` BindingPattern[?Yield, ?Await] `)` + `(` Expression[+In, ?Yield, ?Await] `,` `...` BindingIdentifier[?Yield, ?Await] `)` + `(` Expression[+In, ?Yield, ?Await] `,` `...` BindingPattern[?Yield, ?Await] `)` + + ParenthesizedExpression[Yield, Await] : + `(` Expression[+In, ?Yield, ?Await] `)` ++ `(` ThrowExpression[?Yield, ?Await] `)` + ++ ThrowExpression[Yield, Await] : ++ `throw` Expression[+In, ?Yield, ?Await] ``` # Other Notes diff --git a/spec.emu b/spec.emu index 1ae2193..5c99c3c 100644 --- a/spec.emu +++ b/spec.emu @@ -2,7 +2,6 @@ -
 title: ECMAScript 'throw' expressions
 status: proposal
@@ -43,6 +42,11 @@ contributors: Ron Buckton, Ecma International
           RegularExpressionLiteral
           TemplateLiteral
 
+        
+        ThrowExpression :
+          `throw` Expression
+        
+
         MemberExpression :
           MemberExpression `[` Expression `]`
           MemberExpression `.` IdentifierName
@@ -69,7 +73,6 @@ contributors: Ron Buckton, Ecma International
           `delete` UnaryExpression
           `void` UnaryExpression
           `typeof` UnaryExpression
-          `throw` UnaryExpression
           `+` UnaryExpression
           `-` UnaryExpression
           `~` UnaryExpression
@@ -220,6 +223,11 @@ contributors: Ron Buckton, Ecma International
           RegularExpressionLiteral
           TemplateLiteral
 
+        
+        ThrowExpression :
+          `throw` Expression
+        
+
         CallExpression :
           CoverCallExpressionAndAsyncArrowHead
           SuperCall
@@ -253,7 +261,6 @@ contributors: Ron Buckton, Ecma International
           `delete` UnaryExpression
           `void` UnaryExpression
           `typeof` UnaryExpression
-          `throw` UnaryExpression
           `+` UnaryExpression
           `-` UnaryExpression
           `~` UnaryExpression
@@ -334,60 +341,453 @@ contributors: Ron Buckton, Ecma International
 
   

ECMAScript Language: Expressions

- -

Unary Operators

+ +

Primary Expression

Syntax

- UnaryExpression[Yield, Await] : - UpdateExpression[?Yield, ?Await] - `delete` UnaryExpression[?Yield, ?Await] - `void` UnaryExpression[?Yield, ?Await] - `typeof` UnaryExpression[?Yield, ?Await] - `throw` UnaryExpression[?Yield, ?Await] [lookahead ∉ ThrowExpressionInvalidPunctuator] - `+` UnaryExpression[?Yield, ?Await] - `-` UnaryExpression[?Yield, ?Await] - `~` UnaryExpression[?Yield, ?Await] - `!` UnaryExpression[?Yield, ?Await] - [+Await] AwaitExpression[?Yield] + PrimaryExpression[Yield, Await] : + `this` + IdentifierReference[?Yield, ?Await] + Literal + ArrayLiteral[?Yield, ?Await] + ObjectLiteral[?Yield, ?Await] + FunctionExpression + ClassExpression[?Yield, ?Await] + GeneratorExpression + AsyncFunctionExpression + AsyncGeneratorExpression + RegularExpressionLiteral + TemplateLiteral[?Yield, ?Await, ~Tagged] + CoverParenthesizedExpressionAndArrowParameterList[?Yield, ?Await] #parencover + + CoverParenthesizedExpressionAndArrowParameterList[Yield, Await] : + `(` Expression[+In, ?Yield, ?Await] `)` + `(` NestedExpression[?Yield, ?Await] `)` + `(` Expression[+In, ?Yield, ?Await] `,` `)` + `(` `)` + `(` `...` BindingIdentifier[?Yield, ?Await] `)` + `(` `...` BindingPattern[?Yield, ?Await] `)` + `(` Expression[+In, ?Yield, ?Await] `,` `...` BindingIdentifier[?Yield, ?Await] `)` + `(` Expression[+In, ?Yield, ?Await] `,` `...` BindingPattern[?Yield, ?Await] `)` + +

Supplemental Syntax

+

+ When processing an instance of the production
+ PrimaryExpression[Yield, Await] : CoverParenthesizedExpressionAndArrowParameterList[?Yield, ?Await]
+ the interpretation of |CoverParenthesizedExpressionAndArrowParameterList| is refined using the following grammar: +

+ + ParenthesizedExpression[Yield, Await] : + `(` Expression[+In, ?Yield, ?Await] `)` + `(` NestedExpression[?Yield, ?Await] `)` - ThrowExpressionInvalidPunctuator : one of `,` `<` `>` `<=` `>=` `==` `!=` `===` `!==` `+` `-` `*` `/` `%` `**` `<<` `>>` `>>>` `&` `|` `^` `&&` `||` `??` `=` `+=` `-=` `*=` `/=` `%=` `**=` `<<=` `>>=` `>>>=` `&=` `|=` `^=` `&&=` `||=` `??=` `?` + NestedExpression[Yield, Await] : + Expression[+In, ?Yield, ?Await] + ThrowExpression[?Yield, ?Await] -
+ +

The Grouping Operator

+ + +

Static Semantics: Early Errors

+ PrimaryExpression : CoverParenthesizedExpressionAndArrowParameterList +
    +
  • + |CoverParenthesizedExpressionAndArrowParameterList| must cover a |ParenthesizedExpression|. +
  • +
+
+ + +

Runtime Semantics: Evaluation

+ PrimaryExpression : CoverParenthesizedExpressionAndArrowParameterList + + 1. Let _expr_ be the |ParenthesizedExpression| that is covered by |CoverParenthesizedExpressionAndArrowParameterList|. + 1. Return ? Evaluation of _expr_. + + + ParenthesizedExpression : `(` Expression `)` + + + ParenthesizedExpression : `(` NestedExpression `)` + + + 1. Return ? Evaluation of |Expression||NestedExpression|. This may be of type Reference. + + +

This algorithm does not apply GetValue to Evaluation of |Expression||NestedExpression|. The principal motivation for this is so that operators such as `delete` and `typeof` may be applied to parenthesized expressions.

+
+
+
+ + + +

The `throw` Operator

+

Syntax

+ + ThrowExpression[Yield, Await] : + `throw` Expression[?In, ?Yield, ?Await] + +

Runtime Semantics: Evaluation

- UnaryExpression : `throw` UnaryExpression + ThrowExpression : `throw` Expression - 1. Let _exprRef_ be ? Evaluation of |UnaryExpression|. + 1. Let _exprRef_ be ? Evaluation of |Expression|. 1. Let _exprValue_ be ? GetValue(_exprRef_). 1. Return ThrowCompletion(_exprValue_).
+
- -

ECMAScript Language: Statements and Declarations

- -

Expression Statement

-

Syntax

- - ExpressionStatement[Yield, Await] : - [lookahead ∉ { `{`, `function`, `async` [no LineTerminator here] `function`, `class`, `let [` }] Expression[+In, ?Yield, ?Await] `;` - [lookahead ∉ { `{`, `function`, `async` [no LineTerminator here] `function`, `class`, `let [`, `throw` }] Expression[+In, ?Yield, ?Await] `;` - - -

- An |ExpressionStatement| cannot start with a U+007B (LEFT CURLY BRACKET) because that might make it ambiguous with a |Block|. - An |ExpressionStatement| cannot start with the `function` or `class` keywords because that would make it ambiguous with a |FunctionDeclaration|, a |GeneratorDeclaration|, or a |ClassDeclaration|. - An |ExpressionStatement| cannot start with `async function` because that would make it ambiguous with an |AsyncFunctionDeclaration|. - An |ExpressionStatement| cannot start with the two token sequence `let [` because that would make it ambiguous with a `let` |LexicalDeclaration| whose first |LexicalBinding| was an |ArrayBindingPattern|. - An |ExpressionStatement| cannot start with `throw` because that would make it ambiguous with a |ThrowStatement|. -

-
+ +

ECMAScript Language: Functions and Classes

+ +

Tail Position Calls

+ +

+ Static Semantics: HasCallInTailPosition ( + _call_: a |CallExpression| Parse Node, a |MemberExpression| Parse Node, or an |OptionalChain| Parse Node, + ): a Boolean +

+
+
+ +

_call_ is a Parse Node that represents a specific range of source text. When the following algorithms compare _call_ to another Parse Node, it is a test of whether they represent the same source text.

+
+ +

A potential tail position call that is immediately followed by return GetValue of the call result is also a possible tail position call. A function call cannot return a Reference Record, so such a GetValue operation will always return the same value as the actual function call result.

+
+ + StatementList : StatementList StatementListItem + + 1. Let _has_ be HasCallInTailPosition of |StatementList| with argument _call_. + 1. If _has_ is *true*, return *true*. + 1. Return HasCallInTailPosition of |StatementListItem| with argument _call_. + + + FunctionStatementList : + [empty] + + StatementListItem : + Declaration + + Statement : + VariableStatement + EmptyStatement + ExpressionStatement + ContinueStatement + BreakStatement + ThrowStatement + DebuggerStatement + + Block : + `{` `}` + + ReturnStatement : + `return` `;` + + LabelledItem : + FunctionDeclaration + + ForInOfStatement : + `for` `(` LeftHandSideExpression `of` AssignmentExpression `)` Statement + `for` `(` `var` ForBinding `of` AssignmentExpression `)` Statement + `for` `(` ForDeclaration `of` AssignmentExpression `)` Statement + + CaseBlock : + `{` `}` + + + 1. Return *false*. + + IfStatement : `if` `(` Expression `)` Statement `else` Statement + + 1. Let _has_ be HasCallInTailPosition of the first |Statement| with argument _call_. + 1. If _has_ is *true*, return *true*. + 1. Return HasCallInTailPosition of the second |Statement| with argument _call_. + + + IfStatement : + `if` `(` Expression `)` Statement + + DoWhileStatement : + `do` Statement `while` `(` Expression `)` `;` + + WhileStatement : + `while` `(` Expression `)` Statement + + ForStatement : + `for` `(` Expression? `;` Expression? `;` Expression? `)` Statement + `for` `(` `var` VariableDeclarationList `;` Expression? `;` Expression? `)` Statement + `for` `(` LexicalDeclaration Expression? `;` Expression? `)` Statement + + ForInOfStatement : + `for` `(` LeftHandSideExpression `in` Expression `)` Statement + `for` `(` `var` ForBinding `in` Expression `)` Statement + `for` `(` ForDeclaration `in` Expression `)` Statement + + WithStatement : + `with` `(` Expression `)` Statement + + + 1. Return HasCallInTailPosition of |Statement| with argument _call_. + + + LabelledStatement : + LabelIdentifier `:` LabelledItem + + + 1. Return HasCallInTailPosition of |LabelledItem| with argument _call_. + + ReturnStatement : `return` Expression `;` + + 1. Return HasCallInTailPosition of |Expression| with argument _call_. + + SwitchStatement : `switch` `(` Expression `)` CaseBlock + + 1. Return HasCallInTailPosition of |CaseBlock| with argument _call_. + + CaseBlock : `{` CaseClauses? DefaultClause CaseClauses? `}` + + 1. Let _has_ be *false*. + 1. If the first |CaseClauses| is present, let _has_ be HasCallInTailPosition of the first |CaseClauses| with argument _call_. + 1. If _has_ is *true*, return *true*. + 1. Let _has_ be HasCallInTailPosition of |DefaultClause| with argument _call_. + 1. If _has_ is *true*, return *true*. + 1. If the second |CaseClauses| is present, let _has_ be HasCallInTailPosition of the second |CaseClauses| with argument _call_. + 1. Return _has_. + + CaseClauses : CaseClauses CaseClause + + 1. Let _has_ be HasCallInTailPosition of |CaseClauses| with argument _call_. + 1. If _has_ is *true*, return *true*. + 1. Return HasCallInTailPosition of |CaseClause| with argument _call_. + + + CaseClause : `case` Expression `:` StatementList? + + DefaultClause : `default` `:` StatementList? + + + 1. If |StatementList| is present, return HasCallInTailPosition of |StatementList| with argument _call_. + 1. Return *false*. + + TryStatement : `try` Block Catch + + 1. Return HasCallInTailPosition of |Catch| with argument _call_. + + + TryStatement : + `try` Block Finally + `try` Block Catch Finally + + + 1. Return HasCallInTailPosition of |Finally| with argument _call_. + + Catch : `catch` `(` CatchParameter `)` Block + + 1. Return HasCallInTailPosition of |Block| with argument _call_. + + + + AssignmentExpression : + YieldExpression + ArrowFunction + AsyncArrowFunction + LeftHandSideExpression `=` AssignmentExpression + LeftHandSideExpression AssignmentOperator AssignmentExpression + LeftHandSideExpression `&&=` AssignmentExpression + LeftHandSideExpression `||=` AssignmentExpression + LeftHandSideExpression `??=` AssignmentExpression + + BitwiseANDExpression : + BitwiseANDExpression `&` EqualityExpression + + BitwiseXORExpression : + BitwiseXORExpression `^` BitwiseANDExpression + + BitwiseORExpression : + BitwiseORExpression `|` BitwiseXORExpression + + EqualityExpression : + EqualityExpression `==` RelationalExpression + EqualityExpression `!=` RelationalExpression + EqualityExpression `===` RelationalExpression + EqualityExpression `!==` RelationalExpression + + RelationalExpression : + RelationalExpression `<` ShiftExpression + RelationalExpression `>` ShiftExpression + RelationalExpression `<=` ShiftExpression + RelationalExpression `>=` ShiftExpression + RelationalExpression `instanceof` ShiftExpression + RelationalExpression `in` ShiftExpression + PrivateIdentifier `in` ShiftExpression + + ShiftExpression : + ShiftExpression `<<` AdditiveExpression + ShiftExpression `>>` AdditiveExpression + ShiftExpression `>>>` AdditiveExpression + + AdditiveExpression : + AdditiveExpression `+` MultiplicativeExpression + AdditiveExpression `-` MultiplicativeExpression + + MultiplicativeExpression : + MultiplicativeExpression MultiplicativeOperator ExponentiationExpression + + ExponentiationExpression : + UpdateExpression `**` ExponentiationExpression + + UpdateExpression : + LeftHandSideExpression `++` + LeftHandSideExpression `--` + `++` UnaryExpression + `--` UnaryExpression + + UnaryExpression : + `delete` UnaryExpression + `void` UnaryExpression + `typeof` UnaryExpression + `+` UnaryExpression + `-` UnaryExpression + `~` UnaryExpression + `!` UnaryExpression + AwaitExpression + + CallExpression : + SuperCall + CallExpression `[` Expression `]` + CallExpression `.` IdentifierName + CallExpression `.` PrivateIdentifier + + NewExpression : + `new` NewExpression + + MemberExpression : + MemberExpression `[` Expression `]` + MemberExpression `.` IdentifierName + SuperProperty + MetaProperty + `new` MemberExpression Arguments + MemberExpression `.` PrivateIdentifier + + + NestedExpression : + ThrowExpression + + + PrimaryExpression : + `this` + IdentifierReference + Literal + ArrayLiteral + ObjectLiteral + FunctionExpression + ClassExpression + GeneratorExpression + AsyncFunctionExpression + AsyncGeneratorExpression + RegularExpressionLiteral + TemplateLiteral + + + 1. Return *false*. + + + Expression : + AssignmentExpression + Expression `,` AssignmentExpression + + + 1. Return HasCallInTailPosition of |AssignmentExpression| with argument _call_. + + ConditionalExpression : ShortCircuitExpression `?` AssignmentExpression `:` AssignmentExpression + + 1. Let _has_ be HasCallInTailPosition of the first |AssignmentExpression| with argument _call_. + 1. If _has_ is *true*, return *true*. + 1. Return HasCallInTailPosition of the second |AssignmentExpression| with argument _call_. + + LogicalANDExpression : LogicalANDExpression `&&` BitwiseORExpression + + 1. Return HasCallInTailPosition of |BitwiseORExpression| with argument _call_. + + LogicalORExpression : LogicalORExpression `||` LogicalANDExpression + + 1. Return HasCallInTailPosition of |LogicalANDExpression| with argument _call_. + + CoalesceExpression : CoalesceExpressionHead `??` BitwiseORExpression + + 1. Return HasCallInTailPosition of |BitwiseORExpression| with argument _call_. + + + CallExpression : + CoverCallExpressionAndAsyncArrowHead + CallExpression Arguments + CallExpression TemplateLiteral + + + 1. If this |CallExpression| is _call_, return *true*. + 1. Return *false*. + + + OptionalExpression : + MemberExpression OptionalChain + CallExpression OptionalChain + OptionalExpression OptionalChain + + + 1. Return HasCallInTailPosition of |OptionalChain| with argument _call_. + + + OptionalChain : + `?.` `[` Expression `]` + `?.` IdentifierName + `?.` PrivateIdentifier + OptionalChain `[` Expression `]` + OptionalChain `.` IdentifierName + OptionalChain `.` PrivateIdentifier + + + 1. Return *false*. + + + OptionalChain : + `?.` Arguments + OptionalChain Arguments + + + 1. If this |OptionalChain| is _call_, return *true*. + 1. Return *false*. + + + MemberExpression : + MemberExpression TemplateLiteral + + + 1. If this |MemberExpression| is _call_, return *true*. + 1. Return *false*. + + PrimaryExpression : CoverParenthesizedExpressionAndArrowParameterList + + 1. Let _expr_ be the |ParenthesizedExpression| that is covered by |CoverParenthesizedExpressionAndArrowParameterList|. + 1. Return HasCallInTailPosition of _expr_ with argument _call_. + + + ParenthesizedExpression : + `(` Expression `)` + + + 1. Return HasCallInTailPosition of |Expression| with argument _call_. + +
\ No newline at end of file