• 跳转到 … +
    browser.coffee cake.coffee coffee-script.coffee command.coffee grammar.coffee helpers.coffee index.coffee lexer.coffee nodes.coffee optparse.coffee register.coffee repl.coffee rewriter.coffee scope.litcoffee sourcemap.litcoffee
  • grammar.coffee

  • ¶

    CoffeeScript 解析器是由 Jison 从这个语法文件生成的。Jison 是一个自底向上的解析器生成器,类似于 Bison,用 JavaScript 实现。它可以识别 LALR(1), LR(0), SLR(1), 和 LR(1) 类型的语法。为了创建 Jison 解析器,我们在左侧列出要匹配的模式,在右侧列出要执行的操作(通常是创建语法树节点)。解析器运行时,它会从左到右将标记从我们的标记流中移出,并 尝试将 标记序列与下面的规则匹配。当可以进行匹配时,它会缩减为 非终结符(顶部的封闭名称),然后我们从那里继续。

    如果你运行 cake build:parser 命令,Jison 会从我们的规则中构建一个解析表,并将其保存到 lib/parser.js 中。

  • ¶

    唯一的依赖是 Jison.Parser。

    {Parser} = require 'jison'
  • ¶

    Jison DSL

  • ¶
  • ¶

    由于无论如何我们都会被 Jison 包裹在一个函数中,如果我们的操作立即返回一个值,我们可以通过删除函数包装器并直接返回该值来进行优化。

    unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
  • ¶

    我们方便的 Jison 语法生成 DSL,感谢 Tim Caswell。对于语法中的每个规则,我们传递定义模式的字符串、要运行的操作以及额外的选项(可选)。如果没有指定操作,我们只需传递前一个非终结符的值。

    o = (patternString, action, options) ->
      patternString = patternString.replace /\s{2,}/g, ' '
      patternCount = patternString.split(' ').length
      return [patternString, '$$ = $1;', options] unless action
      action = if match = unwrap.exec action then match[1] else "(#{action}())"
  • ¶

    我们需要的全部运行时函数都定义在“yy”上

      action = action.replace /\bnew /g, '$&yy.'
      action = action.replace /\b(?:Block\.wrap|extend)\b/g, 'yy.$&'
  • ¶

    返回一个函数,该函数将位置数据添加到传入的第一个参数,并返回该参数。如果参数不是节点,它将直接通过不受影响。

      addLocationDataFn = (first, last) ->
        if not last
          "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]
  • ¶

    语法规则

  • ¶
  • ¶

    在所有以下规则中,您将看到非终结符的名称作为替代匹配列表的键。对于每个匹配的操作,美元符号变量由 Jison 提供,作为对它们数字位置的值的引用,因此在这个规则中

    "Expression UNLESS Expression"
    

    $1 将是第一个 Expression 的值,$2 将是 UNLESS 终结符的标记,$3 将是第二个 Expression 的值。

    grammar =
  • ¶

    Root 是语法树中的顶层节点。由于我们自底向上解析,所有解析都必须在此结束。

      Root: [
        o '',                                       -> new Block
        o 'Body'
      ]
  • ¶

    任何语句和表达式的列表,用换行符或分号分隔。

      Body: [
        o 'Line',                                   -> Block.wrap [$1]
        o 'Body TERMINATOR Line',                   -> $1.push $3
        o 'Body TERMINATOR'
      ]
  • ¶

    块和语句,它们构成了主体中的一行。YieldReturn 是一个语句,但不包含在 Statement 中,因为这会导致语法不明确。

      Line: [
        o 'Expression'
        o 'Statement'
        o 'YieldReturn'
      ]
  • ¶

    不能作为表达式的纯语句。

      Statement: [
        o 'Return'
        o 'Comment'
        o 'STATEMENT',                              -> new StatementLiteral $1
        o 'Import'
        o 'Export'
      ]
  • ¶

    我们语言中的所有不同类型的表达式。CoffeeScript 的基本单位是 Expression - 所有可以作为表达式的都是一个。块充当许多其他规则的构建块,使它们有点循环。

      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',                                  -> new Op $1, new Value new Literal ''
        o 'YIELD Expression',                       -> new Op $1, $2
        o 'YIELD FROM Expression',                  -> new Op $1.concat($2), $3
      ]
  • ¶

    一个缩进的表达式块。请注意,Rewriter 会通过调整标记流,将一些后缀形式转换为块。

      Block: [
        o 'INDENT OUTDENT',                         -> new Block
        o 'INDENT Body OUTDENT',                    -> $2
      ]
    
      Identifier: [
        o 'IDENTIFIER',                             -> new IdentifierLiteral $1
      ]
    
      Property: [
        o 'PROPERTY',                               -> new PropertyName $1
      ]
  • ¶

    字母数字与其他 Literal 匹配器分开,因为它们也可以用作对象文字中的键。

      AlphaNumeric: [
        o 'NUMBER',                                 -> new NumberLiteral $1
        o 'String'
      ]
    
      String: [
        o 'STRING',                                 -> new StringLiteral $1
        o 'STRING_START Body STRING_END',           -> new StringWithInterpolations $2
      ]
    
      Regex: [
        o 'REGEX',                                  -> new RegexLiteral $1
        o 'REGEX_START Invocation REGEX_END',       -> new RegexWithInterpolations $2.args
      ]
  • ¶

    我们所有直接的值。通常,这些可以直接传递并打印到 JavaScript。

      Literal: [
        o 'AlphaNumeric'
        o 'JS',                                     -> new PassthroughLiteral $1
        o 'Regex'
        o 'UNDEFINED',                              -> new UndefinedLiteral
        o 'NULL',                                   -> new NullLiteral
        o 'BOOL',                                   -> new BooleanLiteral $1
        o 'INFINITY',                               -> new InfinityLiteral $1
        o 'NAN',                                    -> new NaNLiteral
      ]
  • ¶

    将变量、属性或索引分配给值。

      Assign: [
        o 'Assignable = Expression',                -> new Assign $1, $3
        o 'Assignable = TERMINATOR Expression',     -> new Assign $1, $4
        o 'Assignable = INDENT Expression OUTDENT', -> new Assign $1, $4
      ]
  • ¶

    在对象文字中发生的赋值。与普通的 Assign 的区别在于,这些允许数字和字符串作为键。

      AssignObj: [
        o 'ObjAssignable',                          -> new Value $1
        o 'ObjAssignable : Expression',             -> new Assign LOC(1)(new Value $1), $3, 'object',
                                                                  operatorToken: LOC(2)(new Literal $2)
        o 'ObjAssignable :
           INDENT Expression OUTDENT',              -> new Assign LOC(1)(new Value $1), $4, 'object',
                                                                  operatorToken: LOC(2)(new Literal $2)
        o 'SimpleObjAssignable = Expression',       -> new Assign LOC(1)(new Value $1), $3, null,
                                                                  operatorToken: LOC(2)(new Literal $2)
        o 'SimpleObjAssignable =
           INDENT Expression OUTDENT',              -> new Assign LOC(1)(new Value $1), $4, null,
                                                                  operatorToken: LOC(2)(new Literal $2)
        o 'Comment'
      ]
    
      SimpleObjAssignable: [
        o 'Identifier'
        o 'Property'
        o 'ThisProperty'
      ]
    
      ObjAssignable: [
        o 'SimpleObjAssignable'
        o 'AlphaNumeric'
      ]
  • ¶

    函数体中的 return 语句。

      Return: [
        o 'RETURN Expression',                      -> new Return $2
        o 'RETURN INDENT Object OUTDENT',           -> new Return new Value $3
        o 'RETURN',                                 -> new Return
      ]
    
      YieldReturn: [
        o 'YIELD RETURN Expression',                -> new YieldReturn $3
        o 'YIELD RETURN',                           -> new YieldReturn
      ]
  • ¶

    块注释。

      Comment: [
        o 'HERECOMMENT',                            -> new Comment $1
      ]
  • ¶

    Code 节点是函数文字。它由一个缩进的 Block 块定义,前面是函数箭头,并带有一个可选的参数列表。

      Code: [
        o 'PARAM_START ParamList PARAM_END FuncGlyph Block', -> new Code $2, $5, $4
        o 'FuncGlyph Block',                        -> new Code [], $2, $1
      ]
  • ¶

    CoffeeScript 有两种不同的函数符号。-> 用于普通函数,=> 用于绑定到当前 this 值的函数。

      FuncGlyph: [
        o '->',                                     -> 'func'
        o '=>',                                     -> 'boundfunc'
      ]
  • ¶

    一个可选的尾随逗号。

      OptComma: [
        o ''
        o ','
      ]
  • ¶

    函数接受的参数列表可以是任意长度。

      ParamList: [
        o '',                                       -> []
        o 'Param',                                  -> [$1]
        o 'ParamList , Param',                      -> $1.concat $3
        o 'ParamList OptComma TERMINATOR Param',    -> $1.concat $4
        o 'ParamList OptComma INDENT ParamList OptComma OUTDENT', -> $1.concat $4
      ]
  • ¶

    函数定义中的单个参数可以是普通的,也可以是收集剩余参数的 splat。

      Param: [
        o 'ParamVar',                               -> new Param $1
        o 'ParamVar ...',                           -> new Param $1, null, on
        o 'ParamVar = Expression',                  -> new Param $1, $3
        o '...',                                    -> new Expansion
      ]
  • ¶

    函数参数

      ParamVar: [
        o 'Identifier'
        o 'ThisProperty'
        o 'Array'
        o 'Object'
      ]
  • ¶

    发生在参数列表之外的 splat。

      Splat: [
        o 'Expression ...',                         -> new Splat $1
      ]
  • ¶

    可以分配给的变量和属性。

      SimpleAssignable: [
        o 'Identifier',                             -> new Value $1
        o 'Value Accessor',                         -> $1.add $2
        o 'Invocation Accessor',                    -> new Value $1, [].concat $2
        o 'ThisProperty'
      ]
  • ¶

    所有可以分配给的东西。

      Assignable: [
        o 'SimpleAssignable'
        o 'Array',                                  -> new Value $1
        o 'Object',                                 -> new Value $1
      ]
  • ¶

    可以作为值处理的类型 - 分配给、作为函数调用、索引到、命名为类等。

      Value: [
        o 'Assignable'
        o 'Literal',                                -> new Value $1
        o 'Parenthetical',                          -> new Value $1
        o 'Range',                                  -> new Value $1
        o 'This'
      ]
  • ¶

    访问对象的一般组,通过属性、通过原型或通过数组索引或切片。

      Accessor: [
        o '.  Property',                            -> new Access $2
        o '?. Property',                            -> new Access $2, 'soak'
        o ':: Property',                            -> [LOC(1)(new Access new PropertyName('prototype')), LOC(2)(new Access $2)]
        o '?:: Property',                           -> [LOC(1)(new Access new PropertyName('prototype'), 'soak'), LOC(2)(new Access $2)]
        o '::',                                     -> new Access new PropertyName 'prototype'
        o 'Index'
      ]
  • ¶

    使用方括号表示法索引到对象或数组。

      Index: [
        o 'INDEX_START IndexValue INDEX_END',       -> $2
        o 'INDEX_SOAK  Index',                      -> extend $2, soak : yes
      ]
    
      IndexValue: [
        o 'Expression',                             -> new Index $1
        o 'Slice',                                  -> new Slice $1
      ]
  • ¶

    在 CoffeeScript 中,对象文字只是一个赋值列表。

      Object: [
        o '{ AssignList OptComma }',                -> new Obj $2, $1.generated
      ]
  • ¶

    对象文字中属性的赋值可以用逗号分隔,就像在 JavaScript 中一样,也可以用换行符分隔。

      AssignList: [
        o '',                                                       -> []
        o 'AssignObj',                                              -> [$1]
        o 'AssignList , AssignObj',                                 -> $1.concat $3
        o 'AssignList OptComma TERMINATOR AssignObj',               -> $1.concat $4
        o 'AssignList OptComma INDENT AssignList OptComma OUTDENT', -> $1.concat $4
      ]
  • ¶

    类定义具有可选的原型属性赋值主体,以及对超类的可选引用。

      Class: [
        o 'CLASS',                                           -> new Class
        o 'CLASS Block',                                     -> new Class null, null, $2
        o 'CLASS EXTENDS Expression',                        -> new Class null, $3
        o 'CLASS EXTENDS Expression Block',                  -> new Class null, $3, $4
        o 'CLASS SimpleAssignable',                          -> new Class $2
        o 'CLASS SimpleAssignable Block',                    -> new Class $2, null, $3
        o 'CLASS SimpleAssignable EXTENDS Expression',       -> new Class $2, $4
        o 'CLASS SimpleAssignable EXTENDS Expression Block', -> new Class $2, $4, $5
      ]
    
      Import: [
        o 'IMPORT String',                                                                -> new ImportDeclaration null, $2
        o 'IMPORT ImportDefaultSpecifier FROM String',                                    -> new ImportDeclaration new ImportClause($2, null), $4
        o 'IMPORT ImportNamespaceSpecifier FROM String',                                  -> new ImportDeclaration new ImportClause(null, $2), $4
        o 'IMPORT { } FROM String',                                                       -> new ImportDeclaration new ImportClause(null, new ImportSpecifierList []), $5
        o 'IMPORT { ImportSpecifierList OptComma } FROM String',                          -> new ImportDeclaration new ImportClause(null, new ImportSpecifierList $3), $7
        o 'IMPORT ImportDefaultSpecifier , ImportNamespaceSpecifier FROM String',         -> new ImportDeclaration new ImportClause($2, $4), $6
        o 'IMPORT ImportDefaultSpecifier , { ImportSpecifierList OptComma } FROM String', -> new ImportDeclaration new ImportClause($2, new ImportSpecifierList $5), $9
      ]
    
      ImportSpecifierList: [
        o 'ImportSpecifier',                                                          -> [$1]
        o 'ImportSpecifierList , ImportSpecifier',                                    -> $1.concat $3
        o 'ImportSpecifierList OptComma TERMINATOR ImportSpecifier',                  -> $1.concat $4
        o 'INDENT ImportSpecifierList OptComma OUTDENT',                              -> $2
        o 'ImportSpecifierList OptComma INDENT ImportSpecifierList OptComma OUTDENT', -> $1.concat $4
      ]
    
      ImportSpecifier: [
        o 'Identifier',                             -> new ImportSpecifier $1
        o 'Identifier AS Identifier',               -> new ImportSpecifier $1, $3
        o 'DEFAULT',                                -> new ImportSpecifier new Literal $1
        o 'DEFAULT AS Identifier',                  -> new ImportSpecifier new Literal($1), $3
      ]
    
      ImportDefaultSpecifier: [
        o 'Identifier',                             -> new ImportDefaultSpecifier $1
      ]
    
      ImportNamespaceSpecifier: [
        o 'IMPORT_ALL AS Identifier',               -> new ImportNamespaceSpecifier new Literal($1), $3
      ]
    
      Export: [
        o 'EXPORT { }',                                          -> new ExportNamedDeclaration new ExportSpecifierList []
        o 'EXPORT { ExportSpecifierList OptComma }',             -> new ExportNamedDeclaration new ExportSpecifierList $3
        o 'EXPORT Class',                                        -> new ExportNamedDeclaration $2
        o 'EXPORT Identifier = Expression',                      -> new ExportNamedDeclaration new Assign $2, $4, null,
                                                                                                          moduleDeclaration: 'export'
        o 'EXPORT Identifier = TERMINATOR Expression',           -> new ExportNamedDeclaration new Assign $2, $5, null,
                                                                                                          moduleDeclaration: 'export'
        o 'EXPORT Identifier = INDENT Expression OUTDENT',       -> new ExportNamedDeclaration new Assign $2, $5, null,
                                                                                                          moduleDeclaration: 'export'
        o 'EXPORT DEFAULT Expression',                           -> new ExportDefaultDeclaration $3
        o 'EXPORT EXPORT_ALL FROM String',                       -> new ExportAllDeclaration new Literal($2), $4
        o 'EXPORT { ExportSpecifierList OptComma } FROM String', -> new ExportNamedDeclaration new ExportSpecifierList($3), $7
      ]
    
      ExportSpecifierList: [
        o 'ExportSpecifier',                                                          -> [$1]
        o 'ExportSpecifierList , ExportSpecifier',                                    -> $1.concat $3
        o 'ExportSpecifierList OptComma TERMINATOR ExportSpecifier',                  -> $1.concat $4
        o 'INDENT ExportSpecifierList OptComma OUTDENT',                              -> $2
        o 'ExportSpecifierList OptComma INDENT ExportSpecifierList OptComma OUTDENT', -> $1.concat $4
      ]
    
      ExportSpecifier: [
        o 'Identifier',                             -> new ExportSpecifier $1
        o 'Identifier AS Identifier',               -> new ExportSpecifier $1, $3
        o 'Identifier AS DEFAULT',                  -> new ExportSpecifier $1, new Literal $3
        o 'DEFAULT',                                -> new ExportSpecifier new Literal $1
        o 'DEFAULT AS Identifier',                  -> new ExportSpecifier new Literal($1), $3
      ]
  • ¶

    普通函数调用,或一系列链式调用。

      Invocation: [
        o 'Value OptFuncExist String',              -> new TaggedTemplateCall $1, $3, $2
        o 'Value OptFuncExist Arguments',           -> new Call $1, $3, $2
        o 'Invocation OptFuncExist Arguments',      -> new Call $1, $3, $2
        o 'Super'
      ]
    
      Super: [
        o 'SUPER',                                  -> new SuperCall
        o 'SUPER Arguments',                        -> new SuperCall $2
      ]
  • ¶

    对函数的可选存在检查。

      OptFuncExist: [
        o '',                                       -> no
        o 'FUNC_EXIST',                             -> yes
      ]
  • ¶

    函数调用参数列表。

      Arguments: [
        o 'CALL_START CALL_END',                    -> []
        o 'CALL_START ArgList OptComma CALL_END',   -> $2
      ]
  • ¶

    对当前对象 this 的引用。

      This: [
        o 'THIS',                                   -> new Value new ThisLiteral
        o '@',                                      -> new Value new ThisLiteral
      ]
  • ¶

    对 this 上属性的引用。

      ThisProperty: [
        o '@ Property',                             -> new Value LOC(1)(new ThisLiteral), [LOC(2)(new Access($2))], 'this'
      ]
  • ¶

    数组文字。

      Array: [
        o '[ ]',                                    -> new Arr []
        o '[ ArgList OptComma ]',                   -> new Arr $2
      ]
  • ¶

    包含和排除范围点。

      RangeDots: [
        o '..',                                     -> 'inclusive'
        o '...',                                    -> 'exclusive'
      ]
  • ¶

    CoffeeScript 范围文字。

      Range: [
        o '[ Expression RangeDots Expression ]',    -> new Range $2, $4, $3
      ]
  • ¶

    数组切片文字。

      Slice: [
        o 'Expression RangeDots Expression',        -> new Range $1, $3, $2
        o 'Expression RangeDots',                   -> new Range $1, null, $2
        o 'RangeDots Expression',                   -> new Range null, $2, $1
        o 'RangeDots',                              -> new Range null, null, $1
      ]
  • ¶

    ArgList 既是传递给函数调用的对象列表,也是数组文字的内容(即用逗号分隔的表达式)。换行符也可以使用。

      ArgList: [
        o 'Arg',                                              -> [$1]
        o 'ArgList , Arg',                                    -> $1.concat $3
        o 'ArgList OptComma TERMINATOR Arg',                  -> $1.concat $4
        o 'INDENT ArgList OptComma OUTDENT',                  -> $2
        o 'ArgList OptComma INDENT ArgList OptComma OUTDENT', -> $1.concat $4
      ]
  • ¶

    有效参数是块或 splat。

      Arg: [
        o 'Expression'
        o 'Splat'
        o '...',                                     -> new Expansion
      ]
  • ¶

    只是简单的、用逗号分隔的、必需的参数(没有花哨的语法)。我们需要将其与 ArgList 分开,以便在 Switch 块中使用,在 Switch 块中,使用换行符没有意义。

      SimpleArgs: [
        o 'Expression'
        o 'SimpleArgs , Expression',                -> [].concat $1, $3
      ]
  • ¶

    try/catch/finally 异常处理块的变体。

      Try: [
        o 'TRY Block',                              -> new Try $2
        o 'TRY Block Catch',                        -> new Try $2, $3[0], $3[1]
        o 'TRY Block FINALLY Block',                -> new Try $2, null, null, $4
        o 'TRY Block Catch FINALLY Block',          -> new Try $2, $3[0], $3[1], $5
      ]
  • ¶

    catch 子句为其错误命名并运行一段代码。

      Catch: [
        o 'CATCH Identifier Block',                 -> [$2, $3]
        o 'CATCH Object Block',                     -> [LOC(2)(new Value($2)), $3]
        o 'CATCH Block',                            -> [null, $2]
      ]
  • ¶

    抛出一个异常对象。

      Throw: [
        o 'THROW Expression',                       -> new Throw $2
      ]
  • ¶

    圆括号表达式。请注意,Parenthetical 是一个 Value,而不是一个 Expression,因此,如果您需要在只接受值的 地方使用表达式,将其括在圆括号中总是可以的。

      Parenthetical: [
        o '( Body )',                               -> new Parens $2
        o '( INDENT Body OUTDENT )',                -> new Parens $3
      ]
  • ¶

    while 循环的条件部分。

      WhileSource: [
        o 'WHILE Expression',                       -> new While $2
        o 'WHILE Expression WHEN Expression',       -> new While $2, guard: $4
        o 'UNTIL Expression',                       -> new While $2, invert: true
        o 'UNTIL Expression WHEN Expression',       -> new While $2, invert: true, guard: $4
      ]
  • ¶

    while 循环可以是正常的,执行一个表达式块,也可以是后缀的,执行单个表达式。没有 do..while。

      While: [
        o 'WhileSource Block',                      -> $1.addBody $2
        o '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',                             -> new While(LOC(1) new BooleanLiteral 'true').addBody $2
        o 'LOOP Expression',                        -> new While(LOC(1) new BooleanLiteral 'true').addBody LOC(2) Block.wrap [$2]
      ]
  • ¶

    数组、对象和范围推导,在最通用的级别。推导可以是正常的,执行一个表达式块,也可以是后缀的,执行单个表达式。

      For: [
        o 'Statement  ForBody',                     -> new For $1, $2
        o 'Expression ForBody',                     -> new For $1, $2
        o 'ForBody    Block',                       -> new For $2, $1
      ]
    
      ForBody: [
        o 'FOR Range',                              -> source: (LOC(2) new Value($2))
        o 'FOR Range BY Expression',                -> source: (LOC(2) new Value($2)), step: $4
        o 'ForStart ForSource',                     -> $2.own = $1.own; $2.ownTag = $1.ownTag; $2.name = $1[0]; $2.index = $1[1]; $2
      ]
    
      ForStart: [
        o 'FOR ForVariables',                       -> $2
        o 'FOR OWN ForVariables',                   -> $3.own = yes; $3.ownTag = (LOC(2) new Literal($2)); $3
      ]
  • ¶

    循环内变量的所有接受值的数组。这使得支持模式匹配成为可能。

      ForValue: [
        o 'Identifier'
        o 'ThisProperty'
        o 'Array',                                  -> new Value $1
        o 'Object',                                 -> new Value $1
      ]
  • ¶

    数组或范围推导具有当前元素的变量和(可选)对当前索引的引用。或者,在对象推导的情况下,key, value。

      ForVariables: [
        o 'ForValue',                               -> [$1]
        o 'ForValue , ForValue',                    -> [$1, $3]
      ]
  • ¶

    推导的来源是一个数组或对象,带有一个可选的保护子句。如果它是数组推导,您还可以选择以固定大小的增量进行逐步执行。

      ForSource: [
        o 'FORIN Expression',                               -> source: $2
        o 'FOROF Expression',                               -> source: $2, object: yes
        o 'FORIN Expression WHEN Expression',               -> source: $2, guard: $4
        o 'FOROF Expression WHEN Expression',               -> source: $2, guard: $4, object: yes
        o 'FORIN Expression BY Expression',                 -> source: $2, step:  $4
        o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
        o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step:  $4, guard: $6
        o 'FORFROM Expression',                             -> source: $2, from: yes
        o 'FORFROM Expression WHEN Expression',             -> source: $2, guard: $4, from: yes
      ]
    
      Switch: [
        o 'SWITCH Expression INDENT Whens OUTDENT',            -> new Switch $2, $4
        o 'SWITCH Expression INDENT Whens ELSE Block OUTDENT', -> new Switch $2, $4, $6
        o 'SWITCH INDENT Whens OUTDENT',                       -> new Switch null, $3
        o 'SWITCH INDENT Whens ELSE Block OUTDENT',            -> new Switch null, $3, $5
      ]
    
      Whens: [
        o 'When'
        o 'Whens When',                             -> $1.concat $2
      ]
  • ¶

    单个 When 子句,带操作。

      When: [
        o 'LEADING_WHEN SimpleArgs Block',            -> [[$2, $3]]
        o 'LEADING_WHEN SimpleArgs Block TERMINATOR', -> [[$2, $3]]
      ]
  • ¶

    if 的最基本形式是一个条件和一个操作。以下与 if 相关的规则按照这些行划分,以避免歧义。

      IfBlock: [
        o 'IF Expression Block',                    -> new If $2, $3, type: $1
        o 'IfBlock ELSE IF Expression Block',       -> $1.addElse LOC(3,5) new If $4, $5, type: $3
      ]
  • ¶

    if 表达式的全部补充,包括后缀单行 if 和 unless。

      If: [
        o 'IfBlock'
        o 'IfBlock ELSE Block',                     -> $1.addElse $3
        o 'Statement  POST_IF Expression',          -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, statement: true
        o 'Expression POST_IF Expression',          -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, statement: true
      ]
  • ¶

    算术和逻辑运算符,作用于一个或多个操作数。这里它们按优先级分组。实际的优先级规则定义在页面底部。如果我们可以将这些规则中的大多数组合成一个单一的通用 Operand OpSymbol Operand 类型的规则,那会更短,但是为了使优先级绑定成为可能,需要单独的规则。

      Operation: [
        o 'UNARY Expression',                       -> new Op $1 , $2
        o 'UNARY_MATH Expression',                  -> new Op $1 , $2
        o '-     Expression',                      (-> new Op '-', $2), prec: 'UNARY_MATH'
        o '+     Expression',                      (-> new Op '+', $2), prec: 'UNARY_MATH'
    
        o '-- SimpleAssignable',                    -> new Op '--', $2
        o '++ SimpleAssignable',                    -> new Op '++', $2
        o 'SimpleAssignable --',                    -> new Op '--', $1, null, true
        o 'SimpleAssignable ++',                    -> new Op '++', $1, null, true
  • ¶

    存在运算符.

        o 'Expression ?',                           -> new Existence $1
    
        o 'Expression +  Expression',               -> new Op '+' , $1, $3
        o 'Expression -  Expression',               -> new Op '-' , $1, $3
    
        o 'Expression MATH     Expression',         -> new Op $2, $1, $3
        o 'Expression **       Expression',         -> new Op $2, $1, $3
        o 'Expression SHIFT    Expression',         -> new Op $2, $1, $3
        o 'Expression COMPARE  Expression',         -> new Op $2, $1, $3
        o 'Expression &        Expression',         -> new Op $2, $1, $3
        o 'Expression ^        Expression',         -> new Op $2, $1, $3
        o 'Expression |        Expression',         -> new Op $2, $1, $3
        o 'Expression &&       Expression',         -> new Op $2, $1, $3
        o 'Expression ||       Expression',         -> new Op $2, $1, $3
        o 'Expression BIN?     Expression',         -> new Op $2, $1, $3
        o 'Expression RELATION Expression',         ->
          if $2.charAt(0) is '!'
            new Op($2[1..], $1, $3).invert()
          else
            new Op $2, $1, $3
    
        o 'SimpleAssignable COMPOUND_ASSIGN
           Expression',                             -> new Assign $1, $3, $2
        o 'SimpleAssignable COMPOUND_ASSIGN
           INDENT Expression OUTDENT',              -> new Assign $1, $4, $2
        o 'SimpleAssignable COMPOUND_ASSIGN TERMINATOR
           Expression',                             -> new Assign $1, $4, $2
        o 'SimpleAssignable EXTENDS Expression',    -> new Extends $1, $3
      ]
  • ¶

    优先级

  • ¶
  • ¶

    此列表顶部的运算符优先级高于底部的运算符。遵循这些规则是使 2 + 3 * 4 解析为

    2 + (3 * 4)
    

    而不是

    (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']
    ]
  • ¶

    总结

  • ¶
  • ¶

    最后,现在我们有了 grammar 和 operators,我们可以创建 Jison.Parser。我们通过处理所有规则来做到这一点,将所有终结符(每个不作为上面规则名称出现的符号)记录为“tokens”。

    tokens = []
    for name, alternatives of grammar
      grammar[name] = for alt in alternatives
        for token in alt[0].split ' '
          tokens.push token unless grammar[token]
        alt[1] = "return #{alt[1]}" if name is 'Root'
        alt
  • ¶

    使用我们的终结符 tokens 列表、我们的 grammar 规则和根的名称来初始化 Parser。反转运算符,因为 Jison 从低到高排序优先级,而我们从高到低排序(如 Yacc 中)。

    exports.parser = new Parser
      tokens      : tokens.join ' '
      bnf         : grammar
      operators   : operators.reverse()
      startSymbol : 'Root'