{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*\}/
我們方便的 Jison 語法產生 DSL,感謝 Tim Caswell。對於語法中的每個規則,我們傳遞模式定義字串、要執行的動作,以及額外的選項(如果有的話)。如果未指定動作,我們會簡單地傳遞前一個非終端的數值。
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'})"
此程式碼會使用上方定義的 yy.addDataToNode
字串替換對 LOC
的呼叫。當在語法規則中使用 LOC
函式時,會用來確保新建立的節點類別物件會指派正確的位置資料給它們。語法預設會將所有左側代幣所跨越的位置資料(例如字串 'Body TERMINATOR Line'
)指派給語法規則傳回的「頂層」節點(右側的函式)。但對於語法規則建立的「內部」節點類別物件,它們不會在未新增 LOC
的情況下指派正確的位置資料給它們。
例如,考慮語法規則 'NEW_TARGET . Property'
,它由一個函數處理,該函數傳回 new MetaProperty LOC(1)(new IdentifierLiteral $1), LOC(3)(new Access $3)
。LOC(1)
中的 1
指的是第一個代幣 (NEW_TARGET
),LOC(3)
中的 3
指的是第三個代幣 (Property
)。為了讓 new IdentifierLiteral
取得與原始碼中 new
相應的位置資料,我們使用 LOC(1)(new IdentifierLiteral ...)
表示「將此語法規則(NEW_TARGET
)的第一個代幣的位置資料指定給此 new IdentifierLiteral
」。LOC(3)
表示「將此語法規則(Property
)的第三個代幣的位置資料指定給此 new Access
」。
returnsLoc = /^LOC/.test action
action = action.replace /LOC\(([0-9]*)\)/g, getAddDataToNodeFunctionString('$1')
呼叫具有兩個參數的 LOC
,例如 LOC(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
終端的 token,而 $3
會是第二個 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
]
在物件文字中發生指派時。與一般 指派 的差異在於,這些允許數字和字串作為鍵。
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: [
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: [
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: [
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 有兩個不同的函式符號。->
用於一般函式,而 =>
用於繫結到 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
]
有效引數為區塊或展開。
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
]
只要簡單的、以逗號分隔的必要引數(沒有花俏的語法)。我們需要將這個與 ArgList 分開,以便在 Switch 區塊中使用,因為在區塊中換行沒有意義。
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: [
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 是 Value,而不是 Expression,因此如果你需要在僅接受值的場合使用表達式,將其括在括號中永遠都能奏效。
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 迴圈可以是正常的,包含要執行的表達式區塊,也可以是後置的,包含單一表達式。沒有 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]
]
陣列、物件和範圍理解,在最通用的層級。理解可以是正常的,包含要執行的表達式區塊,也可以是後置的,包含單一表達式。
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
]
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, 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
]
算術和邏輯運算子,對一個或多個運算元執行運算。它們在此按優先順序分組。實際的優先順序規則定義在頁面底部。如果我們能將這些規則中的大多數組合成單一的通用 Operand OpSymbol Operand 型別規則,會更簡短,但為了使優先順序約束成為可能,需要有單獨的規則。
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
]
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
exports.parser = new Parser
tokens : tokens.join ' '
bnf : grammar
operators : operators.reverse()
startSymbol : 'Root'