Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anthonybrown/8005622cbdb7eb8f59e82170486e7303 to your computer and use it in GitHub Desktop.
Save anthonybrown/8005622cbdb7eb8f59e82170486e7303 to your computer and use it in GitHub Desktop.
What's the longest keyword sequence in Javascript?

So there were a few threads going around recently about a challenge to write the longest sequence of keywords in Javascript:

There are, however, a few problems:

  • These solutions use non-keyword tokens (null, true, false are actually Literals, not Keywords)
  • One of the solutions isn't quite valid (new super)

Let's see if we can do better!

(...but first let's review the ground rules)

Rules

  1. Code must parse and run as valid Javascript. No ignoring early errors, nuh-uh!
  2. Only keywords are allowed
  3. The only other character allowed other than lower case letters are whitespaces
  4. Cannot repeat keywords within the sequence
  5. You can add as much preamble and postamble code as necessary

Bonus Challenge

  1. Line breaks are allowed between keywords
  2. Keyword-like tokens are allowed

The bar to meet

arjunb_msft starts off with this 15 word solution:

function *f() {
  if (1);
  else do return yield delete true instanceof typeof void new class extends false in this {}; while (1)
}

Unfortunately, it uses true and false, which are ReservedWords, but not Keywords. It also throws an error in Chrome: Uncaught SyntaxError: Unexpected token in.

bluepnume is tied with this 15 word solution:

async function* foo() {
  return yield delete void await null in this instanceof typeof new class extends async function () {} {}
}

This one does run in Chrome, but it uses a null, which is also not a Keyword.

If we remove the null from the second solution, and combine ideas from the first, we can get a different but pedantically kosher 15 keyword solution:

async function* foo() {
  if (0);
  else do return yield delete void await this instanceof typeof new class extends async function
  () {} {}; while (0)
}

Hooray!

Bring the real fun

Now, while being super pedantic can be fun, it's not necessarily fun fun.

But worry not, because just a bit downthread, bterlson added this stipulation:

Oh, and this, null, and undefined were considered keywords even though they aren't technically keywords. It made the contest more fun (plus editors color them as keywords so it makes some sense).

Technically technically, this is in fact a keyword. However, he's right about null and undefined not being keywords.

In the rest of the thread we can see true and false are also considered ok. This brings us to the question: if non-keyword tokens are ok to use, which tokens are kosher for this challenge?

What null, true and false have in common is that they are Literals whose token consist solely of letters (NumericLiterals and StringLiterals are obviously not allowed).

With NullLiterals and BooleanLiterals allowed, we can easily build up on the previous sequence and get this 17 word sequence:

async function* foo() {
  if (0);
  else do return yield await delete void typeof null instanceof this in new class extends async function () {} {}; while (0);
}

What about undefined? Well, that's actually an Identifier. If we allow ASI, one can construct an infinite sequence of arbitrary identifiers, but that's not very interesting and, I think, not in the spirit of the challenge.

a
b
c
// boooring

Instead, I think the spirit of the challenge is to use only tokens that look like keywords (even if they are not technically Keywords in the spec)

Here are a few of those keyword-like-tokens-that-aren't-actually-keywords:

let x
for (foo of bar) {}
class { static foo() {} }
import {foo as bar} from 'baz'
{get foo() {}, set foo() {}}

In case your hobbies don't include meticulously poring over ridiculously large programming language specifications, and you can't tell from a glance which of the tokens above are keywords and which aren't, here are the illegitimate ones: let, of, static, as, from, get and set. They sure look like keywords though.

We could arguably also add things like NaN and Infinity to the list, since they fall in the same bucket as undefined (Identifiers that always point to the same value), but some might also argue that only lower case characters should be allowed, so let's leave those out. We probably should leave out arguments as well, since it doesn't get mentioned as a token in the grammar spec and thus it's really just a magic variable rather than a keyword.

Another one we will need to leave out is new.target because well, it has a period smack in the middle of it.

Some tokens such as enum and public are FutureReservedTokens and look an awful lot like keywords, especially if you are familiar with languages like Java. The problem is that they automatically become SyntaxErrors pretty much everywhere in the grammar, so we can't actually use them even if we allowed them...

// the party poopers

let enum // SyntaxError
interface Bar {} // SyntaxError
package Baz; // SyntaxError
class {
  private foo() {} // SyntaxError
}

Now that we have separated the wheat from the chaff, what can we do?

Well, a LOT.

It turns out that due to historical backwards-compatibility reasons, in some cases many "keywords" act as... umm not keywords. We previously said that abusing ASI and Identifiers is boring, but did you know you that this is valid Javascript syntax?

var undefined
typeof let

That's certainly not boring, and super promising, so in the name of fun, we just have to allow it :)

There's just one tiny last little detail to talk about. While the snippet above is interesting and eyebrow-raising, it has one ugly problem: it spans two lines. It's just unfortunate, but we need ASI to separate the two statements, so there's nothing we can do to put them on the same line.

Or is there?

Enter one of the obscure Unicode characters that the Javascript spec recognizes as a line terminator: the Paragraph Separator character (\u2029), which, when rendered properly, looks like this: .

That's right. It's an invisible character!

Now, armed with knowledge of deliciously devious shenanigans, we can finally come up with this little monstrosity:

async function* foo() {
  from: set: while (0) {
    if (0)
    throw as
 else this
 null
 continue from
 false
 break set
 true
 var let
 debugger
 do return yield await delete void typeof get instanceof static in new class of extends async function undefined
    () {} {}; while (0);
  }
}

Yes, that's valid Javascript that parses and runs on Chrome. And it has a sequence of 32 keywords in a single line!

Granted, not all of them are technically keywords, and this is probably the dirtiest abuse of ASI ever, but hey, it still falls within the spirit of the challenge. Besides, I'm having fun and that's what matters!

So, what do you think? Can you make a larger sequence? Can you figure out why this is syntactically valid? Is this cheating? Are gists the most misappropriated blogging platform ever? Comment below!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment