mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
4544 lines
236 KiB
HTML
4544 lines
236 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">
|
|
|
|
|
|
<a class="source" href="browser.html">
|
|
browser.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="cake.html">
|
|
cake.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="coffee-script.html">
|
|
coffee-script.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="command.html">
|
|
command.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="grammar.html">
|
|
grammar.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="helpers.html">
|
|
helpers.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="index.html">
|
|
index.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="lexer.html">
|
|
lexer.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="nodes.html">
|
|
nodes.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="optparse.html">
|
|
optparse.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="repl.html">
|
|
repl.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="rewriter.html">
|
|
rewriter.coffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="scope.html">
|
|
scope.litcoffee
|
|
</a>
|
|
|
|
|
|
<a class="source" href="sourcemap.html">
|
|
sourcemap.litcoffee
|
|
</a>
|
|
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
|
|
<ul class="sections">
|
|
|
|
<li id="title">
|
|
<div class="annotation">
|
|
<h1>nodes.coffee</h1>
|
|
</div>
|
|
</li>
|
|
|
|
|
|
|
|
<li id="section-1">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" 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>
|
|
Error.stackTraceLimit = Infinity
|
|
|
|
{Scope} = require <span class="string">'./scope'</span>
|
|
{RESERVED, STRICT_PROSCRIBED} = require <span class="string">'./lexer'</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-2">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" 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, last, some,
|
|
addLocationDataFn, locationDataToString, throwSyntaxError} = require <span class="string">'./helpers'</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-3">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-3">¶</a>
|
|
</div>
|
|
<p>Functions required by parser
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.extend = extend
|
|
exports.addLocationDataFn = addLocationDataFn</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-4">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" 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="function"><span class="title">YES</span></span> = -> <span class="literal">yes</span>
|
|
<span class="function"><span class="title">NO</span></span> = -> <span class="literal">no</span>
|
|
<span class="function"><span class="title">THIS</span></span> = -> <span class="keyword">this</span>
|
|
<span class="function"><span class="title">NEGATE</span></span> = -> <span class="property">@negated</span> = <span class="keyword">not</span> <span class="property">@negated</span>; <span class="keyword">this</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-5">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-5">¶</a>
|
|
</div>
|
|
<h3>CodeFragment</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-6">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" 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>exports.CodeFragment = <span class="class"><span class="keyword">class</span> <span class="title">CodeFragment</span></span>
|
|
constructor: (parent, code) ->
|
|
<span class="property">@code</span> = <span class="string">"<span class="subst">#{code}</span>"</span>
|
|
<span class="property">@locationData</span> = parent?.locationData
|
|
<span class="property">@type</span> = parent?.constructor?.name <span class="keyword">or</span> <span class="string">'unknown'</span>
|
|
|
|
toString: ->
|
|
<span class="string">"<span class="subst">#{@code}</span><span class="subst">#{<span class="keyword">if</span> @locationData <span class="keyword">then</span> ": " + locationDataToString(@locationData) <span class="keyword">else</span> ''}</span>"</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-7">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-7">¶</a>
|
|
</div>
|
|
<p>Convert an array of CodeFragments into a string.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre><span class="function"><span class="title">fragmentsToText</span></span> = (fragments) ->
|
|
(fragment.code <span class="keyword">for</span> fragment <span class="keyword">in</span> fragments).join(<span class="string">''</span>)</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-8">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-8">¶</a>
|
|
</div>
|
|
<h3>Base</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-9">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-9">¶</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>exports.Base = <span class="class"><span class="keyword">class</span> <span class="title">Base</span></span>
|
|
|
|
compile: (o, lvl) ->
|
|
fragmentsToText <span class="property">@compileToFragments</span> o, lvl</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-10">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-10">¶</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: (o, lvl) ->
|
|
o = extend {}, o
|
|
o.level = lvl <span class="keyword">if</span> lvl
|
|
node = <span class="property">@unfoldSoak</span>(o) <span class="keyword">or</span> <span class="keyword">this</span>
|
|
node.tab = o.indent
|
|
<span class="keyword">if</span> o.level <span class="keyword">is</span> LEVEL_TOP <span class="keyword">or</span> <span class="keyword">not</span> node.isStatement(o)
|
|
node.compileNode o
|
|
<span class="keyword">else</span>
|
|
node.compileClosure o</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-11">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-11">¶</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: (o) ->
|
|
<span class="keyword">if</span> jumpNode = <span class="property">@jumps</span>()
|
|
jumpNode.error <span class="string">'cannot use a pure statement in an expression'</span>
|
|
o.sharedScope = <span class="literal">yes</span>
|
|
Closure.wrap(<span class="keyword">this</span>).compileNode o</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-12">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-12">¶</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: (o, level, reused) ->
|
|
<span class="keyword">unless</span> <span class="property">@isComplex</span>()
|
|
ref = <span class="keyword">if</span> level <span class="keyword">then</span> <span class="property">@compileToFragments</span> o, level <span class="keyword">else</span> <span class="keyword">this</span>
|
|
[ref, ref]
|
|
<span class="keyword">else</span>
|
|
ref = <span class="keyword">new</span> Literal reused <span class="keyword">or</span> o.scope.freeVariable <span class="string">'ref'</span>
|
|
sub = <span class="keyword">new</span> Assign ref, <span class="keyword">this</span>
|
|
<span class="keyword">if</span> level <span class="keyword">then</span> [sub.compileToFragments(o, level), [<span class="property">@makeCode</span>(ref.value)]] <span class="keyword">else</span> [sub, ref]
|
|
|
|
cacheToCodeFragments: (cacheValues) ->
|
|
[fragmentsToText(cacheValues[<span class="number">0</span>]), fragmentsToText(cacheValues[<span class="number">1</span>])]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-13">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-13">¶</a>
|
|
</div>
|
|
<p>Construct a node that returns the current node's result.
|
|
Note that this is overridden for smarter behavior for
|
|
many statement nodes (e.g. If, For)...
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> makeReturn: (res) ->
|
|
me = <span class="property">@unwrapAll</span>()
|
|
<span class="keyword">if</span> res
|
|
<span class="keyword">new</span> Call <span class="keyword">new</span> Literal(<span class="string">"<span class="subst">#{res}</span>.push"</span>), [me]
|
|
<span class="keyword">else</span>
|
|
<span class="keyword">new</span> Return me</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-14">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-14">¶</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: (pred) ->
|
|
node = <span class="literal">undefined</span>
|
|
<span class="property">@traverseChildren</span> <span class="literal">no</span>, (n) ->
|
|
<span class="keyword">if</span> pred n
|
|
node = n
|
|
<span class="keyword">return</span> <span class="literal">no</span>
|
|
node</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-15">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-15">¶</a>
|
|
</div>
|
|
<p>Pull out the last non-comment node of a node list.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> lastNonComment: (list) ->
|
|
i = list.length
|
|
<span class="keyword">return</span> list[i] <span class="keyword">while</span> i-- <span class="keyword">when</span> list[i] <span class="keyword">not</span> <span class="keyword">instanceof</span> Comment
|
|
<span class="literal">null</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-16">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-16">¶</a>
|
|
</div>
|
|
<p><code>toString</code> 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: (idt = <span class="string">''</span>, name = <span class="property">@constructor</span>.name) ->
|
|
tree = <span class="string">'\n'</span> + idt + name
|
|
tree += <span class="string">'?'</span> <span class="keyword">if</span> <span class="property">@soak</span>
|
|
<span class="property">@eachChild</span> (node) -> tree += node.toString idt + TAB
|
|
tree</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-17">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-17">¶</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: (func) ->
|
|
<span class="keyword">return</span> <span class="keyword">this</span> <span class="keyword">unless</span> <span class="property">@children</span>
|
|
<span class="keyword">for</span> attr <span class="keyword">in</span> <span class="property">@children</span> <span class="keyword">when</span> @[attr]
|
|
<span class="keyword">for</span> child <span class="keyword">in</span> flatten [@[attr]]
|
|
<span class="keyword">return</span> <span class="keyword">this</span> <span class="keyword">if</span> func(child) <span class="keyword">is</span> <span class="literal">false</span>
|
|
<span class="keyword">this</span>
|
|
|
|
traverseChildren: (crossScope, func) ->
|
|
<span class="property">@eachChild</span> (child) ->
|
|
recur = func(child)
|
|
child.traverseChildren(crossScope, func) <span class="keyword">unless</span> recur <span class="keyword">is</span> <span class="literal">no</span>
|
|
|
|
invert: ->
|
|
<span class="keyword">new</span> Op <span class="string">'!'</span>, <span class="keyword">this</span>
|
|
|
|
unwrapAll: ->
|
|
node = <span class="keyword">this</span>
|
|
<span class="keyword">continue</span> <span class="keyword">until</span> node <span class="keyword">is</span> node = node.unwrap()
|
|
node</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-18">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-18">¶</a>
|
|
</div>
|
|
<p>Default implementations of the common node properties and methods. Nodes
|
|
will override these with custom logic, if needed.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> children: []
|
|
|
|
isStatement : NO
|
|
jumps : NO
|
|
isComplex : YES
|
|
isChainable : NO
|
|
isAssignable : NO
|
|
|
|
unwrap : THIS
|
|
unfoldSoak : NO</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-19">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-19">¶</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-20">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-20">¶</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: (locationData) ->
|
|
<span class="property">@locationData</span> <span class="keyword">or</span>= locationData
|
|
|
|
<span class="property">@eachChild</span> (child) ->
|
|
child.updateLocationDataIfMissing locationData</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-21">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-21">¶</a>
|
|
</div>
|
|
<p>Throw a SyntaxError associated with this node's location.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> error: (message) ->
|
|
throwSyntaxError message, <span class="property">@locationData</span>
|
|
|
|
makeCode: (code) ->
|
|
<span class="keyword">new</span> CodeFragment <span class="keyword">this</span>, code
|
|
|
|
wrapInBraces: (fragments) ->
|
|
[].concat <span class="property">@makeCode</span>(<span class="string">'('</span>), fragments, <span class="property">@makeCode</span>(<span class="string">')'</span>)</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-22">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-22">¶</a>
|
|
</div>
|
|
<p><code>fragmentsList</code> is an array of arrays of fragments. Each array in fragmentsList will be
|
|
concatonated 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: (fragmentsList, joinStr) ->
|
|
answer = []
|
|
<span class="keyword">for</span> fragments,i <span class="keyword">in</span> fragmentsList
|
|
<span class="keyword">if</span> i <span class="keyword">then</span> answer.push <span class="property">@makeCode</span> joinStr
|
|
answer = answer.concat fragments
|
|
answer</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-23">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-23">¶</a>
|
|
</div>
|
|
<h3>Block</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-24">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-24">¶</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>exports.Block = <span class="class"><span class="keyword">class</span> <span class="title">Block</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (nodes) ->
|
|
<span class="property">@expressions</span> = compact flatten nodes <span class="keyword">or</span> []
|
|
|
|
children: [<span class="string">'expressions'</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-25">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-25">¶</a>
|
|
</div>
|
|
<p>Tack an expression on to the end of this expression list.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> push: (node) ->
|
|
<span class="property">@expressions</span>.push node
|
|
<span class="keyword">this</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-26">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-26">¶</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="property">@expressions</span>.pop()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-27">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-27">¶</a>
|
|
</div>
|
|
<p>Add an expression at the beginning of this expression list.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> unshift: (node) ->
|
|
<span class="property">@expressions</span>.unshift node
|
|
<span class="keyword">this</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-28">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-28">¶</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="keyword">if</span> <span class="property">@expressions</span>.length <span class="keyword">is</span> <span class="number">1</span> <span class="keyword">then</span> <span class="property">@expressions</span>[<span class="number">0</span>] <span class="keyword">else</span> <span class="keyword">this</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-29">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-29">¶</a>
|
|
</div>
|
|
<p>Is this an empty block of code?
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> isEmpty: ->
|
|
<span class="keyword">not</span> <span class="property">@expressions</span>.length
|
|
|
|
isStatement: (o) ->
|
|
<span class="keyword">for</span> exp <span class="keyword">in</span> <span class="property">@expressions</span> <span class="keyword">when</span> exp.isStatement o
|
|
<span class="keyword">return</span> <span class="literal">yes</span>
|
|
<span class="literal">no</span>
|
|
|
|
jumps: (o) ->
|
|
<span class="keyword">for</span> exp <span class="keyword">in</span> <span class="property">@expressions</span>
|
|
<span class="keyword">return</span> exp <span class="keyword">if</span> exp.jumps o</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-30">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-30">¶</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: (res) ->
|
|
len = <span class="property">@expressions</span>.length
|
|
<span class="keyword">while</span> len--
|
|
expr = <span class="property">@expressions</span>[len]
|
|
<span class="keyword">if</span> expr <span class="keyword">not</span> <span class="keyword">instanceof</span> Comment
|
|
<span class="property">@expressions</span>[len] = expr.makeReturn res
|
|
<span class="property">@expressions</span>.splice(len, <span class="number">1</span>) <span class="keyword">if</span> expr <span class="keyword">instanceof</span> Return <span class="keyword">and</span> <span class="keyword">not</span> expr.expression
|
|
<span class="keyword">break</span>
|
|
<span class="keyword">this</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-31">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-31">¶</a>
|
|
</div>
|
|
<p>A <strong>Block</strong> is the only node that can serve as the root.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> compileToFragments: (o = {}, level) ->
|
|
<span class="keyword">if</span> o.scope <span class="keyword">then</span> <span class="keyword">super</span> o, level <span class="keyword">else</span> <span class="property">@compileRoot</span> o</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-32">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-32">¶</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: (o) ->
|
|
<span class="property">@tab</span> = o.indent
|
|
top = o.level <span class="keyword">is</span> LEVEL_TOP
|
|
compiledNodes = []
|
|
|
|
<span class="keyword">for</span> node, index <span class="keyword">in</span> <span class="property">@expressions</span>
|
|
|
|
node = node.unwrapAll()
|
|
node = (node.unfoldSoak(o) <span class="keyword">or</span> node)
|
|
<span class="keyword">if</span> node <span class="keyword">instanceof</span> Block</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-33">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-33">¶</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="keyword">else</span> <span class="keyword">if</span> top
|
|
node.front = <span class="literal">true</span>
|
|
fragments = node.compileToFragments o
|
|
<span class="keyword">unless</span> node.isStatement o
|
|
fragments.unshift <span class="property">@makeCode</span> <span class="string">"<span class="subst">#{@tab}</span>"</span>
|
|
fragments.push <span class="property">@makeCode</span> <span class="string">";"</span>
|
|
compiledNodes.push fragments
|
|
<span class="keyword">else</span>
|
|
compiledNodes.push node.compileToFragments o, LEVEL_LIST
|
|
<span class="keyword">if</span> top
|
|
<span class="keyword">if</span> <span class="property">@spaced</span>
|
|
<span class="keyword">return</span> [].concat <span class="property">@joinFragmentArrays</span>(compiledNodes, <span class="string">'\n\n'</span>), <span class="property">@makeCode</span>(<span class="string">"\n"</span>)
|
|
<span class="keyword">else</span>
|
|
<span class="keyword">return</span> <span class="property">@joinFragmentArrays</span>(compiledNodes, <span class="string">'\n'</span>)
|
|
<span class="keyword">if</span> compiledNodes.length
|
|
answer = <span class="property">@joinFragmentArrays</span>(compiledNodes, <span class="string">', '</span>)
|
|
<span class="keyword">else</span>
|
|
answer = [<span class="property">@makeCode</span> <span class="string">"void 0"</span>]
|
|
<span class="keyword">if</span> compiledNodes.length > <span class="number">1</span> <span class="keyword">and</span> o.level >= LEVEL_LIST <span class="keyword">then</span> <span class="property">@wrapInBraces</span> answer <span class="keyword">else</span> answer</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-34">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-34">¶</a>
|
|
</div>
|
|
<p>If we happen to be the top-level <strong>Block</strong>, 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> compileRoot: (o) ->
|
|
o.indent = <span class="keyword">if</span> o.bare <span class="keyword">then</span> <span class="string">''</span> <span class="keyword">else</span> TAB
|
|
o.level = LEVEL_TOP
|
|
<span class="property">@spaced</span> = <span class="literal">yes</span>
|
|
o.scope = <span class="keyword">new</span> Scope <span class="literal">null</span>, <span class="keyword">this</span>, <span class="literal">null</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-35">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-35">¶</a>
|
|
</div>
|
|
<p>Mark given local variables in the root scope as parameters so they don't
|
|
end up being declared on this block.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> o.scope.parameter name <span class="keyword">for</span> name <span class="keyword">in</span> o.locals <span class="keyword">or</span> []
|
|
prelude = []
|
|
<span class="keyword">unless</span> o.bare
|
|
preludeExps = <span class="keyword">for</span> exp, i <span class="keyword">in</span> <span class="property">@expressions</span>
|
|
<span class="keyword">break</span> <span class="keyword">unless</span> exp.unwrap() <span class="keyword">instanceof</span> Comment
|
|
exp
|
|
rest = <span class="property">@expressions</span>[preludeExps.length...]
|
|
<span class="property">@expressions</span> = preludeExps
|
|
<span class="keyword">if</span> preludeExps.length
|
|
prelude = <span class="property">@compileNode</span> merge(o, indent: <span class="string">''</span>)
|
|
prelude.push <span class="property">@makeCode</span> <span class="string">"\n"</span>
|
|
<span class="property">@expressions</span> = rest
|
|
fragments = <span class="property">@compileWithDeclarations</span> o
|
|
<span class="keyword">return</span> fragments <span class="keyword">if</span> o.bare
|
|
[].concat prelude, <span class="property">@makeCode</span>(<span class="string">"(function() {\n"</span>), fragments, <span class="property">@makeCode</span>(<span class="string">"\n}).call(this);\n"</span>)</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-36">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-36">¶</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: (o) ->
|
|
fragments = []
|
|
post = []
|
|
<span class="keyword">for</span> exp, i <span class="keyword">in</span> <span class="property">@expressions</span>
|
|
exp = exp.unwrap()
|
|
<span class="keyword">break</span> <span class="keyword">unless</span> exp <span class="keyword">instanceof</span> Comment <span class="keyword">or</span> exp <span class="keyword">instanceof</span> Literal
|
|
o = merge(o, level: LEVEL_TOP)
|
|
<span class="keyword">if</span> i
|
|
rest = <span class="property">@expressions</span>.splice i, <span class="number">9e9</span>
|
|
[spaced, <span class="property">@spaced</span>] = [<span class="property">@spaced</span>, <span class="literal">no</span>]
|
|
[fragments, <span class="property">@spaced</span>] = [<span class="property">@compileNode</span>(o), spaced]
|
|
<span class="property">@expressions</span> = rest
|
|
post = <span class="property">@compileNode</span> o
|
|
{scope} = o
|
|
<span class="keyword">if</span> scope.expressions <span class="keyword">is</span> <span class="keyword">this</span>
|
|
declars = o.scope.hasDeclarations()
|
|
assigns = scope.hasAssignments
|
|
<span class="keyword">if</span> declars <span class="keyword">or</span> assigns
|
|
fragments.push <span class="property">@makeCode</span> <span class="string">'\n'</span> <span class="keyword">if</span> i
|
|
fragments.push <span class="property">@makeCode</span> <span class="string">"<span class="subst">#{@tab}</span>var "</span>
|
|
<span class="keyword">if</span> declars
|
|
fragments.push <span class="property">@makeCode</span> scope.declaredVariables().join(<span class="string">', '</span>)
|
|
<span class="keyword">if</span> assigns
|
|
fragments.push <span class="property">@makeCode</span> <span class="string">",\n<span class="subst">#{@tab + TAB}</span>"</span> <span class="keyword">if</span> declars
|
|
fragments.push <span class="property">@makeCode</span> scope.assignedVariables().join(<span class="string">",\n<span class="subst">#{@tab + TAB}</span>"</span>)
|
|
fragments.push <span class="property">@makeCode</span> <span class="string">";\n<span class="subst">#{<span class="keyword">if</span> @spaced <span class="keyword">then</span> '\n' <span class="keyword">else</span> ''}</span>"</span>
|
|
<span class="keyword">else</span> <span class="keyword">if</span> fragments.length <span class="keyword">and</span> post.length
|
|
fragments.push <span class="property">@makeCode</span> <span class="string">"\n"</span>
|
|
fragments.concat post</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-37">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-37">¶</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> <span class="property">@wrap</span>: (nodes) ->
|
|
<span class="keyword">return</span> nodes[<span class="number">0</span>] <span class="keyword">if</span> nodes.length <span class="keyword">is</span> <span class="number">1</span> <span class="keyword">and</span> nodes[<span class="number">0</span>] <span class="keyword">instanceof</span> Block
|
|
<span class="keyword">new</span> Block nodes</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-38">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-38">¶</a>
|
|
</div>
|
|
<h3>Literal</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-39">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-39">¶</a>
|
|
</div>
|
|
<p>Literals are 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>exports.Literal = <span class="class"><span class="keyword">class</span> <span class="title">Literal</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@value</span>) ->
|
|
|
|
makeReturn: ->
|
|
<span class="keyword">if</span> <span class="property">@isStatement</span>() <span class="keyword">then</span> <span class="keyword">this</span> <span class="keyword">else</span> <span class="keyword">super</span>
|
|
|
|
isAssignable: ->
|
|
IDENTIFIER.test <span class="property">@value</span>
|
|
|
|
isStatement: ->
|
|
<span class="property">@value</span> <span class="keyword">in</span> [<span class="string">'break'</span>, <span class="string">'continue'</span>, <span class="string">'debugger'</span>]
|
|
|
|
isComplex: NO
|
|
|
|
assigns: (name) ->
|
|
name <span class="keyword">is</span> <span class="property">@value</span>
|
|
|
|
jumps: (o) ->
|
|
<span class="keyword">return</span> <span class="keyword">this</span> <span class="keyword">if</span> <span class="property">@value</span> <span class="keyword">is</span> <span class="string">'break'</span> <span class="keyword">and</span> <span class="keyword">not</span> (o?.<span class="keyword">loop</span> <span class="keyword">or</span> o?.block)
|
|
<span class="keyword">return</span> <span class="keyword">this</span> <span class="keyword">if</span> <span class="property">@value</span> <span class="keyword">is</span> <span class="string">'continue'</span> <span class="keyword">and</span> <span class="keyword">not</span> o?.<span class="keyword">loop</span>
|
|
|
|
compileNode: (o) ->
|
|
code = <span class="keyword">if</span> <span class="property">@value</span> <span class="keyword">is</span> <span class="string">'this'</span>
|
|
<span class="keyword">if</span> o.scope.method?.bound <span class="keyword">then</span> o.scope.method.context <span class="keyword">else</span> <span class="property">@value</span>
|
|
<span class="keyword">else</span> <span class="keyword">if</span> <span class="property">@value</span>.reserved
|
|
<span class="string">"\"<span class="subst">#{@value}</span>\""</span>
|
|
<span class="keyword">else</span>
|
|
<span class="property">@value</span>
|
|
answer = <span class="keyword">if</span> <span class="property">@isStatement</span>() <span class="keyword">then</span> <span class="string">"<span class="subst">#{@tab}</span><span class="subst">#{code}</span>;"</span> <span class="keyword">else</span> code
|
|
[<span class="property">@makeCode</span> answer]
|
|
|
|
toString: ->
|
|
<span class="string">' "'</span> + <span class="property">@value</span> + <span class="string">'"'</span>
|
|
|
|
<span class="class"><span class="keyword">class</span> <span class="title">exports</span>.<span class="title">Undefined</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
isAssignable: NO
|
|
isComplex: NO
|
|
compileNode: (o) ->
|
|
[<span class="property">@makeCode</span> <span class="keyword">if</span> o.level >= LEVEL_ACCESS <span class="keyword">then</span> <span class="string">'(void 0)'</span> <span class="keyword">else</span> <span class="string">'void 0'</span>]
|
|
|
|
<span class="class"><span class="keyword">class</span> <span class="title">exports</span>.<span class="title">Null</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
isAssignable: NO
|
|
isComplex: NO
|
|
compileNode: -> [<span class="property">@makeCode</span> <span class="string">"null"</span>]
|
|
|
|
<span class="class"><span class="keyword">class</span> <span class="title">exports</span>.<span class="title">Bool</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
isAssignable: NO
|
|
isComplex: NO
|
|
compileNode: -> [<span class="property">@makeCode</span> <span class="property">@val</span>]
|
|
constructor: (<span class="property">@val</span>) -></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-40">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-40">¶</a>
|
|
</div>
|
|
<h3>Return</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-41">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-41">¶</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>exports.Return = <span class="class"><span class="keyword">class</span> <span class="title">Return</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (expr) ->
|
|
<span class="property">@expression</span> = expr <span class="keyword">if</span> expr <span class="keyword">and</span> <span class="keyword">not</span> expr.unwrap().isUndefined
|
|
|
|
children: [<span class="string">'expression'</span>]
|
|
|
|
isStatement: YES
|
|
makeReturn: THIS
|
|
jumps: THIS
|
|
|
|
compileToFragments: (o, level) ->
|
|
expr = <span class="property">@expression</span>?.makeReturn()
|
|
<span class="keyword">if</span> expr <span class="keyword">and</span> expr <span class="keyword">not</span> <span class="keyword">instanceof</span> Return <span class="keyword">then</span> expr.compileToFragments o, level <span class="keyword">else</span> <span class="keyword">super</span> o, level
|
|
|
|
compileNode: (o) ->
|
|
answer = []</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-42">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-42">¶</a>
|
|
</div>
|
|
<p>TODO: If we call expression.compile() here twice, we'll sometimes get back different results!
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> answer.push <span class="property">@makeCode</span> <span class="property">@tab</span> + <span class="string">"return<span class="subst">#{<span class="keyword">if</span> @expression <span class="keyword">then</span> " " <span class="keyword">else</span> ""}</span>"</span>
|
|
<span class="keyword">if</span> <span class="property">@expression</span>
|
|
answer = answer.concat <span class="property">@expression</span>.compileToFragments o, LEVEL_PAREN
|
|
answer.push <span class="property">@makeCode</span> <span class="string">";"</span>
|
|
<span class="keyword">return</span> answer</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-43">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-43">¶</a>
|
|
</div>
|
|
<h3>Value</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-44">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-44">¶</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>exports.Value = <span class="class"><span class="keyword">class</span> <span class="title">Value</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (base, props, tag) ->
|
|
<span class="keyword">return</span> base <span class="keyword">if</span> <span class="keyword">not</span> props <span class="keyword">and</span> base <span class="keyword">instanceof</span> Value
|
|
<span class="property">@base</span> = base
|
|
<span class="property">@properties</span> = props <span class="keyword">or</span> []
|
|
@[tag] = <span class="literal">true</span> <span class="keyword">if</span> tag
|
|
<span class="keyword">return</span> <span class="keyword">this</span>
|
|
|
|
children: [<span class="string">'base'</span>, <span class="string">'properties'</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-45">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-45">¶</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: (props) ->
|
|
<span class="property">@properties</span> = <span class="property">@properties</span>.concat props
|
|
<span class="keyword">this</span>
|
|
|
|
hasProperties: ->
|
|
!!<span class="property">@properties</span>.length</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-46">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-46">¶</a>
|
|
</div>
|
|
<p>Some boolean checks for the benefit of other nodes.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> isArray : -> <span class="keyword">not</span> <span class="property">@properties</span>.length <span class="keyword">and</span> <span class="property">@base</span> <span class="keyword">instanceof</span> Arr
|
|
isComplex : -> <span class="property">@hasProperties</span>() <span class="keyword">or</span> <span class="property">@base</span>.isComplex()
|
|
isAssignable : -> <span class="property">@hasProperties</span>() <span class="keyword">or</span> <span class="property">@base</span>.isAssignable()
|
|
isSimpleNumber : -> <span class="property">@base</span> <span class="keyword">instanceof</span> Literal <span class="keyword">and</span> SIMPLENUM.test <span class="property">@base</span>.value
|
|
isString : -> <span class="property">@base</span> <span class="keyword">instanceof</span> Literal <span class="keyword">and</span> IS_STRING.test <span class="property">@base</span>.value
|
|
isAtomic : ->
|
|
<span class="keyword">for</span> node <span class="keyword">in</span> <span class="property">@properties</span>.concat <span class="property">@base</span>
|
|
<span class="keyword">return</span> <span class="literal">no</span> <span class="keyword">if</span> node.soak <span class="keyword">or</span> node <span class="keyword">instanceof</span> Call
|
|
<span class="literal">yes</span>
|
|
|
|
isStatement : (o) -> <span class="keyword">not</span> <span class="property">@properties</span>.length <span class="keyword">and</span> <span class="property">@base</span>.isStatement o
|
|
assigns : (name) -> <span class="keyword">not</span> <span class="property">@properties</span>.length <span class="keyword">and</span> <span class="property">@base</span>.assigns name
|
|
jumps : (o) -> <span class="keyword">not</span> <span class="property">@properties</span>.length <span class="keyword">and</span> <span class="property">@base</span>.jumps o
|
|
|
|
isObject: (onlyGenerated) ->
|
|
<span class="keyword">return</span> <span class="literal">no</span> <span class="keyword">if</span> <span class="property">@properties</span>.length
|
|
(<span class="property">@base</span> <span class="keyword">instanceof</span> Obj) <span class="keyword">and</span> (<span class="keyword">not</span> onlyGenerated <span class="keyword">or</span> <span class="property">@base</span>.generated)
|
|
|
|
isSplice: ->
|
|
last(<span class="property">@properties</span>) <span class="keyword">instanceof</span> Slice</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-47">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-47">¶</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="keyword">if</span> <span class="property">@properties</span>.length <span class="keyword">then</span> <span class="keyword">this</span> <span class="keyword">else</span> <span class="property">@base</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-48">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-48">¶</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: (o) ->
|
|
name = last <span class="property">@properties</span>
|
|
<span class="keyword">if</span> <span class="property">@properties</span>.length < <span class="number">2</span> <span class="keyword">and</span> <span class="keyword">not</span> <span class="property">@base</span>.isComplex() <span class="keyword">and</span> <span class="keyword">not</span> name?.isComplex()
|
|
<span class="keyword">return</span> [<span class="keyword">this</span>, <span class="keyword">this</span>] <span class="comment"># `a` `a.b`</span>
|
|
base = <span class="keyword">new</span> Value <span class="property">@base</span>, <span class="property">@properties</span>[...-<span class="number">1</span>]
|
|
<span class="keyword">if</span> base.isComplex() <span class="comment"># `a().b`</span>
|
|
bref = <span class="keyword">new</span> Literal o.scope.freeVariable <span class="string">'base'</span>
|
|
base = <span class="keyword">new</span> Value <span class="keyword">new</span> Parens <span class="keyword">new</span> Assign bref, base
|
|
<span class="keyword">return</span> [base, bref] <span class="keyword">unless</span> name <span class="comment"># `a()`</span>
|
|
<span class="keyword">if</span> name.isComplex() <span class="comment"># `a[b()]`</span>
|
|
nref = <span class="keyword">new</span> Literal o.scope.freeVariable <span class="string">'name'</span>
|
|
name = <span class="keyword">new</span> Index <span class="keyword">new</span> Assign nref, name.index
|
|
nref = <span class="keyword">new</span> Index nref
|
|
[base.add(name), <span class="keyword">new</span> Value(bref <span class="keyword">or</span> base.base, [nref <span class="keyword">or</span> name])]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-49">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-49">¶</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: (o) ->
|
|
<span class="property">@base</span>.front = <span class="property">@front</span>
|
|
props = <span class="property">@properties</span>
|
|
fragments = <span class="property">@base</span>.compileToFragments o, (<span class="keyword">if</span> props.length <span class="keyword">then</span> LEVEL_ACCESS <span class="keyword">else</span> <span class="literal">null</span>)
|
|
<span class="keyword">if</span> (<span class="property">@base</span> <span class="keyword">instanceof</span> Parens <span class="keyword">or</span> props.length) <span class="keyword">and</span> SIMPLENUM.test fragmentsToText fragments
|
|
fragments.push <span class="property">@makeCode</span> <span class="string">'.'</span>
|
|
<span class="keyword">for</span> prop <span class="keyword">in</span> props
|
|
fragments.push (prop.compileToFragments o)...
|
|
fragments</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-50">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-50">¶</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: (o) ->
|
|
<span class="property">@unfoldedSoak</span> ?= <span class="keyword">do</span> =>
|
|
<span class="keyword">if</span> ifn = <span class="property">@base</span>.unfoldSoak o
|
|
ifn.body.properties.push <span class="property">@properties</span>...
|
|
<span class="keyword">return</span> ifn
|
|
<span class="keyword">for</span> prop, i <span class="keyword">in</span> <span class="property">@properties</span> <span class="keyword">when</span> prop.soak
|
|
prop.soak = <span class="literal">off</span>
|
|
fst = <span class="keyword">new</span> Value <span class="property">@base</span>, <span class="property">@properties</span>[...i]
|
|
snd = <span class="keyword">new</span> Value <span class="property">@base</span>, <span class="property">@properties</span>[i..]
|
|
<span class="keyword">if</span> fst.isComplex()
|
|
ref = <span class="keyword">new</span> Literal o.scope.freeVariable <span class="string">'ref'</span>
|
|
fst = <span class="keyword">new</span> Parens <span class="keyword">new</span> Assign ref, fst
|
|
snd.base = ref
|
|
<span class="keyword">return</span> <span class="keyword">new</span> If <span class="keyword">new</span> Existence(fst), snd, soak: <span class="literal">on</span>
|
|
<span class="literal">no</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-51">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-51">¶</a>
|
|
</div>
|
|
<h3>Comment</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-52">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-52">¶</a>
|
|
</div>
|
|
<p>CoffeeScript passes through block comments as JavaScript block comments
|
|
at the same position.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.Comment = <span class="class"><span class="keyword">class</span> <span class="title">Comment</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@comment</span>) ->
|
|
|
|
isStatement: YES
|
|
makeReturn: THIS
|
|
|
|
compileNode: (o, level) ->
|
|
code = <span class="string">"/*<span class="subst">#{multident @comment, @tab}</span><span class="subst">#{<span class="keyword">if</span> '\n' <span class="keyword">in</span> @comment <span class="keyword">then</span> "\n#{@tab}</span>"</span> <span class="keyword">else</span> <span class="string">''</span>}*/\n<span class="string">"
|
|
code = o.indent + code if (level or o.level) is LEVEL_TOP
|
|
[@makeCode code]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-53">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-53">¶</a>
|
|
</div>
|
|
<h3>Call</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-54">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-54">¶</a>
|
|
</div>
|
|
<p>Node for a function invocation. Takes care of converting <code>super()</code> calls into
|
|
calls against the prototype's function of the same name.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.Call = <span class="class"><span class="keyword">class</span> <span class="title">Call</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (variable, <span class="property">@args</span> = [], <span class="property">@soak</span>) ->
|
|
<span class="property">@isNew</span> = <span class="literal">false</span>
|
|
<span class="property">@isSuper</span> = variable <span class="keyword">is</span> <span class="string">'super'</span>
|
|
<span class="property">@variable</span> = <span class="keyword">if</span> <span class="property">@isSuper</span> <span class="keyword">then</span> <span class="literal">null</span> <span class="keyword">else</span> variable
|
|
|
|
children: [<span class="string">'variable'</span>, <span class="string">'args'</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-55">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-55">¶</a>
|
|
</div>
|
|
<p>Tag this invocation as creating a new instance.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> newInstance: ->
|
|
base = <span class="property">@variable</span>?.base <span class="keyword">or</span> <span class="property">@variable</span>
|
|
<span class="keyword">if</span> base <span class="keyword">instanceof</span> Call <span class="keyword">and</span> <span class="keyword">not</span> base.isNew
|
|
base.newInstance()
|
|
<span class="keyword">else</span>
|
|
<span class="property">@isNew</span> = <span class="literal">true</span>
|
|
<span class="keyword">this</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-56">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-56">¶</a>
|
|
</div>
|
|
<p>Grab the reference to the superclass's implementation of the current
|
|
method.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> superReference: (o) ->
|
|
method = o.scope.namedMethod()
|
|
<span class="keyword">if</span> method?.klass
|
|
accesses = [<span class="keyword">new</span> Access(<span class="keyword">new</span> Literal <span class="string">'__super__'</span>)]
|
|
accesses.push <span class="keyword">new</span> Access <span class="keyword">new</span> Literal <span class="string">'constructor'</span> <span class="keyword">if</span> method.static
|
|
accesses.push <span class="keyword">new</span> Access <span class="keyword">new</span> Literal method.name
|
|
(<span class="keyword">new</span> Value (<span class="keyword">new</span> Literal method.klass), accesses).compile o
|
|
<span class="keyword">else</span> <span class="keyword">if</span> method?.ctor
|
|
<span class="string">"<span class="subst">#{method.name}</span>.__super__.constructor"</span>
|
|
<span class="keyword">else</span>
|
|
<span class="property">@error</span> <span class="string">'cannot call super outside of an instance method.'</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-57">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-57">¶</a>
|
|
</div>
|
|
<p>The appropriate <code>this</code> value for a <code>super</code> call.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> superThis : (o) ->
|
|
method = o.scope.method
|
|
(method <span class="keyword">and</span> <span class="keyword">not</span> method.klass <span class="keyword">and</span> method.context) <span class="keyword">or</span> <span class="string">"this"</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-58">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-58">¶</a>
|
|
</div>
|
|
<p>Soaked chained invocations unfold into if/else ternary structures.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> unfoldSoak: (o) ->
|
|
<span class="keyword">if</span> <span class="property">@soak</span>
|
|
<span class="keyword">if</span> <span class="property">@variable</span>
|
|
<span class="keyword">return</span> ifn <span class="keyword">if</span> ifn = unfoldSoak o, <span class="keyword">this</span>, <span class="string">'variable'</span>
|
|
[left, rite] = <span class="keyword">new</span> Value(<span class="property">@variable</span>).cacheReference o
|
|
<span class="keyword">else</span>
|
|
left = <span class="keyword">new</span> Literal <span class="property">@superReference</span> o
|
|
rite = <span class="keyword">new</span> Value left
|
|
rite = <span class="keyword">new</span> Call rite, <span class="property">@args</span>
|
|
rite.isNew = <span class="property">@isNew</span>
|
|
left = <span class="keyword">new</span> Literal <span class="string">"typeof <span class="subst">#{ left.compile o }</span> === \"function\""</span>
|
|
<span class="keyword">return</span> <span class="keyword">new</span> If left, <span class="keyword">new</span> Value(rite), soak: <span class="literal">yes</span>
|
|
call = <span class="keyword">this</span>
|
|
list = []
|
|
<span class="keyword">loop</span>
|
|
<span class="keyword">if</span> call.variable <span class="keyword">instanceof</span> Call
|
|
list.push call
|
|
call = call.variable
|
|
<span class="keyword">continue</span>
|
|
<span class="keyword">break</span> <span class="keyword">unless</span> call.variable <span class="keyword">instanceof</span> Value
|
|
list.push call
|
|
<span class="keyword">break</span> <span class="keyword">unless</span> (call = call.variable.base) <span class="keyword">instanceof</span> Call
|
|
<span class="keyword">for</span> call <span class="keyword">in</span> list.reverse()
|
|
<span class="keyword">if</span> ifn
|
|
<span class="keyword">if</span> call.variable <span class="keyword">instanceof</span> Call
|
|
call.variable = ifn
|
|
<span class="keyword">else</span>
|
|
call.variable.base = ifn
|
|
ifn = unfoldSoak o, call, <span class="string">'variable'</span>
|
|
ifn</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-59">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-59">¶</a>
|
|
</div>
|
|
<p>Compile a vanilla function call.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> compileNode: (o) ->
|
|
<span class="property">@variable</span>?.front = <span class="property">@front</span>
|
|
compiledArray = Splat.compileSplattedArray o, <span class="property">@args</span>, <span class="literal">true</span>
|
|
<span class="keyword">if</span> compiledArray.length
|
|
<span class="keyword">return</span> <span class="property">@compileSplat</span> o, compiledArray
|
|
compiledArgs = []
|
|
<span class="keyword">for</span> arg, argIndex <span class="keyword">in</span> <span class="property">@args</span>
|
|
<span class="keyword">if</span> argIndex <span class="keyword">then</span> compiledArgs.push <span class="property">@makeCode</span> <span class="string">", "</span>
|
|
compiledArgs.push (arg.compileToFragments o, LEVEL_LIST)...
|
|
|
|
fragments = []
|
|
<span class="keyword">if</span> <span class="property">@isSuper</span>
|
|
preface = <span class="property">@superReference</span>(o) + <span class="string">".call(<span class="subst">#{@superThis(o)}</span>"</span>
|
|
<span class="keyword">if</span> compiledArgs.length <span class="keyword">then</span> preface += <span class="string">", "</span>
|
|
fragments.push <span class="property">@makeCode</span> preface
|
|
<span class="keyword">else</span>
|
|
<span class="keyword">if</span> <span class="property">@isNew</span> <span class="keyword">then</span> fragments.push <span class="property">@makeCode</span> <span class="string">'new '</span>
|
|
fragments.push <span class="property">@variable</span>.compileToFragments(o, LEVEL_ACCESS)...
|
|
fragments.push <span class="property">@makeCode</span> <span class="string">"("</span>
|
|
fragments.push compiledArgs...
|
|
fragments.push <span class="property">@makeCode</span> <span class="string">")"</span>
|
|
fragments</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-60">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-60">¶</a>
|
|
</div>
|
|
<p>If you call a function with a splat, it's converted into a JavaScript
|
|
<code>.apply()</code> call to allow an array of arguments to be passed.
|
|
If it's a constructor, then things get real tricky. We have to inject an
|
|
inner constructor in order to be able to pass the varargs.
|
|
|
|
</p>
|
|
<p>splatArgs is an array of CodeFragments to put into the 'apply'.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> compileSplat: (o, splatArgs) ->
|
|
<span class="keyword">if</span> <span class="property">@isSuper</span>
|
|
<span class="keyword">return</span> [].concat <span class="property">@makeCode</span>(<span class="string">"<span class="subst">#{ @superReference o }</span>.apply(<span class="subst">#{@superThis(o)}</span>, "</span>),
|
|
splatArgs, <span class="property">@makeCode</span>(<span class="string">")"</span>)
|
|
|
|
<span class="keyword">if</span> <span class="property">@isNew</span>
|
|
idt = <span class="property">@tab</span> + TAB
|
|
<span class="keyword">return</span> [].concat <span class="property">@makeCode</span>(<span class="string">"""
|
|
(function(func, args, ctor) {
|
|
<span class="subst">#{idt}</span>ctor.prototype = func.prototype;
|
|
<span class="subst">#{idt}</span>var child = new ctor, result = func.apply(child, args);
|
|
<span class="subst">#{idt}</span>return Object(result) === result ? result : child;
|
|
<span class="subst">#{@tab}</span>})("""</span>),
|
|
(<span class="property">@variable</span>.compileToFragments o, LEVEL_LIST),
|
|
<span class="property">@makeCode</span>(<span class="string">", "</span>), splatArgs, <span class="property">@makeCode</span>(<span class="string">", function(){})"</span>)
|
|
|
|
answer = []
|
|
base = <span class="keyword">new</span> Value <span class="property">@variable</span>
|
|
<span class="keyword">if</span> (name = base.properties.pop()) <span class="keyword">and</span> base.isComplex()
|
|
ref = o.scope.freeVariable <span class="string">'ref'</span>
|
|
answer = answer.concat <span class="property">@makeCode</span>(<span class="string">"(<span class="subst">#{ref}</span> = "</span>),
|
|
(base.compileToFragments o, LEVEL_LIST),
|
|
<span class="property">@makeCode</span>(<span class="string">")"</span>),
|
|
name.compileToFragments(o)
|
|
<span class="keyword">else</span>
|
|
fun = base.compileToFragments o, LEVEL_ACCESS
|
|
fun = <span class="property">@wrapInBraces</span> fun <span class="keyword">if</span> SIMPLENUM.test fragmentsToText fun
|
|
<span class="keyword">if</span> name
|
|
ref = fragmentsToText fun
|
|
fun.push (name.compileToFragments o)...
|
|
<span class="keyword">else</span>
|
|
ref = <span class="string">'null'</span>
|
|
answer = answer.concat fun
|
|
answer = answer.concat <span class="property">@makeCode</span>(<span class="string">".apply(<span class="subst">#{ref}</span>, "</span>), splatArgs, <span class="property">@makeCode</span>(<span class="string">")"</span>)</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-61">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-61">¶</a>
|
|
</div>
|
|
<h3>Extends</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-62">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-62">¶</a>
|
|
</div>
|
|
<p>Node to extend an object's prototype with an ancestor object.
|
|
After <code>goog.inherits</code> from the
|
|
<a href="http://closure-library.googlecode.com/svn/docs/closureGoogBase.js.html">Closure Library</a>.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.Extends = <span class="class"><span class="keyword">class</span> <span class="title">Extends</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@child</span>, <span class="property">@parent</span>) ->
|
|
|
|
children: [<span class="string">'child'</span>, <span class="string">'parent'</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-63">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-63">¶</a>
|
|
</div>
|
|
<p>Hooks one constructor into another's prototype chain.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> compileToFragments: (o) ->
|
|
<span class="keyword">new</span> Call(<span class="keyword">new</span> Value(<span class="keyword">new</span> Literal utility <span class="string">'extends'</span>), [<span class="property">@child</span>, <span class="property">@parent</span>]).compileToFragments o</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-64">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-64">¶</a>
|
|
</div>
|
|
<h3>Access</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-65">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-65">¶</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>exports.Access = <span class="class"><span class="keyword">class</span> <span class="title">Access</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@name</span>, tag) ->
|
|
<span class="property">@name</span>.asKey = <span class="literal">yes</span>
|
|
<span class="property">@soak</span> = tag <span class="keyword">is</span> <span class="string">'soak'</span>
|
|
|
|
children: [<span class="string">'name'</span>]
|
|
|
|
compileToFragments: (o) ->
|
|
name = <span class="property">@name</span>.compileToFragments o
|
|
<span class="keyword">if</span> IDENTIFIER.test fragmentsToText name
|
|
name.unshift <span class="property">@makeCode</span> <span class="string">"."</span>
|
|
<span class="keyword">else</span>
|
|
name.unshift <span class="property">@makeCode</span> <span class="string">"["</span>
|
|
name.push <span class="property">@makeCode</span> <span class="string">"]"</span>
|
|
name
|
|
|
|
isComplex: NO</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-66">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-66">¶</a>
|
|
</div>
|
|
<h3>Index</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-67">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-67">¶</a>
|
|
</div>
|
|
<p>A <code>[ ... ]</code> indexed access into an array or object.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.Index = <span class="class"><span class="keyword">class</span> <span class="title">Index</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@index</span>) ->
|
|
|
|
children: [<span class="string">'index'</span>]
|
|
|
|
compileToFragments: (o) ->
|
|
[].concat <span class="property">@makeCode</span>(<span class="string">"["</span>), <span class="property">@index</span>.compileToFragments(o, LEVEL_PAREN), <span class="property">@makeCode</span>(<span class="string">"]"</span>)
|
|
|
|
isComplex: ->
|
|
<span class="property">@index</span>.isComplex()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-68">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-68">¶</a>
|
|
</div>
|
|
<h3>Range</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-69">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-69">¶</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>exports.Range = <span class="class"><span class="keyword">class</span> <span class="title">Range</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
|
|
children: [<span class="string">'from'</span>, <span class="string">'to'</span>]
|
|
|
|
constructor: (<span class="property">@from</span>, <span class="property">@to</span>, tag) ->
|
|
<span class="property">@exclusive</span> = tag <span class="keyword">is</span> <span class="string">'exclusive'</span>
|
|
<span class="property">@equals</span> = <span class="keyword">if</span> <span class="property">@exclusive</span> <span class="keyword">then</span> <span class="string">''</span> <span class="keyword">else</span> <span class="string">'='</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-70">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-70">¶</a>
|
|
</div>
|
|
<p>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: (o) ->
|
|
o = merge o, top: <span class="literal">true</span>
|
|
[<span class="property">@fromC</span>, <span class="property">@fromVar</span>] = <span class="property">@cacheToCodeFragments</span> <span class="property">@from</span>.cache o, LEVEL_LIST
|
|
[<span class="property">@toC</span>, <span class="property">@toVar</span>] = <span class="property">@cacheToCodeFragments</span> <span class="property">@to</span>.cache o, LEVEL_LIST
|
|
[<span class="property">@step</span>, <span class="property">@stepVar</span>] = <span class="property">@cacheToCodeFragments</span> step.cache o, LEVEL_LIST <span class="keyword">if</span> step = del o, <span class="string">'step'</span>
|
|
[<span class="property">@fromNum</span>, <span class="property">@toNum</span>] = [<span class="property">@fromVar</span>.match(SIMPLENUM), <span class="property">@toVar</span>.match(SIMPLENUM)]
|
|
<span class="property">@stepNum</span> = <span class="property">@stepVar</span>.match(SIMPLENUM) <span class="keyword">if</span> <span class="property">@stepVar</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-71">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-71">¶</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: (o) ->
|
|
<span class="property">@compileVariables</span> o <span class="keyword">unless</span> <span class="property">@fromVar</span>
|
|
<span class="keyword">return</span> <span class="property">@compileArray</span>(o) <span class="keyword">unless</span> o.index</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-72">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-72">¶</a>
|
|
</div>
|
|
<p>Set up endpoints.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> known = <span class="property">@fromNum</span> <span class="keyword">and</span> <span class="property">@toNum</span>
|
|
idx = del o, <span class="string">'index'</span>
|
|
idxName = del o, <span class="string">'name'</span>
|
|
namedIndex = idxName <span class="keyword">and</span> idxName <span class="keyword">isnt</span> idx
|
|
varPart = <span class="string">"<span class="subst">#{idx}</span> = <span class="subst">#{@fromC}</span>"</span>
|
|
varPart += <span class="string">", <span class="subst">#{@toC}</span>"</span> <span class="keyword">if</span> <span class="property">@toC</span> <span class="keyword">isnt</span> <span class="property">@toVar</span>
|
|
varPart += <span class="string">", <span class="subst">#{@step}</span>"</span> <span class="keyword">if</span> <span class="property">@step</span> <span class="keyword">isnt</span> <span class="property">@stepVar</span>
|
|
[lt, gt] = [<span class="string">"<span class="subst">#{idx}</span> <<span class="subst">#{@equals}</span>"</span>, <span class="string">"<span class="subst">#{idx}</span> ><span class="subst">#{@equals}</span>"</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-73">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-73">¶</a>
|
|
</div>
|
|
<p>Generate the condition.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> condPart = <span class="keyword">if</span> <span class="property">@stepNum</span>
|
|
<span class="keyword">if</span> +<span class="property">@stepNum</span> > <span class="number">0</span> <span class="keyword">then</span> <span class="string">"<span class="subst">#{lt}</span> <span class="subst">#{@toVar}</span>"</span> <span class="keyword">else</span> <span class="string">"<span class="subst">#{gt}</span> <span class="subst">#{@toVar}</span>"</span>
|
|
<span class="keyword">else</span> <span class="keyword">if</span> known
|
|
[from, to] = [+<span class="property">@fromNum</span>, +<span class="property">@toNum</span>]
|
|
<span class="keyword">if</span> from <= to <span class="keyword">then</span> <span class="string">"<span class="subst">#{lt}</span> <span class="subst">#{to}</span>"</span> <span class="keyword">else</span> <span class="string">"<span class="subst">#{gt}</span> <span class="subst">#{to}</span>"</span>
|
|
<span class="keyword">else</span>
|
|
cond = <span class="keyword">if</span> <span class="property">@stepVar</span> <span class="keyword">then</span> <span class="string">"<span class="subst">#{@stepVar}</span> > 0"</span> <span class="keyword">else</span> <span class="string">"<span class="subst">#{@fromVar}</span> <= <span class="subst">#{@toVar}</span>"</span>
|
|
<span class="string">"<span class="subst">#{cond}</span> ? <span class="subst">#{lt}</span> <span class="subst">#{@toVar}</span> : <span class="subst">#{gt}</span> <span class="subst">#{@toVar}</span>"</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-74">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-74">¶</a>
|
|
</div>
|
|
<p>Generate the step.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> stepPart = <span class="keyword">if</span> <span class="property">@stepVar</span>
|
|
<span class="string">"<span class="subst">#{idx}</span> += <span class="subst">#{@stepVar}</span>"</span>
|
|
<span class="keyword">else</span> <span class="keyword">if</span> known
|
|
<span class="keyword">if</span> namedIndex
|
|
<span class="keyword">if</span> from <= to <span class="keyword">then</span> <span class="string">"++<span class="subst">#{idx}</span>"</span> <span class="keyword">else</span> <span class="string">"--<span class="subst">#{idx}</span>"</span>
|
|
<span class="keyword">else</span>
|
|
<span class="keyword">if</span> from <= to <span class="keyword">then</span> <span class="string">"<span class="subst">#{idx}</span>++"</span> <span class="keyword">else</span> <span class="string">"<span class="subst">#{idx}</span>--"</span>
|
|
<span class="keyword">else</span>
|
|
<span class="keyword">if</span> namedIndex
|
|
<span class="string">"<span class="subst">#{cond}</span> ? ++<span class="subst">#{idx}</span> : --<span class="subst">#{idx}</span>"</span>
|
|
<span class="keyword">else</span>
|
|
<span class="string">"<span class="subst">#{cond}</span> ? <span class="subst">#{idx}</span>++ : <span class="subst">#{idx}</span>--"</span>
|
|
|
|
varPart = <span class="string">"<span class="subst">#{idxName}</span> = <span class="subst">#{varPart}</span>"</span> <span class="keyword">if</span> namedIndex
|
|
stepPart = <span class="string">"<span class="subst">#{idxName}</span> = <span class="subst">#{stepPart}</span>"</span> <span class="keyword">if</span> namedIndex</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-75">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-75">¶</a>
|
|
</div>
|
|
<p>The final loop body.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> [<span class="property">@makeCode</span> <span class="string">"<span class="subst">#{varPart}</span>; <span class="subst">#{condPart}</span>; <span class="subst">#{stepPart}</span>"</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-76">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-76">¶</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: (o) ->
|
|
<span class="keyword">if</span> <span class="property">@fromNum</span> <span class="keyword">and</span> <span class="property">@toNum</span> <span class="keyword">and</span> Math.abs(<span class="property">@fromNum</span> - <span class="property">@toNum</span>) <= <span class="number">20</span>
|
|
range = [+<span class="property">@fromNum</span>..+<span class="property">@toNum</span>]
|
|
range.pop() <span class="keyword">if</span> <span class="property">@exclusive</span>
|
|
<span class="keyword">return</span> [<span class="property">@makeCode</span> <span class="string">"[<span class="subst">#{ range.join(', ') }</span>]"</span>]
|
|
idt = <span class="property">@tab</span> + TAB
|
|
i = o.scope.freeVariable <span class="string">'i'</span>
|
|
result = o.scope.freeVariable <span class="string">'results'</span>
|
|
pre = <span class="string">"\n<span class="subst">#{idt}</span><span class="subst">#{result}</span> = [];"</span>
|
|
<span class="keyword">if</span> <span class="property">@fromNum</span> <span class="keyword">and</span> <span class="property">@toNum</span>
|
|
o.index = i
|
|
body = fragmentsToText <span class="property">@compileNode</span> o
|
|
<span class="keyword">else</span>
|
|
vars = <span class="string">"<span class="subst">#{i}</span> = <span class="subst">#{@fromC}</span>"</span> + <span class="keyword">if</span> <span class="property">@toC</span> <span class="keyword">isnt</span> <span class="property">@toVar</span> <span class="keyword">then</span> <span class="string">", <span class="subst">#{@toC}</span>"</span> <span class="keyword">else</span> <span class="string">''</span>
|
|
cond = <span class="string">"<span class="subst">#{@fromVar}</span> <= <span class="subst">#{@toVar}</span>"</span>
|
|
body = <span class="string">"var <span class="subst">#{vars}</span>; <span class="subst">#{cond}</span> ? <span class="subst">#{i}</span> <<span class="subst">#{@equals}</span> <span class="subst">#{@toVar}</span> : <span class="subst">#{i}</span> ><span class="subst">#{@equals}</span> <span class="subst">#{@toVar}</span>; <span class="subst">#{cond}</span> ? <span class="subst">#{i}</span>++ : <span class="subst">#{i}</span>--"</span>
|
|
post = <span class="string">"{ <span class="subst">#{result}</span>.push(<span class="subst">#{i}</span>); }\n<span class="subst">#{idt}</span>return <span class="subst">#{result}</span>;\n<span class="subst">#{o.indent}</span>"</span>
|
|
<span class="function"><span class="title">hasArgs</span></span> = (node) -> node?.contains (n) -> n <span class="keyword">instanceof</span> Literal <span class="keyword">and</span> n.value <span class="keyword">is</span> <span class="string">'arguments'</span> <span class="keyword">and</span> <span class="keyword">not</span> n.asKey
|
|
args = <span class="string">', arguments'</span> <span class="keyword">if</span> hasArgs(<span class="property">@from</span>) <span class="keyword">or</span> hasArgs(<span class="property">@to</span>)
|
|
[<span class="property">@makeCode</span> <span class="string">"(function() {<span class="subst">#{pre}</span>\n<span class="subst">#{idt}</span>for (<span class="subst">#{body}</span>)<span class="subst">#{post}</span>}).apply(this<span class="subst">#{args ? ''}</span>)"</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-77">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-77">¶</a>
|
|
</div>
|
|
<h3>Slice</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-78">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-78">¶</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>exports.Slice = <span class="class"><span class="keyword">class</span> <span class="title">Slice</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
|
|
children: [<span class="string">'range'</span>]
|
|
|
|
constructor: (<span class="property">@range</span>) ->
|
|
<span class="keyword">super</span>()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-79">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-79">¶</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: (o) ->
|
|
{to, from} = <span class="property">@range</span>
|
|
fromCompiled = from <span class="keyword">and</span> from.compileToFragments(o, LEVEL_PAREN) <span class="keyword">or</span> [<span class="property">@makeCode</span> <span class="string">'0'</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-80">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-80">¶</a>
|
|
</div>
|
|
<p>TODO: jwalton - move this into the 'if'?
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">if</span> to
|
|
compiled = to.compileToFragments o, LEVEL_PAREN
|
|
compiledText = fragmentsToText compiled
|
|
<span class="keyword">if</span> <span class="keyword">not</span> (<span class="keyword">not</span> <span class="property">@range</span>.exclusive <span class="keyword">and</span> +compiledText <span class="keyword">is</span> -<span class="number">1</span>)
|
|
toStr = <span class="string">', '</span> + <span class="keyword">if</span> <span class="property">@range</span>.exclusive
|
|
compiledText
|
|
<span class="keyword">else</span> <span class="keyword">if</span> SIMPLENUM.test compiledText
|
|
<span class="string">"<span class="subst">#{+compiledText + <span class="number">1</span>}</span>"</span>
|
|
<span class="keyword">else</span>
|
|
compiled = to.compileToFragments o, LEVEL_ACCESS
|
|
<span class="string">"+<span class="subst">#{fragmentsToText compiled}</span> + 1 || 9e9"</span>
|
|
[<span class="property">@makeCode</span> <span class="string">".slice(<span class="subst">#{ fragmentsToText fromCompiled }</span><span class="subst">#{ toStr <span class="keyword">or</span> '' }</span>)"</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-81">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-81">¶</a>
|
|
</div>
|
|
<h3>Obj</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-82">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-82">¶</a>
|
|
</div>
|
|
<p>An object literal, nothing fancy.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.Obj = <span class="class"><span class="keyword">class</span> <span class="title">Obj</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (props, <span class="property">@generated</span> = <span class="literal">false</span>) ->
|
|
<span class="property">@objects</span> = <span class="property">@properties</span> = props <span class="keyword">or</span> []
|
|
|
|
children: [<span class="string">'properties'</span>]
|
|
|
|
compileNode: (o) ->
|
|
props = <span class="property">@properties</span>
|
|
<span class="keyword">return</span> [<span class="property">@makeCode</span>(<span class="keyword">if</span> <span class="property">@front</span> <span class="keyword">then</span> <span class="string">'({})'</span> <span class="keyword">else</span> <span class="string">'{}'</span>)] <span class="keyword">unless</span> props.length
|
|
<span class="keyword">if</span> <span class="property">@generated</span>
|
|
<span class="keyword">for</span> node <span class="keyword">in</span> props <span class="keyword">when</span> node <span class="keyword">instanceof</span> Value
|
|
node.error <span class="string">'cannot have an implicit value in an implicit object'</span>
|
|
idt = o.indent += TAB
|
|
lastNoncom = <span class="property">@lastNonComment</span> <span class="property">@properties</span>
|
|
answer = []
|
|
<span class="keyword">for</span> prop, i <span class="keyword">in</span> props
|
|
join = <span class="keyword">if</span> i <span class="keyword">is</span> props.length - <span class="number">1</span>
|
|
<span class="string">''</span>
|
|
<span class="keyword">else</span> <span class="keyword">if</span> prop <span class="keyword">is</span> lastNoncom <span class="keyword">or</span> prop <span class="keyword">instanceof</span> Comment
|
|
<span class="string">'\n'</span>
|
|
<span class="keyword">else</span>
|
|
<span class="string">',\n'</span>
|
|
indent = <span class="keyword">if</span> prop <span class="keyword">instanceof</span> Comment <span class="keyword">then</span> <span class="string">''</span> <span class="keyword">else</span> idt
|
|
<span class="keyword">if</span> prop <span class="keyword">instanceof</span> Assign <span class="keyword">and</span> prop.variable <span class="keyword">instanceof</span> Value <span class="keyword">and</span> prop.variable.hasProperties()
|
|
prop.variable.error <span class="string">'Invalid object key'</span>
|
|
<span class="keyword">if</span> prop <span class="keyword">instanceof</span> Value <span class="keyword">and</span> prop.<span class="keyword">this</span>
|
|
prop = <span class="keyword">new</span> Assign prop.properties[<span class="number">0</span>].name, prop, <span class="string">'object'</span>
|
|
<span class="keyword">if</span> prop <span class="keyword">not</span> <span class="keyword">instanceof</span> Comment
|
|
<span class="keyword">if</span> prop <span class="keyword">not</span> <span class="keyword">instanceof</span> Assign
|
|
prop = <span class="keyword">new</span> Assign prop, prop, <span class="string">'object'</span>
|
|
(prop.variable.base <span class="keyword">or</span> prop.variable).asKey = <span class="literal">yes</span>
|
|
<span class="keyword">if</span> indent <span class="keyword">then</span> answer.push <span class="property">@makeCode</span> indent
|
|
answer.push prop.compileToFragments(o, LEVEL_TOP)...
|
|
<span class="keyword">if</span> join <span class="keyword">then</span> answer.push <span class="property">@makeCode</span> join
|
|
answer.unshift <span class="property">@makeCode</span> <span class="string">"{<span class="subst">#{ props.length <span class="keyword">and</span> '\n' }</span>"</span>
|
|
answer.push <span class="property">@makeCode</span> <span class="string">"<span class="subst">#{ props.length <span class="keyword">and</span> '\n' + @tab }</span>}"</span>
|
|
<span class="keyword">if</span> <span class="property">@front</span> <span class="keyword">then</span> <span class="property">@wrapInBraces</span> answer <span class="keyword">else</span> answer
|
|
|
|
assigns: (name) ->
|
|
<span class="keyword">for</span> prop <span class="keyword">in</span> <span class="property">@properties</span> <span class="keyword">when</span> prop.assigns name <span class="keyword">then</span> <span class="keyword">return</span> <span class="literal">yes</span>
|
|
<span class="literal">no</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-83">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-83">¶</a>
|
|
</div>
|
|
<h3>Arr</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-84">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-84">¶</a>
|
|
</div>
|
|
<p>An array literal.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.Arr = <span class="class"><span class="keyword">class</span> <span class="title">Arr</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (objs) ->
|
|
<span class="property">@objects</span> = objs <span class="keyword">or</span> []
|
|
|
|
children: [<span class="string">'objects'</span>]
|
|
|
|
compileNode: (o) ->
|
|
<span class="keyword">return</span> [<span class="property">@makeCode</span> <span class="string">'[]'</span>] <span class="keyword">unless</span> <span class="property">@objects</span>.length
|
|
o.indent += TAB
|
|
answer = Splat.compileSplattedArray o, <span class="property">@objects</span>
|
|
<span class="keyword">return</span> answer <span class="keyword">if</span> answer.length
|
|
|
|
answer = []
|
|
compiledObjs = (obj.compileToFragments o, LEVEL_LIST <span class="keyword">for</span> obj <span class="keyword">in</span> <span class="property">@objects</span>)
|
|
<span class="keyword">for</span> fragments, index <span class="keyword">in</span> compiledObjs
|
|
<span class="keyword">if</span> index
|
|
answer.push <span class="property">@makeCode</span> <span class="string">", "</span>
|
|
answer.push fragments...
|
|
<span class="keyword">if</span> fragmentsToText(answer).indexOf(<span class="string">'\n'</span>) >= <span class="number">0</span>
|
|
answer.unshift <span class="property">@makeCode</span> <span class="string">"[\n<span class="subst">#{o.indent}</span>"</span>
|
|
answer.push <span class="property">@makeCode</span> <span class="string">"\n<span class="subst">#{@tab}</span>]"</span>
|
|
<span class="keyword">else</span>
|
|
answer.unshift <span class="property">@makeCode</span> <span class="string">"["</span>
|
|
answer.push <span class="property">@makeCode</span> <span class="string">"]"</span>
|
|
answer
|
|
|
|
assigns: (name) ->
|
|
<span class="keyword">for</span> obj <span class="keyword">in</span> <span class="property">@objects</span> <span class="keyword">when</span> obj.assigns name <span class="keyword">then</span> <span class="keyword">return</span> <span class="literal">yes</span>
|
|
<span class="literal">no</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-85">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-85">¶</a>
|
|
</div>
|
|
<h3>Class</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-86">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-86">¶</a>
|
|
</div>
|
|
<p>The CoffeeScript class definition.
|
|
Initialize a <strong>Class</strong> with its name, an optional superclass, and a
|
|
list of prototype property assignments.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.Class = <span class="class"><span class="keyword">class</span> <span class="title">Class</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@variable</span>, <span class="property">@parent</span>, <span class="property">@body</span> = <span class="keyword">new</span> Block) ->
|
|
<span class="property">@boundFuncs</span> = []
|
|
<span class="property">@body</span>.classBody = <span class="literal">yes</span>
|
|
|
|
children: [<span class="string">'variable'</span>, <span class="string">'parent'</span>, <span class="string">'body'</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-87">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-87">¶</a>
|
|
</div>
|
|
<p>Figure out the appropriate name for the constructor function of this class.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> determineName: ->
|
|
<span class="keyword">return</span> <span class="literal">null</span> <span class="keyword">unless</span> <span class="property">@variable</span>
|
|
decl = <span class="keyword">if</span> tail = last <span class="property">@variable</span>.properties
|
|
tail <span class="keyword">instanceof</span> Access <span class="keyword">and</span> tail.name.value
|
|
<span class="keyword">else</span>
|
|
<span class="property">@variable</span>.base.value
|
|
<span class="keyword">if</span> decl <span class="keyword">in</span> STRICT_PROSCRIBED
|
|
<span class="property">@variable</span>.error <span class="string">"class variable name may not be <span class="subst">#{decl}</span>"</span>
|
|
decl <span class="keyword">and</span>= IDENTIFIER.test(decl) <span class="keyword">and</span> decl</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-88">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-88">¶</a>
|
|
</div>
|
|
<p>For all <code>this</code>-references and bound functions in the class definition,
|
|
<code>this</code> is the Class being constructed.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> setContext: (name) ->
|
|
<span class="property">@body</span>.traverseChildren <span class="literal">false</span>, (node) ->
|
|
<span class="keyword">return</span> <span class="literal">false</span> <span class="keyword">if</span> node.classBody
|
|
<span class="keyword">if</span> node <span class="keyword">instanceof</span> Literal <span class="keyword">and</span> node.value <span class="keyword">is</span> <span class="string">'this'</span>
|
|
node.value = name
|
|
<span class="keyword">else</span> <span class="keyword">if</span> node <span class="keyword">instanceof</span> Code
|
|
node.klass = name
|
|
node.context = name <span class="keyword">if</span> node.bound</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-89">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-89">¶</a>
|
|
</div>
|
|
<p>Ensure that all functions bound to the instance are proxied in the
|
|
constructor.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> addBoundFunctions: (o) ->
|
|
<span class="keyword">for</span> bvar <span class="keyword">in</span> <span class="property">@boundFuncs</span>
|
|
lhs = (<span class="keyword">new</span> Value (<span class="keyword">new</span> Literal <span class="string">"this"</span>), [<span class="keyword">new</span> Access bvar]).compile o
|
|
<span class="property">@ctor</span>.body.unshift <span class="keyword">new</span> Literal <span class="string">"<span class="subst">#{lhs}</span> = <span class="subst">#{utility 'bind'}</span>(<span class="subst">#{lhs}</span>, this)"</span>
|
|
<span class="keyword">return</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-90">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-90">¶</a>
|
|
</div>
|
|
<p>Merge the properties from a top-level object as prototypal properties
|
|
on the class.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> addProperties: (node, name, o) ->
|
|
props = node.base.properties[..]
|
|
exprs = <span class="keyword">while</span> assign = props.shift()
|
|
<span class="keyword">if</span> assign <span class="keyword">instanceof</span> Assign
|
|
base = assign.variable.base
|
|
<span class="keyword">delete</span> assign.context
|
|
func = assign.value
|
|
<span class="keyword">if</span> base.value <span class="keyword">is</span> <span class="string">'constructor'</span>
|
|
<span class="keyword">if</span> <span class="property">@ctor</span>
|
|
assign.error <span class="string">'cannot define more than one constructor in a class'</span>
|
|
<span class="keyword">if</span> func.bound
|
|
assign.error <span class="string">'cannot define a constructor as a bound function'</span>
|
|
<span class="keyword">if</span> func <span class="keyword">instanceof</span> Code
|
|
assign = <span class="property">@ctor</span> = func
|
|
<span class="keyword">else</span>
|
|
<span class="property">@externalCtor</span> = o.scope.freeVariable <span class="string">'class'</span>
|
|
assign = <span class="keyword">new</span> Assign <span class="keyword">new</span> Literal(<span class="property">@externalCtor</span>), func
|
|
<span class="keyword">else</span>
|
|
<span class="keyword">if</span> assign.variable.<span class="keyword">this</span>
|
|
func.static = <span class="literal">yes</span>
|
|
<span class="keyword">if</span> func.bound
|
|
func.context = name
|
|
<span class="keyword">else</span>
|
|
assign.variable = <span class="keyword">new</span> Value(<span class="keyword">new</span> Literal(name), [(<span class="keyword">new</span> Access <span class="keyword">new</span> Literal <span class="string">'prototype'</span>), <span class="keyword">new</span> Access base])
|
|
<span class="keyword">if</span> func <span class="keyword">instanceof</span> Code <span class="keyword">and</span> func.bound
|
|
<span class="property">@boundFuncs</span>.push base
|
|
func.bound = <span class="literal">no</span>
|
|
assign
|
|
compact exprs</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-91">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-91">¶</a>
|
|
</div>
|
|
<p>Walk the body of the class, looking for prototype properties to be converted.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> walkBody: (name, o) ->
|
|
<span class="property">@traverseChildren</span> <span class="literal">false</span>, (child) =>
|
|
cont = <span class="literal">true</span>
|
|
<span class="keyword">return</span> <span class="literal">false</span> <span class="keyword">if</span> child <span class="keyword">instanceof</span> Class
|
|
<span class="keyword">if</span> child <span class="keyword">instanceof</span> Block
|
|
<span class="keyword">for</span> node, i <span class="keyword">in</span> exps = child.expressions
|
|
<span class="keyword">if</span> node <span class="keyword">instanceof</span> Value <span class="keyword">and</span> node.isObject(<span class="literal">true</span>)
|
|
cont = <span class="literal">false</span>
|
|
exps[i] = <span class="property">@addProperties</span> node, name, o
|
|
child.expressions = exps = flatten exps
|
|
cont <span class="keyword">and</span> child <span class="keyword">not</span> <span class="keyword">instanceof</span> Class</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-92">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-92">¶</a>
|
|
</div>
|
|
<p><code>use strict</code> (and other directives) must be the first expression statement(s)
|
|
of a function body. This method ensures the prologue is correctly positioned
|
|
above the <code>constructor</code>.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> hoistDirectivePrologue: ->
|
|
index = <span class="number">0</span>
|
|
{expressions} = <span class="property">@body</span>
|
|
++index <span class="keyword">while</span> (node = expressions[index]) <span class="keyword">and</span> node <span class="keyword">instanceof</span> Comment <span class="keyword">or</span>
|
|
node <span class="keyword">instanceof</span> Value <span class="keyword">and</span> node.isString()
|
|
<span class="property">@directives</span> = expressions.splice <span class="number">0</span>, index</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-93">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-93">¶</a>
|
|
</div>
|
|
<p>Make sure that a constructor is defined for the class, and properly
|
|
configured.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> ensureConstructor: (name, o) ->
|
|
missing = <span class="keyword">not</span> <span class="property">@ctor</span>
|
|
<span class="property">@ctor</span> <span class="keyword">or</span>= <span class="keyword">new</span> Code
|
|
<span class="property">@ctor</span>.ctor = <span class="property">@ctor</span>.name = name
|
|
<span class="property">@ctor</span>.klass = <span class="literal">null</span>
|
|
<span class="property">@ctor</span>.noReturn = <span class="literal">yes</span>
|
|
<span class="keyword">if</span> missing
|
|
superCall = <span class="keyword">new</span> Literal <span class="string">"<span class="subst">#{name}</span>.__super__.constructor.apply(this, arguments)"</span> <span class="keyword">if</span> <span class="property">@parent</span>
|
|
superCall = <span class="keyword">new</span> Literal <span class="string">"<span class="subst">#{@externalCtor}</span>.apply(this, arguments)"</span> <span class="keyword">if</span> <span class="property">@externalCtor</span>
|
|
<span class="keyword">if</span> superCall
|
|
ref = <span class="keyword">new</span> Literal o.scope.freeVariable <span class="string">'ref'</span>
|
|
<span class="property">@ctor</span>.body.unshift <span class="keyword">new</span> Assign ref, superCall
|
|
<span class="property">@addBoundFunctions</span> o
|
|
<span class="keyword">if</span> superCall
|
|
<span class="property">@ctor</span>.body.push ref
|
|
<span class="property">@ctor</span>.body.makeReturn()
|
|
<span class="property">@body</span>.expressions.unshift <span class="property">@ctor</span>
|
|
<span class="keyword">else</span>
|
|
<span class="property">@addBoundFunctions</span> o</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-94">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-94">¶</a>
|
|
</div>
|
|
<p>Instead of generating the JavaScript string directly, we build up the
|
|
equivalent syntax tree and compile that, in pieces. You can see the
|
|
constructor, property assignments, and inheritance getting built out below.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> compileNode: (o) ->
|
|
decl = <span class="property">@determineName</span>()
|
|
name = decl <span class="keyword">or</span> <span class="string">'_Class'</span>
|
|
name = <span class="string">"_<span class="subst">#{name}</span>"</span> <span class="keyword">if</span> name.reserved
|
|
lname = <span class="keyword">new</span> Literal name
|
|
|
|
<span class="property">@hoistDirectivePrologue</span>()
|
|
<span class="property">@setContext</span> name
|
|
<span class="property">@walkBody</span> name, o
|
|
<span class="property">@ensureConstructor</span> name, o
|
|
<span class="property">@body</span>.spaced = <span class="literal">yes</span>
|
|
<span class="property">@body</span>.expressions.unshift <span class="property">@ctor</span> <span class="keyword">unless</span> <span class="property">@ctor</span> <span class="keyword">instanceof</span> Code
|
|
<span class="property">@body</span>.expressions.push lname
|
|
<span class="property">@body</span>.expressions.unshift <span class="property">@directives</span>...
|
|
|
|
call = Closure.wrap <span class="property">@body</span>
|
|
|
|
<span class="keyword">if</span> <span class="property">@parent</span>
|
|
<span class="property">@superClass</span> = <span class="keyword">new</span> Literal o.scope.freeVariable <span class="string">'super'</span>, <span class="literal">no</span>
|
|
<span class="property">@body</span>.expressions.unshift <span class="keyword">new</span> Extends lname, <span class="property">@superClass</span>
|
|
call.args.push <span class="property">@parent</span>
|
|
params = call.variable.params <span class="keyword">or</span> call.variable.base.params
|
|
params.push <span class="keyword">new</span> Param <span class="property">@superClass</span>
|
|
|
|
klass = <span class="keyword">new</span> Parens call, <span class="literal">yes</span>
|
|
klass = <span class="keyword">new</span> Assign <span class="property">@variable</span>, klass <span class="keyword">if</span> <span class="property">@variable</span>
|
|
klass.compileToFragments o</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-95">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-95">¶</a>
|
|
</div>
|
|
<h3>Assign</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-96">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-96">¶</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>exports.Assign = <span class="class"><span class="keyword">class</span> <span class="title">Assign</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@variable</span>, <span class="property">@value</span>, <span class="property">@context</span>, options) ->
|
|
<span class="property">@param</span> = options <span class="keyword">and</span> options.param
|
|
<span class="property">@subpattern</span> = options <span class="keyword">and</span> options.subpattern
|
|
forbidden = (name = <span class="property">@variable</span>.unwrapAll().value) <span class="keyword">in</span> STRICT_PROSCRIBED
|
|
<span class="keyword">if</span> forbidden <span class="keyword">and</span> <span class="property">@context</span> <span class="keyword">isnt</span> <span class="string">'object'</span>
|
|
<span class="property">@variable</span>.error <span class="string">"variable name may not be \"<span class="subst">#{name}</span>\""</span>
|
|
|
|
children: [<span class="string">'variable'</span>, <span class="string">'value'</span>]
|
|
|
|
isStatement: (o) ->
|
|
o?.level <span class="keyword">is</span> LEVEL_TOP <span class="keyword">and</span> <span class="property">@context</span>? <span class="keyword">and</span> <span class="string">"?"</span> <span class="keyword">in</span> <span class="property">@context</span>
|
|
|
|
assigns: (name) ->
|
|
@[<span class="keyword">if</span> <span class="property">@context</span> <span class="keyword">is</span> <span class="string">'object'</span> <span class="keyword">then</span> <span class="string">'value'</span> <span class="keyword">else</span> <span class="string">'variable'</span>].assigns name
|
|
|
|
unfoldSoak: (o) ->
|
|
unfoldSoak o, <span class="keyword">this</span>, <span class="string">'variable'</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-97">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-97">¶</a>
|
|
</div>
|
|
<p>Compile an assignment, delegating to <code>compilePatternMatch</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: (o) ->
|
|
<span class="keyword">if</span> isValue = <span class="property">@variable</span> <span class="keyword">instanceof</span> Value
|
|
<span class="keyword">return</span> <span class="property">@compilePatternMatch</span> o <span class="keyword">if</span> <span class="property">@variable</span>.isArray() <span class="keyword">or</span> <span class="property">@variable</span>.isObject()
|
|
<span class="keyword">return</span> <span class="property">@compileSplice</span> o <span class="keyword">if</span> <span class="property">@variable</span>.isSplice()
|
|
<span class="keyword">return</span> <span class="property">@compileConditional</span> o <span class="keyword">if</span> <span class="property">@context</span> <span class="keyword">in</span> [<span class="string">'||='</span>, <span class="string">'&&='</span>, <span class="string">'?='</span>]
|
|
compiledName = <span class="property">@variable</span>.compileToFragments o, LEVEL_LIST
|
|
name = fragmentsToText compiledName
|
|
<span class="keyword">unless</span> <span class="property">@context</span>
|
|
varBase = <span class="property">@variable</span>.unwrapAll()
|
|
<span class="keyword">unless</span> varBase.isAssignable()
|
|
<span class="property">@variable</span>.error <span class="string">"\"<span class="subst">#{@variable.compile o}</span>\" cannot be assigned"</span>
|
|
<span class="keyword">unless</span> varBase.hasProperties?()
|
|
<span class="keyword">if</span> <span class="property">@param</span>
|
|
o.scope.add name, <span class="string">'var'</span>
|
|
<span class="keyword">else</span>
|
|
o.scope.find name
|
|
<span class="keyword">if</span> <span class="property">@value</span> <span class="keyword">instanceof</span> Code <span class="keyword">and</span> match = METHOD_DEF.exec name
|
|
<span class="property">@value</span>.klass = match[<span class="number">1</span>] <span class="keyword">if</span> match[<span class="number">1</span>]
|
|
<span class="property">@value</span>.name = match[<span class="number">2</span>] ? match[<span class="number">3</span>] ? match[<span class="number">4</span>] ? match[<span class="number">5</span>]
|
|
val = <span class="property">@value</span>.compileToFragments o, LEVEL_LIST
|
|
<span class="keyword">return</span> (compiledName.concat <span class="property">@makeCode</span>(<span class="string">": "</span>), val) <span class="keyword">if</span> <span class="property">@context</span> <span class="keyword">is</span> <span class="string">'object'</span>
|
|
answer = compiledName.concat <span class="property">@makeCode</span>(<span class="string">" <span class="subst">#{ @context <span class="keyword">or</span> '=' }</span> "</span>), val
|
|
<span class="keyword">if</span> o.level <= LEVEL_LIST <span class="keyword">then</span> answer <span class="keyword">else</span> <span class="property">@wrapInBraces</span> answer</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-98">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-98">¶</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.
|
|
See the <a href="http://wiki.ecmascript.org/doku.php?id=harmony:destructuring">ECMAScript Harmony Wiki</a>
|
|
for details.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> compilePatternMatch: (o) ->
|
|
top = o.level <span class="keyword">is</span> LEVEL_TOP
|
|
{value} = <span class="keyword">this</span>
|
|
{objects} = <span class="property">@variable</span>.base
|
|
<span class="keyword">unless</span> olen = objects.length
|
|
code = value.compileToFragments o
|
|
<span class="keyword">return</span> <span class="keyword">if</span> o.level >= LEVEL_OP <span class="keyword">then</span> <span class="property">@wrapInBraces</span> code <span class="keyword">else</span> code
|
|
isObject = <span class="property">@variable</span>.isObject()
|
|
<span class="keyword">if</span> top <span class="keyword">and</span> olen <span class="keyword">is</span> <span class="number">1</span> <span class="keyword">and</span> (obj = objects[<span class="number">0</span>]) <span class="keyword">not</span> <span class="keyword">instanceof</span> Splat</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-99">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-99">¶</a>
|
|
</div>
|
|
<p>Unroll simplest cases: <code>{v} = x</code> -> <code>v = x.v</code>
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">if</span> obj <span class="keyword">instanceof</span> Assign
|
|
{variable: {base: idx}, value: obj} = obj
|
|
<span class="keyword">else</span>
|
|
idx = <span class="keyword">if</span> isObject
|
|
<span class="keyword">if</span> obj.<span class="keyword">this</span> <span class="keyword">then</span> obj.properties[<span class="number">0</span>].name <span class="keyword">else</span> obj
|
|
<span class="keyword">else</span>
|
|
<span class="keyword">new</span> Literal <span class="number">0</span>
|
|
acc = IDENTIFIER.test idx.unwrap().value <span class="keyword">or</span> <span class="number">0</span>
|
|
value = <span class="keyword">new</span> Value value
|
|
value.properties.push <span class="keyword">new</span> (<span class="keyword">if</span> acc <span class="keyword">then</span> Access <span class="keyword">else</span> Index) idx
|
|
<span class="keyword">if</span> obj.unwrap().value <span class="keyword">in</span> RESERVED
|
|
obj.error <span class="string">"assignment to a reserved word: <span class="subst">#{obj.compile o}</span>"</span>
|
|
<span class="keyword">return</span> <span class="keyword">new</span> Assign(obj, value, <span class="literal">null</span>, param: <span class="property">@param</span>).compileToFragments o, LEVEL_TOP
|
|
vvar = value.compileToFragments o, LEVEL_LIST
|
|
vvarText = fragmentsToText vvar
|
|
assigns = []
|
|
splat = <span class="literal">false</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-100">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-100">¶</a>
|
|
</div>
|
|
<p>Make vvar into a simple variable if it isn't already.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">if</span> <span class="keyword">not</span> IDENTIFIER.test(vvarText) <span class="keyword">or</span> <span class="property">@variable</span>.assigns(vvarText)
|
|
assigns.push [<span class="property">@makeCode</span>(<span class="string">"<span class="subst">#{ ref = o.scope.freeVariable 'ref' }</span> = "</span>), vvar...]
|
|
vvar = [<span class="property">@makeCode</span> ref]
|
|
vvarText = ref
|
|
<span class="keyword">for</span> obj, i <span class="keyword">in</span> objects</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-101">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-101">¶</a>
|
|
</div>
|
|
<p>A regular array pattern-match.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> idx = i
|
|
<span class="keyword">if</span> isObject
|
|
<span class="keyword">if</span> obj <span class="keyword">instanceof</span> Assign</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-102">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-102">¶</a>
|
|
</div>
|
|
<p>A regular object pattern-match.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> {variable: {base: idx}, value: obj} = obj
|
|
<span class="keyword">else</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-103">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-103">¶</a>
|
|
</div>
|
|
<p>A shorthand <code>{a, b, @c} = val</code> pattern-match.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">if</span> obj.base <span class="keyword">instanceof</span> Parens
|
|
[obj, idx] = <span class="keyword">new</span> Value(obj.unwrapAll()).cacheReference o
|
|
<span class="keyword">else</span>
|
|
idx = <span class="keyword">if</span> obj.<span class="keyword">this</span> <span class="keyword">then</span> obj.properties[<span class="number">0</span>].name <span class="keyword">else</span> obj
|
|
<span class="keyword">if</span> <span class="keyword">not</span> splat <span class="keyword">and</span> obj <span class="keyword">instanceof</span> Splat
|
|
name = obj.name.unwrap().value
|
|
obj = obj.unwrap()
|
|
val = <span class="string">"<span class="subst">#{olen}</span> <= <span class="subst">#{vvarText}</span>.length ? <span class="subst">#{ utility 'slice' }</span>.call(<span class="subst">#{vvarText}</span>, <span class="subst">#{i}</span>"</span>
|
|
<span class="keyword">if</span> rest = olen - i - <span class="number">1</span>
|
|
ivar = o.scope.freeVariable <span class="string">'i'</span>
|
|
val += <span class="string">", <span class="subst">#{ivar}</span> = <span class="subst">#{vvarText}</span>.length - <span class="subst">#{rest}</span>) : (<span class="subst">#{ivar}</span> = <span class="subst">#{i}</span>, [])"</span>
|
|
<span class="keyword">else</span>
|
|
val += <span class="string">") : []"</span>
|
|
val = <span class="keyword">new</span> Literal val
|
|
splat = <span class="string">"<span class="subst">#{ivar}</span>++"</span>
|
|
<span class="keyword">else</span>
|
|
name = obj.unwrap().value
|
|
<span class="keyword">if</span> obj <span class="keyword">instanceof</span> Splat
|
|
obj.error <span class="string">"multiple splats are disallowed in an assignment"</span>
|
|
<span class="keyword">if</span> <span class="keyword">typeof</span> idx <span class="keyword">is</span> <span class="string">'number'</span>
|
|
idx = <span class="keyword">new</span> Literal splat <span class="keyword">or</span> idx
|
|
acc = <span class="literal">no</span>
|
|
<span class="keyword">else</span>
|
|
acc = isObject <span class="keyword">and</span> IDENTIFIER.test idx.unwrap().value <span class="keyword">or</span> <span class="number">0</span>
|
|
val = <span class="keyword">new</span> Value <span class="keyword">new</span> Literal(vvarText), [<span class="keyword">new</span> (<span class="keyword">if</span> acc <span class="keyword">then</span> Access <span class="keyword">else</span> Index) idx]
|
|
<span class="keyword">if</span> name? <span class="keyword">and</span> name <span class="keyword">in</span> RESERVED
|
|
obj.error <span class="string">"assignment to a reserved word: <span class="subst">#{obj.compile o}</span>"</span>
|
|
assigns.push <span class="keyword">new</span> Assign(obj, val, <span class="literal">null</span>, param: <span class="property">@param</span>, subpattern: <span class="literal">yes</span>).compileToFragments o, LEVEL_LIST
|
|
assigns.push vvar <span class="keyword">unless</span> top <span class="keyword">or</span> <span class="property">@subpattern</span>
|
|
fragments = <span class="property">@joinFragmentArrays</span> assigns, <span class="string">', '</span>
|
|
<span class="keyword">if</span> o.level < LEVEL_LIST <span class="keyword">then</span> fragments <span class="keyword">else</span> <span class="property">@wrapInBraces</span> fragments</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-104">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-104">¶</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: (o) ->
|
|
[left, right] = <span class="property">@variable</span>.cacheReference o</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-105">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-105">¶</a>
|
|
</div>
|
|
<p>Disallow conditional assignment of undefined variables.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">if</span> <span class="keyword">not</span> left.properties.length <span class="keyword">and</span> left.base <span class="keyword">instanceof</span> Literal <span class="keyword">and</span>
|
|
left.base.value != <span class="string">"this"</span> <span class="keyword">and</span> <span class="keyword">not</span> o.scope.check left.base.value
|
|
<span class="property">@variable</span>.error <span class="string">"the variable \"<span class="subst">#{left.base.value}</span>\" can't be assigned with <span class="subst">#{@context}</span> because it has not been declared before"</span>
|
|
<span class="keyword">if</span> <span class="string">"?"</span> <span class="keyword">in</span> <span class="property">@context</span> <span class="keyword">then</span> o.isExistentialEquals = <span class="literal">true</span>
|
|
<span class="keyword">new</span> Op(<span class="property">@context</span>[...-<span class="number">1</span>], left, <span class="keyword">new</span> Assign(right, <span class="property">@value</span>, <span class="string">'='</span>)).compileToFragments o</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-106">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-106">¶</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: (o) ->
|
|
{range: {from, to, exclusive}} = <span class="property">@variable</span>.properties.pop()
|
|
name = <span class="property">@variable</span>.compile o
|
|
<span class="keyword">if</span> from
|
|
[fromDecl, fromRef] = <span class="property">@cacheToCodeFragments</span> from.cache o, LEVEL_OP
|
|
<span class="keyword">else</span>
|
|
fromDecl = fromRef = <span class="string">'0'</span>
|
|
<span class="keyword">if</span> to
|
|
<span class="keyword">if</span> from?.isSimpleNumber() <span class="keyword">and</span> to.isSimpleNumber()
|
|
to = +to.compile(o) - +fromRef
|
|
to += <span class="number">1</span> <span class="keyword">unless</span> exclusive
|
|
<span class="keyword">else</span>
|
|
to = to.compile(o, LEVEL_ACCESS) + <span class="string">' - '</span> + fromRef
|
|
to += <span class="string">' + 1'</span> <span class="keyword">unless</span> exclusive
|
|
<span class="keyword">else</span>
|
|
to = <span class="string">"9e9"</span>
|
|
[valDef, valRef] = <span class="property">@value</span>.cache o, LEVEL_LIST
|
|
answer = [].concat <span class="property">@makeCode</span>(<span class="string">"[].splice.apply(<span class="subst">#{name}</span>, [<span class="subst">#{fromDecl}</span>, <span class="subst">#{to}</span>].concat("</span>), valDef, <span class="property">@makeCode</span>(<span class="string">")), "</span>), valRef
|
|
<span class="keyword">if</span> o.level > LEVEL_TOP <span class="keyword">then</span> <span class="property">@wrapInBraces</span> answer <span class="keyword">else</span> answer</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-107">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-107">¶</a>
|
|
</div>
|
|
<h3>Code</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-108">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-108">¶</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>exports.Code = <span class="class"><span class="keyword">class</span> <span class="title">Code</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (params, body, tag) ->
|
|
<span class="property">@params</span> = params <span class="keyword">or</span> []
|
|
<span class="property">@body</span> = body <span class="keyword">or</span> <span class="keyword">new</span> Block
|
|
<span class="property">@bound</span> = tag <span class="keyword">is</span> <span class="string">'boundfunc'</span>
|
|
<span class="property">@context</span> = <span class="string">'_this'</span> <span class="keyword">if</span> <span class="property">@bound</span>
|
|
|
|
children: [<span class="string">'params'</span>, <span class="string">'body'</span>]
|
|
|
|
isStatement: -> !!<span class="property">@ctor</span>
|
|
|
|
jumps: NO</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-109">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-109">¶</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 peeking at
|
|
the JavaScript <code>arguments</code> object. If the function is bound with the <code>=></code>
|
|
arrow, generates a wrapper that saves the current value of <code>this</code> through
|
|
a closure.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> compileNode: (o) ->
|
|
o.scope = <span class="keyword">new</span> Scope o.scope, <span class="property">@body</span>, <span class="keyword">this</span>
|
|
o.scope.shared = del(o, <span class="string">'sharedScope'</span>)
|
|
o.indent += TAB
|
|
<span class="keyword">delete</span> o.bare
|
|
<span class="keyword">delete</span> o.isExistentialEquals
|
|
params = []
|
|
exprs = []
|
|
<span class="property">@eachParamName</span> (name) -> <span class="comment"># this step must be performed before the others</span>
|
|
<span class="keyword">unless</span> o.scope.check name <span class="keyword">then</span> o.scope.parameter name
|
|
<span class="keyword">for</span> param <span class="keyword">in</span> <span class="property">@params</span> <span class="keyword">when</span> param.splat
|
|
<span class="keyword">for</span> {name: p} <span class="keyword">in</span> <span class="property">@params</span>
|
|
<span class="keyword">if</span> p.<span class="keyword">this</span> <span class="keyword">then</span> p = p.properties[<span class="number">0</span>].name
|
|
<span class="keyword">if</span> p.value <span class="keyword">then</span> o.scope.add p.value, <span class="string">'var'</span>, <span class="literal">yes</span>
|
|
splats = <span class="keyword">new</span> Assign <span class="keyword">new</span> Value(<span class="keyword">new</span> Arr(p.asReference o <span class="keyword">for</span> p <span class="keyword">in</span> <span class="property">@params</span>)),
|
|
<span class="keyword">new</span> Value <span class="keyword">new</span> Literal <span class="string">'arguments'</span>
|
|
<span class="keyword">break</span>
|
|
<span class="keyword">for</span> param <span class="keyword">in</span> <span class="property">@params</span>
|
|
<span class="keyword">if</span> param.isComplex()
|
|
val = ref = param.asReference o
|
|
val = <span class="keyword">new</span> Op <span class="string">'?'</span>, ref, param.value <span class="keyword">if</span> param.value
|
|
exprs.push <span class="keyword">new</span> Assign <span class="keyword">new</span> Value(param.name), val, <span class="string">'='</span>, param: <span class="literal">yes</span>
|
|
<span class="keyword">else</span>
|
|
ref = param
|
|
<span class="keyword">if</span> param.value
|
|
lit = <span class="keyword">new</span> Literal ref.name.value + <span class="string">' == null'</span>
|
|
val = <span class="keyword">new</span> Assign <span class="keyword">new</span> Value(param.name), param.value, <span class="string">'='</span>
|
|
exprs.push <span class="keyword">new</span> If lit, val
|
|
params.push ref <span class="keyword">unless</span> splats
|
|
wasEmpty = <span class="property">@body</span>.isEmpty()
|
|
exprs.unshift splats <span class="keyword">if</span> splats
|
|
<span class="property">@body</span>.expressions.unshift exprs... <span class="keyword">if</span> exprs.length
|
|
<span class="keyword">for</span> p, i <span class="keyword">in</span> params
|
|
params[i] = p.compileToFragments o
|
|
o.scope.parameter fragmentsToText params[i]
|
|
uniqs = []
|
|
<span class="property">@eachParamName</span> (name, node) ->
|
|
node.error <span class="string">"multiple parameters named '<span class="subst">#{name}</span>'"</span> <span class="keyword">if</span> name <span class="keyword">in</span> uniqs
|
|
uniqs.push name
|
|
<span class="property">@body</span>.makeReturn() <span class="keyword">unless</span> wasEmpty <span class="keyword">or</span> <span class="property">@noReturn</span>
|
|
<span class="keyword">if</span> <span class="property">@bound</span>
|
|
<span class="keyword">if</span> o.scope.parent.method?.bound
|
|
<span class="property">@bound</span> = <span class="property">@context</span> = o.scope.parent.method.context
|
|
<span class="keyword">else</span> <span class="keyword">if</span> <span class="keyword">not</span> <span class="property">@static</span>
|
|
o.scope.parent.assign <span class="string">'_this'</span>, <span class="string">'this'</span>
|
|
idt = o.indent
|
|
code = <span class="string">'function'</span>
|
|
code += <span class="string">' '</span> + <span class="property">@name</span> <span class="keyword">if</span> <span class="property">@ctor</span>
|
|
code += <span class="string">'('</span>
|
|
answer = [<span class="property">@makeCode</span>(code)]
|
|
<span class="keyword">for</span> p, i <span class="keyword">in</span> params
|
|
<span class="keyword">if</span> i <span class="keyword">then</span> answer.push <span class="property">@makeCode</span> <span class="string">", "</span>
|
|
answer.push p...
|
|
answer.push <span class="property">@makeCode</span> <span class="string">') {'</span>
|
|
answer = answer.concat(<span class="property">@makeCode</span>(<span class="string">"\n"</span>), <span class="property">@body</span>.compileWithDeclarations(o), <span class="property">@makeCode</span>(<span class="string">"\n<span class="subst">#{@tab}</span>"</span>)) <span class="keyword">unless</span> <span class="property">@body</span>.isEmpty()
|
|
answer.push <span class="property">@makeCode</span> <span class="string">'}'</span>
|
|
|
|
<span class="keyword">return</span> [<span class="property">@makeCode</span>(<span class="property">@tab</span>), answer...] <span class="keyword">if</span> <span class="property">@ctor</span>
|
|
<span class="keyword">if</span> <span class="property">@front</span> <span class="keyword">or</span> (o.level >= LEVEL_ACCESS) <span class="keyword">then</span> <span class="property">@wrapInBraces</span> answer <span class="keyword">else</span> answer
|
|
|
|
eachParamName: (iterator) ->
|
|
param.eachName iterator <span class="keyword">for</span> param <span class="keyword">in</span> <span class="property">@params</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-110">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-110">¶</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: (crossScope, func) ->
|
|
<span class="keyword">super</span>(crossScope, func) <span class="keyword">if</span> crossScope</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-111">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-111">¶</a>
|
|
</div>
|
|
<h3>Param</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-112">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-112">¶</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>exports.Param = <span class="class"><span class="keyword">class</span> <span class="title">Param</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@name</span>, <span class="property">@value</span>, <span class="property">@splat</span>) ->
|
|
<span class="keyword">if</span> (name = <span class="property">@name</span>.unwrapAll().value) <span class="keyword">in</span> STRICT_PROSCRIBED
|
|
<span class="property">@name</span>.error <span class="string">"parameter name \"<span class="subst">#{name}</span>\" is not allowed"</span>
|
|
|
|
children: [<span class="string">'name'</span>, <span class="string">'value'</span>]
|
|
|
|
compileToFragments: (o) ->
|
|
<span class="property">@name</span>.compileToFragments o, LEVEL_LIST
|
|
|
|
asReference: (o) ->
|
|
<span class="keyword">return</span> <span class="property">@reference</span> <span class="keyword">if</span> <span class="property">@reference</span>
|
|
node = <span class="property">@name</span>
|
|
<span class="keyword">if</span> node.<span class="keyword">this</span>
|
|
node = node.properties[<span class="number">0</span>].name
|
|
<span class="keyword">if</span> node.value.reserved
|
|
node = <span class="keyword">new</span> Literal o.scope.freeVariable node.value
|
|
<span class="keyword">else</span> <span class="keyword">if</span> node.isComplex()
|
|
node = <span class="keyword">new</span> Literal o.scope.freeVariable <span class="string">'arg'</span>
|
|
node = <span class="keyword">new</span> Value node
|
|
node = <span class="keyword">new</span> Splat node <span class="keyword">if</span> <span class="property">@splat</span>
|
|
<span class="property">@reference</span> = node
|
|
|
|
isComplex: ->
|
|
<span class="property">@name</span>.isComplex()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-113">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-113">¶</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: (iterator, name = <span class="property">@name</span>)->
|
|
<span class="function"><span class="title">atParam</span></span> = (obj) ->
|
|
node = obj.properties[<span class="number">0</span>].name
|
|
iterator node.value, node <span class="keyword">unless</span> node.value.reserved</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-114">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-114">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>simple literals <code>foo</code></li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">return</span> iterator name.value, name <span class="keyword">if</span> name <span class="keyword">instanceof</span> Literal</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-115">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-115">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>at-params <code>@foo</code></li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">return</span> atParam name <span class="keyword">if</span> name <span class="keyword">instanceof</span> Value
|
|
<span class="keyword">for</span> obj <span class="keyword">in</span> name.objects</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-116">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-116">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>assignments within destructured parameters <code>{foo:bar}</code></li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">if</span> obj <span class="keyword">instanceof</span> Assign
|
|
<span class="property">@eachName</span> iterator, obj.value.unwrap()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-117">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-117">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>splats within destructured parameters <code>[xs...]</code></li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">else</span> <span class="keyword">if</span> obj <span class="keyword">instanceof</span> Splat
|
|
node = obj.name.unwrap()
|
|
iterator node.value, node
|
|
<span class="keyword">else</span> <span class="keyword">if</span> obj <span class="keyword">instanceof</span> Value</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-118">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-118">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>destructured parameters within destructured parameters <code>[{a}]</code></li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">if</span> obj.isArray() <span class="keyword">or</span> obj.isObject()
|
|
<span class="property">@eachName</span> iterator, obj.base</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-119">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-119">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>at-params within destructured parameters <code>{@foo}</code></li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">else</span> <span class="keyword">if</span> obj.<span class="keyword">this</span>
|
|
atParam obj</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-120">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-120">¶</a>
|
|
</div>
|
|
<ul>
|
|
<li>simple destructured parameters {foo}</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="keyword">else</span> iterator obj.base.value, obj.base
|
|
<span class="keyword">else</span>
|
|
obj.error <span class="string">"illegal parameter <span class="subst">#{obj.compile()}</span>"</span>
|
|
<span class="keyword">return</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-121">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-121">¶</a>
|
|
</div>
|
|
<h3>Splat</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-122">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-122">¶</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>exports.Splat = <span class="class"><span class="keyword">class</span> <span class="title">Splat</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
|
|
children: [<span class="string">'name'</span>]
|
|
|
|
isAssignable: YES
|
|
|
|
constructor: (name) ->
|
|
<span class="property">@name</span> = <span class="keyword">if</span> name.compile <span class="keyword">then</span> name <span class="keyword">else</span> <span class="keyword">new</span> Literal name
|
|
|
|
assigns: (name) ->
|
|
<span class="property">@name</span>.assigns name
|
|
|
|
compileToFragments: (o) ->
|
|
<span class="property">@name</span>.compileToFragments o
|
|
|
|
unwrap: -> <span class="property">@name</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-123">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-123">¶</a>
|
|
</div>
|
|
<p>Utility function that converts an arbitrary number of elements, mixed with
|
|
splats, to a proper array.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> <span class="property">@compileSplattedArray</span>: (o, list, apply) ->
|
|
index = -<span class="number">1</span>
|
|
<span class="keyword">continue</span> <span class="keyword">while</span> (node = list[++index]) <span class="keyword">and</span> node <span class="keyword">not</span> <span class="keyword">instanceof</span> Splat
|
|
<span class="keyword">return</span> [] <span class="keyword">if</span> index >= list.length
|
|
<span class="keyword">if</span> list.length <span class="keyword">is</span> <span class="number">1</span>
|
|
node = list[<span class="number">0</span>]
|
|
fragments = node.compileToFragments o, LEVEL_LIST
|
|
<span class="keyword">return</span> fragments <span class="keyword">if</span> apply
|
|
<span class="keyword">return</span> [].concat node.makeCode(<span class="string">"<span class="subst">#{ utility 'slice' }</span>.call("</span>), fragments, node.makeCode(<span class="string">")"</span>)
|
|
args = list[index..]
|
|
<span class="keyword">for</span> node, i <span class="keyword">in</span> args
|
|
compiledNode = node.compileToFragments o, LEVEL_LIST
|
|
args[i] = <span class="keyword">if</span> node <span class="keyword">instanceof</span> Splat
|
|
<span class="keyword">then</span> [].concat node.makeCode(<span class="string">"<span class="subst">#{ utility 'slice' }</span>.call("</span>), compiledNode, node.makeCode(<span class="string">")"</span>)
|
|
<span class="keyword">else</span> [].concat node.makeCode(<span class="string">"["</span>), compiledNode, node.makeCode(<span class="string">"]"</span>)
|
|
<span class="keyword">if</span> index <span class="keyword">is</span> <span class="number">0</span>
|
|
node = list[<span class="number">0</span>]
|
|
concatPart = (node.joinFragmentArrays args[<span class="number">1.</span>.], <span class="string">', '</span>)
|
|
<span class="keyword">return</span> args[<span class="number">0</span>].concat node.makeCode(<span class="string">".concat("</span>), concatPart, node.makeCode(<span class="string">")"</span>)
|
|
base = (node.compileToFragments o, LEVEL_LIST <span class="keyword">for</span> node <span class="keyword">in</span> list[...index])
|
|
base = list[<span class="number">0</span>].joinFragmentArrays base, <span class="string">', '</span>
|
|
concatPart = list[index].joinFragmentArrays args, <span class="string">', '</span>
|
|
[].concat list[<span class="number">0</span>].makeCode(<span class="string">"["</span>), base, list[index].makeCode(<span class="string">"].concat("</span>), concatPart, (last list).makeCode(<span class="string">")"</span>)</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-124">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-124">¶</a>
|
|
</div>
|
|
<h3>While</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-125">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-125">¶</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>exports.While = <span class="class"><span class="keyword">class</span> <span class="title">While</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (condition, options) ->
|
|
<span class="property">@condition</span> = <span class="keyword">if</span> options?.invert <span class="keyword">then</span> condition.invert() <span class="keyword">else</span> condition
|
|
<span class="property">@guard</span> = options?.guard
|
|
|
|
children: [<span class="string">'condition'</span>, <span class="string">'guard'</span>, <span class="string">'body'</span>]
|
|
|
|
isStatement: YES
|
|
|
|
makeReturn: (res) ->
|
|
<span class="keyword">if</span> res
|
|
<span class="keyword">super</span>
|
|
<span class="keyword">else</span>
|
|
<span class="property">@returns</span> = <span class="keyword">not</span> <span class="property">@jumps</span> <span class="keyword">loop</span>: <span class="literal">yes</span>
|
|
<span class="keyword">this</span>
|
|
|
|
addBody: (<span class="property">@body</span>) ->
|
|
<span class="keyword">this</span>
|
|
|
|
jumps: ->
|
|
{expressions} = <span class="property">@body</span>
|
|
<span class="keyword">return</span> <span class="literal">no</span> <span class="keyword">unless</span> expressions.length
|
|
<span class="keyword">for</span> node <span class="keyword">in</span> expressions
|
|
<span class="keyword">return</span> node <span class="keyword">if</span> node.jumps <span class="keyword">loop</span>: <span class="literal">yes</span>
|
|
<span class="literal">no</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-126">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-126">¶</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: (o) ->
|
|
o.indent += TAB
|
|
set = <span class="string">''</span>
|
|
{body} = <span class="keyword">this</span>
|
|
<span class="keyword">if</span> body.isEmpty()
|
|
body = <span class="property">@makeCode</span> <span class="string">''</span>
|
|
<span class="keyword">else</span>
|
|
<span class="keyword">if</span> <span class="property">@returns</span>
|
|
body.makeReturn rvar = o.scope.freeVariable <span class="string">'results'</span>
|
|
set = <span class="string">"<span class="subst">#{@tab}</span><span class="subst">#{rvar}</span> = [];\n"</span>
|
|
<span class="keyword">if</span> <span class="property">@guard</span>
|
|
<span class="keyword">if</span> body.expressions.length > <span class="number">1</span>
|
|
body.expressions.unshift <span class="keyword">new</span> If (<span class="keyword">new</span> Parens <span class="property">@guard</span>).invert(), <span class="keyword">new</span> Literal <span class="string">"continue"</span>
|
|
<span class="keyword">else</span>
|
|
body = Block.wrap [<span class="keyword">new</span> If <span class="property">@guard</span>, body] <span class="keyword">if</span> <span class="property">@guard</span>
|
|
body = [].concat <span class="property">@makeCode</span>(<span class="string">"\n"</span>), (body.compileToFragments o, LEVEL_TOP), <span class="property">@makeCode</span>(<span class="string">"\n<span class="subst">#{@tab}</span>"</span>)
|
|
answer = [].concat <span class="property">@makeCode</span>(set + <span class="property">@tab</span> + <span class="string">"while ("</span>), <span class="property">@condition</span>.compileToFragments(o, LEVEL_PAREN),
|
|
<span class="property">@makeCode</span>(<span class="string">") {"</span>), body, <span class="property">@makeCode</span>(<span class="string">"}"</span>)
|
|
<span class="keyword">if</span> <span class="property">@returns</span>
|
|
answer.push <span class="property">@makeCode</span> <span class="string">"\n<span class="subst">#{@tab}</span>return <span class="subst">#{rvar}</span>;"</span>
|
|
answer</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-127">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-127">¶</a>
|
|
</div>
|
|
<h3>Op</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-128">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-128">¶</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>exports.Op = <span class="class"><span class="keyword">class</span> <span class="title">Op</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (op, first, second, flip ) ->
|
|
<span class="keyword">return</span> <span class="keyword">new</span> In first, second <span class="keyword">if</span> op <span class="keyword">is</span> <span class="string">'in'</span>
|
|
<span class="keyword">if</span> op <span class="keyword">is</span> <span class="string">'do'</span>
|
|
<span class="keyword">return</span> <span class="property">@generateDo</span> first
|
|
<span class="keyword">if</span> op <span class="keyword">is</span> <span class="string">'new'</span>
|
|
<span class="keyword">return</span> first.newInstance() <span class="keyword">if</span> first <span class="keyword">instanceof</span> Call <span class="keyword">and</span> <span class="keyword">not</span> first.<span class="keyword">do</span> <span class="keyword">and</span> <span class="keyword">not</span> first.isNew
|
|
first = <span class="keyword">new</span> Parens first <span class="keyword">if</span> first <span class="keyword">instanceof</span> Code <span class="keyword">and</span> first.bound <span class="keyword">or</span> first.<span class="keyword">do</span>
|
|
<span class="property">@operator</span> = CONVERSIONS[op] <span class="keyword">or</span> op
|
|
<span class="property">@first</span> = first
|
|
<span class="property">@second</span> = second
|
|
<span class="property">@flip</span> = !!flip
|
|
<span class="keyword">return</span> <span class="keyword">this</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-129">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-129">¶</a>
|
|
</div>
|
|
<p>The map of conversions from CoffeeScript to JavaScript symbols.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> CONVERSIONS =
|
|
<span class="string">'=='</span>: <span class="string">'==='</span>
|
|
<span class="string">'!='</span>: <span class="string">'!=='</span>
|
|
<span class="string">'of'</span>: <span class="string">'in'</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-130">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-130">¶</a>
|
|
</div>
|
|
<p>The map of invertible operators.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> INVERSIONS =
|
|
<span class="string">'!=='</span>: <span class="string">'==='</span>
|
|
<span class="string">'==='</span>: <span class="string">'!=='</span>
|
|
|
|
children: [<span class="string">'first'</span>, <span class="string">'second'</span>]
|
|
|
|
isSimpleNumber: NO
|
|
|
|
isUnary: ->
|
|
<span class="keyword">not</span> <span class="property">@second</span>
|
|
|
|
isComplex: ->
|
|
<span class="keyword">not</span> (<span class="property">@isUnary</span>() <span class="keyword">and</span> <span class="property">@operator</span> <span class="keyword">in</span> [<span class="string">'+'</span>, <span class="string">'-'</span>]) <span class="keyword">or</span> <span class="property">@first</span>.isComplex()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-131">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-131">¶</a>
|
|
</div>
|
|
<p>Am I capable of
|
|
<a href="http://docs.python.org/reference/expressions.html#notin">Python-style comparison chaining</a>?
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> isChainable: ->
|
|
<span class="property">@operator</span> <span class="keyword">in</span> [<span class="string">'<'</span>, <span class="string">'>'</span>, <span class="string">'>='</span>, <span class="string">'<='</span>, <span class="string">'==='</span>, <span class="string">'!=='</span>]
|
|
|
|
invert: ->
|
|
<span class="keyword">if</span> <span class="property">@isChainable</span>() <span class="keyword">and</span> <span class="property">@first</span>.isChainable()
|
|
allInvertable = <span class="literal">yes</span>
|
|
curr = <span class="keyword">this</span>
|
|
<span class="keyword">while</span> curr <span class="keyword">and</span> curr.operator
|
|
allInvertable <span class="keyword">and</span>= (curr.operator <span class="keyword">of</span> INVERSIONS)
|
|
curr = curr.first
|
|
<span class="keyword">return</span> <span class="keyword">new</span> Parens(<span class="keyword">this</span>).invert() <span class="keyword">unless</span> allInvertable
|
|
curr = <span class="keyword">this</span>
|
|
<span class="keyword">while</span> curr <span class="keyword">and</span> curr.operator
|
|
curr.invert = !curr.invert
|
|
curr.operator = INVERSIONS[curr.operator]
|
|
curr = curr.first
|
|
<span class="keyword">this</span>
|
|
<span class="keyword">else</span> <span class="keyword">if</span> op = INVERSIONS[<span class="property">@operator</span>]
|
|
<span class="property">@operator</span> = op
|
|
<span class="keyword">if</span> <span class="property">@first</span>.unwrap() <span class="keyword">instanceof</span> Op
|
|
<span class="property">@first</span>.invert()
|
|
<span class="keyword">this</span>
|
|
<span class="keyword">else</span> <span class="keyword">if</span> <span class="property">@second</span>
|
|
<span class="keyword">new</span> Parens(<span class="keyword">this</span>).invert()
|
|
<span class="keyword">else</span> <span class="keyword">if</span> <span class="property">@operator</span> <span class="keyword">is</span> <span class="string">'!'</span> <span class="keyword">and</span> (fst = <span class="property">@first</span>.unwrap()) <span class="keyword">instanceof</span> Op <span class="keyword">and</span>
|
|
fst.operator <span class="keyword">in</span> [<span class="string">'!'</span>, <span class="string">'in'</span>, <span class="string">'instanceof'</span>]
|
|
fst
|
|
<span class="keyword">else</span>
|
|
<span class="keyword">new</span> Op <span class="string">'!'</span>, <span class="keyword">this</span>
|
|
|
|
unfoldSoak: (o) ->
|
|
<span class="property">@operator</span> <span class="keyword">in</span> [<span class="string">'++'</span>, <span class="string">'--'</span>, <span class="string">'delete'</span>] <span class="keyword">and</span> unfoldSoak o, <span class="keyword">this</span>, <span class="string">'first'</span>
|
|
|
|
generateDo: (exp) ->
|
|
passedParams = []
|
|
func = <span class="keyword">if</span> exp <span class="keyword">instanceof</span> Assign <span class="keyword">and</span> (ref = exp.value.unwrap()) <span class="keyword">instanceof</span> Code
|
|
ref
|
|
<span class="keyword">else</span>
|
|
exp
|
|
<span class="keyword">for</span> param <span class="keyword">in</span> func.params <span class="keyword">or</span> []
|
|
<span class="keyword">if</span> param.value
|
|
passedParams.push param.value
|
|
<span class="keyword">delete</span> param.value
|
|
<span class="keyword">else</span>
|
|
passedParams.push param
|
|
call = <span class="keyword">new</span> Call exp, passedParams
|
|
call.<span class="keyword">do</span> = <span class="literal">yes</span>
|
|
call
|
|
|
|
compileNode: (o) ->
|
|
isChain = <span class="property">@isChainable</span>() <span class="keyword">and</span> <span class="property">@first</span>.isChainable()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-132">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-132">¶</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> <span class="property">@first</span>.front = <span class="property">@front</span> <span class="keyword">unless</span> isChain
|
|
<span class="keyword">if</span> <span class="property">@operator</span> <span class="keyword">is</span> <span class="string">'delete'</span> <span class="keyword">and</span> o.scope.check(<span class="property">@first</span>.unwrapAll().value)
|
|
<span class="property">@error</span> <span class="string">'delete operand may not be argument or var'</span>
|
|
<span class="keyword">if</span> <span class="property">@operator</span> <span class="keyword">in</span> [<span class="string">'--'</span>, <span class="string">'++'</span>] <span class="keyword">and</span> <span class="property">@first</span>.unwrapAll().value <span class="keyword">in</span> STRICT_PROSCRIBED
|
|
<span class="property">@error</span> <span class="string">"cannot increment/decrement \"<span class="subst">#{@first.unwrapAll().value}</span>\""</span>
|
|
<span class="keyword">return</span> <span class="property">@compileUnary</span> o <span class="keyword">if</span> <span class="property">@isUnary</span>()
|
|
<span class="keyword">return</span> <span class="property">@compileChain</span> o <span class="keyword">if</span> isChain
|
|
<span class="keyword">return</span> <span class="property">@compileExistence</span> o <span class="keyword">if</span> <span class="property">@operator</span> <span class="keyword">is</span> <span class="string">'?'</span>
|
|
answer = [].concat <span class="property">@first</span>.compileToFragments(o, LEVEL_OP), <span class="property">@makeCode</span>(<span class="string">' '</span> + <span class="property">@operator</span> + <span class="string">' '</span>),
|
|
<span class="property">@second</span>.compileToFragments(o, LEVEL_OP)
|
|
<span class="keyword">if</span> o.level <= LEVEL_OP <span class="keyword">then</span> answer <span class="keyword">else</span> <span class="property">@wrapInBraces</span> answer</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-133">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-133">¶</a>
|
|
</div>
|
|
<p>Mimic Python's chained comparisons when multiple comparison operators are
|
|
used sequentially. For example:
|
|
|
|
</p>
|
|
<pre><code>bin/coffee -e 'console.log 50 < 65 > 10'
|
|
true</code></pre>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> compileChain: (o) ->
|
|
[<span class="property">@first</span>.second, shared] = <span class="property">@first</span>.second.cache o
|
|
fst = <span class="property">@first</span>.compileToFragments o, LEVEL_OP
|
|
fragments = fst.concat <span class="property">@makeCode</span>(<span class="string">" <span class="subst">#{<span class="keyword">if</span> @invert <span class="keyword">then</span> '&&' <span class="keyword">else</span> '||'}</span> "</span>),
|
|
(shared.compileToFragments o), <span class="property">@makeCode</span>(<span class="string">" <span class="subst">#{@operator}</span> "</span>), (<span class="property">@second</span>.compileToFragments o, LEVEL_OP)
|
|
<span class="property">@wrapInBraces</span> fragments</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-134">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-134">¶</a>
|
|
</div>
|
|
<p>Keep reference to the left expression, unless this an existential assignment
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> compileExistence: (o) ->
|
|
<span class="keyword">if</span> !o.isExistentialEquals <span class="keyword">and</span> <span class="property">@first</span>.isComplex()
|
|
ref = <span class="keyword">new</span> Literal o.scope.freeVariable <span class="string">'ref'</span>
|
|
fst = <span class="keyword">new</span> Parens <span class="keyword">new</span> Assign ref, <span class="property">@first</span>
|
|
<span class="keyword">else</span>
|
|
fst = <span class="property">@first</span>
|
|
ref = fst
|
|
<span class="keyword">new</span> If(<span class="keyword">new</span> Existence(fst), ref, type: <span class="string">'if'</span>).addElse(<span class="property">@second</span>).compileToFragments o</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-135">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-135">¶</a>
|
|
</div>
|
|
<p>Compile a unary <strong>Op</strong>.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> compileUnary: (o) ->
|
|
parts = []
|
|
op = <span class="property">@operator</span>
|
|
parts.push [<span class="property">@makeCode</span> op]
|
|
<span class="keyword">if</span> op <span class="keyword">is</span> <span class="string">'!'</span> <span class="keyword">and</span> <span class="property">@first</span> <span class="keyword">instanceof</span> Existence
|
|
<span class="property">@first</span>.negated = <span class="keyword">not</span> <span class="property">@first</span>.negated
|
|
<span class="keyword">return</span> <span class="property">@first</span>.compileToFragments o
|
|
<span class="keyword">if</span> o.level >= LEVEL_ACCESS
|
|
<span class="keyword">return</span> (<span class="keyword">new</span> Parens <span class="keyword">this</span>).compileToFragments o
|
|
plusMinus = op <span class="keyword">in</span> [<span class="string">'+'</span>, <span class="string">'-'</span>]
|
|
parts.push [<span class="property">@makeCode</span>(<span class="string">' '</span>)] <span class="keyword">if</span> op <span class="keyword">in</span> [<span class="string">'new'</span>, <span class="string">'typeof'</span>, <span class="string">'delete'</span>] <span class="keyword">or</span>
|
|
plusMinus <span class="keyword">and</span> <span class="property">@first</span> <span class="keyword">instanceof</span> Op <span class="keyword">and</span> <span class="property">@first</span>.operator <span class="keyword">is</span> op
|
|
<span class="keyword">if</span> (plusMinus <span class="keyword">and</span> <span class="property">@first</span> <span class="keyword">instanceof</span> Op) <span class="keyword">or</span> (op <span class="keyword">is</span> <span class="string">'new'</span> <span class="keyword">and</span> <span class="property">@first</span>.isStatement o)
|
|
<span class="property">@first</span> = <span class="keyword">new</span> Parens <span class="property">@first</span>
|
|
parts.push <span class="property">@first</span>.compileToFragments o, LEVEL_OP
|
|
parts.reverse() <span class="keyword">if</span> <span class="property">@flip</span>
|
|
<span class="property">@joinFragmentArrays</span> parts, <span class="string">''</span>
|
|
|
|
toString: (idt) ->
|
|
<span class="keyword">super</span> idt, <span class="property">@constructor</span>.name + <span class="string">' '</span> + <span class="property">@operator</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-136">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-136">¶</a>
|
|
</div>
|
|
<h3>In</h3>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.In = <span class="class"><span class="keyword">class</span> <span class="title">In</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@object</span>, <span class="property">@array</span>) ->
|
|
|
|
children: [<span class="string">'object'</span>, <span class="string">'array'</span>]
|
|
|
|
invert: NEGATE
|
|
|
|
compileNode: (o) ->
|
|
<span class="keyword">if</span> <span class="property">@array</span> <span class="keyword">instanceof</span> Value <span class="keyword">and</span> <span class="property">@array</span>.isArray()
|
|
<span class="keyword">for</span> obj <span class="keyword">in</span> <span class="property">@array</span>.base.objects <span class="keyword">when</span> obj <span class="keyword">instanceof</span> Splat
|
|
hasSplat = <span class="literal">yes</span>
|
|
<span class="keyword">break</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-137">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-137">¶</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="keyword">return</span> <span class="property">@compileOrTest</span> o <span class="keyword">unless</span> hasSplat
|
|
<span class="property">@compileLoopTest</span> o
|
|
|
|
compileOrTest: (o) ->
|
|
<span class="keyword">return</span> [<span class="property">@makeCode</span>(<span class="string">"<span class="subst">#{!!@negated}</span>"</span>)] <span class="keyword">if</span> <span class="property">@array</span>.base.objects.length <span class="keyword">is</span> <span class="number">0</span>
|
|
[sub, ref] = <span class="property">@object</span>.cache o, LEVEL_OP
|
|
[cmp, cnj] = <span class="keyword">if</span> <span class="property">@negated</span> <span class="keyword">then</span> [<span class="string">' !== '</span>, <span class="string">' && '</span>] <span class="keyword">else</span> [<span class="string">' === '</span>, <span class="string">' || '</span>]
|
|
tests = []
|
|
<span class="keyword">for</span> item, i <span class="keyword">in</span> <span class="property">@array</span>.base.objects
|
|
<span class="keyword">if</span> i <span class="keyword">then</span> tests.push <span class="property">@makeCode</span> cnj
|
|
tests = tests.concat (<span class="keyword">if</span> i <span class="keyword">then</span> ref <span class="keyword">else</span> sub), <span class="property">@makeCode</span>(cmp), item.compileToFragments(o, LEVEL_ACCESS)
|
|
<span class="keyword">if</span> o.level < LEVEL_OP <span class="keyword">then</span> tests <span class="keyword">else</span> <span class="property">@wrapInBraces</span> tests
|
|
|
|
compileLoopTest: (o) ->
|
|
[sub, ref] = <span class="property">@object</span>.cache o, LEVEL_LIST
|
|
fragments = [].concat <span class="property">@makeCode</span>(utility(<span class="string">'indexOf'</span>) + <span class="string">".call("</span>), <span class="property">@array</span>.compileToFragments(o, LEVEL_LIST),
|
|
<span class="property">@makeCode</span>(<span class="string">", "</span>), ref, <span class="property">@makeCode</span>(<span class="string">") "</span> + <span class="keyword">if</span> <span class="property">@negated</span> <span class="keyword">then</span> <span class="string">'< 0'</span> <span class="keyword">else</span> <span class="string">'>= 0'</span>)
|
|
<span class="keyword">return</span> fragments <span class="keyword">if</span> fragmentsToText(sub) <span class="keyword">is</span> fragmentsToText(ref)
|
|
fragments = sub.concat <span class="property">@makeCode</span>(<span class="string">', '</span>), fragments
|
|
<span class="keyword">if</span> o.level < LEVEL_LIST <span class="keyword">then</span> fragments <span class="keyword">else</span> <span class="property">@wrapInBraces</span> fragments
|
|
|
|
toString: (idt) ->
|
|
<span class="keyword">super</span> idt, <span class="property">@constructor</span>.name + <span class="keyword">if</span> <span class="property">@negated</span> <span class="keyword">then</span> <span class="string">'!'</span> <span class="keyword">else</span> <span class="string">''</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-138">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-138">¶</a>
|
|
</div>
|
|
<h3>Try</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-139">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-139">¶</a>
|
|
</div>
|
|
<p>A classic <em>try/catch/finally</em> block.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.Try = <span class="class"><span class="keyword">class</span> <span class="title">Try</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@attempt</span>, <span class="property">@errorVariable</span>, <span class="property">@recovery</span>, <span class="property">@ensure</span>) ->
|
|
|
|
children: [<span class="string">'attempt'</span>, <span class="string">'recovery'</span>, <span class="string">'ensure'</span>]
|
|
|
|
isStatement: YES
|
|
|
|
jumps: (o) -> <span class="property">@attempt</span>.jumps(o) <span class="keyword">or</span> <span class="property">@recovery</span>?.jumps(o)
|
|
|
|
makeReturn: (res) ->
|
|
<span class="property">@attempt</span> = <span class="property">@attempt</span> .makeReturn res <span class="keyword">if</span> <span class="property">@attempt</span>
|
|
<span class="property">@recovery</span> = <span class="property">@recovery</span>.makeReturn res <span class="keyword">if</span> <span class="property">@recovery</span>
|
|
<span class="keyword">this</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-140">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-140">¶</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: (o) ->
|
|
o.indent += TAB
|
|
tryPart = <span class="property">@attempt</span>.compileToFragments o, LEVEL_TOP
|
|
|
|
catchPart = <span class="keyword">if</span> <span class="property">@recovery</span>
|
|
placeholder = <span class="keyword">new</span> Literal <span class="string">'_error'</span>
|
|
<span class="property">@recovery</span>.unshift <span class="keyword">new</span> Assign <span class="property">@errorVariable</span>, placeholder <span class="keyword">if</span> <span class="property">@errorVariable</span>
|
|
[].concat <span class="property">@makeCode</span>(<span class="string">" catch ("</span>), placeholder.compileToFragments(o), <span class="property">@makeCode</span>(<span class="string">") {\n"</span>),
|
|
<span class="property">@recovery</span>.compileToFragments(o, LEVEL_TOP), <span class="property">@makeCode</span>(<span class="string">"\n<span class="subst">#{@tab}</span>}"</span>)
|
|
<span class="keyword">else</span> <span class="keyword">unless</span> <span class="property">@ensure</span> <span class="keyword">or</span> <span class="property">@recovery</span>
|
|
[<span class="property">@makeCode</span>(<span class="string">' catch (_error) {}'</span>)]
|
|
<span class="keyword">else</span>
|
|
[]
|
|
|
|
ensurePart = <span class="keyword">if</span> <span class="property">@ensure</span> <span class="keyword">then</span> ([].concat <span class="property">@makeCode</span>(<span class="string">" finally {\n"</span>), <span class="property">@ensure</span>.compileToFragments(o, LEVEL_TOP),
|
|
<span class="property">@makeCode</span>(<span class="string">"\n<span class="subst">#{@tab}</span>}"</span>)) <span class="keyword">else</span> []
|
|
|
|
[].concat <span class="property">@makeCode</span>(<span class="string">"<span class="subst">#{@tab}</span>try {\n"</span>),
|
|
tryPart,
|
|
<span class="property">@makeCode</span>(<span class="string">"\n<span class="subst">#{@tab}</span>}"</span>), catchPart, ensurePart</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-141">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-141">¶</a>
|
|
</div>
|
|
<h3>Throw</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-142">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-142">¶</a>
|
|
</div>
|
|
<p>Simple node to throw an exception.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.Throw = <span class="class"><span class="keyword">class</span> <span class="title">Throw</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@expression</span>) ->
|
|
|
|
children: [<span class="string">'expression'</span>]
|
|
|
|
isStatement: YES
|
|
jumps: NO</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-143">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-143">¶</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: (o) ->
|
|
[].concat <span class="property">@makeCode</span>(<span class="property">@tab</span> + <span class="string">"throw "</span>), <span class="property">@expression</span>.compileToFragments(o), <span class="property">@makeCode</span>(<span class="string">";"</span>)</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-144">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-144">¶</a>
|
|
</div>
|
|
<h3>Existence</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-145">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-145">¶</a>
|
|
</div>
|
|
<p>Checks a variable for existence -- not <em>null</em> and not <em>undefined</em>. This is
|
|
similar to <code>.nil?</code> in Ruby, and avoids having to consult a JavaScript truth
|
|
table.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>exports.Existence = <span class="class"><span class="keyword">class</span> <span class="title">Existence</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@expression</span>) ->
|
|
|
|
children: [<span class="string">'expression'</span>]
|
|
|
|
invert: NEGATE
|
|
|
|
compileNode: (o) ->
|
|
<span class="property">@expression</span>.front = <span class="property">@front</span>
|
|
code = <span class="property">@expression</span>.compile o, LEVEL_OP
|
|
<span class="keyword">if</span> IDENTIFIER.test(code) <span class="keyword">and</span> <span class="keyword">not</span> o.scope.check code
|
|
[cmp, cnj] = <span class="keyword">if</span> <span class="property">@negated</span> <span class="keyword">then</span> [<span class="string">'==='</span>, <span class="string">'||'</span>] <span class="keyword">else</span> [<span class="string">'!=='</span>, <span class="string">'&&'</span>]
|
|
code = <span class="string">"typeof <span class="subst">#{code}</span> <span class="subst">#{cmp}</span> \"undefined\" <span class="subst">#{cnj}</span> <span class="subst">#{code}</span> <span class="subst">#{cmp}</span> null"</span>
|
|
<span class="keyword">else</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-146">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-146">¶</a>
|
|
</div>
|
|
<p>do not use strict equality here; it will break existing code
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> code = <span class="string">"<span class="subst">#{code}</span> <span class="subst">#{<span class="keyword">if</span> @negated <span class="keyword">then</span> '==' <span class="keyword">else</span> '!='}</span> null"</span>
|
|
[<span class="property">@makeCode</span>(<span class="keyword">if</span> o.level <= LEVEL_COND <span class="keyword">then</span> code <span class="keyword">else</span> <span class="string">"(<span class="subst">#{code}</span>)"</span>)]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-147">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-147">¶</a>
|
|
</div>
|
|
<h3>Parens</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-148">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-148">¶</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>exports.Parens = <span class="class"><span class="keyword">class</span> <span class="title">Parens</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@body</span>) ->
|
|
|
|
children: [<span class="string">'body'</span>]
|
|
|
|
unwrap : -> <span class="property">@body</span>
|
|
isComplex : -> <span class="property">@body</span>.isComplex()
|
|
|
|
compileNode: (o) ->
|
|
expr = <span class="property">@body</span>.unwrap()
|
|
<span class="keyword">if</span> expr <span class="keyword">instanceof</span> Value <span class="keyword">and</span> expr.isAtomic()
|
|
expr.front = <span class="property">@front</span>
|
|
<span class="keyword">return</span> expr.compileToFragments o
|
|
fragments = expr.compileToFragments o, LEVEL_PAREN
|
|
bare = o.level < LEVEL_OP <span class="keyword">and</span> (expr <span class="keyword">instanceof</span> Op <span class="keyword">or</span> expr <span class="keyword">instanceof</span> Call <span class="keyword">or</span>
|
|
(expr <span class="keyword">instanceof</span> For <span class="keyword">and</span> expr.returns))
|
|
<span class="keyword">if</span> bare <span class="keyword">then</span> fragments <span class="keyword">else</span> <span class="property">@wrapInBraces</span> fragments</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-149">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-149">¶</a>
|
|
</div>
|
|
<h3>For</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-150">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-150">¶</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>exports.For = <span class="class"><span class="keyword">class</span> <span class="title">For</span> <span class="keyword">extends</span> <span class="title">While</span></span>
|
|
constructor: (body, source) ->
|
|
{<span class="property">@source</span>, <span class="property">@guard</span>, <span class="property">@step</span>, <span class="property">@name</span>, <span class="property">@index</span>} = source
|
|
<span class="property">@body</span> = Block.wrap [body]
|
|
<span class="property">@own</span> = !!source.own
|
|
<span class="property">@object</span> = !!source.object
|
|
[<span class="property">@name</span>, <span class="property">@index</span>] = [<span class="property">@index</span>, <span class="property">@name</span>] <span class="keyword">if</span> <span class="property">@object</span>
|
|
<span class="property">@index</span>.error <span class="string">'index cannot be a pattern matching expression'</span> <span class="keyword">if</span> <span class="property">@index</span> <span class="keyword">instanceof</span> Value
|
|
<span class="property">@range</span> = <span class="property">@source</span> <span class="keyword">instanceof</span> Value <span class="keyword">and</span> <span class="property">@source</span>.base <span class="keyword">instanceof</span> Range <span class="keyword">and</span> <span class="keyword">not</span> <span class="property">@source</span>.properties.length
|
|
<span class="property">@pattern</span> = <span class="property">@name</span> <span class="keyword">instanceof</span> Value
|
|
<span class="property">@index</span>.error <span class="string">'indexes do not apply to range loops'</span> <span class="keyword">if</span> <span class="property">@range</span> <span class="keyword">and</span> <span class="property">@index</span>
|
|
<span class="property">@name</span>.error <span class="string">'cannot pattern match over range loops'</span> <span class="keyword">if</span> <span class="property">@range</span> <span class="keyword">and</span> <span class="property">@pattern</span>
|
|
<span class="property">@index</span>.error <span class="string">'cannot use own with for-in'</span> <span class="keyword">if</span> <span class="property">@own</span> <span class="keyword">and</span> <span class="keyword">not</span> <span class="property">@object</span>
|
|
<span class="property">@returns</span> = <span class="literal">false</span>
|
|
|
|
children: [<span class="string">'body'</span>, <span class="string">'source'</span>, <span class="string">'guard'</span>, <span class="string">'step'</span>]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-151">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-151">¶</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: (o) ->
|
|
body = Block.wrap [<span class="property">@body</span>]
|
|
lastJumps = last(body.expressions)?.jumps()
|
|
<span class="property">@returns</span> = <span class="literal">no</span> <span class="keyword">if</span> lastJumps <span class="keyword">and</span> lastJumps <span class="keyword">instanceof</span> Return
|
|
source = <span class="keyword">if</span> <span class="property">@range</span> <span class="keyword">then</span> <span class="property">@source</span>.base <span class="keyword">else</span> <span class="property">@source</span>
|
|
scope = o.scope
|
|
name = <span class="property">@name</span> <span class="keyword">and</span> (<span class="property">@name</span>.compile o, LEVEL_LIST)
|
|
index = <span class="property">@index</span> <span class="keyword">and</span> (<span class="property">@index</span>.compile o, LEVEL_LIST)
|
|
scope.find(name) <span class="keyword">if</span> name <span class="keyword">and</span> <span class="keyword">not</span> <span class="property">@pattern</span>
|
|
scope.find(index) <span class="keyword">if</span> index
|
|
rvar = scope.freeVariable <span class="string">'results'</span> <span class="keyword">if</span> <span class="property">@returns</span>
|
|
ivar = (<span class="property">@object</span> <span class="keyword">and</span> index) <span class="keyword">or</span> scope.freeVariable <span class="string">'i'</span>
|
|
kvar = (<span class="property">@range</span> <span class="keyword">and</span> name) <span class="keyword">or</span> index <span class="keyword">or</span> ivar
|
|
kvarAssign = <span class="keyword">if</span> kvar <span class="keyword">isnt</span> ivar <span class="keyword">then</span> <span class="string">"<span class="subst">#{kvar}</span> = "</span> <span class="keyword">else</span> <span class="string">""</span>
|
|
<span class="keyword">if</span> <span class="property">@step</span> <span class="keyword">and</span> <span class="keyword">not</span> <span class="property">@range</span>
|
|
[step, stepVar] = <span class="property">@cacheToCodeFragments</span> <span class="property">@step</span>.cache o, LEVEL_LIST
|
|
stepNum = stepVar.match SIMPLENUM
|
|
name = ivar <span class="keyword">if</span> <span class="property">@pattern</span>
|
|
varPart = <span class="string">''</span>
|
|
guardPart = <span class="string">''</span>
|
|
defPart = <span class="string">''</span>
|
|
idt1 = <span class="property">@tab</span> + TAB
|
|
<span class="keyword">if</span> <span class="property">@range</span>
|
|
forPartFragments = source.compileToFragments merge(o, {index: ivar, name, <span class="property">@step</span>})
|
|
<span class="keyword">else</span>
|
|
svar = <span class="property">@source</span>.compile o, LEVEL_LIST
|
|
<span class="keyword">if</span> (name <span class="keyword">or</span> <span class="property">@own</span>) <span class="keyword">and</span> <span class="keyword">not</span> IDENTIFIER.test svar
|
|
defPart += <span class="string">"<span class="subst">#{@tab}</span><span class="subst">#{ref = scope.freeVariable 'ref'}</span> = <span class="subst">#{svar}</span>;\n"</span>
|
|
svar = ref
|
|
<span class="keyword">if</span> name <span class="keyword">and</span> <span class="keyword">not</span> <span class="property">@pattern</span>
|
|
namePart = <span class="string">"<span class="subst">#{name}</span> = <span class="subst">#{svar}</span>[<span class="subst">#{kvar}</span>]"</span>
|
|
<span class="keyword">if</span> <span class="keyword">not</span> <span class="property">@object</span>
|
|
defPart += <span class="string">"<span class="subst">#{@tab}</span><span class="subst">#{step}</span>;\n"</span> <span class="keyword">if</span> step <span class="keyword">isnt</span> stepVar
|
|
lvar = scope.freeVariable <span class="string">'len'</span> <span class="keyword">unless</span> <span class="property">@step</span> <span class="keyword">and</span> stepNum <span class="keyword">and</span> down = (+stepNum < <span class="number">0</span>)
|
|
declare = <span class="string">"<span class="subst">#{kvarAssign}</span><span class="subst">#{ivar}</span> = 0, <span class="subst">#{lvar}</span> = <span class="subst">#{svar}</span>.length"</span>
|
|
declareDown = <span class="string">"<span class="subst">#{kvarAssign}</span><span class="subst">#{ivar}</span> = <span class="subst">#{svar}</span>.length - 1"</span>
|
|
compare = <span class="string">"<span class="subst">#{ivar}</span> < <span class="subst">#{lvar}</span>"</span>
|
|
compareDown = <span class="string">"<span class="subst">#{ivar}</span> >= 0"</span>
|
|
<span class="keyword">if</span> <span class="property">@step</span>
|
|
<span class="keyword">if</span> stepNum
|
|
<span class="keyword">if</span> down
|
|
compare = compareDown
|
|
declare = declareDown
|
|
<span class="keyword">else</span>
|
|
compare = <span class="string">"<span class="subst">#{stepVar}</span> > 0 ? <span class="subst">#{compare}</span> : <span class="subst">#{compareDown}</span>"</span>
|
|
declare = <span class="string">"(<span class="subst">#{stepVar}</span> > 0 ? (<span class="subst">#{declare}</span>) : <span class="subst">#{declareDown}</span>)"</span>
|
|
increment = <span class="string">"<span class="subst">#{ivar}</span> += <span class="subst">#{stepVar}</span>"</span>
|
|
<span class="keyword">else</span>
|
|
increment = <span class="string">"<span class="subst">#{<span class="keyword">if</span> kvar <span class="keyword">isnt</span> ivar <span class="keyword">then</span> "++#{ivar}</span>"</span> <span class="keyword">else</span> <span class="string">"<span class="subst">#{ivar}</span>++"</span>}<span class="string">"
|
|
forPartFragments = [@makeCode("</span><span class="comment">#{declare}; #{compare}; #{kvarAssign}#{increment}")]</span>
|
|
<span class="keyword">if</span> <span class="property">@returns</span>
|
|
resultPart = <span class="string">"<span class="subst">#{@tab}</span><span class="subst">#{rvar}</span> = [];\n"</span>
|
|
returnResult = <span class="string">"\n<span class="subst">#{@tab}</span>return <span class="subst">#{rvar}</span>;"</span>
|
|
body.makeReturn rvar
|
|
<span class="keyword">if</span> <span class="property">@guard</span>
|
|
<span class="keyword">if</span> body.expressions.length > <span class="number">1</span>
|
|
body.expressions.unshift <span class="keyword">new</span> If (<span class="keyword">new</span> Parens <span class="property">@guard</span>).invert(), <span class="keyword">new</span> Literal <span class="string">"continue"</span>
|
|
<span class="keyword">else</span>
|
|
body = Block.wrap [<span class="keyword">new</span> If <span class="property">@guard</span>, body] <span class="keyword">if</span> <span class="property">@guard</span>
|
|
<span class="keyword">if</span> <span class="property">@pattern</span>
|
|
body.expressions.unshift <span class="keyword">new</span> Assign <span class="property">@name</span>, <span class="keyword">new</span> Literal <span class="string">"<span class="subst">#{svar}</span>[<span class="subst">#{kvar}</span>]"</span>
|
|
defPartFragments = [].concat <span class="property">@makeCode</span>(defPart), <span class="property">@pluckDirectCall</span>(o, body)
|
|
varPart = <span class="string">"\n<span class="subst">#{idt1}</span><span class="subst">#{namePart}</span>;"</span> <span class="keyword">if</span> namePart
|
|
<span class="keyword">if</span> <span class="property">@object</span>
|
|
forPartFragments = [<span class="property">@makeCode</span>(<span class="string">"<span class="subst">#{kvar}</span> in <span class="subst">#{svar}</span>"</span>)]
|
|
guardPart = <span class="string">"\n<span class="subst">#{idt1}</span>if (!<span class="subst">#{utility 'hasProp'}</span>.call(<span class="subst">#{svar}</span>, <span class="subst">#{kvar}</span>)) continue;"</span> <span class="keyword">if</span> <span class="property">@own</span>
|
|
bodyFragments = body.compileToFragments merge(o, indent: idt1), LEVEL_TOP
|
|
<span class="keyword">if</span> bodyFragments <span class="keyword">and</span> (bodyFragments.length > <span class="number">0</span>)
|
|
bodyFragments = [].concat <span class="property">@makeCode</span>(<span class="string">"\n"</span>), bodyFragments, <span class="property">@makeCode</span>(<span class="string">"\n"</span>)
|
|
[].concat defPartFragments, <span class="property">@makeCode</span>(<span class="string">"<span class="subst">#{resultPart <span class="keyword">or</span> ''}</span><span class="subst">#{@tab}</span>for ("</span>),
|
|
forPartFragments, <span class="property">@makeCode</span>(<span class="string">") {<span class="subst">#{guardPart}</span><span class="subst">#{varPart}</span>"</span>), bodyFragments,
|
|
<span class="property">@makeCode</span>(<span class="string">"<span class="subst">#{@tab}</span>}<span class="subst">#{returnResult <span class="keyword">or</span> ''}</span>"</span>)
|
|
|
|
pluckDirectCall: (o, body) ->
|
|
defs = []
|
|
<span class="keyword">for</span> expr, idx <span class="keyword">in</span> body.expressions
|
|
expr = expr.unwrapAll()
|
|
<span class="keyword">continue</span> <span class="keyword">unless</span> expr <span class="keyword">instanceof</span> Call
|
|
val = expr.variable.unwrapAll()
|
|
<span class="keyword">continue</span> <span class="keyword">unless</span> (val <span class="keyword">instanceof</span> Code) <span class="keyword">or</span>
|
|
(val <span class="keyword">instanceof</span> Value <span class="keyword">and</span>
|
|
val.base?.unwrapAll() <span class="keyword">instanceof</span> Code <span class="keyword">and</span>
|
|
val.properties.length <span class="keyword">is</span> <span class="number">1</span> <span class="keyword">and</span>
|
|
val.properties[<span class="number">0</span>].name?.value <span class="keyword">in</span> [<span class="string">'call'</span>, <span class="string">'apply'</span>])
|
|
fn = val.base?.unwrapAll() <span class="keyword">or</span> val
|
|
ref = <span class="keyword">new</span> Literal o.scope.freeVariable <span class="string">'fn'</span>
|
|
base = <span class="keyword">new</span> Value ref
|
|
<span class="keyword">if</span> val.base
|
|
[val.base, base] = [base, val]
|
|
body.expressions[idx] = <span class="keyword">new</span> Call base, expr.args
|
|
defs = defs.concat <span class="property">@makeCode</span>(<span class="property">@tab</span>), (<span class="keyword">new</span> Assign(ref, fn).compileToFragments(o, LEVEL_TOP)), <span class="property">@makeCode</span>(<span class="string">';\n'</span>)
|
|
defs</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-152">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-152">¶</a>
|
|
</div>
|
|
<h3>Switch</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-153">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-153">¶</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>exports.Switch = <span class="class"><span class="keyword">class</span> <span class="title">Switch</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (<span class="property">@subject</span>, <span class="property">@cases</span>, <span class="property">@otherwise</span>) ->
|
|
|
|
children: [<span class="string">'subject'</span>, <span class="string">'cases'</span>, <span class="string">'otherwise'</span>]
|
|
|
|
isStatement: YES
|
|
|
|
jumps: (o = {block: <span class="literal">yes</span>}) ->
|
|
<span class="keyword">for</span> [conds, block] <span class="keyword">in</span> <span class="property">@cases</span>
|
|
<span class="keyword">return</span> block <span class="keyword">if</span> block.jumps o
|
|
<span class="property">@otherwise</span>?.jumps o
|
|
|
|
makeReturn: (res) ->
|
|
pair[<span class="number">1</span>].makeReturn res <span class="keyword">for</span> pair <span class="keyword">in</span> <span class="property">@cases</span>
|
|
<span class="property">@otherwise</span> <span class="keyword">or</span>= <span class="keyword">new</span> Block [<span class="keyword">new</span> Literal <span class="string">'void 0'</span>] <span class="keyword">if</span> res
|
|
<span class="property">@otherwise</span>?.makeReturn res
|
|
<span class="keyword">this</span>
|
|
|
|
compileNode: (o) ->
|
|
idt1 = o.indent + TAB
|
|
idt2 = o.indent = idt1 + TAB
|
|
fragments = [].concat <span class="property">@makeCode</span>(<span class="property">@tab</span> + <span class="string">"switch ("</span>),
|
|
(<span class="keyword">if</span> <span class="property">@subject</span> <span class="keyword">then</span> <span class="property">@subject</span>.compileToFragments(o, LEVEL_PAREN) <span class="keyword">else</span> <span class="property">@makeCode</span> <span class="string">"false"</span>),
|
|
<span class="property">@makeCode</span>(<span class="string">") {\n"</span>)
|
|
<span class="keyword">for</span> [conditions, block], i <span class="keyword">in</span> <span class="property">@cases</span>
|
|
<span class="keyword">for</span> cond <span class="keyword">in</span> flatten [conditions]
|
|
cond = cond.invert() <span class="keyword">unless</span> <span class="property">@subject</span>
|
|
fragments = fragments.concat <span class="property">@makeCode</span>(idt1 + <span class="string">"case "</span>), cond.compileToFragments(o, LEVEL_PAREN), <span class="property">@makeCode</span>(<span class="string">":\n"</span>)
|
|
fragments = fragments.concat body, <span class="property">@makeCode</span>(<span class="string">'\n'</span>) <span class="keyword">if</span> (body = block.compileToFragments o, LEVEL_TOP).length > <span class="number">0</span>
|
|
<span class="keyword">break</span> <span class="keyword">if</span> i <span class="keyword">is</span> <span class="property">@cases</span>.length - <span class="number">1</span> <span class="keyword">and</span> <span class="keyword">not</span> <span class="property">@otherwise</span>
|
|
expr = <span class="property">@lastNonComment</span> block.expressions
|
|
<span class="keyword">continue</span> <span class="keyword">if</span> expr <span class="keyword">instanceof</span> Return <span class="keyword">or</span> (expr <span class="keyword">instanceof</span> Literal <span class="keyword">and</span> expr.jumps() <span class="keyword">and</span> expr.value <span class="keyword">isnt</span> <span class="string">'debugger'</span>)
|
|
fragments.push cond.makeCode(idt2 + <span class="string">'break;\n'</span>)
|
|
<span class="keyword">if</span> <span class="property">@otherwise</span> <span class="keyword">and</span> <span class="property">@otherwise</span>.expressions.length
|
|
fragments.push <span class="property">@makeCode</span>(idt1 + <span class="string">"default:\n"</span>), (<span class="property">@otherwise</span>.compileToFragments o, LEVEL_TOP)..., <span class="property">@makeCode</span>(<span class="string">"\n"</span>)
|
|
fragments.push <span class="property">@makeCode</span> <span class="property">@tab</span> + <span class="string">'}'</span>
|
|
fragments</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-154">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-154">¶</a>
|
|
</div>
|
|
<h3>If</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-155">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-155">¶</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>exports.If = <span class="class"><span class="keyword">class</span> <span class="title">If</span> <span class="keyword">extends</span> <span class="title">Base</span></span>
|
|
constructor: (condition, <span class="property">@body</span>, options = {}) ->
|
|
<span class="property">@condition</span> = <span class="keyword">if</span> options.type <span class="keyword">is</span> <span class="string">'unless'</span> <span class="keyword">then</span> condition.invert() <span class="keyword">else</span> condition
|
|
<span class="property">@elseBody</span> = <span class="literal">null</span>
|
|
<span class="property">@isChain</span> = <span class="literal">false</span>
|
|
{<span class="property">@soak</span>} = options
|
|
|
|
children: [<span class="string">'condition'</span>, <span class="string">'body'</span>, <span class="string">'elseBody'</span>]
|
|
|
|
bodyNode: -> <span class="property">@body</span>?.unwrap()
|
|
elseBodyNode: -> <span class="property">@elseBody</span>?.unwrap()</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-156">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-156">¶</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: (elseBody) ->
|
|
<span class="keyword">if</span> <span class="property">@isChain</span>
|
|
<span class="property">@elseBodyNode</span>().addElse elseBody
|
|
<span class="keyword">else</span>
|
|
<span class="property">@isChain</span> = elseBody <span class="keyword">instanceof</span> If
|
|
<span class="property">@elseBody</span> = <span class="property">@ensureBlock</span> elseBody
|
|
<span class="keyword">this</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-157">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-157">¶</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: (o) ->
|
|
o?.level <span class="keyword">is</span> LEVEL_TOP <span class="keyword">or</span>
|
|
<span class="property">@bodyNode</span>().isStatement(o) <span class="keyword">or</span> <span class="property">@elseBodyNode</span>()?.isStatement(o)
|
|
|
|
jumps: (o) -> <span class="property">@body</span>.jumps(o) <span class="keyword">or</span> <span class="property">@elseBody</span>?.jumps(o)
|
|
|
|
compileNode: (o) ->
|
|
<span class="keyword">if</span> <span class="property">@isStatement</span> o <span class="keyword">then</span> <span class="property">@compileStatement</span> o <span class="keyword">else</span> <span class="property">@compileExpression</span> o
|
|
|
|
makeReturn: (res) ->
|
|
<span class="property">@elseBody</span> <span class="keyword">or</span>= <span class="keyword">new</span> Block [<span class="keyword">new</span> Literal <span class="string">'void 0'</span>] <span class="keyword">if</span> res
|
|
<span class="property">@body</span> <span class="keyword">and</span>= <span class="keyword">new</span> Block [<span class="property">@body</span>.makeReturn res]
|
|
<span class="property">@elseBody</span> <span class="keyword">and</span>= <span class="keyword">new</span> Block [<span class="property">@elseBody</span>.makeReturn res]
|
|
<span class="keyword">this</span>
|
|
|
|
ensureBlock: (node) ->
|
|
<span class="keyword">if</span> node <span class="keyword">instanceof</span> Block <span class="keyword">then</span> node <span class="keyword">else</span> <span class="keyword">new</span> Block [node]</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-158">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-158">¶</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: (o) ->
|
|
child = del o, <span class="string">'chainChild'</span>
|
|
exeq = del o, <span class="string">'isExistentialEquals'</span>
|
|
|
|
<span class="keyword">if</span> exeq
|
|
<span class="keyword">return</span> <span class="keyword">new</span> If(<span class="property">@condition</span>.invert(), <span class="property">@elseBodyNode</span>(), type: <span class="string">'if'</span>).compileToFragments o
|
|
|
|
indent = o.indent + TAB
|
|
cond = <span class="property">@condition</span>.compileToFragments o, LEVEL_PAREN
|
|
body = <span class="property">@ensureBlock</span>(<span class="property">@body</span>).compileToFragments merge o, {indent}
|
|
ifPart = [].concat <span class="property">@makeCode</span>(<span class="string">"if ("</span>), cond, <span class="property">@makeCode</span>(<span class="string">") {\n"</span>), body, <span class="property">@makeCode</span>(<span class="string">"\n<span class="subst">#{@tab}</span>}"</span>)
|
|
ifPart.unshift <span class="property">@makeCode</span> <span class="property">@tab</span> <span class="keyword">unless</span> child
|
|
<span class="keyword">return</span> ifPart <span class="keyword">unless</span> <span class="property">@elseBody</span>
|
|
answer = ifPart.concat <span class="property">@makeCode</span>(<span class="string">' else '</span>)
|
|
<span class="keyword">if</span> <span class="property">@isChain</span>
|
|
o.chainChild = <span class="literal">yes</span>
|
|
answer = answer.concat <span class="property">@elseBody</span>.unwrap().compileToFragments o, LEVEL_TOP
|
|
<span class="keyword">else</span>
|
|
answer = answer.concat <span class="property">@makeCode</span>(<span class="string">"{\n"</span>), <span class="property">@elseBody</span>.compileToFragments(merge(o, {indent}), LEVEL_TOP), <span class="property">@makeCode</span>(<span class="string">"\n<span class="subst">#{@tab}</span>}"</span>)
|
|
answer</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-159">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-159">¶</a>
|
|
</div>
|
|
<p>Compile the <code>If</code> as a conditional operator.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> compileExpression: (o) ->
|
|
cond = <span class="property">@condition</span>.compileToFragments o, LEVEL_COND
|
|
body = <span class="property">@bodyNode</span>().compileToFragments o, LEVEL_LIST
|
|
alt = <span class="keyword">if</span> <span class="property">@elseBodyNode</span>() <span class="keyword">then</span> <span class="property">@elseBodyNode</span>().compileToFragments(o, LEVEL_LIST) <span class="keyword">else</span> [<span class="property">@makeCode</span>(<span class="string">'void 0'</span>)]
|
|
fragments = cond.concat <span class="property">@makeCode</span>(<span class="string">" ? "</span>), body, <span class="property">@makeCode</span>(<span class="string">" : "</span>), alt
|
|
<span class="keyword">if</span> o.level >= LEVEL_COND <span class="keyword">then</span> <span class="property">@wrapInBraces</span> fragments <span class="keyword">else</span> fragments
|
|
|
|
unfoldSoak: ->
|
|
<span class="property">@soak</span> <span class="keyword">and</span> <span class="keyword">this</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-160">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-160">¶</a>
|
|
</div>
|
|
<h2>Faux-Nodes</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-161">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-161">¶</a>
|
|
</div>
|
|
<p>Faux-nodes are never created by the grammar, but are used during code
|
|
generation to generate other combinations of nodes.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-162">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h3">
|
|
<a class="pilcrow" href="#section-162">¶</a>
|
|
</div>
|
|
<h3>Closure</h3>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-163">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-163">¶</a>
|
|
</div>
|
|
<p>A faux-node used to wrap an expressions body in a closure.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>Closure =</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-164">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-164">¶</a>
|
|
</div>
|
|
<p>Wrap the expressions body, unless it contains a pure statement,
|
|
in which case, no dice. If the body mentions <code>this</code> or <code>arguments</code>,
|
|
then make sure that the closure wrapper preserves the original values.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> wrap: (expressions, statement, noReturn) ->
|
|
<span class="keyword">return</span> expressions <span class="keyword">if</span> expressions.jumps()
|
|
func = <span class="keyword">new</span> Code [], Block.wrap [expressions]
|
|
args = []
|
|
argumentsNode = expressions.contains <span class="property">@isLiteralArguments</span>
|
|
<span class="keyword">if</span> argumentsNode <span class="keyword">and</span> expressions.classBody
|
|
argumentsNode.error <span class="string">"Class bodies shouldn't reference arguments"</span>
|
|
<span class="keyword">if</span> argumentsNode <span class="keyword">or</span> expressions.contains <span class="property">@isLiteralThis</span>
|
|
meth = <span class="keyword">new</span> Literal <span class="keyword">if</span> argumentsNode <span class="keyword">then</span> <span class="string">'apply'</span> <span class="keyword">else</span> <span class="string">'call'</span>
|
|
args = [<span class="keyword">new</span> Literal <span class="string">'this'</span>]
|
|
args.push <span class="keyword">new</span> Literal <span class="string">'arguments'</span> <span class="keyword">if</span> argumentsNode
|
|
func = <span class="keyword">new</span> Value func, [<span class="keyword">new</span> Access meth]
|
|
func.noReturn = noReturn
|
|
call = <span class="keyword">new</span> Call func, args
|
|
<span class="keyword">if</span> statement <span class="keyword">then</span> Block.wrap [call] <span class="keyword">else</span> call
|
|
|
|
isLiteralArguments: (node) ->
|
|
node <span class="keyword">instanceof</span> Literal <span class="keyword">and</span> node.value <span class="keyword">is</span> <span class="string">'arguments'</span> <span class="keyword">and</span> <span class="keyword">not</span> node.asKey
|
|
|
|
isLiteralThis: (node) ->
|
|
(node <span class="keyword">instanceof</span> Literal <span class="keyword">and</span> node.value <span class="keyword">is</span> <span class="string">'this'</span> <span class="keyword">and</span> <span class="keyword">not</span> node.asKey) <span class="keyword">or</span>
|
|
(node <span class="keyword">instanceof</span> Code <span class="keyword">and</span> node.bound) <span class="keyword">or</span>
|
|
(node <span class="keyword">instanceof</span> Call <span class="keyword">and</span> node.isSuper)</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-165">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-165">¶</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="function"><span class="title">unfoldSoak</span></span> = (o, parent, name) ->
|
|
<span class="keyword">return</span> <span class="keyword">unless</span> ifn = parent[name].unfoldSoak o
|
|
parent[name] = ifn.body
|
|
ifn.body = <span class="keyword">new</span> Value parent
|
|
ifn</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-166">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-166">¶</a>
|
|
</div>
|
|
<h2>Constants</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-167">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-167">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>
|
|
UTILITIES =</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-168">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-168">¶</a>
|
|
</div>
|
|
<p>Correctly set up a prototype chain for inheritance, including a reference
|
|
to the superclass for <code>super()</code> calls, and copies of any static properties.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> extends: -> <span class="string">"""
|
|
function(child, parent) { for (var key in parent) { if (<span class="subst">#{utility 'hasProp'}</span>.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }
|
|
"""</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-169">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-169">¶</a>
|
|
</div>
|
|
<p>Create a function bound to the current value of "this".
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> bind: -> '''
|
|
function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
|
|
'''</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-170">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-170">¶</a>
|
|
</div>
|
|
<p>Discover if an item is in an array.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre> indexOf: -> <span class="string">"""
|
|
[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }
|
|
"""</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-171">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-171">¶</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="string">'{}.hasOwnProperty'</span>
|
|
slice : -> <span class="string">'[].slice'</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-172">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-172">¶</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="number">1</span> <span class="comment"># ...;</span>
|
|
LEVEL_PAREN = <span class="number">2</span> <span class="comment"># (...)</span>
|
|
LEVEL_LIST = <span class="number">3</span> <span class="comment"># [...]</span>
|
|
LEVEL_COND = <span class="number">4</span> <span class="comment"># ... ? x : y</span>
|
|
LEVEL_OP = <span class="number">5</span> <span class="comment"># !...</span>
|
|
LEVEL_ACCESS = <span class="number">6</span> <span class="comment"># ...[0]</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-173">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-173">¶</a>
|
|
</div>
|
|
<p>Tabs are two spaces for pretty printing.
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>TAB = <span class="string">' '</span>
|
|
|
|
IDENTIFIER_STR = <span class="string">"[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*"</span>
|
|
IDENTIFIER = <span class="regexp">/// ^ <span class="comment">#{IDENTIFIER_STR} $ ///</span>
|
|
SIMPLENUM = /^[+-]?\d+$/
|
|
METHOD_DEF = ///</span>
|
|
^
|
|
(?:
|
|
(<span class="comment">#{IDENTIFIER_STR})</span>
|
|
\.prototype
|
|
(?:
|
|
\.(<span class="comment">#{IDENTIFIER_STR})</span>
|
|
| \[(<span class="string">"(?:[^\\"</span>\r\n]|\\.)*<span class="string">"|'(?:[^\\'\r\n]|\\.)*')\]
|
|
| \[(0x[\da-fA-F]+ | \d*\.?\d+ (?:[eE][+-]?\d+)?)\]
|
|
)
|
|
)
|
|
|
|
|
(<span class="subst">#{IDENTIFIER_STR}</span>)
|
|
$
|
|
///</pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-174">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-174">¶</a>
|
|
</div>
|
|
<p>Is a literal value a string?
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<div class="content"><div class='highlight'><pre>IS_STRING = <span class="regexp">/^['"]/</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-175">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap for-h2">
|
|
<a class="pilcrow" href="#section-175">¶</a>
|
|
</div>
|
|
<h2>Utility Functions</h2>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-176">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-176">¶</a>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
|
|
<li id="section-177">
|
|
<div class="annotation">
|
|
|
|
<div class="pilwrap ">
|
|
<a class="pilcrow" href="#section-177">¶</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="function"><span class="title">utility</span></span> = (name) ->
|
|
ref = <span class="string">"__<span class="subst">#{name}</span>"</span>
|
|
Scope.root.assign ref, UTILITIES[name]()
|
|
ref
|
|
|
|
<span class="function"><span class="title">multident</span></span> = (code, tab) ->
|
|
code = code.replace <span class="regexp">/\n/g</span>, <span class="string">'$&'</span> + tab
|
|
code.replace <span class="regexp">/\s+$/</span>, <span class="string">''</span></pre></div></div>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</div>
|
|
</body>
|
|
</html>
|