mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
12fcbfc654
* Bump version to 2.1.1 * 2.1.1 changelog * 2.1.1 updated output
1730 lines
43 KiB
JavaScript
1730 lines
43 KiB
JavaScript
// 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);
|