├── .gitattributes ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── index.html ├── package.json └── spec.emu /.gitattributes: -------------------------------------------------------------------------------- 1 | index.html -diff merge=ours 2 | spec.js -diff merge=ours 3 | spec.css -diff merge=ours 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Only apps should have lockfiles 40 | yarn.lock 41 | package-lock.json 42 | npm-shrinkwrap.json 43 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 ECMA TC39 and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # proposal-logical-assignment 2 | 3 | A proposal to combine Logical Operators and Assignment Expressions: 4 | 5 | ```js 6 | // "Or Or Equals" (or, the Mallet operator :wink:) 7 | a ||= b; 8 | a || (a = b); 9 | 10 | // "And And Equals" 11 | a &&= b; 12 | a && (a = b); 13 | 14 | // "QQ Equals" 15 | a ??= b; 16 | a ?? (a = b); 17 | ``` 18 | 19 | ## Status 20 | 21 | Current [Stage](https://tc39.es/process-document/): 4 22 | 23 | ## Champions 24 | 25 | - Justin Ridgewell ([@jridgewell](https://github.com/jridgewell/)) 26 | - Hemanth HM ([@hemanth](https://github.com/hemanth/)) 27 | 28 | ## Motivation 29 | 30 | Convenience operators, inspired by 31 | [Ruby's](https://docs.ruby-lang.org/en/2.5.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment). 32 | We already have a dozen [mathematical assignment 33 | operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators), 34 | but we don't have ones for the often used logical operators. 35 | 36 | ```js 37 | function example(a) { 38 | // Default `a` to "foo" 39 | if (!a) { 40 | a = 'foo'; 41 | } 42 | } 43 | ``` 44 | 45 | If statements work, but terseness would be nice. Especially when dealing 46 | with deep property assignment: 47 | 48 | ```js 49 | function example(opts) { 50 | // Ok, but could trigger setter. 51 | opts.foo = opts.foo ?? 'bar' 52 | 53 | // No setter, but 'feels wrong' to write. 54 | opts.baz ?? (opts.baz = 'qux'); 55 | } 56 | 57 | example({ foo: 'foo' }) 58 | ``` 59 | 60 | With this proposal, we get terseness and we don't have to suffer from 61 | setter calls: 62 | 63 | ```js 64 | function example(opts) { 65 | // Setters are not needlessly called. 66 | opts.foo ??= 'bar' 67 | 68 | // No repetition of `opts.baz`. 69 | opts.baz ??= 'qux'; 70 | } 71 | 72 | example({ foo: 'foo' }) 73 | ``` 74 | 75 | ## Semantics 76 | 77 | The logical assignment operators function a bit differently than their 78 | mathematical assignment friends. While math assignment operators 79 | _always_ trigger a set operation, logical assignment embraces their 80 | short-circuiting semantics to avoid it when possible. 81 | 82 | ```js 83 | let x = 0; 84 | const obj = { 85 | get x() { 86 | return x; 87 | }, 88 | 89 | set x(value) { 90 | console.log('setter called'); 91 | x = value; 92 | } 93 | }; 94 | 95 | // This always logs "setter called" 96 | obj.x += 1; 97 | assert.equal(obj.x, 1); 98 | 99 | // Logical operators do not call setters unnecessarily 100 | // This will not log. 101 | obj.x ||= 2; 102 | assert.equal(obj.x, 1); 103 | 104 | // But setters are called if the operator does not short circuit 105 | // "setter called" 106 | obj.x &&= 3; 107 | assert.equal(obj.x, 3); 108 | ``` 109 | 110 | In most cases, the fact that the set operation is short-circuited has no 111 | observable impact beyond performance. But when it has side effects, it 112 | is often desirable to avoid it when appropriate. In the following 113 | example, if the `.innerHTML` setter was triggered uselessly, it could 114 | result in the loss of state (such as focus) that is not serialized in 115 | HTML: 116 | 117 | ```js 118 | document.getElementById('previewZone').innerHTML ||= 'Nothing to preview'; 119 | ``` 120 | 121 | See discussion of short-circuit semantics in [#3](https://github.com/tc39/proposal-logical-assignment/issues/3). It also highlights differences already present in mathematical assignment operators in code like `obj.deep[key++] += 1` vs `obj.deep[key] = obj.deep[key++] + 1`. 122 | 123 | ## Related 124 | 125 | - [Ruby's logical operators](https://docs.ruby-lang.org/en/2.5.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment) 126 | - [Explainer on no-set semantics](http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html) 127 | - [CoffeeScript](http://coffeescript.org/#try:a%20%3D%201%0Ab%20%3D%202%0A%0A%0A%23%20%22Or%20Or%20Equals%22%20(or%2C%20the%20Mallet%20operator%20%3Awink%3A)%0Aa%20%7C%7C%3D%20b%3B%0Aa%20%7C%7C%20(a%20%3D%20b)%3B%0A%0A%23%20%22And%20And%20Equals%22%0Aa%20%26%26%3D%20b%3B%0Aa%20%26%26%20(a%20%3D%20b)%3B%0A%0A%23%20Eventually....%0A%23%20%22QQ%20Equals%22%0A%23a%20%3F%3F%3D%20b%3B%0A%23a%20%3F%3F%20(a%20%3D%20b)%3B%0A) 128 | - [C#'s null coalescing assignment](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/null-coalescing-assignment#detailed-design) 129 | - My very first [Babel PR](https://github.com/babel/babel/pull/516) (back when it was still [6to5](https://github.com/babel/babel/tree/ecd85f53b4764ada862537aa767699814f1f1fe2)). 😄 130 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Logical Assignment Operators

