Back to Coffeescript

grammar.coffee

docs/v2/annotated-source/grammar.html

2.7.041.1 KB
Original Source

browser.coffeecake.coffeecoffeescript.coffeecommand.coffeegrammar.coffeehelpers.coffeeindex.coffeelexer.coffeenodes.coffeeoptparse.coffeeregister.coffeerepl.coffeerewriter.coffeescope.litcoffeesourcemap.litcoffee

grammar.coffee

§

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'

§

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. 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(' ').lengthifaction

§

This code block does string replacements in the generated parser.js file, replacing the calls to the LOC function and other strings as listed below.

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 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 = (first, last, forceUpdateLocation = yes) -\>"yy.addDataToNode(yy, @#{first}, #{if first[0] is '$' then '$$' else '$'}#{first}, #{if last then "@#{last}, #{if last[0] is '$' then '$$' else '$'}#{last}" else 'null, null'}, #{if forceUpdateLocation then 'true' else 'false'})"

§

This code replaces the calls to LOC with the yy.addDataToNode string defined above. The LOC function, when used below in the grammar rules, is used to make sure that newly created node class objects get correct location data assigned to them. By default, the grammar will assign the location data spanned by all of the tokens on the left (e.g. a string such as 'Body TERMINATOR Line') to the “top-level” node returned by the grammar rule (the function on the right). But for “inner” node class objects created by grammar rules, they won’t get correct location data assigned to them without adding LOC.

§

For example, consider the grammar rule 'NEW_TARGET . Property', which is handled by a function that returns new MetaProperty LOC(1)(new IdentifierLiteral $1), LOC(3)(new Access $3). The 1 in LOC(1) refers to the first token (NEW_TARGET) and the 3 in LOC(3) refers to the third token (Property). In order for the new IdentifierLiteral to get assigned the location data corresponding to new in the source code, we use LOC(1)(new IdentifierLiteral ...) to mean “assign the location data of the first token of this grammar rule (NEW_TARGET) to this new IdentifierLiteral”. The LOC(3) means “assign the location data of the third token of this grammar rule (Property) to this new Access”.

returnsLoc =/^LOC/.test action
    action = action.replace/LOC\(([0-9]\*)\)/g, getAddDataToNodeFunctionString('$1')

§

A call to LOC with two arguments, e.g. LOC(2,4), sets the location data for the generated node on both of the referenced tokens (the second and fourth in this example).

action = action.replace/LOC\(([0-9]\*),\s\*([0-9]\*)\)/g, getAddDataToNodeFunctionString('$1','$2')
    performActionFunctionString ="$$ = #{getAddDataToNodeFunctionString(1, patternCount, not returnsLoc)}(#{action});"elseperformActionFunctionString ='$$ = $1;'[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'',-\>newRootnewBlock
    o'Body',-\>newRoot $1]

§

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. FuncDirective 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',-\>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'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',-\>newOp $1,newValuenewLiteral''o'YIELD Expression',-\>newOp $1, $2o'YIELD INDENT Object OUTDENT',-\>newOp $1, $3o'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 $1o'JSX\_TAG',-\>newJSXTag $1.toString(),
                                                     tagNameLocationData: $1.tagNameToken[2]
                                                     closingTagOpeningBracketLocationData: $1.closingTagOpeningBracketToken?[2]
                                                     closingTagSlashLocationData: $1.closingTagSlashToken?[2]
                                                     closingTagNameLocationData: $1.closingTagNameToken?[2]
                                                     closingTagClosingBracketLocationData: $1.closingTagClosingBracketToken?[2]
  ]

  Property: [
    o'PROPERTY',-\>newPropertyName $1.toString()
  ]

§

Alphanumerics are separated from the other Literal matchers because they can also serve as keys in object literals.

