{Parser} = require 'jison'
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によって関数でラップされるため、アクションが値をすぐに返す場合は、関数ラッパーを削除して値を直接返すことで最適化できます。
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
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
は2番目の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
]
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には、関数に対して2つの異なる記号があります。->
は通常の関数用であり、=>
は現在の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
]
関数定義内の単一のパラメーターは、通常のパラメーターまたは残りの引数を吸収するスプラットにすることができます。
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: [
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
]
有効な引数は、BlockまたはSplatです。
Arg: [
o 'Expression'
o 'Splat'
o '...', -> new Expansion
]
単純なカンマ区切りの必須引数(特別な構文なし)。**Switch**ブロックで使用するために、**ArgList**とは別に存在する必要があります。ここで改行を使用することは意味がありません。
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
]
配列または範囲の包括には、現在の要素の変数と(オプションの)現在のインデックスへの参照があります。または、オブジェクトの包括の場合はキー、値。
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
]
後置の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, statement: true
o 'Expression POST_IF Expression', -> new If $3, LOC(1)(Block.wrap [$1]), type: $2, statement: true
]
1つ以上のオペランドで動作する算術演算子と論理演算子。ここでは、優先順位順にグループ化されています。実際の優先順位ルールは、ページの下部に定義されています。これらのルールのほとんどを単一の汎用的な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 = []
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
終端**トークン**のリスト、**grammar**ルール、ルートの名前で**Parser**を初期化します。Yaccのように、Jisonは優先順位を低から高に順序付けるため、演算子を逆順にします。
exports.parser = new Parser
tokens : tokens.join ' '
bnf : grammar
operators : operators.reverse()
startSymbol : 'Root'