Stage 4 Draft / July 21, 2020

Logical Assignment Operators

1849 | 1850 | 1851 |

1 Scope

1852 |

1853 | This is the spec text of the Logical Assignment proposal in ECMAScript. 1854 |

1855 |

1856 | Each Logical Assignment operator embraces the short-circuiting semantics of the respective Binary Logical operator. If the operator short circuits, then the [[Set]] operation is skipped. 1857 |

1858 |

1859 | The new LogicalAssignmentPunctuator production defined in Punctuators section is used only to highlight the new additions. When merged with the main ecma262 spec, the new punctuators should just be added to OtherPunctuator. 1860 |

1861 |
1862 | 1863 | 1864 |

2 Punctuators (11.7)

1865 |

Syntax

1866 | 1867 | Punctuator::LogicalAssignmentPunctuator 1868 | OptionalChainingPunctuator 1869 | OtherPunctuator 1870 | 1871 | 1872 | LogicalAssignmentPunctuator::one of&&=||=??= 1873 | 1874 | 1875 | OptionalChainingPunctuator::?.[lookahead ∉ DecimalDigit] 1876 | 1877 | 1878 | OtherPunctuator::one of{()[]....;,<><=>===!====!==+-*%**++--<<>>>>>&|^!~&&||???:=+=-=*=%=**=<<=>>=>>>=&=|=^==> 1879 | 1880 | 1881 | DivPunctuator::/ 1882 | /= 1883 | 1884 | 1885 | RightBracePunctuator::} 1886 | 1887 |
1888 | 1889 | 1890 |

3 Assignment Operators (12.15)

1891 |

Syntax

1892 | 1893 | AssignmentExpression[In, Yield, Await]:ConditionalExpression[?In, ?Yield, ?Await] 1894 | [+Yield]YieldExpression[?In, ?Await] 1895 | ArrowFunction[?In, ?Yield, ?Await] 1896 | AsyncArrowFunction[?In, ?Yield, ?Await] 1897 | LeftHandSideExpression[?Yield, ?Await]=AssignmentExpression[?In, ?Yield, ?Await] 1898 | LeftHandSideExpression[?Yield, ?Await]AssignmentOperatorAssignmentExpression[?In, ?Yield, ?Await] 1899 | LeftHandSideExpression[?Yield, ?Await]&&=AssignmentExpression[?In, ?Yield, ?Await] 1900 | LeftHandSideExpression[?Yield, ?Await]||=AssignmentExpression[?In, ?Yield, ?Await] 1901 | LeftHandSideExpression[?Yield, ?Await]??=AssignmentExpression[?In, ?Yield, ?Await] 1902 | 1903 | 1904 | AssignmentOperator:one of*=/=%=+=-=<<=>>=>>>=&=^=|=**= 1905 | 1906 | 1907 | 1908 |

3.1 Static Semantics: Early Errors

1909 | 1910 | AssignmentExpression:LeftHandSideExpression=AssignmentExpression 1911 | 1912 | 1920 | 1921 | 1922 | AssignmentExpression:LeftHandSideExpressionAssignmentOperatorAssignmentExpression 1923 | LeftHandSideExpression&&=AssignmentExpression 1924 | LeftHandSideExpression||=AssignmentExpression 1925 | LeftHandSideExpression??=AssignmentExpression 1926 | 1927 |
    1928 |
  • 1929 | It is an early Syntax Error if AssignmentTargetType of LeftHandSideExpression is not simple. 1930 |
  • 1931 |
1932 |
1933 | 1934 | 1935 |

3.2 Static Semantics: IsFunctionDefinition

1936 | 1937 | 1938 | AssignmentExpression:ArrowFunction 1939 | AsyncArrowFunction 1940 | 1941 |
  1. Return true.
1942 | 1943 | AssignmentExpression:YieldExpression 1944 | LeftHandSideExpression=AssignmentExpression 1945 | LeftHandSideExpressionAssignmentOperatorAssignmentExpression 1946 | LeftHandSideExpression&&=AssignmentExpression 1947 | LeftHandSideExpression||=AssignmentExpression 1948 | LeftHandSideExpression??=AssignmentExpression 1949 | 1950 |
  1. Return false.
1951 |
1952 | 1953 | 1954 |

3.3 Static Semantics: AssignmentTargetType

1955 | 1956 | 1957 | AssignmentExpression:YieldExpression 1958 | ArrowFunction 1959 | AsyncArrowFunction 1960 | LeftHandSideExpression=AssignmentExpression 1961 | LeftHandSideExpressionAssignmentOperatorAssignmentExpression 1962 | LeftHandSideExpression&&=AssignmentExpression 1963 | LeftHandSideExpression||=AssignmentExpression 1964 | LeftHandSideExpression??=AssignmentExpression 1965 | 1966 |
  1. Return invalid.
1967 |
1968 | 1969 | 1970 |

3.4 Runtime Semantics: Evaluation

1971 | 1972 | AssignmentExpression:LeftHandSideExpression=AssignmentExpression 1973 | 1974 |
  1. If LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral, then
    1. Let lref be the result of evaluating LeftHandSideExpression.
    2. ReturnIfAbrupt(lref).
    3. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then
      1. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref).
    4. Else,
      1. Let rref be the result of evaluating AssignmentExpression.
      2. Let rval be ? GetValue(rref).
    5. Perform ? PutValue(lref, rval).
    6. Return rval.
  2. Let assignmentPattern be the AssignmentPattern that is covered by LeftHandSideExpression.
  3. Let rref be the result of evaluating AssignmentExpression.
  4. Let rval be ? GetValue(rref).
  5. Perform ? DestructuringAssignmentEvaluation of assignmentPattern using rval as the argument.
  6. Return rval.
1975 | 1976 | AssignmentExpression:LeftHandSideExpressionAssignmentOperatorAssignmentExpression 1977 | 1978 |
  1. Let lref be the result of evaluating LeftHandSideExpression.
  2. Let lval be ? GetValue(lref).
  3. Let rref be the result of evaluating AssignmentExpression.
  4. Let rval be ? GetValue(rref).
  5. Let assignmentOpText be the source text matched by AssignmentOperator.
  6. Let opText be the sequence of Unicode code points associated with assignmentOpText in the following table: 1979 |
    1980 | 1981 | 1982 | 1983 | 1984 | 1985 | 1986 | 1987 | 1988 | 1989 | 1990 | 1991 | 1992 | 1993 | 1994 | 1995 | 1996 |
    assignmentOpText opText
    **= **
    *= *
    /= /
    %= %
    += +
    -= -
    <<= <<
    >>= >>
    >>>= >>>
    &= &
    ^= ^
    |= |
    1997 |
  7. Let r be ApplyStringOrNumericBinaryOperator(lval, opText, rval).
  8. Perform ? PutValue(lref, r).
  9. Return r.
1998 | 1999 | 2000 | AssignmentExpression:LeftHandSideExpression&&=AssignmentExpression 2001 | 2002 |
  1. Let lref be the result of evaluating LeftHandSideExpression.
  2. Let lval be ? GetValue(lref).
  3. Let lbool be ! ToBoolean(lval).
  4. If lbool is false, return lval.
  5. If IsAnonymousFunctionDefinition(AssignmentExpression) is true and IsIdentifierRef of LeftHandSideExpression is true, then
    1. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref).
  6. Else,
    1. Let rref be the result of evaluating AssignmentExpression.
    2. Let rval be ? GetValue(rref).
  7. Perform ? PutValue(lref, rval).
  8. Return rval.
2003 |
2004 | 2005 | 2006 | AssignmentExpression:LeftHandSideExpression||=AssignmentExpression 2007 | 2008 |
  1. Let lref be the result of evaluating LeftHandSideExpression.
  2. Let lval be ? GetValue(lref).
  3. Let lbool be ! ToBoolean(lval).
  4. If lbool is true, return lval.
  5. If IsAnonymousFunctionDefinition(AssignmentExpression) is true and IsIdentifierRef of LeftHandSideExpression is true, then
    1. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref).
  6. Else,
    1. Let rref be the result of evaluating AssignmentExpression.
    2. Let rval be ? GetValue(rref).
  7. Perform ? PutValue(lref, rval).
  8. Return rval.
2009 |
2010 | 2011 | 2012 | AssignmentExpression:LeftHandSideExpression??=AssignmentExpression 2013 | 2014 |
  1. Let lref be the result of evaluating LeftHandSideExpression.
  2. Let lval be ? GetValue(lref).
  3. If lval is neither undefined nor null, return lval.
  4. If IsAnonymousFunctionDefinition(AssignmentExpression) is true and IsIdentifierRef of LeftHandSideExpression is true, then
    1. Let rval be NamedEvaluation of AssignmentExpression with argument GetReferencedName(lref).
  5. Else,
    1. Let rref be the result of evaluating AssignmentExpression.
    2. Let rval be ? GetValue(rref).
  6. Perform ? PutValue(lref, rval).
  7. Return rval.
2015 |
2016 | Note
2017 |

When this expression occurs within strict mode code, it is a runtime error if lref in step 1.e, 2, 2, 2, 2 is an unresolvable reference. If it is, a ReferenceError exception is thrown. Additionally, it is a runtime error if the lref in step 8, 7, 7, 6 is a reference to a data property with the attribute value { [[Writable]]: false }, to an accessor property with the attribute value { [[Set]]: undefined }, or to a non-existent property of an object for which the IsExtensible predicate returns the value false. In these cases a TypeError exception is thrown.

2018 |
2019 |
2020 |
2021 | 2022 | 2023 |

4 Static Semantics: HasCallInTailPosition (14.9.2)

2024 |

With parameter call.

2025 | Note
2026 |

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.

2027 |
2028 | 2029 | 2030 |

4.1 Expression Rules (14.9.2.2)

2031 | Note
2032 |

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

2033 |
2034 | 2035 | AssignmentExpression:YieldExpression 2036 | ArrowFunction 2037 | AsyncArrowFunction 2038 | LeftHandSideExpression=AssignmentExpression 2039 | LeftHandSideExpressionAssignmentOperatorAssignmentExpression 2040 | LeftHandSideExpression&&=AssignmentExpression 2041 | LeftHandSideExpression||=AssignmentExpression 2042 | LeftHandSideExpression??=AssignmentExpression 2043 | 2044 | 2045 | BitwiseANDExpression:BitwiseANDExpression&EqualityExpression 2046 | 2047 | 2048 | BitwiseXORExpression:BitwiseXORExpression^BitwiseANDExpression 2049 | 2050 | 2051 | BitwiseORExpression:BitwiseORExpression|BitwiseXORExpression 2052 | 2053 | 2054 | EqualityExpression:EqualityExpression==RelationalExpression 2055 | EqualityExpression!=RelationalExpression 2056 | EqualityExpression===RelationalExpression 2057 | EqualityExpression!==RelationalExpression 2058 | 2059 | 2060 | RelationalExpression:RelationalExpression<ShiftExpression 2061 | RelationalExpression>ShiftExpression 2062 | RelationalExpression<=ShiftExpression 2063 | RelationalExpression>=ShiftExpression 2064 | RelationalExpressioninstanceofShiftExpression 2065 | RelationalExpressioninShiftExpression 2066 | 2067 | 2068 | ShiftExpression:ShiftExpression<<AdditiveExpression 2069 | ShiftExpression>>AdditiveExpression 2070 | ShiftExpression>>>AdditiveExpression 2071 | 2072 | 2073 | AdditiveExpression:AdditiveExpression+MultiplicativeExpression 2074 | AdditiveExpression-MultiplicativeExpression 2075 | 2076 | 2077 | MultiplicativeExpression:MultiplicativeExpressionMultiplicativeOperatorExponentiationExpression 2078 | 2079 | 2080 | ExponentiationExpression:UpdateExpression**ExponentiationExpression 2081 | 2082 | 2083 | UpdateExpression:LeftHandSideExpression++ 2084 | LeftHandSideExpression-- 2085 | ++UnaryExpression 2086 | --UnaryExpression 2087 | 2088 | 2089 | UnaryExpression:deleteUnaryExpression 2090 | voidUnaryExpression 2091 | typeofUnaryExpression 2092 | +UnaryExpression 2093 | -UnaryExpression 2094 | ~UnaryExpression 2095 | !UnaryExpression 2096 | AwaitExpression 2097 | 2098 | 2099 | CallExpression:SuperCall 2100 | CallExpression[Expression] 2101 | CallExpression.IdentifierName 2102 | 2103 | 2104 | NewExpression:newNewExpression 2105 | 2106 | 2107 | MemberExpression:MemberExpression[Expression] 2108 | MemberExpression.IdentifierName 2109 | SuperProperty 2110 | MetaProperty 2111 | newMemberExpressionArguments 2112 | 2113 | 2114 | PrimaryExpression:this 2115 | IdentifierReference 2116 | Literal 2117 | ArrayLiteral 2118 | ObjectLiteral 2119 | FunctionExpression 2120 | ClassExpression 2121 | GeneratorExpression 2122 | AsyncFunctionExpression 2123 | AsyncGeneratorExpression 2124 | RegularExpressionLiteral 2125 | TemplateLiteral 2126 | 2127 |
  1. Return false.
2128 | 2129 | Expression:AssignmentExpression 2130 | Expression,AssignmentExpression 2131 | 2132 |
  1. Return HasCallInTailPosition of AssignmentExpression with argument call.
2133 | 2134 | ConditionalExpression:ShortCircuitExpression?AssignmentExpression:AssignmentExpression 2135 | 2136 |
  1. Let has be HasCallInTailPosition of the first AssignmentExpression with argument call.
  2. If has is true, return true.
  3. Return HasCallInTailPosition of the second AssignmentExpression with argument call.
2137 | 2138 | LogicalANDExpression:LogicalANDExpression&&BitwiseORExpression 2139 | 2140 |
  1. Return HasCallInTailPosition of BitwiseORExpression with argument call.
2141 | 2142 | LogicalORExpression:LogicalORExpression||LogicalANDExpression 2143 | 2144 |
  1. Return HasCallInTailPosition of LogicalANDExpression with argument call.
2145 | 2146 | CoalesceExpression:CoalesceExpressionHead??BitwiseORExpression 2147 | 2148 |
  1. Return HasCallInTailPosition of BitwiseORExpression with argument call.
2149 | 2150 | CallExpression:CoverCallExpressionAndAsyncArrowHead 2151 | CallExpressionArguments 2152 | CallExpressionTemplateLiteral 2153 | 2154 |
  1. If this CallExpression is call, return true.
  2. Return false.