AlphaNumeric: [
    o'NUMBER',-\>newNumberLiteral $1.toString(), parsedValue: $1.parsedValue
    o'String']

  String: [
    o'STRING',-\>newStringLiteral(
        $1.slice1,-1# strip artificial quotes and unwrap to primitive stringquote: $1.quote
        initialChunk: $1.initialChunk
        finalChunk: $1.finalChunk
        indent: $1.indent
        double: $1.double
        heregex: $1.heregex
      )
    o'STRING\_START Interpolations STRING\_END',-\>newStringWithInterpolations Block.wrap($2), quote: $1.quote, startQuote: LOC(1)(newLiteral $1.toString())
  ]

  Interpolations: [
    o'InterpolationChunk',-\>[$1]
    o'Interpolations InterpolationChunk',-\>$1.concat $2]

  InterpolationChunk: [
    o'INTERPOLATION\_START Body INTERPOLATION\_END',-\>newInterpolation $2o'INTERPOLATION\_START INDENT Body OUTDENT INTERPOLATION\_END',-\>newInterpolation $3o'INTERPOLATION\_START INTERPOLATION\_END',-\>newInterpolation
    o'String',-\>$1]

§

The .toString() calls here and elsewhere are to convert String objects back to primitive strings now that we’ve retrieved stowaway extra properties

Regex: [
    o'REGEX',-\>newRegexLiteral $1.toString(), delimiter: $1.delimiter, heregexCommentTokens: $1.heregexCommentTokens
    o'REGEX\_START Invocation REGEX\_END',-\>newRegexWithInterpolations $2, heregexCommentTokens: $3.heregexCommentTokens
  ]

§

All of our immediate values. Generally these can be passed straight through and printed to JavaScript.

Literal: [
    o'AlphaNumeric'o'JS',-\>newPassthroughLiteral $1.toString(), here: $1.here, generated: $1.generated
    o'Regex'o'UNDEFINED',-\>newUndefinedLiteral $1o'NULL',-\>newNullLiteral $1o'BOOL',-\>newBooleanLiteral $1.toString(), originalValue: $1.original
    o'INFINITY',-\>newInfinityLiteral $1.toString(), originalValue: $1.original
    o'NAN',-\>newNaNLiteral $1]

§

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'ObjRestValue'o'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)
  ]

  SimpleObjAssignable: [
    o'Identifier'o'Property'o'ThisProperty']

  ObjAssignable: [
    o'SimpleObjAssignable'o'[Expression]',-\>newValuenewComputedPropertyName $2o'@ [Expression]',-\>newValue LOC(1)(newThisLiteral $1), [LOC(3)(newComputedPropertyName($3))],'this'o'AlphaNumeric']

§

Object literal spread properties.

ObjRestValue: [
    o'SimpleObjAssignable ...',-\>newSplatnewValue $1o'... SimpleObjAssignable',-\>newSplatnewValue($2), postfix:noo'ObjSpreadExpr ...',-\>newSplat $1o'... ObjSpreadExpr',-\>newSplat $2, postfix:no]

  ObjSpreadExpr: [
    o'ObjSpreadIdentifier'o'Object'o'Parenthetical'o'Super'o'This'o'SUPER OptFuncExist Arguments',-\>newSuperCall LOC(1)(newSuper), $3, $2.soak, $1o'DYNAMIC\_IMPORT Arguments',-\>newDynamicImportCall LOC(1)(newDynamicImport), $2o'SimpleObjAssignable OptFuncExist Arguments',-\>newCall (newValue $1), $3, $2.soak
    o'ObjSpreadExpr OptFuncExist Arguments',-\>newCall $1, $3, $2.soak
  ]

  ObjSpreadIdentifier: [
    o'SimpleObjAssignable Accessor',-\>(newValue $1).add $2o'ObjSpreadExpr Accessor',-\>(newValue $1).add $2]

§

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 $3, returnKeyword: LOC(2)(newLiteral $2)
    o'YIELD RETURN',-\>newYieldReturnnull, returnKeyword: LOC(2)(newLiteral $2)
  ]

  AwaitReturn: [
    o'AWAIT RETURN Expression',-\>newAwaitReturn $3, returnKeyword: LOC(2)(newLiteral $2)
    o'AWAIT RETURN',-\>newAwaitReturnnull, returnKeyword: LOC(2)(newLiteral $2)
  ]

§

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, $4, LOC(1)(newLiteral $1)
    o'FuncGlyph Block',-\>newCode [], $2, $1]

§

The Codeline is the Code node with Line instead of indented Block.

CodeLine: [
    o'PARAM\_START ParamList PARAM\_END FuncGlyph Line',-\>newCode $2, LOC(5)(Block.wrap [$5]), $4,
                                                              LOC(1)(newLiteral $1)
    o'FuncGlyph Line',-\>newCode [], 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'-\>',-\>newFuncGlyph $1o'=\>',-\>newFuncGlyph $1]

