mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00

* Changelog for 2.2.0 * Bump version * Bump package versions * Fix `cake doc:source` for Docco 0.8.0 * Update output; some text tweaks
1728 lines
100 KiB
HTML
1728 lines
100 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html>
|
||
<head>
|
||
<title>rewriter.coffee</title>
|
||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||
<meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
|
||
<link rel="stylesheet" media="all" href="docco.css" />
|
||
</head>
|
||
<body>
|
||
<div id="container">
|
||
<div id="background"></div>
|
||
|
||
<ul id="jump_to">
|
||
<li>
|
||
<a class="large" href="javascript:void(0);">Jump To …</a>
|
||
<a class="small" href="javascript:void(0);">+</a>
|
||
<div id="jump_wrapper">
|
||
<div id="jump_page_wrapper">
|
||
<div id="jump_page">
|
||
|
||
|
||
<a class="source" href="browser.html">
|
||
browser.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="cake.html">
|
||
cake.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="coffeescript.html">
|
||
coffeescript.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="command.html">
|
||
command.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="grammar.html">
|
||
grammar.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="helpers.html">
|
||
helpers.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="index.html">
|
||
index.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="lexer.html">
|
||
lexer.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="nodes.html">
|
||
nodes.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="optparse.html">
|
||
optparse.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="register.html">
|
||
register.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="repl.html">
|
||
repl.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="rewriter.html">
|
||
rewriter.coffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="scope.html">
|
||
scope.litcoffee
|
||
</a>
|
||
|
||
|
||
<a class="source" href="sourcemap.html">
|
||
sourcemap.litcoffee
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
|
||
<ul class="sections">
|
||
|
||
<li id="title">
|
||
<div class="annotation">
|
||
<h1>rewriter.coffee</h1>
|
||
</div>
|
||
</li>
|
||
|
||
|
||
|
||
<li id="section-1">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-1">¶</a>
|
||
</div>
|
||
<p>The CoffeeScript language has a good deal of optional syntax, implicit syntax,
|
||
and shorthand syntax. This can greatly complicate a grammar and bloat
|
||
the resulting parse table. Instead of making the parser handle it all, we take
|
||
a series of passes over the token stream, using this <strong>Rewriter</strong> to convert
|
||
shorthand into the unambiguous long form, add implicit indentation and
|
||
parentheses, and generally clean things up.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>
|
||
{throwSyntaxError} = <span class="hljs-built_in">require</span> <span class="hljs-string">'./helpers'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-2">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-2">¶</a>
|
||
</div>
|
||
<p>Move attached comments from one token to another.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">moveComments</span> = <span class="hljs-params">(fromToken, toToken)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> fromToken.comments
|
||
<span class="hljs-keyword">if</span> toToken.comments <span class="hljs-keyword">and</span> toToken.comments.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
unshiftedComments = []
|
||
<span class="hljs-keyword">for</span> comment <span class="hljs-keyword">in</span> fromToken.comments
|
||
<span class="hljs-keyword">if</span> comment.unshift
|
||
unshiftedComments.push comment
|
||
<span class="hljs-keyword">else</span>
|
||
toToken.comments.push comment
|
||
toToken.comments = unshiftedComments.concat toToken.comments
|
||
<span class="hljs-keyword">else</span>
|
||
toToken.comments = fromToken.comments
|
||
<span class="hljs-keyword">delete</span> fromToken.comments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-3">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-3">¶</a>
|
||
</div>
|
||
<p>Create a generated token: one that exists due to a use of implicit syntax.
|
||
Optionally have this new token take the attached comments from another token.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">generate</span> = <span class="hljs-params">(tag, value, origin, commentsToken)</span> -></span>
|
||
token = [tag, value]
|
||
token.generated = <span class="hljs-literal">yes</span>
|
||
token.origin = origin <span class="hljs-keyword">if</span> origin
|
||
moveComments commentsToken, token <span class="hljs-keyword">if</span> commentsToken
|
||
token</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-4">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-4">¶</a>
|
||
</div>
|
||
<p>The <strong>Rewriter</strong> class is used by the <a href="lexer.html">Lexer</a>, directly against
|
||
its internal array of tokens.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>exports.Rewriter = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Rewriter</span></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-5">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-5">¶</a>
|
||
</div>
|
||
<p>Rewrite the token stream in multiple passes, one logical filter at
|
||
a time. This could certainly be changed into a single pass through the
|
||
stream, with a big ol’ efficient switch, but it’s much nicer to work with
|
||
like this. The order of these passes matters—indentation must be
|
||
corrected before implicit parentheses can be wrapped around blocks of code.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> rewrite: <span class="hljs-function"><span class="hljs-params">(@tokens)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-6">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-6">¶</a>
|
||
</div>
|
||
<p>Set environment variable <code>DEBUG_TOKEN_STREAM</code> to <code>true</code> to output token
|
||
debugging info. Also set <code>DEBUG_REWRITTEN_TOKEN_STREAM</code> to <code>true</code> to
|
||
output the token stream after it has been rewritten by this file.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> process?.env?.DEBUG_TOKEN_STREAM
|
||
<span class="hljs-built_in">console</span>.log <span class="hljs-string">'Initial token stream:'</span> <span class="hljs-keyword">if</span> process.env.DEBUG_REWRITTEN_TOKEN_STREAM
|
||
<span class="hljs-built_in">console</span>.log (t[<span class="hljs-number">0</span>] + <span class="hljs-string">'/'</span> + t[<span class="hljs-number">1</span>] + (<span class="hljs-keyword">if</span> t.comments <span class="hljs-keyword">then</span> <span class="hljs-string">'*'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>) <span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> @tokens).join <span class="hljs-string">' '</span>
|
||
@removeLeadingNewlines()
|
||
@closeOpenCalls()
|
||
@closeOpenIndexes()
|
||
@normalizeLines()
|
||
@tagPostfixConditionals()
|
||
@addImplicitBracesAndParens()
|
||
@addParensToChainedDoIife()
|
||
@rescueStowawayComments()
|
||
@addLocationDataToGeneratedTokens()
|
||
@enforceValidCSXAttributes()
|
||
@fixOutdentLocationData()
|
||
<span class="hljs-keyword">if</span> process?.env?.DEBUG_REWRITTEN_TOKEN_STREAM
|
||
<span class="hljs-built_in">console</span>.log <span class="hljs-string">'Rewritten token stream:'</span> <span class="hljs-keyword">if</span> process.env.DEBUG_TOKEN_STREAM
|
||
<span class="hljs-built_in">console</span>.log (t[<span class="hljs-number">0</span>] + <span class="hljs-string">'/'</span> + t[<span class="hljs-number">1</span>] + (<span class="hljs-keyword">if</span> t.comments <span class="hljs-keyword">then</span> <span class="hljs-string">'*'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>) <span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> @tokens).join <span class="hljs-string">' '</span>
|
||
@tokens</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-7">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-7">¶</a>
|
||
</div>
|
||
<p>Rewrite the token stream, looking one token ahead and behind.
|
||
Allow the return value of the block to tell us how many tokens to move
|
||
forwards (or backwards) in the stream, to make sure we don’t miss anything
|
||
as tokens are inserted and removed, and the stream changes length under
|
||
our feet.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> scanTokens: <span class="hljs-function"><span class="hljs-params">(block)</span> -></span>
|
||
{tokens} = <span class="hljs-keyword">this</span>
|
||
i = <span class="hljs-number">0</span>
|
||
i += block.call <span class="hljs-keyword">this</span>, token, i, tokens <span class="hljs-keyword">while</span> token = tokens[i]
|
||
<span class="hljs-literal">true</span>
|
||
|
||
detectEnd: <span class="hljs-function"><span class="hljs-params">(i, condition, action, opts = {})</span> -></span>
|
||
{tokens} = <span class="hljs-keyword">this</span>
|
||
levels = <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">while</span> token = tokens[i]
|
||
<span class="hljs-keyword">return</span> action.call <span class="hljs-keyword">this</span>, token, i <span class="hljs-keyword">if</span> levels <span class="hljs-keyword">is</span> <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> condition.call <span class="hljs-keyword">this</span>, token, i
|
||
<span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> EXPRESSION_START
|
||
levels += <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> EXPRESSION_END
|
||
levels -= <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">if</span> levels < <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> opts.returnOnNegativeLevel
|
||
<span class="hljs-keyword">return</span> action.call <span class="hljs-keyword">this</span>, token, i
|
||
i += <span class="hljs-number">1</span>
|
||
i - <span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-8">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-8">¶</a>
|
||
</div>
|
||
<p>Leading newlines would introduce an ambiguity in the grammar, so we
|
||
dispatch them here.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> removeLeadingNewlines: <span class="hljs-function">-></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-9">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-9">¶</a>
|
||
</div>
|
||
<p>Find the index of the first non-<code>TERMINATOR</code> token.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">break</span> <span class="hljs-keyword">for</span> [tag], i <span class="hljs-keyword">in</span> @tokens <span class="hljs-keyword">when</span> tag <span class="hljs-keyword">isnt</span> <span class="hljs-string">'TERMINATOR'</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> i <span class="hljs-keyword">is</span> <span class="hljs-number">0</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-10">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-10">¶</a>
|
||
</div>
|
||
<p>If there are any comments attached to the tokens we’re about to discard,
|
||
shift them forward to what will become the new first token.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span> leadingNewlineToken <span class="hljs-keyword">in</span> @tokens[<span class="hljs-number">0.</span>..i]
|
||
moveComments leadingNewlineToken, @tokens[i]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-11">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-11">¶</a>
|
||
</div>
|
||
<p>Discard all the leading newline tokens.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @tokens.splice <span class="hljs-number">0</span>, i</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-12">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-12">¶</a>
|
||
</div>
|
||
<p>The lexer has tagged the opening parenthesis of a method call. Match it with
|
||
its paired close.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> closeOpenCalls: <span class="hljs-function">-></span>
|
||
<span class="hljs-function"> <span class="hljs-title">condition</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||
token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">')'</span>, <span class="hljs-string">'CALL_END'</span>]
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">action</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||
token[<span class="hljs-number">0</span>] = <span class="hljs-string">'CALL_END'</span>
|
||
|
||
@scanTokens (token, i) ->
|
||
@detectEnd i + <span class="hljs-number">1</span>, condition, action <span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'CALL_START'</span>
|
||
<span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-13">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-13">¶</a>
|
||
</div>
|
||
<p>The lexer has tagged the opening bracket of an indexing operation call.
|
||
Match it with its paired close.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> closeOpenIndexes: <span class="hljs-function">-></span>
|
||
<span class="hljs-function"> <span class="hljs-title">condition</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||
token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">']'</span>, <span class="hljs-string">'INDEX_END'</span>]
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">action</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||
token[<span class="hljs-number">0</span>] = <span class="hljs-string">'INDEX_END'</span>
|
||
|
||
@scanTokens (token, i) ->
|
||
@detectEnd i + <span class="hljs-number">1</span>, condition, action <span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'INDEX_START'</span>
|
||
<span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-14">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-14">¶</a>
|
||
</div>
|
||
<p>Match tags in token stream starting at <code>i</code> with <code>pattern</code>.
|
||
<code>pattern</code> may consist of strings (equality), an array of strings (one of)
|
||
or null (wildcard). Returns the index of the match or -1 if no match.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> indexOfTag: <span class="hljs-function"><span class="hljs-params">(i, pattern...)</span> -></span>
|
||
fuzz = <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> [<span class="hljs-number">0</span> ... pattern.length]
|
||
<span class="hljs-keyword">continue</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> pattern[j]?
|
||
pattern[j] = [pattern[j]] <span class="hljs-keyword">if</span> <span class="hljs-keyword">typeof</span> pattern[j] <span class="hljs-keyword">is</span> <span class="hljs-string">'string'</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">-1</span> <span class="hljs-keyword">if</span> @tag(i + j + fuzz) <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> pattern[j]
|
||
i + j + fuzz - <span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-15">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-15">¶</a>
|
||
</div>
|
||
<p>Returns <code>yes</code> if standing in front of something looking like
|
||
<code>@<x>:</code>, <code><x>:</code> or <code><EXPRESSION_START><x>...<EXPRESSION_END>:</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> looksObjectish: <span class="hljs-function"><span class="hljs-params">(j)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> @indexOfTag(j, <span class="hljs-string">'@'</span>, <span class="hljs-literal">null</span>, <span class="hljs-string">':'</span>) <span class="hljs-keyword">isnt</span> <span class="hljs-number">-1</span> <span class="hljs-keyword">or</span> @indexOfTag(j, <span class="hljs-literal">null</span>, <span class="hljs-string">':'</span>) <span class="hljs-keyword">isnt</span> <span class="hljs-number">-1</span>
|
||
index = @indexOfTag j, EXPRESSION_START
|
||
<span class="hljs-keyword">if</span> index <span class="hljs-keyword">isnt</span> <span class="hljs-number">-1</span>
|
||
end = <span class="hljs-literal">null</span>
|
||
@detectEnd index + <span class="hljs-number">1</span>, <span class="hljs-function">(<span class="hljs-params">(token)</span> -></span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> EXPRESSION_END), <span class="hljs-function">(<span class="hljs-params">(token, i)</span> -></span> end = i)
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> @tag(end + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">':'</span>
|
||
<span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-16">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-16">¶</a>
|
||
</div>
|
||
<p>Returns <code>yes</code> if current line of tokens contain an element of tags on same
|
||
expression level. Stop searching at <code>LINEBREAKS</code> or explicit start of
|
||
containing balanced expression.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> findTagsBackwards: <span class="hljs-function"><span class="hljs-params">(i, tags)</span> -></span>
|
||
backStack = []
|
||
<span class="hljs-keyword">while</span> i >= <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> (backStack.length <span class="hljs-keyword">or</span>
|
||
@tag(i) <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> tags <span class="hljs-keyword">and</span>
|
||
(@tag(i) <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> EXPRESSION_START <span class="hljs-keyword">or</span> @tokens[i].generated) <span class="hljs-keyword">and</span>
|
||
@tag(i) <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> LINEBREAKS)
|
||
backStack.push @tag(i) <span class="hljs-keyword">if</span> @tag(i) <span class="hljs-keyword">in</span> EXPRESSION_END
|
||
backStack.pop() <span class="hljs-keyword">if</span> @tag(i) <span class="hljs-keyword">in</span> EXPRESSION_START <span class="hljs-keyword">and</span> backStack.length
|
||
i -= <span class="hljs-number">1</span>
|
||
@tag(i) <span class="hljs-keyword">in</span> tags</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-17">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-17">¶</a>
|
||
</div>
|
||
<p>Look for signs of implicit calls and objects in the token stream and
|
||
add them.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> addImplicitBracesAndParens: <span class="hljs-function">-></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-18">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-18">¶</a>
|
||
</div>
|
||
<p>Track current balancing depth (both implicit and explicit) on stack.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> stack = []
|
||
start = <span class="hljs-literal">null</span>
|
||
|
||
@scanTokens (token, i, tokens) ->
|
||
[tag] = token
|
||
[prevTag] = prevToken = <span class="hljs-keyword">if</span> i > <span class="hljs-number">0</span> <span class="hljs-keyword">then</span> tokens[i - <span class="hljs-number">1</span>] <span class="hljs-keyword">else</span> []
|
||
[nextTag] = nextToken = <span class="hljs-keyword">if</span> i < tokens.length - <span class="hljs-number">1</span> <span class="hljs-keyword">then</span> tokens[i + <span class="hljs-number">1</span>] <span class="hljs-keyword">else</span> []
|
||
<span class="hljs-function"> <span class="hljs-title">stackTop</span> = -></span> stack[stack.length - <span class="hljs-number">1</span>]
|
||
startIdx = i</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-19">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-19">¶</a>
|
||
</div>
|
||
<p>Helper function, used for keeping track of the number of tokens consumed
|
||
and spliced, when returning for getting a new token.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"> <span class="hljs-title">forward</span> = <span class="hljs-params">(n)</span> -></span> i - startIdx + n</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-20">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-20">¶</a>
|
||
</div>
|
||
<p>Helper functions</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"> <span class="hljs-title">isImplicit</span> = <span class="hljs-params">(stackItem)</span> -></span> stackItem?[<span class="hljs-number">2</span>]?.ours
|
||
<span class="hljs-function"> <span class="hljs-title">isImplicitObject</span> = <span class="hljs-params">(stackItem)</span> -></span> isImplicit(stackItem) <span class="hljs-keyword">and</span> stackItem?[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'{'</span>
|
||
<span class="hljs-function"> <span class="hljs-title">isImplicitCall</span> = <span class="hljs-params">(stackItem)</span> -></span> isImplicit(stackItem) <span class="hljs-keyword">and</span> stackItem?[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'('</span>
|
||
<span class="hljs-function"> <span class="hljs-title">inImplicit</span> = -></span> isImplicit stackTop()
|
||
<span class="hljs-function"> <span class="hljs-title">inImplicitCall</span> = -></span> isImplicitCall stackTop()
|
||
<span class="hljs-function"> <span class="hljs-title">inImplicitObject</span> = -></span> isImplicitObject stackTop()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-21">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-21">¶</a>
|
||
</div>
|
||
<p>Unclosed control statement inside implicit parens (like
|
||
class declaration or if-conditionals).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"> <span class="hljs-title">inImplicitControl</span> = -></span> inImplicit() <span class="hljs-keyword">and</span> stackTop()?[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'CONTROL'</span>
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">startImplicitCall</span> = <span class="hljs-params">(idx)</span> -></span>
|
||
stack.push [<span class="hljs-string">'('</span>, idx, ours: <span class="hljs-literal">yes</span>]
|
||
tokens.splice idx, <span class="hljs-number">0</span>, generate <span class="hljs-string">'CALL_START'</span>, <span class="hljs-string">'('</span>, [<span class="hljs-string">''</span>, <span class="hljs-string">'implicit function call'</span>, token[<span class="hljs-number">2</span>]], prevToken
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">endImplicitCall</span> = -></span>
|
||
stack.pop()
|
||
tokens.splice i, <span class="hljs-number">0</span>, generate <span class="hljs-string">'CALL_END'</span>, <span class="hljs-string">')'</span>, [<span class="hljs-string">''</span>, <span class="hljs-string">'end of input'</span>, token[<span class="hljs-number">2</span>]], prevToken
|
||
i += <span class="hljs-number">1</span>
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">startImplicitObject</span> = <span class="hljs-params">(idx, startsLine = <span class="hljs-literal">yes</span>)</span> -></span>
|
||
stack.push [<span class="hljs-string">'{'</span>, idx, sameLine: <span class="hljs-literal">yes</span>, startsLine: startsLine, ours: <span class="hljs-literal">yes</span>]
|
||
val = <span class="hljs-keyword">new</span> String <span class="hljs-string">'{'</span>
|
||
val.generated = <span class="hljs-literal">yes</span>
|
||
tokens.splice idx, <span class="hljs-number">0</span>, generate <span class="hljs-string">'{'</span>, val, token, prevToken
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">endImplicitObject</span> = <span class="hljs-params">(j)</span> -></span>
|
||
j = j ? i
|
||
stack.pop()
|
||
tokens.splice j, <span class="hljs-number">0</span>, generate <span class="hljs-string">'}'</span>, <span class="hljs-string">'}'</span>, token, prevToken
|
||
i += <span class="hljs-number">1</span>
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">implicitObjectContinues</span> = <span class="hljs-params">(j)</span> =></span>
|
||
nextTerminatorIdx = <span class="hljs-literal">null</span>
|
||
@detectEnd j,
|
||
<span class="hljs-function"><span class="hljs-params">(token)</span> -></span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span>
|
||
(token, i) -> nextTerminatorIdx = i
|
||
returnOnNegativeLevel: <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> nextTerminatorIdx?
|
||
@looksObjectish nextTerminatorIdx + <span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-22">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-22">¶</a>
|
||
</div>
|
||
<p>Don’t end an implicit call/object on next indent if any of these are in an argument/value.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (
|
||
(inImplicitCall() <span class="hljs-keyword">or</span> inImplicitObject()) <span class="hljs-keyword">and</span> tag <span class="hljs-keyword">in</span> CONTROL_IN_IMPLICIT <span class="hljs-keyword">or</span>
|
||
inImplicitObject() <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">is</span> <span class="hljs-string">':'</span> <span class="hljs-keyword">and</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'FOR'</span>
|
||
)
|
||
stack.push [<span class="hljs-string">'CONTROL'</span>, i, ours: <span class="hljs-literal">yes</span>]
|
||
<span class="hljs-keyword">return</span> forward(<span class="hljs-number">1</span>)
|
||
|
||
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'INDENT'</span> <span class="hljs-keyword">and</span> inImplicit()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-23">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-23">¶</a>
|
||
</div>
|
||
<p>An <code>INDENT</code> closes an implicit call unless</p>
|
||
<ol>
|
||
<li>We have seen a <code>CONTROL</code> argument on the line.</li>
|
||
<li>The last token before the indent is part of the list below.</li>
|
||
</ol>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> prevTag <span class="hljs-keyword">not</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">','</span>, <span class="hljs-string">'{'</span>, <span class="hljs-string">'ELSE'</span>, <span class="hljs-string">'='</span>]
|
||
<span class="hljs-keyword">while</span> inImplicitCall() <span class="hljs-keyword">or</span> inImplicitObject() <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">':'</span>
|
||
<span class="hljs-keyword">if</span> inImplicitCall()
|
||
endImplicitCall()
|
||
<span class="hljs-keyword">else</span>
|
||
endImplicitObject()
|
||
stack.pop() <span class="hljs-keyword">if</span> inImplicitControl()
|
||
stack.push [tag, i]
|
||
<span class="hljs-keyword">return</span> forward(<span class="hljs-number">1</span>)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-24">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-24">¶</a>
|
||
</div>
|
||
<p>Straightforward start of explicit expression.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> EXPRESSION_START
|
||
stack.push [tag, i]
|
||
<span class="hljs-keyword">return</span> forward(<span class="hljs-number">1</span>)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-25">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-25">¶</a>
|
||
</div>
|
||
<p>Close all implicit expressions inside of explicitly closed expressions.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> EXPRESSION_END
|
||
<span class="hljs-keyword">while</span> inImplicit()
|
||
<span class="hljs-keyword">if</span> inImplicitCall()
|
||
endImplicitCall()
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> inImplicitObject()
|
||
endImplicitObject()
|
||
<span class="hljs-keyword">else</span>
|
||
stack.pop()
|
||
start = stack.pop()
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">inControlFlow</span> = =></span>
|
||
seenFor = @findTagsBackwards(i, [<span class="hljs-string">'FOR'</span>]) <span class="hljs-keyword">and</span> @findTagsBackwards(i, [<span class="hljs-string">'FORIN'</span>, <span class="hljs-string">'FOROF'</span>, <span class="hljs-string">'FORFROM'</span>])
|
||
controlFlow = seenFor <span class="hljs-keyword">or</span> @findTagsBackwards i, [<span class="hljs-string">'WHILE'</span>, <span class="hljs-string">'UNTIL'</span>, <span class="hljs-string">'LOOP'</span>, <span class="hljs-string">'LEADING_WHEN'</span>]
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> controlFlow
|
||
isFunc = <span class="hljs-literal">no</span>
|
||
tagCurrentLine = token[<span class="hljs-number">2</span>].first_line
|
||
@detectEnd i,
|
||
<span class="hljs-function"><span class="hljs-params">(token, i)</span> -></span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> LINEBREAKS
|
||
(token, i) ->
|
||
[prevTag, ,{first_line}] = tokens[i - <span class="hljs-number">1</span>] || []
|
||
isFunc = tagCurrentLine <span class="hljs-keyword">is</span> first_line <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">in</span> [<span class="hljs-string">'->'</span>, <span class="hljs-string">'=>'</span>]
|
||
returnOnNegativeLevel: <span class="hljs-literal">yes</span>
|
||
isFunc</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-26">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-26">¶</a>
|
||
</div>
|
||
<p>Recognize standard implicit calls like
|
||
f a, f() b, f? c, h[0] d etc.
|
||
Added support for spread dots on the left side: f …a</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (tag <span class="hljs-keyword">in</span> IMPLICIT_FUNC <span class="hljs-keyword">and</span> token.spaced <span class="hljs-keyword">or</span>
|
||
tag <span class="hljs-keyword">is</span> <span class="hljs-string">'?'</span> <span class="hljs-keyword">and</span> i > <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> tokens[i - <span class="hljs-number">1</span>].spaced) <span class="hljs-keyword">and</span>
|
||
(nextTag <span class="hljs-keyword">in</span> IMPLICIT_CALL <span class="hljs-keyword">or</span>
|
||
(nextTag <span class="hljs-keyword">is</span> <span class="hljs-string">'...'</span> <span class="hljs-keyword">and</span> @tag(i + <span class="hljs-number">2</span>) <span class="hljs-keyword">in</span> IMPLICIT_CALL <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @findTagsBackwards(i, [<span class="hljs-string">'INDEX_START'</span>, <span class="hljs-string">'['</span>])) <span class="hljs-keyword">or</span>
|
||
nextTag <span class="hljs-keyword">in</span> IMPLICIT_UNSPACED_CALL <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> nextToken.spaced <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> nextToken.newLine) <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> inControlFlow()
|
||
tag = token[<span class="hljs-number">0</span>] = <span class="hljs-string">'FUNC_EXIST'</span> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'?'</span>
|
||
startImplicitCall i + <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">return</span> forward(<span class="hljs-number">2</span>)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-27">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-27">¶</a>
|
||
</div>
|
||
<p>Implicit call taking an implicit indented object as first argument.</p>
|
||
<pre><code>f
|
||
a: b
|
||
c: d
|
||
</code></pre><p>Don’t accept implicit calls of this type, when on the same line
|
||
as the control structures below as that may misinterpret constructs like:</p>
|
||
<pre><code><span class="hljs-keyword">if</span> f
|
||
a: <span class="hljs-number">1</span>
|
||
</code></pre><p>as</p>
|
||
<pre><code><span class="hljs-keyword">if</span> f(a: <span class="hljs-number">1</span>)
|
||
</code></pre><p>which is probably always unintended.
|
||
Furthermore don’t allow this in literal arrays, as
|
||
that creates grammatical ambiguities.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> IMPLICIT_FUNC <span class="hljs-keyword">and</span>
|
||
@indexOfTag(i + <span class="hljs-number">1</span>, <span class="hljs-string">'INDENT'</span>) > <span class="hljs-number">-1</span> <span class="hljs-keyword">and</span> @looksObjectish(i + <span class="hljs-number">2</span>) <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> @findTagsBackwards(i, [<span class="hljs-string">'CLASS'</span>, <span class="hljs-string">'EXTENDS'</span>, <span class="hljs-string">'IF'</span>, <span class="hljs-string">'CATCH'</span>,
|
||
<span class="hljs-string">'SWITCH'</span>, <span class="hljs-string">'LEADING_WHEN'</span>, <span class="hljs-string">'FOR'</span>, <span class="hljs-string">'WHILE'</span>, <span class="hljs-string">'UNTIL'</span>])
|
||
startImplicitCall i + <span class="hljs-number">1</span>
|
||
stack.push [<span class="hljs-string">'INDENT'</span>, i + <span class="hljs-number">2</span>]
|
||
<span class="hljs-keyword">return</span> forward(<span class="hljs-number">3</span>)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-28">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-28">¶</a>
|
||
</div>
|
||
<p>Implicit objects start here.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">':'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-29">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-29">¶</a>
|
||
</div>
|
||
<p>Go back to the (implicit) start of the object.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> s = <span class="hljs-keyword">switch</span>
|
||
<span class="hljs-keyword">when</span> @tag(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> EXPRESSION_END <span class="hljs-keyword">then</span> start[<span class="hljs-number">1</span>]
|
||
<span class="hljs-keyword">when</span> @tag(i - <span class="hljs-number">2</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'@'</span> <span class="hljs-keyword">then</span> i - <span class="hljs-number">2</span>
|
||
<span class="hljs-keyword">else</span> i - <span class="hljs-number">1</span>
|
||
|
||
startsLine = s <= <span class="hljs-number">0</span> <span class="hljs-keyword">or</span> @tag(s - <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> LINEBREAKS <span class="hljs-keyword">or</span> tokens[s - <span class="hljs-number">1</span>].newLine</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-30">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-30">¶</a>
|
||
</div>
|
||
<p>Are we just continuing an already declared object?</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> stackTop()
|
||
[stackTag, stackIdx] = stackTop()
|
||
<span class="hljs-keyword">if</span> (stackTag <span class="hljs-keyword">is</span> <span class="hljs-string">'{'</span> <span class="hljs-keyword">or</span> stackTag <span class="hljs-keyword">is</span> <span class="hljs-string">'INDENT'</span> <span class="hljs-keyword">and</span> @tag(stackIdx - <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'{'</span>) <span class="hljs-keyword">and</span>
|
||
(startsLine <span class="hljs-keyword">or</span> @tag(s - <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">','</span> <span class="hljs-keyword">or</span> @tag(s - <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'{'</span>)
|
||
<span class="hljs-keyword">return</span> forward(<span class="hljs-number">1</span>)
|
||
|
||
startImplicitObject(s, !!startsLine)
|
||
<span class="hljs-keyword">return</span> forward(<span class="hljs-number">2</span>)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-31">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-31">¶</a>
|
||
</div>
|
||
<p>End implicit calls when chaining method calls
|
||
like e.g.:</p>
|
||
<pre><code>f ->
|
||
a
|
||
.g b, <span class="hljs-function">-></span>
|
||
c
|
||
.h a
|
||
</code></pre><p>and also</p>
|
||
<pre><code>f a
|
||
.g b
|
||
.h a
|
||
</code></pre>
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-32">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-32">¶</a>
|
||
</div>
|
||
<p>Mark all enclosing objects as not sameLine</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> LINEBREAKS
|
||
<span class="hljs-keyword">for</span> stackItem <span class="hljs-keyword">in</span> stack <span class="hljs-keyword">by</span> <span class="hljs-number">-1</span>
|
||
<span class="hljs-keyword">break</span> <span class="hljs-keyword">unless</span> isImplicit stackItem
|
||
stackItem[<span class="hljs-number">2</span>].sameLine = <span class="hljs-literal">no</span> <span class="hljs-keyword">if</span> isImplicitObject stackItem
|
||
|
||
newLine = prevTag <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span> <span class="hljs-keyword">or</span> prevToken.newLine
|
||
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> IMPLICIT_END <span class="hljs-keyword">or</span>
|
||
(tag <span class="hljs-keyword">in</span> CALL_CLOSERS <span class="hljs-keyword">and</span> newLine) <span class="hljs-keyword">or</span>
|
||
(tag <span class="hljs-keyword">in</span> [<span class="hljs-string">'..'</span>, <span class="hljs-string">'...'</span>] <span class="hljs-keyword">and</span> @findTagsBackwards(i, [<span class="hljs-string">"INDEX_START"</span>]))
|
||
<span class="hljs-keyword">while</span> inImplicit()
|
||
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-33">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-33">¶</a>
|
||
</div>
|
||
<p>Close implicit calls when reached end of argument list</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> inImplicitCall() <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">','</span> <span class="hljs-keyword">or</span>
|
||
(prevTag <span class="hljs-keyword">is</span> <span class="hljs-string">','</span> <span class="hljs-keyword">and</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> nextTag?)
|
||
endImplicitCall()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-34">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-34">¶</a>
|
||
</div>
|
||
<p>Close implicit objects such as:
|
||
return a: 1, b: 2 unless true</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> inImplicitObject() <span class="hljs-keyword">and</span> sameLine <span class="hljs-keyword">and</span>
|
||
tag <span class="hljs-keyword">isnt</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">':'</span> <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> (tag <span class="hljs-keyword">in</span> [<span class="hljs-string">'POST_IF'</span>, <span class="hljs-string">'FOR'</span>, <span class="hljs-string">'WHILE'</span>, <span class="hljs-string">'UNTIL'</span>] <span class="hljs-keyword">and</span> startsLine <span class="hljs-keyword">and</span> implicitObjectContinues(i + <span class="hljs-number">1</span>))
|
||
endImplicitObject()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-35">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-35">¶</a>
|
||
</div>
|
||
<p>Close implicit objects when at end of line, line didn’t end with a comma
|
||
and the implicit object didn’t start the line or the next line doesn’t look like
|
||
the continuation of an object.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> inImplicitObject() <span class="hljs-keyword">and</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">','</span> <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> (startsLine <span class="hljs-keyword">and</span> @looksObjectish(i + <span class="hljs-number">1</span>))
|
||
endImplicitObject()
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">break</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-36">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-36">¶</a>
|
||
</div>
|
||
<p>Close implicit object if comma is the last character
|
||
and what comes after doesn’t look like it belongs.
|
||
This is used for trailing commas and calls, like:</p>
|
||
<pre><code>x =
|
||
a: b,
|
||
c: d,
|
||
e = <span class="hljs-number">2</span>
|
||
</code></pre><p>and</p>
|
||
<pre><code>f a, b: c, d: e, f, g: h: i, j
|
||
</code></pre>
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">','</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @looksObjectish(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">and</span> inImplicitObject() <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> (@tag(i + <span class="hljs-number">2</span>) <span class="hljs-keyword">in</span> [<span class="hljs-string">'FOROF'</span>, <span class="hljs-string">'FORIN'</span>]) <span class="hljs-keyword">and</span>
|
||
(nextTag <span class="hljs-keyword">isnt</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> @looksObjectish(i + <span class="hljs-number">2</span>))</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-37">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-37">¶</a>
|
||
</div>
|
||
<p>When nextTag is OUTDENT the comma is insignificant and
|
||
should just be ignored so embed it in the implicit object.</p>
|
||
<p>When it isn’t the comma go on to play a role in a call or
|
||
array further up the stack, so give it a chance.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> offset = <span class="hljs-keyword">if</span> nextTag <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span> <span class="hljs-keyword">then</span> <span class="hljs-number">1</span> <span class="hljs-keyword">else</span> <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">while</span> inImplicitObject()
|
||
endImplicitObject i + offset
|
||
<span class="hljs-keyword">return</span> forward(<span class="hljs-number">1</span>)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-38">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-38">¶</a>
|
||
</div>
|
||
<p>Make sure only strings and wrapped expressions are used in CSX attributes.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> enforceValidCSXAttributes: <span class="hljs-function">-></span>
|
||
@scanTokens (token, i, tokens) ->
|
||
<span class="hljs-keyword">if</span> token.csxColon
|
||
next = tokens[i + <span class="hljs-number">1</span>]
|
||
<span class="hljs-keyword">if</span> next[<span class="hljs-number">0</span>] <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> [<span class="hljs-string">'STRING_START'</span>, <span class="hljs-string">'STRING'</span>, <span class="hljs-string">'('</span>]
|
||
throwSyntaxError <span class="hljs-string">'expected wrapped or quoted JSX attribute'</span>, next[<span class="hljs-number">2</span>]
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-39">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-39">¶</a>
|
||
</div>
|
||
<p>Not all tokens survive processing by the parser. To avoid comments getting
|
||
lost into the ether, find comments attached to doomed tokens and move them
|
||
to a token that will make it to the other side.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> rescueStowawayComments: <span class="hljs-function">-></span>
|
||
<span class="hljs-function"> <span class="hljs-title">insertPlaceholder</span> = <span class="hljs-params">(token, j, tokens, method)</span> -></span>
|
||
tokens[method] generate <span class="hljs-string">'TERMINATOR'</span>, <span class="hljs-string">'\n'</span>, tokens[j] <span class="hljs-keyword">unless</span> tokens[j][<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span>
|
||
tokens[method] generate <span class="hljs-string">'JS'</span>, <span class="hljs-string">''</span>, tokens[j], token
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">shiftCommentsForward</span> = <span class="hljs-params">(token, i, tokens)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-40">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-40">¶</a>
|
||
</div>
|
||
<p>Find the next surviving token and attach this token’s comments to it,
|
||
with a flag that we know to output such comments <em>before</em> that
|
||
token’s own compilation. (Otherwise comments are output following
|
||
the token they’re attached to.)</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> j = i
|
||
j++ <span class="hljs-keyword">while</span> j <span class="hljs-keyword">isnt</span> tokens.length <span class="hljs-keyword">and</span> tokens[j][<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> DISCARDED
|
||
<span class="hljs-keyword">unless</span> j <span class="hljs-keyword">is</span> tokens.length <span class="hljs-keyword">or</span> tokens[j][<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> DISCARDED
|
||
comment.unshift = <span class="hljs-literal">yes</span> <span class="hljs-keyword">for</span> comment <span class="hljs-keyword">in</span> token.comments
|
||
moveComments token, tokens[j]
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-comment"># All following tokens are doomed!</span>
|
||
j = tokens.length - <span class="hljs-number">1</span>
|
||
insertPlaceholder token, j, tokens, <span class="hljs-string">'push'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-41">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-41">¶</a>
|
||
</div>
|
||
<p>The generated tokens were added to the end, not inline, so we don’t skip.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">shiftCommentsBackward</span> = <span class="hljs-params">(token, i, tokens)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-42">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-42">¶</a>
|
||
</div>
|
||
<p>Find the last surviving token and attach this token’s comments to it.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> j = i
|
||
j-- <span class="hljs-keyword">while</span> j <span class="hljs-keyword">isnt</span> <span class="hljs-number">-1</span> <span class="hljs-keyword">and</span> tokens[j][<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> DISCARDED
|
||
<span class="hljs-keyword">unless</span> j <span class="hljs-keyword">is</span> <span class="hljs-number">-1</span> <span class="hljs-keyword">or</span> tokens[j][<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> DISCARDED
|
||
moveComments token, tokens[j]
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-comment"># All previous tokens are doomed!</span>
|
||
insertPlaceholder token, <span class="hljs-number">0</span>, tokens, <span class="hljs-string">'unshift'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-43">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-43">¶</a>
|
||
</div>
|
||
<p>We added two tokens, so shift forward to account for the insertion.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-number">3</span>
|
||
|
||
@scanTokens (token, i, tokens) ->
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span> <span class="hljs-keyword">unless</span> token.comments
|
||
ret = <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> DISCARDED</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-44">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-44">¶</a>
|
||
</div>
|
||
<p>This token won’t survive passage through the parser, so we need to
|
||
rescue its attached tokens and redistribute them to nearby tokens.
|
||
Comments that don’t start a new line can shift backwards to the last
|
||
safe token, while other tokens should shift forward.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> dummyToken = comments: []
|
||
j = token.comments.length - <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">until</span> j <span class="hljs-keyword">is</span> <span class="hljs-number">-1</span>
|
||
<span class="hljs-keyword">if</span> token.comments[j].newLine <span class="hljs-keyword">is</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">and</span> token.comments[j].here <span class="hljs-keyword">is</span> <span class="hljs-literal">no</span>
|
||
dummyToken.comments.unshift token.comments[j]
|
||
token.comments.splice j, <span class="hljs-number">1</span>
|
||
j--
|
||
<span class="hljs-keyword">if</span> dummyToken.comments.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
ret = shiftCommentsBackward dummyToken, i - <span class="hljs-number">1</span>, tokens
|
||
<span class="hljs-keyword">if</span> token.comments.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
shiftCommentsForward token, i, tokens
|
||
<span class="hljs-keyword">else</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-45">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-45">¶</a>
|
||
</div>
|
||
<p>If any of this token’s comments start a line—there’s only
|
||
whitespace between the preceding newline and the start of the
|
||
comment—and this isn’t one of the special <code>JS</code> tokens, then
|
||
shift this comment forward to precede the next valid token.
|
||
<code>Block.compileComments</code> also has logic to make sure that
|
||
“starting new line” comments follow or precede the nearest
|
||
newline relative to the token that the comment is attached to,
|
||
but that newline might be inside a <code>}</code> or <code>)</code> or other generated
|
||
token that we really want this comment to output after. Therefore
|
||
we need to shift the comments here, avoiding such generated and
|
||
discarded tokens.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> dummyToken = comments: []
|
||
j = token.comments.length - <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">until</span> j <span class="hljs-keyword">is</span> <span class="hljs-number">-1</span>
|
||
<span class="hljs-keyword">if</span> token.comments[j].newLine <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> token.comments[j].unshift <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> (token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'JS'</span> <span class="hljs-keyword">and</span> token.generated)
|
||
dummyToken.comments.unshift token.comments[j]
|
||
token.comments.splice j, <span class="hljs-number">1</span>
|
||
j--
|
||
<span class="hljs-keyword">if</span> dummyToken.comments.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
ret = shiftCommentsForward dummyToken, i + <span class="hljs-number">1</span>, tokens
|
||
<span class="hljs-keyword">delete</span> token.comments <span class="hljs-keyword">if</span> token.comments?.length <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
|
||
ret</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-46">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-46">¶</a>
|
||
</div>
|
||
<p>Add location data to all tokens generated by the rewriter.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> addLocationDataToGeneratedTokens: <span class="hljs-function">-></span>
|
||
@scanTokens (token, i, tokens) ->
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> token[<span class="hljs-number">2</span>]
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span> <span class="hljs-keyword">unless</span> token.generated <span class="hljs-keyword">or</span> token.explicit
|
||
<span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'{'</span> <span class="hljs-keyword">and</span> nextLocation=tokens[i + <span class="hljs-number">1</span>]?[<span class="hljs-number">2</span>]
|
||
{first_line: line, first_column: column} = nextLocation
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> prevLocation = tokens[i - <span class="hljs-number">1</span>]?[<span class="hljs-number">2</span>]
|
||
{last_line: line, last_column: column} = prevLocation
|
||
<span class="hljs-keyword">else</span>
|
||
line = column = <span class="hljs-number">0</span>
|
||
token[<span class="hljs-number">2</span>] =
|
||
first_line: line
|
||
first_column: column
|
||
last_line: line
|
||
last_column: column
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-47">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-47">¶</a>
|
||
</div>
|
||
<p><code>OUTDENT</code> tokens should always be positioned at the last character of the
|
||
previous token, so that AST nodes ending in an <code>OUTDENT</code> token end up with a
|
||
location corresponding to the last “real” token under the node.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> fixOutdentLocationData: <span class="hljs-function">-></span>
|
||
@scanTokens (token, i, tokens) ->
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span> <span class="hljs-keyword">unless</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span> <span class="hljs-keyword">or</span>
|
||
(token.generated <span class="hljs-keyword">and</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'CALL_END'</span>) <span class="hljs-keyword">or</span>
|
||
(token.generated <span class="hljs-keyword">and</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'}'</span>)
|
||
prevLocationData = tokens[i - <span class="hljs-number">1</span>][<span class="hljs-number">2</span>]
|
||
token[<span class="hljs-number">2</span>] =
|
||
first_line: prevLocationData.last_line
|
||
first_column: prevLocationData.last_column
|
||
last_line: prevLocationData.last_line
|
||
last_column: prevLocationData.last_column
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-48">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-48">¶</a>
|
||
</div>
|
||
<p>Add parens around a <code>do</code> IIFE followed by a chained <code>.</code> so that the
|
||
chaining applies to the executed function rather than the function
|
||
object (see #3736)</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> addParensToChainedDoIife: <span class="hljs-function">-></span>
|
||
<span class="hljs-function"> <span class="hljs-title">condition</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||
@tag(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span>
|
||
<span class="hljs-function"> <span class="hljs-title">action</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> CALL_CLOSERS
|
||
@tokens.splice doIndex, <span class="hljs-number">0</span>, generate <span class="hljs-string">'('</span>, <span class="hljs-string">'('</span>, @tokens[doIndex]
|
||
@tokens.splice i + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, generate <span class="hljs-string">')'</span>, <span class="hljs-string">')'</span>, @tokens[i]
|
||
doIndex = <span class="hljs-literal">null</span>
|
||
@scanTokens (token, i, tokens) ->
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span> <span class="hljs-keyword">unless</span> token[<span class="hljs-number">1</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'do'</span>
|
||
doIndex = i
|
||
glyphIndex = i + <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">if</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'PARAM_START'</span>
|
||
glyphIndex = <span class="hljs-literal">null</span>
|
||
@detectEnd i + <span class="hljs-number">1</span>,
|
||
<span class="hljs-function"><span class="hljs-params">(token, i)</span> -></span> @tag(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'PARAM_END'</span>
|
||
(token, i) -> glyphIndex = i
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span> <span class="hljs-keyword">unless</span> glyphIndex? <span class="hljs-keyword">and</span> @tag(glyphIndex) <span class="hljs-keyword">in</span> [<span class="hljs-string">'->'</span>, <span class="hljs-string">'=>'</span>] <span class="hljs-keyword">and</span> @tag(glyphIndex + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'INDENT'</span>
|
||
@detectEnd glyphIndex + <span class="hljs-number">1</span>, condition, action
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">2</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-49">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-49">¶</a>
|
||
</div>
|
||
<p>Because our grammar is LALR(1), it can’t handle some single-line
|
||
expressions that lack ending delimiters. The <strong>Rewriter</strong> adds the implicit
|
||
blocks, so it doesn’t need to. To keep the grammar clean and tidy, trailing
|
||
newlines within expressions are removed and the indentation tokens of empty
|
||
blocks are added.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> normalizeLines: <span class="hljs-function">-></span>
|
||
starter = indent = outdent = <span class="hljs-literal">null</span>
|
||
leading_switch_when = <span class="hljs-literal">null</span>
|
||
leading_if_then = <span class="hljs-literal">null</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-50">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-50">¶</a>
|
||
</div>
|
||
<p>Count <code>THEN</code> tags</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> ifThens = []
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">condition</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||
token[<span class="hljs-number">1</span>] <span class="hljs-keyword">isnt</span> <span class="hljs-string">';'</span> <span class="hljs-keyword">and</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> SINGLE_CLOSERS <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> (token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">and</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> EXPRESSION_CLOSE) <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> (token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'ELSE'</span> <span class="hljs-keyword">and</span>
|
||
(starter <span class="hljs-keyword">isnt</span> <span class="hljs-string">'THEN'</span> <span class="hljs-keyword">or</span> (leading_if_then <span class="hljs-keyword">or</span> leading_switch_when))) <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> (token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">'CATCH'</span>, <span class="hljs-string">'FINALLY'</span>] <span class="hljs-keyword">and</span> starter <span class="hljs-keyword">in</span> [<span class="hljs-string">'->'</span>, <span class="hljs-string">'=>'</span>]) <span class="hljs-keyword">or</span>
|
||
token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> CALL_CLOSERS <span class="hljs-keyword">and</span>
|
||
(@tokens[i - <span class="hljs-number">1</span>].newLine <span class="hljs-keyword">or</span> @tokens[i - <span class="hljs-number">1</span>][<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span>)
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">action</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||
ifThens.pop() <span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'ELSE'</span> <span class="hljs-keyword">and</span> starter <span class="hljs-keyword">is</span> <span class="hljs-string">'THEN'</span>
|
||
@tokens.splice (<span class="hljs-keyword">if</span> @tag(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">','</span> <span class="hljs-keyword">then</span> i - <span class="hljs-number">1</span> <span class="hljs-keyword">else</span> i), <span class="hljs-number">0</span>, outdent
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">closeElseTag</span> = <span class="hljs-params">(tokens, i)</span> =></span>
|
||
tlen = ifThens.length
|
||
<span class="hljs-keyword">return</span> i <span class="hljs-keyword">unless</span> tlen > <span class="hljs-number">0</span>
|
||
lastThen = ifThens.pop()
|
||
[, outdentElse] = @indentation tokens[lastThen]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-51">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-51">¶</a>
|
||
</div>
|
||
<p>Insert <code>OUTDENT</code> to close inner <code>IF</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> outdentElse[<span class="hljs-number">1</span>] = tlen*<span class="hljs-number">2</span>
|
||
tokens.splice(i, <span class="hljs-number">0</span>, outdentElse)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-52">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-52">¶</a>
|
||
</div>
|
||
<p>Insert <code>OUTDENT</code> to close outer <code>IF</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> outdentElse[<span class="hljs-number">1</span>] = <span class="hljs-number">2</span>
|
||
tokens.splice(i + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, outdentElse)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-53">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-53">¶</a>
|
||
</div>
|
||
<p>Remove outdents from the end.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @detectEnd i + <span class="hljs-number">2</span>,
|
||
<span class="hljs-function"><span class="hljs-params">(token, i)</span> -></span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">in</span> [<span class="hljs-string">'OUTDENT'</span>, <span class="hljs-string">'TERMINATOR'</span>]
|
||
(token, i) ->
|
||
<span class="hljs-keyword">if</span> @tag(i) <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span> <span class="hljs-keyword">and</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span>
|
||
tokens.splice i, <span class="hljs-number">2</span>
|
||
i + <span class="hljs-number">2</span>
|
||
|
||
@scanTokens (token, i, tokens) ->
|
||
[tag] = token
|
||
conditionTag = tag <span class="hljs-keyword">in</span> [<span class="hljs-string">'->'</span>, <span class="hljs-string">'=>'</span>] <span class="hljs-keyword">and</span>
|
||
@findTagsBackwards(i, [<span class="hljs-string">'IF'</span>, <span class="hljs-string">'WHILE'</span>, <span class="hljs-string">'FOR'</span>, <span class="hljs-string">'UNTIL'</span>, <span class="hljs-string">'SWITCH'</span>, <span class="hljs-string">'WHEN'</span>, <span class="hljs-string">'LEADING_WHEN'</span>, <span class="hljs-string">'['</span>, <span class="hljs-string">'INDEX_START'</span>]) <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> (@findTagsBackwards i, [<span class="hljs-string">'THEN'</span>, <span class="hljs-string">'..'</span>, <span class="hljs-string">'...'</span>])
|
||
|
||
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span>
|
||
<span class="hljs-keyword">if</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'ELSE'</span> <span class="hljs-keyword">and</span> @tag(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">isnt</span> <span class="hljs-string">'OUTDENT'</span>
|
||
tokens.splice i, <span class="hljs-number">1</span>, @indentation()...
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">if</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">in</span> EXPRESSION_CLOSE
|
||
tokens.splice i, <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'CATCH'</span>
|
||
<span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> [<span class="hljs-number">1.</span><span class="hljs-number">.2</span>] <span class="hljs-keyword">when</span> @tag(i + j) <span class="hljs-keyword">in</span> [<span class="hljs-string">'OUTDENT'</span>, <span class="hljs-string">'TERMINATOR'</span>, <span class="hljs-string">'FINALLY'</span>]
|
||
tokens.splice i + j, <span class="hljs-number">0</span>, @indentation()...
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">2</span> + j
|
||
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> [<span class="hljs-string">'->'</span>, <span class="hljs-string">'=>'</span>] <span class="hljs-keyword">and</span> (@tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">','</span> <span class="hljs-keyword">or</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'.'</span> <span class="hljs-keyword">and</span> token.newLine)
|
||
[indent, outdent] = @indentation tokens[i]
|
||
tokens.splice i + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, indent, outdent
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">in</span> SINGLE_LINERS <span class="hljs-keyword">and</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">isnt</span> <span class="hljs-string">'INDENT'</span> <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> (tag <span class="hljs-keyword">is</span> <span class="hljs-string">'ELSE'</span> <span class="hljs-keyword">and</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'IF'</span>) <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> conditionTag
|
||
starter = tag
|
||
[indent, outdent] = @indentation tokens[i]
|
||
indent.fromThen = <span class="hljs-literal">true</span> <span class="hljs-keyword">if</span> starter <span class="hljs-keyword">is</span> <span class="hljs-string">'THEN'</span>
|
||
<span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'THEN'</span>
|
||
leading_switch_when = @findTagsBackwards(i, [<span class="hljs-string">'LEADING_WHEN'</span>]) <span class="hljs-keyword">and</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'IF'</span>
|
||
leading_if_then = @findTagsBackwards(i, [<span class="hljs-string">'IF'</span>]) <span class="hljs-keyword">and</span> @tag(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'IF'</span>
|
||
ifThens.push i <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'THEN'</span> <span class="hljs-keyword">and</span> @findTagsBackwards(i, [<span class="hljs-string">'IF'</span>])</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-54">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-54">¶</a>
|
||
</div>
|
||
<p><code>ELSE</code> tag is not closed.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'ELSE'</span> <span class="hljs-keyword">and</span> @tag(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">isnt</span> <span class="hljs-string">'OUTDENT'</span>
|
||
i = closeElseTag tokens, i
|
||
tokens.splice i + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, indent
|
||
@detectEnd i + <span class="hljs-number">2</span>, condition, action
|
||
tokens.splice i, <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> tag <span class="hljs-keyword">is</span> <span class="hljs-string">'THEN'</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-55">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-55">¶</a>
|
||
</div>
|
||
<p>Tag postfix conditionals as such, so that we can parse them with a
|
||
different precedence.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> tagPostfixConditionals: <span class="hljs-function">-></span>
|
||
original = <span class="hljs-literal">null</span>
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">condition</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||
[tag] = token
|
||
[prevTag] = @tokens[i - <span class="hljs-number">1</span>]
|
||
tag <span class="hljs-keyword">is</span> <span class="hljs-string">'TERMINATOR'</span> <span class="hljs-keyword">or</span> (tag <span class="hljs-keyword">is</span> <span class="hljs-string">'INDENT'</span> <span class="hljs-keyword">and</span> prevTag <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> SINGLE_LINERS)
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">action</span> = <span class="hljs-params">(token, i)</span> -></span>
|
||
<span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">isnt</span> <span class="hljs-string">'INDENT'</span> <span class="hljs-keyword">or</span> (token.generated <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> token.fromThen)
|
||
original[<span class="hljs-number">0</span>] = <span class="hljs-string">'POST_'</span> + original[<span class="hljs-number">0</span>]
|
||
|
||
@scanTokens (token, i) ->
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span> <span class="hljs-keyword">unless</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'IF'</span>
|
||
original = token
|
||
@detectEnd i + <span class="hljs-number">1</span>, condition, action
|
||
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-56">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-56">¶</a>
|
||
</div>
|
||
<p>Generate the indentation tokens, based on another token on the same line.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> indentation: <span class="hljs-function"><span class="hljs-params">(origin)</span> -></span>
|
||
indent = [<span class="hljs-string">'INDENT'</span>, <span class="hljs-number">2</span>]
|
||
outdent = [<span class="hljs-string">'OUTDENT'</span>, <span class="hljs-number">2</span>]
|
||
<span class="hljs-keyword">if</span> origin
|
||
indent.generated = outdent.generated = <span class="hljs-literal">yes</span>
|
||
indent.origin = outdent.origin = origin
|
||
<span class="hljs-keyword">else</span>
|
||
indent.explicit = outdent.explicit = <span class="hljs-literal">yes</span>
|
||
[indent, outdent]
|
||
|
||
generate: generate</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-57">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-57">¶</a>
|
||
</div>
|
||
<p>Look up a tag by token index.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> tag: <span class="hljs-function"><span class="hljs-params">(i)</span> -></span> @tokens[i]?[<span class="hljs-number">0</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-58">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-58">¶</a>
|
||
</div>
|
||
<h2 id="constants">Constants</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-59">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-59">¶</a>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-60">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-60">¶</a>
|
||
</div>
|
||
<p>List of the token pairs that must be balanced.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>BALANCED_PAIRS = [
|
||
[<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">'INDENT'</span>, <span class="hljs-string">'OUTDENT'</span>],
|
||
[<span class="hljs-string">'CALL_START'</span>, <span class="hljs-string">'CALL_END'</span>]
|
||
[<span class="hljs-string">'PARAM_START'</span>, <span class="hljs-string">'PARAM_END'</span>]
|
||
[<span class="hljs-string">'INDEX_START'</span>, <span class="hljs-string">'INDEX_END'</span>]
|
||
[<span class="hljs-string">'STRING_START'</span>, <span class="hljs-string">'STRING_END'</span>]
|
||
[<span class="hljs-string">'REGEX_START'</span>, <span class="hljs-string">'REGEX_END'</span>]
|
||
]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-61">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-61">¶</a>
|
||
</div>
|
||
<p>The inverse mappings of <code>BALANCED_PAIRS</code> we’re trying to fix up, so we can
|
||
look things up from either end.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>exports.INVERSES = INVERSES = {}</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-62">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-62">¶</a>
|
||
</div>
|
||
<p>The tokens that signal the start/end of a balanced pair.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>EXPRESSION_START = []
|
||
EXPRESSION_END = []
|
||
|
||
<span class="hljs-keyword">for</span> [left, right] <span class="hljs-keyword">in</span> BALANCED_PAIRS
|
||
EXPRESSION_START.push INVERSES[right] = left
|
||
EXPRESSION_END .push INVERSES[left] = right</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-63">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-63">¶</a>
|
||
</div>
|
||
<p>Tokens that indicate the close of a clause of an expression.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>EXPRESSION_CLOSE = [<span class="hljs-string">'CATCH'</span>, <span class="hljs-string">'THEN'</span>, <span class="hljs-string">'ELSE'</span>, <span class="hljs-string">'FINALLY'</span>].concat EXPRESSION_END</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-64">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-64">¶</a>
|
||
</div>
|
||
<p>Tokens that, if followed by an <code>IMPLICIT_CALL</code>, indicate a function invocation.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>IMPLICIT_FUNC = [<span class="hljs-string">'IDENTIFIER'</span>, <span class="hljs-string">'PROPERTY'</span>, <span class="hljs-string">'SUPER'</span>, <span class="hljs-string">')'</span>, <span class="hljs-string">'CALL_END'</span>, <span class="hljs-string">']'</span>, <span class="hljs-string">'INDEX_END'</span>, <span class="hljs-string">'@'</span>, <span class="hljs-string">'THIS'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-65">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-65">¶</a>
|
||
</div>
|
||
<p>If preceded by an <code>IMPLICIT_FUNC</code>, indicates a function invocation.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>IMPLICIT_CALL = [
|
||
<span class="hljs-string">'IDENTIFIER'</span>, <span class="hljs-string">'CSX_TAG'</span>, <span class="hljs-string">'PROPERTY'</span>, <span class="hljs-string">'NUMBER'</span>, <span class="hljs-string">'INFINITY'</span>, <span class="hljs-string">'NAN'</span>
|
||
<span class="hljs-string">'STRING'</span>, <span class="hljs-string">'STRING_START'</span>, <span class="hljs-string">'REGEX'</span>, <span class="hljs-string">'REGEX_START'</span>, <span class="hljs-string">'JS'</span>
|
||
<span class="hljs-string">'NEW'</span>, <span class="hljs-string">'PARAM_START'</span>, <span class="hljs-string">'CLASS'</span>, <span class="hljs-string">'IF'</span>, <span class="hljs-string">'TRY'</span>, <span class="hljs-string">'SWITCH'</span>, <span class="hljs-string">'THIS'</span>
|
||
<span class="hljs-string">'UNDEFINED'</span>, <span class="hljs-string">'NULL'</span>, <span class="hljs-string">'BOOL'</span>
|
||
<span class="hljs-string">'UNARY'</span>, <span class="hljs-string">'YIELD'</span>, <span class="hljs-string">'AWAIT'</span>, <span class="hljs-string">'UNARY_MATH'</span>, <span class="hljs-string">'SUPER'</span>, <span class="hljs-string">'THROW'</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">'{'</span>, <span class="hljs-string">'--'</span>, <span class="hljs-string">'++'</span>
|
||
]
|
||
|
||
IMPLICIT_UNSPACED_CALL = [<span class="hljs-string">'+'</span>, <span class="hljs-string">'-'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-66">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-66">¶</a>
|
||
</div>
|
||
<p>Tokens that always mark the end of an implicit call for single-liners.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>IMPLICIT_END = [<span class="hljs-string">'POST_IF'</span>, <span class="hljs-string">'FOR'</span>, <span class="hljs-string">'WHILE'</span>, <span class="hljs-string">'UNTIL'</span>, <span class="hljs-string">'WHEN'</span>, <span class="hljs-string">'BY'</span>,
|
||
<span class="hljs-string">'LOOP'</span>, <span class="hljs-string">'TERMINATOR'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-67">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-67">¶</a>
|
||
</div>
|
||
<p>Single-line flavors of block expressions that have unclosed endings.
|
||
The grammar can’t disambiguate them, so we insert the implicit indentation.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>SINGLE_LINERS = [<span class="hljs-string">'ELSE'</span>, <span class="hljs-string">'->'</span>, <span class="hljs-string">'=>'</span>, <span class="hljs-string">'TRY'</span>, <span class="hljs-string">'FINALLY'</span>, <span class="hljs-string">'THEN'</span>]
|
||
SINGLE_CLOSERS = [<span class="hljs-string">'TERMINATOR'</span>, <span class="hljs-string">'CATCH'</span>, <span class="hljs-string">'FINALLY'</span>, <span class="hljs-string">'ELSE'</span>, <span class="hljs-string">'OUTDENT'</span>, <span class="hljs-string">'LEADING_WHEN'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-68">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-68">¶</a>
|
||
</div>
|
||
<p>Tokens that end a line.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>LINEBREAKS = [<span class="hljs-string">'TERMINATOR'</span>, <span class="hljs-string">'INDENT'</span>, <span class="hljs-string">'OUTDENT'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-69">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-69">¶</a>
|
||
</div>
|
||
<p>Tokens that close open calls when they follow a newline.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>CALL_CLOSERS = [<span class="hljs-string">'.'</span>, <span class="hljs-string">'?.'</span>, <span class="hljs-string">'::'</span>, <span class="hljs-string">'?::'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-70">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-70">¶</a>
|
||
</div>
|
||
<p>Tokens that prevent a subsequent indent from ending implicit calls/objects</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>CONTROL_IN_IMPLICIT = [<span class="hljs-string">'IF'</span>, <span class="hljs-string">'TRY'</span>, <span class="hljs-string">'FINALLY'</span>, <span class="hljs-string">'CATCH'</span>, <span class="hljs-string">'CLASS'</span>, <span class="hljs-string">'SWITCH'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-71">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-71">¶</a>
|
||
</div>
|
||
<p>Tokens that are swallowed up by the parser, never leading to code generation.
|
||
You can spot these in <code>grammar.coffee</code> because the <code>o</code> function second
|
||
argument doesn’t contain a <code>new</code> call for these tokens.
|
||
<code>STRING_START</code> isn’t on this list because its <code>locationData</code> matches that of
|
||
the node that becomes <code>StringWithInterpolations</code>, and therefore
|
||
<code>addDataToNode</code> attaches <code>STRING_START</code>’s tokens to that node.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>DISCARDED = [<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">'.'</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">'--'</span>, <span class="hljs-string">'?'</span>,
|
||
<span class="hljs-string">'AS'</span>, <span class="hljs-string">'AWAIT'</span>, <span class="hljs-string">'CALL_START'</span>, <span class="hljs-string">'CALL_END'</span>, <span class="hljs-string">'DEFAULT'</span>, <span class="hljs-string">'ELSE'</span>, <span class="hljs-string">'EXTENDS'</span>, <span class="hljs-string">'EXPORT'</span>,
|
||
<span class="hljs-string">'FORIN'</span>, <span class="hljs-string">'FOROF'</span>, <span class="hljs-string">'FORFROM'</span>, <span class="hljs-string">'IMPORT'</span>, <span class="hljs-string">'INDENT'</span>, <span class="hljs-string">'INDEX_SOAK'</span>, <span class="hljs-string">'LEADING_WHEN'</span>,
|
||
<span class="hljs-string">'OUTDENT'</span>, <span class="hljs-string">'PARAM_END'</span>, <span class="hljs-string">'REGEX_START'</span>, <span class="hljs-string">'REGEX_END'</span>, <span class="hljs-string">'RETURN'</span>, <span class="hljs-string">'STRING_END'</span>, <span class="hljs-string">'THROW'</span>,
|
||
<span class="hljs-string">'UNARY'</span>, <span class="hljs-string">'YIELD'</span>
|
||
].concat IMPLICIT_UNSPACED_CALL.concat IMPLICIT_END.concat CALL_CLOSERS.concat CONTROL_IN_IMPLICIT</pre></div></div>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
</body>
|
||
</html>
|