PageRenderTime 62ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/documentation/docs/lexer.html

http://github.com/jashkenas/coffee-script
HTML | 2024 lines | 1865 code | 159 blank | 0 comment | 0 complexity | 9c4487192ff7778f37ad5ad37144c273 MD5 | raw file
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>lexer.coffee</title>
  5. <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  6. <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
  7. <link rel="stylesheet" media="all" href="docco.css" />
  8. </head>
  9. <body>
  10. <div id="container">
  11. <div id="background"></div>
  12. <ul id="jump_to">
  13. <li>
  14. <a class="large" href="javascript:void(0);">Jump To &hellip;</a>
  15. <a class="small" href="javascript:void(0);">+</a>
  16. <div id="jump_wrapper">
  17. <div id="jump_page_wrapper">
  18. <div id="jump_page">
  19. <a class="source" href="browser.html">
  20. browser.coffee
  21. </a>
  22. <a class="source" href="cake.html">
  23. cake.coffee
  24. </a>
  25. <a class="source" href="coffee-script.html">
  26. coffee-script.coffee
  27. </a>
  28. <a class="source" href="command.html">
  29. command.coffee
  30. </a>
  31. <a class="source" href="grammar.html">
  32. grammar.coffee
  33. </a>
  34. <a class="source" href="helpers.html">
  35. helpers.coffee
  36. </a>
  37. <a class="source" href="index.html">
  38. index.coffee
  39. </a>
  40. <a class="source" href="lexer.html">
  41. lexer.coffee
  42. </a>
  43. <a class="source" href="nodes.html">
  44. nodes.coffee
  45. </a>
  46. <a class="source" href="optparse.html">
  47. optparse.coffee
  48. </a>
  49. <a class="source" href="register.html">
  50. register.coffee
  51. </a>
  52. <a class="source" href="repl.html">
  53. repl.coffee
  54. </a>
  55. <a class="source" href="rewriter.html">
  56. rewriter.coffee
  57. </a>
  58. <a class="source" href="scope.html">
  59. scope.litcoffee
  60. </a>
  61. <a class="source" href="sourcemap.html">
  62. sourcemap.litcoffee
  63. </a>
  64. </div>
  65. </div>
  66. </li>
  67. </ul>
  68. <ul class="sections">
  69. <li id="title">
  70. <div class="annotation">
  71. <h1>lexer.coffee</h1>
  72. </div>
  73. </li>
  74. <li id="section-1">
  75. <div class="annotation">
  76. <div class="pilwrap ">
  77. <a class="pilcrow" href="#section-1">&#182;</a>
  78. </div>
  79. <p>The CoffeeScript Lexer. Uses a series of token-matching regexes to attempt
  80. matches against the beginning of the source code. When a match is found,
  81. a token is produced, we consume the match, and start again. Tokens are in the
  82. form:</p>
  83. <pre><code>[tag, value, locationData]
  84. </code></pre><p>where locationData is {first_line, first_column, last_line, last_column}, which is a
  85. format that can be fed directly into <a href="http://github.com/zaach/jison">Jison</a>. These
  86. are read by jison in the <code>parser.lexer</code> function defined in coffee-script.coffee.</p>
  87. </div>
  88. <div class="content"><div class='highlight'><pre>
  89. {Rewriter, INVERSES} = <span class="hljs-built_in">require</span> <span class="hljs-string">'./rewriter'</span></pre></div></div>
  90. </li>
  91. <li id="section-2">
  92. <div class="annotation">
  93. <div class="pilwrap ">
  94. <a class="pilcrow" href="#section-2">&#182;</a>
  95. </div>
  96. <p>Import the helpers we need.</p>
  97. </div>
  98. <div class="content"><div class='highlight'><pre>{count, starts, compact, repeat, invertLiterate,
  99. locationDataToString, throwSyntaxError} = <span class="hljs-built_in">require</span> <span class="hljs-string">'./helpers'</span></pre></div></div>
  100. </li>
  101. <li id="section-3">
  102. <div class="annotation">
  103. <div class="pilwrap ">
  104. <a class="pilcrow" href="#section-3">&#182;</a>
  105. </div>
  106. <h2 id="the-lexer-class">The Lexer Class</h2>
  107. </div>
  108. </li>
  109. <li id="section-4">
  110. <div class="annotation">
  111. <div class="pilwrap ">
  112. <a class="pilcrow" href="#section-4">&#182;</a>
  113. </div>
  114. </div>
  115. </li>
  116. <li id="section-5">
  117. <div class="annotation">
  118. <div class="pilwrap ">
  119. <a class="pilcrow" href="#section-5">&#182;</a>
  120. </div>
  121. <p>The Lexer class reads a stream of CoffeeScript and divvies it up into tagged
  122. tokens. Some potential ambiguity in the grammar has been avoided by
  123. pushing some extra smarts into the Lexer.</p>
  124. </div>
  125. <div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Lexer = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Lexer</span></span></pre></div></div>
  126. </li>
  127. <li id="section-6">
  128. <div class="annotation">
  129. <div class="pilwrap ">
  130. <a class="pilcrow" href="#section-6">&#182;</a>
  131. </div>
  132. <p><strong>tokenize</strong> is the Lexers main method. Scan by attempting to match tokens
  133. one at a time, using a regular expression anchored at the start of the
  134. remaining code, or a custom recursive token-matching method
  135. (for interpolations). When the next token has been recorded, we move forward
  136. within the code past the token, and begin again.</p>
  137. <p>Each tokenizing method is responsible for returning the number of characters
  138. it has consumed.</p>
  139. <p>Before returning the token stream, run it through the <a href="rewriter.html">Rewriter</a>.</p>
  140. </div>
  141. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">tokenize</span>: <span class="hljs-function"><span class="hljs-params">(code, opts = {})</span> -&gt;</span>
  142. <span class="hljs-property">@literate</span> = opts.literate <span class="hljs-comment"># Are we lexing literate CoffeeScript?</span>
  143. <span class="hljs-property">@indent</span> = <span class="hljs-number">0</span> <span class="hljs-comment"># The current indentation level.</span>
  144. <span class="hljs-property">@baseIndent</span> = <span class="hljs-number">0</span> <span class="hljs-comment"># The overall minimum indentation level</span>
  145. <span class="hljs-property">@indebt</span> = <span class="hljs-number">0</span> <span class="hljs-comment"># The over-indentation at the current level.</span>
  146. <span class="hljs-property">@outdebt</span> = <span class="hljs-number">0</span> <span class="hljs-comment"># The under-outdentation at the current level.</span>
  147. <span class="hljs-property">@indents</span> = [] <span class="hljs-comment"># The stack of all current indentation levels.</span>
  148. <span class="hljs-property">@ends</span> = [] <span class="hljs-comment"># The stack for pairing up tokens.</span>
  149. <span class="hljs-property">@tokens</span> = [] <span class="hljs-comment"># Stream of parsed tokens in the form `['TYPE', value, location data]`.</span>
  150. <span class="hljs-property">@seenFor</span> = <span class="hljs-literal">no</span> <span class="hljs-comment"># Used to recognize FORIN and FOROF tokens.</span>
  151. <span class="hljs-property">@chunkLine</span> =
  152. opts.line <span class="hljs-keyword">or</span> <span class="hljs-number">0</span> <span class="hljs-comment"># The start line for the current @chunk.</span>
  153. <span class="hljs-property">@chunkColumn</span> =
  154. opts.column <span class="hljs-keyword">or</span> <span class="hljs-number">0</span> <span class="hljs-comment"># The start column of the current @chunk.</span>
  155. code = <span class="hljs-property">@clean</span> code <span class="hljs-comment"># The stripped, cleaned original source code.</span></pre></div></div>
  156. </li>
  157. <li id="section-7">
  158. <div class="annotation">
  159. <div class="pilwrap ">
  160. <a class="pilcrow" href="#section-7">&#182;</a>
  161. </div>
  162. <p>At every position, run through this list of attempted matches,
  163. short-circuiting if any of them succeed. Their order determines precedence:
  164. <code>@literalToken</code> is the fallback catch-all.</p>
  165. </div>
  166. <div class="content"><div class='highlight'><pre> i = <span class="hljs-number">0</span>
  167. <span class="hljs-keyword">while</span> <span class="hljs-property">@chunk</span> = code[i..]
  168. consumed = \
  169. <span class="hljs-property">@identifierToken</span>() <span class="hljs-keyword">or</span>
  170. <span class="hljs-property">@commentToken</span>() <span class="hljs-keyword">or</span>
  171. <span class="hljs-property">@whitespaceToken</span>() <span class="hljs-keyword">or</span>
  172. <span class="hljs-property">@lineToken</span>() <span class="hljs-keyword">or</span>
  173. <span class="hljs-property">@stringToken</span>() <span class="hljs-keyword">or</span>
  174. <span class="hljs-property">@numberToken</span>() <span class="hljs-keyword">or</span>
  175. <span class="hljs-property">@regexToken</span>() <span class="hljs-keyword">or</span>
  176. <span class="hljs-property">@jsToken</span>() <span class="hljs-keyword">or</span>
  177. <span class="hljs-property">@literalToken</span>()</pre></div></div>
  178. </li>
  179. <li id="section-8">
  180. <div class="annotation">
  181. <div class="pilwrap ">
  182. <a class="pilcrow" href="#section-8">&#182;</a>
  183. </div>
  184. <p>Update position</p>
  185. </div>
  186. <div class="content"><div class='highlight'><pre> [<span class="hljs-property">@chunkLine</span>, <span class="hljs-property">@chunkColumn</span>] = <span class="hljs-property">@getLineAndColumnFromChunk</span> consumed
  187. i += consumed
  188. <span class="hljs-keyword">return</span> {<span class="hljs-property">@tokens</span>, <span class="hljs-attribute">index</span>: i} <span class="hljs-keyword">if</span> opts.untilBalanced <span class="hljs-keyword">and</span> <span class="hljs-property">@ends</span>.length <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
  189. <span class="hljs-property">@closeIndentation</span>()
  190. <span class="hljs-property">@error</span> <span class="hljs-string">"missing <span class="hljs-subst">#{end.tag}</span>"</span>, end.origin[<span class="hljs-number">2</span>] <span class="hljs-keyword">if</span> end = <span class="hljs-property">@ends</span>.pop()
  191. <span class="hljs-keyword">return</span> <span class="hljs-property">@tokens</span> <span class="hljs-keyword">if</span> opts.rewrite <span class="hljs-keyword">is</span> <span class="hljs-literal">off</span>
  192. (<span class="hljs-keyword">new</span> Rewriter).rewrite <span class="hljs-property">@tokens</span></pre></div></div>
  193. </li>
  194. <li id="section-9">
  195. <div class="annotation">
  196. <div class="pilwrap ">
  197. <a class="pilcrow" href="#section-9">&#182;</a>
  198. </div>
  199. <p>Preprocess the code to remove leading and trailing whitespace, carriage
  200. returns, etc. If were lexing literate CoffeeScript, strip external Markdown
  201. by removing all lines that arent indented by at least four spaces or a tab.</p>
  202. </div>
  203. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">clean</span>: <span class="hljs-function"><span class="hljs-params">(code)</span> -&gt;</span>
  204. code = code.slice(<span class="hljs-number">1</span>) <span class="hljs-keyword">if</span> code.charCodeAt(<span class="hljs-number">0</span>) <span class="hljs-keyword">is</span> BOM
  205. code = code.replace(<span class="hljs-regexp">/\r/g</span>, <span class="hljs-string">''</span>).replace TRAILING_SPACES, <span class="hljs-string">''</span>
  206. <span class="hljs-keyword">if</span> WHITESPACE.test code
  207. code = <span class="hljs-string">"\n<span class="hljs-subst">#{code}</span>"</span>
  208. <span class="hljs-property">@chunkLine</span>--
  209. code = invertLiterate code <span class="hljs-keyword">if</span> <span class="hljs-property">@literate</span>
  210. code</pre></div></div>
  211. </li>
  212. <li id="section-10">
  213. <div class="annotation">
  214. <div class="pilwrap ">
  215. <a class="pilcrow" href="#section-10">&#182;</a>
  216. </div>
  217. <h2 id="tokenizers">Tokenizers</h2>
  218. </div>
  219. </li>
  220. <li id="section-11">
  221. <div class="annotation">
  222. <div class="pilwrap ">
  223. <a class="pilcrow" href="#section-11">&#182;</a>
  224. </div>
  225. </div>
  226. </li>
  227. <li id="section-12">
  228. <div class="annotation">
  229. <div class="pilwrap ">
  230. <a class="pilcrow" href="#section-12">&#182;</a>
  231. </div>
  232. <p>Matches identifying literals: variables, keywords, method names, etc.
  233. Check to ensure that JavaScript reserved words arent being used as
  234. identifiers. Because CoffeeScript reserves a handful of keywords that are
  235. allowed in JavaScript, were careful not to tag them as keywords when
  236. referenced as property names here, so you can still do <code>jQuery.is()</code> even
  237. though <code>is</code> means <code>===</code> otherwise.</p>
  238. </div>
  239. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">identifierToken</span>:<span class="hljs-function"> -&gt;</span>
  240. <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">unless</span> match = IDENTIFIER.exec <span class="hljs-property">@chunk</span>
  241. [input, id, colon] = match</pre></div></div>
  242. </li>
  243. <li id="section-13">
  244. <div class="annotation">
  245. <div class="pilwrap ">
  246. <a class="pilcrow" href="#section-13">&#182;</a>
  247. </div>
  248. <p>Preserve length of id for location data</p>
  249. </div>
  250. <div class="content"><div class='highlight'><pre> idLength = id.length
  251. poppedToken = <span class="hljs-literal">undefined</span>
  252. <span class="hljs-keyword">if</span> id <span class="hljs-keyword">is</span> <span class="hljs-string">'own'</span> <span class="hljs-keyword">and</span> <span class="hljs-property">@tag</span>() <span class="hljs-keyword">is</span> <span class="hljs-string">'FOR'</span>
  253. <span class="hljs-property">@token</span> <span class="hljs-string">'OWN'</span>, id
  254. <span class="hljs-keyword">return</span> id.length
  255. <span class="hljs-keyword">if</span> id <span class="hljs-keyword">is</span> <span class="hljs-string">'from'</span> <span class="hljs-keyword">and</span> <span class="hljs-property">@tag</span>() <span class="hljs-keyword">is</span> <span class="hljs-string">'YIELD'</span>
  256. <span class="hljs-property">@token</span> <span class="hljs-string">'FROM'</span>, id
  257. <span class="hljs-keyword">return</span> id.length
  258. [..., prev] = <span class="hljs-property">@tokens</span>
  259. forcedIdentifier = colon <span class="hljs-keyword">or</span> prev? <span class="hljs-keyword">and</span>
  260. (prev[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">'.'</span>, <span class="hljs-string">'?.'</span>, <span class="hljs-string">'::'</span>, <span class="hljs-string">'?::'</span>] <span class="hljs-keyword">or</span>
  261. <span class="hljs-keyword">not</span> prev.spaced <span class="hljs-keyword">and</span> prev[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'@'</span>)
  262. tag = <span class="hljs-string">'IDENTIFIER'</span>
  263. <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> forcedIdentifier <span class="hljs-keyword">and</span> (id <span class="hljs-keyword">in</span> JS_KEYWORDS <span class="hljs-keyword">or</span> id <span class="hljs-keyword">in</span> COFFEE_KEYWORDS)
  264. tag = id.toUpperCase()
  265. <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'WHEN'</span> <span class="hljs-keyword">and</span> <span class="hljs-property">@tag</span>() <span class="hljs-keyword">in</span> LINE_BREAK
  266. tag = <span class="hljs-string">'LEADING_WHEN'</span>
  267. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'FOR'</span>
  268. <span class="hljs-property">@seenFor</span> = <span class="hljs-literal">yes</span>
  269. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'UNLESS'</span>
  270. tag = <span class="hljs-string">'IF'</span>
  271. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> UNARY
  272. tag = <span class="hljs-string">'UNARY'</span>
  273. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> RELATION
  274. <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">isnt</span> <span class="hljs-string">'INSTANCEOF'</span> <span class="hljs-keyword">and</span> <span class="hljs-property">@seenFor</span>
  275. tag = <span class="hljs-string">'FOR'</span> + tag
  276. <span class="hljs-property">@seenFor</span> = <span class="hljs-literal">no</span>
  277. <span class="hljs-keyword">else</span>
  278. tag = <span class="hljs-string">'RELATION'</span>
  279. <span class="hljs-keyword">if</span> <span class="hljs-property">@value</span>() <span class="hljs-keyword">is</span> <span class="hljs-string">'!'</span>
  280. poppedToken = <span class="hljs-property">@tokens</span>.pop()
  281. id = <span class="hljs-string">'!'</span> + id
  282. <span class="hljs-keyword">if</span> id <span class="hljs-keyword">in</span> JS_FORBIDDEN
  283. <span class="hljs-keyword">if</span> forcedIdentifier
  284. tag = <span class="hljs-string">'IDENTIFIER'</span>
  285. id = <span class="hljs-keyword">new</span> String id
  286. id.reserved = <span class="hljs-literal">yes</span>
  287. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> id <span class="hljs-keyword">in</span> RESERVED
  288. <span class="hljs-property">@error</span> <span class="hljs-string">"reserved word '<span class="hljs-subst">#{id}</span>'"</span>, <span class="hljs-attribute">length</span>: id.length
  289. <span class="hljs-keyword">unless</span> forcedIdentifier
  290. <span class="hljs-keyword">if</span> id <span class="hljs-keyword">in</span> COFFEE_ALIASES
  291. alias = id
  292. id = COFFEE_ALIAS_MAP[id]
  293. tag = <span class="hljs-keyword">switch</span> id
  294. <span class="hljs-keyword">when</span> <span class="hljs-string">'!'</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'UNARY'</span>
  295. <span class="hljs-keyword">when</span> <span class="hljs-string">'=='</span>, <span class="hljs-string">'!='</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'COMPARE'</span>
  296. <span class="hljs-keyword">when</span> <span class="hljs-string">'&amp;&amp;'</span>, <span class="hljs-string">'||'</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'LOGIC'</span>
  297. <span class="hljs-keyword">when</span> <span class="hljs-string">'true'</span>, <span class="hljs-string">'false'</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'BOOL'</span>
  298. <span class="hljs-keyword">when</span> <span class="hljs-string">'break'</span>, <span class="hljs-string">'continue'</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'STATEMENT'</span>
  299. <span class="hljs-keyword">else</span> tag
  300. tagToken = <span class="hljs-property">@token</span> tag, id, <span class="hljs-number">0</span>, idLength
  301. tagToken.origin = [tag, alias, tagToken[<span class="hljs-number">2</span>]] <span class="hljs-keyword">if</span> alias
  302. tagToken.variable = <span class="hljs-keyword">not</span> forcedIdentifier
  303. <span class="hljs-keyword">if</span> poppedToken
  304. [tagToken[<span class="hljs-number">2</span>].first_line, tagToken[<span class="hljs-number">2</span>].first_column] =
  305. [poppedToken[<span class="hljs-number">2</span>].first_line, poppedToken[<span class="hljs-number">2</span>].first_column]
  306. <span class="hljs-keyword">if</span> colon
  307. colonOffset = input.lastIndexOf <span class="hljs-string">':'</span>
  308. <span class="hljs-property">@token</span> <span class="hljs-string">':'</span>, <span class="hljs-string">':'</span>, colonOffset, colon.length
  309. input.length</pre></div></div>
  310. </li>
  311. <li id="section-14">
  312. <div class="annotation">
  313. <div class="pilwrap ">
  314. <a class="pilcrow" href="#section-14">&#182;</a>
  315. </div>
  316. <p>Matches numbers, including decimals, hex, and exponential notation.
  317. Be careful not to interfere with ranges-in-progress.</p>
  318. </div>
  319. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">numberToken</span>:<span class="hljs-function"> -&gt;</span>
  320. <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">unless</span> match = NUMBER.exec <span class="hljs-property">@chunk</span>
  321. number = match[<span class="hljs-number">0</span>]
  322. lexedLength = number.length
  323. <span class="hljs-keyword">if</span> <span class="hljs-regexp">/^0[BOX]/</span>.test number
  324. <span class="hljs-property">@error</span> <span class="hljs-string">"radix prefix in '<span class="hljs-subst">#{number}</span>' must be lowercase"</span>, <span class="hljs-attribute">offset</span>: <span class="hljs-number">1</span>
  325. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-regexp">/E/</span>.test(number) <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> <span class="hljs-regexp">/^0x/</span>.test number
  326. <span class="hljs-property">@error</span> <span class="hljs-string">"exponential notation in '<span class="hljs-subst">#{number}</span>' must be indicated with a lowercase 'e'"</span>,
  327. <span class="hljs-attribute">offset</span>: number.indexOf(<span class="hljs-string">'E'</span>)
  328. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-regexp">/^0\d*[89]/</span>.test number
  329. <span class="hljs-property">@error</span> <span class="hljs-string">"decimal literal '<span class="hljs-subst">#{number}</span>' must not be prefixed with '0'"</span>, <span class="hljs-attribute">length</span>: lexedLength
  330. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-regexp">/^0\d+/</span>.test number
  331. <span class="hljs-property">@error</span> <span class="hljs-string">"octal literal '<span class="hljs-subst">#{number}</span>' must be prefixed with '0o'"</span>, <span class="hljs-attribute">length</span>: lexedLength
  332. <span class="hljs-keyword">if</span> octalLiteral = <span class="hljs-regexp">/^0o([0-7]+)/</span>.exec number
  333. number = <span class="hljs-string">'0x'</span> + parseInt(octalLiteral[<span class="hljs-number">1</span>], <span class="hljs-number">8</span>).toString <span class="hljs-number">16</span>
  334. <span class="hljs-keyword">if</span> binaryLiteral = <span class="hljs-regexp">/^0b([01]+)/</span>.exec number
  335. number = <span class="hljs-string">'0x'</span> + parseInt(binaryLiteral[<span class="hljs-number">1</span>], <span class="hljs-number">2</span>).toString <span class="hljs-number">16</span>
  336. <span class="hljs-property">@token</span> <span class="hljs-string">'NUMBER'</span>, number, <span class="hljs-number">0</span>, lexedLength
  337. lexedLength</pre></div></div>
  338. </li>
  339. <li id="section-15">
  340. <div class="annotation">
  341. <div class="pilwrap ">
  342. <a class="pilcrow" href="#section-15">&#182;</a>
  343. </div>
  344. <p>Matches strings, including multi-line strings, as well as heredocs, with or without
  345. interpolation.</p>
  346. </div>
  347. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">stringToken</span>:<span class="hljs-function"> -&gt;</span>
  348. [quote] = STRING_START.exec(<span class="hljs-property">@chunk</span>) || []
  349. <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">unless</span> quote
  350. regex = <span class="hljs-keyword">switch</span> quote
  351. <span class="hljs-keyword">when</span> <span class="hljs-string">"'"</span> <span class="hljs-keyword">then</span> STRING_SINGLE
  352. <span class="hljs-keyword">when</span> <span class="hljs-string">'"'</span> <span class="hljs-keyword">then</span> STRING_DOUBLE
  353. <span class="hljs-keyword">when</span> <span class="hljs-string">"'''"</span> <span class="hljs-keyword">then</span> HEREDOC_SINGLE
  354. <span class="hljs-keyword">when</span> <span class="hljs-string">'"""'</span> <span class="hljs-keyword">then</span> HEREDOC_DOUBLE
  355. heredoc = quote.length <span class="hljs-keyword">is</span> <span class="hljs-number">3</span>
  356. {tokens, <span class="hljs-attribute">index</span>: end} = <span class="hljs-property">@matchWithInterpolations</span> regex, quote
  357. $ = tokens.length - <span class="hljs-number">1</span>
  358. delimiter = quote.charAt(<span class="hljs-number">0</span>)
  359. <span class="hljs-keyword">if</span> heredoc</pre></div></div>
  360. </li>
  361. <li id="section-16">
  362. <div class="annotation">
  363. <div class="pilwrap ">
  364. <a class="pilcrow" href="#section-16">&#182;</a>
  365. </div>
  366. <p>Find the smallest indentation. It will be removed from all lines later.</p>
  367. </div>
  368. <div class="content"><div class='highlight'><pre> indent = <span class="hljs-literal">null</span>
  369. doc = (token[<span class="hljs-number">1</span>] <span class="hljs-keyword">for</span> token, i <span class="hljs-keyword">in</span> tokens <span class="hljs-keyword">when</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'NEOSTRING'</span>).join <span class="hljs-string">'#{}'</span>
  370. <span class="hljs-keyword">while</span> match = HEREDOC_INDENT.exec doc
  371. attempt = match[<span class="hljs-number">1</span>]
  372. indent = attempt <span class="hljs-keyword">if</span> indent <span class="hljs-keyword">is</span> <span class="hljs-literal">null</span> <span class="hljs-keyword">or</span> <span class="hljs-number">0</span> &lt; attempt.length &lt; indent.length
  373. indentRegex = <span class="hljs-regexp">/// ^<span class="hljs-subst">#{indent}</span> ///</span>gm <span class="hljs-keyword">if</span> indent
  374. <span class="hljs-property">@mergeInterpolationTokens</span> tokens, {delimiter}, <span class="hljs-function"><span class="hljs-params">(value, i)</span> =&gt;</span>
  375. value = <span class="hljs-property">@formatString</span> value
  376. value = value.replace LEADING_BLANK_LINE, <span class="hljs-string">''</span> <span class="hljs-keyword">if</span> i <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
  377. value = value.replace TRAILING_BLANK_LINE, <span class="hljs-string">''</span> <span class="hljs-keyword">if</span> i <span class="hljs-keyword">is</span> $
  378. value = value.replace indentRegex, <span class="hljs-string">''</span> <span class="hljs-keyword">if</span> indentRegex
  379. value
  380. <span class="hljs-keyword">else</span>
  381. <span class="hljs-property">@mergeInterpolationTokens</span> tokens, {delimiter}, <span class="hljs-function"><span class="hljs-params">(value, i)</span> =&gt;</span>
  382. value = <span class="hljs-property">@formatString</span> value
  383. value = value.replace SIMPLE_STRING_OMIT, <span class="hljs-function"><span class="hljs-params">(match, offset)</span> -&gt;</span>
  384. <span class="hljs-keyword">if</span> (i <span class="hljs-keyword">is</span> <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> offset <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>) <span class="hljs-keyword">or</span>
  385. (i <span class="hljs-keyword">is</span> $ <span class="hljs-keyword">and</span> offset + match.length <span class="hljs-keyword">is</span> value.length)
  386. <span class="hljs-string">''</span>
  387. <span class="hljs-keyword">else</span>
  388. <span class="hljs-string">' '</span>
  389. value
  390. end</pre></div></div>
  391. </li>
  392. <li id="section-17">
  393. <div class="annotation">
  394. <div class="pilwrap ">
  395. <a class="pilcrow" href="#section-17">&#182;</a>
  396. </div>
  397. <p>Matches and consumes comments.</p>
  398. </div>
  399. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">commentToken</span>:<span class="hljs-function"> -&gt;</span>
  400. <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">unless</span> match = <span class="hljs-property">@chunk</span>.match COMMENT
  401. [comment, here] = match
  402. <span class="hljs-keyword">if</span> here
  403. <span class="hljs-keyword">if</span> match = HERECOMMENT_ILLEGAL.exec comment
  404. <span class="hljs-property">@error</span> <span class="hljs-string">"block comments cannot contain <span class="hljs-subst">#{match[<span class="hljs-number">0</span>]}</span>"</span>,
  405. <span class="hljs-attribute">offset</span>: match.index, <span class="hljs-attribute">length</span>: match[<span class="hljs-number">0</span>].length
  406. <span class="hljs-keyword">if</span> here.indexOf(<span class="hljs-string">'\n'</span>) &gt;= <span class="hljs-number">0</span>
  407. here = here.replace <span class="hljs-regexp">/// \n <span class="hljs-subst">#{repeat <span class="hljs-string">' '</span>, <span class="hljs-property">@indent</span>}</span> ///</span>g, <span class="hljs-string">'\n'</span>
  408. <span class="hljs-property">@token</span> <span class="hljs-string">'HERECOMMENT'</span>, here, <span class="hljs-number">0</span>, comment.length
  409. comment.length</pre></div></div>
  410. </li>
  411. <li id="section-18">
  412. <div class="annotation">
  413. <div class="pilwrap ">
  414. <a class="pilcrow" href="#section-18">&#182;</a>
  415. </div>
  416. <p>Matches JavaScript interpolated directly into the source via backticks.</p>
  417. </div>
  418. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">jsToken</span>:<span class="hljs-function"> -&gt;</span>
  419. <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">unless</span> <span class="hljs-property">@chunk</span>.charAt(<span class="hljs-number">0</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'`'</span> <span class="hljs-keyword">and</span> match = JSTOKEN.exec <span class="hljs-property">@chunk</span>
  420. <span class="hljs-property">@token</span> <span class="hljs-string">'JS'</span>, (script = match[<span class="hljs-number">0</span>])[<span class="hljs-number">1.</span>..-<span class="hljs-number">1</span>], <span class="hljs-number">0</span>, script.length
  421. script.length</pre></div></div>
  422. </li>
  423. <li id="section-19">
  424. <div class="annotation">
  425. <div class="pilwrap ">
  426. <a class="pilcrow" href="#section-19">&#182;</a>
  427. </div>
  428. <p>Matches regular expression literals, as well as multiline extended ones.
  429. Lexing regular expressions is difficult to distinguish from division, so we
  430. borrow some basic heuristics from JavaScript and Ruby.</p>
  431. </div>
  432. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">regexToken</span>:<span class="hljs-function"> -&gt;</span>
  433. <span class="hljs-keyword">switch</span>
  434. <span class="hljs-keyword">when</span> match = REGEX_ILLEGAL.exec <span class="hljs-property">@chunk</span>
  435. <span class="hljs-property">@error</span> <span class="hljs-string">"regular expressions cannot begin with <span class="hljs-subst">#{match[<span class="hljs-number">2</span>]}</span>"</span>,
  436. <span class="hljs-attribute">offset</span>: match.index + match[<span class="hljs-number">1</span>].length
  437. <span class="hljs-keyword">when</span> match = <span class="hljs-property">@matchWithInterpolations</span> HEREGEX, <span class="hljs-string">'///'</span>
  438. {tokens, index} = match
  439. <span class="hljs-keyword">when</span> match = REGEX.exec <span class="hljs-property">@chunk</span>
  440. [regex, body, closed] = match
  441. <span class="hljs-property">@validateEscapes</span> body, <span class="hljs-attribute">isRegex</span>: <span class="hljs-literal">yes</span>, <span class="hljs-attribute">offsetInChunk</span>: <span class="hljs-number">1</span>
  442. index = regex.length
  443. [..., prev] = <span class="hljs-property">@tokens</span>
  444. <span class="hljs-keyword">if</span> prev
  445. <span class="hljs-keyword">if</span> prev.spaced <span class="hljs-keyword">and</span> prev[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> CALLABLE
  446. <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> closed <span class="hljs-keyword">or</span> POSSIBLY_DIVISION.test regex
  447. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> prev[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> NOT_REGEX
  448. <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>
  449. <span class="hljs-property">@error</span> <span class="hljs-string">'missing / (unclosed regex)'</span> <span class="hljs-keyword">unless</span> closed
  450. <span class="hljs-keyword">else</span>
  451. <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>
  452. [flags] = REGEX_FLAGS.exec <span class="hljs-property">@chunk</span>[index..]
  453. end = index + flags.length
  454. origin = <span class="hljs-property">@makeToken</span> <span class="hljs-string">'REGEX'</span>, <span class="hljs-literal">null</span>, <span class="hljs-number">0</span>, end
  455. <span class="hljs-keyword">switch</span>
  456. <span class="hljs-keyword">when</span> <span class="hljs-keyword">not</span> VALID_FLAGS.test flags
  457. <span class="hljs-property">@error</span> <span class="hljs-string">"invalid regular expression flags <span class="hljs-subst">#{flags}</span>"</span>, <span class="hljs-attribute">offset</span>: index, <span class="hljs-attribute">length</span>: flags.length
  458. <span class="hljs-keyword">when</span> regex <span class="hljs-keyword">or</span> tokens.length <span class="hljs-keyword">is</span> <span class="hljs-number">1</span>
  459. body ?= <span class="hljs-property">@formatHeregex</span> tokens[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>]
  460. <span class="hljs-property">@token</span> <span class="hljs-string">'REGEX'</span>, <span class="hljs-string">"<span class="hljs-subst">#{<span class="hljs-property">@makeDelimitedLiteral</span> body, delimiter: <span class="hljs-string">'/'</span>}</span><span class="hljs-subst">#{flags}</span>"</span>, <span class="hljs-number">0</span>, end, origin
  461. <span class="hljs-keyword">else</span>
  462. <span class="hljs-property">@token</span> <span class="hljs-string">'REGEX_START'</span>, <span class="hljs-string">'('</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, origin
  463. <span class="hljs-property">@token</span> <span class="hljs-string">'IDENTIFIER'</span>, <span class="hljs-string">'RegExp'</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>
  464. <span class="hljs-property">@token</span> <span class="hljs-string">'CALL_START'</span>, <span class="hljs-string">'('</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>
  465. <span class="hljs-property">@mergeInterpolationTokens</span> tokens, {<span class="hljs-attribute">delimiter</span>: <span class="hljs-string">'"'</span>, <span class="hljs-attribute">double</span>: <span class="hljs-literal">yes</span>}, <span class="hljs-property">@formatHeregex</span>
  466. <span class="hljs-keyword">if</span> flags
  467. <span class="hljs-property">@token</span> <span class="hljs-string">','</span>, <span class="hljs-string">','</span>, index, <span class="hljs-number">0</span>
  468. <span class="hljs-property">@token</span> <span class="hljs-string">'STRING'</span>, <span class="hljs-string">'"'</span> + flags + <span class="hljs-string">'"'</span>, index, flags.length
  469. <span class="hljs-property">@token</span> <span class="hljs-string">')'</span>, <span class="hljs-string">')'</span>, end, <span class="hljs-number">0</span>
  470. <span class="hljs-property">@token</span> <span class="hljs-string">'REGEX_END'</span>, <span class="hljs-string">')'</span>, end, <span class="hljs-number">0</span>
  471. end</pre></div></div>
  472. </li>
  473. <li id="section-20">
  474. <div class="annotation">
  475. <div class="pilwrap ">
  476. <a class="pilcrow" href="#section-20">&#182;</a>
  477. </div>
  478. <p>Matches newlines, indents, and outdents, and determines which is which.
  479. If we can detect that the current line is continued onto the next line,
  480. then the newline is suppressed:</p>
  481. <pre><code>elements
  482. .each( ... )
  483. .map( ... )
  484. </code></pre><p>Keeps track of the level of indentation, because a single outdent token
  485. can close multiple indents, so we need to know how far in we happen to be.</p>
  486. </div>
  487. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">lineToken</span>:<span class="hljs-function"> -&gt;</span>
  488. <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">unless</span> match = MULTI_DENT.exec <span class="hljs-property">@chunk</span>
  489. indent = match[<span class="hljs-number">0</span>]
  490. <span class="hljs-property">@seenFor</span> = <span class="hljs-literal">no</span>
  491. size = indent.length - <span class="hljs-number">1</span> - indent.lastIndexOf <span class="hljs-string">'\n'</span>
  492. noNewlines = <span class="hljs-property">@unfinished</span>()
  493. <span class="hljs-keyword">if</span> size - <span class="hljs-property">@indebt</span> <span class="hljs-keyword">is</span> <span class="hljs-property">@indent</span>
  494. <span class="hljs-keyword">if</span> noNewlines <span class="hljs-keyword">then</span> <span class="hljs-property">@suppressNewlines</span>() <span class="hljs-keyword">else</span> <span class="hljs-property">@newlineToken</span> <span class="hljs-number">0</span>
  495. <span class="hljs-keyword">return</span> indent.length
  496. <span class="hljs-keyword">if</span> size &gt; <span class="hljs-property">@indent</span>
  497. <span class="hljs-keyword">if</span> noNewlines
  498. <span class="hljs-property">@indebt</span> = size - <span class="hljs-property">@indent</span>
  499. <span class="hljs-property">@suppressNewlines</span>()
  500. <span class="hljs-keyword">return</span> indent.length
  501. <span class="hljs-keyword">unless</span> <span class="hljs-property">@tokens</span>.length
  502. <span class="hljs-property">@baseIndent</span> = <span class="hljs-property">@indent</span> = size
  503. <span class="hljs-keyword">return</span> indent.length
  504. diff = size - <span class="hljs-property">@indent</span> + <span class="hljs-property">@outdebt</span>
  505. <span class="hljs-property">@token</span> <span class="hljs-string">'INDENT'</span>, diff, indent.length - size, size
  506. <span class="hljs-property">@indents</span>.push diff
  507. <span class="hljs-property">@ends</span>.push {<span class="hljs-attribute">tag</span>: <span class="hljs-string">'OUTDENT'</span>}
  508. <span class="hljs-property">@outdebt</span> = <span class="hljs-property">@indebt</span> = <span class="hljs-number">0</span>
  509. <span class="hljs-property">@indent</span> = size
  510. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> size &lt; <span class="hljs-property">@baseIndent</span>
  511. <span class="hljs-property">@error</span> <span class="hljs-string">'missing indentation'</span>, <span class="hljs-attribute">offset</span>: indent.length
  512. <span class="hljs-keyword">else</span>
  513. <span class="hljs-property">@indebt</span> = <span class="hljs-number">0</span>
  514. <span class="hljs-property">@outdentToken</span> <span class="hljs-property">@indent</span> - size, noNewlines, indent.length
  515. indent.length</pre></div></div>
  516. </li>
  517. <li id="section-21">
  518. <div class="annotation">
  519. <div class="pilwrap ">
  520. <a class="pilcrow" href="#section-21">&#182;</a>
  521. </div>
  522. <p>Record an outdent token or multiple tokens, if we happen to be moving back
  523. inwards past several recorded indents. Sets new @indent value.</p>
  524. </div>
  525. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">outdentToken</span>: <span class="hljs-function"><span class="hljs-params">(moveOut, noNewlines, outdentLength)</span> -&gt;</span>
  526. decreasedIndent = <span class="hljs-property">@indent</span> - moveOut
  527. <span class="hljs-keyword">while</span> moveOut &gt; <span class="hljs-number">0</span>
  528. lastIndent = <span class="hljs-property">@indents</span>[<span class="hljs-property">@indents</span>.length - <span class="hljs-number">1</span>]
  529. <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> lastIndent
  530. moveOut = <span class="hljs-number">0</span>
  531. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> lastIndent <span class="hljs-keyword">is</span> <span class="hljs-property">@outdebt</span>
  532. moveOut -= <span class="hljs-property">@outdebt</span>
  533. <span class="hljs-property">@outdebt</span> = <span class="hljs-number">0</span>
  534. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> lastIndent &lt; <span class="hljs-property">@outdebt</span>
  535. <span class="hljs-property">@outdebt</span> -= lastIndent
  536. moveOut -= lastIndent
  537. <span class="hljs-keyword">else</span>
  538. dent = <span class="hljs-property">@indents</span>.pop() + <span class="hljs-property">@outdebt</span>
  539. <span class="hljs-keyword">if</span> outdentLength <span class="hljs-keyword">and</span> <span class="hljs-property">@chunk</span>[outdentLength] <span class="hljs-keyword">in</span> INDENTABLE_CLOSERS
  540. decreasedIndent -= dent - moveOut
  541. moveOut = dent
  542. <span class="hljs-property">@outdebt</span> = <span class="hljs-number">0</span></pre></div></div>
  543. </li>
  544. <li id="section-22">
  545. <div class="annotation">
  546. <div class="pilwrap ">
  547. <a class="pilcrow" href="#section-22">&#182;</a>
  548. </div>
  549. <p>pair might call outdentToken, so preserve decreasedIndent</p>
  550. </div>
  551. <div class="content"><div class='highlight'><pre> <span class="hljs-property">@pair</span> <span class="hljs-string">'OUTDENT'</span>
  552. <span class="hljs-property">@token</span> <span class="hljs-string">'OUTDENT'</span>, moveOut, <span class="hljs-number">0</span>, outdentLength
  553. moveOut -= dent
  554. <span class="hljs-property">@outdebt</span> -= moveOut <span class="hljs-keyword">if</span> dent
  555. <span class="hljs-property">@tokens</span>.pop() <span class="hljs-keyword">while</span> <span class="hljs-property">@value</span>() <span class="hljs-keyword">is</span> <span class="hljs-string">';'</span>
  556. <span class="hljs-property">@token</span> <span class="hljs-string">'TERMINATOR'</span>, <span class="hljs-string">'\n'</span>, outdentLength, <span class="hljs-number">0</span> <span class="hljs-keyword">unless</span> <span class="hljs-property">@tag</span>() <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">or</span> noNewlines
  557. <span class="hljs-property">@indent</span> = decreasedIndent
  558. <span class="hljs-keyword">this</span></pre></div></div>
  559. </li>
  560. <li id="section-23">
  561. <div class="annotation">
  562. <div class="pilwrap ">
  563. <a class="pilcrow" href="#section-23">&#182;</a>
  564. </div>
  565. <p>Matches and consumes non-meaningful whitespace. Tag the previous token
  566. as being spaced, because there are some cases where it makes a difference.</p>
  567. </div>
  568. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">whitespaceToken</span>:<span class="hljs-function"> -&gt;</span>
  569. <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <span class="hljs-keyword">unless</span> (match = WHITESPACE.exec <span class="hljs-property">@chunk</span>) <span class="hljs-keyword">or</span>
  570. (nline = <span class="hljs-property">@chunk</span>.charAt(<span class="hljs-number">0</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'\n'</span>)
  571. [..., prev] = <span class="hljs-property">@tokens</span>
  572. prev[<span class="hljs-keyword">if</span> match <span class="hljs-keyword">then</span> <span class="hljs-string">'spaced'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'newLine'</span>] = <span class="hljs-literal">true</span> <span class="hljs-keyword">if</span> prev
  573. <span class="hljs-keyword">if</span> match <span class="hljs-keyword">then</span> match[<span class="hljs-number">0</span>].length <span class="hljs-keyword">else</span> <span class="hljs-number">0</span></pre></div></div>
  574. </li>
  575. <li id="section-24">
  576. <div class="annotation">
  577. <div class="pilwrap ">
  578. <a class="pilcrow" href="#section-24">&#182;</a>
  579. </div>
  580. <p>Generate a newline token. Consecutive newlines get merged together.</p>
  581. </div>
  582. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">newlineToken</span>: <span class="hljs-function"><span class="hljs-params">(offset)</span> -&gt;</span>
  583. <span class="hljs-property">@tokens</span>.pop() <span class="hljs-keyword">while</span> <span class="hljs-property">@value</span>() <span class="hljs-keyword">is</span> <span class="hljs-string">';'</span>
  584. <span class="hljs-property">@token</span> <span class="hljs-string">'TERMINATOR'</span>, <span class="hljs-string">'\n'</span>, offset, <span class="hljs-number">0</span> <span class="hljs-keyword">unless</span> <span class="hljs-property">@tag</span>() <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span>
  585. <span class="hljs-keyword">this</span></pre></div></div>
  586. </li>
  587. <li id="section-25">
  588. <div class="annotation">
  589. <div class="pilwrap ">
  590. <a class="pilcrow" href="#section-25">&#182;</a>
  591. </div>
  592. <p>Use a <code>\</code> at a line-ending to suppress the newline.
  593. The slash is removed here once its job is done.</p>
  594. </div>
  595. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">suppressNewlines</span>:<span class="hljs-function"> -&gt;</span>
  596. <span class="hljs-property">@tokens</span>.pop() <span class="hljs-keyword">if</span> <span class="hljs-property">@value</span>() <span class="hljs-keyword">is</span> <span class="hljs-string">'\\'</span>
  597. <span class="hljs-keyword">this</span></pre></div></div>
  598. </li>
  599. <li id="section-26">
  600. <div class="annotation">
  601. <div class="pilwrap ">
  602. <a class="pilcrow" href="#section-26">&#182;</a>
  603. </div>
  604. <p>We treat all other single characters as a token. E.g.: <code>( ) , . !</code>
  605. Multi-character operators are also literal tokens, so that Jison can assign
  606. the proper order of operations. There are some symbols that we tag specially
  607. here. <code>;</code> and newlines are both treated as a <code>TERMINATOR</code>, we distinguish
  608. parentheses that indicate a method call from regular parentheses, and so on.</p>
  609. </div>
  610. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">literalToken</span>:<span class="hljs-function"> -&gt;</span>
  611. <span class="hljs-keyword">if</span> match = OPERATOR.exec <span class="hljs-property">@chunk</span>
  612. [value] = match
  613. <span class="hljs-property">@tagParameters</span>() <span class="hljs-keyword">if</span> CODE.test value
  614. <span class="hljs-keyword">else</span>
  615. value = <span class="hljs-property">@chunk</span>.charAt <span class="hljs-number">0</span>
  616. tag = value
  617. [..., prev] = <span class="hljs-property">@tokens</span>
  618. <span class="hljs-keyword">if</span> value <span class="hljs-keyword">is</span> <span class="hljs-string">'='</span> <span class="hljs-keyword">and</span> prev
  619. <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> prev[<span class="hljs-number">1</span>].reserved <span class="hljs-keyword">and</span> prev[<span class="hljs-number">1</span>] <span class="hljs-keyword">in</span> JS_FORBIDDEN
  620. prev = prev.origin <span class="hljs-keyword">if</span> prev.origin
  621. <span class="hljs-property">@error</span> <span class="hljs-string">"reserved word '<span class="hljs-subst">#{prev[<span class="hljs-number">1</span>]}</span>' can't be assigned"</span>, prev[<span class="hljs-number">2</span>]
  622. <span class="hljs-keyword">if</span> prev[<span class="hljs-number">1</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">'||'</span>, <span class="hljs-string">'&amp;&amp;'</span>]
  623. prev[<span class="hljs-number">0</span>] = <span class="hljs-string">'COMPOUND_ASSIGN'</span>
  624. prev[<span class="hljs-number">1</span>] += <span class="hljs-string">'='</span>
  625. <span class="hljs-keyword">return</span> value.length
  626. <span class="hljs-keyword">if</span> value <span class="hljs-keyword">is</span> <span class="hljs-string">';'</span>
  627. <span class="hljs-property">@seenFor</span> = <span class="hljs-literal">no</span>
  628. tag = <span class="hljs-string">'TERMINATOR'</span>
  629. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> value <span class="hljs-keyword">in</span> MATH <span class="hljs-keyword">then</span> tag = <span class="hljs-string">'MATH'</span>
  630. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> value <span class="hljs-keyword">in</span> COMPARE <span class="hljs-keyword">then</span> tag = <span class="hljs-string">'COMPARE'</span>
  631. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> value <span class="hljs-keyword">in</span> COMPOUND_ASSIGN <span class="hljs-keyword">then</span> tag = <span class="hljs-string">'COMPOUND_ASSIGN'</span>
  632. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> value <span class="hljs-keyword">in</span> UNARY <span class="hljs-keyword">then</span> tag = <span class="hljs-string">'UNARY'</span>
  633. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> value <span class="hljs-keyword">in</span> UNARY_MATH <span class="hljs-keyword">then</span> tag = <span class="hljs-string">'UNARY_MATH'</span>
  634. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> value <span class="hljs-keyword">in</span> SHIFT <span class="hljs-keyword">then</span> tag = <span class="hljs-string">'SHIFT'</span>
  635. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> value <span class="hljs-keyword">in</span> LOGIC <span class="hljs-keyword">or</span> value <span class="hljs-keyword">is</span> <span class="hljs-string">'?'</span> <span class="hljs-keyword">and</span> prev?.spaced <span class="hljs-keyword">then</span> tag = <span class="hljs-string">'LOGIC'</span>
  636. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> prev <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> prev.spaced
  637. <span class="hljs-keyword">if</span> value <span class="hljs-keyword">is</span> <span class="hljs-string">'('</span> <span class="hljs-keyword">and</span> prev[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> CALLABLE
  638. prev[<span class="hljs-number">0</span>] = <span class="hljs-string">'FUNC_EXIST'</span> <span class="hljs-keyword">if</span> prev[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'?'</span>
  639. tag = <span class="hljs-string">'CALL_START'</span>
  640. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> value <span class="hljs-keyword">is</span> <span class="hljs-string">'['</span> <span class="hljs-keyword">and</span> prev[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> INDEXABLE
  641. tag = <span class="hljs-string">'INDEX_START'</span>
  642. <span class="hljs-keyword">switch</span> prev[<span class="hljs-number">0</span>]
  643. <span class="hljs-keyword">when</span> <span class="hljs-string">'?'</span> <span class="hljs-keyword">then</span> prev[<span class="hljs-number">0</span>] = <span class="hljs-string">'INDEX_SOAK'</span>
  644. token = <span class="hljs-property">@makeToken</span> tag, value
  645. <span class="hljs-keyword">switch</span> value
  646. <span class="hljs-keyword">when</span> <span class="hljs-string">'('</span>, <span class="hljs-string">'{'</span>, <span class="hljs-string">'['</span> <span class="hljs-keyword">then</span> <span class="hljs-property">@ends</span>.push {<span class="hljs-attribute">tag</span>: INVERSES[value], <span class="hljs-attribute">origin</span>: token}
  647. <span class="hljs-keyword">when</span> <span class="hljs-string">')'</span>, <span class="hljs-string">'}'</span>, <span class="hljs-string">']'</span> <span class="hljs-keyword">then</span> <span class="hljs-property">@pair</span> value
  648. <span class="hljs-property">@tokens</span>.push token
  649. value.length</pre></div></div>
  650. </li>
  651. <li id="section-27">
  652. <div class="annotation">
  653. <div class="pilwrap ">
  654. <a class="pilcrow" href="#section-27">&#182;</a>
  655. </div>
  656. <h2 id="token-manipulators">Token Manipulators</h2>
  657. </div>
  658. </li>
  659. <li id="section-28">
  660. <div class="annotation">
  661. <div class="pilwrap ">
  662. <a class="pilcrow" href="#section-28">&#182;</a>
  663. </div>
  664. </div>
  665. </li>
  666. <li id="section-29">
  667. <div class="annotation">
  668. <div class="pilwrap ">
  669. <a class="pilcrow" href="#section-29">&#182;</a>
  670. </div>
  671. <p>A source of ambiguity in our grammar used to be parameter lists in function
  672. definitions versus argument lists in function calls. Walk backwards, tagging
  673. parameters specially in order to make things easier for the parser.</p>
  674. </div>
  675. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">tagParameters</span>:<span class="hljs-function"> -&gt;</span>
  676. <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span> <span class="hljs-keyword">if</span> <span class="hljs-property">@tag</span>() <span class="hljs-keyword">isnt</span> <span class="hljs-string">')'</span>
  677. stack = []
  678. {tokens} = <span class="hljs-keyword">this</span>
  679. i = tokens.length
  680. tokens[--i][<span class="hljs-number">0</span>] = <span class="hljs-string">'PARAM_END'</span>
  681. <span class="hljs-keyword">while</span> tok = tokens[--i]
  682. <span class="hljs-keyword">switch</span> tok[<span class="hljs-number">0</span>]
  683. <span class="hljs-keyword">when</span> <span class="hljs-string">')'</span>
  684. stack.push tok
  685. <span class="hljs-keyword">when</span> <span class="hljs-string">'('</span>, <span class="hljs-string">'CALL_START'</span>
  686. <span class="hljs-keyword">if</span> stack.length <span class="hljs-keyword">then</span> stack.pop()
  687. <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> tok[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'('</span>
  688. tok[<span class="hljs-number">0</span>] = <span class="hljs-string">'PARAM_START'</span>
  689. <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>
  690. <span class="hljs-keyword">else</span> <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>
  691. <span class="hljs-keyword">this</span></pre></div></div>
  692. </li>
  693. <li id="section-30">
  694. <div class="annotation">
  695. <div class="pilwrap ">
  696. <a class="pilcrow" href="#section-30">&#182;</a>
  697. </div>
  698. <p>Close up all remaining open blocks at the end of the file.</p>
  699. </div>
  700. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">closeIndentation</span>:<span class="hljs-function"> -&gt;</span>
  701. <span class="hljs-property">@outdentToken</span> <span class="hljs-property">@indent</span></pre></div></div>
  702. </li>
  703. <li id="section-31">
  704. <div class="annotation">
  705. <div class="pilwrap ">
  706. <a class="pilcrow" href="#section-31">&#182;</a>
  707. </div>
  708. <p>Match the contents of a delimited token and expand variables and expressions
  709. inside it using Ruby-like notation for substitution of arbitrary
  710. expressions.</p>
  711. <pre><code><span class="hljs-string">"Hello <span class="hljs-subst">#{name.capitalize()}</span>."</span>
  712. </code></pre><p>If it encounters an interpolation, this method will recursively create a new
  713. Lexer and tokenize until the <code>{</code> of <code>#{</code> is balanced with a <code>}</code>.</p>
  714. <ul>
  715. <li><code>regex</code> matches the contents of a token (but not <code>delimiter</code>, and not
  716. <code>#{</code> if interpolations are desired).</li>
  717. <li><code>delimiter</code> is the delimiter of the token. Examples are <code>&#39;</code>, <code>&quot;</code>, <code>&#39;&#39;&#39;</code>,
  718. <code>&quot;&quot;&quot;</code> and <code>///</code>.</li>
  719. </ul>
  720. <p>This method allows us to have strings within interpolations within strings,
  721. ad infinitum.</p>
  722. </div>
  723. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">matchWithInterpolations</span>: <span class="hljs-function"><span class="hljs-params">(regex, delimiter)</span> -&gt;</span>
  724. tokens = []
  725. offsetInChunk = delimiter.length
  726. <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span> <span class="hljs-keyword">unless</span> <span class="hljs-property">@chunk</span>[...offsetInChunk] <span class="hljs-keyword">is</span> delimiter
  727. str = <span class="hljs-property">@chunk</span>[offsetInChunk..]
  728. <span class="hljs-keyword">loop</span>
  729. [strPart] = regex.exec str
  730. <span class="hljs-property">@validateEscapes</span> strPart, {<span class="hljs-attribute">isRegex</span>: delimiter.charAt(<span class="hljs-number">0</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'/'</span>, offsetInChunk}</pre></div></div>
  731. </li>
  732. <li id="section-32">
  733. <div class="annotation">
  734. <div class="pilwrap ">
  735. <a class="pilcrow" href="#section-32">&#182;</a>
  736. </div>
  737. <p>Push a fake NEOSTRING token, which will get turned into a real string later.</p>
  738. </div>
  739. <div class="content"><div class='highlight'><pre> tokens.push <span class="hljs-property">@makeToken</span> <span class="hljs-string">'NEOSTRING'</span>, strPart, offsetInChunk
  740. str = str[strPart.length..]
  741. offsetInChunk += strPart.length
  742. <span class="hljs-keyword">break</span> <span class="hljs-keyword">unless</span> str[..<span class="hljs-number">.2</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'#{'</span></pre></div></div>
  743. </li>
  744. <li id="section-33">
  745. <div class="annotation">
  746. <div class="pilwrap ">
  747. <a class="pilcrow" href="#section-33">&#182;</a>
  748. </div>
  749. <p>The <code>1</code>s are to remove the <code>#</code> in <code>#{</code>.</p>
  750. </div>
  751. <div class="content"><div class='highlight'><pre> [line, column] = <span class="hljs-property">@getLineAndColumnFromChunk</span> offsetInChunk + <span class="hljs-number">1</span>
  752. {<span class="hljs-attribute">tokens</span>: nested, index} =
  753. <span class="hljs-keyword">new</span> Lexer().tokenize str[<span class="hljs-number">1.</span>.], <span class="hljs-attribute">line</span>: line, <span class="hljs-attribute">column</span>: column, <span class="hljs-attribute">untilBalanced</span>: <span class="hljs-literal">on</span></pre></div></div>
  754. </li>
  755. <li id="section-34">
  756. <div class="annotation">
  757. <div class="pilwrap ">
  758. <a class="pilcrow" href="#section-34">&#182;</a>
  759. </div>
  760. <p>Skip the trailing <code>}</code>.</p>
  761. </div>
  762. <div class="content"><div class='highlight'><pre> index += <span class="hljs-number">1</span></pre></div></div>
  763. </li>
  764. <li id="section-35">
  765. <div class="annotation">
  766. <div class="pilwrap ">
  767. <a class="pilcrow" href="#section-35">&#182;</a>
  768. </div>
  769. <p>Turn the leading and trailing <code>{</code> and <code>}</code> into parentheses. Unnecessary
  770. parentheses will be removed later.</p>
  771. </div>
  772. <div class="content"><div class='highlight'><pre> [open, ..., close] = nested
  773. open[<span class="hljs-number">0</span>] = open[<span class="hljs-number">1</span>] = <span class="hljs-string">'('</span>
  774. close[<span class="hljs-number">0</span>] = close[<span class="hljs-number">1</span>] = <span class="hljs-string">')'</span>
  775. close.origin = [<span class="hljs-string">''</span>, <span class="hljs-string">'end of interpolation'</span>, close[<span class="hljs-number">2</span>]]</pre></div></div>
  776. </li>
  777. <li id="section-36">
  778. <div class="annotation">
  779. <div class="pilwrap ">
  780. <a class="pilcrow" href="#section-36">&#182;</a>
  781. </div>
  782. <p>Remove leading TERMINATOR (if any).</p>
  783. </div>
  784. <div class="content"><div class='highlight'><pre> nested.splice <span class="hljs-number">1</span>, <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> nested[<span class="hljs-number">1</span>]?[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span></pre></div></div>
  785. </li>
  786. <li id="section-37">
  787. <div class="annotation">
  788. <div class="pilwrap ">
  789. <a class="pilcrow" href="#section-37">&#182;</a>
  790. </div>
  791. <p>Push a fake TOKENS token, which will get turned into real tokens later.</p>
  792. </div>
  793. <div class="content"><div class='highlight'><pre> tokens.push [<span class="hljs-string">'TOKENS'</span>, nested]
  794. str = str[index..]
  795. offsetInChunk += index
  796. <span class="hljs-keyword">unless</span> str[...delimiter.length] <span class="hljs-keyword">is</span> delimiter
  797. <span class="hljs-property">@error</span> <span class="hljs-string">"missing <span class="hljs-subst">#{delimiter}</span>"</span>, <span class="hljs-attribute">length</span>: delimiter.length
  798. [firstToken, ..., lastToken] = tokens
  799. firstToken[<span class="hljs-number">2</span>].first_column -= delimiter.length
  800. lastToken[<span class="hljs-number">2</span>].last_column += delimiter.length
  801. lastToken[<span class="hljs-number">2</span>].last_column -= <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> lastToken[<span class="hljs-number">1</span>].length <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
  802. {tokens, <span class="hljs-attribute">index</span>: offsetInChunk + delimiter.length}</pre></div></div>
  803. </li>
  804. <li id="section-38">
  805. <div class="annotation">
  806. <div class="pilwrap ">
  807. <a class="pilcrow" href="#section-38">&#182;</a>
  808. </div>
  809. <p>Merge the array <code>tokens</code> of the fake token types TOKENS and NEOSTRING
  810. (as returned by <code>matchWithInterpolations</code>) into the token stream. The value
  811. of NEOSTRINGs are converted using <code>fn</code> and turned into strings using
  812. <code>options</code> first.</p>
  813. </div>
  814. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">mergeInterpolationTokens</span>: <span class="hljs-function"><span class="hljs-params">(tokens, options, fn)</span> -&gt;</span>
  815. <span class="hljs-keyword">if</span> tokens.length &gt; <span class="hljs-number">1</span>
  816. lparen = <span class="hljs-property">@token</span> <span class="hljs-string">'STRING_START'</span>, <span class="hljs-string">'('</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>
  817. firstIndex = <span class="hljs-property">@tokens</span>.length
  818. <span class="hljs-keyword">for</span> token, i <span class="hljs-keyword">in</span> tokens
  819. [tag, value] = token
  820. <span class="hljs-keyword">switch</span> tag
  821. <span class="hljs-keyword">when</span> <span class="hljs-string">'TOKENS'</span></pre></div></div>
  822. </li>
  823. <li id="section-39">
  824. <div class="annotation">
  825. <div class="pilwrap ">
  826. <a class="pilcrow" href="#section-39">&#182;</a>
  827. </div>
  828. <p>Optimize out empty interpolations (an empty pair of parentheses).</p>
  829. </div>
  830. <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">continue</span> <span class="hljs-keyword">if</span> value.length <span class="hljs-keyword">is</span> <span class="hljs-number">2</span></pre></div></div>
  831. </li>
  832. <li id="section-40">
  833. <div class="annotation">
  834. <div class="pilwrap ">
  835. <a class="pilcrow" href="#section-40">&#182;</a>
  836. </div>
  837. <p>Push all the tokens in the fake TOKENS token. These already have
  838. sane location data.</p>
  839. </div>
  840. <div class="content"><div class='highlight'><pre> locationToken = value[<span class="hljs-number">0</span>]
  841. tokensToPush = value
  842. <span class="hljs-keyword">when</span> <span class="hljs-string">'NEOSTRING'</span></pre></div></div>
  843. </li>
  844. <li id="section-41">
  845. <div class="annotation">
  846. <div class="pilwrap ">
  847. <a class="pilcrow" href="#section-41">&#182;</a>
  848. </div>
  849. <p>Convert NEOSTRING into STRING.</p>
  850. </div>
  851. <div class="content"><div class='highlight'><pre> converted = fn token[<span class="hljs-number">1</span>], i</pre></div></div>
  852. </li>
  853. <li id="section-42">
  854. <div class="annotation">
  855. <div class="pilwrap ">
  856. <a class="pilcrow" href="#section-42">&#182;</a>
  857. </div>
  858. <p>Optimize out empty strings. We ensure that the tokens stream always
  859. starts with a string token, though, to make sure that the result
  860. really is a string.</p>
  861. </div>
  862. <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> converted.length <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
  863. <span class="hljs-keyword">if</span> i <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
  864. firstEmptyStringIndex = <span class="hljs-property">@tokens</span>.length
  865. <span class="hljs-keyword">else</span>
  866. <span class="hljs-keyword">continue</span></pre></div></div>
  867. </li>
  868. <li id="section-43">
  869. <div class="annotation">
  870. <div class="pilwrap ">
  871. <a class="pilcrow" href="#section-43">&#182;</a>
  872. </div>
  873. <p>However, there is one case where we can optimize away a starting
  874. empty string.</p>
  875. </div>
  876. <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> i <span class="hljs-keyword">is</span> <span class="hljs-number">2</span> <span class="hljs-keyword">and</span> firstEmptyStringIndex?
  877. <span class="hljs-property">@tokens</span>.splice firstEmptyStringIndex, <span class="hljs-number">2</span> <span class="hljs-comment"># Remove empty string and the plus.</span>
  878. token[<span class="hljs-number">0</span>] = <span class="hljs-string">'STRING'</span>
  879. token[<span class="hljs-number">1</span>] = <span class="hljs-property">@makeDelimitedLiteral</span> converted, options
  880. locationToken = token
  881. tokensToPush = [token]
  882. <span class="hljs-keyword">if</span> <span class="hljs-property">@tokens</span>.length &gt; firstIndex</pre></div></div>
  883. </li>
  884. <li id="section-44">
  885. <div class="annotation">
  886. <div class="pilwrap ">
  887. <a class="pilcrow" href="#section-44">&#182;</a>
  888. </div>
  889. <p>Create a 0-length + token.</p>
  890. </div>
  891. <div class="content"><div class='highlight'><pre> plusToken = <span class="hljs-property">@token</span> <span class="hljs-string">'+'</span>, <span class="hljs-string">'+'</span>
  892. plusToken[<span class="hljs-number">2</span>] =
  893. <span class="hljs-attribute">first_line</span>: locationToken[<span class="hljs-number">2</span>].first_line
  894. <span class="hljs-attribute">first_column</span>: locationToken[<span class="hljs-number">2</span>].first_column
  895. <span class="hljs-attribute">last_line</span>: locationToken[<span class="hljs-number">2</span>].first_line
  896. <span class="hljs-attribute">last_column</span>: locationToken[<span class="hljs-number">2</span>].first_column
  897. <span class="hljs-property">@tokens</span>.push tokensToPush...
  898. <span class="hljs-keyword">if</span> lparen
  899. [..., lastToken] = tokens
  900. lparen.origin = [<span class="hljs-string">'STRING'</span>, <span class="hljs-literal">null</span>,
  901. <span class="hljs-attribute">first_line</span>: lparen[<span class="hljs-number">2</span>].first_line
  902. <span class="hljs-attribute">first_column</span>: lparen[<span class="hljs-number">2</span>].first_column
  903. <span class="hljs-attribute">last_line</span>: lastToken[<span class="hljs-number">2</span>].last_line
  904. <span class="hljs-attribute">last_column</span>: lastToken[<span class="hljs-number">2</span>].last_column
  905. ]
  906. rparen = <span class="hljs-property">@token</span> <span class="hljs-string">'STRING_END'</span>, <span class="hljs-string">')'</span>
  907. rparen[<span class="hljs-number">2</span>] =
  908. <span class="hljs-attribute">first_line</span>: lastToken[<span class="hljs-number">2</span>].last_line
  909. <span class="hljs-attribute">first_column</span>: lastToken[<span class="hljs-number">2</span>].last_column
  910. <span class="hljs-attribute">last_line</span>: lastToken[<span class="hljs-number">2</span>].last_line
  911. <span class="hljs-attribute">last_column</span>: lastToken[<span class="hljs-number">2</span>].last_column</pre></div></div>
  912. </li>
  913. <li id="section-45">
  914. <div class="annotation">
  915. <div class="pilwrap ">
  916. <a class="pilcrow" href="#section-45">&#182;</a>
  917. </div>
  918. <p>Pairs up a closing token, ensuring that all listed pairs of tokens are
  919. correctly balanced throughout the course of the token stream.</p>
  920. </div>
  921. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">pair</span>: <span class="hljs-function"><span class="hljs-params">(tag)</span> -&gt;</span>
  922. [..., prev] = <span class="hljs-property">@ends</span>
  923. <span class="hljs-keyword">unless</span> tag <span class="hljs-keyword">is</span> wanted = prev?.tag
  924. <span class="hljs-property">@error</span> <span class="hljs-string">"unmatched <span class="hljs-subst">#{tag}</span>"</span> <span class="hljs-keyword">unless</span> <span class="hljs-string">'OUTDENT'</span> <span class="hljs-keyword">is</span> wanted</pre></div></div>
  925. </li>
  926. <li id="section-46">
  927. <div class="annotation">
  928. <div class="pilwrap ">
  929. <a class="pilcrow" href="#section-46">&#182;</a>
  930. </div>
  931. <p>Auto-close INDENT to support syntax like this:</p>
  932. <pre><code>el.click<span class="hljs-function"><span class="hljs-params">((event) -&gt;
  933. el.hide())</span></span>
  934. </code></pre>
  935. </div>
  936. <div class="content"><div class='highlight'><pre> [..., lastIndent] = <span class="hljs-property">@indents</span>
  937. <span class="hljs-property">@outdentToken</span> lastIndent, <span class="hljs-literal">true</span>
  938. <span class="hljs-keyword">return</span> <span class="hljs-property">@pair</span> tag
  939. <span class="hljs-property">@ends</span>.pop()</pre></div></div>
  940. </li>
  941. <li id="section-47">
  942. <div class="annotation">
  943. <div class="pilwrap ">
  944. <a class="pilcrow" href="#section-47">&#182;</a>
  945. </div>
  946. <h2 id="helpers">Helpers</h2>
  947. </div>
  948. </li>
  949. <li id="section-48">
  950. <div class="annotation">
  951. <div class="pilwrap ">
  952. <a class="pilcrow" href="#section-48">&#182;</a>
  953. </div>
  954. </div>
  955. </li>
  956. <li id="section-49">
  957. <div class="annotation">
  958. <div class="pilwrap ">
  959. <a class="pilcrow" href="#section-49">&#182;</a>
  960. </div>
  961. <p>Returns the line and column number from an offset into the current chunk.</p>
  962. <p><code>offset</code> is a number of characters into @chunk.</p>
  963. </div>
  964. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">getLineAndColumnFromChunk</span>: <span class="hljs-function"><span class="hljs-params">(offset)</span> -&gt;</span>
  965. <span class="hljs-keyword">if</span> offset <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
  966. <span class="hljs-keyword">return</span> [<span class="hljs-property">@chunkLine</span>, <span class="hljs-property">@chunkColumn</span>]
  967. <span class="hljs-keyword">if</span> offset &gt;= <span class="hljs-property">@chunk</span>.length
  968. string = <span class="hljs-property">@chunk</span>
  969. <span class="hljs-keyword">else</span>
  970. string = <span class="hljs-property">@chunk</span>[..offset-<span class="hljs-number">1</span>]
  971. lineCount = count string, <span class="hljs-string">'\n'</span>
  972. column = <span class="hljs-property">@chunkColumn</span>
  973. <span class="hljs-keyword">if</span> lineCount &gt; <span class="hljs-number">0</span>
  974. [..., lastLine] = string.split <span class="hljs-string">'\n'</span>
  975. column = lastLine.length
  976. <span class="hljs-keyword">else</span>
  977. column += string.length
  978. [<span class="hljs-property">@chunkLine</span> + lineCount, column]</pre></div></div>
  979. </li>
  980. <li id="section-50">
  981. <div class="annotation">
  982. <div class="pilwrap ">
  983. <a class="pilcrow" href="#section-50">&#182;</a>
  984. </div>
  985. <p>Same as token, exception this just returns the token without adding it
  986. to the results.</p>
  987. </div>
  988. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">makeToken</span>: <span class="hljs-function"><span class="hljs-params">(tag, value, offsetInChunk = <span class="hljs-number">0</span>, length = value.length)</span> -&gt;</span>
  989. locationData = {}
  990. [locationData.first_line, locationData.first_column] =
  991. <span class="hljs-property">@getLineAndColumnFromChunk</span> offsetInChunk</pre></div></div>
  992. </li>
  993. <li id="section-51">
  994. <div class="annotation">
  995. <div class="pilwrap ">
  996. <a class="pilcrow" href="#section-51">&#182;</a>
  997. </div>
  998. <p>Use length - 1 for the final offset - were supplying the last_line and the last_column,
  999. so if last_column == first_column, then were looking at a character of length 1.</p>
  1000. </div>
  1001. <div class="content"><div class='highlight'><pre> lastCharacter = Math.max <span class="hljs-number">0</span>, length - <span class="hljs-number">1</span>
  1002. [locationData.last_line, locationData.last_column] =
  1003. <span class="hljs-property">@getLineAndColumnFromChunk</span> offsetInChunk + lastCharacter
  1004. token = [tag, value, locationData]
  1005. token</pre></div></div>
  1006. </li>
  1007. <li id="section-52">
  1008. <div class="annotation">
  1009. <div class="pilwrap ">
  1010. <a class="pilcrow" href="#section-52">&#182;</a>
  1011. </div>
  1012. <p>Add a token to the results.
  1013. <code>offset</code> is the offset into the current @chunk where the token starts.
  1014. <code>length</code> is the length of the token in the @chunk, after the offset. If
  1015. not specified, the length of <code>value</code> will be used.</p>
  1016. <p>Returns the new token.</p>
  1017. </div>
  1018. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">token</span>: <span class="hljs-function"><span class="hljs-params">(tag, value, offsetInChunk, length, origin)</span> -&gt;</span>
  1019. token = <span class="hljs-property">@makeToken</span> tag, value, offsetInChunk, length
  1020. token.origin = origin <span class="hljs-keyword">if</span> origin
  1021. <span class="hljs-property">@tokens</span>.push token
  1022. token</pre></div></div>
  1023. </li>
  1024. <li id="section-53">
  1025. <div class="annotation">
  1026. <div class="pilwrap ">
  1027. <a class="pilcrow" href="#section-53">&#182;</a>
  1028. </div>
  1029. <p>Peek at the last tag in the token stream.</p>
  1030. </div>
  1031. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">tag</span>:<span class="hljs-function"> -&gt;</span>
  1032. [..., token] = <span class="hljs-property">@tokens</span>
  1033. token?[<span class="hljs-number">0</span>]</pre></div></div>
  1034. </li>
  1035. <li id="section-54">
  1036. <div class="annotation">
  1037. <div class="pilwrap ">
  1038. <a class="pilcrow" href="#section-54">&#182;</a>
  1039. </div>
  1040. <p>Peek at the last value in the token stream.</p>
  1041. </div>
  1042. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">value</span>:<span class="hljs-function"> -&gt;</span>
  1043. [..., token] = <span class="hljs-property">@tokens</span>
  1044. token?[<span class="hljs-number">1</span>]</pre></div></div>
  1045. </li>
  1046. <li id="section-55">
  1047. <div class="annotation">
  1048. <div class="pilwrap ">
  1049. <a class="pilcrow" href="#section-55">&#182;</a>
  1050. </div>
  1051. <p>Are we in the midst of an unfinished expression?</p>
  1052. </div>
  1053. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">unfinished</span>:<span class="hljs-function"> -&gt;</span>
  1054. LINE_CONTINUER.test(<span class="hljs-property">@chunk</span>) <span class="hljs-keyword">or</span>
  1055. <span class="hljs-property">@tag</span>() <span class="hljs-keyword">in</span> [<span class="hljs-string">'\\'</span>, <span class="hljs-string">'.'</span>, <span class="hljs-string">'?.'</span>, <span class="hljs-string">'?::'</span>, <span class="hljs-string">'UNARY'</span>, <span class="hljs-string">'MATH'</span>, <span class="hljs-string">'UNARY_MATH'</span>, <span class="hljs-string">'+'</span>, <span class="hljs-string">'-'</span>, <span class="hljs-string">'YIELD'</span>,
  1056. <span class="hljs-string">'**'</span>, <span class="hljs-string">'SHIFT'</span>, <span class="hljs-string">'RELATION'</span>, <span class="hljs-string">'COMPARE'</span>, <span class="hljs-string">'LOGIC'</span>, <span class="hljs-string">'THROW'</span>, <span class="hljs-string">'EXTENDS'</span>]
  1057. <span class="hljs-attribute">formatString</span>: <span class="hljs-function"><span class="hljs-params">(str)</span> -&gt;</span>
  1058. str.replace STRING_OMIT, <span class="hljs-string">'$1'</span>
  1059. <span class="hljs-attribute">formatHeregex</span>: <span class="hljs-function"><span class="hljs-params">(str)</span> -&gt;</span>
  1060. str.replace HEREGEX_OMIT, <span class="hljs-string">'$1$2'</span></pre></div></div>
  1061. </li>
  1062. <li id="section-56">
  1063. <div class="annotation">
  1064. <div class="pilwrap ">
  1065. <a class="pilcrow" href="#section-56">&#182;</a>
  1066. </div>
  1067. <p>Validates escapes in strings and regexes.</p>
  1068. </div>
  1069. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">validateEscapes</span>: <span class="hljs-function"><span class="hljs-params">(str, options = {})</span> -&gt;</span>
  1070. match = INVALID_ESCAPE.exec str
  1071. <span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> match
  1072. [[], before, octal, hex, unicode] = match
  1073. <span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> options.isRegex <span class="hljs-keyword">and</span> octal <span class="hljs-keyword">and</span> octal.charAt(<span class="hljs-number">0</span>) <span class="hljs-keyword">isnt</span> <span class="hljs-string">'0'</span>
  1074. message =
  1075. <span class="hljs-keyword">if</span> octal
  1076. <span class="hljs-string">"octal escape sequences are not allowed"</span>
  1077. <span class="hljs-keyword">else</span>
  1078. <span class="hljs-string">"invalid escape sequence"</span>
  1079. invalidEscape = <span class="hljs-string">"\\<span class="hljs-subst">#{octal <span class="hljs-keyword">or</span> hex <span class="hljs-keyword">or</span> unicode}</span>"</span>
  1080. <span class="hljs-property">@error</span> <span class="hljs-string">"<span class="hljs-subst">#{message}</span> <span class="hljs-subst">#{invalidEscape}</span>"</span>,
  1081. <span class="hljs-attribute">offset</span>: (options.offsetInChunk ? <span class="hljs-number">0</span>) + match.index + before.length
  1082. <span class="hljs-attribute">length</span>: invalidEscape.length</pre></div></div>
  1083. </li>
  1084. <li id="section-57">
  1085. <div class="annotation">
  1086. <div class="pilwrap ">
  1087. <a class="pilcrow" href="#section-57">&#182;</a>
  1088. </div>
  1089. <p>Constructs a string or regex by escaping certain characters.</p>
  1090. </div>
  1091. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">makeDelimitedLiteral</span>: <span class="hljs-function"><span class="hljs-params">(body, options = {})</span> -&gt;</span>
  1092. body = <span class="hljs-string">'(?:)'</span> <span class="hljs-keyword">if</span> body <span class="hljs-keyword">is</span> <span class="hljs-string">''</span> <span class="hljs-keyword">and</span> options.delimiter <span class="hljs-keyword">is</span> <span class="hljs-string">'/'</span>
  1093. regex = <span class="hljs-regexp">///
  1094. (\\\\) <span class="hljs-comment"># escaped backslash</span>
  1095. | (\\0(?=[1-7])) <span class="hljs-comment"># nul character mistaken as octal escape</span>
  1096. | \\?(<span class="hljs-subst">#{options.delimiter}</span>) <span class="hljs-comment"># (possibly escaped) delimiter</span>
  1097. | \\?(?: (\n)|(\r)|(\u2028)|(\u2029) ) <span class="hljs-comment"># (possibly escaped) newlines</span>
  1098. | (\\.) <span class="hljs-comment"># other escapes</span>
  1099. ///</span>g
  1100. body = body.replace regex, <span class="hljs-function"><span class="hljs-params">(match, backslash, nul, delimiter, lf, cr, ls, ps, other)</span> -&gt;</span> <span class="hljs-keyword">switch</span></pre></div></div>
  1101. </li>
  1102. <li id="section-58">
  1103. <div class="annotation">
  1104. <div class="pilwrap ">
  1105. <a class="pilcrow" href="#section-58">&#182;</a>
  1106. </div>
  1107. <p>Ignore escaped backslashes.</p>
  1108. </div>
  1109. <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">when</span> backslash <span class="hljs-keyword">then</span> (<span class="hljs-keyword">if</span> options.double <span class="hljs-keyword">then</span> backslash + backslash <span class="hljs-keyword">else</span> backslash)
  1110. <span class="hljs-keyword">when</span> nul <span class="hljs-keyword">then</span> <span class="hljs-string">'\\x00'</span>
  1111. <span class="hljs-keyword">when</span> delimiter <span class="hljs-keyword">then</span> <span class="hljs-string">"\\<span class="hljs-subst">#{delimiter}</span>"</span>
  1112. <span class="hljs-keyword">when</span> lf <span class="hljs-keyword">then</span> <span class="hljs-string">'\\n'</span>
  1113. <span class="hljs-keyword">when</span> cr <span class="hljs-keyword">then</span> <span class="hljs-string">'\\r'</span>
  1114. <span class="hljs-keyword">when</span> ls <span class="hljs-keyword">then</span> <span class="hljs-string">'\\u2028'</span>
  1115. <span class="hljs-keyword">when</span> ps <span class="hljs-keyword">then</span> <span class="hljs-string">'\\u2029'</span>
  1116. <span class="hljs-keyword">when</span> other <span class="hljs-keyword">then</span> (<span class="hljs-keyword">if</span> options.double <span class="hljs-keyword">then</span> <span class="hljs-string">"\\<span class="hljs-subst">#{other}</span>"</span> <span class="hljs-keyword">else</span> other)
  1117. <span class="hljs-string">"<span class="hljs-subst">#{options.delimiter}</span><span class="hljs-subst">#{body}</span><span class="hljs-subst">#{options.delimiter}</span>"</span></pre></div></div>
  1118. </li>
  1119. <li id="section-59">
  1120. <div class="annotation">
  1121. <div class="pilwrap ">
  1122. <a class="pilcrow" href="#section-59">&#182;</a>
  1123. </div>
  1124. <p>Throws an error at either a given offset from the current chunk or at the
  1125. location of a token (<code>token[2]</code>).</p>
  1126. </div>
  1127. <div class="content"><div class='highlight'><pre> <span class="hljs-attribute">error</span>: <span class="hljs-function"><span class="hljs-params">(message, options = {})</span> -&gt;</span>
  1128. location =
  1129. <span class="hljs-keyword">if</span> <span class="hljs-string">'first_line'</span> <span class="hljs-keyword">of</span> options
  1130. options
  1131. <span class="hljs-keyword">else</span>
  1132. [first_line, first_column] = <span class="hljs-property">@getLineAndColumnFromChunk</span> options.offset ? <span class="hljs-number">0</span>
  1133. {first_line, first_column, <span class="hljs-attribute">last_column</span>: first_column + (options.length ? <span class="hljs-number">1</span>) - <span class="hljs-number">1</span>}
  1134. throwSyntaxError message, location</pre></div></div>
  1135. </li>
  1136. <li id="section-60">
  1137. <div class="annotation">
  1138. <div class="pilwrap ">
  1139. <a class="pilcrow" href="#section-60">&#182;</a>
  1140. </div>
  1141. <h2 id="constants">Constants</h2>
  1142. </div>
  1143. </li>
  1144. <li id="section-61">
  1145. <div class="annotation">
  1146. <div class="pilwrap ">
  1147. <a class="pilcrow" href="#section-61">&#182;</a>
  1148. </div>
  1149. </div>
  1150. </li>
  1151. <li id="section-62">
  1152. <div class="annotation">
  1153. <div class="pilwrap ">
  1154. <a class="pilcrow" href="#section-62">&#182;</a>
  1155. </div>
  1156. <p>Keywords that CoffeeScript shares in common with JavaScript.</p>
  1157. </div>
  1158. <div class="content"><div class='highlight'><pre>JS_KEYWORDS = [
  1159. <span class="hljs-string">'true'</span>, <span class="hljs-string">'false'</span>, <span class="hljs-string">'null'</span>, <span class="hljs-string">'this'</span>
  1160. <span class="hljs-string">'new'</span>, <span class="hljs-string">'delete'</span>, <span class="hljs-string">'typeof'</span>, <span class="hljs-string">'in'</span>, <span class="hljs-string">'instanceof'</span>
  1161. <span class="hljs-string">'return'</span>, <span class="hljs-string">'throw'</span>, <span class="hljs-string">'break'</span>, <span class="hljs-string">'continue'</span>, <span class="hljs-string">'debugger'</span>, <span class="hljs-string">'yield'</span>
  1162. <span class="hljs-string">'if'</span>, <span class="hljs-string">'else'</span>, <span class="hljs-string">'switch'</span>, <span class="hljs-string">'for'</span>, <span class="hljs-string">'while'</span>, <span class="hljs-string">'do'</span>, <span class="hljs-string">'try'</span>, <span class="hljs-string">'catch'</span>, <span class="hljs-string">'finally'</span>
  1163. <span class="hljs-string">'class'</span>, <span class="hljs-string">'extends'</span>, <span class="hljs-string">'super'</span>
  1164. ]</pre></div></div>
  1165. </li>
  1166. <li id="section-63">
  1167. <div class="annotation">
  1168. <div class="pilwrap ">
  1169. <a class="pilcrow" href="#section-63">&#182;</a>
  1170. </div>
  1171. <p>CoffeeScript-only keywords.</p>
  1172. </div>
  1173. <div class="content"><div class='highlight'><pre>COFFEE_KEYWORDS = [<span class="hljs-string">'undefined'</span>, <span class="hljs-string">'then'</span>, <span class="hljs-string">'unless'</span>, <span class="hljs-string">'until'</span>, <span class="hljs-string">'loop'</span>, <span class="hljs-string">'of'</span>, <span class="hljs-string">'by'</span>, <span class="hljs-string">'when'</span>]
  1174. COFFEE_ALIAS_MAP =
  1175. <span class="hljs-keyword">and</span> : <span class="hljs-string">'&amp;&amp;'</span>
  1176. <span class="hljs-keyword">or</span> : <span class="hljs-string">'||'</span>
  1177. <span class="hljs-keyword">is</span> : <span class="hljs-string">'=='</span>
  1178. <span class="hljs-keyword">isnt</span> : <span class="hljs-string">'!='</span>
  1179. <span class="hljs-keyword">not</span> : <span class="hljs-string">'!'</span>
  1180. <span class="hljs-literal">yes</span> : <span class="hljs-string">'true'</span>
  1181. <span class="hljs-literal">no</span> : <span class="hljs-string">'false'</span>
  1182. <span class="hljs-literal">on</span> : <span class="hljs-string">'true'</span>
  1183. <span class="hljs-literal">off</span> : <span class="hljs-string">'false'</span>
  1184. COFFEE_ALIASES = (key <span class="hljs-keyword">for</span> key <span class="hljs-keyword">of</span> COFFEE_ALIAS_MAP)
  1185. COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat COFFEE_ALIASES</pre></div></div>
  1186. </li>
  1187. <li id="section-64">
  1188. <div class="annotation">
  1189. <div class="pilwrap ">
  1190. <a class="pilcrow" href="#section-64">&#182;</a>
  1191. </div>
  1192. <p>The list of keywords that are reserved by JavaScript, but not used, or are
  1193. used by CoffeeScript internally. We throw an error when these are encountered,
  1194. to avoid having a JavaScript error at runtime.</p>
  1195. </div>
  1196. <div class="content"><div class='highlight'><pre>RESERVED = [
  1197. <span class="hljs-string">'case'</span>, <span class="hljs-string">'default'</span>, <span class="hljs-string">'function'</span>, <span class="hljs-string">'var'</span>, <span class="hljs-string">'void'</span>, <span class="hljs-string">'with'</span>, <span class="hljs-string">'const'</span>, <span class="hljs-string">'let'</span>, <span class="hljs-string">'enum'</span>
  1198. <span class="hljs-string">'export'</span>, <span class="hljs-string">'import'</span>, <span class="hljs-string">'native'</span>, <span class="hljs-string">'implements'</span>, <span class="hljs-string">'interface'</span>, <span class="hljs-string">'package'</span>, <span class="hljs-string">'private'</span>
  1199. <span class="hljs-string">'protected'</span>, <span class="hljs-string">'public'</span>, <span class="hljs-string">'static'</span>
  1200. ]
  1201. STRICT_PROSCRIBED = [<span class="hljs-string">'arguments'</span>, <span class="hljs-string">'eval'</span>, <span class="hljs-string">'yield*'</span>]</pre></div></div>
  1202. </li>
  1203. <li id="section-65">
  1204. <div class="annotation">
  1205. <div class="pilwrap ">
  1206. <a class="pilcrow" href="#section-65">&#182;</a>
  1207. </div>
  1208. <p>The superset of both JavaScript keywords and reserved words, none of which may
  1209. be used as identifiers or properties.</p>
  1210. </div>
  1211. <div class="content"><div class='highlight'><pre>JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED).concat(STRICT_PROSCRIBED)
  1212. <span class="hljs-built_in">exports</span>.RESERVED = RESERVED.concat(JS_KEYWORDS).concat(COFFEE_KEYWORDS).concat(STRICT_PROSCRIBED)
  1213. <span class="hljs-built_in">exports</span>.STRICT_PROSCRIBED = STRICT_PROSCRIBED</pre></div></div>
  1214. </li>
  1215. <li id="section-66">
  1216. <div class="annotation">
  1217. <div class="pilwrap ">
  1218. <a class="pilcrow" href="#section-66">&#182;</a>
  1219. </div>
  1220. <p>The character code of the nasty Microsoft madness otherwise known as the BOM.</p>
  1221. </div>
  1222. <div class="content"><div class='highlight'><pre>BOM = <span class="hljs-number">65279</span></pre></div></div>
  1223. </li>
  1224. <li id="section-67">
  1225. <div class="annotation">
  1226. <div class="pilwrap ">
  1227. <a class="pilcrow" href="#section-67">&#182;</a>
  1228. </div>
  1229. <p>Token matching regexes.</p>
  1230. </div>
  1231. <div class="content"><div class='highlight'><pre>IDENTIFIER = <span class="hljs-regexp">/// ^
  1232. (?!\d)
  1233. ( (?: (?!\s)[$\w\x7f-\uffff] )+ )
  1234. ( [^\n\S]* : (?!:) )? <span class="hljs-comment"># Is this a property name?</span>
  1235. ///</span>
  1236. NUMBER = <span class="hljs-regexp">///
  1237. ^ 0b[01]+ | <span class="hljs-comment"># binary</span>
  1238. ^ 0o[0-7]+ | <span class="hljs-comment"># octal</span>
  1239. ^ 0x[\da-f]+ | <span class="hljs-comment"># hex</span>
  1240. ^ \d*\.?\d+ (?:e[+-]?\d+)? <span class="hljs-comment"># decimal</span>
  1241. ///</span>i
  1242. OPERATOR = <span class="hljs-regexp">/// ^ (
  1243. ?: [-=]&gt; <span class="hljs-comment"># function</span>
  1244. | [-+*/%&lt;&gt;&amp;|^!?=]= <span class="hljs-comment"># compound assign / compare</span>
  1245. | &gt;&gt;&gt;=? <span class="hljs-comment"># zero-fill right shift</span>
  1246. | ([-+:])\1 <span class="hljs-comment"># doubles</span>
  1247. | ([&amp;|&lt;&gt;*/%])\2=? <span class="hljs-comment"># logic / shift / power / floor division / modulo</span>
  1248. | \?(\.|::) <span class="hljs-comment"># soak access</span>
  1249. | \.{2,3} <span class="hljs-comment"># range or splat</span>
  1250. ) ///</span>
  1251. WHITESPACE = <span class="hljs-regexp">/^[^\n\S]+/</span>
  1252. COMMENT = <span class="hljs-regexp">/^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/</span>
  1253. CODE = <span class="hljs-regexp">/^[-=]&gt;/</span>
  1254. MULTI_DENT = <span class="hljs-regexp">/^(?:\n[^\n\S]*)+/</span>
  1255. JSTOKEN = <span class="hljs-regexp">/^`[^\\`]*(?:\\.[^\\`]*)*`/</span></pre></div></div>
  1256. </li>
  1257. <li id="section-68">
  1258. <div class="annotation">
  1259. <div class="pilwrap ">
  1260. <a class="pilcrow" href="#section-68">&#182;</a>
  1261. </div>
  1262. <p>String-matching-regexes.</p>
  1263. </div>
  1264. <div class="content"><div class='highlight'><pre>STRING_START = <span class="hljs-regexp">/^(?:'''|"""|'|")/</span>
  1265. STRING_SINGLE = <span class="hljs-regexp">/// ^(?: [^\\'] | \\[\s\S] )* ///</span>
  1266. STRING_DOUBLE = <span class="hljs-regexp">/// ^(?: [^\\"<span class="hljs-comment">#] | \\[\s\S] | \#(?!\{) )* ///</span>
  1267. HEREDOC_SINGLE = ///</span> ^(?: [^\\<span class="hljs-string">'] | \\[\s\S] | '</span>(?!<span class="hljs-string">''</span>) )* <span class="hljs-regexp">///
  1268. HEREDOC_DOUBLE = ///</span> ^(?: [^\\<span class="hljs-string">"#] | \\[\s\S] | "</span>(?!<span class="hljs-string">""</span>) | \<span class="hljs-comment">#(?!\{) )* ///</span>
  1269. STRING_OMIT = <span class="hljs-regexp">///
  1270. ((?:\\\\)+) <span class="hljs-comment"># consume (and preserve) an even number of backslashes</span>
  1271. | \\[^\S\n]*\n\s* <span class="hljs-comment"># remove escaped newlines</span>
  1272. ///</span>g
  1273. SIMPLE_STRING_OMIT = <span class="hljs-regexp">/\s*\n\s*/g</span>
  1274. HEREDOC_INDENT = <span class="hljs-regexp">/\n+([^\n\S]*)(?=\S)/g</span></pre></div></div>
  1275. </li>
  1276. <li id="section-69">
  1277. <div class="annotation">
  1278. <div class="pilwrap ">
  1279. <a class="pilcrow" href="#section-69">&#182;</a>
  1280. </div>
  1281. <p>Regex-matching-regexes.</p>
  1282. </div>
  1283. <div class="content"><div class='highlight'><pre>REGEX = <span class="hljs-regexp">/// ^
  1284. / (?!/) ((
  1285. ?: [^ [ / \n \\ ] <span class="hljs-comment"># every other thing</span>
  1286. | \\[^\n] <span class="hljs-comment"># anything but newlines escaped</span>
  1287. | \[ <span class="hljs-comment"># character class</span>
  1288. (?: \\[^\n] | [^ \] \n \\ ] )*
  1289. \]
  1290. )*) (/)?
  1291. ///</span>
  1292. REGEX_FLAGS = <span class="hljs-regexp">/^\w*/</span>
  1293. VALID_FLAGS = <span class="hljs-regexp">/^(?!.*(.).*\1)[imgy]*$/</span>
  1294. HEREGEX = <span class="hljs-regexp">/// ^(?: [^\\/<span class="hljs-comment">#] | \\[\s\S] | /(?!//) | \#(?!\{) )* ///</span>
  1295. HEREGEX_OMIT = ///</span>
  1296. ((?:\\\\)+) <span class="hljs-comment"># consume (and preserve) an even number of backslashes</span>
  1297. | \\(\s) <span class="hljs-comment"># preserve escaped whitespace</span>
  1298. | \s+(?:<span class="hljs-comment">#.*)? # remove whitespace and comments</span>
  1299. <span class="hljs-regexp">///g
  1300. REGEX_ILLEGAL = ///</span> ^ ( / | <span class="hljs-regexp">/{3}\s*) (\*) /</span><span class="hljs-regexp">//</span>
  1301. POSSIBLY_DIVISION = <span class="hljs-regexp">/// ^ /=?\s ///</span></pre></div></div>
  1302. </li>
  1303. <li id="section-70">
  1304. <div class="annotation">
  1305. <div class="pilwrap ">
  1306. <a class="pilcrow" href="#section-70">&#182;</a>
  1307. </div>
  1308. <p>Other regexes.</p>
  1309. </div>
  1310. <div class="content"><div class='highlight'><pre>HERECOMMENT_ILLEGAL = <span class="hljs-regexp">/\*\//</span>
  1311. LINE_CONTINUER = <span class="hljs-regexp">/// ^ \s* (?: , | \??\.(?![.\d]) | :: ) ///</span>
  1312. INVALID_ESCAPE = <span class="hljs-regexp">///
  1313. ( (?:^|[^\\]) (?:\\\\)* ) <span class="hljs-comment"># make sure the escape isnt escaped</span>
  1314. \\ (
  1315. ?: (0[0-7]|[1-7]) <span class="hljs-comment"># octal escape</span>
  1316. | (x(?![\da-fA-F]{2}).{0,2}) <span class="hljs-comment"># hex escape</span>
  1317. | (u(?![\da-fA-F]{4}).{0,4}) <span class="hljs-comment"># unicode escape</span>
  1318. )
  1319. ///</span>
  1320. LEADING_BLANK_LINE = <span class="hljs-regexp">/^[^\n\S]*\n/</span>
  1321. TRAILING_BLANK_LINE = <span class="hljs-regexp">/\n[^\n\S]*$/</span>
  1322. TRAILING_SPACES = <span class="hljs-regexp">/\s+$/</span></pre></div></div>
  1323. </li>
  1324. <li id="section-71">
  1325. <div class="annotation">
  1326. <div class="pilwrap ">
  1327. <a class="pilcrow" href="#section-71">&#182;</a>
  1328. </div>
  1329. <p>Compound assignment tokens.</p>
  1330. </div>
  1331. <div class="content"><div class='highlight'><pre>COMPOUND_ASSIGN = [
  1332. <span class="hljs-string">'-='</span>, <span class="hljs-string">'+='</span>, <span class="hljs-string">'/='</span>, <span class="hljs-string">'*='</span>, <span class="hljs-string">'%='</span>, <span class="hljs-string">'||='</span>, <span class="hljs-string">'&amp;&amp;='</span>, <span class="hljs-string">'?='</span>, <span class="hljs-string">'&lt;&lt;='</span>, <span class="hljs-string">'&gt;&gt;='</span>, <span class="hljs-string">'&gt;&gt;&gt;='</span>
  1333. <span class="hljs-string">'&amp;='</span>, <span class="hljs-string">'^='</span>, <span class="hljs-string">'|='</span>, <span class="hljs-string">'**='</span>, <span class="hljs-string">'//='</span>, <span class="hljs-string">'%%='</span>
  1334. ]</pre></div></div>
  1335. </li>
  1336. <li id="section-72">
  1337. <div class="annotation">
  1338. <div class="pilwrap ">
  1339. <a class="pilcrow" href="#section-72">&#182;</a>
  1340. </div>
  1341. <p>Unary tokens.</p>
  1342. </div>
  1343. <div class="content"><div class='highlight'><pre>UNARY = [<span class="hljs-string">'NEW'</span>, <span class="hljs-string">'TYPEOF'</span>, <span class="hljs-string">'DELETE'</span>, <span class="hljs-string">'DO'</span>]
  1344. UNARY_MATH = [<span class="hljs-string">'!'</span>, <span class="hljs-string">'~'</span>]</pre></div></div>
  1345. </li>
  1346. <li id="section-73">
  1347. <div class="annotation">
  1348. <div class="pilwrap ">
  1349. <a class="pilcrow" href="#section-73">&#182;</a>
  1350. </div>
  1351. <p>Logical tokens.</p>
  1352. </div>
  1353. <div class="content"><div class='highlight'><pre>LOGIC = [<span class="hljs-string">'&amp;&amp;'</span>, <span class="hljs-string">'||'</span>, <span class="hljs-string">'&amp;'</span>, <span class="hljs-string">'|'</span>, <span class="hljs-string">'^'</span>]</pre></div></div>
  1354. </li>
  1355. <li id="section-74">
  1356. <div class="annotation">
  1357. <div class="pilwrap ">
  1358. <a class="pilcrow" href="#section-74">&#182;</a>
  1359. </div>
  1360. <p>Bit-shifting tokens.</p>
  1361. </div>
  1362. <div class="content"><div class='highlight'><pre>SHIFT = [<span class="hljs-string">'&lt;&lt;'</span>, <span class="hljs-string">'&gt;&gt;'</span>, <span class="hljs-string">'&gt;&gt;&gt;'</span>]</pre></div></div>
  1363. </li>
  1364. <li id="section-75">
  1365. <div class="annotation">
  1366. <div class="pilwrap ">
  1367. <a class="pilcrow" href="#section-75">&#182;</a>
  1368. </div>
  1369. <p>Comparison tokens.</p>
  1370. </div>
  1371. <div class="content"><div class='highlight'><pre>COMPARE = [<span class="hljs-string">'=='</span>, <span class="hljs-string">'!='</span>, <span class="hljs-string">'&lt;'</span>, <span class="hljs-string">'&gt;'</span>, <span class="hljs-string">'&lt;='</span>, <span class="hljs-string">'&gt;='</span>]</pre></div></div>
  1372. </li>
  1373. <li id="section-76">
  1374. <div class="annotation">
  1375. <div class="pilwrap ">
  1376. <a class="pilcrow" href="#section-76">&#182;</a>
  1377. </div>
  1378. <p>Mathematical tokens.</p>
  1379. </div>
  1380. <div class="content"><div class='highlight'><pre>MATH = [<span class="hljs-string">'*'</span>, <span class="hljs-string">'/'</span>, <span class="hljs-string">'%'</span>, <span class="hljs-string">'//'</span>, <span class="hljs-string">'%%'</span>]</pre></div></div>
  1381. </li>
  1382. <li id="section-77">
  1383. <div class="annotation">
  1384. <div class="pilwrap ">
  1385. <a class="pilcrow" href="#section-77">&#182;</a>
  1386. </div>
  1387. <p>Relational tokens that are negatable with <code>not</code> prefix.</p>
  1388. </div>
  1389. <div class="content"><div class='highlight'><pre>RELATION = [<span class="hljs-string">'IN'</span>, <span class="hljs-string">'OF'</span>, <span class="hljs-string">'INSTANCEOF'</span>]</pre></div></div>
  1390. </li>
  1391. <li id="section-78">
  1392. <div class="annotation">
  1393. <div class="pilwrap ">
  1394. <a class="pilcrow" href="#section-78">&#182;</a>
  1395. </div>
  1396. <p>Boolean tokens.</p>
  1397. </div>
  1398. <div class="content"><div class='highlight'><pre>BOOL = [<span class="hljs-string">'TRUE'</span>, <span class="hljs-string">'FALSE'</span>]</pre></div></div>
  1399. </li>
  1400. <li id="section-79">
  1401. <div class="annotation">
  1402. <div class="pilwrap ">
  1403. <a class="pilcrow" href="#section-79">&#182;</a>
  1404. </div>
  1405. <p>Tokens which could legitimately be invoked or indexed. An opening
  1406. parentheses or bracket following these tokens will be recorded as the start
  1407. of a function invocation or indexing operation.</p>
  1408. </div>
  1409. <div class="content"><div class='highlight'><pre>CALLABLE = [<span class="hljs-string">'IDENTIFIER'</span>, <span class="hljs-string">')'</span>, <span class="hljs-string">']'</span>, <span class="hljs-string">'?'</span>, <span class="hljs-string">'@'</span>, <span class="hljs-string">'THIS'</span>, <span class="hljs-string">'SUPER'</span>]
  1410. INDEXABLE = CALLABLE.concat [
  1411. <span class="hljs-string">'NUMBER'</span>, <span class="hljs-string">'STRING'</span>, <span class="hljs-string">'STRING_END'</span>, <span class="hljs-string">'REGEX'</span>, <span class="hljs-string">'REGEX_END'</span>
  1412. <span class="hljs-string">'BOOL'</span>, <span class="hljs-string">'NULL'</span>, <span class="hljs-string">'UNDEFINED'</span>, <span class="hljs-string">'}'</span>, <span class="hljs-string">'::'</span>
  1413. ]</pre></div></div>
  1414. </li>
  1415. <li id="section-80">
  1416. <div class="annotation">
  1417. <div class="pilwrap ">
  1418. <a class="pilcrow" href="#section-80">&#182;</a>
  1419. </div>
  1420. <p>Tokens which a regular expression will never immediately follow (except spaced
  1421. CALLABLEs in some cases), but which a division operator can.</p>
  1422. <p>See: <a href="http://www-archive.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions">http://www-archive.mozilla.org/js/language/js20-2002-04/rationale/syntax.html#regular-expressions</a></p>
  1423. </div>
  1424. <div class="content"><div class='highlight'><pre>NOT_REGEX = INDEXABLE.concat [<span class="hljs-string">'++'</span>, <span class="hljs-string">'--'</span>]</pre></div></div>
  1425. </li>
  1426. <li id="section-81">
  1427. <div class="annotation">
  1428. <div class="pilwrap ">
  1429. <a class="pilcrow" href="#section-81">&#182;</a>
  1430. </div>
  1431. <p>Tokens that, when immediately preceding a <code>WHEN</code>, indicate that the <code>WHEN</code>
  1432. occurs at the start of a line. We disambiguate these from trailing whens to
  1433. avoid an ambiguity in the grammar.</p>
  1434. </div>
  1435. <div class="content"><div class='highlight'><pre>LINE_BREAK = [<span class="hljs-string">'INDENT'</span>, <span class="hljs-string">'OUTDENT'</span>, <span class="hljs-string">'TERMINATOR'</span>]</pre></div></div>
  1436. </li>
  1437. <li id="section-82">
  1438. <div class="annotation">
  1439. <div class="pilwrap ">
  1440. <a class="pilcrow" href="#section-82">&#182;</a>
  1441. </div>
  1442. <p>Additional indent in front of these is ignored.</p>
  1443. </div>
  1444. <div class="content"><div class='highlight'><pre>INDENTABLE_CLOSERS = [<span class="hljs-string">')'</span>, <span class="hljs-string">'}'</span>, <span class="hljs-string">']'</span>]</pre></div></div>
  1445. </li>
  1446. </ul>
  1447. </div>
  1448. </body>
  1449. </html>