// Generated by CoffeeScript 2.3.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;
    patternString = patternString.replace(/\s{2,}/g, ' ');
    patternCount = patternString.split(' ').length;
    if (action) {
      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) {
        return `yy.addDataToNode(yy, @${first}${(last ? `, @${last}` : '')})`;
      };
      action = action.replace(/LOC\(([0-9]*)\)/g, getAddDataToNodeFunctionString('$1'));
      action = action.replace(/LOC\(([0-9]*),\s*([0-9]*)\)/g, getAddDataToNodeFunctionString('$1', '$2'));
      performActionFunctionString = `$$ = ${getAddDataToNodeFunctionString(1, patternCount)}(${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 Block;
      }),
      o('Body')
    ],
    // 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. YieldReturn 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 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('CSX_TAG',
      function() {
        return new CSXTag($1);
      })
    ],
    Property: [
      o('PROPERTY',
      function() {
        return new PropertyName($1);
      })
    ],
    // 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);
      }),
      o('String')
    ],
    String: [
      o('STRING',
      function() {
        return new StringLiteral($1);
      }),
      o('STRING_START Body STRING_END',
      function() {
        return new StringWithInterpolations($2);
      })
    ],
    Regex: [
      o('REGEX',
      function() {
        return new RegexLiteral($1);
      }),
      o('REGEX_START Invocation REGEX_END',
      function() {
        return new RegexWithInterpolations($2.args);
      })
    ],
    // 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);
      }),
      o('Regex'),
      o('UNDEFINED',
      function() {
        return new UndefinedLiteral($1);
      }),
      o('NULL',
      function() {
        return new NullLiteral($1);
      }),
      o('BOOL',
      function() {
        return new BooleanLiteral($1);
      }),
      o('INFINITY',
      function() {
        return new InfinityLiteral($1);
      }),
      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('AlphaNumeric')
    ],
    // Object literal spread properties.
    ObjRestValue: [
      o('SimpleObjAssignable ...',
      function() {
        return new Splat(new Value($1));
      }),
      o('... SimpleObjAssignable',
      function() {
        return new Splat(new Value($2));
      }),
      o('ObjSpreadExpr ...',
      function() {
        return new Splat($1);
      }),
      o('... ObjSpreadExpr',
      function() {
        return new Splat($2);
      })
    ],
    ObjSpreadExpr: [
      o('ObjSpreadIdentifier'),
      o('Object'),
      o('Parenthetical'),
      o('Super'),
      o('This'),
      o('SUPER Arguments',
      function() {
        return new SuperCall(LOC(1)(new Super),
      $2,
      false,
      $1);
      }),
      o('SimpleObjAssignable Arguments',
      function() {
        return new Call(new Value($1),
      $2);
      }),
      o('ObjSpreadExpr Arguments',
      function() {
        return new Call($1,
      $2);
      })
    ],
    ObjSpreadIdentifier: [
      o('SimpleObjAssignable ObjSpreadAccessor',
      function() {
        return (new Value($1)).add($2);
      }),
      o('ObjSpreadExpr ObjSpreadAccessor',
      function() {
        return (new Value($1)).add($2);
      })
    ],
    ObjSpreadAccessor: [
      o('. Property',
      function() {
        return new Access($2);
      }),
      o('INDEX_START IndexValue INDEX_END',
      function() {
        return $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);
      }),
      o('YIELD RETURN',
      function() {
        return new YieldReturn;
      })
    ],
    AwaitReturn: [
      o('AWAIT RETURN Expression',
      function() {
        return new AwaitReturn($3);
      }),
      o('AWAIT RETURN',
      function() {
        return new AwaitReturn;
      })
    ],
    // 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,
      true);
      }),
      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);
      })
    ],
    // 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('This'),
      o('Super',
      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)),
      [],
      false,
      $1);
      }),
      o('SUPER INDEX_START Expression INDEX_END',
      function() {
        return new Super(LOC(3)(new Index($3)),
      [],
      false,
      $1);
      })
    ],
    // 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');
      }),
      o(':: Property',
      function() {
        return [LOC(1)(new Access(new PropertyName('prototype'))),
      LOC(2)(new Access($2))];
      }),
      o('?:: Property',
      function() {
        return [LOC(1)(new Access(new PropertyName('prototype'),
      'soak')),
      LOC(2)(new Access($2))];
      }),
      o('::',
      function() {
        return new Access(new PropertyName('prototype'));
      }),
      o('Index')
    ],
    // Indexing into an object or array using bracket notation.
    Index: [
      o('INDEX_START IndexValue INDEX_END',
      function() {
        return $2;
      }),
      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(new Literal($1));
      }),
      o('DEFAULT AS Identifier',
      function() {
        return new ImportSpecifier(new Literal($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(new Assign($2,
      $4,
      null,
      {
          moduleDeclaration: 'export'
        }));
      }),
      o('EXPORT Identifier = TERMINATOR Expression',
      function() {
        return new ExportNamedDeclaration(new Assign($2,
      $5,
      null,
      {
          moduleDeclaration: 'export'
        }));
      }),
      o('EXPORT Identifier = INDENT Expression OUTDENT',
      function() {
        return new ExportNamedDeclaration(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 { 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,
      new Literal($3));
      }),
      o('DEFAULT',
      function() {
        return new ExportSpecifier(new Literal($1));
      }),
      o('DEFAULT AS Identifier',
      function() {
        return new ExportSpecifier(new Literal($1),
      $3);
      })
    ],
    // Ordinary function invocation, or a chained series of calls.
    Invocation: [
      o('Value OptFuncExist String',
      function() {
        return new TaggedTemplateCall($1,
      $3,
      $2);
      }),
      o('Value OptFuncExist Arguments',
      function() {
        return new Call($1,
      $3,
      $2);
      }),
      o('SUPER OptFuncExist Arguments',
      function() {
        return new SuperCall(LOC(1)(new Super),
      $3,
      $2,
      $1);
      })
    ],
    // An optional existence check on a function.
    OptFuncExist: [
      o('',
      function() {
        return false;
      }),
      o('FUNC_EXIST',
      function() {
        return 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() {
        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 'inclusive';
      }),
      o('...',
      function() {
        return 'exclusive';
      })
    ],
    // The CoffeeScript range literal.
    Range: [
      o('[ Expression RangeDots Expression ]',
      function() {
        return new Range($2,
      $4,
      $3);
      }),
      o('[ ExpressionLine RangeDots Expression ]',
      function() {
        return new Range($2,
      $4,
      $3);
      })
    ],
    // Array slice literals.
    Slice: [
      o('Expression RangeDots Expression',
      function() {
        return new Range($1,
      $3,
      $2);
      }),
      o('Expression RangeDots',
      function() {
        return new Range($1,
      null,
      $2);
      }),
      o('ExpressionLine RangeDots Expression',
      function() {
        return new Range($1,
      $3,
      $2);
      }),
      o('ExpressionLine RangeDots',
      function() {
        return new Range($1,
      null,
      $2);
      }),
      o('RangeDots Expression',
      function() {
        return new Range(null,
      $2,
      $1);
      }),
      o('RangeDots',
      function() {
        return new Range(null,
      null,
      $1);
      })
    ],
    // 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 OptElisions TERMINATOR ArgElision',
      function() {
        return $1.concat($2,
      $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;
      })
    ],
    // 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[0],
      $3[1]);
      }),
      o('TRY Block FINALLY Block',
      function() {
        return new Try($2,
      null,
      null,
      $4);
      }),
      o('TRY Block Catch FINALLY Block',
      function() {
        return new Try($2,
      $3[0],
      $3[1],
      $5);
      })
    ],
    // A catch clause names its error and runs a block of code.
    Catch: [
      o('CATCH Identifier Block',
      function() {
        return [$2,
      $3];
      }),
      o('CATCH Object Block',
      function() {
        return [LOC(2)(new Value($2)),
      $3];
      }),
      o('CATCH Block',
      function() {
        return [null,
      $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 $2.addBody(LOC(1)(Block.wrap([$1])));
      }),
      o('Expression WhileSource',
      function() {
        return $2.addBody(LOC(1)(Block.wrap([$1])));
      }),
      o('Loop',
      function() {
        return $1;
      })
    ],
    Loop: [
      o('LOOP Block',
      function() {
        return new While(LOC(1)(new BooleanLiteral('true'))).addBody($2);
      }),
      o('LOOP Expression',
      function() {
        return new While(LOC(1)(new BooleanLiteral('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() {
        return $2.addBody($1);
      }),
      o('Expression   ForBody',
      function() {
        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,
      $6);
      }),
      o('SWITCH ExpressionLine INDENT Whens ELSE Block OUTDENT',
      function() {
        return new Switch($2,
      $4,
      $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,
      $5);
      })
    ],
    Whens: [
      o('When'),
      o('Whens When',
      function() {
        return $1.concat($2);
      })
    ],
    // An individual **When** clause, with action.
    When: [
      o('LEADING_WHEN SimpleArgs Block',
      function() {
        return [[$2,
      $3]];
      }),
      o('LEADING_WHEN SimpleArgs Block TERMINATOR',
      function() {
        return [[$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,
          statement: true
        });
      }),
      o('Expression POST_IF Expression',
      function() {
        return new If($3,
      LOC(1)(Block.wrap([$1])),
      {
          type: $2,
          statement: 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,
          statement: true
        });
      }),
      o('Expression POST_IF ExpressionLine',
      function() {
        return new If($3,
      LOC(1)(Block.wrap([$1])),
      {
          type: $2,
          statement: 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);
      })
    ],
    Operation: [
      o('UNARY 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('-- 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](http://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,
      $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,
      $1,
      $3);
      }),
      o('Expression &&       Expression',
      function() {
        return new Op($2,
      $1,
      $3);
      }),
      o('Expression ||       Expression',
      function() {
        return new Op($2,
      $1,
      $3);
      }),
      o('Expression BIN?     Expression',
      function() {
        return new Op($2,
      $1,
      $3);
      }),
      o('Expression RELATION Expression',
      function() {
        if ($2.charAt(0) === '!') {
          return new Op($2.slice(1),
      $1,
      $3).invert();
        } else {
          return new Op($2,
      $1,
      $3);
        }
      }),
      o('SimpleAssignable COMPOUND_ASSIGN Expression',
      function() {
        return new Assign($1,
      $3,
      $2);
      }),
      o('SimpleAssignable COMPOUND_ASSIGN INDENT Expression OUTDENT',
      function() {
        return new Assign($1,
      $4,
      $2);
      }),
      o('SimpleAssignable COMPOUND_ASSIGN TERMINATOR Expression',
      function() {
        return new Assign($1,
      $4,
      $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 = [['left', '.', '?.', '::', '?::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['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'], ['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);
