mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
1152 lines
65 KiB
HTML
1152 lines
65 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">
|
|
|
|
|
|
<a class="source" href="browser.html">
|
|
browser.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="cake.html">
|
|
cake.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="coffee-script.html">
|
|
coffee-script.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>
|
|
</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>
|
|
<p>Create a generated token: one that exists due to a use of implicit syntax.</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)</span> -></span>
|
|
tok = [tag, value]
|
|
tok.generated = <span class="hljs-literal">yes</span>
|
|
tok.origin = origin <span class="hljs-keyword">if</span> origin
|
|
tok</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-2">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-2">¶</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><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">exports</span>.<span class="hljs-title">Rewriter</span></span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-3">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-3">¶</a>
|
|
</div>
|
|
<p>Helpful snippet for debugging:</p>
|
|
<pre><code>console.log (t[0] + '/' + t[1] for t in @tokens).join ' '
|
|
</code></pre><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> <span class="hljs-attribute">rewrite</span>: <span class="hljs-function"><span class="hljs-params">(<span class="hljs-property">@tokens</span>)</span> -></span>
|
|
<span class="hljs-property">@removeLeadingNewlines</span>()
|
|
<span class="hljs-property">@closeOpenCalls</span>()
|
|
<span class="hljs-property">@closeOpenIndexes</span>()
|
|
<span class="hljs-property">@normalizeLines</span>()
|
|
<span class="hljs-property">@tagPostfixConditionals</span>()
|
|
<span class="hljs-property">@addImplicitBracesAndParens</span>()
|
|
<span class="hljs-property">@addLocationDataToGeneratedTokens</span>()
|
|
<span class="hljs-property">@tokens</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-4">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-4">¶</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> <span class="hljs-attribute">scanTokens</span>: <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>
|
|
|
|
<span class="hljs-attribute">detectEnd</span>: <span class="hljs-function"><span class="hljs-params">(i, condition, action)</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">return</span> action.call <span class="hljs-keyword">this</span>, token, i - <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> token <span class="hljs-keyword">or</span> levels < <span class="hljs-number">0</span>
|
|
<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>
|
|
i += <span class="hljs-number">1</span>
|
|
i - <span class="hljs-number">1</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-5">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-5">¶</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> <span class="hljs-attribute">removeLeadingNewlines</span>:<span class="hljs-function"> -></span>
|
|
<span class="hljs-keyword">break</span> <span class="hljs-keyword">for</span> [tag], i <span class="hljs-keyword">in</span> <span class="hljs-property">@tokens</span> <span class="hljs-keyword">when</span> tag <span class="hljs-keyword">isnt</span> <span class="hljs-string">'TERMINATOR'</span>
|
|
<span class="hljs-property">@tokens</span>.splice <span class="hljs-number">0</span>, i <span class="hljs-keyword">if</span> i</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-6">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-6">¶</a>
|
|
</div>
|
|
<p>The lexer has tagged the opening parenthesis of a method call. Match it with
|
|
its paired close. We have the mis-nested outdent case included here for
|
|
calls that close on the same line, just before their outdent.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">closeOpenCalls</span>:<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-keyword">or</span>
|
|
token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</span> <span class="hljs-keyword">and</span> <span class="hljs-property">@tag</span>(i - <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">')'</span>
|
|
|
|
<span class="hljs-function"><span class="hljs-title">action</span> = <span class="hljs-params">(token, i)</span> -></span>
|
|
<span class="hljs-property">@tokens</span>[<span class="hljs-keyword">if</span> token[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'OUTDENT'</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>] = <span class="hljs-string">'CALL_END'</span>
|
|
|
|
<span class="hljs-property">@scanTokens</span> <span class="hljs-function"><span class="hljs-params">(token, i)</span> -></span>
|
|
<span class="hljs-property">@detectEnd</span> 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-7">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-7">¶</a>
|
|
</div>
|
|
<p>The lexer has tagged the opening parenthesis of an indexing operation call.
|
|
Match it with its paired close.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">closeOpenIndexes</span>:<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>
|
|
|
|
<span class="hljs-property">@scanTokens</span> <span class="hljs-function"><span class="hljs-params">(token, i)</span> -></span>
|
|
<span class="hljs-property">@detectEnd</span> 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-8">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-8">¶</a>
|
|
</div>
|
|
<p>Match tags in token stream starting at i with pattern, skipping HERECOMMENTs
|
|
Pattern may consist of strings (equality), an array of strings (one of)
|
|
or null (wildcard)</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">matchTags</span>: <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]
|
|
fuzz += <span class="hljs-number">2</span> <span class="hljs-keyword">while</span> <span class="hljs-property">@tag</span>(i + j + fuzz) <span class="hljs-keyword">is</span> <span class="hljs-string">'HERECOMMENT'</span>
|
|
<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-literal">no</span> <span class="hljs-keyword">if</span> <span class="hljs-property">@tag</span>(i + j + fuzz) <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> pattern[j]
|
|
<span class="hljs-literal">yes</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-9">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-9">¶</a>
|
|
</div>
|
|
<p>yes iff standing in front of something looking like
|
|
@<x>: or <x>:, skipping over 'HERECOMMENT's</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">looksObjectish</span>: <span class="hljs-function"><span class="hljs-params">(j)</span> -></span>
|
|
<span class="hljs-property">@matchTags</span>(j, <span class="hljs-string">'@'</span>, <span class="hljs-literal">null</span>, <span class="hljs-string">':'</span>) <span class="hljs-keyword">or</span> <span class="hljs-property">@matchTags</span>(j, <span class="hljs-literal">null</span>, <span class="hljs-string">':'</span>)</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-10">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-10">¶</a>
|
|
</div>
|
|
<p>yes iff current line of tokens contain an element of tags on same
|
|
expression level. Stop searching at LINEBREAKS or explicit start of
|
|
containing balanced expression.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">findTagsBackwards</span>: <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>
|
|
<span class="hljs-property">@tag</span>(i) <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> tags <span class="hljs-keyword">and</span>
|
|
(<span class="hljs-property">@tag</span>(i) <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> EXPRESSION_START <span class="hljs-keyword">or</span> <span class="hljs-property">@tokens</span>[i].generated) <span class="hljs-keyword">and</span>
|
|
<span class="hljs-property">@tag</span>(i) <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> LINEBREAKS)
|
|
backStack.push <span class="hljs-property">@tag</span>(i) <span class="hljs-keyword">if</span> <span class="hljs-property">@tag</span>(i) <span class="hljs-keyword">in</span> EXPRESSION_END
|
|
backStack.pop() <span class="hljs-keyword">if</span> <span class="hljs-property">@tag</span>(i) <span class="hljs-keyword">in</span> EXPRESSION_START <span class="hljs-keyword">and</span> backStack.length
|
|
i -= <span class="hljs-number">1</span>
|
|
<span class="hljs-property">@tag</span>(i) <span class="hljs-keyword">in</span> tags</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-11">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-11">¶</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> <span class="hljs-attribute">addImplicitBracesAndParens</span>:<span class="hljs-function"> -></span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-12">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-12">¶</a>
|
|
</div>
|
|
<p>Track current balancing depth (both implicit and explicit) on stack.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> stack = []
|
|
|
|
<span class="hljs-property">@scanTokens</span> <span class="hljs-function"><span class="hljs-params">(token, i, tokens)</span> -></span>
|
|
[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] = <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-13">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-13">¶</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-14">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-14">¶</a>
|
|
</div>
|
|
<p>Helper functions</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-title">inImplicit</span> = -></span> stackTop()?[<span class="hljs-number">2</span>]?.ours
|
|
<span class="hljs-function"><span class="hljs-title">inImplicitCall</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">'('</span>
|
|
<span class="hljs-function"><span class="hljs-title">inImplicitObject</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">'{'</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-15">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-15">¶</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">(j)</span> -></span>
|
|
idx = j ? i
|
|
stack.push [<span class="hljs-string">'('</span>, idx, <span class="hljs-attribute">ours</span>: <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>
|
|
i += <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> j?
|
|
|
|
<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>
|
|
i += <span class="hljs-number">1</span>
|
|
|
|
<span class="hljs-function"><span class="hljs-title">startImplicitObject</span> = <span class="hljs-params">(j, startsLine = <span class="hljs-literal">yes</span>)</span> -></span>
|
|
idx = j ? i
|
|
stack.push [<span class="hljs-string">'{'</span>, idx, <span class="hljs-attribute">sameLine</span>: <span class="hljs-literal">yes</span>, <span class="hljs-attribute">startsLine</span>: startsLine, <span class="hljs-attribute">ours</span>: <span class="hljs-literal">yes</span>]
|
|
tokens.splice idx, <span class="hljs-number">0</span>, generate <span class="hljs-string">'{'</span>, generate(<span class="hljs-keyword">new</span> String(<span class="hljs-string">'{'</span>)), token
|
|
i += <span class="hljs-number">1</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> j?
|
|
|
|
<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
|
|
i += <span class="hljs-number">1</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-16">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-16">¶</a>
|
|
</div>
|
|
<p>Don't end an implicit call on next indent if any of these are in an argument</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> inImplicitCall() <span class="hljs-keyword">and</span> tag <span class="hljs-keyword">in</span> [<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>]
|
|
stack.push [<span class="hljs-string">'CONTROL'</span>, i, <span class="hljs-attribute">ours</span>: <span class="hljs-literal">true</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-17">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-17">¶</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">'TRY'</span>, <span class="hljs-string">'ELSE'</span>, <span class="hljs-string">'='</span>]
|
|
endImplicitCall() <span class="hljs-keyword">while</span> inImplicitCall()
|
|
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-18">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-18">¶</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-19">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-19">¶</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()
|
|
stack.pop()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-20">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-20">¶</a>
|
|
</div>
|
|
<p>Recognize standard implicit calls like
|
|
f a, f() b, f? c, h[0] d etc.</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">and</span> <span class="hljs-keyword">not</span> token.stringEnd <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">in</span> IMPLICIT_UNSPACED_CALL <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> <span class="hljs-keyword">not</span> tokens[i + <span class="hljs-number">1</span>]?.newLine)
|
|
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-21">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-21">¶</a>
|
|
</div>
|
|
<p>Implicit call taking an implicit indented object as first argument.</p>
|
|
<pre><code>f
|
|
a: b
|
|
c: d
|
|
</code></pre><p>and</p>
|
|
<pre><code>f
|
|
1
|
|
a: b
|
|
b: c
|
|
</code></pre><p>Don't accept implicit calls of this type, when on the same line
|
|
as the control strucutures below as that may misinterpret constructs like:</p>
|
|
<pre><code>if f
|
|
a: 1
|
|
</code></pre><p>as</p>
|
|
<pre><code>if f(a: 1)
|
|
</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> <span class="hljs-property">@matchTags</span>(i + <span class="hljs-number">1</span>, <span class="hljs-string">'INDENT'</span>, <span class="hljs-literal">null</span>, <span class="hljs-string">':'</span>) <span class="hljs-keyword">and</span>
|
|
<span class="hljs-keyword">not</span> <span class="hljs-property">@findTagsBackwards</span>(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-22">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-22">¶</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-23">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-23">¶</a>
|
|
</div>
|
|
<p>Go back to the (implicit) start of the object</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> <span class="hljs-property">@tag</span>(i - <span class="hljs-number">2</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'@'</span> <span class="hljs-keyword">then</span> s = i - <span class="hljs-number">2</span> <span class="hljs-keyword">else</span> s = i - <span class="hljs-number">1</span>
|
|
s -= <span class="hljs-number">2</span> <span class="hljs-keyword">while</span> <span class="hljs-property">@tag</span>(s - <span class="hljs-number">2</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'HERECOMMENT'</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-24">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-24">¶</a>
|
|
</div>
|
|
<p>Mark if the value is a for loop</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-property">@insideForDeclaration</span> = nextTag <span class="hljs-keyword">is</span> <span class="hljs-string">'FOR'</span>
|
|
|
|
startsLine = s <span class="hljs-keyword">is</span> <span class="hljs-number">0</span> <span class="hljs-keyword">or</span> <span class="hljs-property">@tag</span>(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-25">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-25">¶</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> <span class="hljs-property">@tag</span>(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> <span class="hljs-property">@tag</span>(s - <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">','</span> <span class="hljs-keyword">or</span> <span class="hljs-property">@tag</span>(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-26">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-26">¶</a>
|
|
</div>
|
|
<p>End implicit calls when chaining method calls
|
|
like e.g.:</p>
|
|
<pre><code>f ->
|
|
a
|
|
.g b, ->
|
|
c
|
|
.h a
|
|
</code></pre><p>and also</p>
|
|
<pre><code>f a
|
|
.g b
|
|
.h a
|
|
</code></pre>
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> stackTop()[<span class="hljs-number">2</span>].sameLine = <span class="hljs-literal">no</span> <span class="hljs-keyword">if</span> inImplicitObject() <span class="hljs-keyword">and</span> tag <span class="hljs-keyword">in</span> LINEBREAKS
|
|
|
|
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">while</span> inImplicit()
|
|
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-27">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-27">¶</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>
|
|
endImplicitCall()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-28">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-28">¶</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> <span class="hljs-keyword">not</span> <span class="hljs-property">@insideForDeclaration</span> <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>
|
|
endImplicitObject()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-29">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-29">¶</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> <span class="hljs-property">@looksObjectish</span>(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-30">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-30">¶</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 = 2
|
|
</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> <span class="hljs-property">@looksObjectish</span>(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> <span class="hljs-property">@insideForDeclaration</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> <span class="hljs-property">@looksObjectish</span>(i + <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>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-32">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-32">¶</a>
|
|
</div>
|
|
<p>Add location data to all tokens generated by the rewriter.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">addLocationDataToGeneratedTokens</span>:<span class="hljs-function"> -></span>
|
|
<span class="hljs-property">@scanTokens</span> <span class="hljs-function"><span class="hljs-params">(token, i, tokens)</span> -></span>
|
|
<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>]
|
|
{<span class="hljs-attribute">first_line</span>: line, <span class="hljs-attribute">first_column</span>: 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>]
|
|
{<span class="hljs-attribute">last_line</span>: line, <span class="hljs-attribute">last_column</span>: column} = prevLocation
|
|
<span class="hljs-keyword">else</span>
|
|
line = column = <span class="hljs-number">0</span>
|
|
token[<span class="hljs-number">2</span>] =
|
|
<span class="hljs-attribute">first_line</span>: line
|
|
<span class="hljs-attribute">first_column</span>: column
|
|
<span class="hljs-attribute">last_line</span>: line
|
|
<span class="hljs-attribute">last_column</span>: column
|
|
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-33">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-33">¶</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> <span class="hljs-attribute">normalizeLines</span>:<span class="hljs-function"> -></span>
|
|
starter = indent = outdent = <span class="hljs-literal">null</span>
|
|
|
|
<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> <span class="hljs-property">@tag</span>(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">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> <span class="hljs-property">@tokens</span>[i - <span class="hljs-number">1</span>].newLine
|
|
|
|
<span class="hljs-function"><span class="hljs-title">action</span> = <span class="hljs-params">(token, i)</span> -></span>
|
|
<span class="hljs-property">@tokens</span>.splice (<span class="hljs-keyword">if</span> <span class="hljs-property">@tag</span>(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-property">@scanTokens</span> <span class="hljs-function"><span class="hljs-params">(token, i, tokens)</span> -></span>
|
|
[tag] = token
|
|
<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> <span class="hljs-property">@tag</span>(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> <span class="hljs-property">@tag</span>(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>, <span class="hljs-property">@indentation</span>()...
|
|
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
|
|
<span class="hljs-keyword">if</span> <span class="hljs-property">@tag</span>(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> <span class="hljs-property">@tag</span>(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>, <span class="hljs-property">@indentation</span>()...
|
|
<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> SINGLE_LINERS <span class="hljs-keyword">and</span> <span class="hljs-property">@tag</span>(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> <span class="hljs-property">@tag</span>(i + <span class="hljs-number">1</span>) <span class="hljs-keyword">is</span> <span class="hljs-string">'IF'</span>)
|
|
starter = tag
|
|
[indent, outdent] = <span class="hljs-property">@indentation</span> 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>
|
|
tokens.splice i + <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, indent
|
|
<span class="hljs-property">@detectEnd</span> 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-34">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-34">¶</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> <span class="hljs-attribute">tagPostfixConditionals</span>:<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] = <span class="hljs-property">@tokens</span>[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>]
|
|
|
|
<span class="hljs-property">@scanTokens</span> <span class="hljs-function"><span class="hljs-params">(token, i)</span> -></span>
|
|
<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
|
|
<span class="hljs-property">@detectEnd</span> 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-35">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-35">¶</a>
|
|
</div>
|
|
<p>Generate the indentation tokens, based on another token on the same line.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">indentation</span>: <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]
|
|
|
|
<span class="hljs-attribute">generate</span>: generate</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-36">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-36">¶</a>
|
|
</div>
|
|
<p>Look up a tag by token index.</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="hljs-attribute">tag</span>: <span class="hljs-function"><span class="hljs-params">(i)</span> -></span> <span class="hljs-property">@tokens</span>[i]?[<span class="hljs-number">0</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-37">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-37">¶</a>
|
|
</div>
|
|
<h2 id="constants">Constants</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-38">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-38">¶</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>]
|
|
]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-39">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-39">¶</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><span class="hljs-built_in">exports</span>.INVERSES = INVERSES = {}</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-40">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-40">¶</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, rite] <span class="hljs-keyword">in</span> BALANCED_PAIRS
|
|
EXPRESSION_START.push INVERSES[rite] = left
|
|
EXPRESSION_END .push INVERSES[left] = rite</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-41">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-41">¶</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-42">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-42">¶</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">'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-43">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-43">¶</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">'NUMBER'</span>, <span class="hljs-string">'STRING'</span>, <span class="hljs-string">'JS'</span>, <span class="hljs-string">'REGEX'</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">'BOOL'</span>, <span class="hljs-string">'NULL'</span>, <span class="hljs-string">'UNDEFINED'</span>, <span class="hljs-string">'UNARY'</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-44">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-44">¶</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-45">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-45">¶</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-46">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-46">¶</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-47">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-47">¶</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>
|
|
|
|
</ul>
|
|
</div>
|
|
</body>
|
|
</html>
|