docs/v1/annotated-source/grammar.html
browser.coffeecake.coffeecoffee-script.coffeecommand.coffeegrammar.coffeehelpers.coffeeindex.coffeelexer.coffeenodes.coffeeoptparse.coffeeregister.coffeerepl.coffeerewriter.coffeescope.litcoffeesourcemap.litcoffee
The CoffeeScript parser is generated by Jison from this grammar file. Jison is a bottom-up parser generator, similar in style to Bison, implemented in JavaScript. It can recognize LALR(1), LR(0), SLR(1), and LR(1) 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 the token sequence against the rules below. When a match can be made, it reduces into the nonterminal (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.
{Parser} =require'jison'
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. 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 = (patternString, action, options) -\>patternString = patternString.replace/\s{2,}/g,' 'patternCount = patternString.split(' ').lengthreturn[patternString,'$$ = $1;', options]unlessaction
action =ifmatch = unwrap.exec actionthenmatch[1]else"(#{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 a function which adds location data to the first parameter passed in, and returns the parameter. If the parameter is not a node, it will just be passed through unaffected.
addLocationDataFn = (first, last) -\>ifnotlast"yy.addLocationDataFn(@#{first})"else"yy.addLocationDataFn(@#{first}, @#{last})"action = action.replace/LOC\(([0-9]\*)\)/g, addLocationDataFn('$1')
action = action.replace/LOC\(([0-9]\*),\s\*([0-9]\*)\)/g, addLocationDataFn('$1','$2')
[patternString,"$$ = #{addLocationDataFn(1, patternCount)}(#{action});", options]
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'',-\>newBlock
o'Body']
Any list of statements and expressions, separated by line breaks or semicolons.
Body: [
o'Line',-\>Block.wrap [$1]
o'Body TERMINATOR Line',-\>$1.push $3o'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'YieldReturn']
Pure statements which cannot be expressions.
Statement: [
o'Return'o'Comment'o'STATEMENT',-\>newStatementLiteral $1o'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'Invocation'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',-\>newOp $1,newValuenewLiteral''o'YIELD Expression',-\>newOp $1, $2o'YIELD FROM Expression',-\>newOp $1.concat($2), $3]
An indented block of expressions. Note that the Rewriter will convert some postfix forms into blocks for us, by adjusting the token stream.
Block: [
o'INDENT OUTDENT',-\>newBlock
o'INDENT Body OUTDENT',-\>$2]
Identifier: [
o'IDENTIFIER',-\>newIdentifierLiteral $1]
Property: [
o'PROPERTY',-\>newPropertyName $1]
Alphanumerics are separated from the other Literal matchers because they can also serve as keys in object literals.
AlphaNumeric: [
o'NUMBER',-\>newNumberLiteral $1o'String']
String: [
o'STRING',-\>newStringLiteral $1o'STRING\_START Body STRING\_END',-\>newStringWithInterpolations $2]
Regex: [
o'REGEX',-\>newRegexLiteral $1o'REGEX\_START Invocation REGEX\_END',-\>newRegexWithInterpolations $2.args
]
All of our immediate values. Generally these can be passed straight through and printed to JavaScript.
Literal: [
o'AlphaNumeric'o'JS',-\>newPassthroughLiteral $1o'Regex'o'UNDEFINED',-\>newUndefinedLiteral
o'NULL',-\>newNullLiteral
o'BOOL',-\>newBooleanLiteral $1o'INFINITY',-\>newInfinityLiteral $1o'NAN',-\>newNaNLiteral
]
Assignment of a variable, property, or index to a value.
Assign: [
o'Assignable = Expression',-\>newAssign $1, $3o'Assignable = TERMINATOR Expression',-\>newAssign $1, $4o'Assignable = INDENT Expression OUTDENT',-\>newAssign $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',-\>newValue $1o'ObjAssignable : Expression',-\>newAssign LOC(1)(newValue $1), $3,'object',
operatorToken: LOC(2)(newLiteral $2)
o'ObjAssignable : INDENT Expression OUTDENT',-\>newAssign LOC(1)(newValue $1), $4,'object',
operatorToken: LOC(2)(newLiteral $2)
o'SimpleObjAssignable = Expression',-\>newAssign LOC(1)(newValue $1), $3,null,
operatorToken: LOC(2)(newLiteral $2)
o'SimpleObjAssignable = INDENT Expression OUTDENT',-\>newAssign LOC(1)(newValue $1), $4,null,
operatorToken: LOC(2)(newLiteral $2)
o'Comment']
SimpleObjAssignable: [
o'Identifier'o'Property'o'ThisProperty']
ObjAssignable: [
o'SimpleObjAssignable'o'AlphaNumeric']
A return statement from a function body.
Return: [
o'RETURN Expression',-\>newReturn $2o'RETURN INDENT Object OUTDENT',-\>newReturnnewValue $3o'RETURN',-\>newReturn
]
YieldReturn: [
o'YIELD RETURN Expression',-\>newYieldReturn $3o'YIELD RETURN',-\>newYieldReturn
]
A block comment.
Comment: [
o'HERECOMMENT',-\>newComment $1]
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',-\>newCode $2, $5, $4o'FuncGlyph Block',-\>newCode [], $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'-\>',-\>'func'o'=\>',-\>'boundfunc']
An optional, trailing comma.
OptComma: [
o''o',']
The list of parameters that a function accepts can be of any length.
ParamList: [
o'',-\>[]
o'Param',-\>[$1]
o'ParamList , Param',-\>$1.concat $3o'ParamList OptComma TERMINATOR Param',-\>$1.concat $4o'ParamList OptComma INDENT ParamList OptComma OUTDENT',-\>$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',-\>newParam $1o'ParamVar ...',-\>newParam $1,null,ono'ParamVar = Expression',-\>newParam $1, $3o'...',-\>newExpansion
]
Function Parameters
ParamVar: [
o'Identifier'o'ThisProperty'o'Array'o'Object']
A splat that occurs outside of a parameter list.
Splat: [
o'Expression ...',-\>newSplat $1]
Variables and properties that can be assigned to.
SimpleAssignable: [
o'Identifier',-\>newValue $1o'Value Accessor',-\>$1.add $2o'Invocation Accessor',-\>newValue $1, [].concat $2o'ThisProperty']
Everything that can be assigned to.
Assignable: [
o'SimpleAssignable'o'Array',-\>newValue $1o'Object',-\>newValue $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',-\>newValue $1o'Parenthetical',-\>newValue $1o'Range',-\>newValue $1o'This']
The general group of accessors into an object, by property, by prototype or by array index or slice.
Accessor: [
o'. Property',-\>newAccess $2o'?. Property',-\>newAccess $2,'soak'o':: Property',-\>[LOC(1)(newAccessnewPropertyName('prototype')), LOC(2)(newAccess $2)]
o'?:: Property',-\>[LOC(1)(newAccessnewPropertyName('prototype'),'soak'), LOC(2)(newAccess $2)]
o'::',-\>newAccessnewPropertyName'prototype'o'Index']
Indexing into an object or array using bracket notation.
Index: [
o'INDEX\_START IndexValue INDEX\_END',-\>$2o'INDEX\_SOAK Index',-\>extend $2, soak :yes]
IndexValue: [
o'Expression',-\>newIndex $1o'Slice',-\>newSlice $1]
In CoffeeScript, an object literal is simply a list of assignments.
Object: [
o'{ AssignList OptComma }',-\>newObj $2, $1.generated
]
Assignment of properties within an object literal can be separated by comma, as in JavaScript, or simply by newline.
AssignList: [
o'',-\>[]
o'AssignObj',-\>[$1]
o'AssignList , AssignObj',-\>$1.concat $3o'AssignList OptComma TERMINATOR AssignObj',-\>$1.concat $4o'AssignList OptComma INDENT AssignList OptComma OUTDENT',-\>$1.concat $4]
Class definitions have optional bodies of prototype property assignments, and optional references to the superclass.
Class: [
o'CLASS',-\>newClass
o'CLASS Block',-\>newClassnull,null, $2o'CLASS EXTENDS Expression',-\>newClassnull, $3o'CLASS EXTENDS Expression Block',-\>newClassnull, $3, $4o'CLASS SimpleAssignable',-\>newClass $2o'CLASS SimpleAssignable Block',-\>newClass $2,null, $3o'CLASS SimpleAssignable EXTENDS Expression',-\>newClass $2, $4o'CLASS SimpleAssignable EXTENDS Expression Block',-\>newClass $2, $4, $5]
Import: [
o'IMPORT String',-\>newImportDeclarationnull, $2o'IMPORT ImportDefaultSpecifier FROM String',-\>newImportDeclarationnewImportClause($2,null), $4o'IMPORT ImportNamespaceSpecifier FROM String',-\>newImportDeclarationnewImportClause(null, $2), $4o'IMPORT { } FROM String',-\>newImportDeclarationnewImportClause(null,newImportSpecifierList []), $5o'IMPORT { ImportSpecifierList OptComma } FROM String',-\>newImportDeclarationnewImportClause(null,newImportSpecifierList $3), $7o'IMPORT ImportDefaultSpecifier , ImportNamespaceSpecifier FROM String',-\>newImportDeclarationnewImportClause($2, $4), $6o'IMPORT ImportDefaultSpecifier , { ImportSpecifierList OptComma } FROM String',-\>newImportDeclarationnewImportClause($2,newImportSpecifierList $5), $9]
ImportSpecifierList: [
o'ImportSpecifier',-\>[$1]
o'ImportSpecifierList , ImportSpecifier',-\>$1.concat $3o'ImportSpecifierList OptComma TERMINATOR ImportSpecifier',-\>$1.concat $4o'INDENT ImportSpecifierList OptComma OUTDENT',-\>$2o'ImportSpecifierList OptComma INDENT ImportSpecifierList OptComma OUTDENT',-\>$1.concat $4]
ImportSpecifier: [
o'Identifier',-\>newImportSpecifier $1o'Identifier AS Identifier',-\>newImportSpecifier $1, $3o'DEFAULT',-\>newImportSpecifiernewLiteral $1o'DEFAULT AS Identifier',-\>newImportSpecifiernewLiteral($1), $3]
ImportDefaultSpecifier: [
o'Identifier',-\>newImportDefaultSpecifier $1]
ImportNamespaceSpecifier: [
o'IMPORT\_ALL AS Identifier',-\>newImportNamespaceSpecifiernewLiteral($1), $3]
Export: [
o'EXPORT { }',-\>newExportNamedDeclarationnewExportSpecifierList []
o'EXPORT { ExportSpecifierList OptComma }',-\>newExportNamedDeclarationnewExportSpecifierList $3o'EXPORT Class',-\>newExportNamedDeclaration $2o'EXPORT Identifier = Expression',-\>newExportNamedDeclarationnewAssign $2, $4,null,
moduleDeclaration:'export'o'EXPORT Identifier = TERMINATOR Expression',-\>newExportNamedDeclarationnewAssign $2, $5,null,
moduleDeclaration:'export'o'EXPORT Identifier = INDENT Expression OUTDENT',-\>newExportNamedDeclarationnewAssign $2, $5,null,
moduleDeclaration:'export'o'EXPORT DEFAULT Expression',-\>newExportDefaultDeclaration $3o'EXPORT EXPORT\_ALL FROM String',-\>newExportAllDeclarationnewLiteral($2), $4o'EXPORT { ExportSpecifierList OptComma } FROM String',-\>newExportNamedDeclarationnewExportSpecifierList($3), $7]
ExportSpecifierList: [
o'ExportSpecifier',-\>[$1]
o'ExportSpecifierList , ExportSpecifier',-\>$1.concat $3o'ExportSpecifierList OptComma TERMINATOR ExportSpecifier',-\>$1.concat $4o'INDENT ExportSpecifierList OptComma OUTDENT',-\>$2o'ExportSpecifierList OptComma INDENT ExportSpecifierList OptComma OUTDENT',-\>$1.concat $4]
ExportSpecifier: [
o'Identifier',-\>newExportSpecifier $1o'Identifier AS Identifier',-\>newExportSpecifier $1, $3o'Identifier AS DEFAULT',-\>newExportSpecifier $1,newLiteral $3o'DEFAULT',-\>newExportSpecifiernewLiteral $1o'DEFAULT AS Identifier',-\>newExportSpecifiernewLiteral($1), $3]
Ordinary function invocation, or a chained series of calls.
Invocation: [
o'Value OptFuncExist String',-\>newTaggedTemplateCall $1, $3, $2o'Value OptFuncExist Arguments',-\>newCall $1, $3, $2o'Invocation OptFuncExist Arguments',-\>newCall $1, $3, $2o'Super']
Super: [
o'SUPER',-\>newSuperCall
o'SUPER Arguments',-\>newSuperCall $2]
An optional existence check on a function.
OptFuncExist: [
o'',-\>noo'FUNC\_EXIST',-\>yes]
The list of arguments to a function call.
Arguments: [
o'CALL\_START CALL\_END',-\>[]
o'CALL\_START ArgList OptComma CALL\_END',-\>$2]
A reference to the this current object.
This: [
o'THIS',-\>newValuenewThisLiteral
o'@',-\>newValuenewThisLiteral
]
A reference to a property on this.
ThisProperty: [
o'@ Property',-\>newValue LOC(1)(newThisLiteral), [LOC(2)(newAccess($2))],'this']
The array literal.
Array: [
o'[]',-\>newArr []
o'[ArgList OptComma]',-\>newArr $2]
Inclusive and exclusive range dots.
RangeDots: [
o'..',-\>'inclusive'o'...',-\>'exclusive']
The CoffeeScript range literal.
Range: [
o'[Expression RangeDots Expression]',-\>newRange $2, $4, $3]
Array slice literals.
Slice: [
o'Expression RangeDots Expression',-\>newRange $1, $3, $2o'Expression RangeDots',-\>newRange $1,null, $2o'RangeDots Expression',-\>newRangenull, $2, $1o'RangeDots',-\>newRangenull,null, $1]
The ArgList is both the list of objects passed into a function call, as well as the contents of an array literal (i.e. comma-separated expressions). Newlines work as well.
ArgList: [
o'Arg',-\>[$1]
o'ArgList , Arg',-\>$1.concat $3o'ArgList OptComma TERMINATOR Arg',-\>$1.concat $4o'INDENT ArgList OptComma OUTDENT',-\>$2o'ArgList OptComma INDENT ArgList OptComma OUTDENT',-\>$1.concat $4]
Valid arguments are Blocks or Splats.
Arg: [
o'Expression'o'Splat'o'...',-\>newExpansion
]
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',-\>[].concat $1, $3]
The variants of try/catch/finally exception handling blocks.
Try: [
o'TRY Block',-\>newTry $2o'TRY Block Catch',-\>newTry $2, $3[0], $3[1]
o'TRY Block FINALLY Block',-\>newTry $2,null,null, $4o'TRY Block Catch FINALLY Block',-\>newTry $2, $3[0], $3[1], $5]
A catch clause names its error and runs a block of code.
Catch: [
o'CATCH Identifier Block',-\>[$2, $3]
o'CATCH Object Block',-\>[LOC(2)(newValue($2)), $3]
o'CATCH Block',-\>[null, $2]
]
Throw an exception object.
Throw: [
o'THROW Expression',-\>newThrow $2]
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 )',-\>newParens $2o'( INDENT Body OUTDENT )',-\>newParens $3]
The condition portion of a while loop.
WhileSource: [
o'WHILE Expression',-\>newWhile $2o'WHILE Expression WHEN Expression',-\>newWhile $2, guard: $4o'UNTIL Expression',-\>newWhile $2, invert:trueo'UNTIL Expression WHEN Expression',-\>newWhile $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',-\>$1.addBody $2o'Statement WhileSource',-\>$2.addBody LOC(1) Block.wrap([$1])
o'Expression WhileSource',-\>$2.addBody LOC(1) Block.wrap([$1])
o'Loop',-\>$1]
Loop: [
o'LOOP Block',-\>newWhile(LOC(1)newBooleanLiteral'true').addBody $2o'LOOP Expression',-\>newWhile(LOC(1)newBooleanLiteral'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',-\>newFor $1, $2o'Expression ForBody',-\>newFor $1, $2o'ForBody Block',-\>newFor $2, $1]
ForBody: [
o'FOR Range',-\>source: (LOC(2)newValue($2))
o'FOR Range BY Expression',-\>source: (LOC(2)newValue($2)), step: $4o'ForStart ForSource',-\>$2.own = $1.own; $2.ownTag = $1.ownTag; $2.name = $1[0]; $2.index = $1[1]; $2]
ForStart: [
o'FOR ForVariables',-\>$2o'FOR OWN ForVariables',-\>$3.own =yes; $3.ownTag = (LOC(2)newLiteral($2)); $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',-\>newValue $1o'Object',-\>newValue $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',-\>[$1]
o'ForValue , ForValue',-\>[$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',-\>source: $2o'FOROF Expression',-\>source: $2, object:yeso'FORIN Expression WHEN Expression',-\>source: $2, guard: $4o'FOROF Expression WHEN Expression',-\>source: $2, guard: $4, object:yeso'FORIN Expression BY Expression',-\>source: $2, step: $4o'FORIN Expression WHEN Expression BY Expression',-\>source: $2, guard: $4, step: $6o'FORIN Expression BY Expression WHEN Expression',-\>source: $2, step: $4, guard: $6o'FORFROM Expression',-\>source: $2, from:yeso'FORFROM Expression WHEN Expression',-\>source: $2, guard: $4, from:yes]
Switch: [
o'SWITCH Expression INDENT Whens OUTDENT',-\>newSwitch $2, $4o'SWITCH Expression INDENT Whens ELSE Block OUTDENT',-\>newSwitch $2, $4, $6o'SWITCH INDENT Whens OUTDENT',-\>newSwitchnull, $3o'SWITCH INDENT Whens ELSE Block OUTDENT',-\>newSwitchnull, $3, $5]
Whens: [
o'When'o'Whens When',-\>$1.concat $2]
An individual When clause, with action.
When: [
o'LEADING\_WHEN SimpleArgs Block',-\>[[$2, $3]]
o'LEADING\_WHEN SimpleArgs Block TERMINATOR',-\>[[$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',-\>newIf $2, $3, type: $1o'IfBlock ELSE IF Expression Block',-\>$1.addElse LOC(3,5)newIf $4, $5, type: $3]
The full complement of if expressions, including postfix one-liner if and unless.
If: [
o'IfBlock'o'IfBlock ELSE Block',-\>$1.addElse $3o'Statement POST\_IF Expression',-\>newIf $3, LOC(1)(Block.wrap [$1]), type: $2, statement:trueo'Expression POST\_IF Expression',-\>newIf $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',-\>newOp $1, $2o'UNARY\_MATH Expression',-\>newOp $1, $2o'- Expression', (-\>newOp'-', $2), prec:'UNARY\_MATH'o'+ Expression', (-\>newOp'+', $2), prec:'UNARY\_MATH'o'-- SimpleAssignable',-\>newOp'--', $2o'++ SimpleAssignable',-\>newOp'++', $2o'SimpleAssignable --',-\>newOp'--', $1,null,trueo'SimpleAssignable ++',-\>newOp'++', $1,null,true
o'Expression ?',-\>newExistence $1o'Expression + Expression',-\>newOp'+', $1, $3o'Expression - Expression',-\>newOp'-', $1, $3o'Expression MATH Expression',-\>newOp $2, $1, $3o'Expression \*\* Expression',-\>newOp $2, $1, $3o'Expression SHIFT Expression',-\>newOp $2, $1, $3o'Expression COMPARE Expression',-\>newOp $2, $1, $3o'Expression & Expression',-\>newOp $2, $1, $3o'Expression ^ Expression',-\>newOp $2, $1, $3o'Expression | Expression',-\>newOp $2, $1, $3o'Expression && Expression',-\>newOp $2, $1, $3o'Expression || Expression',-\>newOp $2, $1, $3o'Expression BIN? Expression',-\>newOp $2, $1, $3o'Expression RELATION Expression',-\>if$2.charAt(0)is'!'newOp($2[1..], $1, $3).invert()elsenewOp $2, $1, $3o'SimpleAssignable COMPOUND\_ASSIGN Expression',-\>newAssign $1, $3, $2o'SimpleAssignable COMPOUND\_ASSIGN INDENT Expression OUTDENT',-\>newAssign $1, $4, $2o'SimpleAssignable COMPOUND\_ASSIGN TERMINATOR Expression',-\>newAssign $1, $4, $2o'SimpleAssignable EXTENDS Expression',-\>newExtends $1, $3]
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','\*\*']
['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']
]
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 = []forname, alternativesofgrammar
grammar[name] =foraltinalternativesfortokeninalt[0].split' 'tokens.push tokenunlessgrammar[token]
alt[1] ="return #{alt[1]}"ifnameis'Root'alt
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).
exports.parser =newParser
tokens : tokens.join' 'bnf : grammar
operators : operators.reverse()
startSymbol :'Root'