10786 lines
585 KiB
HTML
10786 lines
585 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html>
|
||
<head>
|
||
<title>nodes.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>nodes.coffee</h1>
|
||
</div>
|
||
</li>
|
||
|
||
|
||
|
||
<li id="section-1">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-1">§</a>
|
||
</div>
|
||
<p><code>nodes.coffee</code> contains all of the node classes for the syntax tree. Most
|
||
nodes are created as the result of actions in the <a href="grammar.html">grammar</a>,
|
||
but some are created by other nodes as a method of code generation. To convert
|
||
the syntax tree into a string of JavaScript code, call <code>compile()</code> on the root.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>
|
||
<span class="hljs-built_in">Error</span>.stackTraceLimit = <span class="hljs-literal">Infinity</span>
|
||
|
||
{Scope} = <span class="hljs-built_in">require</span> <span class="hljs-string">'./scope'</span>
|
||
{isUnassignable, JS_FORBIDDEN} = <span class="hljs-built_in">require</span> <span class="hljs-string">'./lexer'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-2">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-2">§</a>
|
||
</div>
|
||
<p>Import the helpers we plan to use.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>{compact, flatten, extend, merge, del, starts, ends, some,
|
||
addDataToNode, attachCommentsToNode, locationDataToString,
|
||
throwSyntaxError, replaceUnicodeCodePointEscapes,
|
||
isFunction, isPlainObject, isNumber, parseNumber} = <span class="hljs-built_in">require</span> <span class="hljs-string">'./helpers'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-3">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-3">§</a>
|
||
</div>
|
||
<p>Functions required by parser.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.extend = extend
|
||
<span class="hljs-built_in">exports</span>.addDataToNode = addDataToNode</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-4">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-4">§</a>
|
||
</div>
|
||
<p>Constant functions for nodes that don’t need customization.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">YES</span> = -></span> <span class="hljs-literal">yes</span>
|
||
<span class="hljs-function"><span class="hljs-title">NO</span> = -></span> <span class="hljs-literal">no</span>
|
||
<span class="hljs-function"><span class="hljs-title">THIS</span> = -></span> this
|
||
<span class="hljs-function"><span class="hljs-title">NEGATE</span> = -></span> @negated = <span class="hljs-keyword">not</span> @negated; this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-5">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-5">§</a>
|
||
</div>
|
||
<h3 id="codefragment">CodeFragment</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-6">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-6">§</a>
|
||
</div>
|
||
<p>The various nodes defined below all compile to a collection of <strong>CodeFragment</strong> objects.
|
||
A CodeFragments is a block of generated code, and the location in the source file where the code
|
||
came from. CodeFragments can be assembled together into working code just by catting together
|
||
all the CodeFragments’ <code>code</code> snippets, in order.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.CodeFragment = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CodeFragment</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(parent, code)</span> -></span>
|
||
@code = <span class="hljs-string">"<span class="hljs-subst">#{code}</span>"</span>
|
||
@type = parent?.constructor?.name <span class="hljs-keyword">or</span> <span class="hljs-string">'unknown'</span>
|
||
@locationData = parent?.locationData
|
||
@comments = parent?.comments
|
||
|
||
toString: <span class="hljs-function">-></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-7">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-7">§</a>
|
||
</div>
|
||
<p>This is only intended for debugging.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-string">"<span class="hljs-subst">#{@code}</span><span class="hljs-subst">#{<span class="hljs-keyword">if</span> @locationData <span class="hljs-keyword">then</span> <span class="hljs-string">": "</span> + locationDataToString(@locationData) <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>}</span>"</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-8">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-8">§</a>
|
||
</div>
|
||
<p>Convert an array of CodeFragments into a string.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">fragmentsToText</span> = <span class="hljs-params">(fragments)</span> -></span>
|
||
(fragment.code <span class="hljs-keyword">for</span> fragment <span class="hljs-keyword">in</span> fragments).join(<span class="hljs-string">''</span>)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-9">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-9">§</a>
|
||
</div>
|
||
<h3 id="base">Base</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-10">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-10">§</a>
|
||
</div>
|
||
<p>The <strong>Base</strong> is the abstract base class for all nodes in the syntax tree.
|
||
Each subclass implements the <code>compileNode</code> method, which performs the
|
||
code generation for that node. To compile a node to JavaScript,
|
||
call <code>compile</code> on it, which wraps <code>compileNode</code> in some generic extra smarts,
|
||
to know when the generated code needs to be wrapped up in a closure.
|
||
An options hash is passed and cloned throughout, containing information about
|
||
the environment from higher in the tree (such as if a returned value is
|
||
being requested by the surrounding function), information about the current
|
||
scope, and indentation level.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Base = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Base</span></span>
|
||
|
||
compile: <span class="hljs-function"><span class="hljs-params">(o, lvl)</span> -></span>
|
||
fragmentsToText @compileToFragments o, lvl</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-11">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-11">§</a>
|
||
</div>
|
||
<p>Occasionally a node is compiled multiple times, for example to get the name
|
||
of a variable to add to scope tracking. When we know that a “premature”
|
||
compilation won’t result in comments being output, set those comments aside
|
||
so that they’re preserved for a later <code>compile</code> call that will result in
|
||
the comments being included in the output.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileWithoutComments: <span class="hljs-function"><span class="hljs-params">(o, lvl, method = <span class="hljs-string">'compile'</span>)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @comments
|
||
@ignoreTheseCommentsTemporarily = @comments
|
||
<span class="hljs-keyword">delete</span> @comments
|
||
unwrapped = @unwrapAll()
|
||
<span class="hljs-keyword">if</span> unwrapped.comments
|
||
unwrapped.ignoreTheseCommentsTemporarily = unwrapped.comments
|
||
<span class="hljs-keyword">delete</span> unwrapped.comments
|
||
|
||
fragments = @[method] o, lvl
|
||
|
||
<span class="hljs-keyword">if</span> @ignoreTheseCommentsTemporarily
|
||
@comments = @ignoreTheseCommentsTemporarily
|
||
<span class="hljs-keyword">delete</span> @ignoreTheseCommentsTemporarily
|
||
<span class="hljs-keyword">if</span> unwrapped.ignoreTheseCommentsTemporarily
|
||
unwrapped.comments = unwrapped.ignoreTheseCommentsTemporarily
|
||
<span class="hljs-keyword">delete</span> unwrapped.ignoreTheseCommentsTemporarily
|
||
|
||
fragments
|
||
|
||
compileNodeWithoutComments: <span class="hljs-function"><span class="hljs-params">(o, lvl)</span> -></span>
|
||
@compileWithoutComments o, lvl, <span class="hljs-string">'compileNode'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-12">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-12">§</a>
|
||
</div>
|
||
<p>Common logic for determining whether to wrap this node in a closure before
|
||
compiling it, or to compile directly. We need to wrap if this node is a
|
||
<em>statement</em>, and it’s not a <em>pureStatement</em>, and we’re not at
|
||
the top level of a block (which would be unnecessary), and we haven’t
|
||
already been asked to return the result (because statements know how to
|
||
return results).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileToFragments: <span class="hljs-function"><span class="hljs-params">(o, lvl)</span> -></span>
|
||
o = extend {}, o
|
||
o.level = lvl <span class="hljs-keyword">if</span> lvl
|
||
node = @unfoldSoak(o) <span class="hljs-keyword">or</span> this
|
||
node.tab = o.indent
|
||
|
||
fragments = <span class="hljs-keyword">if</span> o.level <span class="hljs-keyword">is</span> LEVEL_TOP <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> node.isStatement(o)
|
||
node.compileNode o
|
||
<span class="hljs-keyword">else</span>
|
||
node.compileClosure o
|
||
@compileCommentFragments o, node, fragments
|
||
fragments
|
||
|
||
compileToFragmentsWithoutComments: <span class="hljs-function"><span class="hljs-params">(o, lvl)</span> -></span>
|
||
@compileWithoutComments o, lvl, <span class="hljs-string">'compileToFragments'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-13">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-13">§</a>
|
||
</div>
|
||
<p>Statements converted into expressions via closure-wrapping share a scope
|
||
object with their parent closure, to preserve the expected lexical scope.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileClosure: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkForPureStatementInExpression()
|
||
o.sharedScope = <span class="hljs-literal">yes</span>
|
||
func = <span class="hljs-keyword">new</span> Code [], Block.wrap [this]
|
||
args = []
|
||
<span class="hljs-keyword">if</span> @contains (<span class="hljs-function"><span class="hljs-params">(node)</span> -></span> node <span class="hljs-keyword">instanceof</span> SuperCall)
|
||
func.bound = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (argumentsNode = @contains isLiteralArguments) <span class="hljs-keyword">or</span> @contains isLiteralThis
|
||
args = [<span class="hljs-keyword">new</span> ThisLiteral]
|
||
<span class="hljs-keyword">if</span> argumentsNode
|
||
meth = <span class="hljs-string">'apply'</span>
|
||
args.push <span class="hljs-keyword">new</span> IdentifierLiteral <span class="hljs-string">'arguments'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
meth = <span class="hljs-string">'call'</span>
|
||
func = <span class="hljs-keyword">new</span> Value func, [<span class="hljs-keyword">new</span> Access <span class="hljs-keyword">new</span> PropertyName meth]
|
||
parts = (<span class="hljs-keyword">new</span> Call func, args).compileNode o
|
||
|
||
<span class="hljs-keyword">switch</span>
|
||
<span class="hljs-keyword">when</span> func.isGenerator <span class="hljs-keyword">or</span> func.base?.isGenerator
|
||
parts.unshift @makeCode <span class="hljs-string">"(yield* "</span>
|
||
parts.push @makeCode <span class="hljs-string">")"</span>
|
||
<span class="hljs-keyword">when</span> func.isAsync <span class="hljs-keyword">or</span> func.base?.isAsync
|
||
parts.unshift @makeCode <span class="hljs-string">"(await "</span>
|
||
parts.push @makeCode <span class="hljs-string">")"</span>
|
||
parts
|
||
|
||
compileCommentFragments: <span class="hljs-function"><span class="hljs-params">(o, node, fragments)</span> -></span>
|
||
<span class="hljs-keyword">return</span> fragments <span class="hljs-keyword">unless</span> node.comments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-14">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-14">§</a>
|
||
</div>
|
||
<p>This is where comments, that are attached to nodes as a <code>comments</code>
|
||
property, become <code>CodeFragment</code>s. “Inline block comments,” e.g.
|
||
<code>/* */</code>-delimited comments that are interspersed within code on a line,
|
||
are added to the current <code>fragments</code> stream. All other fragments are
|
||
attached as properties to the nearest preceding or following fragment,
|
||
to remain stowaways until they get properly output in <code>compileComments</code>
|
||
later on.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"> <span class="hljs-title">unshiftCommentFragment</span> = <span class="hljs-params">(commentFragment)</span> -></span>
|
||
<span class="hljs-keyword">if</span> commentFragment.unshift</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-15">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-15">§</a>
|
||
</div>
|
||
<p>Find the first non-comment fragment and insert <code>commentFragment</code>
|
||
before it.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> unshiftAfterComments fragments, commentFragment
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> fragments.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
precedingFragment = fragments[fragments.length - <span class="hljs-number">1</span>]
|
||
<span class="hljs-keyword">if</span> commentFragment.newLine <span class="hljs-keyword">and</span> precedingFragment.code <span class="hljs-keyword">isnt</span> <span class="hljs-string">''</span> <span class="hljs-keyword">and</span>
|
||
<span class="hljs-keyword">not</span> <span class="hljs-regexp">/\n\s*$/</span>.test precedingFragment.code
|
||
commentFragment.code = <span class="hljs-string">"\n<span class="hljs-subst">#{commentFragment.code}</span>"</span>
|
||
fragments.push commentFragment
|
||
|
||
<span class="hljs-keyword">for</span> comment <span class="hljs-keyword">in</span> node.comments <span class="hljs-keyword">when</span> comment <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> @compiledComments
|
||
@compiledComments.push comment <span class="hljs-comment"># Don’t output this comment twice.</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-16">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-16">§</a>
|
||
</div>
|
||
<p>For block/here comments, denoted by <code>###</code>, that are inline comments
|
||
like <code>1 + ### comment ### 2</code>, create fragments and insert them into
|
||
the fragments array.
|
||
Otherwise attach comment fragments to their closest fragment for now,
|
||
so they can be inserted into the output later after all the newlines
|
||
have been added.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> comment.here <span class="hljs-comment"># Block comment, delimited by `###`.</span>
|
||
commentFragment = <span class="hljs-keyword">new</span> HereComment(comment).compileNode o
|
||
<span class="hljs-keyword">else</span> <span class="hljs-comment"># Line comment, delimited by `#`.</span>
|
||
commentFragment = <span class="hljs-keyword">new</span> LineComment(comment).compileNode o
|
||
<span class="hljs-keyword">if</span> (commentFragment.isHereComment <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> commentFragment.newLine) <span class="hljs-keyword">or</span>
|
||
node.includeCommentFragments()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-17">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-17">§</a>
|
||
</div>
|
||
<p>Inline block comments, like <code>1 + /* comment */ 2</code>, or a node whose
|
||
<code>compileToFragments</code> method has logic for outputting comments.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> unshiftCommentFragment commentFragment
|
||
<span class="hljs-keyword">else</span>
|
||
fragments.push @makeCode <span class="hljs-string">''</span> <span class="hljs-keyword">if</span> fragments.length <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">if</span> commentFragment.unshift
|
||
fragments[<span class="hljs-number">0</span>].precedingComments ?= []
|
||
fragments[<span class="hljs-number">0</span>].precedingComments.push commentFragment
|
||
<span class="hljs-keyword">else</span>
|
||
fragments[fragments.length - <span class="hljs-number">1</span>].followingComments ?= []
|
||
fragments[fragments.length - <span class="hljs-number">1</span>].followingComments.push commentFragment
|
||
fragments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-18">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-18">§</a>
|
||
</div>
|
||
<p>If the code generation wishes to use the result of a complex expression
|
||
in multiple places, ensure that the expression is only ever evaluated once,
|
||
by assigning it to a temporary variable. Pass a level to precompile.</p>
|
||
<p>If <code>level</code> is passed, then returns <code>[val, ref]</code>, where <code>val</code> is the compiled value, and <code>ref</code>
|
||
is the compiled reference. If <code>level</code> is not passed, this returns <code>[val, ref]</code> where
|
||
the two values are raw nodes which have not been compiled.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> cache: <span class="hljs-function"><span class="hljs-params">(o, level, shouldCache)</span> -></span>
|
||
complex = <span class="hljs-keyword">if</span> shouldCache? <span class="hljs-keyword">then</span> shouldCache this <span class="hljs-keyword">else</span> @shouldCache()
|
||
<span class="hljs-keyword">if</span> complex
|
||
ref = <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.freeVariable <span class="hljs-string">'ref'</span>
|
||
sub = <span class="hljs-keyword">new</span> Assign ref, this
|
||
<span class="hljs-keyword">if</span> level <span class="hljs-keyword">then</span> [sub.compileToFragments(o, level), [@makeCode(ref.value)]] <span class="hljs-keyword">else</span> [sub, ref]
|
||
<span class="hljs-keyword">else</span>
|
||
ref = <span class="hljs-keyword">if</span> level <span class="hljs-keyword">then</span> @compileToFragments o, level <span class="hljs-keyword">else</span> this
|
||
[ref, ref]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-19">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-19">§</a>
|
||
</div>
|
||
<p>Occasionally it may be useful to make an expression behave as if it was ‘hoisted’, whereby the
|
||
result of the expression is available before its location in the source, but the expression’s
|
||
variable scope corresponds to the source position. This is used extensively to deal with executable
|
||
class bodies in classes.</p>
|
||
<p>Calling this method mutates the node, proxying the <code>compileNode</code> and <code>compileToFragments</code>
|
||
methods to store their result for later replacing the <code>target</code> node, which is returned by the
|
||
call.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> hoist: <span class="hljs-function">-></span>
|
||
@hoisted = <span class="hljs-literal">yes</span>
|
||
target = <span class="hljs-keyword">new</span> HoistTarget @
|
||
|
||
compileNode = @compileNode
|
||
compileToFragments = @compileToFragments
|
||
|
||
@compileNode = <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
target.update compileNode, o
|
||
|
||
@compileToFragments = <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
target.update compileToFragments, o
|
||
|
||
target
|
||
|
||
cacheToCodeFragments: <span class="hljs-function"><span class="hljs-params">(cacheValues)</span> -></span>
|
||
[fragmentsToText(cacheValues[<span class="hljs-number">0</span>]), fragmentsToText(cacheValues[<span class="hljs-number">1</span>])]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-20">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-20">§</a>
|
||
</div>
|
||
<p>Construct a node that returns the current node’s result.
|
||
Note that this is overridden for smarter behavior for
|
||
many statement nodes (e.g. <code>If</code>, <code>For</code>).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> makeReturn: <span class="hljs-function"><span class="hljs-params">(results, mark)</span> -></span>
|
||
<span class="hljs-keyword">if</span> mark</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-21">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-21">§</a>
|
||
</div>
|
||
<p>Mark this node as implicitly returned, so that it can be part of the
|
||
node metadata returned in the AST.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @canBeReturned = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">return</span>
|
||
node = @unwrapAll()
|
||
<span class="hljs-keyword">if</span> results
|
||
<span class="hljs-keyword">new</span> Call <span class="hljs-keyword">new</span> Literal(<span class="hljs-string">"<span class="hljs-subst">#{results}</span>.push"</span>), [node]
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">new</span> Return node</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-22">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-22">§</a>
|
||
</div>
|
||
<p>Does this node, or any of its children, contain a node of a certain kind?
|
||
Recursively traverses down the <em>children</em> nodes and returns the first one
|
||
that verifies <code>pred</code>. Otherwise return undefined. <code>contains</code> does not cross
|
||
scope boundaries.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> contains: <span class="hljs-function"><span class="hljs-params">(pred)</span> -></span>
|
||
node = <span class="hljs-literal">undefined</span>
|
||
@traverseChildren <span class="hljs-literal">no</span>, <span class="hljs-function"><span class="hljs-params">(n)</span> -></span>
|
||
<span class="hljs-keyword">if</span> pred n
|
||
node = n
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span>
|
||
node</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-23">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-23">§</a>
|
||
</div>
|
||
<p>Pull out the last node of a node list.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> lastNode: <span class="hljs-function"><span class="hljs-params">(list)</span> -></span>
|
||
<span class="hljs-keyword">if</span> list.length <span class="hljs-keyword">is</span> <span class="hljs-number">0</span> <span class="hljs-keyword">then</span> <span class="hljs-literal">null</span> <span class="hljs-keyword">else</span> list[list.length - <span class="hljs-number">1</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-24">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-24">§</a>
|
||
</div>
|
||
<p>Debugging representation of the node, for inspecting the parse tree.
|
||
This is what <code>coffee --nodes</code> prints out.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> toString: <span class="hljs-function"><span class="hljs-params">(idt = <span class="hljs-string">''</span>, name = @constructor.name)</span> -></span>
|
||
tree = <span class="hljs-string">'\n'</span> + idt + name
|
||
tree += <span class="hljs-string">'?'</span> <span class="hljs-keyword">if</span> @soak
|
||
@eachChild (node) -> tree += node.toString idt + TAB
|
||
tree
|
||
|
||
checkForPureStatementInExpression: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> jumpNode = @jumps()
|
||
jumpNode.error <span class="hljs-string">'cannot use a pure statement in an expression'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-25">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-25">§</a>
|
||
</div>
|
||
<p>Plain JavaScript object representation of the node, that can be serialized
|
||
as JSON. This is what the <code>ast</code> option in the Node API returns.
|
||
We try to follow the <a href="https://github.com/babel/babel/blob/master/packages/babel-parser/ast/spec.md">Babel AST spec</a>
|
||
as closely as possible, for improved interoperability with other tools.
|
||
<strong>WARNING: DO NOT OVERRIDE THIS METHOD IN CHILD CLASSES.</strong>
|
||
Only override the component <code>ast*</code> methods as needed.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> ast: <span class="hljs-function"><span class="hljs-params">(o, level)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-26">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-26">§</a>
|
||
</div>
|
||
<p>Merge <code>level</code> into <code>o</code> and perform other universal checks.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> o = @astInitialize o, level</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-27">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-27">§</a>
|
||
</div>
|
||
<p>Create serializable representation of this node.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> astNode = @astNode o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-28">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-28">§</a>
|
||
</div>
|
||
<p>Mark AST nodes that correspond to expressions that (implicitly) return.
|
||
We can’t do this as part of <code>astNode</code> because we need to assemble child
|
||
nodes first before marking the parent being returned.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> @astNode? <span class="hljs-keyword">and</span> @canBeReturned
|
||
<span class="hljs-built_in">Object</span>.assign astNode, {returns: <span class="hljs-literal">yes</span>}
|
||
astNode
|
||
|
||
astInitialize: <span class="hljs-function"><span class="hljs-params">(o, level)</span> -></span>
|
||
o = <span class="hljs-built_in">Object</span>.assign {}, o
|
||
o.level = level <span class="hljs-keyword">if</span> level?
|
||
<span class="hljs-keyword">if</span> o.level > LEVEL_TOP
|
||
@checkForPureStatementInExpression()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-29">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-29">§</a>
|
||
</div>
|
||
<p><code>@makeReturn</code> must be called before <code>astProperties</code>, because the latter may call
|
||
<code>.ast()</code> for child nodes and those nodes would need the return logic from <code>makeReturn</code>
|
||
already executed by then.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @makeReturn <span class="hljs-literal">null</span>, <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> @isStatement(o) <span class="hljs-keyword">and</span> o.level <span class="hljs-keyword">isnt</span> LEVEL_TOP <span class="hljs-keyword">and</span> o.scope?
|
||
o
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-30">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-30">§</a>
|
||
</div>
|
||
<p>Every abstract syntax tree node object has four categories of properties:</p>
|
||
<ul>
|
||
<li>type, stored in the <code>type</code> field and a string like <code>NumberLiteral</code>.</li>
|
||
<li>location data, stored in the <code>loc</code>, <code>start</code>, <code>end</code> and <code>range</code> fields.</li>
|
||
<li>properties specific to this node, like <code>parsedValue</code>.</li>
|
||
<li>properties that are themselves child nodes, like <code>body</code>.
|
||
These fields are all intermixed in the Babel spec; <code>type</code> and <code>start</code> and
|
||
<code>parsedValue</code> are all top level fields in the AST node object. We have
|
||
separate methods for returning each category, that we merge together here.</li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-built_in">Object</span>.assign {}, {type: @astType(o)}, @astProperties(o), @astLocationData()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-31">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-31">§</a>
|
||
</div>
|
||
<p>By default, a node class has no specific properties.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> astProperties: <span class="hljs-function">-></span> {}</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-32">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-32">§</a>
|
||
</div>
|
||
<p>By default, a node class’s AST <code>type</code> is its class name.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> astType: <span class="hljs-function">-></span> @constructor.name</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-33">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-33">§</a>
|
||
</div>
|
||
<p>The AST location data is a rearranged version of our Jison location data,
|
||
mutated into the structure that the Babel spec uses.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> astLocationData: <span class="hljs-function">-></span>
|
||
jisonLocationDataToAstLocationData @locationData</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-34">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-34">§</a>
|
||
</div>
|
||
<p>Determines whether an AST node needs an <code>ExpressionStatement</code> wrapper.
|
||
Typically matches our <code>isStatement()</code> logic but this allows overriding.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> isStatementAst: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@isStatement o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-35">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-35">§</a>
|
||
</div>
|
||
<p>Passes each child to a function, breaking when the function returns <code>false</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> eachChild: <span class="hljs-function"><span class="hljs-params">(func)</span> -></span>
|
||
<span class="hljs-keyword">return</span> this <span class="hljs-keyword">unless</span> @children
|
||
<span class="hljs-keyword">for</span> attr <span class="hljs-keyword">in</span> @children <span class="hljs-keyword">when</span> @[attr]
|
||
<span class="hljs-keyword">for</span> child <span class="hljs-keyword">in</span> flatten [@[attr]]
|
||
<span class="hljs-keyword">return</span> this <span class="hljs-keyword">if</span> func(child) <span class="hljs-keyword">is</span> <span class="hljs-literal">false</span>
|
||
this
|
||
|
||
traverseChildren: <span class="hljs-function"><span class="hljs-params">(crossScope, func)</span> -></span>
|
||
@eachChild (child) ->
|
||
recur = func(child)
|
||
child.traverseChildren(crossScope, func) <span class="hljs-keyword">unless</span> recur <span class="hljs-keyword">is</span> <span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-36">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-36">§</a>
|
||
</div>
|
||
<p><code>replaceInContext</code> will traverse children looking for a node for which <code>match</code> returns
|
||
true. Once found, the matching node will be replaced by the result of calling <code>replacement</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> replaceInContext: <span class="hljs-function"><span class="hljs-params">(match, replacement)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span> <span class="hljs-keyword">unless</span> @children
|
||
<span class="hljs-keyword">for</span> attr <span class="hljs-keyword">in</span> @children <span class="hljs-keyword">when</span> children = @[attr]
|
||
<span class="hljs-keyword">if</span> <span class="hljs-built_in">Array</span>.isArray children
|
||
<span class="hljs-keyword">for</span> child, i <span class="hljs-keyword">in</span> children
|
||
<span class="hljs-keyword">if</span> match child
|
||
children[i..i] = replacement child, @
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span> <span class="hljs-keyword">if</span> child.replaceInContext match, replacement
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> match children
|
||
@[attr] = replacement children, @
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span> <span class="hljs-keyword">if</span> children.replaceInContext match, replacement
|
||
|
||
invert: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">new</span> Op <span class="hljs-string">'!'</span>, this
|
||
|
||
unwrapAll: <span class="hljs-function">-></span>
|
||
node = this
|
||
<span class="hljs-keyword">continue</span> <span class="hljs-keyword">until</span> node <span class="hljs-keyword">is</span> node = node.unwrap()
|
||
node</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-37">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-37">§</a>
|
||
</div>
|
||
<p>Default implementations of the common node properties and methods. Nodes
|
||
will override these with custom logic, if needed.</p>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-38">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-38">§</a>
|
||
</div>
|
||
<p><code>children</code> are the properties to recurse into when tree walking. The
|
||
<code>children</code> list <em>is</em> the structure of the AST. The <code>parent</code> pointer, and
|
||
the pointer to the <code>children</code> are how you can traverse the tree.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> children: []</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-39">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-39">§</a>
|
||
</div>
|
||
<p><code>isStatement</code> has to do with “everything is an expression”. A few things
|
||
can’t be expressions, such as <code>break</code>. Things that <code>isStatement</code> returns
|
||
<code>true</code> for are things that can’t be used as expressions. There are some
|
||
error messages that come from <code>nodes.coffee</code> due to statements ending up
|
||
in expression position.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> isStatement: NO</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-40">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-40">§</a>
|
||
</div>
|
||
<p>Track comments that have been compiled into fragments, to avoid outputting
|
||
them twice.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compiledComments: []</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-41">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-41">§</a>
|
||
</div>
|
||
<p><code>includeCommentFragments</code> lets <code>compileCommentFragments</code> know whether this node
|
||
has special awareness of how to handle comments within its output.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> includeCommentFragments: NO</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-42">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-42">§</a>
|
||
</div>
|
||
<p><code>jumps</code> tells you if an expression, or an internal part of an expression,
|
||
has a flow control construct (like <code>break</code>, <code>continue</code>, or <code>return</code>)
|
||
that jumps out of the normal flow of control and can’t be used as a value.
|
||
(Note that <code>throw</code> is not considered a flow control construct.)
|
||
This is important because flow control in the middle of an expression
|
||
makes no sense; we have to disallow it.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> jumps: NO</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-43">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-43">§</a>
|
||
</div>
|
||
<p>If <code>node.shouldCache() is false</code>, it is safe to use <code>node</code> more than once.
|
||
Otherwise you need to store the value of <code>node</code> in a variable and output
|
||
that variable several times instead. Kind of like this: <code>5</code> need not be
|
||
cached. <code>returnFive()</code>, however, could have side effects as a result of
|
||
evaluating it more than once, and therefore we need to cache it. The
|
||
parameter is named <code>shouldCache</code> rather than <code>mustCache</code> because there are
|
||
also cases where we might not need to cache but where we want to, for
|
||
example a long expression that may well be idempotent but we want to cache
|
||
for brevity.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> shouldCache: YES
|
||
|
||
isChainable: NO
|
||
isAssignable: NO
|
||
isNumber: NO
|
||
|
||
unwrap: THIS
|
||
unfoldSoak: NO</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-44">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-44">§</a>
|
||
</div>
|
||
<p>Is this node used to assign a certain variable?</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> assigns: NO</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-45">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-45">§</a>
|
||
</div>
|
||
<p>For this node and all descendents, set the location data to <code>locationData</code>
|
||
if the location data is not already set.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> updateLocationDataIfMissing: <span class="hljs-function"><span class="hljs-params">(locationData, force)</span> -></span>
|
||
@forceUpdateLocation = <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> force
|
||
<span class="hljs-keyword">return</span> this <span class="hljs-keyword">if</span> @locationData <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @forceUpdateLocation
|
||
<span class="hljs-keyword">delete</span> @forceUpdateLocation
|
||
@locationData = locationData
|
||
|
||
@eachChild (child) ->
|
||
child.updateLocationDataIfMissing locationData</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-46">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-46">§</a>
|
||
</div>
|
||
<p>Add location data from another node</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> withLocationDataFrom: <span class="hljs-function"><span class="hljs-params">({locationData})</span> -></span>
|
||
@updateLocationDataIfMissing locationData</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-47">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-47">§</a>
|
||
</div>
|
||
<p>Add location data and comments from another node</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> withLocationDataAndCommentsFrom: <span class="hljs-function"><span class="hljs-params">(node)</span> -></span>
|
||
@withLocationDataFrom node
|
||
{comments} = node
|
||
@comments = comments <span class="hljs-keyword">if</span> comments?.length
|
||
this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-48">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-48">§</a>
|
||
</div>
|
||
<p>Throw a SyntaxError associated with this node’s location.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> error: <span class="hljs-function"><span class="hljs-params">(message)</span> -></span>
|
||
throwSyntaxError message, @locationData
|
||
|
||
makeCode: <span class="hljs-function"><span class="hljs-params">(code)</span> -></span>
|
||
<span class="hljs-keyword">new</span> CodeFragment this, code
|
||
|
||
wrapInParentheses: <span class="hljs-function"><span class="hljs-params">(fragments)</span> -></span>
|
||
[@makeCode(<span class="hljs-string">'('</span>), fragments..., @makeCode(<span class="hljs-string">')'</span>)]
|
||
|
||
wrapInBraces: <span class="hljs-function"><span class="hljs-params">(fragments)</span> -></span>
|
||
[@makeCode(<span class="hljs-string">'{'</span>), fragments..., @makeCode(<span class="hljs-string">'}'</span>)]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-49">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-49">§</a>
|
||
</div>
|
||
<p><code>fragmentsList</code> is an array of arrays of fragments. Each array in fragmentsList will be
|
||
concatenated together, with <code>joinStr</code> added in between each, to produce a final flat array
|
||
of fragments.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> joinFragmentArrays: <span class="hljs-function"><span class="hljs-params">(fragmentsList, joinStr)</span> -></span>
|
||
answer = []
|
||
<span class="hljs-keyword">for</span> fragments, i <span class="hljs-keyword">in</span> fragmentsList
|
||
<span class="hljs-keyword">if</span> i <span class="hljs-keyword">then</span> answer.push @makeCode joinStr
|
||
answer = answer.concat fragments
|
||
answer</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-50">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-50">§</a>
|
||
</div>
|
||
<h3 id="hoisttarget">HoistTarget</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-51">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-51">§</a>
|
||
</div>
|
||
<p>A <strong>HoistTargetNode</strong> represents the output location in the node tree for a hoisted node.
|
||
See Base#hoist.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.HoistTarget = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HoistTarget</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-52">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-52">§</a>
|
||
</div>
|
||
<p>Expands hoisted fragments in the given array</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @expand = <span class="hljs-function"><span class="hljs-params">(fragments)</span> -></span>
|
||
<span class="hljs-keyword">for</span> fragment, i <span class="hljs-keyword">in</span> fragments <span class="hljs-keyword">by</span> <span class="hljs-number">-1</span> <span class="hljs-keyword">when</span> fragment.fragments
|
||
fragments[i..i] = @expand fragment.fragments
|
||
fragments
|
||
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@source)</span> -></span>
|
||
super()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-53">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-53">§</a>
|
||
</div>
|
||
<p>Holds presentational options to apply when the source node is compiled.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @options = {}</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-54">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-54">§</a>
|
||
</div>
|
||
<p>Placeholder fragments to be replaced by the source node’s compilation.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @targetFragments = { fragments: [] }
|
||
|
||
isStatement: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@source.isStatement o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-55">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-55">§</a>
|
||
</div>
|
||
<p>Update the target fragments with the result of compiling the source.
|
||
Calls the given compile function with the node and options (overriden with the target
|
||
presentational options).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> update: <span class="hljs-function"><span class="hljs-params">(compile, o)</span> -></span>
|
||
@targetFragments.fragments = compile.call @source, merge o, @options</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-56">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-56">§</a>
|
||
</div>
|
||
<p>Copies the target indent and level, and returns the placeholder fragments</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileToFragments: <span class="hljs-function"><span class="hljs-params">(o, level)</span> -></span>
|
||
@options.indent = o.indent
|
||
@options.level = level ? o.level
|
||
[ @targetFragments ]
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@compileToFragments o
|
||
|
||
compileClosure: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@compileToFragments o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-57">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-57">§</a>
|
||
</div>
|
||
<h3 id="root">Root</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-58">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-58">§</a>
|
||
</div>
|
||
<p>The root node of the node tree</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Root = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Root</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@body)</span> -></span>
|
||
super()
|
||
|
||
@isAsync = (<span class="hljs-keyword">new</span> Code [], @body).isAsync
|
||
|
||
children: [<span class="hljs-string">'body'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-59">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-59">§</a>
|
||
</div>
|
||
<p>Wrap everything in a safety closure, unless requested not to. It would be
|
||
better not to generate them in the first place, but for now, clean up
|
||
obvious double-parentheses.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o.indent = <span class="hljs-keyword">if</span> o.bare <span class="hljs-keyword">then</span> <span class="hljs-string">''</span> <span class="hljs-keyword">else</span> TAB
|
||
o.level = LEVEL_TOP
|
||
o.compiling = <span class="hljs-literal">yes</span>
|
||
@initializeScope o
|
||
fragments = @body.compileRoot o
|
||
<span class="hljs-keyword">return</span> fragments <span class="hljs-keyword">if</span> o.bare
|
||
functionKeyword = <span class="hljs-string">"<span class="hljs-subst">#{<span class="hljs-keyword">if</span> @isAsync <span class="hljs-keyword">then</span> <span class="hljs-string">'async '</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>}</span>function"</span>
|
||
[].concat @makeCode(<span class="hljs-string">"(<span class="hljs-subst">#{functionKeyword}</span>() {\n"</span>), fragments, @makeCode(<span class="hljs-string">"\n}).call(this);\n"</span>)
|
||
|
||
initializeScope: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o.scope = <span class="hljs-keyword">new</span> Scope <span class="hljs-literal">null</span>, @body, <span class="hljs-literal">null</span>, o.referencedVars ? []</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-60">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-60">§</a>
|
||
</div>
|
||
<p>Mark given local variables in the root scope as parameters so they don’t
|
||
end up being declared on the root block.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> o.scope.parameter name <span class="hljs-keyword">for</span> name <span class="hljs-keyword">in</span> o.locals <span class="hljs-keyword">or</span> []
|
||
|
||
commentsAst: <span class="hljs-function">-></span>
|
||
@allComments ?=
|
||
<span class="hljs-keyword">for</span> commentToken <span class="hljs-keyword">in</span> (@allCommentTokens ? []) <span class="hljs-keyword">when</span> <span class="hljs-keyword">not</span> commentToken.heregex
|
||
<span class="hljs-keyword">if</span> commentToken.here
|
||
<span class="hljs-keyword">new</span> HereComment commentToken
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">new</span> LineComment commentToken
|
||
comment.ast() <span class="hljs-keyword">for</span> comment <span class="hljs-keyword">in</span> @allComments
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o.level = LEVEL_TOP
|
||
@initializeScope o
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'File'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@body.isRootBlock = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">return</span>
|
||
program: <span class="hljs-built_in">Object</span>.assign @body.ast(o), @astLocationData()
|
||
comments: @commentsAst()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-61">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-61">§</a>
|
||
</div>
|
||
<h3 id="block">Block</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-62">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-62">§</a>
|
||
</div>
|
||
<p>The block is the list of expressions that forms the body of an
|
||
indented block of code – the implementation of a function, a clause in an
|
||
<code>if</code>, <code>switch</code>, or <code>try</code>, and so on…</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Block = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Block</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(nodes)</span> -></span>
|
||
super()
|
||
|
||
@expressions = compact flatten nodes <span class="hljs-keyword">or</span> []
|
||
|
||
children: [<span class="hljs-string">'expressions'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-63">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-63">§</a>
|
||
</div>
|
||
<p>Tack an expression on to the end of this expression list.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> push: <span class="hljs-function"><span class="hljs-params">(node)</span> -></span>
|
||
@expressions.push node
|
||
this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-64">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-64">§</a>
|
||
</div>
|
||
<p>Remove and return the last expression of this expression list.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> pop: <span class="hljs-function">-></span>
|
||
@expressions.pop()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-65">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-65">§</a>
|
||
</div>
|
||
<p>Add an expression at the beginning of this expression list.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> unshift: <span class="hljs-function"><span class="hljs-params">(node)</span> -></span>
|
||
@expressions.unshift node
|
||
this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-66">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-66">§</a>
|
||
</div>
|
||
<p>If this Block consists of just a single node, unwrap it by pulling
|
||
it back out.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> unwrap: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @expressions.length <span class="hljs-keyword">is</span> <span class="hljs-number">1</span> <span class="hljs-keyword">then</span> @expressions[<span class="hljs-number">0</span>] <span class="hljs-keyword">else</span> this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-67">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-67">§</a>
|
||
</div>
|
||
<p>Is this an empty block of code?</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> isEmpty: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">not</span> @expressions.length
|
||
|
||
isStatement: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">for</span> exp <span class="hljs-keyword">in</span> @expressions <span class="hljs-keyword">when</span> exp.isStatement o
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span>
|
||
<span class="hljs-literal">no</span>
|
||
|
||
jumps: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">for</span> exp <span class="hljs-keyword">in</span> @expressions
|
||
<span class="hljs-keyword">return</span> jumpNode <span class="hljs-keyword">if</span> jumpNode = exp.jumps o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-68">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-68">§</a>
|
||
</div>
|
||
<p>A Block node does not return its entire body, rather it
|
||
ensures that the final expression is returned.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> makeReturn: <span class="hljs-function"><span class="hljs-params">(results, mark)</span> -></span>
|
||
len = @expressions.length
|
||
[..., lastExp] = @expressions
|
||
lastExp = lastExp?.unwrap() <span class="hljs-keyword">or</span> <span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-69">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-69">§</a>
|
||
</div>
|
||
<p>We also need to check that we’re not returning a JSX tag if there’s an
|
||
adjacent one at the same level; JSX doesn’t allow that.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> lastExp <span class="hljs-keyword">and</span> lastExp <span class="hljs-keyword">instanceof</span> Parens <span class="hljs-keyword">and</span> lastExp.body.expressions.length > <span class="hljs-number">1</span>
|
||
{body:{expressions}} = lastExp
|
||
[..., penult, last] = expressions
|
||
penult = penult.unwrap()
|
||
last = last.unwrap()
|
||
<span class="hljs-keyword">if</span> penult <span class="hljs-keyword">instanceof</span> JSXElement <span class="hljs-keyword">and</span> last <span class="hljs-keyword">instanceof</span> JSXElement
|
||
expressions[expressions.length - <span class="hljs-number">1</span>].error <span class="hljs-string">'Adjacent JSX elements must be wrapped in an enclosing tag'</span>
|
||
<span class="hljs-keyword">if</span> mark
|
||
@expressions[len - <span class="hljs-number">1</span>]?.makeReturn results, mark
|
||
<span class="hljs-keyword">return</span>
|
||
<span class="hljs-keyword">while</span> len--
|
||
expr = @expressions[len]
|
||
@expressions[len] = expr.makeReturn results
|
||
@expressions.splice(len, <span class="hljs-number">1</span>) <span class="hljs-keyword">if</span> expr <span class="hljs-keyword">instanceof</span> Return <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> expr.expression
|
||
<span class="hljs-keyword">break</span>
|
||
this
|
||
|
||
compile: <span class="hljs-function"><span class="hljs-params">(o, lvl)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Root(this).withLocationDataFrom(this).compile o, lvl <span class="hljs-keyword">unless</span> o.scope
|
||
|
||
super o, lvl</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-70">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-70">§</a>
|
||
</div>
|
||
<p>Compile all expressions within the <strong>Block</strong> body. If we need to return
|
||
the result, and it’s an expression, simply return it. If it’s a statement,
|
||
ask the statement to do so.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@tab = o.indent
|
||
top = o.level <span class="hljs-keyword">is</span> LEVEL_TOP
|
||
compiledNodes = []
|
||
|
||
<span class="hljs-keyword">for</span> node, index <span class="hljs-keyword">in</span> @expressions
|
||
<span class="hljs-keyword">if</span> node.hoisted</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-71">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-71">§</a>
|
||
</div>
|
||
<p>This is a hoisted expression.
|
||
We want to compile this and ignore the result.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> node.compileToFragments o
|
||
<span class="hljs-keyword">continue</span>
|
||
node = (node.unfoldSoak(o) <span class="hljs-keyword">or</span> node)
|
||
<span class="hljs-keyword">if</span> node <span class="hljs-keyword">instanceof</span> Block</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-72">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-72">§</a>
|
||
</div>
|
||
<p>This is a nested block. We don’t do anything special here like
|
||
enclose it in a new scope; we just compile the statements in this
|
||
block along with our own.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compiledNodes.push node.compileNode o
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> top
|
||
node.front = <span class="hljs-literal">yes</span>
|
||
fragments = node.compileToFragments o
|
||
<span class="hljs-keyword">unless</span> node.isStatement o
|
||
fragments = indentInitial fragments, @
|
||
[..., lastFragment] = fragments
|
||
<span class="hljs-keyword">unless</span> lastFragment.code <span class="hljs-keyword">is</span> <span class="hljs-string">''</span> <span class="hljs-keyword">or</span> lastFragment.isComment
|
||
fragments.push @makeCode <span class="hljs-string">';'</span>
|
||
compiledNodes.push fragments
|
||
<span class="hljs-keyword">else</span>
|
||
compiledNodes.push node.compileToFragments o, LEVEL_LIST
|
||
<span class="hljs-keyword">if</span> top
|
||
<span class="hljs-keyword">if</span> @spaced
|
||
<span class="hljs-keyword">return</span> [].concat @joinFragmentArrays(compiledNodes, <span class="hljs-string">'\n\n'</span>), @makeCode(<span class="hljs-string">'\n'</span>)
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">return</span> @joinFragmentArrays(compiledNodes, <span class="hljs-string">'\n'</span>)
|
||
<span class="hljs-keyword">if</span> compiledNodes.length
|
||
answer = @joinFragmentArrays(compiledNodes, <span class="hljs-string">', '</span>)
|
||
<span class="hljs-keyword">else</span>
|
||
answer = [@makeCode <span class="hljs-string">'void 0'</span>]
|
||
<span class="hljs-keyword">if</span> compiledNodes.length > <span class="hljs-number">1</span> <span class="hljs-keyword">and</span> o.level >= LEVEL_LIST <span class="hljs-keyword">then</span> @wrapInParentheses answer <span class="hljs-keyword">else</span> answer
|
||
|
||
compileRoot: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@spaced = <span class="hljs-literal">yes</span>
|
||
fragments = @compileWithDeclarations o
|
||
HoistTarget.expand fragments
|
||
@compileComments fragments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-73">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-73">§</a>
|
||
</div>
|
||
<p>Compile the expressions body for the contents of a function, with
|
||
declarations of all inner variables pushed up to the top.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileWithDeclarations: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
fragments = []
|
||
post = []
|
||
<span class="hljs-keyword">for</span> exp, i <span class="hljs-keyword">in</span> @expressions
|
||
exp = exp.unwrap()
|
||
<span class="hljs-keyword">break</span> <span class="hljs-keyword">unless</span> exp <span class="hljs-keyword">instanceof</span> Literal
|
||
o = merge(o, level: LEVEL_TOP)
|
||
<span class="hljs-keyword">if</span> i
|
||
rest = @expressions.splice i, <span class="hljs-number">9e9</span>
|
||
[spaced, @spaced] = [@spaced, <span class="hljs-literal">no</span>]
|
||
[fragments, @spaced] = [@compileNode(o), spaced]
|
||
@expressions = rest
|
||
post = @compileNode o
|
||
{scope} = o
|
||
<span class="hljs-keyword">if</span> scope.expressions <span class="hljs-keyword">is</span> this
|
||
declars = o.scope.hasDeclarations()
|
||
assigns = scope.hasAssignments
|
||
<span class="hljs-keyword">if</span> declars <span class="hljs-keyword">or</span> assigns
|
||
fragments.push @makeCode <span class="hljs-string">'\n'</span> <span class="hljs-keyword">if</span> i
|
||
fragments.push @makeCode <span class="hljs-string">"<span class="hljs-subst">#{@tab}</span>var "</span>
|
||
<span class="hljs-keyword">if</span> declars
|
||
declaredVariables = scope.declaredVariables()
|
||
<span class="hljs-keyword">for</span> declaredVariable, declaredVariablesIndex <span class="hljs-keyword">in</span> declaredVariables
|
||
fragments.push @makeCode declaredVariable
|
||
<span class="hljs-keyword">if</span> Object::hasOwnProperty.call o.scope.comments, declaredVariable
|
||
fragments.push o.scope.comments[declaredVariable]...
|
||
<span class="hljs-keyword">if</span> declaredVariablesIndex <span class="hljs-keyword">isnt</span> declaredVariables.length - <span class="hljs-number">1</span>
|
||
fragments.push @makeCode <span class="hljs-string">', '</span>
|
||
<span class="hljs-keyword">if</span> assigns
|
||
fragments.push @makeCode <span class="hljs-string">",\n<span class="hljs-subst">#{@tab + TAB}</span>"</span> <span class="hljs-keyword">if</span> declars
|
||
fragments.push @makeCode scope.assignedVariables().join(<span class="hljs-string">",\n<span class="hljs-subst">#{@tab + TAB}</span>"</span>)
|
||
fragments.push @makeCode <span class="hljs-string">";\n<span class="hljs-subst">#{<span class="hljs-keyword">if</span> @spaced <span class="hljs-keyword">then</span> <span class="hljs-string">'\n'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>}</span>"</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> fragments.length <span class="hljs-keyword">and</span> post.length
|
||
fragments.push @makeCode <span class="hljs-string">"\n"</span>
|
||
fragments.concat post
|
||
|
||
compileComments: <span class="hljs-function"><span class="hljs-params">(fragments)</span> -></span>
|
||
<span class="hljs-keyword">for</span> fragment, fragmentIndex <span class="hljs-keyword">in</span> fragments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-74">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-74">§</a>
|
||
</div>
|
||
<p>Insert comments into the output at the next or previous newline.
|
||
If there are no newlines at which to place comments, create them.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> fragment.precedingComments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-75">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-75">§</a>
|
||
</div>
|
||
<p>Determine the indentation level of the fragment that we are about
|
||
to insert comments before, and use that indentation level for our
|
||
inserted comments. At this point, the fragments’ <code>code</code> property
|
||
is the generated output JavaScript, and CoffeeScript always
|
||
generates output indented by two spaces; so all we need to do is
|
||
search for a <code>code</code> property that begins with at least two spaces.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> fragmentIndent = <span class="hljs-string">''</span>
|
||
<span class="hljs-keyword">for</span> pastFragment <span class="hljs-keyword">in</span> fragments[<span class="hljs-number">0.</span>..(fragmentIndex + <span class="hljs-number">1</span>)] <span class="hljs-keyword">by</span> <span class="hljs-number">-1</span>
|
||
indent = <span class="hljs-regexp">/^ {2,}/m</span>.exec pastFragment.code
|
||
<span class="hljs-keyword">if</span> indent
|
||
fragmentIndent = indent[<span class="hljs-number">0</span>]
|
||
<span class="hljs-keyword">break</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-string">'\n'</span> <span class="hljs-keyword">in</span> pastFragment.code
|
||
<span class="hljs-keyword">break</span>
|
||
code = <span class="hljs-string">"\n<span class="hljs-subst">#{fragmentIndent}</span>"</span> + (
|
||
<span class="hljs-keyword">for</span> commentFragment <span class="hljs-keyword">in</span> fragment.precedingComments
|
||
<span class="hljs-keyword">if</span> commentFragment.isHereComment <span class="hljs-keyword">and</span> commentFragment.multiline
|
||
multident commentFragment.code, fragmentIndent, <span class="hljs-literal">no</span>
|
||
<span class="hljs-keyword">else</span>
|
||
commentFragment.code
|
||
).join(<span class="hljs-string">"\n<span class="hljs-subst">#{fragmentIndent}</span>"</span>).replace <span class="hljs-regexp">/^(\s*)$/gm</span>, <span class="hljs-string">''</span>
|
||
<span class="hljs-keyword">for</span> pastFragment, pastFragmentIndex <span class="hljs-keyword">in</span> fragments[<span class="hljs-number">0.</span>..(fragmentIndex + <span class="hljs-number">1</span>)] <span class="hljs-keyword">by</span> <span class="hljs-number">-1</span>
|
||
newLineIndex = pastFragment.code.lastIndexOf <span class="hljs-string">'\n'</span>
|
||
<span class="hljs-keyword">if</span> newLineIndex <span class="hljs-keyword">is</span> <span class="hljs-number">-1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-76">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-76">§</a>
|
||
</div>
|
||
<p>Keep searching previous fragments until we can’t go back any
|
||
further, either because there are no fragments left or we’ve
|
||
discovered that we’re in a code block that is interpolated
|
||
inside a string.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> pastFragmentIndex <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
|
||
pastFragment.code = <span class="hljs-string">'\n'</span> + pastFragment.code
|
||
newLineIndex = <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> pastFragment.isStringWithInterpolations <span class="hljs-keyword">and</span> pastFragment.code <span class="hljs-keyword">is</span> <span class="hljs-string">'{'</span>
|
||
code = code[<span class="hljs-number">1.</span>.] + <span class="hljs-string">'\n'</span> <span class="hljs-comment"># Move newline to end.</span>
|
||
newLineIndex = <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">continue</span>
|
||
<span class="hljs-keyword">delete</span> fragment.precedingComments
|
||
pastFragment.code = pastFragment.code[<span class="hljs-number">0.</span>..newLineIndex] +
|
||
code + pastFragment.code[newLineIndex..]
|
||
<span class="hljs-keyword">break</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-77">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-77">§</a>
|
||
</div>
|
||
<p>Yes, this is awfully similar to the previous <code>if</code> block, but if you
|
||
look closely you’ll find lots of tiny differences that make this
|
||
confusing if it were abstracted into a function that both blocks share.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> fragment.followingComments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-78">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-78">§</a>
|
||
</div>
|
||
<p>Does the first trailing comment follow at the end of a line of code,
|
||
like <code>; // Comment</code>, or does it start a new line after a line of code?</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> trail = fragment.followingComments[<span class="hljs-number">0</span>].trail
|
||
fragmentIndent = <span class="hljs-string">''</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-79">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-79">§</a>
|
||
</div>
|
||
<p>Find the indent of the next line of code, if we have any non-trailing
|
||
comments to output. We need to first find the next newline, as these
|
||
comments will be output after that; and then the indent of the line
|
||
that follows the next newline.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">unless</span> trail <span class="hljs-keyword">and</span> fragment.followingComments.length <span class="hljs-keyword">is</span> <span class="hljs-number">1</span>
|
||
onNextLine = <span class="hljs-literal">no</span>
|
||
<span class="hljs-keyword">for</span> upcomingFragment <span class="hljs-keyword">in</span> fragments[fragmentIndex...]
|
||
<span class="hljs-keyword">unless</span> onNextLine
|
||
<span class="hljs-keyword">if</span> <span class="hljs-string">'\n'</span> <span class="hljs-keyword">in</span> upcomingFragment.code
|
||
onNextLine = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">continue</span>
|
||
<span class="hljs-keyword">else</span>
|
||
indent = <span class="hljs-regexp">/^ {2,}/m</span>.exec upcomingFragment.code
|
||
<span class="hljs-keyword">if</span> indent
|
||
fragmentIndent = indent[<span class="hljs-number">0</span>]
|
||
<span class="hljs-keyword">break</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-string">'\n'</span> <span class="hljs-keyword">in</span> upcomingFragment.code
|
||
<span class="hljs-keyword">break</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-80">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-80">§</a>
|
||
</div>
|
||
<p>Is this comment following the indent inserted by bare mode?
|
||
If so, there’s no need to indent this further.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> code = <span class="hljs-keyword">if</span> fragmentIndex <span class="hljs-keyword">is</span> <span class="hljs-number">1</span> <span class="hljs-keyword">and</span> <span class="hljs-regexp">/^\s+$/</span>.test fragments[<span class="hljs-number">0</span>].code
|
||
<span class="hljs-string">''</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> trail
|
||
<span class="hljs-string">' '</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">"\n<span class="hljs-subst">#{fragmentIndent}</span>"</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-81">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-81">§</a>
|
||
</div>
|
||
<p>Assemble properly indented comments.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> code += (
|
||
<span class="hljs-keyword">for</span> commentFragment <span class="hljs-keyword">in</span> fragment.followingComments
|
||
<span class="hljs-keyword">if</span> commentFragment.isHereComment <span class="hljs-keyword">and</span> commentFragment.multiline
|
||
multident commentFragment.code, fragmentIndent, <span class="hljs-literal">no</span>
|
||
<span class="hljs-keyword">else</span>
|
||
commentFragment.code
|
||
).join(<span class="hljs-string">"\n<span class="hljs-subst">#{fragmentIndent}</span>"</span>).replace <span class="hljs-regexp">/^(\s*)$/gm</span>, <span class="hljs-string">''</span>
|
||
<span class="hljs-keyword">for</span> upcomingFragment, upcomingFragmentIndex <span class="hljs-keyword">in</span> fragments[fragmentIndex...]
|
||
newLineIndex = upcomingFragment.code.indexOf <span class="hljs-string">'\n'</span>
|
||
<span class="hljs-keyword">if</span> newLineIndex <span class="hljs-keyword">is</span> <span class="hljs-number">-1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-82">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-82">§</a>
|
||
</div>
|
||
<p>Keep searching upcoming fragments until we can’t go any
|
||
further, either because there are no fragments left or we’ve
|
||
discovered that we’re in a code block that is interpolated
|
||
inside a string.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> upcomingFragmentIndex <span class="hljs-keyword">is</span> fragments.length - <span class="hljs-number">1</span>
|
||
upcomingFragment.code = upcomingFragment.code + <span class="hljs-string">'\n'</span>
|
||
newLineIndex = upcomingFragment.code.length
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> upcomingFragment.isStringWithInterpolations <span class="hljs-keyword">and</span> upcomingFragment.code <span class="hljs-keyword">is</span> <span class="hljs-string">'}'</span>
|
||
code = <span class="hljs-string">"<span class="hljs-subst">#{code}</span>\n"</span>
|
||
newLineIndex = <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">continue</span>
|
||
<span class="hljs-keyword">delete</span> fragment.followingComments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-83">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-83">§</a>
|
||
</div>
|
||
<p>Avoid inserting extra blank lines.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> code = code.replace <span class="hljs-regexp">/^\n/</span>, <span class="hljs-string">''</span> <span class="hljs-keyword">if</span> upcomingFragment.code <span class="hljs-keyword">is</span> <span class="hljs-string">'\n'</span>
|
||
upcomingFragment.code = upcomingFragment.code[<span class="hljs-number">0.</span>..newLineIndex] +
|
||
code + upcomingFragment.code[newLineIndex..]
|
||
<span class="hljs-keyword">break</span>
|
||
|
||
fragments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-84">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-84">§</a>
|
||
</div>
|
||
<p>Wrap up the given nodes as a <strong>Block</strong>, unless it already happens
|
||
to be one.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @wrap: <span class="hljs-function"><span class="hljs-params">(nodes)</span> -></span>
|
||
<span class="hljs-keyword">return</span> nodes[<span class="hljs-number">0</span>] <span class="hljs-keyword">if</span> nodes.length <span class="hljs-keyword">is</span> <span class="hljs-number">1</span> <span class="hljs-keyword">and</span> nodes[<span class="hljs-number">0</span>] <span class="hljs-keyword">instanceof</span> Block
|
||
<span class="hljs-keyword">new</span> Block nodes
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> (o.level? <span class="hljs-keyword">and</span> o.level <span class="hljs-keyword">isnt</span> LEVEL_TOP) <span class="hljs-keyword">and</span> @expressions.length
|
||
<span class="hljs-keyword">return</span> (<span class="hljs-keyword">new</span> Sequence(@expressions).withLocationDataFrom @).ast o
|
||
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @isRootBlock
|
||
<span class="hljs-string">'Program'</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @isClassBody
|
||
<span class="hljs-string">'ClassBody'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'BlockStatement'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
checkForDirectives = del o, <span class="hljs-string">'checkForDirectives'</span>
|
||
|
||
sniffDirectives @expressions, notFinalExpression: checkForDirectives <span class="hljs-keyword">if</span> @isRootBlock <span class="hljs-keyword">or</span> checkForDirectives
|
||
directives = []
|
||
body = []
|
||
<span class="hljs-keyword">for</span> expression <span class="hljs-keyword">in</span> @expressions
|
||
expressionAst = expression.ast o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-85">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-85">§</a>
|
||
</div>
|
||
<p>Ignore generated PassthroughLiteral</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> expressionAst?
|
||
<span class="hljs-keyword">continue</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> expression <span class="hljs-keyword">instanceof</span> Directive
|
||
directives.push expressionAst</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-86">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-86">§</a>
|
||
</div>
|
||
<p>If an expression is a statement, it can be added to the body as is.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> expression.isStatementAst o
|
||
body.push expressionAst</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-87">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-87">§</a>
|
||
</div>
|
||
<p>Otherwise, we need to wrap it in an <code>ExpressionStatement</code> AST node.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span>
|
||
body.push <span class="hljs-built_in">Object</span>.assign
|
||
type: <span class="hljs-string">'ExpressionStatement'</span>
|
||
expression: expressionAst
|
||
,
|
||
expression.astLocationData()
|
||
|
||
<span class="hljs-keyword">return</span> {</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-88">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-88">§</a>
|
||
</div>
|
||
<p>For now, we’re not including <code>sourceType</code> on the <code>Program</code> AST node.
|
||
Its value could be either <code>'script'</code> or <code>'module'</code>, and there’s no way
|
||
for CoffeeScript to always know which it should be. The presence of an
|
||
<code>import</code> or <code>export</code> statement in source code would imply that it should
|
||
be a <code>module</code>, but a project may consist of mostly such files and also
|
||
an outlier file that lacks <code>import</code> or <code>export</code> but is still imported
|
||
into the project and therefore expects to be treated as a <code>module</code>.
|
||
Determining the value of <code>sourceType</code> is essentially the same challenge
|
||
posed by determining the parse goal of a JavaScript file, also <code>module</code>
|
||
or <code>script</code>, and so if Node figures out a way to do so for <code>.js</code> files
|
||
then CoffeeScript can copy Node’s algorithm.</p>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-89">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-89">§</a>
|
||
</div>
|
||
<p>sourceType: ‘module’</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> body, directives
|
||
}
|
||
|
||
astLocationData: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> @isRootBlock <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @locationData?
|
||
super()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-90">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-90">§</a>
|
||
</div>
|
||
<p>A directive e.g. ‘use strict’.
|
||
Currently only used during AST generation.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Directive = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Directive</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@value)</span> -></span>
|
||
super()
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
value: <span class="hljs-built_in">Object</span>.assign {},
|
||
@value.ast o
|
||
type: <span class="hljs-string">'DirectiveLiteral'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-91">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-91">§</a>
|
||
</div>
|
||
<h3 id="literal">Literal</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-92">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-92">§</a>
|
||
</div>
|
||
<p><code>Literal</code> is a base class for static values that can be passed through
|
||
directly into JavaScript without translation, such as: strings, numbers,
|
||
<code>true</code>, <code>false</code>, <code>null</code>…</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Literal = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Literal</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@value)</span> -></span>
|
||
super()
|
||
|
||
shouldCache: NO
|
||
|
||
assigns: <span class="hljs-function"><span class="hljs-params">(name)</span> -></span>
|
||
name <span class="hljs-keyword">is</span> @value
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[@makeCode @value]
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
value: @value
|
||
|
||
toString: <span class="hljs-function">-></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-93">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-93">§</a>
|
||
</div>
|
||
<p>This is only intended for debugging.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-string">" <span class="hljs-subst">#{<span class="hljs-keyword">if</span> @isStatement() <span class="hljs-keyword">then</span> super() <span class="hljs-keyword">else</span> @constructor.name}</span>: <span class="hljs-subst">#{@value}</span>"</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.NumberLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NumberLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@value, {@parsedValue} = {})</span> -></span>
|
||
super()
|
||
<span class="hljs-keyword">unless</span> @parsedValue?
|
||
<span class="hljs-keyword">if</span> isNumber @value
|
||
@parsedValue = @value
|
||
@value = <span class="hljs-string">"<span class="hljs-subst">#{@value}</span>"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
@parsedValue = parseNumber @value
|
||
|
||
isBigInt: <span class="hljs-function">-></span>
|
||
<span class="hljs-regexp">/n$/</span>.test @value
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @isBigInt()
|
||
<span class="hljs-string">'BigIntLiteral'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'NumericLiteral'</span>
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
value:
|
||
<span class="hljs-keyword">if</span> @isBigInt()
|
||
@parsedValue.toString()
|
||
<span class="hljs-keyword">else</span>
|
||
@parsedValue
|
||
extra:
|
||
rawValue:
|
||
<span class="hljs-keyword">if</span> @isBigInt()
|
||
@parsedValue.toString()
|
||
<span class="hljs-keyword">else</span>
|
||
@parsedValue
|
||
raw: @value
|
||
|
||
<span class="hljs-built_in">exports</span>.InfinityLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InfinityLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">NumberLiteral</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@value, {@originalValue = <span class="hljs-string">'Infinity'</span>} = {})</span> -></span>
|
||
super()
|
||
|
||
compileNode: <span class="hljs-function">-></span>
|
||
[@makeCode <span class="hljs-string">'2e308'</span>]
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">unless</span> @originalValue <span class="hljs-keyword">is</span> <span class="hljs-string">'Infinity'</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> NumberLiteral(@value).withLocationDataFrom(@).ast o
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'Identifier'</span>
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
name: <span class="hljs-string">'Infinity'</span>
|
||
declaration: <span class="hljs-literal">no</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.NaNLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NaNLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">NumberLiteral</span></span>
|
||
constructor: <span class="hljs-function">-></span>
|
||
super <span class="hljs-string">'NaN'</span>
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
code = [@makeCode <span class="hljs-string">'0/0'</span>]
|
||
<span class="hljs-keyword">if</span> o.level >= LEVEL_OP <span class="hljs-keyword">then</span> @wrapInParentheses code <span class="hljs-keyword">else</span> code
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'Identifier'</span>
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
name: <span class="hljs-string">'NaN'</span>
|
||
declaration: <span class="hljs-literal">no</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.StringLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StringLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@originalValue, {@quote, @initialChunk, @finalChunk, @indent, @double, @heregex} = {})</span> -></span>
|
||
super <span class="hljs-string">''</span>
|
||
@quote = <span class="hljs-literal">null</span> <span class="hljs-keyword">if</span> @quote <span class="hljs-keyword">is</span> <span class="hljs-string">'///'</span>
|
||
@fromSourceString = @quote?
|
||
@quote ?= <span class="hljs-string">'"'</span>
|
||
heredoc = @isFromHeredoc()
|
||
|
||
val = @originalValue
|
||
<span class="hljs-keyword">if</span> @heregex
|
||
val = val.replace HEREGEX_OMIT, <span class="hljs-string">'$1$2'</span>
|
||
val = replaceUnicodeCodePointEscapes val, flags: @heregex.flags
|
||
<span class="hljs-keyword">else</span>
|
||
val = val.replace STRING_OMIT, <span class="hljs-string">'$1'</span>
|
||
val =
|
||
<span class="hljs-keyword">unless</span> @fromSourceString
|
||
val
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> heredoc
|
||
indentRegex = <span class="hljs-regexp">/// \n<span class="hljs-subst">#{@indent}</span> ///</span>g <span class="hljs-keyword">if</span> @indent
|
||
|
||
val = val.replace indentRegex, <span class="hljs-string">'\n'</span> <span class="hljs-keyword">if</span> indentRegex
|
||
val = val.replace LEADING_BLANK_LINE, <span class="hljs-string">''</span> <span class="hljs-keyword">if</span> @initialChunk
|
||
val = val.replace TRAILING_BLANK_LINE, <span class="hljs-string">''</span> <span class="hljs-keyword">if</span> @finalChunk
|
||
val
|
||
<span class="hljs-keyword">else</span>
|
||
val.replace SIMPLE_STRING_OMIT, <span class="hljs-function"><span class="hljs-params">(match, offset)</span> =></span>
|
||
<span class="hljs-keyword">if</span> (@initialChunk <span class="hljs-keyword">and</span> offset <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>) <span class="hljs-keyword">or</span>
|
||
(@finalChunk <span class="hljs-keyword">and</span> offset + match.length <span class="hljs-keyword">is</span> val.length)
|
||
<span class="hljs-string">''</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">' '</span>
|
||
@delimiter = @quote.charAt <span class="hljs-number">0</span>
|
||
@value = makeDelimitedLiteral val, {
|
||
@delimiter
|
||
@double
|
||
}
|
||
|
||
@unquotedValueForTemplateLiteral = makeDelimitedLiteral val, {
|
||
delimiter: <span class="hljs-string">'`'</span>
|
||
@double
|
||
escapeNewlines: <span class="hljs-literal">no</span>
|
||
includeDelimiters: <span class="hljs-literal">no</span>
|
||
convertTrailingNullEscapes: <span class="hljs-literal">yes</span>
|
||
}
|
||
|
||
@unquotedValueForJSX = makeDelimitedLiteral val, {
|
||
@double
|
||
escapeNewlines: <span class="hljs-literal">no</span>
|
||
includeDelimiters: <span class="hljs-literal">no</span>
|
||
escapeDelimiter: <span class="hljs-literal">no</span>
|
||
}
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> StringWithInterpolations.fromStringLiteral(@).compileNode o <span class="hljs-keyword">if</span> @shouldGenerateTemplateLiteral()
|
||
<span class="hljs-keyword">return</span> [@makeCode @unquotedValueForJSX] <span class="hljs-keyword">if</span> @jsx
|
||
super o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-94">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-94">§</a>
|
||
</div>
|
||
<p><code>StringLiteral</code>s can represent either entire literal strings
|
||
or pieces of text inside of e.g. an interpolated string.
|
||
When parsed as the former but needing to be treated as the latter
|
||
(e.g. the string part of a tagged template literal), this will return
|
||
a copy of the <code>StringLiteral</code> with the quotes trimmed from its location
|
||
data (like it would have if parsed as part of an interpolated string).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> withoutQuotesInLocationData: <span class="hljs-function">-></span>
|
||
endsWithNewline = @originalValue[<span class="hljs-number">-1.</span>.] <span class="hljs-keyword">is</span> <span class="hljs-string">'\n'</span>
|
||
locationData = <span class="hljs-built_in">Object</span>.assign {}, @locationData
|
||
locationData.first_column += @quote.length
|
||
<span class="hljs-keyword">if</span> endsWithNewline
|
||
locationData.last_line -= <span class="hljs-number">1</span>
|
||
locationData.last_column =
|
||
<span class="hljs-keyword">if</span> locationData.last_line <span class="hljs-keyword">is</span> locationData.first_line
|
||
locationData.first_column + @originalValue.length - <span class="hljs-string">'\n'</span>.length
|
||
<span class="hljs-keyword">else</span>
|
||
@originalValue[...<span class="hljs-number">-1</span>].length - <span class="hljs-string">'\n'</span>.length - @originalValue[...<span class="hljs-number">-1</span>].lastIndexOf(<span class="hljs-string">'\n'</span>)
|
||
<span class="hljs-keyword">else</span>
|
||
locationData.last_column -= @quote.length
|
||
locationData.last_column_exclusive -= @quote.length
|
||
locationData.range = [
|
||
locationData.range[<span class="hljs-number">0</span>] + @quote.length
|
||
locationData.range[<span class="hljs-number">1</span>] - @quote.length
|
||
]
|
||
copy = <span class="hljs-keyword">new</span> StringLiteral @originalValue, {@quote, @initialChunk, @finalChunk, @indent, @double, @heregex}
|
||
copy.locationData = locationData
|
||
copy
|
||
|
||
isFromHeredoc: <span class="hljs-function">-></span>
|
||
@quote.length <span class="hljs-keyword">is</span> <span class="hljs-number">3</span>
|
||
|
||
shouldGenerateTemplateLiteral: <span class="hljs-function">-></span>
|
||
@isFromHeredoc()
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> StringWithInterpolations.fromStringLiteral(@).ast o <span class="hljs-keyword">if</span> @shouldGenerateTemplateLiteral()
|
||
super o
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
value: @originalValue
|
||
extra:
|
||
raw: <span class="hljs-string">"<span class="hljs-subst">#{@delimiter}</span><span class="hljs-subst">#{@originalValue}</span><span class="hljs-subst">#{@delimiter}</span>"</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.RegexLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegexLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(value, {@delimiter = <span class="hljs-string">'/'</span>, @heregexCommentTokens = []} = {})</span> -></span>
|
||
super <span class="hljs-string">''</span>
|
||
heregex = @delimiter <span class="hljs-keyword">is</span> <span class="hljs-string">'///'</span>
|
||
endDelimiterIndex = value.lastIndexOf <span class="hljs-string">'/'</span>
|
||
@flags = value[endDelimiterIndex + <span class="hljs-number">1.</span>.]
|
||
val = @originalValue = value[<span class="hljs-number">1.</span>..endDelimiterIndex]
|
||
val = val.replace HEREGEX_OMIT, <span class="hljs-string">'$1$2'</span> <span class="hljs-keyword">if</span> heregex
|
||
val = replaceUnicodeCodePointEscapes val, {@flags}
|
||
@value = <span class="hljs-string">"<span class="hljs-subst">#{makeDelimitedLiteral val, delimiter: <span class="hljs-string">'/'</span>}</span><span class="hljs-subst">#{@flags}</span>"</span>
|
||
|
||
REGEX_REGEX: <span class="hljs-regexp">/// ^ / (.*) / \w* $ ///</span>
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'RegExpLiteral'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[, pattern] = @REGEX_REGEX.exec @value
|
||
<span class="hljs-keyword">return</span> {
|
||
value: <span class="hljs-literal">undefined</span>
|
||
pattern, @flags, @delimiter
|
||
originalPattern: @originalValue
|
||
extra:
|
||
raw: @value
|
||
originalRaw: <span class="hljs-string">"<span class="hljs-subst">#{@delimiter}</span><span class="hljs-subst">#{@originalValue}</span><span class="hljs-subst">#{@delimiter}</span><span class="hljs-subst">#{@flags}</span>"</span>
|
||
rawValue: <span class="hljs-literal">undefined</span>
|
||
comments:
|
||
<span class="hljs-keyword">for</span> heregexCommentToken <span class="hljs-keyword">in</span> @heregexCommentTokens
|
||
<span class="hljs-keyword">if</span> heregexCommentToken.here
|
||
<span class="hljs-keyword">new</span> HereComment(heregexCommentToken).ast o
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">new</span> LineComment(heregexCommentToken).ast o
|
||
}
|
||
|
||
<span class="hljs-built_in">exports</span>.PassthroughLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PassthroughLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@originalValue, {@here, @generated} = {})</span> -></span>
|
||
super <span class="hljs-string">''</span>
|
||
@value = @originalValue.replace <span class="hljs-regexp">/\\+(`|$)/g</span>, <span class="hljs-function"><span class="hljs-params">(string)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-95">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-95">§</a>
|
||
</div>
|
||
<p><code>string</code> is always a value like ‘`‘, ‘\`‘, ‘\\`‘, etc.
|
||
By reducing it to its latter half, we turn ‘`‘ to ‘<code>', '\\\</code>‘ to ‘`‘, etc.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> string[-<span class="hljs-built_in">Math</span>.ceil(string.length / <span class="hljs-number">2</span>)..]
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">null</span> <span class="hljs-keyword">if</span> @generated
|
||
super o
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> {
|
||
value: @originalValue
|
||
here: !!@here
|
||
}
|
||
|
||
<span class="hljs-built_in">exports</span>.IdentifierLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IdentifierLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
isAssignable: YES
|
||
|
||
eachName: <span class="hljs-function"><span class="hljs-params">(iterator)</span> -></span>
|
||
iterator @
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @jsx
|
||
<span class="hljs-string">'JSXIdentifier'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'Identifier'</span>
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
name: @value
|
||
declaration: !!@isDeclaration
|
||
|
||
<span class="hljs-built_in">exports</span>.PropertyName = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PropertyName</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
isAssignable: YES
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @jsx
|
||
<span class="hljs-string">'JSXIdentifier'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'Identifier'</span>
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
name: @value
|
||
declaration: <span class="hljs-literal">no</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.ComputedPropertyName = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ComputedPropertyName</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">PropertyName</span></span>
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[@makeCode(<span class="hljs-string">'['</span>), @value.compileToFragments(o, LEVEL_LIST)..., @makeCode(<span class="hljs-string">']'</span>)]
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@value.ast o
|
||
|
||
<span class="hljs-built_in">exports</span>.StatementLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StatementLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
isStatement: YES
|
||
|
||
makeReturn: THIS
|
||
|
||
jumps: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> this <span class="hljs-keyword">if</span> @value <span class="hljs-keyword">is</span> <span class="hljs-string">'break'</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> (o?.<span class="hljs-keyword">loop</span> <span class="hljs-keyword">or</span> o?.block)
|
||
<span class="hljs-keyword">return</span> this <span class="hljs-keyword">if</span> @value <span class="hljs-keyword">is</span> <span class="hljs-string">'continue'</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> o?.<span class="hljs-keyword">loop</span>
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[@makeCode <span class="hljs-string">"<span class="hljs-subst">#{@tab}</span><span class="hljs-subst">#{@value}</span>;"</span>]
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">switch</span> @value
|
||
<span class="hljs-keyword">when</span> <span class="hljs-string">'continue'</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'ContinueStatement'</span>
|
||
<span class="hljs-keyword">when</span> <span class="hljs-string">'break'</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'BreakStatement'</span>
|
||
<span class="hljs-keyword">when</span> <span class="hljs-string">'debugger'</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'DebuggerStatement'</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.ThisLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThisLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(value)</span> -></span>
|
||
super <span class="hljs-string">'this'</span>
|
||
@shorthand = value <span class="hljs-keyword">is</span> <span class="hljs-string">'@'</span>
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
code = <span class="hljs-keyword">if</span> o.scope.method?.bound <span class="hljs-keyword">then</span> o.scope.method.context <span class="hljs-keyword">else</span> @value
|
||
[@makeCode code]
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'ThisExpression'</span>
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
shorthand: @shorthand
|
||
|
||
<span class="hljs-built_in">exports</span>.UndefinedLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UndefinedLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
constructor: <span class="hljs-function">-></span>
|
||
super <span class="hljs-string">'undefined'</span>
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[@makeCode <span class="hljs-keyword">if</span> o.level >= LEVEL_ACCESS <span class="hljs-keyword">then</span> <span class="hljs-string">'(void 0)'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'void 0'</span>]
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'Identifier'</span>
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
name: @value
|
||
declaration: <span class="hljs-literal">no</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.NullLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NullLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
constructor: <span class="hljs-function">-></span>
|
||
super <span class="hljs-string">'null'</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.BooleanLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BooleanLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(value, {@originalValue} = {})</span> -></span>
|
||
super value
|
||
@originalValue ?= @value
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
value: <span class="hljs-keyword">if</span> @value <span class="hljs-keyword">is</span> <span class="hljs-string">'true'</span> <span class="hljs-keyword">then</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">else</span> <span class="hljs-literal">no</span>
|
||
name: @originalValue
|
||
|
||
<span class="hljs-built_in">exports</span>.DefaultLiteral = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DefaultLiteral</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Literal</span></span>
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'Identifier'</span>
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
name: <span class="hljs-string">'default'</span>
|
||
declaration: <span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-96">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-96">§</a>
|
||
</div>
|
||
<h3 id="return">Return</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-97">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-97">§</a>
|
||
</div>
|
||
<p>A <code>return</code> is a <em>pureStatement</em>—wrapping it in a closure wouldn’t make sense.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Return = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Return</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@expression, {@belongsToFuncDirectiveReturn} = {})</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'expression'</span>]
|
||
|
||
isStatement: YES
|
||
makeReturn: THIS
|
||
jumps: THIS
|
||
|
||
compileToFragments: <span class="hljs-function"><span class="hljs-params">(o, level)</span> -></span>
|
||
expr = @expression?.makeReturn()
|
||
<span class="hljs-keyword">if</span> expr <span class="hljs-keyword">and</span> expr <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> Return <span class="hljs-keyword">then</span> expr.compileToFragments o, level <span class="hljs-keyword">else</span> super o, level
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
answer = []</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-98">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-98">§</a>
|
||
</div>
|
||
<p>TODO: If we call <code>expression.compile()</code> here twice, we’ll sometimes
|
||
get back different results!</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> @expression
|
||
answer = @expression.compileToFragments o, LEVEL_PAREN
|
||
unshiftAfterComments answer, @makeCode <span class="hljs-string">"<span class="hljs-subst">#{@tab}</span>return "</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-99">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-99">§</a>
|
||
</div>
|
||
<p>Since the <code>return</code> got indented by <code>@tab</code>, preceding comments that are
|
||
multiline need to be indented.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span> fragment <span class="hljs-keyword">in</span> answer
|
||
<span class="hljs-keyword">if</span> fragment.isHereComment <span class="hljs-keyword">and</span> <span class="hljs-string">'\n'</span> <span class="hljs-keyword">in</span> fragment.code
|
||
fragment.code = multident fragment.code, @tab
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> fragment.isLineComment
|
||
fragment.code = <span class="hljs-string">"<span class="hljs-subst">#{@tab}</span><span class="hljs-subst">#{fragment.code}</span>"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">break</span>
|
||
<span class="hljs-keyword">else</span>
|
||
answer.push @makeCode <span class="hljs-string">"<span class="hljs-subst">#{@tab}</span>return"</span>
|
||
answer.push @makeCode <span class="hljs-string">';'</span>
|
||
answer
|
||
|
||
checkForPureStatementInExpression: <span class="hljs-function">-></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-100">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-100">§</a>
|
||
</div>
|
||
<p>don’t flag <code>return</code> from <code>await return</code>/<code>yield return</code> as invalid.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> @belongsToFuncDirectiveReturn
|
||
super()
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'ReturnStatement'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
argument: @expression?.ast(o, LEVEL_PAREN) ? <span class="hljs-literal">null</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-101">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-101">§</a>
|
||
</div>
|
||
<p>Parent class for <code>YieldReturn</code>/<code>AwaitReturn</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.FuncDirectiveReturn = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FuncDirectiveReturn</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Return</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(expression, {@returnKeyword})</span> -></span>
|
||
super expression
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkScope o
|
||
super o
|
||
|
||
checkScope: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">unless</span> o.scope.parent?
|
||
@error <span class="hljs-string">"<span class="hljs-subst">#{@keyword}</span> can only occur inside functions"</span>
|
||
|
||
isStatementAst: NO
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkScope o
|
||
|
||
<span class="hljs-keyword">new</span> Op @keyword,
|
||
<span class="hljs-keyword">new</span> Return @expression, belongsToFuncDirectiveReturn: <span class="hljs-literal">yes</span>
|
||
.withLocationDataFrom(
|
||
<span class="hljs-keyword">if</span> @expression?
|
||
locationData: mergeLocationData @returnKeyword.locationData, @expression.locationData
|
||
<span class="hljs-keyword">else</span>
|
||
@returnKeyword
|
||
)
|
||
.withLocationDataFrom @
|
||
.ast o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-102">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-102">§</a>
|
||
</div>
|
||
<p><code>yield return</code> works exactly like <code>return</code>, except that it turns the function
|
||
into a generator.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.YieldReturn = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">YieldReturn</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">FuncDirectiveReturn</span></span>
|
||
keyword: <span class="hljs-string">'yield'</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.AwaitReturn = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AwaitReturn</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">FuncDirectiveReturn</span></span>
|
||
keyword: <span class="hljs-string">'await'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-103">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-103">§</a>
|
||
</div>
|
||
<h3 id="value">Value</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-104">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-104">§</a>
|
||
</div>
|
||
<p>A value, variable or literal or parenthesized, indexed or dotted into,
|
||
or vanilla.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Value = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Value</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(base, props, tag, isDefaultValue = <span class="hljs-literal">no</span>)</span> -></span>
|
||
super()
|
||
<span class="hljs-keyword">return</span> base <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> props <span class="hljs-keyword">and</span> base <span class="hljs-keyword">instanceof</span> Value
|
||
@base = base
|
||
@properties = props <span class="hljs-keyword">or</span> []
|
||
@tag = tag
|
||
@[tag] = <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> tag
|
||
@isDefaultValue = isDefaultValue</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-105">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-105">§</a>
|
||
</div>
|
||
<p>If this is a <code>@foo =</code> assignment, if there are comments on <code>@</code> move them
|
||
to be on <code>foo</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> @base?.comments <span class="hljs-keyword">and</span> @base <span class="hljs-keyword">instanceof</span> ThisLiteral <span class="hljs-keyword">and</span> @properties[<span class="hljs-number">0</span>]?.name?
|
||
moveComments @base, @properties[<span class="hljs-number">0</span>].name
|
||
|
||
children: [<span class="hljs-string">'base'</span>, <span class="hljs-string">'properties'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-106">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-106">§</a>
|
||
</div>
|
||
<p>Add a property (or <em>properties</em> ) <code>Access</code> to the list.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> add: <span class="hljs-function"><span class="hljs-params">(props)</span> -></span>
|
||
@properties = @properties.concat props
|
||
@forceUpdateLocation = <span class="hljs-literal">yes</span>
|
||
this
|
||
|
||
hasProperties: <span class="hljs-function">-></span>
|
||
@properties.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
|
||
bareLiteral: <span class="hljs-function"><span class="hljs-params">(type)</span> -></span>
|
||
<span class="hljs-keyword">not</span> @properties.length <span class="hljs-keyword">and</span> @base <span class="hljs-keyword">instanceof</span> type</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-107">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-107">§</a>
|
||
</div>
|
||
<p>Some boolean checks for the benefit of other nodes.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> isArray : <span class="hljs-function">-></span> @bareLiteral(Arr)
|
||
isRange : <span class="hljs-function">-></span> @bareLiteral(Range)
|
||
shouldCache : <span class="hljs-function">-></span> @hasProperties() <span class="hljs-keyword">or</span> @base.shouldCache()
|
||
isAssignable : <span class="hljs-function"><span class="hljs-params">(opts)</span> -></span> @hasProperties() <span class="hljs-keyword">or</span> @base.isAssignable opts
|
||
isNumber : <span class="hljs-function">-></span> @bareLiteral(NumberLiteral)
|
||
isString : <span class="hljs-function">-></span> @bareLiteral(StringLiteral)
|
||
isRegex : <span class="hljs-function">-></span> @bareLiteral(RegexLiteral)
|
||
isUndefined : <span class="hljs-function">-></span> @bareLiteral(UndefinedLiteral)
|
||
isNull : <span class="hljs-function">-></span> @bareLiteral(NullLiteral)
|
||
isBoolean : <span class="hljs-function">-></span> @bareLiteral(BooleanLiteral)
|
||
isAtomic : <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">for</span> node <span class="hljs-keyword">in</span> @properties.concat @base
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">if</span> node.soak <span class="hljs-keyword">or</span> node <span class="hljs-keyword">instanceof</span> Call <span class="hljs-keyword">or</span> node <span class="hljs-keyword">instanceof</span> Op <span class="hljs-keyword">and</span> node.operator <span class="hljs-keyword">is</span> <span class="hljs-string">'do'</span>
|
||
<span class="hljs-literal">yes</span>
|
||
|
||
isNotCallable : <span class="hljs-function">-></span> @isNumber() <span class="hljs-keyword">or</span> @isString() <span class="hljs-keyword">or</span> @isRegex() <span class="hljs-keyword">or</span>
|
||
@isArray() <span class="hljs-keyword">or</span> @isRange() <span class="hljs-keyword">or</span> @isSplice() <span class="hljs-keyword">or</span> @isObject() <span class="hljs-keyword">or</span>
|
||
@isUndefined() <span class="hljs-keyword">or</span> @isNull() <span class="hljs-keyword">or</span> @isBoolean()
|
||
|
||
isStatement : <span class="hljs-function"><span class="hljs-params">(o)</span> -></span> <span class="hljs-keyword">not</span> @properties.length <span class="hljs-keyword">and</span> @base.isStatement o
|
||
isJSXTag : <span class="hljs-function">-></span> @base <span class="hljs-keyword">instanceof</span> JSXTag
|
||
assigns : <span class="hljs-function"><span class="hljs-params">(name)</span> -></span> <span class="hljs-keyword">not</span> @properties.length <span class="hljs-keyword">and</span> @base.assigns name
|
||
jumps : <span class="hljs-function"><span class="hljs-params">(o)</span> -></span> <span class="hljs-keyword">not</span> @properties.length <span class="hljs-keyword">and</span> @base.jumps o
|
||
|
||
isObject: <span class="hljs-function"><span class="hljs-params">(onlyGenerated)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">if</span> @properties.length
|
||
(@base <span class="hljs-keyword">instanceof</span> Obj) <span class="hljs-keyword">and</span> (<span class="hljs-keyword">not</span> onlyGenerated <span class="hljs-keyword">or</span> @base.generated)
|
||
|
||
isElision: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> @base <span class="hljs-keyword">instanceof</span> Arr
|
||
@base.hasElision()
|
||
|
||
isSplice: <span class="hljs-function">-></span>
|
||
[..., lastProperty] = @properties
|
||
lastProperty <span class="hljs-keyword">instanceof</span> Slice
|
||
|
||
looksStatic: <span class="hljs-function"><span class="hljs-params">(className)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> ((thisLiteral = @base) <span class="hljs-keyword">instanceof</span> ThisLiteral <span class="hljs-keyword">or</span> (name = @base).value <span class="hljs-keyword">is</span> className) <span class="hljs-keyword">and</span>
|
||
@properties.length <span class="hljs-keyword">is</span> <span class="hljs-number">1</span> <span class="hljs-keyword">and</span> @properties[<span class="hljs-number">0</span>].name?.value <span class="hljs-keyword">isnt</span> <span class="hljs-string">'prototype'</span>
|
||
<span class="hljs-keyword">return</span>
|
||
staticClassName: thisLiteral ? name</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-108">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-108">§</a>
|
||
</div>
|
||
<p>The value can be unwrapped as its inner node, if there are no attached
|
||
properties.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> unwrap: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @properties.length <span class="hljs-keyword">then</span> this <span class="hljs-keyword">else</span> @base</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-109">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-109">§</a>
|
||
</div>
|
||
<p>A reference has base part (<code>this</code> value) and name part.
|
||
We cache them separately for compiling complex expressions.
|
||
<code>a()[b()] ?= c</code> -> <code>(_base = a())[_name = b()] ? _base[_name] = c</code></p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> cacheReference: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[..., name] = @properties
|
||
<span class="hljs-keyword">if</span> @properties.length < <span class="hljs-number">2</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @base.shouldCache() <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> name?.shouldCache()
|
||
<span class="hljs-keyword">return</span> [this, this] <span class="hljs-comment"># `a` `a.b`</span>
|
||
base = <span class="hljs-keyword">new</span> Value @base, @properties[...<span class="hljs-number">-1</span>]
|
||
<span class="hljs-keyword">if</span> base.shouldCache() <span class="hljs-comment"># `a().b`</span>
|
||
bref = <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.freeVariable <span class="hljs-string">'base'</span>
|
||
base = <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Parens <span class="hljs-keyword">new</span> Assign bref, base
|
||
<span class="hljs-keyword">return</span> [base, bref] <span class="hljs-keyword">unless</span> name <span class="hljs-comment"># `a()`</span>
|
||
<span class="hljs-keyword">if</span> name.shouldCache() <span class="hljs-comment"># `a[b()]`</span>
|
||
nref = <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.freeVariable <span class="hljs-string">'name'</span>
|
||
name = <span class="hljs-keyword">new</span> Index <span class="hljs-keyword">new</span> Assign nref, name.index
|
||
nref = <span class="hljs-keyword">new</span> Index nref
|
||
[base.add(name), <span class="hljs-keyword">new</span> Value(bref <span class="hljs-keyword">or</span> base.base, [nref <span class="hljs-keyword">or</span> name])]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-110">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-110">§</a>
|
||
</div>
|
||
<p>We compile a value to JavaScript by compiling and joining each property.
|
||
Things get much more interesting if the chain of properties has <em>soak</em>
|
||
operators <code>?.</code> interspersed. Then we have to take care not to accidentally
|
||
evaluate anything twice when building the soak chain.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@base.front = @front
|
||
props = @properties
|
||
<span class="hljs-keyword">if</span> props.length <span class="hljs-keyword">and</span> @base.cached?</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-111">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-111">§</a>
|
||
</div>
|
||
<p>Cached fragments enable correct order of the compilation,
|
||
and reuse of variables in the scope.
|
||
Example:
|
||
<code>a(x = 5).b(-> x = 6)</code> should compile in the same order as
|
||
<code>a(x = 5); b(-> x = 6)</code>
|
||
(see issue #4437, <a href="https://github.com/jashkenas/coffeescript/issues/4437">https://github.com/jashkenas/coffeescript/issues/4437</a>)</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> fragments = @base.cached
|
||
<span class="hljs-keyword">else</span>
|
||
fragments = @base.compileToFragments o, (<span class="hljs-keyword">if</span> props.length <span class="hljs-keyword">then</span> LEVEL_ACCESS <span class="hljs-keyword">else</span> <span class="hljs-literal">null</span>)
|
||
<span class="hljs-keyword">if</span> props.length <span class="hljs-keyword">and</span> SIMPLENUM.test fragmentsToText fragments
|
||
fragments.push @makeCode <span class="hljs-string">'.'</span>
|
||
<span class="hljs-keyword">for</span> prop <span class="hljs-keyword">in</span> props
|
||
fragments.push (prop.compileToFragments o)...
|
||
|
||
fragments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-112">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-112">§</a>
|
||
</div>
|
||
<p>Unfold a soak into an <code>If</code>: <code>a?.b</code> -> <code>a.b if a?</code></p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> unfoldSoak: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@unfoldedSoak ?= <span class="hljs-keyword">do</span> =>
|
||
ifn = @base.unfoldSoak o
|
||
<span class="hljs-keyword">if</span> ifn
|
||
ifn.body.properties.push @properties...
|
||
<span class="hljs-keyword">return</span> ifn
|
||
<span class="hljs-keyword">for</span> prop, i <span class="hljs-keyword">in</span> @properties <span class="hljs-keyword">when</span> prop.soak
|
||
prop.soak = <span class="hljs-literal">off</span>
|
||
fst = <span class="hljs-keyword">new</span> Value @base, @properties[...i]
|
||
snd = <span class="hljs-keyword">new</span> Value @base, @properties[i..]
|
||
<span class="hljs-keyword">if</span> fst.shouldCache()
|
||
ref = <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.freeVariable <span class="hljs-string">'ref'</span>
|
||
fst = <span class="hljs-keyword">new</span> Parens <span class="hljs-keyword">new</span> Assign ref, fst
|
||
snd.base = ref
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> If <span class="hljs-keyword">new</span> Existence(fst), snd, soak: <span class="hljs-literal">on</span>
|
||
<span class="hljs-literal">no</span>
|
||
|
||
eachName: <span class="hljs-function"><span class="hljs-params">(iterator, {checkAssignability = <span class="hljs-literal">yes</span>} = {})</span> -></span>
|
||
<span class="hljs-keyword">if</span> @hasProperties()
|
||
iterator @
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> checkAssignability <span class="hljs-keyword">or</span> @base.isAssignable()
|
||
@base.eachName iterator
|
||
<span class="hljs-keyword">else</span>
|
||
@error <span class="hljs-string">'tried to assign to unassignable value'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-113">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-113">§</a>
|
||
</div>
|
||
<p>For AST generation, we need an <code>object</code> that’s this <code>Value</code> minus its last
|
||
property, if it has properties.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> object: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> @ <span class="hljs-keyword">unless</span> @hasProperties()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-114">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-114">§</a>
|
||
</div>
|
||
<p>Get all properties except the last one; for a <code>Value</code> with only one
|
||
property, <code>initialProperties</code> is an empty array.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> initialProperties = @properties[<span class="hljs-number">0.</span>..@properties.length - <span class="hljs-number">1</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-115">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-115">§</a>
|
||
</div>
|
||
<p>Create the <code>object</code> that becomes the new “base” for the split-off final
|
||
property.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> object = <span class="hljs-keyword">new</span> Value @base, initialProperties, @tag, @isDefaultValue</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-116">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-116">§</a>
|
||
</div>
|
||
<p>Add location data to our new node, so that it has correct location data
|
||
for source maps or later conversion into AST location data.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> object.locationData =
|
||
<span class="hljs-keyword">if</span> initialProperties.length <span class="hljs-keyword">is</span> <span class="hljs-number">0</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-117">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-117">§</a>
|
||
</div>
|
||
<p>This new <code>Value</code> has only one property, so the location data is just
|
||
that of the parent <code>Value</code>’s base.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @base.locationData
|
||
<span class="hljs-keyword">else</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-118">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-118">§</a>
|
||
</div>
|
||
<p>This new <code>Value</code> has multiple properties, so the location data spans
|
||
from the parent <code>Value</code>’s base to the last property that’s included
|
||
in this new node (a.k.a. the second-to-last property of the parent).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> mergeLocationData @base.locationData, initialProperties[initialProperties.length - <span class="hljs-number">1</span>].locationData
|
||
object
|
||
|
||
containsSoak: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> @hasProperties()
|
||
|
||
<span class="hljs-keyword">for</span> property <span class="hljs-keyword">in</span> @properties <span class="hljs-keyword">when</span> property.soak
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span>
|
||
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> @base <span class="hljs-keyword">instanceof</span> Call <span class="hljs-keyword">and</span> @base.soak
|
||
|
||
<span class="hljs-literal">no</span>
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-119">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-119">§</a>
|
||
</div>
|
||
<p>If the <code>Value</code> has no properties, the AST node is just whatever this
|
||
node’s <code>base</code> is.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> @base.ast o <span class="hljs-keyword">unless</span> @hasProperties()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-120">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-120">§</a>
|
||
</div>
|
||
<p>Otherwise, call <code>Base::ast</code> which in turn calls the <code>astType</code> and
|
||
<code>astProperties</code> methods below.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> super o
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @isJSXTag()
|
||
<span class="hljs-string">'JSXMemberExpression'</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @containsSoak()
|
||
<span class="hljs-string">'OptionalMemberExpression'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'MemberExpression'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-121">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-121">§</a>
|
||
</div>
|
||
<p>If this <code>Value</code> has properties, the <em>last</em> property (e.g. <code>c</code> in <code>a.b.c</code>)
|
||
becomes the <code>property</code>, and the preceding properties (e.g. <code>a.b</code>) become
|
||
a child <code>Value</code> node assigned to the <code>object</code> property.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[..., property] = @properties
|
||
property.name.jsx = <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> @isJSXTag()
|
||
computed = property <span class="hljs-keyword">instanceof</span> Index <span class="hljs-keyword">or</span> property.name?.unwrap() <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> PropertyName
|
||
<span class="hljs-keyword">return</span> {
|
||
object: @object().ast o, LEVEL_ACCESS
|
||
property: property.ast o, (LEVEL_PAREN <span class="hljs-keyword">if</span> computed)
|
||
computed
|
||
optional: !!property.soak
|
||
shorthand: !!property.shorthand
|
||
}
|
||
|
||
astLocationData: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> super() <span class="hljs-keyword">unless</span> @isJSXTag()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-122">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-122">§</a>
|
||
</div>
|
||
<p>don’t include leading < of JSX tag in location data</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> mergeAstLocationData(
|
||
jisonLocationDataToAstLocationData(@base.tagNameLocationData),
|
||
jisonLocationDataToAstLocationData(@properties[@properties.length - <span class="hljs-number">1</span>].locationData)
|
||
)
|
||
|
||
<span class="hljs-built_in">exports</span>.MetaProperty = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MetaProperty</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@meta, @property)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'meta'</span>, <span class="hljs-string">'property'</span>]
|
||
|
||
checkValid: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @meta.value <span class="hljs-keyword">is</span> <span class="hljs-string">'new'</span>
|
||
<span class="hljs-keyword">if</span> @property <span class="hljs-keyword">instanceof</span> Access <span class="hljs-keyword">and</span> @property.name.value <span class="hljs-keyword">is</span> <span class="hljs-string">'target'</span>
|
||
<span class="hljs-keyword">unless</span> o.scope.parent?
|
||
@error <span class="hljs-string">"new.target can only occur inside functions"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
@error <span class="hljs-string">"the only valid meta property for new is new.target"</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @meta.value <span class="hljs-keyword">is</span> <span class="hljs-string">'import'</span>
|
||
<span class="hljs-keyword">unless</span> @property <span class="hljs-keyword">instanceof</span> Access <span class="hljs-keyword">and</span> @property.name.value <span class="hljs-keyword">is</span> <span class="hljs-string">'meta'</span>
|
||
@error <span class="hljs-string">"the only valid meta property for import is import.meta"</span>
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkValid o
|
||
fragments = []
|
||
fragments.push @meta.compileToFragments(o, LEVEL_ACCESS)...
|
||
fragments.push @property.compileToFragments(o)...
|
||
fragments
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkValid o
|
||
|
||
<span class="hljs-keyword">return</span>
|
||
meta: @meta.ast o, LEVEL_ACCESS
|
||
property: @property.ast o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-123">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-123">§</a>
|
||
</div>
|
||
<h3 id="herecomment">HereComment</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-124">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-124">§</a>
|
||
</div>
|
||
<p>Comment delimited by <code>###</code> (becoming <code>/* */</code>).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.HereComment = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HereComment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">({ @content, @newLine, @unshift, @locationData })</span> -></span>
|
||
super()
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
multiline = <span class="hljs-string">'\n'</span> <span class="hljs-keyword">in</span> @content</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-125">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-125">§</a>
|
||
</div>
|
||
<p>Unindent multiline comments. They will be reindented later.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> multiline
|
||
indent = <span class="hljs-literal">null</span>
|
||
<span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> @content.split <span class="hljs-string">'\n'</span>
|
||
leadingWhitespace = <span class="hljs-regexp">/^\s*/</span>.exec(line)[<span class="hljs-number">0</span>]
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> indent <span class="hljs-keyword">or</span> leadingWhitespace.length < indent.length
|
||
indent = leadingWhitespace
|
||
@content = @content.replace <span class="hljs-regexp">/// \n <span class="hljs-subst">#{indent}</span> ///</span>g, <span class="hljs-string">'\n'</span> <span class="hljs-keyword">if</span> indent
|
||
|
||
hasLeadingMarks = <span class="hljs-regexp">/\n\s*[#|\*]/</span>.test @content
|
||
@content = @content.replace <span class="hljs-regexp">/^([ \t]*)#(?=\s)/gm</span>, <span class="hljs-string">' *'</span> <span class="hljs-keyword">if</span> hasLeadingMarks
|
||
|
||
@content = <span class="hljs-string">"/*<span class="hljs-subst">#{@content}</span><span class="hljs-subst">#{<span class="hljs-keyword">if</span> hasLeadingMarks <span class="hljs-keyword">then</span> <span class="hljs-string">' '</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>}</span>*/"</span>
|
||
fragment = @makeCode @content
|
||
fragment.newLine = @newLine
|
||
fragment.unshift = @unshift
|
||
fragment.multiline = multiline</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-126">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-126">§</a>
|
||
</div>
|
||
<p>Don’t rely on <code>fragment.type</code>, which can break when the compiler is minified.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> fragment.isComment = fragment.isHereComment = <span class="hljs-literal">yes</span>
|
||
fragment
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'CommentBlock'</span>
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
value: @content</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-127">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-127">§</a>
|
||
</div>
|
||
<h3 id="linecomment">LineComment</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-128">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-128">§</a>
|
||
</div>
|
||
<p>Comment running from <code>#</code> to the end of a line (becoming <code>//</code>).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.LineComment = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LineComment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">({ @content, @newLine, @unshift, @locationData, @precededByBlankLine })</span> -></span>
|
||
super()
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
fragment = @makeCode(<span class="hljs-keyword">if</span> <span class="hljs-regexp">/^\s*$/</span>.test @content <span class="hljs-keyword">then</span> <span class="hljs-string">''</span> <span class="hljs-keyword">else</span> <span class="hljs-string">"<span class="hljs-subst">#{<span class="hljs-keyword">if</span> @precededByBlankLine <span class="hljs-keyword">then</span> <span class="hljs-string">"\n<span class="hljs-subst">#{o.indent}</span>"</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>}</span>//<span class="hljs-subst">#{@content}</span>"</span>)
|
||
fragment.newLine = @newLine
|
||
fragment.unshift = @unshift
|
||
fragment.trail = <span class="hljs-keyword">not</span> @newLine <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @unshift</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-129">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-129">§</a>
|
||
</div>
|
||
<p>Don’t rely on <code>fragment.type</code>, which can break when the compiler is minified.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> fragment.isComment = fragment.isLineComment = <span class="hljs-literal">yes</span>
|
||
fragment
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'CommentLine'</span>
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
value: @content</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-130">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-130">§</a>
|
||
</div>
|
||
<h3 id="jsx">JSX</h3>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>
|
||
<span class="hljs-built_in">exports</span>.JSXIdentifier = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JSXIdentifier</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">IdentifierLiteral</span></span>
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'JSXIdentifier'</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.JSXTag = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JSXTag</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">JSXIdentifier</span></span>
|
||
constructor: (value, {
|
||
@tagNameLocationData
|
||
@closingTagOpeningBracketLocationData
|
||
@closingTagSlashLocationData
|
||
@closingTagNameLocationData
|
||
@closingTagClosingBracketLocationData
|
||
}) ->
|
||
super value
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
name: @value
|
||
|
||
<span class="hljs-built_in">exports</span>.JSXExpressionContainer = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JSXExpressionContainer</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@expression, {locationData} = {})</span> -></span>
|
||
super()
|
||
@expression.jsxAttribute = <span class="hljs-literal">yes</span>
|
||
@locationData = locationData ? @expression.locationData
|
||
|
||
children: [<span class="hljs-string">'expression'</span>]
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@expression.compileNode(o)
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
expression: astAsBlockIfNeeded @expression, o
|
||
|
||
<span class="hljs-built_in">exports</span>.JSXEmptyExpression = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JSXEmptyExpression</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
|
||
<span class="hljs-built_in">exports</span>.JSXText = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JSXText</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(stringLiteral)</span> -></span>
|
||
super()
|
||
@value = stringLiteral.unquotedValueForJSX
|
||
@locationData = stringLiteral.locationData
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> {
|
||
@value
|
||
extra:
|
||
raw: @value
|
||
}
|
||
|
||
<span class="hljs-built_in">exports</span>.JSXAttribute = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JSXAttribute</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">({@name, value})</span> -></span>
|
||
super()
|
||
@value =
|
||
<span class="hljs-keyword">if</span> value?
|
||
value = value.base
|
||
<span class="hljs-keyword">if</span> value <span class="hljs-keyword">instanceof</span> StringLiteral <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> value.shouldGenerateTemplateLiteral()
|
||
value
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">new</span> JSXExpressionContainer value
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-literal">null</span>
|
||
@value?.comments = value.comments
|
||
|
||
children: [<span class="hljs-string">'name'</span>, <span class="hljs-string">'value'</span>]
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
compiledName = @name.compileToFragments o, LEVEL_LIST
|
||
<span class="hljs-keyword">return</span> compiledName <span class="hljs-keyword">unless</span> @value?
|
||
val = @value.compileToFragments o, LEVEL_LIST
|
||
compiledName.concat @makeCode(<span class="hljs-string">'='</span>), val
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
name = @name
|
||
<span class="hljs-keyword">if</span> <span class="hljs-string">':'</span> <span class="hljs-keyword">in</span> name.value
|
||
name = <span class="hljs-keyword">new</span> JSXNamespacedName name
|
||
<span class="hljs-keyword">return</span>
|
||
name: name.ast o
|
||
value: @value?.ast(o) ? <span class="hljs-literal">null</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.JSXAttributes = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JSXAttributes</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(arr)</span> -></span>
|
||
super()
|
||
@attributes = []
|
||
<span class="hljs-keyword">for</span> object <span class="hljs-keyword">in</span> arr.objects
|
||
@checkValidAttribute object
|
||
{base} = object
|
||
<span class="hljs-keyword">if</span> base <span class="hljs-keyword">instanceof</span> IdentifierLiteral</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-131">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-131">§</a>
|
||
</div>
|
||
<p>attribute with no value eg disabled</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> attribute = <span class="hljs-keyword">new</span> JSXAttribute name: <span class="hljs-keyword">new</span> JSXIdentifier(base.value).withLocationDataAndCommentsFrom base
|
||
attribute.locationData = base.locationData
|
||
@attributes.push attribute
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> base.generated</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-132">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-132">§</a>
|
||
</div>
|
||
<p>object spread attribute eg {…props}</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> attribute = base.properties[<span class="hljs-number">0</span>]
|
||
attribute.jsx = <span class="hljs-literal">yes</span>
|
||
attribute.locationData = base.locationData
|
||
@attributes.push attribute
|
||
<span class="hljs-keyword">else</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-133">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-133">§</a>
|
||
</div>
|
||
<p>Obj containing attributes with values eg a=”b” c={d}</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span> property <span class="hljs-keyword">in</span> base.properties
|
||
{variable, value} = property
|
||
attribute = <span class="hljs-keyword">new</span> JSXAttribute {
|
||
name: <span class="hljs-keyword">new</span> JSXIdentifier(variable.base.value).withLocationDataAndCommentsFrom variable.base
|
||
value
|
||
}
|
||
attribute.locationData = property.locationData
|
||
@attributes.push attribute
|
||
@locationData = arr.locationData
|
||
|
||
children: [<span class="hljs-string">'attributes'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-134">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-134">§</a>
|
||
</div>
|
||
<p>Catch invalid attributes: <div {a:”b”, props} {props} “value” /></p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> checkValidAttribute: <span class="hljs-function"><span class="hljs-params">(object)</span> -></span>
|
||
{base: attribute} = object
|
||
properties = attribute?.properties <span class="hljs-keyword">or</span> []
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> (attribute <span class="hljs-keyword">instanceof</span> Obj <span class="hljs-keyword">or</span> attribute <span class="hljs-keyword">instanceof</span> IdentifierLiteral) <span class="hljs-keyword">or</span> (attribute <span class="hljs-keyword">instanceof</span> Obj <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> attribute.generated <span class="hljs-keyword">and</span> (properties.length > <span class="hljs-number">1</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> (properties[<span class="hljs-number">0</span>] <span class="hljs-keyword">instanceof</span> Splat)))
|
||
object.error <span class="hljs-string">"""
|
||
Unexpected token. Allowed JSX attributes are: id="val", src={source}, {props...} or attribute.
|
||
"""</span>
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
fragments = []
|
||
<span class="hljs-keyword">for</span> attribute <span class="hljs-keyword">in</span> @attributes
|
||
fragments.push @makeCode <span class="hljs-string">' '</span>
|
||
fragments.push attribute.compileToFragments(o, LEVEL_TOP)...
|
||
fragments
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
attribute.ast(o) <span class="hljs-keyword">for</span> attribute <span class="hljs-keyword">in</span> @attributes
|
||
|
||
<span class="hljs-built_in">exports</span>.JSXNamespacedName = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JSXNamespacedName</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(tag)</span> -></span>
|
||
super()
|
||
[namespace, name] = tag.value.split <span class="hljs-string">':'</span>
|
||
@namespace = <span class="hljs-keyword">new</span> JSXIdentifier(namespace).withLocationDataFrom locationData: extractSameLineLocationDataFirst(namespace.length) tag.locationData
|
||
@name = <span class="hljs-keyword">new</span> JSXIdentifier(name ).withLocationDataFrom locationData: extractSameLineLocationDataLast(name.length ) tag.locationData
|
||
@locationData = tag.locationData
|
||
|
||
children: [<span class="hljs-string">'namespace'</span>, <span class="hljs-string">'name'</span>]
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
namespace: @namespace.ast o
|
||
name: @name.ast o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-135">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-135">§</a>
|
||
</div>
|
||
<p>Node for a JSX element</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.JSXElement = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JSXElement</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">({@tagName, @attributes, @content})</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'tagName'</span>, <span class="hljs-string">'attributes'</span>, <span class="hljs-string">'content'</span>]
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@content?.base.jsx = <span class="hljs-literal">yes</span>
|
||
fragments = [@makeCode(<span class="hljs-string">'<'</span>)]
|
||
fragments.push (tag = @tagName.compileToFragments(o, LEVEL_ACCESS))...
|
||
fragments.push @attributes.compileToFragments(o)...
|
||
<span class="hljs-keyword">if</span> @content
|
||
fragments.push @makeCode(<span class="hljs-string">'>'</span>)
|
||
fragments.push @content.compileNode(o, LEVEL_LIST)...
|
||
fragments.push [@makeCode(<span class="hljs-string">'</'</span>), tag..., @makeCode(<span class="hljs-string">'>'</span>)]...
|
||
<span class="hljs-keyword">else</span>
|
||
fragments.push @makeCode(<span class="hljs-string">' />'</span>)
|
||
fragments
|
||
|
||
isFragment: <span class="hljs-function">-></span>
|
||
!@tagName.base.value.length
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-136">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-136">§</a>
|
||
</div>
|
||
<p>The location data spanning the opening element < … > is captured by
|
||
the generated Arr which contains the element’s attributes</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @openingElementLocationData = jisonLocationDataToAstLocationData @attributes.locationData
|
||
|
||
tagName = @tagName.base
|
||
tagName.locationData = tagName.tagNameLocationData
|
||
<span class="hljs-keyword">if</span> @content?
|
||
@closingElementLocationData = mergeAstLocationData(
|
||
jisonLocationDataToAstLocationData tagName.closingTagOpeningBracketLocationData
|
||
jisonLocationDataToAstLocationData tagName.closingTagClosingBracketLocationData
|
||
)
|
||
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @isFragment()
|
||
<span class="hljs-string">'JSXFragment'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'JSXElement'</span>
|
||
|
||
elementAstProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-function"> <span class="hljs-title">tagNameAst</span> = =></span>
|
||
tag = @tagName.unwrap()
|
||
<span class="hljs-keyword">if</span> tag?.value <span class="hljs-keyword">and</span> <span class="hljs-string">':'</span> <span class="hljs-keyword">in</span> tag.value
|
||
tag = <span class="hljs-keyword">new</span> JSXNamespacedName tag
|
||
tag.ast o
|
||
|
||
openingElement = <span class="hljs-built_in">Object</span>.assign {
|
||
type: <span class="hljs-string">'JSXOpeningElement'</span>
|
||
name: tagNameAst()
|
||
selfClosing: <span class="hljs-keyword">not</span> @closingElementLocationData?
|
||
attributes: @attributes.ast o
|
||
}, @openingElementLocationData
|
||
|
||
closingElement = <span class="hljs-literal">null</span>
|
||
<span class="hljs-keyword">if</span> @closingElementLocationData?
|
||
closingElement = <span class="hljs-built_in">Object</span>.assign {
|
||
type: <span class="hljs-string">'JSXClosingElement'</span>
|
||
name: <span class="hljs-built_in">Object</span>.assign(
|
||
tagNameAst(),
|
||
jisonLocationDataToAstLocationData @tagName.base.closingTagNameLocationData
|
||
)
|
||
}, @closingElementLocationData
|
||
<span class="hljs-keyword">if</span> closingElement.name.type <span class="hljs-keyword">in</span> [<span class="hljs-string">'JSXMemberExpression'</span>, <span class="hljs-string">'JSXNamespacedName'</span>]
|
||
rangeDiff = closingElement.range[<span class="hljs-number">0</span>] - openingElement.range[<span class="hljs-number">0</span>] + <span class="hljs-string">'/'</span>.length
|
||
columnDiff = closingElement.loc.start.column - openingElement.loc.start.column + <span class="hljs-string">'/'</span>.length
|
||
<span class="hljs-function"> <span class="hljs-title">shiftAstLocationData</span> = <span class="hljs-params">(node)</span> =></span>
|
||
node.range = [
|
||
node.range[<span class="hljs-number">0</span>] + rangeDiff
|
||
node.range[<span class="hljs-number">1</span>] + rangeDiff
|
||
]
|
||
node.start += rangeDiff
|
||
node.end += rangeDiff
|
||
node.loc.start =
|
||
line: @closingElementLocationData.loc.start.line
|
||
column: node.loc.start.column + columnDiff
|
||
node.loc.end =
|
||
line: @closingElementLocationData.loc.start.line
|
||
column: node.loc.end.column + columnDiff
|
||
<span class="hljs-keyword">if</span> closingElement.name.type <span class="hljs-keyword">is</span> <span class="hljs-string">'JSXMemberExpression'</span>
|
||
currentExpr = closingElement.name
|
||
<span class="hljs-keyword">while</span> currentExpr.type <span class="hljs-keyword">is</span> <span class="hljs-string">'JSXMemberExpression'</span>
|
||
shiftAstLocationData currentExpr <span class="hljs-keyword">unless</span> currentExpr <span class="hljs-keyword">is</span> closingElement.name
|
||
shiftAstLocationData currentExpr.property
|
||
currentExpr = currentExpr.object
|
||
shiftAstLocationData currentExpr
|
||
<span class="hljs-keyword">else</span> <span class="hljs-comment"># JSXNamespacedName</span>
|
||
shiftAstLocationData closingElement.name.namespace
|
||
shiftAstLocationData closingElement.name.name
|
||
|
||
{openingElement, closingElement}
|
||
|
||
fragmentAstProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
openingFragment = <span class="hljs-built_in">Object</span>.assign {
|
||
type: <span class="hljs-string">'JSXOpeningFragment'</span>
|
||
}, @openingElementLocationData
|
||
|
||
closingFragment = <span class="hljs-built_in">Object</span>.assign {
|
||
type: <span class="hljs-string">'JSXClosingFragment'</span>
|
||
}, @closingElementLocationData
|
||
|
||
{openingFragment, closingFragment}
|
||
|
||
contentAst: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> [] <span class="hljs-keyword">unless</span> @content <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @content.base.isEmpty?()
|
||
|
||
content = @content.unwrapAll()
|
||
children =
|
||
<span class="hljs-keyword">if</span> content <span class="hljs-keyword">instanceof</span> StringLiteral
|
||
[<span class="hljs-keyword">new</span> JSXText content]
|
||
<span class="hljs-keyword">else</span> <span class="hljs-comment"># StringWithInterpolations</span>
|
||
<span class="hljs-keyword">for</span> element <span class="hljs-keyword">in</span> @content.unwrapAll().extractElements o, includeInterpolationWrappers: <span class="hljs-literal">yes</span>, isJsx: <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">if</span> element <span class="hljs-keyword">instanceof</span> StringLiteral
|
||
<span class="hljs-keyword">new</span> JSXText element
|
||
<span class="hljs-keyword">else</span> <span class="hljs-comment"># Interpolation</span>
|
||
{expression} = element
|
||
<span class="hljs-keyword">unless</span> expression?
|
||
emptyExpression = <span class="hljs-keyword">new</span> JSXEmptyExpression()
|
||
emptyExpression.locationData = emptyExpressionLocationData {
|
||
interpolationNode: element
|
||
openingBrace: <span class="hljs-string">'{'</span>
|
||
closingBrace: <span class="hljs-string">'}'</span>
|
||
}
|
||
|
||
<span class="hljs-keyword">new</span> JSXExpressionContainer emptyExpression, locationData: element.locationData
|
||
<span class="hljs-keyword">else</span>
|
||
unwrapped = expression.unwrapAll()
|
||
<span class="hljs-keyword">if</span> unwrapped <span class="hljs-keyword">instanceof</span> JSXElement <span class="hljs-keyword">and</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-137">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-137">§</a>
|
||
</div>
|
||
<p>distinguish <code><a><b /></a></code> from <code><a>{<b />}</a></code></p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> unwrapped.locationData.range[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> element.locationData.range[<span class="hljs-number">0</span>]
|
||
unwrapped
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">new</span> JSXExpressionContainer unwrapped, locationData: element.locationData
|
||
|
||
child.ast(o) <span class="hljs-keyword">for</span> child <span class="hljs-keyword">in</span> children <span class="hljs-keyword">when</span> <span class="hljs-keyword">not</span> (child <span class="hljs-keyword">instanceof</span> JSXText <span class="hljs-keyword">and</span> child.value.length <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>)
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-built_in">Object</span>.assign(
|
||
<span class="hljs-keyword">if</span> @isFragment()
|
||
@fragmentAstProperties o
|
||
<span class="hljs-keyword">else</span>
|
||
@elementAstProperties o
|
||
,
|
||
children: @contentAst o
|
||
)
|
||
|
||
astLocationData: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @closingElementLocationData?
|
||
mergeAstLocationData @openingElementLocationData, @closingElementLocationData
|
||
<span class="hljs-keyword">else</span>
|
||
@openingElementLocationData</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-138">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-138">§</a>
|
||
</div>
|
||
<h3 id="call">Call</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-139">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-139">§</a>
|
||
</div>
|
||
<p>Node for a function invocation.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Call = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Call</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@variable, @args = [], @soak, @token)</span> -></span>
|
||
super()
|
||
|
||
@implicit = @args.implicit
|
||
@isNew = <span class="hljs-literal">no</span>
|
||
<span class="hljs-keyword">if</span> @variable <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> @variable.isNotCallable()
|
||
@variable.error <span class="hljs-string">"literal is not a function"</span>
|
||
|
||
<span class="hljs-keyword">if</span> @variable.base <span class="hljs-keyword">instanceof</span> JSXTag
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> JSXElement(
|
||
tagName: @variable
|
||
attributes: <span class="hljs-keyword">new</span> JSXAttributes @args[<span class="hljs-number">0</span>].base
|
||
content: @args[<span class="hljs-number">1</span>]
|
||
)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-140">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-140">§</a>
|
||
</div>
|
||
<p><code>@variable</code> never gets output as a result of this node getting created as
|
||
part of <code>RegexWithInterpolations</code>, so for that case move any comments to
|
||
the <code>args</code> property that gets passed into <code>RegexWithInterpolations</code> via
|
||
the grammar.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> @variable.base?.value <span class="hljs-keyword">is</span> <span class="hljs-string">'RegExp'</span> <span class="hljs-keyword">and</span> @args.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
moveComments @variable, @args[<span class="hljs-number">0</span>]
|
||
|
||
children: [<span class="hljs-string">'variable'</span>, <span class="hljs-string">'args'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-141">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-141">§</a>
|
||
</div>
|
||
<p>When setting the location, we sometimes need to update the start location to
|
||
account for a newly-discovered <code>new</code> operator to the left of us. This
|
||
expands the range on the left, but not the right.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> updateLocationDataIfMissing: <span class="hljs-function"><span class="hljs-params">(locationData)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @locationData <span class="hljs-keyword">and</span> @needsUpdatedStartLocation
|
||
@locationData = <span class="hljs-built_in">Object</span>.assign {},
|
||
@locationData,
|
||
first_line: locationData.first_line
|
||
first_column: locationData.first_column
|
||
range: [
|
||
locationData.range[<span class="hljs-number">0</span>]
|
||
@locationData.range[<span class="hljs-number">1</span>]
|
||
]
|
||
base = @variable?.base <span class="hljs-keyword">or</span> @variable
|
||
<span class="hljs-keyword">if</span> base.needsUpdatedStartLocation
|
||
@variable.locationData = <span class="hljs-built_in">Object</span>.assign {},
|
||
@variable.locationData,
|
||
first_line: locationData.first_line
|
||
first_column: locationData.first_column
|
||
range: [
|
||
locationData.range[<span class="hljs-number">0</span>]
|
||
@variable.locationData.range[<span class="hljs-number">1</span>]
|
||
]
|
||
base.updateLocationDataIfMissing locationData
|
||
<span class="hljs-keyword">delete</span> @needsUpdatedStartLocation
|
||
super locationData</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-142">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-142">§</a>
|
||
</div>
|
||
<p>Tag this invocation as creating a new instance.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> newInstance: <span class="hljs-function">-></span>
|
||
base = @variable?.base <span class="hljs-keyword">or</span> @variable
|
||
<span class="hljs-keyword">if</span> base <span class="hljs-keyword">instanceof</span> Call <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> base.isNew
|
||
base.newInstance()
|
||
<span class="hljs-keyword">else</span>
|
||
@isNew = <span class="hljs-literal">true</span>
|
||
@needsUpdatedStartLocation = <span class="hljs-literal">true</span>
|
||
this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-143">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-143">§</a>
|
||
</div>
|
||
<p>Soaked chained invocations unfold into if/else ternary structures.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> unfoldSoak: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @soak
|
||
<span class="hljs-keyword">if</span> @variable <span class="hljs-keyword">instanceof</span> Super
|
||
left = <span class="hljs-keyword">new</span> Literal @variable.compile o
|
||
rite = <span class="hljs-keyword">new</span> Value left
|
||
@variable.error <span class="hljs-string">"Unsupported reference to 'super'"</span> <span class="hljs-keyword">unless</span> @variable.accessor?
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">return</span> ifn <span class="hljs-keyword">if</span> ifn = unfoldSoak o, this, <span class="hljs-string">'variable'</span>
|
||
[left, rite] = <span class="hljs-keyword">new</span> Value(@variable).cacheReference o
|
||
rite = <span class="hljs-keyword">new</span> Call rite, @args
|
||
rite.isNew = @isNew
|
||
left = <span class="hljs-keyword">new</span> Literal <span class="hljs-string">"typeof <span class="hljs-subst">#{ left.compile o }</span> === \"function\""</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> If left, <span class="hljs-keyword">new</span> Value(rite), soak: <span class="hljs-literal">yes</span>
|
||
call = this
|
||
list = []
|
||
<span class="hljs-keyword">loop</span>
|
||
<span class="hljs-keyword">if</span> call.variable <span class="hljs-keyword">instanceof</span> Call
|
||
list.push call
|
||
call = call.variable
|
||
<span class="hljs-keyword">continue</span>
|
||
<span class="hljs-keyword">break</span> <span class="hljs-keyword">unless</span> call.variable <span class="hljs-keyword">instanceof</span> Value
|
||
list.push call
|
||
<span class="hljs-keyword">break</span> <span class="hljs-keyword">unless</span> (call = call.variable.base) <span class="hljs-keyword">instanceof</span> Call
|
||
<span class="hljs-keyword">for</span> call <span class="hljs-keyword">in</span> list.reverse()
|
||
<span class="hljs-keyword">if</span> ifn
|
||
<span class="hljs-keyword">if</span> call.variable <span class="hljs-keyword">instanceof</span> Call
|
||
call.variable = ifn
|
||
<span class="hljs-keyword">else</span>
|
||
call.variable.base = ifn
|
||
ifn = unfoldSoak o, call, <span class="hljs-string">'variable'</span>
|
||
ifn</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-144">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-144">§</a>
|
||
</div>
|
||
<p>Compile a vanilla function call.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkForNewSuper()
|
||
@variable?.front = @front
|
||
compiledArgs = []</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-145">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-145">§</a>
|
||
</div>
|
||
<p>If variable is <code>Accessor</code> fragments are cached and used later
|
||
in <code>Value::compileNode</code> to ensure correct order of the compilation,
|
||
and reuse of variables in the scope.
|
||
Example:
|
||
<code>a(x = 5).b(-> x = 6)</code> should compile in the same order as
|
||
<code>a(x = 5); b(-> x = 6)</code>
|
||
(see issue #4437, <a href="https://github.com/jashkenas/coffeescript/issues/4437">https://github.com/jashkenas/coffeescript/issues/4437</a>)</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> varAccess = @variable?.properties?[<span class="hljs-number">0</span>] <span class="hljs-keyword">instanceof</span> Access
|
||
argCode = (arg <span class="hljs-keyword">for</span> arg <span class="hljs-keyword">in</span> (@args || []) <span class="hljs-keyword">when</span> arg <span class="hljs-keyword">instanceof</span> Code)
|
||
<span class="hljs-keyword">if</span> argCode.length > <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> varAccess <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @variable.base.cached
|
||
[cache] = @variable.base.cache o, LEVEL_ACCESS, <span class="hljs-function">-></span> <span class="hljs-literal">no</span>
|
||
@variable.base.cached = cache
|
||
|
||
<span class="hljs-keyword">for</span> arg, argIndex <span class="hljs-keyword">in</span> @args
|
||
<span class="hljs-keyword">if</span> argIndex <span class="hljs-keyword">then</span> compiledArgs.push @makeCode <span class="hljs-string">", "</span>
|
||
compiledArgs.push (arg.compileToFragments o, LEVEL_LIST)...
|
||
|
||
fragments = []
|
||
<span class="hljs-keyword">if</span> @isNew
|
||
fragments.push @makeCode <span class="hljs-string">'new '</span>
|
||
fragments.push @variable.compileToFragments(o, LEVEL_ACCESS)...
|
||
fragments.push @makeCode(<span class="hljs-string">'('</span>), compiledArgs..., @makeCode(<span class="hljs-string">')'</span>)
|
||
fragments
|
||
|
||
checkForNewSuper: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @isNew
|
||
@variable.error <span class="hljs-string">"Unsupported reference to 'super'"</span> <span class="hljs-keyword">if</span> @variable <span class="hljs-keyword">instanceof</span> Super
|
||
|
||
containsSoak: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> @soak
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> @variable?.containsSoak?()
|
||
<span class="hljs-literal">no</span>
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @soak <span class="hljs-keyword">and</span> @variable <span class="hljs-keyword">instanceof</span> Super <span class="hljs-keyword">and</span> o.scope.namedMethod()?.ctor
|
||
@variable.error <span class="hljs-string">"Unsupported reference to 'super'"</span>
|
||
@checkForNewSuper()
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @isNew
|
||
<span class="hljs-string">'NewExpression'</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @containsSoak()
|
||
<span class="hljs-string">'OptionalCallExpression'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'CallExpression'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
callee: @variable.ast o, LEVEL_ACCESS
|
||
arguments: arg.ast(o, LEVEL_LIST) <span class="hljs-keyword">for</span> arg <span class="hljs-keyword">in</span> @args
|
||
optional: !!@soak
|
||
implicit: !!@implicit</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-146">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-146">§</a>
|
||
</div>
|
||
<h3 id="super">Super</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-147">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-147">§</a>
|
||
</div>
|
||
<p>Takes care of converting <code>super()</code> calls into calls against the prototype’s
|
||
function of the same name.
|
||
When <code>expressions</code> are set the call will be compiled in such a way that the
|
||
expressions are evaluated without altering the return value of the <code>SuperCall</code>
|
||
expression.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.SuperCall = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SuperCall</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Call</span></span>
|
||
children: Call::children.concat [<span class="hljs-string">'expressions'</span>]
|
||
|
||
isStatement: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@expressions?.length <span class="hljs-keyword">and</span> o.level <span class="hljs-keyword">is</span> LEVEL_TOP
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> super o <span class="hljs-keyword">unless</span> @expressions?.length
|
||
|
||
superCall = <span class="hljs-keyword">new</span> Literal fragmentsToText super o
|
||
replacement = <span class="hljs-keyword">new</span> Block @expressions.slice()
|
||
|
||
<span class="hljs-keyword">if</span> o.level > LEVEL_TOP</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-148">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-148">§</a>
|
||
</div>
|
||
<p>If we might be in an expression we need to cache and return the result</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> [superCall, ref] = superCall.cache o, <span class="hljs-literal">null</span>, YES
|
||
replacement.push ref
|
||
|
||
replacement.unshift superCall
|
||
replacement.compileToFragments o, <span class="hljs-keyword">if</span> o.level <span class="hljs-keyword">is</span> LEVEL_TOP <span class="hljs-keyword">then</span> o.level <span class="hljs-keyword">else</span> LEVEL_LIST
|
||
|
||
<span class="hljs-built_in">exports</span>.Super = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Super</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@accessor, @superLiteral)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'accessor'</span>]
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkInInstanceMethod o
|
||
|
||
method = o.scope.namedMethod()
|
||
<span class="hljs-keyword">unless</span> method.ctor? <span class="hljs-keyword">or</span> @accessor?
|
||
{name, variable} = method
|
||
<span class="hljs-keyword">if</span> name.shouldCache() <span class="hljs-keyword">or</span> (name <span class="hljs-keyword">instanceof</span> Index <span class="hljs-keyword">and</span> name.index.isAssignable())
|
||
nref = <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.parent.freeVariable <span class="hljs-string">'name'</span>
|
||
name.index = <span class="hljs-keyword">new</span> Assign nref, name.index
|
||
@accessor = <span class="hljs-keyword">if</span> nref? <span class="hljs-keyword">then</span> <span class="hljs-keyword">new</span> Index nref <span class="hljs-keyword">else</span> name
|
||
|
||
<span class="hljs-keyword">if</span> @accessor?.name?.comments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-149">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-149">§</a>
|
||
</div>
|
||
<p>A <code>super()</code> call gets compiled to e.g. <code>super.method()</code>, which means
|
||
the <code>method</code> property name gets compiled for the first time here, and
|
||
again when the <code>method:</code> property of the class gets compiled. Since
|
||
this compilation happens first, comments attached to <code>method:</code> would
|
||
get incorrectly output near <code>super.method()</code>, when we want them to
|
||
get output on the second pass when <code>method:</code> is output. So set them
|
||
aside during this compilation pass, and put them back on the object so
|
||
that they’re there for the later compilation.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> salvagedComments = @accessor.name.comments
|
||
<span class="hljs-keyword">delete</span> @accessor.name.comments
|
||
fragments = (<span class="hljs-keyword">new</span> Value (<span class="hljs-keyword">new</span> Literal <span class="hljs-string">'super'</span>), <span class="hljs-keyword">if</span> @accessor <span class="hljs-keyword">then</span> [ @accessor ] <span class="hljs-keyword">else</span> [])
|
||
.compileToFragments o
|
||
attachCommentsToNode salvagedComments, @accessor.name <span class="hljs-keyword">if</span> salvagedComments
|
||
fragments
|
||
|
||
checkInInstanceMethod: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
method = o.scope.namedMethod()
|
||
@error <span class="hljs-string">'cannot use super outside of an instance method'</span> <span class="hljs-keyword">unless</span> method?.isMethod
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkInInstanceMethod o
|
||
|
||
<span class="hljs-keyword">if</span> @accessor?
|
||
<span class="hljs-keyword">return</span> (
|
||
<span class="hljs-keyword">new</span> Value(
|
||
<span class="hljs-keyword">new</span> Super().withLocationDataFrom (@superLiteral ? @)
|
||
[@accessor]
|
||
).withLocationDataFrom @
|
||
).ast o
|
||
|
||
super o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-150">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-150">§</a>
|
||
</div>
|
||
<h3 id="regexwithinterpolations">RegexWithInterpolations</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-151">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-151">§</a>
|
||
</div>
|
||
<p>Regexes with interpolations are in fact just a variation of a <code>Call</code> (a
|
||
<code>RegExp()</code> call to be precise) with a <code>StringWithInterpolations</code> inside.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.RegexWithInterpolations = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegexWithInterpolations</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@call, {@heregexCommentTokens = []} = {})</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'call'</span>]
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@call.compileNode o
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'InterpolatedRegExpLiteral'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
interpolatedPattern: @call.args[<span class="hljs-number">0</span>].ast o
|
||
flags: @call.args[<span class="hljs-number">1</span>]?.unwrap().originalValue ? <span class="hljs-string">''</span>
|
||
comments:
|
||
<span class="hljs-keyword">for</span> heregexCommentToken <span class="hljs-keyword">in</span> @heregexCommentTokens
|
||
<span class="hljs-keyword">if</span> heregexCommentToken.here
|
||
<span class="hljs-keyword">new</span> HereComment(heregexCommentToken).ast o
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">new</span> LineComment(heregexCommentToken).ast o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-152">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-152">§</a>
|
||
</div>
|
||
<h3 id="taggedtemplatecall">TaggedTemplateCall</h3>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>
|
||
<span class="hljs-built_in">exports</span>.TaggedTemplateCall = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TaggedTemplateCall</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Call</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(variable, arg, soak)</span> -></span>
|
||
arg = StringWithInterpolations.fromStringLiteral arg <span class="hljs-keyword">if</span> arg <span class="hljs-keyword">instanceof</span> StringLiteral
|
||
super variable, [ arg ], soak
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@variable.compileToFragments(o, LEVEL_ACCESS).concat @args[<span class="hljs-number">0</span>].compileToFragments(o, LEVEL_LIST)
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'TaggedTemplateExpression'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
tag: @variable.ast o, LEVEL_ACCESS
|
||
quasi: @args[<span class="hljs-number">0</span>].ast o, LEVEL_LIST</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-153">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-153">§</a>
|
||
</div>
|
||
<h3 id="extends">Extends</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-154">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-154">§</a>
|
||
</div>
|
||
<p>Node to extend an object’s prototype with an ancestor object.
|
||
After <code>goog.inherits</code> from the
|
||
<a href="https://github.com/google/closure-library/blob/master/closure/goog/base.js">Closure Library</a>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Extends = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Extends</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@child, @parent)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'child'</span>, <span class="hljs-string">'parent'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-155">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-155">§</a>
|
||
</div>
|
||
<p>Hooks one constructor into another’s prototype chain.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileToFragments: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">new</span> Call(<span class="hljs-keyword">new</span> Value(<span class="hljs-keyword">new</span> Literal utility <span class="hljs-string">'extend'</span>, o), [@child, @parent]).compileToFragments o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-156">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-156">§</a>
|
||
</div>
|
||
<h3 id="access">Access</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-157">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-157">§</a>
|
||
</div>
|
||
<p>A <code>.</code> access into a property of a value, or the <code>::</code> shorthand for
|
||
an access into the object’s prototype.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Access = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Access</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@name, {@soak, @shorthand} = {})</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'name'</span>]
|
||
|
||
compileToFragments: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
name = @name.compileToFragments o
|
||
node = @name.unwrap()
|
||
<span class="hljs-keyword">if</span> node <span class="hljs-keyword">instanceof</span> PropertyName
|
||
[@makeCode(<span class="hljs-string">'.'</span>), name...]
|
||
<span class="hljs-keyword">else</span>
|
||
[@makeCode(<span class="hljs-string">'['</span>), name..., @makeCode(<span class="hljs-string">']'</span>)]
|
||
|
||
shouldCache: NO
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-158">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-158">§</a>
|
||
</div>
|
||
<p>Babel doesn’t have an AST node for <code>Access</code>, but rather just includes
|
||
this Access node’s child <code>name</code> Identifier node as the <code>property</code> of
|
||
the <code>MemberExpression</code> node.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @name.ast o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-159">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-159">§</a>
|
||
</div>
|
||
<h3 id="index">Index</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-160">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-160">§</a>
|
||
</div>
|
||
<p>A <code>[ ... ]</code> indexed access into an array or object.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Index = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Index</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@index)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'index'</span>]
|
||
|
||
compileToFragments: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[].concat @makeCode(<span class="hljs-string">"["</span>), @index.compileToFragments(o, LEVEL_PAREN), @makeCode(<span class="hljs-string">"]"</span>)
|
||
|
||
shouldCache: <span class="hljs-function">-></span>
|
||
@index.shouldCache()
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-161">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-161">§</a>
|
||
</div>
|
||
<p>Babel doesn’t have an AST node for <code>Index</code>, but rather just includes
|
||
this Index node’s child <code>index</code> Identifier node as the <code>property</code> of
|
||
the <code>MemberExpression</code> node. The fact that the <code>MemberExpression</code>’s
|
||
<code>property</code> is an Index means that <code>computed</code> is <code>true</code> for the
|
||
<code>MemberExpression</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @index.ast o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-162">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-162">§</a>
|
||
</div>
|
||
<h3 id="range">Range</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-163">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-163">§</a>
|
||
</div>
|
||
<p>A range literal. Ranges can be used to extract portions (slices) of arrays,
|
||
to specify a range for comprehensions, or as a value, to be expanded into the
|
||
corresponding array of integers at runtime.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Range = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Range</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
|
||
children: [<span class="hljs-string">'from'</span>, <span class="hljs-string">'to'</span>]
|
||
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@from, @to, tag)</span> -></span>
|
||
super()
|
||
|
||
@exclusive = tag <span class="hljs-keyword">is</span> <span class="hljs-string">'exclusive'</span>
|
||
@equals = <span class="hljs-keyword">if</span> @exclusive <span class="hljs-keyword">then</span> <span class="hljs-string">''</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'='</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-164">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-164">§</a>
|
||
</div>
|
||
<p>Compiles the range’s source variables – where it starts and where it ends.
|
||
But only if they need to be cached to avoid double evaluation.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileVariables: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o = merge o, top: <span class="hljs-literal">true</span>
|
||
shouldCache = del o, <span class="hljs-string">'shouldCache'</span>
|
||
[@fromC, @fromVar] = @cacheToCodeFragments @from.cache o, LEVEL_LIST, shouldCache
|
||
[@toC, @toVar] = @cacheToCodeFragments @to.cache o, LEVEL_LIST, shouldCache
|
||
[@step, @stepVar] = @cacheToCodeFragments step.cache o, LEVEL_LIST, shouldCache <span class="hljs-keyword">if</span> step = del o, <span class="hljs-string">'step'</span>
|
||
@fromNum = <span class="hljs-keyword">if</span> @from.isNumber() <span class="hljs-keyword">then</span> parseNumber @fromVar <span class="hljs-keyword">else</span> <span class="hljs-literal">null</span>
|
||
@toNum = <span class="hljs-keyword">if</span> @to.isNumber() <span class="hljs-keyword">then</span> parseNumber @toVar <span class="hljs-keyword">else</span> <span class="hljs-literal">null</span>
|
||
@stepNum = <span class="hljs-keyword">if</span> step?.isNumber() <span class="hljs-keyword">then</span> parseNumber @stepVar <span class="hljs-keyword">else</span> <span class="hljs-literal">null</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-165">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-165">§</a>
|
||
</div>
|
||
<p>When compiled normally, the range returns the contents of the <em>for loop</em>
|
||
needed to iterate over the values in the range. Used by comprehensions.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@compileVariables o <span class="hljs-keyword">unless</span> @fromVar
|
||
<span class="hljs-keyword">return</span> @compileArray(o) <span class="hljs-keyword">unless</span> o.index</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-166">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-166">§</a>
|
||
</div>
|
||
<p>Set up endpoints.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> known = @fromNum? <span class="hljs-keyword">and</span> @toNum?
|
||
idx = del o, <span class="hljs-string">'index'</span>
|
||
idxName = del o, <span class="hljs-string">'name'</span>
|
||
namedIndex = idxName <span class="hljs-keyword">and</span> idxName <span class="hljs-keyword">isnt</span> idx
|
||
varPart =
|
||
<span class="hljs-keyword">if</span> known <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> namedIndex
|
||
<span class="hljs-string">"var <span class="hljs-subst">#{idx}</span> = <span class="hljs-subst">#{@fromC}</span>"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">"<span class="hljs-subst">#{idx}</span> = <span class="hljs-subst">#{@fromC}</span>"</span>
|
||
varPart += <span class="hljs-string">", <span class="hljs-subst">#{@toC}</span>"</span> <span class="hljs-keyword">if</span> @toC <span class="hljs-keyword">isnt</span> @toVar
|
||
varPart += <span class="hljs-string">", <span class="hljs-subst">#{@step}</span>"</span> <span class="hljs-keyword">if</span> @step <span class="hljs-keyword">isnt</span> @stepVar
|
||
[lt, gt] = [<span class="hljs-string">"<span class="hljs-subst">#{idx}</span> <<span class="hljs-subst">#{@equals}</span>"</span>, <span class="hljs-string">"<span class="hljs-subst">#{idx}</span> ><span class="hljs-subst">#{@equals}</span>"</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-167">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-167">§</a>
|
||
</div>
|
||
<p>Generate the condition.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> [<span class="hljs-keyword">from</span>, to] = [@fromNum, @toNum]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-168">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-168">§</a>
|
||
</div>
|
||
<p>Always check if the <code>step</code> isn’t zero to avoid the infinite loop.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> stepNotZero = <span class="hljs-string">"<span class="hljs-subst">#{ @stepNum ? @stepVar }</span> !== 0"</span>
|
||
stepCond = <span class="hljs-string">"<span class="hljs-subst">#{ @stepNum ? @stepVar }</span> > 0"</span>
|
||
lowerBound = <span class="hljs-string">"<span class="hljs-subst">#{lt}</span> <span class="hljs-subst">#{ <span class="hljs-keyword">if</span> known <span class="hljs-keyword">then</span> to <span class="hljs-keyword">else</span> @toVar }</span>"</span>
|
||
upperBound = <span class="hljs-string">"<span class="hljs-subst">#{gt}</span> <span class="hljs-subst">#{ <span class="hljs-keyword">if</span> known <span class="hljs-keyword">then</span> to <span class="hljs-keyword">else</span> @toVar }</span>"</span>
|
||
condPart =
|
||
<span class="hljs-keyword">if</span> @step?
|
||
<span class="hljs-keyword">if</span> @stepNum? <span class="hljs-keyword">and</span> @stepNum <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">if</span> @stepNum > <span class="hljs-number">0</span> <span class="hljs-keyword">then</span> <span class="hljs-string">"<span class="hljs-subst">#{lowerBound}</span>"</span> <span class="hljs-keyword">else</span> <span class="hljs-string">"<span class="hljs-subst">#{upperBound}</span>"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">"<span class="hljs-subst">#{stepNotZero}</span> && (<span class="hljs-subst">#{stepCond}</span> ? <span class="hljs-subst">#{lowerBound}</span> : <span class="hljs-subst">#{upperBound}</span>)"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> known
|
||
<span class="hljs-string">"<span class="hljs-subst">#{ <span class="hljs-keyword">if</span> <span class="hljs-keyword">from</span> <= to <span class="hljs-keyword">then</span> lt <span class="hljs-keyword">else</span> gt }</span> <span class="hljs-subst">#{to}</span>"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">"(<span class="hljs-subst">#{@fromVar}</span> <= <span class="hljs-subst">#{@toVar}</span> ? <span class="hljs-subst">#{lowerBound}</span> : <span class="hljs-subst">#{upperBound}</span>)"</span>
|
||
|
||
cond = <span class="hljs-keyword">if</span> @stepVar <span class="hljs-keyword">then</span> <span class="hljs-string">"<span class="hljs-subst">#{@stepVar}</span> > 0"</span> <span class="hljs-keyword">else</span> <span class="hljs-string">"<span class="hljs-subst">#{@fromVar}</span> <= <span class="hljs-subst">#{@toVar}</span>"</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-169">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-169">§</a>
|
||
</div>
|
||
<p>Generate the step.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> stepPart = <span class="hljs-keyword">if</span> @stepVar
|
||
<span class="hljs-string">"<span class="hljs-subst">#{idx}</span> += <span class="hljs-subst">#{@stepVar}</span>"</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> known
|
||
<span class="hljs-keyword">if</span> namedIndex
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">from</span> <= to <span class="hljs-keyword">then</span> <span class="hljs-string">"++<span class="hljs-subst">#{idx}</span>"</span> <span class="hljs-keyword">else</span> <span class="hljs-string">"--<span class="hljs-subst">#{idx}</span>"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">from</span> <= to <span class="hljs-keyword">then</span> <span class="hljs-string">"<span class="hljs-subst">#{idx}</span>++"</span> <span class="hljs-keyword">else</span> <span class="hljs-string">"<span class="hljs-subst">#{idx}</span>--"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> namedIndex
|
||
<span class="hljs-string">"<span class="hljs-subst">#{cond}</span> ? ++<span class="hljs-subst">#{idx}</span> : --<span class="hljs-subst">#{idx}</span>"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">"<span class="hljs-subst">#{cond}</span> ? <span class="hljs-subst">#{idx}</span>++ : <span class="hljs-subst">#{idx}</span>--"</span>
|
||
|
||
varPart = <span class="hljs-string">"<span class="hljs-subst">#{idxName}</span> = <span class="hljs-subst">#{varPart}</span>"</span> <span class="hljs-keyword">if</span> namedIndex
|
||
stepPart = <span class="hljs-string">"<span class="hljs-subst">#{idxName}</span> = <span class="hljs-subst">#{stepPart}</span>"</span> <span class="hljs-keyword">if</span> namedIndex</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-170">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-170">§</a>
|
||
</div>
|
||
<p>The final loop body.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> [@makeCode <span class="hljs-string">"<span class="hljs-subst">#{varPart}</span>; <span class="hljs-subst">#{condPart}</span>; <span class="hljs-subst">#{stepPart}</span>"</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-171">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-171">§</a>
|
||
</div>
|
||
<p>When used as a value, expand the range into the equivalent array.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileArray: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
known = @fromNum? <span class="hljs-keyword">and</span> @toNum?
|
||
<span class="hljs-keyword">if</span> known <span class="hljs-keyword">and</span> <span class="hljs-built_in">Math</span>.abs(@fromNum - @toNum) <= <span class="hljs-number">20</span>
|
||
range = [@fromNum..@toNum]
|
||
range.pop() <span class="hljs-keyword">if</span> @exclusive
|
||
<span class="hljs-keyword">return</span> [@makeCode <span class="hljs-string">"[<span class="hljs-subst">#{ range.join(<span class="hljs-string">', '</span>) }</span>]"</span>]
|
||
idt = @tab + TAB
|
||
i = o.scope.freeVariable <span class="hljs-string">'i'</span>, single: <span class="hljs-literal">true</span>, reserve: <span class="hljs-literal">no</span>
|
||
result = o.scope.freeVariable <span class="hljs-string">'results'</span>, reserve: <span class="hljs-literal">no</span>
|
||
pre = <span class="hljs-string">"\n<span class="hljs-subst">#{idt}</span>var <span class="hljs-subst">#{result}</span> = [];"</span>
|
||
<span class="hljs-keyword">if</span> known
|
||
o.index = i
|
||
body = fragmentsToText @compileNode o
|
||
<span class="hljs-keyword">else</span>
|
||
vars = <span class="hljs-string">"<span class="hljs-subst">#{i}</span> = <span class="hljs-subst">#{@fromC}</span>"</span> + <span class="hljs-keyword">if</span> @toC <span class="hljs-keyword">isnt</span> @toVar <span class="hljs-keyword">then</span> <span class="hljs-string">", <span class="hljs-subst">#{@toC}</span>"</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>
|
||
cond = <span class="hljs-string">"<span class="hljs-subst">#{@fromVar}</span> <= <span class="hljs-subst">#{@toVar}</span>"</span>
|
||
body = <span class="hljs-string">"var <span class="hljs-subst">#{vars}</span>; <span class="hljs-subst">#{cond}</span> ? <span class="hljs-subst">#{i}</span> <<span class="hljs-subst">#{@equals}</span> <span class="hljs-subst">#{@toVar}</span> : <span class="hljs-subst">#{i}</span> ><span class="hljs-subst">#{@equals}</span> <span class="hljs-subst">#{@toVar}</span>; <span class="hljs-subst">#{cond}</span> ? <span class="hljs-subst">#{i}</span>++ : <span class="hljs-subst">#{i}</span>--"</span>
|
||
post = <span class="hljs-string">"{ <span class="hljs-subst">#{result}</span>.push(<span class="hljs-subst">#{i}</span>); }\n<span class="hljs-subst">#{idt}</span>return <span class="hljs-subst">#{result}</span>;\n<span class="hljs-subst">#{o.indent}</span>"</span>
|
||
<span class="hljs-function"> <span class="hljs-title">hasArgs</span> = <span class="hljs-params">(node)</span> -></span> node?.contains isLiteralArguments
|
||
args = <span class="hljs-string">', arguments'</span> <span class="hljs-keyword">if</span> hasArgs(@from) <span class="hljs-keyword">or</span> hasArgs(@to)
|
||
[@makeCode <span class="hljs-string">"(function() {<span class="hljs-subst">#{pre}</span>\n<span class="hljs-subst">#{idt}</span>for (<span class="hljs-subst">#{body}</span>)<span class="hljs-subst">#{post}</span>}).apply(this<span class="hljs-subst">#{args ? <span class="hljs-string">''</span>}</span>)"</span>]
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> {
|
||
from: @from?.ast(o) ? <span class="hljs-literal">null</span>
|
||
to: @to?.ast(o) ? <span class="hljs-literal">null</span>
|
||
@exclusive
|
||
}</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-172">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-172">§</a>
|
||
</div>
|
||
<h3 id="slice">Slice</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-173">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-173">§</a>
|
||
</div>
|
||
<p>An array slice literal. Unlike JavaScript’s <code>Array#slice</code>, the second parameter
|
||
specifies the index of the end of the slice, just as the first parameter
|
||
is the index of the beginning.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Slice = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Slice</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
|
||
children: [<span class="hljs-string">'range'</span>]
|
||
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@range)</span> -></span>
|
||
super()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-174">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-174">§</a>
|
||
</div>
|
||
<p>We have to be careful when trying to slice through the end of the array,
|
||
<code>9e9</code> is used because not all implementations respect <code>undefined</code> or <code>1/0</code>.
|
||
<code>9e9</code> should be safe because <code>9e9</code> > <code>2**32</code>, the max array length.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
{to, <span class="hljs-keyword">from</span>} = @range</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-175">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-175">§</a>
|
||
</div>
|
||
<p>Handle an expression in the property access, e.g. <code>a[!b in c..]</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> <span class="hljs-keyword">from</span>?.shouldCache()
|
||
<span class="hljs-keyword">from</span> = <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Parens <span class="hljs-keyword">from</span>
|
||
<span class="hljs-keyword">if</span> to?.shouldCache()
|
||
to = <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Parens to
|
||
fromCompiled = <span class="hljs-keyword">from</span>?.compileToFragments(o, LEVEL_PAREN) <span class="hljs-keyword">or</span> [@makeCode <span class="hljs-string">'0'</span>]
|
||
<span class="hljs-keyword">if</span> to
|
||
compiled = to.compileToFragments o, LEVEL_PAREN
|
||
compiledText = fragmentsToText compiled
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> (<span class="hljs-keyword">not</span> @range.exclusive <span class="hljs-keyword">and</span> +compiledText <span class="hljs-keyword">is</span> <span class="hljs-number">-1</span>)
|
||
toStr = <span class="hljs-string">', '</span> + <span class="hljs-keyword">if</span> @range.exclusive
|
||
compiledText
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> to.isNumber()
|
||
<span class="hljs-string">"<span class="hljs-subst">#{+compiledText + <span class="hljs-number">1</span>}</span>"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
compiled = to.compileToFragments o, LEVEL_ACCESS
|
||
<span class="hljs-string">"+<span class="hljs-subst">#{fragmentsToText compiled}</span> + 1 || 9e9"</span>
|
||
[@makeCode <span class="hljs-string">".slice(<span class="hljs-subst">#{ fragmentsToText fromCompiled }</span><span class="hljs-subst">#{ toStr <span class="hljs-keyword">or</span> <span class="hljs-string">''</span> }</span>)"</span>]
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@range.ast o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-176">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-176">§</a>
|
||
</div>
|
||
<h3 id="obj">Obj</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-177">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-177">§</a>
|
||
</div>
|
||
<p>An object literal, nothing fancy.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Obj = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Obj</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(props, @generated = <span class="hljs-literal">no</span>)</span> -></span>
|
||
super()
|
||
|
||
@objects = @properties = props <span class="hljs-keyword">or</span> []
|
||
|
||
children: [<span class="hljs-string">'properties'</span>]
|
||
|
||
isAssignable: <span class="hljs-function"><span class="hljs-params">(opts)</span> -></span>
|
||
<span class="hljs-keyword">for</span> prop <span class="hljs-keyword">in</span> @properties</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-178">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-178">§</a>
|
||
</div>
|
||
<p>Check for reserved words.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> message = isUnassignable prop.unwrapAll().value
|
||
prop.error message <span class="hljs-keyword">if</span> message
|
||
|
||
prop = prop.value <span class="hljs-keyword">if</span> prop <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span>
|
||
prop.context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span> <span class="hljs-keyword">and</span>
|
||
prop.value?.base <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> Arr
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> prop.isAssignable opts
|
||
<span class="hljs-literal">yes</span>
|
||
|
||
shouldCache: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">not</span> @isAssignable()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-179">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-179">§</a>
|
||
</div>
|
||
<p>Check if object contains splat.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> hasSplat: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">for</span> prop <span class="hljs-keyword">in</span> @properties <span class="hljs-keyword">when</span> prop <span class="hljs-keyword">instanceof</span> Splat
|
||
<span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-180">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-180">§</a>
|
||
</div>
|
||
<p>Move rest property to the end of the list.
|
||
<code>{a, rest..., b} = obj</code> -> <code>{a, b, rest...} = obj</code>
|
||
<code>foo = ({a, rest..., b}) -></code> -> <code>foo = {a, b, rest...}) -></code></p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> reorderProperties: <span class="hljs-function">-></span>
|
||
props = @properties
|
||
splatProps = @getAndCheckSplatProps()
|
||
splatProp = props.splice splatProps[<span class="hljs-number">0</span>], <span class="hljs-number">1</span>
|
||
@objects = @properties = [].concat props, splatProp
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@reorderProperties() <span class="hljs-keyword">if</span> @hasSplat() <span class="hljs-keyword">and</span> @lhs
|
||
props = @properties
|
||
<span class="hljs-keyword">if</span> @generated
|
||
<span class="hljs-keyword">for</span> node <span class="hljs-keyword">in</span> props <span class="hljs-keyword">when</span> node <span class="hljs-keyword">instanceof</span> Value
|
||
node.error <span class="hljs-string">'cannot have an implicit value in an implicit object'</span>
|
||
|
||
idt = o.indent += TAB
|
||
lastNode = @lastNode @properties</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-181">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-181">§</a>
|
||
</div>
|
||
<p>If this object is the left-hand side of an assignment, all its children
|
||
are too.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @propagateLhs()
|
||
|
||
isCompact = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">for</span> prop <span class="hljs-keyword">in</span> @properties
|
||
<span class="hljs-keyword">if</span> prop <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span> prop.context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span>
|
||
isCompact = <span class="hljs-literal">no</span>
|
||
|
||
answer = []
|
||
answer.push @makeCode <span class="hljs-keyword">if</span> isCompact <span class="hljs-keyword">then</span> <span class="hljs-string">''</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'\n'</span>
|
||
<span class="hljs-keyword">for</span> prop, i <span class="hljs-keyword">in</span> props
|
||
join = <span class="hljs-keyword">if</span> i <span class="hljs-keyword">is</span> props.length - <span class="hljs-number">1</span>
|
||
<span class="hljs-string">''</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> isCompact
|
||
<span class="hljs-string">', '</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> prop <span class="hljs-keyword">is</span> lastNode
|
||
<span class="hljs-string">'\n'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">',\n'</span>
|
||
indent = <span class="hljs-keyword">if</span> isCompact <span class="hljs-keyword">then</span> <span class="hljs-string">''</span> <span class="hljs-keyword">else</span> idt
|
||
|
||
key = <span class="hljs-keyword">if</span> prop <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span> prop.context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span>
|
||
prop.variable
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> prop <span class="hljs-keyword">instanceof</span> Assign
|
||
prop.operatorToken.error <span class="hljs-string">"unexpected <span class="hljs-subst">#{prop.operatorToken.value}</span>"</span> <span class="hljs-keyword">unless</span> @lhs
|
||
prop.variable
|
||
<span class="hljs-keyword">else</span>
|
||
prop
|
||
<span class="hljs-keyword">if</span> key <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> key.hasProperties()
|
||
key.error <span class="hljs-string">'invalid object key'</span> <span class="hljs-keyword">if</span> prop.context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> key.this
|
||
key = key.properties[<span class="hljs-number">0</span>].name
|
||
prop = <span class="hljs-keyword">new</span> Assign key, prop, <span class="hljs-string">'object'</span>
|
||
<span class="hljs-keyword">if</span> key <span class="hljs-keyword">is</span> prop
|
||
<span class="hljs-keyword">if</span> prop.shouldCache()
|
||
[key, value] = prop.base.cache o
|
||
key = <span class="hljs-keyword">new</span> PropertyName key.value <span class="hljs-keyword">if</span> key <span class="hljs-keyword">instanceof</span> IdentifierLiteral
|
||
prop = <span class="hljs-keyword">new</span> Assign key, value, <span class="hljs-string">'object'</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> key <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> key.base <span class="hljs-keyword">instanceof</span> ComputedPropertyName</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-182">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-182">§</a>
|
||
</div>
|
||
<p><code>{ [foo()] }</code> output as <code>{ [ref = foo()]: ref }</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> prop.base.value.shouldCache()
|
||
[key, value] = prop.base.value.cache o
|
||
key = <span class="hljs-keyword">new</span> ComputedPropertyName key.value <span class="hljs-keyword">if</span> key <span class="hljs-keyword">instanceof</span> IdentifierLiteral
|
||
prop = <span class="hljs-keyword">new</span> Assign key, value, <span class="hljs-string">'object'</span>
|
||
<span class="hljs-keyword">else</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-183">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-183">§</a>
|
||
</div>
|
||
<p><code>{ [expression] }</code> output as <code>{ [expression]: expression }</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> prop = <span class="hljs-keyword">new</span> Assign key, prop.base.value, <span class="hljs-string">'object'</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> prop.bareLiteral?(IdentifierLiteral) <span class="hljs-keyword">and</span> prop <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> Splat
|
||
prop = <span class="hljs-keyword">new</span> Assign prop, prop, <span class="hljs-string">'object'</span>
|
||
<span class="hljs-keyword">if</span> indent <span class="hljs-keyword">then</span> answer.push @makeCode indent
|
||
answer.push prop.compileToFragments(o, LEVEL_TOP)...
|
||
<span class="hljs-keyword">if</span> join <span class="hljs-keyword">then</span> answer.push @makeCode join
|
||
answer.push @makeCode <span class="hljs-keyword">if</span> isCompact <span class="hljs-keyword">then</span> <span class="hljs-string">''</span> <span class="hljs-keyword">else</span> <span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>"</span>
|
||
answer = @wrapInBraces answer
|
||
<span class="hljs-keyword">if</span> @front <span class="hljs-keyword">then</span> @wrapInParentheses answer <span class="hljs-keyword">else</span> answer
|
||
|
||
getAndCheckSplatProps: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> @hasSplat() <span class="hljs-keyword">and</span> @lhs
|
||
props = @properties
|
||
splatProps = (i <span class="hljs-keyword">for</span> prop, i <span class="hljs-keyword">in</span> props <span class="hljs-keyword">when</span> prop <span class="hljs-keyword">instanceof</span> Splat)
|
||
props[splatProps[<span class="hljs-number">1</span>]].error <span class="hljs-string">"multiple spread elements are disallowed"</span> <span class="hljs-keyword">if</span> splatProps?.length > <span class="hljs-number">1</span>
|
||
splatProps
|
||
|
||
assigns: <span class="hljs-function"><span class="hljs-params">(name)</span> -></span>
|
||
<span class="hljs-keyword">for</span> prop <span class="hljs-keyword">in</span> @properties <span class="hljs-keyword">when</span> prop.assigns name <span class="hljs-keyword">then</span> <span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span>
|
||
<span class="hljs-literal">no</span>
|
||
|
||
eachName: <span class="hljs-function"><span class="hljs-params">(iterator)</span> -></span>
|
||
<span class="hljs-keyword">for</span> prop <span class="hljs-keyword">in</span> @properties
|
||
prop = prop.value <span class="hljs-keyword">if</span> prop <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span> prop.context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span>
|
||
prop = prop.unwrapAll()
|
||
prop.eachName iterator <span class="hljs-keyword">if</span> prop.eachName?</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-184">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-184">§</a>
|
||
</div>
|
||
<p>Convert “bare” properties to <code>ObjectProperty</code>s (or <code>Splat</code>s).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> expandProperty: <span class="hljs-function"><span class="hljs-params">(property)</span> -></span>
|
||
{variable, context, operatorToken} = property
|
||
key = <span class="hljs-keyword">if</span> property <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span> context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span>
|
||
variable
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> property <span class="hljs-keyword">instanceof</span> Assign
|
||
operatorToken.error <span class="hljs-string">"unexpected <span class="hljs-subst">#{operatorToken.value}</span>"</span> <span class="hljs-keyword">unless</span> @lhs
|
||
variable
|
||
<span class="hljs-keyword">else</span>
|
||
property
|
||
<span class="hljs-keyword">if</span> key <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> key.hasProperties()
|
||
key.error <span class="hljs-string">'invalid object key'</span> <span class="hljs-keyword">unless</span> context <span class="hljs-keyword">isnt</span> <span class="hljs-string">'object'</span> <span class="hljs-keyword">and</span> key.this
|
||
<span class="hljs-keyword">if</span> property <span class="hljs-keyword">instanceof</span> Assign
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ObjectProperty fromAssign: property
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ObjectProperty key: property
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ObjectProperty(fromAssign: property) <span class="hljs-keyword">unless</span> key <span class="hljs-keyword">is</span> property
|
||
<span class="hljs-keyword">return</span> property <span class="hljs-keyword">if</span> property <span class="hljs-keyword">instanceof</span> Splat
|
||
|
||
<span class="hljs-keyword">new</span> ObjectProperty key: property
|
||
|
||
expandProperties: <span class="hljs-function">-></span>
|
||
@expandProperty(property) <span class="hljs-keyword">for</span> property <span class="hljs-keyword">in</span> @properties
|
||
|
||
propagateLhs: <span class="hljs-function"><span class="hljs-params">(setLhs)</span> -></span>
|
||
@lhs = <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> setLhs
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> @lhs
|
||
|
||
<span class="hljs-keyword">for</span> property <span class="hljs-keyword">in</span> @properties
|
||
<span class="hljs-keyword">if</span> property <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span> property.context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span>
|
||
{value} = property
|
||
unwrappedValue = value.unwrapAll()
|
||
<span class="hljs-keyword">if</span> unwrappedValue <span class="hljs-keyword">instanceof</span> Arr <span class="hljs-keyword">or</span> unwrappedValue <span class="hljs-keyword">instanceof</span> Obj
|
||
unwrappedValue.propagateLhs <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> unwrappedValue <span class="hljs-keyword">instanceof</span> Assign
|
||
unwrappedValue.nestedLhs = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> property <span class="hljs-keyword">instanceof</span> Assign</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-185">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-185">§</a>
|
||
</div>
|
||
<p>Shorthand property with default, e.g. <code>{a = 1} = b</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> property.nestedLhs = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> property <span class="hljs-keyword">instanceof</span> Splat
|
||
property.propagateLhs <span class="hljs-literal">yes</span>
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@getAndCheckSplatProps()
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @lhs
|
||
<span class="hljs-string">'ObjectPattern'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'ObjectExpression'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
implicit: !!@generated
|
||
properties:
|
||
property.ast(o) <span class="hljs-keyword">for</span> property <span class="hljs-keyword">in</span> @expandProperties()
|
||
|
||
<span class="hljs-built_in">exports</span>.ObjectProperty = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ObjectProperty</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">({key, fromAssign})</span> -></span>
|
||
super()
|
||
<span class="hljs-keyword">if</span> fromAssign
|
||
{variable: @key, value, context} = fromAssign
|
||
<span class="hljs-keyword">if</span> context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-186">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-186">§</a>
|
||
</div>
|
||
<p>All non-shorthand properties (i.e. includes <code>:</code>).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @value = value
|
||
<span class="hljs-keyword">else</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-187">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-187">§</a>
|
||
</div>
|
||
<p>Left-hand-side shorthand with default e.g. <code>{a = 1} = b</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @value = fromAssign
|
||
@shorthand = <span class="hljs-literal">yes</span>
|
||
@locationData = fromAssign.locationData
|
||
<span class="hljs-keyword">else</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-188">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-188">§</a>
|
||
</div>
|
||
<p>Shorthand without default e.g. <code>{a}</code> or <code>{@a}</code> or <code>{[a]}</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @key = key
|
||
@shorthand = <span class="hljs-literal">yes</span>
|
||
@locationData = key.locationData
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
isComputedPropertyName = (@key <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> @key.base <span class="hljs-keyword">instanceof</span> ComputedPropertyName) <span class="hljs-keyword">or</span> @key.unwrap() <span class="hljs-keyword">instanceof</span> StringWithInterpolations
|
||
keyAst = @key.ast o, LEVEL_LIST
|
||
|
||
<span class="hljs-keyword">return</span>
|
||
key:
|
||
<span class="hljs-keyword">if</span> keyAst?.declaration
|
||
<span class="hljs-built_in">Object</span>.assign {}, keyAst, declaration: <span class="hljs-literal">no</span>
|
||
<span class="hljs-keyword">else</span>
|
||
keyAst
|
||
value: @value?.ast(o, LEVEL_LIST) ? keyAst
|
||
shorthand: !!@shorthand
|
||
computed: !!isComputedPropertyName
|
||
method: <span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-189">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-189">§</a>
|
||
</div>
|
||
<h3 id="arr">Arr</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-190">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-190">§</a>
|
||
</div>
|
||
<p>An array literal.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Arr = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Arr</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(objs, @lhs = <span class="hljs-literal">no</span>)</span> -></span>
|
||
super()
|
||
@objects = objs <span class="hljs-keyword">or</span> []
|
||
@propagateLhs()
|
||
|
||
children: [<span class="hljs-string">'objects'</span>]
|
||
|
||
hasElision: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">for</span> obj <span class="hljs-keyword">in</span> @objects <span class="hljs-keyword">when</span> obj <span class="hljs-keyword">instanceof</span> Elision
|
||
<span class="hljs-literal">no</span>
|
||
|
||
isAssignable: <span class="hljs-function"><span class="hljs-params">(opts)</span> -></span>
|
||
{allowExpansion, allowNontrailingSplat, allowEmptyArray = <span class="hljs-literal">no</span>} = opts ? {}
|
||
<span class="hljs-keyword">return</span> allowEmptyArray <span class="hljs-keyword">unless</span> @objects.length
|
||
|
||
<span class="hljs-keyword">for</span> obj, i <span class="hljs-keyword">in</span> @objects
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> allowNontrailingSplat <span class="hljs-keyword">and</span> obj <span class="hljs-keyword">instanceof</span> Splat <span class="hljs-keyword">and</span> i + <span class="hljs-number">1</span> <span class="hljs-keyword">isnt</span> @objects.length
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> (allowExpansion <span class="hljs-keyword">and</span> obj <span class="hljs-keyword">instanceof</span> Expansion) <span class="hljs-keyword">or</span> (obj.isAssignable(opts) <span class="hljs-keyword">and</span> (<span class="hljs-keyword">not</span> obj.isAtomic <span class="hljs-keyword">or</span> obj.isAtomic()))
|
||
<span class="hljs-literal">yes</span>
|
||
|
||
shouldCache: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">not</span> @isAssignable()
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> [@makeCode <span class="hljs-string">'[]'</span>] <span class="hljs-keyword">unless</span> @objects.length
|
||
o.indent += TAB
|
||
<span class="hljs-function"> <span class="hljs-title">fragmentIsElision</span> = <span class="hljs-params">([ fragment ])</span> -></span>
|
||
fragment.type <span class="hljs-keyword">is</span> <span class="hljs-string">'Elision'</span> <span class="hljs-keyword">and</span> fragment.code.trim() <span class="hljs-keyword">is</span> <span class="hljs-string">','</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-191">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-191">§</a>
|
||
</div>
|
||
<p>Detect if <code>Elision</code>s at the beginning of the array are processed (e.g. [, , , a]).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> passedElision = <span class="hljs-literal">no</span>
|
||
|
||
answer = []
|
||
<span class="hljs-keyword">for</span> obj, objIndex <span class="hljs-keyword">in</span> @objects
|
||
unwrappedObj = obj.unwrapAll()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-192">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-192">§</a>
|
||
</div>
|
||
<p>Let <code>compileCommentFragments</code> know to intersperse block comments
|
||
into the fragments created when compiling this array.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> unwrappedObj.comments <span class="hljs-keyword">and</span>
|
||
unwrappedObj.comments.filter(<span class="hljs-function"><span class="hljs-params">(comment)</span> -></span> <span class="hljs-keyword">not</span> comment.here).length <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
|
||
unwrappedObj.includeCommentFragments = YES
|
||
|
||
compiledObjs = (obj.compileToFragments o, LEVEL_LIST <span class="hljs-keyword">for</span> obj <span class="hljs-keyword">in</span> @objects)
|
||
olen = compiledObjs.length</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-193">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-193">§</a>
|
||
</div>
|
||
<p>If <code>compiledObjs</code> includes newlines, we will output this as a multiline
|
||
array (i.e. with a newline and indentation after the <code>[</code>). If an element
|
||
contains line comments, that should also trigger multiline output since
|
||
by definition line comments will introduce newlines into our output.
|
||
The exception is if only the first element has line comments; in that
|
||
case, output as the compact form if we otherwise would have, so that the
|
||
first element’s line comments get output before or after the array.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> includesLineCommentsOnNonFirstElement = <span class="hljs-literal">no</span>
|
||
<span class="hljs-keyword">for</span> fragments, index <span class="hljs-keyword">in</span> compiledObjs
|
||
<span class="hljs-keyword">for</span> fragment <span class="hljs-keyword">in</span> fragments
|
||
<span class="hljs-keyword">if</span> fragment.isHereComment
|
||
fragment.code = fragment.code.trim()
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> index <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> includesLineCommentsOnNonFirstElement <span class="hljs-keyword">is</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">and</span> hasLineComments fragment
|
||
includesLineCommentsOnNonFirstElement = <span class="hljs-literal">yes</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-194">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-194">§</a>
|
||
</div>
|
||
<p>Add ‘, ‘ if all <code>Elisions</code> from the beginning of the array are processed (e.g. [, , , a]) and
|
||
element isn’t <code>Elision</code> or last element is <code>Elision</code> (e.g. [a,,b,,])</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> index <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> passedElision <span class="hljs-keyword">and</span> (<span class="hljs-keyword">not</span> fragmentIsElision(fragments) <span class="hljs-keyword">or</span> index <span class="hljs-keyword">is</span> olen - <span class="hljs-number">1</span>)
|
||
answer.push @makeCode <span class="hljs-string">', '</span>
|
||
passedElision = passedElision <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> fragmentIsElision fragments
|
||
answer.push fragments...
|
||
<span class="hljs-keyword">if</span> includesLineCommentsOnNonFirstElement <span class="hljs-keyword">or</span> <span class="hljs-string">'\n'</span> <span class="hljs-keyword">in</span> fragmentsToText(answer)
|
||
<span class="hljs-keyword">for</span> fragment, fragmentIndex <span class="hljs-keyword">in</span> answer
|
||
<span class="hljs-keyword">if</span> fragment.isHereComment
|
||
fragment.code = <span class="hljs-string">"<span class="hljs-subst">#{multident(fragment.code, o.indent, <span class="hljs-literal">no</span>)}</span>\n<span class="hljs-subst">#{o.indent}</span>"</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> fragment.code <span class="hljs-keyword">is</span> <span class="hljs-string">', '</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> fragment?.isElision <span class="hljs-keyword">and</span> fragment.type <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> [<span class="hljs-string">'StringLiteral'</span>, <span class="hljs-string">'StringWithInterpolations'</span>]
|
||
fragment.code = <span class="hljs-string">",\n<span class="hljs-subst">#{o.indent}</span>"</span>
|
||
answer.unshift @makeCode <span class="hljs-string">"[\n<span class="hljs-subst">#{o.indent}</span>"</span>
|
||
answer.push @makeCode <span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>]"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">for</span> fragment <span class="hljs-keyword">in</span> answer <span class="hljs-keyword">when</span> fragment.isHereComment
|
||
fragment.code = <span class="hljs-string">"<span class="hljs-subst">#{fragment.code}</span> "</span>
|
||
answer.unshift @makeCode <span class="hljs-string">'['</span>
|
||
answer.push @makeCode <span class="hljs-string">']'</span>
|
||
answer
|
||
|
||
assigns: <span class="hljs-function"><span class="hljs-params">(name)</span> -></span>
|
||
<span class="hljs-keyword">for</span> obj <span class="hljs-keyword">in</span> @objects <span class="hljs-keyword">when</span> obj.assigns name <span class="hljs-keyword">then</span> <span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span>
|
||
<span class="hljs-literal">no</span>
|
||
|
||
eachName: <span class="hljs-function"><span class="hljs-params">(iterator)</span> -></span>
|
||
<span class="hljs-keyword">for</span> obj <span class="hljs-keyword">in</span> @objects
|
||
obj = obj.unwrapAll()
|
||
obj.eachName iterator</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-195">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-195">§</a>
|
||
</div>
|
||
<p>If this array is the left-hand side of an assignment, all its children
|
||
are too.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> propagateLhs: <span class="hljs-function"><span class="hljs-params">(setLhs)</span> -></span>
|
||
@lhs = <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> setLhs
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> @lhs
|
||
<span class="hljs-keyword">for</span> object <span class="hljs-keyword">in</span> @objects
|
||
object.lhs = <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> object <span class="hljs-keyword">instanceof</span> Splat <span class="hljs-keyword">or</span> object <span class="hljs-keyword">instanceof</span> Expansion
|
||
unwrappedObject = object.unwrapAll()
|
||
<span class="hljs-keyword">if</span> unwrappedObject <span class="hljs-keyword">instanceof</span> Arr <span class="hljs-keyword">or</span> unwrappedObject <span class="hljs-keyword">instanceof</span> Obj
|
||
unwrappedObject.propagateLhs <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> unwrappedObject <span class="hljs-keyword">instanceof</span> Assign
|
||
unwrappedObject.nestedLhs = <span class="hljs-literal">yes</span>
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @lhs
|
||
<span class="hljs-string">'ArrayPattern'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'ArrayExpression'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
elements:
|
||
object.ast(o, LEVEL_LIST) <span class="hljs-keyword">for</span> object <span class="hljs-keyword">in</span> @objects</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-196">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-196">§</a>
|
||
</div>
|
||
<h3 id="class">Class</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-197">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-197">§</a>
|
||
</div>
|
||
<p>The CoffeeScript class definition.
|
||
Initialize a <strong>Class</strong> with its name, an optional superclass, and a body.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>
|
||
<span class="hljs-built_in">exports</span>.Class = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Class</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
children: [<span class="hljs-string">'variable'</span>, <span class="hljs-string">'parent'</span>, <span class="hljs-string">'body'</span>]
|
||
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@variable, @parent, @body)</span> -></span>
|
||
super()
|
||
<span class="hljs-keyword">unless</span> @body?
|
||
@body = <span class="hljs-keyword">new</span> Block
|
||
@hasGeneratedBody = <span class="hljs-literal">yes</span>
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@name = @determineName()
|
||
executableBody = @walkBody o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-198">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-198">§</a>
|
||
</div>
|
||
<p>Special handling to allow <code>class expr.A extends A</code> declarations</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> parentName = @parent.base.value <span class="hljs-keyword">if</span> @parent <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @parent.hasProperties()
|
||
@hasNameClash = @name? <span class="hljs-keyword">and</span> @name <span class="hljs-keyword">is</span> parentName
|
||
|
||
node = @
|
||
|
||
<span class="hljs-keyword">if</span> executableBody <span class="hljs-keyword">or</span> @hasNameClash
|
||
node = <span class="hljs-keyword">new</span> ExecutableClassBody node, executableBody
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> @name? <span class="hljs-keyword">and</span> o.level <span class="hljs-keyword">is</span> LEVEL_TOP</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-199">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-199">§</a>
|
||
</div>
|
||
<p>Anonymous classes are only valid in expressions</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> node = <span class="hljs-keyword">new</span> Parens node
|
||
|
||
<span class="hljs-keyword">if</span> @boundMethods.length <span class="hljs-keyword">and</span> @parent
|
||
@variable ?= <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.freeVariable <span class="hljs-string">'_class'</span>
|
||
[@variable, @variableRef] = @variable.cache o <span class="hljs-keyword">unless</span> @variableRef?
|
||
|
||
<span class="hljs-keyword">if</span> @variable
|
||
node = <span class="hljs-keyword">new</span> Assign @variable, node, <span class="hljs-literal">null</span>, { @moduleDeclaration }
|
||
|
||
@compileNode = @compileClassDeclaration
|
||
<span class="hljs-keyword">try</span>
|
||
<span class="hljs-keyword">return</span> node.compileToFragments o
|
||
<span class="hljs-keyword">finally</span>
|
||
<span class="hljs-keyword">delete</span> @compileNode
|
||
|
||
compileClassDeclaration: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@ctor ?= @makeDefaultConstructor() <span class="hljs-keyword">if</span> @externalCtor <span class="hljs-keyword">or</span> @boundMethods.length
|
||
@ctor?.noReturn = <span class="hljs-literal">true</span>
|
||
|
||
@proxyBoundMethods() <span class="hljs-keyword">if</span> @boundMethods.length
|
||
|
||
o.indent += TAB
|
||
|
||
result = []
|
||
result.push @makeCode <span class="hljs-string">"class "</span>
|
||
result.push @makeCode @name <span class="hljs-keyword">if</span> @name
|
||
@compileCommentFragments o, @variable, result <span class="hljs-keyword">if</span> @variable?.comments?
|
||
result.push @makeCode <span class="hljs-string">' '</span> <span class="hljs-keyword">if</span> @name
|
||
result.push @makeCode(<span class="hljs-string">'extends '</span>), @parent.compileToFragments(o)..., @makeCode <span class="hljs-string">' '</span> <span class="hljs-keyword">if</span> @parent
|
||
|
||
result.push @makeCode <span class="hljs-string">'{'</span>
|
||
<span class="hljs-keyword">unless</span> @body.isEmpty()
|
||
@body.spaced = <span class="hljs-literal">true</span>
|
||
result.push @makeCode <span class="hljs-string">'\n'</span>
|
||
result.push @body.compileToFragments(o, LEVEL_TOP)...
|
||
result.push @makeCode <span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>"</span>
|
||
result.push @makeCode <span class="hljs-string">'}'</span>
|
||
|
||
result</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-200">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-200">§</a>
|
||
</div>
|
||
<p>Figure out the appropriate name for this class</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> determineName: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">null</span> <span class="hljs-keyword">unless</span> @variable
|
||
[..., tail] = @variable.properties
|
||
node = <span class="hljs-keyword">if</span> tail
|
||
tail <span class="hljs-keyword">instanceof</span> Access <span class="hljs-keyword">and</span> tail.name
|
||
<span class="hljs-keyword">else</span>
|
||
@variable.base
|
||
<span class="hljs-keyword">unless</span> node <span class="hljs-keyword">instanceof</span> IdentifierLiteral <span class="hljs-keyword">or</span> node <span class="hljs-keyword">instanceof</span> PropertyName
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>
|
||
name = node.value
|
||
<span class="hljs-keyword">unless</span> tail
|
||
message = isUnassignable name
|
||
@variable.error message <span class="hljs-keyword">if</span> message
|
||
<span class="hljs-keyword">if</span> name <span class="hljs-keyword">in</span> JS_FORBIDDEN <span class="hljs-keyword">then</span> <span class="hljs-string">"_<span class="hljs-subst">#{name}</span>"</span> <span class="hljs-keyword">else</span> name
|
||
|
||
walkBody: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@ctor = <span class="hljs-literal">null</span>
|
||
@boundMethods = []
|
||
executableBody = <span class="hljs-literal">null</span>
|
||
|
||
initializer = []
|
||
{ expressions } = @body
|
||
|
||
i = <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">for</span> expression <span class="hljs-keyword">in</span> expressions.slice()
|
||
<span class="hljs-keyword">if</span> expression <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> expression.isObject <span class="hljs-literal">true</span>
|
||
{ properties } = expression.base
|
||
exprs = []
|
||
end = <span class="hljs-number">0</span>
|
||
start = <span class="hljs-number">0</span>
|
||
<span class="hljs-function"> <span class="hljs-title">pushSlice</span> = -></span> exprs.push <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Obj properties[start...end], <span class="hljs-literal">true</span> <span class="hljs-keyword">if</span> end > start
|
||
|
||
<span class="hljs-keyword">while</span> assign = properties[end]
|
||
<span class="hljs-keyword">if</span> initializerExpression = @addInitializerExpression assign, o
|
||
pushSlice()
|
||
exprs.push initializerExpression
|
||
initializer.push initializerExpression
|
||
start = end + <span class="hljs-number">1</span>
|
||
end++
|
||
pushSlice()
|
||
|
||
expressions[i..i] = exprs
|
||
i += exprs.length
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> initializerExpression = @addInitializerExpression expression, o
|
||
initializer.push initializerExpression
|
||
expressions[i] = initializerExpression
|
||
i += <span class="hljs-number">1</span>
|
||
|
||
<span class="hljs-keyword">for</span> method <span class="hljs-keyword">in</span> initializer <span class="hljs-keyword">when</span> method <span class="hljs-keyword">instanceof</span> Code
|
||
<span class="hljs-keyword">if</span> method.ctor
|
||
method.error <span class="hljs-string">'Cannot define more than one constructor in a class'</span> <span class="hljs-keyword">if</span> @ctor
|
||
@ctor = method
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> method.isStatic <span class="hljs-keyword">and</span> method.bound
|
||
method.context = @name
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> method.bound
|
||
@boundMethods.push method
|
||
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> o.compiling
|
||
<span class="hljs-keyword">if</span> initializer.length <span class="hljs-keyword">isnt</span> expressions.length
|
||
@body.expressions = (expression.hoist() <span class="hljs-keyword">for</span> expression <span class="hljs-keyword">in</span> initializer)
|
||
<span class="hljs-keyword">new</span> Block expressions</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-201">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-201">§</a>
|
||
</div>
|
||
<p>Add an expression to the class initializer</p>
|
||
<p>This is the key method for determining whether an expression in a class
|
||
body should appear in the initializer or the executable body. If the given
|
||
<code>node</code> is valid in a class body the method will return a (new, modified,
|
||
or identical) node for inclusion in the class initializer, otherwise
|
||
nothing will be returned and the node will appear in the executable body.</p>
|
||
<p>At time of writing, only methods (instance and static) are valid in ES
|
||
class initializers. As new ES class features (such as class fields) reach
|
||
Stage 4, this method will need to be updated to support them. We
|
||
additionally allow <code>PassthroughLiteral</code>s (backticked expressions) in the
|
||
initializer as an escape hatch for ES features that are not implemented
|
||
(e.g. getters and setters defined via the <code>get</code> and <code>set</code> keywords as
|
||
opposed to the <code>Object.defineProperty</code> method).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> addInitializerExpression: <span class="hljs-function"><span class="hljs-params">(node, o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> node.unwrapAll() <span class="hljs-keyword">instanceof</span> PassthroughLiteral
|
||
node
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @validInitializerMethod node
|
||
@addInitializerMethod node
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> o.compiling <span class="hljs-keyword">and</span> @validClassProperty node
|
||
@addClassProperty node
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> o.compiling <span class="hljs-keyword">and</span> @validClassPrototypeProperty node
|
||
@addClassPrototypeProperty node
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-literal">null</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-202">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-202">§</a>
|
||
</div>
|
||
<p>Checks if the given node is a valid ES class initializer method.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> validInitializerMethod: <span class="hljs-function"><span class="hljs-params">(node)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> node <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span> node.value <span class="hljs-keyword">instanceof</span> Code
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> node.context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> node.variable.hasProperties()
|
||
<span class="hljs-keyword">return</span> node.variable.looksStatic(@name) <span class="hljs-keyword">and</span> (@name <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> node.value.bound)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-203">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-203">§</a>
|
||
</div>
|
||
<p>Returns a configured class initializer method</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> addInitializerMethod: <span class="hljs-function"><span class="hljs-params">(assign)</span> -></span>
|
||
{ variable, value: method, operatorToken } = assign
|
||
method.isMethod = <span class="hljs-literal">yes</span>
|
||
method.isStatic = variable.looksStatic @name
|
||
|
||
<span class="hljs-keyword">if</span> method.isStatic
|
||
method.name = variable.properties[<span class="hljs-number">0</span>]
|
||
<span class="hljs-keyword">else</span>
|
||
methodName = variable.base
|
||
method.name = <span class="hljs-keyword">new</span> (<span class="hljs-keyword">if</span> methodName.shouldCache() <span class="hljs-keyword">then</span> Index <span class="hljs-keyword">else</span> Access) methodName
|
||
method.name.updateLocationDataIfMissing methodName.locationData
|
||
isConstructor =
|
||
<span class="hljs-keyword">if</span> methodName <span class="hljs-keyword">instanceof</span> StringLiteral
|
||
methodName.originalValue <span class="hljs-keyword">is</span> <span class="hljs-string">'constructor'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
methodName.value <span class="hljs-keyword">is</span> <span class="hljs-string">'constructor'</span>
|
||
method.ctor = (<span class="hljs-keyword">if</span> @parent <span class="hljs-keyword">then</span> <span class="hljs-string">'derived'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'base'</span>) <span class="hljs-keyword">if</span> isConstructor
|
||
method.error <span class="hljs-string">'Cannot define a constructor as a bound (fat arrow) function'</span> <span class="hljs-keyword">if</span> method.bound <span class="hljs-keyword">and</span> method.ctor
|
||
|
||
method.operatorToken = operatorToken
|
||
method
|
||
|
||
validClassProperty: <span class="hljs-function"><span class="hljs-params">(node)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> node <span class="hljs-keyword">instanceof</span> Assign
|
||
<span class="hljs-keyword">return</span> node.variable.looksStatic @name
|
||
|
||
addClassProperty: <span class="hljs-function"><span class="hljs-params">(assign)</span> -></span>
|
||
{variable, value, operatorToken} = assign
|
||
{staticClassName} = variable.looksStatic @name
|
||
<span class="hljs-keyword">new</span> ClassProperty({
|
||
name: variable.properties[<span class="hljs-number">0</span>]
|
||
isStatic: <span class="hljs-literal">yes</span>
|
||
staticClassName
|
||
value
|
||
operatorToken
|
||
}).withLocationDataFrom assign
|
||
|
||
validClassPrototypeProperty: <span class="hljs-function"><span class="hljs-params">(node)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> node <span class="hljs-keyword">instanceof</span> Assign
|
||
node.context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> node.variable.hasProperties()
|
||
|
||
addClassPrototypeProperty: <span class="hljs-function"><span class="hljs-params">(assign)</span> -></span>
|
||
{variable, value} = assign
|
||
<span class="hljs-keyword">new</span> ClassPrototypeProperty({
|
||
name: variable.base
|
||
value
|
||
}).withLocationDataFrom assign
|
||
|
||
makeDefaultConstructor: <span class="hljs-function">-></span>
|
||
ctor = @addInitializerMethod <span class="hljs-keyword">new</span> Assign (<span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> PropertyName <span class="hljs-string">'constructor'</span>), <span class="hljs-keyword">new</span> Code
|
||
@body.unshift ctor
|
||
|
||
<span class="hljs-keyword">if</span> @parent
|
||
ctor.body.push <span class="hljs-keyword">new</span> SuperCall <span class="hljs-keyword">new</span> Super, [<span class="hljs-keyword">new</span> Splat <span class="hljs-keyword">new</span> IdentifierLiteral <span class="hljs-string">'arguments'</span>]
|
||
|
||
<span class="hljs-keyword">if</span> @externalCtor
|
||
applyCtor = <span class="hljs-keyword">new</span> Value @externalCtor, [ <span class="hljs-keyword">new</span> Access <span class="hljs-keyword">new</span> PropertyName <span class="hljs-string">'apply'</span> ]
|
||
applyArgs = [ <span class="hljs-keyword">new</span> ThisLiteral, <span class="hljs-keyword">new</span> IdentifierLiteral <span class="hljs-string">'arguments'</span> ]
|
||
ctor.body.push <span class="hljs-keyword">new</span> Call applyCtor, applyArgs
|
||
ctor.body.makeReturn()
|
||
|
||
ctor
|
||
|
||
proxyBoundMethods: <span class="hljs-function">-></span>
|
||
@ctor.thisAssignments = <span class="hljs-keyword">for</span> method <span class="hljs-keyword">in</span> @boundMethods
|
||
method.classVariable = @variableRef <span class="hljs-keyword">if</span> @parent
|
||
|
||
name = <span class="hljs-keyword">new</span> Value(<span class="hljs-keyword">new</span> ThisLiteral, [ method.name ])
|
||
<span class="hljs-keyword">new</span> Assign name, <span class="hljs-keyword">new</span> Call(<span class="hljs-keyword">new</span> Value(name, [<span class="hljs-keyword">new</span> Access <span class="hljs-keyword">new</span> PropertyName <span class="hljs-string">'bind'</span>]), [<span class="hljs-keyword">new</span> ThisLiteral])
|
||
|
||
<span class="hljs-literal">null</span>
|
||
|
||
declareName: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> (name = @variable?.unwrap()) <span class="hljs-keyword">instanceof</span> IdentifierLiteral
|
||
alreadyDeclared = o.scope.find name.value
|
||
name.isDeclaration = <span class="hljs-keyword">not</span> alreadyDeclared
|
||
|
||
isStatementAst: <span class="hljs-function">-></span> <span class="hljs-literal">yes</span>
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> jumpNode = @body.jumps()
|
||
jumpNode.error <span class="hljs-string">'Class bodies cannot contain pure statements'</span>
|
||
<span class="hljs-keyword">if</span> argumentsNode = @body.contains isLiteralArguments
|
||
argumentsNode.error <span class="hljs-string">"Class bodies shouldn't reference arguments"</span>
|
||
@declareName o
|
||
@name = @determineName()
|
||
@body.isClassBody = <span class="hljs-literal">yes</span>
|
||
@body.locationData = zeroWidthLocationDataFromEndLocation @locationData <span class="hljs-keyword">if</span> @hasGeneratedBody
|
||
@walkBody o
|
||
sniffDirectives @body.expressions
|
||
@ctor?.noReturn = <span class="hljs-literal">yes</span>
|
||
|
||
super o
|
||
|
||
astType: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> o.level <span class="hljs-keyword">is</span> LEVEL_TOP
|
||
<span class="hljs-string">'ClassDeclaration'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'ClassExpression'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
id: @variable?.ast(o) ? <span class="hljs-literal">null</span>
|
||
superClass: @parent?.ast(o, LEVEL_PAREN) ? <span class="hljs-literal">null</span>
|
||
body: @body.ast o, LEVEL_TOP
|
||
|
||
<span class="hljs-built_in">exports</span>.ExecutableClassBody = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExecutableClassBody</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
children: [ <span class="hljs-string">'class'</span>, <span class="hljs-string">'body'</span> ]
|
||
|
||
defaultClassVariableName: <span class="hljs-string">'_Class'</span>
|
||
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@class, @body = <span class="hljs-keyword">new</span> Block)</span> -></span>
|
||
super()
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> jumpNode = @body.jumps()
|
||
jumpNode.error <span class="hljs-string">'Class bodies cannot contain pure statements'</span>
|
||
<span class="hljs-keyword">if</span> argumentsNode = @body.contains isLiteralArguments
|
||
argumentsNode.error <span class="hljs-string">"Class bodies shouldn't reference arguments"</span>
|
||
|
||
params = []
|
||
args = [<span class="hljs-keyword">new</span> ThisLiteral]
|
||
wrapper = <span class="hljs-keyword">new</span> Code params, @body
|
||
klass = <span class="hljs-keyword">new</span> Parens <span class="hljs-keyword">new</span> Call (<span class="hljs-keyword">new</span> Value wrapper, [<span class="hljs-keyword">new</span> Access <span class="hljs-keyword">new</span> PropertyName <span class="hljs-string">'call'</span>]), args
|
||
|
||
@body.spaced = <span class="hljs-literal">true</span>
|
||
|
||
o.classScope = wrapper.makeScope o.scope
|
||
|
||
@name = @class.name ? o.classScope.freeVariable @defaultClassVariableName
|
||
ident = <span class="hljs-keyword">new</span> IdentifierLiteral @name
|
||
directives = @walkBody()
|
||
@setContext()
|
||
|
||
<span class="hljs-keyword">if</span> @class.hasNameClash
|
||
parent = <span class="hljs-keyword">new</span> IdentifierLiteral o.classScope.freeVariable <span class="hljs-string">'superClass'</span>
|
||
wrapper.params.push <span class="hljs-keyword">new</span> Param parent
|
||
args.push @class.parent
|
||
@class.parent = parent
|
||
|
||
<span class="hljs-keyword">if</span> @externalCtor
|
||
externalCtor = <span class="hljs-keyword">new</span> IdentifierLiteral o.classScope.freeVariable <span class="hljs-string">'ctor'</span>, reserve: <span class="hljs-literal">no</span>
|
||
@class.externalCtor = externalCtor
|
||
@externalCtor.variable.base = externalCtor
|
||
|
||
<span class="hljs-keyword">if</span> @name <span class="hljs-keyword">isnt</span> @class.name
|
||
@body.expressions.unshift <span class="hljs-keyword">new</span> Assign (<span class="hljs-keyword">new</span> IdentifierLiteral @name), @class
|
||
<span class="hljs-keyword">else</span>
|
||
@body.expressions.unshift @class
|
||
@body.expressions.unshift directives...
|
||
@body.push ident
|
||
|
||
klass.compileToFragments o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-204">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-204">§</a>
|
||
</div>
|
||
<p>Traverse the class’s children and:</p>
|
||
<ul>
|
||
<li>Hoist valid ES properties into <code>@properties</code></li>
|
||
<li>Hoist static assignments into <code>@properties</code></li>
|
||
<li>Convert invalid ES properties into class or prototype assignments</li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> walkBody: <span class="hljs-function">-></span>
|
||
directives = []
|
||
|
||
index = <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">while</span> expr = @body.expressions[index]
|
||
<span class="hljs-keyword">break</span> <span class="hljs-keyword">unless</span> expr <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> expr.isString()
|
||
<span class="hljs-keyword">if</span> expr.hoisted
|
||
index++
|
||
<span class="hljs-keyword">else</span>
|
||
directives.push @body.expressions.splice(index, <span class="hljs-number">1</span>)...
|
||
|
||
@traverseChildren <span class="hljs-literal">false</span>, <span class="hljs-function"><span class="hljs-params">(child)</span> =></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span> <span class="hljs-keyword">if</span> child <span class="hljs-keyword">instanceof</span> Class <span class="hljs-keyword">or</span> child <span class="hljs-keyword">instanceof</span> HoistTarget
|
||
|
||
cont = <span class="hljs-literal">true</span>
|
||
<span class="hljs-keyword">if</span> child <span class="hljs-keyword">instanceof</span> Block
|
||
<span class="hljs-keyword">for</span> node, i <span class="hljs-keyword">in</span> child.expressions
|
||
<span class="hljs-keyword">if</span> node <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> node.isObject(<span class="hljs-literal">true</span>)
|
||
cont = <span class="hljs-literal">false</span>
|
||
child.expressions[i] = @addProperties node.base.properties
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> node <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span> node.variable.looksStatic @name
|
||
node.value.isStatic = <span class="hljs-literal">yes</span>
|
||
child.expressions = flatten child.expressions
|
||
cont
|
||
|
||
directives
|
||
|
||
setContext: <span class="hljs-function">-></span>
|
||
@body.traverseChildren <span class="hljs-literal">false</span>, <span class="hljs-function"><span class="hljs-params">(node)</span> =></span>
|
||
<span class="hljs-keyword">if</span> node <span class="hljs-keyword">instanceof</span> ThisLiteral
|
||
node.value = @name
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> node <span class="hljs-keyword">instanceof</span> Code <span class="hljs-keyword">and</span> node.bound <span class="hljs-keyword">and</span> (node.isStatic <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> node.name)
|
||
node.context = @name</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-205">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-205">§</a>
|
||
</div>
|
||
<p>Make class/prototype assignments for invalid ES properties</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> addProperties: <span class="hljs-function"><span class="hljs-params">(assigns)</span> -></span>
|
||
result = <span class="hljs-keyword">for</span> assign <span class="hljs-keyword">in</span> assigns
|
||
variable = assign.variable
|
||
base = variable?.base
|
||
value = assign.value
|
||
<span class="hljs-keyword">delete</span> assign.context
|
||
|
||
<span class="hljs-keyword">if</span> base.value <span class="hljs-keyword">is</span> <span class="hljs-string">'constructor'</span>
|
||
<span class="hljs-keyword">if</span> value <span class="hljs-keyword">instanceof</span> Code
|
||
base.error <span class="hljs-string">'constructors must be defined at the top level of a class body'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-206">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-206">§</a>
|
||
</div>
|
||
<p>The class scope is not available yet, so return the assignment to update later</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> assign = @externalCtor = <span class="hljs-keyword">new</span> Assign <span class="hljs-keyword">new</span> Value, value
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> assign.variable.this
|
||
name =
|
||
<span class="hljs-keyword">if</span> base <span class="hljs-keyword">instanceof</span> ComputedPropertyName
|
||
<span class="hljs-keyword">new</span> Index base.value
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">new</span> (<span class="hljs-keyword">if</span> base.shouldCache() <span class="hljs-keyword">then</span> Index <span class="hljs-keyword">else</span> Access) base
|
||
prototype = <span class="hljs-keyword">new</span> Access <span class="hljs-keyword">new</span> PropertyName <span class="hljs-string">'prototype'</span>
|
||
variable = <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> ThisLiteral(), [ prototype, name ]
|
||
|
||
assign.variable = variable
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> assign.value <span class="hljs-keyword">instanceof</span> Code
|
||
assign.value.isStatic = <span class="hljs-literal">true</span>
|
||
|
||
assign
|
||
compact result
|
||
|
||
<span class="hljs-built_in">exports</span>.ClassProperty = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassProperty</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">({@name, @isStatic, @staticClassName, @value, @operatorToken})</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'name'</span>, <span class="hljs-string">'value'</span>, <span class="hljs-string">'staticClassName'</span>]
|
||
|
||
isStatement: YES
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
key: @name.ast o, LEVEL_LIST
|
||
value: @value.ast o, LEVEL_LIST
|
||
static: !!@isStatic
|
||
computed: @name <span class="hljs-keyword">instanceof</span> Index <span class="hljs-keyword">or</span> @name <span class="hljs-keyword">instanceof</span> ComputedPropertyName
|
||
operator: @operatorToken?.value ? <span class="hljs-string">'='</span>
|
||
staticClassName: @staticClassName?.ast(o) ? <span class="hljs-literal">null</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.ClassPrototypeProperty = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassPrototypeProperty</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">({@name, @value})</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'name'</span>, <span class="hljs-string">'value'</span>]
|
||
|
||
isStatement: YES
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
key: @name.ast o, LEVEL_LIST
|
||
value: @value.ast o, LEVEL_LIST
|
||
computed: @name <span class="hljs-keyword">instanceof</span> ComputedPropertyName <span class="hljs-keyword">or</span> @name <span class="hljs-keyword">instanceof</span> StringWithInterpolations</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-207">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-207">§</a>
|
||
</div>
|
||
<h3 id="import-and-export">Import and Export</h3>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>
|
||
<span class="hljs-built_in">exports</span>.ModuleDeclaration = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ModuleDeclaration</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@clause, @source, @assertions)</span> -></span>
|
||
super()
|
||
@checkSource()
|
||
|
||
children: [<span class="hljs-string">'clause'</span>, <span class="hljs-string">'source'</span>, <span class="hljs-string">'assertions'</span>]
|
||
|
||
isStatement: YES
|
||
jumps: THIS
|
||
makeReturn: THIS
|
||
|
||
checkSource: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @source? <span class="hljs-keyword">and</span> @source <span class="hljs-keyword">instanceof</span> StringWithInterpolations
|
||
@source.error <span class="hljs-string">'the name of the module to be imported from must be an uninterpolated string'</span>
|
||
|
||
checkScope: <span class="hljs-function"><span class="hljs-params">(o, moduleDeclarationType)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-208">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-208">§</a>
|
||
</div>
|
||
<p>TODO: would be appropriate to flag this error during AST generation (as
|
||
well as when compiling to JS). But <code>o.indent</code> isn’t tracked during AST
|
||
generation, and there doesn’t seem to be a current alternative way to track
|
||
whether we’re at the “program top-level”.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> o.indent.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
@error <span class="hljs-string">"<span class="hljs-subst">#{moduleDeclarationType}</span> statements must be at top-level scope"</span>
|
||
|
||
astAssertions: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @assertions?.properties?
|
||
@assertions.properties.map (assertion) =>
|
||
{ start, end, loc, left, right } = assertion.ast(o)
|
||
{ type: <span class="hljs-string">'ImportAttribute'</span>, start, end, loc, key: left, value: right }
|
||
<span class="hljs-keyword">else</span>
|
||
[]
|
||
|
||
<span class="hljs-built_in">exports</span>.ImportDeclaration = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImportDeclaration</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ModuleDeclaration</span></span>
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkScope o, <span class="hljs-string">'import'</span>
|
||
o.importedSymbols = []
|
||
|
||
code = []
|
||
code.push @makeCode <span class="hljs-string">"<span class="hljs-subst">#{@tab}</span>import "</span>
|
||
code.push @clause.compileNode(o)... <span class="hljs-keyword">if</span> @clause?
|
||
|
||
<span class="hljs-keyword">if</span> @source?.value?
|
||
code.push @makeCode <span class="hljs-string">' from '</span> <span class="hljs-keyword">unless</span> @clause <span class="hljs-keyword">is</span> <span class="hljs-literal">null</span>
|
||
code.push @makeCode @source.value
|
||
<span class="hljs-keyword">if</span> @assertions?
|
||
code.push @makeCode <span class="hljs-string">' assert '</span>
|
||
code.push @assertions.compileToFragments(o)...
|
||
|
||
code.push @makeCode <span class="hljs-string">';'</span>
|
||
code
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o.importedSymbols = []
|
||
super o
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
ret =
|
||
specifiers: @clause?.ast(o) ? []
|
||
source: @source.ast o
|
||
assertions: @astAssertions(o)
|
||
ret.importKind = <span class="hljs-string">'value'</span> <span class="hljs-keyword">if</span> @clause
|
||
ret
|
||
|
||
<span class="hljs-built_in">exports</span>.ImportClause = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImportClause</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@defaultBinding, @namedImports)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'defaultBinding'</span>, <span class="hljs-string">'namedImports'</span>]
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
code = []
|
||
|
||
<span class="hljs-keyword">if</span> @defaultBinding?
|
||
code.push @defaultBinding.compileNode(o)...
|
||
code.push @makeCode <span class="hljs-string">', '</span> <span class="hljs-keyword">if</span> @namedImports?
|
||
|
||
<span class="hljs-keyword">if</span> @namedImports?
|
||
code.push @namedImports.compileNode(o)...
|
||
|
||
code
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-209">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-209">§</a>
|
||
</div>
|
||
<p>The AST for <code>ImportClause</code> is the non-nested list of import specifiers
|
||
that will be the <code>specifiers</code> property of an <code>ImportDeclaration</code> AST</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compact flatten [
|
||
@defaultBinding?.ast o
|
||
@namedImports?.ast o
|
||
]
|
||
|
||
<span class="hljs-built_in">exports</span>.ExportDeclaration = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExportDeclaration</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ModuleDeclaration</span></span>
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkScope o, <span class="hljs-string">'export'</span>
|
||
@checkForAnonymousClassExport()
|
||
|
||
code = []
|
||
code.push @makeCode <span class="hljs-string">"<span class="hljs-subst">#{@tab}</span>export "</span>
|
||
code.push @makeCode <span class="hljs-string">'default '</span> <span class="hljs-keyword">if</span> @ <span class="hljs-keyword">instanceof</span> ExportDefaultDeclaration
|
||
|
||
<span class="hljs-keyword">if</span> @ <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> ExportDefaultDeclaration <span class="hljs-keyword">and</span>
|
||
(@clause <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">or</span> @clause <span class="hljs-keyword">instanceof</span> Class)
|
||
code.push @makeCode <span class="hljs-string">'var '</span>
|
||
@clause.moduleDeclaration = <span class="hljs-string">'export'</span>
|
||
|
||
<span class="hljs-keyword">if</span> @clause.body? <span class="hljs-keyword">and</span> @clause.body <span class="hljs-keyword">instanceof</span> Block
|
||
code = code.concat @clause.compileToFragments o, LEVEL_TOP
|
||
<span class="hljs-keyword">else</span>
|
||
code = code.concat @clause.compileNode o
|
||
|
||
<span class="hljs-keyword">if</span> @source?.value?
|
||
code.push @makeCode <span class="hljs-string">" from <span class="hljs-subst">#{@source.value}</span>"</span>
|
||
<span class="hljs-keyword">if</span> @assertions?
|
||
code.push @makeCode <span class="hljs-string">' assert '</span>
|
||
code.push @assertions.compileToFragments(o)...
|
||
|
||
code.push @makeCode <span class="hljs-string">';'</span>
|
||
code</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-210">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-210">§</a>
|
||
</div>
|
||
<p>Prevent exporting an anonymous class; all exported members must be named</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> checkForAnonymousClassExport: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @ <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> ExportDefaultDeclaration <span class="hljs-keyword">and</span> @clause <span class="hljs-keyword">instanceof</span> Class <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @clause.variable
|
||
@clause.error <span class="hljs-string">'anonymous classes cannot be exported'</span>
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkForAnonymousClassExport()
|
||
super o
|
||
|
||
<span class="hljs-built_in">exports</span>.ExportNamedDeclaration = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExportNamedDeclaration</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ExportDeclaration</span></span>
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
ret =
|
||
source: @source?.ast(o) ? <span class="hljs-literal">null</span>
|
||
assertions: @astAssertions(o)
|
||
exportKind: <span class="hljs-string">'value'</span>
|
||
clauseAst = @clause.ast o
|
||
<span class="hljs-keyword">if</span> @clause <span class="hljs-keyword">instanceof</span> ExportSpecifierList
|
||
ret.specifiers = clauseAst
|
||
ret.declaration = <span class="hljs-literal">null</span>
|
||
<span class="hljs-keyword">else</span>
|
||
ret.specifiers = []
|
||
ret.declaration = clauseAst
|
||
ret
|
||
|
||
<span class="hljs-built_in">exports</span>.ExportDefaultDeclaration = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExportDefaultDeclaration</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ExportDeclaration</span></span>
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
declaration: @clause.ast o
|
||
assertions: @astAssertions(o)
|
||
|
||
<span class="hljs-built_in">exports</span>.ExportAllDeclaration = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExportAllDeclaration</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ExportDeclaration</span></span>
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
source: @source.ast o
|
||
assertions: @astAssertions(o)
|
||
exportKind: <span class="hljs-string">'value'</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.ModuleSpecifierList = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ModuleSpecifierList</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@specifiers)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'specifiers'</span>]
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
code = []
|
||
o.indent += TAB
|
||
compiledList = (specifier.compileToFragments o, LEVEL_LIST <span class="hljs-keyword">for</span> specifier <span class="hljs-keyword">in</span> @specifiers)
|
||
|
||
<span class="hljs-keyword">if</span> @specifiers.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
code.push @makeCode <span class="hljs-string">"{\n<span class="hljs-subst">#{o.indent}</span>"</span>
|
||
<span class="hljs-keyword">for</span> fragments, index <span class="hljs-keyword">in</span> compiledList
|
||
code.push @makeCode(<span class="hljs-string">",\n<span class="hljs-subst">#{o.indent}</span>"</span>) <span class="hljs-keyword">if</span> index
|
||
code.push fragments...
|
||
code.push @makeCode <span class="hljs-string">"\n}"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
code.push @makeCode <span class="hljs-string">'{}'</span>
|
||
code
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
specifier.ast(o) <span class="hljs-keyword">for</span> specifier <span class="hljs-keyword">in</span> @specifiers
|
||
|
||
<span class="hljs-built_in">exports</span>.ImportSpecifierList = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImportSpecifierList</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ModuleSpecifierList</span></span>
|
||
|
||
<span class="hljs-built_in">exports</span>.ExportSpecifierList = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExportSpecifierList</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ModuleSpecifierList</span></span>
|
||
|
||
<span class="hljs-built_in">exports</span>.ModuleSpecifier = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ModuleSpecifier</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@original, @alias, @moduleDeclarationType)</span> -></span>
|
||
super()
|
||
|
||
<span class="hljs-keyword">if</span> @original.comments <span class="hljs-keyword">or</span> @alias?.comments
|
||
@comments = []
|
||
@comments.push @original.comments... <span class="hljs-keyword">if</span> @original.comments
|
||
@comments.push @alias.comments... <span class="hljs-keyword">if</span> @alias?.comments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-211">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-211">§</a>
|
||
</div>
|
||
<p>The name of the variable entering the local scope</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @identifier = <span class="hljs-keyword">if</span> @alias? <span class="hljs-keyword">then</span> @alias.value <span class="hljs-keyword">else</span> @original.value
|
||
|
||
children: [<span class="hljs-string">'original'</span>, <span class="hljs-string">'alias'</span>]
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@addIdentifierToScope o
|
||
code = []
|
||
code.push @makeCode @original.value
|
||
code.push @makeCode <span class="hljs-string">" as <span class="hljs-subst">#{@alias.value}</span>"</span> <span class="hljs-keyword">if</span> @alias?
|
||
code
|
||
|
||
addIdentifierToScope: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o.scope.find @identifier, @moduleDeclarationType
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@addIdentifierToScope o
|
||
super o
|
||
|
||
<span class="hljs-built_in">exports</span>.ImportSpecifier = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImportSpecifier</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ModuleSpecifier</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(imported, local)</span> -></span>
|
||
super imported, local, <span class="hljs-string">'import'</span>
|
||
|
||
addIdentifierToScope: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-212">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-212">§</a>
|
||
</div>
|
||
<p>Per the spec, symbols can’t be imported multiple times
|
||
(e.g. <code>import { foo, foo } from 'lib'</code> is invalid)</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> @identifier <span class="hljs-keyword">in</span> o.importedSymbols <span class="hljs-keyword">or</span> o.scope.check(@identifier)
|
||
@error <span class="hljs-string">"'<span class="hljs-subst">#{@identifier}</span>' has already been declared"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
o.importedSymbols.push @identifier
|
||
super o
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
originalAst = @original.ast o
|
||
<span class="hljs-keyword">return</span>
|
||
imported: originalAst
|
||
local: @alias?.ast(o) ? originalAst
|
||
importKind: <span class="hljs-literal">null</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.ImportDefaultSpecifier = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImportDefaultSpecifier</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ImportSpecifier</span></span>
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
local: @original.ast o
|
||
|
||
<span class="hljs-built_in">exports</span>.ImportNamespaceSpecifier = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ImportNamespaceSpecifier</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ImportSpecifier</span></span>
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
local: @alias.ast o
|
||
|
||
<span class="hljs-built_in">exports</span>.ExportSpecifier = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExportSpecifier</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ModuleSpecifier</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(local, exported)</span> -></span>
|
||
super local, exported, <span class="hljs-string">'export'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
originalAst = @original.ast o
|
||
<span class="hljs-keyword">return</span>
|
||
local: originalAst
|
||
exported: @alias?.ast(o) ? originalAst
|
||
|
||
<span class="hljs-built_in">exports</span>.DynamicImport = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DynamicImport</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
compileNode: <span class="hljs-function">-></span>
|
||
[@makeCode <span class="hljs-string">'import'</span>]
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'Import'</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.DynamicImportCall = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DynamicImportCall</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Call</span></span>
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkArguments()
|
||
super o
|
||
|
||
checkArguments: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">unless</span> <span class="hljs-number">1</span> <= @args.length <= <span class="hljs-number">2</span>
|
||
@error <span class="hljs-string">'import() accepts either one or two arguments'</span>
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkArguments()
|
||
super o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-213">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-213">§</a>
|
||
</div>
|
||
<h3 id="assign">Assign</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-214">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-214">§</a>
|
||
</div>
|
||
<p>The <strong>Assign</strong> is used to assign a local variable to value, or to set the
|
||
property of an object – including within object literals.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Assign = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Assign</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@variable, @value, @context, options = {})</span> -></span>
|
||
super()
|
||
{@param, @subpattern, @operatorToken, @moduleDeclaration, @originalContext = @context} = options
|
||
@propagateLhs()
|
||
|
||
children: [<span class="hljs-string">'variable'</span>, <span class="hljs-string">'value'</span>]
|
||
|
||
isAssignable: YES
|
||
|
||
isStatement: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o?.level <span class="hljs-keyword">is</span> LEVEL_TOP <span class="hljs-keyword">and</span> @context? <span class="hljs-keyword">and</span> (@moduleDeclaration <span class="hljs-keyword">or</span> <span class="hljs-string">"?"</span> <span class="hljs-keyword">in</span> @context)
|
||
|
||
checkNameAssignability: <span class="hljs-function"><span class="hljs-params">(o, varBase)</span> -></span>
|
||
<span class="hljs-keyword">if</span> o.scope.type(varBase.value) <span class="hljs-keyword">is</span> <span class="hljs-string">'import'</span>
|
||
varBase.error <span class="hljs-string">"'<span class="hljs-subst">#{varBase.value}</span>' is read-only"</span>
|
||
|
||
assigns: <span class="hljs-function"><span class="hljs-params">(name)</span> -></span>
|
||
@[<span class="hljs-keyword">if</span> @context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'value'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'variable'</span>].assigns name
|
||
|
||
unfoldSoak: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
unfoldSoak o, this, <span class="hljs-string">'variable'</span>
|
||
|
||
addScopeVariables: (o, {</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-215">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-215">§</a>
|
||
</div>
|
||
<p>During AST generation, we need to allow assignment to these constructs
|
||
that are considered “unassignable” during compile-to-JS, while still
|
||
flagging things like <code>[null] = b</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> allowAssignmentToExpansion = <span class="hljs-literal">no</span>,
|
||
allowAssignmentToNontrailingSplat = <span class="hljs-literal">no</span>,
|
||
allowAssignmentToEmptyArray = <span class="hljs-literal">no</span>,
|
||
allowAssignmentToComplexSplat = <span class="hljs-literal">no</span>
|
||
} = {}) ->
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> <span class="hljs-keyword">not</span> @context <span class="hljs-keyword">or</span> @context <span class="hljs-keyword">is</span> <span class="hljs-string">'**='</span>
|
||
|
||
varBase = @variable.unwrapAll()
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> varBase.isAssignable {
|
||
allowExpansion: allowAssignmentToExpansion
|
||
allowNontrailingSplat: allowAssignmentToNontrailingSplat
|
||
allowEmptyArray: allowAssignmentToEmptyArray
|
||
allowComplexSplat: allowAssignmentToComplexSplat
|
||
}
|
||
@variable.error <span class="hljs-string">"'<span class="hljs-subst">#{@variable.compile o}</span>' can't be assigned"</span>
|
||
|
||
varBase.eachName (name) =>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> name.hasProperties?()
|
||
|
||
message = isUnassignable name.value
|
||
name.error message <span class="hljs-keyword">if</span> message</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-216">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-216">§</a>
|
||
</div>
|
||
<p><code>moduleDeclaration</code> can be <code>'import'</code> or <code>'export'</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @checkNameAssignability o, name
|
||
<span class="hljs-keyword">if</span> @moduleDeclaration
|
||
o.scope.add name.value, @moduleDeclaration
|
||
name.isDeclaration = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @param
|
||
o.scope.add name.value,
|
||
<span class="hljs-keyword">if</span> @param <span class="hljs-keyword">is</span> <span class="hljs-string">'alwaysDeclare'</span>
|
||
<span class="hljs-string">'var'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'param'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
alreadyDeclared = o.scope.find name.value
|
||
name.isDeclaration ?= <span class="hljs-keyword">not</span> alreadyDeclared</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-217">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-217">§</a>
|
||
</div>
|
||
<p>If this assignment identifier has one or more herecomments
|
||
attached, output them as part of the declarations line (unless
|
||
other herecomments are already staged there) for compatibility
|
||
with Flow typing. Don’t do this if this assignment is for a
|
||
class, e.g. <code>ClassName = class ClassName {</code>, as Flow requires
|
||
the comment to be between the class name and the <code>{</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> name.comments <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> o.scope.comments[name.value] <span class="hljs-keyword">and</span>
|
||
@value <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> Class <span class="hljs-keyword">and</span>
|
||
name.comments.every(<span class="hljs-function"><span class="hljs-params">(comment)</span> -></span> comment.here <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> comment.multiline)
|
||
commentsNode = <span class="hljs-keyword">new</span> IdentifierLiteral name.value
|
||
commentsNode.comments = name.comments
|
||
commentFragments = []
|
||
@compileCommentFragments o, commentsNode, commentFragments
|
||
o.scope.comments[name.value] = commentFragments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-218">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-218">§</a>
|
||
</div>
|
||
<p>Compile an assignment, delegating to <code>compileDestructuring</code> or
|
||
<code>compileSplice</code> if appropriate. Keep track of the name of the base object
|
||
we’ve been assigned to, for correct internal references. If the variable
|
||
has not been seen yet within the current scope, declare it.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
isValue = @variable <span class="hljs-keyword">instanceof</span> Value
|
||
<span class="hljs-keyword">if</span> isValue</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-219">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-219">§</a>
|
||
</div>
|
||
<p>If <code>@variable</code> is an array or an object, we’re destructuring;
|
||
if it’s also <code>isAssignable()</code>, the destructuring syntax is supported
|
||
in ES and we can output it as is; otherwise we <code>@compileDestructuring</code>
|
||
and convert this ES-unsupported destructuring into acceptable output.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> @variable.isArray() <span class="hljs-keyword">or</span> @variable.isObject()
|
||
<span class="hljs-keyword">unless</span> @variable.isAssignable()
|
||
<span class="hljs-keyword">if</span> @variable.isObject() <span class="hljs-keyword">and</span> @variable.base.hasSplat()
|
||
<span class="hljs-keyword">return</span> @compileObjectDestruct o
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">return</span> @compileDestructuring o
|
||
|
||
<span class="hljs-keyword">return</span> @compileSplice o <span class="hljs-keyword">if</span> @variable.isSplice()
|
||
<span class="hljs-keyword">return</span> @compileConditional o <span class="hljs-keyword">if</span> @isConditional()
|
||
<span class="hljs-keyword">return</span> @compileSpecialMath o <span class="hljs-keyword">if</span> @context <span class="hljs-keyword">in</span> [<span class="hljs-string">'//='</span>, <span class="hljs-string">'%%='</span>]
|
||
|
||
@addScopeVariables o
|
||
<span class="hljs-keyword">if</span> @value <span class="hljs-keyword">instanceof</span> Code
|
||
<span class="hljs-keyword">if</span> @value.isStatic
|
||
@value.name = @variable.properties[<span class="hljs-number">0</span>]
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @variable.properties?.length >= <span class="hljs-number">2</span>
|
||
[properties..., prototype, name] = @variable.properties
|
||
@value.name = name <span class="hljs-keyword">if</span> prototype.name?.value <span class="hljs-keyword">is</span> <span class="hljs-string">'prototype'</span>
|
||
|
||
val = @value.compileToFragments o, LEVEL_LIST
|
||
compiledName = @variable.compileToFragments o, LEVEL_LIST
|
||
|
||
<span class="hljs-keyword">if</span> @context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span>
|
||
<span class="hljs-keyword">if</span> @variable.shouldCache()
|
||
compiledName.unshift @makeCode <span class="hljs-string">'['</span>
|
||
compiledName.push @makeCode <span class="hljs-string">']'</span>
|
||
<span class="hljs-keyword">return</span> compiledName.concat @makeCode(<span class="hljs-string">': '</span>), val
|
||
|
||
answer = compiledName.concat @makeCode(<span class="hljs-string">" <span class="hljs-subst">#{ @context <span class="hljs-keyword">or</span> <span class="hljs-string">'='</span> }</span> "</span>), val</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-220">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-220">§</a>
|
||
</div>
|
||
<p>Per <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assignment_without_declaration">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assignment_without_declaration</a>,
|
||
if we’re destructuring without declaring, the destructuring assignment must be wrapped in parentheses.
|
||
The assignment is wrapped in parentheses if ‘o.level’ has lower precedence than LEVEL_LIST (3)
|
||
(i.e. LEVEL_COND (4), LEVEL_OP (5) or LEVEL_ACCESS (6)), or if we’re destructuring object, e.g. {a,b} = obj.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> o.level > LEVEL_LIST <span class="hljs-keyword">or</span> isValue <span class="hljs-keyword">and</span> @variable.base <span class="hljs-keyword">instanceof</span> Obj <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @nestedLhs <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> (@param <span class="hljs-keyword">is</span> <span class="hljs-literal">yes</span>)
|
||
@wrapInParentheses answer
|
||
<span class="hljs-keyword">else</span>
|
||
answer</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-221">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-221">§</a>
|
||
</div>
|
||
<p>Object rest property is not assignable: <code>{{a}...}</code></p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileObjectDestruct: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@variable.base.reorderProperties()
|
||
{properties: props} = @variable.base
|
||
[..., splat] = props
|
||
splatProp = splat.name
|
||
assigns = []
|
||
refVal = <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.freeVariable <span class="hljs-string">'ref'</span>
|
||
props.splice <span class="hljs-number">-1</span>, <span class="hljs-number">1</span>, <span class="hljs-keyword">new</span> Splat refVal
|
||
assigns.push <span class="hljs-keyword">new</span> Assign(<span class="hljs-keyword">new</span> Value(<span class="hljs-keyword">new</span> Obj props), @value).compileToFragments o, LEVEL_LIST
|
||
assigns.push <span class="hljs-keyword">new</span> Assign(<span class="hljs-keyword">new</span> Value(splatProp), refVal).compileToFragments o, LEVEL_LIST
|
||
@joinFragmentArrays assigns, <span class="hljs-string">', '</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-222">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-222">§</a>
|
||
</div>
|
||
<p>Brief implementation of recursive pattern matching, when assigning array or
|
||
object literals to a value. Peeks at their properties to assign inner names.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileDestructuring: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
top = o.level <span class="hljs-keyword">is</span> LEVEL_TOP
|
||
{value} = this
|
||
{objects} = @variable.base
|
||
olen = objects.length</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-223">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-223">§</a>
|
||
</div>
|
||
<p>Special-case for <code>{} = a</code> and <code>[] = a</code> (empty patterns).
|
||
Compile to simply <code>a</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> olen <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
|
||
code = value.compileToFragments o
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> o.level >= LEVEL_OP <span class="hljs-keyword">then</span> @wrapInParentheses code <span class="hljs-keyword">else</span> code
|
||
[obj] = objects
|
||
|
||
@disallowLoneExpansion()
|
||
{splats, expans, splatsAndExpans} = @getAndCheckSplatsAndExpansions()
|
||
|
||
isSplat = splats?.length > <span class="hljs-number">0</span>
|
||
isExpans = expans?.length > <span class="hljs-number">0</span>
|
||
|
||
vvar = value.compileToFragments o, LEVEL_LIST
|
||
vvarText = fragmentsToText vvar
|
||
assigns = []
|
||
<span class="hljs-function"> <span class="hljs-title">pushAssign</span> = <span class="hljs-params">(variable, val)</span> =></span>
|
||
assigns.push <span class="hljs-keyword">new</span> Assign(variable, val, <span class="hljs-literal">null</span>, param: @param, subpattern: <span class="hljs-literal">yes</span>).compileToFragments o, LEVEL_LIST
|
||
|
||
<span class="hljs-keyword">if</span> isSplat
|
||
splatVar = objects[splats[<span class="hljs-number">0</span>]].name.unwrap()
|
||
<span class="hljs-keyword">if</span> splatVar <span class="hljs-keyword">instanceof</span> Arr <span class="hljs-keyword">or</span> splatVar <span class="hljs-keyword">instanceof</span> Obj
|
||
splatVarRef = <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.freeVariable <span class="hljs-string">'ref'</span>
|
||
objects[splats[<span class="hljs-number">0</span>]].name = splatVarRef
|
||
<span class="hljs-function"> <span class="hljs-title">splatVarAssign</span> = -></span> pushAssign <span class="hljs-keyword">new</span> Value(splatVar), splatVarRef</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-224">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-224">§</a>
|
||
</div>
|
||
<p>At this point, there are several things to destructure. So the <code>fn()</code> in
|
||
<code>{a, b} = fn()</code> must be cached, for example. Make vvar into a simple
|
||
variable if it isn’t already.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> value.unwrap() <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> IdentifierLiteral <span class="hljs-keyword">or</span> @variable.assigns(vvarText)
|
||
ref = o.scope.freeVariable <span class="hljs-string">'ref'</span>
|
||
assigns.push [@makeCode(ref + <span class="hljs-string">' = '</span>), vvar...]
|
||
vvar = [@makeCode ref]
|
||
vvarText = ref
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">slicer</span> = <span class="hljs-params">(type)</span> -></span> (vvar, start, end = <span class="hljs-literal">no</span>) ->
|
||
vvar = <span class="hljs-keyword">new</span> IdentifierLiteral vvar <span class="hljs-keyword">unless</span> vvar <span class="hljs-keyword">instanceof</span> Value
|
||
args = [vvar, <span class="hljs-keyword">new</span> NumberLiteral(start)]
|
||
args.push <span class="hljs-keyword">new</span> NumberLiteral end <span class="hljs-keyword">if</span> end
|
||
slice = <span class="hljs-keyword">new</span> Value (<span class="hljs-keyword">new</span> IdentifierLiteral utility type, o), [<span class="hljs-keyword">new</span> Access <span class="hljs-keyword">new</span> PropertyName <span class="hljs-string">'call'</span>]
|
||
<span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Call slice, args</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-225">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-225">§</a>
|
||
</div>
|
||
<p>Helper which outputs <code>[].slice</code> code.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compSlice = slicer <span class="hljs-string">"slice"</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-226">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-226">§</a>
|
||
</div>
|
||
<p>Helper which outputs <code>[].splice</code> code.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compSplice = slicer <span class="hljs-string">"splice"</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-227">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-227">§</a>
|
||
</div>
|
||
<p>Check if <code>objects</code> array contains any instance of <code>Assign</code>, e.g. {a:1}.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"> <span class="hljs-title">hasObjAssigns</span> = <span class="hljs-params">(objs)</span> -></span>
|
||
(i <span class="hljs-keyword">for</span> obj, i <span class="hljs-keyword">in</span> objs <span class="hljs-keyword">when</span> obj <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span> obj.context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span>)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-228">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-228">§</a>
|
||
</div>
|
||
<p>Check if <code>objects</code> array contains any unassignable object.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"> <span class="hljs-title">objIsUnassignable</span> = <span class="hljs-params">(objs)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">for</span> obj <span class="hljs-keyword">in</span> objs <span class="hljs-keyword">when</span> <span class="hljs-keyword">not</span> obj.isAssignable()
|
||
<span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-229">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-229">§</a>
|
||
</div>
|
||
<p><code>objects</code> are complex when there is object assign ({a:1}),
|
||
unassignable object, or just a single node.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"> <span class="hljs-title">complexObjects</span> = <span class="hljs-params">(objs)</span> -></span>
|
||
hasObjAssigns(objs).length <span class="hljs-keyword">or</span> objIsUnassignable(objs) <span class="hljs-keyword">or</span> olen <span class="hljs-keyword">is</span> <span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-230">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-230">§</a>
|
||
</div>
|
||
<p>“Complex” <code>objects</code> are processed in a loop.
|
||
Examples: [a, b, {c, r…}, d], [a, …, {b, r…}, c, d]</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"> <span class="hljs-title">loopObjects</span> = <span class="hljs-params">(objs, vvar, vvarTxt)</span> =></span>
|
||
<span class="hljs-keyword">for</span> obj, i <span class="hljs-keyword">in</span> objs</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-231">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-231">§</a>
|
||
</div>
|
||
<p><code>Elision</code> can be skipped.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">continue</span> <span class="hljs-keyword">if</span> obj <span class="hljs-keyword">instanceof</span> Elision</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-232">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-232">§</a>
|
||
</div>
|
||
<p>If <code>obj</code> is {a: 1}</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> obj <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span> obj.context <span class="hljs-keyword">is</span> <span class="hljs-string">'object'</span>
|
||
{variable: {base: idx}, value: vvar} = obj
|
||
{variable: vvar} = vvar <span class="hljs-keyword">if</span> vvar <span class="hljs-keyword">instanceof</span> Assign
|
||
idx =
|
||
<span class="hljs-keyword">if</span> vvar.this
|
||
vvar.properties[<span class="hljs-number">0</span>].name
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">new</span> PropertyName vvar.unwrap().value
|
||
acc = idx.unwrap() <span class="hljs-keyword">instanceof</span> PropertyName
|
||
vval = <span class="hljs-keyword">new</span> Value value, [<span class="hljs-keyword">new</span> (<span class="hljs-keyword">if</span> acc <span class="hljs-keyword">then</span> Access <span class="hljs-keyword">else</span> Index) idx]
|
||
<span class="hljs-keyword">else</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-233">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-233">§</a>
|
||
</div>
|
||
<p><code>obj</code> is [a…], {a…} or a</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> vvar = <span class="hljs-keyword">switch</span>
|
||
<span class="hljs-keyword">when</span> obj <span class="hljs-keyword">instanceof</span> Splat <span class="hljs-keyword">then</span> <span class="hljs-keyword">new</span> Value obj.name
|
||
<span class="hljs-keyword">else</span> obj
|
||
vval = <span class="hljs-keyword">switch</span>
|
||
<span class="hljs-keyword">when</span> obj <span class="hljs-keyword">instanceof</span> Splat <span class="hljs-keyword">then</span> compSlice(vvarTxt, i)
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Literal(vvarTxt), [<span class="hljs-keyword">new</span> Index <span class="hljs-keyword">new</span> NumberLiteral i]
|
||
message = isUnassignable vvar.unwrap().value
|
||
vvar.error message <span class="hljs-keyword">if</span> message
|
||
pushAssign vvar, vval</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-234">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-234">§</a>
|
||
</div>
|
||
<p>“Simple” <code>objects</code> can be split and compiled to arrays, [a, b, c] = arr, [a, b, c…] = arr</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"> <span class="hljs-title">assignObjects</span> = <span class="hljs-params">(objs, vvar, vvarTxt)</span> =></span>
|
||
vvar = <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Arr(objs, <span class="hljs-literal">yes</span>)
|
||
vval = <span class="hljs-keyword">if</span> vvarTxt <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">then</span> vvarTxt <span class="hljs-keyword">else</span> <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Literal(vvarTxt)
|
||
pushAssign vvar, vval
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">processObjects</span> = <span class="hljs-params">(objs, vvar, vvarTxt)</span> -></span>
|
||
<span class="hljs-keyword">if</span> complexObjects objs
|
||
loopObjects objs, vvar, vvarTxt
|
||
<span class="hljs-keyword">else</span>
|
||
assignObjects objs, vvar, vvarTxt</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-235">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-235">§</a>
|
||
</div>
|
||
<p>In case there is <code>Splat</code> or <code>Expansion</code> in <code>objects</code>,
|
||
we can split array in two simple subarrays.
|
||
<code>Splat</code> [a, b, c…, d, e] can be split into [a, b, c…] and [d, e].
|
||
<code>Expansion</code> [a, b, …, c, d] can be split into [a, b] and [c, d].
|
||
Examples:
|
||
a) <code>Splat</code>
|
||
CS: [a, b, c…, d, e] = arr
|
||
JS: [a, b, …c] = arr, [d, e] = splice.call(c, -2)
|
||
b) <code>Expansion</code>
|
||
CS: [a, b, …, d, e] = arr
|
||
JS: [a, b] = arr, [d, e] = slice.call(arr, -2)</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> splatsAndExpans.length
|
||
expIdx = splatsAndExpans[<span class="hljs-number">0</span>]
|
||
leftObjs = objects.slice <span class="hljs-number">0</span>, expIdx + (<span class="hljs-keyword">if</span> isSplat <span class="hljs-keyword">then</span> <span class="hljs-number">1</span> <span class="hljs-keyword">else</span> <span class="hljs-number">0</span>)
|
||
rightObjs = objects.slice expIdx + <span class="hljs-number">1</span>
|
||
processObjects leftObjs, vvar, vvarText <span class="hljs-keyword">if</span> leftObjs.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">if</span> rightObjs.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-236">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-236">§</a>
|
||
</div>
|
||
<p>Slice or splice <code>objects</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> refExp = <span class="hljs-keyword">switch</span>
|
||
<span class="hljs-keyword">when</span> isSplat <span class="hljs-keyword">then</span> compSplice <span class="hljs-keyword">new</span> Value(objects[expIdx].name), rightObjs.length * <span class="hljs-number">-1</span>
|
||
<span class="hljs-keyword">when</span> isExpans <span class="hljs-keyword">then</span> compSlice vvarText, rightObjs.length * <span class="hljs-number">-1</span>
|
||
<span class="hljs-keyword">if</span> complexObjects rightObjs
|
||
restVar = refExp
|
||
refExp = o.scope.freeVariable <span class="hljs-string">'ref'</span>
|
||
assigns.push [@makeCode(refExp + <span class="hljs-string">' = '</span>), restVar.compileToFragments(o, LEVEL_LIST)...]
|
||
processObjects rightObjs, vvar, refExp
|
||
<span class="hljs-keyword">else</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-237">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-237">§</a>
|
||
</div>
|
||
<p>There is no <code>Splat</code> or <code>Expansion</code> in <code>objects</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> processObjects objects, vvar, vvarText
|
||
splatVarAssign?()
|
||
assigns.push vvar <span class="hljs-keyword">unless</span> top <span class="hljs-keyword">or</span> @subpattern
|
||
fragments = @joinFragmentArrays assigns, <span class="hljs-string">', '</span>
|
||
<span class="hljs-keyword">if</span> o.level < LEVEL_LIST <span class="hljs-keyword">then</span> fragments <span class="hljs-keyword">else</span> @wrapInParentheses fragments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-238">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-238">§</a>
|
||
</div>
|
||
<p>Disallow <code>[...] = a</code> for some reason. (Could be equivalent to <code>[] = a</code>?)</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> disallowLoneExpansion: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> @variable.base <span class="hljs-keyword">instanceof</span> Arr
|
||
{objects} = @variable.base
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> objects?.length <span class="hljs-keyword">is</span> <span class="hljs-number">1</span>
|
||
[loneObject] = objects
|
||
<span class="hljs-keyword">if</span> loneObject <span class="hljs-keyword">instanceof</span> Expansion
|
||
loneObject.error <span class="hljs-string">'Destructuring assignment has no target'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-239">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-239">§</a>
|
||
</div>
|
||
<p>Show error if there is more than one <code>Splat</code>, or <code>Expansion</code>.
|
||
Examples: [a, b, c…, d, e, f…], [a, b, …, c, d, …], [a, b, …, c, d, e…]</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> getAndCheckSplatsAndExpansions: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> {splats: [], expans: [], splatsAndExpans: []} <span class="hljs-keyword">unless</span> @variable.base <span class="hljs-keyword">instanceof</span> Arr
|
||
{objects} = @variable.base</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-240">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-240">§</a>
|
||
</div>
|
||
<p>Count all <code>Splats</code>: [a, b, c…, d, e]</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> splats = (i <span class="hljs-keyword">for</span> obj, i <span class="hljs-keyword">in</span> objects <span class="hljs-keyword">when</span> obj <span class="hljs-keyword">instanceof</span> Splat)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-241">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-241">§</a>
|
||
</div>
|
||
<p>Count all <code>Expansions</code>: [a, b, …, c, d]</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> expans = (i <span class="hljs-keyword">for</span> obj, i <span class="hljs-keyword">in</span> objects <span class="hljs-keyword">when</span> obj <span class="hljs-keyword">instanceof</span> Expansion)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-242">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-242">§</a>
|
||
</div>
|
||
<p>Combine splats and expansions.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> splatsAndExpans = [splats..., expans...]
|
||
<span class="hljs-keyword">if</span> splatsAndExpans.length > <span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-243">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-243">§</a>
|
||
</div>
|
||
<p>Sort ‘splatsAndExpans’ so we can show error at first disallowed token.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> objects[splatsAndExpans.sort()[<span class="hljs-number">1</span>]].error <span class="hljs-string">"multiple splats/expansions are disallowed in an assignment"</span>
|
||
{splats, expans, splatsAndExpans}</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-244">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-244">§</a>
|
||
</div>
|
||
<p>When compiling a conditional assignment, take care to ensure that the
|
||
operands are only evaluated once, even though we have to reference them
|
||
more than once.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileConditional: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[left, right] = @variable.cacheReference o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-245">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-245">§</a>
|
||
</div>
|
||
<p>Disallow conditional assignment of undefined variables.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> left.properties.length <span class="hljs-keyword">and</span> left.base <span class="hljs-keyword">instanceof</span> Literal <span class="hljs-keyword">and</span>
|
||
left.base <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> ThisLiteral <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> o.scope.check left.base.value
|
||
@throwUnassignableConditionalError left.base.value
|
||
<span class="hljs-keyword">if</span> <span class="hljs-string">"?"</span> <span class="hljs-keyword">in</span> @context
|
||
o.isExistentialEquals = <span class="hljs-literal">true</span>
|
||
<span class="hljs-keyword">new</span> If(<span class="hljs-keyword">new</span> Existence(left), right, type: <span class="hljs-string">'if'</span>).addElse(<span class="hljs-keyword">new</span> Assign(right, @value, <span class="hljs-string">'='</span>)).compileToFragments o
|
||
<span class="hljs-keyword">else</span>
|
||
fragments = <span class="hljs-keyword">new</span> Op(@context[...<span class="hljs-number">-1</span>], left, <span class="hljs-keyword">new</span> Assign(right, @value, <span class="hljs-string">'='</span>)).compileToFragments o
|
||
<span class="hljs-keyword">if</span> o.level <= LEVEL_LIST <span class="hljs-keyword">then</span> fragments <span class="hljs-keyword">else</span> @wrapInParentheses fragments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-246">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-246">§</a>
|
||
</div>
|
||
<p>Convert special math assignment operators like <code>a //= b</code> to the equivalent
|
||
extended form <code>a = a ** b</code> and then compiles that.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileSpecialMath: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[left, right] = @variable.cacheReference o
|
||
<span class="hljs-keyword">new</span> Assign(left, <span class="hljs-keyword">new</span> Op(@context[...<span class="hljs-number">-1</span>], right, @value)).compileToFragments o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-247">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-247">§</a>
|
||
</div>
|
||
<p>Compile the assignment from an array splice literal, using JavaScript’s
|
||
<code>Array#splice</code> method.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileSplice: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
{range: {<span class="hljs-keyword">from</span>, to, exclusive}} = @variable.properties.pop()
|
||
unwrappedVar = @variable.unwrapAll()
|
||
<span class="hljs-keyword">if</span> unwrappedVar.comments
|
||
moveComments unwrappedVar, @
|
||
<span class="hljs-keyword">delete</span> @variable.comments
|
||
name = @variable.compile o
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">from</span>
|
||
[fromDecl, fromRef] = @cacheToCodeFragments <span class="hljs-keyword">from</span>.cache o, LEVEL_OP
|
||
<span class="hljs-keyword">else</span>
|
||
fromDecl = fromRef = <span class="hljs-string">'0'</span>
|
||
<span class="hljs-keyword">if</span> to
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">from</span>?.isNumber() <span class="hljs-keyword">and</span> to.isNumber()
|
||
to = to.compile(o) - fromRef
|
||
to += <span class="hljs-number">1</span> <span class="hljs-keyword">unless</span> exclusive
|
||
<span class="hljs-keyword">else</span>
|
||
to = to.compile(o, LEVEL_ACCESS) + <span class="hljs-string">' - '</span> + fromRef
|
||
to += <span class="hljs-string">' + 1'</span> <span class="hljs-keyword">unless</span> exclusive
|
||
<span class="hljs-keyword">else</span>
|
||
to = <span class="hljs-string">"9e9"</span>
|
||
[valDef, valRef] = @value.cache o, LEVEL_LIST
|
||
answer = [].concat @makeCode(<span class="hljs-string">"<span class="hljs-subst">#{utility <span class="hljs-string">'splice'</span>, o}</span>.apply(<span class="hljs-subst">#{name}</span>, [<span class="hljs-subst">#{fromDecl}</span>, <span class="hljs-subst">#{to}</span>].concat("</span>), valDef, @makeCode(<span class="hljs-string">")), "</span>), valRef
|
||
<span class="hljs-keyword">if</span> o.level > LEVEL_TOP <span class="hljs-keyword">then</span> @wrapInParentheses answer <span class="hljs-keyword">else</span> answer
|
||
|
||
eachName: <span class="hljs-function"><span class="hljs-params">(iterator)</span> -></span>
|
||
@variable.unwrapAll().eachName iterator
|
||
|
||
isDefaultAssignment: <span class="hljs-function">-></span> @param <span class="hljs-keyword">or</span> @nestedLhs
|
||
|
||
propagateLhs: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> @variable?.isArray?() <span class="hljs-keyword">or</span> @variable?.isObject?()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-248">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-248">§</a>
|
||
</div>
|
||
<p>This is the left-hand side of an assignment; let <code>Arr</code> and <code>Obj</code>
|
||
know that, so that those nodes know that they’re assignable as
|
||
destructured variables.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @variable.base.propagateLhs <span class="hljs-literal">yes</span>
|
||
|
||
throwUnassignableConditionalError: <span class="hljs-function"><span class="hljs-params">(name)</span> -></span>
|
||
@variable.error <span class="hljs-string">"the variable \"<span class="hljs-subst">#{name}</span>\" can't be assigned with <span class="hljs-subst">#{@context}</span> because it has not been declared before"</span>
|
||
|
||
isConditional: <span class="hljs-function">-></span>
|
||
@context <span class="hljs-keyword">in</span> [<span class="hljs-string">'||='</span>, <span class="hljs-string">'&&='</span>, <span class="hljs-string">'?='</span>]
|
||
|
||
isStatementAst: NO
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@disallowLoneExpansion()
|
||
@getAndCheckSplatsAndExpansions()
|
||
<span class="hljs-keyword">if</span> @isConditional()
|
||
variable = @variable.unwrap()
|
||
<span class="hljs-keyword">if</span> variable <span class="hljs-keyword">instanceof</span> IdentifierLiteral <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> o.scope.check variable.value
|
||
@throwUnassignableConditionalError variable.value
|
||
@addScopeVariables o, allowAssignmentToExpansion: <span class="hljs-literal">yes</span>, allowAssignmentToNontrailingSplat: <span class="hljs-literal">yes</span>, allowAssignmentToEmptyArray: <span class="hljs-literal">yes</span>, allowAssignmentToComplexSplat: <span class="hljs-literal">yes</span>
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @isDefaultAssignment()
|
||
<span class="hljs-string">'AssignmentPattern'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'AssignmentExpression'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
ret =
|
||
right: @value.ast o, LEVEL_LIST
|
||
left: @variable.ast o, LEVEL_LIST
|
||
|
||
<span class="hljs-keyword">unless</span> @isDefaultAssignment()
|
||
ret.operator = @originalContext ? <span class="hljs-string">'='</span>
|
||
|
||
ret</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-249">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-249">§</a>
|
||
</div>
|
||
<h3 id="funcglyph">FuncGlyph</h3>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>
|
||
<span class="hljs-built_in">exports</span>.FuncGlyph = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FuncGlyph</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@glyph)</span> -></span>
|
||
super()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-250">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-250">§</a>
|
||
</div>
|
||
<h3 id="code">Code</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-251">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-251">§</a>
|
||
</div>
|
||
<p>A function definition. This is the only node that creates a new Scope.
|
||
When for the purposes of walking the contents of a function body, the Code
|
||
has no <em>children</em> – they’re within the inner scope.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Code = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Code</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(params, body, @funcGlyph, @paramStart)</span> -></span>
|
||
super()
|
||
|
||
@params = params <span class="hljs-keyword">or</span> []
|
||
@body = body <span class="hljs-keyword">or</span> <span class="hljs-keyword">new</span> Block
|
||
@bound = @funcGlyph?.glyph <span class="hljs-keyword">is</span> <span class="hljs-string">'=>'</span>
|
||
@isGenerator = <span class="hljs-literal">no</span>
|
||
@isAsync = <span class="hljs-literal">no</span>
|
||
@isMethod = <span class="hljs-literal">no</span>
|
||
|
||
@body.traverseChildren <span class="hljs-literal">no</span>, <span class="hljs-function"><span class="hljs-params">(node)</span> =></span>
|
||
<span class="hljs-keyword">if</span> (node <span class="hljs-keyword">instanceof</span> Op <span class="hljs-keyword">and</span> node.isYield()) <span class="hljs-keyword">or</span> node <span class="hljs-keyword">instanceof</span> YieldReturn
|
||
@isGenerator = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">if</span> (node <span class="hljs-keyword">instanceof</span> Op <span class="hljs-keyword">and</span> node.isAwait()) <span class="hljs-keyword">or</span> node <span class="hljs-keyword">instanceof</span> AwaitReturn
|
||
@isAsync = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">if</span> node <span class="hljs-keyword">instanceof</span> For <span class="hljs-keyword">and</span> node.isAwait()
|
||
@isAsync = <span class="hljs-literal">yes</span>
|
||
|
||
@propagateLhs()
|
||
|
||
children: [<span class="hljs-string">'params'</span>, <span class="hljs-string">'body'</span>]
|
||
|
||
isStatement: <span class="hljs-function">-></span> @isMethod
|
||
|
||
jumps: NO
|
||
|
||
makeScope: <span class="hljs-function"><span class="hljs-params">(parentScope)</span> -></span> <span class="hljs-keyword">new</span> Scope parentScope, @body, this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-252">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-252">§</a>
|
||
</div>
|
||
<p>Compilation creates a new scope unless explicitly asked to share with the
|
||
outer scope. Handles splat parameters in the parameter list by setting
|
||
such parameters to be the final parameter in the function definition, as
|
||
required per the ES2015 spec. If the CoffeeScript function definition had
|
||
parameters after the splat, they are declared via expressions in the
|
||
function body.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkForAsyncOrGeneratorConstructor()
|
||
|
||
<span class="hljs-keyword">if</span> @bound
|
||
@context = o.scope.method.context <span class="hljs-keyword">if</span> o.scope.method?.bound
|
||
@context = <span class="hljs-string">'this'</span> <span class="hljs-keyword">unless</span> @context
|
||
|
||
@updateOptions o
|
||
params = []
|
||
exprs = []
|
||
thisAssignments = @thisAssignments?.slice() ? []
|
||
paramsAfterSplat = []
|
||
haveSplatParam = <span class="hljs-literal">no</span>
|
||
haveBodyParam = <span class="hljs-literal">no</span>
|
||
|
||
@checkForDuplicateParams()
|
||
@disallowLoneExpansionAndMultipleSplats()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-253">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-253">§</a>
|
||
</div>
|
||
<p>Separate <code>this</code> assignments.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @eachParamName (name, node, param, obj) ->
|
||
<span class="hljs-keyword">if</span> node.this
|
||
name = node.properties[<span class="hljs-number">0</span>].name.value
|
||
name = <span class="hljs-string">"_<span class="hljs-subst">#{name}</span>"</span> <span class="hljs-keyword">if</span> name <span class="hljs-keyword">in</span> JS_FORBIDDEN
|
||
target = <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.freeVariable name, reserve: <span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-254">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-254">§</a>
|
||
</div>
|
||
<p><code>Param</code> is object destructuring with a default value: ({@prop = 1}) ->
|
||
In a case when the variable name is already reserved, we have to assign
|
||
a new variable name to the destructured variable: ({prop:prop1 = 1}) -></p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> replacement =
|
||
<span class="hljs-keyword">if</span> param.name <span class="hljs-keyword">instanceof</span> Obj <span class="hljs-keyword">and</span> obj <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span>
|
||
obj.operatorToken.value <span class="hljs-keyword">is</span> <span class="hljs-string">'='</span>
|
||
<span class="hljs-keyword">new</span> Assign (<span class="hljs-keyword">new</span> IdentifierLiteral name), target, <span class="hljs-string">'object'</span> <span class="hljs-comment">#, operatorToken: new Literal ':'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
target
|
||
param.renameParam node, replacement
|
||
thisAssignments.push <span class="hljs-keyword">new</span> Assign node, target</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-255">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-255">§</a>
|
||
</div>
|
||
<p>Parse the parameters, adding them to the list of parameters to put in the
|
||
function definition; and dealing with splats or expansions, including
|
||
adding expressions to the function body to declare all parameter
|
||
variables that would have been after the splat/expansion parameter.
|
||
If we encounter a parameter that needs to be declared in the function
|
||
body for any reason, for example it’s destructured with <code>this</code>, also
|
||
declare and assign all subsequent parameters in the function body so that
|
||
any non-idempotent parameters are evaluated in the correct order.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span> param, i <span class="hljs-keyword">in</span> @params</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-256">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-256">§</a>
|
||
</div>
|
||
<p>Was <code>...</code> used with this parameter? Splat/expansion parameters cannot
|
||
have default values, so we need not worry about that.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> param.splat <span class="hljs-keyword">or</span> param <span class="hljs-keyword">instanceof</span> Expansion
|
||
haveSplatParam = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">if</span> param.splat
|
||
<span class="hljs-keyword">if</span> param.name <span class="hljs-keyword">instanceof</span> Arr <span class="hljs-keyword">or</span> param.name <span class="hljs-keyword">instanceof</span> Obj</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-257">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-257">§</a>
|
||
</div>
|
||
<p>Splat arrays are treated oddly by ES; deal with them the legacy
|
||
way in the function body. TODO: Should this be handled in the
|
||
function parameter list, and if so, how?</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> splatParamName = o.scope.freeVariable <span class="hljs-string">'arg'</span>
|
||
params.push ref = <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> IdentifierLiteral splatParamName
|
||
exprs.push <span class="hljs-keyword">new</span> Assign <span class="hljs-keyword">new</span> Value(param.name), ref
|
||
<span class="hljs-keyword">else</span>
|
||
params.push ref = param.asReference o
|
||
splatParamName = fragmentsToText ref.compileNodeWithoutComments o
|
||
<span class="hljs-keyword">if</span> param.shouldCache()
|
||
exprs.push <span class="hljs-keyword">new</span> Assign <span class="hljs-keyword">new</span> Value(param.name), ref
|
||
<span class="hljs-keyword">else</span> <span class="hljs-comment"># `param` is an Expansion</span>
|
||
splatParamName = o.scope.freeVariable <span class="hljs-string">'args'</span>
|
||
params.push <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> IdentifierLiteral splatParamName
|
||
|
||
o.scope.parameter splatParamName</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-258">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-258">§</a>
|
||
</div>
|
||
<p>Parse all other parameters; if a splat paramater has not yet been
|
||
encountered, add these other parameters to the list to be output in
|
||
the function definition.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> param.shouldCache() <span class="hljs-keyword">or</span> haveBodyParam
|
||
param.assignedInBody = <span class="hljs-literal">yes</span>
|
||
haveBodyParam = <span class="hljs-literal">yes</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-259">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-259">§</a>
|
||
</div>
|
||
<p>This parameter cannot be declared or assigned in the parameter
|
||
list. So put a reference in the parameter list and add a statement
|
||
to the function body assigning it, e.g.
|
||
<code>(arg) => { var a = arg.a; }</code>, with a default value if it has one.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> param.value?
|
||
condition = <span class="hljs-keyword">new</span> Op <span class="hljs-string">'==='</span>, param, <span class="hljs-keyword">new</span> UndefinedLiteral
|
||
ifTrue = <span class="hljs-keyword">new</span> Assign <span class="hljs-keyword">new</span> Value(param.name), param.value
|
||
exprs.push <span class="hljs-keyword">new</span> If condition, ifTrue
|
||
<span class="hljs-keyword">else</span>
|
||
exprs.push <span class="hljs-keyword">new</span> Assign <span class="hljs-keyword">new</span> Value(param.name), param.asReference(o), <span class="hljs-literal">null</span>, param: <span class="hljs-string">'alwaysDeclare'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-260">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-260">§</a>
|
||
</div>
|
||
<p>If this parameter comes before the splat or expansion, it will go
|
||
in the function definition parameter list.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">unless</span> haveSplatParam</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-261">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-261">§</a>
|
||
</div>
|
||
<p>If this parameter has a default value, and it hasn’t already been
|
||
set by the <code>shouldCache()</code> block above, define it as a statement in
|
||
the function body. This parameter comes after the splat parameter,
|
||
so we can’t define its default value in the parameter list.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> param.shouldCache()
|
||
ref = param.asReference o
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> param.value? <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> param.assignedInBody
|
||
ref = <span class="hljs-keyword">new</span> Assign <span class="hljs-keyword">new</span> Value(param.name), param.value, <span class="hljs-literal">null</span>, param: <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">else</span>
|
||
ref = param</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-262">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-262">§</a>
|
||
</div>
|
||
<p>Add this parameter’s reference(s) to the function scope.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> param.name <span class="hljs-keyword">instanceof</span> Arr <span class="hljs-keyword">or</span> param.name <span class="hljs-keyword">instanceof</span> Obj</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-263">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-263">§</a>
|
||
</div>
|
||
<p>This parameter is destructured.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> param.name.lhs = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">unless</span> param.shouldCache()
|
||
param.name.eachName (prop) ->
|
||
o.scope.parameter prop.value
|
||
<span class="hljs-keyword">else</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-264">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-264">§</a>
|
||
</div>
|
||
<p>This compilation of the parameter is only to get its name to add
|
||
to the scope name tracking; since the compilation output here
|
||
isn’t kept for eventual output, don’t include comments in this
|
||
compilation, so that they get output the “real” time this param
|
||
is compiled.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> paramToAddToScope = <span class="hljs-keyword">if</span> param.value? <span class="hljs-keyword">then</span> param <span class="hljs-keyword">else</span> ref
|
||
o.scope.parameter fragmentsToText paramToAddToScope.compileToFragmentsWithoutComments o
|
||
params.push ref
|
||
<span class="hljs-keyword">else</span>
|
||
paramsAfterSplat.push param</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-265">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-265">§</a>
|
||
</div>
|
||
<p>If this parameter had a default value, since it’s no longer in the
|
||
function parameter list we need to assign its default value
|
||
(if necessary) as an expression in the body.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> param.value? <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> param.shouldCache()
|
||
condition = <span class="hljs-keyword">new</span> Op <span class="hljs-string">'==='</span>, param, <span class="hljs-keyword">new</span> UndefinedLiteral
|
||
ifTrue = <span class="hljs-keyword">new</span> Assign <span class="hljs-keyword">new</span> Value(param.name), param.value
|
||
exprs.push <span class="hljs-keyword">new</span> If condition, ifTrue</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-266">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-266">§</a>
|
||
</div>
|
||
<p>Add this parameter to the scope, since it wouldn’t have been added
|
||
yet since it was skipped earlier.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> o.scope.add param.name.value, <span class="hljs-string">'var'</span>, <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> param.name?.value?</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-267">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-267">§</a>
|
||
</div>
|
||
<p>If there were parameters after the splat or expansion parameter, those
|
||
parameters need to be assigned in the body of the function.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> paramsAfterSplat.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-268">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-268">§</a>
|
||
</div>
|
||
<p>Create a destructured assignment, e.g. <code>[a, b, c] = [args..., b, c]</code></p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> exprs.unshift <span class="hljs-keyword">new</span> Assign <span class="hljs-keyword">new</span> Value(
|
||
<span class="hljs-keyword">new</span> Arr [<span class="hljs-keyword">new</span> Splat(<span class="hljs-keyword">new</span> IdentifierLiteral(splatParamName)), (param.asReference o <span class="hljs-keyword">for</span> param <span class="hljs-keyword">in</span> paramsAfterSplat)...]
|
||
), <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> IdentifierLiteral splatParamName</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-269">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-269">§</a>
|
||
</div>
|
||
<p>Add new expressions to the function body</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> wasEmpty = @body.isEmpty()
|
||
@disallowSuperInParamDefaults()
|
||
@checkSuperCallsInConstructorBody()
|
||
@body.expressions.unshift thisAssignments... <span class="hljs-keyword">unless</span> @expandCtorSuper thisAssignments
|
||
@body.expressions.unshift exprs...
|
||
<span class="hljs-keyword">if</span> @isMethod <span class="hljs-keyword">and</span> @bound <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @isStatic <span class="hljs-keyword">and</span> @classVariable
|
||
boundMethodCheck = <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Literal utility <span class="hljs-string">'boundMethodCheck'</span>, o
|
||
@body.expressions.unshift <span class="hljs-keyword">new</span> Call(boundMethodCheck, [<span class="hljs-keyword">new</span> Value(<span class="hljs-keyword">new</span> ThisLiteral), @classVariable])
|
||
@body.makeReturn() <span class="hljs-keyword">unless</span> wasEmpty <span class="hljs-keyword">or</span> @noReturn</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-270">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-270">§</a>
|
||
</div>
|
||
<p>JavaScript doesn’t allow bound (<code>=></code>) functions to also be generators.
|
||
This is usually caught via <code>Op::compileContinuation</code>, but double-check:</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> @bound <span class="hljs-keyword">and</span> @isGenerator
|
||
yieldNode = @body.contains (node) -> node <span class="hljs-keyword">instanceof</span> Op <span class="hljs-keyword">and</span> node.operator <span class="hljs-keyword">is</span> <span class="hljs-string">'yield'</span>
|
||
(yieldNode <span class="hljs-keyword">or</span> @).error <span class="hljs-string">'yield cannot occur inside bound (fat arrow) functions'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-271">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-271">§</a>
|
||
</div>
|
||
<p>Assemble the output</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> modifiers = []
|
||
modifiers.push <span class="hljs-string">'static'</span> <span class="hljs-keyword">if</span> @isMethod <span class="hljs-keyword">and</span> @isStatic
|
||
modifiers.push <span class="hljs-string">'async'</span> <span class="hljs-keyword">if</span> @isAsync
|
||
<span class="hljs-keyword">unless</span> @isMethod <span class="hljs-keyword">or</span> @bound
|
||
modifiers.push <span class="hljs-string">"function<span class="hljs-subst">#{<span class="hljs-keyword">if</span> @isGenerator <span class="hljs-keyword">then</span> <span class="hljs-string">'*'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>}</span>"</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @isGenerator
|
||
modifiers.push <span class="hljs-string">'*'</span>
|
||
|
||
signature = [@makeCode <span class="hljs-string">'('</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-272">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-272">§</a>
|
||
</div>
|
||
<p>Block comments between a function name and <code>(</code> get output between
|
||
<code>function</code> and <code>(</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> @paramStart?.comments?
|
||
@compileCommentFragments o, @paramStart, signature
|
||
<span class="hljs-keyword">for</span> param, i <span class="hljs-keyword">in</span> params
|
||
signature.push @makeCode <span class="hljs-string">', '</span> <span class="hljs-keyword">if</span> i <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
signature.push @makeCode <span class="hljs-string">'...'</span> <span class="hljs-keyword">if</span> haveSplatParam <span class="hljs-keyword">and</span> i <span class="hljs-keyword">is</span> params.length - <span class="hljs-number">1</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-273">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-273">§</a>
|
||
</div>
|
||
<p>Compile this parameter, but if any generated variables get created
|
||
(e.g. <code>ref</code>), shift those into the parent scope since we can’t put a
|
||
<code>var</code> line inside a function parameter list.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> scopeVariablesCount = o.scope.variables.length
|
||
signature.push param.compileToFragments(o, LEVEL_PAREN)...
|
||
<span class="hljs-keyword">if</span> scopeVariablesCount <span class="hljs-keyword">isnt</span> o.scope.variables.length
|
||
generatedVariables = o.scope.variables.splice scopeVariablesCount
|
||
o.scope.parent.variables.push generatedVariables...
|
||
signature.push @makeCode <span class="hljs-string">')'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-274">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-274">§</a>
|
||
</div>
|
||
<p>Block comments between <code>)</code> and <code>-></code>/<code>=></code> get output between <code>)</code> and <code>{</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> @funcGlyph?.comments?
|
||
comment.unshift = <span class="hljs-literal">no</span> <span class="hljs-keyword">for</span> comment <span class="hljs-keyword">in</span> @funcGlyph.comments
|
||
@compileCommentFragments o, @funcGlyph, signature
|
||
|
||
body = @body.compileWithDeclarations o <span class="hljs-keyword">unless</span> @body.isEmpty()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-275">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-275">§</a>
|
||
</div>
|
||
<p>We need to compile the body before method names to ensure <code>super</code>
|
||
references are handled.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> @isMethod
|
||
[methodScope, o.scope] = [o.scope, o.scope.parent]
|
||
name = @name.compileToFragments o
|
||
name.shift() <span class="hljs-keyword">if</span> name[<span class="hljs-number">0</span>].code <span class="hljs-keyword">is</span> <span class="hljs-string">'.'</span>
|
||
o.scope = methodScope
|
||
|
||
answer = @joinFragmentArrays (@makeCode m <span class="hljs-keyword">for</span> m <span class="hljs-keyword">in</span> modifiers), <span class="hljs-string">' '</span>
|
||
answer.push @makeCode <span class="hljs-string">' '</span> <span class="hljs-keyword">if</span> modifiers.length <span class="hljs-keyword">and</span> name
|
||
answer.push name... <span class="hljs-keyword">if</span> name
|
||
answer.push signature...
|
||
answer.push @makeCode <span class="hljs-string">' =>'</span> <span class="hljs-keyword">if</span> @bound <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @isMethod
|
||
answer.push @makeCode <span class="hljs-string">' {'</span>
|
||
answer.push @makeCode(<span class="hljs-string">'\n'</span>), body..., @makeCode(<span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>"</span>) <span class="hljs-keyword">if</span> body?.length
|
||
answer.push @makeCode <span class="hljs-string">'}'</span>
|
||
|
||
<span class="hljs-keyword">return</span> indentInitial answer, @ <span class="hljs-keyword">if</span> @isMethod
|
||
<span class="hljs-keyword">if</span> @front <span class="hljs-keyword">or</span> (o.level >= LEVEL_ACCESS) <span class="hljs-keyword">then</span> @wrapInParentheses answer <span class="hljs-keyword">else</span> answer
|
||
|
||
updateOptions: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o.scope = del(o, <span class="hljs-string">'classScope'</span>) <span class="hljs-keyword">or</span> @makeScope o.scope
|
||
o.scope.shared = del(o, <span class="hljs-string">'sharedScope'</span>)
|
||
o.indent += TAB
|
||
<span class="hljs-keyword">delete</span> o.bare
|
||
<span class="hljs-keyword">delete</span> o.isExistentialEquals
|
||
|
||
checkForDuplicateParams: <span class="hljs-function">-></span>
|
||
paramNames = []
|
||
@eachParamName (name, node, param) ->
|
||
node.error <span class="hljs-string">"multiple parameters named '<span class="hljs-subst">#{name}</span>'"</span> <span class="hljs-keyword">if</span> name <span class="hljs-keyword">in</span> paramNames
|
||
paramNames.push name
|
||
|
||
eachParamName: <span class="hljs-function"><span class="hljs-params">(iterator)</span> -></span>
|
||
param.eachName iterator <span class="hljs-keyword">for</span> param <span class="hljs-keyword">in</span> @params</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-276">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-276">§</a>
|
||
</div>
|
||
<p>Short-circuit <code>traverseChildren</code> method to prevent it from crossing scope
|
||
boundaries unless <code>crossScope</code> is <code>true</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> traverseChildren: <span class="hljs-function"><span class="hljs-params">(crossScope, func)</span> -></span>
|
||
super(crossScope, func) <span class="hljs-keyword">if</span> crossScope</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-277">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-277">§</a>
|
||
</div>
|
||
<p>Short-circuit <code>replaceInContext</code> method to prevent it from crossing context boundaries. Bound
|
||
functions have the same context.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> replaceInContext: <span class="hljs-function"><span class="hljs-params">(child, replacement)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @bound
|
||
super child, replacement
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-literal">false</span>
|
||
|
||
disallowSuperInParamDefaults: <span class="hljs-function"><span class="hljs-params">({forAst} = {})</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span> <span class="hljs-keyword">unless</span> @ctor
|
||
|
||
@eachSuperCall Block.wrap(@params), <span class="hljs-function"><span class="hljs-params">(superCall)</span> -></span>
|
||
superCall.error <span class="hljs-string">"'super' is not allowed in constructor parameter defaults"</span>
|
||
, checkForThisBeforeSuper: <span class="hljs-keyword">not</span> forAst
|
||
|
||
checkSuperCallsInConstructorBody: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span> <span class="hljs-keyword">unless</span> @ctor
|
||
|
||
seenSuper = @eachSuperCall @body, <span class="hljs-function"><span class="hljs-params">(superCall)</span> =></span>
|
||
superCall.error <span class="hljs-string">"'super' is only allowed in derived class constructors"</span> <span class="hljs-keyword">if</span> @ctor <span class="hljs-keyword">is</span> <span class="hljs-string">'base'</span>
|
||
|
||
seenSuper
|
||
|
||
flagThisParamInDerivedClassConstructorWithoutCallingSuper: <span class="hljs-function"><span class="hljs-params">(param)</span> -></span>
|
||
param.error <span class="hljs-string">"Can't use @params in derived class constructors without calling super"</span>
|
||
|
||
checkForAsyncOrGeneratorConstructor: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @ctor
|
||
@name.error <span class="hljs-string">'Class constructor may not be async'</span> <span class="hljs-keyword">if</span> @isAsync
|
||
@name.error <span class="hljs-string">'Class constructor may not be a generator'</span> <span class="hljs-keyword">if</span> @isGenerator
|
||
|
||
disallowLoneExpansionAndMultipleSplats: <span class="hljs-function">-></span>
|
||
seenSplatParam = <span class="hljs-literal">no</span>
|
||
<span class="hljs-keyword">for</span> param <span class="hljs-keyword">in</span> @params</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-278">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-278">§</a>
|
||
</div>
|
||
<p>Was <code>...</code> used with this parameter? (Only one such parameter is allowed
|
||
per function.)</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> param.splat <span class="hljs-keyword">or</span> param <span class="hljs-keyword">instanceof</span> Expansion
|
||
<span class="hljs-keyword">if</span> seenSplatParam
|
||
param.error <span class="hljs-string">'only one splat or expansion parameter is allowed per function definition'</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> param <span class="hljs-keyword">instanceof</span> Expansion <span class="hljs-keyword">and</span> @params.length <span class="hljs-keyword">is</span> <span class="hljs-number">1</span>
|
||
param.error <span class="hljs-string">'an expansion parameter cannot be the only parameter in a function definition'</span>
|
||
seenSplatParam = <span class="hljs-literal">yes</span>
|
||
|
||
expandCtorSuper: <span class="hljs-function"><span class="hljs-params">(thisAssignments)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span> <span class="hljs-keyword">unless</span> @ctor
|
||
|
||
seenSuper = @eachSuperCall @body, <span class="hljs-function"><span class="hljs-params">(superCall)</span> =></span>
|
||
superCall.expressions = thisAssignments
|
||
|
||
haveThisParam = thisAssignments.length <span class="hljs-keyword">and</span> thisAssignments.length <span class="hljs-keyword">isnt</span> @thisAssignments?.length
|
||
<span class="hljs-keyword">if</span> @ctor <span class="hljs-keyword">is</span> <span class="hljs-string">'derived'</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> seenSuper <span class="hljs-keyword">and</span> haveThisParam
|
||
param = thisAssignments[<span class="hljs-number">0</span>].variable
|
||
@flagThisParamInDerivedClassConstructorWithoutCallingSuper param
|
||
|
||
seenSuper</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-279">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-279">§</a>
|
||
</div>
|
||
<p>Find all super calls in the given context node;
|
||
returns <code>true</code> if <code>iterator</code> is called.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> eachSuperCall: <span class="hljs-function"><span class="hljs-params">(context, iterator, {checkForThisBeforeSuper = <span class="hljs-literal">yes</span>} = {})</span> -></span>
|
||
seenSuper = <span class="hljs-literal">no</span>
|
||
|
||
context.traverseChildren <span class="hljs-literal">yes</span>, <span class="hljs-function"><span class="hljs-params">(child)</span> =></span>
|
||
<span class="hljs-keyword">if</span> child <span class="hljs-keyword">instanceof</span> SuperCall</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-280">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-280">§</a>
|
||
</div>
|
||
<p><code>super</code> in a constructor (the only <code>super</code> without an accessor)
|
||
cannot be given an argument with a reference to <code>this</code>, as that would
|
||
be referencing <code>this</code> before calling <code>super</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">unless</span> child.variable.accessor
|
||
childArgs = child.args.filter (arg) ->
|
||
arg <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> Class <span class="hljs-keyword">and</span> (arg <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> Code <span class="hljs-keyword">or</span> arg.bound)
|
||
Block.wrap(childArgs).traverseChildren <span class="hljs-literal">yes</span>, <span class="hljs-function"><span class="hljs-params">(node)</span> =></span>
|
||
node.error <span class="hljs-string">"Can't call super with @params in derived class constructors"</span> <span class="hljs-keyword">if</span> node.this
|
||
seenSuper = <span class="hljs-literal">yes</span>
|
||
iterator child
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> checkForThisBeforeSuper <span class="hljs-keyword">and</span> child <span class="hljs-keyword">instanceof</span> ThisLiteral <span class="hljs-keyword">and</span> @ctor <span class="hljs-keyword">is</span> <span class="hljs-string">'derived'</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> seenSuper
|
||
child.error <span class="hljs-string">"Can't reference 'this' before calling super in derived class constructors"</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-281">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-281">§</a>
|
||
</div>
|
||
<p><code>super</code> has the same target in bound (arrow) functions, so check them too</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> child <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> SuperCall <span class="hljs-keyword">and</span> (child <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> Code <span class="hljs-keyword">or</span> child.bound)
|
||
|
||
seenSuper
|
||
|
||
propagateLhs: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">for</span> param <span class="hljs-keyword">in</span> @params
|
||
{name} = param
|
||
<span class="hljs-keyword">if</span> name <span class="hljs-keyword">instanceof</span> Arr <span class="hljs-keyword">or</span> name <span class="hljs-keyword">instanceof</span> Obj
|
||
name.propagateLhs <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> param <span class="hljs-keyword">instanceof</span> Expansion
|
||
param.lhs = <span class="hljs-literal">yes</span>
|
||
|
||
astAddParamsToScope: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@eachParamName (name) ->
|
||
o.scope.add name, <span class="hljs-string">'param'</span>
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@updateOptions o
|
||
@checkForAsyncOrGeneratorConstructor()
|
||
@checkForDuplicateParams()
|
||
@disallowSuperInParamDefaults forAst: <span class="hljs-literal">yes</span>
|
||
@disallowLoneExpansionAndMultipleSplats()
|
||
seenSuper = @checkSuperCallsInConstructorBody()
|
||
<span class="hljs-keyword">if</span> @ctor <span class="hljs-keyword">is</span> <span class="hljs-string">'derived'</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> seenSuper
|
||
@eachParamName (name, node) =>
|
||
<span class="hljs-keyword">if</span> node.this
|
||
@flagThisParamInDerivedClassConstructorWithoutCallingSuper node
|
||
@astAddParamsToScope o
|
||
@body.makeReturn <span class="hljs-literal">null</span>, <span class="hljs-literal">yes</span> <span class="hljs-keyword">unless</span> @body.isEmpty() <span class="hljs-keyword">or</span> @noReturn
|
||
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @isMethod
|
||
<span class="hljs-string">'ClassMethod'</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @bound
|
||
<span class="hljs-string">'ArrowFunctionExpression'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'FunctionExpression'</span>
|
||
|
||
paramForAst: <span class="hljs-function"><span class="hljs-params">(param)</span> -></span>
|
||
<span class="hljs-keyword">return</span> param <span class="hljs-keyword">if</span> param <span class="hljs-keyword">instanceof</span> Expansion
|
||
{name, value, splat} = param
|
||
<span class="hljs-keyword">if</span> splat
|
||
<span class="hljs-keyword">new</span> Splat name, lhs: <span class="hljs-literal">yes</span>, postfix: splat.postfix
|
||
.withLocationDataFrom param
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> value?
|
||
<span class="hljs-keyword">new</span> Assign name, value, <span class="hljs-literal">null</span>, param: <span class="hljs-literal">yes</span>
|
||
.withLocationDataFrom locationData: mergeLocationData name.locationData, value.locationData
|
||
<span class="hljs-keyword">else</span>
|
||
name
|
||
|
||
methodAstProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-function"> <span class="hljs-title">getIsComputed</span> = =></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> @name <span class="hljs-keyword">instanceof</span> Index
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> @name <span class="hljs-keyword">instanceof</span> ComputedPropertyName
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> @name.name <span class="hljs-keyword">instanceof</span> ComputedPropertyName
|
||
<span class="hljs-literal">no</span>
|
||
|
||
<span class="hljs-keyword">return</span>
|
||
static: !!@isStatic
|
||
key: @name.ast o
|
||
computed: getIsComputed()
|
||
kind:
|
||
<span class="hljs-keyword">if</span> @ctor
|
||
<span class="hljs-string">'constructor'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'method'</span>
|
||
operator: @operatorToken?.value ? <span class="hljs-string">'='</span>
|
||
staticClassName: @isStatic.staticClassName?.ast(o) ? <span class="hljs-literal">null</span>
|
||
bound: !!@bound
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.assign
|
||
params: @paramForAst(param).ast(o) <span class="hljs-keyword">for</span> param <span class="hljs-keyword">in</span> @params
|
||
body: @body.ast (<span class="hljs-built_in">Object</span>.assign {}, o, checkForDirectives: <span class="hljs-literal">yes</span>), LEVEL_TOP
|
||
generator: !!@isGenerator
|
||
async: !!@isAsync</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-282">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-282">§</a>
|
||
</div>
|
||
<p>We never generate named functions, so specify <code>id</code> as <code>null</code>, which
|
||
matches the Babel AST for anonymous function expressions/arrow functions</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> id: <span class="hljs-literal">null</span>
|
||
hasIndentedBody: @body.locationData.first_line > @funcGlyph?.locationData.first_line
|
||
,
|
||
<span class="hljs-keyword">if</span> @isMethod <span class="hljs-keyword">then</span> @methodAstProperties o <span class="hljs-keyword">else</span> {}
|
||
|
||
astLocationData: <span class="hljs-function">-></span>
|
||
functionLocationData = super()
|
||
<span class="hljs-keyword">return</span> functionLocationData <span class="hljs-keyword">unless</span> @isMethod
|
||
|
||
astLocationData = mergeAstLocationData @name.astLocationData(), functionLocationData
|
||
<span class="hljs-keyword">if</span> @isStatic.staticClassName?
|
||
astLocationData = mergeAstLocationData @isStatic.staticClassName.astLocationData(), astLocationData
|
||
astLocationData</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-283">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-283">§</a>
|
||
</div>
|
||
<h3 id="param">Param</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-284">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-284">§</a>
|
||
</div>
|
||
<p>A parameter in a function definition. Beyond a typical JavaScript parameter,
|
||
these parameters can also attach themselves to the context of the function,
|
||
as well as be a splat, gathering up a group of parameters into an array.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Param = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Param</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@name, @value, @splat)</span> -></span>
|
||
super()
|
||
|
||
message = isUnassignable @name.unwrapAll().value
|
||
@name.error message <span class="hljs-keyword">if</span> message
|
||
<span class="hljs-keyword">if</span> @name <span class="hljs-keyword">instanceof</span> Obj <span class="hljs-keyword">and</span> @name.generated
|
||
token = @name.objects[<span class="hljs-number">0</span>].operatorToken
|
||
token.error <span class="hljs-string">"unexpected <span class="hljs-subst">#{token.value}</span>"</span>
|
||
|
||
children: [<span class="hljs-string">'name'</span>, <span class="hljs-string">'value'</span>]
|
||
|
||
compileToFragments: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@name.compileToFragments o, LEVEL_LIST
|
||
|
||
compileToFragmentsWithoutComments: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@name.compileToFragmentsWithoutComments o, LEVEL_LIST
|
||
|
||
asReference: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> @reference <span class="hljs-keyword">if</span> @reference
|
||
node = @name
|
||
<span class="hljs-keyword">if</span> node.this
|
||
name = node.properties[<span class="hljs-number">0</span>].name.value
|
||
name = <span class="hljs-string">"_<span class="hljs-subst">#{name}</span>"</span> <span class="hljs-keyword">if</span> name <span class="hljs-keyword">in</span> JS_FORBIDDEN
|
||
node = <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.freeVariable name
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> node.shouldCache()
|
||
node = <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.freeVariable <span class="hljs-string">'arg'</span>
|
||
node = <span class="hljs-keyword">new</span> Value node
|
||
node.updateLocationDataIfMissing @locationData
|
||
@reference = node
|
||
|
||
shouldCache: <span class="hljs-function">-></span>
|
||
@name.shouldCache()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-285">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-285">§</a>
|
||
</div>
|
||
<p>Iterates the name or names of a <code>Param</code>.
|
||
In a sense, a destructured parameter represents multiple JS parameters. This
|
||
method allows to iterate them all.
|
||
The <code>iterator</code> function will be called as <code>iterator(name, node)</code> where
|
||
<code>name</code> is the name of the parameter and <code>node</code> is the AST node corresponding
|
||
to that name.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> eachName: <span class="hljs-function"><span class="hljs-params">(iterator, name = @name)</span> -></span>
|
||
<span class="hljs-function"> <span class="hljs-title">checkAssignabilityOfLiteral</span> = <span class="hljs-params">(literal)</span> -></span>
|
||
message = isUnassignable literal.value
|
||
<span class="hljs-keyword">if</span> message
|
||
literal.error message
|
||
<span class="hljs-keyword">unless</span> literal.isAssignable()
|
||
literal.error <span class="hljs-string">"'<span class="hljs-subst">#{literal.value}</span>' can't be assigned"</span>
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">atParam</span> = <span class="hljs-params">(obj, originalObj = <span class="hljs-literal">null</span>)</span> =></span> iterator <span class="hljs-string">"@<span class="hljs-subst">#{obj.properties[<span class="hljs-number">0</span>].name.value}</span>"</span>, obj, @, originalObj
|
||
<span class="hljs-keyword">if</span> name <span class="hljs-keyword">instanceof</span> Call
|
||
name.error <span class="hljs-string">"Function invocation can't be assigned"</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-286">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-286">§</a>
|
||
</div>
|
||
<ul>
|
||
<li>simple literals <code>foo</code></li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> name <span class="hljs-keyword">instanceof</span> Literal
|
||
checkAssignabilityOfLiteral name
|
||
<span class="hljs-keyword">return</span> iterator name.value, name, @</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-287">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-287">§</a>
|
||
</div>
|
||
<ul>
|
||
<li>at-params <code>@foo</code></li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> atParam name <span class="hljs-keyword">if</span> name <span class="hljs-keyword">instanceof</span> Value
|
||
<span class="hljs-keyword">for</span> obj <span class="hljs-keyword">in</span> name.objects ? []</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-288">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-288">§</a>
|
||
</div>
|
||
<p>Save original obj.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> nObj = obj</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-289">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-289">§</a>
|
||
</div>
|
||
<ul>
|
||
<li>destructured parameter with default value</li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> obj <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> obj.context?
|
||
obj = obj.variable</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-290">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-290">§</a>
|
||
</div>
|
||
<ul>
|
||
<li>assignments within destructured parameters <code>{foo:bar}</code></li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> obj <span class="hljs-keyword">instanceof</span> Assign</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-291">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-291">§</a>
|
||
</div>
|
||
<p>… possibly with a default value</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> obj.value <span class="hljs-keyword">instanceof</span> Assign
|
||
obj = obj.value.variable
|
||
<span class="hljs-keyword">else</span>
|
||
obj = obj.value
|
||
@eachName iterator, obj.unwrap()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-292">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-292">§</a>
|
||
</div>
|
||
<ul>
|
||
<li>splats within destructured parameters <code>[xs...]</code></li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> obj <span class="hljs-keyword">instanceof</span> Splat
|
||
node = obj.name.unwrap()
|
||
iterator node.value, node, @
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> obj <span class="hljs-keyword">instanceof</span> Value</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-293">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-293">§</a>
|
||
</div>
|
||
<ul>
|
||
<li>destructured parameters within destructured parameters <code>[{a}]</code></li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> obj.isArray() <span class="hljs-keyword">or</span> obj.isObject()
|
||
@eachName iterator, obj.base</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-294">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-294">§</a>
|
||
</div>
|
||
<ul>
|
||
<li>at-params within destructured parameters <code>{@foo}</code></li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> obj.this
|
||
atParam obj, nObj</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-295">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-295">§</a>
|
||
</div>
|
||
<ul>
|
||
<li>simple destructured parameters {foo}</li>
|
||
</ul>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">else</span>
|
||
checkAssignabilityOfLiteral obj.base
|
||
iterator obj.base.value, obj.base, @
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> obj <span class="hljs-keyword">instanceof</span> Elision
|
||
obj
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> obj <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> Expansion
|
||
obj.error <span class="hljs-string">"illegal parameter <span class="hljs-subst">#{obj.compile()}</span>"</span>
|
||
<span class="hljs-keyword">return</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-296">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-296">§</a>
|
||
</div>
|
||
<p>Rename a param by replacing the given AST node for a name with a new node.
|
||
This needs to ensure that the the source for object destructuring does not change.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> renameParam: <span class="hljs-function"><span class="hljs-params">(node, newNode)</span> -></span>
|
||
<span class="hljs-function"> <span class="hljs-title">isNode</span> = <span class="hljs-params">(candidate)</span> -></span> candidate <span class="hljs-keyword">is</span> node
|
||
<span class="hljs-function"> <span class="hljs-title">replacement</span> = <span class="hljs-params">(node, parent)</span> =></span>
|
||
<span class="hljs-keyword">if</span> parent <span class="hljs-keyword">instanceof</span> Obj
|
||
key = node
|
||
key = node.properties[<span class="hljs-number">0</span>].name <span class="hljs-keyword">if</span> node.this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-297">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-297">§</a>
|
||
</div>
|
||
<p>No need to assign a new variable for the destructured variable if the variable isn’t reserved.
|
||
Examples:
|
||
<code>({@foo}) -></code> should compile to <code>({foo}) { this.foo = foo}</code>
|
||
<code>foo = 1; ({@foo}) -></code> should compile to <code>foo = 1; ({foo:foo1}) { this.foo = foo1 }</code></p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> node.this <span class="hljs-keyword">and</span> key.value <span class="hljs-keyword">is</span> newNode.value
|
||
<span class="hljs-keyword">new</span> Value newNode
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">new</span> Assign <span class="hljs-keyword">new</span> Value(key), newNode, <span class="hljs-string">'object'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
newNode
|
||
|
||
@replaceInContext isNode, replacement</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-298">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-298">§</a>
|
||
</div>
|
||
<h3 id="splat">Splat</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-299">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-299">§</a>
|
||
</div>
|
||
<p>A splat, either as a parameter to a function, an argument to a call,
|
||
or as part of a destructuring assignment.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Splat = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Splat</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(name, {@lhs, @postfix = <span class="hljs-literal">true</span>} = {})</span> -></span>
|
||
super()
|
||
@name = <span class="hljs-keyword">if</span> name.compile <span class="hljs-keyword">then</span> name <span class="hljs-keyword">else</span> <span class="hljs-keyword">new</span> Literal name
|
||
|
||
children: [<span class="hljs-string">'name'</span>]
|
||
|
||
shouldCache: <span class="hljs-function">-></span> <span class="hljs-literal">no</span>
|
||
|
||
isAssignable: <span class="hljs-function"><span class="hljs-params">({allowComplexSplat = <span class="hljs-literal">no</span>} = {})</span>-></span>
|
||
<span class="hljs-keyword">return</span> allowComplexSplat <span class="hljs-keyword">if</span> @name <span class="hljs-keyword">instanceof</span> Obj <span class="hljs-keyword">or</span> @name <span class="hljs-keyword">instanceof</span> Parens
|
||
@name.isAssignable() <span class="hljs-keyword">and</span> (<span class="hljs-keyword">not</span> @name.isAtomic <span class="hljs-keyword">or</span> @name.isAtomic())
|
||
|
||
assigns: <span class="hljs-function"><span class="hljs-params">(name)</span> -></span>
|
||
@name.assigns name
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
compiledSplat = [@makeCode(<span class="hljs-string">'...'</span>), @name.compileToFragments(o, LEVEL_OP)...]
|
||
<span class="hljs-keyword">return</span> compiledSplat <span class="hljs-keyword">unless</span> @jsx
|
||
<span class="hljs-keyword">return</span> [@makeCode(<span class="hljs-string">'{'</span>), compiledSplat..., @makeCode(<span class="hljs-string">'}'</span>)]
|
||
|
||
unwrap: <span class="hljs-function">-></span> @name
|
||
|
||
propagateLhs: <span class="hljs-function"><span class="hljs-params">(setLhs)</span> -></span>
|
||
@lhs = <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> setLhs
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> @lhs
|
||
@name.propagateLhs? <span class="hljs-literal">yes</span>
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @jsx
|
||
<span class="hljs-string">'JSXSpreadAttribute'</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @lhs
|
||
<span class="hljs-string">'RestElement'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'SpreadElement'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span> {
|
||
argument: @name.ast o, LEVEL_OP
|
||
@postfix
|
||
}</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-300">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-300">§</a>
|
||
</div>
|
||
<h3 id="expansion">Expansion</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-301">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-301">§</a>
|
||
</div>
|
||
<p>Used to skip values inside an array destructuring (pattern matching) or
|
||
parameter list.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Expansion = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Expansion</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
|
||
shouldCache: NO
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@throwLhsError()
|
||
|
||
asReference: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
this
|
||
|
||
eachName: <span class="hljs-function"><span class="hljs-params">(iterator)</span> -></span>
|
||
|
||
throwLhsError: <span class="hljs-function">-></span>
|
||
@error <span class="hljs-string">'Expansion must be used inside a destructuring assignment or parameter list'</span>
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">unless</span> @lhs
|
||
@throwLhsError()
|
||
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'RestElement'</span>
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
argument: <span class="hljs-literal">null</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-302">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-302">§</a>
|
||
</div>
|
||
<h3 id="elision">Elision</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-303">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-303">§</a>
|
||
</div>
|
||
<p>Array elision element (for example, [,a, , , b, , c, ,]).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Elision = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Elision</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
|
||
isAssignable: YES
|
||
|
||
shouldCache: NO
|
||
|
||
compileToFragments: <span class="hljs-function"><span class="hljs-params">(o, level)</span> -></span>
|
||
fragment = super o, level
|
||
fragment.isElision = <span class="hljs-literal">yes</span>
|
||
fragment
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[@makeCode <span class="hljs-string">', '</span>]
|
||
|
||
asReference: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
this
|
||
|
||
eachName: <span class="hljs-function"><span class="hljs-params">(iterator)</span> -></span>
|
||
|
||
astNode: <span class="hljs-function">-></span>
|
||
<span class="hljs-literal">null</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-304">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-304">§</a>
|
||
</div>
|
||
<h3 id="while">While</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-305">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-305">§</a>
|
||
</div>
|
||
<p>A while loop, the only sort of low-level loop exposed by CoffeeScript. From
|
||
it, all other loops can be manufactured. Useful in cases where you need more
|
||
flexibility or more speed than a comprehension can provide.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.While = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">While</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@condition, {invert: @inverted, @guard, @isLoop} = {})</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'condition'</span>, <span class="hljs-string">'guard'</span>, <span class="hljs-string">'body'</span>]
|
||
|
||
isStatement: YES
|
||
|
||
makeReturn: <span class="hljs-function"><span class="hljs-params">(results, mark)</span> -></span>
|
||
<span class="hljs-keyword">return</span> super(results, mark) <span class="hljs-keyword">if</span> results
|
||
@returns = <span class="hljs-keyword">not</span> @jumps()
|
||
<span class="hljs-keyword">if</span> mark
|
||
@body.makeReturn(results, mark) <span class="hljs-keyword">if</span> @returns
|
||
<span class="hljs-keyword">return</span>
|
||
this
|
||
|
||
addBody: <span class="hljs-function"><span class="hljs-params">(@body)</span> -></span>
|
||
this
|
||
|
||
jumps: <span class="hljs-function">-></span>
|
||
{expressions} = @body
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> expressions.length
|
||
<span class="hljs-keyword">for</span> node <span class="hljs-keyword">in</span> expressions
|
||
<span class="hljs-keyword">return</span> jumpNode <span class="hljs-keyword">if</span> jumpNode = node.jumps loop: <span class="hljs-literal">yes</span>
|
||
<span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-306">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-306">§</a>
|
||
</div>
|
||
<p>The main difference from a JavaScript <em>while</em> is that the CoffeeScript
|
||
<em>while</em> can be used as a part of a larger expression – while loops may
|
||
return an array containing the computed result of each iteration.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o.indent += TAB
|
||
set = <span class="hljs-string">''</span>
|
||
{body} = this
|
||
<span class="hljs-keyword">if</span> body.isEmpty()
|
||
body = @makeCode <span class="hljs-string">''</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> @returns
|
||
body.makeReturn rvar = o.scope.freeVariable <span class="hljs-string">'results'</span>
|
||
set = <span class="hljs-string">"<span class="hljs-subst">#{@tab}</span><span class="hljs-subst">#{rvar}</span> = [];\n"</span>
|
||
<span class="hljs-keyword">if</span> @guard
|
||
<span class="hljs-keyword">if</span> body.expressions.length > <span class="hljs-number">1</span>
|
||
body.expressions.unshift <span class="hljs-keyword">new</span> If (<span class="hljs-keyword">new</span> Parens @guard).invert(), <span class="hljs-keyword">new</span> StatementLiteral <span class="hljs-string">"continue"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
body = Block.wrap [<span class="hljs-keyword">new</span> If @guard, body] <span class="hljs-keyword">if</span> @guard
|
||
body = [].concat @makeCode(<span class="hljs-string">"\n"</span>), (body.compileToFragments o, LEVEL_TOP), @makeCode(<span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>"</span>)
|
||
answer = [].concat @makeCode(set + @tab + <span class="hljs-string">"while ("</span>), @processedCondition().compileToFragments(o, LEVEL_PAREN),
|
||
@makeCode(<span class="hljs-string">") {"</span>), body, @makeCode(<span class="hljs-string">"}"</span>)
|
||
<span class="hljs-keyword">if</span> @returns
|
||
answer.push @makeCode <span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>return <span class="hljs-subst">#{rvar}</span>;"</span>
|
||
answer
|
||
|
||
processedCondition: <span class="hljs-function">-></span>
|
||
@processedConditionCache ?= <span class="hljs-keyword">if</span> @inverted <span class="hljs-keyword">then</span> @condition.invert() <span class="hljs-keyword">else</span> @condition
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'WhileStatement'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
test: @condition.ast o, LEVEL_PAREN
|
||
body: @body.ast o, LEVEL_TOP
|
||
guard: @guard?.ast(o) ? <span class="hljs-literal">null</span>
|
||
inverted: !!@inverted
|
||
postfix: !!@postfix
|
||
loop: !!@isLoop</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-307">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-307">§</a>
|
||
</div>
|
||
<h3 id="op">Op</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-308">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-308">§</a>
|
||
</div>
|
||
<p>Simple Arithmetic and logical operations. Performs some conversion from
|
||
CoffeeScript operations into their JavaScript equivalents.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Op = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Op</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(op, first, second, flip, {@invertOperator, @originalOperator = op} = {})</span> -></span>
|
||
super()
|
||
|
||
<span class="hljs-keyword">if</span> op <span class="hljs-keyword">is</span> <span class="hljs-string">'new'</span>
|
||
<span class="hljs-keyword">if</span> ((firstCall = unwrapped = first.unwrap()) <span class="hljs-keyword">instanceof</span> Call <span class="hljs-keyword">or</span> (firstCall = unwrapped.base) <span class="hljs-keyword">instanceof</span> Call) <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> firstCall.<span class="hljs-keyword">do</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> firstCall.isNew
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Value firstCall.newInstance(), <span class="hljs-keyword">if</span> firstCall <span class="hljs-keyword">is</span> unwrapped <span class="hljs-keyword">then</span> [] <span class="hljs-keyword">else</span> unwrapped.properties
|
||
first = <span class="hljs-keyword">new</span> Parens first <span class="hljs-keyword">unless</span> first <span class="hljs-keyword">instanceof</span> Parens <span class="hljs-keyword">or</span> first.unwrap() <span class="hljs-keyword">instanceof</span> IdentifierLiteral <span class="hljs-keyword">or</span> first.hasProperties?()
|
||
call = <span class="hljs-keyword">new</span> Call first, []
|
||
call.locationData = @locationData
|
||
call.isNew = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">return</span> call
|
||
|
||
@operator = CONVERSIONS[op] <span class="hljs-keyword">or</span> op
|
||
@first = first
|
||
@second = second
|
||
@flip = !!flip
|
||
|
||
<span class="hljs-keyword">if</span> @operator <span class="hljs-keyword">in</span> [<span class="hljs-string">'--'</span>, <span class="hljs-string">'++'</span>]
|
||
message = isUnassignable @first.unwrapAll().value
|
||
@first.error message <span class="hljs-keyword">if</span> message
|
||
|
||
<span class="hljs-keyword">return</span> this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-309">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-309">§</a>
|
||
</div>
|
||
<p>The map of conversions from CoffeeScript to JavaScript symbols.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> CONVERSIONS =
|
||
<span class="hljs-string">'=='</span>: <span class="hljs-string">'==='</span>
|
||
<span class="hljs-string">'!='</span>: <span class="hljs-string">'!=='</span>
|
||
<span class="hljs-string">'of'</span>: <span class="hljs-string">'in'</span>
|
||
<span class="hljs-string">'yieldfrom'</span>: <span class="hljs-string">'yield*'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-310">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-310">§</a>
|
||
</div>
|
||
<p>The map of invertible operators.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> INVERSIONS =
|
||
<span class="hljs-string">'!=='</span>: <span class="hljs-string">'==='</span>
|
||
<span class="hljs-string">'==='</span>: <span class="hljs-string">'!=='</span>
|
||
|
||
children: [<span class="hljs-string">'first'</span>, <span class="hljs-string">'second'</span>]
|
||
|
||
isNumber: <span class="hljs-function">-></span>
|
||
@isUnary() <span class="hljs-keyword">and</span> @operator <span class="hljs-keyword">in</span> [<span class="hljs-string">'+'</span>, <span class="hljs-string">'-'</span>] <span class="hljs-keyword">and</span>
|
||
@first <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> @first.isNumber()
|
||
|
||
isAwait: <span class="hljs-function">-></span>
|
||
@operator <span class="hljs-keyword">is</span> <span class="hljs-string">'await'</span>
|
||
|
||
isYield: <span class="hljs-function">-></span>
|
||
@operator <span class="hljs-keyword">in</span> [<span class="hljs-string">'yield'</span>, <span class="hljs-string">'yield*'</span>]
|
||
|
||
isUnary: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">not</span> @second
|
||
|
||
shouldCache: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">not</span> @isNumber()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-311">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-311">§</a>
|
||
</div>
|
||
<p>Am I capable of
|
||
<a href="https://docs.python.org/3/reference/expressions.html#not-in">Python-style comparison chaining</a>?</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> isChainable: <span class="hljs-function">-></span>
|
||
@operator <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>]
|
||
|
||
isChain: <span class="hljs-function">-></span>
|
||
@isChainable() <span class="hljs-keyword">and</span> @first.isChainable()
|
||
|
||
invert: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @isInOperator()
|
||
@invertOperator = <span class="hljs-string">'!'</span>
|
||
<span class="hljs-keyword">return</span> @
|
||
<span class="hljs-keyword">if</span> @isChain()
|
||
allInvertable = <span class="hljs-literal">yes</span>
|
||
curr = this
|
||
<span class="hljs-keyword">while</span> curr <span class="hljs-keyword">and</span> curr.operator
|
||
allInvertable <span class="hljs-keyword">and</span>= (curr.operator <span class="hljs-keyword">of</span> INVERSIONS)
|
||
curr = curr.first
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Parens(this).invert() <span class="hljs-keyword">unless</span> allInvertable
|
||
curr = this
|
||
<span class="hljs-keyword">while</span> curr <span class="hljs-keyword">and</span> curr.operator
|
||
curr.invert = !curr.invert
|
||
curr.operator = INVERSIONS[curr.operator]
|
||
curr = curr.first
|
||
this
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> op = INVERSIONS[@operator]
|
||
@operator = op
|
||
<span class="hljs-keyword">if</span> @first.unwrap() <span class="hljs-keyword">instanceof</span> Op
|
||
@first.invert()
|
||
this
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @second
|
||
<span class="hljs-keyword">new</span> Parens(this).invert()
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @operator <span class="hljs-keyword">is</span> <span class="hljs-string">'!'</span> <span class="hljs-keyword">and</span> (fst = @first.unwrap()) <span class="hljs-keyword">instanceof</span> Op <span class="hljs-keyword">and</span>
|
||
fst.operator <span class="hljs-keyword">in</span> [<span class="hljs-string">'!'</span>, <span class="hljs-string">'in'</span>, <span class="hljs-string">'instanceof'</span>]
|
||
fst
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">new</span> Op <span class="hljs-string">'!'</span>, this
|
||
|
||
unfoldSoak: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@operator <span class="hljs-keyword">in</span> [<span class="hljs-string">'++'</span>, <span class="hljs-string">'--'</span>, <span class="hljs-string">'delete'</span>] <span class="hljs-keyword">and</span> unfoldSoak o, this, <span class="hljs-string">'first'</span>
|
||
|
||
generateDo: <span class="hljs-function"><span class="hljs-params">(exp)</span> -></span>
|
||
passedParams = []
|
||
func = <span class="hljs-keyword">if</span> exp <span class="hljs-keyword">instanceof</span> Assign <span class="hljs-keyword">and</span> (ref = exp.value.unwrap()) <span class="hljs-keyword">instanceof</span> Code
|
||
ref
|
||
<span class="hljs-keyword">else</span>
|
||
exp
|
||
<span class="hljs-keyword">for</span> param <span class="hljs-keyword">in</span> func.params <span class="hljs-keyword">or</span> []
|
||
<span class="hljs-keyword">if</span> param.value
|
||
passedParams.push param.value
|
||
<span class="hljs-keyword">delete</span> param.value
|
||
<span class="hljs-keyword">else</span>
|
||
passedParams.push param
|
||
call = <span class="hljs-keyword">new</span> Call exp, passedParams
|
||
call.<span class="hljs-keyword">do</span> = <span class="hljs-literal">yes</span>
|
||
call
|
||
|
||
isInOperator: <span class="hljs-function">-></span>
|
||
@originalOperator <span class="hljs-keyword">is</span> <span class="hljs-string">'in'</span>
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @isInOperator()
|
||
inNode = <span class="hljs-keyword">new</span> In @first, @second
|
||
<span class="hljs-keyword">return</span> (<span class="hljs-keyword">if</span> @invertOperator <span class="hljs-keyword">then</span> inNode.invert() <span class="hljs-keyword">else</span> inNode).compileNode o
|
||
<span class="hljs-keyword">if</span> @invertOperator
|
||
@invertOperator = <span class="hljs-literal">null</span>
|
||
<span class="hljs-keyword">return</span> @invert().compileNode(o)
|
||
<span class="hljs-keyword">return</span> Op::generateDo(@first).compileNode o <span class="hljs-keyword">if</span> @operator <span class="hljs-keyword">is</span> <span class="hljs-string">'do'</span>
|
||
isChain = @isChain()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-312">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-312">§</a>
|
||
</div>
|
||
<p>In chains, there’s no need to wrap bare obj literals in parens,
|
||
as the chained expression is wrapped.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> @first.front = @front <span class="hljs-keyword">unless</span> isChain
|
||
@checkDeleteOperand o
|
||
<span class="hljs-keyword">return</span> @compileContinuation o <span class="hljs-keyword">if</span> @isYield() <span class="hljs-keyword">or</span> @isAwait()
|
||
<span class="hljs-keyword">return</span> @compileUnary o <span class="hljs-keyword">if</span> @isUnary()
|
||
<span class="hljs-keyword">return</span> @compileChain o <span class="hljs-keyword">if</span> isChain
|
||
<span class="hljs-keyword">switch</span> @operator
|
||
<span class="hljs-keyword">when</span> <span class="hljs-string">'?'</span> <span class="hljs-keyword">then</span> @compileExistence o, @second.isDefaultValue
|
||
<span class="hljs-keyword">when</span> <span class="hljs-string">'//'</span> <span class="hljs-keyword">then</span> @compileFloorDivision o
|
||
<span class="hljs-keyword">when</span> <span class="hljs-string">'%%'</span> <span class="hljs-keyword">then</span> @compileModulo o
|
||
<span class="hljs-keyword">else</span>
|
||
lhs = @first.compileToFragments o, LEVEL_OP
|
||
rhs = @second.compileToFragments o, LEVEL_OP
|
||
answer = [].concat lhs, @makeCode(<span class="hljs-string">" <span class="hljs-subst">#{@operator}</span> "</span>), rhs
|
||
<span class="hljs-keyword">if</span> o.level <= LEVEL_OP <span class="hljs-keyword">then</span> answer <span class="hljs-keyword">else</span> @wrapInParentheses answer</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-313">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-313">§</a>
|
||
</div>
|
||
<p>Mimic Python’s chained comparisons when multiple comparison operators are
|
||
used sequentially. For example:</p>
|
||
<pre><code>bin/coffee -e <span class="hljs-string">'console.log 50 < 65 > 10'</span>
|
||
<span class="hljs-literal">true</span>
|
||
</code></pre>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileChain: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[@first.second, shared] = @first.second.cache o
|
||
fst = @first.compileToFragments o, LEVEL_OP
|
||
fragments = fst.concat @makeCode(<span class="hljs-string">" <span class="hljs-subst">#{<span class="hljs-keyword">if</span> @invert <span class="hljs-keyword">then</span> <span class="hljs-string">'&&'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'||'</span>}</span> "</span>),
|
||
(shared.compileToFragments o), @makeCode(<span class="hljs-string">" <span class="hljs-subst">#{@operator}</span> "</span>), (@second.compileToFragments o, LEVEL_OP)
|
||
@wrapInParentheses fragments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-314">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-314">§</a>
|
||
</div>
|
||
<p>Keep reference to the left expression, unless this an existential assignment</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileExistence: <span class="hljs-function"><span class="hljs-params">(o, checkOnlyUndefined)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @first.shouldCache()
|
||
ref = <span class="hljs-keyword">new</span> IdentifierLiteral o.scope.freeVariable <span class="hljs-string">'ref'</span>
|
||
fst = <span class="hljs-keyword">new</span> Parens <span class="hljs-keyword">new</span> Assign ref, @first
|
||
<span class="hljs-keyword">else</span>
|
||
fst = @first
|
||
ref = fst
|
||
<span class="hljs-keyword">new</span> If(<span class="hljs-keyword">new</span> Existence(fst, checkOnlyUndefined), ref, type: <span class="hljs-string">'if'</span>).addElse(@second).compileToFragments o</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-315">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-315">§</a>
|
||
</div>
|
||
<p>Compile a unary <strong>Op</strong>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileUnary: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
parts = []
|
||
op = @operator
|
||
parts.push [@makeCode op]
|
||
<span class="hljs-keyword">if</span> op <span class="hljs-keyword">is</span> <span class="hljs-string">'!'</span> <span class="hljs-keyword">and</span> @first <span class="hljs-keyword">instanceof</span> Existence
|
||
@first.negated = <span class="hljs-keyword">not</span> @first.negated
|
||
<span class="hljs-keyword">return</span> @first.compileToFragments o
|
||
<span class="hljs-keyword">if</span> o.level >= LEVEL_ACCESS
|
||
<span class="hljs-keyword">return</span> (<span class="hljs-keyword">new</span> Parens this).compileToFragments o
|
||
plusMinus = op <span class="hljs-keyword">in</span> [<span class="hljs-string">'+'</span>, <span class="hljs-string">'-'</span>]
|
||
parts.push [@makeCode(<span class="hljs-string">' '</span>)] <span class="hljs-keyword">if</span> op <span class="hljs-keyword">in</span> [<span class="hljs-string">'typeof'</span>, <span class="hljs-string">'delete'</span>] <span class="hljs-keyword">or</span>
|
||
plusMinus <span class="hljs-keyword">and</span> @first <span class="hljs-keyword">instanceof</span> Op <span class="hljs-keyword">and</span> @first.operator <span class="hljs-keyword">is</span> op
|
||
<span class="hljs-keyword">if</span> plusMinus <span class="hljs-keyword">and</span> @first <span class="hljs-keyword">instanceof</span> Op
|
||
@first = <span class="hljs-keyword">new</span> Parens @first
|
||
parts.push @first.compileToFragments o, LEVEL_OP
|
||
parts.reverse() <span class="hljs-keyword">if</span> @flip
|
||
@joinFragmentArrays parts, <span class="hljs-string">''</span>
|
||
|
||
compileContinuation: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
parts = []
|
||
op = @operator
|
||
@checkContinuation o <span class="hljs-keyword">unless</span> @isAwait()
|
||
<span class="hljs-keyword">if</span> <span class="hljs-string">'expression'</span> <span class="hljs-keyword">in</span> <span class="hljs-built_in">Object</span>.keys(@first) <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> (@first <span class="hljs-keyword">instanceof</span> Throw)
|
||
parts.push @first.expression.compileToFragments o, LEVEL_OP <span class="hljs-keyword">if</span> @first.expression?
|
||
<span class="hljs-keyword">else</span>
|
||
parts.push [@makeCode <span class="hljs-string">"("</span>] <span class="hljs-keyword">if</span> o.level >= LEVEL_PAREN
|
||
parts.push [@makeCode op]
|
||
parts.push [@makeCode <span class="hljs-string">" "</span>] <span class="hljs-keyword">if</span> @first.base?.value <span class="hljs-keyword">isnt</span> <span class="hljs-string">''</span>
|
||
parts.push @first.compileToFragments o, LEVEL_OP
|
||
parts.push [@makeCode <span class="hljs-string">")"</span>] <span class="hljs-keyword">if</span> o.level >= LEVEL_PAREN
|
||
@joinFragmentArrays parts, <span class="hljs-string">''</span>
|
||
|
||
checkContinuation: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">unless</span> o.scope.parent?
|
||
@error <span class="hljs-string">"<span class="hljs-subst">#{@operator}</span> can only occur inside functions"</span>
|
||
<span class="hljs-keyword">if</span> o.scope.method?.bound <span class="hljs-keyword">and</span> o.scope.method.isGenerator
|
||
@error <span class="hljs-string">'yield cannot occur inside bound (fat arrow) functions'</span>
|
||
|
||
compileFloorDivision: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
floor = <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> IdentifierLiteral(<span class="hljs-string">'Math'</span>), [<span class="hljs-keyword">new</span> Access <span class="hljs-keyword">new</span> PropertyName <span class="hljs-string">'floor'</span>]
|
||
second = <span class="hljs-keyword">if</span> @second.shouldCache() <span class="hljs-keyword">then</span> <span class="hljs-keyword">new</span> Parens @second <span class="hljs-keyword">else</span> @second
|
||
div = <span class="hljs-keyword">new</span> Op <span class="hljs-string">'/'</span>, @first, second
|
||
<span class="hljs-keyword">new</span> Call(floor, [div]).compileToFragments o
|
||
|
||
compileModulo: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
mod = <span class="hljs-keyword">new</span> Value <span class="hljs-keyword">new</span> Literal utility <span class="hljs-string">'modulo'</span>, o
|
||
<span class="hljs-keyword">new</span> Call(mod, [@first, @second]).compileToFragments o
|
||
|
||
toString: <span class="hljs-function"><span class="hljs-params">(idt)</span> -></span>
|
||
super idt, @constructor.name + <span class="hljs-string">' '</span> + @operator
|
||
|
||
checkDeleteOperand: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @operator <span class="hljs-keyword">is</span> <span class="hljs-string">'delete'</span> <span class="hljs-keyword">and</span> o.scope.check(@first.unwrapAll().value)
|
||
@error <span class="hljs-string">'delete operand may not be argument or var'</span>
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkContinuation o <span class="hljs-keyword">if</span> @isYield()
|
||
@checkDeleteOperand o
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-string">'AwaitExpression'</span> <span class="hljs-keyword">if</span> @isAwait()
|
||
<span class="hljs-keyword">return</span> <span class="hljs-string">'YieldExpression'</span> <span class="hljs-keyword">if</span> @isYield()
|
||
<span class="hljs-keyword">return</span> <span class="hljs-string">'ChainedComparison'</span> <span class="hljs-keyword">if</span> @isChain()
|
||
<span class="hljs-keyword">switch</span> @operator
|
||
<span class="hljs-keyword">when</span> <span class="hljs-string">'||'</span>, <span class="hljs-string">'&&'</span>, <span class="hljs-string">'?'</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'LogicalExpression'</span>
|
||
<span class="hljs-keyword">when</span> <span class="hljs-string">'++'</span>, <span class="hljs-string">'--'</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'UpdateExpression'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> @isUnary() <span class="hljs-keyword">then</span> <span class="hljs-string">'UnaryExpression'</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-string">'BinaryExpression'</span>
|
||
|
||
operatorAst: <span class="hljs-function">-></span>
|
||
<span class="hljs-string">"<span class="hljs-subst">#{<span class="hljs-keyword">if</span> @invertOperator <span class="hljs-keyword">then</span> <span class="hljs-string">"<span class="hljs-subst">#{@invertOperator}</span> "</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>}</span><span class="hljs-subst">#{@originalOperator}</span>"</span>
|
||
|
||
chainAstProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
operators = [@operatorAst()]
|
||
operands = [@second]
|
||
currentOp = @first
|
||
<span class="hljs-keyword">loop</span>
|
||
operators.unshift currentOp.operatorAst()
|
||
operands.unshift currentOp.second
|
||
currentOp = currentOp.first
|
||
<span class="hljs-keyword">unless</span> currentOp.isChainable()
|
||
operands.unshift currentOp
|
||
<span class="hljs-keyword">break</span>
|
||
<span class="hljs-keyword">return</span> {
|
||
operators
|
||
operands: (operand.ast(o, LEVEL_OP) <span class="hljs-keyword">for</span> operand <span class="hljs-keyword">in</span> operands)
|
||
}
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> @chainAstProperties(o) <span class="hljs-keyword">if</span> @isChain()
|
||
|
||
firstAst = @first.ast o, LEVEL_OP
|
||
secondAst = @second?.ast o, LEVEL_OP
|
||
operatorAst = @operatorAst()
|
||
<span class="hljs-keyword">switch</span>
|
||
<span class="hljs-keyword">when</span> @isUnary()
|
||
argument =
|
||
<span class="hljs-keyword">if</span> @isYield() <span class="hljs-keyword">and</span> @first.unwrap().value <span class="hljs-keyword">is</span> <span class="hljs-string">''</span>
|
||
<span class="hljs-literal">null</span>
|
||
<span class="hljs-keyword">else</span>
|
||
firstAst
|
||
<span class="hljs-keyword">return</span> {argument} <span class="hljs-keyword">if</span> @isAwait()
|
||
<span class="hljs-keyword">return</span> {
|
||
argument
|
||
delegate: @operator <span class="hljs-keyword">is</span> <span class="hljs-string">'yield*'</span>
|
||
} <span class="hljs-keyword">if</span> @isYield()
|
||
<span class="hljs-keyword">return</span> {
|
||
argument
|
||
operator: operatorAst
|
||
prefix: !@flip
|
||
}
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">return</span>
|
||
left: firstAst
|
||
right: secondAst
|
||
operator: operatorAst</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-316">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-316">§</a>
|
||
</div>
|
||
<h3 id="in">In</h3>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.In = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">In</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@object, @array)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'object'</span>, <span class="hljs-string">'array'</span>]
|
||
|
||
invert: NEGATE
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @array <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> @array.isArray() <span class="hljs-keyword">and</span> @array.base.objects.length
|
||
<span class="hljs-keyword">for</span> obj <span class="hljs-keyword">in</span> @array.base.objects <span class="hljs-keyword">when</span> obj <span class="hljs-keyword">instanceof</span> Splat
|
||
hasSplat = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">break</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-317">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-317">§</a>
|
||
</div>
|
||
<p><code>compileOrTest</code> only if we have an array literal with no splats</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> @compileOrTest o <span class="hljs-keyword">unless</span> hasSplat
|
||
@compileLoopTest o
|
||
|
||
compileOrTest: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[sub, ref] = @object.cache o, LEVEL_OP
|
||
[cmp, cnj] = <span class="hljs-keyword">if</span> @negated <span class="hljs-keyword">then</span> [<span class="hljs-string">' !== '</span>, <span class="hljs-string">' && '</span>] <span class="hljs-keyword">else</span> [<span class="hljs-string">' === '</span>, <span class="hljs-string">' || '</span>]
|
||
tests = []
|
||
<span class="hljs-keyword">for</span> item, i <span class="hljs-keyword">in</span> @array.base.objects
|
||
<span class="hljs-keyword">if</span> i <span class="hljs-keyword">then</span> tests.push @makeCode cnj
|
||
tests = tests.concat (<span class="hljs-keyword">if</span> i <span class="hljs-keyword">then</span> ref <span class="hljs-keyword">else</span> sub), @makeCode(cmp), item.compileToFragments(o, LEVEL_ACCESS)
|
||
<span class="hljs-keyword">if</span> o.level < LEVEL_OP <span class="hljs-keyword">then</span> tests <span class="hljs-keyword">else</span> @wrapInParentheses tests
|
||
|
||
compileLoopTest: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
[sub, ref] = @object.cache o, LEVEL_LIST
|
||
fragments = [].concat @makeCode(utility(<span class="hljs-string">'indexOf'</span>, o) + <span class="hljs-string">".call("</span>), @array.compileToFragments(o, LEVEL_LIST),
|
||
@makeCode(<span class="hljs-string">", "</span>), ref, @makeCode(<span class="hljs-string">") "</span> + <span class="hljs-keyword">if</span> @negated <span class="hljs-keyword">then</span> <span class="hljs-string">'< 0'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'>= 0'</span>)
|
||
<span class="hljs-keyword">return</span> fragments <span class="hljs-keyword">if</span> fragmentsToText(sub) <span class="hljs-keyword">is</span> fragmentsToText(ref)
|
||
fragments = sub.concat @makeCode(<span class="hljs-string">', '</span>), fragments
|
||
<span class="hljs-keyword">if</span> o.level < LEVEL_LIST <span class="hljs-keyword">then</span> fragments <span class="hljs-keyword">else</span> @wrapInParentheses fragments
|
||
|
||
toString: <span class="hljs-function"><span class="hljs-params">(idt)</span> -></span>
|
||
super idt, @constructor.name + <span class="hljs-keyword">if</span> @negated <span class="hljs-keyword">then</span> <span class="hljs-string">'!'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-318">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-318">§</a>
|
||
</div>
|
||
<h3 id="try">Try</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-319">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-319">§</a>
|
||
</div>
|
||
<p>A classic <em>try/catch/finally</em> block.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Try = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Try</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@attempt, @catch, @ensure, @finallyTag)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'attempt'</span>, <span class="hljs-string">'catch'</span>, <span class="hljs-string">'ensure'</span>]
|
||
|
||
isStatement: YES
|
||
|
||
jumps: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span> @attempt.jumps(o) <span class="hljs-keyword">or</span> @catch?.jumps(o)
|
||
|
||
makeReturn: <span class="hljs-function"><span class="hljs-params">(results, mark)</span> -></span>
|
||
<span class="hljs-keyword">if</span> mark
|
||
@attempt?.makeReturn results, mark
|
||
@catch?.makeReturn results, mark
|
||
<span class="hljs-keyword">return</span>
|
||
@attempt = @attempt.makeReturn results <span class="hljs-keyword">if</span> @attempt
|
||
@catch = @catch .makeReturn results <span class="hljs-keyword">if</span> @catch
|
||
this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-320">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-320">§</a>
|
||
</div>
|
||
<p>Compilation is more or less as you would expect – the <em>finally</em> clause
|
||
is optional, the <em>catch</em> is not.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
originalIndent = o.indent
|
||
o.indent += TAB
|
||
tryPart = @attempt.compileToFragments o, LEVEL_TOP
|
||
|
||
catchPart = <span class="hljs-keyword">if</span> @catch
|
||
@catch.compileToFragments merge(o, indent: originalIndent), LEVEL_TOP
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">unless</span> @ensure <span class="hljs-keyword">or</span> @catch
|
||
generatedErrorVariableName = o.scope.freeVariable <span class="hljs-string">'error'</span>, reserve: <span class="hljs-literal">no</span>
|
||
[@makeCode(<span class="hljs-string">" catch (<span class="hljs-subst">#{generatedErrorVariableName}</span>) {}"</span>)]
|
||
<span class="hljs-keyword">else</span>
|
||
[]
|
||
|
||
ensurePart = <span class="hljs-keyword">if</span> @ensure <span class="hljs-keyword">then</span> ([].concat @makeCode(<span class="hljs-string">" finally {\n"</span>), @ensure.compileToFragments(o, LEVEL_TOP),
|
||
@makeCode(<span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>}"</span>)) <span class="hljs-keyword">else</span> []
|
||
|
||
[].concat @makeCode(<span class="hljs-string">"<span class="hljs-subst">#{@tab}</span>try {\n"</span>),
|
||
tryPart,
|
||
@makeCode(<span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>}"</span>), catchPart, ensurePart
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'TryStatement'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
block: @attempt.ast o, LEVEL_TOP
|
||
handler: @catch?.ast(o) ? <span class="hljs-literal">null</span>
|
||
finalizer:
|
||
<span class="hljs-keyword">if</span> @ensure?
|
||
<span class="hljs-built_in">Object</span>.assign @ensure.ast(o, LEVEL_TOP),</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-321">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-321">§</a>
|
||
</div>
|
||
<p>Include <code>finally</code> keyword in location data.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> mergeAstLocationData(
|
||
jisonLocationDataToAstLocationData(@finallyTag.locationData),
|
||
@ensure.astLocationData()
|
||
)
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-literal">null</span>
|
||
|
||
<span class="hljs-built_in">exports</span>.Catch = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Catch</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@recovery, @errorVariable)</span> -></span>
|
||
super()
|
||
@errorVariable?.unwrap().propagateLhs? <span class="hljs-literal">yes</span>
|
||
|
||
children: [<span class="hljs-string">'recovery'</span>, <span class="hljs-string">'errorVariable'</span>]
|
||
|
||
isStatement: YES
|
||
|
||
jumps: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span> @recovery.jumps o
|
||
|
||
makeReturn: <span class="hljs-function"><span class="hljs-params">(results, mark)</span> -></span>
|
||
ret = @recovery.makeReturn results, mark
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> mark
|
||
@recovery = ret
|
||
this
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o.indent += TAB
|
||
generatedErrorVariableName = o.scope.freeVariable <span class="hljs-string">'error'</span>, reserve: <span class="hljs-literal">no</span>
|
||
placeholder = <span class="hljs-keyword">new</span> IdentifierLiteral generatedErrorVariableName
|
||
@checkUnassignable()
|
||
<span class="hljs-keyword">if</span> @errorVariable
|
||
@recovery.unshift <span class="hljs-keyword">new</span> Assign @errorVariable, placeholder
|
||
[].concat @makeCode(<span class="hljs-string">" catch ("</span>), placeholder.compileToFragments(o), @makeCode(<span class="hljs-string">") {\n"</span>),
|
||
@recovery.compileToFragments(o, LEVEL_TOP), @makeCode(<span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>}"</span>)
|
||
|
||
checkUnassignable: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">if</span> @errorVariable
|
||
message = isUnassignable @errorVariable.unwrapAll().value
|
||
@errorVariable.error message <span class="hljs-keyword">if</span> message
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@checkUnassignable()
|
||
@errorVariable?.eachName (name) ->
|
||
alreadyDeclared = o.scope.find name.value
|
||
name.isDeclaration = <span class="hljs-keyword">not</span> alreadyDeclared
|
||
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'CatchClause'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
param: @errorVariable?.ast(o) ? <span class="hljs-literal">null</span>
|
||
body: @recovery.ast o, LEVEL_TOP</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-322">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-322">§</a>
|
||
</div>
|
||
<h3 id="throw">Throw</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-323">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-323">§</a>
|
||
</div>
|
||
<p>Simple node to throw an exception.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Throw = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Throw</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@expression)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'expression'</span>]
|
||
|
||
isStatement: YES
|
||
jumps: NO</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-324">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-324">§</a>
|
||
</div>
|
||
<p>A <strong>Throw</strong> is already a return, of sorts…</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> makeReturn: THIS
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
fragments = @expression.compileToFragments o, LEVEL_LIST
|
||
unshiftAfterComments fragments, @makeCode <span class="hljs-string">'throw '</span>
|
||
fragments.unshift @makeCode @tab
|
||
fragments.push @makeCode <span class="hljs-string">';'</span>
|
||
fragments
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'ThrowStatement'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
argument: @expression.ast o, LEVEL_LIST</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-325">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-325">§</a>
|
||
</div>
|
||
<h3 id="existence">Existence</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-326">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-326">§</a>
|
||
</div>
|
||
<p>Checks a variable for existence – not <code>null</code> and not <code>undefined</code>. This is
|
||
similar to <code>.nil?</code> in Ruby, and avoids having to consult a JavaScript truth
|
||
table. Optionally only check if a variable is not <code>undefined</code>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Existence = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Existence</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@expression, onlyNotUndefined = <span class="hljs-literal">no</span>)</span> -></span>
|
||
super()
|
||
@comparisonTarget = <span class="hljs-keyword">if</span> onlyNotUndefined <span class="hljs-keyword">then</span> <span class="hljs-string">'undefined'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'null'</span>
|
||
salvagedComments = []
|
||
@expression.traverseChildren <span class="hljs-literal">yes</span>, <span class="hljs-function"><span class="hljs-params">(child)</span> -></span>
|
||
<span class="hljs-keyword">if</span> child.comments
|
||
<span class="hljs-keyword">for</span> comment <span class="hljs-keyword">in</span> child.comments
|
||
salvagedComments.push comment <span class="hljs-keyword">unless</span> comment <span class="hljs-keyword">in</span> salvagedComments
|
||
<span class="hljs-keyword">delete</span> child.comments
|
||
attachCommentsToNode salvagedComments, @
|
||
moveComments @expression, @
|
||
|
||
children: [<span class="hljs-string">'expression'</span>]
|
||
|
||
invert: NEGATE
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@expression.front = @front
|
||
code = @expression.compile o, LEVEL_OP
|
||
<span class="hljs-keyword">if</span> @expression.unwrap() <span class="hljs-keyword">instanceof</span> IdentifierLiteral <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> o.scope.check code
|
||
[cmp, cnj] = <span class="hljs-keyword">if</span> @negated <span class="hljs-keyword">then</span> [<span class="hljs-string">'==='</span>, <span class="hljs-string">'||'</span>] <span class="hljs-keyword">else</span> [<span class="hljs-string">'!=='</span>, <span class="hljs-string">'&&'</span>]
|
||
code = <span class="hljs-string">"typeof <span class="hljs-subst">#{code}</span> <span class="hljs-subst">#{cmp}</span> \"undefined\""</span> + <span class="hljs-keyword">if</span> @comparisonTarget <span class="hljs-keyword">isnt</span> <span class="hljs-string">'undefined'</span> <span class="hljs-keyword">then</span> <span class="hljs-string">" <span class="hljs-subst">#{cnj}</span> <span class="hljs-subst">#{code}</span> <span class="hljs-subst">#{cmp}</span> <span class="hljs-subst">#{@comparisonTarget}</span>"</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>
|
||
<span class="hljs-keyword">else</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-327">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-327">§</a>
|
||
</div>
|
||
<p>We explicity want to use loose equality (<code>==</code>) when comparing against <code>null</code>,
|
||
so that an existence check roughly corresponds to a check for truthiness.
|
||
Do <em>not</em> change this to <code>===</code> for <code>null</code>, as this will break mountains of
|
||
existing code. When comparing only against <code>undefined</code>, however, we want to
|
||
use <code>===</code> because this use case is for parity with ES2015+ default values,
|
||
which only get assigned when the variable is <code>undefined</code> (but not <code>null</code>).</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> cmp = <span class="hljs-keyword">if</span> @comparisonTarget <span class="hljs-keyword">is</span> <span class="hljs-string">'null'</span>
|
||
<span class="hljs-keyword">if</span> @negated <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">else</span> <span class="hljs-comment"># `undefined`</span>
|
||
<span class="hljs-keyword">if</span> @negated <span class="hljs-keyword">then</span> <span class="hljs-string">'==='</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'!=='</span>
|
||
code = <span class="hljs-string">"<span class="hljs-subst">#{code}</span> <span class="hljs-subst">#{cmp}</span> <span class="hljs-subst">#{@comparisonTarget}</span>"</span>
|
||
[@makeCode(<span class="hljs-keyword">if</span> o.level <= LEVEL_COND <span class="hljs-keyword">then</span> code <span class="hljs-keyword">else</span> <span class="hljs-string">"(<span class="hljs-subst">#{code}</span>)"</span>)]
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'UnaryExpression'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
argument: @expression.ast o
|
||
operator: <span class="hljs-string">'?'</span>
|
||
prefix: <span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-328">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-328">§</a>
|
||
</div>
|
||
<h3 id="parens">Parens</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-329">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-329">§</a>
|
||
</div>
|
||
<p>An extra set of parentheses, specified explicitly in the source. At one time
|
||
we tried to clean up the results by detecting and removing redundant
|
||
parentheses, but no longer – you can put in as many as you please.</p>
|
||
<p>Parentheses are a good way to force any statement to become an expression.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Parens = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Parens</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@body)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'body'</span>]
|
||
|
||
unwrap: <span class="hljs-function">-></span> @body
|
||
|
||
shouldCache: <span class="hljs-function">-></span> @body.shouldCache()
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
expr = @body.unwrap()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-330">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-330">§</a>
|
||
</div>
|
||
<p>If these parentheses are wrapping an <code>IdentifierLiteral</code> followed by a
|
||
block comment, output the parentheses (or put another way, don’t optimize
|
||
away these redundant parentheses). This is because Flow requires
|
||
parentheses in certain circumstances to distinguish identifiers followed
|
||
by comment-based type annotations from JavaScript labels.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> shouldWrapComment = expr.comments?.some(
|
||
<span class="hljs-function"><span class="hljs-params">(comment)</span> -></span> comment.here <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> comment.unshift <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> comment.newLine)
|
||
<span class="hljs-keyword">if</span> expr <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> expr.isAtomic() <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @jsxAttribute <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> shouldWrapComment
|
||
expr.front = @front
|
||
<span class="hljs-keyword">return</span> expr.compileToFragments o
|
||
fragments = expr.compileToFragments o, LEVEL_PAREN
|
||
bare = o.level < LEVEL_OP <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> shouldWrapComment <span class="hljs-keyword">and</span> (
|
||
expr <span class="hljs-keyword">instanceof</span> Op <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> expr.isInOperator() <span class="hljs-keyword">or</span> expr.unwrap() <span class="hljs-keyword">instanceof</span> Call <span class="hljs-keyword">or</span>
|
||
(expr <span class="hljs-keyword">instanceof</span> For <span class="hljs-keyword">and</span> expr.returns)
|
||
) <span class="hljs-keyword">and</span> (o.level < LEVEL_COND <span class="hljs-keyword">or</span> fragments.length <= <span class="hljs-number">3</span>)
|
||
<span class="hljs-keyword">return</span> @wrapInBraces fragments <span class="hljs-keyword">if</span> @jsxAttribute
|
||
<span class="hljs-keyword">if</span> bare <span class="hljs-keyword">then</span> fragments <span class="hljs-keyword">else</span> @wrapInParentheses fragments
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span> @body.unwrap().ast o, LEVEL_PAREN</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-331">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-331">§</a>
|
||
</div>
|
||
<h3 id="stringwithinterpolations">StringWithInterpolations</h3>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>
|
||
<span class="hljs-built_in">exports</span>.StringWithInterpolations = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StringWithInterpolations</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@body, {@quote, @startQuote, @jsxAttribute} = {})</span> -></span>
|
||
super()
|
||
|
||
@fromStringLiteral: <span class="hljs-function"><span class="hljs-params">(stringLiteral)</span> -></span>
|
||
updatedString = stringLiteral.withoutQuotesInLocationData()
|
||
updatedStringValue = <span class="hljs-keyword">new</span> Value(updatedString).withLocationDataFrom updatedString
|
||
<span class="hljs-keyword">new</span> StringWithInterpolations Block.wrap([updatedStringValue]), quote: stringLiteral.quote, jsxAttribute: stringLiteral.jsxAttribute
|
||
.withLocationDataFrom stringLiteral
|
||
|
||
children: [<span class="hljs-string">'body'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-332">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-332">§</a>
|
||
</div>
|
||
<p><code>unwrap</code> returns <code>this</code> to stop ancestor nodes reaching in to grab @body,
|
||
and using @body.compileNode. <code>StringWithInterpolations.compileNode</code> is
|
||
<em>the</em> custom logic to output interpolated strings as code.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> unwrap: <span class="hljs-function">-></span> this
|
||
|
||
shouldCache: <span class="hljs-function">-></span> @body.shouldCache()
|
||
|
||
extractElements: <span class="hljs-function"><span class="hljs-params">(o, {includeInterpolationWrappers, isJsx} = {})</span> -></span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-333">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-333">§</a>
|
||
</div>
|
||
<p>Assumes that <code>expr</code> is <code>Block</code></p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> expr = @body.unwrap()
|
||
|
||
elements = []
|
||
salvagedComments = []
|
||
expr.traverseChildren <span class="hljs-literal">no</span>, <span class="hljs-function"><span class="hljs-params">(node)</span> =></span>
|
||
<span class="hljs-keyword">if</span> node <span class="hljs-keyword">instanceof</span> StringLiteral
|
||
<span class="hljs-keyword">if</span> node.comments
|
||
salvagedComments.push node.comments...
|
||
<span class="hljs-keyword">delete</span> node.comments
|
||
elements.push node
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> node <span class="hljs-keyword">instanceof</span> Interpolation
|
||
<span class="hljs-keyword">if</span> salvagedComments.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">for</span> comment <span class="hljs-keyword">in</span> salvagedComments
|
||
comment.unshift = <span class="hljs-literal">yes</span>
|
||
comment.newLine = <span class="hljs-literal">yes</span>
|
||
attachCommentsToNode salvagedComments, node
|
||
<span class="hljs-keyword">if</span> (unwrapped = node.expression?.unwrapAll()) <span class="hljs-keyword">instanceof</span> PassthroughLiteral <span class="hljs-keyword">and</span> unwrapped.generated <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> (isJsx <span class="hljs-keyword">and</span> o.compiling)
|
||
<span class="hljs-keyword">if</span> o.compiling
|
||
commentPlaceholder = <span class="hljs-keyword">new</span> StringLiteral(<span class="hljs-string">''</span>).withLocationDataFrom node
|
||
commentPlaceholder.comments = unwrapped.comments
|
||
(commentPlaceholder.comments ?= []).push node.comments... <span class="hljs-keyword">if</span> node.comments
|
||
elements.push <span class="hljs-keyword">new</span> Value commentPlaceholder
|
||
<span class="hljs-keyword">else</span>
|
||
empty = <span class="hljs-keyword">new</span> Interpolation().withLocationDataFrom node
|
||
empty.comments = node.comments
|
||
elements.push empty
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> node.expression <span class="hljs-keyword">or</span> includeInterpolationWrappers
|
||
(node.expression?.comments ?= []).push node.comments... <span class="hljs-keyword">if</span> node.comments
|
||
elements.push <span class="hljs-keyword">if</span> includeInterpolationWrappers <span class="hljs-keyword">then</span> node <span class="hljs-keyword">else</span> node.expression
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> node.comments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-334">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-334">§</a>
|
||
</div>
|
||
<p>This node is getting discarded, but salvage its comments.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> elements.length <span class="hljs-keyword">isnt</span> <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> elements[elements.length - <span class="hljs-number">1</span>] <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> StringLiteral
|
||
<span class="hljs-keyword">for</span> comment <span class="hljs-keyword">in</span> node.comments
|
||
comment.unshift = <span class="hljs-literal">no</span>
|
||
comment.newLine = <span class="hljs-literal">yes</span>
|
||
attachCommentsToNode node.comments, elements[elements.length - <span class="hljs-number">1</span>]
|
||
<span class="hljs-keyword">else</span>
|
||
salvagedComments.push node.comments...
|
||
<span class="hljs-keyword">delete</span> node.comments
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span>
|
||
|
||
elements
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
@comments ?= @startQuote?.comments
|
||
|
||
<span class="hljs-keyword">if</span> @jsxAttribute
|
||
wrapped = <span class="hljs-keyword">new</span> Parens <span class="hljs-keyword">new</span> StringWithInterpolations @body
|
||
wrapped.jsxAttribute = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">return</span> wrapped.compileNode o
|
||
|
||
elements = @extractElements o, isJsx: @jsx
|
||
|
||
fragments = []
|
||
fragments.push @makeCode <span class="hljs-string">'`'</span> <span class="hljs-keyword">unless</span> @jsx
|
||
<span class="hljs-keyword">for</span> element <span class="hljs-keyword">in</span> elements
|
||
<span class="hljs-keyword">if</span> element <span class="hljs-keyword">instanceof</span> StringLiteral
|
||
unquotedElementValue = <span class="hljs-keyword">if</span> @jsx <span class="hljs-keyword">then</span> element.unquotedValueForJSX <span class="hljs-keyword">else</span> element.unquotedValueForTemplateLiteral
|
||
fragments.push @makeCode unquotedElementValue
|
||
<span class="hljs-keyword">else</span>
|
||
fragments.push @makeCode <span class="hljs-string">'$'</span> <span class="hljs-keyword">unless</span> @jsx
|
||
code = element.compileToFragments(o, LEVEL_PAREN)
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> @isNestedTag(element) <span class="hljs-keyword">or</span>
|
||
code.some(<span class="hljs-function"><span class="hljs-params">(fragment)</span> -></span> fragment.comments?.some(<span class="hljs-function"><span class="hljs-params">(comment)</span> -></span> comment.here <span class="hljs-keyword">is</span> <span class="hljs-literal">no</span>))
|
||
code = @wrapInBraces code</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-335">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-335">§</a>
|
||
</div>
|
||
<p>Flag the <code>{</code> and <code>}</code> fragments as having been generated by this
|
||
<code>StringWithInterpolations</code> node, so that <code>compileComments</code> knows
|
||
to treat them as bounds. But the braces are unnecessary if all of
|
||
the enclosed comments are <code>/* */</code> comments. Don’t trust
|
||
<code>fragment.type</code>, which can report minified variable names when
|
||
this compiler is minified.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> code[<span class="hljs-number">0</span>].isStringWithInterpolations = <span class="hljs-literal">yes</span>
|
||
code[code.length - <span class="hljs-number">1</span>].isStringWithInterpolations = <span class="hljs-literal">yes</span>
|
||
fragments.push code...
|
||
fragments.push @makeCode <span class="hljs-string">'`'</span> <span class="hljs-keyword">unless</span> @jsx
|
||
fragments
|
||
|
||
isNestedTag: <span class="hljs-function"><span class="hljs-params">(element)</span> -></span>
|
||
call = element.unwrapAll?()
|
||
@jsx <span class="hljs-keyword">and</span> call <span class="hljs-keyword">instanceof</span> JSXElement
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'TemplateLiteral'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
elements = @extractElements o, includeInterpolationWrappers: <span class="hljs-literal">yes</span>
|
||
[..., last] = elements
|
||
|
||
quasis = []
|
||
expressions = []
|
||
|
||
<span class="hljs-keyword">for</span> element, index <span class="hljs-keyword">in</span> elements
|
||
<span class="hljs-keyword">if</span> element <span class="hljs-keyword">instanceof</span> StringLiteral
|
||
quasis.push <span class="hljs-keyword">new</span> TemplateElement(
|
||
element.originalValue
|
||
tail: element <span class="hljs-keyword">is</span> last
|
||
).withLocationDataFrom(element).ast o
|
||
<span class="hljs-keyword">else</span> <span class="hljs-comment"># Interpolation</span>
|
||
{expression} = element
|
||
node =
|
||
<span class="hljs-keyword">unless</span> expression?
|
||
emptyInterpolation = <span class="hljs-keyword">new</span> EmptyInterpolation()
|
||
emptyInterpolation.locationData = emptyExpressionLocationData {
|
||
interpolationNode: element
|
||
openingBrace: <span class="hljs-string">'#{'</span>
|
||
closingBrace: <span class="hljs-string">'}'</span>
|
||
}
|
||
emptyInterpolation
|
||
<span class="hljs-keyword">else</span>
|
||
expression.unwrapAll()
|
||
expressions.push astAsBlockIfNeeded node, o
|
||
|
||
{expressions, quasis, @quote}
|
||
|
||
<span class="hljs-built_in">exports</span>.TemplateElement = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TemplateElement</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@value, {@tail} = {})</span> -></span>
|
||
super()
|
||
|
||
astProperties: <span class="hljs-function">-></span>
|
||
<span class="hljs-keyword">return</span>
|
||
value:
|
||
raw: @value
|
||
tail: !!@tail
|
||
|
||
<span class="hljs-built_in">exports</span>.Interpolation = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Interpolation</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@expression)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'expression'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-336">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-336">§</a>
|
||
</div>
|
||
<p>Represents the contents of an empty interpolation (e.g. <code>#{}</code>).
|
||
Only used during AST generation.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.EmptyInterpolation = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmptyInterpolation</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function">-></span>
|
||
super()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-337">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-337">§</a>
|
||
</div>
|
||
<h3 id="for">For</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-338">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-338">§</a>
|
||
</div>
|
||
<p>CoffeeScript’s replacement for the <em>for</em> loop is our array and object
|
||
comprehensions, that compile into <em>for</em> loops here. They also act as an
|
||
expression, able to return the result of each filtered iteration.</p>
|
||
<p>Unlike Python array comprehensions, they can be multi-line, and you can pass
|
||
the current index of the loop as a second parameter. Unlike Ruby blocks,
|
||
you can map and filter in a single pass.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.For = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">For</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">While</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(body, source)</span> -></span>
|
||
super()
|
||
@addBody body
|
||
@addSource source
|
||
|
||
children: [<span class="hljs-string">'body'</span>, <span class="hljs-string">'source'</span>, <span class="hljs-string">'guard'</span>, <span class="hljs-string">'step'</span>]
|
||
|
||
isAwait: <span class="hljs-function">-></span> @await ? <span class="hljs-literal">no</span>
|
||
|
||
addBody: <span class="hljs-function"><span class="hljs-params">(body)</span> -></span>
|
||
@body = Block.wrap [body]
|
||
{expressions} = @body
|
||
<span class="hljs-keyword">if</span> expressions.length
|
||
@body.locationData ?= mergeLocationData expressions[<span class="hljs-number">0</span>].locationData, expressions[expressions.length - <span class="hljs-number">1</span>].locationData
|
||
this
|
||
|
||
addSource: <span class="hljs-function"><span class="hljs-params">(source)</span> -></span>
|
||
{@source = <span class="hljs-literal">no</span>} = source
|
||
attribs = [<span class="hljs-string">"name"</span>, <span class="hljs-string">"index"</span>, <span class="hljs-string">"guard"</span>, <span class="hljs-string">"step"</span>, <span class="hljs-string">"own"</span>, <span class="hljs-string">"ownTag"</span>, <span class="hljs-string">"await"</span>, <span class="hljs-string">"awaitTag"</span>, <span class="hljs-string">"object"</span>, <span class="hljs-string">"from"</span>]
|
||
@[attr] = source[attr] ? @[attr] <span class="hljs-keyword">for</span> attr <span class="hljs-keyword">in</span> attribs
|
||
<span class="hljs-keyword">return</span> this <span class="hljs-keyword">unless</span> @source
|
||
@index.error <span class="hljs-string">'cannot use index with for-from'</span> <span class="hljs-keyword">if</span> @from <span class="hljs-keyword">and</span> @index
|
||
@ownTag.error <span class="hljs-string">"cannot use own with for-<span class="hljs-subst">#{<span class="hljs-keyword">if</span> @from <span class="hljs-keyword">then</span> <span class="hljs-string">'from'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'in'</span>}</span>"</span> <span class="hljs-keyword">if</span> @own <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @object
|
||
[@name, @index] = [@index, @name] <span class="hljs-keyword">if</span> @object
|
||
@index.error <span class="hljs-string">'index cannot be a pattern matching expression'</span> <span class="hljs-keyword">if</span> @index?.isArray?() <span class="hljs-keyword">or</span> @index?.isObject?()
|
||
@awaitTag.error <span class="hljs-string">'await must be used with for-from'</span> <span class="hljs-keyword">if</span> @await <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @from
|
||
@range = @source <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> @source.base <span class="hljs-keyword">instanceof</span> Range <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @source.properties.length <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @from
|
||
@pattern = @name <span class="hljs-keyword">instanceof</span> Value
|
||
@name.unwrap().propagateLhs?(<span class="hljs-literal">yes</span>) <span class="hljs-keyword">if</span> @pattern
|
||
@index.error <span class="hljs-string">'indexes do not apply to range loops'</span> <span class="hljs-keyword">if</span> @range <span class="hljs-keyword">and</span> @index
|
||
@name.error <span class="hljs-string">'cannot pattern match over range loops'</span> <span class="hljs-keyword">if</span> @range <span class="hljs-keyword">and</span> @pattern
|
||
@returns = <span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-339">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-339">§</a>
|
||
</div>
|
||
<p>Move up any comments in the “<code>for</code> line”, i.e. the line of code with <code>for</code>,
|
||
from any child nodes of that line up to the <code>for</code> node itself so that these
|
||
comments get output, and get output above the <code>for</code> loop.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span> attribute <span class="hljs-keyword">in</span> [<span class="hljs-string">'source'</span>, <span class="hljs-string">'guard'</span>, <span class="hljs-string">'step'</span>, <span class="hljs-string">'name'</span>, <span class="hljs-string">'index'</span>] <span class="hljs-keyword">when</span> @[attribute]
|
||
@[attribute].traverseChildren <span class="hljs-literal">yes</span>, <span class="hljs-function"><span class="hljs-params">(node)</span> =></span>
|
||
<span class="hljs-keyword">if</span> node.comments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-340">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-340">§</a>
|
||
</div>
|
||
<p>These comments are buried pretty deeply, so if they happen to be
|
||
trailing comments the line they trail will be unrecognizable when
|
||
we’re done compiling this <code>for</code> loop; so just shift them up to
|
||
output above the <code>for</code> line.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> comment.newLine = comment.unshift = <span class="hljs-literal">yes</span> <span class="hljs-keyword">for</span> comment <span class="hljs-keyword">in</span> node.comments
|
||
moveComments node, @[attribute]
|
||
moveComments @[attribute], @
|
||
this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-341">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-341">§</a>
|
||
</div>
|
||
<p>Welcome to the hairiest method in all of CoffeeScript. Handles the inner
|
||
loop, filtering, stepping, and result saving for array, object, and range
|
||
comprehensions. Some of the generated code can be shared in common, and
|
||
some cannot.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
body = Block.wrap [@body]
|
||
[..., last] = body.expressions
|
||
@returns = <span class="hljs-literal">no</span> <span class="hljs-keyword">if</span> last?.jumps() <span class="hljs-keyword">instanceof</span> Return
|
||
source = <span class="hljs-keyword">if</span> @range <span class="hljs-keyword">then</span> @source.base <span class="hljs-keyword">else</span> @source
|
||
scope = o.scope
|
||
name = @name <span class="hljs-keyword">and</span> (@name.compile o, LEVEL_LIST) <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> @pattern
|
||
index = @index <span class="hljs-keyword">and</span> (@index.compile o, LEVEL_LIST)
|
||
scope.find(name) <span class="hljs-keyword">if</span> name <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @pattern
|
||
scope.find(index) <span class="hljs-keyword">if</span> index <span class="hljs-keyword">and</span> @index <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> Value
|
||
rvar = scope.freeVariable <span class="hljs-string">'results'</span> <span class="hljs-keyword">if</span> @returns
|
||
<span class="hljs-keyword">if</span> @from
|
||
ivar = scope.freeVariable <span class="hljs-string">'x'</span>, single: <span class="hljs-literal">true</span> <span class="hljs-keyword">if</span> @pattern
|
||
<span class="hljs-keyword">else</span>
|
||
ivar = (@object <span class="hljs-keyword">and</span> index) <span class="hljs-keyword">or</span> scope.freeVariable <span class="hljs-string">'i'</span>, single: <span class="hljs-literal">true</span>
|
||
kvar = ((@range <span class="hljs-keyword">or</span> @from) <span class="hljs-keyword">and</span> name) <span class="hljs-keyword">or</span> index <span class="hljs-keyword">or</span> ivar
|
||
kvarAssign = <span class="hljs-keyword">if</span> kvar <span class="hljs-keyword">isnt</span> ivar <span class="hljs-keyword">then</span> <span class="hljs-string">"<span class="hljs-subst">#{kvar}</span> = "</span> <span class="hljs-keyword">else</span> <span class="hljs-string">""</span>
|
||
<span class="hljs-keyword">if</span> @step <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @range
|
||
[step, stepVar] = @cacheToCodeFragments @step.cache o, LEVEL_LIST, shouldCacheOrIsAssignable
|
||
stepNum = parseNumber stepVar <span class="hljs-keyword">if</span> @step.isNumber()
|
||
name = ivar <span class="hljs-keyword">if</span> @pattern
|
||
varPart = <span class="hljs-string">''</span>
|
||
guardPart = <span class="hljs-string">''</span>
|
||
defPart = <span class="hljs-string">''</span>
|
||
idt1 = @tab + TAB
|
||
<span class="hljs-keyword">if</span> @range
|
||
forPartFragments = source.compileToFragments merge o,
|
||
{index: ivar, name, @step, shouldCache: shouldCacheOrIsAssignable}
|
||
<span class="hljs-keyword">else</span>
|
||
svar = @source.compile o, LEVEL_LIST
|
||
<span class="hljs-keyword">if</span> (name <span class="hljs-keyword">or</span> @own) <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @from <span class="hljs-keyword">and</span> @source.unwrap() <span class="hljs-keyword">not</span> <span class="hljs-keyword">instanceof</span> IdentifierLiteral
|
||
defPart += <span class="hljs-string">"<span class="hljs-subst">#{@tab}</span><span class="hljs-subst">#{ref = scope.freeVariable <span class="hljs-string">'ref'</span>}</span> = <span class="hljs-subst">#{svar}</span>;\n"</span>
|
||
svar = ref
|
||
<span class="hljs-keyword">if</span> name <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @pattern <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @from
|
||
namePart = <span class="hljs-string">"<span class="hljs-subst">#{name}</span> = <span class="hljs-subst">#{svar}</span>[<span class="hljs-subst">#{kvar}</span>]"</span>
|
||
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> @object <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @from
|
||
defPart += <span class="hljs-string">"<span class="hljs-subst">#{@tab}</span><span class="hljs-subst">#{step}</span>;\n"</span> <span class="hljs-keyword">if</span> step <span class="hljs-keyword">isnt</span> stepVar
|
||
down = stepNum < <span class="hljs-number">0</span>
|
||
lvar = scope.freeVariable <span class="hljs-string">'len'</span> <span class="hljs-keyword">unless</span> @step <span class="hljs-keyword">and</span> stepNum? <span class="hljs-keyword">and</span> down
|
||
declare = <span class="hljs-string">"<span class="hljs-subst">#{kvarAssign}</span><span class="hljs-subst">#{ivar}</span> = 0, <span class="hljs-subst">#{lvar}</span> = <span class="hljs-subst">#{svar}</span>.length"</span>
|
||
declareDown = <span class="hljs-string">"<span class="hljs-subst">#{kvarAssign}</span><span class="hljs-subst">#{ivar}</span> = <span class="hljs-subst">#{svar}</span>.length - 1"</span>
|
||
compare = <span class="hljs-string">"<span class="hljs-subst">#{ivar}</span> < <span class="hljs-subst">#{lvar}</span>"</span>
|
||
compareDown = <span class="hljs-string">"<span class="hljs-subst">#{ivar}</span> >= 0"</span>
|
||
<span class="hljs-keyword">if</span> @step
|
||
<span class="hljs-keyword">if</span> stepNum?
|
||
<span class="hljs-keyword">if</span> down
|
||
compare = compareDown
|
||
declare = declareDown
|
||
<span class="hljs-keyword">else</span>
|
||
compare = <span class="hljs-string">"<span class="hljs-subst">#{stepVar}</span> > 0 ? <span class="hljs-subst">#{compare}</span> : <span class="hljs-subst">#{compareDown}</span>"</span>
|
||
declare = <span class="hljs-string">"(<span class="hljs-subst">#{stepVar}</span> > 0 ? (<span class="hljs-subst">#{declare}</span>) : <span class="hljs-subst">#{declareDown}</span>)"</span>
|
||
increment = <span class="hljs-string">"<span class="hljs-subst">#{ivar}</span> += <span class="hljs-subst">#{stepVar}</span>"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
increment = <span class="hljs-string">"<span class="hljs-subst">#{<span class="hljs-keyword">if</span> kvar <span class="hljs-keyword">isnt</span> ivar <span class="hljs-keyword">then</span> <span class="hljs-string">"++<span class="hljs-subst">#{ivar}</span>"</span> <span class="hljs-keyword">else</span> <span class="hljs-string">"<span class="hljs-subst">#{ivar}</span>++"</span>}</span>"</span>
|
||
forPartFragments = [@makeCode(<span class="hljs-string">"<span class="hljs-subst">#{declare}</span>; <span class="hljs-subst">#{compare}</span>; <span class="hljs-subst">#{kvarAssign}</span><span class="hljs-subst">#{increment}</span>"</span>)]
|
||
<span class="hljs-keyword">if</span> @returns
|
||
resultPart = <span class="hljs-string">"<span class="hljs-subst">#{@tab}</span><span class="hljs-subst">#{rvar}</span> = [];\n"</span>
|
||
returnResult = <span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>return <span class="hljs-subst">#{rvar}</span>;"</span>
|
||
body.makeReturn rvar
|
||
<span class="hljs-keyword">if</span> @guard
|
||
<span class="hljs-keyword">if</span> body.expressions.length > <span class="hljs-number">1</span>
|
||
body.expressions.unshift <span class="hljs-keyword">new</span> If (<span class="hljs-keyword">new</span> Parens @guard).invert(), <span class="hljs-keyword">new</span> StatementLiteral <span class="hljs-string">"continue"</span>
|
||
<span class="hljs-keyword">else</span>
|
||
body = Block.wrap [<span class="hljs-keyword">new</span> If @guard, body] <span class="hljs-keyword">if</span> @guard
|
||
<span class="hljs-keyword">if</span> @pattern
|
||
body.expressions.unshift <span class="hljs-keyword">new</span> Assign @name, <span class="hljs-keyword">if</span> @from <span class="hljs-keyword">then</span> <span class="hljs-keyword">new</span> IdentifierLiteral kvar <span class="hljs-keyword">else</span> <span class="hljs-keyword">new</span> Literal <span class="hljs-string">"<span class="hljs-subst">#{svar}</span>[<span class="hljs-subst">#{kvar}</span>]"</span>
|
||
|
||
varPart = <span class="hljs-string">"\n<span class="hljs-subst">#{idt1}</span><span class="hljs-subst">#{namePart}</span>;"</span> <span class="hljs-keyword">if</span> namePart
|
||
<span class="hljs-keyword">if</span> @object
|
||
forPartFragments = [@makeCode(<span class="hljs-string">"<span class="hljs-subst">#{kvar}</span> in <span class="hljs-subst">#{svar}</span>"</span>)]
|
||
guardPart = <span class="hljs-string">"\n<span class="hljs-subst">#{idt1}</span>if (!<span class="hljs-subst">#{utility <span class="hljs-string">'hasProp'</span>, o}</span>.call(<span class="hljs-subst">#{svar}</span>, <span class="hljs-subst">#{kvar}</span>)) continue;"</span> <span class="hljs-keyword">if</span> @own
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> @from
|
||
<span class="hljs-keyword">if</span> @await
|
||
forPartFragments = <span class="hljs-keyword">new</span> Op <span class="hljs-string">'await'</span>, <span class="hljs-keyword">new</span> Parens <span class="hljs-keyword">new</span> Literal <span class="hljs-string">"<span class="hljs-subst">#{kvar}</span> of <span class="hljs-subst">#{svar}</span>"</span>
|
||
forPartFragments = forPartFragments.compileToFragments o, LEVEL_TOP
|
||
<span class="hljs-keyword">else</span>
|
||
forPartFragments = [@makeCode(<span class="hljs-string">"<span class="hljs-subst">#{kvar}</span> of <span class="hljs-subst">#{svar}</span>"</span>)]
|
||
bodyFragments = body.compileToFragments merge(o, indent: idt1), LEVEL_TOP
|
||
<span class="hljs-keyword">if</span> bodyFragments <span class="hljs-keyword">and</span> bodyFragments.length > <span class="hljs-number">0</span>
|
||
bodyFragments = [].concat @makeCode(<span class="hljs-string">'\n'</span>), bodyFragments, @makeCode(<span class="hljs-string">'\n'</span>)
|
||
|
||
fragments = [@makeCode(defPart)]
|
||
fragments.push @makeCode(resultPart) <span class="hljs-keyword">if</span> resultPart
|
||
forCode = <span class="hljs-keyword">if</span> @await <span class="hljs-keyword">then</span> <span class="hljs-string">'for '</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'for ('</span>
|
||
forClose = <span class="hljs-keyword">if</span> @await <span class="hljs-keyword">then</span> <span class="hljs-string">''</span> <span class="hljs-keyword">else</span> <span class="hljs-string">')'</span>
|
||
fragments = fragments.concat @makeCode(@tab), @makeCode( forCode),
|
||
forPartFragments, @makeCode(<span class="hljs-string">"<span class="hljs-subst">#{forClose}</span> {<span class="hljs-subst">#{guardPart}</span><span class="hljs-subst">#{varPart}</span>"</span>), bodyFragments,
|
||
@makeCode(@tab), @makeCode(<span class="hljs-string">'}'</span>)
|
||
fragments.push @makeCode(returnResult) <span class="hljs-keyword">if</span> returnResult
|
||
fragments
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-function"> <span class="hljs-title">addToScope</span> = <span class="hljs-params">(name)</span> -></span>
|
||
alreadyDeclared = o.scope.find name.value
|
||
name.isDeclaration = <span class="hljs-keyword">not</span> alreadyDeclared
|
||
@name?.eachName addToScope, checkAssignability: <span class="hljs-literal">no</span>
|
||
@index?.eachName addToScope, checkAssignability: <span class="hljs-literal">no</span>
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'For'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
source: @source?.ast o
|
||
body: @body.ast o, LEVEL_TOP
|
||
guard: @guard?.ast(o) ? <span class="hljs-literal">null</span>
|
||
name: @name?.ast(o) ? <span class="hljs-literal">null</span>
|
||
index: @index?.ast(o) ? <span class="hljs-literal">null</span>
|
||
step: @step?.ast(o) ? <span class="hljs-literal">null</span>
|
||
postfix: !!@postfix
|
||
own: !!@own
|
||
await: !!@await
|
||
style: <span class="hljs-keyword">switch</span>
|
||
<span class="hljs-keyword">when</span> @from <span class="hljs-keyword">then</span> <span class="hljs-string">'from'</span>
|
||
<span class="hljs-keyword">when</span> @object <span class="hljs-keyword">then</span> <span class="hljs-string">'of'</span>
|
||
<span class="hljs-keyword">when</span> @name <span class="hljs-keyword">then</span> <span class="hljs-string">'in'</span>
|
||
<span class="hljs-keyword">else</span> <span class="hljs-string">'range'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-342">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-342">§</a>
|
||
</div>
|
||
<h3 id="switch">Switch</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-343">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-343">§</a>
|
||
</div>
|
||
<p>A JavaScript <em>switch</em> statement. Converts into a returnable expression on-demand.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Switch = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Switch</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@subject, @cases, @otherwise)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'subject'</span>, <span class="hljs-string">'cases'</span>, <span class="hljs-string">'otherwise'</span>]
|
||
|
||
isStatement: YES
|
||
|
||
jumps: <span class="hljs-function"><span class="hljs-params">(o = {block: <span class="hljs-literal">yes</span>})</span> -></span>
|
||
<span class="hljs-keyword">for</span> {block} <span class="hljs-keyword">in</span> @cases
|
||
<span class="hljs-keyword">return</span> jumpNode <span class="hljs-keyword">if</span> jumpNode = block.jumps o
|
||
@otherwise?.jumps o
|
||
|
||
makeReturn: <span class="hljs-function"><span class="hljs-params">(results, mark)</span> -></span>
|
||
block.makeReturn(results, mark) <span class="hljs-keyword">for</span> {block} <span class="hljs-keyword">in</span> @cases
|
||
@otherwise <span class="hljs-keyword">or</span>= <span class="hljs-keyword">new</span> Block [<span class="hljs-keyword">new</span> Literal <span class="hljs-string">'void 0'</span>] <span class="hljs-keyword">if</span> results
|
||
@otherwise?.makeReturn results, mark
|
||
this
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
idt1 = o.indent + TAB
|
||
idt2 = o.indent = idt1 + TAB
|
||
fragments = [].concat @makeCode(@tab + <span class="hljs-string">"switch ("</span>),
|
||
(<span class="hljs-keyword">if</span> @subject <span class="hljs-keyword">then</span> @subject.compileToFragments(o, LEVEL_PAREN) <span class="hljs-keyword">else</span> @makeCode <span class="hljs-string">"false"</span>),
|
||
@makeCode(<span class="hljs-string">") {\n"</span>)
|
||
<span class="hljs-keyword">for</span> {conditions, block}, i <span class="hljs-keyword">in</span> @cases
|
||
<span class="hljs-keyword">for</span> cond <span class="hljs-keyword">in</span> flatten [conditions]
|
||
cond = cond.invert() <span class="hljs-keyword">unless</span> @subject
|
||
fragments = fragments.concat @makeCode(idt1 + <span class="hljs-string">"case "</span>), cond.compileToFragments(o, LEVEL_PAREN), @makeCode(<span class="hljs-string">":\n"</span>)
|
||
fragments = fragments.concat body, @makeCode(<span class="hljs-string">'\n'</span>) <span class="hljs-keyword">if</span> (body = block.compileToFragments o, LEVEL_TOP).length > <span class="hljs-number">0</span>
|
||
<span class="hljs-keyword">break</span> <span class="hljs-keyword">if</span> i <span class="hljs-keyword">is</span> @cases.length - <span class="hljs-number">1</span> <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> @otherwise
|
||
expr = @lastNode block.expressions
|
||
<span class="hljs-keyword">continue</span> <span class="hljs-keyword">if</span> expr <span class="hljs-keyword">instanceof</span> Return <span class="hljs-keyword">or</span> expr <span class="hljs-keyword">instanceof</span> Throw <span class="hljs-keyword">or</span> (expr <span class="hljs-keyword">instanceof</span> Literal <span class="hljs-keyword">and</span> expr.jumps() <span class="hljs-keyword">and</span> expr.value <span class="hljs-keyword">isnt</span> <span class="hljs-string">'debugger'</span>)
|
||
fragments.push cond.makeCode(idt2 + <span class="hljs-string">'break;\n'</span>)
|
||
<span class="hljs-keyword">if</span> @otherwise <span class="hljs-keyword">and</span> @otherwise.expressions.length
|
||
fragments.push @makeCode(idt1 + <span class="hljs-string">"default:\n"</span>), (@otherwise.compileToFragments o, LEVEL_TOP)..., @makeCode(<span class="hljs-string">"\n"</span>)
|
||
fragments.push @makeCode @tab + <span class="hljs-string">'}'</span>
|
||
fragments
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'SwitchStatement'</span>
|
||
|
||
casesAst: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
cases = []
|
||
|
||
<span class="hljs-keyword">for</span> kase, caseIndex <span class="hljs-keyword">in</span> @cases
|
||
{conditions: tests, block: consequent} = kase
|
||
tests = flatten [tests]
|
||
lastTestIndex = tests.length - <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">for</span> test, testIndex <span class="hljs-keyword">in</span> tests
|
||
testConsequent =
|
||
<span class="hljs-keyword">if</span> testIndex <span class="hljs-keyword">is</span> lastTestIndex
|
||
consequent
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-literal">null</span>
|
||
|
||
caseLocationData = test.locationData
|
||
caseLocationData = mergeLocationData caseLocationData, testConsequent.expressions[testConsequent.expressions.length - <span class="hljs-number">1</span>].locationData <span class="hljs-keyword">if</span> testConsequent?.expressions.length
|
||
caseLocationData = mergeLocationData caseLocationData, kase.locationData, justLeading: <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> testIndex <span class="hljs-keyword">is</span> <span class="hljs-number">0</span>
|
||
caseLocationData = mergeLocationData caseLocationData, kase.locationData, justEnding: <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> testIndex <span class="hljs-keyword">is</span> lastTestIndex
|
||
|
||
cases.push <span class="hljs-keyword">new</span> SwitchCase(test, testConsequent, trailing: testIndex <span class="hljs-keyword">is</span> lastTestIndex).withLocationDataFrom locationData: caseLocationData
|
||
|
||
<span class="hljs-keyword">if</span> @otherwise?.expressions.length
|
||
cases.push <span class="hljs-keyword">new</span> SwitchCase(<span class="hljs-literal">null</span>, @otherwise).withLocationDataFrom @otherwise
|
||
|
||
kase.ast(o) <span class="hljs-keyword">for</span> kase <span class="hljs-keyword">in</span> cases
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
discriminant: @subject?.ast(o, LEVEL_PAREN) ? <span class="hljs-literal">null</span>
|
||
cases: @casesAst o
|
||
|
||
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SwitchCase</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@test, @block, {@trailing} = {})</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'test'</span>, <span class="hljs-string">'block'</span>]
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
test: @test?.ast(o, LEVEL_PAREN) ? <span class="hljs-literal">null</span>
|
||
consequent: @block?.ast(o, LEVEL_TOP).body ? []
|
||
trailing: !!@trailing
|
||
|
||
<span class="hljs-built_in">exports</span>.SwitchWhen = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SwitchWhen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@conditions, @block)</span> -></span>
|
||
super()
|
||
|
||
children: [<span class="hljs-string">'conditions'</span>, <span class="hljs-string">'block'</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-344">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-344">§</a>
|
||
</div>
|
||
<h3 id="if">If</h3>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-345">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-345">§</a>
|
||
</div>
|
||
<p><em>If/else</em> statements. Acts as an expression by pushing down requested returns
|
||
to the last line of each clause.</p>
|
||
<p>Single-expression <strong>Ifs</strong> are compiled into conditional operators if possible,
|
||
because ternaries are already proper expressions, and don’t need conversion.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.If = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">If</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@condition, @body, options = {})</span> -></span>
|
||
super()
|
||
@elseBody = <span class="hljs-literal">null</span>
|
||
@isChain = <span class="hljs-literal">false</span>
|
||
{@soak, @postfix, @type} = options
|
||
moveComments @condition, @ <span class="hljs-keyword">if</span> @condition.comments
|
||
|
||
children: [<span class="hljs-string">'condition'</span>, <span class="hljs-string">'body'</span>, <span class="hljs-string">'elseBody'</span>]
|
||
|
||
bodyNode: <span class="hljs-function">-></span> @body?.unwrap()
|
||
elseBodyNode: <span class="hljs-function">-></span> @elseBody?.unwrap()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-346">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-346">§</a>
|
||
</div>
|
||
<p>Rewrite a chain of <strong>Ifs</strong> to add a default case as the final <em>else</em>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> addElse: <span class="hljs-function"><span class="hljs-params">(elseBody)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @isChain
|
||
@elseBodyNode().addElse elseBody
|
||
@locationData = mergeLocationData @locationData, @elseBodyNode().locationData
|
||
<span class="hljs-keyword">else</span>
|
||
@isChain = elseBody <span class="hljs-keyword">instanceof</span> If
|
||
@elseBody = @ensureBlock elseBody
|
||
@elseBody.updateLocationDataIfMissing elseBody.locationData
|
||
@locationData = mergeLocationData @locationData, @elseBody.locationData <span class="hljs-keyword">if</span> @locationData? <span class="hljs-keyword">and</span> @elseBody.locationData?
|
||
this</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-347">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-347">§</a>
|
||
</div>
|
||
<p>The <strong>If</strong> only compiles into a statement if either of its bodies needs
|
||
to be a statement. Otherwise a conditional operator is safe.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> isStatement: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o?.level <span class="hljs-keyword">is</span> LEVEL_TOP <span class="hljs-keyword">or</span>
|
||
@bodyNode().isStatement(o) <span class="hljs-keyword">or</span> @elseBodyNode()?.isStatement(o)
|
||
|
||
jumps: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span> @body.jumps(o) <span class="hljs-keyword">or</span> @elseBody?.jumps(o)
|
||
|
||
compileNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @isStatement o <span class="hljs-keyword">then</span> @compileStatement o <span class="hljs-keyword">else</span> @compileExpression o
|
||
|
||
makeReturn: <span class="hljs-function"><span class="hljs-params">(results, mark)</span> -></span>
|
||
<span class="hljs-keyword">if</span> mark
|
||
@body?.makeReturn results, mark
|
||
@elseBody?.makeReturn results, mark
|
||
<span class="hljs-keyword">return</span>
|
||
@elseBody <span class="hljs-keyword">or</span>= <span class="hljs-keyword">new</span> Block [<span class="hljs-keyword">new</span> Literal <span class="hljs-string">'void 0'</span>] <span class="hljs-keyword">if</span> results
|
||
@body <span class="hljs-keyword">and</span>= <span class="hljs-keyword">new</span> Block [@body.makeReturn results]
|
||
@elseBody <span class="hljs-keyword">and</span>= <span class="hljs-keyword">new</span> Block [@elseBody.makeReturn results]
|
||
this
|
||
|
||
ensureBlock: <span class="hljs-function"><span class="hljs-params">(node)</span> -></span>
|
||
<span class="hljs-keyword">if</span> node <span class="hljs-keyword">instanceof</span> Block <span class="hljs-keyword">then</span> node <span class="hljs-keyword">else</span> <span class="hljs-keyword">new</span> Block [node]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-348">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-348">§</a>
|
||
</div>
|
||
<p>Compile the <code>If</code> as a regular <em>if-else</em> statement. Flattened chains
|
||
force inner <em>else</em> bodies into statement form.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileStatement: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
child = del o, <span class="hljs-string">'chainChild'</span>
|
||
exeq = del o, <span class="hljs-string">'isExistentialEquals'</span>
|
||
|
||
<span class="hljs-keyword">if</span> exeq
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> If(@processedCondition().invert(), @elseBodyNode(), type: <span class="hljs-string">'if'</span>).compileToFragments o
|
||
|
||
indent = o.indent + TAB
|
||
cond = @processedCondition().compileToFragments o, LEVEL_PAREN
|
||
body = @ensureBlock(@body).compileToFragments merge o, {indent}
|
||
ifPart = [].concat @makeCode(<span class="hljs-string">"if ("</span>), cond, @makeCode(<span class="hljs-string">") {\n"</span>), body, @makeCode(<span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>}"</span>)
|
||
ifPart.unshift @makeCode @tab <span class="hljs-keyword">unless</span> child
|
||
<span class="hljs-keyword">return</span> ifPart <span class="hljs-keyword">unless</span> @elseBody
|
||
answer = ifPart.concat @makeCode(<span class="hljs-string">' else '</span>)
|
||
<span class="hljs-keyword">if</span> @isChain
|
||
o.chainChild = <span class="hljs-literal">yes</span>
|
||
answer = answer.concat @elseBody.unwrap().compileToFragments o, LEVEL_TOP
|
||
<span class="hljs-keyword">else</span>
|
||
answer = answer.concat @makeCode(<span class="hljs-string">"{\n"</span>), @elseBody.compileToFragments(merge(o, {indent}), LEVEL_TOP), @makeCode(<span class="hljs-string">"\n<span class="hljs-subst">#{@tab}</span>}"</span>)
|
||
answer</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-349">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-349">§</a>
|
||
</div>
|
||
<p>Compile the <code>If</code> as a conditional operator.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> compileExpression: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
cond = @processedCondition().compileToFragments o, LEVEL_COND
|
||
body = @bodyNode().compileToFragments o, LEVEL_LIST
|
||
alt = <span class="hljs-keyword">if</span> @elseBodyNode() <span class="hljs-keyword">then</span> @elseBodyNode().compileToFragments(o, LEVEL_LIST) <span class="hljs-keyword">else</span> [@makeCode(<span class="hljs-string">'void 0'</span>)]
|
||
fragments = cond.concat @makeCode(<span class="hljs-string">" ? "</span>), body, @makeCode(<span class="hljs-string">" : "</span>), alt
|
||
<span class="hljs-keyword">if</span> o.level >= LEVEL_COND <span class="hljs-keyword">then</span> @wrapInParentheses fragments <span class="hljs-keyword">else</span> fragments
|
||
|
||
unfoldSoak: <span class="hljs-function">-></span>
|
||
@soak <span class="hljs-keyword">and</span> this
|
||
|
||
processedCondition: <span class="hljs-function">-></span>
|
||
@processedConditionCache ?= <span class="hljs-keyword">if</span> @type <span class="hljs-keyword">is</span> <span class="hljs-string">'unless'</span> <span class="hljs-keyword">then</span> @condition.invert() <span class="hljs-keyword">else</span> @condition
|
||
|
||
isStatementAst: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
o.level <span class="hljs-keyword">is</span> LEVEL_TOP
|
||
|
||
astType: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">if</span> @isStatementAst o
|
||
<span class="hljs-string">'IfStatement'</span>
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">'ConditionalExpression'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
isStatement = @isStatementAst o
|
||
|
||
<span class="hljs-keyword">return</span>
|
||
test: @condition.ast o, <span class="hljs-keyword">if</span> isStatement <span class="hljs-keyword">then</span> LEVEL_PAREN <span class="hljs-keyword">else</span> LEVEL_COND
|
||
consequent:
|
||
<span class="hljs-keyword">if</span> isStatement
|
||
@body.ast o, LEVEL_TOP
|
||
<span class="hljs-keyword">else</span>
|
||
@bodyNode().ast o, LEVEL_TOP
|
||
alternate:
|
||
<span class="hljs-keyword">if</span> @isChain
|
||
@elseBody.unwrap().ast o, <span class="hljs-keyword">if</span> isStatement <span class="hljs-keyword">then</span> LEVEL_TOP <span class="hljs-keyword">else</span> LEVEL_COND
|
||
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isStatement <span class="hljs-keyword">and</span> @elseBody?.expressions?.length <span class="hljs-keyword">is</span> <span class="hljs-number">1</span>
|
||
@elseBody.expressions[<span class="hljs-number">0</span>].ast o, LEVEL_TOP
|
||
<span class="hljs-keyword">else</span>
|
||
@elseBody?.ast(o, LEVEL_TOP) ? <span class="hljs-literal">null</span>
|
||
postfix: !!@postfix
|
||
inverted: @type <span class="hljs-keyword">is</span> <span class="hljs-string">'unless'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-350">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-350">§</a>
|
||
</div>
|
||
<p>A sequence expression e.g. <code>(a; b)</code>.
|
||
Currently only used during AST generation.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.Sequence = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Sequence</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Base</span></span>
|
||
children: [<span class="hljs-string">'expressions'</span>]
|
||
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@expressions)</span> -></span>
|
||
super()
|
||
|
||
astNode: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span> @expressions[<span class="hljs-number">0</span>].ast(o) <span class="hljs-keyword">if</span> @expressions.length <span class="hljs-keyword">is</span> <span class="hljs-number">1</span>
|
||
super o
|
||
|
||
astType: <span class="hljs-function">-></span> <span class="hljs-string">'SequenceExpression'</span>
|
||
|
||
astProperties: <span class="hljs-function"><span class="hljs-params">(o)</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
expressions:
|
||
expression.ast(o) <span class="hljs-keyword">for</span> expression <span class="hljs-keyword">in</span> @expressions</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-351">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-351">§</a>
|
||
</div>
|
||
<h2 id="constants">Constants</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-352">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-352">§</a>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>
|
||
UTILITIES =
|
||
modulo: <span class="hljs-function">-></span> <span class="hljs-string">'function(a, b) { return (+a % (b = +b) + b) % b; }'</span>
|
||
|
||
boundMethodCheck: <span class="hljs-function">-></span> <span class="hljs-string">"
|
||
function(instance, Constructor) {
|
||
if (!(instance instanceof Constructor)) {
|
||
throw new Error('Bound instance method accessed before binding');
|
||
}
|
||
}
|
||
"</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-353">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-353">§</a>
|
||
</div>
|
||
<p>Shortcuts to speed up the lookup time for native functions.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> hasProp: <span class="hljs-function">-></span> <span class="hljs-string">'{}.hasOwnProperty'</span>
|
||
indexOf: <span class="hljs-function">-></span> <span class="hljs-string">'[].indexOf'</span>
|
||
slice : <span class="hljs-function">-></span> <span class="hljs-string">'[].slice'</span>
|
||
splice : <span class="hljs-function">-></span> <span class="hljs-string">'[].splice'</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-354">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-354">§</a>
|
||
</div>
|
||
<p>Levels indicate a node’s position in the AST. Useful for knowing if
|
||
parens are necessary or superfluous.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>LEVEL_TOP = <span class="hljs-number">1</span> <span class="hljs-comment"># ...;</span>
|
||
LEVEL_PAREN = <span class="hljs-number">2</span> <span class="hljs-comment"># (...)</span>
|
||
LEVEL_LIST = <span class="hljs-number">3</span> <span class="hljs-comment"># [...]</span>
|
||
LEVEL_COND = <span class="hljs-number">4</span> <span class="hljs-comment"># ... ? x : y</span>
|
||
LEVEL_OP = <span class="hljs-number">5</span> <span class="hljs-comment"># !...</span>
|
||
LEVEL_ACCESS = <span class="hljs-number">6</span> <span class="hljs-comment"># ...[0]</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-355">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-355">§</a>
|
||
</div>
|
||
<p>Tabs are two spaces for pretty printing.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre>TAB = <span class="hljs-string">' '</span>
|
||
|
||
SIMPLENUM = <span class="hljs-regexp">/^[+-]?\d+(?:_\d+)*$/</span>
|
||
SIMPLE_STRING_OMIT = <span class="hljs-regexp">/\s*\n\s*/g</span>
|
||
LEADING_BLANK_LINE = <span class="hljs-regexp">/^[^\n\S]*\n/</span>
|
||
TRAILING_BLANK_LINE = <span class="hljs-regexp">/\n[^\n\S]*$/</span>
|
||
STRING_OMIT = <span class="hljs-regexp">///
|
||
((?:\\\\)+) <span class="hljs-comment"># Consume (and preserve) an even number of backslashes.</span>
|
||
| \\[^\S\n]*\n\s* <span class="hljs-comment"># Remove escaped newlines.</span>
|
||
///</span>g
|
||
HEREGEX_OMIT = <span class="hljs-regexp">///
|
||
((?:\\\\)+) <span class="hljs-comment"># Consume (and preserve) an even number of backslashes.</span>
|
||
| \\(\s) <span class="hljs-comment"># Preserve escaped whitespace.</span>
|
||
| \s+(?:<span class="hljs-comment">#.*)? # Remove whitespace and comments.</span>
|
||
///</span>g</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-356">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-356">§</a>
|
||
</div>
|
||
<h2 id="helper-functions">Helper Functions</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-357">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-357">§</a>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-358">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-358">§</a>
|
||
</div>
|
||
<p>Helper for ensuring that utility functions are assigned at the top level.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">utility</span> = <span class="hljs-params">(name, o)</span> -></span>
|
||
{root} = o.scope
|
||
<span class="hljs-keyword">if</span> name <span class="hljs-keyword">of</span> root.utilities
|
||
root.utilities[name]
|
||
<span class="hljs-keyword">else</span>
|
||
ref = root.freeVariable name
|
||
root.assign ref, UTILITIES[name] o
|
||
root.utilities[name] = ref
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">multident</span> = <span class="hljs-params">(code, tab, includingFirstLine = <span class="hljs-literal">yes</span>)</span> -></span>
|
||
endsWithNewLine = code[code.length - <span class="hljs-number">1</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">'\n'</span>
|
||
code = (<span class="hljs-keyword">if</span> includingFirstLine <span class="hljs-keyword">then</span> tab <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>) + code.replace <span class="hljs-regexp">/\n/g</span>, <span class="hljs-string">"$&<span class="hljs-subst">#{tab}</span>"</span>
|
||
code = code.replace <span class="hljs-regexp">/\s+$/</span>, <span class="hljs-string">''</span>
|
||
code = code + <span class="hljs-string">'\n'</span> <span class="hljs-keyword">if</span> endsWithNewLine
|
||
code</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-359">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-359">§</a>
|
||
</div>
|
||
<p>Wherever in CoffeeScript 1 we might’ve inserted a <code>makeCode "#{@tab}"</code> to
|
||
indent a line of code, now we must account for the possibility of comments
|
||
preceding that line of code. If there are such comments, indent each line of
|
||
such comments, and <em>then</em> indent the first following line of code.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">indentInitial</span> = <span class="hljs-params">(fragments, node)</span> -></span>
|
||
<span class="hljs-keyword">for</span> fragment, fragmentIndex <span class="hljs-keyword">in</span> fragments
|
||
<span class="hljs-keyword">if</span> fragment.isHereComment
|
||
fragment.code = multident fragment.code, node.tab
|
||
<span class="hljs-keyword">else</span>
|
||
fragments.splice fragmentIndex, <span class="hljs-number">0</span>, node.makeCode <span class="hljs-string">"<span class="hljs-subst">#{node.tab}</span>"</span>
|
||
<span class="hljs-keyword">break</span>
|
||
fragments
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">hasLineComments</span> = <span class="hljs-params">(node)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> node.comments
|
||
<span class="hljs-keyword">for</span> comment <span class="hljs-keyword">in</span> node.comments
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> comment.here <span class="hljs-keyword">is</span> <span class="hljs-literal">no</span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-360">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-360">§</a>
|
||
</div>
|
||
<p>Move the <code>comments</code> property from one object to another, deleting it from
|
||
the first object.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">moveComments</span> = <span class="hljs-params">(<span class="hljs-keyword">from</span>, to)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> <span class="hljs-keyword">from</span>?.comments
|
||
attachCommentsToNode <span class="hljs-keyword">from</span>.comments, to
|
||
<span class="hljs-keyword">delete</span> <span class="hljs-keyword">from</span>.comments</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-361">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-361">§</a>
|
||
</div>
|
||
<p>Sometimes when compiling a node, we want to insert a fragment at the start
|
||
of an array of fragments; but if the start has one or more comment fragments,
|
||
we want to insert this fragment after those but before any non-comments.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">unshiftAfterComments</span> = <span class="hljs-params">(fragments, fragmentToInsert)</span> -></span>
|
||
inserted = <span class="hljs-literal">no</span>
|
||
<span class="hljs-keyword">for</span> fragment, fragmentIndex <span class="hljs-keyword">in</span> fragments <span class="hljs-keyword">when</span> <span class="hljs-keyword">not</span> fragment.isComment
|
||
fragments.splice fragmentIndex, <span class="hljs-number">0</span>, fragmentToInsert
|
||
inserted = <span class="hljs-literal">yes</span>
|
||
<span class="hljs-keyword">break</span>
|
||
fragments.push fragmentToInsert <span class="hljs-keyword">unless</span> inserted
|
||
fragments
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">isLiteralArguments</span> = <span class="hljs-params">(node)</span> -></span>
|
||
node <span class="hljs-keyword">instanceof</span> IdentifierLiteral <span class="hljs-keyword">and</span> node.value <span class="hljs-keyword">is</span> <span class="hljs-string">'arguments'</span>
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">isLiteralThis</span> = <span class="hljs-params">(node)</span> -></span>
|
||
node <span class="hljs-keyword">instanceof</span> ThisLiteral <span class="hljs-keyword">or</span> (node <span class="hljs-keyword">instanceof</span> Code <span class="hljs-keyword">and</span> node.bound)
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">shouldCacheOrIsAssignable</span> = <span class="hljs-params">(node)</span> -></span> node.shouldCache() <span class="hljs-keyword">or</span> node.isAssignable?()</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-362">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-362">§</a>
|
||
</div>
|
||
<p>Unfold a node’s child if soak, then tuck the node under created <code>If</code></p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">unfoldSoak</span> = <span class="hljs-params">(o, parent, name)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">unless</span> ifn = parent[name].unfoldSoak o
|
||
parent[name] = ifn.body
|
||
ifn.body = <span class="hljs-keyword">new</span> Value parent
|
||
ifn</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-363">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-363">§</a>
|
||
</div>
|
||
<p>Constructs a string or regex by escaping certain characters.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">makeDelimitedLiteral</span> = <span class="hljs-params">(body, {delimiter: delimiterOption, escapeNewlines, double, includeDelimiters = <span class="hljs-literal">yes</span>, escapeDelimiter = <span class="hljs-literal">yes</span>, convertTrailingNullEscapes} = {})</span> -></span>
|
||
body = <span class="hljs-string">'(?:)'</span> <span class="hljs-keyword">if</span> body <span class="hljs-keyword">is</span> <span class="hljs-string">''</span> <span class="hljs-keyword">and</span> delimiterOption <span class="hljs-keyword">is</span> <span class="hljs-string">'/'</span>
|
||
escapeTemplateLiteralCurlies = delimiterOption <span class="hljs-keyword">is</span> <span class="hljs-string">'`'</span>
|
||
regex = <span class="hljs-regexp">///
|
||
(\\\\) <span class="hljs-comment"># Escaped backslash.</span>
|
||
| (\\0(?=\d)) <span class="hljs-comment"># Null character mistaken as octal escape.</span>
|
||
<span class="hljs-subst">#{
|
||
<span class="hljs-keyword">if</span> convertTrailingNullEscapes
|
||
<span class="hljs-regexp">/// | (\\0) $ ///</span>.source # Trailing <span class="hljs-literal">null</span> character that could be mistaken <span class="hljs-keyword">as</span> octal <span class="hljs-built_in">escape</span>.
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">''</span>
|
||
}</span>
|
||
<span class="hljs-subst">#{
|
||
<span class="hljs-keyword">if</span> escapeDelimiter
|
||
<span class="hljs-regexp">/// | \\?(<span class="hljs-subst">#{delimiterOption}</span>) ///</span>.source # (Possibly escaped) delimiter.
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">''</span>
|
||
}</span>
|
||
<span class="hljs-subst">#{
|
||
<span class="hljs-keyword">if</span> escapeTemplateLiteralCurlies
|
||
<span class="hljs-regexp">/// | \\?(\$\{) ///</span>.source # `<span class="language-javascript">${</span>` inside template literals must be escaped.
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-string">''</span>
|
||
}</span>
|
||
| \\?(?:
|
||
<span class="hljs-subst">#{<span class="hljs-keyword">if</span> escapeNewlines <span class="hljs-keyword">then</span> <span class="hljs-string">'(\n)|'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>}</span>
|
||
(\r)
|
||
| (\u2028)
|
||
| (\u2029)
|
||
) <span class="hljs-comment"># (Possibly escaped) newlines.</span>
|
||
| (\\.) <span class="hljs-comment"># Other escapes.</span>
|
||
///</span>g
|
||
body = body.replace regex, <span class="hljs-function"><span class="hljs-params">(match, backslash, nul, ...args)</span> -></span>
|
||
trailingNullEscape =
|
||
args.shift() <span class="hljs-keyword">if</span> convertTrailingNullEscapes
|
||
delimiter =
|
||
args.shift() <span class="hljs-keyword">if</span> escapeDelimiter
|
||
templateLiteralCurly =
|
||
args.shift() <span class="hljs-keyword">if</span> escapeTemplateLiteralCurlies
|
||
lf =
|
||
args.shift() <span class="hljs-keyword">if</span> escapeNewlines
|
||
[cr, ls, ps, other] = args
|
||
<span class="hljs-keyword">switch</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-364">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-364">§</a>
|
||
</div>
|
||
<p>Ignore escaped backslashes.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">when</span> backslash <span class="hljs-keyword">then</span> (<span class="hljs-keyword">if</span> double <span class="hljs-keyword">then</span> backslash + backslash <span class="hljs-keyword">else</span> backslash)
|
||
<span class="hljs-keyword">when</span> nul <span class="hljs-keyword">then</span> <span class="hljs-string">'\\x00'</span>
|
||
<span class="hljs-keyword">when</span> trailingNullEscape <span class="hljs-keyword">then</span> <span class="hljs-string">"\\x00"</span>
|
||
<span class="hljs-keyword">when</span> delimiter <span class="hljs-keyword">then</span> <span class="hljs-string">"\\<span class="hljs-subst">#{delimiter}</span>"</span>
|
||
<span class="hljs-keyword">when</span> templateLiteralCurly <span class="hljs-keyword">then</span> <span class="hljs-string">"\\${"</span>
|
||
<span class="hljs-keyword">when</span> lf <span class="hljs-keyword">then</span> <span class="hljs-string">'\\n'</span>
|
||
<span class="hljs-keyword">when</span> cr <span class="hljs-keyword">then</span> <span class="hljs-string">'\\r'</span>
|
||
<span class="hljs-keyword">when</span> ls <span class="hljs-keyword">then</span> <span class="hljs-string">'\\u2028'</span>
|
||
<span class="hljs-keyword">when</span> ps <span class="hljs-keyword">then</span> <span class="hljs-string">'\\u2029'</span>
|
||
<span class="hljs-keyword">when</span> other <span class="hljs-keyword">then</span> (<span class="hljs-keyword">if</span> double <span class="hljs-keyword">then</span> <span class="hljs-string">"\\<span class="hljs-subst">#{other}</span>"</span> <span class="hljs-keyword">else</span> other)
|
||
printedDelimiter = <span class="hljs-keyword">if</span> includeDelimiters <span class="hljs-keyword">then</span> delimiterOption <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>
|
||
<span class="hljs-string">"<span class="hljs-subst">#{printedDelimiter}</span><span class="hljs-subst">#{body}</span><span class="hljs-subst">#{printedDelimiter}</span>"</span>
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">sniffDirectives</span> = <span class="hljs-params">(expressions, {notFinalExpression} = {})</span> -></span>
|
||
index = <span class="hljs-number">0</span>
|
||
lastIndex = expressions.length - <span class="hljs-number">1</span>
|
||
<span class="hljs-keyword">while</span> index <= lastIndex
|
||
<span class="hljs-keyword">break</span> <span class="hljs-keyword">if</span> index <span class="hljs-keyword">is</span> lastIndex <span class="hljs-keyword">and</span> notFinalExpression
|
||
expression = expressions[index]
|
||
<span class="hljs-keyword">if</span> (unwrapped = expression?.unwrap?()) <span class="hljs-keyword">instanceof</span> PassthroughLiteral <span class="hljs-keyword">and</span> unwrapped.generated
|
||
index++
|
||
<span class="hljs-keyword">continue</span>
|
||
<span class="hljs-keyword">break</span> <span class="hljs-keyword">unless</span> expression <span class="hljs-keyword">instanceof</span> Value <span class="hljs-keyword">and</span> expression.isString() <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> expression.unwrap().shouldGenerateTemplateLiteral()
|
||
expressions[index] =
|
||
<span class="hljs-keyword">new</span> Directive expression
|
||
.withLocationDataFrom expression
|
||
index++
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">astAsBlockIfNeeded</span> = <span class="hljs-params">(node, o)</span> -></span>
|
||
unwrapped = node.unwrap()
|
||
<span class="hljs-keyword">if</span> unwrapped <span class="hljs-keyword">instanceof</span> Block <span class="hljs-keyword">and</span> unwrapped.expressions.length > <span class="hljs-number">1</span>
|
||
unwrapped.makeReturn <span class="hljs-literal">null</span>, <span class="hljs-literal">yes</span>
|
||
unwrapped.ast o, LEVEL_TOP
|
||
<span class="hljs-keyword">else</span>
|
||
node.ast o, LEVEL_PAREN</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-365">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-365">§</a>
|
||
</div>
|
||
<p>Helpers for <code>mergeLocationData</code> and <code>mergeAstLocationData</code> below.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">lesser</span> = <span class="hljs-params">(a, b)</span> -></span> <span class="hljs-keyword">if</span> a < b <span class="hljs-keyword">then</span> a <span class="hljs-keyword">else</span> b
|
||
<span class="hljs-function"><span class="hljs-title">greater</span> = <span class="hljs-params">(a, b)</span> -></span> <span class="hljs-keyword">if</span> a > b <span class="hljs-keyword">then</span> a <span class="hljs-keyword">else</span> b
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">isAstLocGreater</span> = <span class="hljs-params">(a, b)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> a.line > b.line
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> a.line <span class="hljs-keyword">is</span> b.line
|
||
a.column > b.column
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">isLocationDataStartGreater</span> = <span class="hljs-params">(a, b)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> a.first_line > b.first_line
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> a.first_line <span class="hljs-keyword">is</span> b.first_line
|
||
a.first_column > b.first_column
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">isLocationDataEndGreater</span> = <span class="hljs-params">(a, b)</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">yes</span> <span class="hljs-keyword">if</span> a.last_line > b.last_line
|
||
<span class="hljs-keyword">return</span> <span class="hljs-literal">no</span> <span class="hljs-keyword">unless</span> a.last_line <span class="hljs-keyword">is</span> b.last_line
|
||
a.last_column > b.last_column</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-366">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-366">§</a>
|
||
</div>
|
||
<p>Take two nodes’ location data and return a new <code>locationData</code> object that
|
||
encompasses the location data of both nodes. So the new <code>first_line</code> value
|
||
will be the earlier of the two nodes’ <code>first_line</code> values, the new
|
||
<code>last_column</code> the later of the two nodes’ <code>last_column</code> values, etc.</p>
|
||
<p>If you only want to extend the first node’s location data with the start or
|
||
end location data of the second node, pass the <code>justLeading</code> or <code>justEnding</code>
|
||
options. So e.g. if <code>first</code>’s range is [4, 5] and <code>second</code>’s range is [1, 10],
|
||
you’d get:</p>
|
||
<pre><code>mergeLocationData(first, second).range <span class="hljs-comment"># [1, 10]</span>
|
||
mergeLocationData(first, second, justLeading: <span class="hljs-literal">yes</span>).range <span class="hljs-comment"># [1, 5]</span>
|
||
mergeLocationData(first, second, justEnding: <span class="hljs-literal">yes</span>).range <span class="hljs-comment"># [4, 10]</span>
|
||
</code></pre>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.mergeLocationData = mergeLocationData = <span class="hljs-function"><span class="hljs-params">(locationDataA, locationDataB, {justLeading, justEnding} = {})</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.assign(
|
||
<span class="hljs-keyword">if</span> justEnding
|
||
first_line: locationDataA.first_line
|
||
first_column: locationDataA.first_column
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> isLocationDataStartGreater locationDataA, locationDataB
|
||
first_line: locationDataB.first_line
|
||
first_column: locationDataB.first_column
|
||
<span class="hljs-keyword">else</span>
|
||
first_line: locationDataA.first_line
|
||
first_column: locationDataA.first_column
|
||
,
|
||
<span class="hljs-keyword">if</span> justLeading
|
||
last_line: locationDataA.last_line
|
||
last_column: locationDataA.last_column
|
||
last_line_exclusive: locationDataA.last_line_exclusive
|
||
last_column_exclusive: locationDataA.last_column_exclusive
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> isLocationDataEndGreater locationDataA, locationDataB
|
||
last_line: locationDataA.last_line
|
||
last_column: locationDataA.last_column
|
||
last_line_exclusive: locationDataA.last_line_exclusive
|
||
last_column_exclusive: locationDataA.last_column_exclusive
|
||
<span class="hljs-keyword">else</span>
|
||
last_line: locationDataB.last_line
|
||
last_column: locationDataB.last_column
|
||
last_line_exclusive: locationDataB.last_line_exclusive
|
||
last_column_exclusive: locationDataB.last_column_exclusive
|
||
,
|
||
range: [
|
||
<span class="hljs-keyword">if</span> justEnding
|
||
locationDataA.range[<span class="hljs-number">0</span>]
|
||
<span class="hljs-keyword">else</span>
|
||
lesser locationDataA.range[<span class="hljs-number">0</span>], locationDataB.range[<span class="hljs-number">0</span>]
|
||
,
|
||
<span class="hljs-keyword">if</span> justLeading
|
||
locationDataA.range[<span class="hljs-number">1</span>]
|
||
<span class="hljs-keyword">else</span>
|
||
greater locationDataA.range[<span class="hljs-number">1</span>], locationDataB.range[<span class="hljs-number">1</span>]
|
||
]
|
||
)</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-367">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-367">§</a>
|
||
</div>
|
||
<p>Take two AST nodes, or two AST nodes’ location data objects, and return a new
|
||
location data object that encompasses the location data of both nodes. So the
|
||
new <code>start</code> value will be the earlier of the two nodes’ <code>start</code> values, the
|
||
new <code>end</code> value will be the later of the two nodes’ <code>end</code> values, etc.</p>
|
||
<p>If you only want to extend the first node’s location data with the start or
|
||
end location data of the second node, pass the <code>justLeading</code> or <code>justEnding</code>
|
||
options. So e.g. if <code>first</code>’s range is [4, 5] and <code>second</code>’s range is [1, 10],
|
||
you’d get:</p>
|
||
<pre><code>mergeAstLocationData(first, second).range <span class="hljs-comment"># [1, 10]</span>
|
||
mergeAstLocationData(first, second, justLeading: <span class="hljs-literal">yes</span>).range <span class="hljs-comment"># [1, 5]</span>
|
||
mergeAstLocationData(first, second, justEnding: <span class="hljs-literal">yes</span>).range <span class="hljs-comment"># [4, 10]</span>
|
||
</code></pre>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.mergeAstLocationData = mergeAstLocationData = <span class="hljs-function"><span class="hljs-params">(nodeA, nodeB, {justLeading, justEnding} = {})</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
loc:
|
||
start:
|
||
<span class="hljs-keyword">if</span> justEnding
|
||
nodeA.loc.start
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> isAstLocGreater nodeA.loc.start, nodeB.loc.start
|
||
nodeB.loc.start
|
||
<span class="hljs-keyword">else</span>
|
||
nodeA.loc.start
|
||
end:
|
||
<span class="hljs-keyword">if</span> justLeading
|
||
nodeA.loc.end
|
||
<span class="hljs-keyword">else</span>
|
||
<span class="hljs-keyword">if</span> isAstLocGreater nodeA.loc.end, nodeB.loc.end
|
||
nodeA.loc.end
|
||
<span class="hljs-keyword">else</span>
|
||
nodeB.loc.end
|
||
range: [
|
||
<span class="hljs-keyword">if</span> justEnding
|
||
nodeA.range[<span class="hljs-number">0</span>]
|
||
<span class="hljs-keyword">else</span>
|
||
lesser nodeA.range[<span class="hljs-number">0</span>], nodeB.range[<span class="hljs-number">0</span>]
|
||
,
|
||
<span class="hljs-keyword">if</span> justLeading
|
||
nodeA.range[<span class="hljs-number">1</span>]
|
||
<span class="hljs-keyword">else</span>
|
||
greater nodeA.range[<span class="hljs-number">1</span>], nodeB.range[<span class="hljs-number">1</span>]
|
||
]
|
||
start:
|
||
<span class="hljs-keyword">if</span> justEnding
|
||
nodeA.start
|
||
<span class="hljs-keyword">else</span>
|
||
lesser nodeA.start, nodeB.start
|
||
end:
|
||
<span class="hljs-keyword">if</span> justLeading
|
||
nodeA.end
|
||
<span class="hljs-keyword">else</span>
|
||
greater nodeA.end, nodeB.end</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-368">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-368">§</a>
|
||
</div>
|
||
<p>Convert Jison-style node class location data to Babel-style location data</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">exports</span>.jisonLocationDataToAstLocationData = jisonLocationDataToAstLocationData = <span class="hljs-function"><span class="hljs-params">({first_line, first_column, last_line_exclusive, last_column_exclusive, range})</span> -></span>
|
||
<span class="hljs-keyword">return</span>
|
||
loc:
|
||
start:
|
||
line: first_line + <span class="hljs-number">1</span>
|
||
column: first_column
|
||
end:
|
||
line: last_line_exclusive + <span class="hljs-number">1</span>
|
||
column: last_column_exclusive
|
||
range: [
|
||
range[<span class="hljs-number">0</span>]
|
||
range[<span class="hljs-number">1</span>]
|
||
]
|
||
start: range[<span class="hljs-number">0</span>]
|
||
end: range[<span class="hljs-number">1</span>]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-369">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-369">§</a>
|
||
</div>
|
||
<p>Generate a zero-width location data that corresponds to the end of another node’s location.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">zeroWidthLocationDataFromEndLocation</span> = <span class="hljs-params">({range: [, endRange], last_line_exclusive, last_column_exclusive})</span> -></span> {
|
||
first_line: last_line_exclusive
|
||
first_column: last_column_exclusive
|
||
last_line: last_line_exclusive
|
||
last_column: last_column_exclusive
|
||
last_line_exclusive
|
||
last_column_exclusive
|
||
range: [endRange, endRange]
|
||
}
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">extractSameLineLocationDataFirst</span> = <span class="hljs-params">(numChars)</span> -></span> ({range: [startRange], first_line, first_column}) -> {
|
||
first_line
|
||
first_column
|
||
last_line: first_line
|
||
last_column: first_column + numChars - <span class="hljs-number">1</span>
|
||
last_line_exclusive: first_line
|
||
last_column_exclusive: first_column + numChars
|
||
range: [startRange, startRange + numChars]
|
||
}
|
||
<span class="hljs-function">
|
||
<span class="hljs-title">extractSameLineLocationDataLast</span> = <span class="hljs-params">(numChars)</span> -></span> ({range: [, endRange], last_line, last_column, last_line_exclusive, last_column_exclusive}) -> {
|
||
first_line: last_line
|
||
first_column: last_column - (numChars - <span class="hljs-number">1</span>)
|
||
last_line: last_line
|
||
last_column: last_column
|
||
last_line_exclusive
|
||
last_column_exclusive
|
||
range: [endRange - numChars, endRange]
|
||
}</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-370">
|
||
<div class="annotation">
|
||
|
||
<div class="sswrap ">
|
||
<a class="ss" href="#section-370">§</a>
|
||
</div>
|
||
<p>We don’t currently have a token corresponding to the empty space
|
||
between interpolation/JSX expression braces, so piece together the location
|
||
data by trimming the braces from the Interpolation’s location data.
|
||
Technically the last_line/last_column calculation here could be
|
||
incorrect if the ending brace is preceded by a newline, but
|
||
last_line/last_column aren’t used for AST generation anyway.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">emptyExpressionLocationData</span> = <span class="hljs-params">({interpolationNode: element, openingBrace, closingBrace})</span> -></span>
|
||
first_line: element.locationData.first_line
|
||
first_column: element.locationData.first_column + openingBrace.length
|
||
last_line: element.locationData.last_line
|
||
last_column: element.locationData.last_column - closingBrace.length
|
||
last_line_exclusive: element.locationData.last_line
|
||
last_column_exclusive: element.locationData.last_column
|
||
range: [
|
||
element.locationData.range[<span class="hljs-number">0</span>] + openingBrace.length
|
||
element.locationData.range[<span class="hljs-number">1</span>] - closingBrace.length
|
||
]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
</body>
|
||
</html>
|