{Parser} = require 'jison'
CoffeeScriptパーサーは、この文法ファイルからJisonによって生成されます。Jisonは、JavaScriptで実装された、Bisonと同様のスタイルのボトムアップパーサジェネレータです。LALR(1), LR(0), SLR(1), and LR(1)タイプの文法を認識できます。Jisonパーサーを作成するには、左側に一致させるパターンを、右側に実行するアクション(通常は構文ツリーノードの作成)を記述します。パーサーは実行時に、トークンストリームからトークンを左から右にシフトし、以下のルールとの一致を試みます。一致が見つかると、非終端記号(上部の囲み名)に還元され、そこから処理が進みます。
cake build:parser
コマンドを実行すると、Jisonはルールから解析テーブルを構築し、それをlib/parser.js
に保存します。
唯一の依存関係は Jison.Parser です。
{Parser} = require 'jison'
Jisonによって関数でラップされるため、アクションがすぐに値を返す場合は、関数ラッパーを削除して値を直接返すことで最適化できます。
unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
Tim Caswell氏のおかげで、Jison文法生成のための便利なDSLができました。文法のすべてのルールに対して、パターン定義文字列、実行するアクション、およびオプションで追加のオプションを渡します。アクションが指定されていない場合は、前の非終端記号の値を渡します。
o = (patternString, action, options) ->
patternString = patternString.replace /\s{2,}/g, ' '
patternCount = patternString.split(' ').length
if action
このコードブロックは、生成されたparser.js
ファイル内で文字列置換を実行し、LOC
関数および以下にリストされている他の文字列への呼び出しを置き換えます。
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.$&'
ノードが持つ可能性のある追加データ(コメントや位置データなど)を追加するparser.js
に追加する関数の文字列を返します。位置データは渡された最初のパラメータに追加され、パラメータが返されます。パラメータがノードでない場合は、影響を受けずに渡されます。
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'})"
このコードは、LOC
への呼び出しを、上記で定義されたyy.addDataToNode
文字列に置き換えます。以下の文法規則で使用されるLOC
関数は、新しく作成されたノードクラスオブジェクトに正しい位置データが割り当てられるようにするために使用されます。デフォルトでは、文法は左側の*すべて*のトークン(例:'Body TERMINATOR Line'
のような文字列)によって広がる位置データを、文法規則によって返される「トップレベル」ノード(右側の関数)に割り当てます。しかし、文法規則によって作成された「内部」ノードクラスオブジェクトは、LOC
を追加しないと、正しい位置データが割り当てられません。
たとえば、new MetaProperty LOC(1)(new IdentifierLiteral $1), LOC(3)(new Access $3)
を返す関数によって処理される文法規則'NEW_TARGET . Property'
を考えてみましょう。LOC(1)
の1
は最初のトークン(NEW_TARGET
)を指し、LOC(3)
の3
は3番目のトークン(Property
)を指します。new IdentifierLiteral
にソースコードのnew
に対応する位置データを割り当てるために、LOC(1)(new IdentifierLiteral ...)
を使用して、「この文法規則の*最初*のトークン(NEW_TARGET
)の位置データをこのnew IdentifierLiteral
に割り当てる」ことを意味します。LOC(3)
は、「この文法規則の*3番目*のトークン(Property
)の位置データをこのnew Access
に割り当てる」ことを意味します。
returnsLoc = /^LOC/.test action
action = action.replace /LOC\(([0-9]*)\)/g, getAddDataToNodeFunctionString('$1')
2つの引数を持つLOC
の呼び出し(例:LOC(2,4)
)は、参照される両方のトークン(この例では2番目と4番目)で生成されたノードの位置データを設定します。
action = action.replace /LOC\(([0-9]*),\s*([0-9]*)\)/g, getAddDataToNodeFunctionString('$1', '$2')
performActionFunctionString = "$$ = #{getAddDataToNodeFunctionString(1, patternCount, not returnsLoc)}(#{action});"
else
performActionFunctionString = '$$ = $1;'
[patternString, performActionFunctionString, options]
以下のすべてのルールでは、非終端記号の名前が代替一致のリストのキーとして表示されます。それぞれの一致のアクションでは、ドル記号変数はJisonによって数値位置の値への参照として提供されるため、このルールでは
'Expression UNLESS Expression'
$1
は最初のExpression
の値、$2
はUNLESS
終端記号のトークン、$3
は2番目のExpression
の値になります。
grammar =
**Root**は構文ツリーの最上位ノードです。ボトムアップで解析するため、すべての解析はここで終了する必要があります。
Root: [
o '', -> new Root new Block
o 'Body', -> new Root $1
]
改行またはセミコロンで区切られたステートメントと式のリスト。
Body: [
o 'Line', -> Block.wrap [$1]
o 'Body TERMINATOR Line', -> $1.push $3
o 'Body TERMINATOR'
]
本体の行を構成するブロックとステートメント。FuncDirectiveはステートメントですが、あいまいな文法になるためStatementには含まれていません。
Line: [
o 'Expression'
o 'ExpressionLine'
o 'Statement'
o 'FuncDirective'
]
FuncDirective: [
o 'YieldReturn'
o 'AwaitReturn'
]
式にすることができない純粋なステートメント。
Statement: [
o 'Return'
o 'STATEMENT', -> new StatementLiteral $1
o 'Import'
o 'Export'
]
言語内のすべての異なるタイプの式。CoffeeScriptの基本単位は**Expression**です。式になり得るものはすべて式です。ブロックは他の多くのルールの構成要素として機能するため、いくぶん循環的です。
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'
]
単一行で記述され、そうでなければ中括弧で囲む必要がある式:例: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', -> new Op $1, new Value new Literal ''
o 'YIELD Expression', -> new Op $1, $2
o 'YIELD INDENT Object OUTDENT', -> new Op $1, $3
o 'YIELD FROM Expression', -> new Op $1.concat($2), $3
]
Block: [
o 'INDENT OUTDENT', -> new Block
o 'INDENT Body OUTDENT', -> $2
]
Identifier: [
o 'IDENTIFIER', -> new IdentifierLiteral $1
o 'JSX_TAG', -> new JSXTag $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', -> new PropertyName $1.toString()
]
英数字は、オブジェクトリテラルのキーとしても機能するため、他の**Literal**マッチャーから分離されています。
AlphaNumeric: [
o 'NUMBER', -> new NumberLiteral $1.toString(), parsedValue: $1.parsedValue
o 'String'
]
String: [
o 'STRING', ->
new StringLiteral(
$1.slice 1, -1 # strip artificial quotes and unwrap to primitive string
quote: $1.quote
initialChunk: $1.initialChunk
finalChunk: $1.finalChunk
indent: $1.indent
double: $1.double
heregex: $1.heregex
)
o 'STRING_START Interpolations STRING_END', -> new StringWithInterpolations Block.wrap($2), quote: $1.quote, startQuote: LOC(1)(new Literal $1.toString())
]
Interpolations: [
o 'InterpolationChunk', -> [$1]
o 'Interpolations InterpolationChunk', -> $1.concat $2
]
InterpolationChunk: [
o 'INTERPOLATION_START Body INTERPOLATION_END', -> new Interpolation $2
o 'INTERPOLATION_START INDENT Body OUTDENT INTERPOLATION_END', -> new Interpolation $3
o 'INTERPOLATION_START INTERPOLATION_END', -> new Interpolation
o 'String', -> $1
]
ここや他の場所での.toString()呼び出しは、隠れた追加のプロパティを取得したため、`String`オブジェクトをプリミティブ文字列に戻すためです。
Regex: [
o 'REGEX', -> new RegexLiteral $1.toString(), delimiter: $1.delimiter, heregexCommentTokens: $1.heregexCommentTokens
o 'REGEX_START Invocation REGEX_END', -> new RegexWithInterpolations $2, heregexCommentTokens: $3.heregexCommentTokens
]
すべての即値。一般に、これらはそのままJavaScriptに渡して出力できます。
Literal: [
o 'AlphaNumeric'
o 'JS', -> new PassthroughLiteral $1.toString(), here: $1.here, generated: $1.generated
o 'Regex'
o 'UNDEFINED', -> new UndefinedLiteral $1
o 'NULL', -> new NullLiteral $1
o 'BOOL', -> new BooleanLiteral $1.toString(), originalValue: $1.original
o 'INFINITY', -> new InfinityLiteral $1.toString(), originalValue: $1.original
o 'NAN', -> new NaNLiteral $1
]
変数、プロパティ、またはインデックスへの値の割り当て。
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 'ObjRestValue'
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)
]
SimpleObjAssignable: [
o 'Identifier'
o 'Property'
o 'ThisProperty'
]
ObjAssignable: [
o 'SimpleObjAssignable'
o '[ Expression ]', -> new Value new ComputedPropertyName $2
o '@ [ Expression ]', -> new Value LOC(1)(new ThisLiteral $1), [LOC(3)(new ComputedPropertyName($3))], 'this'
o 'AlphaNumeric'
]
オブジェクトリテラルのスプレッドプロパティ。
ObjRestValue: [
o 'SimpleObjAssignable ...', -> new Splat new Value $1
o '... SimpleObjAssignable', -> new Splat new Value($2), postfix: no
o 'ObjSpreadExpr ...', -> new Splat $1
o '... ObjSpreadExpr', -> new Splat $2, postfix: no
]
ObjSpreadExpr: [
o 'ObjSpreadIdentifier'
o 'Object'
o 'Parenthetical'
o 'Super'
o 'This'
o 'SUPER OptFuncExist Arguments', -> new SuperCall LOC(1)(new Super), $3, $2.soak, $1
o 'DYNAMIC_IMPORT Arguments', -> new DynamicImportCall LOC(1)(new DynamicImport), $2
o 'SimpleObjAssignable OptFuncExist Arguments', -> new Call (new Value $1), $3, $2.soak
o 'ObjSpreadExpr OptFuncExist Arguments', -> new Call $1, $3, $2.soak
]
ObjSpreadIdentifier: [
o 'SimpleObjAssignable Accessor', -> (new Value $1).add $2
o 'ObjSpreadExpr Accessor', -> (new Value $1).add $2
]
関数本体からの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, returnKeyword: LOC(2)(new Literal $2)
o 'YIELD RETURN', -> new YieldReturn null, returnKeyword: LOC(2)(new Literal $2)
]
AwaitReturn: [
o 'AWAIT RETURN Expression', -> new AwaitReturn $3, returnKeyword: LOC(2)(new Literal $2)
o 'AWAIT RETURN', -> new AwaitReturn null, returnKeyword: LOC(2)(new Literal $2)
]
**Code**ノードは関数リテラルです。関数矢印が前に付いた**Block**のインデントされたブロックと、オプションのパラメータリストによって定義されます。
Code: [
o 'PARAM_START ParamList PARAM_END FuncGlyph Block', -> new Code $2, $5, $4, LOC(1)(new Literal $1)
o 'FuncGlyph Block', -> new Code [], $2, $1
]
Codelineは、インデントされた**Block**の代わりに**Line**を持つ**Code**ノードです。
CodeLine: [
o 'PARAM_START ParamList PARAM_END FuncGlyph Line', -> new Code $2, LOC(5)(Block.wrap [$5]), $4,
LOC(1)(new Literal $1)
o 'FuncGlyph Line', -> new Code [], LOC(2)(Block.wrap [$2]), $1
]
CoffeeScriptには、関数用に2つの異なる記号があります。 ->
は通常の関数用で、=>
は*this*の現在の値にバインドされた関数用です。
FuncGlyph: [
o '->', -> new FuncGlyph $1
o '=>', -> new FuncGlyph $1
]
オプションの末尾のカンマ。
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', -> new Param $2, null, postfix: no
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
o '... Expression', -> new Splat $2, {postfix: no}
]
割り当てることができる変数とプロパティ。
SimpleAssignable: [
o 'Identifier', -> new Value $1
o 'Value Accessor', -> $1.add $2
o 'Code Accessor', -> new Value($1).add $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 'Invocation', -> new Value $1
o 'DoIife', -> new Value $1
o 'This'
o 'Super', -> new Value $1
o 'MetaProperty', -> new Value $1
]
値として使用できる*super*ベースの式。
Super: [
o 'SUPER . Property', -> new Super LOC(3)(new Access $3), LOC(1)(new Literal $1)
o 'SUPER INDEX_START Expression INDEX_END', -> new Super LOC(3)(new Index $3), LOC(1)(new Literal $1)
o 'SUPER INDEX_START INDENT Expression OUTDENT INDEX_END', -> new Super LOC(4)(new Index $4), LOC(1)(new Literal $1)
]
「メタプロパティ」アクセス。例:new.target
またはimport.meta
。プロパティのように見えるものがキーワードで参照されます。
MetaProperty: [
o 'NEW_TARGET . Property', -> new MetaProperty LOC(1)(new IdentifierLiteral $1), LOC(3)(new Access $3)
o 'IMPORT_META . Property', -> new MetaProperty LOC(1)(new IdentifierLiteral $1), LOC(3)(new Access $3)
]
プロパティ、プロトタイプ、または配列インデックスまたはスライスによる、オブジェクトへのアクセサーの一般的なグループ。
Accessor: [
o '. Property', -> new Access $2
o '?. Property', -> new Access $2, soak: yes
o ':: Property', -> [LOC(1)(new Access new PropertyName('prototype'), shorthand: yes), LOC(2)(new Access $2)]
o '?:: Property', -> [LOC(1)(new Access new PropertyName('prototype'), shorthand: yes, soak: yes), LOC(2)(new Access $2)]
o '::', -> new Access new PropertyName('prototype'), shorthand: yes
o '?::', -> new Access new PropertyName('prototype'), shorthand: yes, soak: yes
o 'Index'
]
ブラケット表記法を使用したオブジェクトまたは配列へのインデックス付け。
Index: [
o 'INDEX_START IndexValue INDEX_END', -> $2
o 'INDEX_START INDENT IndexValue OUTDENT INDEX_END', -> $3
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 String ASSERT Object', -> new ImportDeclaration null, $2, $4
o 'IMPORT ImportDefaultSpecifier FROM String', -> new ImportDeclaration new ImportClause($2, null), $4
o 'IMPORT ImportDefaultSpecifier FROM String ASSERT Object', -> new ImportDeclaration new ImportClause($2, null), $4, $6
o 'IMPORT ImportNamespaceSpecifier FROM String', -> new ImportDeclaration new ImportClause(null, $2), $4
o 'IMPORT ImportNamespaceSpecifier FROM String ASSERT Object', -> new ImportDeclaration new ImportClause(null, $2), $4, $6
o 'IMPORT { } FROM String', -> new ImportDeclaration new ImportClause(null, new ImportSpecifierList []), $5
o 'IMPORT { } FROM String ASSERT Object', -> new ImportDeclaration new ImportClause(null, new ImportSpecifierList []), $5, $7
o 'IMPORT { ImportSpecifierList OptComma } FROM String', -> new ImportDeclaration new ImportClause(null, new ImportSpecifierList $3), $7
o 'IMPORT { ImportSpecifierList OptComma } FROM String ASSERT Object', -> new ImportDeclaration new ImportClause(null, new ImportSpecifierList $3), $7, $9
o 'IMPORT ImportDefaultSpecifier , ImportNamespaceSpecifier FROM String', -> new ImportDeclaration new ImportClause($2, $4), $6
o 'IMPORT ImportDefaultSpecifier , ImportNamespaceSpecifier FROM String ASSERT Object', -> new ImportDeclaration new ImportClause($2, $4), $6, $8
o 'IMPORT ImportDefaultSpecifier , { ImportSpecifierList OptComma } FROM String', -> new ImportDeclaration new ImportClause($2, new ImportSpecifierList $5), $9
o 'IMPORT ImportDefaultSpecifier , { ImportSpecifierList OptComma } FROM String ASSERT Object', -> new ImportDeclaration new ImportClause($2, new ImportSpecifierList $5), $9, $11
]
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 LOC(1)(new DefaultLiteral $1)
o 'DEFAULT AS Identifier', -> new ImportSpecifier LOC(1)(new DefaultLiteral($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 LOC(2,4)(new Assign $2, $4, null,
moduleDeclaration: 'export')
o 'EXPORT Identifier = TERMINATOR Expression', -> new ExportNamedDeclaration LOC(2,5)(new Assign $2, $5, null,
moduleDeclaration: 'export')
o 'EXPORT Identifier = INDENT Expression OUTDENT', -> new ExportNamedDeclaration LOC(2,6)(new Assign $2, $5, null,
moduleDeclaration: 'export')
o 'EXPORT DEFAULT Expression', -> new ExportDefaultDeclaration $3
o 'EXPORT DEFAULT INDENT Object OUTDENT', -> new ExportDefaultDeclaration new Value $4
o 'EXPORT EXPORT_ALL FROM String', -> new ExportAllDeclaration new Literal($2), $4
o 'EXPORT EXPORT_ALL FROM String ASSERT Object', -> new ExportAllDeclaration new Literal($2), $4, $6
o 'EXPORT { } FROM String', -> new ExportNamedDeclaration new ExportSpecifierList([]), $5
o 'EXPORT { } FROM String ASSERT Object', -> new ExportNamedDeclaration new ExportSpecifierList([]), $5, $7
o 'EXPORT { ExportSpecifierList OptComma } FROM String', -> new ExportNamedDeclaration new ExportSpecifierList($3), $7
o 'EXPORT { ExportSpecifierList OptComma } FROM String ASSERT Object', -> new ExportNamedDeclaration new ExportSpecifierList($3), $7, $9
]
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, LOC(3)(new DefaultLiteral $3)
o 'DEFAULT', -> new ExportSpecifier LOC(1)(new DefaultLiteral $1)
o 'DEFAULT AS Identifier', -> new ExportSpecifier LOC(1)(new DefaultLiteral($1)), $3
]
通常の関数呼び出し、または一連の連鎖呼び出し。
Invocation: [
o 'Value OptFuncExist String', -> new TaggedTemplateCall $1, $3, $2.soak
o 'Value OptFuncExist Arguments', -> new Call $1, $3, $2.soak
o 'SUPER OptFuncExist Arguments', -> new SuperCall LOC(1)(new Super), $3, $2.soak, $1
o 'DYNAMIC_IMPORT Arguments', -> new DynamicImportCall LOC(1)(new DynamicImport), $2
]
関数のオプションの 존재 チェック。
OptFuncExist: [
o '', -> soak: no
o 'FUNC_EXIST', -> soak: yes
]
関数呼び出しの引数リスト。
Arguments: [
o 'CALL_START CALL_END', -> []
o 'CALL_START ArgList OptComma CALL_END', -> $2.implicit = $1.generated; $2
]
現在のオブジェクトである *this* への参照。
This: [
o 'THIS', -> new Value new ThisLiteral $1
o '@', -> new Value new ThisLiteral $1
]
*this* のプロパティへの参照。
ThisProperty: [
o '@ Property', -> new Value LOC(1)(new ThisLiteral $1), [LOC(2)(new Access($2))], 'this'
]
配列リテラル。
Array: [
o '[ ]', -> new Arr []
o '[ Elisions ]', -> new Arr $2
o '[ ArgElisionList OptElisions ]', -> new Arr [].concat $2, $3
]
包括的および排他的範囲ドット。
RangeDots: [
o '..', -> exclusive: no
o '...', -> exclusive: yes
]
CoffeeScript範囲リテラル。
Range: [
o '[ Expression RangeDots Expression ]', -> new Range $2, $4, if $3.exclusive then 'exclusive' else 'inclusive'
o '[ ExpressionLine RangeDots Expression ]', -> new Range $2, $4, if $3.exclusive then 'exclusive' else 'inclusive'
]
配列スライスリテラル。
Slice: [
o 'Expression RangeDots Expression', -> new Range $1, $3, if $2.exclusive then 'exclusive' else 'inclusive'
o 'Expression RangeDots', -> new Range $1, null, if $2.exclusive then 'exclusive' else 'inclusive'
o 'ExpressionLine RangeDots Expression', -> new Range $1, $3, if $2.exclusive then 'exclusive' else 'inclusive'
o 'ExpressionLine RangeDots', -> new Range $1, null, if $2.exclusive then 'exclusive' else 'inclusive'
o 'RangeDots Expression', -> new Range null, $2, if $1.exclusive then 'exclusive' else 'inclusive'
o 'RangeDots', -> new Range null, null, if $1.exclusive then 'exclusive' else 'inclusive'
]
**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
]
有効な引数はBlocksまたはSplatsです。
Arg: [
o 'Expression'
o 'ExpressionLine'
o 'Splat'
o '...', -> new Expansion
]
**ArgElisionList**は、オブジェクトのリスト、配列リテラルの内容(つまり、カンマ区切りの式と省略)です。改行も機能します。
ArgElisionList: [
o 'ArgElision'
o 'ArgElisionList , ArgElision', -> $1.concat $3
o 'ArgElisionList OptComma TERMINATOR ArgElision', -> $1.concat $4
o 'INDENT ArgElisionList OptElisions OUTDENT', -> $2.concat $3
o '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 ',', -> new Elision
o 'Elision TERMINATOR', -> $1
]
単純な、カンマ区切りの必須引数(派手な構文なし)。**Switch**ブロックで使用するために、これを**ArgList**とは別に設定する必要があります。そこでは改行は意味がありません。
SimpleArgs: [
o 'Expression'
o 'ExpressionLine'
o 'SimpleArgs , Expression', -> [].concat $1, $3
o 'SimpleArgs , ExpressionLine', -> [].concat $1, $3
]
*try/catch/finally*例外処理ブロックのバリアント。
Try: [
o 'TRY Block', -> new Try $2
o 'TRY Block Catch', -> new Try $2, $3
o 'TRY Block FINALLY Block', -> new Try $2, null, $4, LOC(3)(new Literal $3)
o 'TRY Block Catch FINALLY Block', -> new Try $2, $3, $5, LOC(4)(new Literal $4)
]
catch句はエラーに名前を付け、コードブロックを実行します。
Catch: [
o 'CATCH Identifier Block', -> new Catch $3, $2
o 'CATCH Object Block', -> new Catch $3, LOC(2)(new Value($2))
o 'CATCH Block', -> new Catch $2
]
例外オブジェクトをスローします。
Throw: [
o 'THROW Expression', -> new Throw $2
o 'THROW INDENT Object OUTDENT', -> new Throw new Value $3
]
括弧で囲まれた式。**Parenthetical**は**Expression**ではなく**Value**であるため、値のみが受け入れられる場所で式を使用する必要がある場合は、括弧で囲むと常にうまくいきます。
Parenthetical: [
o '( Body )', -> new Parens $2
o '( INDENT Body OUTDENT )', -> new Parens $3
]
whileループの条件部分。
WhileLineSource: [
o 'WHILE ExpressionLine', -> new While $2
o 'WHILE ExpressionLine WHEN ExpressionLine', -> new While $2, guard: $4
o 'UNTIL ExpressionLine', -> new While $2, invert: true
o 'UNTIL ExpressionLine WHEN ExpressionLine', -> new While $2, invert: true, guard: $4
]
WhileSource: [
o 'WHILE Expression', -> new While $2
o 'WHILE Expression WHEN Expression', -> new While $2, guard: $4
o 'WHILE ExpressionLine 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
o 'UNTIL ExpressionLine WHEN Expression', -> new While $2, invert: true, guard: $4
]
whileループは、実行する式のブロックを持つ通常のループ、または単一の式を持つpostfixループのいずれかになります。do..whileはありません。
While: [
o 'WhileSource Block', -> $1.addBody $2
o 'WhileLineSource Block', -> $1.addBody $2
o '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', -> new While(LOC(1)(new BooleanLiteral 'true'), isLoop: yes).addBody $2
o 'LOOP Expression', -> new While(LOC(1)(new BooleanLiteral 'true'), isLoop: yes).addBody LOC(2) Block.wrap [$2]
]
最も一般的なレベルでの配列、オブジェクト、および範囲内包表記。内包表記は、実行する式のブロックを持つ通常の表記、または単一の式を持つpostfix表記のいずれかになります。
For: [
o 'Statement ForBody', -> $2.postfix = yes; $2.addBody $1
o 'Expression ForBody', -> $2.postfix = yes; $2.addBody $1
o 'ForBody Block', -> $1.addBody $2
o 'ForLineBody Block', -> $1.addBody $2
]
ForBody: [
o 'FOR Range', -> new For [], source: (LOC(2) new Value($2))
o 'FOR Range BY Expression', -> new For [], source: (LOC(2) new Value($2)), step: $4
o 'ForStart ForSource', -> $1.addSource $2
]
ForLineBody: [
o 'FOR Range BY ExpressionLine', -> new For [], source: (LOC(2) new Value($2)), step: $4
o 'ForStart ForLineSource', -> $1.addSource $2
]
ForStart: [
o 'FOR ForVariables', -> new For [], name: $2[0], index: $2[1]
o 'FOR AWAIT ForVariables', ->
[name, index] = $3
new For [], {name, index, await: yes, awaitTag: (LOC(2) new Literal($2))}
o 'FOR OWN ForVariables', ->
[name, index] = $3
new For [], {name, index, own: yes, ownTag: (LOC(2) new Literal($2))}
]
ループ内の変数に許可されるすべての値の配列。これにより、パターンマッチングのサポートが可能になります。
ForValue: [
o 'Identifier'
o 'ThisProperty'
o 'Array', -> new Value $1
o 'Object', -> new Value $1
]
配列または範囲内包表記は、現在の要素の変数と、(オプションで)現在のインデックスへの参照を持ちます。または、オブジェクト内包表記の場合は、キー、値を持ちます。
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 'FORIN ExpressionLine WHEN Expression', -> source: $2, guard: $4
o 'FOROF Expression WHEN Expression', -> source: $2, guard: $4, object: yes
o 'FOROF ExpressionLine WHEN Expression', -> source: $2, guard: $4, object: yes
o 'FORIN Expression BY Expression', -> source: $2, step: $4
o 'FORIN ExpressionLine BY Expression', -> source: $2, step: $4
o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN Expression WHEN ExpressionLine BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN ExpressionLine BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORIN Expression BY ExpressionLine WHEN Expression', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY ExpressionLine 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
o 'FORFROM ExpressionLine WHEN Expression', -> source: $2, guard: $4, from: yes
]
ForLineSource: [
o 'FORIN ExpressionLine', -> source: $2
o 'FOROF ExpressionLine', -> source: $2, object: yes
o 'FORIN Expression WHEN ExpressionLine', -> source: $2, guard: $4
o 'FORIN ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4
o 'FOROF Expression WHEN ExpressionLine', -> source: $2, guard: $4, object: yes
o 'FOROF ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4, object: yes
o 'FORIN Expression BY ExpressionLine', -> source: $2, step: $4
o 'FORIN ExpressionLine BY ExpressionLine', -> source: $2, step: $4
o 'FORIN Expression WHEN Expression BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN Expression BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN Expression WHEN ExpressionLine BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN ExpressionLine WHEN ExpressionLine BY ExpressionLine', -> source: $2, guard: $4, step: $6
o 'FORIN Expression BY Expression WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY Expression WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORIN Expression BY ExpressionLine WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORIN ExpressionLine BY ExpressionLine WHEN ExpressionLine', -> source: $2, step: $4, guard: $6
o 'FORFROM ExpressionLine', -> source: $2, from: yes
o 'FORFROM Expression WHEN ExpressionLine', -> source: $2, guard: $4, from: yes
o 'FORFROM ExpressionLine WHEN ExpressionLine', -> source: $2, guard: $4, from: yes
]
Switch: [
o 'SWITCH Expression INDENT Whens OUTDENT', -> new Switch $2, $4
o 'SWITCH ExpressionLine INDENT Whens OUTDENT', -> new Switch $2, $4
o 'SWITCH Expression INDENT Whens ELSE Block OUTDENT', -> new Switch $2, $4, LOC(5,6) $6
o 'SWITCH ExpressionLine INDENT Whens ELSE Block OUTDENT', -> new Switch $2, $4, LOC(5,6) $6
o 'SWITCH INDENT Whens OUTDENT', -> new Switch null, $3
o 'SWITCH INDENT Whens ELSE Block OUTDENT', -> new Switch null, $3, LOC(4,5) $5
]
Whens: [
o 'When', -> [$1]
o 'Whens When', -> $1.concat $2
]
アクションを持つ個々のWhen句。
When: [
o 'LEADING_WHEN SimpleArgs Block', -> new SwitchWhen $2, $3
o 'LEADING_WHEN SimpleArgs Block TERMINATOR', -> LOC(1, 3) new SwitchWhen $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
]
後置の1行ifおよびunlessを含む、if式の完全な補完。
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, postfix: true
o 'Expression POST_IF Expression', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, postfix: true
]
IfBlockLine: [
o 'IF ExpressionLine Block', -> new If $2, $3, type: $1
o 'IfBlockLine ELSE IF ExpressionLine Block', -> $1.addElse LOC(3,5) new If $4, $5, type: $3
]
IfLine: [
o 'IfBlockLine'
o 'IfBlockLine ELSE Block', -> $1.addElse $3
o 'Statement POST_IF ExpressionLine', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, postfix: true
o 'Expression POST_IF ExpressionLine', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, postfix: true
]
1つ以上のオペランドで動作する算術演算子と論理演算子。ここでは、演算子の優先順位に従ってグループ化されています。実際の優先順位ルールはページの下部に定義されています。これらのルールのほとんどを単一の汎用オペランド 演算子記号 オペランドタイプのルールに組み合わせることができれば短くなりますが、優先順位のバインディングを可能にするために、個別のルールが必要です。
OperationLine: [
o 'UNARY ExpressionLine', -> new Op $1, $2
o 'DO ExpressionLine', -> new Op $1, $2
o 'DO_IIFE CodeLine', -> new Op $1, $2
]
Operation: [
o 'UNARY Expression', -> new Op $1.toString(), $2, undefined, undefined, originalOperator: $1.original
o 'DO 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 'AWAIT Expression', -> new Op $1, $2
o 'AWAIT INDENT Object OUTDENT', -> new Op $1, $3
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.toString(), $1, $3, undefined, originalOperator: $2.original
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.toString(), $1, $3, undefined, originalOperator: $2.original
o 'Expression || Expression', -> new Op $2.toString(), $1, $3, undefined, originalOperator: $2.original
o 'Expression BIN? Expression', -> new Op $2, $1, $3
o 'Expression RELATION Expression', -> new Op $2.toString(), $1, $3, undefined, invertOperator: $2.invert?.original ? $2.invert
o 'SimpleAssignable COMPOUND_ASSIGN
Expression', -> new Assign $1, $3, $2.toString(), originalContext: $2.original
o 'SimpleAssignable COMPOUND_ASSIGN
INDENT Expression OUTDENT', -> new Assign $1, $4, $2.toString(), originalContext: $2.original
o 'SimpleAssignable COMPOUND_ASSIGN TERMINATOR
Expression', -> new Assign $1, $4, $2.toString(), originalContext: $2.original
]
DoIife: [
o 'DO_IIFE Code', -> new Op $1 , $2
]
このリストの上部にある演算子は、下部にある演算子よりも優先順位が高くなります。これらのルールに従うことで、2 + 3 * 4
は次のように解析されます。
2 + (3 * 4)
ではなく、次のように解析されます。
(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']
]
最後に、文法と演算子ができたので、Jison.Parserを作成できます。これは、すべてのルールを処理し、すべての終端記号(上記のルールの名前として表示されないすべての記号)を「トークン」として記録することによって行います。
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
終端トークンのリスト、文法ルール、およびルートの名前を使用してParserを初期化します。Jisonは優先順位を低いものから高いものへと順序付けますが、ここでは(Yaccのように)高いものから低いものへと順序付けているため、演算子の順序を逆にします。
exports.parser = new Parser
tokens : tokens.join ' '
bnf : grammar
operators : operators.reverse()
startSymbol : 'Root'