// Generated by CoffeeScript 2.6.0
(function() {
  // The CoffeeScript parser is generated by [Jison](https://github.com/zaach/jison)
  // from this grammar file. Jison is a bottom-up parser generator, similar in
  // style to [Bison](http://www.gnu.org/software/bison), implemented in JavaScript.
  // It can recognize [LALR(1), LR(0), SLR(1), and LR(1)](https://en.wikipedia.org/wiki/LR_grammar)
  // type grammars. To create the Jison parser, we list the pattern to match
  // on the left-hand side, and the action to take (usually the creation of syntax
  // tree nodes) on the right. As the parser runs, it
  // shifts tokens from our token stream, from left to right, and
  // [attempts to match](https://en.wikipedia.org/wiki/Bottom-up_parsing)
  // the token sequence against the rules below. When a match can be made, it
  // reduces into the [nonterminal](https://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols)
  // (the enclosing name at the top), and we proceed from there.

  // If you run the `cake build:parser` command, Jison constructs a parse table
  // from our rules and saves it into `lib/parser.js`.

  // The only dependency is on the **Jison.Parser**.
  var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;

  ({Parser} = require('jison'));

  // Jison DSL
  // ---------

  // Since we're going to be wrapped in a function by Jison in any case, if our
  // action immediately returns a value, we can optimize by removing the function
  // wrapper and just returning the value directly.
  unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/;

  // Our handy DSL for Jison grammar generation, thanks to
  // [Tim Caswell](https://github.com/creationix). For every rule in the grammar,
  // we pass the pattern-defining string, the action to run, and extra options,
  // optionally. If no action is specified, we simply pass the value of the
  // previous nonterminal.
  o = function(patternString, action, options) {
    var getAddDataToNodeFunctionString, match, patternCount, performActionFunctionString, returnsLoc;
    patternString = patternString.replace(/\s{2,}/g, ' ');
    patternCount = patternString.split(' ').length;
    if (action) {
      // This code block does string replacements in the generated `parser.js`
      // file, replacing the calls to the `LOC` function and other strings as
      // listed below.
      action = (match = unwrap.exec(action)) ? match[1] : `(${action}())`;
      // All runtime functions we need are defined on `yy`
      action = action.replace(/\bnew /g, '$&yy.');
      action = action.replace(/\b(?:Block\.wrap|extend)\b/g, 'yy.$&');
      // Returns strings of functions to add to `parser.js` which add extra data
      // that nodes may have, such as comments or location data. Location data
      // is added to the first parameter passed in, and the parameter is returned.
      // If the parameter is not a node, it will just be passed through unaffected.
      getAddDataToNodeFunctionString = function(first, last, forceUpdateLocation = true) {
        return `yy.addDataToNode(yy, @${first}, ${first[0] === '$' ? '$$' : '$'}${first}, ${last ? `@${last}, ${last[0] === '$' ? '$$' : '$'}${last}` : 'null, null'}, ${forceUpdateLocation ? 'true' : 'false'})`;
      };
      // This code replaces the calls to `LOC` with the `yy.addDataToNode` string
      // defined above. The `LOC` function, when used below in the grammar rules,
      // is used to make sure that newly created node class objects get correct
      // location data assigned to them. By default, the grammar will assign the
      // location data spanned by *all* of the tokens on the left (e.g. a string
      // such as `'Body TERMINATOR Line'`) to the “top-level” node returned by 
      // the grammar rule (the function on the right). But for “inner” node class
      // objects created by grammar rules, they won’t get correct location data
      // assigned to them without adding `LOC`.

      // For example, consider the grammar rule `'NEW_TARGET . Property'`, which
      // is handled by a function that returns
      // `new MetaProperty LOC(1)(new IdentifierLiteral $1), LOC(3)(new Access $3)`.
      // The `1` in `LOC(1)` refers to the first token (`NEW_TARGET`) and the `3`
      // in `LOC(3)` refers to the third token (`Property`). In order for the
      // `new IdentifierLiteral` to get assigned the location data corresponding
      // to `new` in the source code, we use
      // `LOC(1)(new IdentifierLiteral ...)` to mean “assign the location data of
      // the *first* token of this grammar rule (`NEW_TARGET`) to this
      // `new IdentifierLiteral`”. The `LOC(3)` means “assign the location data of
      // the *third* token of this grammar rule (`Property`) to this
      // `new Access`”.
      returnsLoc = /^LOC/.test(action);
      action = action.replace(/LOC\(([0-9]*)\)/g, getAddDataToNodeFunctionString('$1'));
      // A call to `LOC` with two arguments, e.g. `LOC(2,4)`, sets the location
      // data for the generated node on both of the referenced tokens  (the second
      // and fourth in this example).
      action = action.replace(/LOC\(([0-9]*),\s*([0-9]*)\)/g, getAddDataToNodeFunctionString('$1', '$2'));
      performActionFunctionString = `$$ = ${getAddDataToNodeFunctionString(1, patternCount, !returnsLoc)}(${action});`;
    } else {
      performActionFunctionString = '$$ = $1;';
    }
    return [patternString, performActionFunctionString, options];
  };

  // Grammatical Rules
  // -----------------

  // In all of the rules that follow, you'll see the name of the nonterminal as
  // the key to a list of alternative matches. With each match's action, the
  // dollar-sign variables are provided by Jison as references to the value of
  // their numeric position, so in this rule:

  //     'Expression UNLESS Expression'

  // `$1` would be the value of the first `Expression`, `$2` would be the token
  // for the `UNLESS` terminal, and `$3` would be the value of the second
  // `Expression`.
  grammar = {
    // The **Root** is the top-level node in the syntax tree. Since we parse bottom-up,
    // all parsing must end here.
    Root: [
      o('',
      function() {
        return new Root(new Block());
      }),
      o('Body',
      function() {
        return new Root($1);
      })
    ],
    // Any list of statements and expressions, separated by line breaks or semicolons.
    Body: [
      o('Line',
      function() {
        return Block.wrap([$1]);
      }),
      o('Body TERMINATOR Line',
      function() {
        return $1.push($3);
      }),
      o('Body TERMINATOR')
    ],
    // Block and statements, which make up a line in a body. FuncDirective is a
    // statement, but not included in Statement because that results in an ambiguous
    // grammar.
    Line: [o('Expression'), o('ExpressionLine'), o('Statement'), o('FuncDirective')],
    FuncDirective: [o('YieldReturn'), o('AwaitReturn')],
    // Pure statements which cannot be expressions.
    Statement: [
      o('Return'),
      o('STATEMENT',
      function() {
        return new StatementLiteral($1);
      }),
      o('Import'),
      o('Export')
    ],
    // All the different types of expressions in our language. The basic unit of
    // CoffeeScript is the **Expression** -- everything that can be an expression
    // is one. Blocks serve as the building blocks of many other rules, making
    // them somewhat circular.
    Expression: [o('Value'), o('Code'), o('Operation'), o('Assign'), o('If'), o('Try'), o('While'), o('For'), o('Switch'), o('Class'), o('Throw'), o('Yield')],
    // Expressions which are written in single line and would otherwise require being
    // wrapped in braces: E.g `a = b if do -> f a is 1`, `if f (a) -> a*2 then ...`,
    // `for x in do (obj) -> f obj when x > 8 then f x`
    ExpressionLine: [o('CodeLine'), o('IfLine'), o('OperationLine')],
    Yield: [
      o('YIELD',
      function() {
        return new Op($1,
      new Value(new Literal('')));
      }),
      o('YIELD Expression',
      function() {
        return new Op($1,
      $2);
      }),
      o('YIELD INDENT Object OUTDENT',
      function() {
        return new Op($1,
      $3);
      }),
      o('YIELD FROM Expression',
      function() {
        return new Op($1.concat($2),
      $3);
      })
    ],
    // An indented block of expressions. Note that the [Rewriter](rewriter.html)
    // will convert some postfix forms into blocks for us, by adjusting the
    // token stream.
    Block: [
      o('INDENT OUTDENT',
      function() {
        return new Block();
      }),
      o('INDENT Body OUTDENT',
      function() {
        return $2;
      })
    ],
    Identifier: [
      o('IDENTIFIER',
      function() {
        return new IdentifierLiteral($1);
      }),
      o('JSX_TAG',
      function() {
        var ref,
      ref1,
      ref2,
      ref3;
        return new JSXTag($1.toString(),
      {
          tagNameLocationData: $1.tagNameToken[2],
          closingTagOpeningBracketLocationData: (ref = $1.closingTagOpeningBracketToken) != null ? ref[2] : void 0,
          closingTagSlashLocationData: (ref1 = $1.closingTagSlashToken) != null ? ref1[2] : void 0,
          closingTagNameLocationData: (ref2 = $1.closingTagNameToken) != null ? ref2[2] : void 0,
          closingTagClosingBracketLocationData: (ref3 = $1.closingTagClosingBracketToken) != null ? ref3[2] : void 0
        });
      })
    ],
    Property: [
      o('PROPERTY',
      function() {
        return new PropertyName($1.toString());
      })
    ],
    // Alphanumerics are separated from the other **Literal** matchers because
    // they can also serve as keys in object literals.
    AlphaNumeric: [
      o('NUMBER',
      function() {
        return new NumberLiteral($1.toString(),
      {
          parsedValue: $1.parsedValue
        });
      }),
      o('String')
    ],
    String: [
      o('STRING',
      function() {
        return new StringLiteral($1.slice(1,
      -1), // strip artificial quotes and unwrap to primitive string
      {
          quote: $1.quote,
          initialChunk: $1.initialChunk,
          finalChunk: $1.finalChunk,
          indent: $1.indent,
          double: $1.double,
          heregex: $1.heregex
        });
      }),
      o('STRING_START Interpolations STRING_END',
      function() {
        return new StringWithInterpolations(Block.wrap($2),
      {
          quote: $1.quote,
          startQuote: LOC(1)(new Literal($1.toString()))
        });
      })
    ],
    Interpolations: [
      o('InterpolationChunk',
      function() {
        return [$1];
      }),
      o('Interpolations InterpolationChunk',
      function() {
        return $1.concat($2);
      })
    ],
    InterpolationChunk: [
      o('INTERPOLATION_START Body INTERPOLATION_END',
      function() {
        return new Interpolation($2);
      }),
      o('INTERPOLATION_START INDENT Body OUTDENT INTERPOLATION_END',
      function() {
        return new Interpolation($3);
      }),
      o('INTERPOLATION_START INTERPOLATION_END',
      function() {
        return new Interpolation();
      }),
      o('String',
      function() {
        return $1;
      })
    ],
    // The .toString() calls here and elsewhere are to convert `String` objects
    // back to primitive strings now that we've retrieved stowaway extra properties
    Regex: [
      o('REGEX',
      function() {
        return new RegexLiteral($1.toString(),
      {
          delimiter: $1.delimiter,
          heregexCommentTokens: $1.heregexCommentTokens
        });
      }),
      o('REGEX_START Invocation REGEX_END',
      function() {
        return new RegexWithInterpolations($2,
      {
          heregexCommentTokens: $3.heregexCommentTokens
        });
      })
    ],
    // All of our immediate values. Generally these can be passed straight
    // through and printed to JavaScript.
    Literal: [
      o('AlphaNumeric'),
      o('JS',
      function() {
        return new PassthroughLiteral($1.toString(),
      {
          here: $1.here,
          generated: $1.generated
        });
      }),
      o('Regex'),
      o('UNDEFINED',
      function() {
        return new UndefinedLiteral($1);
      }),
      o('NULL',
      function() {
        return new NullLiteral($1);
      }),
      o('BOOL',
      function() {
        return new BooleanLiteral($1.toString(),
      {
          originalValue: $1.original
        });
      }),
      o('INFINITY',
      function() {
        return new InfinityLiteral($1.toString(),
      {
          originalValue: $1.original
        });
      }),
      o('NAN',
      function() {
        return new NaNLiteral($1);
      })
    ],
    // Assignment of a variable, property, or index to a value.
    Assign: [
      o('Assignable = Expression',
      function() {
        return new Assign($1,
      $3);
      }),
      o('Assignable = TERMINATOR Expression',
      function() {
        return new Assign($1,
      $4);
      }),
      o('Assignable = INDENT Expression OUTDENT',
      function() {
        return new Assign($1,
      $4);
      })
    ],
    // Assignment when it happens within an object literal. The difference from
    // the ordinary **Assign** is that these allow numbers and strings as keys.
    AssignObj: [
      o('ObjAssignable',
      function() {
        return new Value($1);
      }),
      o('ObjRestValue'),
      o('ObjAssignable : Expression',
      function() {
        return new Assign(LOC(1)(new Value($1)),
      $3,
      'object',
      {
          operatorToken: LOC(2)(new Literal($2))
        });
      }),
      o('ObjAssignable : INDENT Expression OUTDENT',
      function() {
        return new Assign(LOC(1)(new Value($1)),
      $4,
      'object',
      {
          operatorToken: LOC(2)(new Literal($2))
        });
      }),
      o('SimpleObjAssignable = Expression',
      function() {
        return new Assign(LOC(1)(new Value($1)),
      $3,
      null,
      {
          operatorToken: LOC(2)(new Literal($2))
        });
      }),
      o('SimpleObjAssignable = INDENT Expression OUTDENT',
      function() {
        return new Assign(LOC(1)(new Value($1)),
      $4,
      null,
      {
          operatorToken: LOC(2)(new Literal($2))
        });
      })
    ],
    SimpleObjAssignable: [o('Identifier'), o('Property'), o('ThisProperty')],
    ObjAssignable: [
      o('SimpleObjAssignable'),
      o('[ Expression ]',
      function() {
        return new Value(new ComputedPropertyName($2));
      }),
      o('@ [ Expression ]',
      function() {
        return new Value(LOC(1)(new ThisLiteral($1)),
      [LOC(3)(new ComputedPropertyName($3))],
      'this');
      }),
      o('AlphaNumeric')
    ],
    // Object literal spread properties.
    ObjRestValue: [
      o('SimpleObjAssignable ...',
      function() {
        return new Splat(new Value($1));
      }),
      o('... SimpleObjAssignable',
      function() {
        return new Splat(new Value($2),
      {
          postfix: false
        });
      }),
      o('ObjSpreadExpr ...',
      function() {
        return new Splat($1);
      }),
      o('... ObjSpreadExpr',
      function() {
        return new Splat($2,
      {
          postfix: false
        });
      })
    ],
    ObjSpreadExpr: [
      o('ObjSpreadIdentifier'),
      o('Object'),
      o('Parenthetical'),
      o('Super'),
      o('This'),
      o('SUPER OptFuncExist Arguments',
      function() {
        return new SuperCall(LOC(1)(new Super()),
      $3,
      $2.soak,
      $1);
      }),
      o('DYNAMIC_IMPORT Arguments',
      function() {
        return new DynamicImportCall(LOC(1)(new DynamicImport()),
      $2);
      }),
      o('SimpleObjAssignable OptFuncExist Arguments',
      function() {
        return new Call(new Value($1),
      $3,
      $2.soak);
      }),
      o('ObjSpreadExpr OptFuncExist Arguments',
      function() {
        return new Call($1,
      $3,
      $2.soak);
      })
    ],
    ObjSpreadIdentifier: [
      o('SimpleObjAssignable Accessor',
      function() {
        return (new Value($1)).add($2);
      }),
      o('ObjSpreadExpr Accessor',
      function() {
        return (new Value($1)).add($2);
      })
    ],
    // A return statement from a function body.
    Return: [
      o('RETURN Expression',
      function() {
        return new Return($2);
      }),
      o('RETURN INDENT Object OUTDENT',
      function() {
        return new Return(new Value($3));
      }),
      o('RETURN',
      function() {
        return new Return();
      })
    ],
    YieldReturn: [
      o('YIELD RETURN Expression',
      function() {
        return new YieldReturn($3,
      {
          returnKeyword: LOC(2)(new Literal($2))
        });
      }),
      o('YIELD RETURN',
      function() {
        return new YieldReturn(null,
      {
          returnKeyword: LOC(2)(new Literal($2))
        });
      })
    ],
    AwaitReturn: [
      o('AWAIT RETURN Expression',
      function() {
        return new AwaitReturn($3,
      {
          returnKeyword: LOC(2)(new Literal($2))
        });
      }),
      o('AWAIT RETURN',
      function() {
        return new AwaitReturn(null,
      {
          returnKeyword: LOC(2)(new Literal($2))
        });
      })
    ],
    // The **Code** node is the function literal. It’s defined by an indented block
    // of **Block** preceded by a function arrow, with an optional parameter list.
    Code: [
      o('PARAM_START ParamList PARAM_END FuncGlyph Block',
      function() {
        return new Code($2,
      $5,
      $4,
      LOC(1)(new Literal($1)));
      }),
      o('FuncGlyph Block',
      function() {
        return new Code([],
      $2,
      $1);
      })
    ],
    // The Codeline is the **Code** node with **Line** instead of indented **Block**.
    CodeLine: [
      o('PARAM_START ParamList PARAM_END FuncGlyph Line',
      function() {
        return new Code($2,
      LOC(5)(Block.wrap([$5])),
      $4,
      LOC(1)(new Literal($1)));
      }),
      o('FuncGlyph Line',
      function() {
        return new Code([],
      LOC(2)(Block.wrap([$2])),
      $1);
      })
    ],
    // CoffeeScript has two different symbols for functions. `->` is for ordinary
    // functions, and `=>` is for functions bound to the current value of *this*.
    FuncGlyph: [
      o('->',
      function() {
        return new FuncGlyph($1);
      }),
      o('=>',
      function() {
        return new FuncGlyph($1);
      })
    ],
    // An optional, trailing comma.
    OptComma: [o(''), o(',')],
    // The list of parameters that a function accepts can be of any length.
    ParamList: [
      o('',
      function() {
        return [];
      }),
      o('Param',
      function() {
        return [$1];
      }),
      o('ParamList , Param',
      function() {
        return $1.concat($3);
      }),
      o('ParamList OptComma TERMINATOR Param',
      function() {
        return $1.concat($4);
      }),
      o('ParamList OptComma INDENT ParamList OptComma OUTDENT',
      function() {
        return $1.concat($4);
      })
    ],
    // A single parameter in a function definition can be ordinary, or a splat
    // that hoovers up the remaining arguments.
    Param: [
      o('ParamVar',
      function() {
        return new Param($1);
      }),
      o('ParamVar ...',
      function() {
        return new Param($1,
      null,
      true);
      }),
      o('... ParamVar',
      function() {
        return new Param($2,
      null,
      {
          postfix: false
        });
      }),
      o('ParamVar = Expression',
      function() {
        return new Param($1,
      $3);
      }),
      o('...',
      function() {
        return new Expansion();
      })
    ],
    // Function Parameters
    ParamVar: [o('Identifier'), o('ThisProperty'), o('Array'), o('Object')],
    // A splat that occurs outside of a parameter list.
    Splat: [
      o('Expression ...',
      function() {
        return new Splat($1);
      }),
      o('... Expression',
      function() {
        return new Splat($2,
      {
          postfix: false
        });
      })
    ],
    // Variables and properties that can be assigned to.
    SimpleAssignable: [
      o('Identifier',
      function() {
        return new Value($1);
      }),
      o('Value Accessor',
      function() {
        return $1.add($2);
      }),
      o('Code Accessor',
      function() {
        return new Value($1).add($2);
      }),
      o('ThisProperty')
    ],
    // Everything that can be assigned to.
    Assignable: [
      o('SimpleAssignable'),
      o('Array',
      function() {
        return new Value($1);
      }),
      o('Object',
      function() {
        return new Value($1);
      })
    ],
    // The types of things that can be treated as values -- assigned to, invoked
    // as functions, indexed into, named as a class, etc.
    Value: [
      o('Assignable'),
      o('Literal',
      function() {
        return new Value($1);
      }),
      o('Parenthetical',
      function() {
        return new Value($1);
      }),
      o('Range',
      function() {
        return new Value($1);
      }),
      o('Invocation',
      function() {
        return new Value($1);
      }),
      o('DoIife',
      function() {
        return new Value($1);
      }),
      o('This'),
      o('Super',
      function() {
        return new Value($1);
      }),
      o('MetaProperty',
      function() {
        return new Value($1);
      })
    ],
    // A `super`-based expression that can be used as a value.
    Super: [
      o('SUPER . Property',
      function() {
        return new Super(LOC(3)(new Access($3)),
      LOC(1)(new Literal($1)));
      }),
      o('SUPER INDEX_START Expression INDEX_END',
      function() {
        return new Super(LOC(3)(new Index($3)),
      LOC(1)(new Literal($1)));
      }),
      o('SUPER INDEX_START INDENT Expression OUTDENT INDEX_END',
      function() {
        return new Super(LOC(4)(new Index($4)),
      LOC(1)(new Literal($1)));
      })
    ],
    // A “meta-property” access e.g. `new.target` or `import.meta`, where
    // something that looks like a property is referenced on a keyword.
    MetaProperty: [
      o('NEW_TARGET . Property',
      function() {
        return new MetaProperty(LOC(1)(new IdentifierLiteral($1)),
      LOC(3)(new Access($3)));
      }),
      o('IMPORT_META . Property',
      function() {
        return new MetaProperty(LOC(1)(new IdentifierLiteral($1)),
      LOC(3)(new Access($3)));
      })
    ],
    // The general group of accessors into an object, by property, by prototype
    // or by array index or slice.
    Accessor: [
      o('.  Property',
      function() {
        return new Access($2);
      }),
      o('?. Property',
      function() {
        return new Access($2,
      {
          soak: true
        });
      }),
      o(':: Property',
      function() {
        return [
          LOC(1)(new Access(new PropertyName('prototype'),
          {
            shorthand: true
          })),
          LOC(2)(new Access($2))
        ];
      }),
      o('?:: Property',
      function() {
        return [
          LOC(1)(new Access(new PropertyName('prototype'),
          {
            shorthand: true,
            soak: true
          })),
          LOC(2)(new Access($2))
        ];
      }),
      o('::',
      function() {
        return new Access(new PropertyName('prototype'),
      {
          shorthand: true
        });
      }),
      o('?::',
      function() {
        return new Access(new PropertyName('prototype'),
      {
          shorthand: true,
          soak: true
        });
      }),
      o('Index')
    ],
    // Indexing into an object or array using bracket notation.
    Index: [
      o('INDEX_START IndexValue INDEX_END',
      function() {
        return $2;
      }),
      o('INDEX_START INDENT IndexValue OUTDENT INDEX_END',
      function() {
        return $3;
      }),
      o('INDEX_SOAK  Index',
      function() {
        return extend($2,
      {
          soak: true
        });
      })
    ],
    IndexValue: [
      o('Expression',
      function() {
        return new Index($1);
      }),
      o('Slice',
      function() {
        return new Slice($1);
      })
    ],
    // In CoffeeScript, an object literal is simply a list of assignments.
    Object: [
      o('{ AssignList OptComma }',
      function() {
        return new Obj($2,
      $1.generated);
      })
    ],
    // Assignment of properties within an object literal can be separated by
    // comma, as in JavaScript, or simply by newline.
    AssignList: [
      o('',
      function() {
        return [];
      }),
      o('AssignObj',
      function() {
        return [$1];
      }),
      o('AssignList , AssignObj',
      function() {
        return $1.concat($3);
      }),
      o('AssignList OptComma TERMINATOR AssignObj',
      function() {
        return $1.concat($4);
      }),
      o('AssignList OptComma INDENT AssignList OptComma OUTDENT',
      function() {
        return $1.concat($4);
      })
    ],
    // Class definitions have optional bodies of prototype property assignments,
    // and optional references to the superclass.
    Class: [
      o('CLASS',
      function() {
        return new Class();
      }),
      o('CLASS Block',
      function() {
        return new Class(null,
      null,
      $2);
      }),
      o('CLASS EXTENDS Expression',
      function() {
        return new Class(null,
      $3);
      }),
      o('CLASS EXTENDS Expression Block',
      function() {
        return new Class(null,
      $3,
      $4);
      }),
      o('CLASS SimpleAssignable',
      function() {
        return new Class($2);
      }),
      o('CLASS SimpleAssignable Block',
      function() {
        return new Class($2,
      null,
      $3);
      }),
      o('CLASS SimpleAssignable EXTENDS Expression',
      function() {
        return new Class($2,
      $4);
      }),
      o('CLASS SimpleAssignable EXTENDS Expression Block',
      function() {
        return new Class($2,
      $4,
      $5);
      })
    ],
    Import: [
      o('IMPORT String',
      function() {
        return new ImportDeclaration(null,
      $2);
      }),
      o('IMPORT ImportDefaultSpecifier FROM String',
      function() {
        return new ImportDeclaration(new ImportClause($2,
      null),
      $4);
      }),
      o('IMPORT ImportNamespaceSpecifier FROM String',
      function() {
        return new ImportDeclaration(new ImportClause(null,
      $2),
      $4);
      }),
      o('IMPORT { } FROM String',
      function() {
        return new ImportDeclaration(new ImportClause(null,
      new ImportSpecifierList([])),
      $5);
      }),
      o('IMPORT { ImportSpecifierList OptComma } FROM String',
      function() {
        return new ImportDeclaration(new ImportClause(null,
      new ImportSpecifierList($3)),
      $7);
      }),
      o('IMPORT ImportDefaultSpecifier , ImportNamespaceSpecifier FROM String',
      function() {
        return new ImportDeclaration(new ImportClause($2,
      $4),
      $6);
      }),
      o('IMPORT ImportDefaultSpecifier , { ImportSpecifierList OptComma } FROM String',
      function() {
        return new ImportDeclaration(new ImportClause($2,
      new ImportSpecifierList($5)),
      $9);
      })
    ],
    ImportSpecifierList: [
      o('ImportSpecifier',
      function() {
        return [$1];
      }),
      o('ImportSpecifierList , ImportSpecifier',
      function() {
        return $1.concat($3);
      }),
      o('ImportSpecifierList OptComma TERMINATOR ImportSpecifier',
      function() {
        return $1.concat($4);
      }),
      o('INDENT ImportSpecifierList OptComma OUTDENT',
      function() {
        return $2;
      }),
      o('ImportSpecifierList OptComma INDENT ImportSpecifierList OptComma OUTDENT',
      function() {
        return $1.concat($4);
      })
    ],
    ImportSpecifier: [
      o('Identifier',
      function() {
        return new ImportSpecifier($1);
      }),
      o('Identifier AS Identifier',
      function() {
        return new ImportSpecifier($1,
      $3);
      }),
      o('DEFAULT',
      function() {
        return new ImportSpecifier(LOC(1)(new DefaultLiteral($1)));
      }),
      o('DEFAULT AS Identifier',
      function() {
        return new ImportSpecifier(LOC(1)(new DefaultLiteral($1)),
      $3);
      })
    ],
    ImportDefaultSpecifier: [
      o('Identifier',
      function() {
        return new ImportDefaultSpecifier($1);
      })
    ],
    ImportNamespaceSpecifier: [
      o('IMPORT_ALL AS Identifier',
      function() {
        return new ImportNamespaceSpecifier(new Literal($1),
      $3);
      })
    ],
    Export: [
      o('EXPORT { }',
      function() {
        return new ExportNamedDeclaration(new ExportSpecifierList([]));
      }),
      o('EXPORT { ExportSpecifierList OptComma }',
      function() {
        return new ExportNamedDeclaration(new ExportSpecifierList($3));
      }),
      o('EXPORT Class',
      function() {
        return new ExportNamedDeclaration($2);
      }),
      o('EXPORT Identifier = Expression',
      function() {
        return new ExportNamedDeclaration(LOC(2,
      4)(new Assign($2,
      $4,
      null,
      {
          moduleDeclaration: 'export'
        })));
      }),
      o('EXPORT Identifier = TERMINATOR Expression',
      function() {
        return new ExportNamedDeclaration(LOC(2,
      5)(new Assign($2,
      $5,
      null,
      {
          moduleDeclaration: 'export'
        })));
      }),
      o('EXPORT Identifier = INDENT Expression OUTDENT',
      function() {
        return new ExportNamedDeclaration(LOC(2,
      6)(new Assign($2,
      $5,
      null,
      {
          moduleDeclaration: 'export'
        })));
      }),
      o('EXPORT DEFAULT Expression',
      function() {
        return new ExportDefaultDeclaration($3);
      }),
      o('EXPORT DEFAULT INDENT Object OUTDENT',
      function() {
        return new ExportDefaultDeclaration(new Value($4));
      }),
      o('EXPORT EXPORT_ALL FROM String',
      function() {
        return new ExportAllDeclaration(new Literal($2),
      $4);
      }),
      o('EXPORT { } FROM String',
      function() {
        return new ExportNamedDeclaration(new ExportSpecifierList([]),
      $5);
      }),
      o('EXPORT { ExportSpecifierList OptComma } FROM String',
      function() {
        return new ExportNamedDeclaration(new ExportSpecifierList($3),
      $7);
      })
    ],
    ExportSpecifierList: [
      o('ExportSpecifier',
      function() {
        return [$1];
      }),
      o('ExportSpecifierList , ExportSpecifier',
      function() {
        return $1.concat($3);
      }),
      o('ExportSpecifierList OptComma TERMINATOR ExportSpecifier',
      function() {
        return $1.concat($4);
      }),
      o('INDENT ExportSpecifierList OptComma OUTDENT',
      function() {
        return $2;
      }),
      o('ExportSpecifierList OptComma INDENT ExportSpecifierList OptComma OUTDENT',
      function() {
        return $1.concat($4);
      })
    ],
    ExportSpecifier: [
      o('Identifier',
      function() {
        return new ExportSpecifier($1);
      }),
      o('Identifier AS Identifier',
      function() {
        return new ExportSpecifier($1,
      $3);
      }),
      o('Identifier AS DEFAULT',
      function() {
        return new ExportSpecifier($1,
      LOC(3)(new DefaultLiteral($3)));
      }),
      o('DEFAULT',
      function() {
        return new ExportSpecifier(LOC(1)(new DefaultLiteral($1)));
      }),
      o('DEFAULT AS Identifier',
      function() {
        return new ExportSpecifier(LOC(1)(new DefaultLiteral($1)),
      $3);
      })
    ],
    // Ordinary function invocation, or a chained series of calls.
    Invocation: [
      o('Value OptFuncExist String',
      function() {
        return new TaggedTemplateCall($1,
      $3,
      $2.soak);
      }),
      o('Value OptFuncExist Arguments',
      function() {
        return new Call($1,
      $3,
      $2.soak);
      }),
      o('SUPER OptFuncExist Arguments',
      function() {
        return new SuperCall(LOC(1)(new Super()),
      $3,
      $2.soak,
      $1);
      }),
      o('DYNAMIC_IMPORT Arguments',
      function() {
        return new DynamicImportCall(LOC(1)(new DynamicImport()),
      $2);
      })
    ],
    // An optional existence check on a function.
    OptFuncExist: [
      o('',
      function() {
        return {
          soak: false
        };
      }),
      o('FUNC_EXIST',
      function() {
        return {
          soak: true
        };
      })
    ],
    // The list of arguments to a function call.
    Arguments: [
      o('CALL_START CALL_END',
      function() {
        return [];
      }),
      o('CALL_START ArgList OptComma CALL_END',
      function() {
        $2.implicit = $1.generated;
        return $2;
      })
    ],
    // A reference to the *this* current object.
    This: [
      o('THIS',
      function() {
        return new Value(new ThisLiteral($1));
      }),
      o('@',
      function() {
        return new Value(new ThisLiteral($1));
      })
    ],
    // A reference to a property on *this*.
    ThisProperty: [
      o('@ Property',
      function() {
        return new Value(LOC(1)(new ThisLiteral($1)),
      [LOC(2)(new Access($2))],
      'this');
      })
    ],
    // The array literal.
    Array: [
      o('[ ]',
      function() {
        return new Arr([]);
      }),
      o('[ Elisions ]',
      function() {
        return new Arr($2);
      }),
      o('[ ArgElisionList OptElisions ]',
      function() {
        return new Arr([].concat($2,
      $3));
      })
    ],
    // Inclusive and exclusive range dots.
    RangeDots: [
      o('..',
      function() {
        return {
          exclusive: false
        };
      }),
      o('...',
      function() {
        return {
          exclusive: true
        };
      })
    ],
    // The CoffeeScript range literal.
    Range: [
      o('[ Expression RangeDots Expression ]',
      function() {
        return new Range($2,
      $4,
      $3.exclusive ? 'exclusive' : 'inclusive');
      }),
      o('[ ExpressionLine RangeDots Expression ]',
      function() {
        return new Range($2,
      $4,
      $3.exclusive ? 'exclusive' : 'inclusive');
      })
    ],
    // Array slice literals.
    Slice: [
      o('Expression RangeDots Expression',
      function() {
        return new Range($1,
      $3,
      $2.exclusive ? 'exclusive' : 'inclusive');
      }),
      o('Expression RangeDots',
      function() {
        return new Range($1,
      null,
      $2.exclusive ? 'exclusive' : 'inclusive');
      }),
      o('ExpressionLine RangeDots Expression',
      function() {
        return new Range($1,
      $3,
      $2.exclusive ? 'exclusive' : 'inclusive');
      }),
      o('ExpressionLine RangeDots',
      function() {
        return new Range($1,
      null,
      $2.exclusive ? 'exclusive' : 'inclusive');
      }),
      o('RangeDots Expression',
      function() {
        return new Range(null,
      $2,
      $1.exclusive ? 'exclusive' : 'inclusive');
      }),
      o('RangeDots',
      function() {
        return new Range(null,
      null,
      $1.exclusive ? 'exclusive' : 'inclusive');
      })
    ],
    // The **ArgList** is the list of objects passed into a function call
    // (i.e. comma-separated expressions). Newlines work as well.
    ArgList: [
      o('Arg',
      function() {
        return [$1];
      }),
      o('ArgList , Arg',
      function() {
        return $1.concat($3);
      }),
      o('ArgList OptComma TERMINATOR Arg',
      function() {
        return $1.concat($4);
      }),
      o('INDENT ArgList OptComma OUTDENT',
      function() {
        return $2;
      }),
      o('ArgList OptComma INDENT ArgList OptComma OUTDENT',
      function() {
        return $1.concat($4);
      })
    ],
    // Valid arguments are Blocks or Splats.
    Arg: [
      o('Expression'),
      o('ExpressionLine'),
      o('Splat'),
      o('...',
      function() {
        return new Expansion();
      })
    ],
    // The **ArgElisionList** is the list of objects, contents of an array literal
    // (i.e. comma-separated expressions and elisions). Newlines work as well.
    ArgElisionList: [
      o('ArgElision'),
      o('ArgElisionList , ArgElision',
      function() {
        return $1.concat($3);
      }),
      o('ArgElisionList OptComma TERMINATOR ArgElision',
      function() {
        return $1.concat($4);
      }),
      o('INDENT ArgElisionList OptElisions OUTDENT',
      function() {
        return $2.concat($3);
      }),
      o('ArgElisionList OptElisions INDENT ArgElisionList OptElisions OUTDENT',
      function() {
        return $1.concat($2,
      $4,
      $5);
      })
    ],
    ArgElision: [
      o('Arg',
      function() {
        return [$1];
      }),
      o('Elisions Arg',
      function() {
        return $1.concat($2);
      })
    ],
    OptElisions: [
      o('OptComma',
      function() {
        return [];
      }),
      o(', Elisions',
      function() {
        return [].concat($2);
      })
    ],
    Elisions: [
      o('Elision',
      function() {
        return [$1];
      }),
      o('Elisions Elision',
      function() {
        return $1.concat($2);
      })
    ],
    Elision: [
      o(',',
      function() {
        return new Elision();
      }),
      o('Elision TERMINATOR',
      function() {
        return $1;
      })
    ],
    // Just simple, comma-separated, required arguments (no fancy syntax). We need
    // this to be separate from the **ArgList** for use in **Switch** blocks, where
    // having the newlines wouldn't make sense.
    SimpleArgs: [
      o('Expression'),
      o('ExpressionLine'),
      o('SimpleArgs , Expression',
      function() {
        return [].concat($1,
      $3);
      }),
      o('SimpleArgs , ExpressionLine',
      function() {
        return [].concat($1,
      $3);
      })
    ],
    // The variants of *try/catch/finally* exception handling blocks.
    Try: [
      o('TRY Block',
      function() {
        return new Try($2);
      }),
      o('TRY Block Catch',
      function() {
        return new Try($2,
      $3);
      }),
      o('TRY Block FINALLY Block',
      function() {
        return new Try($2,
      null,
      $4,
      LOC(3)(new Literal($3)));
      }),
      o('TRY Block Catch FINALLY Block',
      function() {
        return new Try($2,
      $3,
      $5,
      LOC(4)(new Literal($4)));
      })
    ],
    // A catch clause names its error and runs a block of code.
    Catch: [
      o('CATCH Identifier Block',
      function() {
        return new Catch($3,
      $2);
      }),
      o('CATCH Object Block',
      function() {
        return new Catch($3,
      LOC(2)(new Value($2)));
      }),
      o('CATCH Block',
      function() {
        return new Catch($2);
      })
    ],
    // Throw an exception object.
    Throw: [
      o('THROW Expression',
      function() {
        return new Throw($2);
      }),
      o('THROW INDENT Object OUTDENT',
      function() {
        return new Throw(new Value($3));
      })
    ],
    // Parenthetical expressions. Note that the **Parenthetical** is a **Value**,
    // not an **Expression**, so if you need to use an expression in a place
    // where only values are accepted, wrapping it in parentheses will always do
    // the trick.
    Parenthetical: [
      o('( Body )',
      function() {
        return new Parens($2);
      }),
      o('( INDENT Body OUTDENT )',
      function() {
        return new Parens($3);
      })
    ],
    // The condition portion of a while loop.
    WhileLineSource: [
      o('WHILE ExpressionLine',
      function() {
        return new While($2);
      }),
      o('WHILE ExpressionLine WHEN ExpressionLine',
      function() {
        return new While($2,
      {
          guard: $4
        });
      }),
      o('UNTIL ExpressionLine',
      function() {
        return new While($2,
      {
          invert: true
        });
      }),
      o('UNTIL ExpressionLine WHEN ExpressionLine',
      function() {
        return new While($2,
      {
          invert: true,
          guard: $4
        });
      })
    ],
    WhileSource: [
      o('WHILE Expression',
      function() {
        return new While($2);
      }),
      o('WHILE Expression WHEN Expression',
      function() {
        return new While($2,
      {
          guard: $4
        });
      }),
      o('WHILE ExpressionLine WHEN Expression',
      function() {
        return new While($2,
      {
          guard: $4
        });
      }),
      o('UNTIL Expression',
      function() {
        return new While($2,
      {
          invert: true
        });
      }),
      o('UNTIL Expression WHEN Expression',
      function() {
        return new While($2,
      {
          invert: true,
          guard: $4
        });
      }),
      o('UNTIL ExpressionLine WHEN Expression',
      function() {
        return new While($2,
      {
          invert: true,
          guard: $4
        });
      })
    ],
    // The while loop can either be normal, with a block of expressions to execute,
    // or postfix, with a single expression. There is no do..while.
    While: [
      o('WhileSource Block',
      function() {
        return $1.addBody($2);
      }),
      o('WhileLineSource Block',
      function() {
        return $1.addBody($2);
      }),
      o('Statement  WhileSource',
      function() {
        return (Object.assign($2,
      {
          postfix: true
        })).addBody(LOC(1)(Block.wrap([$1])));
      }),
      o('Expression WhileSource',
      function() {
        return (Object.assign($2,
      {
          postfix: true
        })).addBody(LOC(1)(Block.wrap([$1])));
      }),
      o('Loop',
      function() {
        return $1;
      })
    ],
    Loop: [
      o('LOOP Block',
      function() {
        return new While(LOC(1)(new BooleanLiteral('true')),
      {
          isLoop: true
        }).addBody($2);
      }),
      o('LOOP Expression',
      function() {
        return new While(LOC(1)(new BooleanLiteral('true')),
      {
          isLoop: true
        }).addBody(LOC(2)(Block.wrap([$2])));
      })
    ],
    // Array, object, and range comprehensions, at the most generic level.
    // Comprehensions can either be normal, with a block of expressions to execute,
    // or postfix, with a single expression.
    For: [
      o('Statement    ForBody',
      function() {
        $2.postfix = true;
        return $2.addBody($1);
      }),
      o('Expression   ForBody',
      function() {
        $2.postfix = true;
        return $2.addBody($1);
      }),
      o('ForBody      Block',
      function() {
        return $1.addBody($2);
      }),
      o('ForLineBody  Block',
      function() {
        return $1.addBody($2);
      })
    ],
    ForBody: [
      o('FOR Range',
      function() {
        return new For([],
      {
          source: LOC(2)(new Value($2))
        });
      }),
      o('FOR Range BY Expression',
      function() {
        return new For([],
      {
          source: LOC(2)(new Value($2)),
          step: $4
        });
      }),
      o('ForStart ForSource',
      function() {
        return $1.addSource($2);
      })
    ],
    ForLineBody: [
      o('FOR Range BY ExpressionLine',
      function() {
        return new For([],
      {
          source: LOC(2)(new Value($2)),
          step: $4
        });
      }),
      o('ForStart ForLineSource',
      function() {
        return $1.addSource($2);
      })
    ],
    ForStart: [
      o('FOR ForVariables',
      function() {
        return new For([],
      {
          name: $2[0],
          index: $2[1]
        });
      }),
      o('FOR AWAIT ForVariables',
      function() {
        var index,
      name;
        [name,
      index] = $3;
        return new For([],
      {
          name,
          index,
          await: true,
          awaitTag: LOC(2)(new Literal($2))
        });
      }),
      o('FOR OWN ForVariables',
      function() {
        var index,
      name;
        [name,
      index] = $3;
        return new For([],
      {
          name,
          index,
          own: true,
          ownTag: LOC(2)(new Literal($2))
        });
      })
    ],
    // An array of all accepted values for a variable inside the loop.
    // This enables support for pattern matching.
    ForValue: [
      o('Identifier'),
      o('ThisProperty'),
      o('Array',
      function() {
        return new Value($1);
      }),
      o('Object',
      function() {
        return new Value($1);
      })
    ],
    // An array or range comprehension has variables for the current element
    // and (optional) reference to the current index. Or, *key, value*, in the case
    // of object comprehensions.
    ForVariables: [
      o('ForValue',
      function() {
        return [$1];
      }),
      o('ForValue , ForValue',
      function() {
        return [$1,
      $3];
      })
    ],
    // The source of a comprehension is an array or object with an optional guard
    // clause. If it’s an array comprehension, you can also choose to step through
    // in fixed-size increments.
    ForSource: [
      o('FORIN Expression',
      function() {
        return {
          source: $2
        };
      }),
      o('FOROF Expression',
      function() {
        return {
          source: $2,
          object: true
        };
      }),
      o('FORIN Expression WHEN Expression',
      function() {
        return {
          source: $2,
          guard: $4
        };
      }),
      o('FORIN ExpressionLine WHEN Expression',
      function() {
        return {
          source: $2,
          guard: $4
        };
      }),
      o('FOROF Expression WHEN Expression',
      function() {
        return {
          source: $2,
          guard: $4,
          object: true
        };
      }),
      o('FOROF ExpressionLine WHEN Expression',
      function() {
        return {
          source: $2,
          guard: $4,
          object: true
        };
      }),
      o('FORIN Expression BY Expression',
      function() {
        return {
          source: $2,
          step: $4
        };
      }),
      o('FORIN ExpressionLine BY Expression',
      function() {
        return {
          source: $2,
          step: $4
        };
      }),
      o('FORIN Expression WHEN Expression BY Expression',
      function() {
        return {
          source: $2,
          guard: $4,
          step: $6
        };
      }),
      o('FORIN ExpressionLine WHEN Expression BY Expression',
      function() {
        return {
          source: $2,
          guard: $4,
          step: $6
        };
      }),
      o('FORIN Expression WHEN ExpressionLine BY Expression',
      function() {
        return {
          source: $2,
          guard: $4,
          step: $6
        };
      }),
      o('FORIN ExpressionLine WHEN ExpressionLine BY Expression',
      function() {
        return {
          source: $2,
          guard: $4,
          step: $6
        };
      }),
      o('FORIN Expression BY Expression WHEN Expression',
      function() {
        return {
          source: $2,
          step: $4,
          guard: $6
        };
      }),
      o('FORIN ExpressionLine BY Expression WHEN Expression',
      function() {
        return {
          source: $2,
          step: $4,
          guard: $6
        };
      }),
      o('FORIN Expression BY ExpressionLine WHEN Expression',
      function() {
        return {
          source: $2,
          step: $4,
          guard: $6
        };
      }),
      o('FORIN ExpressionLine BY ExpressionLine WHEN Expression',
      function() {
        return {
          source: $2,
          step: $4,
          guard: $6
        };
      }),
      o('FORFROM Expression',
      function() {
        return {
          source: $2,
          from: true
        };
      }),
      o('FORFROM Expression WHEN Expression',
      function() {
        return {
          source: $2,
          guard: $4,
          from: true
        };
      }),
      o('FORFROM ExpressionLine WHEN Expression',
      function() {
        return {
          source: $2,
          guard: $4,
          from: true
        };
      })
    ],
    ForLineSource: [
      o('FORIN ExpressionLine',
      function() {
        return {
          source: $2
        };
      }),
      o('FOROF ExpressionLine',
      function() {
        return {
          source: $2,
          object: true
        };
      }),
      o('FORIN Expression WHEN ExpressionLine',
      function() {
        return {
          source: $2,
          guard: $4
        };
      }),
      o('FORIN ExpressionLine WHEN ExpressionLine',
      function() {
        return {
          source: $2,
          guard: $4
        };
      }),
      o('FOROF Expression WHEN ExpressionLine',
      function() {
        return {
          source: $2,
          guard: $4,
          object: true
        };
      }),
      o('FOROF ExpressionLine WHEN ExpressionLine',
      function() {
        return {
          source: $2,
          guard: $4,
          object: true
        };
      }),
      o('FORIN Expression BY ExpressionLine',
      function() {
        return {
          source: $2,
          step: $4
        };
      }),
      o('FORIN ExpressionLine BY ExpressionLine',
      function() {
        return {
          source: $2,
          step: $4
        };
      }),
      o('FORIN Expression WHEN Expression BY ExpressionLine',
      function() {
        return {
          source: $2,
          guard: $4,
          step: $6
        };
      }),
      o('FORIN ExpressionLine WHEN Expression BY ExpressionLine',
      function() {
        return {
          source: $2,
          guard: $4,
          step: $6
        };
      }),
      o('FORIN Expression WHEN ExpressionLine BY ExpressionLine',
      function() {
        return {
          source: $2,
          guard: $4,
          step: $6
        };
      }),
      o('FORIN ExpressionLine WHEN ExpressionLine BY ExpressionLine',
      function() {
        return {
          source: $2,
          guard: $4,
          step: $6
        };
      }),
      o('FORIN Expression BY Expression WHEN ExpressionLine',
      function() {
        return {
          source: $2,
          step: $4,
          guard: $6
        };
      }),
      o('FORIN ExpressionLine BY Expression WHEN ExpressionLine',
      function() {
        return {
          source: $2,
          step: $4,
          guard: $6
        };
      }),
      o('FORIN Expression BY ExpressionLine WHEN ExpressionLine',
      function() {
        return {
          source: $2,
          step: $4,
          guard: $6
        };
      }),
      o('FORIN ExpressionLine BY ExpressionLine WHEN ExpressionLine',
      function() {
        return {
          source: $2,
          step: $4,
          guard: $6
        };
      }),
      o('FORFROM ExpressionLine',
      function() {
        return {
          source: $2,
          from: true
        };
      }),
      o('FORFROM Expression WHEN ExpressionLine',
      function() {
        return {
          source: $2,
          guard: $4,
          from: true
        };
      }),
      o('FORFROM ExpressionLine WHEN ExpressionLine',
      function() {
        return {
          source: $2,
          guard: $4,
          from: true
        };
      })
    ],
    Switch: [
      o('SWITCH Expression INDENT Whens OUTDENT',
      function() {
        return new Switch($2,
      $4);
      }),
      o('SWITCH ExpressionLine INDENT Whens OUTDENT',
      function() {
        return new Switch($2,
      $4);
      }),
      o('SWITCH Expression INDENT Whens ELSE Block OUTDENT',
      function() {
        return new Switch($2,
      $4,
      LOC(5,
      6)($6));
      }),
      o('SWITCH ExpressionLine INDENT Whens ELSE Block OUTDENT',
      function() {
        return new Switch($2,
      $4,
      LOC(5,
      6)($6));
      }),
      o('SWITCH INDENT Whens OUTDENT',
      function() {
        return new Switch(null,
      $3);
      }),
      o('SWITCH INDENT Whens ELSE Block OUTDENT',
      function() {
        return new Switch(null,
      $3,
      LOC(4,
      5)($5));
      })
    ],
    Whens: [
      o('When',
      function() {
        return [$1];
      }),
      o('Whens When',
      function() {
        return $1.concat($2);
      })
    ],
    // An individual **When** clause, with action.
    When: [
      o('LEADING_WHEN SimpleArgs Block',
      function() {
        return new SwitchWhen($2,
      $3);
      }),
      o('LEADING_WHEN SimpleArgs Block TERMINATOR',
      function() {
        return LOC(1,
      3)(new SwitchWhen($2,
      $3));
      })
    ],
    // The most basic form of *if* is a condition and an action. The following
    // if-related rules are broken up along these lines in order to avoid
    // ambiguity.
    IfBlock: [
      o('IF Expression Block',
      function() {
        return new If($2,
      $3,
      {
          type: $1
        });
      }),
      o('IfBlock ELSE IF Expression Block',
      function() {
        return $1.addElse(LOC(3,
      5)(new If($4,
      $5,
      {
          type: $3
        })));
      })
    ],
    // The full complement of *if* expressions, including postfix one-liner
    // *if* and *unless*.
    If: [
      o('IfBlock'),
      o('IfBlock ELSE Block',
      function() {
        return $1.addElse($3);
      }),
      o('Statement  POST_IF Expression',
      function() {
        return new If($3,
      LOC(1)(Block.wrap([$1])),
      {
          type: $2,
          postfix: true
        });
      }),
      o('Expression POST_IF Expression',
      function() {
        return new If($3,
      LOC(1)(Block.wrap([$1])),
      {
          type: $2,
          postfix: true
        });
      })
    ],
    IfBlockLine: [
      o('IF ExpressionLine Block',
      function() {
        return new If($2,
      $3,
      {
          type: $1
        });
      }),
      o('IfBlockLine ELSE IF ExpressionLine Block',
      function() {
        return $1.addElse(LOC(3,
      5)(new If($4,
      $5,
      {
          type: $3
        })));
      })
    ],
    IfLine: [
      o('IfBlockLine'),
      o('IfBlockLine ELSE Block',
      function() {
        return $1.addElse($3);
      }),
      o('Statement  POST_IF ExpressionLine',
      function() {
        return new If($3,
      LOC(1)(Block.wrap([$1])),
      {
          type: $2,
          postfix: true
        });
      }),
      o('Expression POST_IF ExpressionLine',
      function() {
        return new If($3,
      LOC(1)(Block.wrap([$1])),
      {
          type: $2,
          postfix: true
        });
      })
    ],
    // Arithmetic and logical operators, working on one or more operands.
    // Here they are grouped by order of precedence. The actual precedence rules
    // are defined at the bottom of the page. It would be shorter if we could
    // combine most of these rules into a single generic *Operand OpSymbol Operand*
    // -type rule, but in order to make the precedence binding possible, separate
    // rules are necessary.
    OperationLine: [
      o('UNARY ExpressionLine',
      function() {
        return new Op($1,
      $2);
      }),
      o('DO ExpressionLine',
      function() {
        return new Op($1,
      $2);
      }),
      o('DO_IIFE CodeLine',
      function() {
        return new Op($1,
      $2);
      })
    ],
    Operation: [
      o('UNARY Expression',
      function() {
        return new Op($1.toString(),
      $2,
      void 0,
      void 0,
      {
          originalOperator: $1.original
        });
      }),
      o('DO Expression',
      function() {
        return new Op($1,
      $2);
      }),
      o('UNARY_MATH Expression',
      function() {
        return new Op($1,
      $2);
      }),
      o('-     Expression',
      (function() {
        return new Op('-',
      $2);
      }),
      {
        prec: 'UNARY_MATH'
      }),
      o('+     Expression',
      (function() {
        return new Op('+',
      $2);
      }),
      {
        prec: 'UNARY_MATH'
      }),
      o('AWAIT Expression',
      function() {
        return new Op($1,
      $2);
      }),
      o('AWAIT INDENT Object OUTDENT',
      function() {
        return new Op($1,
      $3);
      }),
      o('-- SimpleAssignable',
      function() {
        return new Op('--',
      $2);
      }),
      o('++ SimpleAssignable',
      function() {
        return new Op('++',
      $2);
      }),
      o('SimpleAssignable --',
      function() {
        return new Op('--',
      $1,
      null,
      true);
      }),
      o('SimpleAssignable ++',
      function() {
        return new Op('++',
      $1,
      null,
      true);
      }),
      // [The existential operator](https://coffeescript.org/#existential-operator).
      o('Expression ?',
      function() {
        return new Existence($1);
      }),
      o('Expression +  Expression',
      function() {
        return new Op('+',
      $1,
      $3);
      }),
      o('Expression -  Expression',
      function() {
        return new Op('-',
      $1,
      $3);
      }),
      o('Expression MATH     Expression',
      function() {
        return new Op($2,
      $1,
      $3);
      }),
      o('Expression **       Expression',
      function() {
        return new Op($2,
      $1,
      $3);
      }),
      o('Expression SHIFT    Expression',
      function() {
        return new Op($2,
      $1,
      $3);
      }),
      o('Expression COMPARE  Expression',
      function() {
        return new Op($2.toString(),
      $1,
      $3,
      void 0,
      {
          originalOperator: $2.original
        });
      }),
      o('Expression &        Expression',
      function() {
        return new Op($2,
      $1,
      $3);
      }),
      o('Expression ^        Expression',
      function() {
        return new Op($2,
      $1,
      $3);
      }),
      o('Expression |        Expression',
      function() {
        return new Op($2,
      $1,
      $3);
      }),
      o('Expression &&       Expression',
      function() {
        return new Op($2.toString(),
      $1,
      $3,
      void 0,
      {
          originalOperator: $2.original
        });
      }),
      o('Expression ||       Expression',
      function() {
        return new Op($2.toString(),
      $1,
      $3,
      void 0,
      {
          originalOperator: $2.original
        });
      }),
      o('Expression BIN?     Expression',
      function() {
        return new Op($2,
      $1,
      $3);
      }),
      o('Expression RELATION Expression',
      function() {
        var ref,
      ref1;
        return new Op($2.toString(),
      $1,
      $3,
      void 0,
      {
          invertOperator: (ref = (ref1 = $2.invert) != null ? ref1.original : void 0) != null ? ref : $2.invert
        });
      }),
      o('SimpleAssignable COMPOUND_ASSIGN Expression',
      function() {
        return new Assign($1,
      $3,
      $2.toString(),
      {
          originalContext: $2.original
        });
      }),
      o('SimpleAssignable COMPOUND_ASSIGN INDENT Expression OUTDENT',
      function() {
        return new Assign($1,
      $4,
      $2.toString(),
      {
          originalContext: $2.original
        });
      }),
      o('SimpleAssignable COMPOUND_ASSIGN TERMINATOR Expression',
      function() {
        return new Assign($1,
      $4,
      $2.toString(),
      {
          originalContext: $2.original
        });
      })
    ],
    DoIife: [
      o('DO_IIFE Code',
      function() {
        return new Op($1,
      $2);
      })
    ]
  };

  // Precedence
  // ----------

  // Operators at the top of this list have higher precedence than the ones lower
  // down. Following these rules is what makes `2 + 3 * 4` parse as:

  //     2 + (3 * 4)

  // And not:

  //     (2 + 3) * 4
  operators = [['right', 'DO_IIFE'], ['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY', 'DO'], ['right', 'AWAIT'], ['right', '**'], ['right', 'UNARY_MATH'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', '&'], ['left', '^'], ['left', '|'], ['left', '&&'], ['left', '||'], ['left', 'BIN?'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', 'YIELD'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'FORFROM', 'BY', 'WHEN'], ['right', 'IF', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'IMPORT', 'EXPORT', 'DYNAMIC_IMPORT'], ['left', 'POST_IF']];

  // Wrapping Up
  // -----------

  // Finally, now that we have our **grammar** and our **operators**, we can create
  // our **Jison.Parser**. We do this by processing all of our rules, recording all
  // terminals (every symbol which does not appear as the name of a rule above)
  // as "tokens".
  tokens = [];

  for (name in grammar) {
    alternatives = grammar[name];
    grammar[name] = (function() {
      var i, j, len, len1, ref, results;
      results = [];
      for (i = 0, len = alternatives.length; i < len; i++) {
        alt = alternatives[i];
        ref = alt[0].split(' ');
        for (j = 0, len1 = ref.length; j < len1; j++) {
          token = ref[j];
          if (!grammar[token]) {
            tokens.push(token);
          }
        }
        if (name === 'Root') {
          alt[1] = `return ${alt[1]}`;
        }
        results.push(alt);
      }
      return results;
    })();
  }

  // Initialize the **Parser** with our list of terminal **tokens**, our **grammar**
  // rules, and the name of the root. Reverse the operators because Jison orders
  // precedence from low to high, and we have it high to low
  // (as in [Yacc](http://dinosaur.compilertools.net/yacc/index.html)).
  exports.parser = new Parser({
    tokens: tokens.join(' '),
    bnf: grammar,
    operators: operators.reverse(),
    startSymbol: 'Root'
  });

}).call(this);
