18

Why is {} || [] not valid?

$ echo '[] || {}' | node # this works
$ echo '{} || []' | node # but this doesn't
[stdin]:1
{} || []
   ^^

SyntaxError: Unexpected token ||
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Object.<anonymous> ([stdin]-wrapper:6:22)
    at Module._compile (module.js:652:30)
    at evalScript (bootstrap_node.js:466:27)
    at Socket.<anonymous> (bootstrap_node.js:237:15)
    at emitNone (events.js:111:20)
    at Socket.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1064:12)
    at _combinedTickCallback (internal/process/next_tick.js:138:11)

$ echo '({}) || []' | node # unless you do this
charmoniumQ
  • 5,214
  • 5
  • 33
  • 51

1 Answers1

22

When a statement starts with {, it's assumed by the parser to be the beginning of a block statement. In the case of {}, it's an empty block statement. So it's as if you had

{
  // no code here
}
|| []

and || cannot start a statement.

The one that does work, [] || {}, is unambiguous because a statement whose first token is [ can only be an expression statement.

Wrapping {} in ( ) means that the first token of the statement is (, not {. The ( token cannot start any form of statement other than an expression (though it does have a little ambiguity, since it can start an anonymous "fat arrow" function; that's still an expression and the parser just has to disambiguate that later).

Note: the implementation of various debugging environments like your browser console and the Node command-line "console" have an effect on this sort of syntax. In order to keep things simple, such tools take the code you type in and one way or another they "wrap" it so that it can be parsed and evaluated interactively, statement by statement as you type. Unfortunately that process can introduce anomalies, such that something you try in the console may work fine there but not when you try it in a real block of code.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 1
    To me this doesn't explain why `{} || {}` is allowed. – Explosion Pills Sep 21 '18 at 18:10
  • @ExplosionPills but `{} || {}` is **not** allowed for the same reason. `[] || {}` is allowed because the `[` does not have the same syntactic ambiguity that `{` has. – Pointy Sep 21 '18 at 18:10
  • 3
    In node and Chrome, running `{} || {}` returns `{}` – Explosion Pills Sep 21 '18 at 18:12
  • Also, enclosing the curly braces in parentheses takes care of the ambiguity, that makes the block an expression. – Luca Kiebel Sep 21 '18 at 18:13
  • @ExplosionPills Yes. But Firefox throws an error. – Ram Sep 21 '18 at 18:15
  • @undefined fair enough, but this question is about node.js specifically. I'm curious if there is more information about why V8 (I guess) handles this differently. – Explosion Pills Sep 21 '18 at 18:16
  • 4
    @ExplosionPills It works in the console but not as a propper script. Wrap `{} || {}` in a ` – ibrahim mahrir Sep 21 '18 at 18:23
  • 6
    @ExplosionPills This is specific to how REPL works in Chrome and Node, not anything else. – Estus Flask Sep 21 '18 at 18:31
  • This explanation still doesn't explain the last line of the code in OP's question. If starting a statement with `||` is the problem, why would it not still be the issue when the `{}` are wrapped `({})`? – Pytth Sep 21 '18 at 21:17
  • 1
    @estus [That is a hack](https://stackoverflow.com/q/36438034/1048572) in the devtools, not because it is valid syntax. And the hack only works one `{} || {}` but not on `{} || []`. – Bergi Sep 21 '18 at 21:42
  • 1
    @Pytth Because `({})` does not start with a `{` so it's not a block but a valid expression. – Bergi Sep 21 '18 at 21:43
  • @Bergi I hadn't thought about it before but dealing with the ambiguity of `(` introducing a simple parenthesized expression vs an anonymous fat-arrow function seems like a pain. Maybe the parser can treat the contents of the `()` as an expression tentatively, and if it sees the `=>` token it can just re-do the AST as a formal parameter list (or throw a syntax error). I don't envy JavaScript parser implementors. – Pointy Sep 21 '18 at 22:03
  • @Bergi I suppose it's a bit more complicated than that because REPL also processes `{ ... }` blocks (and also logs the last block expression), while adding parentheses around a block would result in syntax error. But I guess yes, the code you've linked is actual shortcut Chrome uses for potential expression. – Estus Flask Sep 21 '18 at 22:08