2155 | 2156 | OptionalExpression:MemberExpressionOptionalChain 2157 | CallExpressionOptionalChain 2158 | OptionalExpressionOptionalChain 2159 | 2160 |
  1. Return HasCallInTailPosition of OptionalChain with argument call.
2161 | 2162 | OptionalChain:?.[Expression] 2163 | ?.IdentifierName 2164 | OptionalChain[Expression] 2165 | OptionalChain.IdentifierName 2166 | 2167 |
  1. Return false.
2168 | 2169 | OptionalChain:?.Arguments 2170 | OptionalChainArguments 2171 | 2172 |
  1. If this OptionalChain is call, return true.
  2. Return false.
2173 | 2174 | MemberExpression:MemberExpressionTemplateLiteral 2175 | 2176 |
  1. If this MemberExpression is call, return true.
  2. Return false.
2177 | 2178 | PrimaryExpression:CoverParenthesizedExpressionAndArrowParameterList 2179 | 2180 |
  1. Let expr be CoveredParenthesizedExpression of CoverParenthesizedExpressionAndArrowParameterList.
  2. Return HasCallInTailPosition of expr with argument call.
2181 | 2182 | ParenthesizedExpression:(Expression) 2183 | 2184 |
  1. Return HasCallInTailPosition of Expression with argument call.
2185 |
2186 |
2187 |

A Copyright & Software License

2188 | 2189 |

Copyright Notice

2190 |

© 2020 Justin Ridgewell

2191 | 2192 |

Software License

2193 |

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

2194 | 2195 |

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

2196 | 2197 |
    2198 |
  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. 2199 |
  3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  4. 2200 |
  5. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.
  6. 2201 |
2202 | 2203 |

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

2204 | 2205 |
2206 |
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "proposals-logical-assignment", 4 | "description": "A proposal to combine Logical Operators and Assignment Expressions", 5 | "scripts": { 6 | "build": "ecmarkup spec.emu index.html" 7 | }, 8 | "homepage": "https://github.com/tc39/proposal-logical-assignment#readme", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/tc39/proposal-logical-assignment.git" 12 | }, 13 | "license": "MIT", 14 | "devDependencies": { 15 | "ecmarkup": "3.25.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spec.emu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
  7 | title: Logical Assignment Operators
  8 | status: proposal
  9 | stage: 4
 10 | contributors: Justin Ridgewell
 11 | location: https://tc39.es/proposal-logical-assignment
 12 | 
13 | 14 | 15 |

Scope

16 |

17 | This is the spec text of the Logical Assignment proposal in ECMAScript. 18 |

19 |

20 | Each Logical Assignment operator embraces the short-circuiting semantics of the respective Binary Logical operator. If the operator short circuits, then the [[Set]] operation is skipped. 21 |

22 |

23 | The new `LogicalAssignmentPunctuator` production defined in Punctuators section is used only to highlight the new additions. When merged with the main ecma262 spec, the new punctuators should just be added to `OtherPunctuator`. 24 |

25 |
26 | 27 | 28 |

Punctuators (11.7)

29 |

Syntax

30 | 31 | Punctuator :: 32 | LogicalAssignmentPunctuator 33 | OptionalChainingPunctuator 34 | OtherPunctuator 35 | 36 | 37 | LogicalAssignmentPunctuator :: one of 38 | `&&=` `||=` `??=` 39 | 40 | 41 | OptionalChainingPunctuator :: 42 | `?.` [lookahead <! DecimalDigit] 43 | 44 | OtherPunctuator :: one of 45 | `{` `(` `)` `[` `]` 46 | `.` `...` `;` `,` 47 | `<` `>` `<=` `>=` 48 | `==` `!=` `===` `!==` 49 | `+` `-` `*` `%` `**` 50 | `++` `--` 51 | `<<` `>>` `>>>` 52 | `&` `|` `^` 53 | `!` `~` 54 | `&&` `||` `??` 55 | `?` `:` 56 | `=` `+=` `-=` `*=` `%=` `**=` `<<=` `>>=` `>>>=` `&=` `|=` `^=` 57 | `=>` 58 | 59 | DivPunctuator :: 60 | `/` 61 | `/=` 62 | 63 | RightBracePunctuator :: 64 | `}` 65 | 66 |
67 | 68 | 69 |

Assignment Operators (12.15)

70 |

Syntax

71 | 72 | AssignmentExpression[In, Yield, Await] : 73 | ConditionalExpression[?In, ?Yield, ?Await] 74 | [+Yield] YieldExpression[?In, ?Await] 75 | ArrowFunction[?In, ?Yield, ?Await] 76 | AsyncArrowFunction[?In, ?Yield, ?Await] 77 | LeftHandSideExpression[?Yield, ?Await] `=` AssignmentExpression[?In, ?Yield, ?Await] #assignment 78 | LeftHandSideExpression[?Yield, ?Await] AssignmentOperator AssignmentExpression[?In, ?Yield, ?Await] 79 | LeftHandSideExpression[?Yield, ?Await] `&&=` AssignmentExpression[?In, ?Yield, ?Await] 80 | LeftHandSideExpression[?Yield, ?Await] `||=` AssignmentExpression[?In, ?Yield, ?Await] 81 | LeftHandSideExpression[?Yield, ?Await] `??=` AssignmentExpression[?In, ?Yield, ?Await] 82 | 83 | AssignmentOperator : one of 84 | `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `>>>=` `&=` `^=` `|=` `**=` 85 | 86 | 87 | 88 | 89 |

Static Semantics: Early Errors

90 | AssignmentExpression : LeftHandSideExpression `=` AssignmentExpression 91 | 99 | 100 | 101 | AssignmentExpression : 102 | LeftHandSideExpression AssignmentOperator AssignmentExpression 103 | LeftHandSideExpression `&&=` AssignmentExpression 104 | LeftHandSideExpression `||=` AssignmentExpression 105 | LeftHandSideExpression `??=` AssignmentExpression 106 | 107 | 112 |
113 | 114 | 115 |

Static Semantics: IsFunctionDefinition

116 | 117 | 118 | AssignmentExpression : 119 | ArrowFunction 120 | AsyncArrowFunction 121 | 122 | 123 | 1. Return *true*. 124 | 125 | 126 | AssignmentExpression : 127 | YieldExpression 128 | LeftHandSideExpression `=` AssignmentExpression 129 | LeftHandSideExpression AssignmentOperator AssignmentExpression 130 | LeftHandSideExpression `&&=` AssignmentExpression 131 | LeftHandSideExpression `||=` AssignmentExpression 132 | LeftHandSideExpression `??=` AssignmentExpression 133 | 134 | 135 | 1. Return *false*. 136 | 137 |
138 | 139 | 140 |

Static Semantics: AssignmentTargetType

141 | 142 | 143 | AssignmentExpression : 144 | YieldExpression 145 | ArrowFunction 146 | AsyncArrowFunction 147 | LeftHandSideExpression `=` AssignmentExpression 148 | LeftHandSideExpression AssignmentOperator AssignmentExpression 149 | LeftHandSideExpression `&&=` AssignmentExpression 150 | LeftHandSideExpression `||=` AssignmentExpression 151 | LeftHandSideExpression `??=` AssignmentExpression 152 | 153 | 154 | 1. Return ~invalid~. 155 | 156 |
157 | 158 | 159 |

Runtime Semantics: Evaluation

160 | AssignmentExpression : LeftHandSideExpression `=` AssignmentExpression 161 | 162 | 1. If |LeftHandSideExpression| is neither an |ObjectLiteral| nor an |ArrayLiteral|, then 163 | 1. Let _lref_ be the result of evaluating |LeftHandSideExpression|. 164 | 1. ReturnIfAbrupt(_lref_). 165 | 1. If IsAnonymousFunctionDefinition(|AssignmentExpression|) and IsIdentifierRef of |LeftHandSideExpression| are both *true*, then 166 | 1. Let _rval_ be NamedEvaluation of |AssignmentExpression| with argument GetReferencedName(_lref_). 167 | 1. Else, 168 | 1. Let _rref_ be the result of evaluating |AssignmentExpression|. 169 | 1. Let _rval_ be ? GetValue(_rref_). 170 | 1. [id="step-assignmentexpression-evaluation-simple-putvalue"] Perform ? PutValue(_lref_, _rval_). 171 | 1. Return _rval_. 172 | 1. Let _assignmentPattern_ be the |AssignmentPattern| that is covered by |LeftHandSideExpression|. 173 | 1. Let _rref_ be the result of evaluating |AssignmentExpression|. 174 | 1. Let _rval_ be ? GetValue(_rref_). 175 | 1. Perform ? DestructuringAssignmentEvaluation of _assignmentPattern_ using _rval_ as the argument. 176 | 1. Return _rval_. 177 | 178 | AssignmentExpression : LeftHandSideExpression AssignmentOperator AssignmentExpression 179 | 180 | 1. Let _lref_ be the result of evaluating |LeftHandSideExpression|. 181 | 1. [id="step-assignmentexpression-evaluation-compound-getvalue"] Let _lval_ be ? GetValue(_lref_). 182 | 1. Let _rref_ be the result of evaluating |AssignmentExpression|. 183 | 1. Let _rval_ be ? GetValue(_rref_). 184 | 1. Let _assignmentOpText_ be the source text matched by |AssignmentOperator|. 185 | 1. Let _opText_ be the sequence of Unicode code points associated with _assignmentOpText_ in the following table: 186 |
187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 |
_assignmentOpText_ _opText_
`**=` `**`
`*=` `*`
`/=` `/`
`%=` `%`
`+=` `+`
`-=` `-`
`<<=` `<<`
`>>=` `>>`
`>>>=` `>>>`
`&=` `&`
`^=` `^`
`|=` `|`
204 |
205 | 1. Let _r_ be ApplyStringOrNumericBinaryOperator(_lval_, _opText_, _rval_). 206 | 1. [id="step-assignmentexpression-evaluation-compound-putvalue"] Perform ? PutValue(_lref_, _r_). 207 | 1. Return _r_. 208 |
209 | 210 | AssignmentExpression : LeftHandSideExpression `&&=` AssignmentExpression 211 | 212 | 1. Let _lref_ be the result of evaluating |LeftHandSideExpression|. 213 | 1. [id="step-assignmentexpression-evaluation-lgcl-and-getvalue"] Let _lval_ be ? GetValue(_lref_). 214 | 1. Let _lbool_ be ! ToBoolean(_lval_). 215 | 1. If _lbool_ is *false*, return _lval_. 216 | 1. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and IsIdentifierRef of |LeftHandSideExpression| is *true*, then 217 | 1. Let _rval_ be NamedEvaluation of |AssignmentExpression| with argument GetReferencedName(_lref_). 218 | 1. Else, 219 | 1. Let _rref_ be the result of evaluating |AssignmentExpression|. 220 | 1. Let _rval_ be ? GetValue(_rref_). 221 | 1. [id="step-assignmentexpression-evaluation-lgcl-and-putvalue"] Perform ? PutValue(_lref_, _rval_). 222 | 1. Return _rval_. 223 | 224 | 225 | 226 | AssignmentExpression : LeftHandSideExpression `||=` AssignmentExpression 227 | 228 | 1. Let _lref_ be the result of evaluating |LeftHandSideExpression|. 229 | 1. [id="step-assignmentexpression-evaluation-lgcl-or-getvalue"] Let _lval_ be ? GetValue(_lref_). 230 | 1. Let _lbool_ be ! ToBoolean(_lval_). 231 | 1. If _lbool_ is *true*, return _lval_. 232 | 1. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and IsIdentifierRef of |LeftHandSideExpression| is *true*, then 233 | 1. Let _rval_ be NamedEvaluation of |AssignmentExpression| with argument GetReferencedName(_lref_). 234 | 1. Else, 235 | 1. Let _rref_ be the result of evaluating |AssignmentExpression|. 236 | 1. Let _rval_ be ? GetValue(_rref_). 237 | 1. [id="step-assignmentexpression-evaluation-lgcl-or-putvalue"] Perform ? PutValue(_lref_, _rval_). 238 | 1. Return _rval_. 239 | 240 | 241 | 242 | AssignmentExpression : LeftHandSideExpression `??=` AssignmentExpression 243 | 244 | 1. Let _lref_ be the result of evaluating |LeftHandSideExpression|. 245 | 1. [id="step-assignmentexpression-evaluation-lgcl-nullish-getvalue"] Let _lval_ be ? GetValue(_lref_). 246 | 1. If _lval_ is neither *undefined* nor *null*, return _lval_. 247 | 1. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and IsIdentifierRef of |LeftHandSideExpression| is *true*, then 248 | 1. Let _rval_ be NamedEvaluation of |AssignmentExpression| with argument GetReferencedName(_lref_). 249 | 1. Else, 250 | 1. Let _rref_ be the result of evaluating |AssignmentExpression|. 251 | 1. Let _rval_ be ? GetValue(_rref_). 252 | 1. [id="step-assignmentexpression-evaluation-lgcl-nullish-putvalue"] Perform ? PutValue(_lref_, _rval_). 253 | 1. Return _rval_. 254 | 255 | 256 | 257 |

When this expression occurs within strict mode code, it is a runtime error if _lref_ in step , , , , is an unresolvable reference. If it is, a *ReferenceError* exception is thrown. Additionally, it is a runtime error if the _lref_ in step , , , is a reference to a data property with the attribute value { [[Writable]]: *false* }, to an accessor property with the attribute value { [[Set]]: *undefined* }, or to a non-existent property of an object for which the IsExtensible predicate returns the value *false*. In these cases a *TypeError* exception is thrown.

258 |
259 |
260 |
261 | 262 | 263 |

Static Semantics: HasCallInTailPosition (14.9.2)

264 |

With parameter _call_.

265 | 266 |

_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.

267 |
268 | 269 | 270 |

Expression Rules (14.9.2.2)

271 | 272 |

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