§

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',-\>newParam $2,null, postfix:noo'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 $1o'... Expression',-\>newSplat $2, {postfix:no}
  ]

§

Variables and properties that can be assigned to.

SimpleAssignable: [
    o'Identifier',-\>newValue $1o'Value Accessor',-\>$1.add $2o'Code Accessor',-\>newValue($1).add $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'Invocation',-\>newValue $1o'DoIife',-\>newValue $1o'This'o'Super',-\>newValue $1o'MetaProperty',-\>newValue $1]

§

A super-based expression that can be used as a value.

Super: [
    o'SUPER . Property',-\>newSuper LOC(3)(newAccess $3), LOC(1)(newLiteral $1)
    o'SUPER INDEX\_START Expression INDEX\_END',-\>newSuper LOC(3)(newIndex $3), LOC(1)(newLiteral $1)
    o'SUPER INDEX\_START INDENT Expression OUTDENT INDEX\_END',-\>newSuper LOC(4)(newIndex $4), LOC(1)(newLiteral $1)
  ]

§

A “meta-property” access e.g. new.target or import.meta, where something that looks like a property is referenced on a keyword.

MetaProperty: [
    o'NEW\_TARGET . Property',-\>newMetaProperty LOC(1)(newIdentifierLiteral $1), LOC(3)(newAccess $3)
    o'IMPORT\_META . Property',-\>newMetaProperty LOC(1)(newIdentifierLiteral $1), LOC(3)(newAccess $3)
  ]

§

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:yeso':: Property',-\>[LOC(1)(newAccessnewPropertyName('prototype'), shorthand:yes), LOC(2)(newAccess $2)]
    o'?:: Property',-\>[LOC(1)(newAccessnewPropertyName('prototype'), shorthand:yes, soak:yes), LOC(2)(newAccess $2)]
    o'::',-\>newAccessnewPropertyName('prototype'), shorthand:yeso'?::',-\>newAccessnewPropertyName('prototype'), shorthand:yes, soak:yeso'Index']

§

Indexing into an object or array using bracket notation.

Index: [
    o'INDEX\_START IndexValue INDEX\_END',-\>$2o'INDEX\_START INDENT IndexValue OUTDENT INDEX\_END',-\>$3o'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 String ASSERT Object',-\>newImportDeclarationnull, $2, $4o'IMPORT ImportDefaultSpecifier FROM String',-\>newImportDeclarationnewImportClause($2,null), $4o'IMPORT ImportDefaultSpecifier FROM String ASSERT Object',-\>newImportDeclarationnewImportClause($2,null), $4, $6o'IMPORT ImportNamespaceSpecifier FROM String',-\>newImportDeclarationnewImportClause(null, $2), $4o'IMPORT ImportNamespaceSpecifier FROM String ASSERT Object',-\>newImportDeclarationnewImportClause(null, $2), $4, $6o'IMPORT { } FROM String',-\>newImportDeclarationnewImportClause(null,newImportSpecifierList []), $5o'IMPORT { } FROM String ASSERT Object',-\>newImportDeclarationnewImportClause(null,newImportSpecifierList []), $5, $7o'IMPORT { ImportSpecifierList OptComma } FROM String',-\>newImportDeclarationnewImportClause(null,newImportSpecifierList $3), $7o'IMPORT { ImportSpecifierList OptComma } FROM String ASSERT Object',-\>newImportDeclarationnewImportClause(null,newImportSpecifierList $3), $7, $9o'IMPORT ImportDefaultSpecifier , ImportNamespaceSpecifier FROM String',-\>newImportDeclarationnewImportClause($2, $4), $6o'IMPORT ImportDefaultSpecifier , ImportNamespaceSpecifier FROM String ASSERT Object',-\>newImportDeclarationnewImportClause($2, $4), $6, $8o'IMPORT ImportDefaultSpecifier , { ImportSpecifierList OptComma } FROM String',-\>newImportDeclarationnewImportClause($2,newImportSpecifierList $5), $9o'IMPORT ImportDefaultSpecifier , { ImportSpecifierList OptComma } FROM String ASSERT Object',-\>newImportDeclarationnewImportClause($2,newImportSpecifierList $5), $9, $11]

  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',-\>newImportSpecifier LOC(1)(newDefaultLiteral $1)
    o'DEFAULT AS Identifier',-\>newImportSpecifier LOC(1)(newDefaultLiteral($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',-\>newExportNamedDeclaration LOC(2,4)(newAssign $2, $4,null,
                                                                                                      moduleDeclaration:'export')
    o'EXPORT Identifier = TERMINATOR Expression',-\>newExportNamedDeclaration LOC(2,5)(newAssign $2, $5,null,
                                                                                                      moduleDeclaration:'export')
    o'EXPORT Identifier = INDENT Expression OUTDENT',-\>newExportNamedDeclaration LOC(2,6)(newAssign $2, $5,null,
                                                                                                      moduleDeclaration:'export')
    o'EXPORT DEFAULT Expression',-\>newExportDefaultDeclaration $3o'EXPORT DEFAULT INDENT Object OUTDENT',-\>newExportDefaultDeclarationnewValue $4o'EXPORT EXPORT\_ALL FROM String',-\>newExportAllDeclarationnewLiteral($2), $4o'EXPORT EXPORT\_ALL FROM String ASSERT Object',-\>newExportAllDeclarationnewLiteral($2), $4, $6o'EXPORT { } FROM String',-\>newExportNamedDeclarationnewExportSpecifierList([]), $5o'EXPORT { } FROM String ASSERT Object',-\>newExportNamedDeclarationnewExportSpecifierList([]), $5, $7o'EXPORT { ExportSpecifierList OptComma } FROM String',-\>newExportNamedDeclarationnewExportSpecifierList($3), $7o'EXPORT { ExportSpecifierList OptComma } FROM String ASSERT Object',-\>newExportNamedDeclarationnewExportSpecifierList($3), $7, $9]

  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, LOC(3)(newDefaultLiteral $3)
    o'DEFAULT',-\>newExportSpecifier LOC(1)(newDefaultLiteral $1)
    o'DEFAULT AS Identifier',-\>newExportSpecifier LOC(1)(newDefaultLiteral($1)), $3]

§

Ordinary function invocation, or a chained series of calls.

Invocation: [
    o'Value OptFuncExist String',-\>newTaggedTemplateCall $1, $3, $2.soak
    o'Value OptFuncExist Arguments',-\>newCall $1, $3, $2.soak
    o'SUPER OptFuncExist Arguments',-\>newSuperCall LOC(1)(newSuper), $3, $2.soak, $1o'DYNAMIC\_IMPORT Arguments',-\>newDynamicImportCall LOC(1)(newDynamicImport), $2]

§

An optional existence check on a function.

OptFuncExist: [
    o'',-\>soak:noo'FUNC\_EXIST',-\>soak:yes]

§

The list of arguments to a function call.

Arguments: [
    o'CALL\_START CALL\_END',-\>[]
    o'CALL\_START ArgList OptComma CALL\_END',-\>$2.implicit = $1.generated; $2]

§

A reference to the this current object.

This: [
    o'THIS',-\>newValuenewThisLiteral $1o'@',-\>newValuenewThisLiteral $1]

§

A reference to a property on this.

ThisProperty: [
    o'@ Property',-\>newValue LOC(1)(newThisLiteral $1), [LOC(2)(newAccess($2))],'this']

§

The array literal.

Array: [
    o'[]',-\>newArr []
    o'[Elisions]',-\>newArr $2o'[ArgElisionList OptElisions]',-\>newArr [].concat $2, $3]

§

Inclusive and exclusive range dots.

RangeDots: [
    o'..',-\>exclusive:noo'...',-\>exclusive:yes]

§

The CoffeeScript range literal.

Range: [
    o'[Expression RangeDots Expression]',-\>newRange $2, $4,if$3.exclusivethen'exclusive'else'inclusive'o'[ExpressionLine RangeDots Expression]',-\>newRange $2, $4,if$3.exclusivethen'exclusive'else'inclusive']

§

Array slice literals.

Slice: [
    o'Expression RangeDots Expression',-\>newRange $1, $3,if$2.exclusivethen'exclusive'else'inclusive'o'Expression RangeDots',-\>newRange $1,null,if$2.exclusivethen'exclusive'else'inclusive'o'ExpressionLine RangeDots Expression',-\>newRange $1, $3,if$2.exclusivethen'exclusive'else'inclusive'o'ExpressionLine RangeDots',-\>newRange $1,null,if$2.exclusivethen'exclusive'else'inclusive'o'RangeDots Expression',-\>newRangenull, $2,if$1.exclusivethen'exclusive'else'inclusive'o'RangeDots',-\>newRangenull,null,if$1.exclusivethen'exclusive'else'inclusive']

§

The ArgList is the list of objects passed into a function call (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'ExpressionLine'o'Splat'o'...',-\>newExpansion
  ]

§

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',-\>$1.concat $3o'ArgElisionList OptComma TERMINATOR ArgElision',-\>$1.concat $4o'INDENT ArgElisionList OptElisions OUTDENT',-\>$2.concat $3o'ArgElisionList OptElisions INDENT ArgElisionList OptElisions OUTDENT',-\>$1.concat $2, $4, $5]

  ArgElision: [
    o'Arg',-\>[$1]
    o'Elisions Arg',-\>$1.concat $2]

  OptElisions: [
    o'OptComma',-\>[]
    o', Elisions',-\>[].concat $2]

  Elisions: [
    o'Elision',-\>[$1]
    o'Elisions Elision',-\>$1.concat $2]

  Elision: [
    o',',-\>newElision
    o'Elision TERMINATOR',-\>$1]

§

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',-\>[].concat $1, $3o'SimpleArgs , ExpressionLine',-\>[].concat $1, $3]

§

The variants of try/catch/finally exception handling blocks.

Try: [
    o'TRY Block',-\>newTry $2o'TRY Block Catch',-\>newTry $2, $3o'TRY Block FINALLY Block',-\>newTry $2,null, $4, LOC(3)(newLiteral $3)
    o'TRY Block Catch FINALLY Block',-\>newTry $2, $3, $5, LOC(4)(newLiteral $4)
  ]

§

A catch clause names its error and runs a block of code.

Catch: [
    o'CATCH Identifier Block',-\>newCatch $3, $2o'CATCH Object Block',-\>newCatch $3, LOC(2)(newValue($2))
    o'CATCH Block',-\>newCatch $2]

§

Throw an exception object.

Throw: [
    o'THROW Expression',-\>newThrow $2o'THROW INDENT Object OUTDENT',-\>newThrownewValue $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 )',-\>newParens $2o'( INDENT Body OUTDENT )',-\>newParens $3]

§

The condition portion of a while loop.

