Yes, there's been a bit of talk about modifying the `do` proposal in
various ways (like allowing more than a block, using `=` to
differentiate, etc.), but each of these have potential ambiguities
(like conflicting with `do-while` or object literal syntax,
respectively), and give unintuitive results with loops.
Here's my idea: prepend a `::` to any non-expression statement, and it
becomes an expression. The last statement's value (as if through
`eval`) is used as the return value, with exception of loops, which
return an array generated from their loop body. Here's a few examples
to clarify:
```js
// plain block
const foo = :: { let a = 1; a }
assert.equal(foo, 1)
// if-else
let cond = false
const bar = :: if (cond) "hi" else "bye"
assert.equal(bar, "bye")
// try-catch
let e = new Error()
const error = :: try { throw e } catch (e) { e }
assert.equal(error, e)
// while loop
let i = 0
const range = :: while (i < 10) i++
assert.deepEqual(range, [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
])
// for + nested if-else
const fizzBuzz = :: for (let i = 0; i < 100; i++) {
if (i % 15) "FizzBuzz"
else if (i % 3) "Fizz"
else if (i % 5) "Buzz"
// If no expression is evaluated in the body, then no value
// is pushed.
}
assert.deepEqual(fizzBuzz, [
// 100 lines of the classic "Fizz", "Buzz", and "FizzBuzz"
])
```
What do you all think of this?
-----------
To clarify, here's some more detailed semantics:
### Grammar
- `:: ExpressionStatement`
- `:: Declaration`
- `:: VariableStatement`
These are early errors, and they are all redundant, anyways, since you
already have a value you could use.
- `:: ContinueStatement`
- `:: BreakStatement`
- `:: ReturnStatement`
- `:: DebuggerStatement`
- `:: ThrowStatement`
- `:: EmptyStatement`
These are also early errors, since there's no real value you can
associate with them.
- `:: BlockStatement`
- `:: IfStatement`
- `:: SwitchStatement`
- `:: WithStatement`
- `:: TryStatement`
1. Let `completion` be ? ExecuteStatement(`statement`), where
`statement` is one of the statements above.
2. If `completion` is *none*, return `undefined`
3. Otherwise, return `completion`.
- `:: IterationStatement`
1. Return ? ExecuteIterationStatement(`statement`), where `statement`
is one of the statements above.
- `:: LabelledStatement`
This works similarly, but has an outer label instead. The same above
statements are also similarly banned as `LabelledItem` entries.
Additionally, the Annex B extension for FunctionDeclaraions are also
early errors.
(Should these even be allowed? I'm open to making this an early error instead.)
### Abstract Operation ExecuteStatement(`statement`)
1. If `statement` is an IterationStatement:
1. Let `list` be a new empty list.
2. Evaluate `statement`, but for each `child` statement:
1. Let `result` be ? ExecuteStatement(`child`).
2. If `result` is not *empty*, then append `result` to `list`.
3. Return `list`.
2. If `statement` is an ExpressionStatement:
1. Return the result of evaluating the expression within.
3. If `statement` is a `BreakStatement` or `ContinueStatement`:
1. Evaluate `statement`
2. Return *empty*.
4. If `statement` is a `Declaration`:
1. Evaluate `statement`.
2. Return the value of the resulting declaration
5. Otherwise:
1. Let `last` be *empty*
2. Evaluate `statement`. For each `child` statement:
1. Let `last` be ? ExecuteStatement(`child`).
3. Note: `last` is the final expression's value, or `empty` if there
was no final expression.
4. Return `last`.
(I probably missed several edge cases, but this is just a mailing list
strawman.)
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss