jashkenas--coffeescript/lib/coffeescript/grammar.js

2172 lines
53 KiB
JavaScript

// Generated by CoffeeScript 2.3.2
(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 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('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('DYNAMIC_IMPORT Arguments',
function() {
return new DynamicImportCall(LOC(1)(new DynamicImport),
$2);
}),
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('?::',
function() {
return new Access(new PropertyName('prototype'),
'soak');
}),
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);
}),
o('DYNAMIC_IMPORT Arguments',
function() {
return new DynamicImportCall(LOC(1)(new DynamicImport),
$2);
})
],
// 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('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,
$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', '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);