// Generated by CoffeeScript 2.1.1 (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('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')], 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'), o('[ Expression ]', function() { return new Value(new ComputedPropertyName($2)); }) ], ObjAssignable: [o('SimpleObjAssignable'), 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); }) ], // 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); }) ], // 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('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('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('SimpleArgs , Expression', 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. WhileSource: [ o('WHILE Expression', function() { return new While($2); }), o('WHILE Expression 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 }); }) ], // 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('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 new For($1, $2); }), o('Expression ForBody', function() { return new For($1, $2); }), o('ForBody Block', function() { return new For($2, $1); }) ], ForBody: [ o('FOR Range', function() { return { source: LOC(2)(new Value($2)) }; }), o('FOR Range BY Expression', function() { return { source: LOC(2)(new Value($2)), step: $4 }; }), o('ForStart ForSource', function() { $2.own = $1.own; $2.ownTag = $1.ownTag; $2.name = $1[0]; $2.index = $1[1]; return $2; }) ], ForStart: [ o('FOR ForVariables', function() { return $2; }), o('FOR OWN ForVariables', function() { $3.own = true; $3.ownTag = LOC(2)(new Literal($2)); return $3; }) ], // 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('FOROF Expression WHEN Expression', function() { return { source: $2, guard: $4, object: true }; }), o('FORIN Expression 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 Expression BY Expression 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 }; }) ], Switch: [ o('SWITCH Expression 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 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 }); }) ], // 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. 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);