273 |
274 | 275 | AssignmentExpression : 276 | YieldExpression 277 | ArrowFunction 278 | AsyncArrowFunction 279 | LeftHandSideExpression `=` AssignmentExpression 280 | LeftHandSideExpression AssignmentOperator AssignmentExpression 281 | LeftHandSideExpression `&&=` AssignmentExpression 282 | LeftHandSideExpression `||=` AssignmentExpression 283 | LeftHandSideExpression `??=` AssignmentExpression 284 | 285 | BitwiseANDExpression : BitwiseANDExpression `&` EqualityExpression 286 | 287 | BitwiseXORExpression : BitwiseXORExpression `^` BitwiseANDExpression 288 | 289 | BitwiseORExpression : BitwiseORExpression `|` BitwiseXORExpression 290 | 291 | EqualityExpression : 292 | EqualityExpression `==` RelationalExpression 293 | EqualityExpression `!=` RelationalExpression 294 | EqualityExpression `===` RelationalExpression 295 | EqualityExpression `!==` RelationalExpression 296 | 297 | RelationalExpression : 298 | RelationalExpression `<` ShiftExpression 299 | RelationalExpression `>` ShiftExpression 300 | RelationalExpression `<=` ShiftExpression 301 | RelationalExpression `>=` ShiftExpression 302 | RelationalExpression `instanceof` ShiftExpression 303 | RelationalExpression `in` ShiftExpression 304 | 305 | ShiftExpression : 306 | ShiftExpression `<<` AdditiveExpression 307 | ShiftExpression `>>` AdditiveExpression 308 | ShiftExpression `>>>` AdditiveExpression 309 | 310 | AdditiveExpression : 311 | AdditiveExpression `+` MultiplicativeExpression 312 | AdditiveExpression `-` MultiplicativeExpression 313 | 314 | MultiplicativeExpression : 315 | MultiplicativeExpression MultiplicativeOperator ExponentiationExpression 316 | 317 | ExponentiationExpression : 318 | UpdateExpression `**` ExponentiationExpression 319 | 320 | UpdateExpression : 321 | LeftHandSideExpression `++` 322 | LeftHandSideExpression `--` 323 | `++` UnaryExpression 324 | `--` UnaryExpression 325 | 326 | UnaryExpression : 327 | `delete` UnaryExpression 328 | `void` UnaryExpression 329 | `typeof` UnaryExpression 330 | `+` UnaryExpression 331 | `-` UnaryExpression 332 | `~` UnaryExpression 333 | `!` UnaryExpression 334 | AwaitExpression 335 | 336 | CallExpression : 337 | SuperCall 338 | CallExpression `[` Expression `]` 339 | CallExpression `.` IdentifierName 340 | 341 | NewExpression : `new` NewExpression 342 | 343 | MemberExpression : 344 | MemberExpression `[` Expression `]` 345 | MemberExpression `.` IdentifierName 346 | SuperProperty 347 | MetaProperty 348 | `new` MemberExpression Arguments 349 | 350 | PrimaryExpression : 351 | `this` 352 | IdentifierReference 353 | Literal 354 | ArrayLiteral 355 | ObjectLiteral 356 | FunctionExpression 357 | ClassExpression 358 | GeneratorExpression 359 | AsyncFunctionExpression 360 | AsyncGeneratorExpression 361 | RegularExpressionLiteral 362 | TemplateLiteral 363 | 364 | 365 | 1. Return *false*. 366 | 367 | 368 | Expression : 369 | AssignmentExpression 370 | Expression `,` AssignmentExpression 371 | 372 | 373 | 1. Return HasCallInTailPosition of |AssignmentExpression| with argument _call_. 374 | 375 | ConditionalExpression : ShortCircuitExpression `?` AssignmentExpression `:` AssignmentExpression 376 | 377 | 1. Let _has_ be HasCallInTailPosition of the first |AssignmentExpression| with argument _call_. 378 | 1. If _has_ is *true*, return *true*. 379 | 1. Return HasCallInTailPosition of the second |AssignmentExpression| with argument _call_. 380 | 381 | LogicalANDExpression : LogicalANDExpression `&&` BitwiseORExpression 382 | 383 | 1. Return HasCallInTailPosition of |BitwiseORExpression| with argument _call_. 384 | 385 | LogicalORExpression : LogicalORExpression `||` LogicalANDExpression 386 | 387 | 1. Return HasCallInTailPosition of |LogicalANDExpression| with argument _call_. 388 | 389 | CoalesceExpression : CoalesceExpressionHead `??` BitwiseORExpression 390 | 391 | 1. Return HasCallInTailPosition of |BitwiseORExpression| with argument _call_. 392 | 393 | 394 | CallExpression : 395 | CoverCallExpressionAndAsyncArrowHead 396 | CallExpression Arguments 397 | CallExpression TemplateLiteral 398 | 399 | 400 | 1. If this |CallExpression| is _call_, return *true*. 401 | 1. Return *false*. 402 | 403 | 404 | OptionalExpression : 405 | MemberExpression OptionalChain 406 | CallExpression OptionalChain 407 | OptionalExpression OptionalChain 408 | 409 | 410 | 1. Return HasCallInTailPosition of |OptionalChain| with argument _call_. 411 | 412 | 413 | OptionalChain : 414 | `?.` `[` Expression `]` 415 | `?.` IdentifierName 416 | OptionalChain `[` Expression `]` 417 | OptionalChain `.` IdentifierName 418 | 419 | 420 | 1. Return *false*. 421 | 422 | 423 | OptionalChain : 424 | `?.` Arguments 425 | OptionalChain Arguments 426 | 427 | 428 | 1. If this |OptionalChain| is _call_, return *true*. 429 | 1. Return *false*. 430 | 431 | 432 | MemberExpression : 433 | MemberExpression TemplateLiteral 434 | 435 | 436 | 1. If this |MemberExpression| is _call_, return *true*. 437 | 1. Return *false*. 438 | 439 | PrimaryExpression : CoverParenthesizedExpressionAndArrowParameterList 440 | 441 | 1. Let _expr_ be CoveredParenthesizedExpression of |CoverParenthesizedExpressionAndArrowParameterList|. 442 | 1. Return HasCallInTailPosition of _expr_ with argument _call_. 443 | 444 | 445 | ParenthesizedExpression : 446 | `(` Expression `)` 447 | 448 | 449 | 1. Return HasCallInTailPosition of |Expression| with argument _call_. 450 | 451 |
452 |
453 | --------------------------------------------------------------------------------