WhileLineSource: [
    o'WHILE ExpressionLine',-\>newWhile $2o'WHILE ExpressionLine WHEN ExpressionLine',-\>newWhile $2, guard: $4o'UNTIL ExpressionLine',-\>newWhile $2, invert:trueo'UNTIL ExpressionLine WHEN ExpressionLine',-\>newWhile $2, invert:true, guard: $4]

  WhileSource: [
    o'WHILE Expression',-\>newWhile $2o'WHILE Expression WHEN Expression',-\>newWhile $2, guard: $4o'WHILE ExpressionLine WHEN Expression',-\>newWhile $2, guard: $4o'UNTIL Expression',-\>newWhile $2, invert:trueo'UNTIL Expression WHEN Expression',-\>newWhile $2, invert:true, guard: $4o'UNTIL ExpressionLine 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'WhileLineSource Block',-\>$1.addBody $2o'Statement WhileSource',-\>(Object.assign $2, postfix:yes).addBody LOC(1) Block.wrap([$1])
    o'Expression WhileSource',-\>(Object.assign $2, postfix:yes).addBody LOC(1) Block.wrap([$1])
    o'Loop',-\>$1]

  Loop: [
    o'LOOP Block',-\>newWhile(LOC(1)(newBooleanLiteral'true'), isLoop:yes).addBody $2o'LOOP Expression',-\>newWhile(LOC(1)(newBooleanLiteral'true'), isLoop:yes).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',-\>$2.postfix =yes; $2.addBody $1o'Expression ForBody',-\>$2.postfix =yes; $2.addBody $1o'ForBody Block',-\>$1.addBody $2o'ForLineBody Block',-\>$1.addBody $2]

  ForBody: [
    o'FOR Range',-\>newFor [], source: (LOC(2)newValue($2))
    o'FOR Range BY Expression',-\>newFor [], source: (LOC(2)newValue($2)), step: $4o'ForStart ForSource',-\>$1.addSource $2]

  ForLineBody: [
    o'FOR Range BY ExpressionLine',-\>newFor [], source: (LOC(2)newValue($2)), step: $4o'ForStart ForLineSource',-\>$1.addSource $2]

  ForStart: [
    o'FOR ForVariables',-\>newFor [], name: $2[0], index: $2[1]
    o'FOR AWAIT ForVariables',-\>[name, index] = $3newFor [], {name, index, await:yes, awaitTag: (LOC(2)newLiteral($2))}
    o'FOR OWN ForVariables',-\>[name, index] = $3newFor [], {name, index, own:yes, ownTag: (LOC(2)newLiteral($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',-\>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'FORIN ExpressionLine WHEN Expression',-\>source: $2, guard: $4o'FOROF Expression WHEN Expression',-\>source: $2, guard: $4, object:yeso'FOROF ExpressionLine WHEN Expression',-\>source: $2, guard: $4, object:yeso'FORIN Expression BY Expression',-\>source: $2, step: $4o'FORIN ExpressionLine BY Expression',-\>source: $2, step: $4o'FORIN Expression WHEN Expression BY Expression',-\>source: $2, guard: $4, step: $6o'FORIN ExpressionLine WHEN Expression BY Expression',-\>source: $2, guard: $4, step: $6o'FORIN Expression WHEN ExpressionLine BY Expression',-\>source: $2, guard: $4, step: $6o'FORIN ExpressionLine WHEN ExpressionLine BY Expression',-\>source: $2, guard: $4, step: $6o'FORIN Expression BY Expression WHEN Expression',-\>source: $2, step: $4, guard: $6o'FORIN ExpressionLine BY Expression WHEN Expression',-\>source: $2, step: $4, guard: $6o'FORIN Expression BY ExpressionLine WHEN Expression',-\>source: $2, step: $4, guard: $6o'FORIN ExpressionLine BY ExpressionLine WHEN Expression',-\>source: $2, step: $4, guard: $6o'FORFROM Expression',-\>source: $2, from:yeso'FORFROM Expression WHEN Expression',-\>source: $2, guard: $4, from:yeso'FORFROM ExpressionLine WHEN Expression',-\>source: $2, guard: $4, from:yes]

  ForLineSource: [
    o'FORIN ExpressionLine',-\>source: $2o'FOROF ExpressionLine',-\>source: $2, object:yeso'FORIN Expression WHEN ExpressionLine',-\>source: $2, guard: $4o'FORIN ExpressionLine WHEN ExpressionLine',-\>source: $2, guard: $4o'FOROF Expression WHEN ExpressionLine',-\>source: $2, guard: $4, object:yeso'FOROF ExpressionLine WHEN ExpressionLine',-\>source: $2, guard: $4, object:yeso'FORIN Expression BY ExpressionLine',-\>source: $2, step: $4o'FORIN ExpressionLine BY ExpressionLine',-\>source: $2, step: $4o'FORIN Expression WHEN Expression BY ExpressionLine',-\>source: $2, guard: $4, step: $6o'FORIN ExpressionLine WHEN Expression BY ExpressionLine',-\>source: $2, guard: $4, step: $6o'FORIN Expression WHEN ExpressionLine BY ExpressionLine',-\>source: $2, guard: $4, step: $6o'FORIN ExpressionLine WHEN ExpressionLine BY ExpressionLine',-\>source: $2, guard: $4, step: $6o'FORIN Expression BY Expression WHEN ExpressionLine',-\>source: $2, step: $4, guard: $6o'FORIN ExpressionLine BY Expression WHEN ExpressionLine',-\>source: $2, step: $4, guard: $6o'FORIN Expression BY ExpressionLine WHEN ExpressionLine',-\>source: $2, step: $4, guard: $6o'FORIN ExpressionLine BY ExpressionLine WHEN ExpressionLine',-\>source: $2, step: $4, guard: $6o'FORFROM ExpressionLine',-\>source: $2, from:yeso'FORFROM Expression WHEN ExpressionLine',-\>source: $2, guard: $4, from:yeso'FORFROM ExpressionLine WHEN ExpressionLine',-\>source: $2, guard: $4, from:yes]

  Switch: [
    o'SWITCH Expression INDENT Whens OUTDENT',-\>newSwitch $2, $4o'SWITCH ExpressionLine INDENT Whens OUTDENT',-\>newSwitch $2, $4o'SWITCH Expression INDENT Whens ELSE Block OUTDENT',-\>newSwitch $2, $4, LOC(5,6) $6o'SWITCH ExpressionLine INDENT Whens ELSE Block OUTDENT',-\>newSwitch $2, $4, LOC(5,6) $6o'SWITCH INDENT Whens OUTDENT',-\>newSwitchnull, $3o'SWITCH INDENT Whens ELSE Block OUTDENT',-\>newSwitchnull, $3, LOC(4,5) $5]

  Whens: [
    o'When',-\>[$1]
    o'Whens When',-\>$1.concat $2]

§

An individual When clause, with action.

When: [
    o'LEADING\_WHEN SimpleArgs Block',-\>newSwitchWhen $2, $3o'LEADING\_WHEN SimpleArgs Block TERMINATOR',-\>LOC(1,3)newSwitchWhen $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, postfix:trueo'Expression POST\_IF Expression',-\>newIf $3, LOC(1)(Block.wrap [$1]), type: $2, postfix:true]

  IfBlockLine: [
    o'IF ExpressionLine Block',-\>newIf $2, $3, type: $1o'IfBlockLine ELSE IF ExpressionLine Block',-\>$1.addElse LOC(3,5)newIf $4, $5, type: $3]

  IfLine: [
    o'IfBlockLine'o'IfBlockLine ELSE Block',-\>$1.addElse $3o'Statement POST\_IF ExpressionLine',-\>newIf $3, LOC(1)(Block.wrap [$1]), type: $2, postfix:trueo'Expression POST\_IF ExpressionLine',-\>newIf $3, LOC(1)(Block.wrap [$1]), type: $2, postfix: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',-\>newOp $1, $2o'DO ExpressionLine',-\>newOp $1, $2o'DO\_IIFE CodeLine',-\>newOp $1, $2]

  Operation: [
    o'UNARY Expression',-\>newOp $1.toString(), $2,undefined,undefined, originalOperator: $1.original
    o'DO Expression',-\>newOp $1, $2o'UNARY\_MATH Expression',-\>newOp $1, $2o'- Expression', (-\>newOp'-', $2), prec:'UNARY\_MATH'o'+ Expression', (-\>newOp'+', $2), prec:'UNARY\_MATH'o'AWAIT Expression',-\>newOp $1, $2o'AWAIT INDENT Object OUTDENT',-\>newOp $1, $3o'-- SimpleAssignable',-\>newOp'--', $2o'++ SimpleAssignable',-\>newOp'++', $2o'SimpleAssignable --',-\>newOp'--', $1,null,trueo'SimpleAssignable ++',-\>newOp'++', $1,null,true

§

The existential operator.

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.toString(), $1, $3,undefined, originalOperator: $2.original
    o'Expression & Expression',-\>newOp $2, $1, $3o'Expression ^ Expression',-\>newOp $2, $1, $3o'Expression | Expression',-\>newOp $2, $1, $3o'Expression && Expression',-\>newOp $2.toString(), $1, $3,undefined, originalOperator: $2.original
    o'Expression || Expression',-\>newOp $2.toString(), $1, $3,undefined, originalOperator: $2.original
    o'Expression BIN? Expression',-\>newOp $2, $1, $3o'Expression RELATION Expression',-\>newOp $2.toString(), $1, $3,undefined, invertOperator: $2.invert?.original ? $2.invert

    o'SimpleAssignable COMPOUND\_ASSIGN Expression',-\>newAssign $1, $3, $2.toString(), originalContext: $2.original
    o'SimpleAssignable COMPOUND\_ASSIGN INDENT Expression OUTDENT',-\>newAssign $1, $4, $2.toString(), originalContext: $2.original
    o'SimpleAssignable COMPOUND\_ASSIGN TERMINATOR Expression',-\>newAssign $1, $4, $2.toString(), originalContext: $2.original
  ]

  DoIife: [
    o'DO\_IIFE Code',-\>newOp $1, $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 = [
  ['right','DO\_IIFE']
  ['left','.','?.','::','?::']
  ['left','CALL\_START','CALL\_END']
  ['nonassoc','++','--']
  ['left','?']
  ['right','UNARY','DO']
  ['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 = []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'